~ubuntu-branches/ubuntu/saucy/phpmyadmin/saucy-proposed

« back to all changes in this revision

Viewing changes to libraries/tcpdf/tcpdf.php

  • Committer: Package Import Robot
  • Author(s): Thijs Kinkhorst
  • Date: 2012-02-14 19:25:33 UTC
  • mfrom: (1.2.35)
  • Revision ID: package-import@ubuntu.com-20120214192533-fhjisornt2d9ycxg
Tags: 4:3.4.10-1
* New upstream release.
  + Fixes ODS import (closes: #593621)
* Update reference to compressed README.Debian (closes: #656664)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
<?php
2
2
//============================================================+
3
3
// File name   : tcpdf.php
4
 
// Version     : 5.9.039
 
4
// Version     : 5.9.145
5
5
// Begin       : 2002-08-03
6
 
// Last Update : 2011-01-12
7
 
// Author      : Nicola Asuni - Tecnick.com S.r.l - Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
8
 
// License     : http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT GNU-LGPLv3 + YOU CAN'T REMOVE ANY TCPDF COPYRIGHT NOTICE OR LINK FROM THE GENERATED PDF DOCUMENTS.
 
6
// Last Update : 2012-01-28
 
7
// Author      : Nicola Asuni - Tecnick.com LTD - Manor Coach House, Church Hill, Aldershot, Hants, GU12 4RQ, UK - www.tecnick.com - info@tecnick.com
 
8
// License     : http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT GNU-LGPLv3
9
9
// -------------------------------------------------------------------
10
 
// Copyright (C) 2002-2011  Nicola Asuni - Tecnick.com S.r.l.
 
10
// Copyright (C) 2002-2012 Nicola Asuni - Tecnick.com LTD
11
11
//
12
12
// This file is part of TCPDF software library.
13
13
//
14
14
// TCPDF is free software: you can redistribute it and/or modify it
15
15
// under the terms of the GNU Lesser General Public License as
16
16
// published by the Free Software Foundation, either version 3 of the
17
 
// License, or (at your option) any later version. Additionally,
18
 
// YOU CAN'T REMOVE ANY TCPDF COPYRIGHT NOTICE OR LINK FROM THE
19
 
// GENERATED PDF DOCUMENTS.
 
17
// License, or (at your option) any later version.
20
18
//
21
19
// TCPDF is distributed in the hope that it will be useful, but
22
20
// WITHOUT ANY WARRANTY; without even the implied warranty of
30
28
// See LICENSE.TXT file for more information.
31
29
// -------------------------------------------------------------------
32
30
//
33
 
// Description : This is a PHP class for generating PDF documents without
34
 
//               requiring external extensions.
 
31
// Description :
 
32
//   This is a PHP class for generating PDF documents without requiring external extensions.
35
33
//
36
34
// NOTE:
37
35
//   This class was originally derived in 2002 from the Public
43
41
//  * no external libraries are required for the basic functions;
44
42
//  * all standard page formats, custom page formats, custom margins and units of measure;
45
43
//  * UTF-8 Unicode and Right-To-Left languages;
46
 
//  * TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;
 
44
//  * TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;
47
45
//  * font subsetting;
48
46
//  * methods to publish some XHTML + CSS code, Javascript and Forms;
49
47
//  * images, graphic (geometric figures) and transformation methods;
50
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)
51
 
//  * 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, QR-Code, PDF417;
52
 
//  * Grayscale, RGB, CMYK, Spot Colors and Transparencies;
 
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;
53
51
//  * automatic page header and footer management;
54
52
//  * document encryption up to 256 bit and digital signature certifications;
55
53
//  * transactions to UNDO commands;
57
55
//  * text rendering modes (fill, stroke and clipping);
58
56
//  * multiple columns mode;
59
57
//  * no-write page regions;
60
 
//  * bookmarks and table of content;
 
58
//  * bookmarks, named destinations and table of content;
61
59
//  * text hyphenation;
62
60
//  * text stretching and spacing (tracking/kerning);
63
61
//  * automatic page break, line break and text alignments including justification;
65
63
//  * move and delete pages;
66
64
//  * page compression (requires php-zlib extension);
67
65
//  * XOBject Templates;
 
66
//  * Layers and object visibility.
 
67
//      * PDF/A-1b support.
68
68
//
69
69
// -----------------------------------------------------------
70
70
// THANKS TO:
96
96
// Dominik Dzienia for QR-code support.
97
97
// Laurent Minguet for some suggestions.
98
98
// Christian Deligant for some suggestions and fixes.
 
99
// Travis Harris for crop mark suggestion.
99
100
// Anyone that has reported a bug or sent a suggestion.
100
101
//============================================================+
101
102
 
108
109
 * <li>no external libraries are required for the basic functions;</li>
109
110
 * <li>all standard page formats, custom page formats, custom margins and units of measure;</li>
110
111
 * <li>UTF-8 Unicode and Right-To-Left languages;</li>
111
 
 * <li>TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;</li>
 
112
 * <li>TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;</li>
112
113
 * <li>font subsetting;</li>
113
114
 * <li>methods to publish some XHTML + CSS code, Javascript and Forms;</li>
114
115
 * <li>images, graphic (geometric figures) and transformation methods;
115
116
 * <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>
116
 
 * <li>1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, QR-Code, PDF417;</li>
117
 
 * <li>Grayscale, RGB, CMYK, Spot Colors and Transparencies;</li>
 
117
 * <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>
 
118
 * <li>JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;</li>
118
119
 * <li>automatic page header and footer management;</li>
119
120
 * <li>document encryption up to 256 bit and digital signature certifications;</li>
120
121
 * <li>transactions to UNDO commands;</li>
122
123
 * <li>text rendering modes (fill, stroke and clipping);</li>
123
124
 * <li>multiple columns mode;</li>
124
125
 * <li>no-write page regions;</li>
125
 
 * <li>bookmarks and table of content;</li>
 
126
 * <li>bookmarks, named destinations and table of content;</li>
126
127
 * <li>text hyphenation;</li>
127
128
 * <li>text stretching and spacing (tracking/kerning);</li>
128
129
 * <li>automatic page break, line break and text alignments including justification;</li>
130
131
 * <li>move and delete pages;</li>
131
132
 * <li>page compression (requires php-zlib extension);</li>
132
133
 * <li>XOBject Templates;</li>
 
134
 * <li>Layers and object visibility;</li>
 
135
 * <li>PDF/A-1b support.</li>
133
136
 * </ul>
134
137
 * Tools to encode your unicode fonts are on fonts/utils directory.</p>
135
138
 * @package com.tecnick.tcpdf
136
139
 * @author Nicola Asuni
137
 
 * @version 5.9.039
 
140
 * @version 5.9.145
138
141
 */
139
142
 
140
143
// Main configuration file. Define the K_TCPDF_EXTERNAL_CONFIG constant to skip this file.
146
149
 * TCPDF project (http://www.tcpdf.org) has been originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br>
147
150
 * @package com.tecnick.tcpdf
148
151
 * @brief PHP class for generating PDF documents without requiring external extensions.
149
 
 * @version 5.9.039
 
152
 * @version 5.9.145
150
153
 * @author Nicola Asuni - info@tecnick.com
151
154
 */
152
155
class TCPDF {
153
156
 
154
 
    // private properties
155
 
 
156
 
    /**
157
 
     * Current TCPDF version.
158
 
     * @private
159
 
     */
160
 
    private $tcpdf_version = '5.9.039';
161
 
 
162
 
    // Protected properties
163
 
 
164
 
    /**
165
 
     * Current page number.
166
 
     * @protected
167
 
     */
168
 
    protected $page;
169
 
 
170
 
    /**
171
 
     * Current object number.
172
 
     * @protected
173
 
     */
174
 
    protected $n;
175
 
 
176
 
    /**
177
 
     * Array of object offsets.
178
 
     * @protected
179
 
     */
180
 
    protected $offsets;
181
 
 
182
 
    /**
183
 
     * Buffer holding in-memory PDF.
184
 
     * @protected
185
 
     */
186
 
    protected $buffer;
187
 
 
188
 
    /**
189
 
     * Array containing pages.
190
 
     * @protected
191
 
     */
192
 
    protected $pages = array();
193
 
 
194
 
    /**
195
 
     * Current document state.
196
 
     * @protected
197
 
     */
198
 
    protected $state;
199
 
 
200
 
    /**
201
 
     * Compression flag.
202
 
     * @protected
203
 
     */
204
 
    protected $compress;
205
 
 
206
 
    /**
207
 
     * Current page orientation (P = Portrait, L = Landscape).
208
 
     * @protected
209
 
     */
210
 
    protected $CurOrientation;
211
 
 
212
 
    /**
213
 
     * Page dimensions.
214
 
     * @protected
215
 
     */
216
 
    protected $pagedim = array();
217
 
 
218
 
    /**
219
 
     * Scale factor (number of points in user unit).
220
 
     * @protected
221
 
     */
222
 
    protected $k;
223
 
 
224
 
    /**
225
 
     * Width of page format in points.
226
 
     * @protected
227
 
     */
228
 
    protected $fwPt;
229
 
 
230
 
    /**
231
 
     * Height of page format in points.
232
 
     * @protected
233
 
     */
234
 
    protected $fhPt;
235
 
 
236
 
    /**
237
 
     * Current width of page in points.
238
 
     * @protected
239
 
     */
240
 
    protected $wPt;
241
 
 
242
 
    /**
243
 
     * Current height of page in points.
244
 
     * @protected
245
 
     */
246
 
    protected $hPt;
247
 
 
248
 
    /**
249
 
     * Current width of page in user unit.
250
 
     * @protected
251
 
     */
252
 
    protected $w;
253
 
 
254
 
    /**
255
 
     * Current height of page in user unit.
256
 
     * @protected
257
 
     */
258
 
    protected $h;
259
 
 
260
 
    /**
261
 
     * Left margin.
262
 
     * @protected
263
 
     */
264
 
    protected $lMargin;
265
 
 
266
 
    /**
267
 
     * Top margin.
268
 
     * @protected
269
 
     */
270
 
    protected $tMargin;
271
 
 
272
 
    /**
273
 
     * Right margin.
274
 
     * @protected
275
 
     */
276
 
    protected $rMargin;
277
 
 
278
 
    /**
279
 
     * Page break margin.
280
 
     * @protected
281
 
     */
282
 
    protected $bMargin;
283
 
 
284
 
    /**
285
 
     * Array of cell internal paddings ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
286
 
     * @since 5.9.000 (2010-10-03)
287
 
     * @protected
288
 
     */
289
 
    protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
290
 
 
291
 
    /**
292
 
     * Array of cell margins ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
293
 
     * @since 5.9.000 (2010-10-04)
294
 
     * @protected
295
 
     */
296
 
    protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
297
 
 
298
 
    /**
299
 
     * Current horizontal position in user unit for cell positioning.
300
 
     * @protected
301
 
     */
302
 
    protected $x;
303
 
 
304
 
    /**
305
 
     * Current vertical position in user unit for cell positioning.
306
 
     * @protected
307
 
     */
308
 
    protected $y;
309
 
 
310
 
    /**
311
 
     * Height of last cell printed.
312
 
     * @protected
313
 
     */
314
 
    protected $lasth;
315
 
 
316
 
    /**
317
 
     * Line width in user unit.
318
 
     * @protected
319
 
     */
320
 
    protected $LineWidth;
321
 
 
322
 
    /**
323
 
     * Array of standard font names.
324
 
     * @protected
325
 
     */
326
 
    protected $CoreFonts;
327
 
 
328
 
    /**
329
 
     * Array of used fonts.
330
 
     * @protected
331
 
     */
332
 
    protected $fonts = array();
333
 
 
334
 
    /**
335
 
     * Array of font files.
336
 
     * @protected
337
 
     */
338
 
    protected $FontFiles = array();
339
 
 
340
 
    /**
341
 
     * Array of encoding differences.
342
 
     * @protected
343
 
     */
344
 
    protected $diffs = array();
345
 
 
346
 
    /**
347
 
     * Array of used images.
348
 
     * @protected
349
 
     */
350
 
    protected $images = array();
351
 
 
352
 
    /**
353
 
     * Array of Annotations in pages.
354
 
     * @protected
355
 
     */
356
 
    protected $PageAnnots = array();
357
 
 
358
 
    /**
359
 
     * Array of internal links.
360
 
     * @protected
361
 
     */
362
 
    protected $links = array();
363
 
 
364
 
    /**
365
 
     * Current font family.
366
 
     * @protected
367
 
     */
368
 
    protected $FontFamily;
369
 
 
370
 
    /**
371
 
     * Current font style.
372
 
     * @protected
373
 
     */
374
 
    protected $FontStyle;
375
 
 
376
 
    /**
377
 
     * Current font ascent (distance between font top and baseline).
378
 
     * @protected
379
 
     * @since 2.8.000 (2007-03-29)
380
 
     */
381
 
    protected $FontAscent;
382
 
 
383
 
    /**
384
 
     * Current font descent (distance between font bottom and baseline).
385
 
     * @protected
386
 
     * @since 2.8.000 (2007-03-29)
387
 
     */
388
 
    protected $FontDescent;
389
 
 
390
 
    /**
391
 
     * Underlining flag.
392
 
     * @protected
393
 
     */
394
 
    protected $underline;
395
 
 
396
 
    /**
397
 
     * Overlining flag.
398
 
     * @protected
399
 
     */
400
 
    protected $overline;
401
 
 
402
 
    /**
403
 
     * Current font info.
404
 
     * @protected
405
 
     */
406
 
    protected $CurrentFont;
407
 
 
408
 
    /**
409
 
     * Current font size in points.
410
 
     * @protected
411
 
     */
412
 
    protected $FontSizePt;
413
 
 
414
 
    /**
415
 
     * Current font size in user unit.
416
 
     * @protected
417
 
     */
418
 
    protected $FontSize;
419
 
 
420
 
    /**
421
 
     * Commands for drawing color.
422
 
     * @protected
423
 
     */
424
 
    protected $DrawColor;
425
 
 
426
 
    /**
427
 
     * Commands for filling color.
428
 
     * @protected
429
 
     */
430
 
    protected $FillColor;
431
 
 
432
 
    /**
433
 
     * Commands for text color.
434
 
     * @protected
435
 
     */
436
 
    protected $TextColor;
437
 
 
438
 
    /**
439
 
     * Indicates whether fill and text colors are different.
440
 
     * @protected
441
 
     */
442
 
    protected $ColorFlag;
443
 
 
444
 
    /**
445
 
     * Automatic page breaking.
446
 
     * @protected
447
 
     */
448
 
    protected $AutoPageBreak;
449
 
 
450
 
    /**
451
 
     * Threshold used to trigger page breaks.
452
 
     * @protected
453
 
     */
454
 
    protected $PageBreakTrigger;
455
 
 
456
 
    /**
457
 
     * Flag set when processing footer.
458
 
     * @protected
459
 
     */
460
 
    protected $InFooter = false;
461
 
 
462
 
    /**
463
 
     * Zoom display mode.
464
 
     * @protected
465
 
     */
466
 
    protected $ZoomMode;
467
 
 
468
 
    /**
469
 
     * Layout display mode.
470
 
     * @protected
471
 
     */
472
 
    protected $LayoutMode;
473
 
 
474
 
    /**
475
 
     * If true set the document information dictionary in Unicode.
476
 
     * @protected
477
 
     */
478
 
    protected $docinfounicode = true;
479
 
 
480
 
    /**
481
 
     * Document title.
482
 
     * @protected
483
 
     */
484
 
    protected $title = '';
485
 
 
486
 
    /**
487
 
     * Document subject.
488
 
     * @protected
489
 
     */
490
 
    protected $subject = '';
491
 
 
492
 
    /**
493
 
     * Document author.
494
 
     * @protected
495
 
     */
496
 
    protected $author = '';
497
 
 
498
 
    /**
499
 
     * Document keywords.
500
 
     * @protected
501
 
     */
502
 
    protected $keywords = '';
503
 
 
504
 
    /**
505
 
     * Document creator.
506
 
     * @protected
507
 
     */
508
 
    protected $creator = '';
509
 
 
510
 
    /**
511
 
     * String alias for total number of pages.
512
 
     * @protected
513
 
     */
514
 
    protected $AliasNbPages = '{nb}';
515
 
 
516
 
    /**
517
 
     * String alias for page number.
518
 
     * @protected
519
 
     */
520
 
    protected $AliasNumPage = '{pnb}';
521
 
 
522
 
    /**
523
 
     * The right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image.
524
 
     * @since 2002-07-31
525
 
     * @author Nicola Asuni
526
 
     * @protected
527
 
     */
528
 
    protected $img_rb_x;
529
 
 
530
 
    /**
531
 
     * The right-bottom corner Y coordinate of last inserted image.
532
 
     * @since 2002-07-31
533
 
     * @author Nicola Asuni
534
 
     * @protected
535
 
     */
536
 
    protected $img_rb_y;
537
 
 
538
 
    /**
539
 
     * Adjusting factor to convert pixels to user units.
540
 
     * @since 2004-06-14
541
 
     * @author Nicola Asuni
542
 
     * @protected
543
 
     */
544
 
    protected $imgscale = 1;
545
 
 
546
 
    /**
547
 
     * Boolean flag set to true when the input text is unicode (require unicode fonts).
548
 
     * @since 2005-01-02
549
 
     * @author Nicola Asuni
550
 
     * @protected
551
 
     */
552
 
    protected $isunicode = false;
553
 
 
554
 
    /**
555
 
     * Object containing unicode data.
556
 
     * @since 5.9.004 (2010-10-18)
557
 
     * @author Nicola Asuni
558
 
     * @protected
559
 
     */
560
 
    protected $unicode;
561
 
 
562
 
    /**
563
 
     * PDF version.
564
 
     * @since 1.5.3
565
 
     * @protected
566
 
     */
567
 
    protected $PDFVersion = '1.7';
568
 
 
569
 
    /**
570
 
     * Minimum distance between header and top page margin.
571
 
     * @protected
572
 
     */
573
 
    protected $header_margin;
574
 
 
575
 
    /**
576
 
     * Minimum distance between footer and bottom page margin.
577
 
     * @protected
578
 
     */
579
 
    protected $footer_margin;
580
 
 
581
 
    /**
582
 
     * Original left margin value.
583
 
     * @protected
584
 
     * @since 1.53.0.TC013
585
 
     */
586
 
    protected $original_lMargin;
587
 
 
588
 
    /**
589
 
     * Original right margin value.
590
 
     * @protected
591
 
     * @since 1.53.0.TC013
592
 
     */
593
 
    protected $original_rMargin;
594
 
 
595
 
    /**
596
 
     * Default font used on page header.
597
 
     * @protected
598
 
     */
599
 
    protected $header_font;
600
 
 
601
 
    /**
602
 
     * Default font used on page footer.
603
 
     * @protected
604
 
     */
605
 
    protected $footer_font;
606
 
 
607
 
    /**
608
 
     * Language templates.
609
 
     * @protected
610
 
     */
611
 
    protected $l;
612
 
 
613
 
    /**
614
 
     * Barcode to print on page footer (only if set).
615
 
     * @protected
616
 
     */
617
 
    protected $barcode = false;
618
 
 
619
 
    /**
620
 
     * Boolean flag to print/hide page header.
621
 
     * @protected
622
 
     */
623
 
    protected $print_header = true;
624
 
 
625
 
    /**
626
 
     * Boolean flag to print/hide page footer.
627
 
     * @protected
628
 
     */
629
 
    protected $print_footer = true;
630
 
 
631
 
    /**
632
 
     * Header image logo.
633
 
     * @protected
634
 
     */
635
 
    protected $header_logo = '';
636
 
 
637
 
    /**
638
 
     * Width of header image logo in user units.
639
 
     * @protected
640
 
     */
641
 
    protected $header_logo_width = 30;
642
 
 
643
 
    /**
644
 
     * Title to be printed on default page header.
645
 
     * @protected
646
 
     */
647
 
    protected $header_title = '';
648
 
 
649
 
    /**
650
 
     * String to pring on page header after title.
651
 
     * @protected
652
 
     */
653
 
    protected $header_string = '';
654
 
 
655
 
    /**
656
 
     * Default number of columns for html table.
657
 
     * @protected
658
 
     */
659
 
    protected $default_table_columns = 4;
660
 
 
661
 
    // variables for html parser
662
 
 
663
 
    /**
664
 
     * HTML PARSER: array to store current link and rendering styles.
665
 
     * @protected
666
 
     */
667
 
    protected $HREF = array();
668
 
 
669
 
    /**
670
 
     * List of available fonts on filesystem.
671
 
     * @protected
672
 
     */
673
 
    protected $fontlist = array();
674
 
 
675
 
    /**
676
 
     * Current foreground color.
677
 
     * @protected
678
 
     */
679
 
    protected $fgcolor;
680
 
 
681
 
    /**
682
 
     * HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
683
 
     * @protected
684
 
     */
685
 
    protected $listordered = array();
686
 
 
687
 
    /**
688
 
     * HTML PARSER: array count list items on nested lists.
689
 
     * @protected
690
 
     */
691
 
    protected $listcount = array();
692
 
 
693
 
    /**
694
 
     * HTML PARSER: current list nesting level.
695
 
     * @protected
696
 
     */
697
 
    protected $listnum = 0;
698
 
 
699
 
    /**
700
 
     * HTML PARSER: indent amount for lists.
701
 
     * @protected
702
 
     */
703
 
    protected $listindent = 0;
704
 
 
705
 
    /**
706
 
     * HTML PARSER: current list indententation level.
707
 
     * @protected
708
 
     */
709
 
    protected $listindentlevel = 0;
710
 
 
711
 
    /**
712
 
     * Current background color.
713
 
     * @protected
714
 
     */
715
 
    protected $bgcolor;
716
 
 
717
 
    /**
718
 
     * Temporary font size in points.
719
 
     * @protected
720
 
     */
721
 
    protected $tempfontsize = 10;
722
 
 
723
 
    /**
724
 
     * Spacer string for LI tags.
725
 
     * @protected
726
 
     */
727
 
    protected $lispacer = '';
728
 
 
729
 
    /**
730
 
     * Default encoding.
731
 
     * @protected
732
 
     * @since 1.53.0.TC010
733
 
     */
734
 
    protected $encoding = 'UTF-8';
735
 
 
736
 
    /**
737
 
     * PHP internal encoding.
738
 
     * @protected
739
 
     * @since 1.53.0.TC016
740
 
     */
741
 
    protected $internal_encoding;
742
 
 
743
 
    /**
744
 
     * Boolean flag to indicate if the document language is Right-To-Left.
745
 
     * @protected
746
 
     * @since 2.0.000
747
 
     */
748
 
    protected $rtl = false;
749
 
 
750
 
    /**
751
 
     * Boolean flag used to force RTL or LTR string direction.
752
 
     * @protected
753
 
     * @since 2.0.000
754
 
     */
755
 
    protected $tmprtl = false;
756
 
 
757
 
    // --- Variables used for document encryption:
758
 
 
759
 
    /**
760
 
     * IBoolean flag indicating whether document is protected.
761
 
     * @protected
762
 
     * @since 2.0.000 (2008-01-02)
763
 
     */
764
 
    protected $encrypted;
765
 
 
766
 
    /**
767
 
     * Array containing encryption settings.
768
 
     * @protected
769
 
     * @since 5.0.005 (2010-05-11)
770
 
     */
771
 
    protected $encryptdata = array();
772
 
 
773
 
    /**
774
 
     * Last RC4 key encrypted (cached for optimisation).
775
 
     * @protected
776
 
     * @since 2.0.000 (2008-01-02)
777
 
     */
778
 
    protected $last_enc_key;
779
 
 
780
 
    /**
781
 
     * Last RC4 computed key.
782
 
     * @protected
783
 
     * @since 2.0.000 (2008-01-02)
784
 
     */
785
 
    protected $last_enc_key_c;
786
 
 
787
 
    /**
788
 
     * Encryption padding string.
789
 
     * @protected
790
 
     */
791
 
    protected $enc_padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
792
 
 
793
 
    /**
794
 
     * File ID (used on document trailer).
795
 
     * @protected
796
 
     * @since 5.0.005 (2010-05-12)
797
 
     */
798
 
    protected $file_id;
799
 
 
800
 
    // --- bookmark ---
801
 
 
802
 
    /**
803
 
     * Outlines for bookmark.
804
 
     * @protected
805
 
     * @since 2.1.002 (2008-02-12)
806
 
     */
807
 
    protected $outlines = array();
808
 
 
809
 
    /**
810
 
     * Outline root for bookmark.
811
 
     * @protected
812
 
     * @since 2.1.002 (2008-02-12)
813
 
     */
814
 
    protected $OutlineRoot;
815
 
 
816
 
    // --- javascript and form ---
817
 
 
818
 
    /**
819
 
     * Javascript code.
820
 
     * @protected
821
 
     * @since 2.1.002 (2008-02-12)
822
 
     */
823
 
    protected $javascript = '';
824
 
 
825
 
    /**
826
 
     * Javascript counter.
827
 
     * @protected
828
 
     * @since 2.1.002 (2008-02-12)
829
 
     */
830
 
    protected $n_js;
831
 
 
832
 
    /**
833
 
     * line trough state
834
 
     * @protected
835
 
     * @since 2.8.000 (2008-03-19)
836
 
     */
837
 
    protected $linethrough;
838
 
 
839
 
    /**
840
 
     * Array with additional document-wide usage rights for the document.
841
 
     * @protected
842
 
     * @since 5.8.014 (2010-08-23)
843
 
     */
844
 
    protected $ur = array();
845
 
 
846
 
    /**
847
 
     * DPI (Dot Per Inch) Document Resolution (do not change).
848
 
     * @protected
849
 
     * @since 3.0.000 (2008-03-27)
850
 
     */
851
 
    protected $dpi = 72;
852
 
 
853
 
    /**
854
 
     * Array of page numbers were a new page group was started.
855
 
     * @protected
856
 
     * @since 3.0.000 (2008-03-27)
857
 
     */
858
 
    protected $newpagegroup = array();
859
 
 
860
 
    /**
861
 
     * Contains the number of pages of the groups.
862
 
     * @protected
863
 
     * @since 3.0.000 (2008-03-27)
864
 
     */
865
 
    protected $pagegroups;
866
 
 
867
 
    /**
868
 
     * Contains the alias of the current page group.
869
 
     * @protected
870
 
     * @since 3.0.000 (2008-03-27)
871
 
     */
872
 
    protected $currpagegroup;
873
 
 
874
 
    /**
875
 
     * Restrict the rendering of some elements to screen or printout.
876
 
     * @protected
877
 
     * @since 3.0.000 (2008-03-27)
878
 
     */
879
 
    protected $visibility = 'all';
880
 
 
881
 
    /**
882
 
     * Print visibility.
883
 
     * @protected
884
 
     * @since 3.0.000 (2008-03-27)
885
 
     */
886
 
    protected $n_ocg_print;
887
 
 
888
 
    /**
889
 
     * View visibility.
890
 
     * @protected
891
 
     * @since 3.0.000 (2008-03-27)
892
 
     */
893
 
    protected $n_ocg_view;
894
 
 
895
 
    /**
896
 
     * Array of transparency objects and parameters.
897
 
     * @protected
898
 
     * @since 3.0.000 (2008-03-27)
899
 
     */
900
 
    protected $extgstates;
901
 
 
902
 
    /**
903
 
     * Set the default JPEG compression quality (1-100).
904
 
     * @protected
905
 
     * @since 3.0.000 (2008-03-27)
906
 
     */
907
 
    protected $jpeg_quality;
908
 
 
909
 
    /**
910
 
     * Default cell height ratio.
911
 
     * @protected
912
 
     * @since 3.0.014 (2008-05-23)
913
 
     */
914
 
    protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
915
 
 
916
 
    /**
917
 
     * PDF viewer preferences.
918
 
     * @protected
919
 
     * @since 3.1.000 (2008-06-09)
920
 
     */
921
 
    protected $viewer_preferences;
922
 
 
923
 
    /**
924
 
     * A name object specifying how the document should be displayed when opened.
925
 
     * @protected
926
 
     * @since 3.1.000 (2008-06-09)
927
 
     */
928
 
    protected $PageMode;
929
 
 
930
 
    /**
931
 
     * Array for storing gradient information.
932
 
     * @protected
933
 
     * @since 3.1.000 (2008-06-09)
934
 
     */
935
 
    protected $gradients = array();
936
 
 
937
 
    /**
938
 
     * Array used to store positions inside the pages buffer (keys are the page numbers).
939
 
     * @protected
940
 
     * @since 3.2.000 (2008-06-26)
941
 
     */
942
 
    protected $intmrk = array();
943
 
 
944
 
    /**
945
 
     * Array used to store positions inside the pages buffer (keys are the page numbers).
946
 
     * @protected
947
 
     * @since 5.7.000 (2010-08-03)
948
 
     */
949
 
    protected $bordermrk = array();
950
 
 
951
 
    /**
952
 
     * Array used to store page positions to track empty pages (keys are the page numbers).
953
 
     * @protected
954
 
     * @since 5.8.007 (2010-08-18)
955
 
     */
956
 
    protected $emptypagemrk = array();
957
 
 
958
 
    /**
959
 
     * Array used to store content positions inside the pages buffer (keys are the page numbers).
960
 
     * @protected
961
 
     * @since 4.6.021 (2009-07-20)
962
 
     */
963
 
    protected $cntmrk = array();
964
 
 
965
 
    /**
966
 
     * Array used to store footer positions of each page.
967
 
     * @protected
968
 
     * @since 3.2.000 (2008-07-01)
969
 
     */
970
 
    protected $footerpos = array();
971
 
 
972
 
    /**
973
 
     * Array used to store footer length of each page.
974
 
     * @protected
975
 
     * @since 4.0.014 (2008-07-29)
976
 
     */
977
 
    protected $footerlen = array();
978
 
 
979
 
    /**
980
 
     * Boolean flag to indicate if a new line is created.
981
 
     * @protected
982
 
     * @since 3.2.000 (2008-07-01)
983
 
     */
984
 
    protected $newline = true;
985
 
 
986
 
    /**
987
 
     * End position of the latest inserted line.
988
 
     * @protected
989
 
     * @since 3.2.000 (2008-07-01)
990
 
     */
991
 
    protected $endlinex = 0;
992
 
 
993
 
    /**
994
 
     * PDF string for width value of the last line.
995
 
     * @protected
996
 
     * @since 4.0.006 (2008-07-16)
997
 
     */
998
 
    protected $linestyleWidth = '';
999
 
 
1000
 
    /**
1001
 
     * PDF string for CAP value of the last line.
1002
 
     * @protected
1003
 
     * @since 4.0.006 (2008-07-16)
1004
 
     */
1005
 
    protected $linestyleCap = '0 J';
1006
 
 
1007
 
    /**
1008
 
     * PDF string for join value of the last line.
1009
 
     * @protected
1010
 
     * @since 4.0.006 (2008-07-16)
1011
 
     */
1012
 
    protected $linestyleJoin = '0 j';
1013
 
 
1014
 
    /**
1015
 
     * PDF string for dash value of the last line.
1016
 
     * @protected
1017
 
     * @since 4.0.006 (2008-07-16)
1018
 
     */
1019
 
    protected $linestyleDash = '[] 0 d';
1020
 
 
1021
 
    /**
1022
 
     * Boolean flag to indicate if marked-content sequence is open.
1023
 
     * @protected
1024
 
     * @since 4.0.013 (2008-07-28)
1025
 
     */
1026
 
    protected $openMarkedContent = false;
1027
 
 
1028
 
    /**
1029
 
     * Count the latest inserted vertical spaces on HTML.
1030
 
     * @protected
1031
 
     * @since 4.0.021 (2008-08-24)
1032
 
     */
1033
 
    protected $htmlvspace = 0;
1034
 
 
1035
 
    /**
1036
 
     * Array of Spot colors.
1037
 
     * @protected
1038
 
     * @since 4.0.024 (2008-09-12)
1039
 
     */
1040
 
    protected $spot_colors = array();
1041
 
 
1042
 
    /**
1043
 
     * Symbol used for HTML unordered list items.
1044
 
     * @protected
1045
 
     * @since 4.0.028 (2008-09-26)
1046
 
     */
1047
 
    protected $lisymbol = '';
1048
 
 
1049
 
    /**
1050
 
     * String used to mark the beginning and end of EPS image blocks.
1051
 
     * @protected
1052
 
     * @since 4.1.000 (2008-10-18)
1053
 
     */
1054
 
    protected $epsmarker = 'x#!#EPS#!#x';
1055
 
 
1056
 
    /**
1057
 
     * Array of transformation matrix.
1058
 
     * @protected
1059
 
     * @since 4.2.000 (2008-10-29)
1060
 
     */
1061
 
    protected $transfmatrix = array();
1062
 
 
1063
 
    /**
1064
 
     * Current key for transformation matrix.
1065
 
     * @protected
1066
 
     * @since 4.8.005 (2009-09-17)
1067
 
     */
1068
 
    protected $transfmatrix_key = 0;
1069
 
 
1070
 
    /**
1071
 
     * Booklet mode for double-sided pages.
1072
 
     * @protected
1073
 
     * @since 4.2.000 (2008-10-29)
1074
 
     */
1075
 
    protected $booklet = false;
1076
 
 
1077
 
    /**
1078
 
     * Epsilon value used for float calculations.
1079
 
     * @protected
1080
 
     * @since 4.2.000 (2008-10-29)
1081
 
     */
1082
 
    protected $feps = 0.005;
1083
 
 
1084
 
    /**
1085
 
     * Array used for custom vertical spaces for HTML tags.
1086
 
     * @protected
1087
 
     * @since 4.2.001 (2008-10-30)
1088
 
     */
1089
 
    protected $tagvspaces = array();
1090
 
 
1091
 
    /**
1092
 
     * HTML PARSER: custom indent amount for lists. Negative value means disabled.
1093
 
     * @protected
1094
 
     * @since 4.2.007 (2008-11-12)
1095
 
     */
1096
 
    protected $customlistindent = -1;
1097
 
 
1098
 
    /**
1099
 
     * Boolean flag to indicate if the border of the cell sides that cross the page should be removed.
1100
 
     * @protected
1101
 
     * @since 4.2.010 (2008-11-14)
1102
 
     */
1103
 
    protected $opencell = true;
1104
 
 
1105
 
    /**
1106
 
     * Array of files to embedd.
1107
 
     * @protected
1108
 
     * @since 4.4.000 (2008-12-07)
1109
 
     */
1110
 
    protected $embeddedfiles = array();
1111
 
 
1112
 
    /**
1113
 
     * Boolean flag to indicate if we are inside a PRE tag.
1114
 
     * @protected
1115
 
     * @since 4.4.001 (2008-12-08)
1116
 
     */
1117
 
    protected $premode = false;
1118
 
 
1119
 
    /**
1120
 
     * Array used to store positions of graphics transformation blocks inside the page buffer.
1121
 
     * keys are the page numbers
1122
 
     * @protected
1123
 
     * @since 4.4.002 (2008-12-09)
1124
 
     */
1125
 
    protected $transfmrk = array();
1126
 
 
1127
 
    /**
1128
 
     * Default color for html links.
1129
 
     * @protected
1130
 
     * @since 4.4.003 (2008-12-09)
1131
 
     */
1132
 
    protected $htmlLinkColorArray = array(0, 0, 255);
1133
 
 
1134
 
    /**
1135
 
     * Default font style to add to html links.
1136
 
     * @protected
1137
 
     * @since 4.4.003 (2008-12-09)
1138
 
     */
1139
 
    protected $htmlLinkFontStyle = 'U';
1140
 
 
1141
 
    /**
1142
 
     * Counts the number of pages.
1143
 
     * @protected
1144
 
     * @since 4.5.000 (2008-12-31)
1145
 
     */
1146
 
    protected $numpages = 0;
1147
 
 
1148
 
    /**
1149
 
     * Array containing page lengths in bytes.
1150
 
     * @protected
1151
 
     * @since 4.5.000 (2008-12-31)
1152
 
     */
1153
 
    protected $pagelen = array();
1154
 
 
1155
 
    /**
1156
 
     * Counts the number of pages.
1157
 
     * @protected
1158
 
     * @since 4.5.000 (2008-12-31)
1159
 
     */
1160
 
    protected $numimages = 0;
1161
 
 
1162
 
    /**
1163
 
     * Store the image keys.
1164
 
     * @protected
1165
 
     * @since 4.5.000 (2008-12-31)
1166
 
     */
1167
 
    protected $imagekeys = array();
1168
 
 
1169
 
    /**
1170
 
     * Length of the buffer in bytes.
1171
 
     * @protected
1172
 
     * @since 4.5.000 (2008-12-31)
1173
 
     */
1174
 
    protected $bufferlen = 0;
1175
 
 
1176
 
    /**
1177
 
     * If true enables disk caching.
1178
 
     * @protected
1179
 
     * @since 4.5.000 (2008-12-31)
1180
 
     */
1181
 
    protected $diskcache = false;
1182
 
 
1183
 
    /**
1184
 
     * Counts the number of fonts.
1185
 
     * @protected
1186
 
     * @since 4.5.000 (2009-01-02)
1187
 
     */
1188
 
    protected $numfonts = 0;
1189
 
 
1190
 
    /**
1191
 
     * Store the font keys.
1192
 
     * @protected
1193
 
     * @since 4.5.000 (2009-01-02)
1194
 
     */
1195
 
    protected $fontkeys = array();
1196
 
 
1197
 
    /**
1198
 
     * Store the font object IDs.
1199
 
     * @protected
1200
 
     * @since 4.8.001 (2009-09-09)
1201
 
     */
1202
 
    protected $font_obj_ids = array();
1203
 
 
1204
 
    /**
1205
 
     * Store the fage status (true when opened, false when closed).
1206
 
     * @protected
1207
 
     * @since 4.5.000 (2009-01-02)
1208
 
     */
1209
 
    protected $pageopen = array();
1210
 
 
1211
 
    /**
1212
 
     * Default monospace font.
1213
 
     * @protected
1214
 
     * @since 4.5.025 (2009-03-10)
1215
 
     */
1216
 
    protected $default_monospaced_font = 'courier';
1217
 
 
1218
 
    /**
1219
 
     * Cloned copy of the current class object.
1220
 
     * @protected
1221
 
     * @since 4.5.029 (2009-03-19)
1222
 
     */
1223
 
    protected $objcopy;
1224
 
 
1225
 
    /**
1226
 
     * Array used to store the lengths of cache files.
1227
 
     * @protected
1228
 
     * @since 4.5.029 (2009-03-19)
1229
 
     */
1230
 
    protected $cache_file_length = array();
1231
 
 
1232
 
    /**
1233
 
     * Table header content to be repeated on each new page.
1234
 
     * @protected
1235
 
     * @since 4.5.030 (2009-03-20)
1236
 
     */
1237
 
    protected $thead = '';
1238
 
 
1239
 
    /**
1240
 
     * Margins used for table header.
1241
 
     * @protected
1242
 
     * @since 4.5.030 (2009-03-20)
1243
 
     */
1244
 
    protected $theadMargins = array();
1245
 
 
1246
 
    /**
1247
 
     * Cache array for UTF8StringToArray() method.
1248
 
     * @protected
1249
 
     * @since 4.5.037 (2009-04-07)
1250
 
     */
1251
 
    protected $cache_UTF8StringToArray = array();
1252
 
 
1253
 
    /**
1254
 
     * Maximum size of cache array used for UTF8StringToArray() method.
1255
 
     * @protected
1256
 
     * @since 4.5.037 (2009-04-07)
1257
 
     */
1258
 
    protected $cache_maxsize_UTF8StringToArray = 8;
1259
 
 
1260
 
    /**
1261
 
     * Current size of cache array used for UTF8StringToArray() method.
1262
 
     * @protected
1263
 
     * @since 4.5.037 (2009-04-07)
1264
 
     */
1265
 
    protected $cache_size_UTF8StringToArray = 0;
1266
 
 
1267
 
    /**
1268
 
     * Boolean flag to enable document digital signature.
1269
 
     * @protected
1270
 
     * @since 4.6.005 (2009-04-24)
1271
 
     */
1272
 
    protected $sign = false;
1273
 
 
1274
 
    /**
1275
 
     * Digital signature data.
1276
 
     * @protected
1277
 
     * @since 4.6.005 (2009-04-24)
1278
 
     */
1279
 
    protected $signature_data = array();
1280
 
 
1281
 
    /**
1282
 
     * Digital signature max length.
1283
 
     * @protected
1284
 
     * @since 4.6.005 (2009-04-24)
1285
 
     */
1286
 
    protected $signature_max_length = 11742;
1287
 
 
1288
 
    /**
1289
 
     * Data for digital signature appearance.
1290
 
     * @protected
1291
 
     * @since 5.3.011 (2010-06-16)
1292
 
     */
1293
 
    protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
1294
 
 
1295
 
    /**
1296
 
     * Regular expression used to find blank characters (required for word-wrapping).
1297
 
     * @protected
1298
 
     * @since 4.6.006 (2009-04-28)
1299
 
     */
1300
 
    protected $re_spaces = '/[^\S\xa0]/';
1301
 
 
1302
 
    /**
1303
 
     * Array of $re_spaces parts.
1304
 
     * @protected
1305
 
     * @since 5.5.011 (2010-07-09)
1306
 
     */
1307
 
    protected $re_space = array('p' => '[^\S\xa0]', 'm' => '');
1308
 
 
1309
 
    /**
1310
 
     * Digital signature object ID.
1311
 
     * @protected
1312
 
     * @since 4.6.022 (2009-06-23)
1313
 
     */
1314
 
    protected $sig_obj_id = 0;
1315
 
 
1316
 
    /**
1317
 
     * ByteRange placemark used during digital signature process.
1318
 
     * @protected
1319
 
     * @since 4.6.028 (2009-08-25)
1320
 
     */
1321
 
    protected $byterange_string = '/ByteRange[0 ********** ********** **********]';
1322
 
 
1323
 
    /**
1324
 
     * Placemark used during digital signature process.
1325
 
     * @protected
1326
 
     * @since 4.6.028 (2009-08-25)
1327
 
     */
1328
 
    protected $sig_annot_ref = '***SIGANNREF*** 0 R';
1329
 
 
1330
 
    /**
1331
 
     * ID of page objects.
1332
 
     * @protected
1333
 
     * @since 4.7.000 (2009-08-29)
1334
 
     */
1335
 
    protected $page_obj_id = array();
1336
 
 
1337
 
    /**
1338
 
     * List of form annotations IDs.
1339
 
     * @protected
1340
 
     * @since 4.8.000 (2009-09-07)
1341
 
     */
1342
 
    protected $form_obj_id = array();
1343
 
 
1344
 
    /**
1345
 
     * 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.
1346
 
     * @protected
1347
 
     * @since 4.8.000 (2009-09-07)
1348
 
     */
1349
 
    protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1350
 
 
1351
 
    /**
1352
 
     * Javascript objects array.
1353
 
     * @protected
1354
 
     * @since 4.8.000 (2009-09-07)
1355
 
     */
1356
 
    protected $js_objects = array();
1357
 
 
1358
 
    /**
1359
 
     * Current form action (used during XHTML rendering).
1360
 
     * @protected
1361
 
     * @since 4.8.000 (2009-09-07)
1362
 
     */
1363
 
    protected $form_action = '';
1364
 
 
1365
 
    /**
1366
 
     * Current form encryption type (used during XHTML rendering).
1367
 
     * @protected
1368
 
     * @since 4.8.000 (2009-09-07)
1369
 
     */
1370
 
    protected $form_enctype = 'application/x-www-form-urlencoded';
1371
 
 
1372
 
    /**
1373
 
     * Current method to submit forms.
1374
 
     * @protected
1375
 
     * @since 4.8.000 (2009-09-07)
1376
 
     */
1377
 
    protected $form_mode = 'post';
1378
 
 
1379
 
    /**
1380
 
     * List of fonts used on form fields (fontname => fontkey).
1381
 
     * @protected
1382
 
     * @since 4.8.001 (2009-09-09)
1383
 
     */
1384
 
    protected $annotation_fonts = array();
1385
 
 
1386
 
    /**
1387
 
     * List of radio buttons parent objects.
1388
 
     * @protected
1389
 
     * @since 4.8.001 (2009-09-09)
1390
 
     */
1391
 
    protected $radiobutton_groups = array();
1392
 
 
1393
 
    /**
1394
 
     * List of radio group objects IDs.
1395
 
     * @protected
1396
 
     * @since 4.8.001 (2009-09-09)
1397
 
     */
1398
 
    protected $radio_groups = array();
1399
 
 
1400
 
    /**
1401
 
     * Text indentation value (used for text-indent CSS attribute).
1402
 
     * @protected
1403
 
     * @since 4.8.006 (2009-09-23)
1404
 
     */
1405
 
    protected $textindent = 0;
1406
 
 
1407
 
    /**
1408
 
     * Store page number when startTransaction() is called.
1409
 
     * @protected
1410
 
     * @since 4.8.006 (2009-09-23)
1411
 
     */
1412
 
    protected $start_transaction_page = 0;
1413
 
 
1414
 
    /**
1415
 
     * Store Y position when startTransaction() is called.
1416
 
     * @protected
1417
 
     * @since 4.9.001 (2010-03-28)
1418
 
     */
1419
 
    protected $start_transaction_y = 0;
1420
 
 
1421
 
    /**
1422
 
     * True when we are printing the thead section on a new page.
1423
 
     * @protected
1424
 
     * @since 4.8.027 (2010-01-25)
1425
 
     */
1426
 
    protected $inthead = false;
1427
 
 
1428
 
    /**
1429
 
     * Array of column measures (width, space, starting Y position).
1430
 
     * @protected
1431
 
     * @since 4.9.001 (2010-03-28)
1432
 
     */
1433
 
    protected $columns = array();
1434
 
 
1435
 
    /**
1436
 
     * Number of colums.
1437
 
     * @protected
1438
 
     * @since 4.9.001 (2010-03-28)
1439
 
     */
1440
 
    protected $num_columns = 1;
1441
 
 
1442
 
    /**
1443
 
     * Current column number.
1444
 
     * @protected
1445
 
     * @since 4.9.001 (2010-03-28)
1446
 
     */
1447
 
    protected $current_column = 0;
1448
 
 
1449
 
    /**
1450
 
     * Starting page for columns.
1451
 
     * @protected
1452
 
     * @since 4.9.001 (2010-03-28)
1453
 
     */
1454
 
    protected $column_start_page = 0;
1455
 
 
1456
 
    /**
1457
 
     * Maximum page and column selected.
1458
 
     * @protected
1459
 
     * @since 5.8.000 (2010-08-11)
1460
 
     */
1461
 
    protected $maxselcol = array('page' => 0, 'column' => 0);
1462
 
 
1463
 
    /**
1464
 
     * Array of: X difference between table cell x start and starting page margin, cellspacing, cellpadding.
1465
 
     * @protected
1466
 
     * @since 5.8.000 (2010-08-11)
1467
 
     */
1468
 
    protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
1469
 
 
1470
 
    /**
1471
 
     * 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.
1472
 
     * @protected
1473
 
     * @since 4.9.008 (2010-04-03)
1474
 
     */
1475
 
    protected $textrendermode = 0;
1476
 
 
1477
 
    /**
1478
 
     * Text stroke width in doc units.
1479
 
     * @protected
1480
 
     * @since 4.9.008 (2010-04-03)
1481
 
     */
1482
 
    protected $textstrokewidth = 0;
1483
 
 
1484
 
    /**
1485
 
     * Current stroke color.
1486
 
     * @protected
1487
 
     * @since 4.9.008 (2010-04-03)
1488
 
     */
1489
 
    protected $strokecolor;
1490
 
 
1491
 
    /**
1492
 
     * Default unit of measure for document.
1493
 
     * @protected
1494
 
     * @since 5.0.000 (2010-04-22)
1495
 
     */
1496
 
    protected $pdfunit = 'mm';
1497
 
 
1498
 
    /**
1499
 
     * Boolean flag true when we are on TOC (Table Of Content) page.
1500
 
     * @protected
1501
 
     */
1502
 
    protected $tocpage = false;
1503
 
 
1504
 
    /**
1505
 
     * Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick library.
1506
 
     * @protected
1507
 
     * @since 5.0.000 (2010-04-26)
1508
 
     */
1509
 
    protected $rasterize_vector_images = false;
1510
 
 
1511
 
    /**
1512
 
     * Boolean flag: if true enables font subsetting by default.
1513
 
     * @protected
1514
 
     * @since 5.3.002 (2010-06-07)
1515
 
     */
1516
 
    protected $font_subsetting = true;
1517
 
 
1518
 
    /**
1519
 
     * Array of default graphic settings.
1520
 
     * @protected
1521
 
     * @since 5.5.008 (2010-07-02)
1522
 
     */
1523
 
    protected $default_graphic_vars = array();
1524
 
 
1525
 
    /**
1526
 
     * Array of XObjects.
1527
 
     * @protected
1528
 
     * @since 5.8.014 (2010-08-23)
1529
 
     */
1530
 
    protected $xobjects = array();
1531
 
 
1532
 
    /**
1533
 
     * Boolean value true when we are inside an XObject.
1534
 
     * @protected
1535
 
     * @since 5.8.017 (2010-08-24)
1536
 
     */
1537
 
    protected $inxobj = false;
1538
 
 
1539
 
    /**
1540
 
     * Current XObject ID.
1541
 
     * @protected
1542
 
     * @since 5.8.017 (2010-08-24)
1543
 
     */
1544
 
    protected $xobjid = '';
1545
 
 
1546
 
    /**
1547
 
     * Percentage of character stretching.
1548
 
     * @protected
1549
 
     * @since 5.9.000 (2010-09-29)
1550
 
     */
1551
 
    protected $font_stretching = 100;
1552
 
 
1553
 
    /**
1554
 
     * Increases or decreases the space between characters in a text by the specified amount (tracking/kerning).
1555
 
     * @protected
1556
 
     * @since 5.9.000 (2010-09-29)
1557
 
     */
1558
 
    protected $font_spacing = 0;
1559
 
 
1560
 
    /**
1561
 
     * Array of no-write regions.
1562
 
     * ('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)
1563
 
     * @protected
1564
 
     * @since 5.9.003 (2010-10-14)
1565
 
     */
1566
 
    protected $page_regions = array();
1567
 
 
1568
 
    /**
1569
 
     * Array containing HTML color names and values.
1570
 
     * @protected
1571
 
     * @since 5.9.004 (2010-10-18)
1572
 
     */
1573
 
    protected $webcolor = array();
1574
 
 
1575
 
    /**
1576
 
     * Array containing spot color names and values.
1577
 
     * @protected
1578
 
     * @since 5.9.012 (2010-11-11)
1579
 
     */
1580
 
    protected $spotcolor = array();
1581
 
 
1582
 
    /**
1583
 
     * Directory used for the last SVG image.
1584
 
     * @protected
1585
 
     * @since 5.0.000 (2010-05-05)
1586
 
     */
1587
 
    protected $svgdir = '';
1588
 
 
1589
 
    /**
1590
 
     *  Deafult unit of measure for SVG.
1591
 
     * @protected
1592
 
     * @since 5.0.000 (2010-05-02)
1593
 
     */
1594
 
    protected $svgunit = 'px';
1595
 
 
1596
 
    /**
1597
 
     * Array of SVG gradients.
1598
 
     * @protected
1599
 
     * @since 5.0.000 (2010-05-02)
1600
 
     */
1601
 
    protected $svggradients = array();
1602
 
 
1603
 
    /**
1604
 
     * ID of last SVG gradient.
1605
 
     * @protected
1606
 
     * @since 5.0.000 (2010-05-02)
1607
 
     */
1608
 
    protected $svggradientid = 0;
1609
 
 
1610
 
    /**
1611
 
     * Boolean value true when in SVG defs group.
1612
 
     * @protected
1613
 
     * @since 5.0.000 (2010-05-02)
1614
 
     */
1615
 
    protected $svgdefsmode = false;
1616
 
 
1617
 
    /**
1618
 
     * Array of SVG defs.
1619
 
     * @protected
1620
 
     * @since 5.0.000 (2010-05-02)
1621
 
     */
1622
 
    protected $svgdefs = array();
1623
 
 
1624
 
    /**
1625
 
     * Boolean value true when in SVG clipPath tag.
1626
 
     * @protected
1627
 
     * @since 5.0.000 (2010-04-26)
1628
 
     */
1629
 
    protected $svgclipmode = false;
1630
 
 
1631
 
    /**
1632
 
     * Array of SVG clipPath commands.
1633
 
     * @protected
1634
 
     * @since 5.0.000 (2010-05-02)
1635
 
     */
1636
 
    protected $svgclippaths = array();
1637
 
 
1638
 
    /**
1639
 
     * Array of SVG clipPath tranformation matrix.
1640
 
     * @protected
1641
 
     * @since 5.8.022 (2010-08-31)
1642
 
     */
1643
 
    protected $svgcliptm = array();
1644
 
 
1645
 
    /**
1646
 
     * ID of last SVG clipPath.
1647
 
     * @protected
1648
 
     * @since 5.0.000 (2010-05-02)
1649
 
     */
1650
 
    protected $svgclipid = 0;
1651
 
 
1652
 
    /**
1653
 
     * SVG text.
1654
 
     * @protected
1655
 
     * @since 5.0.000 (2010-05-02)
1656
 
     */
1657
 
    protected $svgtext = '';
1658
 
 
1659
 
    /**
1660
 
     * SVG text properties.
1661
 
     * @protected
1662
 
     * @since 5.8.013 (2010-08-23)
1663
 
     */
1664
 
    protected $svgtextmode = array();
1665
 
 
1666
 
    /**
1667
 
     * Array of hinheritable SVG properties.
1668
 
     * @protected
1669
 
     * @since 5.0.000 (2010-05-02)
1670
 
     */
1671
 
    protected $svginheritprop = array('clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cursor', 'direction', 'fill', 'fill-opacity', 'fill-rule', 'font', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'image-rendering', 'kerning', 'letter-spacing', 'marker', 'marker-end', 'marker-mid', 'marker-start', 'pointer-events', 'shape-rendering', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-rendering', 'visibility', 'word-spacing', 'writing-mode');
1672
 
 
1673
 
    /**
1674
 
     * Array of SVG properties.
1675
 
     * @protected
1676
 
     * @since 5.0.000 (2010-05-02)
1677
 
     */
1678
 
    protected $svgstyles = array(array(
1679
 
        'alignment-baseline' => 'auto',
1680
 
        'baseline-shift' => 'baseline',
1681
 
        'clip' => 'auto',
1682
 
        'clip-path' => 'none',
1683
 
        'clip-rule' => 'nonzero',
1684
 
        'color' => 'black',
1685
 
        'color-interpolation' => 'sRGB',
1686
 
        'color-interpolation-filters' => 'linearRGB',
1687
 
        'color-profile' => 'auto',
1688
 
        'color-rendering' => 'auto',
1689
 
        'cursor' => 'auto',
1690
 
        'direction' => 'ltr',
1691
 
        'display' => 'inline',
1692
 
        'dominant-baseline' => 'auto',
1693
 
        'enable-background' => 'accumulate',
1694
 
        'fill' => 'black',
1695
 
        'fill-opacity' => 1,
1696
 
        'fill-rule' => 'nonzero',
1697
 
        'filter' => 'none',
1698
 
        'flood-color' => 'black',
1699
 
        'flood-opacity' => 1,
1700
 
        'font' => '',
1701
 
        'font-family' => 'helvetica',
1702
 
        'font-size' => 'medium',
1703
 
        'font-size-adjust' => 'none',
1704
 
        'font-stretch' => 'normal',
1705
 
        'font-style' => 'normal',
1706
 
        'font-variant' => 'normal',
1707
 
        'font-weight' => 'normal',
1708
 
        'glyph-orientation-horizontal' => '0deg',
1709
 
        'glyph-orientation-vertical' => 'auto',
1710
 
        'image-rendering' => 'auto',
1711
 
        'kerning' => 'auto',
1712
 
        'letter-spacing' => 'normal',
1713
 
        'lighting-color' => 'white',
1714
 
        'marker' => '',
1715
 
        'marker-end' => 'none',
1716
 
        'marker-mid' => 'none',
1717
 
        'marker-start' => 'none',
1718
 
        'mask' => 'none',
1719
 
        'opacity' => 1,
1720
 
        'overflow' => 'auto',
1721
 
        'pointer-events' => 'visiblePainted',
1722
 
        'shape-rendering' => 'auto',
1723
 
        'stop-color' => 'black',
1724
 
        'stop-opacity' => 1,
1725
 
        'stroke' => 'none',
1726
 
        'stroke-dasharray' => 'none',
1727
 
        'stroke-dashoffset' => 0,
1728
 
        'stroke-linecap' => 'butt',
1729
 
        'stroke-linejoin' => 'miter',
1730
 
        'stroke-miterlimit' => 4,
1731
 
        'stroke-opacity' => 1,
1732
 
        'stroke-width' => 1,
1733
 
        'text-anchor' => 'start',
1734
 
        'text-decoration' => 'none',
1735
 
        'text-rendering' => 'auto',
1736
 
        'unicode-bidi' => 'normal',
1737
 
        'visibility' => 'visible',
1738
 
        'word-spacing' => 'normal',
1739
 
        'writing-mode' => 'lr-tb',
1740
 
        'text-color' => 'black',
1741
 
        'transfmatrix' => array(1, 0, 0, 1, 0, 0)
1742
 
        ));
1743
 
 
1744
 
    //------------------------------------------------------------
1745
 
    // METHODS
1746
 
    //------------------------------------------------------------
1747
 
 
1748
 
    /**
1749
 
     * This is the class constructor.
1750
 
     * It allows to set up the page format, the orientation and the measure unit used in all the methods (except for the font sizes).
1751
 
     * @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>
1752
 
     * @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.
1753
 
     * @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().
1754
 
     * @param $unicode (boolean) TRUE means that the input text is unicode (default = true)
1755
 
     * @param $diskcache (boolean) if TRUE reduce the RAM memory usage by caching temporary data on filesystem (slower).
1756
 
     * @param $encoding (string) charset encoding; default is UTF-8
1757
 
     * @public
1758
 
     * @see getPageSizeFromFormat(), setPageFormat()
1759
 
     */
1760
 
    public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false) {
1761
 
        /* Set internal character encoding to ASCII */
1762
 
        if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
1763
 
            $this->internal_encoding = mb_internal_encoding();
1764
 
            mb_internal_encoding('ASCII');
1765
 
        }
1766
 
        // get array of HTML colors
1767
 
        require(dirname(__FILE__).'/htmlcolors.php');
1768
 
        $this->webcolor = $webcolor;
1769
 
        // get array of custom spot colors
1770
 
        if (file_exists(dirname(__FILE__).'/spotcolors.php')) {
1771
 
            require(dirname(__FILE__).'/spotcolors.php');
1772
 
            $this->spotcolor = $spotcolor;
1773
 
        } else {
1774
 
            $this->spotcolor = array();
1775
 
        }
1776
 
        require_once(dirname(__FILE__).'/unicode_data.php');
1777
 
        $this->unicode = new TCPDF_UNICODE_DATA();
1778
 
        $this->font_obj_ids = array();
1779
 
        $this->page_obj_id = array();
1780
 
        $this->form_obj_id = array();
1781
 
        // set disk caching
1782
 
        $this->diskcache = $diskcache ? true : false;
1783
 
        // set language direction
1784
 
        $this->rtl = false;
1785
 
        $this->tmprtl = false;
1786
 
        // some checks
1787
 
        $this->_dochecks();
1788
 
        // initialization of properties
1789
 
        $this->isunicode = $unicode;
1790
 
        $this->page = 0;
1791
 
        $this->transfmrk[0] = array();
1792
 
        $this->pagedim = array();
1793
 
        $this->n = 2;
1794
 
        $this->buffer = '';
1795
 
        $this->pages = array();
1796
 
        $this->state = 0;
1797
 
        $this->fonts = array();
1798
 
        $this->FontFiles = array();
1799
 
        $this->diffs = array();
1800
 
        $this->images = array();
1801
 
        $this->links = array();
1802
 
        $this->gradients = array();
1803
 
        $this->InFooter = false;
1804
 
        $this->lasth = 0;
1805
 
        $this->FontFamily = 'helvetica';
1806
 
        $this->FontStyle = '';
1807
 
        $this->FontSizePt = 12;
1808
 
        $this->underline = false;
1809
 
        $this->overline = false;
1810
 
        $this->linethrough = false;
1811
 
        $this->DrawColor = '0 G';
1812
 
        $this->FillColor = '0 g';
1813
 
        $this->TextColor = '0 g';
1814
 
        $this->ColorFlag = false;
1815
 
        // encryption values
1816
 
        $this->encrypted = false;
1817
 
        $this->last_enc_key = '';
1818
 
        // standard Unicode fonts
1819
 
        $this->CoreFonts = array(
1820
 
            'courier'=>'Courier',
1821
 
            'courierB'=>'Courier-Bold',
1822
 
            'courierI'=>'Courier-Oblique',
1823
 
            'courierBI'=>'Courier-BoldOblique',
1824
 
            'helvetica'=>'Helvetica',
1825
 
            'helveticaB'=>'Helvetica-Bold',
1826
 
            'helveticaI'=>'Helvetica-Oblique',
1827
 
            'helveticaBI'=>'Helvetica-BoldOblique',
1828
 
            'times'=>'Times-Roman',
1829
 
            'timesB'=>'Times-Bold',
1830
 
            'timesI'=>'Times-Italic',
1831
 
            'timesBI'=>'Times-BoldItalic',
1832
 
            'symbol'=>'Symbol',
1833
 
            'zapfdingbats'=>'ZapfDingbats'
1834
 
        );
1835
 
        // set scale factor
1836
 
        $this->setPageUnit($unit);
1837
 
        // set page format and orientation
1838
 
        $this->setPageFormat($format, $orientation);
1839
 
        // page margins (1 cm)
1840
 
        $margin = 28.35 / $this->k;
1841
 
        $this->SetMargins($margin, $margin);
1842
 
        // internal cell padding
1843
 
        $cpadding = $margin / 10;
1844
 
        $this->setCellPaddings($cpadding, 0, $cpadding, 0);
1845
 
        // cell margins
1846
 
        $this->setCellMargins(0, 0, 0, 0);
1847
 
        // line width (0.2 mm)
1848
 
        $this->LineWidth = 0.57 / $this->k;
1849
 
        $this->linestyleWidth = sprintf('%.2F w', ($this->LineWidth * $this->k));
1850
 
        $this->linestyleCap = '0 J';
1851
 
        $this->linestyleJoin = '0 j';
1852
 
        $this->linestyleDash = '[] 0 d';
1853
 
        // automatic page break
1854
 
        $this->SetAutoPageBreak(true, (2 * $margin));
1855
 
        // full width display mode
1856
 
        $this->SetDisplayMode('fullwidth');
1857
 
        // compression
1858
 
        $this->SetCompression(true);
1859
 
        // set default PDF version number
1860
 
        $this->PDFVersion = '1.7';
1861
 
        $this->encoding = $encoding;
1862
 
        $this->HREF = array();
1863
 
        $this->getFontsList();
1864
 
        $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1865
 
        $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0);
1866
 
        $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1867
 
        $this->extgstates = array();
1868
 
        // user's rights
1869
 
        $this->sign = false;
1870
 
        $this->ur['enabled'] = false;
1871
 
        $this->ur['document'] = '/FullSave';
1872
 
        $this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export';
1873
 
        $this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1874
 
        $this->ur['signature'] = '/Modify';
1875
 
        $this->ur['ef'] = '/Create/Delete/Modify/Import';
1876
 
        $this->ur['formex'] = '';
1877
 
        $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
1878
 
        // set default JPEG quality
1879
 
        $this->jpeg_quality = 75;
1880
 
        // initialize some settings
1881
 
        $this->utf8Bidi(array(''), '');
1882
 
        // set default font
1883
 
        $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
1884
 
        // check if PCRE Unicode support is enabled
1885
 
        if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
1886
 
            // PCRE unicode support is turned ON
1887
 
            // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
1888
 
            // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
1889
 
            // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
1890
 
            //$this->setSpacesRE('/[^\S\P{Z}\P{Lo}\xa0]/u');
1891
 
            $this->setSpacesRE('/[^\S\P{Z}\xa0]/u');
1892
 
        } else {
1893
 
            // PCRE unicode support is turned OFF
1894
 
            $this->setSpacesRE('/[^\S\xa0]/');
1895
 
        }
1896
 
        $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1897
 
        // set file ID for trailer
1898
 
        $this->file_id = md5($this->getRandomSeed('TCPDF'.$orientation.$unit.$format.$encoding));
1899
 
        // get default graphic vars
1900
 
        $this->default_graphic_vars = $this->getGraphicVars();
1901
 
    }
1902
 
 
1903
 
    /**
1904
 
     * Default destructor.
1905
 
     * @public
1906
 
     * @since 1.53.0.TC016
1907
 
     */
1908
 
    public function __destruct() {
1909
 
        // restore internal encoding
1910
 
        if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
1911
 
            mb_internal_encoding($this->internal_encoding);
1912
 
        }
1913
 
        // unset all class variables
1914
 
        $this->_destroy(true);
1915
 
    }
1916
 
 
1917
 
    /**
1918
 
     * Return the current TCPDF version.
1919
 
     * @return TCPDF version string
1920
 
     * @public
1921
 
     * @since 5.9.012 (2010-11-10)
1922
 
     */
1923
 
    public function getTCPDFVersion() {
1924
 
        return $this->tcpdf_version;
1925
 
    }
1926
 
 
1927
 
    /**
1928
 
     * Set the units of measure for the document.
1929
 
     * @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.
1930
 
     * @public
1931
 
     * @since 3.0.015 (2008-06-06)
1932
 
     */
1933
 
    public function setPageUnit($unit) {
1934
 
        $unit = strtolower($unit);
1935
 
        //Set scale factor
1936
 
        switch ($unit) {
1937
 
            // points
1938
 
            case 'px':
1939
 
            case 'pt': {
1940
 
                $this->k = 1;
1941
 
                break;
1942
 
            }
1943
 
            // millimeters
1944
 
            case 'mm': {
1945
 
                $this->k = $this->dpi / 25.4;
1946
 
                break;
1947
 
            }
1948
 
            // centimeters
1949
 
            case 'cm': {
1950
 
                $this->k = $this->dpi / 2.54;
1951
 
                break;
1952
 
            }
1953
 
            // inches
1954
 
            case 'in': {
1955
 
                $this->k = $this->dpi;
1956
 
                break;
1957
 
            }
1958
 
            // unsupported unit
1959
 
            default : {
1960
 
                $this->Error('Incorrect unit: '.$unit);
1961
 
                break;
1962
 
            }
1963
 
        }
1964
 
        $this->pdfunit = $unit;
1965
 
        if (isset($this->CurOrientation)) {
1966
 
            $this->setPageOrientation($this->CurOrientation);
1967
 
        }
1968
 
    }
1969
 
 
1970
 
    /**
1971
 
     * Get page dimensions from format name.
1972
 
     * @param $format (mixed) The format name. It can be: <ul>
1973
 
     * <li><b>ISO 216 A Series + 2 SIS 014711 extensions</b></li>
1974
 
     * <li>A0 (841x1189 mm ; 33.11x46.81 in)</li>
1975
 
     * <li>A1 (594x841 mm ; 23.39x33.11 in)</li>
1976
 
     * <li>A2 (420x594 mm ; 16.54x23.39 in)</li>
1977
 
     * <li>A3 (297x420 mm ; 11.69x16.54 in)</li>
1978
 
     * <li>A4 (210x297 mm ; 8.27x11.69 in)</li>
1979
 
     * <li>A5 (148x210 mm ; 5.83x8.27 in)</li>
1980
 
     * <li>A6 (105x148 mm ; 4.13x5.83 in)</li>
1981
 
     * <li>A7 (74x105 mm ; 2.91x4.13 in)</li>
1982
 
     * <li>A8 (52x74 mm ; 2.05x2.91 in)</li>
1983
 
     * <li>A9 (37x52 mm ; 1.46x2.05 in)</li>
1984
 
     * <li>A10 (26x37 mm ; 1.02x1.46 in)</li>
1985
 
     * <li>A11 (18x26 mm ; 0.71x1.02 in)</li>
1986
 
     * <li>A12 (13x18 mm ; 0.51x0.71 in)</li>
1987
 
     * <li><b>ISO 216 B Series + 2 SIS 014711 extensions</b></li>
1988
 
     * <li>B0 (1000x1414 mm ; 39.37x55.67 in)</li>
1989
 
     * <li>B1 (707x1000 mm ; 27.83x39.37 in)</li>
1990
 
     * <li>B2 (500x707 mm ; 19.69x27.83 in)</li>
1991
 
     * <li>B3 (353x500 mm ; 13.90x19.69 in)</li>
1992
 
     * <li>B4 (250x353 mm ; 9.84x13.90 in)</li>
1993
 
     * <li>B5 (176x250 mm ; 6.93x9.84 in)</li>
1994
 
     * <li>B6 (125x176 mm ; 4.92x6.93 in)</li>
1995
 
     * <li>B7 (88x125 mm ; 3.46x4.92 in)</li>
1996
 
     * <li>B8 (62x88 mm ; 2.44x3.46 in)</li>
1997
 
     * <li>B9 (44x62 mm ; 1.73x2.44 in)</li>
1998
 
     * <li>B10 (31x44 mm ; 1.22x1.73 in)</li>
1999
 
     * <li>B11 (22x31 mm ; 0.87x1.22 in)</li>
2000
 
     * <li>B12 (15x22 mm ; 0.59x0.87 in)</li>
2001
 
     * <li><b>ISO 216 C Series + 2 SIS 014711 extensions + 2 EXTENSION</b></li>
2002
 
     * <li>C0 (917x1297 mm ; 36.10x51.06 in)</li>
2003
 
     * <li>C1 (648x917 mm ; 25.51x36.10 in)</li>
2004
 
     * <li>C2 (458x648 mm ; 18.03x25.51 in)</li>
2005
 
     * <li>C3 (324x458 mm ; 12.76x18.03 in)</li>
2006
 
     * <li>C4 (229x324 mm ; 9.02x12.76 in)</li>
2007
 
     * <li>C5 (162x229 mm ; 6.38x9.02 in)</li>
2008
 
     * <li>C6 (114x162 mm ; 4.49x6.38 in)</li>
2009
 
     * <li>C7 (81x114 mm ; 3.19x4.49 in)</li>
2010
 
     * <li>C8 (57x81 mm ; 2.24x3.19 in)</li>
2011
 
     * <li>C9 (40x57 mm ; 1.57x2.24 in)</li>
2012
 
     * <li>C10 (28x40 mm ; 1.10x1.57 in)</li>
2013
 
     * <li>C11 (20x28 mm ; 0.79x1.10 in)</li>
2014
 
     * <li>C12 (14x20 mm ; 0.55x0.79 in)</li>
2015
 
     * <li>C76 (81x162 mm ; 3.19x6.38 in)</li>
2016
 
     * <li>DL (110x220 mm ; 4.33x8.66 in)</li>
2017
 
     * <li><b>SIS 014711 E Series</b></li>
2018
 
     * <li>E0 (879x1241 mm ; 34.61x48.86 in)</li>
2019
 
     * <li>E1 (620x879 mm ; 24.41x34.61 in)</li>
2020
 
     * <li>E2 (440x620 mm ; 17.32x24.41 in)</li>
2021
 
     * <li>E3 (310x440 mm ; 12.20x17.32 in)</li>
2022
 
     * <li>E4 (220x310 mm ; 8.66x12.20 in)</li>
2023
 
     * <li>E5 (155x220 mm ; 6.10x8.66 in)</li>
2024
 
     * <li>E6 (110x155 mm ; 4.33x6.10 in)</li>
2025
 
     * <li>E7 (78x110 mm ; 3.07x4.33 in)</li>
2026
 
     * <li>E8 (55x78 mm ; 2.17x3.07 in)</li>
2027
 
     * <li>E9 (39x55 mm ; 1.54x2.17 in)</li>
2028
 
     * <li>E10 (27x39 mm ; 1.06x1.54 in)</li>
2029
 
     * <li>E11 (19x27 mm ; 0.75x1.06 in)</li>
2030
 
     * <li>E12 (13x19 mm ; 0.51x0.75 in)</li>
2031
 
     * <li><b>SIS 014711 G Series</b></li>
2032
 
     * <li>G0 (958x1354 mm ; 37.72x53.31 in)</li>
2033
 
     * <li>G1 (677x958 mm ; 26.65x37.72 in)</li>
2034
 
     * <li>G2 (479x677 mm ; 18.86x26.65 in)</li>
2035
 
     * <li>G3 (338x479 mm ; 13.31x18.86 in)</li>
2036
 
     * <li>G4 (239x338 mm ; 9.41x13.31 in)</li>
2037
 
     * <li>G5 (169x239 mm ; 6.65x9.41 in)</li>
2038
 
     * <li>G6 (119x169 mm ; 4.69x6.65 in)</li>
2039
 
     * <li>G7 (84x119 mm ; 3.31x4.69 in)</li>
2040
 
     * <li>G8 (59x84 mm ; 2.32x3.31 in)</li>
2041
 
     * <li>G9 (42x59 mm ; 1.65x2.32 in)</li>
2042
 
     * <li>G10 (29x42 mm ; 1.14x1.65 in)</li>
2043
 
     * <li>G11 (21x29 mm ; 0.83x1.14 in)</li>
2044
 
     * <li>G12 (14x21 mm ; 0.55x0.83 in)</li>
2045
 
     * <li><b>ISO Press</b></li>
2046
 
     * <li>RA0 (860x1220 mm ; 33.86x48.03 in)</li>
2047
 
     * <li>RA1 (610x860 mm ; 24.02x33.86 in)</li>
2048
 
     * <li>RA2 (430x610 mm ; 16.93x24.02 in)</li>
2049
 
     * <li>RA3 (305x430 mm ; 12.01x16.93 in)</li>
2050
 
     * <li>RA4 (215x305 mm ; 8.46x12.01 in)</li>
2051
 
     * <li>SRA0 (900x1280 mm ; 35.43x50.39 in)</li>
2052
 
     * <li>SRA1 (640x900 mm ; 25.20x35.43 in)</li>
2053
 
     * <li>SRA2 (450x640 mm ; 17.72x25.20 in)</li>
2054
 
     * <li>SRA3 (320x450 mm ; 12.60x17.72 in)</li>
2055
 
     * <li>SRA4 (225x320 mm ; 8.86x12.60 in)</li>
2056
 
     * <li><b>German DIN 476</b></li>
2057
 
     * <li>4A0 (1682x2378 mm ; 66.22x93.62 in)</li>
2058
 
     * <li>2A0 (1189x1682 mm ; 46.81x66.22 in)</li>
2059
 
     * <li><b>Variations on the ISO Standard</b></li>
2060
 
     * <li>A2_EXTRA (445x619 mm ; 17.52x24.37 in)</li>
2061
 
     * <li>A3+ (329x483 mm ; 12.95x19.02 in)</li>
2062
 
     * <li>A3_EXTRA (322x445 mm ; 12.68x17.52 in)</li>
2063
 
     * <li>A3_SUPER (305x508 mm ; 12.01x20.00 in)</li>
2064
 
     * <li>SUPER_A3 (305x487 mm ; 12.01x19.17 in)</li>
2065
 
     * <li>A4_EXTRA (235x322 mm ; 9.25x12.68 in)</li>
2066
 
     * <li>A4_SUPER (229x322 mm ; 9.02x12.68 in)</li>
2067
 
     * <li>SUPER_A4 (227x356 mm ; 8.94x14.02 in)</li>
2068
 
     * <li>A4_LONG (210x348 mm ; 8.27x13.70 in)</li>
2069
 
     * <li>F4 (210x330 mm ; 8.27x12.99 in)</li>
2070
 
     * <li>SO_B5_EXTRA (202x276 mm ; 7.95x10.87 in)</li>
2071
 
     * <li>A5_EXTRA (173x235 mm ; 6.81x9.25 in)</li>
2072
 
     * <li><b>ANSI Series</b></li>
2073
 
     * <li>ANSI_E (864x1118 mm ; 34.00x44.00 in)</li>
2074
 
     * <li>ANSI_D (559x864 mm ; 22.00x34.00 in)</li>
2075
 
     * <li>ANSI_C (432x559 mm ; 17.00x22.00 in)</li>
2076
 
     * <li>ANSI_B (279x432 mm ; 11.00x17.00 in)</li>
2077
 
     * <li>ANSI_A (216x279 mm ; 8.50x11.00 in)</li>
2078
 
     * <li><b>Traditional 'Loose' North American Paper Sizes</b></li>
2079
 
     * <li>LEDGER, USLEDGER (432x279 mm ; 17.00x11.00 in)</li>
2080
 
     * <li>TABLOID, USTABLOID, BIBLE, ORGANIZERK (279x432 mm ; 11.00x17.00 in)</li>
2081
 
     * <li>LETTER, USLETTER, ORGANIZERM (216x279 mm ; 8.50x11.00 in)</li>
2082
 
     * <li>LEGAL, USLEGAL (216x356 mm ; 8.50x14.00 in)</li>
2083
 
     * <li>GLETTER, GOVERNMENTLETTER (203x267 mm ; 8.00x10.50 in)</li>
2084
 
     * <li>JLEGAL, JUNIORLEGAL (203x127 mm ; 8.00x5.00 in)</li>
2085
 
     * <li><b>Other North American Paper Sizes</b></li>
2086
 
     * <li>QUADDEMY (889x1143 mm ; 35.00x45.00 in)</li>
2087
 
     * <li>SUPER_B (330x483 mm ; 13.00x19.00 in)</li>
2088
 
     * <li>QUARTO (229x279 mm ; 9.00x11.00 in)</li>
2089
 
     * <li>FOLIO, GOVERNMENTLEGAL (216x330 mm ; 8.50x13.00 in)</li>
2090
 
     * <li>EXECUTIVE, MONARCH (184x267 mm ; 7.25x10.50 in)</li>
2091
 
     * <li>MEMO, STATEMENT, ORGANIZERL (140x216 mm ; 5.50x8.50 in)</li>
2092
 
     * <li>FOOLSCAP (210x330 mm ; 8.27x13.00 in)</li>
2093
 
     * <li>COMPACT (108x171 mm ; 4.25x6.75 in)</li>
2094
 
     * <li>ORGANIZERJ (70x127 mm ; 2.75x5.00 in)</li>
2095
 
     * <li><b>Canadian standard CAN 2-9.60M</b></li>
2096
 
     * <li>P1 (560x860 mm ; 22.05x33.86 in)</li>
2097
 
     * <li>P2 (430x560 mm ; 16.93x22.05 in)</li>
2098
 
     * <li>P3 (280x430 mm ; 11.02x16.93 in)</li>
2099
 
     * <li>P4 (215x280 mm ; 8.46x11.02 in)</li>
2100
 
     * <li>P5 (140x215 mm ; 5.51x8.46 in)</li>
2101
 
     * <li>P6 (107x140 mm ; 4.21x5.51 in)</li>
2102
 
     * <li><b>North American Architectural Sizes</b></li>
2103
 
     * <li>ARCH_E (914x1219 mm ; 36.00x48.00 in)</li>
2104
 
     * <li>ARCH_E1 (762x1067 mm ; 30.00x42.00 in)</li>
2105
 
     * <li>ARCH_D (610x914 mm ; 24.00x36.00 in)</li>
2106
 
     * <li>ARCH_C, BROADSHEET (457x610 mm ; 18.00x24.00 in)</li>
2107
 
     * <li>ARCH_B (305x457 mm ; 12.00x18.00 in)</li>
2108
 
     * <li>ARCH_A (229x305 mm ; 9.00x12.00 in)</li>
2109
 
     * <li><b>Announcement Envelopes</b></li>
2110
 
     * <li>ANNENV_A2 (111x146 mm ; 4.37x5.75 in)</li>
2111
 
     * <li>ANNENV_A6 (121x165 mm ; 4.75x6.50 in)</li>
2112
 
     * <li>ANNENV_A7 (133x184 mm ; 5.25x7.25 in)</li>
2113
 
     * <li>ANNENV_A8 (140x206 mm ; 5.50x8.12 in)</li>
2114
 
     * <li>ANNENV_A10 (159x244 mm ; 6.25x9.62 in)</li>
2115
 
     * <li>ANNENV_SLIM (98x225 mm ; 3.87x8.87 in)</li>
2116
 
     * <li><b>Commercial Envelopes</b></li>
2117
 
     * <li>COMMENV_N6_1/4 (89x152 mm ; 3.50x6.00 in)</li>
2118
 
     * <li>COMMENV_N6_3/4 (92x165 mm ; 3.62x6.50 in)</li>
2119
 
     * <li>COMMENV_N8 (98x191 mm ; 3.87x7.50 in)</li>
2120
 
     * <li>COMMENV_N9 (98x225 mm ; 3.87x8.87 in)</li>
2121
 
     * <li>COMMENV_N10 (105x241 mm ; 4.12x9.50 in)</li>
2122
 
     * <li>COMMENV_N11 (114x263 mm ; 4.50x10.37 in)</li>
2123
 
     * <li>COMMENV_N12 (121x279 mm ; 4.75x11.00 in)</li>
2124
 
     * <li>COMMENV_N14 (127x292 mm ; 5.00x11.50 in)</li>
2125
 
     * <li><b>Catalogue Envelopes</b></li>
2126
 
     * <li>CATENV_N1 (152x229 mm ; 6.00x9.00 in)</li>
2127
 
     * <li>CATENV_N1_3/4 (165x241 mm ; 6.50x9.50 in)</li>
2128
 
     * <li>CATENV_N2 (165x254 mm ; 6.50x10.00 in)</li>
2129
 
     * <li>CATENV_N3 (178x254 mm ; 7.00x10.00 in)</li>
2130
 
     * <li>CATENV_N6 (191x267 mm ; 7.50x10.50 in)</li>
2131
 
     * <li>CATENV_N7 (203x279 mm ; 8.00x11.00 in)</li>
2132
 
     * <li>CATENV_N8 (210x286 mm ; 8.25x11.25 in)</li>
2133
 
     * <li>CATENV_N9_1/2 (216x267 mm ; 8.50x10.50 in)</li>
2134
 
     * <li>CATENV_N9_3/4 (222x286 mm ; 8.75x11.25 in)</li>
2135
 
     * <li>CATENV_N10_1/2 (229x305 mm ; 9.00x12.00 in)</li>
2136
 
     * <li>CATENV_N12_1/2 (241x318 mm ; 9.50x12.50 in)</li>
2137
 
     * <li>CATENV_N13_1/2 (254x330 mm ; 10.00x13.00 in)</li>
2138
 
     * <li>CATENV_N14_1/4 (286x311 mm ; 11.25x12.25 in)</li>
2139
 
     * <li>CATENV_N14_1/2 (292x368 mm ; 11.50x14.50 in)</li>
2140
 
     * <li><b>Japanese (JIS P 0138-61) Standard B-Series</b></li>
2141
 
     * <li>JIS_B0 (1030x1456 mm ; 40.55x57.32 in)</li>
2142
 
     * <li>JIS_B1 (728x1030 mm ; 28.66x40.55 in)</li>
2143
 
     * <li>JIS_B2 (515x728 mm ; 20.28x28.66 in)</li>
2144
 
     * <li>JIS_B3 (364x515 mm ; 14.33x20.28 in)</li>
2145
 
     * <li>JIS_B4 (257x364 mm ; 10.12x14.33 in)</li>
2146
 
     * <li>JIS_B5 (182x257 mm ; 7.17x10.12 in)</li>
2147
 
     * <li>JIS_B6 (128x182 mm ; 5.04x7.17 in)</li>
2148
 
     * <li>JIS_B7 (91x128 mm ; 3.58x5.04 in)</li>
2149
 
     * <li>JIS_B8 (64x91 mm ; 2.52x3.58 in)</li>
2150
 
     * <li>JIS_B9 (45x64 mm ; 1.77x2.52 in)</li>
2151
 
     * <li>JIS_B10 (32x45 mm ; 1.26x1.77 in)</li>
2152
 
     * <li>JIS_B11 (22x32 mm ; 0.87x1.26 in)</li>
2153
 
     * <li>JIS_B12 (16x22 mm ; 0.63x0.87 in)</li>
2154
 
     * <li><b>PA Series</b></li>
2155
 
     * <li>PA0 (840x1120 mm ; 33.07x44.09 in)</li>
2156
 
     * <li>PA1 (560x840 mm ; 22.05x33.07 in)</li>
2157
 
     * <li>PA2 (420x560 mm ; 16.54x22.05 in)</li>
2158
 
     * <li>PA3 (280x420 mm ; 11.02x16.54 in)</li>
2159
 
     * <li>PA4 (210x280 mm ; 8.27x11.02 in)</li>
2160
 
     * <li>PA5 (140x210 mm ; 5.51x8.27 in)</li>
2161
 
     * <li>PA6 (105x140 mm ; 4.13x5.51 in)</li>
2162
 
     * <li>PA7 (70x105 mm ; 2.76x4.13 in)</li>
2163
 
     * <li>PA8 (52x70 mm ; 2.05x2.76 in)</li>
2164
 
     * <li>PA9 (35x52 mm ; 1.38x2.05 in)</li>
2165
 
     * <li>PA10 (26x35 mm ; 1.02x1.38 in)</li>
2166
 
     * <li><b>Standard Photographic Print Sizes</b></li>
2167
 
     * <li>PASSPORT_PHOTO (35x45 mm ; 1.38x1.77 in)</li>
2168
 
     * <li>E (82x120 mm ; 3.25x4.72 in)</li>
2169
 
     * <li>3R, L (89x127 mm ; 3.50x5.00 in)</li>
2170
 
     * <li>4R, KG (102x152 mm ; 4.02x5.98 in)</li>
2171
 
     * <li>4D (120x152 mm ; 4.72x5.98 in)</li>
2172
 
     * <li>5R, 2L (127x178 mm ; 5.00x7.01 in)</li>
2173
 
     * <li>6R, 8P (152x203 mm ; 5.98x7.99 in)</li>
2174
 
     * <li>8R, 6P (203x254 mm ; 7.99x10.00 in)</li>
2175
 
     * <li>S8R, 6PW (203x305 mm ; 7.99x12.01 in)</li>
2176
 
     * <li>10R, 4P (254x305 mm ; 10.00x12.01 in)</li>
2177
 
     * <li>S10R, 4PW (254x381 mm ; 10.00x15.00 in)</li>
2178
 
     * <li>11R (279x356 mm ; 10.98x14.02 in)</li>
2179
 
     * <li>S11R (279x432 mm ; 10.98x17.01 in)</li>
2180
 
     * <li>12R (305x381 mm ; 12.01x15.00 in)</li>
2181
 
     * <li>S12R (305x456 mm ; 12.01x17.95 in)</li>
2182
 
     * <li><b>Common Newspaper Sizes</b></li>
2183
 
     * <li>NEWSPAPER_BROADSHEET (750x600 mm ; 29.53x23.62 in)</li>
2184
 
     * <li>NEWSPAPER_BERLINER (470x315 mm ; 18.50x12.40 in)</li>
2185
 
     * <li>NEWSPAPER_COMPACT, NEWSPAPER_TABLOID (430x280 mm ; 16.93x11.02 in)</li>
2186
 
     * <li><b>Business Cards</b></li>
2187
 
     * <li>CREDIT_CARD, BUSINESS_CARD, BUSINESS_CARD_ISO7810 (54x86 mm ; 2.13x3.37 in)</li>
2188
 
     * <li>BUSINESS_CARD_ISO216 (52x74 mm ; 2.05x2.91 in)</li>
2189
 
     * <li>BUSINESS_CARD_IT, BUSINESS_CARD_UK, BUSINESS_CARD_FR, BUSINESS_CARD_DE, BUSINESS_CARD_ES (55x85 mm ; 2.17x3.35 in)</li>
2190
 
     * <li>BUSINESS_CARD_US, BUSINESS_CARD_CA (51x89 mm ; 2.01x3.50 in)</li>
2191
 
     * <li>BUSINESS_CARD_JP (55x91 mm ; 2.17x3.58 in)</li>
2192
 
     * <li>BUSINESS_CARD_HK (54x90 mm ; 2.13x3.54 in)</li>
2193
 
     * <li>BUSINESS_CARD_AU, BUSINESS_CARD_DK, BUSINESS_CARD_SE (55x90 mm ; 2.17x3.54 in)</li>
2194
 
     * <li>BUSINESS_CARD_RU, BUSINESS_CARD_CZ, BUSINESS_CARD_FI, BUSINESS_CARD_HU, BUSINESS_CARD_IL (50x90 mm ; 1.97x3.54 in)</li>
2195
 
     * <li><b>Billboards</b></li>
2196
 
     * <li>4SHEET (1016x1524 mm ; 40.00x60.00 in)</li>
2197
 
     * <li>6SHEET (1200x1800 mm ; 47.24x70.87 in)</li>
2198
 
     * <li>12SHEET (3048x1524 mm ; 120.00x60.00 in)</li>
2199
 
     * <li>16SHEET (2032x3048 mm ; 80.00x120.00 in)</li>
2200
 
     * <li>32SHEET (4064x3048 mm ; 160.00x120.00 in)</li>
2201
 
     * <li>48SHEET (6096x3048 mm ; 240.00x120.00 in)</li>
2202
 
     * <li>64SHEET (8128x3048 mm ; 320.00x120.00 in)</li>
2203
 
     * <li>96SHEET (12192x3048 mm ; 480.00x120.00 in)</li>
2204
 
     * <li><b>Old Imperial English (some are still used in USA)</b></li>
2205
 
     * <li>EN_EMPEROR (1219x1829 mm ; 48.00x72.00 in)</li>
2206
 
     * <li>EN_ANTIQUARIAN (787x1346 mm ; 31.00x53.00 in)</li>
2207
 
     * <li>EN_GRAND_EAGLE (730x1067 mm ; 28.75x42.00 in)</li>
2208
 
     * <li>EN_DOUBLE_ELEPHANT (679x1016 mm ; 26.75x40.00 in)</li>
2209
 
     * <li>EN_ATLAS (660x864 mm ; 26.00x34.00 in)</li>
2210
 
     * <li>EN_COLOMBIER (597x876 mm ; 23.50x34.50 in)</li>
2211
 
     * <li>EN_ELEPHANT (584x711 mm ; 23.00x28.00 in)</li>
2212
 
     * <li>EN_DOUBLE_DEMY (572x902 mm ; 22.50x35.50 in)</li>
2213
 
     * <li>EN_IMPERIAL (559x762 mm ; 22.00x30.00 in)</li>
2214
 
     * <li>EN_PRINCESS (546x711 mm ; 21.50x28.00 in)</li>
2215
 
     * <li>EN_CARTRIDGE (533x660 mm ; 21.00x26.00 in)</li>
2216
 
     * <li>EN_DOUBLE_LARGE_POST (533x838 mm ; 21.00x33.00 in)</li>
2217
 
     * <li>EN_ROYAL (508x635 mm ; 20.00x25.00 in)</li>
2218
 
     * <li>EN_SHEET, EN_HALF_POST (495x597 mm ; 19.50x23.50 in)</li>
2219
 
     * <li>EN_SUPER_ROYAL (483x686 mm ; 19.00x27.00 in)</li>
2220
 
     * <li>EN_DOUBLE_POST (483x775 mm ; 19.00x30.50 in)</li>
2221
 
     * <li>EN_MEDIUM (445x584 mm ; 17.50x23.00 in)</li>
2222
 
     * <li>EN_DEMY (445x572 mm ; 17.50x22.50 in)</li>
2223
 
     * <li>EN_LARGE_POST (419x533 mm ; 16.50x21.00 in)</li>
2224
 
     * <li>EN_COPY_DRAUGHT (406x508 mm ; 16.00x20.00 in)</li>
2225
 
     * <li>EN_POST (394x489 mm ; 15.50x19.25 in)</li>
2226
 
     * <li>EN_CROWN (381x508 mm ; 15.00x20.00 in)</li>
2227
 
     * <li>EN_PINCHED_POST (375x470 mm ; 14.75x18.50 in)</li>
2228
 
     * <li>EN_BRIEF (343x406 mm ; 13.50x16.00 in)</li>
2229
 
     * <li>EN_FOOLSCAP (343x432 mm ; 13.50x17.00 in)</li>
2230
 
     * <li>EN_SMALL_FOOLSCAP (337x419 mm ; 13.25x16.50 in)</li>
2231
 
     * <li>EN_POTT (318x381 mm ; 12.50x15.00 in)</li>
2232
 
     * <li><b>Old Imperial Belgian</b></li>
2233
 
     * <li>BE_GRAND_AIGLE (700x1040 mm ; 27.56x40.94 in)</li>
2234
 
     * <li>BE_COLOMBIER (620x850 mm ; 24.41x33.46 in)</li>
2235
 
     * <li>BE_DOUBLE_CARRE (620x920 mm ; 24.41x36.22 in)</li>
2236
 
     * <li>BE_ELEPHANT (616x770 mm ; 24.25x30.31 in)</li>
2237
 
     * <li>BE_PETIT_AIGLE (600x840 mm ; 23.62x33.07 in)</li>
2238
 
     * <li>BE_GRAND_JESUS (550x730 mm ; 21.65x28.74 in)</li>
2239
 
     * <li>BE_JESUS (540x730 mm ; 21.26x28.74 in)</li>
2240
 
     * <li>BE_RAISIN (500x650 mm ; 19.69x25.59 in)</li>
2241
 
     * <li>BE_GRAND_MEDIAN (460x605 mm ; 18.11x23.82 in)</li>
2242
 
     * <li>BE_DOUBLE_POSTE (435x565 mm ; 17.13x22.24 in)</li>
2243
 
     * <li>BE_COQUILLE (430x560 mm ; 16.93x22.05 in)</li>
2244
 
     * <li>BE_PETIT_MEDIAN (415x530 mm ; 16.34x20.87 in)</li>
2245
 
     * <li>BE_RUCHE (360x460 mm ; 14.17x18.11 in)</li>
2246
 
     * <li>BE_PROPATRIA (345x430 mm ; 13.58x16.93 in)</li>
2247
 
     * <li>BE_LYS (317x397 mm ; 12.48x15.63 in)</li>
2248
 
     * <li>BE_POT (307x384 mm ; 12.09x15.12 in)</li>
2249
 
     * <li>BE_ROSETTE (270x347 mm ; 10.63x13.66 in)</li>
2250
 
     * <li><b>Old Imperial French</b></li>
2251
 
     * <li>FR_UNIVERS (1000x1300 mm ; 39.37x51.18 in)</li>
2252
 
     * <li>FR_DOUBLE_COLOMBIER (900x1260 mm ; 35.43x49.61 in)</li>
2253
 
     * <li>FR_GRANDE_MONDE (900x1260 mm ; 35.43x49.61 in)</li>
2254
 
     * <li>FR_DOUBLE_SOLEIL (800x1200 mm ; 31.50x47.24 in)</li>
2255
 
     * <li>FR_DOUBLE_JESUS (760x1120 mm ; 29.92x44.09 in)</li>
2256
 
     * <li>FR_GRAND_AIGLE (750x1060 mm ; 29.53x41.73 in)</li>
2257
 
     * <li>FR_PETIT_AIGLE (700x940 mm ; 27.56x37.01 in)</li>
2258
 
     * <li>FR_DOUBLE_RAISIN (650x1000 mm ; 25.59x39.37 in)</li>
2259
 
     * <li>FR_JOURNAL (650x940 mm ; 25.59x37.01 in)</li>
2260
 
     * <li>FR_COLOMBIER_AFFICHE (630x900 mm ; 24.80x35.43 in)</li>
2261
 
     * <li>FR_DOUBLE_CAVALIER (620x920 mm ; 24.41x36.22 in)</li>
2262
 
     * <li>FR_CLOCHE (600x800 mm ; 23.62x31.50 in)</li>
2263
 
     * <li>FR_SOLEIL (600x800 mm ; 23.62x31.50 in)</li>
2264
 
     * <li>FR_DOUBLE_CARRE (560x900 mm ; 22.05x35.43 in)</li>
2265
 
     * <li>FR_DOUBLE_COQUILLE (560x880 mm ; 22.05x34.65 in)</li>
2266
 
     * <li>FR_JESUS (560x760 mm ; 22.05x29.92 in)</li>
2267
 
     * <li>FR_RAISIN (500x650 mm ; 19.69x25.59 in)</li>
2268
 
     * <li>FR_CAVALIER (460x620 mm ; 18.11x24.41 in)</li>
2269
 
     * <li>FR_DOUBLE_COURONNE (460x720 mm ; 18.11x28.35 in)</li>
2270
 
     * <li>FR_CARRE (450x560 mm ; 17.72x22.05 in)</li>
2271
 
     * <li>FR_COQUILLE (440x560 mm ; 17.32x22.05 in)</li>
2272
 
     * <li>FR_DOUBLE_TELLIERE (440x680 mm ; 17.32x26.77 in)</li>
2273
 
     * <li>FR_DOUBLE_CLOCHE (400x600 mm ; 15.75x23.62 in)</li>
2274
 
     * <li>FR_DOUBLE_POT (400x620 mm ; 15.75x24.41 in)</li>
2275
 
     * <li>FR_ECU (400x520 mm ; 15.75x20.47 in)</li>
2276
 
     * <li>FR_COURONNE (360x460 mm ; 14.17x18.11 in)</li>
2277
 
     * <li>FR_TELLIERE (340x440 mm ; 13.39x17.32 in)</li>
2278
 
     * <li>FR_POT (310x400 mm ; 12.20x15.75 in)</li>
2279
 
     * </ul>
2280
 
     * @return array containing page width and height in points
2281
 
     * @public
2282
 
     * @since 5.0.010 (2010-05-17)
2283
 
     */
2284
 
    public function getPageSizeFromFormat($format) {
2285
 
        // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 25.4 mm)
2286
 
        switch (strtoupper($format)) {
2287
 
            // ISO 216 A Series + 2 SIS 014711 extensions
2288
 
            case 'A0' : {$pf = array( 2383.937, 3370.394); break;}
2289
 
            case 'A1' : {$pf = array( 1683.780, 2383.937); break;}
2290
 
            case 'A2' : {$pf = array( 1190.551, 1683.780); break;}
2291
 
            case 'A3' : {$pf = array(  841.890, 1190.551); break;}
2292
 
            case 'A4' : {$pf = array(  595.276,  841.890); break;}
2293
 
            case 'A5' : {$pf = array(  419.528,  595.276); break;}
2294
 
            case 'A6' : {$pf = array(  297.638,  419.528); break;}
2295
 
            case 'A7' : {$pf = array(  209.764,  297.638); break;}
2296
 
            case 'A8' : {$pf = array(  147.402,  209.764); break;}
2297
 
            case 'A9' : {$pf = array(  104.882,  147.402); break;}
2298
 
            case 'A10': {$pf = array(   73.701,  104.882); break;}
2299
 
            case 'A11': {$pf = array(   51.024,   73.701); break;}
2300
 
            case 'A12': {$pf = array(   36.850,   51.024); break;}
2301
 
            // ISO 216 B Series + 2 SIS 014711 extensions
2302
 
            case 'B0' : {$pf = array( 2834.646, 4008.189); break;}
2303
 
            case 'B1' : {$pf = array( 2004.094, 2834.646); break;}
2304
 
            case 'B2' : {$pf = array( 1417.323, 2004.094); break;}
2305
 
            case 'B3' : {$pf = array( 1000.630, 1417.323); break;}
2306
 
            case 'B4' : {$pf = array(  708.661, 1000.630); break;}
2307
 
            case 'B5' : {$pf = array(  498.898,  708.661); break;}
2308
 
            case 'B6' : {$pf = array(  354.331,  498.898); break;}
2309
 
            case 'B7' : {$pf = array(  249.449,  354.331); break;}
2310
 
            case 'B8' : {$pf = array(  175.748,  249.449); break;}
2311
 
            case 'B9' : {$pf = array(  124.724,  175.748); break;}
2312
 
            case 'B10': {$pf = array(   87.874,  124.724); break;}
2313
 
            case 'B11': {$pf = array(   62.362,   87.874); break;}
2314
 
            case 'B12': {$pf = array(   42.520,   62.362); break;}
2315
 
            // ISO 216 C Series + 2 SIS 014711 extensions + 2 EXTENSION
2316
 
            case 'C0' : {$pf = array( 2599.370, 3676.535); break;}
2317
 
            case 'C1' : {$pf = array( 1836.850, 2599.370); break;}
2318
 
            case 'C2' : {$pf = array( 1298.268, 1836.850); break;}
2319
 
            case 'C3' : {$pf = array(  918.425, 1298.268); break;}
2320
 
            case 'C4' : {$pf = array(  649.134,  918.425); break;}
2321
 
            case 'C5' : {$pf = array(  459.213,  649.134); break;}
2322
 
            case 'C6' : {$pf = array(  323.150,  459.213); break;}
2323
 
            case 'C7' : {$pf = array(  229.606,  323.150); break;}
2324
 
            case 'C8' : {$pf = array(  161.575,  229.606); break;}
2325
 
            case 'C9' : {$pf = array(  113.386,  161.575); break;}
2326
 
            case 'C10': {$pf = array(   79.370,  113.386); break;}
2327
 
            case 'C11': {$pf = array(   56.693,   79.370); break;}
2328
 
            case 'C12': {$pf = array(   39.685,   56.693); break;}
2329
 
            case 'C76': {$pf = array(  229.606,  459.213); break;}
2330
 
            case 'DL' : {$pf = array(  311.811,  623.622); break;}
2331
 
            // SIS 014711 E Series
2332
 
            case 'E0' : {$pf = array( 2491.654, 3517.795); break;}
2333
 
            case 'E1' : {$pf = array( 1757.480, 2491.654); break;}
2334
 
            case 'E2' : {$pf = array( 1247.244, 1757.480); break;}
2335
 
            case 'E3' : {$pf = array(  878.740, 1247.244); break;}
2336
 
            case 'E4' : {$pf = array(  623.622,  878.740); break;}
2337
 
            case 'E5' : {$pf = array(  439.370,  623.622); break;}
2338
 
            case 'E6' : {$pf = array(  311.811,  439.370); break;}
2339
 
            case 'E7' : {$pf = array(  221.102,  311.811); break;}
2340
 
            case 'E8' : {$pf = array(  155.906,  221.102); break;}
2341
 
            case 'E9' : {$pf = array(  110.551,  155.906); break;}
2342
 
            case 'E10': {$pf = array(   76.535,  110.551); break;}
2343
 
            case 'E11': {$pf = array(   53.858,   76.535); break;}
2344
 
            case 'E12': {$pf = array(   36.850,   53.858); break;}
2345
 
            // SIS 014711 G Series
2346
 
            case 'G0' : {$pf = array( 2715.591, 3838.110); break;}
2347
 
            case 'G1' : {$pf = array( 1919.055, 2715.591); break;}
2348
 
            case 'G2' : {$pf = array( 1357.795, 1919.055); break;}
2349
 
            case 'G3' : {$pf = array(  958.110, 1357.795); break;}
2350
 
            case 'G4' : {$pf = array(  677.480,  958.110); break;}
2351
 
            case 'G5' : {$pf = array(  479.055,  677.480); break;}
2352
 
            case 'G6' : {$pf = array(  337.323,  479.055); break;}
2353
 
            case 'G7' : {$pf = array(  238.110,  337.323); break;}
2354
 
            case 'G8' : {$pf = array(  167.244,  238.110); break;}
2355
 
            case 'G9' : {$pf = array(  119.055,  167.244); break;}
2356
 
            case 'G10': {$pf = array(   82.205,  119.055); break;}
2357
 
            case 'G11': {$pf = array(   59.528,   82.205); break;}
2358
 
            case 'G12': {$pf = array(   39.685,   59.528); break;}
2359
 
            // ISO Press
2360
 
            case 'RA0': {$pf = array( 2437.795, 3458.268); break;}
2361
 
            case 'RA1': {$pf = array( 1729.134, 2437.795); break;}
2362
 
            case 'RA2': {$pf = array( 1218.898, 1729.134); break;}
2363
 
            case 'RA3': {$pf = array(  864.567, 1218.898); break;}
2364
 
            case 'RA4': {$pf = array(  609.449,  864.567); break;}
2365
 
            case 'SRA0': {$pf = array( 2551.181, 3628.346); break;}
2366
 
            case 'SRA1': {$pf = array( 1814.173, 2551.181); break;}
2367
 
            case 'SRA2': {$pf = array( 1275.591, 1814.173); break;}
2368
 
            case 'SRA3': {$pf = array(  907.087, 1275.591); break;}
2369
 
            case 'SRA4': {$pf = array(  637.795,  907.087); break;}
2370
 
            // German  DIN 476
2371
 
            case '4A0': {$pf = array( 4767.874, 6740.787); break;}
2372
 
            case '2A0': {$pf = array( 3370.394, 4767.874); break;}
2373
 
            // Variations on the ISO Standard
2374
 
            case 'A2_EXTRA'   : {$pf = array( 1261.417, 1754.646); break;}
2375
 
            case 'A3+'        : {$pf = array(  932.598, 1369.134); break;}
2376
 
            case 'A3_EXTRA'   : {$pf = array(  912.756, 1261.417); break;}
2377
 
            case 'A3_SUPER'   : {$pf = array(  864.567, 1440.000); break;}
2378
 
            case 'SUPER_A3'   : {$pf = array(  864.567, 1380.472); break;}
2379
 
            case 'A4_EXTRA'   : {$pf = array(  666.142,  912.756); break;}
2380
 
            case 'A4_SUPER'   : {$pf = array(  649.134,  912.756); break;}
2381
 
            case 'SUPER_A4'   : {$pf = array(  643.465, 1009.134); break;}
2382
 
            case 'A4_LONG'    : {$pf = array(  595.276,  986.457); break;}
2383
 
            case 'F4'         : {$pf = array(  595.276,  935.433); break;}
2384
 
            case 'SO_B5_EXTRA': {$pf = array(  572.598,  782.362); break;}
2385
 
            case 'A5_EXTRA'   : {$pf = array(  490.394,  666.142); break;}
2386
 
            // ANSI Series
2387
 
            case 'ANSI_E': {$pf = array( 2448.000, 3168.000); break;}
2388
 
            case 'ANSI_D': {$pf = array( 1584.000, 2448.000); break;}
2389
 
            case 'ANSI_C': {$pf = array( 1224.000, 1584.000); break;}
2390
 
            case 'ANSI_B': {$pf = array(  792.000, 1224.000); break;}
2391
 
            case 'ANSI_A': {$pf = array(  612.000,  792.000); break;}
2392
 
            // Traditional 'Loose' North American Paper Sizes
2393
 
            case 'USLEDGER':
2394
 
            case 'LEDGER' : {$pf = array( 1224.000,  792.000); break;}
2395
 
            case 'ORGANIZERK':
2396
 
            case 'BIBLE':
2397
 
            case 'USTABLOID':
2398
 
            case 'TABLOID': {$pf = array(  792.000, 1224.000); break;}
2399
 
            case 'ORGANIZERM':
2400
 
            case 'USLETTER':
2401
 
            case 'LETTER' : {$pf = array(  612.000,  792.000); break;}
2402
 
            case 'USLEGAL':
2403
 
            case 'LEGAL'  : {$pf = array(  612.000, 1008.000); break;}
2404
 
            case 'GOVERNMENTLETTER':
2405
 
            case 'GLETTER': {$pf = array(  576.000,  756.000); break;}
2406
 
            case 'JUNIORLEGAL':
2407
 
            case 'JLEGAL' : {$pf = array(  576.000,  360.000); break;}
2408
 
            // Other North American Paper Sizes
2409
 
            case 'QUADDEMY': {$pf = array( 2520.000, 3240.000); break;}
2410
 
            case 'SUPER_B': {$pf = array(  936.000, 1368.000); break;}
2411
 
            case 'QUARTO': {$pf = array(  648.000,  792.000); break;}
2412
 
            case 'GOVERNMENTLEGAL':
2413
 
            case 'FOLIO': {$pf = array(  612.000,  936.000); break;}
2414
 
            case 'MONARCH':
2415
 
            case 'EXECUTIVE': {$pf = array(  522.000,  756.000); break;}
2416
 
            case 'ORGANIZERL':
2417
 
            case 'STATEMENT':
2418
 
            case 'MEMO': {$pf = array(  396.000,  612.000); break;}
2419
 
            case 'FOOLSCAP': {$pf = array(  595.440,  936.000); break;}
2420
 
            case 'COMPACT': {$pf = array(  306.000,  486.000); break;}
2421
 
            case 'ORGANIZERJ': {$pf = array(  198.000,  360.000); break;}
2422
 
            // Canadian standard CAN 2-9.60M
2423
 
            case 'P1': {$pf = array( 1587.402, 2437.795); break;}
2424
 
            case 'P2': {$pf = array( 1218.898, 1587.402); break;}
2425
 
            case 'P3': {$pf = array(  793.701, 1218.898); break;}
2426
 
            case 'P4': {$pf = array(  609.449,  793.701); break;}
2427
 
            case 'P5': {$pf = array(  396.850,  609.449); break;}
2428
 
            case 'P6': {$pf = array(  303.307,  396.850); break;}
2429
 
            // North American Architectural Sizes
2430
 
            case 'ARCH_E' : {$pf = array( 2592.000, 3456.000); break;}
2431
 
            case 'ARCH_E1': {$pf = array( 2160.000, 3024.000); break;}
2432
 
            case 'ARCH_D' : {$pf = array( 1728.000, 2592.000); break;}
2433
 
            case 'BROADSHEET':
2434
 
            case 'ARCH_C' : {$pf = array( 1296.000, 1728.000); break;}
2435
 
            case 'ARCH_B' : {$pf = array(  864.000, 1296.000); break;}
2436
 
            case 'ARCH_A' : {$pf = array(  648.000,  864.000); break;}
2437
 
            // --- North American Envelope Sizes ---
2438
 
            //   - Announcement Envelopes
2439
 
            case 'ANNENV_A2'  : {$pf = array(  314.640,  414.000); break;}
2440
 
            case 'ANNENV_A6'  : {$pf = array(  342.000,  468.000); break;}
2441
 
            case 'ANNENV_A7'  : {$pf = array(  378.000,  522.000); break;}
2442
 
            case 'ANNENV_A8'  : {$pf = array(  396.000,  584.640); break;}
2443
 
            case 'ANNENV_A10' : {$pf = array(  450.000,  692.640); break;}
2444
 
            case 'ANNENV_SLIM': {$pf = array(  278.640,  638.640); break;}
2445
 
            //   - Commercial Envelopes
2446
 
            case 'COMMENV_N6_1/4': {$pf = array(  252.000,  432.000); break;}
2447
 
            case 'COMMENV_N6_3/4': {$pf = array(  260.640,  468.000); break;}
2448
 
            case 'COMMENV_N8'    : {$pf = array(  278.640,  540.000); break;}
2449
 
            case 'COMMENV_N9'    : {$pf = array(  278.640,  638.640); break;}
2450
 
            case 'COMMENV_N10'   : {$pf = array(  296.640,  684.000); break;}
2451
 
            case 'COMMENV_N11'   : {$pf = array(  324.000,  746.640); break;}
2452
 
            case 'COMMENV_N12'   : {$pf = array(  342.000,  792.000); break;}
2453
 
            case 'COMMENV_N14'   : {$pf = array(  360.000,  828.000); break;}
2454
 
            //   - Catalogue Envelopes
2455
 
            case 'CATENV_N1'     : {$pf = array(  432.000,  648.000); break;}
2456
 
            case 'CATENV_N1_3/4' : {$pf = array(  468.000,  684.000); break;}
2457
 
            case 'CATENV_N2'     : {$pf = array(  468.000,  720.000); break;}
2458
 
            case 'CATENV_N3'     : {$pf = array(  504.000,  720.000); break;}
2459
 
            case 'CATENV_N6'     : {$pf = array(  540.000,  756.000); break;}
2460
 
            case 'CATENV_N7'     : {$pf = array(  576.000,  792.000); break;}
2461
 
            case 'CATENV_N8'     : {$pf = array(  594.000,  810.000); break;}
2462
 
            case 'CATENV_N9_1/2' : {$pf = array(  612.000,  756.000); break;}
2463
 
            case 'CATENV_N9_3/4' : {$pf = array(  630.000,  810.000); break;}
2464
 
            case 'CATENV_N10_1/2': {$pf = array(  648.000,  864.000); break;}
2465
 
            case 'CATENV_N12_1/2': {$pf = array(  684.000,  900.000); break;}
2466
 
            case 'CATENV_N13_1/2': {$pf = array(  720.000,  936.000); break;}
2467
 
            case 'CATENV_N14_1/4': {$pf = array(  810.000,  882.000); break;}
2468
 
            case 'CATENV_N14_1/2': {$pf = array(  828.000, 1044.000); break;}
2469
 
            // Japanese (JIS P 0138-61) Standard B-Series
2470
 
            case 'JIS_B0' : {$pf = array( 2919.685, 4127.244); break;}
2471
 
            case 'JIS_B1' : {$pf = array( 2063.622, 2919.685); break;}
2472
 
            case 'JIS_B2' : {$pf = array( 1459.843, 2063.622); break;}
2473
 
            case 'JIS_B3' : {$pf = array( 1031.811, 1459.843); break;}
2474
 
            case 'JIS_B4' : {$pf = array(  728.504, 1031.811); break;}
2475
 
            case 'JIS_B5' : {$pf = array(  515.906,  728.504); break;}
2476
 
            case 'JIS_B6' : {$pf = array(  362.835,  515.906); break;}
2477
 
            case 'JIS_B7' : {$pf = array(  257.953,  362.835); break;}
2478
 
            case 'JIS_B8' : {$pf = array(  181.417,  257.953); break;}
2479
 
            case 'JIS_B9' : {$pf = array(  127.559,  181.417); break;}
2480
 
            case 'JIS_B10': {$pf = array(   90.709,  127.559); break;}
2481
 
            case 'JIS_B11': {$pf = array(   62.362,   90.709); break;}
2482
 
            case 'JIS_B12': {$pf = array(   45.354,   62.362); break;}
2483
 
            // PA Series
2484
 
            case 'PA0' : {$pf = array( 2381.102, 3174.803,); break;}
2485
 
            case 'PA1' : {$pf = array( 1587.402, 2381.102); break;}
2486
 
            case 'PA2' : {$pf = array( 1190.551, 1587.402); break;}
2487
 
            case 'PA3' : {$pf = array(  793.701, 1190.551); break;}
2488
 
            case 'PA4' : {$pf = array(  595.276,  793.701); break;}
2489
 
            case 'PA5' : {$pf = array(  396.850,  595.276); break;}
2490
 
            case 'PA6' : {$pf = array(  297.638,  396.850); break;}
2491
 
            case 'PA7' : {$pf = array(  198.425,  297.638); break;}
2492
 
            case 'PA8' : {$pf = array(  147.402,  198.425); break;}
2493
 
            case 'PA9' : {$pf = array(   99.213,  147.402); break;}
2494
 
            case 'PA10': {$pf = array(   73.701,   99.213); break;}
2495
 
            // Standard Photographic Print Sizes
2496
 
            case 'PASSPORT_PHOTO': {$pf = array(   99.213,  127.559); break;}
2497
 
            case 'E'   : {$pf = array(  233.858,  340.157); break;}
2498
 
            case 'L':
2499
 
            case '3R'  : {$pf = array(  252.283,  360.000); break;}
2500
 
            case 'KG':
2501
 
            case '4R'  : {$pf = array(  289.134,  430.866); break;}
2502
 
            case '4D'  : {$pf = array(  340.157,  430.866); break;}
2503
 
            case '2L':
2504
 
            case '5R'  : {$pf = array(  360.000,  504.567); break;}
2505
 
            case '8P':
2506
 
            case '6R'  : {$pf = array(  430.866,  575.433); break;}
2507
 
            case '6P':
2508
 
            case '8R'  : {$pf = array(  575.433,  720.000); break;}
2509
 
            case '6PW':
2510
 
            case 'S8R' : {$pf = array(  575.433,  864.567); break;}
2511
 
            case '4P':
2512
 
            case '10R' : {$pf = array(  720.000,  864.567); break;}
2513
 
            case '4PW':
2514
 
            case 'S10R': {$pf = array(  720.000, 1080.000); break;}
2515
 
            case '11R' : {$pf = array(  790.866, 1009.134); break;}
2516
 
            case 'S11R': {$pf = array(  790.866, 1224.567); break;}
2517
 
            case '12R' : {$pf = array(  864.567, 1080.000); break;}
2518
 
            case 'S12R': {$pf = array(  864.567, 1292.598); break;}
2519
 
            // Common Newspaper Sizes
2520
 
            case 'NEWSPAPER_BROADSHEET': {$pf = array( 2125.984, 1700.787); break;}
2521
 
            case 'NEWSPAPER_BERLINER'  : {$pf = array( 1332.283,  892.913); break;}
2522
 
            case 'NEWSPAPER_TABLOID':
2523
 
            case 'NEWSPAPER_COMPACT'   : {$pf = array( 1218.898,  793.701); break;}
2524
 
            // Business Cards
2525
 
            case 'CREDIT_CARD':
2526
 
            case 'BUSINESS_CARD':
2527
 
            case 'BUSINESS_CARD_ISO7810': {$pf = array(  153.014,  242.646); break;}
2528
 
            case 'BUSINESS_CARD_ISO216' : {$pf = array(  147.402,  209.764); break;}
2529
 
            case 'BUSINESS_CARD_IT':
2530
 
            case 'BUSINESS_CARD_UK':
2531
 
            case 'BUSINESS_CARD_FR':
2532
 
            case 'BUSINESS_CARD_DE':
2533
 
            case 'BUSINESS_CARD_ES'     : {$pf = array(  155.906,  240.945); break;}
2534
 
            case 'BUSINESS_CARD_CA':
2535
 
            case 'BUSINESS_CARD_US'     : {$pf = array(  144.567,  252.283); break;}
2536
 
            case 'BUSINESS_CARD_JP'     : {$pf = array(  155.906,  257.953); break;}
2537
 
            case 'BUSINESS_CARD_HK'     : {$pf = array(  153.071,  255.118); break;}
2538
 
            case 'BUSINESS_CARD_AU':
2539
 
            case 'BUSINESS_CARD_DK':
2540
 
            case 'BUSINESS_CARD_SE'     : {$pf = array(  155.906,  255.118); break;}
2541
 
            case 'BUSINESS_CARD_RU':
2542
 
            case 'BUSINESS_CARD_CZ':
2543
 
            case 'BUSINESS_CARD_FI':
2544
 
            case 'BUSINESS_CARD_HU':
2545
 
            case 'BUSINESS_CARD_IL'     : {$pf = array(  141.732,  255.118); break;}
2546
 
            // Billboards
2547
 
            case '4SHEET' : {$pf = array( 2880.000, 4320.000); break;}
2548
 
            case '6SHEET' : {$pf = array( 3401.575, 5102.362); break;}
2549
 
            case '12SHEET': {$pf = array( 8640.000, 4320.000); break;}
2550
 
            case '16SHEET': {$pf = array( 5760.000, 8640.000); break;}
2551
 
            case '32SHEET': {$pf = array(11520.000, 8640.000); break;}
2552
 
            case '48SHEET': {$pf = array(17280.000, 8640.000); break;}
2553
 
            case '64SHEET': {$pf = array(23040.000, 8640.000); break;}
2554
 
            case '96SHEET': {$pf = array(34560.000, 8640.000); break;}
2555
 
            // Old European Sizes
2556
 
            //   - Old Imperial English Sizes
2557
 
            case 'EN_EMPEROR'          : {$pf = array( 3456.000, 5184.000); break;}
2558
 
            case 'EN_ANTIQUARIAN'      : {$pf = array( 2232.000, 3816.000); break;}
2559
 
            case 'EN_GRAND_EAGLE'      : {$pf = array( 2070.000, 3024.000); break;}
2560
 
            case 'EN_DOUBLE_ELEPHANT'  : {$pf = array( 1926.000, 2880.000); break;}
2561
 
            case 'EN_ATLAS'            : {$pf = array( 1872.000, 2448.000); break;}
2562
 
            case 'EN_COLOMBIER'        : {$pf = array( 1692.000, 2484.000); break;}
2563
 
            case 'EN_ELEPHANT'         : {$pf = array( 1656.000, 2016.000); break;}
2564
 
            case 'EN_DOUBLE_DEMY'      : {$pf = array( 1620.000, 2556.000); break;}
2565
 
            case 'EN_IMPERIAL'         : {$pf = array( 1584.000, 2160.000); break;}
2566
 
            case 'EN_PRINCESS'         : {$pf = array( 1548.000, 2016.000); break;}
2567
 
            case 'EN_CARTRIDGE'        : {$pf = array( 1512.000, 1872.000); break;}
2568
 
            case 'EN_DOUBLE_LARGE_POST': {$pf = array( 1512.000, 2376.000); break;}
2569
 
            case 'EN_ROYAL'            : {$pf = array( 1440.000, 1800.000); break;}
2570
 
            case 'EN_SHEET':
2571
 
            case 'EN_HALF_POST'        : {$pf = array( 1404.000, 1692.000); break;}
2572
 
            case 'EN_SUPER_ROYAL'      : {$pf = array( 1368.000, 1944.000); break;}
2573
 
            case 'EN_DOUBLE_POST'      : {$pf = array( 1368.000, 2196.000); break;}
2574
 
            case 'EN_MEDIUM'           : {$pf = array( 1260.000, 1656.000); break;}
2575
 
            case 'EN_DEMY'             : {$pf = array( 1260.000, 1620.000); break;}
2576
 
            case 'EN_LARGE_POST'       : {$pf = array( 1188.000, 1512.000); break;}
2577
 
            case 'EN_COPY_DRAUGHT'     : {$pf = array( 1152.000, 1440.000); break;}
2578
 
            case 'EN_POST'             : {$pf = array( 1116.000, 1386.000); break;}
2579
 
            case 'EN_CROWN'            : {$pf = array( 1080.000, 1440.000); break;}
2580
 
            case 'EN_PINCHED_POST'     : {$pf = array( 1062.000, 1332.000); break;}
2581
 
            case 'EN_BRIEF'            : {$pf = array(  972.000, 1152.000); break;}
2582
 
            case 'EN_FOOLSCAP'         : {$pf = array(  972.000, 1224.000); break;}
2583
 
            case 'EN_SMALL_FOOLSCAP'   : {$pf = array(  954.000, 1188.000); break;}
2584
 
            case 'EN_POTT'             : {$pf = array(  900.000, 1080.000); break;}
2585
 
            //   - Old Imperial Belgian Sizes
2586
 
            case 'BE_GRAND_AIGLE' : {$pf = array( 1984.252, 2948.031); break;}
2587
 
            case 'BE_COLOMBIER'   : {$pf = array( 1757.480, 2409.449); break;}
2588
 
            case 'BE_DOUBLE_CARRE': {$pf = array( 1757.480, 2607.874); break;}
2589
 
            case 'BE_ELEPHANT'    : {$pf = array( 1746.142, 2182.677); break;}
2590
 
            case 'BE_PETIT_AIGLE' : {$pf = array( 1700.787, 2381.102); break;}
2591
 
            case 'BE_GRAND_JESUS' : {$pf = array( 1559.055, 2069.291); break;}
2592
 
            case 'BE_JESUS'       : {$pf = array( 1530.709, 2069.291); break;}
2593
 
            case 'BE_RAISIN'      : {$pf = array( 1417.323, 1842.520); break;}
2594
 
            case 'BE_GRAND_MEDIAN': {$pf = array( 1303.937, 1714.961); break;}
2595
 
            case 'BE_DOUBLE_POSTE': {$pf = array( 1233.071, 1601.575); break;}
2596
 
            case 'BE_COQUILLE'    : {$pf = array( 1218.898, 1587.402); break;}
2597
 
            case 'BE_PETIT_MEDIAN': {$pf = array( 1176.378, 1502.362); break;}
2598
 
            case 'BE_RUCHE'       : {$pf = array( 1020.472, 1303.937); break;}
2599
 
            case 'BE_PROPATRIA'   : {$pf = array(  977.953, 1218.898); break;}
2600
 
            case 'BE_LYS'         : {$pf = array(  898.583, 1125.354); break;}
2601
 
            case 'BE_POT'         : {$pf = array(  870.236, 1088.504); break;}
2602
 
            case 'BE_ROSETTE'     : {$pf = array(  765.354,  983.622); break;}
2603
 
            //   - Old Imperial French Sizes
2604
 
            case 'FR_UNIVERS'          : {$pf = array( 2834.646, 3685.039); break;}
2605
 
            case 'FR_DOUBLE_COLOMBIER' : {$pf = array( 2551.181, 3571.654); break;}
2606
 
            case 'FR_GRANDE_MONDE'     : {$pf = array( 2551.181, 3571.654); break;}
2607
 
            case 'FR_DOUBLE_SOLEIL'    : {$pf = array( 2267.717, 3401.575); break;}
2608
 
            case 'FR_DOUBLE_JESUS'     : {$pf = array( 2154.331, 3174.803); break;}
2609
 
            case 'FR_GRAND_AIGLE'      : {$pf = array( 2125.984, 3004.724); break;}
2610
 
            case 'FR_PETIT_AIGLE'      : {$pf = array( 1984.252, 2664.567); break;}
2611
 
            case 'FR_DOUBLE_RAISIN'    : {$pf = array( 1842.520, 2834.646); break;}
2612
 
            case 'FR_JOURNAL'          : {$pf = array( 1842.520, 2664.567); break;}
2613
 
            case 'FR_COLOMBIER_AFFICHE': {$pf = array( 1785.827, 2551.181); break;}
2614
 
            case 'FR_DOUBLE_CAVALIER'  : {$pf = array( 1757.480, 2607.874); break;}
2615
 
            case 'FR_CLOCHE'           : {$pf = array( 1700.787, 2267.717); break;}
2616
 
            case 'FR_SOLEIL'           : {$pf = array( 1700.787, 2267.717); break;}
2617
 
            case 'FR_DOUBLE_CARRE'     : {$pf = array( 1587.402, 2551.181); break;}
2618
 
            case 'FR_DOUBLE_COQUILLE'  : {$pf = array( 1587.402, 2494.488); break;}
2619
 
            case 'FR_JESUS'            : {$pf = array( 1587.402, 2154.331); break;}
2620
 
            case 'FR_RAISIN'           : {$pf = array( 1417.323, 1842.520); break;}
2621
 
            case 'FR_CAVALIER'         : {$pf = array( 1303.937, 1757.480); break;}
2622
 
            case 'FR_DOUBLE_COURONNE'  : {$pf = array( 1303.937, 2040.945); break;}
2623
 
            case 'FR_CARRE'            : {$pf = array( 1275.591, 1587.402); break;}
2624
 
            case 'FR_COQUILLE'         : {$pf = array( 1247.244, 1587.402); break;}
2625
 
            case 'FR_DOUBLE_TELLIERE'  : {$pf = array( 1247.244, 1927.559); break;}
2626
 
            case 'FR_DOUBLE_CLOCHE'    : {$pf = array( 1133.858, 1700.787); break;}
2627
 
            case 'FR_DOUBLE_POT'       : {$pf = array( 1133.858, 1757.480); break;}
2628
 
            case 'FR_ECU'              : {$pf = array( 1133.858, 1474.016); break;}
2629
 
            case 'FR_COURONNE'         : {$pf = array( 1020.472, 1303.937); break;}
2630
 
            case 'FR_TELLIERE'         : {$pf = array(  963.780, 1247.244); break;}
2631
 
            case 'FR_POT'              : {$pf = array(  878.740, 1133.858); break;}
2632
 
            // DEFAULT ISO A4
2633
 
            default: {$pf = array(  595.276,  841.890); break;}
2634
 
        }
2635
 
        return $pf;
2636
 
    }
2637
 
 
2638
 
    /**
2639
 
     * Change the format of the current page
2640
 
     * @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>
2641
 
     * <li>['format'] = page format name (one of the above);</li>
2642
 
     * <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>
2643
 
     * <li>['PZ'] : The page's preferred zoom (magnification) factor.</li>
2644
 
     * <li>['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed:</li>
2645
 
     * <li>['MediaBox']['llx'] : lower-left x coordinate in points</li>
2646
 
     * <li>['MediaBox']['lly'] : lower-left y coordinate in points</li>
2647
 
     * <li>['MediaBox']['urx'] : upper-right x coordinate in points</li>
2648
 
     * <li>['MediaBox']['ury'] : upper-right y coordinate in points</li>
2649
 
     * <li>['CropBox'] : the visible region of default user space:</li>
2650
 
     * <li>['CropBox']['llx'] : lower-left x coordinate in points</li>
2651
 
     * <li>['CropBox']['lly'] : lower-left y coordinate in points</li>
2652
 
     * <li>['CropBox']['urx'] : upper-right x coordinate in points</li>
2653
 
     * <li>['CropBox']['ury'] : upper-right y coordinate in points</li>
2654
 
     * <li>['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment:</li>
2655
 
     * <li>['BleedBox']['llx'] : lower-left x coordinate in points</li>
2656
 
     * <li>['BleedBox']['lly'] : lower-left y coordinate in points</li>
2657
 
     * <li>['BleedBox']['urx'] : upper-right x coordinate in points</li>
2658
 
     * <li>['BleedBox']['ury'] : upper-right y coordinate in points</li>
2659
 
     * <li>['TrimBox'] : the intended dimensions of the finished page after trimming:</li>
2660
 
     * <li>['TrimBox']['llx'] : lower-left x coordinate in points</li>
2661
 
     * <li>['TrimBox']['lly'] : lower-left y coordinate in points</li>
2662
 
     * <li>['TrimBox']['urx'] : upper-right x coordinate in points</li>
2663
 
     * <li>['TrimBox']['ury'] : upper-right y coordinate in points</li>
2664
 
     * <li>['ArtBox'] : the extent of the page's meaningful content:</li>
2665
 
     * <li>['ArtBox']['llx'] : lower-left x coordinate in points</li>
2666
 
     * <li>['ArtBox']['lly'] : lower-left y coordinate in points</li>
2667
 
     * <li>['ArtBox']['urx'] : upper-right x coordinate in points</li>
2668
 
     * <li>['ArtBox']['ury'] : upper-right y coordinate in points</li>
2669
 
     * <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>
2670
 
     * <li>['BoxColorInfo'][BOXTYPE]['C'] : an array of three numbers in the range 0-255, representing the components in the DeviceRGB colour space.</li>
2671
 
     * <li>['BoxColorInfo'][BOXTYPE]['W'] : the guideline width in default user units</li>
2672
 
     * <li>['BoxColorInfo'][BOXTYPE]['S'] : the guideline style: S = Solid; D = Dashed</li>
2673
 
     * <li>['BoxColorInfo'][BOXTYPE]['D'] : dash array defining a pattern of dashes and gaps to be used in drawing dashed guidelines</li>
2674
 
     * <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>
2675
 
     * <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>
2676
 
     * <li>['trans']['S'] : transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li>
2677
 
     * <li>['trans']['D'] : The duration of the transition effect, in seconds.</li>
2678
 
     * <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>
2679
 
     * <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>
2680
 
     * <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>
2681
 
     * <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>
2682
 
     * <li>['trans']['B'] : (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li>
2683
 
     * </ul>
2684
 
     * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul>
2685
 
     * <li>P or Portrait (default)</li>
2686
 
     * <li>L or Landscape</li>
2687
 
     * <li>'' (empty string) for automatic orientation</li>
2688
 
     * </ul>
2689
 
     * @protected
2690
 
     * @since 3.0.015 (2008-06-06)
2691
 
     * @see getPageSizeFromFormat()
2692
 
     */
2693
 
    protected function setPageFormat($format, $orientation='P') {
2694
 
        if (!empty($format) AND isset($this->pagedim[$this->page])) {
2695
 
            // remove inherited values
2696
 
            unset($this->pagedim[$this->page]);
2697
 
        }
2698
 
        if (is_string($format)) {
2699
 
            // get page measures from format name
2700
 
            $pf = $this->getPageSizeFromFormat($format);
2701
 
            $this->fwPt = $pf[0];
2702
 
            $this->fhPt = $pf[1];
2703
 
        } else {
2704
 
            // the boundaries of the physical medium on which the page shall be displayed or printed
2705
 
            if (isset($format['MediaBox'])) {
2706
 
                $this->setPageBoxes($this->page, 'MediaBox', $format['MediaBox']['llx'], $format['MediaBox']['lly'], $format['MediaBox']['urx'], $format['MediaBox']['ury'], false);
2707
 
                $this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k);
2708
 
                $this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k);
2709
 
            } else {
2710
 
                if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) {
2711
 
                    $pf = array(($format[0] * $this->k), ($format[1] * $this->k));
2712
 
                } else {
2713
 
                    if (!isset($format['format'])) {
2714
 
                        // default value
2715
 
                        $format['format'] = 'A4';
2716
 
                    }
2717
 
                    $pf = $this->getPageSizeFromFormat($format['format']);
2718
 
                }
2719
 
                $this->fwPt = $pf[0];
2720
 
                $this->fhPt = $pf[1];
2721
 
                $this->setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true);
2722
 
            }
2723
 
            // the visible region of default user space
2724
 
            if (isset($format['CropBox'])) {
2725
 
                $this->setPageBoxes($this->page, 'CropBox', $format['CropBox']['llx'], $format['CropBox']['lly'], $format['CropBox']['urx'], $format['CropBox']['ury'], false);
2726
 
            }
2727
 
            // the region to which the contents of the page shall be clipped when output in a production environment
2728
 
            if (isset($format['BleedBox'])) {
2729
 
                $this->setPageBoxes($this->page, 'BleedBox', $format['BleedBox']['llx'], $format['BleedBox']['lly'], $format['BleedBox']['urx'], $format['BleedBox']['ury'], false);
2730
 
            }
2731
 
            // the intended dimensions of the finished page after trimming
2732
 
            if (isset($format['TrimBox'])) {
2733
 
                $this->setPageBoxes($this->page, 'TrimBox', $format['TrimBox']['llx'], $format['TrimBox']['lly'], $format['TrimBox']['urx'], $format['TrimBox']['ury'], false);
2734
 
            }
2735
 
            // the page's meaningful content (including potential white space)
2736
 
            if (isset($format['ArtBox'])) {
2737
 
                $this->setPageBoxes($this->page, 'ArtBox', $format['ArtBox']['llx'], $format['ArtBox']['lly'], $format['ArtBox']['urx'], $format['ArtBox']['ury'], false);
2738
 
            }
2739
 
            // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries
2740
 
            if (isset($format['BoxColorInfo'])) {
2741
 
                $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo'];
2742
 
            }
2743
 
            if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) {
2744
 
                // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2745
 
                $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']);
2746
 
            }
2747
 
            if (isset($format['PZ'])) {
2748
 
                // The page's preferred zoom (magnification) factor
2749
 
                $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']);
2750
 
            }
2751
 
            if (isset($format['trans'])) {
2752
 
                // The style and duration of the visual transition to use when moving from another page to the given page during a presentation
2753
 
                if (isset($format['trans']['Dur'])) {
2754
 
                    // The page's display duration
2755
 
                    $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']);
2756
 
                }
2757
 
                $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade');
2758
 
                if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) {
2759
 
                    // The transition style that shall be used when moving to this page from another during a presentation
2760
 
                    $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S'];
2761
 
                    $valid_effect = array('Split', 'Blinds');
2762
 
                    $valid_vals = array('H', 'V');
2763
 
                    if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) {
2764
 
                        $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm'];
2765
 
                    }
2766
 
                    $valid_effect = array('Split', 'Box', 'Fly');
2767
 
                    $valid_vals = array('I', 'O');
2768
 
                    if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) {
2769
 
                        $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M'];
2770
 
                    }
2771
 
                    $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push');
2772
 
                    if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) {
2773
 
                        if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe'))
2774
 
                            OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter'))
2775
 
                            OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) {
2776
 
                            $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']);
2777
 
                        }
2778
 
                    }
2779
 
                    if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
2780
 
                        $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']);
2781
 
                    }
2782
 
                    if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
2783
 
                        $this->pagedim[$this->page]['trans']['B'] = 'true';
2784
 
                    }
2785
 
                } else {
2786
 
                    $this->pagedim[$this->page]['trans']['S'] = 'R';
2787
 
                }
2788
 
                if (isset($format['trans']['D'])) {
2789
 
                    // The duration of the transition effect, in seconds
2790
 
                    $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']);
2791
 
                } else {
2792
 
                    $this->pagedim[$this->page]['trans']['D'] = 1;
2793
 
                }
2794
 
            }
2795
 
        }
2796
 
        $this->setPageOrientation($orientation);
2797
 
    }
2798
 
 
2799
 
    /**
2800
 
     * Set page boundaries.
2801
 
     * @param $page (int) page number
2802
 
     * @param $type (string) valid values are: <ul><li>'MediaBox' : the boundaries of the physical medium on which the page shall be displayed or printed;</li><li>'CropBox' : the visible region of default user space;</li><li>'BleedBox' : the region to which the contents of the page shall be clipped when output in a production environment;</li><li>'TrimBox' : the intended dimensions of the finished page after trimming;</li><li>'ArtBox' : the page's meaningful content (including potential white space).</li></ul>
2803
 
     * @param $llx (float) lower-left x coordinate in user units
2804
 
     * @param $lly (float) lower-left y coordinate in user units
2805
 
     * @param $urx (float) upper-right x coordinate in user units
2806
 
     * @param $ury (float) upper-right y coordinate in user units
2807
 
     * @param $points (boolean) if true uses user units as unit of measure, otherwise uses PDF points
2808
 
     * @public
2809
 
     * @since 5.0.010 (2010-05-17)
2810
 
     */
2811
 
    public function setPageBoxes($page, $type, $llx, $lly, $urx, $ury, $points=false) {
2812
 
        if (!isset($this->pagedim[$page])) {
2813
 
            // initialize array
2814
 
            $this->pagedim[$page] = array();
2815
 
        }
2816
 
        $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
2817
 
        if (!in_array($type, $pageboxes)) {
2818
 
            return;
2819
 
        }
2820
 
        if ($points) {
2821
 
            $k = 1;
2822
 
        } else {
2823
 
            $k = $this->k;
2824
 
        }
2825
 
        $this->pagedim[$page][$type]['llx'] = ($llx * $k);
2826
 
        $this->pagedim[$page][$type]['lly'] = ($lly * $k);
2827
 
        $this->pagedim[$page][$type]['urx'] = ($urx * $k);
2828
 
        $this->pagedim[$page][$type]['ury'] = ($ury * $k);
2829
 
    }
2830
 
 
2831
 
    /**
2832
 
     * Swap X and Y coordinates of page boxes (change page boxes orientation).
2833
 
     * @param $page (int) page number
2834
 
     * @protected
2835
 
     * @since 5.0.010 (2010-05-17)
2836
 
     */
2837
 
    protected function swapPageBoxCoordinates($page) {
2838
 
        $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
2839
 
        foreach ($pageboxes as $type) {
2840
 
            // swap X and Y coordinates
2841
 
            if (isset($this->pagedim[$page][$type])) {
2842
 
                $tmp = $this->pagedim[$page][$type]['llx'];
2843
 
                $this->pagedim[$page][$type]['llx'] = $this->pagedim[$page][$type]['lly'];
2844
 
                $this->pagedim[$page][$type]['lly'] = $tmp;
2845
 
                $tmp = $this->pagedim[$page][$type]['urx'];
2846
 
                $this->pagedim[$page][$type]['urx'] = $this->pagedim[$page][$type]['ury'];
2847
 
                $this->pagedim[$page][$type]['ury'] = $tmp;
2848
 
            }
2849
 
        }
2850
 
    }
2851
 
 
2852
 
    /**
2853
 
     * Set page orientation.
2854
 
     * @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>
2855
 
     * @param $autopagebreak (boolean) Boolean indicating if auto-page-break mode should be on or off.
2856
 
     * @param $bottommargin (float) bottom margin of the page.
2857
 
     * @public
2858
 
     * @since 3.0.015 (2008-06-06)
2859
 
     */
2860
 
    public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
2861
 
        if (!isset($this->pagedim[$this->page]['MediaBox'])) {
2862
 
            // the boundaries of the physical medium on which the page shall be displayed or printed
2863
 
            $this->setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true);
2864
 
        }
2865
 
        if (!isset($this->pagedim[$this->page]['CropBox'])) {
2866
 
            // the visible region of default user space
2867
 
            $this->setPageBoxes($this->page, 'CropBox', $this->pagedim[$this->page]['MediaBox']['llx'], $this->pagedim[$this->page]['MediaBox']['lly'], $this->pagedim[$this->page]['MediaBox']['urx'], $this->pagedim[$this->page]['MediaBox']['ury'], true);
2868
 
        }
2869
 
        if (!isset($this->pagedim[$this->page]['BleedBox'])) {
2870
 
            // the region to which the contents of the page shall be clipped when output in a production environment
2871
 
            $this->setPageBoxes($this->page, 'BleedBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true);
2872
 
        }
2873
 
        if (!isset($this->pagedim[$this->page]['TrimBox'])) {
2874
 
            // the intended dimensions of the finished page after trimming
2875
 
            $this->setPageBoxes($this->page, 'TrimBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true);
2876
 
        }
2877
 
        if (!isset($this->pagedim[$this->page]['ArtBox'])) {
2878
 
            // the page's meaningful content (including potential white space)
2879
 
            $this->setPageBoxes($this->page, 'ArtBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true);
2880
 
        }
2881
 
        if (!isset($this->pagedim[$this->page]['Rotate'])) {
2882
 
            // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2883
 
            $this->pagedim[$this->page]['Rotate'] = 0;
2884
 
        }
2885
 
        if (!isset($this->pagedim[$this->page]['PZ'])) {
2886
 
            // The page's preferred zoom (magnification) factor
2887
 
            $this->pagedim[$this->page]['PZ'] = 1;
2888
 
        }
2889
 
        if ($this->fwPt > $this->fhPt) {
2890
 
            // landscape
2891
 
            $default_orientation = 'L';
2892
 
        } else {
2893
 
            // portrait
2894
 
            $default_orientation = 'P';
2895
 
        }
2896
 
        $valid_orientations = array('P', 'L');
2897
 
        if (empty($orientation)) {
2898
 
            $orientation = $default_orientation;
2899
 
        } else {
2900
 
            $orientation = strtoupper($orientation{0});
2901
 
        }
2902
 
        if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
2903
 
            $this->CurOrientation = $orientation;
2904
 
            $this->wPt = $this->fhPt;
2905
 
            $this->hPt = $this->fwPt;
2906
 
        } else {
2907
 
            $this->CurOrientation = $default_orientation;
2908
 
            $this->wPt = $this->fwPt;
2909
 
            $this->hPt = $this->fhPt;
2910
 
        }
2911
 
        if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){
2912
 
            // swap X and Y coordinates (change page orientation)
2913
 
            $this->swapPageBoxCoordinates($this->page);
2914
 
        }
2915
 
        $this->w = $this->wPt / $this->k;
2916
 
        $this->h = $this->hPt / $this->k;
2917
 
        if ($this->empty_string($autopagebreak)) {
2918
 
            if (isset($this->AutoPageBreak)) {
2919
 
                $autopagebreak = $this->AutoPageBreak;
2920
 
            } else {
2921
 
                $autopagebreak = true;
2922
 
            }
2923
 
        }
2924
 
        if ($this->empty_string($bottommargin)) {
2925
 
            if (isset($this->bMargin)) {
2926
 
                $bottommargin = $this->bMargin;
2927
 
            } else {
2928
 
                // default value = 2 cm
2929
 
                $bottommargin = 2 * 28.35 / $this->k;
2930
 
            }
2931
 
        }
2932
 
        $this->SetAutoPageBreak($autopagebreak, $bottommargin);
2933
 
        // store page dimensions
2934
 
        $this->pagedim[$this->page]['w'] = $this->wPt;
2935
 
        $this->pagedim[$this->page]['h'] = $this->hPt;
2936
 
        $this->pagedim[$this->page]['wk'] = $this->w;
2937
 
        $this->pagedim[$this->page]['hk'] = $this->h;
2938
 
        $this->pagedim[$this->page]['tm'] = $this->tMargin;
2939
 
        $this->pagedim[$this->page]['bm'] = $bottommargin;
2940
 
        $this->pagedim[$this->page]['lm'] = $this->lMargin;
2941
 
        $this->pagedim[$this->page]['rm'] = $this->rMargin;
2942
 
        $this->pagedim[$this->page]['pb'] = $autopagebreak;
2943
 
        $this->pagedim[$this->page]['or'] = $this->CurOrientation;
2944
 
        $this->pagedim[$this->page]['olm'] = $this->original_lMargin;
2945
 
        $this->pagedim[$this->page]['orm'] = $this->original_rMargin;
2946
 
    }
2947
 
 
2948
 
    /**
2949
 
     * Set regular expression to detect withespaces or word separators.
2950
 
     * The pattern delimiter must be the forward-slash character "/".
2951
 
     * Some example patterns are:
2952
 
     * <pre>
2953
 
     * Non-Unicode or missing PCRE unicode support: "/[^\S\xa0]/"
2954
 
     * Unicode and PCRE unicode support: "/[^\S\P{Z}\xa0]/u"
2955
 
     * Unicode and PCRE unicode support in Chinese mode: "/[^\S\P{Z}\P{Lo}\xa0]/u"
2956
 
     * if PCRE unicode support is turned ON ("\P" is the negate class of "\p"):
2957
 
     * "\p{Z}" or "\p{Separator}": any kind of Unicode whitespace or invisible separator.
2958
 
     * "\p{Lo}" or "\p{Other_Letter}": a Unicode letter or ideograph that does not have lowercase and uppercase variants.
2959
 
     * "\p{Lo}" is needed for Chinese characters because are packed next to each other without spaces in between.
2960
 
     * </pre>
2961
 
     * @param $re (string) regular expression (leave empty for default).
2962
 
     * @public
2963
 
     * @since 4.6.016 (2009-06-15)
2964
 
     */
2965
 
    public function setSpacesRE($re='/[^\S\xa0]/') {
2966
 
        $this->re_spaces = $re;
2967
 
        $re_parts = explode('/', $re);
2968
 
        // get pattern parts
2969
 
        $this->re_space = array();
2970
 
        if (isset($re_parts[1]) AND !empty($re_parts[1])) {
2971
 
            $this->re_space['p'] = $re_parts[1];
2972
 
        } else {
2973
 
            $this->re_space['p'] = '[\s]';
2974
 
        }
2975
 
        // set pattern modifiers
2976
 
        if (isset($re_parts[2]) AND !empty($re_parts[2])) {
2977
 
            $this->re_space['m'] = $re_parts[2];
2978
 
        } else {
2979
 
            $this->re_space['m'] = '';
2980
 
        }
2981
 
    }
2982
 
 
2983
 
    /**
2984
 
     * Enable or disable Right-To-Left language mode
2985
 
     * @param $enable (Boolean) if true enable Right-To-Left language mode.
2986
 
     * @param $resetx (Boolean) if true reset the X position on direction change.
2987
 
     * @public
2988
 
     * @since 2.0.000 (2008-01-03)
2989
 
     */
2990
 
    public function setRTL($enable, $resetx=true) {
2991
 
        $enable = $enable ? true : false;
2992
 
        $resetx = ($resetx AND ($enable != $this->rtl));
2993
 
        $this->rtl = $enable;
2994
 
        $this->tmprtl = false;
2995
 
        if ($resetx) {
2996
 
            $this->Ln(0);
2997
 
        }
2998
 
    }
2999
 
 
3000
 
    /**
3001
 
     * Return the RTL status
3002
 
     * @return boolean
3003
 
     * @public
3004
 
     * @since 4.0.012 (2008-07-24)
3005
 
     */
3006
 
    public function getRTL() {
3007
 
        return $this->rtl;
3008
 
    }
3009
 
 
3010
 
    /**
3011
 
     * Force temporary RTL language direction
3012
 
     * @param $mode (mixed) can be false, 'L' for LTR or 'R' for RTL
3013
 
     * @public
3014
 
     * @since 2.1.000 (2008-01-09)
3015
 
     */
3016
 
    public function setTempRTL($mode) {
3017
 
        $newmode = false;
3018
 
        switch (strtoupper($mode)) {
3019
 
            case 'LTR':
3020
 
            case 'L': {
3021
 
                if ($this->rtl) {
3022
 
                    $newmode = 'L';
3023
 
                }
3024
 
                break;
3025
 
            }
3026
 
            case 'RTL':
3027
 
            case 'R': {
3028
 
                if (!$this->rtl) {
3029
 
                    $newmode = 'R';
3030
 
                }
3031
 
                break;
3032
 
            }
3033
 
            case false:
3034
 
            default: {
3035
 
                $newmode = false;
3036
 
                break;
3037
 
            }
3038
 
        }
3039
 
        $this->tmprtl = $newmode;
3040
 
    }
3041
 
 
3042
 
    /**
3043
 
     * Return the current temporary RTL status
3044
 
     * @return boolean
3045
 
     * @public
3046
 
     * @since 4.8.014 (2009-11-04)
3047
 
     */
3048
 
    public function isRTLTextDir() {
3049
 
        return ($this->rtl OR ($this->tmprtl == 'R'));
3050
 
    }
3051
 
 
3052
 
    /**
3053
 
     * Set the last cell height.
3054
 
     * @param $h (float) cell height.
3055
 
     * @author Nicola Asuni
3056
 
     * @public
3057
 
     * @since 1.53.0.TC034
3058
 
     */
3059
 
    public function setLastH($h) {
3060
 
        $this->lasth = $h;
3061
 
    }
3062
 
 
3063
 
    /**
3064
 
     * Reset the last cell height.
3065
 
     * @public
3066
 
     * @since 5.9.000 (2010-10-03)
3067
 
     */
3068
 
    public function resetLastH() {
3069
 
        $this->lasth = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
3070
 
    }
3071
 
 
3072
 
    /**
3073
 
     * Get the last cell height.
3074
 
     * @return last cell height
3075
 
     * @public
3076
 
     * @since 4.0.017 (2008-08-05)
3077
 
     */
3078
 
    public function getLastH() {
3079
 
        return $this->lasth;
3080
 
    }
3081
 
 
3082
 
    /**
3083
 
     * Set the adjusting factor to convert pixels to user units.
3084
 
     * @param $scale (float) adjusting factor to convert pixels to user units.
3085
 
     * @author Nicola Asuni
3086
 
     * @public
3087
 
     * @since 1.5.2
3088
 
     */
3089
 
    public function setImageScale($scale) {
3090
 
        $this->imgscale = $scale;
3091
 
    }
3092
 
 
3093
 
    /**
3094
 
     * Returns the adjusting factor to convert pixels to user units.
3095
 
     * @return float adjusting factor to convert pixels to user units.
3096
 
     * @author Nicola Asuni
3097
 
     * @public
3098
 
     * @since 1.5.2
3099
 
     */
3100
 
    public function getImageScale() {
3101
 
        return $this->imgscale;
3102
 
    }
3103
 
 
3104
 
    /**
3105
 
     * Returns an array of page dimensions:
3106
 
     * <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>
3107
 
     * @param $pagenum (int) page number (empty = current page)
3108
 
     * @return array of page dimensions.
3109
 
     * @author Nicola Asuni
3110
 
     * @public
3111
 
     * @since 4.5.027 (2009-03-16)
3112
 
     */
3113
 
    public function getPageDimensions($pagenum='') {
3114
 
        if (empty($pagenum)) {
3115
 
            $pagenum = $this->page;
3116
 
        }
3117
 
        return $this->pagedim[$pagenum];
3118
 
    }
3119
 
 
3120
 
    /**
3121
 
     * Returns the page width in units.
3122
 
     * @param $pagenum (int) page number (empty = current page)
3123
 
     * @return int page width.
3124
 
     * @author Nicola Asuni
3125
 
     * @public
3126
 
     * @since 1.5.2
3127
 
     * @see getPageDimensions()
3128
 
     */
3129
 
    public function getPageWidth($pagenum='') {
3130
 
        if (empty($pagenum)) {
3131
 
            return $this->w;
3132
 
        }
3133
 
        return $this->pagedim[$pagenum]['w'];
3134
 
    }
3135
 
 
3136
 
    /**
3137
 
     * Returns the page height in units.
3138
 
     * @param $pagenum (int) page number (empty = current page)
3139
 
     * @return int page height.
3140
 
     * @author Nicola Asuni
3141
 
     * @public
3142
 
     * @since 1.5.2
3143
 
     * @see getPageDimensions()
3144
 
     */
3145
 
    public function getPageHeight($pagenum='') {
3146
 
        if (empty($pagenum)) {
3147
 
            return $this->h;
3148
 
        }
3149
 
        return $this->pagedim[$pagenum]['h'];
3150
 
    }
3151
 
 
3152
 
    /**
3153
 
     * Returns the page break margin.
3154
 
     * @param $pagenum (int) page number (empty = current page)
3155
 
     * @return int page break margin.
3156
 
     * @author Nicola Asuni
3157
 
     * @public
3158
 
     * @since 1.5.2
3159
 
     * @see getPageDimensions()
3160
 
     */
3161
 
    public function getBreakMargin($pagenum='') {
3162
 
        if (empty($pagenum)) {
3163
 
            return $this->bMargin;
3164
 
        }
3165
 
        return $this->pagedim[$pagenum]['bm'];
3166
 
    }
3167
 
 
3168
 
    /**
3169
 
     * Returns the scale factor (number of points in user unit).
3170
 
     * @return int scale factor.
3171
 
     * @author Nicola Asuni
3172
 
     * @public
3173
 
     * @since 1.5.2
3174
 
     */
3175
 
    public function getScaleFactor() {
3176
 
        return $this->k;
3177
 
    }
3178
 
 
3179
 
    /**
3180
 
     * Defines the left, top and right margins.
3181
 
     * @param $left (float) Left margin.
3182
 
     * @param $top (float) Top margin.
3183
 
     * @param $right (float) Right margin. Default value is the left one.
3184
 
     * @param $keepmargins (boolean) if true overwrites the default page margins
3185
 
     * @public
3186
 
     * @since 1.0
3187
 
     * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
3188
 
     */
3189
 
    public function SetMargins($left, $top, $right=-1, $keepmargins=false) {
3190
 
        //Set left, top and right margins
3191
 
        $this->lMargin = $left;
3192
 
        $this->tMargin = $top;
3193
 
        if ($right == -1) {
3194
 
            $right = $left;
3195
 
        }
3196
 
        $this->rMargin = $right;
3197
 
        if ($keepmargins) {
3198
 
            // overwrite original values
3199
 
            $this->original_lMargin = $this->lMargin;
3200
 
            $this->original_rMargin = $this->rMargin;
3201
 
        }
3202
 
    }
3203
 
 
3204
 
    /**
3205
 
     * 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.
3206
 
     * @param $margin (float) The margin.
3207
 
     * @public
3208
 
     * @since 1.4
3209
 
     * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
3210
 
     */
3211
 
    public function SetLeftMargin($margin) {
3212
 
        //Set left margin
3213
 
        $this->lMargin = $margin;
3214
 
        if (($this->page > 0) AND ($this->x < $margin)) {
3215
 
            $this->x = $margin;
3216
 
        }
3217
 
    }
3218
 
 
3219
 
    /**
3220
 
     * Defines the top margin. The method can be called before creating the first page.
3221
 
     * @param $margin (float) The margin.
3222
 
     * @public
3223
 
     * @since 1.5
3224
 
     * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
3225
 
     */
3226
 
    public function SetTopMargin($margin) {
3227
 
        //Set top margin
3228
 
        $this->tMargin = $margin;
3229
 
        if (($this->page > 0) AND ($this->y < $margin)) {
3230
 
            $this->y = $margin;
3231
 
        }
3232
 
    }
3233
 
 
3234
 
    /**
3235
 
     * Defines the right margin. The method can be called before creating the first page.
3236
 
     * @param $margin (float) The margin.
3237
 
     * @public
3238
 
     * @since 1.5
3239
 
     * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
3240
 
     */
3241
 
    public function SetRightMargin($margin) {
3242
 
        $this->rMargin = $margin;
3243
 
        if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
3244
 
            $this->x = $this->w - $margin;
3245
 
        }
3246
 
    }
3247
 
 
3248
 
    /**
3249
 
     * Set the same internal Cell padding for top, right, bottom, left-
3250
 
     * @param $pad (float) internal padding.
3251
 
     * @public
3252
 
     * @since 2.1.000 (2008-01-09)
3253
 
     * @see getCellPaddings(), setCellPaddings()
3254
 
     */
3255
 
    public function SetCellPadding($pad) {
3256
 
        if ($pad >= 0) {
3257
 
            $this->cell_padding['L'] = $pad;
3258
 
            $this->cell_padding['T'] = $pad;
3259
 
            $this->cell_padding['R'] = $pad;
3260
 
            $this->cell_padding['B'] = $pad;
3261
 
        }
3262
 
    }
3263
 
 
3264
 
    /**
3265
 
     * Set the internal Cell paddings.
3266
 
     * @param $left (float) left padding
3267
 
     * @param $top (float) top padding
3268
 
     * @param $right (float) right padding
3269
 
     * @param $bottom (float) bottom padding
3270
 
     * @public
3271
 
     * @since 5.9.000 (2010-10-03)
3272
 
     * @see getCellPaddings(), SetCellPadding()
3273
 
     */
3274
 
    public function setCellPaddings($left='', $top='', $right='', $bottom='') {
3275
 
        if (($left !== '') AND ($left >= 0)) {
3276
 
            $this->cell_padding['L'] = $left;
3277
 
        }
3278
 
        if (($top !== '') AND ($top >= 0)) {
3279
 
            $this->cell_padding['T'] = $top;
3280
 
        }
3281
 
        if (($right !== '') AND ($right >= 0)) {
3282
 
            $this->cell_padding['R'] = $right;
3283
 
        }
3284
 
        if (($bottom !== '') AND ($bottom >= 0)) {
3285
 
            $this->cell_padding['B'] = $bottom;
3286
 
        }
3287
 
    }
3288
 
 
3289
 
    /**
3290
 
     * Get the internal Cell padding array.
3291
 
     * @return array of padding values
3292
 
     * @public
3293
 
     * @since 5.9.000 (2010-10-03)
3294
 
     * @see setCellPaddings(), SetCellPadding()
3295
 
     */
3296
 
    public function getCellPaddings() {
3297
 
        return $this->cell_padding;
3298
 
    }
3299
 
 
3300
 
    /**
3301
 
     * Set the internal Cell margins.
3302
 
     * @param $left (float) left margin
3303
 
     * @param $top (float) top margin
3304
 
     * @param $right (float) right margin
3305
 
     * @param $bottom (float) bottom margin
3306
 
     * @public
3307
 
     * @since 5.9.000 (2010-10-03)
3308
 
     * @see getCellMargins()
3309
 
     */
3310
 
    public function setCellMargins($left='', $top='', $right='', $bottom='') {
3311
 
        if (($left !== '') AND ($left >= 0)) {
3312
 
            $this->cell_margin['L'] = $left;
3313
 
        }
3314
 
        if (($top !== '') AND ($top >= 0)) {
3315
 
            $this->cell_margin['T'] = $top;
3316
 
        }
3317
 
        if (($right !== '') AND ($right >= 0)) {
3318
 
            $this->cell_margin['R'] = $right;
3319
 
        }
3320
 
        if (($bottom !== '') AND ($bottom >= 0)) {
3321
 
            $this->cell_margin['B'] = $bottom;
3322
 
        }
3323
 
    }
3324
 
 
3325
 
    /**
3326
 
     * Get the internal Cell margin array.
3327
 
     * @return array of margin values
3328
 
     * @public
3329
 
     * @since 5.9.000 (2010-10-03)
3330
 
     * @see setCellMargins()
3331
 
     */
3332
 
    public function getCellMargins() {
3333
 
        return $this->cell_margin;
3334
 
    }
3335
 
 
3336
 
    /**
3337
 
     * Adjust the internal Cell padding array to take account of the line width.
3338
 
     * @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)))
3339
 
     * @return array of adjustments
3340
 
     * @public
3341
 
     * @since 5.9.000 (2010-10-03)
3342
 
     */
3343
 
    protected function adjustCellPadding($brd=0) {
3344
 
        if (empty($brd)) {
3345
 
            return;
3346
 
        }
3347
 
        if (is_string($brd)) {
3348
 
            // convert string to array
3349
 
            $slen = strlen($brd);
3350
 
            $newbrd = array();
3351
 
            for ($i = 0; $i < $slen; ++$i) {
3352
 
                $newbrd[$brd{$i}] = true;
3353
 
            }
3354
 
            $brd = $newbrd;
3355
 
        } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) {
3356
 
            $brd = array('LRTB' => true);
3357
 
        }
3358
 
        if (!is_array($brd)) {
3359
 
            return;
3360
 
        }
3361
 
        // store current cell padding
3362
 
        $cp = $this->cell_padding;
3363
 
        // select border mode
3364
 
        if (isset($brd['mode'])) {
3365
 
            $mode = $brd['mode'];
3366
 
            unset($brd['mode']);
3367
 
        } else {
3368
 
            $mode = 'normal';
3369
 
        }
3370
 
        // process borders
3371
 
        foreach ($brd as $border => $style) {
3372
 
            $line_width = $this->LineWidth;
3373
 
            if (is_array($style) AND isset($style['width'])) {
3374
 
                // get border width
3375
 
                $line_width = $style['width'];
3376
 
            }
3377
 
            $adj = 0; // line width inside the cell
3378
 
            switch ($mode) {
3379
 
                case 'ext': {
3380
 
                    $adj = 0;
3381
 
                    break;
3382
 
                }
3383
 
                case 'int': {
3384
 
                    $adj = $line_width;
3385
 
                    break;
3386
 
                }
3387
 
                case 'normal':
3388
 
                default: {
3389
 
                    $adj = ($line_width / 2);
3390
 
                    break;
3391
 
                }
3392
 
            }
3393
 
            // correct internal cell padding if required to avoid overlap between text and lines
3394
 
            if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) {
3395
 
                $this->cell_padding['T'] = $adj;
3396
 
            }
3397
 
            if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) {
3398
 
                $this->cell_padding['R'] = $adj;
3399
 
            }
3400
 
            if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) {
3401
 
                $this->cell_padding['B'] = $adj;
3402
 
            }
3403
 
            if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) {
3404
 
                $this->cell_padding['L'] = $adj;
3405
 
            }
3406
 
        }
3407
 
        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']));
3408
 
    }
3409
 
 
3410
 
    /**
3411
 
     * 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.
3412
 
     * @param $auto (boolean) Boolean indicating if mode should be on or off.
3413
 
     * @param $margin (float) Distance from the bottom of the page.
3414
 
     * @public
3415
 
     * @since 1.0
3416
 
     * @see Cell(), MultiCell(), AcceptPageBreak()
3417
 
     */
3418
 
    public function SetAutoPageBreak($auto, $margin=0) {
3419
 
        $this->AutoPageBreak = $auto;
3420
 
        $this->bMargin = $margin;
3421
 
        $this->PageBreakTrigger = $this->h - $margin;
3422
 
    }
3423
 
 
3424
 
    /**
3425
 
     * Defines the way the document is to be displayed by the viewer.
3426
 
     * @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>
3427
 
     * @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>
3428
 
     * @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>
3429
 
     * @public
3430
 
     * @since 1.2
3431
 
     */
3432
 
    public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
3433
 
        if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
3434
 
            $this->ZoomMode = $zoom;
3435
 
        } else {
3436
 
            $this->Error('Incorrect zoom display mode: '.$zoom);
3437
 
        }
3438
 
        switch ($layout) {
3439
 
            case 'default':
3440
 
            case 'single':
3441
 
            case 'SinglePage': {
3442
 
                $this->LayoutMode = 'SinglePage';
3443
 
                break;
3444
 
            }
3445
 
            case 'continuous':
3446
 
            case 'OneColumn': {
3447
 
                $this->LayoutMode = 'OneColumn';
3448
 
                break;
3449
 
            }
3450
 
            case 'two':
3451
 
            case 'TwoColumnLeft': {
3452
 
                $this->LayoutMode = 'TwoColumnLeft';
3453
 
                break;
3454
 
            }
3455
 
            case 'TwoColumnRight': {
3456
 
                $this->LayoutMode = 'TwoColumnRight';
3457
 
                break;
3458
 
            }
3459
 
            case 'TwoPageLeft': {
3460
 
                $this->LayoutMode = 'TwoPageLeft';
3461
 
                break;
3462
 
            }
3463
 
            case 'TwoPageRight': {
3464
 
                $this->LayoutMode = 'TwoPageRight';
3465
 
                break;
3466
 
            }
3467
 
            default: {
3468
 
                $this->LayoutMode = 'SinglePage';
3469
 
            }
3470
 
        }
3471
 
        switch ($mode) {
3472
 
            case 'UseNone': {
3473
 
                $this->PageMode = 'UseNone';
3474
 
                break;
3475
 
            }
3476
 
            case 'UseOutlines': {
3477
 
                $this->PageMode = 'UseOutlines';
3478
 
                break;
3479
 
            }
3480
 
            case 'UseThumbs': {
3481
 
                $this->PageMode = 'UseThumbs';
3482
 
                break;
3483
 
            }
3484
 
            case 'FullScreen': {
3485
 
                $this->PageMode = 'FullScreen';
3486
 
                break;
3487
 
            }
3488
 
            case 'UseOC': {
3489
 
                $this->PageMode = 'UseOC';
3490
 
                break;
3491
 
            }
3492
 
            case '': {
3493
 
                $this->PageMode = 'UseAttachments';
3494
 
                break;
3495
 
            }
3496
 
            default: {
3497
 
                $this->PageMode = 'UseNone';
3498
 
            }
3499
 
        }
3500
 
    }
3501
 
 
3502
 
    /**
3503
 
     * 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.
3504
 
     * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
3505
 
     * @param $compress (boolean) Boolean indicating if compression must be enabled.
3506
 
     * @public
3507
 
     * @since 1.4
3508
 
     */
3509
 
    public function SetCompression($compress) {
3510
 
        if (function_exists('gzcompress')) {
3511
 
            $this->compress = $compress ? true : false;
3512
 
        } else {
3513
 
            $this->compress = false;
3514
 
        }
3515
 
    }
3516
 
 
3517
 
    /**
3518
 
     * Turn on/off Unicode mode for document information dictionary (meta tags).
3519
 
     * This has effect only when unicode mode is set to false.
3520
 
     * @param $unicode (boolean) if true set the meta information in Unicode
3521
 
     * @since 5.9.027 (2010-12-01)
3522
 
     * @public
3523
 
     */
3524
 
    public function SetDocInfoUnicode($unicode=true) {
3525
 
        $this->docinfounicode = $unicode ? true : false;
3526
 
    }
3527
 
 
3528
 
    /**
3529
 
     * Defines the title of the document.
3530
 
     * @param $title (string) The title.
3531
 
     * @public
3532
 
     * @since 1.2
3533
 
     * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
3534
 
     */
3535
 
    public function SetTitle($title) {
3536
 
        $this->title = $title;
3537
 
    }
3538
 
 
3539
 
    /**
3540
 
     * Defines the subject of the document.
3541
 
     * @param $subject (string) The subject.
3542
 
     * @public
3543
 
     * @since 1.2
3544
 
     * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
3545
 
     */
3546
 
    public function SetSubject($subject) {
3547
 
        $this->subject = $subject;
3548
 
    }
3549
 
 
3550
 
    /**
3551
 
     * Defines the author of the document.
3552
 
     * @param $author (string) The name of the author.
3553
 
     * @public
3554
 
     * @since 1.2
3555
 
     * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
3556
 
     */
3557
 
    public function SetAuthor($author) {
3558
 
        $this->author = $author;
3559
 
    }
3560
 
 
3561
 
    /**
3562
 
     * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
3563
 
     * @param $keywords (string) The list of keywords.
3564
 
     * @public
3565
 
     * @since 1.2
3566
 
     * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
3567
 
     */
3568
 
    public function SetKeywords($keywords) {
3569
 
        $this->keywords = $keywords;
3570
 
    }
3571
 
 
3572
 
    /**
3573
 
     * Defines the creator of the document. This is typically the name of the application that generates the PDF.
3574
 
     * @param $creator (string) The name of the creator.
3575
 
     * @public
3576
 
     * @since 1.2
3577
 
     * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
3578
 
     */
3579
 
    public function SetCreator($creator) {
3580
 
        $this->creator = $creator;
3581
 
    }
3582
 
 
3583
 
    /**
3584
 
     * 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.
3585
 
     * 2004-06-11 :: Nicola Asuni : changed bold tag with strong
3586
 
     * @param $msg (string) The error message
3587
 
     * @public
3588
 
     * @since 1.0
3589
 
     */
3590
 
    public function Error($msg) {
3591
 
        // unset all class variables
3592
 
        $this->_destroy(true);
3593
 
        // exit program and print error
3594
 
        die('<strong>TCPDF ERROR: </strong>'.$msg);
3595
 
    }
3596
 
 
3597
 
    /**
3598
 
     * This method begins the generation of the PDF document.
3599
 
     * It is not necessary to call it explicitly because AddPage() does it automatically.
3600
 
     * Note: no page is created by this method
3601
 
     * @public
3602
 
     * @since 1.0
3603
 
     * @see AddPage(), Close()
3604
 
     */
3605
 
    public function Open() {
3606
 
        $this->state = 1;
3607
 
    }
3608
 
 
3609
 
    /**
3610
 
     * Terminates the PDF document.
3611
 
     * It is not necessary to call this method explicitly because Output() does it automatically.
3612
 
     * If the document contains no page, AddPage() is called to prevent from getting an invalid document.
3613
 
     * @public
3614
 
     * @since 1.0
3615
 
     * @see Open(), Output()
3616
 
     */
3617
 
    public function Close() {
3618
 
        if ($this->state == 3) {
3619
 
            return;
3620
 
        }
3621
 
        if ($this->page == 0) {
3622
 
            $this->AddPage();
3623
 
        }
3624
 
        // save current graphic settings
3625
 
        $gvars = $this->getGraphicVars();
3626
 
        $this->setEqualColumns();
3627
 
        $this->lastpage(true);
3628
 
        $this->SetAutoPageBreak(false);
3629
 
        $this->x = 0;
3630
 
        $this->y = $this->h - (1 / $this->k);
3631
 
        $this->lMargin = 0;
3632
 
        $this->_out('q');
3633
 
        $this->SetFont('helvetica', '', 1);
3634
 
        $this->setTextRenderingMode(0, false, false);
3635
 
        $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";
3636
 
        $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
3637
 
        $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
3638
 
        $this->_out('Q');
3639
 
        // restore graphic settings
3640
 
        $this->setGraphicVars($gvars);
3641
 
        // close page
3642
 
        $this->endPage();
3643
 
        // close document
3644
 
        $this->_enddoc();
3645
 
        // unset all class variables (except critical ones)
3646
 
        $this->_destroy(false);
3647
 
    }
3648
 
 
3649
 
    /**
3650
 
     * Move pointer at the specified document page and update page dimensions.
3651
 
     * @param $pnum (int) page number (1 ... numpages)
3652
 
     * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
3653
 
     * @public
3654
 
     * @since 2.1.000 (2008-01-07)
3655
 
     * @see getPage(), lastpage(), getNumPages()
3656
 
     */
3657
 
    public function setPage($pnum, $resetmargins=false) {
3658
 
        if (($pnum == $this->page) AND ($this->state == 2)) {
3659
 
            return;
3660
 
        }
3661
 
        if (($pnum > 0) AND ($pnum <= $this->numpages)) {
3662
 
            $this->state = 2;
3663
 
            // save current graphic settings
3664
 
            //$gvars = $this->getGraphicVars();
3665
 
            $oldpage = $this->page;
3666
 
            $this->page = $pnum;
3667
 
            $this->wPt = $this->pagedim[$this->page]['w'];
3668
 
            $this->hPt = $this->pagedim[$this->page]['h'];
3669
 
            $this->w = $this->pagedim[$this->page]['wk'];
3670
 
            $this->h = $this->pagedim[$this->page]['hk'];
3671
 
            $this->tMargin = $this->pagedim[$this->page]['tm'];
3672
 
            $this->bMargin = $this->pagedim[$this->page]['bm'];
3673
 
            $this->original_lMargin = $this->pagedim[$this->page]['olm'];
3674
 
            $this->original_rMargin = $this->pagedim[$this->page]['orm'];
3675
 
            $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
3676
 
            $this->CurOrientation = $this->pagedim[$this->page]['or'];
3677
 
            $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
3678
 
            // restore graphic settings
3679
 
            //$this->setGraphicVars($gvars);
3680
 
            if ($resetmargins) {
3681
 
                $this->lMargin = $this->pagedim[$this->page]['olm'];
3682
 
                $this->rMargin = $this->pagedim[$this->page]['orm'];
3683
 
                $this->SetY($this->tMargin);
3684
 
            } else {
3685
 
                // account for booklet mode
3686
 
                if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3687
 
                    $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
3688
 
                    $this->lMargin += $deltam;
3689
 
                    $this->rMargin -= $deltam;
3690
 
                }
3691
 
            }
3692
 
        } else {
3693
 
            $this->Error('Wrong page number on setPage() function: '.$pnum);
3694
 
        }
3695
 
    }
3696
 
 
3697
 
    /**
3698
 
     * Reset pointer to the last document page.
3699
 
     * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
3700
 
     * @public
3701
 
     * @since 2.0.000 (2008-01-04)
3702
 
     * @see setPage(), getPage(), getNumPages()
3703
 
     */
3704
 
    public function lastPage($resetmargins=false) {
3705
 
        $this->setPage($this->getNumPages(), $resetmargins);
3706
 
    }
3707
 
 
3708
 
    /**
3709
 
     * Get current document page number.
3710
 
     * @return int page number
3711
 
     * @public
3712
 
     * @since 2.1.000 (2008-01-07)
3713
 
     * @see setPage(), lastpage(), getNumPages()
3714
 
     */
3715
 
    public function getPage() {
3716
 
        return $this->page;
3717
 
    }
3718
 
 
3719
 
    /**
3720
 
     * Get the total number of insered pages.
3721
 
     * @return int number of pages
3722
 
     * @public
3723
 
     * @since 2.1.000 (2008-01-07)
3724
 
     * @see setPage(), getPage(), lastpage()
3725
 
     */
3726
 
    public function getNumPages() {
3727
 
        return $this->numpages;
3728
 
    }
3729
 
 
3730
 
    /**
3731
 
     * Adds a new TOC (Table Of Content) page to the document.
3732
 
     * @param $orientation (string) page orientation.
3733
 
     * @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().
3734
 
     * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
3735
 
     * @public
3736
 
     * @since 5.0.001 (2010-05-06)
3737
 
     * @see AddPage(), startPage(), endPage(), endTOCPage()
3738
 
     */
3739
 
    public function addTOCPage($orientation='', $format='', $keepmargins=false) {
3740
 
        $this->AddPage($orientation, $format, $keepmargins, true);
3741
 
    }
3742
 
 
3743
 
    /**
3744
 
     * Terminate the current TOC (Table Of Content) page
3745
 
     * @public
3746
 
     * @since 5.0.001 (2010-05-06)
3747
 
     * @see AddPage(), startPage(), endPage(), addTOCPage()
3748
 
     */
3749
 
    public function endTOCPage() {
3750
 
        $this->endPage(true);
3751
 
    }
3752
 
 
3753
 
    /**
3754
 
     * 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).
3755
 
     * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards.
3756
 
     * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3757
 
     * @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().
3758
 
     * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
3759
 
     * @param $tocpage (boolean) if true set the tocpage state to true (the added page will be used to display Table Of Content).
3760
 
     * @public
3761
 
     * @since 1.0
3762
 
     * @see startPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3763
 
     */
3764
 
    public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
3765
 
        if ($this->inxobj) {
3766
 
            // we are inside an XObject template
3767
 
            return;
3768
 
        }
3769
 
        if (!isset($this->original_lMargin) OR $keepmargins) {
3770
 
            $this->original_lMargin = $this->lMargin;
3771
 
        }
3772
 
        if (!isset($this->original_rMargin) OR $keepmargins) {
3773
 
            $this->original_rMargin = $this->rMargin;
3774
 
        }
3775
 
        // terminate previous page
3776
 
        $this->endPage();
3777
 
        // start new page
3778
 
        $this->startPage($orientation, $format, $tocpage);
3779
 
    }
3780
 
 
3781
 
    /**
3782
 
     * Terminate the current page
3783
 
     * @param $tocpage (boolean) if true set the tocpage state to false (end the page used to display Table Of Content).
3784
 
     * @public
3785
 
     * @since 4.2.010 (2008-11-14)
3786
 
     * @see AddPage(), startPage(), addTOCPage(), endTOCPage()
3787
 
     */
3788
 
    public function endPage($tocpage=false) {
3789
 
        // check if page is already closed
3790
 
        if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
3791
 
            return;
3792
 
        }
3793
 
        $this->InFooter = true;
3794
 
        // print page footer
3795
 
        $this->setFooter();
3796
 
        // close page
3797
 
        $this->_endpage();
3798
 
        // mark page as closed
3799
 
        $this->pageopen[$this->page] = false;
3800
 
        $this->InFooter = false;
3801
 
        if ($tocpage) {
3802
 
            $this->tocpage = false;
3803
 
        }
3804
 
    }
3805
 
 
3806
 
    /**
3807
 
     * Starts a new page to the document. The page must be closed using the endPage() function.
3808
 
     * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
3809
 
     * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3810
 
     * @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().
3811
 
     * @param $tocpage (boolean) if true the page is designated to contain the Table-Of-Content.
3812
 
     * @since 4.2.010 (2008-11-14)
3813
 
     * @see AddPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3814
 
     * @public
3815
 
     */
3816
 
    public function startPage($orientation='', $format='', $tocpage=false) {
3817
 
        if ($tocpage) {
3818
 
            $this->tocpage = true;
3819
 
        }
3820
 
        if ($this->numpages > $this->page) {
3821
 
            // this page has been already added
3822
 
            $this->setPage($this->page + 1);
3823
 
            $this->SetY($this->tMargin);
3824
 
            return;
3825
 
        }
3826
 
        // start a new page
3827
 
        if ($this->state == 0) {
3828
 
            $this->Open();
3829
 
        }
3830
 
        ++$this->numpages;
3831
 
        $this->swapMargins($this->booklet);
3832
 
        // save current graphic settings
3833
 
        $gvars = $this->getGraphicVars();
3834
 
        // start new page
3835
 
        $this->_beginpage($orientation, $format);
3836
 
        // mark page as open
3837
 
        $this->pageopen[$this->page] = true;
3838
 
        // restore graphic settings
3839
 
        $this->setGraphicVars($gvars);
3840
 
        // mark this point
3841
 
        $this->setPageMark();
3842
 
        // print page header
3843
 
        $this->setHeader();
3844
 
        // restore graphic settings
3845
 
        $this->setGraphicVars($gvars);
3846
 
        // mark this point
3847
 
        $this->setPageMark();
3848
 
        // print table header (if any)
3849
 
        $this->setTableHeader();
3850
 
        // set mark for empty page check
3851
 
        $this->emptypagemrk[$this->page]= $this->pagelen[$this->page];
3852
 
    }
3853
 
 
3854
 
    /**
3855
 
     * Set start-writing mark on current page stream used to put borders and fills.
3856
 
     * Borders and fills are always created after content and inserted on the position marked by this method.
3857
 
     * This function must be called after calling Image() function for a background image.
3858
 
     * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
3859
 
     * @public
3860
 
     * @since 4.0.016 (2008-07-30)
3861
 
     */
3862
 
    public function setPageMark() {
3863
 
        $this->intmrk[$this->page] = $this->pagelen[$this->page];
3864
 
        $this->bordermrk[$this->page] = $this->intmrk[$this->page];
3865
 
        $this->setContentMark();
3866
 
    }
3867
 
 
3868
 
    /**
3869
 
     * Set start-writing mark on selected page.
3870
 
     * Borders and fills are always created after content and inserted on the position marked by this method.
3871
 
     * @param $page (int) page number (default is the current page)
3872
 
     * @protected
3873
 
     * @since 4.6.021 (2009-07-20)
3874
 
     */
3875
 
    protected function setContentMark($page=0) {
3876
 
        if ($page <= 0) {
3877
 
            $page = $this->page;
3878
 
        }
3879
 
        if (isset($this->footerlen[$page])) {
3880
 
            $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
3881
 
        } else {
3882
 
            $this->cntmrk[$page] = $this->pagelen[$page];
3883
 
        }
3884
 
    }
3885
 
 
3886
 
    /**
3887
 
     * Set header data.
3888
 
     * @param $ln (string) header image logo
3889
 
     * @param $lw (string) header image logo width in mm
3890
 
     * @param $ht (string) string to print as title on document header
3891
 
     * @param $hs (string) string to print on document header
3892
 
     * @public
3893
 
     */
3894
 
    public function setHeaderData($ln='', $lw=0, $ht='', $hs='') {
3895
 
        $this->header_logo = $ln;
3896
 
        $this->header_logo_width = $lw;
3897
 
        $this->header_title = $ht;
3898
 
        $this->header_string = $hs;
3899
 
    }
3900
 
 
3901
 
    /**
3902
 
     * Returns header data:
3903
 
     * <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>
3904
 
     * @return array()
3905
 
     * @public
3906
 
     * @since 4.0.012 (2008-07-24)
3907
 
     */
3908
 
    public function getHeaderData() {
3909
 
        $ret = array();
3910
 
        $ret['logo'] = $this->header_logo;
3911
 
        $ret['logo_width'] = $this->header_logo_width;
3912
 
        $ret['title'] = $this->header_title;
3913
 
        $ret['string'] = $this->header_string;
3914
 
        return $ret;
3915
 
    }
3916
 
 
3917
 
    /**
3918
 
     * Set header margin.
3919
 
     * (minimum distance between header and top page margin)
3920
 
     * @param $hm (int) distance in user units
3921
 
     * @public
3922
 
     */
3923
 
    public function setHeaderMargin($hm=10) {
3924
 
        $this->header_margin = $hm;
3925
 
    }
3926
 
 
3927
 
    /**
3928
 
     * Returns header margin in user units.
3929
 
     * @return float
3930
 
     * @since 4.0.012 (2008-07-24)
3931
 
     * @public
3932
 
     */
3933
 
    public function getHeaderMargin() {
3934
 
        return $this->header_margin;
3935
 
    }
3936
 
 
3937
 
    /**
3938
 
     * Set footer margin.
3939
 
     * (minimum distance between footer and bottom page margin)
3940
 
     * @param $fm (int) distance in user units
3941
 
     * @public
3942
 
     */
3943
 
    public function setFooterMargin($fm=10) {
3944
 
        $this->footer_margin = $fm;
3945
 
    }
3946
 
 
3947
 
    /**
3948
 
     * Returns footer margin in user units.
3949
 
     * @return float
3950
 
     * @since 4.0.012 (2008-07-24)
3951
 
     * @public
3952
 
     */
3953
 
    public function getFooterMargin() {
3954
 
        return $this->footer_margin;
3955
 
    }
3956
 
    /**
3957
 
     * Set a flag to print page header.
3958
 
     * @param $val (boolean) set to true to print the page header (default), false otherwise.
3959
 
     * @public
3960
 
     */
3961
 
    public function setPrintHeader($val=true) {
3962
 
        $this->print_header = $val;
3963
 
    }
3964
 
 
3965
 
    /**
3966
 
     * Set a flag to print page footer.
3967
 
     * @param $val (boolean) set to true to print the page footer (default), false otherwise.
3968
 
     * @public
3969
 
     */
3970
 
    public function setPrintFooter($val=true) {
3971
 
        $this->print_footer = $val;
3972
 
    }
3973
 
 
3974
 
    /**
3975
 
     * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
3976
 
     * @return float
3977
 
     * @public
3978
 
     */
3979
 
    public function getImageRBX() {
3980
 
        return $this->img_rb_x;
3981
 
    }
3982
 
 
3983
 
    /**
3984
 
     * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
3985
 
     * @return float
3986
 
     * @public
3987
 
     */
3988
 
    public function getImageRBY() {
3989
 
        return $this->img_rb_y;
3990
 
    }
3991
 
 
3992
 
    /**
3993
 
     * This method is used to render the page header.
3994
 
     * It is automatically called by AddPage() and could be overwritten in your own inherited class.
3995
 
     * @public
3996
 
     */
3997
 
    public function Header() {
3998
 
        $ormargins = $this->getOriginalMargins();
3999
 
        $headerfont = $this->getHeaderFont();
4000
 
        $headerdata = $this->getHeaderData();
4001
 
        if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
4002
 
            $imgtype = $this->getImageFileType(K_PATH_IMAGES.$headerdata['logo']);
4003
 
            if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
4004
 
                $this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
4005
 
            } elseif ($imgtype == 'svg') {
4006
 
                $this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
4007
 
            } else {
4008
 
                $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
4009
 
            }
4010
 
            $imgy = $this->getImageRBY();
4011
 
        } else {
4012
 
            $imgy = $this->GetY();
4013
 
        }
4014
 
        $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
4015
 
        // set starting margin for text data cell
4016
 
        if ($this->getRTL()) {
4017
 
            $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
4018
 
        } else {
4019
 
            $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
4020
 
        }
4021
 
        $this->SetTextColor(0, 0, 0);
4022
 
        // header title
4023
 
        $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
4024
 
        $this->SetX($header_x);
4025
 
        $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
4026
 
        // header string
4027
 
        $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
4028
 
        $this->SetX($header_x);
4029
 
        $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false);
4030
 
        // print an ending header line
4031
 
        $this->SetLineStyle(array('width' => 0.85 / $this->getScaleFactor(), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
4032
 
        $this->SetY((2.835 / $this->getScaleFactor()) + max($imgy, $this->GetY()));
4033
 
        if ($this->getRTL()) {
4034
 
            $this->SetX($ormargins['right']);
4035
 
        } else {
4036
 
            $this->SetX($ormargins['left']);
4037
 
        }
4038
 
        $this->Cell(0, 0, '', 'T', 0, 'C');
4039
 
    }
4040
 
 
4041
 
    /**
4042
 
     * This method is used to render the page footer.
4043
 
     * It is automatically called by AddPage() and could be overwritten in your own inherited class.
4044
 
     * @public
4045
 
     */
4046
 
    public function Footer() {
4047
 
        $cur_y = $this->GetY();
4048
 
        $ormargins = $this->getOriginalMargins();
4049
 
        $this->SetTextColor(0, 0, 0);
4050
 
        //set style for cell border
4051
 
        $line_width = 0.85 / $this->getScaleFactor();
4052
 
        $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
4053
 
        //print document barcode
4054
 
        $barcode = $this->getBarcode();
4055
 
        if (!empty($barcode)) {
4056
 
            $this->Ln($line_width);
4057
 
            $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right']) / 3);
4058
 
            $style = array(
4059
 
                'position' => $this->rtl?'R':'L',
4060
 
                'align' => $this->rtl?'R':'L',
4061
 
                'stretch' => false,
4062
 
                'fitwidth' => true,
4063
 
                'cellfitalign' => '',
4064
 
                'border' => false,
4065
 
                'padding' => 0,
4066
 
                'fgcolor' => array(0,0,0),
4067
 
                'bgcolor' => false,
4068
 
                'text' => false
4069
 
            );
4070
 
            $this->write1DBarcode($barcode, 'C128B', '', $cur_y + $line_width, '', (($this->getFooterMargin() / 3) - $line_width), 0.3, $style, '');
4071
 
        }
4072
 
        if (empty($this->pagegroups)) {
4073
 
            $pagenumtxt = $this->l['w_page'].' '.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
4074
 
        } else {
4075
 
            $pagenumtxt = $this->l['w_page'].' '.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
4076
 
        }
4077
 
        $this->SetY($cur_y);
4078
 
        //Print page number
4079
 
        if ($this->getRTL()) {
4080
 
            $this->SetX($ormargins['right']);
4081
 
            $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
4082
 
        } else {
4083
 
            $this->SetX($ormargins['left']);
4084
 
            $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R');
4085
 
        }
4086
 
    }
4087
 
 
4088
 
    /**
4089
 
     * This method is used to render the page header.
4090
 
     * @protected
4091
 
     * @since 4.0.012 (2008-07-24)
4092
 
     */
4093
 
    protected function setHeader() {
4094
 
        if ($this->print_header) {
4095
 
            $this->setGraphicVars($this->default_graphic_vars);
4096
 
            $temp_thead = $this->thead;
4097
 
            $temp_theadMargins = $this->theadMargins;
4098
 
            $lasth = $this->lasth;
4099
 
            $this->_out('q');
4100
 
            $this->rMargin = $this->original_rMargin;
4101
 
            $this->lMargin = $this->original_lMargin;
4102
 
            $this->SetCellPadding(0);
4103
 
            //set current position
4104
 
            if ($this->rtl) {
4105
 
                $this->SetXY($this->original_rMargin, $this->header_margin);
4106
 
            } else {
4107
 
                $this->SetXY($this->original_lMargin, $this->header_margin);
4108
 
            }
4109
 
            $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
4110
 
            $this->Header();
4111
 
            //restore position
4112
 
            if ($this->rtl) {
4113
 
                $this->SetXY($this->original_rMargin, $this->tMargin);
4114
 
            } else {
4115
 
                $this->SetXY($this->original_lMargin, $this->tMargin);
4116
 
            }
4117
 
            $this->_out('Q');
4118
 
            $this->lasth = $lasth;
4119
 
            $this->thead = $temp_thead;
4120
 
            $this->theadMargins = $temp_theadMargins;
4121
 
            $this->newline = false;
4122
 
        }
4123
 
    }
4124
 
 
4125
 
    /**
4126
 
     * This method is used to render the page footer.
4127
 
     * @protected
4128
 
     * @since 4.0.012 (2008-07-24)
4129
 
     */
4130
 
    protected function setFooter() {
4131
 
        //Page footer
4132
 
        // save current graphic settings
4133
 
        $gvars = $this->getGraphicVars();
4134
 
        // mark this point
4135
 
        $this->footerpos[$this->page] = $this->pagelen[$this->page];
4136
 
        $this->_out("\n");
4137
 
        if ($this->print_footer) {
4138
 
            $this->setGraphicVars($this->default_graphic_vars);
4139
 
            $this->current_column = 0;
4140
 
            $this->num_columns = 1;
4141
 
            $temp_thead = $this->thead;
4142
 
            $temp_theadMargins = $this->theadMargins;
4143
 
            $lasth = $this->lasth;
4144
 
            $this->_out('q');
4145
 
            $this->rMargin = $this->original_rMargin;
4146
 
            $this->lMargin = $this->original_lMargin;
4147
 
            $this->SetCellPadding(0);
4148
 
            //set current position
4149
 
            $footer_y = $this->h - $this->footer_margin;
4150
 
            if ($this->rtl) {
4151
 
                $this->SetXY($this->original_rMargin, $footer_y);
4152
 
            } else {
4153
 
                $this->SetXY($this->original_lMargin, $footer_y);
4154
 
            }
4155
 
            $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
4156
 
            $this->Footer();
4157
 
            //restore position
4158
 
            if ($this->rtl) {
4159
 
                $this->SetXY($this->original_rMargin, $this->tMargin);
4160
 
            } else {
4161
 
                $this->SetXY($this->original_lMargin, $this->tMargin);
4162
 
            }
4163
 
            $this->_out('Q');
4164
 
            $this->lasth = $lasth;
4165
 
            $this->thead = $temp_thead;
4166
 
            $this->theadMargins = $temp_theadMargins;
4167
 
        }
4168
 
        // restore graphic settings
4169
 
        $this->setGraphicVars($gvars);
4170
 
        $this->current_column = $gvars['current_column'];
4171
 
        $this->num_columns = $gvars['num_columns'];
4172
 
        // calculate footer length
4173
 
        $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
4174
 
    }
4175
 
 
4176
 
    /**
4177
 
     * This method is used to render the table header on new page (if any).
4178
 
     * @protected
4179
 
     * @since 4.5.030 (2009-03-25)
4180
 
     */
4181
 
    protected function setTableHeader() {
4182
 
        if ($this->num_columns > 1) {
4183
 
            // multi column mode
4184
 
            return;
4185
 
        }
4186
 
        if (isset($this->theadMargins['top'])) {
4187
 
            // restore the original top-margin
4188
 
            $this->tMargin = $this->theadMargins['top'];
4189
 
            $this->pagedim[$this->page]['tm'] = $this->tMargin;
4190
 
            $this->y = $this->tMargin;
4191
 
        }
4192
 
        if (!$this->empty_string($this->thead) AND (!$this->inthead)) {
4193
 
            // set margins
4194
 
            $prev_lMargin = $this->lMargin;
4195
 
            $prev_rMargin = $this->rMargin;
4196
 
            $prev_cell_padding = $this->cell_padding;
4197
 
            $this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']);
4198
 
            $this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']);
4199
 
            $this->cell_padding = $this->theadMargins['cell_padding'];
4200
 
            if ($this->rtl) {
4201
 
                $this->x = $this->w - $this->rMargin;
4202
 
            } else {
4203
 
                $this->x = $this->lMargin;
4204
 
            }
4205
 
            // print table header
4206
 
            $this->writeHTML($this->thead, false, false, false, false, '');
4207
 
            // set new top margin to skip the table headers
4208
 
            if (!isset($this->theadMargins['top'])) {
4209
 
                $this->theadMargins['top'] = $this->tMargin;
4210
 
            }
4211
 
            // store end of header position
4212
 
            if (!isset($this->columns[0]['th'])) {
4213
 
                $this->columns[0]['th'] = array();
4214
 
            }
4215
 
            $this->columns[0]['th']['\''.$this->page.'\''] = $this->y;
4216
 
            $this->tMargin = $this->y;
4217
 
            $this->pagedim[$this->page]['tm'] = $this->tMargin;
4218
 
            $this->lasth = 0;
4219
 
            $this->lMargin = $prev_lMargin;
4220
 
            $this->rMargin = $prev_rMargin;
4221
 
            $this->cell_padding = $prev_cell_padding;
4222
 
        }
4223
 
    }
4224
 
 
4225
 
    /**
4226
 
     * Returns the current page number.
4227
 
     * @return int page number
4228
 
     * @public
4229
 
     * @since 1.0
4230
 
     * @see AliasNbPages(), getAliasNbPages()
4231
 
     */
4232
 
    public function PageNo() {
4233
 
        return $this->page;
4234
 
    }
4235
 
 
4236
 
    /**
4237
 
     * Defines a new spot color.
4238
 
     * It can be expressed in RGB components or gray scale.
4239
 
     * The method can be called before the first page is created and the value is retained from page to page.
4240
 
     * @param $name (string) name of the spot color
4241
 
     * @param $c (int) Cyan color for CMYK. Value between 0 and 100
4242
 
     * @param $m (int) Magenta color for CMYK. Value between 0 and 100
4243
 
     * @param $y (int) Yellow color for CMYK. Value between 0 and 100
4244
 
     * @param $k (int) Key (Black) color for CMYK. Value between 0 and 100
4245
 
     * @public
4246
 
     * @since 4.0.024 (2008-09-12)
4247
 
     * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
4248
 
     */
4249
 
    public function AddSpotColor($name, $c, $m, $y, $k) {
4250
 
        if (!isset($this->spot_colors[$name])) {
4251
 
            $i = 1 + count($this->spot_colors);
4252
 
            $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
4253
 
        }
4254
 
        $color = preg_replace('/[\s]*/', '', $name); // remove extra spaces
4255
 
        $color = strtolower($color);
4256
 
        if (!isset($this->spotcolor[$color])) {
4257
 
            $this->spotcolor[$color] = array($c, $m, $y, $k, $name);
4258
 
        }
4259
 
    }
4260
 
 
4261
 
    /**
4262
 
     * Defines the color used for all drawing operations (lines, rectangles and cell borders).
4263
 
     * It can be expressed in RGB, CMYK or GRAY SCALE components.
4264
 
     * The method can be called before the first page is created and the value is retained from page to page.
4265
 
     * @param $color (array) array of colors
4266
 
     * @param $ret (boolean) if true do not send the PDF command.
4267
 
     * @return string the PDF command
4268
 
     * @public
4269
 
     * @since 3.1.000 (2008-06-11)
4270
 
     * @see SetDrawColor()
4271
 
     */
4272
 
    public function SetDrawColorArray($color, $ret=false) {
4273
 
        if (is_array($color)) {
4274
 
            $color = array_values($color);
4275
 
            $r = isset($color[0]) ? $color[0] : -1;
4276
 
            $g = isset($color[1]) ? $color[1] : -1;
4277
 
            $b = isset($color[2]) ? $color[2] : -1;
4278
 
            $k = isset($color[3]) ? $color[3] : -1;
4279
 
            $name = isset($color[4]) ? $color[4] : ''; // spot color name
4280
 
            if ($r >= 0) {
4281
 
                return $this->SetDrawColor($r, $g, $b, $k, $ret, $name);
4282
 
            }
4283
 
        }
4284
 
        return '';
4285
 
    }
4286
 
 
4287
 
    /**
4288
 
     * 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.
4289
 
     * @param $col1 (int) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
4290
 
     * @param $col2 (int) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
4291
 
     * @param $col3 (int) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
4292
 
     * @param $col4 (int) KEY (BLACK) color for CMYK (0-100).
4293
 
     * @param $ret (boolean) if true do not send the command.
4294
 
     * @param $name (string) spot color name (if any)
4295
 
     * @return string the PDF command
4296
 
     * @public
4297
 
     * @since 1.3
4298
 
     * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
4299
 
     */
4300
 
    public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4301
 
        // set default values
4302
 
        if (!is_numeric($col1)) {
4303
 
            $col1 = 0;
4304
 
        }
4305
 
        if (!is_numeric($col2)) {
4306
 
            $col2 = -1;
4307
 
        }
4308
 
        if (!is_numeric($col3)) {
4309
 
            $col3 = -1;
4310
 
        }
4311
 
        if (!is_numeric($col4)) {
4312
 
            $col4 = -1;
4313
 
        }
4314
 
        //Set color for all stroking operations
4315
 
        if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
4316
 
            // Grey scale
4317
 
            $this->DrawColor = sprintf('%.3F G', ($col1 / 255));
4318
 
            $this->strokecolor = array('G' => $col1);
4319
 
        } elseif ($col4 == -1) {
4320
 
            // RGB
4321
 
            $this->DrawColor = sprintf('%.3F %.3F %.3F RG', ($col1 / 255), ($col2 / 255), ($col3 / 255));
4322
 
            $this->strokecolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
4323
 
        } elseif (empty($name)) {
4324
 
            // CMYK
4325
 
            $this->DrawColor = sprintf('%.3F %.3F %.3F %.3F K', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
4326
 
            $this->strokecolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
4327
 
        } else {
4328
 
            // SPOT COLOR
4329
 
            $this->AddSpotColor($name, $col1, $col2, $col3, $col4);
4330
 
            $this->DrawColor = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$name]['i'], 1);
4331
 
            $this->strokecolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
4332
 
        }
4333
 
        if ($this->page > 0) {
4334
 
            if (!$ret) {
4335
 
                $this->_out($this->DrawColor);
4336
 
            }
4337
 
            return $this->DrawColor;
4338
 
        }
4339
 
        return '';
4340
 
    }
4341
 
 
4342
 
    /**
4343
 
     * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
4344
 
     * @param $name (string) name of the spot color
4345
 
     * @param $tint (int) the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
4346
 
     * @public
4347
 
     * @since 4.0.024 (2008-09-12)
4348
 
     * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
4349
 
     */
4350
 
    public function SetDrawSpotColor($name, $tint=100) {
4351
 
        if (!isset($this->spot_colors[$name])) {
4352
 
            $this->Error('Undefined spot color: '.$name);
4353
 
        }
4354
 
        $this->DrawColor = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$name]['i'], ($tint / 100));
4355
 
        $this->strokecolor = array('C' => $this->spot_colors[$name]['c'], 'M' => $this->spot_colors[$name]['m'], 'Y' => $this->spot_colors[$name]['y'], 'K' => $this->spot_colors[$name]['k'], 'name' => $name);
4356
 
        if ($this->page > 0) {
4357
 
            $this->_out($this->DrawColor);
4358
 
        }
4359
 
    }
4360
 
 
4361
 
    /**
4362
 
     * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
4363
 
     * It can be expressed in RGB, CMYK or GRAY SCALE components.
4364
 
     * The method can be called before the first page is created and the value is retained from page to page.
4365
 
     * @param $color (array) array of colors
4366
 
     * @param $ret (boolean) if true do not send the PDF command.
4367
 
     * @public
4368
 
     * @since 3.1.000 (2008-6-11)
4369
 
     * @see SetFillColor()
4370
 
     */
4371
 
    public function SetFillColorArray($color, $ret=false) {
4372
 
        if (is_array($color)) {
4373
 
            $color = array_values($color);
4374
 
            $r = isset($color[0]) ? $color[0] : -1;
4375
 
            $g = isset($color[1]) ? $color[1] : -1;
4376
 
            $b = isset($color[2]) ? $color[2] : -1;
4377
 
            $k = isset($color[3]) ? $color[3] : -1;
4378
 
            $name = isset($color[4]) ? $color[4] : ''; // spot color name
4379
 
            if ($r >= 0) {
4380
 
                $this->SetFillColor($r, $g, $b, $k, $ret, $name);
4381
 
            }
4382
 
        }
4383
 
    }
4384
 
 
4385
 
    /**
4386
 
     * 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.
4387
 
     * @param $col1 (int) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
4388
 
     * @param $col2 (int) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
4389
 
     * @param $col3 (int) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
4390
 
     * @param $col4 (int) KEY (BLACK) color for CMYK (0-100).
4391
 
     * @param $ret (boolean) if true do not send the command.
4392
 
     * @param $name (string) spot color name (if any)
4393
 
     * @return string the PDF command
4394
 
     * @public
4395
 
     * @since 1.3
4396
 
     * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
4397
 
     */
4398
 
    public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4399
 
        // set default values
4400
 
        if (!is_numeric($col1)) {
4401
 
            $col1 = 0;
4402
 
        }
4403
 
        if (!is_numeric($col2)) {
4404
 
            $col2 = -1;
4405
 
        }
4406
 
        if (!is_numeric($col3)) {
4407
 
            $col3 = -1;
4408
 
        }
4409
 
        if (!is_numeric($col4)) {
4410
 
            $col4 = -1;
4411
 
        }
4412
 
        //Set color for all filling operations
4413
 
        if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
4414
 
            // Grey scale
4415
 
            $this->FillColor = sprintf('%.3F g', ($col1 / 255));
4416
 
            $this->bgcolor = array('G' => $col1);
4417
 
        } elseif ($col4 == -1) {
4418
 
            // RGB
4419
 
            $this->FillColor = sprintf('%.3F %.3F %.3F rg', ($col1 / 255), ($col2 / 255), ($col3 / 255));
4420
 
            $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
4421
 
        } elseif (empty($name)) {
4422
 
            // CMYK
4423
 
            $this->FillColor = sprintf('%.3F %.3F %.3F %.3F k', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
4424
 
            $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
4425
 
        } else {
4426
 
            // SPOT COLOR
4427
 
            $this->AddSpotColor($name, $col1, $col2, $col3, $col4);
4428
 
            $this->FillColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], 1);
4429
 
            $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
4430
 
        }
4431
 
        $this->ColorFlag = ($this->FillColor != $this->TextColor);
4432
 
        if ($this->page > 0) {
4433
 
            if (!$ret) {
4434
 
                $this->_out($this->FillColor);
4435
 
            }
4436
 
            return $this->FillColor;
4437
 
        }
4438
 
        return '';
4439
 
    }
4440
 
 
4441
 
    /**
4442
 
     * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
4443
 
     * @param $name (string) name of the spot color
4444
 
     * @param $tint (int) the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
4445
 
     * @public
4446
 
     * @since 4.0.024 (2008-09-12)
4447
 
     * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
4448
 
     */
4449
 
    public function SetFillSpotColor($name, $tint=100) {
4450
 
        if (!isset($this->spot_colors[$name])) {
4451
 
            $this->Error('Undefined spot color: '.$name);
4452
 
        }
4453
 
        $this->FillColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], ($tint / 100));
4454
 
        $this->bgcolor = array('C' => $this->spot_colors[$name]['c'], 'M' => $this->spot_colors[$name]['m'], 'Y' => $this->spot_colors[$name]['y'], 'K' => $this->spot_colors[$name]['k'], 'name' => $name);
4455
 
        $this->ColorFlag = ($this->FillColor != $this->TextColor);
4456
 
        if ($this->page > 0) {
4457
 
            $this->_out($this->FillColor);
4458
 
        }
4459
 
    }
4460
 
 
4461
 
    /**
4462
 
     * Defines the color used for text. It can be expressed in RGB components or gray scale.
4463
 
     * The method can be called before the first page is created and the value is retained from page to page.
4464
 
     * @param $color (array) array of colors
4465
 
     * @param $ret (boolean) if true do not send the PDF command.
4466
 
     * @public
4467
 
     * @since 3.1.000 (2008-6-11)
4468
 
     * @see SetFillColor()
4469
 
     */
4470
 
    public function SetTextColorArray($color, $ret=false) {
4471
 
        if (is_array($color)) {
4472
 
            $color = array_values($color);
4473
 
            $r = isset($color[0]) ? $color[0] : -1;
4474
 
            $g = isset($color[1]) ? $color[1] : -1;
4475
 
            $b = isset($color[2]) ? $color[2] : -1;
4476
 
            $k = isset($color[3]) ? $color[3] : -1;
4477
 
            $name = isset($color[4]) ? $color[4] : ''; // spot color name
4478
 
            if ($r >= 0) {
4479
 
                $this->SetTextColor($r, $g, $b, $k, $ret, $name);
4480
 
            }
4481
 
        }
4482
 
    }
4483
 
 
4484
 
    /**
4485
 
     * 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.
4486
 
     * @param $col1 (int) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
4487
 
     * @param $col2 (int) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
4488
 
     * @param $col3 (int) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
4489
 
     * @param $col4 (int) KEY (BLACK) color for CMYK (0-100).
4490
 
     * @param $ret (boolean) if true do not send the command.
4491
 
     * @param $name (string) spot color name (if any)
4492
 
     * @public
4493
 
     * @since 1.3
4494
 
     * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
4495
 
     */
4496
 
    public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4497
 
        // set default values
4498
 
        if (!is_numeric($col1)) {
4499
 
            $col1 = 0;
4500
 
        }
4501
 
        if (!is_numeric($col2)) {
4502
 
            $col2 = -1;
4503
 
        }
4504
 
        if (!is_numeric($col3)) {
4505
 
            $col3 = -1;
4506
 
        }
4507
 
        if (!is_numeric($col4)) {
4508
 
            $col4 = -1;
4509
 
        }
4510
 
        //Set color for text
4511
 
        if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
4512
 
            // Grey scale
4513
 
            $this->TextColor = sprintf('%.3F g', ($col1 / 255));
4514
 
            $this->fgcolor = array('G' => $col1);
4515
 
        } elseif ($col4 == -1) {
4516
 
            // RGB
4517
 
            $this->TextColor = sprintf('%.3F %.3F %.3F rg', ($col1 / 255), ($col2 / 255), ($col3 / 255));
4518
 
            $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
4519
 
        } elseif (empty($name)) {
4520
 
            // CMYK
4521
 
            $this->TextColor = sprintf('%.3F %.3F %.3F %.3F k', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
4522
 
            $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
4523
 
        } else {
4524
 
            // SPOT COLOR
4525
 
            $this->AddSpotColor($name, $col1, $col2, $col3, $col4);
4526
 
            $this->TextColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], 1);
4527
 
            $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
4528
 
        }
4529
 
        $this->ColorFlag = ($this->FillColor != $this->TextColor);
4530
 
    }
4531
 
 
4532
 
    /**
4533
 
     * Defines the spot color used for text.
4534
 
     * @param $name (string) name of the spot color
4535
 
     * @param $tint (int) the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
4536
 
     * @public
4537
 
     * @since 4.0.024 (2008-09-12)
4538
 
     * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
4539
 
     */
4540
 
    public function SetTextSpotColor($name, $tint=100) {
4541
 
        if (!isset($this->spot_colors[$name])) {
4542
 
            $this->Error('Undefined spot color: '.$name);
4543
 
        }
4544
 
        $this->TextColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], ($tint / 100));
4545
 
        $this->fgcolor = array('C' => $this->spot_colors[$name]['c'], 'M' => $this->spot_colors[$name]['m'], 'Y' => $this->spot_colors[$name]['y'], 'K' => $this->spot_colors[$name]['k'], 'name' => $name);
4546
 
        $this->ColorFlag = ($this->FillColor != $this->TextColor);
4547
 
        if ($this->page > 0) {
4548
 
            $this->_out($this->TextColor);
4549
 
        }
4550
 
    }
4551
 
 
4552
 
    /**
4553
 
     * Returns the length of a string in user unit. A font must be selected.<br>
4554
 
     * @param $s (string) The string whose length is to be computed
4555
 
     * @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.
4556
 
     * @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-trough</li><li>O: overline</li></ul> or any combination. The default value is regular.
4557
 
     * @param $fontsize (float) Font size in points. The default value is the current size.
4558
 
     * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
4559
 
     * @return mixed int total string length or array of characted widths
4560
 
     * @author Nicola Asuni
4561
 
     * @public
4562
 
     * @since 1.2
4563
 
     */
4564
 
    public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4565
 
        return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize, $getarray);
4566
 
    }
4567
 
 
4568
 
    /**
4569
 
     * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br>
4570
 
     * @param $sa (string) The array of chars whose total length is to be computed
4571
 
     * @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.
4572
 
     * @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 trough</li><li>O: overline</li></ul> or any combination. The default value is regular.
4573
 
     * @param $fontsize (float) Font size in points. The default value is the current size.
4574
 
     * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
4575
 
     * @return mixed int total string length or array of characted widths
4576
 
     * @author Nicola Asuni
4577
 
     * @public
4578
 
     * @since 2.4.000 (2008-03-06)
4579
 
     */
4580
 
    public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4581
 
        // store current values
4582
 
        if (!$this->empty_string($fontname)) {
4583
 
            $prev_FontFamily = $this->FontFamily;
4584
 
            $prev_FontStyle = $this->FontStyle;
4585
 
            $prev_FontSizePt = $this->FontSizePt;
4586
 
            $this->SetFont($fontname, $fontstyle, $fontsize);
4587
 
        }
4588
 
        // convert UTF-8 array to Latin1 if required
4589
 
        $sa = $this->UTF8ArrToLatin1($sa);
4590
 
        $w = 0; // total width
4591
 
        $wa = array(); // array of characters widths
4592
 
        foreach ($sa as $ck => $char) {
4593
 
            // character width
4594
 
            $cw = $this->GetCharWidth($char, isset($sa[($ck + 1)]));
4595
 
            $wa[] = $cw;
4596
 
            $w += $cw;
4597
 
        }
4598
 
        // restore previous values
4599
 
        if (!$this->empty_string($fontname)) {
4600
 
            $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt);
4601
 
        }
4602
 
        if ($getarray) {
4603
 
            return $wa;
4604
 
        }
4605
 
        return $w;
4606
 
    }
4607
 
 
4608
 
    /**
4609
 
     * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking/kerning).
4610
 
     * @param $char (int) The char code whose length is to be returned
4611
 
     * @param $notlast (boolean) set to false for the latest character on string, true otherwise (default)
4612
 
     * @return float char width
4613
 
     * @author Nicola Asuni
4614
 
     * @public
4615
 
     * @since 2.4.000 (2008-03-06)
4616
 
     */
4617
 
    public function GetCharWidth($char, $notlast=true) {
4618
 
        // get raw width
4619
 
        $chw = $this->getRawCharWidth($char);
4620
 
        if (($this->font_spacing != 0) AND $notlast) {
4621
 
            // increase/decrease font spacing
4622
 
            $chw += $this->font_spacing;
4623
 
        }
4624
 
        if ($this->font_stretching != 100) {
4625
 
            // fixed stretching mode
4626
 
            $chw *= ($this->font_stretching / 100);
4627
 
        }
4628
 
        return $chw;
4629
 
    }
4630
 
 
4631
 
    /**
4632
 
     * Returns the length of the char in user unit for the current font.
4633
 
     * @param $char (int) The char code whose length is to be returned
4634
 
     * @return float char width
4635
 
     * @author Nicola Asuni
4636
 
     * @public
4637
 
     * @since 5.9.000 (2010-09-28)
4638
 
     */
4639
 
    public function getRawCharWidth($char) {
4640
 
        if ($char == 173) {
4641
 
            // SHY character will not be printed
4642
 
            return (0);
4643
 
        }
4644
 
        $cw = &$this->CurrentFont['cw'];
4645
 
        if (isset($cw[$char])) {
4646
 
            $w = $cw[$char];
4647
 
        } elseif (isset($this->CurrentFont['dw'])) {
4648
 
            // default width
4649
 
            $w = $this->CurrentFont['dw'];
4650
 
        } elseif (isset($cw[32])) {
4651
 
            // default width
4652
 
            $w = $cw[32];
4653
 
        } else {
4654
 
            $w = 600;
4655
 
        }
4656
 
        return ($w * $this->FontSize / 1000);
4657
 
    }
4658
 
 
4659
 
    /**
4660
 
     * Returns the numbero of characters in a string.
4661
 
     * @param $s (string) The input string.
4662
 
     * @return int number of characters
4663
 
     * @public
4664
 
     * @since 2.0.0001 (2008-01-07)
4665
 
     */
4666
 
    public function GetNumChars($s) {
4667
 
        if ($this->isUnicodeFont()) {
4668
 
            return count($this->UTF8StringToArray($s));
4669
 
        }
4670
 
        return strlen($s);
4671
 
    }
4672
 
 
4673
 
    /**
4674
 
     * Fill the list of available fonts ($this->fontlist).
4675
 
     * @protected
4676
 
     * @since 4.0.013 (2008-07-28)
4677
 
     */
4678
 
    protected function getFontsList() {
4679
 
        $fontsdir = opendir($this->_getfontpath());
4680
 
        while (($file = readdir($fontsdir)) !== false) {
4681
 
            if (substr($file, -4) == '.php') {
4682
 
                array_push($this->fontlist, strtolower(basename($file, '.php')));
4683
 
            }
4684
 
        }
4685
 
        closedir($fontsdir);
4686
 
    }
4687
 
 
4688
 
    /**
4689
 
     * Imports a TrueType, Type1, core, or CID0 font and makes it available.
4690
 
     * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
4691
 
     * 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.
4692
 
     * @param $family (string) Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
4693
 
     * @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>
4694
 
     * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4695
 
     * @return array containing the font data, or false in case of error.
4696
 
     * @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.
4697
 
     * @public
4698
 
     * @since 1.5
4699
 
     * @see SetFont(), setFontSubsetting()
4700
 
     */
4701
 
    public function AddFont($family, $style='', $fontfile='', $subset='default') {
4702
 
        if ($subset === 'default') {
4703
 
            $subset = $this->font_subsetting;
4704
 
        }
4705
 
        if ($this->empty_string($family)) {
4706
 
            if (!$this->empty_string($this->FontFamily)) {
4707
 
                $family = $this->FontFamily;
4708
 
            } else {
4709
 
                $this->Error('Empty font family');
4710
 
            }
4711
 
        }
4712
 
        // move embedded styles on $style
4713
 
        if (substr($family, -1) == 'I') {
4714
 
            $style .= 'I';
4715
 
            $family = substr($family, 0, -1);
4716
 
        }
4717
 
        if (substr($family, -1) == 'B') {
4718
 
            $style .= 'B';
4719
 
            $family = substr($family, 0, -1);
4720
 
        }
4721
 
        // normalize family name
4722
 
        $family = strtolower($family);
4723
 
        if ((!$this->isunicode) AND ($family == 'arial')) {
4724
 
            $family = 'helvetica';
4725
 
        }
4726
 
        if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
4727
 
            $style = '';
4728
 
        }
4729
 
        $tempstyle = strtoupper($style);
4730
 
        $style = '';
4731
 
        // underline
4732
 
        if (strpos($tempstyle, 'U') !== false) {
4733
 
            $this->underline = true;
4734
 
        } else {
4735
 
            $this->underline = false;
4736
 
        }
4737
 
        // line-through (deleted)
4738
 
        if (strpos($tempstyle, 'D') !== false) {
4739
 
            $this->linethrough = true;
4740
 
        } else {
4741
 
            $this->linethrough = false;
4742
 
        }
4743
 
        // overline
4744
 
        if (strpos($tempstyle, 'O') !== false) {
4745
 
            $this->overline = true;
4746
 
        } else {
4747
 
            $this->overline = false;
4748
 
        }
4749
 
        // bold
4750
 
        if (strpos($tempstyle, 'B') !== false) {
4751
 
            $style .= 'B';
4752
 
        }
4753
 
        // oblique
4754
 
        if (strpos($tempstyle, 'I') !== false) {
4755
 
            $style .= 'I';
4756
 
        }
4757
 
        $bistyle = $style;
4758
 
        $fontkey = $family.$style;
4759
 
        $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : '');
4760
 
        $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
4761
 
        // check if the font has been already added
4762
 
        $fb = $this->getFontBuffer($fontkey);
4763
 
        if ($fb !== false) {
4764
 
            if ($this->inxobj) {
4765
 
                // we are inside an XObject template
4766
 
                $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i'];
4767
 
            }
4768
 
            return $fontdata;
4769
 
        }
4770
 
        if (isset($type)) {
4771
 
            unset($type);
4772
 
        }
4773
 
        if (isset($cw)) {
4774
 
            unset($cw);
4775
 
        }
4776
 
        // get specified font directory (if any)
4777
 
        $fontdir = false;
4778
 
        if (!$this->empty_string($fontfile)) {
4779
 
            $fontdir = dirname($fontfile);
4780
 
            if ($this->empty_string($fontdir) OR ($fontdir == '.')) {
4781
 
                $fontdir = '';
4782
 
            } else {
4783
 
                $fontdir .= '/';
4784
 
            }
4785
 
        }
4786
 
        // search and include font file
4787
 
        if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) {
4788
 
            // build a standard filenames for specified font
4789
 
            $fontfile1 = str_replace(' ', '', $family).strtolower($style).'.php';
4790
 
            $fontfile2 = str_replace(' ', '', $family).'.php';
4791
 
            // search files on various directories
4792
 
            if (($fontdir !== false) AND file_exists($fontdir.$fontfile1)) {
4793
 
                $fontfile = $fontdir.$fontfile1;
4794
 
            } elseif (file_exists($this->_getfontpath().$fontfile1)) {
4795
 
                $fontfile = $this->_getfontpath().$fontfile1;
4796
 
            } elseif (file_exists($fontfile1)) {
4797
 
                $fontfile = $fontfile1;
4798
 
            } elseif (($fontdir !== false) AND file_exists($fontdir.$fontfile2)) {
4799
 
                $fontfile = $fontdir.$fontfile2;
4800
 
            } elseif (file_exists($this->_getfontpath().$fontfile2)) {
4801
 
                $fontfile = $this->_getfontpath().$fontfile2;
4802
 
            } else {
4803
 
                $fontfile = $fontfile2;
4804
 
            }
4805
 
        }
4806
 
        // include font file
4807
 
        if (file_exists($fontfile)) {
4808
 
            include($fontfile);
4809
 
        } else {
4810
 
            $this->Error('Could not include font definition file: '.$family.'');
4811
 
        }
4812
 
        // check font parameters
4813
 
        if ((!isset($type)) OR (!isset($cw))) {
4814
 
            $this->Error('The font definition file has a bad format: '.$fontfile.'');
4815
 
        }
4816
 
        // SET default parameters
4817
 
        if (!isset($file) OR $this->empty_string($file)) {
4818
 
            $file = '';
4819
 
        }
4820
 
        if (!isset($enc) OR $this->empty_string($enc)) {
4821
 
            $enc = '';
4822
 
        }
4823
 
        if (!isset($cidinfo) OR $this->empty_string($cidinfo)) {
4824
 
            $cidinfo = array('Registry'=>'Adobe','Ordering'=>'Identity','Supplement'=>0);
4825
 
            $cidinfo['uni2cid'] = array();
4826
 
        }
4827
 
        if (!isset($ctg) OR $this->empty_string($ctg)) {
4828
 
            $ctg = '';
4829
 
        }
4830
 
        if (!isset($desc) OR $this->empty_string($desc)) {
4831
 
            $desc = array();
4832
 
        }
4833
 
        if (!isset($up) OR $this->empty_string($up)) {
4834
 
            $up = -100;
4835
 
        }
4836
 
        if (!isset($ut) OR $this->empty_string($ut)) {
4837
 
            $ut = 50;
4838
 
        }
4839
 
        if (!isset($cw) OR $this->empty_string($cw)) {
4840
 
            $cw = array();
4841
 
        }
4842
 
        if (!isset($dw) OR $this->empty_string($dw)) {
4843
 
            // set default width
4844
 
            if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
4845
 
                $dw = $desc['MissingWidth'];
4846
 
            } elseif (isset($cw[32])) {
4847
 
                $dw = $cw[32];
4848
 
            } else {
4849
 
                $dw = 600;
4850
 
            }
4851
 
        }
4852
 
        ++$this->numfonts;
4853
 
        if ($type == 'cidfont0') {
4854
 
            // register CID font (all styles at once)
4855
 
            $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
4856
 
            $sname = $name.$styles[$bistyle];
4857
 
            // artificial bold
4858
 
            if (strpos($bistyle, 'B') !== false) {
4859
 
                if (isset($desc['StemV'])) {
4860
 
                    $desc['StemV'] *= 2;
4861
 
                } else {
4862
 
                    $desc['StemV'] = 120;
4863
 
                }
4864
 
            }
4865
 
            // artificial italic
4866
 
            if (strpos($bistyle, 'I') !== false) {
4867
 
                if (isset($desc['ItalicAngle'])) {
4868
 
                    $desc['ItalicAngle'] -= 11;
4869
 
                } else {
4870
 
                    $desc['ItalicAngle'] = -11;
4871
 
                }
4872
 
            }
4873
 
        } elseif ($type == 'core') {
4874
 
            $name = $this->CoreFonts[$fontkey];
4875
 
            $subset = false;
4876
 
        } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
4877
 
            $subset = false;
4878
 
        } elseif ($type == 'TrueTypeUnicode') {
4879
 
            $enc = 'Identity-H';
4880
 
        } else {
4881
 
            $this->Error('Unknow font type: '.$type.'');
4882
 
        }
4883
 
        // initialize subsetchars to contain default ASCII values (0-255)
4884
 
        $subsetchars = array_fill(0, 256, true);
4885
 
        $this->setFontBuffer($fontkey, array('fontkey' => $fontkey, 'i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc, 'cidinfo' => $cidinfo, 'file' => $file, 'ctg' => $ctg, 'subset' => $subset, 'subsetchars' => $subsetchars));
4886
 
        if ($this->inxobj) {
4887
 
            // we are inside an XObject template
4888
 
            $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts;
4889
 
        }
4890
 
        if (isset($diff) AND (!empty($diff))) {
4891
 
            //Search existing encodings
4892
 
            $d = 0;
4893
 
            $nb = count($this->diffs);
4894
 
            for ($i=1; $i <= $nb; ++$i) {
4895
 
                if ($this->diffs[$i] == $diff) {
4896
 
                    $d = $i;
4897
 
                    break;
4898
 
                }
4899
 
            }
4900
 
            if ($d == 0) {
4901
 
                $d = $nb + 1;
4902
 
                $this->diffs[$d] = $diff;
4903
 
            }
4904
 
            $this->setFontSubBuffer($fontkey, 'diff', $d);
4905
 
        }
4906
 
        if (!$this->empty_string($file)) {
4907
 
            if (!isset($this->FontFiles[$file])) {
4908
 
                if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
4909
 
                    $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4910
 
                } elseif ($type != 'core') {
4911
 
                    $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4912
 
                }
4913
 
            } else {
4914
 
                // update fontkeys that are sharing this font file
4915
 
                $this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset);
4916
 
                if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) {
4917
 
                    $this->FontFiles[$file]['fontkeys'][] = $fontkey;
4918
 
                }
4919
 
            }
4920
 
        }
4921
 
        return $fontdata;
4922
 
    }
4923
 
 
4924
 
    /**
4925
 
     * Sets the font used to print character strings.
4926
 
     * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
4927
 
     * The method can be called before the first page is created and the font is retained from page to page.
4928
 
     * If you just wish to change the current font size, it is simpler to call SetFontSize().
4929
 
     * 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 />
4930
 
     * @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.
4931
 
     * @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 trough</li><li>O: overline</li></ul> or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats basic fonts or other fonts when not defined.
4932
 
     * @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
4933
 
     * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4934
 
     * @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.
4935
 
     * @author Nicola Asuni
4936
 
     * @public
4937
 
     * @since 1.0
4938
 
     * @see AddFont(), SetFontSize()
4939
 
     */
4940
 
    public function SetFont($family, $style='', $size=0, $fontfile='', $subset='default') {
4941
 
        //Select a font; size given in points
4942
 
        if ($size == 0) {
4943
 
            $size = $this->FontSizePt;
4944
 
        }
4945
 
        // try to add font (if not already added)
4946
 
        $fontdata = $this->AddFont($family, $style, $fontfile, $subset);
4947
 
        $this->FontFamily = $fontdata['family'];
4948
 
        $this->FontStyle = $fontdata['style'];
4949
 
        $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
4950
 
        $this->SetFontSize($size);
4951
 
    }
4952
 
 
4953
 
    /**
4954
 
     * Defines the size of the current font.
4955
 
     * @param $size (float) The size (in points)
4956
 
     * @param $out (boolean) if true output the font size command, otherwise only set the font properties.
4957
 
     * @public
4958
 
     * @since 1.0
4959
 
     * @see SetFont()
4960
 
     */
4961
 
    public function SetFontSize($size, $out=true) {
4962
 
        // font size in points
4963
 
        $this->FontSizePt = $size;
4964
 
        // font size in user units
4965
 
        $this->FontSize = $size / $this->k;
4966
 
        // calculate some font metrics
4967
 
        if (isset($this->CurrentFont['desc']['FontBBox'])) {
4968
 
            $bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4969
 
            $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
4970
 
        } else {
4971
 
            $font_height = $size * 1.219;
4972
 
        }
4973
 
        if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
4974
 
            $font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000);
4975
 
        }
4976
 
        if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) {
4977
 
            $font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000);
4978
 
        }
4979
 
        if (!isset($font_ascent) AND !isset($font_descent)) {
4980
 
            // core font
4981
 
            $font_ascent = 0.76 * $font_height;
4982
 
            $font_descent = $font_height - $font_ascent;
4983
 
        } elseif (!isset($font_descent)) {
4984
 
            $font_descent = $font_height - $font_ascent;
4985
 
        } elseif (!isset($font_ascent)) {
4986
 
            $font_ascent = $font_height - $font_descent;
4987
 
        }
4988
 
        $this->FontAscent = $font_ascent / $this->k;
4989
 
        $this->FontDescent = $font_descent / $this->k;
4990
 
        if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i']))) {
4991
 
            $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
4992
 
        }
4993
 
    }
4994
 
 
4995
 
    /**
4996
 
     * Return the font descent value
4997
 
     * @param $font (string) font name
4998
 
     * @param $style (string) font style
4999
 
     * @param $size (float) The size (in points)
5000
 
     * @return int font descent
5001
 
     * @public
5002
 
     * @author Nicola Asuni
5003
 
     * @since 4.9.003 (2010-03-30)
5004
 
     */
5005
 
    public function getFontDescent($font, $style='', $size=0) {
5006
 
        $fontdata = $this->AddFont($font, $style);
5007
 
        $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
5008
 
        if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
5009
 
            $descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
5010
 
        } else {
5011
 
            $descent = 1.219 * 0.24 * $size;
5012
 
        }
5013
 
        return ($descent / $this->k);
5014
 
    }
5015
 
 
5016
 
    /**
5017
 
     * Return the font ascent value
5018
 
     * @param $font (string) font name
5019
 
     * @param $style (string) font style
5020
 
     * @param $size (float) The size (in points)
5021
 
     * @return int font ascent
5022
 
     * @public
5023
 
     * @author Nicola Asuni
5024
 
     * @since 4.9.003 (2010-03-30)
5025
 
     */
5026
 
    public function getFontAscent($font, $style='', $size=0) {
5027
 
        $fontdata = $this->AddFont($font, $style);
5028
 
        $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
5029
 
        if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
5030
 
            $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
5031
 
        } else {
5032
 
            $ascent = 1.219 * 0.76 * $size;
5033
 
        }
5034
 
        return ($ascent / $this->k);
5035
 
    }
5036
 
 
5037
 
    /**
5038
 
     * Defines the default monospaced font.
5039
 
     * @param $font (string) Font name.
5040
 
     * @public
5041
 
     * @since 4.5.025
5042
 
     */
5043
 
    public function SetDefaultMonospacedFont($font) {
5044
 
        $this->default_monospaced_font = $font;
5045
 
    }
5046
 
 
5047
 
    /**
5048
 
     * 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 />
5049
 
     * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
5050
 
     * @public
5051
 
     * @since 1.5
5052
 
     * @see Cell(), Write(), Image(), Link(), SetLink()
5053
 
     */
5054
 
    public function AddLink() {
5055
 
        //Create a new internal link
5056
 
        $n = count($this->links) + 1;
5057
 
        $this->links[$n] = array(0, 0);
5058
 
        return $n;
5059
 
    }
5060
 
 
5061
 
    /**
5062
 
     * Defines the page and position a link points to.
5063
 
     * @param $link (int) The link identifier returned by AddLink()
5064
 
     * @param $y (float) Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
5065
 
     * @param $page (int) Number of target page; -1 indicates the current page. This is the default value
5066
 
     * @public
5067
 
     * @since 1.5
5068
 
     * @see AddLink()
5069
 
     */
5070
 
    public function SetLink($link, $y=0, $page=-1) {
5071
 
        if ($y == -1) {
5072
 
            $y = $this->y;
5073
 
        }
5074
 
        if ($page == -1) {
5075
 
            $page = $this->page;
5076
 
        }
5077
 
        $this->links[$link] = array($page, $y);
5078
 
    }
5079
 
 
5080
 
    /**
5081
 
     * Puts a link on a rectangular area of the page.
5082
 
     * 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.
5083
 
     * @param $x (float) Abscissa of the upper-left corner of the rectangle
5084
 
     * @param $y (float) Ordinate of the upper-left corner of the rectangle
5085
 
     * @param $w (float) Width of the rectangle
5086
 
     * @param $h (float) Height of the rectangle
5087
 
     * @param $link (mixed) URL or identifier returned by AddLink()
5088
 
     * @param $spaces (int) number of spaces on the text to link
5089
 
     * @public
5090
 
     * @since 1.5
5091
 
     * @see AddLink(), Annotation(), Cell(), Write(), Image()
5092
 
     */
5093
 
    public function Link($x, $y, $w, $h, $link, $spaces=0) {
5094
 
        $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
5095
 
    }
5096
 
 
5097
 
    /**
5098
 
     * Puts a markup annotation on a rectangular area of the page.
5099
 
     * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!!
5100
 
     * @param $x (float) Abscissa of the upper-left corner of the rectangle
5101
 
     * @param $y (float) Ordinate of the upper-left corner of the rectangle
5102
 
     * @param $w (float) Width of the rectangle
5103
 
     * @param $h (float) Height of the rectangle
5104
 
     * @param $text (string) annotation text or alternate content
5105
 
     * @param $opt (array) array of options (see section 8.4 of PDF reference 1.7).
5106
 
     * @param $spaces (int) number of spaces on the text to link
5107
 
     * @public
5108
 
     * @since 4.0.018 (2008-08-06)
5109
 
     */
5110
 
    public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
5111
 
        if ($this->inxobj) {
5112
 
            // store parameters for later use on template
5113
 
            $this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
5114
 
            return;
5115
 
        }
5116
 
        if ($x === '') {
5117
 
            $x = $this->x;
5118
 
        }
5119
 
        if ($y === '') {
5120
 
            $y = $this->y;
5121
 
        }
5122
 
        // check page for no-write regions and adapt page margins if necessary
5123
 
        $this->checkPageRegions($h, $x, $y);
5124
 
        // recalculate coordinates to account for graphic transformations
5125
 
        if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) {
5126
 
            for ($i=$this->transfmatrix_key; $i > 0; --$i) {
5127
 
                $maxid = count($this->transfmatrix[$i]) - 1;
5128
 
                for ($j=$maxid; $j >= 0; --$j) {
5129
 
                    $ctm = $this->transfmatrix[$i][$j];
5130
 
                    if (isset($ctm['a'])) {
5131
 
                        $x = $x * $this->k;
5132
 
                        $y = ($this->h - $y) * $this->k;
5133
 
                        $w = $w * $this->k;
5134
 
                        $h = $h * $this->k;
5135
 
                        // top left
5136
 
                        $xt = $x;
5137
 
                        $yt = $y;
5138
 
                        $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
5139
 
                        $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
5140
 
                        // top right
5141
 
                        $xt = $x + $w;
5142
 
                        $yt = $y;
5143
 
                        $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
5144
 
                        $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
5145
 
                        // bottom left
5146
 
                        $xt = $x;
5147
 
                        $yt = $y - $h;
5148
 
                        $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
5149
 
                        $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
5150
 
                        // bottom right
5151
 
                        $xt = $x + $w;
5152
 
                        $yt = $y - $h;
5153
 
                        $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
5154
 
                        $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
5155
 
                        // new coordinates (rectangle area)
5156
 
                        $x = min($x1, $x2, $x3, $x4);
5157
 
                        $y = max($y1, $y2, $y3, $y4);
5158
 
                        $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
5159
 
                        $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
5160
 
                        $x = $x / $this->k;
5161
 
                        $y = $this->h - ($y / $this->k);
5162
 
                    }
5163
 
                }
5164
 
            }
5165
 
        }
5166
 
        if ($this->page <= 0) {
5167
 
            $page = 1;
5168
 
        } else {
5169
 
            $page = $this->page;
5170
 
        }
5171
 
        if (!isset($this->PageAnnots[$page])) {
5172
 
            $this->PageAnnots[$page] = array();
5173
 
        }
5174
 
        ++$this->n;
5175
 
        $this->PageAnnots[$page][] = array('n' => $this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
5176
 
        if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!$this->empty_string($opt['FS'])) AND file_exists($opt['FS']) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
5177
 
            ++$this->n;
5178
 
            $this->embeddedfiles[basename($opt['FS'])] = array('n' => $this->n, 'file' => $opt['FS']);
5179
 
        }
5180
 
        // Add widgets annotation's icons
5181
 
        if (isset($opt['mk']['i']) AND file_exists($opt['mk']['i'])) {
5182
 
            $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
5183
 
        }
5184
 
        if (isset($opt['mk']['ri']) AND file_exists($opt['mk']['ri'])) {
5185
 
            $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
5186
 
        }
5187
 
        if (isset($opt['mk']['ix']) AND file_exists($opt['mk']['ix'])) {
5188
 
            $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
5189
 
        }
5190
 
    }
5191
 
 
5192
 
    /**
5193
 
     * Embedd the attached files.
5194
 
     * @since 4.4.000 (2008-12-07)
5195
 
     * @protected
5196
 
     * @see Annotation()
5197
 
     */
5198
 
    protected function _putEmbeddedFiles() {
5199
 
        reset($this->embeddedfiles);
5200
 
        foreach ($this->embeddedfiles as $filename => $filedata) {
5201
 
            $data = file_get_contents($filedata['file']);
5202
 
            $filter = '';
5203
 
            if ($this->compress) {
5204
 
                $data = gzcompress($data);
5205
 
                $filter = ' /Filter /FlateDecode';
5206
 
            }
5207
 
            $stream = $this->_getrawstream($data, $filedata['n']);
5208
 
            $out = $this->_getobj($filedata['n'])."\n";
5209
 
            $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' >>';
5210
 
            $out .= ' stream'."\n".$stream."\n".'endstream';
5211
 
            $out .= "\n".'endobj';
5212
 
            $this->_out($out);
5213
 
        }
5214
 
    }
5215
 
 
5216
 
    /**
5217
 
     * Prints a text cell at the specified position.
5218
 
     * This method allows to place a string precisely on the page.
5219
 
     * @param $x (float) Abscissa of the cell origin
5220
 
     * @param $y (float) Ordinate of the cell origin
5221
 
     * @param $txt (string) String to print
5222
 
     * @param $fstroke (int) outline size in user units (false = disable)
5223
 
     * @param $fclip (boolean) if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
5224
 
     * @param $ffill (boolean) if true fills the text
5225
 
     * @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)))
5226
 
     * @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.
5227
 
     * @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>
5228
 
     * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5229
 
     * @param $link (mixed) URL or identifier returned by AddLink().
5230
 
     * @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.
5231
 
     * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
5232
 
     * @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>
5233
 
     * @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>
5234
 
     * @param $rtloff (boolean) if true uses the page top-left corner as origin of axis for $x and $y initial position.
5235
 
     * @public
5236
 
     * @since 1.0
5237
 
     * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell()
5238
 
     */
5239
 
    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) {
5240
 
        $textrendermode = $this->textrendermode;
5241
 
        $textstrokewidth = $this->textstrokewidth;
5242
 
        $this->setTextRenderingMode($fstroke, $ffill, $fclip);
5243
 
        $this->SetXY($x, $y, $rtloff);
5244
 
        $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
5245
 
        // restore previous rendering mode
5246
 
        $this->textrendermode = $textrendermode;
5247
 
        $this->textstrokewidth = $textstrokewidth;
5248
 
    }
5249
 
 
5250
 
    /**
5251
 
     * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
5252
 
     * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
5253
 
     * This method is called automatically and should not be called directly by the application.
5254
 
     * @return boolean
5255
 
     * @public
5256
 
     * @since 1.4
5257
 
     * @see SetAutoPageBreak()
5258
 
     */
5259
 
    public function AcceptPageBreak() {
5260
 
        if ($this->num_columns > 1) {
5261
 
            // multi column mode
5262
 
            if($this->current_column < ($this->num_columns - 1)) {
5263
 
                // go to next column
5264
 
                $this->selectColumn($this->current_column + 1);
5265
 
            } else {
5266
 
                // add a new page
5267
 
                $this->AddPage();
5268
 
                // set first column
5269
 
                $this->selectColumn(0);
5270
 
            }
5271
 
            // avoid page breaking from checkPageBreak()
5272
 
            return false;
5273
 
        }
5274
 
        return $this->AutoPageBreak;
5275
 
    }
5276
 
 
5277
 
    /**
5278
 
     * Add page if needed.
5279
 
     * @param $h (float) Cell height. Default value: 0.
5280
 
     * @param $y (mixed) starting y position, leave empty for current position.
5281
 
     * @param $addpage (boolean) if true add a page, otherwise only return the true/false state
5282
 
     * @return boolean true in case of page break, false otherwise.
5283
 
     * @since 3.2.000 (2008-07-01)
5284
 
     * @protected
5285
 
     */
5286
 
    protected function checkPageBreak($h=0, $y='', $addpage=true) {
5287
 
        if ($this->empty_string($y)) {
5288
 
            $y = $this->y;
5289
 
        }
5290
 
        $current_page = $this->page;
5291
 
        if ((($y + $h) > $this->PageBreakTrigger) AND (!$this->InFooter) AND ($this->AcceptPageBreak())) {
5292
 
            if ($addpage) {
5293
 
                //Automatic page break
5294
 
                $x = $this->x;
5295
 
                $this->AddPage($this->CurOrientation);
5296
 
                $this->y = $this->tMargin;
5297
 
                $oldpage = $this->page - 1;
5298
 
                if ($this->rtl) {
5299
 
                    if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
5300
 
                        $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
5301
 
                    } else {
5302
 
                        $this->x = $x;
5303
 
                    }
5304
 
                } else {
5305
 
                    if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
5306
 
                        $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
5307
 
                    } else {
5308
 
                        $this->x = $x;
5309
 
                    }
5310
 
                }
5311
 
            }
5312
 
            return true;
5313
 
        }
5314
 
        if ($current_page != $this->page) {
5315
 
            // account for columns mode
5316
 
            return true;
5317
 
        }
5318
 
        return false;
5319
 
    }
5320
 
 
5321
 
    /**
5322
 
     * Removes SHY characters from text.
5323
 
     * Unicode Data:<ul>
5324
 
     * <li>Name : SOFT HYPHEN, commonly abbreviated as SHY</li>
5325
 
     * <li>HTML Entity (decimal): "&amp;#173;"</li>
5326
 
     * <li>HTML Entity (hex): "&amp;#xad;"</li>
5327
 
     * <li>HTML Entity (named): "&amp;shy;"</li>
5328
 
     * <li>How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173]</li>
5329
 
     * <li>UTF-8 (hex): 0xC2 0xAD (c2ad)</li>
5330
 
     * <li>UTF-8 character: chr(194).chr(173)</li>
5331
 
     * </ul>
5332
 
     * @param $txt (string) input string
5333
 
     * @return string without SHY characters.
5334
 
     * @public
5335
 
     * @since (4.5.019) 2009-02-28
5336
 
     */
5337
 
    public function removeSHY($txt='') {
5338
 
        $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt);
5339
 
        if (!$this->isunicode) {
5340
 
            $txt = preg_replace('/([\\xad]{1})/', '', $txt);
5341
 
        }
5342
 
        return $txt;
5343
 
    }
5344
 
 
5345
 
    /**
5346
 
     * 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 />
5347
 
     * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
5348
 
     * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
5349
 
     * @param $h (float) Cell height. Default value: 0.
5350
 
     * @param $txt (string) String to print. Default value: empty string.
5351
 
     * @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)))
5352
 
     * @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.
5353
 
     * @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>
5354
 
     * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5355
 
     * @param $link (mixed) URL or identifier returned by AddLink().
5356
 
     * @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.
5357
 
     * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
5358
 
     * @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>
5359
 
     * @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>
5360
 
     * @public
5361
 
     * @since 1.0
5362
 
     * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
5363
 
     */
5364
 
    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') {
5365
 
        $prev_cell_margin = $this->cell_margin;
5366
 
        $prev_cell_padding = $this->cell_padding;
5367
 
        $this->adjustCellPadding($border);
5368
 
        if (!$ignore_min_height) {
5369
 
            $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
5370
 
            if ($h < $min_cell_height) {
5371
 
                $h = $min_cell_height;
5372
 
            }
5373
 
        }
5374
 
        $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']);
5375
 
        $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5376
 
        $this->cell_padding = $prev_cell_padding;
5377
 
        $this->cell_margin = $prev_cell_margin;
5378
 
    }
5379
 
 
5380
 
    /**
5381
 
     * 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 />
5382
 
     * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
5383
 
     * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
5384
 
     * @param $h (float) Cell height. Default value: 0.
5385
 
     * @param $txt (string) String to print. Default value: empty string.
5386
 
     * @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)))
5387
 
     * @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.
5388
 
     * @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>
5389
 
     * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5390
 
     * @param $link (mixed) URL or identifier returned by AddLink().
5391
 
     * @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.
5392
 
     * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
5393
 
     * @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>
5394
 
     * @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>
5395
 
     * @return string containing cell code
5396
 
     * @protected
5397
 
     * @since 1.0
5398
 
     * @see Cell()
5399
 
     */
5400
 
    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') {
5401
 
        $prev_cell_margin = $this->cell_margin;
5402
 
        $prev_cell_padding = $this->cell_padding;
5403
 
        $txt = $this->removeSHY($txt);
5404
 
        $rs = ''; //string to be returned
5405
 
        $this->adjustCellPadding($border);
5406
 
        if (!$ignore_min_height) {
5407
 
            $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
5408
 
            if ($h < $min_cell_height) {
5409
 
                $h = $min_cell_height;
5410
 
            }
5411
 
        }
5412
 
        $k = $this->k;
5413
 
        // check page for no-write regions and adapt page margins if necessary
5414
 
        $this->checkPageRegions($h, $this->x, $this->y);
5415
 
        if ($this->rtl) {
5416
 
            $x = $this->x - $this->cell_margin['R'];
5417
 
        } else {
5418
 
            $x = $this->x + $this->cell_margin['L'];
5419
 
        }
5420
 
        $y = $this->y + $this->cell_margin['T'];
5421
 
        $prev_font_stretching = $this->font_stretching;
5422
 
        $prev_font_spacing = $this->font_spacing;
5423
 
        // cell vertical alignment
5424
 
        switch ($calign) {
5425
 
            case 'A': {
5426
 
                // font top
5427
 
                switch ($valign) {
5428
 
                    case 'T': {
5429
 
                        // top
5430
 
                        $y -= $this->cell_padding['T'];
5431
 
                        break;
5432
 
                    }
5433
 
                    case 'B': {
5434
 
                        // bottom
5435
 
                        $y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent);
5436
 
                        break;
5437
 
                    }
5438
 
                    default:
5439
 
                    case 'C':
5440
 
                    case 'M': {
5441
 
                        // center
5442
 
                        $y -= (($h - $this->FontAscent - $this->FontDescent) / 2);
5443
 
                        break;
5444
 
                    }
5445
 
                }
5446
 
                break;
5447
 
            }
5448
 
            case 'L': {
5449
 
                // font baseline
5450
 
                switch ($valign) {
5451
 
                    case 'T': {
5452
 
                        // top
5453
 
                        $y -= ($this->cell_padding['T'] + $this->FontAscent);
5454
 
                        break;
5455
 
                    }
5456
 
                    case 'B': {
5457
 
                        // bottom
5458
 
                        $y -= ($h - $this->cell_padding['B'] - $this->FontDescent);
5459
 
                        break;
5460
 
                    }
5461
 
                    default:
5462
 
                    case 'C':
5463
 
                    case 'M': {
5464
 
                        // center
5465
 
                        $y -= (($h + $this->FontAscent - $this->FontDescent) / 2);
5466
 
                        break;
5467
 
                    }
5468
 
                }
5469
 
                break;
5470
 
            }
5471
 
            case 'D': {
5472
 
                // font bottom
5473
 
                switch ($valign) {
5474
 
                    case 'T': {
5475
 
                        // top
5476
 
                        $y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent);
5477
 
                        break;
5478
 
                    }
5479
 
                    case 'B': {
5480
 
                        // bottom
5481
 
                        $y -= ($h - $this->cell_padding['B']);
5482
 
                        break;
5483
 
                    }
5484
 
                    default:
5485
 
                    case 'C':
5486
 
                    case 'M': {
5487
 
                        // center
5488
 
                        $y -= (($h + $this->FontAscent + $this->FontDescent) / 2);
5489
 
                        break;
5490
 
                    }
5491
 
                }
5492
 
                break;
5493
 
            }
5494
 
            case 'B': {
5495
 
                // cell bottom
5496
 
                $y -= $h;
5497
 
                break;
5498
 
            }
5499
 
            case 'C':
5500
 
            case 'M': {
5501
 
                // cell center
5502
 
                $y -= ($h / 2);
5503
 
                break;
5504
 
            }
5505
 
            default:
5506
 
            case 'T': {
5507
 
                // cell top
5508
 
                break;
5509
 
            }
5510
 
        }
5511
 
        // text vertical alignment
5512
 
        switch ($valign) {
5513
 
            case 'T': {
5514
 
                // top
5515
 
                $yt = $y + $this->cell_padding['T'];
5516
 
                break;
5517
 
            }
5518
 
            case 'B': {
5519
 
                // bottom
5520
 
                $yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent;
5521
 
                break;
5522
 
            }
5523
 
            default:
5524
 
            case 'C':
5525
 
            case 'M': {
5526
 
                // center
5527
 
                $yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2);
5528
 
                break;
5529
 
            }
5530
 
        }
5531
 
        $basefonty = $yt + $this->FontAscent;
5532
 
        if ($this->empty_string($w) OR ($w <= 0)) {
5533
 
            if ($this->rtl) {
5534
 
                $w = $x - $this->lMargin;
5535
 
            } else {
5536
 
                $w = $this->w - $this->rMargin - $x;
5537
 
            }
5538
 
        }
5539
 
        $s = '';
5540
 
        // fill and borders
5541
 
        if (is_string($border) AND (strlen($border) == 4)) {
5542
 
            // full border
5543
 
            $border = 1;
5544
 
        }
5545
 
        if ($fill OR ($border == 1)) {
5546
 
            if ($fill) {
5547
 
                $op = ($border == 1) ? 'B' : 'f';
5548
 
            } else {
5549
 
                $op = 'S';
5550
 
            }
5551
 
            if ($this->rtl) {
5552
 
                $xk = (($x - $w) * $k);
5553
 
            } else {
5554
 
                $xk = ($x * $k);
5555
 
            }
5556
 
            $s .= sprintf('%.2F %.2F %.2F %.2F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op);
5557
 
        }
5558
 
        // draw borders
5559
 
        $s .= $this->getCellBorder($x, $y, $w, $h, $border);
5560
 
        if ($txt != '') {
5561
 
            $txt2 = $txt;
5562
 
            if ($this->isunicode) {
5563
 
                if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5564
 
                    $txt2 = $this->UTF8ToLatin1($txt2);
5565
 
                } else {
5566
 
                    $unicode = $this->UTF8StringToArray($txt); // array of UTF-8 unicode values
5567
 
                    $unicode = $this->utf8Bidi($unicode, '', $this->tmprtl);
5568
 
                    if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) {
5569
 
                        // ---- Fix for bug #2977340 "Incorrect Thai characters position arrangement" ----
5570
 
                        // NOTE: this doesn't work with HTML justification
5571
 
                        // Symbols that could overlap on the font top (only works in LTR)
5572
 
                        $topchar = array(3611, 3613, 3615, 3650, 3651, 3652); // chars that extends on top
5573
 
                        $topsym = array(3633, 3636, 3637, 3638, 3639, 3655, 3656, 3657, 3658, 3659, 3660, 3661, 3662); // symbols with top position
5574
 
                        $numchars = count($unicode); // number of chars
5575
 
                        $unik = 0;
5576
 
                        $uniblock = array();
5577
 
                        $uniblock[$unik] = array();
5578
 
                        $uniblock[$unik][] = $unicode[0];
5579
 
                        // resolve overlapping conflicts by splitting the string in several parts
5580
 
                        for ($i = 1; $i < $numchars; ++$i) {
5581
 
                            // check if symbols overlaps at top
5582
 
                            if (in_array($unicode[$i], $topsym) AND (in_array($unicode[($i - 1)], $topsym) OR in_array($unicode[($i - 1)], $topchar))) {
5583
 
                                // move symbols to another array
5584
 
                                ++$unik;
5585
 
                                $uniblock[$unik] = array();
5586
 
                                $uniblock[$unik][] = $unicode[$i];
5587
 
                                ++$unik;
5588
 
                                $uniblock[$unik] = array();
5589
 
                                $unicode[$i] = 0x200b; // Unicode Character 'ZERO WIDTH SPACE' (DEC:8203, U+200B)
5590
 
                            } else {
5591
 
                                $uniblock[$unik][] = $unicode[$i];
5592
 
                            }
5593
 
                        }
5594
 
                        // ---- END OF Fix for bug #2977340
5595
 
                    }
5596
 
                    $txt2 = $this->arrUTF8ToUTF16BE($unicode, false);
5597
 
                }
5598
 
            }
5599
 
            $txt2 = $this->_escape($txt2);
5600
 
            // get current text width (considering general font stretching and spacing)
5601
 
            $txwidth = $this->GetStringWidth($txt);
5602
 
            $width = $txwidth;
5603
 
            // check for stretch mode
5604
 
            if ($stretch > 0) {
5605
 
                // calculate ratio between cell width and text width
5606
 
                if ($width <= 0) {
5607
 
                    $ratio = 1;
5608
 
                } else {
5609
 
                    $ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width);
5610
 
                }
5611
 
                // check if stretching is required
5612
 
                if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) {
5613
 
                    // the text will be stretched to fit cell width
5614
 
                    if ($stretch > 2) {
5615
 
                        // set new character spacing
5616
 
                        $this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100));
5617
 
                    } else {
5618
 
                        // set new horizontal stretching
5619
 
                        $this->font_stretching *= $ratio;
5620
 
                    }
5621
 
                    // recalculate text width (the text fills the entire cell)
5622
 
                    $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5623
 
                    // reset alignment
5624
 
                    $align = '';
5625
 
                }
5626
 
            }
5627
 
            if ($this->font_stretching != 100) {
5628
 
                // apply font stretching
5629
 
                $rs .= sprintf('BT %.2F Tz ET ', $this->font_stretching);
5630
 
            }
5631
 
            if ($this->font_spacing != 0) {
5632
 
                // increase/decrease font spacing
5633
 
                $rs .= sprintf('BT %.2F Tc ET ', ($this->font_spacing * $this->k));
5634
 
            }
5635
 
            if ($this->ColorFlag) {
5636
 
                $s .= 'q '.$this->TextColor.' ';
5637
 
            }
5638
 
            // rendering mode
5639
 
            $s .= sprintf('BT %d Tr %.2F w ET ', $this->textrendermode, $this->textstrokewidth);
5640
 
            // count number of spaces
5641
 
            $ns = substr_count($txt, chr(32));
5642
 
            // Justification
5643
 
            $spacewidth = 0;
5644
 
            if (($align == 'J') AND ($ns > 0)) {
5645
 
                if ($this->isUnicodeFont()) {
5646
 
                    // get string width without spaces
5647
 
                    $width = $this->GetStringWidth(str_replace(' ', '', $txt));
5648
 
                    // calculate average space width
5649
 
                    $spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / $this->FontSize;
5650
 
                    if ($this->font_stretching != 100) {
5651
 
                        // word spacing is affected by stretching
5652
 
                        $spacewidth /= ($this->font_stretching / 100);
5653
 
                    }
5654
 
                    // set word position to be used with TJ operator
5655
 
                    $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%.3F', $spacewidth).' (', $txt2);
5656
 
                    $unicode_justification = true;
5657
 
                } else {
5658
 
                    // get string width
5659
 
                    $width = $txwidth;
5660
 
                    // new space width
5661
 
                    $spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k;
5662
 
                    if ($this->font_stretching != 100) {
5663
 
                        // word spacing (Tw) is affected by stretching
5664
 
                        $spacewidth /= ($this->font_stretching / 100);
5665
 
                    }
5666
 
                    // set word spacing
5667
 
                    $rs .= sprintf('BT %.3F Tw ET ', $spacewidth);
5668
 
                }
5669
 
                $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5670
 
            }
5671
 
            // replace carriage return characters
5672
 
            $txt2 = str_replace("\r", ' ', $txt2);
5673
 
            switch ($align) {
5674
 
                case 'C': {
5675
 
                    $dx = ($w - $width) / 2;
5676
 
                    break;
5677
 
                }
5678
 
                case 'R': {
5679
 
                    if ($this->rtl) {
5680
 
                        $dx = $this->cell_padding['R'];
5681
 
                    } else {
5682
 
                        $dx = $w - $width - $this->cell_padding['R'];
5683
 
                    }
5684
 
                    break;
5685
 
                }
5686
 
                case 'L': {
5687
 
                    if ($this->rtl) {
5688
 
                        $dx = $w - $width - $this->cell_padding['L'];
5689
 
                    } else {
5690
 
                        $dx = $this->cell_padding['L'];
5691
 
                    }
5692
 
                    break;
5693
 
                }
5694
 
                case 'J':
5695
 
                default: {
5696
 
                    if ($this->rtl) {
5697
 
                        $dx = $this->cell_padding['R'];
5698
 
                    } else {
5699
 
                        $dx = $this->cell_padding['L'];
5700
 
                    }
5701
 
                    break;
5702
 
                }
5703
 
            }
5704
 
            if ($this->rtl) {
5705
 
                $xdx = $x - $dx - $width;
5706
 
            } else {
5707
 
                $xdx = $x + $dx;
5708
 
            }
5709
 
            $xdk = $xdx * $k;
5710
 
            // print text
5711
 
            $s .= sprintf('BT %.2F %.2F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
5712
 
            if (isset($uniblock)) {
5713
 
                // print overlapping characters as separate string
5714
 
                $xshift = 0; // horizontal shift
5715
 
                $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k);
5716
 
                $spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1));
5717
 
                foreach ($uniblock as $uk => $uniarr) {
5718
 
                    if (($uk % 2) == 0) {
5719
 
                        // x space to skip
5720
 
                        if ($spacewidth != 0) {
5721
 
                            // justification shift
5722
 
                            $xshift += (count(array_keys($uniarr, 32)) * $spw);
5723
 
                        }
5724
 
                        $xshift += $this->GetArrStringWidth($uniarr); // + shift justification
5725
 
                    } else {
5726
 
                        // character to print
5727
 
                        $topchr = $this->arrUTF8ToUTF16BE($uniarr, false);
5728
 
                        $topchr = $this->_escape($topchr);
5729
 
                        $s .= sprintf(' BT %.2F %.2F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr);
5730
 
                    }
5731
 
                }
5732
 
            }
5733
 
            if ($this->underline) {
5734
 
                $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
5735
 
            }
5736
 
            if ($this->linethrough) {
5737
 
                $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
5738
 
            }
5739
 
            if ($this->overline) {
5740
 
                $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
5741
 
            }
5742
 
            if ($this->ColorFlag) {
5743
 
                $s .= ' Q';
5744
 
            }
5745
 
            if ($link) {
5746
 
                $this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns);
5747
 
            }
5748
 
        }
5749
 
        // output cell
5750
 
        if ($s) {
5751
 
            // output cell
5752
 
            $rs .= $s;
5753
 
            if ($this->font_spacing != 0) {
5754
 
                // reset font spacing mode
5755
 
                $rs .= ' BT 0 Tc ET';
5756
 
            }
5757
 
            if ($this->font_stretching != 100) {
5758
 
                // reset font stretching mode
5759
 
                $rs .= ' BT 100 Tz ET';
5760
 
            }
5761
 
        }
5762
 
        // reset word spacing
5763
 
        if (!$this->isUnicodeFont() AND ($align == 'J')) {
5764
 
            $rs .= ' BT 0 Tw ET';
5765
 
        }
5766
 
        // reset stretching and spacing
5767
 
        $this->font_stretching = $prev_font_stretching;
5768
 
        $this->font_spacing = $prev_font_spacing;
5769
 
        $this->lasth = $h;
5770
 
        if ($ln > 0) {
5771
 
            //Go to the beginning of the next line
5772
 
            $this->y = $y + $h + $this->cell_margin['B'];
5773
 
            if ($ln == 1) {
5774
 
                if ($this->rtl) {
5775
 
                    $this->x = $this->w - $this->rMargin;
5776
 
                } else {
5777
 
                    $this->x = $this->lMargin;
5778
 
                }
5779
 
            }
5780
 
        } else {
5781
 
            // go left or right by case
5782
 
            if ($this->rtl) {
5783
 
                $this->x = $x - $w - $this->cell_margin['L'];
5784
 
            } else {
5785
 
                $this->x = $x + $w + $this->cell_margin['R'];
5786
 
            }
5787
 
        }
5788
 
        $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
5789
 
        $rs = $gstyles.$rs;
5790
 
        $this->cell_padding = $prev_cell_padding;
5791
 
        $this->cell_margin = $prev_cell_margin;
5792
 
        return $rs;
5793
 
    }
5794
 
 
5795
 
    /**
5796
 
     * Returns the code to draw the cell border
5797
 
     * @param $x (float) X coordinate.
5798
 
     * @param $y (float) Y coordinate.
5799
 
     * @param $w (float) Cell width.
5800
 
     * @param $h (float) Cell height.
5801
 
     * @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)))
5802
 
     * @return string containing cell border code
5803
 
     * @protected
5804
 
     * @see SetLineStyle()
5805
 
     * @since 5.7.000 (2010-08-02)
5806
 
     */
5807
 
    protected function getCellBorder($x, $y, $w, $h, $brd) {
5808
 
        $s = ''; // string to be returned
5809
 
        if (empty($brd)) {
5810
 
            return $s;
5811
 
        }
5812
 
        if ($brd == 1) {
5813
 
            $brd = array('LRTB' => true);
5814
 
        }
5815
 
        // calculate coordinates for border
5816
 
        $k = $this->k;
5817
 
        if ($this->rtl) {
5818
 
            $xeL = ($x - $w) * $k;
5819
 
            $xeR = $x * $k;
5820
 
        } else {
5821
 
            $xeL = $x * $k;
5822
 
            $xeR = ($x + $w) * $k;
5823
 
        }
5824
 
        $yeL = (($this->h - ($y + $h)) * $k);
5825
 
        $yeT = (($this->h - $y) * $k);
5826
 
        $xeT = $xeL;
5827
 
        $xeB = $xeR;
5828
 
        $yeR = $yeT;
5829
 
        $yeB = $yeL;
5830
 
        if (is_string($brd)) {
5831
 
            // convert string to array
5832
 
            $slen = strlen($brd);
5833
 
            $newbrd = array();
5834
 
            for ($i = 0; $i < $slen; ++$i) {
5835
 
                $newbrd[$brd{$i}] = array('cap' => 'square', 'join' => 'miter');
5836
 
            }
5837
 
            $brd = $newbrd;
5838
 
        }
5839
 
        if (isset($brd['mode'])) {
5840
 
            $mode = $brd['mode'];
5841
 
            unset($brd['mode']);
5842
 
        } else {
5843
 
            $mode = 'normal';
5844
 
        }
5845
 
        foreach ($brd as $border => $style) {
5846
 
            if (is_array($style) AND !empty($style)) {
5847
 
                // apply border style
5848
 
                $prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' ';
5849
 
                $s .= $this->SetLineStyle($style, true)."\n";
5850
 
            }
5851
 
            switch ($mode) {
5852
 
                case 'ext': {
5853
 
                    $off = (($this->LineWidth / 2) * $k);
5854
 
                    $xL = $xeL - $off;
5855
 
                    $xR = $xeR + $off;
5856
 
                    $yT = $yeT + $off;
5857
 
                    $yL = $yeL - $off;
5858
 
                    $xT = $xL;
5859
 
                    $xB = $xR;
5860
 
                    $yR = $yT;
5861
 
                    $yB = $yL;
5862
 
                    $w += $this->LineWidth;
5863
 
                    $h += $this->LineWidth;
5864
 
                    break;
5865
 
                }
5866
 
                case 'int': {
5867
 
                    $off = ($this->LineWidth / 2) * $k;
5868
 
                    $xL = $xeL + $off;
5869
 
                    $xR = $xeR - $off;
5870
 
                    $yT = $yeT - $off;
5871
 
                    $yL = $yeL + $off;
5872
 
                    $xT = $xL;
5873
 
                    $xB = $xR;
5874
 
                    $yR = $yT;
5875
 
                    $yB = $yL;
5876
 
                    $w -= $this->LineWidth;
5877
 
                    $h -= $this->LineWidth;
5878
 
                    break;
5879
 
                }
5880
 
                case 'normal':
5881
 
                default: {
5882
 
                    $xL = $xeL;
5883
 
                    $xT = $xeT;
5884
 
                    $xB = $xeB;
5885
 
                    $xR = $xeR;
5886
 
                    $yL = $yeL;
5887
 
                    $yT = $yeT;
5888
 
                    $yB = $yeB;
5889
 
                    $yR = $yeR;
5890
 
                    break;
5891
 
                }
5892
 
            }
5893
 
            // draw borders by case
5894
 
            if (strlen($border) == 4) {
5895
 
                $s .= sprintf('%.2F %.2F %.2F %.2F re S ', $xT, $yT, ($w * $k), (-$h * $k));
5896
 
            } elseif (strlen($border) == 3) {
5897
 
                if (strpos($border,'B') === false) { // LTR
5898
 
                    $s .= sprintf('%.2F %.2F m ', $xL, $yL);
5899
 
                    $s .= sprintf('%.2F %.2F l ', $xT, $yT);
5900
 
                    $s .= sprintf('%.2F %.2F l ', $xR, $yR);
5901
 
                    $s .= sprintf('%.2F %.2F l ', $xB, $yB);
5902
 
                    $s .= 'S ';
5903
 
                } elseif (strpos($border,'L') === false) { // TRB
5904
 
                    $s .= sprintf('%.2F %.2F m ', $xT, $yT);
5905
 
                    $s .= sprintf('%.2F %.2F l ', $xR, $yR);
5906
 
                    $s .= sprintf('%.2F %.2F l ', $xB, $yB);
5907
 
                    $s .= sprintf('%.2F %.2F l ', $xL, $yL);
5908
 
                    $s .= 'S ';
5909
 
                } elseif (strpos($border,'T') === false) { // RBL
5910
 
                    $s .= sprintf('%.2F %.2F m ', $xR, $yR);
5911
 
                    $s .= sprintf('%.2F %.2F l ', $xB, $yB);
5912
 
                    $s .= sprintf('%.2F %.2F l ', $xL, $yL);
5913
 
                    $s .= sprintf('%.2F %.2F l ', $xT, $yT);
5914
 
                    $s .= 'S ';
5915
 
                } elseif (strpos($border,'R') === false) { // BLT
5916
 
                    $s .= sprintf('%.2F %.2F m ', $xB, $yB);
5917
 
                    $s .= sprintf('%.2F %.2F l ', $xL, $yL);
5918
 
                    $s .= sprintf('%.2F %.2F l ', $xT, $yT);
5919
 
                    $s .= sprintf('%.2F %.2F l ', $xR, $yR);
5920
 
                    $s .= 'S ';
5921
 
                }
5922
 
            } elseif (strlen($border) == 2) {
5923
 
                if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
5924
 
                    $s .= sprintf('%.2F %.2F m ', $xL, $yL);
5925
 
                    $s .= sprintf('%.2F %.2F l ', $xT, $yT);
5926
 
                    $s .= sprintf('%.2F %.2F l ', $xR, $yR);
5927
 
                    $s .= 'S ';
5928
 
                } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
5929
 
                    $s .= sprintf('%.2F %.2F m ', $xT, $yT);
5930
 
                    $s .= sprintf('%.2F %.2F l ', $xR, $yR);
5931
 
                    $s .= sprintf('%.2F %.2F l ', $xB, $yB);
5932
 
                    $s .= 'S ';
5933
 
                } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
5934
 
                    $s .= sprintf('%.2F %.2F m ', $xR, $yR);
5935
 
                    $s .= sprintf('%.2F %.2F l ', $xB, $yB);
5936
 
                    $s .= sprintf('%.2F %.2F l ', $xL, $yL);
5937
 
                    $s .= 'S ';
5938
 
                } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
5939
 
                    $s .= sprintf('%.2F %.2F m ', $xB, $yB);
5940
 
                    $s .= sprintf('%.2F %.2F l ', $xL, $yL);
5941
 
                    $s .= sprintf('%.2F %.2F l ', $xT, $yT);
5942
 
                    $s .= 'S ';
5943
 
                } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
5944
 
                    $s .= sprintf('%.2F %.2F m ', $xL, $yL);
5945
 
                    $s .= sprintf('%.2F %.2F l ', $xT, $yT);
5946
 
                    $s .= 'S ';
5947
 
                    $s .= sprintf('%.2F %.2F m ', $xR, $yR);
5948
 
                    $s .= sprintf('%.2F %.2F l ', $xB, $yB);
5949
 
                    $s .= 'S ';
5950
 
                } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
5951
 
                    $s .= sprintf('%.2F %.2F m ', $xT, $yT);
5952
 
                    $s .= sprintf('%.2F %.2F l ', $xR, $yR);
5953
 
                    $s .= 'S ';
5954
 
                    $s .= sprintf('%.2F %.2F m ', $xB, $yB);
5955
 
                    $s .= sprintf('%.2F %.2F l ', $xL, $yL);
5956
 
                    $s .= 'S ';
5957
 
                }
5958
 
            } else { // strlen($border) == 1
5959
 
                if (strpos($border,'L') !== false) { // L
5960
 
                    $s .= sprintf('%.2F %.2F m ', $xL, $yL);
5961
 
                    $s .= sprintf('%.2F %.2F l ', $xT, $yT);
5962
 
                    $s .= 'S ';
5963
 
                } elseif (strpos($border,'T') !== false) { // T
5964
 
                    $s .= sprintf('%.2F %.2F m ', $xT, $yT);
5965
 
                    $s .= sprintf('%.2F %.2F l ', $xR, $yR);
5966
 
                    $s .= 'S ';
5967
 
                } elseif (strpos($border,'R') !== false) { // R
5968
 
                    $s .= sprintf('%.2F %.2F m ', $xR, $yR);
5969
 
                    $s .= sprintf('%.2F %.2F l ', $xB, $yB);
5970
 
                    $s .= 'S ';
5971
 
                } elseif (strpos($border,'B') !== false) { // B
5972
 
                    $s .= sprintf('%.2F %.2F m ', $xB, $yB);
5973
 
                    $s .= sprintf('%.2F %.2F l ', $xL, $yL);
5974
 
                    $s .= 'S ';
5975
 
                }
5976
 
            }
5977
 
            if (is_array($style) AND !empty($style)) {
5978
 
                // reset border style to previous value
5979
 
                $s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n";
5980
 
            }
5981
 
        }
5982
 
        return $s;
5983
 
    }
5984
 
 
5985
 
    /**
5986
 
     * This method allows printing text with line breaks.
5987
 
     * 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 />
5988
 
     * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
5989
 
     * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
5990
 
     * @param $h (float) Cell minimum height. The cell extends automatically if needed.
5991
 
     * @param $txt (string) String to print
5992
 
     * @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)))
5993
 
     * @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>
5994
 
     * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5995
 
     * @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>
5996
 
     * @param $x (float) x position in user units
5997
 
     * @param $y (float) y position in user units
5998
 
     * @param $reseth (boolean) if true reset the last cell height (default true).
5999
 
     * @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.
6000
 
     * @param $ishtml (boolean) set to true if $txt is HTML content (default = false).
6001
 
     * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
6002
 
     * @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.
6003
 
     * @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.
6004
 
     * @param $fitcell (boolean) if true attempt to fit all the text within the cell by reducing the font size.
6005
 
     * @return int Return the number of cells or 1 for html mode.
6006
 
     * @public
6007
 
     * @since 1.3
6008
 
     * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
6009
 
     */
6010
 
    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) {
6011
 
        $prev_cell_margin = $this->cell_margin;
6012
 
        $prev_cell_padding = $this->cell_padding;
6013
 
        // adjust internal padding
6014
 
        $this->adjustCellPadding($border);
6015
 
        $mc_padding = $this->cell_padding;
6016
 
        $mc_margin = $this->cell_margin;
6017
 
        $this->cell_padding['T'] = 0;
6018
 
        $this->cell_padding['B'] = 0;
6019
 
        $this->setCellMargins(0, 0, 0, 0);
6020
 
        if ($this->empty_string($this->lasth) OR $reseth) {
6021
 
            // reset row height
6022
 
            $this->resetLastH();
6023
 
        }
6024
 
        if (!$this->empty_string($y)) {
6025
 
            $this->SetY($y);
6026
 
        } else {
6027
 
            $y = $this->GetY();
6028
 
        }
6029
 
        $resth = 0;
6030
 
        if ((!$this->InFooter) AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) {
6031
 
            // spit cell in more pages/columns
6032
 
            $newh = $this->PageBreakTrigger - $y;
6033
 
            $resth = $h - $newh; // cell to be printed on the next page/column
6034
 
            $h = $newh;
6035
 
        }
6036
 
        // get current page number
6037
 
        $startpage = $this->page;
6038
 
        // get current column
6039
 
        $startcolumn = $this->current_column;
6040
 
        if (!$this->empty_string($x)) {
6041
 
            $this->SetX($x);
6042
 
        } else {
6043
 
            $x = $this->GetX();
6044
 
        }
6045
 
        // check page for no-write regions and adapt page margins if necessary
6046
 
        $this->checkPageRegions(0, $x, $y);
6047
 
        // apply margins
6048
 
        $oy = $y + $mc_margin['T'];
6049
 
        if ($this->rtl) {
6050
 
            $ox = $this->w - $x - $mc_margin['R'];
6051
 
        } else {
6052
 
            $ox = $x + $mc_margin['L'];
6053
 
        }
6054
 
        $this->x = $ox;
6055
 
        $this->y = $oy;
6056
 
        // set width
6057
 
        if ($this->empty_string($w) OR ($w <= 0)) {
6058
 
            if ($this->rtl) {
6059
 
                $w = $this->x - $this->lMargin - $mc_margin['L'];
6060
 
            } else {
6061
 
                $w = $this->w - $this->x - $this->rMargin - $mc_margin['R'];
6062
 
            }
6063
 
        }
6064
 
        // store original margin values
6065
 
        $lMargin = $this->lMargin;
6066
 
        $rMargin = $this->rMargin;
6067
 
        if ($this->rtl) {
6068
 
            $this->rMargin = $this->w - $this->x;
6069
 
            $this->lMargin = $this->x - $w;
6070
 
        } else {
6071
 
            $this->lMargin = $this->x;
6072
 
            $this->rMargin = $this->w - $this->x - $w;
6073
 
        }
6074
 
        if ($autopadding) {
6075
 
            // add top padding
6076
 
            $this->y += $mc_padding['T'];
6077
 
        }
6078
 
        if ($ishtml) { // ******* Write HTML text
6079
 
            $this->writeHTML($txt, true, 0, $reseth, true, $align);
6080
 
            $nl = 1;
6081
 
        } else { // ******* Write simple text
6082
 
            // vertical alignment
6083
 
            if ($maxh > 0) {
6084
 
                // get text height
6085
 
                $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
6086
 
                if ($fitcell) {
6087
 
                    $prev_FontSizePt = $this->FontSizePt;
6088
 
                    // try to reduce font size to fit text on cell (use a quick search algorithm)
6089
 
                    $fmin = 1;
6090
 
                    $fmax = $this->FontSizePt;
6091
 
                    $prev_text_height = $text_height;
6092
 
                    $maxit = 100; // max number of iterations
6093
 
                    while ($maxit > 0) {
6094
 
                        $fmid = (($fmax + $fmin) / 2);
6095
 
                        $this->SetFontSize($fmid, false);
6096
 
                        $this->resetLastH();
6097
 
                        $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
6098
 
                        if (($text_height == $maxh) OR (($text_height < $maxh) AND ($fmin >= ($fmax - 0.01)))) {
6099
 
                            break;
6100
 
                        } elseif ($text_height < $maxh) {
6101
 
                            $fmin = $fmid;
6102
 
                        } else {
6103
 
                            $fmax = $fmid;
6104
 
                        }
6105
 
                        --$maxit;
6106
 
                    }
6107
 
                    $this->SetFontSize($this->FontSizePt);
6108
 
                }
6109
 
                if ($text_height < $maxh) {
6110
 
                    if ($valign == 'M') {
6111
 
                        // text vertically centered
6112
 
                        $this->y += (($maxh - $text_height) / 2);
6113
 
                    } elseif ($valign == 'B') {
6114
 
                        // text vertically aligned on bottom
6115
 
                        $this->y += ($maxh - $text_height);
6116
 
                    }
6117
 
                }
6118
 
            }
6119
 
            $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
6120
 
            if ($fitcell) {
6121
 
                // restore font size
6122
 
                $this->SetFontSize($prev_FontSizePt);
6123
 
            }
6124
 
        }
6125
 
        if ($autopadding) {
6126
 
            // add bottom padding
6127
 
            $this->y += $mc_padding['B'];
6128
 
        }
6129
 
        // Get end-of-text Y position
6130
 
        $currentY = $this->y;
6131
 
        // get latest page number
6132
 
        $endpage = $this->page;
6133
 
        if ($resth > 0) {
6134
 
            $skip = ($endpage - $startpage);
6135
 
            $tmpresth = $resth;
6136
 
            while ($tmpresth > 0) {
6137
 
                if ($skip <= 0) {
6138
 
                    // add a page (or trig AcceptPageBreak() for multicolumn mode)
6139
 
                    $this->checkPageBreak($this->PageBreakTrigger + 1);
6140
 
                }
6141
 
                if ($this->num_columns > 1) {
6142
 
                    $tmpresth -= ($this->h - $this->y - $this->bMargin);
6143
 
                } else {
6144
 
                    $tmpresth -= ($this->h - $this->tMargin - $this->bMargin);
6145
 
                }
6146
 
                --$skip;
6147
 
            }
6148
 
            $currentY = $this->y;
6149
 
            $endpage = $this->page;
6150
 
        }
6151
 
        // get latest column
6152
 
        $endcolumn = $this->current_column;
6153
 
        if ($this->num_columns == 0) {
6154
 
            $this->num_columns = 1;
6155
 
        }
6156
 
        // get border modes
6157
 
        $border_start = $this->getBorderMode($border, $position='start');
6158
 
        $border_end = $this->getBorderMode($border, $position='end');
6159
 
        $border_middle = $this->getBorderMode($border, $position='middle');
6160
 
        // design borders around HTML cells.
6161
 
        for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
6162
 
            $ccode = '';
6163
 
            $this->setPage($page);
6164
 
            if ($this->num_columns < 2) {
6165
 
                // single-column mode
6166
 
                $this->SetX($x);
6167
 
                $this->y = $this->tMargin;
6168
 
            }
6169
 
            // account for margin changes
6170
 
            if ($page > $startpage) {
6171
 
                if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
6172
 
                    $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
6173
 
                } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
6174
 
                    $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
6175
 
                }
6176
 
            }
6177
 
            if ($startpage == $endpage) {
6178
 
                // single page
6179
 
                for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
6180
 
                    $this->selectColumn($column);
6181
 
                    if ($this->rtl) {
6182
 
                        $this->x -= $mc_margin['R'];
6183
 
                    } else {
6184
 
                        $this->x += $mc_margin['L'];
6185
 
                    }
6186
 
                    if ($startcolumn == $endcolumn) { // single column
6187
 
                        $cborder = $border;
6188
 
                        $h = max($h, ($currentY - $oy));
6189
 
                        $this->y = $oy;
6190
 
                    } elseif ($column == $startcolumn) { // first column
6191
 
                        $cborder = $border_start;
6192
 
                        $this->y = $oy;
6193
 
                        $h = $this->h - $this->y - $this->bMargin;
6194
 
                    } elseif ($column == $endcolumn) { // end column
6195
 
                        $cborder = $border_end;
6196
 
                        $h = $currentY - $this->y;
6197
 
                        if ($resth > $h) {
6198
 
                            $h = $resth;
6199
 
                        }
6200
 
                    } else { // middle column
6201
 
                        $cborder = $border_middle;
6202
 
                        $h = $this->h - $this->y - $this->bMargin;
6203
 
                        $resth -= $h;
6204
 
                    }
6205
 
                    $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6206
 
                } // end for each column
6207
 
            } elseif ($page == $startpage) { // first page
6208
 
                for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
6209
 
                    $this->selectColumn($column);
6210
 
                    if ($this->rtl) {
6211
 
                        $this->x -= $mc_margin['R'];
6212
 
                    } else {
6213
 
                        $this->x += $mc_margin['L'];
6214
 
                    }
6215
 
                    if ($column == $startcolumn) { // first column
6216
 
                        $cborder = $border_start;
6217
 
                        $this->y = $oy;
6218
 
                        $h = $this->h - $this->y - $this->bMargin;
6219
 
                    } else { // middle column
6220
 
                        $cborder = $border_middle;
6221
 
                        $h = $this->h - $this->y - $this->bMargin;
6222
 
                        $resth -= $h;
6223
 
                    }
6224
 
                    $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6225
 
                } // end for each column
6226
 
            } elseif ($page == $endpage) { // last page
6227
 
                for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
6228
 
                    $this->selectColumn($column);
6229
 
                    if ($this->rtl) {
6230
 
                        $this->x -= $mc_margin['R'];
6231
 
                    } else {
6232
 
                        $this->x += $mc_margin['L'];
6233
 
                    }
6234
 
                    if ($column == $endcolumn) {
6235
 
                        // end column
6236
 
                        $cborder = $border_end;
6237
 
                        $h = $currentY - $this->y;
6238
 
                        if ($resth > $h) {
6239
 
                            $h = $resth;
6240
 
                        }
6241
 
                    } else {
6242
 
                        // middle column
6243
 
                        $cborder = $border_middle;
6244
 
                        $h = $this->h - $this->y - $this->bMargin;
6245
 
                        $resth -= $h;
6246
 
                    }
6247
 
                    $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6248
 
                } // end for each column
6249
 
            } else { // middle page
6250
 
                for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
6251
 
                    $this->selectColumn($column);
6252
 
                    if ($this->rtl) {
6253
 
                        $this->x -= $mc_margin['R'];
6254
 
                    } else {
6255
 
                        $this->x += $mc_margin['L'];
6256
 
                    }
6257
 
                    $cborder = $border_middle;
6258
 
                    $h = $this->h - $this->y - $this->bMargin;
6259
 
                    $resth -= $h;
6260
 
                    $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6261
 
                } // end for each column
6262
 
            }
6263
 
            if ($cborder OR $fill) {
6264
 
                // draw border and fill
6265
 
                if ($this->inxobj) {
6266
 
                    // we are inside an XObject template
6267
 
                    if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
6268
 
                        $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
6269
 
                        $pagemark = &$this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
6270
 
                    } else {
6271
 
                        $pagemark = &$this->xobjects[$this->xobjid]['intmrk'];
6272
 
                    }
6273
 
                    $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
6274
 
                    $pstart = substr($pagebuff, 0, $pagemark);
6275
 
                    $pend = substr($pagebuff, $pagemark);
6276
 
                    $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
6277
 
                    $pagemark += strlen($ccode);
6278
 
                } else {
6279
 
                    if (end($this->transfmrk[$this->page]) !== false) {
6280
 
                        $pagemarkkey = key($this->transfmrk[$this->page]);
6281
 
                        $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
6282
 
                    } elseif ($this->InFooter) {
6283
 
                        $pagemark = &$this->footerpos[$this->page];
6284
 
                    } else {
6285
 
                        $pagemark = &$this->intmrk[$this->page];
6286
 
                    }
6287
 
                    $pagebuff = $this->getPageBuffer($this->page);
6288
 
                    $pstart = substr($pagebuff, 0, $pagemark);
6289
 
                    $pend = substr($pagebuff, $pagemark);
6290
 
                    $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
6291
 
                    $pagemark += strlen($ccode);
6292
 
                }
6293
 
            }
6294
 
        } // end for each page
6295
 
        // Get end-of-cell Y position
6296
 
        $currentY = $this->GetY();
6297
 
        // restore original margin values
6298
 
        $this->SetLeftMargin($lMargin);
6299
 
        $this->SetRightMargin($rMargin);
6300
 
        if ($ln > 0) {
6301
 
            //Go to the beginning of the next line
6302
 
            $this->SetY($currentY + $mc_margin['B']);
6303
 
            if ($ln == 2) {
6304
 
                $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6305
 
            }
6306
 
        } else {
6307
 
            // go left or right by case
6308
 
            $this->setPage($startpage);
6309
 
            $this->y = $y;
6310
 
            $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6311
 
        }
6312
 
        $this->setContentMark();
6313
 
        $this->cell_padding = $prev_cell_padding;
6314
 
        $this->cell_margin = $prev_cell_margin;
6315
 
        return $nl;
6316
 
    }
6317
 
 
6318
 
    /**
6319
 
     * Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages)
6320
 
     * @param $brd (mixed) Indicates if borders must be drawn around the cell block. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
6321
 
     * @param $position (string) multicell position: 'start', 'middle', 'end'
6322
 
     * @return border mode array
6323
 
     * @protected
6324
 
     * @since 4.4.002 (2008-12-09)
6325
 
     */
6326
 
    protected function getBorderMode($brd, $position='start') {
6327
 
        if ((!$this->opencell) OR empty($brd)) {
6328
 
            return $brd;
6329
 
        }
6330
 
        if ($brd == 1) {
6331
 
            $brd = 'LTRB';
6332
 
        }
6333
 
        if (is_string($brd)) {
6334
 
            // convert string to array
6335
 
            $slen = strlen($brd);
6336
 
            $newbrd = array();
6337
 
            for ($i = 0; $i < $slen; ++$i) {
6338
 
                $newbrd[$brd{$i}] = array('cap' => 'square', 'join' => 'miter');
6339
 
            }
6340
 
            $brd = $newbrd;
6341
 
        }
6342
 
        foreach ($brd as $border => $style) {
6343
 
            switch ($position) {
6344
 
                case 'start': {
6345
 
                    if (strpos($border, 'B') !== false) {
6346
 
                        // remove bottom line
6347
 
                        $newkey = str_replace('B', '', $border);
6348
 
                        if (strlen($newkey) > 0) {
6349
 
                            $brd[$newkey] = $style;
6350
 
                        }
6351
 
                        unset($brd[$border]);
6352
 
                    }
6353
 
                    break;
6354
 
                }
6355
 
                case 'middle': {
6356
 
                    if (strpos($border, 'B') !== false) {
6357
 
                        // remove bottom line
6358
 
                        $newkey = str_replace('B', '', $border);
6359
 
                        if (strlen($newkey) > 0) {
6360
 
                            $brd[$newkey] = $style;
6361
 
                        }
6362
 
                        unset($brd[$border]);
6363
 
                        $border = $newkey;
6364
 
                    }
6365
 
                    if (strpos($border, 'T') !== false) {
6366
 
                        // remove bottom line
6367
 
                        $newkey = str_replace('T', '', $border);
6368
 
                        if (strlen($newkey) > 0) {
6369
 
                            $brd[$newkey] = $style;
6370
 
                        }
6371
 
                        unset($brd[$border]);
6372
 
                    }
6373
 
                    break;
6374
 
                }
6375
 
                case 'end': {
6376
 
                    if (strpos($border, 'T') !== false) {
6377
 
                        // remove bottom line
6378
 
                        $newkey = str_replace('T', '', $border);
6379
 
                        if (strlen($newkey) > 0) {
6380
 
                            $brd[$newkey] = $style;
6381
 
                        }
6382
 
                        unset($brd[$border]);
6383
 
                    }
6384
 
                    break;
6385
 
                }
6386
 
            }
6387
 
        }
6388
 
        return $brd;
6389
 
    }
6390
 
 
6391
 
    /**
6392
 
     * This method return the estimated number of lines for print a simple text string using Multicell() method.
6393
 
     * @param $txt (string) String for calculating his height
6394
 
     * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
6395
 
     * @param $reseth (boolean) if true reset the last cell height (default false).
6396
 
     * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
6397
 
     * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
6398
 
     * @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)))
6399
 
     * @return float Return the minimal height needed for multicell method for printing the $txt param.
6400
 
     * @author Alexander Escalona Fern�ndez, Nicola Asuni
6401
 
     * @public
6402
 
     * @since 4.5.011
6403
 
     */
6404
 
    public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6405
 
        if ($txt === '') {
6406
 
            // empty string
6407
 
            return 1;
6408
 
        }
6409
 
        // adjust internal padding
6410
 
        $prev_cell_padding = $this->cell_padding;
6411
 
        $prev_lasth = $this->lasth;
6412
 
        if (is_array($cellpadding)) {
6413
 
            $this->cell_padding = $cellpadding;
6414
 
        }
6415
 
        $this->adjustCellPadding($border);
6416
 
        if ($this->empty_string($w) OR ($w <= 0)) {
6417
 
            if ($this->rtl) {
6418
 
                $w = $this->x - $this->lMargin;
6419
 
            } else {
6420
 
                $w = $this->w - $this->rMargin - $this->x;
6421
 
            }
6422
 
        }
6423
 
        $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6424
 
        if ($reseth) {
6425
 
            // reset row height
6426
 
            $this->resetLastH();
6427
 
        }
6428
 
        $lines = 1;
6429
 
        $sum = 0;
6430
 
        $chars = $this->utf8Bidi($this->UTF8StringToArray($txt), $txt, $this->tmprtl);
6431
 
        $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
6432
 
        $length = count($chars);
6433
 
        $lastSeparator = -1;
6434
 
        for ($i = 0; $i < $length; ++$i) {
6435
 
            $charWidth = $charsWidth[$i];
6436
 
            if (preg_match($this->re_spaces, $this->unichr($chars[$i]))) {
6437
 
                $lastSeparator = $i;
6438
 
            }
6439
 
            if ((($sum + $charWidth) > $wmax) OR ($chars[$i] == 10)) {
6440
 
                ++$lines;
6441
 
                if ($lastSeparator != -1) {
6442
 
                    $i = $lastSeparator;
6443
 
                    $lastSeparator = -1;
6444
 
                    $sum = 0;
6445
 
                } else {
6446
 
                    $sum = $charWidth;
6447
 
                }
6448
 
            } else {
6449
 
                $sum += $charWidth;
6450
 
            }
6451
 
        }
6452
 
        if ($chars[($length - 1)] == 10) {
6453
 
            --$lines;
6454
 
        }
6455
 
        $this->cell_padding = $prev_cell_padding;
6456
 
        $this->lasth = $prev_lasth;
6457
 
        return $lines;
6458
 
    }
6459
 
 
6460
 
    /**
6461
 
     * This method return the estimated needed height for print a simple text string in Multicell() method.
6462
 
     * Generally, if you want to know the exact height for a block of content you can use the following alternative technique:
6463
 
     * @pre
6464
 
     *  // store current object
6465
 
     *  $pdf->startTransaction();
6466
 
     *  // store starting values
6467
 
     *  $start_y = $pdf->GetY();
6468
 
     *  $start_page = $pdf->getPage();
6469
 
     *  // call your printing functions with your parameters
6470
 
     *  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6471
 
     *  $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);
6472
 
     *  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6473
 
     *  // get the new Y
6474
 
     *  $end_y = $pdf->GetY();
6475
 
     *  $end_page = $pdf->getPage();
6476
 
     *  // calculate height
6477
 
     *  $height = 0;
6478
 
     *  if ($end_page == $start_page) {
6479
 
     *      $height = $end_y - $start_y;
6480
 
     *  } else {
6481
 
     *      for ($page=$start_page; $page <= $end_page; ++$page) {
6482
 
     *          $this->setPage($page);
6483
 
     *          if ($page == $start_page) {
6484
 
     *              // first page
6485
 
     *              $height = $this->h - $start_y - $this->bMargin;
6486
 
     *          } elseif ($page == $end_page) {
6487
 
     *              // last page
6488
 
     *              $height = $end_y - $this->tMargin;
6489
 
     *          } else {
6490
 
     *              $height = $this->h - $this->tMargin - $this->bMargin;
6491
 
     *          }
6492
 
     *      }
6493
 
     *  }
6494
 
     *  // restore previous object
6495
 
     *  $pdf = $pdf->rollbackTransaction();
6496
 
     *
6497
 
     * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
6498
 
     * @param $txt (string) String for calculating his height
6499
 
     * @param $reseth (boolean) if true reset the last cell height (default false).
6500
 
     * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
6501
 
     * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
6502
 
     * @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)))
6503
 
     * @return float Return the minimal height needed for multicell method for printing the $txt param.
6504
 
     * @author Nicola Asuni, Alexander Escalona Fern�ndez
6505
 
     * @public
6506
 
     */
6507
 
    public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6508
 
        // adjust internal padding
6509
 
        $prev_cell_padding = $this->cell_padding;
6510
 
        $prev_lasth = $this->lasth;
6511
 
        if (is_array($cellpadding)) {
6512
 
            $this->cell_padding = $cellpadding;
6513
 
        }
6514
 
        $this->adjustCellPadding($border);
6515
 
        $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
6516
 
        $height = $lines * ($this->FontSize * $this->cell_height_ratio);
6517
 
        if ($autopadding) {
6518
 
            // add top and bottom padding
6519
 
            $height += ($this->cell_padding['T'] + $this->cell_padding['B']);
6520
 
        }
6521
 
        $this->cell_padding = $prev_cell_padding;
6522
 
        $this->lasth = $prev_lasth;
6523
 
        return $height;
6524
 
    }
6525
 
 
6526
 
    /**
6527
 
     * This method prints text from the current position.<br />
6528
 
     * @param $h (float) Line height
6529
 
     * @param $txt (string) String to print
6530
 
     * @param $link (mixed) URL or identifier returned by AddLink()
6531
 
     * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
6532
 
     * @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>
6533
 
     * @param $ln (boolean) if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
6534
 
     * @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.
6535
 
     * @param $firstline (boolean) if true prints only the first line and return the remaining string.
6536
 
     * @param $firstblock (boolean) if true the string is the starting of a line.
6537
 
     * @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.
6538
 
     * @param $wadj (float) first line width will be reduced by this amount (used in HTML mode).
6539
 
     * @param $margin (array) margin array of the parent container
6540
 
     * @return mixed Return the number of cells or the remaining string if $firstline = true.
6541
 
     * @public
6542
 
     * @since 1.5
6543
 
     */
6544
 
    public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') {
6545
 
        // check page for no-write regions and adapt page margins if necessary
6546
 
        $this->checkPageRegions($h, $this->x, $this->y);
6547
 
        if (strlen($txt) == 0) {
6548
 
            // fix empty text
6549
 
            $txt = ' ';
6550
 
        }
6551
 
        if ($margin === '') {
6552
 
            // set default margins
6553
 
            $margin = $this->cell_margin;
6554
 
        }
6555
 
        // remove carriage returns
6556
 
        $s = str_replace("\r", '', $txt);
6557
 
        // check if string contains arabic text
6558
 
        if (preg_match($this->unicode->uni_RE_PATTERN_ARABIC, $s)) {
6559
 
            $arabic = true;
6560
 
        } else {
6561
 
            $arabic = false;
6562
 
        }
6563
 
        // check if string contains RTL text
6564
 
        if ($arabic OR ($this->tmprtl == 'R') OR preg_match($this->unicode->uni_RE_PATTERN_RTL, $s)) {
6565
 
            $rtlmode = true;
6566
 
        } else {
6567
 
            $rtlmode = false;
6568
 
        }
6569
 
        // get a char width
6570
 
        $chrwidth = $this->GetCharWidth('.');
6571
 
        // get array of unicode values
6572
 
        $chars = $this->UTF8StringToArray($s);
6573
 
        // get array of chars
6574
 
        $uchars = $this->UTF8ArrayToUniArray($chars);
6575
 
        // get the number of characters
6576
 
        $nb = count($chars);
6577
 
        // replacement for SHY character (minus symbol)
6578
 
        $shy_replacement = 45;
6579
 
        $shy_replacement_char = $this->unichr($shy_replacement);
6580
 
        // widht for SHY replacement
6581
 
        $shy_replacement_width = $this->GetCharWidth($shy_replacement);
6582
 
        // max Y
6583
 
        $maxy = $this->y + $maxh - $h - $this->cell_padding['T'] - $this->cell_padding['B'];
6584
 
        // calculate remaining line width ($w)
6585
 
        if ($this->rtl) {
6586
 
            $w = $this->x - $this->lMargin;
6587
 
        } else {
6588
 
            $w = $this->w - $this->rMargin - $this->x;
6589
 
        }
6590
 
        // max column width
6591
 
        $wmax = $w - $wadj;
6592
 
        if (!$firstline) {
6593
 
            $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']);
6594
 
        }
6595
 
        if ((!$firstline) AND (($chrwidth > $wmax) OR ($this->GetCharWidth($chars[0]) > $wmax))) {
6596
 
            // a single character do not fit on column
6597
 
            return '';
6598
 
        }
6599
 
        // minimum row height
6600
 
        $row_height = max($h, $this->FontSize * $this->cell_height_ratio);
6601
 
        $start_page = $this->page;
6602
 
        $i = 0; // character position
6603
 
        $j = 0; // current starting position
6604
 
        $sep = -1; // position of the last blank space
6605
 
        $shy = false; // true if the last blank is a soft hypen (SHY)
6606
 
        $l = 0; // current string length
6607
 
        $nl = 0; //number of lines
6608
 
        $linebreak = false;
6609
 
        $pc = 0; // previous character
6610
 
        // for each character
6611
 
        while ($i < $nb) {
6612
 
            if (($maxh > 0) AND ($this->y >= $maxy) ) {
6613
 
                break;
6614
 
            }
6615
 
            //Get the current character
6616
 
            $c = $chars[$i];
6617
 
            if ($c == 10) { // 10 = "\n" = new line
6618
 
                //Explicit line break
6619
 
                if ($align == 'J') {
6620
 
                    if ($this->rtl) {
6621
 
                        $talign = 'R';
6622
 
                    } else {
6623
 
                        $talign = 'L';
6624
 
                    }
6625
 
                } else {
6626
 
                    $talign = $align;
6627
 
                }
6628
 
                $tmpstr = $this->UniArrSubString($uchars, $j, $i);
6629
 
                if ($firstline) {
6630
 
                    $startx = $this->x;
6631
 
                    $tmparr = array_slice($chars, $j, ($i - $j));
6632
 
                    if ($rtlmode) {
6633
 
                        $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
6634
 
                    }
6635
 
                    $linew = $this->GetArrStringWidth($tmparr);
6636
 
                    unset($tmparr);
6637
 
                    if ($this->rtl) {
6638
 
                        $this->endlinex = $startx - $linew;
6639
 
                    } else {
6640
 
                        $this->endlinex = $startx + $linew;
6641
 
                    }
6642
 
                    $w = $linew;
6643
 
                    $tmpcellpadding = $this->cell_padding;
6644
 
                    if ($maxh == 0) {
6645
 
                        $this->SetCellPadding(0);
6646
 
                    }
6647
 
                }
6648
 
                if ($firstblock AND $this->isRTLTextDir()) {
6649
 
                    $tmpstr = $this->stringRightTrim($tmpstr);
6650
 
                }
6651
 
                // Skip newlines at the begining of a page or column
6652
 
                if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) {
6653
 
                    $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
6654
 
                }
6655
 
                unset($tmpstr);
6656
 
                if ($firstline) {
6657
 
                    $this->cell_padding = $tmpcellpadding;
6658
 
                    return ($this->UniArrSubString($uchars, $i));
6659
 
                }
6660
 
                ++$nl;
6661
 
                $j = $i + 1;
6662
 
                $l = 0;
6663
 
                $sep = -1;
6664
 
                $shy = false;
6665
 
                // account for margin changes
6666
 
                if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
6667
 
                    $this->AcceptPageBreak();
6668
 
                    if ($this->rtl) {
6669
 
                        $this->x -= $margin['R'];
6670
 
                    } else {
6671
 
                        $this->x += $margin['L'];
6672
 
                    }
6673
 
                    $this->lMargin += $margin['L'];
6674
 
                    $this->rMargin += $margin['R'];
6675
 
                }
6676
 
                $w = $this->getRemainingWidth();
6677
 
                $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6678
 
            } else {
6679
 
                // 160 is the non-breaking space.
6680
 
                // 173 is SHY (Soft Hypen).
6681
 
                // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
6682
 
                // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
6683
 
                // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
6684
 
                if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) {
6685
 
                    // update last blank space position
6686
 
                    $sep = $i;
6687
 
                    // check if is a SHY
6688
 
                    if ($c == 173) {
6689
 
                        $shy = true;
6690
 
                        if ($pc == 45) {
6691
 
                            $tmp_shy_replacement_width = 0;
6692
 
                            $tmp_shy_replacement_char = '';
6693
 
                        } else {
6694
 
                            $tmp_shy_replacement_width = $shy_replacement_width;
6695
 
                            $tmp_shy_replacement_char = $shy_replacement_char;
6696
 
                        }
6697
 
                    } else {
6698
 
                        $shy = false;
6699
 
                    }
6700
 
                }
6701
 
                // update string length
6702
 
                if ($this->isUnicodeFont() AND ($arabic)) {
6703
 
                    // with bidirectional algorithm some chars may be changed affecting the line length
6704
 
                    // *** very slow ***
6705
 
                    $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl));
6706
 
                } else {
6707
 
                    $l += $this->GetCharWidth($c);
6708
 
                }
6709
 
                if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) ) {
6710
 
                    // we have reached the end of column
6711
 
                    if ($sep == -1) {
6712
 
                        // check if the line was already started
6713
 
                        if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth)))
6714
 
                            OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) {
6715
 
                            // print a void cell and go to next line
6716
 
                            $this->Cell($w, $h, '', 0, 1);
6717
 
                            $linebreak = true;
6718
 
                            if ($firstline) {
6719
 
                                return ($this->UniArrSubString($uchars, $j));
6720
 
                            }
6721
 
                        } else {
6722
 
                            // truncate the word because do not fit on column
6723
 
                            $tmpstr = $this->UniArrSubString($uchars, $j, $i);
6724
 
                            if ($firstline) {
6725
 
                                $startx = $this->x;
6726
 
                                $tmparr = array_slice($chars, $j, ($i - $j));
6727
 
                                if ($rtlmode) {
6728
 
                                    $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
6729
 
                                }
6730
 
                                $linew = $this->GetArrStringWidth($tmparr);
6731
 
                                unset($tmparr);
6732
 
                                if ($this->rtl) {
6733
 
                                    $this->endlinex = $startx - $linew;
6734
 
                                } else {
6735
 
                                    $this->endlinex = $startx + $linew;
6736
 
                                }
6737
 
                                $w = $linew;
6738
 
                                $tmpcellpadding = $this->cell_padding;
6739
 
                                if ($maxh == 0) {
6740
 
                                    $this->SetCellPadding(0);
6741
 
                                }
6742
 
                            }
6743
 
                            if ($firstblock AND $this->isRTLTextDir()) {
6744
 
                                $tmpstr = $this->stringRightTrim($tmpstr);
6745
 
                            }
6746
 
                            $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6747
 
                            unset($tmpstr);
6748
 
                            if ($firstline) {
6749
 
                                $this->cell_padding = $tmpcellpadding;
6750
 
                                return ($this->UniArrSubString($uchars, $i));
6751
 
                            }
6752
 
                            $j = $i;
6753
 
                            --$i;
6754
 
                        }
6755
 
                    } else {
6756
 
                        // word wrapping
6757
 
                        if ($this->rtl AND (!$firstblock) AND ($sep < $i)) {
6758
 
                            $endspace = 1;
6759
 
                        } else {
6760
 
                            $endspace = 0;
6761
 
                        }
6762
 
                        if ($shy) {
6763
 
                            // add hypen (minus symbol) at the end of the line
6764
 
                            $shy_width = $tmp_shy_replacement_width;
6765
 
                            if ($this->rtl) {
6766
 
                                $shy_char_left = $tmp_shy_replacement_char;
6767
 
                                $shy_char_right = '';
6768
 
                            } else {
6769
 
                                $shy_char_left = '';
6770
 
                                $shy_char_right = $tmp_shy_replacement_char;
6771
 
                            }
6772
 
                        } else {
6773
 
                            $shy_width = 0;
6774
 
                            $shy_char_left = '';
6775
 
                            $shy_char_right = '';
6776
 
                        }
6777
 
                        $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace));
6778
 
                        if ($firstline) {
6779
 
                            $startx = $this->x;
6780
 
                            $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j));
6781
 
                            if ($rtlmode) {
6782
 
                                $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
6783
 
                            }
6784
 
                            $linew = $this->GetArrStringWidth($tmparr);
6785
 
                            unset($tmparr);
6786
 
                            if ($this->rtl) {
6787
 
                                $this->endlinex = $startx - $linew - $shy_width;
6788
 
                            } else {
6789
 
                                $this->endlinex = $startx + $linew + $shy_width;
6790
 
                            }
6791
 
                            $w = $linew;
6792
 
                            $tmpcellpadding = $this->cell_padding;
6793
 
                            if ($maxh == 0) {
6794
 
                                $this->SetCellPadding(0);
6795
 
                            }
6796
 
                        }
6797
 
                        // print the line
6798
 
                        if ($firstblock AND $this->isRTLTextDir()) {
6799
 
                            $tmpstr = $this->stringRightTrim($tmpstr);
6800
 
                        }
6801
 
                        $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
6802
 
                        unset($tmpstr);
6803
 
                        if ($firstline) {
6804
 
                            // return the remaining text
6805
 
                            $this->cell_padding = $tmpcellpadding;
6806
 
                            return ($this->UniArrSubString($uchars, ($sep + $endspace)));
6807
 
                        }
6808
 
                        $i = $sep;
6809
 
                        $sep = -1;
6810
 
                        $shy = false;
6811
 
                        $j = ($i+1);
6812
 
                    }
6813
 
                    // account for margin changes
6814
 
                    if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
6815
 
                        $this->AcceptPageBreak();
6816
 
                        if ($this->rtl) {
6817
 
                            $this->x -= $margin['R'];
6818
 
                        } else {
6819
 
                            $this->x += $margin['L'];
6820
 
                        }
6821
 
                        $this->lMargin += $margin['L'];
6822
 
                        $this->rMargin += $margin['R'];
6823
 
                    }
6824
 
                    $w = $this->getRemainingWidth();
6825
 
                    $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6826
 
                    if ($linebreak) {
6827
 
                        $linebreak = false;
6828
 
                    } else {
6829
 
                        ++$nl;
6830
 
                        $l = 0;
6831
 
                    }
6832
 
                }
6833
 
            }
6834
 
            // save last character
6835
 
            $pc = $c;
6836
 
            ++$i;
6837
 
        } // end while i < nb
6838
 
        // print last substring (if any)
6839
 
        if ($l > 0) {
6840
 
            switch ($align) {
6841
 
                case 'J':
6842
 
                case 'C': {
6843
 
                    $w = $w;
6844
 
                    break;
6845
 
                }
6846
 
                case 'L': {
6847
 
                    if ($this->rtl) {
6848
 
                        $w = $w;
6849
 
                    } else {
6850
 
                        $w = $l;
6851
 
                    }
6852
 
                    break;
6853
 
                }
6854
 
                case 'R': {
6855
 
                    if ($this->rtl) {
6856
 
                        $w = $l;
6857
 
                    } else {
6858
 
                        $w = $w;
6859
 
                    }
6860
 
                    break;
6861
 
                }
6862
 
                default: {
6863
 
                    $w = $l;
6864
 
                    break;
6865
 
                }
6866
 
            }
6867
 
            $tmpstr = $this->UniArrSubString($uchars, $j, $nb);
6868
 
            if ($firstline) {
6869
 
                $startx = $this->x;
6870
 
                $tmparr = array_slice($chars, $j, ($nb - $j));
6871
 
                if ($rtlmode) {
6872
 
                    $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
6873
 
                }
6874
 
                $linew = $this->GetArrStringWidth($tmparr);
6875
 
                unset($tmparr);
6876
 
                if ($this->rtl) {
6877
 
                    $this->endlinex = $startx - $linew;
6878
 
                } else {
6879
 
                    $this->endlinex = $startx + $linew;
6880
 
                }
6881
 
                $w = $linew;
6882
 
                $tmpcellpadding = $this->cell_padding;
6883
 
                if ($maxh == 0) {
6884
 
                    $this->SetCellPadding(0);
6885
 
                }
6886
 
            }
6887
 
            if ($firstblock AND $this->isRTLTextDir()) {
6888
 
                $tmpstr = $this->stringRightTrim($tmpstr);
6889
 
            }
6890
 
            $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
6891
 
            unset($tmpstr);
6892
 
            if ($firstline) {
6893
 
                $this->cell_padding = $tmpcellpadding;
6894
 
                return ($this->UniArrSubString($uchars, $nb));
6895
 
            }
6896
 
            ++$nl;
6897
 
        }
6898
 
        if ($firstline) {
6899
 
            return '';
6900
 
        }
6901
 
        return $nl;
6902
 
    }
6903
 
 
6904
 
    /**
6905
 
     * Returns the remaining width between the current position and margins.
6906
 
     * @return int Return the remaining width
6907
 
     * @protected
6908
 
     */
6909
 
    protected function getRemainingWidth() {
6910
 
        $this->checkPageRegions(0, $this->x, $this->y);
6911
 
        if ($this->rtl) {
6912
 
            return ($this->x - $this->lMargin);
6913
 
        } else {
6914
 
            return ($this->w - $this->rMargin - $this->x);
6915
 
        }
6916
 
    }
6917
 
 
6918
 
    /**
6919
 
     * Extract a slice of the $strarr array and return it as string.
6920
 
     * @param $strarr (string) The input array of characters.
6921
 
     * @param $start (int) the starting element of $strarr.
6922
 
     * @param $end (int) first element that will not be returned.
6923
 
     * @return Return part of a string
6924
 
     * @public
6925
 
     */
6926
 
    public function UTF8ArrSubString($strarr, $start='', $end='') {
6927
 
        if (strlen($start) == 0) {
6928
 
            $start = 0;
6929
 
        }
6930
 
        if (strlen($end) == 0) {
6931
 
            $end = count($strarr);
6932
 
        }
6933
 
        $string = '';
6934
 
        for ($i=$start; $i < $end; ++$i) {
6935
 
            $string .= $this->unichr($strarr[$i]);
6936
 
        }
6937
 
        return $string;
6938
 
    }
6939
 
 
6940
 
    /**
6941
 
     * Extract a slice of the $uniarr array and return it as string.
6942
 
     * @param $uniarr (string) The input array of characters.
6943
 
     * @param $start (int) the starting element of $strarr.
6944
 
     * @param $end (int) first element that will not be returned.
6945
 
     * @return Return part of a string
6946
 
     * @public
6947
 
     * @since 4.5.037 (2009-04-07)
6948
 
     */
6949
 
    public function UniArrSubString($uniarr, $start='', $end='') {
6950
 
        if (strlen($start) == 0) {
6951
 
            $start = 0;
6952
 
        }
6953
 
        if (strlen($end) == 0) {
6954
 
            $end = count($uniarr);
6955
 
        }
6956
 
        $string = '';
6957
 
        for ($i=$start; $i < $end; ++$i) {
6958
 
            $string .= $uniarr[$i];
6959
 
        }
6960
 
        return $string;
6961
 
    }
6962
 
 
6963
 
    /**
6964
 
     * Convert an array of UTF8 values to array of unicode characters
6965
 
     * @param $ta (string) The input array of UTF8 values.
6966
 
     * @return Return array of unicode characters
6967
 
     * @public
6968
 
     * @since 4.5.037 (2009-04-07)
6969
 
     */
6970
 
    public function UTF8ArrayToUniArray($ta) {
6971
 
        return array_map(array($this, 'unichr'), $ta);
6972
 
    }
6973
 
 
6974
 
    /**
6975
 
     * Returns the unicode caracter specified by UTF-8 value
6976
 
     * @param $c (int) UTF-8 value
6977
 
     * @return Returns the specified character.
6978
 
     * @author Miguel Perez, Nicola Asuni
6979
 
     * @public
6980
 
     * @since 2.3.000 (2008-03-05)
6981
 
     */
6982
 
    public function unichr($c) {
6983
 
        if (!$this->isunicode) {
6984
 
            return chr($c);
6985
 
        } elseif ($c <= 0x7F) {
6986
 
            // one byte
6987
 
            return chr($c);
6988
 
        } elseif ($c <= 0x7FF) {
6989
 
            // two bytes
6990
 
            return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
6991
 
        } elseif ($c <= 0xFFFF) {
6992
 
            // three bytes
6993
 
            return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
6994
 
        } elseif ($c <= 0x10FFFF) {
6995
 
            // four bytes
6996
 
            return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
6997
 
        } else {
6998
 
            return '';
6999
 
        }
7000
 
    }
7001
 
 
7002
 
    /**
7003
 
     * Return the image type given the file name or array returned by getimagesize() function.
7004
 
     * @param $imgfile (string) image file name
7005
 
     * @param $iminfo (array) array of image information returned by getimagesize() function.
7006
 
     * @return string image type
7007
 
     * @since 4.8.017 (2009-11-27)
7008
 
     */
7009
 
    public function getImageFileType($imgfile, $iminfo=array()) {
7010
 
        $type = '';
7011
 
        if (isset($iminfo['mime']) AND !empty($iminfo['mime'])) {
7012
 
            $mime = explode('/', $iminfo['mime']);
7013
 
            if ((count($mime) > 1) AND ($mime[0] == 'image') AND (!empty($mime[1]))) {
7014
 
                $type = strtolower(trim($mime[1]));
7015
 
            }
7016
 
        }
7017
 
        if (empty($type)) {
7018
 
            $fileinfo = pathinfo($imgfile);
7019
 
            if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
7020
 
                $type = strtolower(trim($fileinfo['extension']));
7021
 
            }
7022
 
        }
7023
 
        if ($type == 'jpg') {
7024
 
            $type = 'jpeg';
7025
 
        }
7026
 
        return $type;
7027
 
    }
7028
 
 
7029
 
    /**
7030
 
     * Set the block dimensions accounting for page breaks and page/column fitting
7031
 
     * @param $w (float) width
7032
 
     * @param $h (float) height
7033
 
     * @param $x (float) X coordinate
7034
 
     * @param $y (float) Y coodiante
7035
 
     * @param $fitonpage (boolean) if true the block is resized to not exceed page dimensions.
7036
 
     * @protected
7037
 
     * @since 5.5.009 (2010-07-05)
7038
 
     */
7039
 
    protected function fitBlock(&$w, &$h, &$x, &$y, $fitonpage=false) {
7040
 
        if ($w <= 0) {
7041
 
            // set maximum width
7042
 
            $w = ($this->w - $this->lMargin - $this->rMargin);
7043
 
        }
7044
 
        if ($h <= 0) {
7045
 
            // set maximum height
7046
 
            $h = ($this->PageBreakTrigger - $this->tMargin);
7047
 
        }
7048
 
        // resize the block to be vertically contained on a single page or single column
7049
 
        if ($fitonpage OR $this->AutoPageBreak) {
7050
 
            $ratio_wh = ($w / $h);
7051
 
            if ($h > ($this->PageBreakTrigger - $this->tMargin)) {
7052
 
                $h = $this->PageBreakTrigger - $this->tMargin;
7053
 
                $w = ($h * $ratio_wh);
7054
 
            }
7055
 
            // resize the block to be horizontally contained on a single page or single column
7056
 
            if ($fitonpage) {
7057
 
                $maxw = ($this->w - $this->lMargin - $this->rMargin);
7058
 
                if ($w > $maxw) {
7059
 
                    $w = $maxw;
7060
 
                    $h = ($w / $ratio_wh);
7061
 
                }
7062
 
            }
7063
 
        }
7064
 
        // Check whether we need a new page or new column first as this does not fit
7065
 
        $prev_x = $this->x;
7066
 
        $prev_y = $this->y;
7067
 
        if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) {
7068
 
            $y = $this->y;
7069
 
            if ($this->rtl) {
7070
 
                $x += ($prev_x - $this->x);
7071
 
            } else {
7072
 
                $x += ($this->x - $prev_x);
7073
 
            }
7074
 
        }
7075
 
        // resize the block to be contained on the remaining available page or column space
7076
 
        if ($fitonpage) {
7077
 
            $ratio_wh = ($w / $h);
7078
 
            if (($y + $h) > $this->PageBreakTrigger) {
7079
 
                $h = $this->PageBreakTrigger - $y;
7080
 
                $w = ($h * $ratio_wh);
7081
 
            }
7082
 
            if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) {
7083
 
                $w = $this->w - $this->rMargin - $x;
7084
 
                $h = ($w / $ratio_wh);
7085
 
            } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) {
7086
 
                $w = $x - $this->lMargin;
7087
 
                $h = ($w / $ratio_wh);
7088
 
            }
7089
 
        }
7090
 
    }
7091
 
 
7092
 
    /**
7093
 
     * Puts an image in the page.
7094
 
     * The upper-left corner must be given.
7095
 
     * The dimensions can be specified in different ways:<ul>
7096
 
     * <li>explicit width and height (expressed in user unit)</li>
7097
 
     * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
7098
 
     * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
7099
 
     * 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;
7100
 
     * The format can be specified explicitly or inferred from the file extension.<br />
7101
 
     * It is possible to put a link on the image.<br />
7102
 
     * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
7103
 
     * @param $file (string) Name of the file containing the image or a '@' character followed by the image data string.
7104
 
     * @param $x (float) Abscissa of the upper-left corner (LTR) or upper-right corner (RTL).
7105
 
     * @param $y (float) Ordinate of the upper-left corner (LTR) or upper-right corner (RTL).
7106
 
     * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
7107
 
     * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
7108
 
     * @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.
7109
 
     * @param $link (mixed) URL or identifier returned by AddLink().
7110
 
     * @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>
7111
 
     * @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).
7112
 
     * @param $dpi (int) dot-per-inch resolution used on resize
7113
 
     * @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>
7114
 
     * @param $ismask (boolean) true if this image is a mask, false otherwise
7115
 
     * @param $imgmask (mixed) image object returned by this function or false
7116
 
     * @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)))
7117
 
     * @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).
7118
 
     * @param $hidden (boolean) if true do not display the image.
7119
 
     * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
7120
 
     * @return image information
7121
 
     * @public
7122
 
     * @since 1.1
7123
 
     */
7124
 
    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) {
7125
 
        if ($x === '') {
7126
 
            $x = $this->x;
7127
 
        }
7128
 
        if ($y === '') {
7129
 
            $y = $this->y;
7130
 
        }
7131
 
        // check page for no-write regions and adapt page margins if necessary
7132
 
        $this->checkPageRegions($h, $x, $y);
7133
 
        $cached_file = false; // true when the file is cached
7134
 
        // check if we are passing an image as file or string
7135
 
        if ($file{0} === '@') { // image from string
7136
 
            $imgdata = substr($file, 1);
7137
 
            $file = tempnam(K_PATH_CACHE, 'img_');
7138
 
            $fp = fopen($file, 'w');
7139
 
            fwrite($fp, $imgdata);
7140
 
            fclose($fp);
7141
 
            unset($imgdata);
7142
 
            $cached_file = true;
7143
 
            $imsize = @getimagesize($file);
7144
 
            if ($imsize === FALSE) {
7145
 
                unlink($file);
7146
 
                $cached_file = false;
7147
 
            }
7148
 
        } else { // image file
7149
 
            // check if is local file
7150
 
            if (!@file_exists($file)) {
7151
 
                // encode spaces on filename (file is probably an URL)
7152
 
                $file = str_replace(' ', '%20', $file);
7153
 
            }
7154
 
            // get image dimensions
7155
 
            $imsize = @getimagesize($file);
7156
 
            if ($imsize === FALSE) {
7157
 
                if (function_exists('curl_init')) {
7158
 
                    // try to get remote file data using cURL
7159
 
                    $cs = curl_init(); // curl session
7160
 
                    curl_setopt($cs, CURLOPT_URL, $file);
7161
 
                    curl_setopt($cs, CURLOPT_BINARYTRANSFER, true);
7162
 
                    curl_setopt($cs, CURLOPT_FAILONERROR, true);
7163
 
                    curl_setopt($cs, CURLOPT_RETURNTRANSFER, true);
7164
 
                    curl_setopt($cs, CURLOPT_CONNECTTIMEOUT, 5);
7165
 
                    curl_setopt($cs, CURLOPT_TIMEOUT, 30);
7166
 
                    $imgdata = curl_exec($cs);
7167
 
                    curl_close($cs);
7168
 
                    if($imgdata !== FALSE) {
7169
 
                        // copy image to cache
7170
 
                        $file = tempnam(K_PATH_CACHE, 'img_');
7171
 
                        $fp = fopen($file, 'w');
7172
 
                        fwrite($fp, $imgdata);
7173
 
                        fclose($fp);
7174
 
                        unset($imgdata);
7175
 
                        $cached_file = true;
7176
 
                        $imsize = @getimagesize($file);
7177
 
                        if ($imsize === FALSE) {
7178
 
                            unlink($file);
7179
 
                            $cached_file = false;
7180
 
                        }
7181
 
                    }
7182
 
                } elseif (($w > 0) AND ($h > 0)) {
7183
 
                    // get measures from specified data
7184
 
                    $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
7185
 
                    $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
7186
 
                    $imsize = array($pw, $ph);
7187
 
                }
7188
 
            }
7189
 
        }
7190
 
        if ($imsize === FALSE) {
7191
 
            $this->Error('[Image] Unable to get image: '.$file);
7192
 
        }
7193
 
        // get original image width and height in pixels
7194
 
        list($pixw, $pixh) = $imsize;
7195
 
        // calculate image width and height on document
7196
 
        if (($w <= 0) AND ($h <= 0)) {
7197
 
            // convert image size to document unit
7198
 
            $w = $this->pixelsToUnits($pixw);
7199
 
            $h = $this->pixelsToUnits($pixh);
7200
 
        } elseif ($w <= 0) {
7201
 
            $w = $h * $pixw / $pixh;
7202
 
        } elseif ($h <= 0) {
7203
 
            $h = $w * $pixh / $pixw;
7204
 
        } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
7205
 
            if (strlen($fitbox) !== 2) {
7206
 
                // set default alignment
7207
 
                $fitbox = '--';
7208
 
            }
7209
 
            // scale image dimensions proportionally to fit within the ($w, $h) box
7210
 
            if ((($w * $pixh) / ($h * $pixw)) < 1) {
7211
 
                // store current height
7212
 
                $oldh = $h;
7213
 
                // calculate new height
7214
 
                $h = $w * $pixh / $pixw;
7215
 
                // height difference
7216
 
                $hdiff = ($oldh - $h);
7217
 
                // vertical alignment
7218
 
                switch (strtoupper($fitbox{1})) {
7219
 
                    case 'T': {
7220
 
                        break;
7221
 
                    }
7222
 
                    case 'M': {
7223
 
                        $y += ($hdiff / 2);
7224
 
                        break;
7225
 
                    }
7226
 
                    case 'B': {
7227
 
                        $y += $hdiff;
7228
 
                        break;
7229
 
                    }
7230
 
                }
7231
 
            } else {
7232
 
                // store current width
7233
 
                $oldw = $w;
7234
 
                // calculate new width
7235
 
                $w = $h * $pixw / $pixh;
7236
 
                // width difference
7237
 
                $wdiff = ($oldw - $w);
7238
 
                // horizontal alignment
7239
 
                switch (strtoupper($fitbox{0})) {
7240
 
                    case 'L': {
7241
 
                        if ($this->rtl) {
7242
 
                            $x -= $wdiff;
7243
 
                        }
7244
 
                        break;
7245
 
                    }
7246
 
                    case 'C': {
7247
 
                        if ($this->rtl) {
7248
 
                            $x -= ($wdiff / 2);
7249
 
                        } else {
7250
 
                            $x += ($wdiff / 2);
7251
 
                        }
7252
 
                        break;
7253
 
                    }
7254
 
                    case 'R': {
7255
 
                        if (!$this->rtl) {
7256
 
                            $x += $wdiff;
7257
 
                        }
7258
 
                        break;
7259
 
                    }
7260
 
                }
7261
 
            }
7262
 
        }
7263
 
        // fit the image on available space
7264
 
        $this->fitBlock($w, $h, $x, $y, $fitonpage);
7265
 
        // calculate new minimum dimensions in pixels
7266
 
        $neww = round($w * $this->k * $dpi / $this->dpi);
7267
 
        $newh = round($h * $this->k * $dpi / $this->dpi);
7268
 
        // check if resize is necessary (resize is used only to reduce the image)
7269
 
        $newsize = ($neww * $newh);
7270
 
        $pixsize = ($pixw * $pixh);
7271
 
        if (intval($resize) == 2) {
7272
 
            $resize = true;
7273
 
        } elseif ($newsize >= $pixsize) {
7274
 
            $resize = false;
7275
 
        }
7276
 
        // check if image has been already added on document
7277
 
        $newimage = true;
7278
 
        if (in_array($file, $this->imagekeys)) {
7279
 
            $newimage = false;
7280
 
            // get existing image data
7281
 
            $info = $this->getImageBuffer($file);
7282
 
            // check if the newer image is larger
7283
 
            $oldsize = ($info['w'] * $info['h']);
7284
 
            if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
7285
 
                $newimage = true;
7286
 
            }
7287
 
        }
7288
 
        if ($newimage) {
7289
 
            //First use of image, get info
7290
 
            $type = strtolower($type);
7291
 
            if ($type == '') {
7292
 
                $type = $this->getImageFileType($file, $imsize);
7293
 
            } elseif ($type == 'jpg') {
7294
 
                $type = 'jpeg';
7295
 
            }
7296
 
            $mqr = $this->get_mqr();
7297
 
            $this->set_mqr(false);
7298
 
            // Specific image handlers
7299
 
            $mtd = '_parse'.$type;
7300
 
            // GD image handler function
7301
 
            $gdfunction = 'imagecreatefrom'.$type;
7302
 
            $info = false;
7303
 
            if ((method_exists($this, $mtd)) AND (!($resize AND function_exists($gdfunction)))) {
7304
 
                // TCPDF image functions
7305
 
                $info = $this->$mtd($file);
7306
 
                if ($info == 'pngalpha') {
7307
 
                    return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign);
7308
 
                }
7309
 
            }
7310
 
            if (!$info) {
7311
 
                if (function_exists($gdfunction)) {
7312
 
                    // GD library
7313
 
                    $img = $gdfunction($file);
7314
 
                    if ($resize) {
7315
 
                        $imgr = imagecreatetruecolor($neww, $newh);
7316
 
                        if (($type == 'gif') OR ($type == 'png')) {
7317
 
                            $imgr = $this->_setGDImageTransparency($imgr, $img);
7318
 
                        }
7319
 
                        imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
7320
 
                        if (($type == 'gif') OR ($type == 'png')) {
7321
 
                            $info = $this->_toPNG($imgr);
7322
 
                        } else {
7323
 
                            $info = $this->_toJPEG($imgr);
7324
 
                        }
7325
 
                    } else {
7326
 
                        if (($type == 'gif') OR ($type == 'png')) {
7327
 
                            $info = $this->_toPNG($img);
7328
 
                        } else {
7329
 
                            $info = $this->_toJPEG($img);
7330
 
                        }
7331
 
                    }
7332
 
                } elseif (extension_loaded('imagick')) {
7333
 
                    // ImageMagick library
7334
 
                    $img = new Imagick();
7335
 
                    if ($type == 'SVG') {
7336
 
                        // get SVG file content
7337
 
                        $svgimg = file_get_contents($file);
7338
 
                        // get width and height
7339
 
                        $regs = array();
7340
 
                        if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) {
7341
 
                            $svgtag = $regs[1];
7342
 
                            $tmp = array();
7343
 
                            if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7344
 
                                $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7345
 
                                $owu = sprintf('%.3F', ($ow * $dpi / 72)).$this->pdfunit;
7346
 
                                $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
7347
 
                            } else {
7348
 
                                $ow = $w;
7349
 
                            }
7350
 
                            $tmp = array();
7351
 
                            if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7352
 
                                $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7353
 
                                $ohu = sprintf('%.3F', ($oh * $dpi / 72)).$this->pdfunit;
7354
 
                                $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
7355
 
                            } else {
7356
 
                                $oh = $h;
7357
 
                            }
7358
 
                            $tmp = array();
7359
 
                            if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
7360
 
                                $vbw = ($ow * $this->imgscale * $this->k);
7361
 
                                $vbh = ($oh * $this->imgscale * $this->k);
7362
 
                                $vbox = sprintf(' viewBox="0 0 %.3F %.3F" ', $vbw, $vbh);
7363
 
                                $svgtag = $vbox.$svgtag;
7364
 
                            }
7365
 
                            $svgimg = preg_replace('/<svg([^\>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
7366
 
                        }
7367
 
                        $img->readImageBlob($svgimg);
7368
 
                    } else {
7369
 
                        $img->readImage($file);
7370
 
                    }
7371
 
                    if ($resize) {
7372
 
                        $img->resizeImage($neww, $newh, 10, 1, false);
7373
 
                    }
7374
 
                    $img->setCompressionQuality($this->jpeg_quality);
7375
 
                    $img->setImageFormat('jpeg');
7376
 
                    $tempname = tempnam(K_PATH_CACHE, 'jpg_');
7377
 
                    $img->writeImage($tempname);
7378
 
                    $info = $this->_parsejpeg($tempname);
7379
 
                    unlink($tempname);
7380
 
                    $img->destroy();
7381
 
                } else {
7382
 
                    return;
7383
 
                }
7384
 
            }
7385
 
            if ($info === false) {
7386
 
                //If false, we cannot process image
7387
 
                return;
7388
 
            }
7389
 
            $this->set_mqr($mqr);
7390
 
            if ($ismask) {
7391
 
                // force grayscale
7392
 
                $info['cs'] = 'DeviceGray';
7393
 
            }
7394
 
            $info['i'] = $this->numimages;
7395
 
            if (!in_array($file, $this->imagekeys)) {
7396
 
                ++$info['i'];
7397
 
            }
7398
 
            if ($imgmask !== false) {
7399
 
                $info['masked'] = $imgmask;
7400
 
            }
7401
 
            // add image to document
7402
 
            $this->setImageBuffer($file, $info);
7403
 
        }
7404
 
        if ($cached_file) {
7405
 
            // remove cached file
7406
 
            unlink($file);
7407
 
        }
7408
 
        // set alignment
7409
 
        $this->img_rb_y = $y + $h;
7410
 
        // set alignment
7411
 
        if ($this->rtl) {
7412
 
            if ($palign == 'L') {
7413
 
                $ximg = $this->lMargin;
7414
 
            } elseif ($palign == 'C') {
7415
 
                $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7416
 
            } elseif ($palign == 'R') {
7417
 
                $ximg = $this->w - $this->rMargin - $w;
7418
 
            } else {
7419
 
                $ximg = $x - $w;
7420
 
            }
7421
 
            $this->img_rb_x = $ximg;
7422
 
        } else {
7423
 
            if ($palign == 'L') {
7424
 
                $ximg = $this->lMargin;
7425
 
            } elseif ($palign == 'C') {
7426
 
                $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7427
 
            } elseif ($palign == 'R') {
7428
 
                $ximg = $this->w - $this->rMargin - $w;
7429
 
            } else {
7430
 
                $ximg = $x;
7431
 
            }
7432
 
            $this->img_rb_x = $ximg + $w;
7433
 
        }
7434
 
        if ($ismask OR $hidden) {
7435
 
            // image is not displayed
7436
 
            return $info['i'];
7437
 
        }
7438
 
        $xkimg = $ximg * $this->k;
7439
 
        $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%u Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i']));
7440
 
        if (!empty($border)) {
7441
 
            $bx = $this->x;
7442
 
            $by = $this->y;
7443
 
            $this->x = $ximg;
7444
 
            if ($this->rtl) {
7445
 
                $this->x += $w;
7446
 
            }
7447
 
            $this->y = $y;
7448
 
            $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
7449
 
            $this->x = $bx;
7450
 
            $this->y = $by;
7451
 
        }
7452
 
        if ($link) {
7453
 
            $this->Link($ximg, $y, $w, $h, $link, 0);
7454
 
        }
7455
 
        // set pointer to align the next text/objects
7456
 
        switch($align) {
7457
 
            case 'T': {
7458
 
                $this->y = $y;
7459
 
                $this->x = $this->img_rb_x;
7460
 
                break;
7461
 
            }
7462
 
            case 'M': {
7463
 
                $this->y = $y + round($h/2);
7464
 
                $this->x = $this->img_rb_x;
7465
 
                break;
7466
 
            }
7467
 
            case 'B': {
7468
 
                $this->y = $this->img_rb_y;
7469
 
                $this->x = $this->img_rb_x;
7470
 
                break;
7471
 
            }
7472
 
            case 'N': {
7473
 
                $this->SetY($this->img_rb_y);
7474
 
                break;
7475
 
            }
7476
 
            default:{
7477
 
                break;
7478
 
            }
7479
 
        }
7480
 
        $this->endlinex = $this->img_rb_x;
7481
 
        if ($this->inxobj) {
7482
 
            // we are inside an XObject template
7483
 
            $this->xobjects[$this->xobjid]['images'][] = $info['i'];
7484
 
        }
7485
 
        return $info['i'];
7486
 
    }
7487
 
 
7488
 
    /**
7489
 
     * Sets the current active configuration setting of magic_quotes_runtime (if the set_magic_quotes_runtime function exist)
7490
 
     * @param $mqr (boolean) FALSE for off, TRUE for on.
7491
 
     * @since 4.6.025 (2009-08-17)
7492
 
     */
7493
 
    public function set_mqr($mqr) {
7494
 
        if(!defined('PHP_VERSION_ID')) {
7495
 
            $version = PHP_VERSION;
7496
 
            define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4}));
7497
 
        }
7498
 
        if (PHP_VERSION_ID < 50300) {
7499
 
            @set_magic_quotes_runtime($mqr);
7500
 
        }
7501
 
    }
7502
 
 
7503
 
    /**
7504
 
     * Gets the current active configuration setting of magic_quotes_runtime (if the get_magic_quotes_runtime function exist)
7505
 
     * @return Returns 0 if magic quotes runtime is off or get_magic_quotes_runtime doesn't exist, 1 otherwise.
7506
 
     * @since 4.6.025 (2009-08-17)
7507
 
     */
7508
 
    public function get_mqr() {
7509
 
        if(!defined('PHP_VERSION_ID')) {
7510
 
            $version = PHP_VERSION;
7511
 
            define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4}));
7512
 
        }
7513
 
        if (PHP_VERSION_ID < 50300) {
7514
 
            return @get_magic_quotes_runtime();
7515
 
        }
7516
 
        return 0;
7517
 
    }
7518
 
 
7519
 
    /**
7520
 
     * Convert the loaded image to a JPEG and then return a structure for the PDF creator.
7521
 
     * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
7522
 
     * @param $image (image) Image object.
7523
 
     * return image JPEG image object.
7524
 
     * @protected
7525
 
     */
7526
 
    protected function _toJPEG($image) {
7527
 
        $tempname = tempnam(K_PATH_CACHE, 'jpg_');
7528
 
        imagejpeg($image, $tempname, $this->jpeg_quality);
7529
 
        imagedestroy($image);
7530
 
        $retvars = $this->_parsejpeg($tempname);
7531
 
        // tidy up by removing temporary image
7532
 
        unlink($tempname);
7533
 
        return $retvars;
7534
 
    }
7535
 
 
7536
 
    /**
7537
 
     * Convert the loaded image to a PNG and then return a structure for the PDF creator.
7538
 
     * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
7539
 
     * @param $image (image) Image object.
7540
 
     * return image PNG image object.
7541
 
     * @protected
7542
 
     * @since 4.9.016 (2010-04-20)
7543
 
     */
7544
 
    protected function _toPNG($image) {
7545
 
        // set temporary image file name
7546
 
        $tempname = tempnam(K_PATH_CACHE, 'jpg_');
7547
 
        // turn off interlaced mode
7548
 
        imageinterlace($image, 0);
7549
 
        // create temporary PNG image
7550
 
        imagepng($image, $tempname);
7551
 
        // remove image from memory
7552
 
        imagedestroy($image);
7553
 
        // get PNG image data
7554
 
        $retvars = $this->_parsepng($tempname);
7555
 
        // tidy up by removing temporary image
7556
 
        unlink($tempname);
7557
 
        return $retvars;
7558
 
    }
7559
 
 
7560
 
    /**
7561
 
     * Set the transparency for the given GD image.
7562
 
     * @param $new_image (image) GD image object
7563
 
     * @param $image (image) GD image object.
7564
 
     * return GD image object.
7565
 
     * @protected
7566
 
     * @since 4.9.016 (2010-04-20)
7567
 
     */
7568
 
    protected function _setGDImageTransparency($new_image, $image) {
7569
 
        // transparency index
7570
 
        $tid = imagecolortransparent($image);
7571
 
        // default transparency color
7572
 
        $tcol = array('red' => 255, 'green' => 255, 'blue' => 255);
7573
 
        if ($tid >= 0) {
7574
 
            // get the colors for the transparency index
7575
 
            $tcol = imagecolorsforindex($image, $tid);
7576
 
        }
7577
 
        $tid = imagecolorallocate($new_image, $tcol['red'], $tcol['green'], $tcol['blue']);
7578
 
        imagefill($new_image, 0, 0, $tid);
7579
 
        imagecolortransparent($new_image, $tid);
7580
 
        return $new_image;
7581
 
    }
7582
 
 
7583
 
    /**
7584
 
     * Extract info from a JPEG file without using the GD library.
7585
 
     * @param $file (string) image file to parse
7586
 
     * @return array structure containing the image data
7587
 
     * @protected
7588
 
     */
7589
 
    protected function _parsejpeg($file) {
7590
 
        $a = getimagesize($file);
7591
 
        if (empty($a)) {
7592
 
            $this->Error('Missing or incorrect image file: '.$file);
7593
 
        }
7594
 
        if ($a[2] != 2) {
7595
 
            $this->Error('Not a JPEG file: '.$file);
7596
 
        }
7597
 
        if ((!isset($a['channels'])) OR ($a['channels'] == 3)) {
7598
 
            $colspace = 'DeviceRGB';
7599
 
        } elseif ($a['channels'] == 4) {
7600
 
            $colspace = 'DeviceCMYK';
7601
 
        } else {
7602
 
            $colspace = 'DeviceGray';
7603
 
        }
7604
 
        $bpc = isset($a['bits']) ? $a['bits'] : 8;
7605
 
        $data = file_get_contents($file);
7606
 
        return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
7607
 
    }
7608
 
 
7609
 
    /**
7610
 
     * Extract info from a PNG file without using the GD library.
7611
 
     * @param $file (string) image file to parse
7612
 
     * @return array structure containing the image data
7613
 
     * @protected
7614
 
     */
7615
 
    protected function _parsepng($file) {
7616
 
        $f = fopen($file, 'rb');
7617
 
        if ($f === false) {
7618
 
            $this->Error('Can\'t open image file: '.$file);
7619
 
        }
7620
 
        //Check signature
7621
 
        if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
7622
 
            $this->Error('Not a PNG file: '.$file);
7623
 
        }
7624
 
        //Read header chunk
7625
 
        fread($f, 4);
7626
 
        if (fread($f, 4) != 'IHDR') {
7627
 
            $this->Error('Incorrect PNG file: '.$file);
7628
 
        }
7629
 
        $w = $this->_freadint($f);
7630
 
        $h = $this->_freadint($f);
7631
 
        $bpc = ord(fread($f, 1));
7632
 
        if ($bpc > 8) {
7633
 
            //$this->Error('16-bit depth not supported: '.$file);
7634
 
            fclose($f);
7635
 
            return false;
7636
 
        }
7637
 
        $ct = ord(fread($f, 1));
7638
 
        if ($ct == 0) {
7639
 
            $colspace = 'DeviceGray';
7640
 
        } elseif ($ct == 2) {
7641
 
            $colspace = 'DeviceRGB';
7642
 
        } elseif ($ct == 3) {
7643
 
            $colspace = 'Indexed';
7644
 
        } else {
7645
 
            // alpha channel
7646
 
            fclose($f);
7647
 
            return 'pngalpha';
7648
 
        }
7649
 
        if (ord(fread($f, 1)) != 0) {
7650
 
            //$this->Error('Unknown compression method: '.$file);
7651
 
            fclose($f);
7652
 
            return false;
7653
 
        }
7654
 
        if (ord(fread($f, 1)) != 0) {
7655
 
            //$this->Error('Unknown filter method: '.$file);
7656
 
            fclose($f);
7657
 
            return false;
7658
 
        }
7659
 
        if (ord(fread($f, 1)) != 0) {
7660
 
            //$this->Error('Interlacing not supported: '.$file);
7661
 
            fclose($f);
7662
 
            return false;
7663
 
        }
7664
 
        fread($f, 4);
7665
 
        $parms = '/DecodeParms << /Predictor 15 /Colors '.($ct == 2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.' >>';
7666
 
        //Scan chunks looking for palette, transparency and image data
7667
 
        $pal = '';
7668
 
        $trns = '';
7669
 
        $data = '';
7670
 
        do {
7671
 
            $n = $this->_freadint($f);
7672
 
            $type = fread($f, 4);
7673
 
            if ($type == 'PLTE') {
7674
 
                //Read palette
7675
 
                $pal = $this->rfread($f, $n);
7676
 
                fread($f, 4);
7677
 
            } elseif ($type == 'tRNS') {
7678
 
                //Read transparency info
7679
 
                $t = $this->rfread($f, $n);
7680
 
                if ($ct == 0) {
7681
 
                    $trns = array(ord(substr($t, 1, 1)));
7682
 
                } elseif ($ct == 2) {
7683
 
                    $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
7684
 
                } else {
7685
 
                    $pos = strpos($t, chr(0));
7686
 
                    if ($pos !== false) {
7687
 
                        $trns = array($pos);
7688
 
                    }
7689
 
                }
7690
 
                fread($f, 4);
7691
 
            } elseif ($type == 'IDAT') {
7692
 
                //Read image data block
7693
 
                $data .= $this->rfread($f, $n);
7694
 
                fread($f, 4);
7695
 
            } elseif ($type == 'IEND') {
7696
 
                break;
7697
 
            } else {
7698
 
                $this->rfread($f, $n + 4);
7699
 
            }
7700
 
        } while ($n);
7701
 
        if (($colspace == 'Indexed') AND (empty($pal))) {
7702
 
            //$this->Error('Missing palette in '.$file);
7703
 
            fclose($f);
7704
 
            return false;
7705
 
        }
7706
 
        fclose($f);
7707
 
        return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
7708
 
    }
7709
 
 
7710
 
    /**
7711
 
     * Binary-safe and URL-safe file read.
7712
 
     * Reads up to length bytes from the file pointer referenced by handle. Reading stops as soon as one of the following conditions is met: length bytes have been read; EOF (end of file) is reached.
7713
 
     * @param $handle (resource)
7714
 
     * @param $length (int)
7715
 
     * @return Returns the read string or FALSE in case of error.
7716
 
     * @author Nicola Asuni
7717
 
     * @protected
7718
 
     * @since 4.5.027 (2009-03-16)
7719
 
     */
7720
 
    protected function rfread($handle, $length) {
7721
 
        $data = fread($handle, $length);
7722
 
        if ($data === false) {
7723
 
            return false;
7724
 
        }
7725
 
        $rest = $length - strlen($data);
7726
 
        if ($rest > 0) {
7727
 
            $data .= $this->rfread($handle, $rest);
7728
 
        }
7729
 
        return $data;
7730
 
    }
7731
 
 
7732
 
    /**
7733
 
     * Extract info from a PNG image with alpha channel using the GD library.
7734
 
     * @param $file (string) Name of the file containing the image.
7735
 
     * @param $x (float) Abscissa of the upper-left corner.
7736
 
     * @param $y (float) Ordinate of the upper-left corner.
7737
 
     * @param $wpx (float) Original width of the image in pixels.
7738
 
     * @param $hpx (float) original height of the image in pixels.
7739
 
     * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
7740
 
     * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
7741
 
     * @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.
7742
 
     * @param $link (mixed) URL or identifier returned by AddLink().
7743
 
     * @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>
7744
 
     * @param $resize (boolean) If true resize (reduce) the image to fit $w and $h (requires GD library).
7745
 
     * @param $dpi (int) dot-per-inch resolution used on resize
7746
 
     * @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>
7747
 
     * @author Nicola Asuni
7748
 
     * @protected
7749
 
     * @since 4.3.007 (2008-12-04)
7750
 
     * @see Image()
7751
 
     */
7752
 
    protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign) {
7753
 
        // create temp image file (without alpha channel)
7754
 
        $tempfile_plain = tempnam(K_PATH_CACHE, 'mskp_');
7755
 
        // create temp alpha file
7756
 
        $tempfile_alpha = tempnam(K_PATH_CACHE, 'mska_');
7757
 
        if (extension_loaded('imagick')) { // ImageMagick
7758
 
            // ImageMagick library
7759
 
            $img = new Imagick();
7760
 
            $img->readImage($file);
7761
 
            // clone image object
7762
 
            $imga = $img->clone();
7763
 
            // extract alpha channel
7764
 
            $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
7765
 
            $img->negateImage(true);
7766
 
            $img->setImageFormat('png');
7767
 
            $img->writeImage($tempfile_alpha);
7768
 
            // remove alpha channel
7769
 
            $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
7770
 
            $imga->setImageFormat('png');
7771
 
            $imga->writeImage($tempfile_plain);
7772
 
        } else { // GD library
7773
 
            // generate images
7774
 
            $img = imagecreatefrompng($file);
7775
 
            $imgalpha = imagecreate($wpx, $hpx);
7776
 
            // generate gray scale palette (0 -> 255)
7777
 
            for ($c = 0; $c < 256; ++$c) {
7778
 
                ImageColorAllocate($imgalpha, $c, $c, $c);
7779
 
            }
7780
 
            // extract alpha channel
7781
 
            for ($xpx = 0; $xpx < $wpx; ++$xpx) {
7782
 
                for ($ypx = 0; $ypx < $hpx; ++$ypx) {
7783
 
                    $color = imagecolorat($img, $xpx, $ypx);
7784
 
                    $alpha = ($color >> 24); // shifts off the first 24 bits (where 8x3 are used for each color), and returns the remaining 7 allocated bits (commonly used for alpha)
7785
 
                    $alpha = (((127 - $alpha) / 127) * 255); // GD alpha is only 7 bit (0 -> 127)
7786
 
                    $alpha = $this->getGDgamma($alpha); // correct gamma
7787
 
                    imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
7788
 
                }
7789
 
            }
7790
 
            imagepng($imgalpha, $tempfile_alpha);
7791
 
            imagedestroy($imgalpha);
7792
 
            // extract image without alpha channel
7793
 
            $imgplain = imagecreatetruecolor($wpx, $hpx);
7794
 
            imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
7795
 
            imagepng($imgplain, $tempfile_plain);
7796
 
            imagedestroy($imgplain);
7797
 
        }
7798
 
        // embed mask image
7799
 
        $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7800
 
        // embed image, masked with previously embedded mask
7801
 
        $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7802
 
        // remove temp files
7803
 
        unlink($tempfile_alpha);
7804
 
        unlink($tempfile_plain);
7805
 
    }
7806
 
 
7807
 
    /**
7808
 
     * Correct the gamma value to be used with GD library
7809
 
     * @param $v (float) the gamma value to be corrected
7810
 
     * @protected
7811
 
     * @since 4.3.007 (2008-12-04)
7812
 
     */
7813
 
    protected function getGDgamma($v) {
7814
 
        return (pow(($v / 255), 2.2) * 255);
7815
 
    }
7816
 
 
7817
 
    /**
7818
 
     * Performs a line break.
7819
 
     * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
7820
 
     * @param $h (float) The height of the break. By default, the value equals the height of the last printed cell.
7821
 
     * @param $cell (boolean) if true add the current left (or right o for RTL) padding to the X coordinate
7822
 
     * @public
7823
 
     * @since 1.0
7824
 
     * @see Cell()
7825
 
     */
7826
 
    public function Ln($h='', $cell=false) {
7827
 
        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'])) {
7828
 
            // revove vertical space from the top of the column
7829
 
            return;
7830
 
        }
7831
 
        if ($cell) {
7832
 
            if ($this->rtl) {
7833
 
                $cellpadding = $this->cell_padding['R'];
7834
 
            } else {
7835
 
                $cellpadding = $this->cell_padding['L'];
7836
 
            }
7837
 
        } else {
7838
 
            $cellpadding = 0;
7839
 
        }
7840
 
        if ($this->rtl) {
7841
 
            $this->x = $this->w - $this->rMargin - $cellpadding;
7842
 
        } else {
7843
 
            $this->x = $this->lMargin + $cellpadding;
7844
 
        }
7845
 
        if (is_string($h)) {
7846
 
            $this->y += $this->lasth;
7847
 
        } else {
7848
 
            $this->y += $h;
7849
 
        }
7850
 
        $this->newline = true;
7851
 
    }
7852
 
 
7853
 
    /**
7854
 
     * Returns the relative X value of current position.
7855
 
     * The value is relative to the left border for LTR languages and to the right border for RTL languages.
7856
 
     * @return float
7857
 
     * @public
7858
 
     * @since 1.2
7859
 
     * @see SetX(), GetY(), SetY()
7860
 
     */
7861
 
    public function GetX() {
7862
 
        //Get x position
7863
 
        if ($this->rtl) {
7864
 
            return ($this->w - $this->x);
7865
 
        } else {
7866
 
            return $this->x;
7867
 
        }
7868
 
    }
7869
 
 
7870
 
    /**
7871
 
     * Returns the absolute X value of current position.
7872
 
     * @return float
7873
 
     * @public
7874
 
     * @since 1.2
7875
 
     * @see SetX(), GetY(), SetY()
7876
 
     */
7877
 
    public function GetAbsX() {
7878
 
        return $this->x;
7879
 
    }
7880
 
 
7881
 
    /**
7882
 
     * Returns the ordinate of the current position.
7883
 
     * @return float
7884
 
     * @public
7885
 
     * @since 1.0
7886
 
     * @see SetY(), GetX(), SetX()
7887
 
     */
7888
 
    public function GetY() {
7889
 
        return $this->y;
7890
 
    }
7891
 
 
7892
 
    /**
7893
 
     * Defines the abscissa of the current position.
7894
 
     * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
7895
 
     * @param $x (float) The value of the abscissa.
7896
 
     * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7897
 
     * @public
7898
 
     * @since 1.2
7899
 
     * @see GetX(), GetY(), SetY(), SetXY()
7900
 
     */
7901
 
    public function SetX($x, $rtloff=false) {
7902
 
        if (!$rtloff AND $this->rtl) {
7903
 
            if ($x >= 0) {
7904
 
                $this->x = $this->w - $x;
7905
 
            } else {
7906
 
                $this->x = abs($x);
7907
 
            }
7908
 
        } else {
7909
 
            if ($x >= 0) {
7910
 
                $this->x = $x;
7911
 
            } else {
7912
 
                $this->x = $this->w + $x;
7913
 
            }
7914
 
        }
7915
 
        if ($this->x < 0) {
7916
 
            $this->x = 0;
7917
 
        }
7918
 
        if ($this->x > $this->w) {
7919
 
            $this->x = $this->w;
7920
 
        }
7921
 
    }
7922
 
 
7923
 
    /**
7924
 
     * Moves the current abscissa back to the left margin and sets the ordinate.
7925
 
     * If the passed value is negative, it is relative to the bottom of the page.
7926
 
     * @param $y (float) The value of the ordinate.
7927
 
     * @param $resetx (bool) if true (default) reset the X position.
7928
 
     * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7929
 
     * @public
7930
 
     * @since 1.0
7931
 
     * @see GetX(), GetY(), SetY(), SetXY()
7932
 
     */
7933
 
    public function SetY($y, $resetx=true, $rtloff=false) {
7934
 
        if ($resetx) {
7935
 
            //reset x
7936
 
            if (!$rtloff AND $this->rtl) {
7937
 
                $this->x = $this->w - $this->rMargin;
7938
 
            } else {
7939
 
                $this->x = $this->lMargin;
7940
 
            }
7941
 
        }
7942
 
        if ($y >= 0) {
7943
 
            $this->y = $y;
7944
 
        } else {
7945
 
            $this->y = $this->h + $y;
7946
 
        }
7947
 
        if ($this->y < 0) {
7948
 
            $this->y = 0;
7949
 
        }
7950
 
        if ($this->y > $this->h) {
7951
 
            $this->y = $this->h;
7952
 
        }
7953
 
    }
7954
 
 
7955
 
    /**
7956
 
     * Defines the abscissa and ordinate of the current position.
7957
 
     * If the passed values are negative, they are relative respectively to the right and bottom of the page.
7958
 
     * @param $x (float) The value of the abscissa.
7959
 
     * @param $y (float) The value of the ordinate.
7960
 
     * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7961
 
     * @public
7962
 
     * @since 1.2
7963
 
     * @see SetX(), SetY()
7964
 
     */
7965
 
    public function SetXY($x, $y, $rtloff=false) {
7966
 
        $this->SetY($y, false, $rtloff);
7967
 
        $this->SetX($x, $rtloff);
7968
 
    }
7969
 
 
7970
 
    /**
7971
 
     * Send the document to a given destination: string, local file or browser.
7972
 
     * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
7973
 
     * The method first calls Close() if necessary to terminate the document.
7974
 
     * @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.
7975
 
     * @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>
7976
 
     * @public
7977
 
     * @since 1.0
7978
 
     * @see Close()
7979
 
     */
7980
 
    public function Output($name='doc.pdf', $dest='I') {
7981
 
        //Output PDF to some destination
7982
 
        //Finish document if necessary
7983
 
        if ($this->state < 3) {
7984
 
            $this->Close();
7985
 
        }
7986
 
        //Normalize parameters
7987
 
        if (is_bool($dest)) {
7988
 
            $dest = $dest ? 'D' : 'F';
7989
 
        }
7990
 
        $dest = strtoupper($dest);
7991
 
        if ($dest{0} != 'F') {
7992
 
            $name = preg_replace('/[\s]+/', '_', $name);
7993
 
            $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
7994
 
        }
7995
 
        if ($this->sign) {
7996
 
            // *** apply digital signature to the document ***
7997
 
            // get the document content
7998
 
            $pdfdoc = $this->getBuffer();
7999
 
            // remove last newline
8000
 
            $pdfdoc = substr($pdfdoc, 0, -1);
8001
 
            // Remove the original buffer
8002
 
            if (isset($this->diskcache) AND $this->diskcache) {
8003
 
                // remove buffer file from cache
8004
 
                unlink($this->buffer);
8005
 
            }
8006
 
            unset($this->buffer);
8007
 
            // remove filler space
8008
 
            $byterange_string_len = strlen($this->byterange_string);
8009
 
            // define the ByteRange
8010
 
            $byte_range = array();
8011
 
            $byte_range[0] = 0;
8012
 
            $byte_range[1] = strpos($pdfdoc, $this->byterange_string) + $byterange_string_len + 10;
8013
 
            $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
8014
 
            $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
8015
 
            $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
8016
 
            // replace the ByteRange
8017
 
            $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
8018
 
            $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
8019
 
            $pdfdoc = str_replace($this->byterange_string, $byterange, $pdfdoc);
8020
 
            // write the document to a temporary folder
8021
 
            $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_');
8022
 
            $f = fopen($tempdoc, 'wb');
8023
 
            if (!$f) {
8024
 
                $this->Error('Unable to create temporary file: '.$tempdoc);
8025
 
            }
8026
 
            $pdfdoc_length = strlen($pdfdoc);
8027
 
            fwrite($f, $pdfdoc, $pdfdoc_length);
8028
 
            fclose($f);
8029
 
            // get digital signature via openssl library
8030
 
            $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_');
8031
 
            if (empty($this->signature_data['extracerts'])) {
8032
 
                openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
8033
 
            } else {
8034
 
                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']);
8035
 
            }
8036
 
            unlink($tempdoc);
8037
 
            // read signature
8038
 
            $signature = file_get_contents($tempsign);
8039
 
            unlink($tempsign);
8040
 
            // extract signature
8041
 
            $signature = substr($signature, $pdfdoc_length);
8042
 
            $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
8043
 
            $tmparr = explode("\n\n", $signature);
8044
 
            $signature = $tmparr[1];
8045
 
            unset($tmparr);
8046
 
            // decode signature
8047
 
            $signature = base64_decode(trim($signature));
8048
 
            // convert signature to hex
8049
 
            $signature = current(unpack('H*', $signature));
8050
 
            $signature = str_pad($signature, $this->signature_max_length, '0');
8051
 
            // Add signature to the document
8052
 
            $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
8053
 
            $this->diskcache = false;
8054
 
            $this->buffer = &$pdfdoc;
8055
 
            $this->bufferlen = strlen($pdfdoc);
8056
 
        }
8057
 
        switch($dest) {
8058
 
            case 'I': {
8059
 
                // Send PDF to the standard output
8060
 
                if (ob_get_contents()) {
8061
 
                    $this->Error('Some data has already been output, can\'t send PDF file');
8062
 
                }
8063
 
                if (php_sapi_name() != 'cli') {
8064
 
                    //We send to a browser
8065
 
                    header('Content-Type: application/pdf');
8066
 
                    if (headers_sent()) {
8067
 
                        $this->Error('Some data has already been output to browser, can\'t send PDF file');
8068
 
                    }
8069
 
                    header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
8070
 
                    header('Pragma: public');
8071
 
                    header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
8072
 
                    header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
8073
 
                    header('Content-Length: '.$this->bufferlen);
8074
 
                    header('Content-Disposition: inline; filename="'.basename($name).'";');
8075
 
                }
8076
 
                echo $this->getBuffer();
8077
 
                break;
8078
 
            }
8079
 
            case 'D': {
8080
 
                // Download PDF as file
8081
 
                if (ob_get_contents()) {
8082
 
                    $this->Error('Some data has already been output, can\'t send PDF file');
8083
 
                }
8084
 
                header('Content-Description: File Transfer');
8085
 
                if (headers_sent()) {
8086
 
                    $this->Error('Some data has already been output to browser, can\'t send PDF file');
8087
 
                }
8088
 
                header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
8089
 
                header('Pragma: public');
8090
 
                header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
8091
 
                header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
8092
 
                // force download dialog
8093
 
                if (strpos(php_sapi_name(), 'cgi') === false) {
8094
 
                    header('Content-Type: application/force-download');
8095
 
                    header('Content-Type: application/octet-stream', false);
8096
 
                    header('Content-Type: application/download', false);
8097
 
                    header('Content-Type: application/pdf', false);
8098
 
                } else {
8099
 
                    header('Content-Type: application/pdf');
8100
 
                }
8101
 
                // use the Content-Disposition header to supply a recommended filename
8102
 
                header('Content-Disposition: attachment; filename="'.basename($name).'";');
8103
 
                header('Content-Transfer-Encoding: binary');
8104
 
                header('Content-Length: '.$this->bufferlen);
8105
 
                echo $this->getBuffer();
8106
 
                break;
8107
 
            }
8108
 
            case 'F':
8109
 
            case 'FI':
8110
 
            case 'FD': {
8111
 
                // Save PDF to a local file
8112
 
                if ($this->diskcache) {
8113
 
                    copy($this->buffer, $name);
8114
 
                } else {
8115
 
                    $f = fopen($name, 'wb');
8116
 
                    if (!$f) {
8117
 
                        $this->Error('Unable to create output file: '.$name);
8118
 
                    }
8119
 
                    fwrite($f, $this->getBuffer(), $this->bufferlen);
8120
 
                    fclose($f);
8121
 
                }
8122
 
                if ($dest == 'FI') {
8123
 
                    // send headers to browser
8124
 
                    header('Content-Type: application/pdf');
8125
 
                    header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
8126
 
                    header('Pragma: public');
8127
 
                    header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
8128
 
                    header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
8129
 
                    header('Content-Length: '.filesize($name));
8130
 
                    header('Content-Disposition: inline; filename="'.basename($name).'";');
8131
 
                    // send document to the browser
8132
 
                    echo file_get_contents($name);
8133
 
                } elseif ($dest == 'FD') {
8134
 
                    // send headers to browser
8135
 
                    if (ob_get_contents()) {
8136
 
                        $this->Error('Some data has already been output, can\'t send PDF file');
8137
 
                    }
8138
 
                    header('Content-Description: File Transfer');
8139
 
                    if (headers_sent()) {
8140
 
                        $this->Error('Some data has already been output to browser, can\'t send PDF file');
8141
 
                    }
8142
 
                    header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
8143
 
                    header('Pragma: public');
8144
 
                    header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
8145
 
                    header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
8146
 
                    // force download dialog
8147
 
                    if (strpos(php_sapi_name(), 'cgi') === false) {
8148
 
                        header('Content-Type: application/force-download');
8149
 
                        header('Content-Type: application/octet-stream', false);
8150
 
                        header('Content-Type: application/download', false);
8151
 
                        header('Content-Type: application/pdf', false);
8152
 
                    } else {
8153
 
                        header('Content-Type: application/pdf');
8154
 
                    }
8155
 
                    // use the Content-Disposition header to supply a recommended filename
8156
 
                    header('Content-Disposition: attachment; filename="'.basename($name).'";');
8157
 
                    header('Content-Transfer-Encoding: binary');
8158
 
                    header('Content-Length: '.filesize($name));
8159
 
                    // send document to the browser
8160
 
                    echo file_get_contents($name);
8161
 
                }
8162
 
                break;
8163
 
            }
8164
 
            case 'E': {
8165
 
                // Return PDF as base64 mime multi-part email attachment (RFC 2045)
8166
 
                $retval = 'Content-Type: application/pdf;'."\r\n";
8167
 
                $retval .= ' name="'.$name.'"'."\r\n";
8168
 
                $retval .= 'Content-Transfer-Encoding: base64'."\r\n";
8169
 
                $retval .= 'Content-Disposition: attachment;'."\r\n";
8170
 
                $retval .= ' filename="'.$name.'"'."\r\n\r\n";
8171
 
                $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
8172
 
                return $retval;
8173
 
            }
8174
 
            case 'S': {
8175
 
                // Returns PDF as a string
8176
 
                return $this->getBuffer();
8177
 
            }
8178
 
            default: {
8179
 
                $this->Error('Incorrect output destination: '.$dest);
8180
 
            }
8181
 
        }
8182
 
        return '';
8183
 
    }
8184
 
 
8185
 
    /**
8186
 
     * Unset all class variables except the following critical variables: internal_encoding, state, bufferlen, buffer and diskcache.
8187
 
     * @param $destroyall (boolean) if true destroys all class variables, otherwise preserves critical variables.
8188
 
     * @param $preserve_objcopy (boolean) if true preserves the objcopy variable
8189
 
     * @public
8190
 
     * @since 4.5.016 (2009-02-24)
8191
 
     */
8192
 
    public function _destroy($destroyall=false, $preserve_objcopy=false) {
8193
 
        if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) {
8194
 
            // remove buffer file from cache
8195
 
            unlink($this->buffer);
8196
 
        }
8197
 
        foreach (array_keys(get_object_vars($this)) as $val) {
8198
 
            if ($destroyall OR (
8199
 
                ($val != 'internal_encoding')
8200
 
                AND ($val != 'state')
8201
 
                AND ($val != 'bufferlen')
8202
 
                AND ($val != 'buffer')
8203
 
                AND ($val != 'diskcache')
8204
 
                AND ($val != 'sign')
8205
 
                AND ($val != 'signature_data')
8206
 
                AND ($val != 'signature_max_length')
8207
 
                AND ($val != 'byterange_string')
8208
 
                )) {
8209
 
                if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) {
8210
 
                    unset($this->$val);
8211
 
                }
8212
 
            }
8213
 
        }
8214
 
    }
8215
 
 
8216
 
    /**
8217
 
     * Check for locale-related bug
8218
 
     * @protected
8219
 
     */
8220
 
    protected function _dochecks() {
8221
 
        //Check for locale-related bug
8222
 
        if (1.1 == 1) {
8223
 
            $this->Error('Don\'t alter the locale before including class file');
8224
 
        }
8225
 
        //Check for decimal separator
8226
 
        if (sprintf('%.1F', 1.0) != '1.0') {
8227
 
            setlocale(LC_NUMERIC, 'C');
8228
 
        }
8229
 
    }
8230
 
 
8231
 
    /**
8232
 
     * Return fonts path
8233
 
     * @return string
8234
 
     * @protected
8235
 
     */
8236
 
    protected function _getfontpath() {
8237
 
        if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
8238
 
            define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
8239
 
        }
8240
 
        return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
8241
 
    }
8242
 
 
8243
 
    /**
8244
 
     * Output pages.
8245
 
     * @protected
8246
 
     */
8247
 
    protected function _putpages() {
8248
 
        $nb = $this->numpages;
8249
 
        if (!empty($this->AliasNbPages)) {
8250
 
            $nbs = $this->formatPageNumber($nb);
8251
 
            $nbu = $this->UTF8ToUTF16BE($nbs, false); // replacement for unicode font
8252
 
            $alias_a = $this->_escape($this->AliasNbPages);
8253
 
            $alias_au = $this->_escape('{'.$this->AliasNbPages.'}');
8254
 
            if ($this->isunicode) {
8255
 
                $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages));
8256
 
                $alias_bu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNbPages.'}'));
8257
 
                $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl));
8258
 
                $alias_cu = $this->_escape($this->utf8StrRev('{'.$this->AliasNbPages.'}', false, $this->tmprtl));
8259
 
            }
8260
 
        }
8261
 
        if (!empty($this->AliasNumPage)) {
8262
 
            $alias_pa = $this->_escape($this->AliasNumPage);
8263
 
            $alias_pau = $this->_escape('{'.$this->AliasNumPage.'}');
8264
 
            if ($this->isunicode) {
8265
 
                $alias_pb = $this->_escape($this->UTF8ToLatin1($this->AliasNumPage));
8266
 
                $alias_pbu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNumPage.'}'));
8267
 
                $alias_pc = $this->_escape($this->utf8StrRev($this->AliasNumPage, false, $this->tmprtl));
8268
 
                $alias_pcu = $this->_escape($this->utf8StrRev('{'.$this->AliasNumPage.'}', false, $this->tmprtl));
8269
 
            }
8270
 
        }
8271
 
        $pagegroupnum = 0;
8272
 
        $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
8273
 
        for ($n=1; $n <= $nb; ++$n) {
8274
 
            $temppage = $this->getPageBuffer($n);
8275
 
            if (!empty($this->pagegroups)) {
8276
 
                if(isset($this->newpagegroup[$n])) {
8277
 
                    $pagegroupnum = 0;
8278
 
                }
8279
 
                ++$pagegroupnum;
8280
 
                foreach ($this->pagegroups as $k => $v) {
8281
 
                    // replace total pages group numbers
8282
 
                    $vs = $this->formatPageNumber($v);
8283
 
                    $vu = $this->UTF8ToUTF16BE($vs, false);
8284
 
                    $alias_ga = $this->_escape($k);
8285
 
                    $alias_gau = $this->_escape('{'.$k.'}');
8286
 
                    if ($this->isunicode) {
8287
 
                        $alias_gb = $this->_escape($this->UTF8ToLatin1($k));
8288
 
                        $alias_gbu = $this->_escape($this->UTF8ToLatin1('{'.$k.'}'));
8289
 
                        $alias_gc = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
8290
 
                        $alias_gcu = $this->_escape($this->utf8StrRev('{'.$k.'}', false, $this->tmprtl));
8291
 
                    }
8292
 
                    $temppage = str_replace($alias_gau, $vu, $temppage);
8293
 
                    if ($this->isunicode) {
8294
 
                        $temppage = str_replace($alias_gbu, $vu, $temppage);
8295
 
                        $temppage = str_replace($alias_gcu, $vu, $temppage);
8296
 
                        $temppage = str_replace($alias_gb, $vs, $temppage);
8297
 
                        $temppage = str_replace($alias_gc, $vs, $temppage);
8298
 
                    }
8299
 
                    $temppage = str_replace($alias_ga, $vs, $temppage);
8300
 
                    // replace page group numbers
8301
 
                    $pvs = $this->formatPageNumber($pagegroupnum);
8302
 
                    $pvu = $this->UTF8ToUTF16BE($pvs, false);
8303
 
                    $pk = str_replace('{nb', '{pnb', $k);
8304
 
                    $alias_pga = $this->_escape($pk);
8305
 
                    $alias_pgau = $this->_escape('{'.$pk.'}');
8306
 
                    if ($this->isunicode) {
8307
 
                        $alias_pgb = $this->_escape($this->UTF8ToLatin1($pk));
8308
 
                        $alias_pgbu = $this->_escape($this->UTF8ToLatin1('{'.$pk.'}'));
8309
 
                        $alias_pgc = $this->_escape($this->utf8StrRev($pk, false, $this->tmprtl));
8310
 
                        $alias_pgcu = $this->_escape($this->utf8StrRev('{'.$pk.'}', false, $this->tmprtl));
8311
 
                    }
8312
 
                    $temppage = str_replace($alias_pgau, $pvu, $temppage);
8313
 
                    if ($this->isunicode) {
8314
 
                        $temppage = str_replace($alias_pgbu, $pvu, $temppage);
8315
 
                        $temppage = str_replace($alias_pgcu, $pvu, $temppage);
8316
 
                        $temppage = str_replace($alias_pgb, $pvs, $temppage);
8317
 
                        $temppage = str_replace($alias_pgc, $pvs, $temppage);
8318
 
                    }
8319
 
                    $temppage = str_replace($alias_pga, $pvs, $temppage);
8320
 
                }
8321
 
            }
8322
 
            if (!empty($this->AliasNbPages)) {
8323
 
                // replace total pages number
8324
 
                $temppage = str_replace($alias_au, $nbu, $temppage);
8325
 
                if ($this->isunicode) {
8326
 
                    $temppage = str_replace($alias_bu, $nbu, $temppage);
8327
 
                    $temppage = str_replace($alias_cu, $nbu, $temppage);
8328
 
                    $temppage = str_replace($alias_b, $nbs, $temppage);
8329
 
                    $temppage = str_replace($alias_c, $nbs, $temppage);
8330
 
                }
8331
 
                $temppage = str_replace($alias_a, $nbs, $temppage);
8332
 
            }
8333
 
            if (!empty($this->AliasNumPage)) {
8334
 
                // replace page number
8335
 
                $pnbs = $this->formatPageNumber($n);
8336
 
                $pnbu = $this->UTF8ToUTF16BE($pnbs, false); // replacement for unicode font
8337
 
                $temppage = str_replace($alias_pau, $pnbu, $temppage);
8338
 
                if ($this->isunicode) {
8339
 
                    $temppage = str_replace($alias_pbu, $pnbu, $temppage);
8340
 
                    $temppage = str_replace($alias_pcu, $pnbu, $temppage);
8341
 
                    $temppage = str_replace($alias_pb, $pnbs, $temppage);
8342
 
                    $temppage = str_replace($alias_pc, $pnbs, $temppage);
8343
 
                }
8344
 
                $temppage = str_replace($alias_pa, $pnbs, $temppage);
8345
 
            }
8346
 
            $temppage = str_replace($this->epsmarker, '', $temppage);
8347
 
            //Page
8348
 
            $this->page_obj_id[$n] = $this->_newobj();
8349
 
            $out = '<<';
8350
 
            $out .= ' /Type /Page';
8351
 
            $out .= ' /Parent 1 0 R';
8352
 
            $out .= ' /LastModified '.$this->_datestring();
8353
 
            $out .= ' /Resources 2 0 R';
8354
 
            $boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
8355
 
            foreach ($boxes as $box) {
8356
 
                $out .= ' /'.$box;
8357
 
                $out .= sprintf(' [%.2F %.2F %.2F %.2F]', $this->pagedim[$n][$box]['llx'], $this->pagedim[$n][$box]['lly'], $this->pagedim[$n][$box]['urx'], $this->pagedim[$n][$box]['ury']);
8358
 
            }
8359
 
            if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) {
8360
 
                $out .= ' /BoxColorInfo <<';
8361
 
                foreach ($boxes as $box) {
8362
 
                    if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) {
8363
 
                        $out .= ' /'.$box.' <<';
8364
 
                        if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) {
8365
 
                            $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C'];
8366
 
                            $out .= ' /C [';
8367
 
                            $out .= sprintf(' %.3F %.3F %.3F', $color[0]/255, $color[1]/255, $color[2]/255);
8368
 
                            $out .= ' ]';
8369
 
                        }
8370
 
                        if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) {
8371
 
                            $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k);
8372
 
                        }
8373
 
                        if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) {
8374
 
                            $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S'];
8375
 
                        }
8376
 
                        if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) {
8377
 
                            $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D'];
8378
 
                            $out .= ' /D [';
8379
 
                            foreach ($dashes as $dash) {
8380
 
                                $out .= sprintf(' %.3F', ($dash * $this->k));
8381
 
                            }
8382
 
                            $out .= ' ]';
8383
 
                        }
8384
 
                        $out .= ' >>';
8385
 
                    }
8386
 
                }
8387
 
                $out .= ' >>';
8388
 
            }
8389
 
            $out .= ' /Contents '.($this->n + 1).' 0 R';
8390
 
            $out .= ' /Rotate '.$this->pagedim[$n]['Rotate'];
8391
 
            $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
8392
 
            if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) {
8393
 
                // page transitions
8394
 
                if (isset($this->pagedim[$n]['trans']['Dur'])) {
8395
 
                    $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur'];
8396
 
                }
8397
 
                $out .= ' /Trans <<';
8398
 
                $out .= ' /Type /Trans';
8399
 
                if (isset($this->pagedim[$n]['trans']['S'])) {
8400
 
                    $out .= ' /S /'.$this->pagedim[$n]['trans']['S'];
8401
 
                }
8402
 
                if (isset($this->pagedim[$n]['trans']['D'])) {
8403
 
                    $out .= ' /D '.$this->pagedim[$n]['trans']['D'];
8404
 
                }
8405
 
                if (isset($this->pagedim[$n]['trans']['Dm'])) {
8406
 
                    $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm'];
8407
 
                }
8408
 
                if (isset($this->pagedim[$n]['trans']['M'])) {
8409
 
                    $out .= ' /M /'.$this->pagedim[$n]['trans']['M'];
8410
 
                }
8411
 
                if (isset($this->pagedim[$n]['trans']['Di'])) {
8412
 
                    $out .= ' /Di '.$this->pagedim[$n]['trans']['Di'];
8413
 
                }
8414
 
                if (isset($this->pagedim[$n]['trans']['SS'])) {
8415
 
                    $out .= ' /SS '.$this->pagedim[$n]['trans']['SS'];
8416
 
                }
8417
 
                if (isset($this->pagedim[$n]['trans']['B'])) {
8418
 
                    $out .= ' /B '.$this->pagedim[$n]['trans']['B'];
8419
 
                }
8420
 
                $out .= ' >>';
8421
 
            }
8422
 
            $out .= $this->_getannotsrefs($n);
8423
 
            $out .= ' /PZ '.$this->pagedim[$n]['PZ'];
8424
 
            $out .= ' >>';
8425
 
            $out .= "\n".'endobj';
8426
 
            $this->_out($out);
8427
 
            //Page content
8428
 
            $p = ($this->compress) ? gzcompress($temppage) : $temppage;
8429
 
            $this->_newobj();
8430
 
            $p = $this->_getrawstream($p);
8431
 
            $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
8432
 
            if ($this->diskcache) {
8433
 
                // remove temporary files
8434
 
                unlink($this->pages[$n]);
8435
 
            }
8436
 
        }
8437
 
        //Pages root
8438
 
        $out = $this->_getobj(1)."\n";
8439
 
        $out .= '<< /Type /Pages /Kids [';
8440
 
        foreach($this->page_obj_id as $page_obj) {
8441
 
            $out .= ' '.$page_obj.' 0 R';
8442
 
        }
8443
 
        $out .= ' ] /Count '.$nb.' >>';
8444
 
        $out .= "\n".'endobj';
8445
 
        $this->_out($out);
8446
 
    }
8447
 
 
8448
 
    /**
8449
 
     * Output references to page annotations
8450
 
     * @param $n (int) page number
8451
 
     * @protected
8452
 
     * @author Nicola Asuni
8453
 
     * @since 4.7.000 (2008-08-29)
8454
 
     * @deprecated
8455
 
     */
8456
 
    protected function _putannotsrefs($n) {
8457
 
        $this->_out($this->_getannotsrefs($n));
8458
 
    }
8459
 
 
8460
 
    /**
8461
 
     * Get references to page annotations.
8462
 
     * @param $n (int) page number
8463
 
     * @return string
8464
 
     * @protected
8465
 
     * @author Nicola Asuni
8466
 
     * @since 5.0.010 (2010-05-17)
8467
 
     */
8468
 
    protected function _getannotsrefs($n) {
8469
 
        if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
8470
 
            return '';
8471
 
        }
8472
 
        $out = ' /Annots [';
8473
 
        if (isset($this->PageAnnots[$n])) {
8474
 
            foreach ($this->PageAnnots[$n] as $key => $val) {
8475
 
                if (!in_array($val['n'], $this->radio_groups)) {
8476
 
                    $out .= ' '.$val['n'].' 0 R';
8477
 
                }
8478
 
            }
8479
 
            // add radiobutton groups
8480
 
            if (isset($this->radiobutton_groups[$n])) {
8481
 
                foreach ($this->radiobutton_groups[$n] as $key => $data) {
8482
 
                    if (isset($data['n'])) {
8483
 
                        $out .= ' '.$data['n'].' 0 R';
8484
 
                    }
8485
 
                }
8486
 
            }
8487
 
        }
8488
 
        if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
8489
 
            // set reference for signature object
8490
 
            $out .= ' '.$this->sig_obj_id.' 0 R';
8491
 
        }
8492
 
        $out .= ' ]';
8493
 
        return $out;
8494
 
    }
8495
 
 
8496
 
    /**
8497
 
     * Output annotations objects for all pages.
8498
 
     * !!! THIS METHOD IS NOT YET COMPLETED !!!
8499
 
     * See section 12.5 of PDF 32000_2008 reference.
8500
 
     * @protected
8501
 
     * @author Nicola Asuni
8502
 
     * @since 4.0.018 (2008-08-06)
8503
 
     */
8504
 
    protected function _putannotsobjs() {
8505
 
        // reset object counter
8506
 
        for ($n=1; $n <= $this->numpages; ++$n) {
8507
 
            if (isset($this->PageAnnots[$n])) {
8508
 
                // set page annotations
8509
 
                foreach ($this->PageAnnots[$n] as $key => $pl) {
8510
 
                    $annot_obj_id = $this->PageAnnots[$n][$key]['n'];
8511
 
                    // create annotation object for grouping radiobuttons
8512
 
                    if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
8513
 
                        $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n'];
8514
 
                        $annots = '<<';
8515
 
                        $annots .= ' /Type /Annot';
8516
 
                        $annots .= ' /Subtype /Widget';
8517
 
                        $annots .= ' /Rect [0 0 0 0]';
8518
 
                        $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
8519
 
                        $annots .= ' /FT /Btn';
8520
 
                        $annots .= ' /Ff 49152';
8521
 
                        $annots .= ' /Kids [';
8522
 
                        foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) {
8523
 
                            if ($key !== 'n') {
8524
 
                                $annots .= ' '.$data['kid'].' 0 R';
8525
 
                                if ($data['def'] !== 'Off') {
8526
 
                                    $defval = $data['def'];
8527
 
                                }
8528
 
                            }
8529
 
                        }
8530
 
                        $annots .= ' ]';
8531
 
                        if (isset($defval)) {
8532
 
                            $annots .= ' /V /'.$defval;
8533
 
                        }
8534
 
                        $annots .= ' >>';
8535
 
                        $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
8536
 
                        $this->form_obj_id[] = $radio_button_obj_id;
8537
 
                        // store object id to be used on Parent entry of Kids
8538
 
                        $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id;
8539
 
                    }
8540
 
                    $formfield = false;
8541
 
                    $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
8542
 
                    $a = $pl['x'] * $this->k;
8543
 
                    $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k);
8544
 
                    $c = $pl['w'] * $this->k;
8545
 
                    $d = $pl['h'] * $this->k;
8546
 
                    $rect = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b+$d);
8547
 
                    // create new annotation object
8548
 
                    $annots = '<</Type /Annot';
8549
 
                    $annots .= ' /Subtype /'.$pl['opt']['subtype'];
8550
 
                    $annots .= ' /Rect ['.$rect.']';
8551
 
                    $ft = array('Btn', 'Tx', 'Ch', 'Sig');
8552
 
                    if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
8553
 
                        $annots .= ' /FT /'.$pl['opt']['ft'];
8554
 
                        $formfield = true;
8555
 
                    }
8556
 
                    $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
8557
 
                    $annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
8558
 
                    $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
8559
 
                    $annots .= ' /M '.$this->_datestring($annot_obj_id);
8560
 
                    if (isset($pl['opt']['f'])) {
8561
 
                        $val = 0;
8562
 
                        if (is_array($pl['opt']['f'])) {
8563
 
                            foreach ($pl['opt']['f'] as $f) {
8564
 
                                switch (strtolower($f)) {
8565
 
                                    case 'invisible': {
8566
 
                                        $val += 1 << 0;
8567
 
                                        break;
8568
 
                                    }
8569
 
                                    case 'hidden': {
8570
 
                                        $val += 1 << 1;
8571
 
                                        break;
8572
 
                                    }
8573
 
                                    case 'print': {
8574
 
                                        $val += 1 << 2;
8575
 
                                        break;
8576
 
                                    }
8577
 
                                    case 'nozoom': {
8578
 
                                        $val += 1 << 3;
8579
 
                                        break;
8580
 
                                    }
8581
 
                                    case 'norotate': {
8582
 
                                        $val += 1 << 4;
8583
 
                                        break;
8584
 
                                    }
8585
 
                                    case 'noview': {
8586
 
                                        $val += 1 << 5;
8587
 
                                        break;
8588
 
                                    }
8589
 
                                    case 'readonly': {
8590
 
                                        $val += 1 << 6;
8591
 
                                        break;
8592
 
                                    }
8593
 
                                    case 'locked': {
8594
 
                                        $val += 1 << 8;
8595
 
                                        break;
8596
 
                                    }
8597
 
                                    case 'togglenoview': {
8598
 
                                        $val += 1 << 9;
8599
 
                                        break;
8600
 
                                    }
8601
 
                                    case 'lockedcontents': {
8602
 
                                        $val += 1 << 10;
8603
 
                                        break;
8604
 
                                    }
8605
 
                                    default: {
8606
 
                                        break;
8607
 
                                    }
8608
 
                                }
8609
 
                            }
8610
 
                        } else {
8611
 
                            $val = intval($pl['opt']['f']);
8612
 
                        }
8613
 
                        $annots .= ' /F '.intval($val);
8614
 
                    }
8615
 
                    if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
8616
 
                        $annots .= ' /AS /'.$pl['opt']['as'];
8617
 
                    }
8618
 
                    if (isset($pl['opt']['ap'])) {
8619
 
                        // appearance stream
8620
 
                        $annots .= ' /AP <<';
8621
 
                        if (is_array($pl['opt']['ap'])) {
8622
 
                            foreach ($pl['opt']['ap'] as $apmode => $apdef) {
8623
 
                                // $apmode can be: n = normal; r = rollover; d = down;
8624
 
                                $annots .= ' /'.strtoupper($apmode);
8625
 
                                if (is_array($apdef)) {
8626
 
                                    $annots .= ' <<';
8627
 
                                    foreach ($apdef as $apstate => $stream) {
8628
 
                                        // reference to XObject that define the appearance for this mode-state
8629
 
                                        $apsobjid = $this->_putAPXObject($c, $d, $stream);
8630
 
                                        $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
8631
 
                                    }
8632
 
                                    $annots .= ' >>';
8633
 
                                } else {
8634
 
                                    // reference to XObject that define the appearance for this mode
8635
 
                                    $apsobjid = $this->_putAPXObject($c, $d, $apdef);
8636
 
                                    $annots .= ' '.$apsobjid.' 0 R';
8637
 
                                }
8638
 
                            }
8639
 
                        } else {
8640
 
                            $annots .= $pl['opt']['ap'];
8641
 
                        }
8642
 
                        $annots .= ' >>';
8643
 
                    }
8644
 
                    if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
8645
 
                        $annots .= ' /BS <<';
8646
 
                        $annots .= ' /Type /Border';
8647
 
                        if (isset($pl['opt']['bs']['w'])) {
8648
 
                            $annots .= ' /W '.intval($pl['opt']['bs']['w']);
8649
 
                        }
8650
 
                        $bstyles = array('S', 'D', 'B', 'I', 'U');
8651
 
                        if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
8652
 
                            $annots .= ' /S /'.$pl['opt']['bs']['s'];
8653
 
                        }
8654
 
                        if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
8655
 
                            $annots .= ' /D [';
8656
 
                            foreach ($pl['opt']['bs']['d'] as $cord) {
8657
 
                                $annots .= ' '.intval($cord);
8658
 
                            }
8659
 
                            $annots .= ']';
8660
 
                        }
8661
 
                        $annots .= ' >>';
8662
 
                    } else {
8663
 
                        $annots .= ' /Border [';
8664
 
                        if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
8665
 
                            $annots .= intval($pl['opt']['border'][0]).' ';
8666
 
                            $annots .= intval($pl['opt']['border'][1]).' ';
8667
 
                            $annots .= intval($pl['opt']['border'][2]);
8668
 
                            if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
8669
 
                                $annots .= ' [';
8670
 
                                foreach ($pl['opt']['border'][3] as $dash) {
8671
 
                                    $annots .= intval($dash).' ';
8672
 
                                }
8673
 
                                $annots .= ']';
8674
 
                            }
8675
 
                        } else {
8676
 
                            $annots .= '0 0 0';
8677
 
                        }
8678
 
                        $annots .= ']';
8679
 
                    }
8680
 
                    if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
8681
 
                        $annots .= ' /BE <<';
8682
 
                        $bstyles = array('S', 'C');
8683
 
                        if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
8684
 
                            $annots .= ' /S /'.$pl['opt']['bs']['s'];
8685
 
                        } else {
8686
 
                            $annots .= ' /S /S';
8687
 
                        }
8688
 
                        if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
8689
 
                            $annots .= ' /I '.sprintf(' %.4F', $pl['opt']['be']['i']);
8690
 
                        }
8691
 
                        $annots .= '>>';
8692
 
                    }
8693
 
                    if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
8694
 
                        $annots .= ' /C [';
8695
 
                        foreach ($pl['opt']['c'] as $col) {
8696
 
                            $col = intval($col);
8697
 
                            $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
8698
 
                            $annots .= sprintf(' %.4F', $color);
8699
 
                        }
8700
 
                        $annots .= ']';
8701
 
                    }
8702
 
                    //$annots .= ' /StructParent ';
8703
 
                    //$annots .= ' /OC ';
8704
 
                    $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
8705
 
                    if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
8706
 
                        // this is a markup type
8707
 
                        if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8708
 
                            $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
8709
 
                        }
8710
 
                        //$annots .= ' /Popup ';
8711
 
                        if (isset($pl['opt']['ca'])) {
8712
 
                            $annots .= ' /CA '.sprintf('%.4F', floatval($pl['opt']['ca']));
8713
 
                        }
8714
 
                        if (isset($pl['opt']['rc'])) {
8715
 
                            $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8716
 
                        }
8717
 
                        $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id);
8718
 
                        //$annots .= ' /IRT ';
8719
 
                        if (isset($pl['opt']['subj'])) {
8720
 
                            $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
8721
 
                        }
8722
 
                        //$annots .= ' /RT ';
8723
 
                        //$annots .= ' /IT ';
8724
 
                        //$annots .= ' /ExData ';
8725
 
                    }
8726
 
                    $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
8727
 
                    // Annotation types
8728
 
                    switch (strtolower($pl['opt']['subtype'])) {
8729
 
                        case 'text': {
8730
 
                            if (isset($pl['opt']['open'])) {
8731
 
                                $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
8732
 
                            }
8733
 
                            $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
8734
 
                            if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8735
 
                                $annots .= ' /Name /'.$pl['opt']['name'];
8736
 
                            } else {
8737
 
                                $annots .= ' /Name /Note';
8738
 
                            }
8739
 
                            $statemodels = array('Marked', 'Review');
8740
 
                            if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
8741
 
                                $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8742
 
                            } else {
8743
 
                                $pl['opt']['statemodel'] = 'Marked';
8744
 
                                $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8745
 
                            }
8746
 
                            if ($pl['opt']['statemodel'] == 'Marked') {
8747
 
                                $states = array('Accepted', 'Unmarked');
8748
 
                            } else {
8749
 
                                $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
8750
 
                            }
8751
 
                            if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
8752
 
                                $annots .= ' /State /'.$pl['opt']['state'];
8753
 
                            } else {
8754
 
                                if ($pl['opt']['statemodel'] == 'Marked') {
8755
 
                                    $annots .= ' /State /Unmarked';
8756
 
                                } else {
8757
 
                                    $annots .= ' /State /None';
8758
 
                                }
8759
 
                            }
8760
 
                            break;
8761
 
                        }
8762
 
                        case 'link': {
8763
 
                            if(is_string($pl['txt'])) {
8764
 
                                // external URI link
8765
 
                                $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
8766
 
                            } else {
8767
 
                                // internal link
8768
 
                                $l = $this->links[$pl['txt']];
8769
 
                                $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %.2F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
8770
 
                            }
8771
 
                            $hmodes = array('N', 'I', 'O', 'P');
8772
 
                            if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
8773
 
                                $annots .= ' /H /'.$pl['opt']['h'];
8774
 
                            } else {
8775
 
                                $annots .= ' /H /I';
8776
 
                            }
8777
 
                            //$annots .= ' /PA ';
8778
 
                            //$annots .= ' /Quadpoints ';
8779
 
                            break;
8780
 
                        }
8781
 
                        case 'freetext': {
8782
 
                            if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8783
 
                                $annots .= ' /DA ('.$pl['opt']['da'].')';
8784
 
                            }
8785
 
                            if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8786
 
                                $annots .= ' /Q '.intval($pl['opt']['q']);
8787
 
                            }
8788
 
                            if (isset($pl['opt']['rc'])) {
8789
 
                                $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8790
 
                            }
8791
 
                            if (isset($pl['opt']['ds'])) {
8792
 
                                $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
8793
 
                            }
8794
 
                            if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
8795
 
                                $annots .= ' /CL [';
8796
 
                                foreach ($pl['opt']['cl'] as $cl) {
8797
 
                                    $annots .= sprintf('%.4F ', $cl * $this->k);
8798
 
                                }
8799
 
                                $annots .= ']';
8800
 
                            }
8801
 
                            $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
8802
 
                            if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
8803
 
                                $annots .= ' /IT /'.$pl['opt']['it'];
8804
 
                            }
8805
 
                            if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
8806
 
                                $l = $pl['opt']['rd'][0] * $this->k;
8807
 
                                $r = $pl['opt']['rd'][1] * $this->k;
8808
 
                                $t = $pl['opt']['rd'][2] * $this->k;
8809
 
                                $b = $pl['opt']['rd'][3] * $this->k;
8810
 
                                $annots .= ' /RD ['.sprintf('%.2F %.2F %.2F %.2F', $l, $r, $t, $b).']';
8811
 
                            }
8812
 
                            if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
8813
 
                                $annots .= ' /LE /'.$pl['opt']['le'];
8814
 
                            }
8815
 
                            break;
8816
 
                        }
8817
 
                        case 'line': {
8818
 
                            break;
8819
 
                        }
8820
 
                        case 'square': {
8821
 
                            break;
8822
 
                        }
8823
 
                        case 'circle': {
8824
 
                            break;
8825
 
                        }
8826
 
                        case 'polygon': {
8827
 
                            break;
8828
 
                        }
8829
 
                        case 'polyline': {
8830
 
                            break;
8831
 
                        }
8832
 
                        case 'highlight': {
8833
 
                            break;
8834
 
                        }
8835
 
                        case 'underline': {
8836
 
                            break;
8837
 
                        }
8838
 
                        case 'squiggly': {
8839
 
                            break;
8840
 
                        }
8841
 
                        case 'strikeout': {
8842
 
                            break;
8843
 
                        }
8844
 
                        case 'stamp': {
8845
 
                            break;
8846
 
                        }
8847
 
                        case 'caret': {
8848
 
                            break;
8849
 
                        }
8850
 
                        case 'ink': {
8851
 
                            break;
8852
 
                        }
8853
 
                        case 'popup': {
8854
 
                            break;
8855
 
                        }
8856
 
                        case 'fileattachment': {
8857
 
                            if (!isset($pl['opt']['fs'])) {
8858
 
                                break;
8859
 
                            }
8860
 
                            $filename = basename($pl['opt']['fs']);
8861
 
                            if (isset($this->embeddedfiles[$filename]['n'])) {
8862
 
                                $annots .= ' /FS <</Type /Filespec /F '.$this->_datastring($filename, $annot_obj_id).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
8863
 
                                $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
8864
 
                                if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8865
 
                                    $annots .= ' /Name /'.$pl['opt']['name'];
8866
 
                                } else {
8867
 
                                    $annots .= ' /Name /PushPin';
8868
 
                                }
8869
 
                            }
8870
 
                            break;
8871
 
                        }
8872
 
                        case 'sound': {
8873
 
                            if (!isset($pl['opt']['fs'])) {
8874
 
                                break;
8875
 
                            }
8876
 
                            $filename = basename($pl['opt']['fs']);
8877
 
                            if (isset($this->embeddedfiles[$filename]['n'])) {
8878
 
                                // ... TO BE COMPLETED ...
8879
 
                                // /R /C /B /E /CO /CP
8880
 
                                $annots .= ' /Sound <</Type /Filespec /F '.$this->_datastring($filename, $annot_obj_id).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
8881
 
                                $iconsapp = array('Speaker', 'Mic');
8882
 
                                if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8883
 
                                    $annots .= ' /Name /'.$pl['opt']['name'];
8884
 
                                } else {
8885
 
                                    $annots .= ' /Name /Speaker';
8886
 
                                }
8887
 
                            }
8888
 
                            break;
8889
 
                        }
8890
 
                        case 'movie': {
8891
 
                            break;
8892
 
                        }
8893
 
                        case 'widget': {
8894
 
                            $hmode = array('N', 'I', 'O', 'P', 'T');
8895
 
                            if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
8896
 
                                $annots .= ' /H /'.$pl['opt']['h'];
8897
 
                            }
8898
 
                            if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
8899
 
                                $annots .= ' /MK <<';
8900
 
                                if (isset($pl['opt']['mk']['r'])) {
8901
 
                                    $annots .= ' /R '.$pl['opt']['mk']['r'];
8902
 
                                }
8903
 
                                if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
8904
 
                                    $annots .= ' /BC [';
8905
 
                                    foreach($pl['opt']['mk']['bc'] AS $col) {
8906
 
                                        $col = intval($col);
8907
 
                                        $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
8908
 
                                        $annots .= sprintf(' %.2F', $color);
8909
 
                                    }
8910
 
                                    $annots .= ']';
8911
 
                                }
8912
 
                                if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
8913
 
                                    $annots .= ' /BG [';
8914
 
                                    foreach($pl['opt']['mk']['bg'] AS $col) {
8915
 
                                        $col = intval($col);
8916
 
                                        $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
8917
 
                                        $annots .= sprintf(' %.2F', $color);
8918
 
                                    }
8919
 
                                    $annots .= ']';
8920
 
                                }
8921
 
                                if (isset($pl['opt']['mk']['ca'])) {
8922
 
                                    $annots .= ' /CA '.$pl['opt']['mk']['ca'];
8923
 
                                }
8924
 
                                if (isset($pl['opt']['mk']['rc'])) {
8925
 
                                    $annots .= ' /RC '.$pl['opt']['mk']['rc'];
8926
 
                                }
8927
 
                                if (isset($pl['opt']['mk']['ac'])) {
8928
 
                                    $annots .= ' /AC '.$pl['opt']['mk']['ac'];
8929
 
                                }
8930
 
                                if (isset($pl['opt']['mk']['i'])) {
8931
 
                                    $info = $this->getImageBuffer($pl['opt']['mk']['i']);
8932
 
                                    if ($info !== false) {
8933
 
                                        $annots .= ' /I '.$info['n'].' 0 R';
8934
 
                                    }
8935
 
                                }
8936
 
                                if (isset($pl['opt']['mk']['ri'])) {
8937
 
                                    $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
8938
 
                                    if ($info !== false) {
8939
 
                                        $annots .= ' /RI '.$info['n'].' 0 R';
8940
 
                                    }
8941
 
                                }
8942
 
                                if (isset($pl['opt']['mk']['ix'])) {
8943
 
                                    $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
8944
 
                                    if ($info !== false) {
8945
 
                                        $annots .= ' /IX '.$info['n'].' 0 R';
8946
 
                                    }
8947
 
                                }
8948
 
                                if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
8949
 
                                    $annots .= ' /IF <<';
8950
 
                                    $if_sw = array('A', 'B', 'S', 'N');
8951
 
                                    if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
8952
 
                                        $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
8953
 
                                    }
8954
 
                                    $if_s = array('A', 'P');
8955
 
                                    if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
8956
 
                                        $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
8957
 
                                    }
8958
 
                                    if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
8959
 
                                        $annots .= sprintf(' /A [%.2F %.2F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
8960
 
                                    }
8961
 
                                    if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
8962
 
                                        $annots .= ' /FB true';
8963
 
                                    }
8964
 
                                    $annots .= '>>';
8965
 
                                }
8966
 
                                if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
8967
 
                                    $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
8968
 
                                } else {
8969
 
                                    $annots .= ' /TP 0';
8970
 
                                }
8971
 
                                $annots .= '>>';
8972
 
                            } // end MK
8973
 
                            // --- Entries for field dictionaries ---
8974
 
                            if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
8975
 
                                // set parent
8976
 
                                $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
8977
 
                            }
8978
 
                            if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8979
 
                                $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
8980
 
                            }
8981
 
                            if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8982
 
                                $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
8983
 
                            }
8984
 
                            if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
8985
 
                                $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
8986
 
                            }
8987
 
                            if (isset($pl['opt']['ff'])) {
8988
 
                                if (is_array($pl['opt']['ff'])) {
8989
 
                                    // array of bit settings
8990
 
                                    $flag = 0;
8991
 
                                    foreach($pl['opt']['ff'] as $val) {
8992
 
                                        $flag += 1 << ($val - 1);
8993
 
                                    }
8994
 
                                } else {
8995
 
                                    $flag = intval($pl['opt']['ff']);
8996
 
                                }
8997
 
                                $annots .= ' /Ff '.$flag;
8998
 
                            }
8999
 
                            if (isset($pl['opt']['maxlen'])) {
9000
 
                                $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
9001
 
                            }
9002
 
                            if (isset($pl['opt']['v'])) {
9003
 
                                $annots .= ' /V';
9004
 
                                if (is_array($pl['opt']['v'])) {
9005
 
                                    foreach ($pl['opt']['v'] AS $optval) {
9006
 
                                        if (is_float($optval)) {
9007
 
                                            $optval = sprintf('%.2F', $optval);
9008
 
                                        }
9009
 
                                        $annots .= ' '.$optval;
9010
 
                                    }
9011
 
                                } else {
9012
 
                                    $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
9013
 
                                }
9014
 
                            }
9015
 
                            if (isset($pl['opt']['dv'])) {
9016
 
                                $annots .= ' /DV';
9017
 
                                if (is_array($pl['opt']['dv'])) {
9018
 
                                    foreach ($pl['opt']['dv'] AS $optval) {
9019
 
                                        if (is_float($optval)) {
9020
 
                                            $optval = sprintf('%.2F', $optval);
9021
 
                                        }
9022
 
                                        $annots .= ' '.$optval;
9023
 
                                    }
9024
 
                                } else {
9025
 
                                    $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
9026
 
                                }
9027
 
                            }
9028
 
                            if (isset($pl['opt']['rv'])) {
9029
 
                                $annots .= ' /RV';
9030
 
                                if (is_array($pl['opt']['rv'])) {
9031
 
                                    foreach ($pl['opt']['rv'] AS $optval) {
9032
 
                                        if (is_float($optval)) {
9033
 
                                            $optval = sprintf('%.2F', $optval);
9034
 
                                        }
9035
 
                                        $annots .= ' '.$optval;
9036
 
                                    }
9037
 
                                } else {
9038
 
                                    $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
9039
 
                                }
9040
 
                            }
9041
 
                            if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
9042
 
                                $annots .= ' /A << '.$pl['opt']['a'].' >>';
9043
 
                            }
9044
 
                            if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
9045
 
                                $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
9046
 
                            }
9047
 
                            if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
9048
 
                                $annots .= ' /DA ('.$pl['opt']['da'].')';
9049
 
                            }
9050
 
                            if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
9051
 
                                $annots .= ' /Q '.intval($pl['opt']['q']);
9052
 
                            }
9053
 
                            if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
9054
 
                                $annots .= ' /Opt [';
9055
 
                                foreach($pl['opt']['opt'] AS $copt) {
9056
 
                                    if (is_array($copt)) {
9057
 
                                        $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
9058
 
                                    } else {
9059
 
                                        $annots .= ' '.$this->_textstring($copt, $annot_obj_id);
9060
 
                                    }
9061
 
                                }
9062
 
                                $annots .= ']';
9063
 
                            }
9064
 
                            if (isset($pl['opt']['ti'])) {
9065
 
                                $annots .= ' /TI '.intval($pl['opt']['ti']);
9066
 
                            }
9067
 
                            if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
9068
 
                                $annots .= ' /I [';
9069
 
                                foreach($pl['opt']['i'] AS $copt) {
9070
 
                                    $annots .= intval($copt).' ';
9071
 
                                }
9072
 
                                $annots .= ']';
9073
 
                            }
9074
 
                            break;
9075
 
                        }
9076
 
                        case 'screen': {
9077
 
                            break;
9078
 
                        }
9079
 
                        case 'printermark': {
9080
 
                            break;
9081
 
                        }
9082
 
                        case 'trapnet': {
9083
 
                            break;
9084
 
                        }
9085
 
                        case 'watermark': {
9086
 
                            break;
9087
 
                        }
9088
 
                        case '3d': {
9089
 
                            break;
9090
 
                        }
9091
 
                        default: {
9092
 
                            break;
9093
 
                        }
9094
 
                    }
9095
 
                    $annots .= '>>';
9096
 
                    // create new annotation object
9097
 
                    $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
9098
 
                    if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) {
9099
 
                        // store reference of form object
9100
 
                        $this->form_obj_id[] = $annot_obj_id;
9101
 
                    }
9102
 
                }
9103
 
            }
9104
 
        } // end for each page
9105
 
    }
9106
 
 
9107
 
    /**
9108
 
     * Put appearance streams XObject used to define annotation's appearance states
9109
 
     * @param $w (int) annotation width
9110
 
     * @param $h (int) annotation height
9111
 
     * @param $stream (string) appearance stream
9112
 
     * @return int object ID
9113
 
     * @protected
9114
 
     * @since 4.8.001 (2009-09-09)
9115
 
     */
9116
 
    protected function _putAPXObject($w=0, $h=0, $stream='') {
9117
 
        $stream = trim($stream);
9118
 
        $out = $this->_getobj()."\n";
9119
 
        $this->xobjects['AX'.$this->n] = array('n' => $this->n);
9120
 
        $out .= '<<';
9121
 
        $out .= ' /Type /XObject';
9122
 
        $out .= ' /Subtype /Form';
9123
 
        $out .= ' /FormType 1';
9124
 
        if ($this->compress) {
9125
 
            $stream = gzcompress($stream);
9126
 
            $out .= ' /Filter /FlateDecode';
9127
 
        }
9128
 
        $rect = sprintf('%.2F %.2F', $w, $h);
9129
 
        $out .= ' /BBox [0 0 '.$rect.']';
9130
 
        $out .= ' /Matrix [1 0 0 1 0 0]';
9131
 
        $out .= ' /Resources <<';
9132
 
        $out .= ' /ProcSet [/PDF /Text]';
9133
 
        $out .= ' /Font <<';
9134
 
        foreach ($this->annotation_fonts as $fontkey => $fontid) {
9135
 
            $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9136
 
        }
9137
 
        $out .= ' >>';
9138
 
        $out .= ' >>';
9139
 
        $stream = $this->_getrawstream($stream);
9140
 
        $out .= ' /Length '.strlen($stream);
9141
 
        $out .= ' >>';
9142
 
        $out .= ' stream'."\n".$stream."\n".'endstream';
9143
 
        $out .= "\n".'endobj';
9144
 
        $this->_out($out);
9145
 
        return $this->n;
9146
 
    }
9147
 
 
9148
 
    /**
9149
 
     * Get ULONG from string (Big Endian 32-bit unsigned integer).
9150
 
     * @param $str (string) string from where to extract value
9151
 
     * @param $offset (int) point from where to read the data
9152
 
     * @return int 32 bit value
9153
 
     * @author Nicola Asuni
9154
 
     * @protected
9155
 
     * @since 5.2.000 (2010-06-02)
9156
 
     */
9157
 
    protected function _getULONG(&$str, &$offset) {
9158
 
        $v = unpack('Ni', substr($str, $offset, 4));
9159
 
        $offset += 4;
9160
 
        return $v['i'];
9161
 
    }
9162
 
 
9163
 
    /**
9164
 
     * Get USHORT from string (Big Endian 16-bit unsigned integer).
9165
 
     * @param $str (string) string from where to extract value
9166
 
     * @param $offset (int) point from where to read the data
9167
 
     * @return int 16 bit value
9168
 
     * @author Nicola Asuni
9169
 
     * @protected
9170
 
     * @since 5.2.000 (2010-06-02)
9171
 
     */
9172
 
    protected function _getUSHORT(&$str, &$offset) {
9173
 
        $v = unpack('ni', substr($str, $offset, 2));
9174
 
        $offset += 2;
9175
 
        return $v['i'];
9176
 
    }
9177
 
 
9178
 
    /**
9179
 
     * Get SHORT from string (Big Endian 16-bit signed integer).
9180
 
     * @param $str (string) string from where to extract value
9181
 
     * @param $offset (int) point from where to read the data
9182
 
     * @return int 16 bit value
9183
 
     * @author Nicola Asuni
9184
 
     * @protected
9185
 
     * @since 5.2.000 (2010-06-02)
9186
 
     */
9187
 
    protected function _getSHORT(&$str, &$offset) {
9188
 
        $v = unpack('si', substr($str, $offset, 2));
9189
 
        $offset += 2;
9190
 
        return $v['i'];
9191
 
    }
9192
 
 
9193
 
    /**
9194
 
     * Get BYTE from string (8-bit unsigned integer).
9195
 
     * @param $str (string) string from where to extract value
9196
 
     * @param $offset (int) point from where to read the data
9197
 
     * @return int 8 bit value
9198
 
     * @author Nicola Asuni
9199
 
     * @protected
9200
 
     * @since 5.2.000 (2010-06-02)
9201
 
     */
9202
 
    protected function _getBYTE(&$str, &$offset) {
9203
 
        $v = unpack('Ci', substr($str, $offset, 1));
9204
 
        ++$offset;
9205
 
        return $v['i'];
9206
 
    }
9207
 
 
9208
 
    /**
9209
 
     * Returns a subset of the TrueType font data without the unused glyphs.
9210
 
     * @param $font (string) TrueType font data
9211
 
     * @param $subsetchars (array) array of used characters (the glyphs to keep)
9212
 
     * @return string a subset of TrueType font data without the unused glyphs
9213
 
     * @author Nicola Asuni
9214
 
     * @protected
9215
 
     * @since 5.2.000 (2010-06-02)
9216
 
     */
9217
 
    protected function _getTrueTypeFontSubset($font, $subsetchars) {
9218
 
        ksort($subsetchars);
9219
 
        $offset = 0; // offset position of the font data
9220
 
        if ($this->_getULONG($font, $offset) != 0x10000) {
9221
 
            // sfnt version must be 0x00010000 for TrueType version 1.0.
9222
 
            return $font;
9223
 
        }
9224
 
        // get number of tables
9225
 
        $numTables = $this->_getUSHORT($font, $offset);
9226
 
        // skip searchRange, entrySelector and rangeShift
9227
 
        $offset += 6;
9228
 
        // tables array
9229
 
        $table = array();
9230
 
        // for each table
9231
 
        for ($i = 0; $i < $numTables; ++$i) {
9232
 
            // get table info
9233
 
            $tag = substr($font, $offset, 4);
9234
 
            $offset += 4;
9235
 
            $table[$tag] = array();
9236
 
            $table[$tag]['checkSum'] = $this->_getULONG($font, $offset);
9237
 
            $table[$tag]['offset'] = $this->_getULONG($font, $offset);
9238
 
            $table[$tag]['length'] = $this->_getULONG($font, $offset);
9239
 
        }
9240
 
        // check magicNumber
9241
 
        $offset = $table['head']['offset'] + 12;
9242
 
        if ($this->_getULONG($font, $offset) != 0x5F0F3CF5) {
9243
 
            // magicNumber must be 0x5F0F3CF5
9244
 
            return $font;
9245
 
        }
9246
 
        // get offset mode (indexToLocFormat : 0 = short, 1 = long)
9247
 
        $offset = $table['head']['offset'] + 50;
9248
 
        $short_offset = ($this->_getSHORT($font, $offset) == 0);
9249
 
        // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table
9250
 
        $indexToLoc = array();
9251
 
        $offset = $table['loca']['offset'];
9252
 
        if ($short_offset) {
9253
 
            // short version
9254
 
            $n = $table['loca']['length'] / 2; // numGlyphs + 1
9255
 
            for ($i = 0; $i < $n; ++$i) {
9256
 
                $indexToLoc[$i] = $this->_getUSHORT($font, $offset) * 2;
9257
 
            }
9258
 
        } else {
9259
 
            // long version
9260
 
            $n = $table['loca']['length'] / 4; // numGlyphs + 1
9261
 
            for ($i = 0; $i < $n; ++$i) {
9262
 
                $indexToLoc[$i] = $this->_getULONG($font, $offset);
9263
 
            }
9264
 
        }
9265
 
        // get glyphs indexes of chars from cmap table
9266
 
        $subsetglyphs = array(); // glyph IDs on key
9267
 
        $subsetglyphs[0] = true; // character codes that do not correspond to any glyph in the font should be mapped to glyph index 0
9268
 
        $offset = $table['cmap']['offset'] + 2;
9269
 
        $numEncodingTables = $this->_getUSHORT($font, $offset);
9270
 
        $encodingTables = array();
9271
 
        for ($i = 0; $i < $numEncodingTables; ++$i) {
9272
 
            $encodingTables[$i]['platformID'] = $this->_getUSHORT($font, $offset);
9273
 
            $encodingTables[$i]['encodingID'] = $this->_getUSHORT($font, $offset);
9274
 
            $encodingTables[$i]['offset'] = $this->_getULONG($font, $offset);
9275
 
        }
9276
 
        foreach ($encodingTables as $enctable) {
9277
 
            if (($enctable['platformID'] == 3) AND ($enctable['encodingID'] == 0)) {
9278
 
                $modesymbol = true;
9279
 
            } else {
9280
 
                $modesymbol = false;
9281
 
            }
9282
 
            $offset = $table['cmap']['offset'] + $enctable['offset'];
9283
 
            $format = $this->_getUSHORT($font, $offset);
9284
 
            switch ($format) {
9285
 
                case 0: { // Format 0: Byte encoding table
9286
 
                    $offset += 4; // skip length and version/language
9287
 
                    for ($k = 0; $k < 256; ++$k) {
9288
 
                        if (isset($subsetchars[$k])) {
9289
 
                            $g = $this->_getBYTE($font, $offset);
9290
 
                            $subsetglyphs[$g] = $k;
9291
 
                        } else {
9292
 
                            ++$offset;
9293
 
                        }
9294
 
                    }
9295
 
                    break;
9296
 
                }
9297
 
                case 2: { // Format 2: High-byte mapping through table
9298
 
                    $offset += 4; // skip length and version
9299
 
                    // to be implemented ...
9300
 
                    break;
9301
 
                }
9302
 
                case 4: { // Format 4: Segment mapping to delta values
9303
 
                    $length = $this->_getUSHORT($font, $offset);
9304
 
                    $offset += 2; // skip version/language
9305
 
                    $segCount = ($this->_getUSHORT($font, $offset) / 2);
9306
 
                    $offset += 6; // skip searchRange, entrySelector, rangeShift
9307
 
                    $endCount = array(); // array of end character codes for each segment
9308
 
                    for ($k = 0; $k < $segCount; ++$k) {
9309
 
                        $endCount[$k] = $this->_getUSHORT($font, $offset);
9310
 
                    }
9311
 
                    $offset += 2; // skip reservedPad
9312
 
                    $startCount = array(); // array of start character codes for each segment
9313
 
                    for ($k = 0; $k < $segCount; ++$k) {
9314
 
                        $startCount[$k] = $this->_getUSHORT($font, $offset);
9315
 
                    }
9316
 
                    $idDelta = array(); // delta for all character codes in segment
9317
 
                    for ($k = 0; $k < $segCount; ++$k) {
9318
 
                        $idDelta[$k] = $this->_getUSHORT($font, $offset);
9319
 
                    }
9320
 
                    $idRangeOffset = array(); // Offsets into glyphIdArray or 0
9321
 
                    for ($k = 0; $k < $segCount; ++$k) {
9322
 
                        $idRangeOffset[$k] = $this->_getUSHORT($font, $offset);
9323
 
                    }
9324
 
                    $gidlen = ($length / 2) - 8 - (4 * $segCount);
9325
 
                    $glyphIdArray = array(); // glyph index array
9326
 
                    for ($k = 0; $k < $gidlen; ++$k) {
9327
 
                        $glyphIdArray[$k] = $this->_getUSHORT($font, $offset);
9328
 
                    }
9329
 
                    for ($k = 0; $k < $segCount; ++$k) {
9330
 
                        for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) {
9331
 
                            if (isset($subsetchars[$c])) {
9332
 
                                if ($idRangeOffset[$k] == 0) {
9333
 
                                    $g = $c;
9334
 
                                } else {
9335
 
                                    $gid = (($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k));
9336
 
                                    $g = $glyphIdArray[$gid];
9337
 
                                }
9338
 
                                $g += ($idDelta[$k] - 65536);
9339
 
                                if ($g < 0) {
9340
 
                                    $g = 0;
9341
 
                                }
9342
 
                                $subsetglyphs[$g] = $c;
9343
 
                            }
9344
 
                        }
9345
 
                    }
9346
 
                    break;
9347
 
                }
9348
 
                case 6: { // Format 6: Trimmed table mapping
9349
 
                    $offset += 4; // skip length and version/language
9350
 
                    $firstCode = $this->_getUSHORT($font, $offset);
9351
 
                    $entryCount = $this->_getUSHORT($font, $offset);
9352
 
                    for ($k = 0; $k < $entryCount; ++$k) {
9353
 
                        $c = ($k + $firstCode);
9354
 
                        if (isset($subsetchars[$c])) {
9355
 
                            $g = $this->_getUSHORT($font, $offset);
9356
 
                            $subsetglyphs[$g] = $c;
9357
 
                        } else {
9358
 
                            $offset += 2;
9359
 
                        }
9360
 
                    }
9361
 
                    break;
9362
 
                }
9363
 
                case 8: { // Format 8: Mixed 16-bit and 32-bit coverage
9364
 
                    $offset += 10; // skip length and version
9365
 
                    // to be implemented ...
9366
 
                    break;
9367
 
                }
9368
 
                case 10: { // Format 10: Trimmed array
9369
 
                    $offset += 10; // skip length and version/language
9370
 
                    $startCharCode = $this->_getULONG($font, $offset);
9371
 
                    $numChars = $this->_getULONG($font, $offset);
9372
 
                    for ($k = 0; $k < $numChars; ++$k) {
9373
 
                        $c = ($k + $startCharCode);
9374
 
                        if (isset($subsetchars[$c])) {
9375
 
                            $g = $this->_getUSHORT($font, $offset);
9376
 
                            $subsetglyphs[$g] = $c;
9377
 
                        } else {
9378
 
                            $offset += 2;
9379
 
                        }
9380
 
                    }
9381
 
                    break;
9382
 
                }
9383
 
                case 12: { // Format 12: Segmented coverage
9384
 
                    $offset += 10; // skip length and version/language
9385
 
                    $nGroups = $this->_getULONG($font, $offset);
9386
 
                    for ($k = 0; $k < $nGroups; ++$k) {
9387
 
                        $startCharCode = $this->_getULONG($font, $offset);
9388
 
                        $endCharCode = $this->_getULONG($font, $offset);
9389
 
                        $startGlyphCode = $this->_getULONG($font, $offset);
9390
 
                        for ($c = $startCharCode; $c <= $endCharCode; ++$c) {
9391
 
                            if (isset($subsetchars[$c])) {
9392
 
                                $subsetglyphs[$startGlyphCode] = $c;
9393
 
                            }
9394
 
                            ++$startGlyphCode;
9395
 
                        }
9396
 
                    }
9397
 
                    break;
9398
 
                }
9399
 
            }
9400
 
        }
9401
 
        // sort glyphs by key
9402
 
        ksort($subsetglyphs);
9403
 
        // add composite glyps to $subsetglyphs and remove missing glyphs
9404
 
        foreach ($subsetglyphs as $key => $val) {
9405
 
            if (isset($indexToLoc[$key])) {
9406
 
                $offset = $table['glyf']['offset'] + $indexToLoc[$key];
9407
 
                $numberOfContours = $this->_getSHORT($font, $offset);
9408
 
                if ($numberOfContours < 0) { // composite glyph
9409
 
                    $offset += 8; // skip xMin, yMin, xMax, yMax
9410
 
                    do {
9411
 
                        $flags = $this->_getUSHORT($font, $offset);
9412
 
                        $glyphIndex = $this->_getUSHORT($font, $offset);
9413
 
                        if (!isset($subsetglyphs[$glyphIndex]) AND isset($indexToLoc[$glyphIndex])) {
9414
 
                            // add missing glyphs
9415
 
                            $subsetglyphs[$glyphIndex] = true;
9416
 
                        }
9417
 
                        // skip some bytes by case
9418
 
                        if ($flags & 1) {
9419
 
                            $offset += 4;
9420
 
                        } else {
9421
 
                            $offset += 2;
9422
 
                        }
9423
 
                        if ($flags & 8) {
9424
 
                            $offset += 2;
9425
 
                        } elseif ($flags & 64) {
9426
 
                            $offset += 4;
9427
 
                        } elseif ($flags & 128) {
9428
 
                            $offset += 8;
9429
 
                        }
9430
 
                    } while ($flags & 32);
9431
 
                }
9432
 
            } else {
9433
 
                unset($subsetglyphs[$key]);
9434
 
            }
9435
 
        }
9436
 
        // build new glyf table with only used glyphs
9437
 
        $glyf = '';
9438
 
        $glyfSize = 0;
9439
 
        // create new empty indexToLoc table
9440
 
        $newIndexToLoc = array_fill(0, count($indexToLoc), 0);
9441
 
        $goffset = 0;
9442
 
        foreach ($subsetglyphs as $glyphID => $char) {
9443
 
            if (isset($indexToLoc[$glyphID]) AND isset($indexToLoc[($glyphID + 1)])) {
9444
 
                $start = $indexToLoc[$glyphID];
9445
 
                $length = ($indexToLoc[($glyphID + 1)] - $start);
9446
 
                $glyf .= substr($font, ($table['glyf']['offset'] + $start), $length);
9447
 
                $newIndexToLoc[$glyphID] = $goffset;
9448
 
                $goffset += $length;
9449
 
            }
9450
 
        }
9451
 
        // build new loca table
9452
 
        $loca = '';
9453
 
        if ($short_offset) {
9454
 
            foreach ($newIndexToLoc as $glyphID => $offset) {
9455
 
                $loca .= pack('n', ($offset / 2));
9456
 
            }
9457
 
        } else {
9458
 
            foreach ($newIndexToLoc as $glyphID => $offset) {
9459
 
                $loca .= pack('N', $offset);
9460
 
            }
9461
 
        }
9462
 
        // array of table names to preserve (loca and glyf tables will be added later)
9463
 
        //$table_names = array ('cmap', 'head', 'hhea', 'hmtx', 'maxp', 'name', 'OS/2', 'post', 'cvt ', 'fpgm', 'prep');
9464
 
        // the cmap table is not needed and shall not be present, since the mapping from character codes to glyph descriptions is provided separately
9465
 
        $table_names = array ('head', 'hhea', 'hmtx', 'maxp', 'cvt ', 'fpgm', 'prep'); // minimum required table names
9466
 
        // get the tables to preserve
9467
 
        $offset = 12;
9468
 
        foreach ($table as $tag => $val) {
9469
 
            if (in_array($tag, $table_names)) {
9470
 
                $table[$tag]['data'] = substr($font, $table[$tag]['offset'], $table[$tag]['length']);
9471
 
                if ($tag == 'head') {
9472
 
                    // set the checkSumAdjustment to 0
9473
 
                    $table[$tag]['data'] = substr($table[$tag]['data'], 0, 8)."\x0\x0\x0\x0".substr($table[$tag]['data'], 12);
9474
 
                }
9475
 
                $pad = 4 - ($table[$tag]['length'] % 4);
9476
 
                if ($pad != 4) {
9477
 
                    // the length of a table must be a multiple of four bytes
9478
 
                    $table[$tag]['length'] += $pad;
9479
 
                    $table[$tag]['data'] .= str_repeat("\x0", $pad);
9480
 
                }
9481
 
                $table[$tag]['offset'] = $offset;
9482
 
                $offset += $table[$tag]['length'];
9483
 
                // check sum is not changed (so keep the following line commented)
9484
 
                //$table[$tag]['checkSum'] = $this->_getTTFtableChecksum($table[$tag]['data'], $table[$tag]['length']);
9485
 
            } else {
9486
 
                unset($table[$tag]);
9487
 
            }
9488
 
        }
9489
 
        // add loca
9490
 
        $table['loca']['data'] = $loca;
9491
 
        $table['loca']['length'] = strlen($loca);
9492
 
        $pad = 4 - ($table['loca']['length'] % 4);
9493
 
        if ($pad != 4) {
9494
 
            // the length of a table must be a multiple of four bytes
9495
 
            $table['loca']['length'] += $pad;
9496
 
            $table['loca']['data'] .= str_repeat("\x0", $pad);
9497
 
        }
9498
 
        $table['loca']['offset'] = $offset;
9499
 
        $table['loca']['checkSum'] = $this->_getTTFtableChecksum($table['loca']['data'], $table['loca']['length']);
9500
 
        $offset += $table['loca']['length'];
9501
 
        // add glyf
9502
 
        $table['glyf']['data'] = $glyf;
9503
 
        $table['glyf']['length'] = strlen($glyf);
9504
 
        $pad = 4 - ($table['glyf']['length'] % 4);
9505
 
        if ($pad != 4) {
9506
 
            // the length of a table must be a multiple of four bytes
9507
 
            $table['glyf']['length'] += $pad;
9508
 
            $table['glyf']['data'] .= str_repeat("\x0", $pad);
9509
 
        }
9510
 
        $table['glyf']['offset'] = $offset;
9511
 
        $table['glyf']['checkSum'] = $this->_getTTFtableChecksum($table['glyf']['data'], $table['glyf']['length']);
9512
 
        // rebuild font
9513
 
        $font = '';
9514
 
        $font .= pack('N', 0x10000); // sfnt version
9515
 
        $numTables = count($table);
9516
 
        $font .= pack('n', $numTables); // numTables
9517
 
        $entrySelector = floor(log($numTables, 2));
9518
 
        $searchRange = pow(2, $entrySelector) * 16;
9519
 
        $rangeShift = ($numTables * 16) - $searchRange;
9520
 
        $font .= pack('n', $searchRange); // searchRange
9521
 
        $font .= pack('n', $entrySelector); // entrySelector
9522
 
        $font .= pack('n', $rangeShift); // rangeShift
9523
 
        $offset = ($numTables * 16);
9524
 
        foreach ($table as $tag => $data) {
9525
 
            $font .= $tag; // tag
9526
 
            $font .= pack('N', $data['checkSum']); // checkSum
9527
 
            $font .= pack('N', ($data['offset'] + $offset)); // offset
9528
 
            $font .= pack('N', $data['length']); // length
9529
 
        }
9530
 
        foreach ($table as $data) {
9531
 
            $font .= $data['data'];
9532
 
        }
9533
 
        // set checkSumAdjustment on head table
9534
 
        $checkSumAdjustment = 0xB1B0AFBA - $this->_getTTFtableChecksum($font, strlen($font));
9535
 
        $font = substr($font, 0, $table['head']['offset'] + 8).pack('N', $checkSumAdjustment).substr($font, $table['head']['offset'] + 12);
9536
 
        return $font;
9537
 
    }
9538
 
 
9539
 
    /**
9540
 
     * Returs the checksum of a TTF table.
9541
 
     * @param $table (string) table to check
9542
 
     * @param $length (int) lenght of table in bytes
9543
 
     * @return int checksum
9544
 
     * @author Nicola Asuni
9545
 
     * @protected
9546
 
     * @since 5.2.000 (2010-06-02)
9547
 
     */
9548
 
    protected function _getTTFtableChecksum($table, $length) {
9549
 
        $sum = 0;
9550
 
        $tlen = ($length / 4);
9551
 
        $offset = 0;
9552
 
        for ($i = 0; $i < $tlen; ++$i) {
9553
 
            $v = unpack('Ni', substr($table, $offset, 4));
9554
 
            $sum += $v['i'];
9555
 
            $offset += 4;
9556
 
        }
9557
 
        $sum = unpack('Ni', pack('N', $sum));
9558
 
        return $sum['i'];
9559
 
    }
9560
 
 
9561
 
    /**
9562
 
     * Outputs font widths
9563
 
     * @param $font (array) font data
9564
 
     * @param $cidoffset (int) offset for CID values
9565
 
     * @return PDF command string for font widths
9566
 
     * @author Nicola Asuni
9567
 
     * @protected
9568
 
     * @since 4.4.000 (2008-12-07)
9569
 
     */
9570
 
    protected function _putfontwidths($font, $cidoffset=0) {
9571
 
        ksort($font['cw']);
9572
 
        $rangeid = 0;
9573
 
        $range = array();
9574
 
        $prevcid = -2;
9575
 
        $prevwidth = -1;
9576
 
        $interval = false;
9577
 
        // for each character
9578
 
        foreach ($font['cw'] as $cid => $width) {
9579
 
            $cid -= $cidoffset;
9580
 
            if ($font['subset'] AND ($cid > 255) AND (!isset($font['subsetchars'][$cid]))) {
9581
 
                // ignore the unused characters (font subsetting)
9582
 
                continue;
9583
 
            }
9584
 
            if ($width != $font['dw']) {
9585
 
                if ($cid == ($prevcid + 1)) {
9586
 
                    // consecutive CID
9587
 
                    if ($width == $prevwidth) {
9588
 
                        if ($width == $range[$rangeid][0]) {
9589
 
                            $range[$rangeid][] = $width;
9590
 
                        } else {
9591
 
                            array_pop($range[$rangeid]);
9592
 
                            // new range
9593
 
                            $rangeid = $prevcid;
9594
 
                            $range[$rangeid] = array();
9595
 
                            $range[$rangeid][] = $prevwidth;
9596
 
                            $range[$rangeid][] = $width;
9597
 
                        }
9598
 
                        $interval = true;
9599
 
                        $range[$rangeid]['interval'] = true;
9600
 
                    } else {
9601
 
                        if ($interval) {
9602
 
                            // new range
9603
 
                            $rangeid = $cid;
9604
 
                            $range[$rangeid] = array();
9605
 
                            $range[$rangeid][] = $width;
9606
 
                        } else {
9607
 
                            $range[$rangeid][] = $width;
9608
 
                        }
9609
 
                        $interval = false;
9610
 
                    }
9611
 
                } else {
9612
 
                    // new range
9613
 
                    $rangeid = $cid;
9614
 
                    $range[$rangeid] = array();
9615
 
                    $range[$rangeid][] = $width;
9616
 
                    $interval = false;
9617
 
                }
9618
 
                $prevcid = $cid;
9619
 
                $prevwidth = $width;
9620
 
            }
9621
 
        }
9622
 
        // optimize ranges
9623
 
        $prevk = -1;
9624
 
        $nextk = -1;
9625
 
        $prevint = false;
9626
 
        foreach ($range as $k => $ws) {
9627
 
            $cws = count($ws);
9628
 
            if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
9629
 
                if (isset($range[$k]['interval'])) {
9630
 
                    unset($range[$k]['interval']);
9631
 
                }
9632
 
                $range[$prevk] = array_merge($range[$prevk], $range[$k]);
9633
 
                unset($range[$k]);
9634
 
            } else {
9635
 
                $prevk = $k;
9636
 
            }
9637
 
            $nextk = $k + $cws;
9638
 
            if (isset($ws['interval'])) {
9639
 
                if ($cws > 3) {
9640
 
                    $prevint = true;
9641
 
                } else {
9642
 
                    $prevint = false;
9643
 
                }
9644
 
                unset($range[$k]['interval']);
9645
 
                --$nextk;
9646
 
            } else {
9647
 
                $prevint = false;
9648
 
            }
9649
 
        }
9650
 
        // output data
9651
 
        $w = '';
9652
 
        foreach ($range as $k => $ws) {
9653
 
            if (count(array_count_values($ws)) == 1) {
9654
 
                // interval mode is more compact
9655
 
                $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0];
9656
 
            } else {
9657
 
                // range mode
9658
 
                $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
9659
 
            }
9660
 
        }
9661
 
        return '/W ['.$w.' ]';
9662
 
    }
9663
 
 
9664
 
    /**
9665
 
     * Output fonts.
9666
 
     * @author Nicola Asuni
9667
 
     * @protected
9668
 
     */
9669
 
    protected function _putfonts() {
9670
 
        $nf = $this->n;
9671
 
        foreach ($this->diffs as $diff) {
9672
 
            //Encodings
9673
 
            $this->_newobj();
9674
 
            $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
9675
 
        }
9676
 
        $mqr = $this->get_mqr();
9677
 
        $this->set_mqr(false);
9678
 
        foreach ($this->FontFiles as $file => $info) {
9679
 
            // search and get font file to embedd
9680
 
            $fontdir = $info['fontdir'];
9681
 
            $file = strtolower($file);
9682
 
            $fontfile = '';
9683
 
            // search files on various directories
9684
 
            if (($fontdir !== false) AND file_exists($fontdir.$file)) {
9685
 
                $fontfile = $fontdir.$file;
9686
 
            } elseif (file_exists($this->_getfontpath().$file)) {
9687
 
                $fontfile = $this->_getfontpath().$file;
9688
 
            } elseif (file_exists($file)) {
9689
 
                $fontfile = $file;
9690
 
            }
9691
 
            if (!$this->empty_string($fontfile)) {
9692
 
                $font = file_get_contents($fontfile);
9693
 
                $compressed = (substr($file, -2) == '.z');
9694
 
                if ((!$compressed) AND (isset($info['length2']))) {
9695
 
                    $header = (ord($font{0}) == 128);
9696
 
                    if ($header) {
9697
 
                        //Strip first binary header
9698
 
                        $font = substr($font, 6);
9699
 
                    }
9700
 
                    if ($header AND (ord($font{$info['length1']}) == 128)) {
9701
 
                        //Strip second binary header
9702
 
                        $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
9703
 
                    }
9704
 
                } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
9705
 
                    if ($compressed) {
9706
 
                        // uncompress font
9707
 
                        $font = gzuncompress($font);
9708
 
                    }
9709
 
                    // merge subset characters
9710
 
                    $subsetchars = array(); // used chars
9711
 
                    foreach ($info['fontkeys'] as $fontkey) {
9712
 
                        $fontinfo = $this->getFontBuffer($fontkey);
9713
 
                        $subsetchars += $fontinfo['subsetchars'];
9714
 
                    }
9715
 
                    $font = $this->_getTrueTypeFontSubset($font, $subsetchars);
9716
 
                    if ($compressed) {
9717
 
                        // recompress font
9718
 
                        $font = gzcompress($font);
9719
 
                    }
9720
 
                }
9721
 
                $this->_newobj();
9722
 
                $this->FontFiles[$file]['n'] = $this->n;
9723
 
                $stream = $this->_getrawstream($font);
9724
 
                $out = '<< /Length '.strlen($stream);
9725
 
                if ($compressed) {
9726
 
                    $out .= ' /Filter /FlateDecode';
9727
 
                }
9728
 
                $out .= ' /Length1 '.$info['length1'];
9729
 
                if (isset($info['length2'])) {
9730
 
                    $out .= ' /Length2 '.$info['length2'].' /Length3 0';
9731
 
                }
9732
 
                $out .= ' >>';
9733
 
                $out .= ' stream'."\n".$stream."\n".'endstream';
9734
 
                $out .= "\n".'endobj';
9735
 
                $this->_out($out);
9736
 
            }
9737
 
        }
9738
 
        $this->set_mqr($mqr);
9739
 
        foreach ($this->fontkeys as $k) {
9740
 
            //Font objects
9741
 
            $font = $this->getFontBuffer($k);
9742
 
            $type = $font['type'];
9743
 
            $name = $font['name'];
9744
 
            if ($type == 'core') {
9745
 
                // standard core font
9746
 
                $out = $this->_getobj($this->font_obj_ids[$k])."\n";
9747
 
                $out .= '<</Type /Font';
9748
 
                $out .= ' /Subtype /Type1';
9749
 
                $out .= ' /BaseFont /'.$name;
9750
 
                $out .= ' /Name /F'.$font['i'];
9751
 
                if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
9752
 
                    $out .= ' /Encoding /WinAnsiEncoding';
9753
 
                }
9754
 
                if ($k == 'helvetica') {
9755
 
                    // add default font for annotations
9756
 
                    $this->annotation_fonts[$k] = $font['i'];
9757
 
                }
9758
 
                $out .= ' >>';
9759
 
                $out .= "\n".'endobj';
9760
 
                $this->_out($out);
9761
 
            } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
9762
 
                // additional Type1 or TrueType font
9763
 
                $out = $this->_getobj($this->font_obj_ids[$k])."\n";
9764
 
                $out .= '<</Type /Font';
9765
 
                $out .= ' /Subtype /'.$type;
9766
 
                $out .= ' /BaseFont /'.$name;
9767
 
                $out .= ' /Name /F'.$font['i'];
9768
 
                $out .= ' /FirstChar 32 /LastChar 255';
9769
 
                $out .= ' /Widths '.($this->n + 1).' 0 R';
9770
 
                $out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
9771
 
                if ($font['enc']) {
9772
 
                    if (isset($font['diff'])) {
9773
 
                        $out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
9774
 
                    } else {
9775
 
                        $out .= ' /Encoding /WinAnsiEncoding';
9776
 
                    }
9777
 
                }
9778
 
                $out .= ' >>';
9779
 
                $out .= "\n".'endobj';
9780
 
                $this->_out($out);
9781
 
                // Widths
9782
 
                $this->_newobj();
9783
 
                $cw = &$font['cw'];
9784
 
                $s = '[';
9785
 
                for ($i = 32; $i < 256; ++$i) {
9786
 
                    $s .= $cw[$i].' ';
9787
 
                }
9788
 
                $s .= ']';
9789
 
                $s .= "\n".'endobj';
9790
 
                $this->_out($s);
9791
 
                //Descriptor
9792
 
                $this->_newobj();
9793
 
                $s = '<</Type /FontDescriptor /FontName /'.$name;
9794
 
                foreach ($font['desc'] as $fdk => $fdv) {
9795
 
                    if(is_float($fdv)) {
9796
 
                        $fdv = sprintf('%.3F', $fdv);
9797
 
                    }
9798
 
                    $s .= ' /'.$fdk.' '.$fdv.'';
9799
 
                }
9800
 
                if (!$this->empty_string($font['file'])) {
9801
 
                    $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
9802
 
                }
9803
 
                $s .= '>>';
9804
 
                $s .= "\n".'endobj';
9805
 
                $this->_out($s);
9806
 
            } else {
9807
 
                // additional types
9808
 
                $mtd = '_put'.strtolower($type);
9809
 
                if (!method_exists($this, $mtd)) {
9810
 
                    $this->Error('Unsupported font type: '.$type);
9811
 
                }
9812
 
                $this->$mtd($font);
9813
 
            }
9814
 
        }
9815
 
    }
9816
 
 
9817
 
    /**
9818
 
     * Adds unicode fonts.<br>
9819
 
     * Based on PDF Reference 1.3 (section 5)
9820
 
     * @param $font (array) font data
9821
 
     * @protected
9822
 
     * @author Nicola Asuni
9823
 
     * @since 1.52.0.TC005 (2005-01-05)
9824
 
     */
9825
 
    protected function _puttruetypeunicode($font) {
9826
 
        $fontname = '';
9827
 
        if ($font['subset']) {
9828
 
            // change name for font subsetting
9829
 
            $subtag = sprintf('%06u', $font['i']);
9830
 
            $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
9831
 
            $fontname .= $subtag.'+';
9832
 
        }
9833
 
        $fontname .= $font['name'];
9834
 
        // Type0 Font
9835
 
        // A composite font composed of other fonts, organized hierarchically
9836
 
        $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
9837
 
        $out .= '<< /Type /Font';
9838
 
        $out .= ' /Subtype /Type0';
9839
 
        $out .= ' /BaseFont /'.$fontname;
9840
 
        $out .= ' /Name /F'.$font['i'];
9841
 
        $out .= ' /Encoding /'.$font['enc'];
9842
 
        $out .= ' /ToUnicode '.($this->n + 1).' 0 R';
9843
 
        $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]';
9844
 
        $out .= ' >>';
9845
 
        $out .= "\n".'endobj';
9846
 
        $this->_out($out);
9847
 
        // ToUnicode map for Identity-H
9848
 
        $stream = "/CIDInit /ProcSet findresource begin\n";
9849
 
        $stream .= "12 dict begin\n";
9850
 
        $stream .= "begincmap\n";
9851
 
        $stream .= "/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n";
9852
 
        $stream .= "/CMapName /Adobe-Identity-UCS def\n";
9853
 
        $stream .= "/CMapType 2 def\n";
9854
 
        $stream .= "/WMode 0 def\n";
9855
 
        $stream .= "1 begincodespacerange\n";
9856
 
        $stream .= "<0000> <FFFF>\n";
9857
 
        $stream .= "endcodespacerange\n";
9858
 
        $stream .= "100 beginbfrange\n";
9859
 
        $stream .= "<0000> <00ff> <0000>\n";
9860
 
        $stream .= "<0100> <01ff> <0100>\n";
9861
 
        $stream .= "<0200> <02ff> <0200>\n";
9862
 
        $stream .= "<0300> <03ff> <0300>\n";
9863
 
        $stream .= "<0400> <04ff> <0400>\n";
9864
 
        $stream .= "<0500> <05ff> <0500>\n";
9865
 
        $stream .= "<0600> <06ff> <0600>\n";
9866
 
        $stream .= "<0700> <07ff> <0700>\n";
9867
 
        $stream .= "<0800> <08ff> <0800>\n";
9868
 
        $stream .= "<0900> <09ff> <0900>\n";
9869
 
        $stream .= "<0a00> <0aff> <0a00>\n";
9870
 
        $stream .= "<0b00> <0bff> <0b00>\n";
9871
 
        $stream .= "<0c00> <0cff> <0c00>\n";
9872
 
        $stream .= "<0d00> <0dff> <0d00>\n";
9873
 
        $stream .= "<0e00> <0eff> <0e00>\n";
9874
 
        $stream .= "<0f00> <0fff> <0f00>\n";
9875
 
        $stream .= "<1000> <10ff> <1000>\n";
9876
 
        $stream .= "<1100> <11ff> <1100>\n";
9877
 
        $stream .= "<1200> <12ff> <1200>\n";
9878
 
        $stream .= "<1300> <13ff> <1300>\n";
9879
 
        $stream .= "<1400> <14ff> <1400>\n";
9880
 
        $stream .= "<1500> <15ff> <1500>\n";
9881
 
        $stream .= "<1600> <16ff> <1600>\n";
9882
 
        $stream .= "<1700> <17ff> <1700>\n";
9883
 
        $stream .= "<1800> <18ff> <1800>\n";
9884
 
        $stream .= "<1900> <19ff> <1900>\n";
9885
 
        $stream .= "<1a00> <1aff> <1a00>\n";
9886
 
        $stream .= "<1b00> <1bff> <1b00>\n";
9887
 
        $stream .= "<1c00> <1cff> <1c00>\n";
9888
 
        $stream .= "<1d00> <1dff> <1d00>\n";
9889
 
        $stream .= "<1e00> <1eff> <1e00>\n";
9890
 
        $stream .= "<1f00> <1fff> <1f00>\n";
9891
 
        $stream .= "<2000> <20ff> <2000>\n";
9892
 
        $stream .= "<2100> <21ff> <2100>\n";
9893
 
        $stream .= "<2200> <22ff> <2200>\n";
9894
 
        $stream .= "<2300> <23ff> <2300>\n";
9895
 
        $stream .= "<2400> <24ff> <2400>\n";
9896
 
        $stream .= "<2500> <25ff> <2500>\n";
9897
 
        $stream .= "<2600> <26ff> <2600>\n";
9898
 
        $stream .= "<2700> <27ff> <2700>\n";
9899
 
        $stream .= "<2800> <28ff> <2800>\n";
9900
 
        $stream .= "<2900> <29ff> <2900>\n";
9901
 
        $stream .= "<2a00> <2aff> <2a00>\n";
9902
 
        $stream .= "<2b00> <2bff> <2b00>\n";
9903
 
        $stream .= "<2c00> <2cff> <2c00>\n";
9904
 
        $stream .= "<2d00> <2dff> <2d00>\n";
9905
 
        $stream .= "<2e00> <2eff> <2e00>\n";
9906
 
        $stream .= "<2f00> <2fff> <2f00>\n";
9907
 
        $stream .= "<3000> <30ff> <3000>\n";
9908
 
        $stream .= "<3100> <31ff> <3100>\n";
9909
 
        $stream .= "<3200> <32ff> <3200>\n";
9910
 
        $stream .= "<3300> <33ff> <3300>\n";
9911
 
        $stream .= "<3400> <34ff> <3400>\n";
9912
 
        $stream .= "<3500> <35ff> <3500>\n";
9913
 
        $stream .= "<3600> <36ff> <3600>\n";
9914
 
        $stream .= "<3700> <37ff> <3700>\n";
9915
 
        $stream .= "<3800> <38ff> <3800>\n";
9916
 
        $stream .= "<3900> <39ff> <3900>\n";
9917
 
        $stream .= "<3a00> <3aff> <3a00>\n";
9918
 
        $stream .= "<3b00> <3bff> <3b00>\n";
9919
 
        $stream .= "<3c00> <3cff> <3c00>\n";
9920
 
        $stream .= "<3d00> <3dff> <3d00>\n";
9921
 
        $stream .= "<3e00> <3eff> <3e00>\n";
9922
 
        $stream .= "<3f00> <3fff> <3f00>\n";
9923
 
        $stream .= "<4000> <40ff> <4000>\n";
9924
 
        $stream .= "<4100> <41ff> <4100>\n";
9925
 
        $stream .= "<4200> <42ff> <4200>\n";
9926
 
        $stream .= "<4300> <43ff> <4300>\n";
9927
 
        $stream .= "<4400> <44ff> <4400>\n";
9928
 
        $stream .= "<4500> <45ff> <4500>\n";
9929
 
        $stream .= "<4600> <46ff> <4600>\n";
9930
 
        $stream .= "<4700> <47ff> <4700>\n";
9931
 
        $stream .= "<4800> <48ff> <4800>\n";
9932
 
        $stream .= "<4900> <49ff> <4900>\n";
9933
 
        $stream .= "<4a00> <4aff> <4a00>\n";
9934
 
        $stream .= "<4b00> <4bff> <4b00>\n";
9935
 
        $stream .= "<4c00> <4cff> <4c00>\n";
9936
 
        $stream .= "<4d00> <4dff> <4d00>\n";
9937
 
        $stream .= "<4e00> <4eff> <4e00>\n";
9938
 
        $stream .= "<4f00> <4fff> <4f00>\n";
9939
 
        $stream .= "<5000> <50ff> <5000>\n";
9940
 
        $stream .= "<5100> <51ff> <5100>\n";
9941
 
        $stream .= "<5200> <52ff> <5200>\n";
9942
 
        $stream .= "<5300> <53ff> <5300>\n";
9943
 
        $stream .= "<5400> <54ff> <5400>\n";
9944
 
        $stream .= "<5500> <55ff> <5500>\n";
9945
 
        $stream .= "<5600> <56ff> <5600>\n";
9946
 
        $stream .= "<5700> <57ff> <5700>\n";
9947
 
        $stream .= "<5800> <58ff> <5800>\n";
9948
 
        $stream .= "<5900> <59ff> <5900>\n";
9949
 
        $stream .= "<5a00> <5aff> <5a00>\n";
9950
 
        $stream .= "<5b00> <5bff> <5b00>\n";
9951
 
        $stream .= "<5c00> <5cff> <5c00>\n";
9952
 
        $stream .= "<5d00> <5dff> <5d00>\n";
9953
 
        $stream .= "<5e00> <5eff> <5e00>\n";
9954
 
        $stream .= "<5f00> <5fff> <5f00>\n";
9955
 
        $stream .= "<6000> <60ff> <6000>\n";
9956
 
        $stream .= "<6100> <61ff> <6100>\n";
9957
 
        $stream .= "<6200> <62ff> <6200>\n";
9958
 
        $stream .= "<6300> <63ff> <6300>\n";
9959
 
        $stream .= "endbfrange\n";
9960
 
        $stream .= "100 beginbfrange\n";
9961
 
        $stream .= "<6400> <64ff> <6400>\n";
9962
 
        $stream .= "<6500> <65ff> <6500>\n";
9963
 
        $stream .= "<6600> <66ff> <6600>\n";
9964
 
        $stream .= "<6700> <67ff> <6700>\n";
9965
 
        $stream .= "<6800> <68ff> <6800>\n";
9966
 
        $stream .= "<6900> <69ff> <6900>\n";
9967
 
        $stream .= "<6a00> <6aff> <6a00>\n";
9968
 
        $stream .= "<6b00> <6bff> <6b00>\n";
9969
 
        $stream .= "<6c00> <6cff> <6c00>\n";
9970
 
        $stream .= "<6d00> <6dff> <6d00>\n";
9971
 
        $stream .= "<6e00> <6eff> <6e00>\n";
9972
 
        $stream .= "<6f00> <6fff> <6f00>\n";
9973
 
        $stream .= "<7000> <70ff> <7000>\n";
9974
 
        $stream .= "<7100> <71ff> <7100>\n";
9975
 
        $stream .= "<7200> <72ff> <7200>\n";
9976
 
        $stream .= "<7300> <73ff> <7300>\n";
9977
 
        $stream .= "<7400> <74ff> <7400>\n";
9978
 
        $stream .= "<7500> <75ff> <7500>\n";
9979
 
        $stream .= "<7600> <76ff> <7600>\n";
9980
 
        $stream .= "<7700> <77ff> <7700>\n";
9981
 
        $stream .= "<7800> <78ff> <7800>\n";
9982
 
        $stream .= "<7900> <79ff> <7900>\n";
9983
 
        $stream .= "<7a00> <7aff> <7a00>\n";
9984
 
        $stream .= "<7b00> <7bff> <7b00>\n";
9985
 
        $stream .= "<7c00> <7cff> <7c00>\n";
9986
 
        $stream .= "<7d00> <7dff> <7d00>\n";
9987
 
        $stream .= "<7e00> <7eff> <7e00>\n";
9988
 
        $stream .= "<7f00> <7fff> <7f00>\n";
9989
 
        $stream .= "<8000> <80ff> <8000>\n";
9990
 
        $stream .= "<8100> <81ff> <8100>\n";
9991
 
        $stream .= "<8200> <82ff> <8200>\n";
9992
 
        $stream .= "<8300> <83ff> <8300>\n";
9993
 
        $stream .= "<8400> <84ff> <8400>\n";
9994
 
        $stream .= "<8500> <85ff> <8500>\n";
9995
 
        $stream .= "<8600> <86ff> <8600>\n";
9996
 
        $stream .= "<8700> <87ff> <8700>\n";
9997
 
        $stream .= "<8800> <88ff> <8800>\n";
9998
 
        $stream .= "<8900> <89ff> <8900>\n";
9999
 
        $stream .= "<8a00> <8aff> <8a00>\n";
10000
 
        $stream .= "<8b00> <8bff> <8b00>\n";
10001
 
        $stream .= "<8c00> <8cff> <8c00>\n";
10002
 
        $stream .= "<8d00> <8dff> <8d00>\n";
10003
 
        $stream .= "<8e00> <8eff> <8e00>\n";
10004
 
        $stream .= "<8f00> <8fff> <8f00>\n";
10005
 
        $stream .= "<9000> <90ff> <9000>\n";
10006
 
        $stream .= "<9100> <91ff> <9100>\n";
10007
 
        $stream .= "<9200> <92ff> <9200>\n";
10008
 
        $stream .= "<9300> <93ff> <9300>\n";
10009
 
        $stream .= "<9400> <94ff> <9400>\n";
10010
 
        $stream .= "<9500> <95ff> <9500>\n";
10011
 
        $stream .= "<9600> <96ff> <9600>\n";
10012
 
        $stream .= "<9700> <97ff> <9700>\n";
10013
 
        $stream .= "<9800> <98ff> <9800>\n";
10014
 
        $stream .= "<9900> <99ff> <9900>\n";
10015
 
        $stream .= "<9a00> <9aff> <9a00>\n";
10016
 
        $stream .= "<9b00> <9bff> <9b00>\n";
10017
 
        $stream .= "<9c00> <9cff> <9c00>\n";
10018
 
        $stream .= "<9d00> <9dff> <9d00>\n";
10019
 
        $stream .= "<9e00> <9eff> <9e00>\n";
10020
 
        $stream .= "<9f00> <9fff> <9f00>\n";
10021
 
        $stream .= "<a000> <a0ff> <a000>\n";
10022
 
        $stream .= "<a100> <a1ff> <a100>\n";
10023
 
        $stream .= "<a200> <a2ff> <a200>\n";
10024
 
        $stream .= "<a300> <a3ff> <a300>\n";
10025
 
        $stream .= "<a400> <a4ff> <a400>\n";
10026
 
        $stream .= "<a500> <a5ff> <a500>\n";
10027
 
        $stream .= "<a600> <a6ff> <a600>\n";
10028
 
        $stream .= "<a700> <a7ff> <a700>\n";
10029
 
        $stream .= "<a800> <a8ff> <a800>\n";
10030
 
        $stream .= "<a900> <a9ff> <a900>\n";
10031
 
        $stream .= "<aa00> <aaff> <aa00>\n";
10032
 
        $stream .= "<ab00> <abff> <ab00>\n";
10033
 
        $stream .= "<ac00> <acff> <ac00>\n";
10034
 
        $stream .= "<ad00> <adff> <ad00>\n";
10035
 
        $stream .= "<ae00> <aeff> <ae00>\n";
10036
 
        $stream .= "<af00> <afff> <af00>\n";
10037
 
        $stream .= "<b000> <b0ff> <b000>\n";
10038
 
        $stream .= "<b100> <b1ff> <b100>\n";
10039
 
        $stream .= "<b200> <b2ff> <b200>\n";
10040
 
        $stream .= "<b300> <b3ff> <b300>\n";
10041
 
        $stream .= "<b400> <b4ff> <b400>\n";
10042
 
        $stream .= "<b500> <b5ff> <b500>\n";
10043
 
        $stream .= "<b600> <b6ff> <b600>\n";
10044
 
        $stream .= "<b700> <b7ff> <b700>\n";
10045
 
        $stream .= "<b800> <b8ff> <b800>\n";
10046
 
        $stream .= "<b900> <b9ff> <b900>\n";
10047
 
        $stream .= "<ba00> <baff> <ba00>\n";
10048
 
        $stream .= "<bb00> <bbff> <bb00>\n";
10049
 
        $stream .= "<bc00> <bcff> <bc00>\n";
10050
 
        $stream .= "<bd00> <bdff> <bd00>\n";
10051
 
        $stream .= "<be00> <beff> <be00>\n";
10052
 
        $stream .= "<bf00> <bfff> <bf00>\n";
10053
 
        $stream .= "<c000> <c0ff> <c000>\n";
10054
 
        $stream .= "<c100> <c1ff> <c100>\n";
10055
 
        $stream .= "<c200> <c2ff> <c200>\n";
10056
 
        $stream .= "<c300> <c3ff> <c300>\n";
10057
 
        $stream .= "<c400> <c4ff> <c400>\n";
10058
 
        $stream .= "<c500> <c5ff> <c500>\n";
10059
 
        $stream .= "<c600> <c6ff> <c600>\n";
10060
 
        $stream .= "<c700> <c7ff> <c700>\n";
10061
 
        $stream .= "endbfrange\n";
10062
 
        $stream .= "56 beginbfrange\n";
10063
 
        $stream .= "<c800> <c8ff> <c800>\n";
10064
 
        $stream .= "<c900> <c9ff> <c900>\n";
10065
 
        $stream .= "<ca00> <caff> <ca00>\n";
10066
 
        $stream .= "<cb00> <cbff> <cb00>\n";
10067
 
        $stream .= "<cc00> <ccff> <cc00>\n";
10068
 
        $stream .= "<cd00> <cdff> <cd00>\n";
10069
 
        $stream .= "<ce00> <ceff> <ce00>\n";
10070
 
        $stream .= "<cf00> <cfff> <cf00>\n";
10071
 
        $stream .= "<d000> <d0ff> <d000>\n";
10072
 
        $stream .= "<d100> <d1ff> <d100>\n";
10073
 
        $stream .= "<d200> <d2ff> <d200>\n";
10074
 
        $stream .= "<d300> <d3ff> <d300>\n";
10075
 
        $stream .= "<d400> <d4ff> <d400>\n";
10076
 
        $stream .= "<d500> <d5ff> <d500>\n";
10077
 
        $stream .= "<d600> <d6ff> <d600>\n";
10078
 
        $stream .= "<d700> <d7ff> <d700>\n";
10079
 
        $stream .= "<d800> <d8ff> <d800>\n";
10080
 
        $stream .= "<d900> <d9ff> <d900>\n";
10081
 
        $stream .= "<da00> <daff> <da00>\n";
10082
 
        $stream .= "<db00> <dbff> <db00>\n";
10083
 
        $stream .= "<dc00> <dcff> <dc00>\n";
10084
 
        $stream .= "<dd00> <ddff> <dd00>\n";
10085
 
        $stream .= "<de00> <deff> <de00>\n";
10086
 
        $stream .= "<df00> <dfff> <df00>\n";
10087
 
        $stream .= "<e000> <e0ff> <e000>\n";
10088
 
        $stream .= "<e100> <e1ff> <e100>\n";
10089
 
        $stream .= "<e200> <e2ff> <e200>\n";
10090
 
        $stream .= "<e300> <e3ff> <e300>\n";
10091
 
        $stream .= "<e400> <e4ff> <e400>\n";
10092
 
        $stream .= "<e500> <e5ff> <e500>\n";
10093
 
        $stream .= "<e600> <e6ff> <e600>\n";
10094
 
        $stream .= "<e700> <e7ff> <e700>\n";
10095
 
        $stream .= "<e800> <e8ff> <e800>\n";
10096
 
        $stream .= "<e900> <e9ff> <e900>\n";
10097
 
        $stream .= "<ea00> <eaff> <ea00>\n";
10098
 
        $stream .= "<eb00> <ebff> <eb00>\n";
10099
 
        $stream .= "<ec00> <ecff> <ec00>\n";
10100
 
        $stream .= "<ed00> <edff> <ed00>\n";
10101
 
        $stream .= "<ee00> <eeff> <ee00>\n";
10102
 
        $stream .= "<ef00> <efff> <ef00>\n";
10103
 
        $stream .= "<f000> <f0ff> <f000>\n";
10104
 
        $stream .= "<f100> <f1ff> <f100>\n";
10105
 
        $stream .= "<f200> <f2ff> <f200>\n";
10106
 
        $stream .= "<f300> <f3ff> <f300>\n";
10107
 
        $stream .= "<f400> <f4ff> <f400>\n";
10108
 
        $stream .= "<f500> <f5ff> <f500>\n";
10109
 
        $stream .= "<f600> <f6ff> <f600>\n";
10110
 
        $stream .= "<f700> <f7ff> <f700>\n";
10111
 
        $stream .= "<f800> <f8ff> <f800>\n";
10112
 
        $stream .= "<f900> <f9ff> <f900>\n";
10113
 
        $stream .= "<fa00> <faff> <fa00>\n";
10114
 
        $stream .= "<fb00> <fbff> <fb00>\n";
10115
 
        $stream .= "<fc00> <fcff> <fc00>\n";
10116
 
        $stream .= "<fd00> <fdff> <fd00>\n";
10117
 
        $stream .= "<fe00> <feff> <fe00>\n";
10118
 
        $stream .= "<ff00> <ffff> <ff00>\n";
10119
 
        $stream .= "endbfrange\n";
10120
 
        $stream .= "endcmap\n";
10121
 
        $stream .= "CMapName currentdict /CMap defineresource pop\n";
10122
 
        $stream .= "end\n";
10123
 
        $stream .= "end";
10124
 
        // ToUnicode Object
10125
 
        $this->_newobj();
10126
 
        $stream = ($this->compress) ? gzcompress($stream) : $stream;
10127
 
        $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
10128
 
        $stream = $this->_getrawstream($stream);
10129
 
        $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
10130
 
        // CIDFontType2
10131
 
        // A CIDFont whose glyph descriptions are based on TrueType font technology
10132
 
        $oid = $this->_newobj();
10133
 
        $out = '<< /Type /Font';
10134
 
        $out .= ' /Subtype /CIDFontType2';
10135
 
        $out .= ' /BaseFont /'.$fontname;
10136
 
        // A dictionary containing entries that define the character collection of the CIDFont.
10137
 
        $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
10138
 
        $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
10139
 
        $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
10140
 
        $out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
10141
 
        $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
10142
 
        $out .= ' /DW '.$font['dw']; // default width
10143
 
        $out .= "\n".$this->_putfontwidths($font, 0);
10144
 
        if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
10145
 
            $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R';
10146
 
        }
10147
 
        $out .= ' >>';
10148
 
        $out .= "\n".'endobj';
10149
 
        $this->_out($out);
10150
 
        // Font descriptor
10151
 
        // A font descriptor describing the CIDFont default metrics other than its glyph widths
10152
 
        $this->_newobj();
10153
 
        $out = '<< /Type /FontDescriptor';
10154
 
        $out .= ' /FontName /'.$fontname;
10155
 
        foreach ($font['desc'] as $key => $value) {
10156
 
            if(is_float($value)) {
10157
 
                $value = sprintf('%.3F', $value);
10158
 
            }
10159
 
            $out .= ' /'.$key.' '.$value;
10160
 
        }
10161
 
        $fontdir = false;
10162
 
        if (!$this->empty_string($font['file'])) {
10163
 
            // A stream containing a TrueType font
10164
 
            $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
10165
 
            $fontdir = $this->FontFiles[$font['file']]['fontdir'];
10166
 
        }
10167
 
        $out .= ' >>';
10168
 
        $out .= "\n".'endobj';
10169
 
        $this->_out($out);
10170
 
        if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
10171
 
            $this->_newobj();
10172
 
            // Embed CIDToGIDMap
10173
 
            // A specification of the mapping from CIDs to glyph indices
10174
 
            // search and get CTG font file to embedd
10175
 
            $ctgfile = strtolower($font['ctg']);
10176
 
            // search and get ctg font file to embedd
10177
 
            $fontfile = '';
10178
 
            // search files on various directories
10179
 
            if (($fontdir !== false) AND file_exists($fontdir.$ctgfile)) {
10180
 
                $fontfile = $fontdir.$ctgfile;
10181
 
            } elseif (file_exists($this->_getfontpath().$ctgfile)) {
10182
 
                $fontfile = $this->_getfontpath().$ctgfile;
10183
 
            } elseif (file_exists($ctgfile)) {
10184
 
                $fontfile = $ctgfile;
10185
 
            }
10186
 
            if ($this->empty_string($fontfile)) {
10187
 
                $this->Error('Font file not found: '.$ctgfile);
10188
 
            }
10189
 
            $stream = $this->_getrawstream(file_get_contents($fontfile));
10190
 
            $out = '<< /Length '.strlen($stream).'';
10191
 
            if (substr($fontfile, -2) == '.z') { // check file extension
10192
 
                // Decompresses data encoded using the public-domain
10193
 
                // zlib/deflate compression method, reproducing the
10194
 
                // original text or binary data
10195
 
                $out .= ' /Filter /FlateDecode';
10196
 
            }
10197
 
            $out .= ' >>';
10198
 
            $out .= ' stream'."\n".$stream."\n".'endstream';
10199
 
            $out .= "\n".'endobj';
10200
 
            $this->_out($out);
10201
 
        }
10202
 
    }
10203
 
 
10204
 
    /**
10205
 
     * Output CID-0 fonts.
10206
 
     * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format
10207
 
     * @param $font (array) font data
10208
 
     * @protected
10209
 
     * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
10210
 
     * @since 3.2.000 (2008-06-23)
10211
 
     */
10212
 
    protected function _putcidfont0($font) {
10213
 
        $cidoffset = 0;
10214
 
        if (!isset($font['cw'][1])) {
10215
 
            $cidoffset = 31;
10216
 
        }
10217
 
        if (isset($font['cidinfo']['uni2cid'])) {
10218
 
            // convert unicode to cid.
10219
 
            $uni2cid = $font['cidinfo']['uni2cid'];
10220
 
            $cw = array();
10221
 
            foreach ($font['cw'] as $uni => $width) {
10222
 
                if (isset($uni2cid[$uni])) {
10223
 
                    $cw[($uni2cid[$uni] + $cidoffset)] = $width;
10224
 
                } elseif ($uni < 256) {
10225
 
                    $cw[$uni] = $width;
10226
 
                } // else unknown character
10227
 
            }
10228
 
            $font = array_merge($font, array('cw' => $cw));
10229
 
        }
10230
 
        $name = $font['name'];
10231
 
        $enc = $font['enc'];
10232
 
        if ($enc) {
10233
 
            $longname = $name.'-'.$enc;
10234
 
        } else {
10235
 
            $longname = $name;
10236
 
        }
10237
 
        $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
10238
 
        $out .= '<</Type /Font';
10239
 
        $out .= ' /Subtype /Type0';
10240
 
        $out .= ' /BaseFont /'.$longname;
10241
 
        $out .= ' /Name /F'.$font['i'];
10242
 
        if ($enc) {
10243
 
            $out .= ' /Encoding /'.$enc;
10244
 
        }
10245
 
        $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
10246
 
        $out .= ' >>';
10247
 
        $out .= "\n".'endobj';
10248
 
        $this->_out($out);
10249
 
        $oid = $this->_newobj();
10250
 
        $out = '<</Type /Font';
10251
 
        $out .= ' /Subtype /CIDFontType0';
10252
 
        $out .= ' /BaseFont /'.$name;
10253
 
        $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
10254
 
        $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
10255
 
        $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
10256
 
        $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
10257
 
        $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
10258
 
        $out .= ' /DW '.$font['dw'];
10259
 
        $out .= "\n".$this->_putfontwidths($font, $cidoffset);
10260
 
        $out .= ' >>';
10261
 
        $out .= "\n".'endobj';
10262
 
        $this->_out($out);
10263
 
        $this->_newobj();
10264
 
        $s = '<</Type /FontDescriptor /FontName /'.$name;
10265
 
        foreach ($font['desc'] as $k => $v) {
10266
 
            if ($k != 'Style') {
10267
 
                if(is_float($v)) {
10268
 
                    $v = sprintf('%.3F', $v);
10269
 
                }
10270
 
                $s .= ' /'.$k.' '.$v.'';
10271
 
            }
10272
 
        }
10273
 
        $s .= '>>';
10274
 
        $s .= "\n".'endobj';
10275
 
        $this->_out($s);
10276
 
    }
10277
 
 
10278
 
    /**
10279
 
     * Output images.
10280
 
     * @protected
10281
 
     */
10282
 
    protected function _putimages() {
10283
 
        $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
10284
 
        foreach ($this->imagekeys as $file) {
10285
 
            $info = $this->getImageBuffer($file);
10286
 
            $oid = $this->_newobj();
10287
 
            $this->xobjects['I'.$info['i']] = array('n' => $oid);
10288
 
            $this->setImageSubBuffer($file, 'n', $this->n);
10289
 
            $out = '<</Type /XObject';
10290
 
            $out .= ' /Subtype /Image';
10291
 
            $out .= ' /Width '.$info['w'];
10292
 
            $out .= ' /Height '.$info['h'];
10293
 
            if (array_key_exists('masked', $info)) {
10294
 
                $out .= ' /SMask '.($this->n - 1).' 0 R';
10295
 
            }
10296
 
            if ($info['cs'] == 'Indexed') {
10297
 
                $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
10298
 
            } else {
10299
 
                $out .= ' /ColorSpace /'.$info['cs'];
10300
 
                if ($info['cs'] == 'DeviceCMYK') {
10301
 
                    $out .= ' /Decode [1 0 1 0 1 0 1 0]';
10302
 
                }
10303
 
            }
10304
 
            $out .= ' /BitsPerComponent '.$info['bpc'];
10305
 
            if (isset($info['f'])) {
10306
 
                $out .= ' /Filter /'.$info['f'];
10307
 
            }
10308
 
            if (isset($info['parms'])) {
10309
 
                $out .= ' '.$info['parms'];
10310
 
            }
10311
 
            if (isset($info['trns']) AND is_array($info['trns'])) {
10312
 
                $trns='';
10313
 
                $count_info = count($info['trns']);
10314
 
                for ($i=0; $i < $count_info; ++$i) {
10315
 
                    $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
10316
 
                }
10317
 
                $out .= ' /Mask ['.$trns.']';
10318
 
            }
10319
 
            $stream = $this->_getrawstream($info['data']);
10320
 
            $out .= ' /Length '.strlen($stream).' >>';
10321
 
            $out .= ' stream'."\n".$stream."\n".'endstream';
10322
 
            $out .= "\n".'endobj';
10323
 
            $this->_out($out);
10324
 
            //Palette
10325
 
            if ($info['cs'] == 'Indexed') {
10326
 
                $this->_newobj();
10327
 
                $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
10328
 
                $pal = $this->_getrawstream($pal);
10329
 
                $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
10330
 
            }
10331
 
        }
10332
 
    }
10333
 
 
10334
 
    /**
10335
 
     * Output Form XObjects Templates.
10336
 
     * @author Nicola Asuni
10337
 
     * @since 5.8.017 (2010-08-24)
10338
 
     * @protected
10339
 
     * @see startTemplate(), endTemplate(), printTemplate()
10340
 
     */
10341
 
    protected function _putxobjects() {
10342
 
        foreach ($this->xobjects as $key => $data) {
10343
 
            if (isset($data['outdata'])) {
10344
 
                $stream = trim($data['outdata']);
10345
 
                $out = $this->_getobj($data['n'])."\n";
10346
 
                $out .= '<<';
10347
 
                $out .= ' /Type /XObject';
10348
 
                $out .= ' /Subtype /Form';
10349
 
                $out .= ' /FormType 1';
10350
 
                if ($this->compress) {
10351
 
                    $stream = gzcompress($stream);
10352
 
                    $out .= ' /Filter /FlateDecode';
10353
 
                }
10354
 
                $out .= sprintf(' /BBox [%.2F %.2F %.2F %.2F]', ($data['x'] * $this->k), (-$data['y'] * $this->k), (($data['w'] + $data['x']) * $this->k), (($data['h'] - $data['y']) * $this->k));
10355
 
                $out .= ' /Matrix [1 0 0 1 0 0]';
10356
 
                $out .= ' /Resources <<';
10357
 
                $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
10358
 
                // fonts
10359
 
                if (!empty($data['fonts'])) {
10360
 
                    $out .= ' /Font <<';
10361
 
                    foreach ($data['fonts'] as $fontkey => $fontid) {
10362
 
                        $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
10363
 
                    }
10364
 
                    $out .= ' >>';
10365
 
                }
10366
 
                // images or nested xobjects
10367
 
                if (!empty($data['images']) OR !empty($data['xobjects'])) {
10368
 
                    $out .= ' /XObject <<';
10369
 
                    foreach ($data['images'] as $imgid) {
10370
 
                        $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R';
10371
 
                    }
10372
 
                    foreach ($data['xobjects'] as $sub_id => $sub_objid) {
10373
 
                        $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
10374
 
                    }
10375
 
                    $out .= ' >>';
10376
 
                }
10377
 
                $out .= ' >>';
10378
 
                $stream = $this->_getrawstream($stream);
10379
 
                $out .= ' /Length '.strlen($stream);
10380
 
                $out .= ' >>';
10381
 
                $out .= ' stream'."\n".$stream."\n".'endstream';
10382
 
                $out .= "\n".'endobj';
10383
 
                $this->_out($out);
10384
 
            }
10385
 
        }
10386
 
    }
10387
 
 
10388
 
    /**
10389
 
     * Output Spot Colors Resources.
10390
 
     * @protected
10391
 
     * @since 4.0.024 (2008-09-12)
10392
 
     */
10393
 
    protected function _putspotcolors() {
10394
 
        foreach ($this->spot_colors as $name => $color) {
10395
 
            $this->_newobj();
10396
 
            $this->spot_colors[$name]['n'] = $this->n;
10397
 
            $out = '[/Separation /'.str_replace(' ', '#20', $name);
10398
 
            $out .= ' /DeviceCMYK <<';
10399
 
            $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
10400
 
            $out .= ' '.sprintf('/C1 [%.4F %.4F %.4F %.4F] ', ($color['c'] / 100), ($color['m'] / 100), ($color['y'] / 100), ($color['k'] / 100));
10401
 
            $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
10402
 
            $out .= "\n".'endobj';
10403
 
            $this->_out($out);
10404
 
        }
10405
 
    }
10406
 
 
10407
 
    /**
10408
 
     * Return XObjects Dictionary.
10409
 
     * @return string XObjects dictionary
10410
 
     * @protected
10411
 
     * @since 5.8.014 (2010-08-23)
10412
 
     */
10413
 
    protected function _getxobjectdict() {
10414
 
        $out = '';
10415
 
        foreach ($this->xobjects as $id => $objid) {
10416
 
            $out .= ' /'.$id.' '.$objid['n'].' 0 R';
10417
 
        }
10418
 
        return $out;
10419
 
    }
10420
 
 
10421
 
    /**
10422
 
     * Output Resources Dictionary.
10423
 
     * @protected
10424
 
     */
10425
 
    protected function _putresourcedict() {
10426
 
        $out = $this->_getobj(2)."\n";
10427
 
        $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
10428
 
        $out .= ' /Font <<';
10429
 
        foreach ($this->fontkeys as $fontkey) {
10430
 
            $font = $this->getFontBuffer($fontkey);
10431
 
            $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
10432
 
        }
10433
 
        $out .= ' >>';
10434
 
        $out .= ' /XObject <<';
10435
 
        $out .= $this->_getxobjectdict();
10436
 
        $out .= ' >>';
10437
 
        // visibility
10438
 
        $out .= ' /Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>';
10439
 
        // transparency
10440
 
        $out .= ' /ExtGState <<';
10441
 
        foreach ($this->extgstates as $k => $extgstate) {
10442
 
            if (isset($extgstate['name'])) {
10443
 
                $out .= ' /'.$extgstate['name'];
10444
 
            } else {
10445
 
                $out .= ' /GS'.$k;
10446
 
            }
10447
 
            $out .= ' '.$extgstate['n'].' 0 R';
10448
 
        }
10449
 
        $out .= ' >>';
10450
 
        // gradient patterns
10451
 
        if (isset($this->gradients) AND (count($this->gradients) > 0)) {
10452
 
            $out .= ' /Pattern <<';
10453
 
            foreach ($this->gradients as $id => $grad) {
10454
 
                $out .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
10455
 
            }
10456
 
            $out .= ' >>';
10457
 
        }
10458
 
        // gradient shadings
10459
 
        if (isset($this->gradients) AND (count($this->gradients) > 0)) {
10460
 
            $out .= ' /Shading <<';
10461
 
            foreach ($this->gradients as $id => $grad) {
10462
 
                $out .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
10463
 
            }
10464
 
            $out .= ' >>';
10465
 
        }
10466
 
        // spot colors
10467
 
        if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) {
10468
 
            $out .= ' /ColorSpace <<';
10469
 
            foreach ($this->spot_colors as $color) {
10470
 
                $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
10471
 
            }
10472
 
            $out .= ' >>';
10473
 
        }
10474
 
        $out .= ' >>';
10475
 
        $out .= "\n".'endobj';
10476
 
        $this->_out($out);
10477
 
    }
10478
 
 
10479
 
    /**
10480
 
     * Output Resources.
10481
 
     * @protected
10482
 
     */
10483
 
    protected function _putresources() {
10484
 
        $this->_putextgstates();
10485
 
        $this->_putocg();
10486
 
        $this->_putfonts();
10487
 
        $this->_putimages();
10488
 
        $this->_putxobjects();
10489
 
        $this->_putspotcolors();
10490
 
        $this->_putshaders();
10491
 
        $this->_putresourcedict();
10492
 
        $this->_putbookmarks();
10493
 
        $this->_putEmbeddedFiles();
10494
 
        $this->_putannotsobjs();
10495
 
        $this->_putjavascript();
10496
 
        $this->_putencryption();
10497
 
    }
10498
 
 
10499
 
    /**
10500
 
     * Adds some Metadata information (Document Information Dictionary)
10501
 
     * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference)
10502
 
     * @return int object id
10503
 
     * @protected
10504
 
     */
10505
 
    protected function _putinfo() {
10506
 
        $oid = $this->_newobj();
10507
 
        $out = '<<';
10508
 
        // store current isunicode value
10509
 
        $prev_isunicode = $this->isunicode;
10510
 
        if ($this->docinfounicode) {
10511
 
            $this->isunicode = true;
10512
 
        }
10513
 
        if (!$this->empty_string($this->title)) {
10514
 
            // The document's title.
10515
 
            $out .= ' /Title '.$this->_textstring($this->title, $oid);
10516
 
        }
10517
 
        if (!$this->empty_string($this->author)) {
10518
 
            // The name of the person who created the document.
10519
 
            $out .= ' /Author '.$this->_textstring($this->author, $oid);
10520
 
        }
10521
 
        if (!$this->empty_string($this->subject)) {
10522
 
            // The subject of the document.
10523
 
            $out .= ' /Subject '.$this->_textstring($this->subject, $oid);
10524
 
        }
10525
 
        if (!$this->empty_string($this->keywords)) {
10526
 
            // Keywords associated with the document.
10527
 
            $out .= ' /Keywords '.$this->_textstring($this->keywords.' TCPDF', $oid);
10528
 
        }
10529
 
        if (!$this->empty_string($this->creator)) {
10530
 
            // 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.
10531
 
            $out .= ' /Creator '.$this->_textstring($this->creator, $oid);
10532
 
        }
10533
 
        // restore previous isunicode value
10534
 
        $this->isunicode = $prev_isunicode;
10535
 
        // default producer
10536
 
        $out .= ' /Producer '.$this->_textstring("\x54\x43\x50\x44\x46\x20".$this->tcpdf_version."\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29", $oid);
10537
 
        // The date and time the document was created, in human-readable form
10538
 
        $out .= ' /CreationDate '.$this->_datestring();
10539
 
        // The date and time the document was most recently modified, in human-readable form
10540
 
        $out .= ' /ModDate '.$this->_datestring();
10541
 
        // A name object indicating whether the document has been modified to include trapping information
10542
 
        $out .= ' /Trapped /False';
10543
 
        $out .= ' >>';
10544
 
        $out .= "\n".'endobj';
10545
 
        $this->_out($out);
10546
 
        return $oid;
10547
 
    }
10548
 
 
10549
 
    /**
10550
 
     * Output Catalog.
10551
 
     * @return int object id
10552
 
     * @protected
10553
 
     */
10554
 
    protected function _putcatalog() {
10555
 
        $oid = $this->_newobj();
10556
 
        $out = '<< /Type /Catalog';
10557
 
        $out .= ' /Pages 1 0 R';
10558
 
        if ($this->ZoomMode == 'fullpage') {
10559
 
            $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]';
10560
 
        } elseif ($this->ZoomMode == 'fullwidth') {
10561
 
            $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]';
10562
 
        } elseif ($this->ZoomMode == 'real') {
10563
 
            $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]';
10564
 
        } elseif (!is_string($this->ZoomMode)) {
10565
 
            $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %.2F]',($this->ZoomMode / 100));
10566
 
        }
10567
 
        if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) {
10568
 
            $out .= ' /PageLayout /'.$this->LayoutMode;
10569
 
        }
10570
 
        if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) {
10571
 
            $out .= ' /PageMode /'.$this->PageMode;
10572
 
        }
10573
 
        if (isset($this->l['a_meta_language'])) {
10574
 
            $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid);
10575
 
        }
10576
 
        $out .= ' /Names <<';
10577
 
        if ((!empty($this->javascript)) OR (!empty($this->js_objects))) {
10578
 
            $out .= ' /JavaScript '.($this->n_js).' 0 R';
10579
 
        }
10580
 
        $out .= ' >>';
10581
 
        if (count($this->outlines) > 0) {
10582
 
            $out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
10583
 
            $out .= ' /PageMode /UseOutlines';
10584
 
        }
10585
 
        $out .= ' '.$this->_putviewerpreferences();
10586
 
        $p = $this->n_ocg_print.' 0 R';
10587
 
        $v = $this->n_ocg_view.' 0 R';
10588
 
        $as = '<< /Event /Print /OCGs ['.$p.' '.$v.'] /Category [/Print] >> << /Event /View /OCGs ['.$p.' '.$v.'] /Category [/View] >>';
10589
 
        $out .= ' /OCProperties << /OCGs ['.$p.' '.$v.'] /D << /ON ['.$p.'] /OFF ['.$v.'] /AS ['.$as.'] >> >>';
10590
 
        // AcroForm
10591
 
        if (!empty($this->form_obj_id) OR ($this->sign AND isset($this->signature_data['cert_type']))) {
10592
 
            $out .= ' /AcroForm <<';
10593
 
            $objrefs = '';
10594
 
            if ($this->sign AND isset($this->signature_data['cert_type'])) {
10595
 
                $objrefs .= $this->sig_obj_id.' 0 R';
10596
 
            }
10597
 
            if (!empty($this->form_obj_id)) {
10598
 
                foreach($this->form_obj_id as $objid) {
10599
 
                    $objrefs .= ' '.$objid.' 0 R';
10600
 
                }
10601
 
            }
10602
 
            $out .= ' /Fields ['.$objrefs.']';
10603
 
            if (!empty($this->form_obj_id) AND !$this->sign) {
10604
 
                // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
10605
 
                $out .= ' /NeedAppearances true';
10606
 
            }
10607
 
            if ($this->sign AND isset($this->signature_data['cert_type'])) {
10608
 
                if ($this->signature_data['cert_type'] > 0) {
10609
 
                    $out .= ' /SigFlags 3';
10610
 
                } else {
10611
 
                    $out .= ' /SigFlags 1';
10612
 
                }
10613
 
            }
10614
 
            //$out .= ' /CO ';
10615
 
            if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
10616
 
                $out .= ' /DR <<';
10617
 
                $out .= ' /Font <<';
10618
 
                foreach ($this->annotation_fonts as $fontkey => $fontid) {
10619
 
                    $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
10620
 
                }
10621
 
                $out .= ' >> >>';
10622
 
            }
10623
 
            $font = $this->getFontBuffer('helvetica');
10624
 
            $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
10625
 
            $out .= ' /Q '.(($this->rtl)?'2':'0');
10626
 
            //$out .= ' /XFA ';
10627
 
            $out .= ' >>';
10628
 
            // signatures
10629
 
            if ($this->sign AND isset($this->signature_data['cert_type'])) {
10630
 
                if ($this->signature_data['cert_type'] > 0) {
10631
 
                    $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>';
10632
 
                } else {
10633
 
                    $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>';
10634
 
                }
10635
 
            }
10636
 
        }
10637
 
        $out .= ' >>';
10638
 
        $out .= "\n".'endobj';
10639
 
        $this->_out($out);
10640
 
        return $oid;
10641
 
    }
10642
 
 
10643
 
    /**
10644
 
     * Output viewer preferences.
10645
 
     * @return string for viewer preferences
10646
 
     * @author Nicola asuni
10647
 
     * @since 3.1.000 (2008-06-09)
10648
 
     * @protected
10649
 
     */
10650
 
    protected function _putviewerpreferences() {
10651
 
        $out = '/ViewerPreferences <<';
10652
 
        if ($this->rtl) {
10653
 
            $out .= ' /Direction /R2L';
10654
 
        } else {
10655
 
            $out .= ' /Direction /L2R';
10656
 
        }
10657
 
        if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
10658
 
            $out .= ' /HideToolbar true';
10659
 
        }
10660
 
        if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
10661
 
            $out .= ' /HideMenubar true';
10662
 
        }
10663
 
        if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
10664
 
            $out .= ' /HideWindowUI true';
10665
 
        }
10666
 
        if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
10667
 
            $out .= ' /FitWindow true';
10668
 
        }
10669
 
        if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
10670
 
            $out .= ' /CenterWindow true';
10671
 
        }
10672
 
        if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
10673
 
            $out .= ' /DisplayDocTitle true';
10674
 
        }
10675
 
        if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
10676
 
            $out .= ' /NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'];
10677
 
        }
10678
 
        if (isset($this->viewer_preferences['ViewArea'])) {
10679
 
            $out .= ' /ViewArea /'.$this->viewer_preferences['ViewArea'];
10680
 
        }
10681
 
        if (isset($this->viewer_preferences['ViewClip'])) {
10682
 
            $out .= ' /ViewClip /'.$this->viewer_preferences['ViewClip'];
10683
 
        }
10684
 
        if (isset($this->viewer_preferences['PrintArea'])) {
10685
 
            $out .= ' /PrintArea /'.$this->viewer_preferences['PrintArea'];
10686
 
        }
10687
 
        if (isset($this->viewer_preferences['PrintClip'])) {
10688
 
            $out .= ' /PrintClip /'.$this->viewer_preferences['PrintClip'];
10689
 
        }
10690
 
        if (isset($this->viewer_preferences['PrintScaling'])) {
10691
 
            $out .= ' /PrintScaling /'.$this->viewer_preferences['PrintScaling'];
10692
 
        }
10693
 
        if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) {
10694
 
            $out .= ' /Duplex /'.$this->viewer_preferences['Duplex'];
10695
 
        }
10696
 
        if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
10697
 
            if ($this->viewer_preferences['PickTrayByPDFSize']) {
10698
 
                $out .= ' /PickTrayByPDFSize true';
10699
 
            } else {
10700
 
                $out .= ' /PickTrayByPDFSize false';
10701
 
            }
10702
 
        }
10703
 
        if (isset($this->viewer_preferences['PrintPageRange'])) {
10704
 
            $PrintPageRangeNum = '';
10705
 
            foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
10706
 
                $PrintPageRangeNum .= ' '.($v - 1).'';
10707
 
            }
10708
 
            $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
10709
 
        }
10710
 
        if (isset($this->viewer_preferences['NumCopies'])) {
10711
 
            $out .= ' /NumCopies '.intval($this->viewer_preferences['NumCopies']);
10712
 
        }
10713
 
        $out .= ' >>';
10714
 
        return $out;
10715
 
    }
10716
 
 
10717
 
    /**
10718
 
     * Output PDF header.
10719
 
     * @protected
10720
 
     */
10721
 
    protected function _putheader() {
10722
 
        $this->_out('%PDF-'.$this->PDFVersion);
10723
 
    }
10724
 
 
10725
 
    /**
10726
 
     * Output end of document (EOF).
10727
 
     * @protected
10728
 
     */
10729
 
    protected function _enddoc() {
10730
 
        $this->state = 1;
10731
 
        $this->_putheader();
10732
 
        $this->_putpages();
10733
 
        $this->_putresources();
10734
 
        // Signature
10735
 
        if ($this->sign AND isset($this->signature_data['cert_type'])) {
10736
 
            // widget annotation for signature
10737
 
            $out = $this->_getobj($this->sig_obj_id)."\n";
10738
 
            $out .= '<< /Type /Annot';
10739
 
            $out .= ' /Subtype /Widget';
10740
 
            $out .= ' /Rect ['.$this->signature_appearance['rect'].']';
10741
 
            $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page
10742
 
            $out .= ' /F 4';
10743
 
            $out .= ' /FT /Sig';
10744
 
            $out .= ' /T '.$this->_textstring('Signature', $this->sig_obj_id);
10745
 
            $out .= ' /Ff 0';
10746
 
            $out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
10747
 
            $out .= ' >>';
10748
 
            $out .= "\n".'endobj';
10749
 
            $this->_out($out);
10750
 
            // signature
10751
 
            $this->_putsignature();
10752
 
        }
10753
 
        // Info
10754
 
        $objid_info = $this->_putinfo();
10755
 
        // Catalog
10756
 
        $objid_catalog = $this->_putcatalog();
10757
 
        // Cross-ref
10758
 
        $o = $this->bufferlen;
10759
 
        // XREF section
10760
 
        $this->_out('xref');
10761
 
        $this->_out('0 '.($this->n + 1));
10762
 
        $this->_out('0000000000 65535 f ');
10763
 
        for ($i=1; $i <= $this->n; ++$i) {
10764
 
            $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
10765
 
        }
10766
 
        // TRAILER
10767
 
        $out = 'trailer <<';
10768
 
        $out .= ' /Size '.($this->n + 1);
10769
 
        $out .= ' /Root '.$objid_catalog.' 0 R';
10770
 
        $out .= ' /Info '.$objid_info.' 0 R';
10771
 
        if ($this->encrypted) {
10772
 
            $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R';
10773
 
        }
10774
 
        $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]';
10775
 
        $out .= ' >>';
10776
 
        $this->_out($out);
10777
 
        $this->_out('startxref');
10778
 
        $this->_out($o);
10779
 
        $this->_out('%%EOF');
10780
 
        $this->state = 3; // end-of-doc
10781
 
        if ($this->diskcache) {
10782
 
            // remove temporary files used for images
10783
 
            foreach ($this->imagekeys as $key) {
10784
 
                // remove temporary files
10785
 
                unlink($this->images[$key]);
10786
 
            }
10787
 
            foreach ($this->fontkeys as $key) {
10788
 
                // remove temporary files
10789
 
                unlink($this->fonts[$key]);
10790
 
            }
10791
 
        }
10792
 
    }
10793
 
 
10794
 
    /**
10795
 
     * Initialize a new page.
10796
 
     * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
10797
 
     * @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().
10798
 
     * @protected
10799
 
     * @see getPageSizeFromFormat(), setPageFormat()
10800
 
     */
10801
 
    protected function _beginpage($orientation='', $format='') {
10802
 
        ++$this->page;
10803
 
        $this->setPageBuffer($this->page, '');
10804
 
        // initialize array for graphics tranformation positions inside a page buffer
10805
 
        $this->transfmrk[$this->page] = array();
10806
 
        $this->state = 2;
10807
 
        if ($this->empty_string($orientation)) {
10808
 
            if (isset($this->CurOrientation)) {
10809
 
                $orientation = $this->CurOrientation;
10810
 
            } elseif ($this->fwPt > $this->fhPt) {
10811
 
                // landscape
10812
 
                $orientation = 'L';
10813
 
            } else {
10814
 
                // portrait
10815
 
                $orientation = 'P';
10816
 
            }
10817
 
        }
10818
 
        if ($this->empty_string($format)) {
10819
 
            $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)];
10820
 
            $this->setPageOrientation($orientation);
10821
 
        } else {
10822
 
            $this->setPageFormat($format, $orientation);
10823
 
        }
10824
 
        if ($this->rtl) {
10825
 
            $this->x = $this->w - $this->rMargin;
10826
 
        } else {
10827
 
            $this->x = $this->lMargin;
10828
 
        }
10829
 
        $this->y = $this->tMargin;
10830
 
        if (isset($this->newpagegroup[$this->page])) {
10831
 
            // start a new group
10832
 
            $n = sizeof($this->pagegroups) + 1;
10833
 
            $alias = '{nb'.$n.'}';
10834
 
            $this->pagegroups[$alias] = 1;
10835
 
            $this->currpagegroup = $alias;
10836
 
        } elseif ($this->currpagegroup) {
10837
 
            ++$this->pagegroups[$this->currpagegroup];
10838
 
        }
10839
 
    }
10840
 
 
10841
 
    /**
10842
 
     * Mark end of page.
10843
 
     * @protected
10844
 
     */
10845
 
    protected function _endpage() {
10846
 
        $this->setVisibility('all');
10847
 
        $this->state = 1;
10848
 
    }
10849
 
 
10850
 
    /**
10851
 
     * Begin a new object and return the object number.
10852
 
     * @return int object number
10853
 
     * @protected
10854
 
     */
10855
 
    protected function _newobj() {
10856
 
        $this->_out($this->_getobj());
10857
 
        return $this->n;
10858
 
    }
10859
 
 
10860
 
    /**
10861
 
     * Return the starting object string for the selected object ID.
10862
 
     * @param $objid (int) Object ID (leave empty to get a new ID).
10863
 
     * @return string the starting object string
10864
 
     * @protected
10865
 
     * @since 5.8.009 (2010-08-20)
10866
 
     */
10867
 
    protected function _getobj($objid='') {
10868
 
        if ($objid === '') {
10869
 
            ++$this->n;
10870
 
            $objid = $this->n;
10871
 
        }
10872
 
        $this->offsets[$objid] = $this->bufferlen;
10873
 
        return $objid.' 0 obj';
10874
 
    }
10875
 
 
10876
 
    /**
10877
 
     * Underline text.
10878
 
     * @param $x (int) X coordinate
10879
 
     * @param $y (int) Y coordinate
10880
 
     * @param $txt (string) text to underline
10881
 
     * @protected
10882
 
     */
10883
 
    protected function _dounderline($x, $y, $txt) {
10884
 
        $w = $this->GetStringWidth($txt);
10885
 
        return $this->_dounderlinew($x, $y, $w);
10886
 
    }
10887
 
 
10888
 
    /**
10889
 
     * Underline for rectangular text area.
10890
 
     * @param $x (int) X coordinate
10891
 
     * @param $y (int) Y coordinate
10892
 
     * @param $w (int) width to underline
10893
 
     * @protected
10894
 
     * @since 4.8.008 (2009-09-29)
10895
 
     */
10896
 
    protected function _dounderlinew($x, $y, $w) {
10897
 
        $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10898
 
        return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew);
10899
 
    }
10900
 
 
10901
 
    /**
10902
 
     * Line through text.
10903
 
     * @param $x (int) X coordinate
10904
 
     * @param $y (int) Y coordinate
10905
 
     * @param $txt (string) text to linethrough
10906
 
     * @protected
10907
 
     */
10908
 
    protected function _dolinethrough($x, $y, $txt) {
10909
 
        $w = $this->GetStringWidth($txt);
10910
 
        return $this->_dolinethroughw($x, $y, $w);
10911
 
    }
10912
 
 
10913
 
    /**
10914
 
     * Line through for rectangular text area.
10915
 
     * @param $x (int) X coordinate
10916
 
     * @param $y (int) Y coordinate
10917
 
     * @param $w (int) line lenght (width)
10918
 
     * @protected
10919
 
     * @since 4.9.008 (2009-09-29)
10920
 
     */
10921
 
    protected function _dolinethroughw($x, $y, $w) {
10922
 
        $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10923
 
        return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew);
10924
 
    }
10925
 
 
10926
 
    /**
10927
 
     * Overline text.
10928
 
     * @param $x (int) X coordinate
10929
 
     * @param $y (int) Y coordinate
10930
 
     * @param $txt (string) text to overline
10931
 
     * @protected
10932
 
     * @since 4.9.015 (2010-04-19)
10933
 
     */
10934
 
    protected function _dooverline($x, $y, $txt) {
10935
 
        $w = $this->GetStringWidth($txt);
10936
 
        return $this->_dooverlinew($x, $y, $w);
10937
 
    }
10938
 
 
10939
 
    /**
10940
 
     * Overline for rectangular text area.
10941
 
     * @param $x (int) X coordinate
10942
 
     * @param $y (int) Y coordinate
10943
 
     * @param $w (int) width to overline
10944
 
     * @protected
10945
 
     * @since 4.9.015 (2010-04-19)
10946
 
     */
10947
 
    protected function _dooverlinew($x, $y, $w) {
10948
 
        $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10949
 
        return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew);
10950
 
 
10951
 
    }
10952
 
 
10953
 
    /**
10954
 
     * Read a 4-byte (32 bit) integer from file.
10955
 
     * @param $f (string) file name.
10956
 
     * @return 4-byte integer
10957
 
     * @protected
10958
 
     */
10959
 
    protected function _freadint($f) {
10960
 
        $a = unpack('Ni', fread($f, 4));
10961
 
        return $a['i'];
10962
 
    }
10963
 
 
10964
 
    /**
10965
 
     * Add "\" before "\", "(" and ")"
10966
 
     * @param $s (string) string to escape.
10967
 
     * @return string escaped string.
10968
 
     * @protected
10969
 
     */
10970
 
    protected function _escape($s) {
10971
 
        // the chr(13) substitution fixes the Bugs item #1421290.
10972
 
        return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
10973
 
    }
10974
 
 
10975
 
    /**
10976
 
     * Format a data string for meta information
10977
 
     * @param $s (string) data string to escape.
10978
 
     * @param $n (int) object ID
10979
 
     * @return string escaped string.
10980
 
     * @protected
10981
 
     */
10982
 
    protected function _datastring($s, $n=0) {
10983
 
        if ($n == 0) {
10984
 
            $n = $this->n;
10985
 
        }
10986
 
        $s = $this->_encrypt_data($n, $s);
10987
 
        return '('. $this->_escape($s).')';
10988
 
    }
10989
 
 
10990
 
    /**
10991
 
     * Returns a formatted date for meta information
10992
 
     * @param $n (int) object ID
10993
 
     * @return string escaped date string.
10994
 
     * @protected
10995
 
     * @since 4.6.028 (2009-08-25)
10996
 
     */
10997
 
    protected function _datestring($n=0) {
10998
 
        $current_time = substr_replace(date('YmdHisO'), '\'', (0 - 2), 0).'\'';
10999
 
        return $this->_datastring('D:'.$current_time, $n);
11000
 
    }
11001
 
 
11002
 
    /**
11003
 
     * Format a text string for meta information
11004
 
     * @param $s (string) string to escape.
11005
 
     * @param $n (int) object ID
11006
 
     * @return string escaped string.
11007
 
     * @protected
11008
 
     */
11009
 
    protected function _textstring($s, $n=0) {
11010
 
        if ($this->isunicode) {
11011
 
            //Convert string to UTF-16BE
11012
 
            $s = $this->UTF8ToUTF16BE($s, true);
11013
 
        }
11014
 
        return $this->_datastring($s, $n);
11015
 
    }
11016
 
 
11017
 
    /**
11018
 
     * THIS METHOD IS DEPRECATED
11019
 
     * Format a text string
11020
 
     * @param $s (string) string to escape.
11021
 
     * @return string escaped string.
11022
 
     * @protected
11023
 
     * @deprecated
11024
 
     */
11025
 
    protected function _escapetext($s) {
11026
 
        if ($this->isunicode) {
11027
 
            if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
11028
 
                $s = $this->UTF8ToLatin1($s);
11029
 
            } else {
11030
 
                //Convert string to UTF-16BE and reverse RTL language
11031
 
                $s = $this->utf8StrRev($s, false, $this->tmprtl);
11032
 
            }
11033
 
        }
11034
 
        return $this->_escape($s);
11035
 
    }
11036
 
 
11037
 
    /**
11038
 
     * get raw output stream.
11039
 
     * @param $s (string) string to output.
11040
 
     * @param $n (int) object reference for encryption mode
11041
 
     * @protected
11042
 
     * @author Nicola Asuni
11043
 
     * @since 5.5.000 (2010-06-22)
11044
 
     */
11045
 
    protected function _getrawstream($s, $n=0) {
11046
 
        if ($n <= 0) {
11047
 
            // default to current object
11048
 
            $n = $this->n;
11049
 
        }
11050
 
        return $this->_encrypt_data($n, $s);
11051
 
    }
11052
 
 
11053
 
    /**
11054
 
     * Format output stream (DEPRECATED).
11055
 
     * @param $s (string) string to output.
11056
 
     * @param $n (int) object reference for encryption mode
11057
 
     * @protected
11058
 
     * @deprecated
11059
 
     */
11060
 
    protected function _getstream($s, $n=0) {
11061
 
        return 'stream'."\n".$this->_getrawstream($s, $n)."\n".'endstream';
11062
 
    }
11063
 
 
11064
 
    /**
11065
 
     * Output a stream (DEPRECATED).
11066
 
     * @param $s (string) string to output.
11067
 
     * @param $n (int) object reference for encryption mode
11068
 
     * @protected
11069
 
     * @deprecated
11070
 
     */
11071
 
    protected function _putstream($s, $n=0) {
11072
 
        $this->_out($this->_getstream($s, $n));
11073
 
    }
11074
 
 
11075
 
    /**
11076
 
     * Output a string to the document.
11077
 
     * @param $s (string) string to output.
11078
 
     * @protected
11079
 
     */
11080
 
    protected function _out($s) {
11081
 
        if ($this->state == 2) {
11082
 
            if ($this->inxobj) {
11083
 
                // we are inside an XObject template
11084
 
                $this->xobjects[$this->xobjid]['outdata'] .= $s."\n";
11085
 
            } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
11086
 
                // puts data before page footer
11087
 
                $pagebuff = $this->getPageBuffer($this->page);
11088
 
                $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
11089
 
                $footer = substr($pagebuff, -$this->footerlen[$this->page]);
11090
 
                $this->setPageBuffer($this->page, $page.$s."\n".$footer);
11091
 
                // update footer position
11092
 
                $this->footerpos[$this->page] += strlen($s."\n");
11093
 
            } else {
11094
 
                $this->setPageBuffer($this->page, $s."\n", true);
11095
 
            }
11096
 
        } else {
11097
 
            $this->setBuffer($s."\n");
11098
 
        }
11099
 
    }
11100
 
 
11101
 
    /**
11102
 
     * Converts UTF-8 strings to codepoints array.<br>
11103
 
     * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>
11104
 
     * Based on: http://www.faqs.org/rfcs/rfc3629.html
11105
 
     * <pre>
11106
 
     *    Char. number range  |        UTF-8 octet sequence
11107
 
     *       (hexadecimal)    |              (binary)
11108
 
     *    --------------------+-----------------------------------------------
11109
 
     *    0000 0000-0000 007F | 0xxxxxxx
11110
 
     *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
11111
 
     *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
11112
 
     *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
11113
 
     *    ---------------------------------------------------------------------
11114
 
     *
11115
 
     *   ABFN notation:
11116
 
     *   ---------------------------------------------------------------------
11117
 
     *   UTF8-octets = *( UTF8-char )
11118
 
     *   UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
11119
 
     *   UTF8-1      = %x00-7F
11120
 
     *   UTF8-2      = %xC2-DF UTF8-tail
11121
 
     *
11122
 
     *   UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
11123
 
     *                 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
11124
 
     *   UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
11125
 
     *                 %xF4 %x80-8F 2( UTF8-tail )
11126
 
     *   UTF8-tail   = %x80-BF
11127
 
     *   ---------------------------------------------------------------------
11128
 
     * </pre>
11129
 
     * @param $str (string) string to process.
11130
 
     * @return array containing codepoints (UTF-8 characters values)
11131
 
     * @protected
11132
 
     * @author Nicola Asuni
11133
 
     * @since 1.53.0.TC005 (2005-01-05)
11134
 
     */
11135
 
    protected function UTF8StringToArray($str) {
11136
 
        // build a unique string key
11137
 
        $strkey = md5($str);
11138
 
        if (isset($this->cache_UTF8StringToArray[$strkey])) {
11139
 
            // return cached value
11140
 
            $chrarray = $this->cache_UTF8StringToArray[$strkey]['s'];
11141
 
            if (!isset($this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']])) {
11142
 
                if ($this->isunicode) {
11143
 
                    foreach ($chrarray as $chr) {
11144
 
                        // store this char for font subsetting
11145
 
                        $this->CurrentFont['subsetchars'][$chr] = true;
11146
 
                    }
11147
 
                    // update font subsetchars
11148
 
                    $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
11149
 
                }
11150
 
                $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true;
11151
 
            }
11152
 
            return $chrarray;
11153
 
        }
11154
 
        // check cache size
11155
 
        if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) {
11156
 
            // remove first element
11157
 
            array_shift($this->cache_UTF8StringToArray);
11158
 
        }
11159
 
        // new cache array for selected string
11160
 
        $this->cache_UTF8StringToArray[$strkey] = array('s' => array(), 'f' => array());
11161
 
        ++$this->cache_size_UTF8StringToArray;
11162
 
        if (!$this->isunicode) {
11163
 
            // split string into array of equivalent codes
11164
 
            $strarr = array();
11165
 
            $strlen = strlen($str);
11166
 
            for ($i=0; $i < $strlen; ++$i) {
11167
 
                $strarr[] = ord($str{$i});
11168
 
            }
11169
 
            // insert new value on cache
11170
 
            $this->cache_UTF8StringToArray[$strkey]['s'] = $strarr;
11171
 
            $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true;
11172
 
            return $strarr;
11173
 
        }
11174
 
        $unichar = -1; // last unicode char
11175
 
        $unicode = array(); // array containing unicode values
11176
 
        $bytes  = array(); // array containing single character byte sequences
11177
 
        $numbytes = 1; // number of octetc needed to represent the UTF-8 character
11178
 
        $str .= ''; // force $str to be a string
11179
 
        $length = strlen($str);
11180
 
        for ($i = 0; $i < $length; ++$i) {
11181
 
            $char = ord($str{$i}); // get one string character at time
11182
 
            if (count($bytes) == 0) { // get starting octect
11183
 
                if ($char <= 0x7F) {
11184
 
                    $unichar = $char; // use the character "as is" because is ASCII
11185
 
                    $numbytes = 1;
11186
 
                } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
11187
 
                    $bytes[] = ($char - 0xC0) << 0x06;
11188
 
                    $numbytes = 2;
11189
 
                } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
11190
 
                    $bytes[] = ($char - 0xE0) << 0x0C;
11191
 
                    $numbytes = 3;
11192
 
                } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
11193
 
                    $bytes[] = ($char - 0xF0) << 0x12;
11194
 
                    $numbytes = 4;
11195
 
                } else {
11196
 
                    // use replacement character for other invalid sequences
11197
 
                    $unichar = 0xFFFD;
11198
 
                    $bytes = array();
11199
 
                    $numbytes = 1;
11200
 
                }
11201
 
            } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
11202
 
                $bytes[] = $char - 0x80;
11203
 
                if (count($bytes) == $numbytes) {
11204
 
                    // compose UTF-8 bytes to a single unicode value
11205
 
                    $char = $bytes[0];
11206
 
                    for ($j = 1; $j < $numbytes; ++$j) {
11207
 
                        $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
11208
 
                    }
11209
 
                    if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
11210
 
                        /* The definition of UTF-8 prohibits encoding character numbers between
11211
 
                        U+D800 and U+DFFF, which are reserved for use with the UTF-16
11212
 
                        encoding form (as surrogate pairs) and do not directly represent
11213
 
                        characters. */
11214
 
                        $unichar = 0xFFFD; // use replacement character
11215
 
                    } else {
11216
 
                        $unichar = $char; // add char to array
11217
 
                    }
11218
 
                    // reset data for next char
11219
 
                    $bytes = array();
11220
 
                    $numbytes = 1;
11221
 
                }
11222
 
            } else {
11223
 
                // use replacement character for other invalid sequences
11224
 
                $unichar = 0xFFFD;
11225
 
                $bytes = array();
11226
 
                $numbytes = 1;
11227
 
            }
11228
 
            if ($unichar >= 0) {
11229
 
                // insert unicode value into array
11230
 
                $unicode[] = $unichar;
11231
 
                // store this char for font subsetting
11232
 
                $this->CurrentFont['subsetchars'][$unichar] = true;
11233
 
                $unichar = -1;
11234
 
            }
11235
 
        }
11236
 
        // update font subsetchars
11237
 
        $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
11238
 
        // insert new value on cache
11239
 
        $this->cache_UTF8StringToArray[$strkey]['s'] = $unicode;
11240
 
        $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true;
11241
 
        return $unicode;
11242
 
    }
11243
 
 
11244
 
    /**
11245
 
     * Converts UTF-8 strings to UTF16-BE.<br>
11246
 
     * @param $str (string) string to process.
11247
 
     * @param $setbom (boolean) if true set the Byte Order Mark (BOM = 0xFEFF)
11248
 
     * @return string
11249
 
     * @author Nicola Asuni
11250
 
     * @since 1.53.0.TC005 (2005-01-05)
11251
 
     * @see UTF8StringToArray(), arrUTF8ToUTF16BE()
11252
 
     * @protected
11253
 
     */
11254
 
    protected function UTF8ToUTF16BE($str, $setbom=true) {
11255
 
        if (!$this->isunicode) {
11256
 
            return $str; // string is not in unicode
11257
 
        }
11258
 
        $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
11259
 
        return $this->arrUTF8ToUTF16BE($unicode, $setbom);
11260
 
    }
11261
 
 
11262
 
    /**
11263
 
     * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br>
11264
 
     * @param $str (string) string to process.
11265
 
     * @return string
11266
 
     * @author Andrew Whitehead, Nicola Asuni
11267
 
     * @protected
11268
 
     * @since 3.2.000 (2008-06-23)
11269
 
     */
11270
 
    protected function UTF8ToLatin1($str) {
11271
 
        if (!$this->isunicode) {
11272
 
            return $str; // string is not in unicode
11273
 
        }
11274
 
        $outstr = ''; // string to be returned
11275
 
        $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
11276
 
        foreach ($unicode as $char) {
11277
 
            if ($char < 256) {
11278
 
                $outstr .= chr($char);
11279
 
            } elseif (array_key_exists($char, $this->unicode->uni_utf8tolatin)) {
11280
 
                // map from UTF-8
11281
 
                $outstr .= chr($this->unicode->uni_utf8tolatin[$char]);
11282
 
            } elseif ($char == 0xFFFD) {
11283
 
                // skip
11284
 
            } else {
11285
 
                $outstr .= '?';
11286
 
            }
11287
 
        }
11288
 
        return $outstr;
11289
 
    }
11290
 
 
11291
 
    /**
11292
 
     * Converts UTF-8 characters array to array of Latin1 characters<br>
11293
 
     * @param $unicode (array) array containing UTF-8 unicode values
11294
 
     * @return array
11295
 
     * @author Nicola Asuni
11296
 
     * @protected
11297
 
     * @since 4.8.023 (2010-01-15)
11298
 
     */
11299
 
    protected function UTF8ArrToLatin1($unicode) {
11300
 
        if ((!$this->isunicode) OR $this->isUnicodeFont()) {
11301
 
            return $unicode;
11302
 
        }
11303
 
        $outarr = array(); // array to be returned
11304
 
        foreach ($unicode as $char) {
11305
 
            if ($char < 256) {
11306
 
                $outarr[] = $char;
11307
 
            } elseif (array_key_exists($char, $this->unicode->uni_utf8tolatin)) {
11308
 
                // map from UTF-8
11309
 
                $outarr[] = $this->unicode->uni_utf8tolatin[$char];
11310
 
            } elseif ($char == 0xFFFD) {
11311
 
                // skip
11312
 
            } else {
11313
 
                $outarr[] = 63; // '?' character
11314
 
            }
11315
 
        }
11316
 
        return $outarr;
11317
 
    }
11318
 
 
11319
 
    /**
11320
 
     * Converts array of UTF-8 characters to UTF16-BE string.<br>
11321
 
     * Based on: http://www.faqs.org/rfcs/rfc2781.html
11322
 
     * <pre>
11323
 
     *   Encoding UTF-16:
11324
 
     *
11325
 
     *   Encoding of a single character from an ISO 10646 character value to
11326
 
     *    UTF-16 proceeds as follows. Let U be the character number, no greater
11327
 
     *    than 0x10FFFF.
11328
 
     *
11329
 
     *    1) If U < 0x10000, encode U as a 16-bit unsigned integer and
11330
 
     *       terminate.
11331
 
     *
11332
 
     *    2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
11333
 
     *       U' must be less than or equal to 0xFFFFF. That is, U' can be
11334
 
     *       represented in 20 bits.
11335
 
     *
11336
 
     *    3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
11337
 
     *       0xDC00, respectively. These integers each have 10 bits free to
11338
 
     *       encode the character value, for a total of 20 bits.
11339
 
     *
11340
 
     *    4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
11341
 
     *       bits of W1 and the 10 low-order bits of U' to the 10 low-order
11342
 
     *       bits of W2. Terminate.
11343
 
     *
11344
 
     *    Graphically, steps 2 through 4 look like:
11345
 
     *    U' = yyyyyyyyyyxxxxxxxxxx
11346
 
     *    W1 = 110110yyyyyyyyyy
11347
 
     *    W2 = 110111xxxxxxxxxx
11348
 
     * </pre>
11349
 
     * @param $unicode (array) array containing UTF-8 unicode values
11350
 
     * @param $setbom (boolean) if true set the Byte Order Mark (BOM = 0xFEFF)
11351
 
     * @return string
11352
 
     * @protected
11353
 
     * @author Nicola Asuni
11354
 
     * @since 2.1.000 (2008-01-08)
11355
 
     * @see UTF8ToUTF16BE()
11356
 
     */
11357
 
    protected function arrUTF8ToUTF16BE($unicode, $setbom=true) {
11358
 
        $outstr = ''; // string to be returned
11359
 
        if ($setbom) {
11360
 
            $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
11361
 
        }
11362
 
        foreach ($unicode as $char) {
11363
 
            if ($char == 0x200b) {
11364
 
                // skip Unicode Character 'ZERO WIDTH SPACE' (DEC:8203, U+200B)
11365
 
            } elseif ($char == 0xFFFD) {
11366
 
                $outstr .= "\xFF\xFD"; // replacement character
11367
 
            } elseif ($char < 0x10000) {
11368
 
                $outstr .= chr($char >> 0x08);
11369
 
                $outstr .= chr($char & 0xFF);
11370
 
            } else {
11371
 
                $char -= 0x10000;
11372
 
                $w1 = 0xD800 | ($char >> 0x10);
11373
 
                $w2 = 0xDC00 | ($char & 0x3FF);
11374
 
                $outstr .= chr($w1 >> 0x08);
11375
 
                $outstr .= chr($w1 & 0xFF);
11376
 
                $outstr .= chr($w2 >> 0x08);
11377
 
                $outstr .= chr($w2 & 0xFF);
11378
 
            }
11379
 
        }
11380
 
        return $outstr;
11381
 
    }
11382
 
    // ====================================================
11383
 
 
11384
 
    /**
11385
 
     * Set header font.
11386
 
     * @param $font (array) font
11387
 
     * @public
11388
 
     * @since 1.1
11389
 
     */
11390
 
    public function setHeaderFont($font) {
11391
 
        $this->header_font = $font;
11392
 
    }
11393
 
 
11394
 
    /**
11395
 
     * Get header font.
11396
 
     * @return array()
11397
 
     * @public
11398
 
     * @since 4.0.012 (2008-07-24)
11399
 
     */
11400
 
    public function getHeaderFont() {
11401
 
        return $this->header_font;
11402
 
    }
11403
 
 
11404
 
    /**
11405
 
     * Set footer font.
11406
 
     * @param $font (array) font
11407
 
     * @public
11408
 
     * @since 1.1
11409
 
     */
11410
 
    public function setFooterFont($font) {
11411
 
        $this->footer_font = $font;
11412
 
    }
11413
 
 
11414
 
    /**
11415
 
     * Get Footer font.
11416
 
     * @return array()
11417
 
     * @public
11418
 
     * @since 4.0.012 (2008-07-24)
11419
 
     */
11420
 
    public function getFooterFont() {
11421
 
        return $this->footer_font;
11422
 
    }
11423
 
 
11424
 
    /**
11425
 
     * Set language array.
11426
 
     * @param $language (array)
11427
 
     * @public
11428
 
     * @since 1.1
11429
 
     */
11430
 
    public function setLanguageArray($language) {
11431
 
        $this->l = $language;
11432
 
        if (isset($this->l['a_meta_dir'])) {
11433
 
            $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
11434
 
        } else {
11435
 
            $this->rtl = false;
11436
 
        }
11437
 
    }
11438
 
 
11439
 
    /**
11440
 
     * Returns the PDF data.
11441
 
     * @public
11442
 
     */
11443
 
    public function getPDFData() {
11444
 
        if ($this->state < 3) {
11445
 
            $this->Close();
11446
 
        }
11447
 
        return $this->buffer;
11448
 
    }
11449
 
 
11450
 
    /**
11451
 
     * Output anchor link.
11452
 
     * @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;)
11453
 
     * @param $name (string) link name
11454
 
     * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
11455
 
     * @param $firstline (boolean) if true prints only the first line and return the remaining string.
11456
 
     * @param $color (array) array of RGB text color
11457
 
     * @param $style (string) font style (U, D, B, I)
11458
 
     * @param $firstblock (boolean) if true the string is the starting of a line.
11459
 
     * @return the number of cells used or the remaining text if $firstline = true;
11460
 
     * @public
11461
 
     */
11462
 
    public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
11463
 
        if (!$this->empty_string($url) AND ($url{0} == '#')) {
11464
 
            // convert url to internal link
11465
 
            $lnkdata = explode(',', $url);
11466
 
            if (isset($lnkdata[0])) {
11467
 
                $page = intval(substr($lnkdata[0], 1));
11468
 
                if (empty($page) OR ($page <= 0)) {
11469
 
                    $page = $this->page;
11470
 
                }
11471
 
                if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
11472
 
                    $lnky = floatval($lnkdata[1]);
11473
 
                } else {
11474
 
                    $lnky = 0;
11475
 
                }
11476
 
                $url = $this->AddLink();
11477
 
                $this->SetLink($url, $lnky, $page);
11478
 
            }
11479
 
        }
11480
 
        // store current settings
11481
 
        $prevcolor = $this->fgcolor;
11482
 
        $prevstyle = $this->FontStyle;
11483
 
        if (empty($color)) {
11484
 
            $this->SetTextColorArray($this->htmlLinkColorArray);
11485
 
        } else {
11486
 
            $this->SetTextColorArray($color);
11487
 
        }
11488
 
        if ($style == -1) {
11489
 
            $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
11490
 
        } else {
11491
 
            $this->SetFont('', $this->FontStyle.$style);
11492
 
        }
11493
 
        $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
11494
 
        // restore settings
11495
 
        $this->SetFont('', $prevstyle);
11496
 
        $this->SetTextColorArray($prevcolor);
11497
 
        return $ret;
11498
 
    }
11499
 
 
11500
 
    /**
11501
 
     * Returns an array (RGB or CMYK) from an html color name or a six-digit (i.e. #3FE5AA) or three-digit (i.e. #7FF) hexadecimal color representation.
11502
 
     * @param $hcolor (string) html color
11503
 
     * @return array RGB or CMYK color, or false in case of error.
11504
 
     * @public
11505
 
     */
11506
 
    public function convertHTMLColorToDec($hcolor='#FFFFFF') {
11507
 
        $returncolor = false;
11508
 
        $color = preg_replace('/[\s]*/', '', $hcolor); // remove extra spaces
11509
 
        $color = strtolower($color);
11510
 
        if (($dotpos = strpos($color, '.')) !== false) {
11511
 
            // remove class parent (i.e.: color.red)
11512
 
            $color = substr($color, ($dotpos + 1));
11513
 
        }
11514
 
        if (strlen($color) == 0) {
11515
 
            return false;
11516
 
        }
11517
 
        // RGB ARRAY
11518
 
        if (substr($color, 0, 3) == 'rgb') {
11519
 
            $codes = substr($color, 4);
11520
 
            $codes = str_replace(')', '', $codes);
11521
 
            $returncolor = explode(',', $codes);
11522
 
            foreach ($returncolor as $key => $val) {
11523
 
                if (strpos($val, '%') > 0) {
11524
 
                    // percentage
11525
 
                    $returncolor[$key] = (255 * intval($val) / 100);
11526
 
                } else {
11527
 
                    $returncolor[$key] = intval($val);
11528
 
                }
11529
 
                // normalize value
11530
 
                $returncolor[$key] = max(0, min(255, $returncolor[$key]));
11531
 
            }
11532
 
            return $returncolor;
11533
 
        }
11534
 
        // CMYK ARRAY
11535
 
        if (substr($color, 0, 4) == 'cmyk') {
11536
 
            $codes = substr($color, 5);
11537
 
            $codes = str_replace(')', '', $codes);
11538
 
            $returncolor = explode(',', $codes);
11539
 
            foreach ($returncolor as $key => $val) {
11540
 
                if (strpos($val, '%') !== false) {
11541
 
                    // percentage
11542
 
                    $returncolor[$key] = (100 * intval($val) / 100);
11543
 
                } else {
11544
 
                    $returncolor[$key] = intval($val);
11545
 
                }
11546
 
                // normalize value
11547
 
                $returncolor[$key] = max(0, min(100, $returncolor[$key]));
11548
 
            }
11549
 
            return $returncolor;
11550
 
        }
11551
 
        // COLOR NAME
11552
 
        if (substr($color, 0, 1) != '#') {
11553
 
            // decode color name
11554
 
            if (isset($this->webcolor[$color])) {
11555
 
                // web color
11556
 
                $color_code = $this->webcolor[$color];
11557
 
            } elseif (isset($this->spot_colors[$hcolor])) {
11558
 
                // custom defined spot color
11559
 
                return array($this->spot_colors[$hcolor]['c'], $this->spot_colors[$hcolor]['m'], $this->spot_colors[$hcolor]['y'], $this->spot_colors[$hcolor]['k'], $hcolor);
11560
 
            } elseif (isset($this->spotcolor[$color])) {
11561
 
                // spot color from configuration file
11562
 
                return $this->spotcolor[$color];
11563
 
            } else {
11564
 
                return false;
11565
 
            }
11566
 
        } else {
11567
 
            $color_code = substr($color, 1);
11568
 
        }
11569
 
        // RGB VALUE
11570
 
        switch (strlen($color_code)) {
11571
 
            case 3: {
11572
 
                // three-digit hexadecimal representation
11573
 
                $r = substr($color_code, 0, 1);
11574
 
                $g = substr($color_code, 1, 1);
11575
 
                $b = substr($color_code, 2, 1);
11576
 
                $returncolor = array();
11577
 
                $returncolor['R'] = max(0, min(255, hexdec($r.$r)));
11578
 
                $returncolor['G'] = max(0, min(255, hexdec($g.$g)));
11579
 
                $returncolor['B'] = max(0, min(255, hexdec($b.$b)));
11580
 
                break;
11581
 
            }
11582
 
            case 6: {
11583
 
                // six-digit hexadecimal representation
11584
 
                $returncolor = array();
11585
 
                $returncolor['R'] = max(0, min(255, hexdec(substr($color_code, 0, 2))));
11586
 
                $returncolor['G'] = max(0, min(255, hexdec(substr($color_code, 2, 2))));
11587
 
                $returncolor['B'] = max(0, min(255, hexdec(substr($color_code, 4, 2))));
11588
 
                break;
11589
 
            }
11590
 
        }
11591
 
        return $returncolor;
11592
 
    }
11593
 
 
11594
 
    /**
11595
 
     * Converts pixels to User's Units.
11596
 
     * @param $px (int) pixels
11597
 
     * @return float value in user's unit
11598
 
     * @public
11599
 
     * @see setImageScale(), getImageScale()
11600
 
     */
11601
 
    public function pixelsToUnits($px) {
11602
 
        return ($px / ($this->imgscale * $this->k));
11603
 
    }
11604
 
 
11605
 
    /**
11606
 
     * Reverse function for htmlentities.
11607
 
     * Convert entities in UTF-8.
11608
 
     * @param $text_to_convert (string) Text to convert.
11609
 
     * @return string converted text string
11610
 
     * @public
11611
 
     */
11612
 
    public function unhtmlentities($text_to_convert) {
11613
 
        return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
11614
 
    }
11615
 
 
11616
 
    // ENCRYPTION METHODS ----------------------------------
11617
 
 
11618
 
    /**
11619
 
     * Returns a string containing random data to be used as a seed for encryption methods.
11620
 
     * @param $seed (string) starting seed value
11621
 
     * @return string containing random data
11622
 
     * @author Nicola Asuni
11623
 
     * @since 5.9.006 (2010-10-19)
11624
 
     * @protected
11625
 
     */
11626
 
    protected function getRandomSeed($seed='') {
11627
 
        $seed .= microtime();
11628
 
        if (function_exists('openssl_random_pseudo_bytes') AND (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) {
11629
 
            // this is not used on windows systems because it is very slow for a know bug
11630
 
            $seed .= openssl_random_pseudo_bytes(512);
11631
 
        } else {
11632
 
            for ($i = 0; $i < 23; ++$i) {
11633
 
                $seed .= uniqid('', true);
11634
 
            }
11635
 
        }
11636
 
        $seed .= uniqid('', true);
11637
 
        $seed .= rand();
11638
 
        $seed .= getmypid();
11639
 
        $seed .= __FILE__;
11640
 
        $seed .= $this->bufferlen;
11641
 
        if (isset($_SERVER['REMOTE_ADDR'])) {
11642
 
            $seed .= $_SERVER['REMOTE_ADDR'];
11643
 
        }
11644
 
        if (isset($_SERVER['HTTP_USER_AGENT'])) {
11645
 
            $seed .= $_SERVER['HTTP_USER_AGENT'];
11646
 
        }
11647
 
        if (isset($_SERVER['HTTP_ACCEPT'])) {
11648
 
            $seed .= $_SERVER['HTTP_ACCEPT'];
11649
 
        }
11650
 
        if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
11651
 
            $seed .= $_SERVER['HTTP_ACCEPT_ENCODING'];
11652
 
        }
11653
 
        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
11654
 
            $seed .= $_SERVER['HTTP_ACCEPT_LANGUAGE'];
11655
 
        }
11656
 
        if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) {
11657
 
            $seed .= $_SERVER['HTTP_ACCEPT_CHARSET'];
11658
 
        }
11659
 
        $seed .= rand();
11660
 
        $seed .= uniqid('', true);
11661
 
        $seed .= microtime();
11662
 
        return $seed;
11663
 
    }
11664
 
 
11665
 
    /**
11666
 
     * Compute encryption key depending on object number where the encrypted data is stored.
11667
 
     * This is used for all strings and streams without crypt filter specifier.
11668
 
     * @param $n (int) object number
11669
 
     * @return int object key
11670
 
     * @protected
11671
 
     * @author Nicola Asuni
11672
 
     * @since 2.0.000 (2008-01-02)
11673
 
     */
11674
 
    protected function _objectkey($n) {
11675
 
        $objkey = $this->encryptdata['key'].pack('VXxx', $n);
11676
 
        if ($this->encryptdata['mode'] == 2) { // AES-128
11677
 
            // AES padding
11678
 
            $objkey .= "\x73\x41\x6C\x54"; // sAlT
11679
 
        }
11680
 
        $objkey = substr($this->_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5));
11681
 
        $objkey = substr($objkey, 0, 16);
11682
 
        return $objkey;
11683
 
    }
11684
 
 
11685
 
    /**
11686
 
     * Encrypt the input string.
11687
 
     * @param $n (int) object number
11688
 
     * @param $s (string) data string to encrypt
11689
 
     * @return encrypted string
11690
 
     * @protected
11691
 
     * @author Nicola Asuni
11692
 
     * @since 5.0.005 (2010-05-11)
11693
 
     */
11694
 
    protected function _encrypt_data($n, $s) {
11695
 
        if (!$this->encrypted) {
11696
 
            return $s;
11697
 
        }
11698
 
        switch ($this->encryptdata['mode']) {
11699
 
            case 0:   // RC4-40
11700
 
            case 1: { // RC4-128
11701
 
                $s = $this->_RC4($this->_objectkey($n), $s);
11702
 
                break;
11703
 
            }
11704
 
            case 2: { // AES-128
11705
 
                $s = $this->_AES($this->_objectkey($n), $s);
11706
 
                break;
11707
 
            }
11708
 
            case 3: { // AES-256
11709
 
                $s = $this->_AES($this->encryptdata['key'], $s);
11710
 
                break;
11711
 
            }
11712
 
        }
11713
 
        return $s;
11714
 
    }
11715
 
 
11716
 
    /**
11717
 
     * Put encryption on PDF document.
11718
 
     * @protected
11719
 
     * @author Nicola Asuni
11720
 
     * @since 2.0.000 (2008-01-02)
11721
 
     */
11722
 
    protected function _putencryption() {
11723
 
        if (!$this->encrypted) {
11724
 
            return;
11725
 
        }
11726
 
        $this->encryptdata['objid'] = $this->_newobj();
11727
 
        $out = '<<';
11728
 
        if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) {
11729
 
            $this->encryptdata['Filter'] = 'Standard';
11730
 
        }
11731
 
        $out .= ' /Filter /'.$this->encryptdata['Filter'];
11732
 
        if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) {
11733
 
            $out .= ' /SubFilter /'.$this->encryptdata['SubFilter'];
11734
 
        }
11735
 
        if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) {
11736
 
            $this->encryptdata['V'] = 1;
11737
 
        }
11738
 
        // V is a code specifying the algorithm to be used in encrypting and decrypting the document
11739
 
        $out .= ' /V '.$this->encryptdata['V'];
11740
 
        if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) {
11741
 
            // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
11742
 
            $out .= ' /Length '.$this->encryptdata['Length'];
11743
 
        } else {
11744
 
            $out .= ' /Length 40';
11745
 
        }
11746
 
        if ($this->encryptdata['V'] >= 4) {
11747
 
            if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) {
11748
 
                $this->encryptdata['StmF'] = 'Identity';
11749
 
            }
11750
 
            if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) {
11751
 
                // The name of the crypt filter that shall be used when decrypting all strings in the document.
11752
 
                $this->encryptdata['StrF'] = 'Identity';
11753
 
            }
11754
 
            // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
11755
 
            if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) {
11756
 
                $out .= ' /CF <<';
11757
 
                $out .= ' /'.$this->encryptdata['StmF'].' <<';
11758
 
                $out .= ' /Type /CryptFilter';
11759
 
                if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) {
11760
 
                    // The method used
11761
 
                    $out .= ' /CFM /'.$this->encryptdata['CF']['CFM'];
11762
 
                    if ($this->encryptdata['pubkey']) {
11763
 
                        $out .= ' /Recipients [';
11764
 
                        foreach ($this->encryptdata['Recipients'] as $rec) {
11765
 
                            $out .= ' <'.$rec.'>';
11766
 
                        }
11767
 
                        $out .= ' ]';
11768
 
                        if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) {
11769
 
                            $out .= ' /EncryptMetadata false';
11770
 
                        } else {
11771
 
                            $out .= ' /EncryptMetadata true';
11772
 
                        }
11773
 
                    }
11774
 
                } else {
11775
 
                    $out .= ' /CFM /None';
11776
 
                }
11777
 
                if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) {
11778
 
                    // The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
11779
 
                    $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent'];
11780
 
                } else {
11781
 
                    $out .= ' /AuthEvent /DocOpen';
11782
 
                }
11783
 
                if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) {
11784
 
                    // The bit length of the encryption key.
11785
 
                    $out .= ' /Length '.$this->encryptdata['CF']['Length'];
11786
 
                }
11787
 
                $out .= ' >> >>';
11788
 
            }
11789
 
            // The name of the crypt filter that shall be used by default when decrypting streams.
11790
 
            $out .= ' /StmF /'.$this->encryptdata['StmF'];
11791
 
            // The name of the crypt filter that shall be used when decrypting all strings in the document.
11792
 
            $out .= ' /StrF /'.$this->encryptdata['StrF'];
11793
 
            if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) {
11794
 
                // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
11795
 
                $out .= ' /EFF /'.$this->encryptdata[''];
11796
 
            }
11797
 
        }
11798
 
        // Additional encryption dictionary entries for the standard security handler
11799
 
        if ($this->encryptdata['pubkey']) {
11800
 
            if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) {
11801
 
                $out .= ' /Recipients [';
11802
 
                foreach ($this->encryptdata['Recipients'] as $rec) {
11803
 
                    $out .= ' <'.$rec.'>';
11804
 
                }
11805
 
                $out .= ' ]';
11806
 
            }
11807
 
        } else {
11808
 
            $out .= ' /R';
11809
 
            if ($this->encryptdata['V'] == 5) { // AES-256
11810
 
                $out .= ' 5';
11811
 
                $out .= ' /OE ('.$this->_escape($this->encryptdata['OE']).')';
11812
 
                $out .= ' /UE ('.$this->_escape($this->encryptdata['UE']).')';
11813
 
                $out .= ' /Perms ('.$this->_escape($this->encryptdata['perms']).')';
11814
 
            } elseif ($this->encryptdata['V'] == 4) { // AES-128
11815
 
                $out .= ' 4';
11816
 
            } elseif ($this->encryptdata['V'] < 2) { // RC-40
11817
 
                $out .= ' 2';
11818
 
            } else { // RC-128
11819
 
                $out .= ' 3';
11820
 
            }
11821
 
            $out .= ' /O ('.$this->_escape($this->encryptdata['O']).')';
11822
 
            $out .= ' /U ('.$this->_escape($this->encryptdata['U']).')';
11823
 
            $out .= ' /P '.$this->encryptdata['P'];
11824
 
            if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) {
11825
 
                $out .= ' /EncryptMetadata false';
11826
 
            } else {
11827
 
                $out .= ' /EncryptMetadata true';
11828
 
            }
11829
 
        }
11830
 
        $out .= ' >>';
11831
 
        $out .= "\n".'endobj';
11832
 
        $this->_out($out);
11833
 
    }
11834
 
 
11835
 
    /**
11836
 
     * Returns the input text encrypted using RC4 algorithm and the specified key.
11837
 
     * RC4 is the standard encryption algorithm used in PDF format
11838
 
     * @param $key (string) encryption key
11839
 
     * @param $text (String) input text to be encrypted
11840
 
     * @return String encrypted text
11841
 
     * @protected
11842
 
     * @since 2.0.000 (2008-01-02)
11843
 
     * @author Klemen Vodopivec, Nicola Asuni
11844
 
     */
11845
 
    protected function _RC4($key, $text) {
11846
 
        if (function_exists('mcrypt_decrypt') AND ($out = @mcrypt_decrypt(MCRYPT_ARCFOUR, $key, $text, MCRYPT_MODE_STREAM, ''))) {
11847
 
            // try to use mcrypt function if exist
11848
 
            return $out;
11849
 
        }
11850
 
        if ($this->last_enc_key != $key) {
11851
 
            $k = str_repeat($key, ((256 / strlen($key)) + 1));
11852
 
            $rc4 = range(0, 255);
11853
 
            $j = 0;
11854
 
            for ($i = 0; $i < 256; ++$i) {
11855
 
                $t = $rc4[$i];
11856
 
                $j = ($j + $t + ord($k{$i})) % 256;
11857
 
                $rc4[$i] = $rc4[$j];
11858
 
                $rc4[$j] = $t;
11859
 
            }
11860
 
            $this->last_enc_key = $key;
11861
 
            $this->last_enc_key_c = $rc4;
11862
 
        } else {
11863
 
            $rc4 = $this->last_enc_key_c;
11864
 
        }
11865
 
        $len = strlen($text);
11866
 
        $a = 0;
11867
 
        $b = 0;
11868
 
        $out = '';
11869
 
        for ($i = 0; $i < $len; ++$i) {
11870
 
            $a = ($a + 1) % 256;
11871
 
            $t = $rc4[$a];
11872
 
            $b = ($b + $t) % 256;
11873
 
            $rc4[$a] = $rc4[$b];
11874
 
            $rc4[$b] = $t;
11875
 
            $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
11876
 
            $out .= chr(ord($text{$i}) ^ $k);
11877
 
        }
11878
 
        return $out;
11879
 
    }
11880
 
 
11881
 
    /**
11882
 
     * Returns the input text exrypted using AES algorithm and the specified key.
11883
 
     * This method requires mcrypt.
11884
 
     * @param $key (string) encryption key
11885
 
     * @param $text (String) input text to be encrypted
11886
 
     * @return String encrypted text
11887
 
     * @protected
11888
 
     * @author Nicola Asuni
11889
 
     * @since 5.0.005 (2010-05-11)
11890
 
     */
11891
 
    protected function _AES($key, $text) {
11892
 
        // padding (RFC 2898, PKCS #5: Password-Based Cryptography Specification Version 2.0)
11893
 
        $padding = 16 - (strlen($text) % 16);
11894
 
        $text .= str_repeat(chr($padding), $padding);
11895
 
        $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
11896
 
        $text = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv);
11897
 
        $text = $iv.$text;
11898
 
        return $text;
11899
 
    }
11900
 
 
11901
 
    /**
11902
 
     * Encrypts a string using MD5 and returns it's value as a binary string.
11903
 
     * @param $str (string) input string
11904
 
     * @return String MD5 encrypted binary string
11905
 
     * @protected
11906
 
     * @since 2.0.000 (2008-01-02)
11907
 
     * @author Klemen Vodopivec
11908
 
     */
11909
 
    protected function _md5_16($str) {
11910
 
        return pack('H*', md5($str));
11911
 
    }
11912
 
 
11913
 
    /**
11914
 
     * Compute U value (used for encryption)
11915
 
     * @return string U value
11916
 
     * @protected
11917
 
     * @since 2.0.000 (2008-01-02)
11918
 
     * @author Nicola Asuni
11919
 
     */
11920
 
    protected function _Uvalue() {
11921
 
        if ($this->encryptdata['mode'] == 0) { // RC4-40
11922
 
            return $this->_RC4($this->encryptdata['key'], $this->enc_padding);
11923
 
        } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
11924
 
            $tmp = $this->_md5_16($this->enc_padding.$this->encryptdata['fileid']);
11925
 
            $enc = $this->_RC4($this->encryptdata['key'], $tmp);
11926
 
            $len = strlen($tmp);
11927
 
            for ($i = 1; $i <= 19; ++$i) {
11928
 
                $ek = '';
11929
 
                for ($j = 0; $j < $len; ++$j) {
11930
 
                    $ek .= chr(ord($this->encryptdata['key']{$j}) ^ $i);
11931
 
                }
11932
 
                $enc = $this->_RC4($ek, $enc);
11933
 
            }
11934
 
            $enc .= str_repeat("\x00", 16);
11935
 
            return substr($enc, 0, 32);
11936
 
        } elseif ($this->encryptdata['mode'] == 3) { // AES-256
11937
 
            $seed = $this->_md5_16($this->getRandomSeed());
11938
 
            // User Validation Salt
11939
 
            $this->encryptdata['UVS'] = substr($seed, 0, 8);
11940
 
            // User Key Salt
11941
 
            $this->encryptdata['UKS'] = substr($seed, 8, 16);
11942
 
            return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS'];
11943
 
        }
11944
 
    }
11945
 
 
11946
 
    /**
11947
 
     * Compute UE value (used for encryption)
11948
 
     * @return string UE value
11949
 
     * @protected
11950
 
     * @since 5.9.006 (2010-10-19)
11951
 
     * @author Nicola Asuni
11952
 
     */
11953
 
    protected function _UEvalue() {
11954
 
        $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true);
11955
 
        $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
11956
 
        return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
11957
 
    }
11958
 
 
11959
 
    /**
11960
 
     * Compute O value (used for encryption)
11961
 
     * @return string O value
11962
 
     * @protected
11963
 
     * @since 2.0.000 (2008-01-02)
11964
 
     * @author Nicola Asuni
11965
 
     */
11966
 
    protected function _Ovalue() {
11967
 
        if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
11968
 
            $tmp = $this->_md5_16($this->encryptdata['owner_password']);
11969
 
            if ($this->encryptdata['mode'] > 0) {
11970
 
                for ($i = 0; $i < 50; ++$i) {
11971
 
                    $tmp = $this->_md5_16($tmp);
11972
 
                }
11973
 
            }
11974
 
            $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
11975
 
            $enc = $this->_RC4($owner_key, $this->encryptdata['user_password']);
11976
 
            if ($this->encryptdata['mode'] > 0) {
11977
 
                $len = strlen($owner_key);
11978
 
                for ($i = 1; $i <= 19; ++$i) {
11979
 
                    $ek = '';
11980
 
                    for ($j = 0; $j < $len; ++$j) {
11981
 
                        $ek .= chr(ord($owner_key{$j}) ^ $i);
11982
 
                    }
11983
 
                    $enc = $this->_RC4($ek, $enc);
11984
 
                }
11985
 
            }
11986
 
            return $enc;
11987
 
        } elseif ($this->encryptdata['mode'] == 3) { // AES-256
11988
 
            $seed = $this->_md5_16($this->getRandomSeed());
11989
 
            // Owner Validation Salt
11990
 
            $this->encryptdata['OVS'] = substr($seed, 0, 8);
11991
 
            // Owner Key Salt
11992
 
            $this->encryptdata['OKS'] = substr($seed, 8, 16);
11993
 
            return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS'];
11994
 
        }
11995
 
    }
11996
 
 
11997
 
    /**
11998
 
     * Compute OE value (used for encryption)
11999
 
     * @return string OE value
12000
 
     * @protected
12001
 
     * @since 5.9.006 (2010-10-19)
12002
 
     * @author Nicola Asuni
12003
 
     */
12004
 
    protected function _OEvalue() {
12005
 
        $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true);
12006
 
        $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
12007
 
        return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
12008
 
    }
12009
 
 
12010
 
    /**
12011
 
     * Convert password for AES-256 encryption mode
12012
 
     * @param $password (string) password
12013
 
     * @return string password
12014
 
     * @protected
12015
 
     * @since 5.9.006 (2010-10-19)
12016
 
     * @author Nicola Asuni
12017
 
     */
12018
 
    protected function _fixAES256Password($password) {
12019
 
        $psw = ''; // password to be returned
12020
 
        $psw_array = $this->utf8Bidi($this->UTF8StringToArray($password), $password, $this->rtl);
12021
 
        foreach ($psw_array as $c) {
12022
 
            $psw .= $this->unichr($c);
12023
 
        }
12024
 
        return substr($psw, 0, 127);
12025
 
    }
12026
 
 
12027
 
    /**
12028
 
     * Compute encryption key
12029
 
     * @protected
12030
 
     * @since 2.0.000 (2008-01-02)
12031
 
     * @author Nicola Asuni
12032
 
     */
12033
 
    protected function _generateencryptionkey() {
12034
 
        $keybytelen = ($this->encryptdata['Length'] / 8);
12035
 
        if (!$this->encryptdata['pubkey']) { // standard mode
12036
 
            if ($this->encryptdata['mode'] == 3) { // AES-256
12037
 
                // generate 256 bit random key
12038
 
                $this->encryptdata['key'] = substr(hash('sha256', $this->getRandomSeed(), true), 0, $keybytelen);
12039
 
                // truncate passwords
12040
 
                $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']);
12041
 
                $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']);
12042
 
                // Compute U value
12043
 
                $this->encryptdata['U'] = $this->_Uvalue();
12044
 
                // Compute UE value
12045
 
                $this->encryptdata['UE'] = $this->_UEvalue();
12046
 
                // Compute O value
12047
 
                $this->encryptdata['O'] = $this->_Ovalue();
12048
 
                // Compute OE value
12049
 
                $this->encryptdata['OE'] = $this->_OEvalue();
12050
 
                // Compute P value
12051
 
                $this->encryptdata['P'] = $this->encryptdata['protection'];
12052
 
                // Computing the encryption dictionary's Perms (permissions) value
12053
 
                $perms = $this->getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
12054
 
                $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
12055
 
                if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8
12056
 
                    $perms .= 'F';
12057
 
                } else {
12058
 
                    $perms .= 'T';
12059
 
                }
12060
 
                $perms .= 'adb'; // bytes 9-11
12061
 
                $perms .= 'nick'; // bytes 12-15
12062
 
                $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB));
12063
 
                $this->encryptdata['perms'] = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->encryptdata['key'], $perms, MCRYPT_MODE_ECB, $iv);
12064
 
            } else { // RC4-40, RC4-128, AES-128
12065
 
                // Pad passwords
12066
 
                $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].$this->enc_padding, 0, 32);
12067
 
                $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].$this->enc_padding, 0, 32);
12068
 
                // Compute O value
12069
 
                $this->encryptdata['O'] = $this->_Ovalue();
12070
 
                // get default permissions (reverse byte order)
12071
 
                $permissions = $this->getEncPermissionsString($this->encryptdata['protection']);
12072
 
                // Compute encryption key
12073
 
                $tmp = $this->_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']);
12074
 
                if ($this->encryptdata['mode'] > 0) {
12075
 
                    for ($i = 0; $i < 50; ++$i) {
12076
 
                        $tmp = $this->_md5_16(substr($tmp, 0, $keybytelen));
12077
 
                    }
12078
 
                }
12079
 
                $this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
12080
 
                // Compute U value
12081
 
                $this->encryptdata['U'] = $this->_Uvalue();
12082
 
                // Compute P value
12083
 
                $this->encryptdata['P'] = $this->encryptdata['protection'];
12084
 
            }
12085
 
        } else { // Public-Key mode
12086
 
            // random 20-byte seed
12087
 
            $seed = sha1($this->getRandomSeed(), true);
12088
 
            $recipient_bytes = '';
12089
 
            foreach ($this->encryptdata['pubkeys'] as $pubkey) {
12090
 
                // for each public certificate
12091
 
                if (isset($pubkey['p'])) {
12092
 
                    $pkprotection = $this->getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
12093
 
                } else {
12094
 
                    $pkprotection = $this->encryptdata['protection'];
12095
 
                }
12096
 
                // get default permissions (reverse byte order)
12097
 
                $pkpermissions = $this->getEncPermissionsString($pkprotection);
12098
 
                // envelope data
12099
 
                $envelope = $seed.$pkpermissions;
12100
 
                // write the envelope data to a temporary file
12101
 
                $tempkeyfile = tempnam(K_PATH_CACHE, 'tmpkey_');
12102
 
                $f = fopen($tempkeyfile, 'wb');
12103
 
                if (!$f) {
12104
 
                    $this->Error('Unable to create temporary key file: '.$tempkeyfile);
12105
 
                }
12106
 
                $envelope_lenght = strlen($envelope);
12107
 
                fwrite($f, $envelope, $envelope_lenght);
12108
 
                fclose($f);
12109
 
                $tempencfile = tempnam(K_PATH_CACHE, 'tmpenc_');
12110
 
                if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_DETACHED | PKCS7_BINARY)) {
12111
 
                    $this->Error('Unable to encrypt the file: '.$tempkeyfile);
12112
 
                }
12113
 
                unlink($tempkeyfile);
12114
 
                // read encryption signature
12115
 
                $signature = file_get_contents($tempencfile, false, null, $envelope_lenght);
12116
 
                unlink($tempencfile);
12117
 
                // extract signature
12118
 
                $signature = substr($signature, strpos($signature, 'Content-Disposition'));
12119
 
                $tmparr = explode("\n\n", $signature);
12120
 
                $signature = trim($tmparr[1]);
12121
 
                unset($tmparr);
12122
 
                // decode signature
12123
 
                $signature = base64_decode($signature);
12124
 
                // convert signature to hex
12125
 
                $hexsignature = current(unpack('H*', $signature));
12126
 
                // store signature on recipients array
12127
 
                $this->encryptdata['Recipients'][] = $hexsignature;
12128
 
                // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
12129
 
                $recipient_bytes .= $signature;
12130
 
            }
12131
 
            // calculate encryption key
12132
 
            if ($this->encryptdata['mode'] == 3) { // AES-256
12133
 
                $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
12134
 
            } else { // RC4-40, RC4-128, AES-128
12135
 
                $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
12136
 
            }
12137
 
        }
12138
 
    }
12139
 
 
12140
 
    /**
12141
 
     * Return the premission code used on encryption (P value).
12142
 
     * @param $permissions (Array) the set of permissions (specify the ones you want to block).
12143
 
     * @param $mode (int) encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit.
12144
 
     * @protected
12145
 
     * @since 5.0.005 (2010-05-12)
12146
 
     * @author Nicola Asuni
12147
 
     */
12148
 
    protected function getUserPermissionCode($permissions, $mode=0) {
12149
 
        $options = array(
12150
 
            'owner' => 2, // bit 2 -- inverted logic: cleared by default
12151
 
            'print' => 4, // bit 3
12152
 
            'modify' => 8, // bit 4
12153
 
            'copy' => 16, // bit 5
12154
 
            'annot-forms' => 32, // bit 6
12155
 
            'fill-forms' => 256, // bit 9
12156
 
            'extract' => 512, // bit 10
12157
 
            'assemble' => 1024,// bit 11
12158
 
            'print-high' => 2048 // bit 12
12159
 
            );
12160
 
        $protection = 2147422012; // 32 bit: (01111111 11111111 00001111 00111100)
12161
 
        foreach ($permissions as $permission) {
12162
 
            if (!isset($options[$permission])) {
12163
 
                $this->Error('Incorrect permission: '.$permission);
12164
 
            }
12165
 
            if (($mode > 0) OR ($options[$permission] <= 32)) {
12166
 
                // set only valid permissions
12167
 
                if ($options[$permission] == 2) {
12168
 
                    // the logic for bit 2 is inverted (cleared by default)
12169
 
                    $protection += $options[$permission];
12170
 
                } else {
12171
 
                    $protection -= $options[$permission];
12172
 
                }
12173
 
            }
12174
 
        }
12175
 
        return $protection;
12176
 
    }
12177
 
 
12178
 
    /**
12179
 
     * Set document protection
12180
 
     * Remark: the protection against modification is for people who have the full Acrobat product.
12181
 
     * 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.
12182
 
     * 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.
12183
 
     * @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>
12184
 
     * @param $user_pass (String) user password. Empty by default.
12185
 
     * @param $owner_pass (String) owner password. If not specified, a random value is used.
12186
 
     * @param $mode (int) encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit.
12187
 
     * @param $pubkeys (String) array of recipients containing public-key certificates ('c') and permissions ('p'). For example: array(array('c' => 'file://../tcpdf.crt', 'p' => array('print')))
12188
 
     * @public
12189
 
     * @since 2.0.000 (2008-01-02)
12190
 
     * @author Nicola Asuni
12191
 
     */
12192
 
    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) {
12193
 
        $this->encryptdata['protection'] = $this->getUserPermissionCode($permissions, $mode);
12194
 
        if (($pubkeys !== null) AND (is_array($pubkeys))) {
12195
 
            // public-key mode
12196
 
            $this->encryptdata['pubkeys'] = $pubkeys;
12197
 
            if ($mode == 0) {
12198
 
                // public-Key Security requires at least 128 bit
12199
 
                $mode = 1;
12200
 
            }
12201
 
            if (!function_exists('openssl_pkcs7_encrypt')) {
12202
 
                $this->Error('Public-Key Security requires openssl library.');
12203
 
            }
12204
 
            // Set Public-Key filter (availabe are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
12205
 
            $this->encryptdata['pubkey'] = true;
12206
 
            $this->encryptdata['Filter'] = 'Adobe.PubSec';
12207
 
            $this->encryptdata['StmF'] = 'DefaultCryptFilter';
12208
 
            $this->encryptdata['StrF'] = 'DefaultCryptFilter';
12209
 
        } else {
12210
 
            // standard mode (password mode)
12211
 
            $this->encryptdata['pubkey'] = false;
12212
 
            $this->encryptdata['Filter'] = 'Standard';
12213
 
            $this->encryptdata['StmF'] = 'StdCF';
12214
 
            $this->encryptdata['StrF'] = 'StdCF';
12215
 
        }
12216
 
        if ($mode > 1) { // AES
12217
 
            if (!extension_loaded('mcrypt')) {
12218
 
                $this->Error('AES encryption requires mcrypt library (http://www.php.net/manual/en/mcrypt.requirements.php).');
12219
 
            }
12220
 
            if (mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) {
12221
 
                $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
12222
 
            }
12223
 
            if (($mode == 3) AND !function_exists('hash')) {
12224
 
                // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
12225
 
                $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
12226
 
            }
12227
 
        }
12228
 
        if ($owner_pass === null) {
12229
 
            $owner_pass = md5($this->getRandomSeed());
12230
 
        }
12231
 
        $this->encryptdata['user_password'] = $user_pass;
12232
 
        $this->encryptdata['owner_password'] = $owner_pass;
12233
 
        $this->encryptdata['mode'] = $mode;
12234
 
        switch ($mode) {
12235
 
            case 0: { // RC4 40 bit
12236
 
                $this->encryptdata['V'] = 1;
12237
 
                $this->encryptdata['Length'] = 40;
12238
 
                $this->encryptdata['CF']['CFM'] = 'V2';
12239
 
                break;
12240
 
            }
12241
 
            case 1: { // RC4 128 bit
12242
 
                $this->encryptdata['V'] = 2;
12243
 
                $this->encryptdata['Length'] = 128;
12244
 
                $this->encryptdata['CF']['CFM'] = 'V2';
12245
 
                if ($this->encryptdata['pubkey']) {
12246
 
                    $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4';
12247
 
                    $this->encryptdata['Recipients'] = array();
12248
 
                }
12249
 
                break;
12250
 
            }
12251
 
            case 2: { // AES 128 bit
12252
 
                $this->encryptdata['V'] = 4;
12253
 
                $this->encryptdata['Length'] = 128;
12254
 
                $this->encryptdata['CF']['CFM'] = 'AESV2';
12255
 
                $this->encryptdata['CF']['Length'] = 128;
12256
 
                if ($this->encryptdata['pubkey']) {
12257
 
                    $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
12258
 
                    $this->encryptdata['Recipients'] = array();
12259
 
                }
12260
 
                break;
12261
 
            }
12262
 
            case 3: { // AES 256 bit
12263
 
                $this->encryptdata['V'] = 5;
12264
 
                $this->encryptdata['Length'] = 256;
12265
 
                $this->encryptdata['CF']['CFM'] = 'AESV3';
12266
 
                $this->encryptdata['CF']['Length'] = 256;
12267
 
                if ($this->encryptdata['pubkey']) {
12268
 
                    $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
12269
 
                    $this->encryptdata['Recipients'] = array();
12270
 
                }
12271
 
                break;
12272
 
            }
12273
 
        }
12274
 
        $this->encrypted = true;
12275
 
        $this->encryptdata['fileid'] = $this->convertHexStringToString($this->file_id);
12276
 
        $this->_generateencryptionkey();
12277
 
    }
12278
 
 
12279
 
    /**
12280
 
     * Convert hexadecimal string to string
12281
 
     * @param $bs (string) byte-string to convert
12282
 
     * @return String
12283
 
     * @protected
12284
 
     * @since 5.0.005 (2010-05-12)
12285
 
     * @author Nicola Asuni
12286
 
     */
12287
 
    protected function convertHexStringToString($bs) {
12288
 
        $string = ''; // string to be returned
12289
 
        $bslenght = strlen($bs);
12290
 
        if (($bslenght % 2) != 0) {
12291
 
            // padding
12292
 
            $bs .= '0';
12293
 
            ++$bslenght;
12294
 
        }
12295
 
        for ($i = 0; $i < $bslenght; $i += 2) {
12296
 
            $string .= chr(hexdec($bs{$i}.$bs{($i + 1)}));
12297
 
        }
12298
 
        return $string;
12299
 
    }
12300
 
 
12301
 
    /**
12302
 
     * Convert string to hexadecimal string (byte string)
12303
 
     * @param $s (string) string to convert
12304
 
     * @return byte string
12305
 
     * @protected
12306
 
     * @since 5.0.010 (2010-05-17)
12307
 
     * @author Nicola Asuni
12308
 
     */
12309
 
    protected function convertStringToHexString($s) {
12310
 
        $bs = '';
12311
 
        $chars = preg_split('//', $s, -1, PREG_SPLIT_NO_EMPTY);
12312
 
        foreach ($chars as $c) {
12313
 
            $bs .= sprintf('%02s', dechex(ord($c)));
12314
 
        }
12315
 
        return $bs;
12316
 
    }
12317
 
 
12318
 
    /**
12319
 
     * Convert encryption P value to a string of bytes, low-order byte first.
12320
 
     * @param $protection (string) 32bit encryption permission value (P value)
12321
 
     * @return String
12322
 
     * @protected
12323
 
     * @since 5.0.005 (2010-05-12)
12324
 
     * @author Nicola Asuni
12325
 
     */
12326
 
    protected function getEncPermissionsString($protection) {
12327
 
        $binprot = sprintf('%032b', $protection);
12328
 
        $str = chr(bindec(substr($binprot, 24, 8)));
12329
 
        $str .= chr(bindec(substr($binprot, 16, 8)));
12330
 
        $str .= chr(bindec(substr($binprot, 8, 8)));
12331
 
        $str .= chr(bindec(substr($binprot, 0, 8)));
12332
 
        return $str;
12333
 
    }
12334
 
 
12335
 
    // END OF ENCRYPTION FUNCTIONS -------------------------
12336
 
 
12337
 
    // START TRANSFORMATIONS SECTION -----------------------
12338
 
 
12339
 
    /**
12340
 
     * Starts a 2D tranformation saving current graphic state.
12341
 
     * This function must be called before scaling, mirroring, translation, rotation and skewing.
12342
 
     * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
12343
 
     * @public
12344
 
     * @since 2.1.000 (2008-01-07)
12345
 
     * @see StartTransform(), StopTransform()
12346
 
     */
12347
 
    public function StartTransform() {
12348
 
        $this->_out('q');
12349
 
        if ($this->inxobj) {
12350
 
            // we are inside an XObject template
12351
 
            $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']);
12352
 
        } else {
12353
 
            $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
12354
 
        }
12355
 
        ++$this->transfmatrix_key;
12356
 
        $this->transfmatrix[$this->transfmatrix_key] = array();
12357
 
    }
12358
 
 
12359
 
    /**
12360
 
     * Stops a 2D tranformation restoring previous graphic state.
12361
 
     * This function must be called after scaling, mirroring, translation, rotation and skewing.
12362
 
     * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
12363
 
     * @public
12364
 
     * @since 2.1.000 (2008-01-07)
12365
 
     * @see StartTransform(), StopTransform()
12366
 
     */
12367
 
    public function StopTransform() {
12368
 
        $this->_out('Q');
12369
 
        if (isset($this->transfmatrix[$this->transfmatrix_key])) {
12370
 
            array_pop($this->transfmatrix[$this->transfmatrix_key]);
12371
 
            --$this->transfmatrix_key;
12372
 
        }
12373
 
        if ($this->inxobj) {
12374
 
            // we are inside an XObject template
12375
 
            array_pop($this->xobjects[$this->xobjid]['transfmrk']);
12376
 
        } else {
12377
 
            array_pop($this->transfmrk[$this->page]);
12378
 
        }
12379
 
    }
12380
 
    /**
12381
 
     * Horizontal Scaling.
12382
 
     * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
12383
 
     * @param $x (int) abscissa of the scaling center. Default is current x position
12384
 
     * @param $y (int) ordinate of the scaling center. Default is current y position
12385
 
     * @public
12386
 
     * @since 2.1.000 (2008-01-07)
12387
 
     * @see StartTransform(), StopTransform()
12388
 
     */
12389
 
    public function ScaleX($s_x, $x='', $y='') {
12390
 
        $this->Scale($s_x, 100, $x, $y);
12391
 
    }
12392
 
 
12393
 
    /**
12394
 
     * Vertical Scaling.
12395
 
     * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
12396
 
     * @param $x (int) abscissa of the scaling center. Default is current x position
12397
 
     * @param $y (int) ordinate of the scaling center. Default is current y position
12398
 
     * @public
12399
 
     * @since 2.1.000 (2008-01-07)
12400
 
     * @see StartTransform(), StopTransform()
12401
 
     */
12402
 
    public function ScaleY($s_y, $x='', $y='') {
12403
 
        $this->Scale(100, $s_y, $x, $y);
12404
 
    }
12405
 
 
12406
 
    /**
12407
 
     * Vertical and horizontal proportional Scaling.
12408
 
     * @param $s (float) scaling factor for width and height as percent. 0 is not allowed.
12409
 
     * @param $x (int) abscissa of the scaling center. Default is current x position
12410
 
     * @param $y (int) ordinate of the scaling center. Default is current y position
12411
 
     * @public
12412
 
     * @since 2.1.000 (2008-01-07)
12413
 
     * @see StartTransform(), StopTransform()
12414
 
     */
12415
 
    public function ScaleXY($s, $x='', $y='') {
12416
 
        $this->Scale($s, $s, $x, $y);
12417
 
    }
12418
 
 
12419
 
    /**
12420
 
     * Vertical and horizontal non-proportional Scaling.
12421
 
     * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
12422
 
     * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
12423
 
     * @param $x (int) abscissa of the scaling center. Default is current x position
12424
 
     * @param $y (int) ordinate of the scaling center. Default is current y position
12425
 
     * @public
12426
 
     * @since 2.1.000 (2008-01-07)
12427
 
     * @see StartTransform(), StopTransform()
12428
 
     */
12429
 
    public function Scale($s_x, $s_y, $x='', $y='') {
12430
 
        if ($x === '') {
12431
 
            $x = $this->x;
12432
 
        }
12433
 
        if ($y === '') {
12434
 
            $y = $this->y;
12435
 
        }
12436
 
        if (($s_x == 0) OR ($s_y == 0)) {
12437
 
            $this->Error('Please do not use values equal to zero for scaling');
12438
 
        }
12439
 
        $y = ($this->h - $y) * $this->k;
12440
 
        $x *= $this->k;
12441
 
        //calculate elements of transformation matrix
12442
 
        $s_x /= 100;
12443
 
        $s_y /= 100;
12444
 
        $tm = array();
12445
 
        $tm[0] = $s_x;
12446
 
        $tm[1] = 0;
12447
 
        $tm[2] = 0;
12448
 
        $tm[3] = $s_y;
12449
 
        $tm[4] = $x * (1 - $s_x);
12450
 
        $tm[5] = $y * (1 - $s_y);
12451
 
        //scale the coordinate system
12452
 
        $this->Transform($tm);
12453
 
    }
12454
 
 
12455
 
    /**
12456
 
     * Horizontal Mirroring.
12457
 
     * @param $x (int) abscissa of the point. Default is current x position
12458
 
     * @public
12459
 
     * @since 2.1.000 (2008-01-07)
12460
 
     * @see StartTransform(), StopTransform()
12461
 
     */
12462
 
    public function MirrorH($x='') {
12463
 
        $this->Scale(-100, 100, $x);
12464
 
    }
12465
 
 
12466
 
    /**
12467
 
     * Verical Mirroring.
12468
 
     * @param $y (int) ordinate of the point. Default is current y position
12469
 
     * @public
12470
 
     * @since 2.1.000 (2008-01-07)
12471
 
     * @see StartTransform(), StopTransform()
12472
 
     */
12473
 
    public function MirrorV($y='') {
12474
 
        $this->Scale(100, -100, '', $y);
12475
 
    }
12476
 
 
12477
 
    /**
12478
 
     * Point reflection mirroring.
12479
 
     * @param $x (int) abscissa of the point. Default is current x position
12480
 
     * @param $y (int) ordinate of the point. Default is current y position
12481
 
     * @public
12482
 
     * @since 2.1.000 (2008-01-07)
12483
 
     * @see StartTransform(), StopTransform()
12484
 
     */
12485
 
    public function MirrorP($x='',$y='') {
12486
 
        $this->Scale(-100, -100, $x, $y);
12487
 
    }
12488
 
 
12489
 
    /**
12490
 
     * Reflection against a straight line through point (x, y) with the gradient angle (angle).
12491
 
     * @param $angle (float) gradient angle of the straight line. Default is 0 (horizontal line).
12492
 
     * @param $x (int) abscissa of the point. Default is current x position
12493
 
     * @param $y (int) ordinate of the point. Default is current y position
12494
 
     * @public
12495
 
     * @since 2.1.000 (2008-01-07)
12496
 
     * @see StartTransform(), StopTransform()
12497
 
     */
12498
 
    public function MirrorL($angle=0, $x='',$y='') {
12499
 
        $this->Scale(-100, 100, $x, $y);
12500
 
        $this->Rotate(-2*($angle-90), $x, $y);
12501
 
    }
12502
 
 
12503
 
    /**
12504
 
     * Translate graphic object horizontally.
12505
 
     * @param $t_x (int) movement to the right (or left for RTL)
12506
 
     * @public
12507
 
     * @since 2.1.000 (2008-01-07)
12508
 
     * @see StartTransform(), StopTransform()
12509
 
     */
12510
 
    public function TranslateX($t_x) {
12511
 
        $this->Translate($t_x, 0);
12512
 
    }
12513
 
 
12514
 
    /**
12515
 
     * Translate graphic object vertically.
12516
 
     * @param $t_y (int) movement to the bottom
12517
 
     * @public
12518
 
     * @since 2.1.000 (2008-01-07)
12519
 
     * @see StartTransform(), StopTransform()
12520
 
     */
12521
 
    public function TranslateY($t_y) {
12522
 
        $this->Translate(0, $t_y);
12523
 
    }
12524
 
 
12525
 
    /**
12526
 
     * Translate graphic object horizontally and vertically.
12527
 
     * @param $t_x (int) movement to the right
12528
 
     * @param $t_y (int) movement to the bottom
12529
 
     * @public
12530
 
     * @since 2.1.000 (2008-01-07)
12531
 
     * @see StartTransform(), StopTransform()
12532
 
     */
12533
 
    public function Translate($t_x, $t_y) {
12534
 
        //calculate elements of transformation matrix
12535
 
        $tm = array();
12536
 
        $tm[0] = 1;
12537
 
        $tm[1] = 0;
12538
 
        $tm[2] = 0;
12539
 
        $tm[3] = 1;
12540
 
        $tm[4] = $t_x * $this->k;
12541
 
        $tm[5] = -$t_y * $this->k;
12542
 
        //translate the coordinate system
12543
 
        $this->Transform($tm);
12544
 
    }
12545
 
 
12546
 
    /**
12547
 
     * Rotate object.
12548
 
     * @param $angle (float) angle in degrees for counter-clockwise rotation
12549
 
     * @param $x (int) abscissa of the rotation center. Default is current x position
12550
 
     * @param $y (int) ordinate of the rotation center. Default is current y position
12551
 
     * @public
12552
 
     * @since 2.1.000 (2008-01-07)
12553
 
     * @see StartTransform(), StopTransform()
12554
 
     */
12555
 
    public function Rotate($angle, $x='', $y='') {
12556
 
        if ($x === '') {
12557
 
            $x = $this->x;
12558
 
        }
12559
 
        if ($y === '') {
12560
 
            $y = $this->y;
12561
 
        }
12562
 
        $y = ($this->h - $y) * $this->k;
12563
 
        $x *= $this->k;
12564
 
        //calculate elements of transformation matrix
12565
 
        $tm = array();
12566
 
        $tm[0] = cos(deg2rad($angle));
12567
 
        $tm[1] = sin(deg2rad($angle));
12568
 
        $tm[2] = -$tm[1];
12569
 
        $tm[3] = $tm[0];
12570
 
        $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
12571
 
        $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
12572
 
        //rotate the coordinate system around ($x,$y)
12573
 
        $this->Transform($tm);
12574
 
    }
12575
 
 
12576
 
    /**
12577
 
     * Skew horizontally.
12578
 
     * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
12579
 
     * @param $x (int) abscissa of the skewing center. default is current x position
12580
 
     * @param $y (int) ordinate of the skewing center. default is current y position
12581
 
     * @public
12582
 
     * @since 2.1.000 (2008-01-07)
12583
 
     * @see StartTransform(), StopTransform()
12584
 
     */
12585
 
    public function SkewX($angle_x, $x='', $y='') {
12586
 
        $this->Skew($angle_x, 0, $x, $y);
12587
 
    }
12588
 
 
12589
 
    /**
12590
 
     * Skew vertically.
12591
 
     * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
12592
 
     * @param $x (int) abscissa of the skewing center. default is current x position
12593
 
     * @param $y (int) ordinate of the skewing center. default is current y position
12594
 
     * @public
12595
 
     * @since 2.1.000 (2008-01-07)
12596
 
     * @see StartTransform(), StopTransform()
12597
 
     */
12598
 
    public function SkewY($angle_y, $x='', $y='') {
12599
 
        $this->Skew(0, $angle_y, $x, $y);
12600
 
    }
12601
 
 
12602
 
    /**
12603
 
     * Skew.
12604
 
     * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
12605
 
     * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
12606
 
     * @param $x (int) abscissa of the skewing center. default is current x position
12607
 
     * @param $y (int) ordinate of the skewing center. default is current y position
12608
 
     * @public
12609
 
     * @since 2.1.000 (2008-01-07)
12610
 
     * @see StartTransform(), StopTransform()
12611
 
     */
12612
 
    public function Skew($angle_x, $angle_y, $x='', $y='') {
12613
 
        if ($x === '') {
12614
 
            $x = $this->x;
12615
 
        }
12616
 
        if ($y === '') {
12617
 
            $y = $this->y;
12618
 
        }
12619
 
        if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
12620
 
            $this->Error('Please use values between -90 and +90 degrees for Skewing.');
12621
 
        }
12622
 
        $x *= $this->k;
12623
 
        $y = ($this->h - $y) * $this->k;
12624
 
        //calculate elements of transformation matrix
12625
 
        $tm = array();
12626
 
        $tm[0] = 1;
12627
 
        $tm[1] = tan(deg2rad($angle_y));
12628
 
        $tm[2] = tan(deg2rad($angle_x));
12629
 
        $tm[3] = 1;
12630
 
        $tm[4] = -$tm[2] * $y;
12631
 
        $tm[5] = -$tm[1] * $x;
12632
 
        //skew the coordinate system
12633
 
        $this->Transform($tm);
12634
 
    }
12635
 
 
12636
 
    /**
12637
 
     * Apply graphic transformations.
12638
 
     * @param $tm (array) transformation matrix
12639
 
     * @protected
12640
 
     * @since 2.1.000 (2008-01-07)
12641
 
     * @see StartTransform(), StopTransform()
12642
 
     */
12643
 
    protected function Transform($tm) {
12644
 
        $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
12645
 
        // add tranformation matrix
12646
 
        $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
12647
 
        // update transformation mark
12648
 
        if ($this->inxobj) {
12649
 
            // we are inside an XObject template
12650
 
            if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
12651
 
                $key = key($this->xobjects[$this->xobjid]['transfmrk']);
12652
 
                $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']);
12653
 
            }
12654
 
        } elseif (end($this->transfmrk[$this->page]) !== false) {
12655
 
            $key = key($this->transfmrk[$this->page]);
12656
 
            $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
12657
 
        }
12658
 
    }
12659
 
 
12660
 
    // END TRANSFORMATIONS SECTION -------------------------
12661
 
 
12662
 
    // START GRAPHIC FUNCTIONS SECTION ---------------------
12663
 
    // The following section is based on the code provided by David Hernandez Sanz
12664
 
 
12665
 
    /**
12666
 
     * 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.
12667
 
     * @param $width (float) The width.
12668
 
     * @public
12669
 
     * @since 1.0
12670
 
     * @see Line(), Rect(), Cell(), MultiCell()
12671
 
     */
12672
 
    public function SetLineWidth($width) {
12673
 
        //Set line width
12674
 
        $this->LineWidth = $width;
12675
 
        $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k));
12676
 
        if ($this->page > 0) {
12677
 
            $this->_out($this->linestyleWidth);
12678
 
        }
12679
 
    }
12680
 
 
12681
 
    /**
12682
 
     * Returns the current the line width.
12683
 
     * @return int Line width
12684
 
     * @public
12685
 
     * @since 2.1.000 (2008-01-07)
12686
 
     * @see Line(), SetLineWidth()
12687
 
     */
12688
 
    public function GetLineWidth() {
12689
 
        return $this->LineWidth;
12690
 
    }
12691
 
 
12692
 
    /**
12693
 
     * Set line style.
12694
 
     * @param $style (array) Line style. Array with keys among the following:
12695
 
     * <ul>
12696
 
     *     <li>width (float): Width of the line in user units.</li>
12697
 
     *     <li>cap (string): Type of cap to put on the line. Possible values are:
12698
 
     * butt, round, square. The difference between "square" and "butt" is that
12699
 
     * "square" projects a flat end past the end of the line.</li>
12700
 
     *     <li>join (string): Type of join. Possible values are: miter, round,
12701
 
     * bevel.</li>
12702
 
     *     <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
12703
 
     * series of length values, which are the lengths of the on and off dashes.
12704
 
     * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
12705
 
     * 1 off, 2 on, 1 off, ...</li>
12706
 
     *     <li>phase (integer): Modifier on the dash pattern which is used to shift
12707
 
     * the point at which the pattern starts.</li>
12708
 
     *     <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K).</li>
12709
 
     * </ul>
12710
 
     * @param $ret (boolean) if true do not send the command.
12711
 
     * @return string the PDF command
12712
 
     * @public
12713
 
     * @since 2.1.000 (2008-01-08)
12714
 
     */
12715
 
    public function SetLineStyle($style, $ret=false) {
12716
 
        $s = ''; // string to be returned
12717
 
        if (!is_array($style)) {
12718
 
            return;
12719
 
        }
12720
 
        extract($style);
12721
 
        if (isset($width)) {
12722
 
            $this->LineWidth = $width;
12723
 
            $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k));
12724
 
            $s .= $this->linestyleWidth.' ';
12725
 
        }
12726
 
        if (isset($cap)) {
12727
 
            $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
12728
 
            if (isset($ca[$cap])) {
12729
 
                $this->linestyleCap = $ca[$cap].' J';
12730
 
                $s .= $this->linestyleCap.' ';
12731
 
            }
12732
 
        }
12733
 
        if (isset($join)) {
12734
 
            $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
12735
 
            if (isset($ja[$join])) {
12736
 
                $this->linestyleJoin = $ja[$join].' j';
12737
 
                $s .= $this->linestyleJoin.' ';
12738
 
            }
12739
 
        }
12740
 
        if (isset($dash)) {
12741
 
            $dash_string = '';
12742
 
            if ($dash) {
12743
 
                if (preg_match('/^.+,/', $dash) > 0) {
12744
 
                    $tab = explode(',', $dash);
12745
 
                } else {
12746
 
                    $tab = array($dash);
12747
 
                }
12748
 
                $dash_string = '';
12749
 
                foreach ($tab as $i => $v) {
12750
 
                    if ($i) {
12751
 
                        $dash_string .= ' ';
12752
 
                    }
12753
 
                    $dash_string .= sprintf('%.2F', $v);
12754
 
                }
12755
 
            }
12756
 
            if (!isset($phase) OR !$dash) {
12757
 
                $phase = 0;
12758
 
            }
12759
 
            $this->linestyleDash = sprintf('[%s] %.2F d', $dash_string, $phase);
12760
 
            $s .= $this->linestyleDash.' ';
12761
 
        }
12762
 
        if (isset($color)) {
12763
 
            $s .= $this->SetDrawColorArray($color, true).' ';
12764
 
        }
12765
 
        if (!$ret) {
12766
 
            $this->_out($s);
12767
 
        }
12768
 
        return $s;
12769
 
    }
12770
 
 
12771
 
    /**
12772
 
     * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment.
12773
 
     * @param $x (float) Abscissa of point.
12774
 
     * @param $y (float) Ordinate of point.
12775
 
     * @protected
12776
 
     * @since 2.1.000 (2008-01-08)
12777
 
     */
12778
 
    protected function _outPoint($x, $y) {
12779
 
        $this->_out(sprintf('%.2F %.2F m', $x * $this->k, ($this->h - $y) * $this->k));
12780
 
    }
12781
 
 
12782
 
    /**
12783
 
     * Append a straight line segment from the current point to the point (x, y).
12784
 
     * The new current point shall be (x, y).
12785
 
     * @param $x (float) Abscissa of end point.
12786
 
     * @param $y (float) Ordinate of end point.
12787
 
     * @protected
12788
 
     * @since 2.1.000 (2008-01-08)
12789
 
     */
12790
 
    protected function _outLine($x, $y) {
12791
 
        $this->_out(sprintf('%.2F %.2F l', $x * $this->k, ($this->h - $y) * $this->k));
12792
 
    }
12793
 
 
12794
 
    /**
12795
 
     * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space.
12796
 
     * @param $x (float) Abscissa of upper-left corner.
12797
 
     * @param $y (float) Ordinate of upper-left corner.
12798
 
     * @param $w (float) Width.
12799
 
     * @param $h (float) Height.
12800
 
     * @param $op (string) options
12801
 
     * @protected
12802
 
     * @since 2.1.000 (2008-01-08)
12803
 
     */
12804
 
    protected function _outRect($x, $y, $w, $h, $op) {
12805
 
        $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x * $this->k, ($this->h - $y) * $this->k, $w * $this->k, -$h * $this->k, $op));
12806
 
    }
12807
 
 
12808
 
    /**
12809
 
     * 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.
12810
 
     * The new current point shall be (x3, y3).
12811
 
     * @param $x1 (float) Abscissa of control point 1.
12812
 
     * @param $y1 (float) Ordinate of control point 1.
12813
 
     * @param $x2 (float) Abscissa of control point 2.
12814
 
     * @param $y2 (float) Ordinate of control point 2.
12815
 
     * @param $x3 (float) Abscissa of end point.
12816
 
     * @param $y3 (float) Ordinate of end point.
12817
 
     * @protected
12818
 
     * @since 2.1.000 (2008-01-08)
12819
 
     */
12820
 
    protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
12821
 
        $this->_out(sprintf('%.2F %.2F %.2F %.2F %.2F %.2F c', $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
12822
 
    }
12823
 
 
12824
 
    /**
12825
 
     * 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.
12826
 
     * The new current point shall be (x3, y3).
12827
 
     * @param $x2 (float) Abscissa of control point 2.
12828
 
     * @param $y2 (float) Ordinate of control point 2.
12829
 
     * @param $x3 (float) Abscissa of end point.
12830
 
     * @param $y3 (float) Ordinate of end point.
12831
 
     * @protected
12832
 
     * @since 4.9.019 (2010-04-26)
12833
 
     */
12834
 
    protected function _outCurveV($x2, $y2, $x3, $y3) {
12835
 
        $this->_out(sprintf('%.2F %.2F %.2F %.2F v', $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
12836
 
    }
12837
 
 
12838
 
    /**
12839
 
     * 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.
12840
 
     * The new current point shall be (x3, y3).
12841
 
     * @param $x1 (float) Abscissa of control point 1.
12842
 
     * @param $y1 (float) Ordinate of control point 1.
12843
 
     * @param $x3 (float) Abscissa of end point.
12844
 
     * @param $y3 (float) Ordinate of end point.
12845
 
     * @protected
12846
 
     * @since 2.1.000 (2008-01-08)
12847
 
     */
12848
 
    protected function _outCurveY($x1, $y1, $x3, $y3) {
12849
 
        $this->_out(sprintf('%.2F %.2F %.2F %.2F y', $x1 * $this->k, ($this->h - $y1) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
12850
 
    }
12851
 
 
12852
 
    /**
12853
 
     * Draws a line between two points.
12854
 
     * @param $x1 (float) Abscissa of first point.
12855
 
     * @param $y1 (float) Ordinate of first point.
12856
 
     * @param $x2 (float) Abscissa of second point.
12857
 
     * @param $y2 (float) Ordinate of second point.
12858
 
     * @param $style (array) Line style. Array like for SetLineStyle(). Default value: default line style (empty array).
12859
 
     * @public
12860
 
     * @since 1.0
12861
 
     * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
12862
 
     */
12863
 
    public function Line($x1, $y1, $x2, $y2, $style=array()) {
12864
 
        if (is_array($style)) {
12865
 
            $this->SetLineStyle($style);
12866
 
        }
12867
 
        $this->_outPoint($x1, $y1);
12868
 
        $this->_outLine($x2, $y2);
12869
 
        $this->_out('S');
12870
 
    }
12871
 
 
12872
 
    /**
12873
 
     * Draws a rectangle.
12874
 
     * @param $x (float) Abscissa of upper-left corner.
12875
 
     * @param $y (float) Ordinate of upper-left corner.
12876
 
     * @param $w (float) Width.
12877
 
     * @param $h (float) Height.
12878
 
     * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12879
 
     * @param $border_style (array) Border style of rectangle. Array with keys among the following:
12880
 
     * <ul>
12881
 
     *     <li>all: Line style of all borders. Array like for SetLineStyle().</li>
12882
 
     *     <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for SetLineStyle().</li>
12883
 
     * </ul>
12884
 
     * If a key is not present or is null, not draws the border. Default value: default line style (empty array).
12885
 
     * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
12886
 
     * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
12887
 
     * @public
12888
 
     * @since 1.0
12889
 
     * @see SetLineStyle()
12890
 
     */
12891
 
    public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
12892
 
        if (!(false === strpos($style, 'F')) AND !empty($fill_color)) {
12893
 
            $this->SetFillColorArray($fill_color);
12894
 
        }
12895
 
        $op = $this->getPathPaintOperator($style);
12896
 
        if ((!$border_style) OR (isset($border_style['all']))) {
12897
 
            if (isset($border_style['all']) AND $border_style['all']) {
12898
 
                $this->SetLineStyle($border_style['all']);
12899
 
                $border_style = array();
12900
 
            }
12901
 
        }
12902
 
        $this->_outRect($x, $y, $w, $h, $op);
12903
 
        if ($border_style) {
12904
 
            $border_style2 = array();
12905
 
            foreach ($border_style as $line => $value) {
12906
 
                $length = strlen($line);
12907
 
                for ($i = 0; $i < $length; ++$i) {
12908
 
                    $border_style2[$line[$i]] = $value;
12909
 
                }
12910
 
            }
12911
 
            $border_style = $border_style2;
12912
 
            if (isset($border_style['L']) AND $border_style['L']) {
12913
 
                $this->Line($x, $y, $x, $y + $h, $border_style['L']);
12914
 
            }
12915
 
            if (isset($border_style['T']) AND $border_style['T']) {
12916
 
                $this->Line($x, $y, $x + $w, $y, $border_style['T']);
12917
 
            }
12918
 
            if (isset($border_style['R']) AND $border_style['R']) {
12919
 
                $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
12920
 
            }
12921
 
            if (isset($border_style['B']) AND $border_style['B']) {
12922
 
                $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
12923
 
            }
12924
 
        }
12925
 
    }
12926
 
 
12927
 
    /**
12928
 
     * Draws a Bezier curve.
12929
 
     * The Bezier curve is a tangent to the line between the control points at
12930
 
     * either end of the curve.
12931
 
     * @param $x0 (float) Abscissa of start point.
12932
 
     * @param $y0 (float) Ordinate of start point.
12933
 
     * @param $x1 (float) Abscissa of control point 1.
12934
 
     * @param $y1 (float) Ordinate of control point 1.
12935
 
     * @param $x2 (float) Abscissa of control point 2.
12936
 
     * @param $y2 (float) Ordinate of control point 2.
12937
 
     * @param $x3 (float) Abscissa of end point.
12938
 
     * @param $y3 (float) Ordinate of end point.
12939
 
     * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12940
 
     * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
12941
 
     * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
12942
 
     * @public
12943
 
     * @see SetLineStyle()
12944
 
     * @since 2.1.000 (2008-01-08)
12945
 
     */
12946
 
    public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
12947
 
        if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12948
 
            $this->SetFillColorArray($fill_color);
12949
 
        }
12950
 
        $op = $this->getPathPaintOperator($style);
12951
 
        if ($line_style) {
12952
 
            $this->SetLineStyle($line_style);
12953
 
        }
12954
 
        $this->_outPoint($x0, $y0);
12955
 
        $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
12956
 
        $this->_out($op);
12957
 
    }
12958
 
 
12959
 
    /**
12960
 
     * Draws a poly-Bezier curve.
12961
 
     * Each Bezier curve segment is a tangent to the line between the control points at
12962
 
     * either end of the curve.
12963
 
     * @param $x0 (float) Abscissa of start point.
12964
 
     * @param $y0 (float) Ordinate of start point.
12965
 
     * @param $segments (float) An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
12966
 
     * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12967
 
     * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
12968
 
     * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
12969
 
     * @public
12970
 
     * @see SetLineStyle()
12971
 
     * @since 3.0008 (2008-05-12)
12972
 
     */
12973
 
    public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
12974
 
        if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12975
 
            $this->SetFillColorArray($fill_color);
12976
 
        }
12977
 
        $op = $this->getPathPaintOperator($style);
12978
 
        if ($op == 'f') {
12979
 
            $line_style = array();
12980
 
        }
12981
 
        if ($line_style) {
12982
 
            $this->SetLineStyle($line_style);
12983
 
        }
12984
 
        $this->_outPoint($x0, $y0);
12985
 
        foreach ($segments as $segment) {
12986
 
            list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
12987
 
            $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
12988
 
        }
12989
 
        $this->_out($op);
12990
 
    }
12991
 
 
12992
 
    /**
12993
 
     * Draws an ellipse.
12994
 
     * An ellipse is formed from n Bezier curves.
12995
 
     * @param $x0 (float) Abscissa of center point.
12996
 
     * @param $y0 (float) Ordinate of center point.
12997
 
     * @param $rx (float) Horizontal radius.
12998
 
     * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
12999
 
     * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
13000
 
     * @param $astart: (float) Angle start of draw line. Default value: 0.
13001
 
     * @param $afinish: (float) Angle finish of draw line. Default value: 360.
13002
 
     * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
13003
 
     * @param $line_style (array) Line style of ellipse. Array like for SetLineStyle(). Default value: default line style (empty array).
13004
 
     * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
13005
 
     * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
13006
 
     * @author Nicola Asuni
13007
 
     * @public
13008
 
     * @since 2.1.000 (2008-01-08)
13009
 
     */
13010
 
    public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
13011
 
        if ($this->empty_string($ry) OR ($ry == 0)) {
13012
 
            $ry = $rx;
13013
 
        }
13014
 
        if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
13015
 
            $this->SetFillColorArray($fill_color);
13016
 
        }
13017
 
        $op = $this->getPathPaintOperator($style);
13018
 
        if ($op == 'f') {
13019
 
            $line_style = array();
13020
 
        }
13021
 
        if ($line_style) {
13022
 
            $this->SetLineStyle($line_style);
13023
 
        }
13024
 
        $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc);
13025
 
        $this->_out($op);
13026
 
    }
13027
 
 
13028
 
    /**
13029
 
     * Append an elliptical arc to the current path.
13030
 
     * An ellipse is formed from n Bezier curves.
13031
 
     * @param $xc (float) Abscissa of center point.
13032
 
     * @param $yc (float) Ordinate of center point.
13033
 
     * @param $rx (float) Horizontal radius.
13034
 
     * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
13035
 
     * @param $xang: (float) Angle between the X-axis and the major axis of the ellipse. Default value: 0.
13036
 
     * @param $angs: (float) Angle start of draw line. Default value: 0.
13037
 
     * @param $angf: (float) Angle finish of draw line. Default value: 360.
13038
 
     * @param $pie (boolean) if true do not mark the border point (used to draw pie sectors).
13039
 
     * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
13040
 
     * @param $startpoint (boolean) if true output a starting point
13041
 
     * @param $ccw (boolean) if true draws in counter-clockwise
13042
 
     * @return array bounding box coordinates (x min, y min, x max, y max)
13043
 
     * @author Nicola Asuni
13044
 
     * @protected
13045
 
     * @since 4.9.019 (2010-04-26)
13046
 
     */
13047
 
    protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true) {
13048
 
        $k = $this->k;
13049
 
        if ($nc < 2) {
13050
 
            $nc = 2;
13051
 
        }
13052
 
        $xmin = 2147483647;
13053
 
        $ymin = 2147483647;
13054
 
        $xmax = 0;
13055
 
        $ymax = 0;
13056
 
        if ($pie) {
13057
 
            // center of the arc
13058
 
            $this->_outPoint($xc, $yc);
13059
 
        }
13060
 
        $xang = deg2rad((float) $xang);
13061
 
        $angs = deg2rad((float) $angs);
13062
 
        $angf = deg2rad((float) $angf);
13063
 
        $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
13064
 
        $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
13065
 
        if ($as < 0) {
13066
 
            $as += (2 * M_PI);
13067
 
        }
13068
 
        if ($af < 0) {
13069
 
            $af += (2 * M_PI);
13070
 
        }
13071
 
        if ($ccw AND ($as > $af)) {
13072
 
            // reverse rotation
13073
 
            $as -= (2 * M_PI);
13074
 
        } elseif (!$ccw AND ($as < $af)) {
13075
 
            // reverse rotation
13076
 
            $af -= (2 * M_PI);
13077
 
        }
13078
 
        $total_angle = ($af - $as);
13079
 
        if ($nc < 2) {
13080
 
            $nc = 2;
13081
 
        }
13082
 
        // total arcs to draw
13083
 
        $nc *= (2 * abs($total_angle) / M_PI);
13084
 
        $nc = round($nc) + 1;
13085
 
        // angle of each arc
13086
 
        $arcang = $total_angle / $nc;
13087
 
        // center point in PDF coordiantes
13088
 
        $x0 = $xc;
13089
 
        $y0 = ($this->h - $yc);
13090
 
        // starting angle
13091
 
        $ang = $as;
13092
 
        $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
13093
 
        $cos_xang = cos($xang);
13094
 
        $sin_xang = sin($xang);
13095
 
        $cos_ang = cos($ang);
13096
 
        $sin_ang = sin($ang);
13097
 
        // first arc point
13098
 
        $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
13099
 
        $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
13100
 
        // first Bezier control point
13101
 
        $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
13102
 
        $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
13103
 
        if ($pie) {
13104
 
            // line from center to arc starting point
13105
 
            $this->_outLine($px1, $this->h - $py1);
13106
 
        } elseif ($startpoint) {
13107
 
            // arc starting point
13108
 
            $this->_outPoint($px1, $this->h - $py1);
13109
 
        }
13110
 
        // draw arcs
13111
 
        for ($i = 1; $i <= $nc; ++$i) {
13112
 
            // starting angle
13113
 
            $ang = $as + ($i * $arcang);
13114
 
            $cos_xang = cos($xang);
13115
 
            $sin_xang = sin($xang);
13116
 
            $cos_ang = cos($ang);
13117
 
            $sin_ang = sin($ang);
13118
 
            // second arc point
13119
 
            $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
13120
 
            $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
13121
 
            // second Bezier control point
13122
 
            $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
13123
 
            $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
13124
 
            // draw arc
13125
 
            $cx1 = ($px1 + $qx1);
13126
 
            $cy1 = ($this->h - ($py1 + $qy1));
13127
 
            $cx2 = ($px2 - $qx2);
13128
 
            $cy2 = ($this->h - ($py2 - $qy2));
13129
 
            $cx3 = $px2;
13130
 
            $cy3 = ($this->h - $py2);
13131
 
            $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
13132
 
            // get bounding box coordinates
13133
 
            $xmin = min($xmin, $cx1, $cx2, $cx3);
13134
 
            $ymin = min($ymin, $cy1, $cy2, $cy3);
13135
 
            $xmax = max($xmax, $cx1, $cx2, $cx3);
13136
 
            $ymax = max($ymax, $cy1, $cy2, $cy3);
13137
 
            // move to next point
13138
 
            $px1 = $px2;
13139
 
            $py1 = $py2;
13140
 
            $qx1 = $qx2;
13141
 
            $qy1 = $qy2;
13142
 
        }
13143
 
        if ($pie) {
13144
 
            $this->_outLine($xc, $yc);
13145
 
            // get bounding box coordinates
13146
 
            $xmin = min($xmin, $xc);
13147
 
            $ymin = min($ymin, $yc);
13148
 
            $xmax = max($xmax, $xc);
13149
 
            $ymax = max($ymax, $yc);
13150
 
        }
13151
 
        return array($xmin, $ymin, $xmax, $ymax);
13152
 
    }
13153
 
 
13154
 
    /**
13155
 
     * Draws a circle.
13156
 
     * A circle is formed from n Bezier curves.
13157
 
     * @param $x0 (float) Abscissa of center point.
13158
 
     * @param $y0 (float) Ordinate of center point.
13159
 
     * @param $r (float) Radius.
13160
 
     * @param $angstr: (float) Angle start of draw line. Default value: 0.
13161
 
     * @param $angend: (float) Angle finish of draw line. Default value: 360.
13162
 
     * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
13163
 
     * @param $line_style (array) Line style of circle. Array like for SetLineStyle(). Default value: default line style (empty array).
13164
 
     * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
13165
 
     * @param $nc (integer) Number of curves used to draw a 90 degrees portion of circle.
13166
 
     * @public
13167
 
     * @since 2.1.000 (2008-01-08)
13168
 
     */
13169
 
    public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
13170
 
        $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
13171
 
    }
13172
 
 
13173
 
    /**
13174
 
     * Draws a polygonal line
13175
 
     * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
13176
 
     * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
13177
 
     * @param $line_style (array) Line style of polygon. Array with keys among the following:
13178
 
     * <ul>
13179
 
     *     <li>all: Line style of all lines. Array like for SetLineStyle().</li>
13180
 
     *     <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
13181
 
     * </ul>
13182
 
     * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
13183
 
     * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
13184
 
     * @since 4.8.003 (2009-09-15)
13185
 
     * @public
13186
 
     */
13187
 
    public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
13188
 
        $this->Polygon($p, $style, $line_style, $fill_color, false);
13189
 
    }
13190
 
 
13191
 
    /**
13192
 
     * Draws a polygon.
13193
 
     * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
13194
 
     * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
13195
 
     * @param $line_style (array) Line style of polygon. Array with keys among the following:
13196
 
     * <ul>
13197
 
     *     <li>all: Line style of all lines. Array like for SetLineStyle().</li>
13198
 
     *     <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
13199
 
     * </ul>
13200
 
     * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
13201
 
     * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
13202
 
     * @param $closed (boolean) if true the polygon is closes, otherwise will remain open
13203
 
     * @public
13204
 
     * @since 2.1.000 (2008-01-08)
13205
 
     */
13206
 
    public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
13207
 
        $nc = count($p); // number of coordinates
13208
 
        $np = $nc / 2; // number of points
13209
 
        if ($closed) {
13210
 
            // close polygon by adding the first 2 points at the end (one line)
13211
 
            for ($i = 0; $i < 4; ++$i) {
13212
 
                $p[$nc + $i] = $p[$i];
13213
 
            }
13214
 
            // copy style for the last added line
13215
 
            if (isset($line_style[0])) {
13216
 
                $line_style[$np] = $line_style[0];
13217
 
            }
13218
 
            $nc += 4;
13219
 
        }
13220
 
        if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
13221
 
            $this->SetFillColorArray($fill_color);
13222
 
        }
13223
 
        $op = $this->getPathPaintOperator($style);
13224
 
        if ($op == 'f') {
13225
 
            $line_style = array();
13226
 
        }
13227
 
        $draw = true;
13228
 
        if ($line_style) {
13229
 
            if (isset($line_style['all'])) {
13230
 
                $this->SetLineStyle($line_style['all']);
13231
 
            } else {
13232
 
                $draw = false;
13233
 
                if ($op == 'B') {
13234
 
                    // draw fill
13235
 
                    $op = 'f';
13236
 
                    $this->_outPoint($p[0], $p[1]);
13237
 
                    for ($i = 2; $i < $nc; $i = $i + 2) {
13238
 
                        $this->_outLine($p[$i], $p[$i + 1]);
13239
 
                    }
13240
 
                    $this->_out($op);
13241
 
                }
13242
 
                // draw outline
13243
 
                $this->_outPoint($p[0], $p[1]);
13244
 
                for ($i = 2; $i < $nc; $i = $i + 2) {
13245
 
                    $line_num = ($i / 2) - 1;
13246
 
                    if (isset($line_style[$line_num])) {
13247
 
                        if ($line_style[$line_num] != 0) {
13248
 
                            if (is_array($line_style[$line_num])) {
13249
 
                                $this->_out('S');
13250
 
                                $this->SetLineStyle($line_style[$line_num]);
13251
 
                                $this->_outPoint($p[$i - 2], $p[$i - 1]);
13252
 
                                $this->_outLine($p[$i], $p[$i + 1]);
13253
 
                                $this->_out('S');
13254
 
                                $this->_outPoint($p[$i], $p[$i + 1]);
13255
 
                            } else {
13256
 
                                $this->_outLine($p[$i], $p[$i + 1]);
13257
 
                            }
13258
 
                        }
13259
 
                    } else {
13260
 
                        $this->_outLine($p[$i], $p[$i + 1]);
13261
 
                    }
13262
 
                }
13263
 
                $this->_out($op);
13264
 
            }
13265
 
        }
13266
 
        if ($draw) {
13267
 
            $this->_outPoint($p[0], $p[1]);
13268
 
            for ($i = 2; $i < $nc; $i = $i + 2) {
13269
 
                $this->_outLine($p[$i], $p[$i + 1]);
13270
 
            }
13271
 
            $this->_out($op);
13272
 
        }
13273
 
    }
13274
 
 
13275
 
    /**
13276
 
     * Draws a regular polygon.
13277
 
     * @param $x0 (float) Abscissa of center point.
13278
 
     * @param $y0 (float) Ordinate of center point.
13279
 
     * @param $r: (float) Radius of inscribed circle.
13280
 
     * @param $ns (integer) Number of sides.
13281
 
     * @param $angle (float) Angle oriented (anti-clockwise). Default value: 0.
13282
 
     * @param $draw_circle (boolean) Draw inscribed circle or not. Default value: false.
13283
 
     * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
13284
 
     * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
13285
 
     * <ul>
13286
 
     *     <li>all: Line style of all sides. Array like for SetLineStyle().</li>
13287
 
     *     <li>0 to ($ns - 1): Line style of each side. Array like for SetLineStyle().</li>
13288
 
     * </ul>
13289
 
     * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
13290
 
     * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
13291
 
     * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
13292
 
     * <ul>
13293
 
     *     <li>D or empty string: Draw (default).</li>
13294
 
     *     <li>F: Fill.</li>
13295
 
     *     <li>DF or FD: Draw and fill.</li>
13296
 
     *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
13297
 
     *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
13298
 
     * </ul>
13299
 
     * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
13300
 
     * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
13301
 
     * @public
13302
 
     * @since 2.1.000 (2008-01-08)
13303
 
     */
13304
 
    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()) {
13305
 
        if (3 > $ns) {
13306
 
            $ns = 3;
13307
 
        }
13308
 
        if ($draw_circle) {
13309
 
            $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
13310
 
        }
13311
 
        $p = array();
13312
 
        for ($i = 0; $i < $ns; ++$i) {
13313
 
            $a = $angle + ($i * 360 / $ns);
13314
 
            $a_rad = deg2rad((float) $a);
13315
 
            $p[] = $x0 + ($r * sin($a_rad));
13316
 
            $p[] = $y0 + ($r * cos($a_rad));
13317
 
        }
13318
 
        $this->Polygon($p, $style, $line_style, $fill_color);
13319
 
    }
13320
 
 
13321
 
    /**
13322
 
     * Draws a star polygon
13323
 
     * @param $x0 (float) Abscissa of center point.
13324
 
     * @param $y0 (float) Ordinate of center point.
13325
 
     * @param $r (float) Radius of inscribed circle.
13326
 
     * @param $nv (integer) Number of vertices.
13327
 
     * @param $ng (integer) Number of gap (if ($ng % $nv = 1) then is a regular polygon).
13328
 
     * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
13329
 
     * @param $draw_circle: (boolean) Draw inscribed circle or not. Default value is false.
13330
 
     * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
13331
 
     * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
13332
 
     * <ul>
13333
 
     *     <li>all: Line style of all sides. Array like for
13334
 
     * SetLineStyle().</li>
13335
 
     *     <li>0 to (n - 1): Line style of each side. Array like for SetLineStyle().</li>
13336
 
     * </ul>
13337
 
     * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
13338
 
     * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
13339
 
     * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
13340
 
     * <ul>
13341
 
     *     <li>D or empty string: Draw (default).</li>
13342
 
     *     <li>F: Fill.</li>
13343
 
     *     <li>DF or FD: Draw and fill.</li>
13344
 
     *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
13345
 
     *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
13346
 
     * </ul>
13347
 
     * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
13348
 
     * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
13349
 
     * @public
13350
 
     * @since 2.1.000 (2008-01-08)
13351
 
     */
13352
 
    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()) {
13353
 
        if ($nv < 2) {
13354
 
            $nv = 2;
13355
 
        }
13356
 
        if ($draw_circle) {
13357
 
            $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
13358
 
        }
13359
 
        $p2 = array();
13360
 
        $visited = array();
13361
 
        for ($i = 0; $i < $nv; ++$i) {
13362
 
            $a = $angle + ($i * 360 / $nv);
13363
 
            $a_rad = deg2rad((float) $a);
13364
 
            $p2[] = $x0 + ($r * sin($a_rad));
13365
 
            $p2[] = $y0 + ($r * cos($a_rad));
13366
 
            $visited[] = false;
13367
 
        }
13368
 
        $p = array();
13369
 
        $i = 0;
13370
 
        do {
13371
 
            $p[] = $p2[$i * 2];
13372
 
            $p[] = $p2[($i * 2) + 1];
13373
 
            $visited[$i] = true;
13374
 
            $i += $ng;
13375
 
            $i %= $nv;
13376
 
        } while (!$visited[$i]);
13377
 
        $this->Polygon($p, $style, $line_style, $fill_color);
13378
 
    }
13379
 
 
13380
 
    /**
13381
 
     * Draws a rounded rectangle.
13382
 
     * @param $x (float) Abscissa of upper-left corner.
13383
 
     * @param $y (float) Ordinate of upper-left corner.
13384
 
     * @param $w (float) Width.
13385
 
     * @param $h (float) Height.
13386
 
     * @param $r (float) the radius of the circle used to round off the corners of the rectangle.
13387
 
     * @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 left, top right, bottom right and bottom left. Default value: all rounded corner ("1111").
13388
 
     * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
13389
 
     * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
13390
 
     * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
13391
 
     * @public
13392
 
     * @since 2.1.000 (2008-01-08)
13393
 
     */
13394
 
    public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
13395
 
        $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
13396
 
    }
13397
 
 
13398
 
    /**
13399
 
     * Draws a rounded rectangle.
13400
 
     * @param $x (float) Abscissa of upper-left corner.
13401
 
     * @param $y (float) Ordinate of upper-left corner.
13402
 
     * @param $w (float) Width.
13403
 
     * @param $h (float) Height.
13404
 
     * @param $rx (float) the x-axis radius of the ellipse used to round off the corners of the rectangle.
13405
 
     * @param $ry (float) the y-axis radius of the ellipse used to round off the corners of the rectangle.
13406
 
     * @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 left, top right, bottom right and bottom left. Default value: all rounded corner ("1111").
13407
 
     * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
13408
 
     * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
13409
 
     * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
13410
 
     * @public
13411
 
     * @since 4.9.019 (2010-04-22)
13412
 
     */
13413
 
    public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
13414
 
        if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
13415
 
            // Not rounded
13416
 
            $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
13417
 
            return;
13418
 
        }
13419
 
        // Rounded
13420
 
        if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
13421
 
            $this->SetFillColorArray($fill_color);
13422
 
        }
13423
 
        $op = $this->getPathPaintOperator($style);
13424
 
        if ($op == 'f') {
13425
 
            $border_style = array();
13426
 
        }
13427
 
        if ($border_style) {
13428
 
            $this->SetLineStyle($border_style);
13429
 
        }
13430
 
        $MyArc = 4 / 3 * (sqrt(2) - 1);
13431
 
        $this->_outPoint($x + $rx, $y);
13432
 
        $xc = $x + $w - $rx;
13433
 
        $yc = $y + $ry;
13434
 
        $this->_outLine($xc, $y);
13435
 
        if ($round_corner[0]) {
13436
 
            $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
13437
 
        } else {
13438
 
            $this->_outLine($x + $w, $y);
13439
 
        }
13440
 
        $xc = $x + $w - $rx;
13441
 
        $yc = $y + $h - $ry;
13442
 
        $this->_outLine($x + $w, $yc);
13443
 
        if ($round_corner[1]) {
13444
 
            $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
13445
 
        } else {
13446
 
            $this->_outLine($x + $w, $y + $h);
13447
 
        }
13448
 
        $xc = $x + $rx;
13449
 
        $yc = $y + $h - $ry;
13450
 
        $this->_outLine($xc, $y + $h);
13451
 
        if ($round_corner[2]) {
13452
 
            $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
13453
 
        } else {
13454
 
            $this->_outLine($x, $y + $h);
13455
 
        }
13456
 
        $xc = $x + $rx;
13457
 
        $yc = $y + $ry;
13458
 
        $this->_outLine($x, $yc);
13459
 
        if ($round_corner[3]) {
13460
 
            $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
13461
 
        } else {
13462
 
            $this->_outLine($x, $y);
13463
 
            $this->_outLine($x + $rx, $y);
13464
 
        }
13465
 
        $this->_out($op);
13466
 
    }
13467
 
 
13468
 
    /**
13469
 
     * Draws a grahic arrow.
13470
 
     * @param $x0 (float) Abscissa of first point.
13471
 
     * @param $y0 (float) Ordinate of first point.
13472
 
     * @param $x1 (float) Abscissa of second point.
13473
 
     * @param $y1 (float) Ordinate of second point.
13474
 
     * @param $head_style (int) (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead)
13475
 
     * @param $arm_size (float) length of arrowhead arms
13476
 
     * @param $arm_angle (int) angle between an arm and the shaft
13477
 
     * @author Piotr Galecki, Nicola Asuni, Andy Meier
13478
 
     * @since 4.6.018 (2009-07-10)
13479
 
     */
13480
 
    public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
13481
 
        // getting arrow direction angle
13482
 
        // 0 deg angle is when both arms go along X axis. angle grows clockwise.
13483
 
        $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
13484
 
        if ($dir_angle < 0) {
13485
 
            $dir_angle += (2 * M_PI);
13486
 
        }
13487
 
        $arm_angle = deg2rad($arm_angle);
13488
 
        $sx1 = $x1;
13489
 
        $sy1 = $y1;
13490
 
        if ($head_style > 0) {
13491
 
            // calculate the stopping point for the arrow shaft
13492
 
            $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
13493
 
            $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
13494
 
        }
13495
 
        // main arrow line / shaft
13496
 
        $this->Line($x0, $y0, $sx1, $sy1);
13497
 
        // left arrowhead arm tip
13498
 
        $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
13499
 
        $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
13500
 
        // right arrowhead arm tip
13501
 
        $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
13502
 
        $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
13503
 
        $mode = 'D';
13504
 
        $style = array();
13505
 
        switch ($head_style) {
13506
 
            case 0: {
13507
 
                // draw only arrowhead arms
13508
 
                $mode = 'D';
13509
 
                $style = array(1, 1, 0);
13510
 
                break;
13511
 
            }
13512
 
            case 1: {
13513
 
                // draw closed arrowhead, but no fill
13514
 
                $mode = 'D';
13515
 
                break;
13516
 
            }
13517
 
            case 2: {
13518
 
                // closed and filled arrowhead
13519
 
                $mode = 'DF';
13520
 
                break;
13521
 
            }
13522
 
            case 3: {
13523
 
                // filled arrowhead
13524
 
                $mode = 'F';
13525
 
                break;
13526
 
            }
13527
 
        }
13528
 
        $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
13529
 
    }
13530
 
 
13531
 
    // END GRAPHIC FUNCTIONS SECTION -----------------------
13532
 
 
13533
 
    // BIDIRECTIONAL TEXT SECTION --------------------------
13534
 
 
13535
 
    /**
13536
 
     * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
13537
 
     * @param $str (string) string to manipulate.
13538
 
     * @param $setbom (bool) if true set the Byte Order Mark (BOM = 0xFEFF)
13539
 
     * @param $forcertl (bool) if true forces RTL text direction
13540
 
     * @return string
13541
 
     * @protected
13542
 
     * @author Nicola Asuni
13543
 
     * @since 2.1.000 (2008-01-08)
13544
 
     */
13545
 
    protected function utf8StrRev($str, $setbom=false, $forcertl=false) {
13546
 
        return $this->utf8StrArrRev($this->UTF8StringToArray($str), $str, $setbom, $forcertl);
13547
 
    }
13548
 
 
13549
 
    /**
13550
 
     * Reverse the RLT substrings array using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
13551
 
     * @param $arr (array) array of unicode values.
13552
 
     * @param $str (string) string to manipulate (or empty value).
13553
 
     * @param $setbom (bool) if true set the Byte Order Mark (BOM = 0xFEFF)
13554
 
     * @param $forcertl (bool) if true forces RTL text direction
13555
 
     * @return string
13556
 
     * @protected
13557
 
     * @author Nicola Asuni
13558
 
     * @since 4.9.000 (2010-03-27)
13559
 
     */
13560
 
    protected function utf8StrArrRev($arr, $str='', $setbom=false, $forcertl=false) {
13561
 
        return $this->arrUTF8ToUTF16BE($this->utf8Bidi($arr, $str, $forcertl), $setbom);
13562
 
    }
13563
 
 
13564
 
    /**
13565
 
     * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
13566
 
     * @param $ta (array) array of characters composing the string.
13567
 
     * @param $str (string) string to process
13568
 
     * @param $forcertl (bool) if 'R' forces RTL, if 'L' forces LTR
13569
 
     * @return array of unicode chars
13570
 
     * @author Nicola Asuni
13571
 
     * @protected
13572
 
     * @since 2.4.000 (2008-03-06)
13573
 
     */
13574
 
    protected function utf8Bidi($ta, $str='', $forcertl=false) {
13575
 
        // paragraph embedding level
13576
 
        $pel = 0;
13577
 
        // max level
13578
 
        $maxlevel = 0;
13579
 
        if ($this->empty_string($str)) {
13580
 
            // create string from array
13581
 
            $str = $this->UTF8ArrSubString($ta);
13582
 
        }
13583
 
        // check if string contains arabic text
13584
 
        if (preg_match($this->unicode->uni_RE_PATTERN_ARABIC, $str)) {
13585
 
            $arabic = true;
13586
 
        } else {
13587
 
            $arabic = false;
13588
 
        }
13589
 
        // check if string contains RTL text
13590
 
        if (!($forcertl OR $arabic OR preg_match($this->unicode->uni_RE_PATTERN_RTL, $str))) {
13591
 
            return $ta;
13592
 
        }
13593
 
 
13594
 
        // get number of chars
13595
 
        $numchars = count($ta);
13596
 
 
13597
 
        if ($forcertl == 'R') {
13598
 
            $pel = 1;
13599
 
        } elseif ($forcertl == 'L') {
13600
 
            $pel = 0;
13601
 
        } else {
13602
 
            // P2. In each paragraph, find the first character of type L, AL, or R.
13603
 
            // P3. If a character is found in P2 and it is of type AL or R, then set the paragraph embedding level to one; otherwise, set it to zero.
13604
 
            for ($i=0; $i < $numchars; ++$i) {
13605
 
                $type = $this->unicode->uni_type[$ta[$i]];
13606
 
                if ($type == 'L') {
13607
 
                    $pel = 0;
13608
 
                    break;
13609
 
                } elseif (($type == 'AL') OR ($type == 'R')) {
13610
 
                    $pel = 1;
13611
 
                    break;
13612
 
                }
13613
 
            }
13614
 
        }
13615
 
 
13616
 
        // Current Embedding Level
13617
 
        $cel = $pel;
13618
 
        // directional override status
13619
 
        $dos = 'N';
13620
 
        $remember = array();
13621
 
        // start-of-level-run
13622
 
        $sor = $pel % 2 ? 'R' : 'L';
13623
 
        $eor = $sor;
13624
 
 
13625
 
        // Array of characters data
13626
 
        $chardata = Array();
13627
 
 
13628
 
        // X1. Begin by setting the current embedding level to the paragraph embedding level. Set the directional override status to neutral. Process each character iteratively, applying rules X2 through X9. Only embedding levels from 0 to 61 are valid in this phase.
13629
 
        // In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
13630
 
        for ($i=0; $i < $numchars; ++$i) {
13631
 
            if ($ta[$i] == $this->unicode->uni_RLE) {
13632
 
                // X2. With each RLE, compute the least greater odd embedding level.
13633
 
                //    a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
13634
 
                //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
13635
 
                $next_level = $cel + ($cel % 2) + 1;
13636
 
                if ($next_level < 62) {
13637
 
                    $remember[] = array('num' => $this->unicode->uni_RLE, 'cel' => $cel, 'dos' => $dos);
13638
 
                    $cel = $next_level;
13639
 
                    $dos = 'N';
13640
 
                    $sor = $eor;
13641
 
                    $eor = $cel % 2 ? 'R' : 'L';
13642
 
                }
13643
 
            } elseif ($ta[$i] == $this->unicode->uni_LRE) {
13644
 
                // X3. With each LRE, compute the least greater even embedding level.
13645
 
                //    a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
13646
 
                //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
13647
 
                $next_level = $cel + 2 - ($cel % 2);
13648
 
                if ( $next_level < 62 ) {
13649
 
                    $remember[] = array('num' => $this->unicode->uni_LRE, 'cel' => $cel, 'dos' => $dos);
13650
 
                    $cel = $next_level;
13651
 
                    $dos = 'N';
13652
 
                    $sor = $eor;
13653
 
                    $eor = $cel % 2 ? 'R' : 'L';
13654
 
                }
13655
 
            } elseif ($ta[$i] == $this->unicode->uni_RLO) {
13656
 
                // X4. With each RLO, compute the least greater odd embedding level.
13657
 
                //    a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to right-to-left.
13658
 
                //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
13659
 
                $next_level = $cel + ($cel % 2) + 1;
13660
 
                if ($next_level < 62) {
13661
 
                    $remember[] = array('num' => $this->unicode->uni_RLO, 'cel' => $cel, 'dos' => $dos);
13662
 
                    $cel = $next_level;
13663
 
                    $dos = 'R';
13664
 
                    $sor = $eor;
13665
 
                    $eor = $cel % 2 ? 'R' : 'L';
13666
 
                }
13667
 
            } elseif ($ta[$i] == $this->unicode->uni_LRO) {
13668
 
                // X5. With each LRO, compute the least greater even embedding level.
13669
 
                //    a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to left-to-right.
13670
 
                //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
13671
 
                $next_level = $cel + 2 - ($cel % 2);
13672
 
                if ( $next_level < 62 ) {
13673
 
                    $remember[] = array('num' => $this->unicode->uni_LRO, 'cel' => $cel, 'dos' => $dos);
13674
 
                    $cel = $next_level;
13675
 
                    $dos = 'L';
13676
 
                    $sor = $eor;
13677
 
                    $eor = $cel % 2 ? 'R' : 'L';
13678
 
                }
13679
 
            } elseif ($ta[$i] == $this->unicode->uni_PDF) {
13680
 
                // X7. With each PDF, determine the matching embedding or override code. If there was a valid matching code, restore (pop) the last remembered (pushed) embedding level and directional override.
13681
 
                if (count($remember)) {
13682
 
                    $last = count($remember ) - 1;
13683
 
                    if (($remember[$last]['num'] == $this->unicode->uni_RLE) OR
13684
 
                        ($remember[$last]['num'] == $this->unicode->uni_LRE) OR
13685
 
                        ($remember[$last]['num'] == $this->unicode->uni_RLO) OR
13686
 
                        ($remember[$last]['num'] == $this->unicode->uni_LRO)) {
13687
 
                        $match = array_pop($remember);
13688
 
                        $cel = $match['cel'];
13689
 
                        $dos = $match['dos'];
13690
 
                        $sor = $eor;
13691
 
                        $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
13692
 
                    }
13693
 
                }
13694
 
            } elseif (($ta[$i] != $this->unicode->uni_RLE) AND
13695
 
                             ($ta[$i] != $this->unicode->uni_LRE) AND
13696
 
                             ($ta[$i] != $this->unicode->uni_RLO) AND
13697
 
                             ($ta[$i] != $this->unicode->uni_LRO) AND
13698
 
                             ($ta[$i] != $this->unicode->uni_PDF)) {
13699
 
                // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
13700
 
                //    a. Set the level of the current character to the current embedding level.
13701
 
                //    b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
13702
 
                if ($dos != 'N') {
13703
 
                    $chardir = $dos;
13704
 
                } else {
13705
 
                    if (isset($this->unicode->uni_type[$ta[$i]])) {
13706
 
                        $chardir = $this->unicode->uni_type[$ta[$i]];
13707
 
                    } else {
13708
 
                        $chardir = 'L';
13709
 
                    }
13710
 
                }
13711
 
                // stores string characters and other information
13712
 
                $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
13713
 
            }
13714
 
        } // end for each char
13715
 
 
13716
 
        // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
13717
 
        // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
13718
 
        // X10. The remaining rules are applied to each run of characters at the same level. For each run, determine the start-of-level-run (sor) and end-of-level-run (eor) type, either L or R. This depends on the higher of the two levels on either side of the boundary (at the start or end of the paragraph, the level of the 'other' run is the base embedding level). If the higher level is odd, the type is R; otherwise, it is L.
13719
 
 
13720
 
        // 3.3.3 Resolving Weak Types
13721
 
        // Weak types are now resolved one level run at a time. At level run boundaries where the type of the character on the other side of the boundary is required, the type assigned to sor or eor is used.
13722
 
        // Nonspacing marks are now resolved based on the previous characters.
13723
 
        $numchars = count($chardata);
13724
 
 
13725
 
        // W1. Examine each nonspacing mark (NSM) in the level run, and change the type of the NSM to the type of the previous character. If the NSM is at the start of the level run, it will get the type of sor.
13726
 
        $prevlevel = -1; // track level changes
13727
 
        $levcount = 0; // counts consecutive chars at the same level
13728
 
        for ($i=0; $i < $numchars; ++$i) {
13729
 
            if ($chardata[$i]['type'] == 'NSM') {
13730
 
                if ($levcount) {
13731
 
                    $chardata[$i]['type'] = $chardata[$i]['sor'];
13732
 
                } elseif ($i > 0) {
13733
 
                    $chardata[$i]['type'] = $chardata[($i-1)]['type'];
13734
 
                }
13735
 
            }
13736
 
            if ($chardata[$i]['level'] != $prevlevel) {
13737
 
                $levcount = 0;
13738
 
            } else {
13739
 
                ++$levcount;
13740
 
            }
13741
 
            $prevlevel = $chardata[$i]['level'];
13742
 
        }
13743
 
 
13744
 
        // W2. Search backward from each instance of a European number until the first strong type (R, L, AL, or sor) is found. If an AL is found, change the type of the European number to Arabic number.
13745
 
        $prevlevel = -1;
13746
 
        $levcount = 0;
13747
 
        for ($i=0; $i < $numchars; ++$i) {
13748
 
            if ($chardata[$i]['char'] == 'EN') {
13749
 
                for ($j=$levcount; $j >= 0; $j--) {
13750
 
                    if ($chardata[$j]['type'] == 'AL') {
13751
 
                        $chardata[$i]['type'] = 'AN';
13752
 
                    } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
13753
 
                        break;
13754
 
                    }
13755
 
                }
13756
 
            }
13757
 
            if ($chardata[$i]['level'] != $prevlevel) {
13758
 
                $levcount = 0;
13759
 
            } else {
13760
 
                ++$levcount;
13761
 
            }
13762
 
            $prevlevel = $chardata[$i]['level'];
13763
 
        }
13764
 
 
13765
 
        // W3. Change all ALs to R.
13766
 
        for ($i=0; $i < $numchars; ++$i) {
13767
 
            if ($chardata[$i]['type'] == 'AL') {
13768
 
                $chardata[$i]['type'] = 'R';
13769
 
            }
13770
 
        }
13771
 
 
13772
 
        // W4. A single European separator between two European numbers changes to a European number. A single common separator between two numbers of the same type changes to that type.
13773
 
        $prevlevel = -1;
13774
 
        $levcount = 0;
13775
 
        for ($i=0; $i < $numchars; ++$i) {
13776
 
            if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
13777
 
                if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
13778
 
                    $chardata[$i]['type'] = 'EN';
13779
 
                } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
13780
 
                    $chardata[$i]['type'] = 'EN';
13781
 
                } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
13782
 
                    $chardata[$i]['type'] = 'AN';
13783
 
                }
13784
 
            }
13785
 
            if ($chardata[$i]['level'] != $prevlevel) {
13786
 
                $levcount = 0;
13787
 
            } else {
13788
 
                ++$levcount;
13789
 
            }
13790
 
            $prevlevel = $chardata[$i]['level'];
13791
 
        }
13792
 
 
13793
 
        // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
13794
 
        $prevlevel = -1;
13795
 
        $levcount = 0;
13796
 
        for ($i=0; $i < $numchars; ++$i) {
13797
 
            if ($chardata[$i]['type'] == 'ET') {
13798
 
                if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
13799
 
                    $chardata[$i]['type'] = 'EN';
13800
 
                } else {
13801
 
                    $j = $i+1;
13802
 
                    while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
13803
 
                        if ($chardata[$j]['type'] == 'EN') {
13804
 
                            $chardata[$i]['type'] = 'EN';
13805
 
                            break;
13806
 
                        } elseif ($chardata[$j]['type'] != 'ET') {
13807
 
                            break;
13808
 
                        }
13809
 
                        ++$j;
13810
 
                    }
13811
 
                }
13812
 
            }
13813
 
            if ($chardata[$i]['level'] != $prevlevel) {
13814
 
                $levcount = 0;
13815
 
            } else {
13816
 
                ++$levcount;
13817
 
            }
13818
 
            $prevlevel = $chardata[$i]['level'];
13819
 
        }
13820
 
 
13821
 
        // W6. Otherwise, separators and terminators change to Other Neutral.
13822
 
        $prevlevel = -1;
13823
 
        $levcount = 0;
13824
 
        for ($i=0; $i < $numchars; ++$i) {
13825
 
            if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
13826
 
                $chardata[$i]['type'] = 'ON';
13827
 
            }
13828
 
            if ($chardata[$i]['level'] != $prevlevel) {
13829
 
                $levcount = 0;
13830
 
            } else {
13831
 
                ++$levcount;
13832
 
            }
13833
 
            $prevlevel = $chardata[$i]['level'];
13834
 
        }
13835
 
 
13836
 
        //W7. Search backward from each instance of a European number until the first strong type (R, L, or sor) is found. If an L is found, then change the type of the European number to L.
13837
 
        $prevlevel = -1;
13838
 
        $levcount = 0;
13839
 
        for ($i=0; $i < $numchars; ++$i) {
13840
 
            if ($chardata[$i]['char'] == 'EN') {
13841
 
                for ($j=$levcount; $j >= 0; $j--) {
13842
 
                    if ($chardata[$j]['type'] == 'L') {
13843
 
                        $chardata[$i]['type'] = 'L';
13844
 
                    } elseif ($chardata[$j]['type'] == 'R') {
13845
 
                        break;
13846
 
                    }
13847
 
                }
13848
 
            }
13849
 
            if ($chardata[$i]['level'] != $prevlevel) {
13850
 
                $levcount = 0;
13851
 
            } else {
13852
 
                ++$levcount;
13853
 
            }
13854
 
            $prevlevel = $chardata[$i]['level'];
13855
 
        }
13856
 
 
13857
 
        // N1. A sequence of neutrals takes the direction of the surrounding strong text if the text on both sides has the same direction. European and Arabic numbers act as if they were R in terms of their influence on neutrals. Start-of-level-run (sor) and end-of-level-run (eor) are used at level run boundaries.
13858
 
        $prevlevel = -1;
13859
 
        $levcount = 0;
13860
 
        for ($i=0; $i < $numchars; ++$i) {
13861
 
            if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
13862
 
                if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
13863
 
                    $chardata[$i]['type'] = 'L';
13864
 
                } elseif (($chardata[$i]['type'] == 'N') AND
13865
 
                 (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
13866
 
                 (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
13867
 
                    $chardata[$i]['type'] = 'R';
13868
 
                } elseif ($chardata[$i]['type'] == 'N') {
13869
 
                    // N2. Any remaining neutrals take the embedding direction
13870
 
                    $chardata[$i]['type'] = $chardata[$i]['sor'];
13871
 
                }
13872
 
            } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
13873
 
                // first char
13874
 
                if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
13875
 
                    $chardata[$i]['type'] = 'L';
13876
 
                } elseif (($chardata[$i]['type'] == 'N') AND
13877
 
                 (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
13878
 
                 (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
13879
 
                    $chardata[$i]['type'] = 'R';
13880
 
                } elseif ($chardata[$i]['type'] == 'N') {
13881
 
                    // N2. Any remaining neutrals take the embedding direction
13882
 
                    $chardata[$i]['type'] = $chardata[$i]['sor'];
13883
 
                }
13884
 
            } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
13885
 
                //last char
13886
 
                if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
13887
 
                    $chardata[$i]['type'] = 'L';
13888
 
                } elseif (($chardata[$i]['type'] == 'N') AND
13889
 
                 (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
13890
 
                 (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
13891
 
                    $chardata[$i]['type'] = 'R';
13892
 
                } elseif ($chardata[$i]['type'] == 'N') {
13893
 
                    // N2. Any remaining neutrals take the embedding direction
13894
 
                    $chardata[$i]['type'] = $chardata[$i]['sor'];
13895
 
                }
13896
 
            } elseif ($chardata[$i]['type'] == 'N') {
13897
 
                // N2. Any remaining neutrals take the embedding direction
13898
 
                $chardata[$i]['type'] = $chardata[$i]['sor'];
13899
 
            }
13900
 
            if ($chardata[$i]['level'] != $prevlevel) {
13901
 
                $levcount = 0;
13902
 
            } else {
13903
 
                ++$levcount;
13904
 
            }
13905
 
            $prevlevel = $chardata[$i]['level'];
13906
 
        }
13907
 
 
13908
 
        // I1. For all characters with an even (left-to-right) embedding direction, those of type R go up one level and those of type AN or EN go up two levels.
13909
 
        // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
13910
 
        for ($i=0; $i < $numchars; ++$i) {
13911
 
            $odd = $chardata[$i]['level'] % 2;
13912
 
            if ($odd) {
13913
 
                if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
13914
 
                    $chardata[$i]['level'] += 1;
13915
 
                }
13916
 
            } else {
13917
 
                if ($chardata[$i]['type'] == 'R') {
13918
 
                    $chardata[$i]['level'] += 1;
13919
 
                } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
13920
 
                    $chardata[$i]['level'] += 2;
13921
 
                }
13922
 
            }
13923
 
            $maxlevel = max($chardata[$i]['level'],$maxlevel);
13924
 
        }
13925
 
 
13926
 
        // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
13927
 
        //    1. Segment separators,
13928
 
        //    2. Paragraph separators,
13929
 
        //    3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
13930
 
        //    4. Any sequence of white space characters at the end of the line.
13931
 
        for ($i=0; $i < $numchars; ++$i) {
13932
 
            if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
13933
 
                $chardata[$i]['level'] = $pel;
13934
 
            } elseif ($chardata[$i]['type'] == 'WS') {
13935
 
                $j = $i+1;
13936
 
                while ($j < $numchars) {
13937
 
                    if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
13938
 
                        (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
13939
 
                        $chardata[$i]['level'] = $pel;
13940
 
                        break;
13941
 
                    } elseif ($chardata[$j]['type'] != 'WS') {
13942
 
                        break;
13943
 
                    }
13944
 
                    ++$j;
13945
 
                }
13946
 
            }
13947
 
        }
13948
 
 
13949
 
        // Arabic Shaping
13950
 
        // Cursively connected scripts, such as Arabic or Syriac, require the selection of positional character shapes that depend on adjacent characters. Shaping is logically applied after the Bidirectional Algorithm is used and is limited to characters within the same directional run.
13951
 
        if ($arabic) {
13952
 
            $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
13953
 
            $alfletter = array(1570,1571,1573,1575);
13954
 
            $chardata2 = $chardata;
13955
 
            $laaletter = false;
13956
 
            $charAL = array();
13957
 
            $x = 0;
13958
 
            for ($i=0; $i < $numchars; ++$i) {
13959
 
                if (($this->unicode->uni_type[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
13960
 
                    $charAL[$x] = $chardata[$i];
13961
 
                    $charAL[$x]['i'] = $i;
13962
 
                    $chardata[$i]['x'] = $x;
13963
 
                    ++$x;
13964
 
                }
13965
 
            }
13966
 
            $numAL = $x;
13967
 
            for ($i=0; $i < $numchars; ++$i) {
13968
 
                $thischar = $chardata[$i];
13969
 
                if ($i > 0) {
13970
 
                    $prevchar = $chardata[($i-1)];
13971
 
                } else {
13972
 
                    $prevchar = false;
13973
 
                }
13974
 
                if (($i+1) < $numchars) {
13975
 
                    $nextchar = $chardata[($i+1)];
13976
 
                } else {
13977
 
                    $nextchar = false;
13978
 
                }
13979
 
                if ($this->unicode->uni_type[$thischar['char']] == 'AL') {
13980
 
                    $x = $thischar['x'];
13981
 
                    if ($x > 0) {
13982
 
                        $prevchar = $charAL[($x-1)];
13983
 
                    } else {
13984
 
                        $prevchar = false;
13985
 
                    }
13986
 
                    if (($x+1) < $numAL) {
13987
 
                        $nextchar = $charAL[($x+1)];
13988
 
                    } else {
13989
 
                        $nextchar = false;
13990
 
                    }
13991
 
                    // if laa letter
13992
 
                    if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
13993
 
                        $arabicarr = $this->unicode->uni_laa_array;
13994
 
                        $laaletter = true;
13995
 
                        if ($x > 1) {
13996
 
                            $prevchar = $charAL[($x-2)];
13997
 
                        } else {
13998
 
                            $prevchar = false;
13999
 
                        }
14000
 
                    } else {
14001
 
                        $arabicarr = $this->unicode->uni_arabicsubst;
14002
 
                        $laaletter = false;
14003
 
                    }
14004
 
                    if (($prevchar !== false) AND ($nextchar !== false) AND
14005
 
                        (($this->unicode->uni_type[$prevchar['char']] == 'AL') OR ($this->unicode->uni_type[$prevchar['char']] == 'NSM')) AND
14006
 
                        (($this->unicode->uni_type[$nextchar['char']] == 'AL') OR ($this->unicode->uni_type[$nextchar['char']] == 'NSM')) AND
14007
 
                        ($prevchar['type'] == $thischar['type']) AND
14008
 
                        ($nextchar['type'] == $thischar['type']) AND
14009
 
                        ($nextchar['char'] != 1567)) {
14010
 
                        if (in_array($prevchar['char'], $endedletter)) {
14011
 
                            if (isset($arabicarr[$thischar['char']][2])) {
14012
 
                                // initial
14013
 
                                $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
14014
 
                            }
14015
 
                        } else {
14016
 
                            if (isset($arabicarr[$thischar['char']][3])) {
14017
 
                                // medial
14018
 
                                $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
14019
 
                            }
14020
 
                        }
14021
 
                    } elseif (($nextchar !== false) AND
14022
 
                        (($this->unicode->uni_type[$nextchar['char']] == 'AL') OR ($this->unicode->uni_type[$nextchar['char']] == 'NSM')) AND
14023
 
                        ($nextchar['type'] == $thischar['type']) AND
14024
 
                        ($nextchar['char'] != 1567)) {
14025
 
                        if (isset($arabicarr[$chardata[$i]['char']][2])) {
14026
 
                            // initial
14027
 
                            $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
14028
 
                        }
14029
 
                    } elseif ((($prevchar !== false) AND
14030
 
                        (($this->unicode->uni_type[$prevchar['char']] == 'AL') OR ($this->unicode->uni_type[$prevchar['char']] == 'NSM')) AND
14031
 
                        ($prevchar['type'] == $thischar['type'])) OR
14032
 
                        (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
14033
 
                        // final
14034
 
                        if (($i > 1) AND ($thischar['char'] == 1607) AND
14035
 
                            ($chardata[$i-1]['char'] == 1604) AND
14036
 
                            ($chardata[$i-2]['char'] == 1604)) {
14037
 
                            //Allah Word
14038
 
                            // mark characters to delete with false
14039
 
                            $chardata2[$i-2]['char'] = false;
14040
 
                            $chardata2[$i-1]['char'] = false;
14041
 
                            $chardata2[$i]['char'] = 65010;
14042
 
                        } else {
14043
 
                            if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
14044
 
                                if (isset($arabicarr[$thischar['char']][0])) {
14045
 
                                    // isolated
14046
 
                                    $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
14047
 
                                }
14048
 
                            } else {
14049
 
                                if (isset($arabicarr[$thischar['char']][1])) {
14050
 
                                    // final
14051
 
                                    $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
14052
 
                                }
14053
 
                            }
14054
 
                        }
14055
 
                    } elseif (isset($arabicarr[$thischar['char']][0])) {
14056
 
                        // isolated
14057
 
                        $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
14058
 
                    }
14059
 
                    // if laa letter
14060
 
                    if ($laaletter) {
14061
 
                        // mark characters to delete with false
14062
 
                        $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
14063
 
                    }
14064
 
                } // end if AL (Arabic Letter)
14065
 
            } // end for each char
14066
 
            /*
14067
 
             * Combining characters that can occur with Arabic Shadda (0651 HEX, 1617 DEC) are replaced.
14068
 
             * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
14069
 
             */
14070
 
            $cw = &$this->CurrentFont['cw'];
14071
 
            for ($i = 0; $i < ($numchars-1); ++$i) {
14072
 
                if (($chardata2[$i]['char'] == 1617) AND (isset($this->unicode->uni_diacritics[($chardata2[$i+1]['char'])]))) {
14073
 
                    // check if the subtitution font is defined on current font
14074
 
                    if (isset($cw[($this->unicode->uni_diacritics[($chardata2[$i+1]['char'])])])) {
14075
 
                        $chardata2[$i]['char'] = false;
14076
 
                        $chardata2[$i+1]['char'] = $this->unicode->uni_diacritics[($chardata2[$i+1]['char'])];
14077
 
                    }
14078
 
                }
14079
 
            }
14080
 
            // remove marked characters
14081
 
            foreach ($chardata2 as $key => $value) {
14082
 
                if ($value['char'] === false) {
14083
 
                    unset($chardata2[$key]);
14084
 
                }
14085
 
            }
14086
 
            $chardata = array_values($chardata2);
14087
 
            $numchars = count($chardata);
14088
 
            unset($chardata2);
14089
 
            unset($arabicarr);
14090
 
            unset($laaletter);
14091
 
            unset($charAL);
14092
 
        }
14093
 
 
14094
 
        // L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher.
14095
 
        for ($j=$maxlevel; $j > 0; $j--) {
14096
 
            $ordarray = Array();
14097
 
            $revarr = Array();
14098
 
            $onlevel = false;
14099
 
            for ($i=0; $i < $numchars; ++$i) {
14100
 
                if ($chardata[$i]['level'] >= $j) {
14101
 
                    $onlevel = true;
14102
 
                    if (isset($this->unicode->uni_mirror[$chardata[$i]['char']])) {
14103
 
                        // L4. A character is depicted by a mirrored glyph if and only if (a) the resolved directionality of that character is R, and (b) the Bidi_Mirrored property value of that character is true.
14104
 
                        $chardata[$i]['char'] = $this->unicode->uni_mirror[$chardata[$i]['char']];
14105
 
                    }
14106
 
                    $revarr[] = $chardata[$i];
14107
 
                } else {
14108
 
                    if ($onlevel) {
14109
 
                        $revarr = array_reverse($revarr);
14110
 
                        $ordarray = array_merge($ordarray, $revarr);
14111
 
                        $revarr = Array();
14112
 
                        $onlevel = false;
14113
 
                    }
14114
 
                    $ordarray[] = $chardata[$i];
14115
 
                }
14116
 
            }
14117
 
            if ($onlevel) {
14118
 
                $revarr = array_reverse($revarr);
14119
 
                $ordarray = array_merge($ordarray, $revarr);
14120
 
            }
14121
 
            $chardata = $ordarray;
14122
 
        }
14123
 
 
14124
 
        $ordarray = array();
14125
 
        for ($i=0; $i < $numchars; ++$i) {
14126
 
            $ordarray[] = $chardata[$i]['char'];
14127
 
            // store char values for subsetting
14128
 
            $this->CurrentFont['subsetchars'][$chardata[$i]['char']] = true;
14129
 
        }
14130
 
        // update font subsetchars
14131
 
        $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
14132
 
        return $ordarray;
14133
 
    }
14134
 
 
14135
 
    // END OF BIDIRECTIONAL TEXT SECTION -------------------
14136
 
 
14137
 
    /**
14138
 
     * Adds a bookmark.
14139
 
     * @param $txt (string) bookmark description.
14140
 
     * @param $level (int) bookmark level (minimum value is 0).
14141
 
     * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
14142
 
     * @param $page (int) target page number (leave empty for current page).
14143
 
     * @public
14144
 
     * @author Olivier Plathey, Nicola Asuni
14145
 
     * @since 2.1.002 (2008-02-12)
14146
 
     */
14147
 
    public function Bookmark($txt, $level=0, $y=-1, $page='') {
14148
 
        if ($level < 0) {
14149
 
            $level = 0;
14150
 
        }
14151
 
        if (isset($this->outlines[0])) {
14152
 
            $lastoutline = end($this->outlines);
14153
 
            $maxlevel = $lastoutline['l'] + 1;
14154
 
        } else {
14155
 
            $maxlevel = 0;
14156
 
        }
14157
 
        if ($level > $maxlevel) {
14158
 
            $level = $maxlevel;
14159
 
        }
14160
 
        if ($y == -1) {
14161
 
            $y = $this->GetY();
14162
 
        }
14163
 
        if (empty($page)) {
14164
 
            $page = $this->PageNo();
14165
 
            if (empty($page)) {
14166
 
                return;
14167
 
            }
14168
 
        }
14169
 
        $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $page);
14170
 
    }
14171
 
 
14172
 
    /**
14173
 
     * Create a bookmark PDF string.
14174
 
     * @protected
14175
 
     * @author Olivier Plathey, Nicola Asuni
14176
 
     * @since 2.1.002 (2008-02-12)
14177
 
     */
14178
 
    protected function _putbookmarks() {
14179
 
        $nb = count($this->outlines);
14180
 
        if ($nb == 0) {
14181
 
            return;
14182
 
        }
14183
 
        // get sorting columns
14184
 
        $outline_p = array();
14185
 
        $outline_y = array();
14186
 
        foreach ($this->outlines as $key => $row) {
14187
 
            $outline_p[$key] = $row['p'];
14188
 
            $outline_k[$key] = $key;
14189
 
        }
14190
 
        // sort outlines by page and original position
14191
 
        array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines);
14192
 
        $lru = array();
14193
 
        $level = 0;
14194
 
        foreach ($this->outlines as $i => $o) {
14195
 
            if ($o['l'] > 0) {
14196
 
                $parent = $lru[($o['l'] - 1)];
14197
 
                //Set parent and last pointers
14198
 
                $this->outlines[$i]['parent'] = $parent;
14199
 
                $this->outlines[$parent]['last'] = $i;
14200
 
                if ($o['l'] > $level) {
14201
 
                    //Level increasing: set first pointer
14202
 
                    $this->outlines[$parent]['first'] = $i;
14203
 
                }
14204
 
            } else {
14205
 
                $this->outlines[$i]['parent'] = $nb;
14206
 
            }
14207
 
            if (($o['l'] <= $level) AND ($i > 0)) {
14208
 
                //Set prev and next pointers
14209
 
                $prev = $lru[$o['l']];
14210
 
                $this->outlines[$prev]['next'] = $i;
14211
 
                $this->outlines[$i]['prev'] = $prev;
14212
 
            }
14213
 
            $lru[$o['l']] = $i;
14214
 
            $level = $o['l'];
14215
 
        }
14216
 
        //Outline items
14217
 
        $n = $this->n + 1;
14218
 
        $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';
14219
 
        foreach ($this->outlines as $i => $o) {
14220
 
            if (isset($this->page_obj_id[($o['p'])])) {
14221
 
                $oid = $this->_newobj();
14222
 
                // covert HTML title to string
14223
 
                $title = preg_replace($nltags, "\n", $o['t']);
14224
 
                $title = preg_replace("/[\r]+/si", '', $title);
14225
 
                $title = preg_replace("/[\n]+/si", "\n", $title);
14226
 
                $title = strip_tags($title);
14227
 
                $title = $this->stringTrim($title);
14228
 
                $out = '<</Title '.$this->_textstring($title, $oid);
14229
 
                $out .= ' /Parent '.($n + $o['parent']).' 0 R';
14230
 
                if (isset($o['prev'])) {
14231
 
                    $out .= ' /Prev '.($n + $o['prev']).' 0 R';
14232
 
                }
14233
 
                if (isset($o['next'])) {
14234
 
                    $out .= ' /Next '.($n + $o['next']).' 0 R';
14235
 
                }
14236
 
                if (isset($o['first'])) {
14237
 
                    $out .= ' /First '.($n + $o['first']).' 0 R';
14238
 
                }
14239
 
                if (isset($o['last'])) {
14240
 
                    $out .= ' /Last '.($n + $o['last']).' 0 R';
14241
 
                }
14242
 
                $out .= ' '.sprintf('/Dest [%u 0 R /XYZ 0 %.2F null]', $this->page_obj_id[($o['p'])], ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
14243
 
                $out .= ' /Count 0 >>';
14244
 
                $out .= "\n".'endobj';
14245
 
                $this->_out($out);
14246
 
            }
14247
 
        }
14248
 
        //Outline root
14249
 
        $this->OutlineRoot = $this->_newobj();
14250
 
        $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj');
14251
 
    }
14252
 
 
14253
 
    // --- JAVASCRIPT ------------------------------------------------------
14254
 
 
14255
 
    /**
14256
 
     * Adds a javascript
14257
 
     * @param $script (string) Javascript code
14258
 
     * @public
14259
 
     * @author Johannes G�ntert, Nicola Asuni
14260
 
     * @since 2.1.002 (2008-02-12)
14261
 
     */
14262
 
    public function IncludeJS($script) {
14263
 
        $this->javascript .= $script;
14264
 
    }
14265
 
 
14266
 
    /**
14267
 
     * Adds a javascript object and return object ID
14268
 
     * @param $script (string) Javascript code
14269
 
     * @param $onload (boolean) if true executes this object when opening the document
14270
 
     * @return int internal object ID
14271
 
     * @public
14272
 
     * @author Nicola Asuni
14273
 
     * @since 4.8.000 (2009-09-07)
14274
 
     */
14275
 
    public function addJavascriptObject($script, $onload=false) {
14276
 
        ++$this->n;
14277
 
        $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload);
14278
 
        return $this->n;
14279
 
    }
14280
 
 
14281
 
    /**
14282
 
     * Create a javascript PDF string.
14283
 
     * @protected
14284
 
     * @author Johannes G�ntert, Nicola Asuni
14285
 
     * @since 2.1.002 (2008-02-12)
14286
 
     */
14287
 
    protected function _putjavascript() {
14288
 
        if (empty($this->javascript) AND empty($this->js_objects)) {
14289
 
            return;
14290
 
        }
14291
 
        if (strpos($this->javascript, 'this.addField') > 0) {
14292
 
            if (!$this->ur['enabled']) {
14293
 
                //$this->setUserRights();
14294
 
            }
14295
 
            // the following two lines are used to avoid form fields duplication after saving
14296
 
            // The addField method only works when releasing user rights (UR3)
14297
 
            $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
14298
 
            $jsb = "getField('tcpdfdocsaved').value='saved';";
14299
 
            $this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
14300
 
        }
14301
 
        $this->n_js = $this->_newobj();
14302
 
        $out = ' << /Names [';
14303
 
        if (!empty($this->javascript)) {
14304
 
            $out .= ' (EmbeddedJS) '.($this->n + 1).' 0 R';
14305
 
        }
14306
 
        if (!empty($this->js_objects)) {
14307
 
            foreach ($this->js_objects as $key => $val) {
14308
 
                if ($val['onload']) {
14309
 
                    $out .= ' (JS'.$key.') '.$key.' 0 R';
14310
 
                }
14311
 
            }
14312
 
        }
14313
 
        $out .= ' ] >>';
14314
 
        $out .= "\n".'endobj';
14315
 
        $this->_out($out);
14316
 
        // default Javascript object
14317
 
        if (!empty($this->javascript)) {
14318
 
            $obj_id = $this->_newobj();
14319
 
            $out = '<< /S /JavaScript';
14320
 
            $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id);
14321
 
            $out .= ' >>';
14322
 
            $out .= "\n".'endobj';
14323
 
            $this->_out($out);
14324
 
        }
14325
 
        // additional Javascript objects
14326
 
        if (!empty($this->js_objects)) {
14327
 
            foreach ($this->js_objects as $key => $val) {
14328
 
                $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
14329
 
                $this->_out($out);
14330
 
            }
14331
 
        }
14332
 
    }
14333
 
 
14334
 
    /**
14335
 
     * Convert color to javascript color.
14336
 
     * @param $color (string) color name or "#RRGGBB"
14337
 
     * @protected
14338
 
     * @author Denis Van Nuffelen, Nicola Asuni
14339
 
     * @since 2.1.002 (2008-02-12)
14340
 
     */
14341
 
    protected function _JScolor($color) {
14342
 
        static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
14343
 
        if (substr($color,0,1) == '#') {
14344
 
            return sprintf("['RGB',%.3F,%.3F,%.3F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
14345
 
        }
14346
 
        if (!in_array($color,$aColors)) {
14347
 
            $this->Error('Invalid color: '.$color);
14348
 
        }
14349
 
        return 'color.'.$color;
14350
 
    }
14351
 
 
14352
 
    /**
14353
 
     * Adds a javascript form field.
14354
 
     * @param $type (string) field type
14355
 
     * @param $name (string) field name
14356
 
     * @param $x (int) horizontal position
14357
 
     * @param $y (int) vertical position
14358
 
     * @param $w (int) width
14359
 
     * @param $h (int) height
14360
 
     * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
14361
 
     * @protected
14362
 
     * @author Denis Van Nuffelen, Nicola Asuni
14363
 
     * @since 2.1.002 (2008-02-12)
14364
 
     */
14365
 
    protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
14366
 
        if ($this->rtl) {
14367
 
            $x = $x - $w;
14368
 
        }
14369
 
        // the followind avoid fields duplication after saving the document
14370
 
        $this->javascript .= "if(getField('tcpdfdocsaved').value != 'saved') {";
14371
 
        $k = $this->k;
14372
 
        $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%u,[%.2F,%.2F,%.2F,%.2F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n";
14373
 
        $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
14374
 
        while (list($key, $val) = each($prop)) {
14375
 
            if (strcmp(substr($key, -5), 'Color') == 0) {
14376
 
                $val = $this->_JScolor($val);
14377
 
            } else {
14378
 
                $val = "'".$val."'";
14379
 
            }
14380
 
            $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
14381
 
        }
14382
 
        if ($this->rtl) {
14383
 
            $this->x -= $w;
14384
 
        } else {
14385
 
            $this->x += $w;
14386
 
        }
14387
 
        $this->javascript .= '}';
14388
 
    }
14389
 
 
14390
 
    // --- FORM FIELDS -----------------------------------------------------
14391
 
 
14392
 
    /**
14393
 
     * Convert JavaScript form fields properties array to Annotation Properties array.
14394
 
     * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
14395
 
     * @return array of annotation properties
14396
 
     * @protected
14397
 
     * @author Nicola Asuni
14398
 
     * @since 4.8.000 (2009-09-06)
14399
 
     */
14400
 
    protected function getAnnotOptFromJSProp($prop) {
14401
 
        if (isset($prop['aopt']) AND is_array($prop['aopt'])) {
14402
 
            // the annotation options area lready defined
14403
 
            return $prop['aopt'];
14404
 
        }
14405
 
        $opt = array(); // value to be returned
14406
 
        // alignment: Controls how the text is laid out within the text field.
14407
 
        if (isset($prop['alignment'])) {
14408
 
            switch ($prop['alignment']) {
14409
 
                case 'left': {
14410
 
                    $opt['q'] = 0;
14411
 
                    break;
14412
 
                }
14413
 
                case 'center': {
14414
 
                    $opt['q'] = 1;
14415
 
                    break;
14416
 
                }
14417
 
                case 'right': {
14418
 
                    $opt['q'] = 2;
14419
 
                    break;
14420
 
                }
14421
 
                default: {
14422
 
                    $opt['q'] = ($this->rtl)?2:0;
14423
 
                    break;
14424
 
                }
14425
 
            }
14426
 
        }
14427
 
        // lineWidth: Specifies the thickness of the border when stroking the perimeter of a field's rectangle.
14428
 
        if (isset($prop['lineWidth'])) {
14429
 
            $linewidth = intval($prop['lineWidth']);
14430
 
        } else {
14431
 
            $linewidth = 1;
14432
 
        }
14433
 
        // borderStyle: The border style for a field.
14434
 
        if (isset($prop['borderStyle'])) {
14435
 
            switch ($prop['borderStyle']) {
14436
 
                case 'border.d':
14437
 
                case 'dashed': {
14438
 
                    $opt['border'] = array(0, 0, $linewidth, array(3, 2));
14439
 
                    $opt['bs'] = array('w'=>$linewidth, 's'=>'D', 'd'=>array(3, 2));
14440
 
                    break;
14441
 
                }
14442
 
                case 'border.b':
14443
 
                case 'beveled': {
14444
 
                    $opt['border'] = array(0, 0, $linewidth);
14445
 
                    $opt['bs'] = array('w'=>$linewidth, 's'=>'B');
14446
 
                    break;
14447
 
                }
14448
 
                case 'border.i':
14449
 
                case 'inset': {
14450
 
                    $opt['border'] = array(0, 0, $linewidth);
14451
 
                    $opt['bs'] = array('w'=>$linewidth, 's'=>'I');
14452
 
                    break;
14453
 
                }
14454
 
                case 'border.u':
14455
 
                case 'underline': {
14456
 
                    $opt['border'] = array(0, 0, $linewidth);
14457
 
                    $opt['bs'] = array('w'=>$linewidth, 's'=>'U');
14458
 
                    break;
14459
 
                }
14460
 
                default:
14461
 
                case 'border.s':
14462
 
                case 'solid': {
14463
 
                    $opt['border'] = array(0, 0, $linewidth);
14464
 
                    $opt['bs'] = array('w'=>$linewidth, 's'=>'S');
14465
 
                    break;
14466
 
                }
14467
 
            }
14468
 
        }
14469
 
        if (isset($prop['border']) AND is_array($prop['border'])) {
14470
 
            $opt['border'] = $prop['border'];
14471
 
        }
14472
 
        if (!isset($opt['mk'])) {
14473
 
            $opt['mk'] = array();
14474
 
        }
14475
 
        if (!isset($opt['mk']['if'])) {
14476
 
            $opt['mk']['if'] = array();
14477
 
        }
14478
 
        $opt['mk']['if']['a'] = array(0.5, 0.5);
14479
 
        // buttonAlignX: Controls how space is distributed from the left of the button face with respect to the icon.
14480
 
        if (isset($prop['buttonAlignX'])) {
14481
 
            $opt['mk']['if']['a'][0] = $prop['buttonAlignX'];
14482
 
        }
14483
 
        // buttonAlignY: Controls how unused space is distributed from the bottom of the button face with respect to the icon.
14484
 
        if (isset($prop['buttonAlignY'])) {
14485
 
            $opt['mk']['if']['a'][1] = $prop['buttonAlignY'];
14486
 
        }
14487
 
        // buttonFitBounds: If true, the extent to which the icon may be scaled is set to the bounds of the button field.
14488
 
        if (isset($prop['buttonFitBounds']) AND ($prop['buttonFitBounds'] == 'true')) {
14489
 
            $opt['mk']['if']['fb'] = true;
14490
 
        }
14491
 
        // buttonScaleHow: Controls how the icon is scaled (if necessary) to fit inside the button face.
14492
 
        if (isset($prop['buttonScaleHow'])) {
14493
 
            switch ($prop['buttonScaleHow']) {
14494
 
                case 'scaleHow.proportional': {
14495
 
                    $opt['mk']['if']['s'] = 'P';
14496
 
                    break;
14497
 
                }
14498
 
                case 'scaleHow.anamorphic': {
14499
 
                    $opt['mk']['if']['s'] = 'A';
14500
 
                    break;
14501
 
                }
14502
 
            }
14503
 
        }
14504
 
        // buttonScaleWhen: Controls when an icon is scaled to fit inside the button face.
14505
 
        if (isset($prop['buttonScaleWhen'])) {
14506
 
            switch ($prop['buttonScaleWhen']) {
14507
 
                case 'scaleWhen.always': {
14508
 
                    $opt['mk']['if']['sw'] = 'A';
14509
 
                    break;
14510
 
                }
14511
 
                case 'scaleWhen.never': {
14512
 
                    $opt['mk']['if']['sw'] = 'N';
14513
 
                    break;
14514
 
                }
14515
 
                case 'scaleWhen.tooBig': {
14516
 
                    $opt['mk']['if']['sw'] = 'B';
14517
 
                    break;
14518
 
                }
14519
 
                case 'scaleWhen.tooSmall': {
14520
 
                    $opt['mk']['if']['sw'] = 'S';
14521
 
                    break;
14522
 
                }
14523
 
            }
14524
 
        }
14525
 
        // buttonPosition: Controls how the text and the icon of the button are positioned with respect to each other within the button face.
14526
 
        if (isset($prop['buttonPosition'])) {
14527
 
            switch ($prop['buttonPosition']) {
14528
 
                case 0:
14529
 
                case 'position.textOnly': {
14530
 
                    $opt['mk']['tp'] = 0;
14531
 
                    break;
14532
 
                }
14533
 
                case 1:
14534
 
                case 'position.iconOnly': {
14535
 
                    $opt['mk']['tp'] = 1;
14536
 
                    break;
14537
 
                }
14538
 
                case 2:
14539
 
                case 'position.iconTextV': {
14540
 
                    $opt['mk']['tp'] = 2;
14541
 
                    break;
14542
 
                }
14543
 
                case 3:
14544
 
                case 'position.textIconV': {
14545
 
                    $opt['mk']['tp'] = 3;
14546
 
                    break;
14547
 
                }
14548
 
                case 4:
14549
 
                case 'position.iconTextH': {
14550
 
                    $opt['mk']['tp'] = 4;
14551
 
                    break;
14552
 
                }
14553
 
                case 5:
14554
 
                case 'position.textIconH': {
14555
 
                    $opt['mk']['tp'] = 5;
14556
 
                    break;
14557
 
                }
14558
 
                case 6:
14559
 
                case 'position.overlay': {
14560
 
                    $opt['mk']['tp'] = 6;
14561
 
                    break;
14562
 
                }
14563
 
            }
14564
 
        }
14565
 
        // fillColor: Specifies the background color for a field.
14566
 
        if (isset($prop['fillColor'])) {
14567
 
            if (is_array($prop['fillColor'])) {
14568
 
                $opt['mk']['bg'] = $prop['fillColor'];
14569
 
            } else {
14570
 
                $opt['mk']['bg'] = $this->convertHTMLColorToDec($prop['fillColor']);
14571
 
            }
14572
 
        }
14573
 
        // strokeColor: Specifies the stroke color for a field that is used to stroke the rectangle of the field with a line as large as the line width.
14574
 
        if (isset($prop['strokeColor'])) {
14575
 
            if (is_array($prop['strokeColor'])) {
14576
 
                $opt['mk']['bc'] = $prop['strokeColor'];
14577
 
            } else {
14578
 
                $opt['mk']['bc'] = $this->convertHTMLColorToDec($prop['strokeColor']);
14579
 
            }
14580
 
        }
14581
 
        // rotation: The rotation of a widget in counterclockwise increments.
14582
 
        if (isset($prop['rotation'])) {
14583
 
            $opt['mk']['r'] = $prop['rotation'];
14584
 
        }
14585
 
        // charLimit: Limits the number of characters that a user can type into a text field.
14586
 
        if (isset($prop['charLimit'])) {
14587
 
            $opt['maxlen'] = intval($prop['charLimit']);
14588
 
        }
14589
 
        if (!isset($ff)) {
14590
 
            $ff = 0;
14591
 
        }
14592
 
        // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
14593
 
        if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) {
14594
 
            $ff += 1 << 0;
14595
 
        }
14596
 
        // required: Specifies whether a field requires a value.
14597
 
        if (isset($prop['required']) AND ($prop['required'] == 'true')) {
14598
 
            $ff += 1 << 1;
14599
 
        }
14600
 
        // multiline: Controls how text is wrapped within the field.
14601
 
        if (isset($prop['multiline']) AND ($prop['multiline'] == 'true')) {
14602
 
            $ff += 1 << 12;
14603
 
        }
14604
 
        // password: Specifies whether the field should display asterisks when data is entered in the field.
14605
 
        if (isset($prop['password']) AND ($prop['password'] == 'true')) {
14606
 
            $ff += 1 << 13;
14607
 
        }
14608
 
        // NoToggleToOff: If set, exactly one radio button shall be selected at all times; selecting the currently selected button has no effect.
14609
 
        if (isset($prop['NoToggleToOff']) AND ($prop['NoToggleToOff'] == 'true')) {
14610
 
            $ff += 1 << 14;
14611
 
        }
14612
 
        // Radio: If set, the field is a set of radio buttons.
14613
 
        if (isset($prop['Radio']) AND ($prop['Radio'] == 'true')) {
14614
 
            $ff += 1 << 15;
14615
 
        }
14616
 
        // Pushbutton: If set, the field is a pushbutton that does not retain a permanent value.
14617
 
        if (isset($prop['Pushbutton']) AND ($prop['Pushbutton'] == 'true')) {
14618
 
            $ff += 1 << 16;
14619
 
        }
14620
 
        // Combo: If set, the field is a combo box; if clear, the field is a list box.
14621
 
        if (isset($prop['Combo']) AND ($prop['Combo'] == 'true')) {
14622
 
            $ff += 1 << 17;
14623
 
        }
14624
 
        // editable: Controls whether a combo box is editable.
14625
 
        if (isset($prop['editable']) AND ($prop['editable'] == 'true')) {
14626
 
            $ff += 1 << 18;
14627
 
        }
14628
 
        // Sort: If set, the field's option items shall be sorted alphabetically.
14629
 
        if (isset($prop['Sort']) AND ($prop['Sort'] == 'true')) {
14630
 
            $ff += 1 << 19;
14631
 
        }
14632
 
        // fileSelect: If true, sets the file-select flag in the Options tab of the text field (Field is Used for File Selection).
14633
 
        if (isset($prop['fileSelect']) AND ($prop['fileSelect'] == 'true')) {
14634
 
            $ff += 1 << 20;
14635
 
        }
14636
 
        // multipleSelection: If true, indicates that a list box allows a multiple selection of items.
14637
 
        if (isset($prop['multipleSelection']) AND ($prop['multipleSelection'] == 'true')) {
14638
 
            $ff += 1 << 21;
14639
 
        }
14640
 
        // doNotSpellCheck: If true, spell checking is not performed on this editable text field.
14641
 
        if (isset($prop['doNotSpellCheck']) AND ($prop['doNotSpellCheck'] == 'true')) {
14642
 
            $ff += 1 << 22;
14643
 
        }
14644
 
        // doNotScroll: If true, the text field does not scroll and the user, therefore, is limited by the rectangular region designed for the field.
14645
 
        if (isset($prop['doNotScroll']) AND ($prop['doNotScroll'] == 'true')) {
14646
 
            $ff += 1 << 23;
14647
 
        }
14648
 
        // comb: If set to true, the field background is drawn as series of boxes (one for each character in the value of the field) and each character of the content is drawn within those boxes. The number of boxes drawn is determined from the charLimit property. It applies only to text fields. The setter will also raise if any of the following field properties are also set multiline, password, and fileSelect. A side-effect of setting this property is that the doNotScroll property is also set.
14649
 
        if (isset($prop['comb']) AND ($prop['comb'] == 'true')) {
14650
 
            $ff += 1 << 24;
14651
 
        }
14652
 
        // radiosInUnison: If false, even if a group of radio buttons have the same name and export value, they behave in a mutually exclusive fashion, like HTML radio buttons.
14653
 
        if (isset($prop['radiosInUnison']) AND ($prop['radiosInUnison'] == 'true')) {
14654
 
            $ff += 1 << 25;
14655
 
        }
14656
 
        // richText: If true, the field allows rich text formatting.
14657
 
        if (isset($prop['richText']) AND ($prop['richText'] == 'true')) {
14658
 
            $ff += 1 << 25;
14659
 
        }
14660
 
        // commitOnSelChange: Controls whether a field value is committed after a selection change.
14661
 
        if (isset($prop['commitOnSelChange']) AND ($prop['commitOnSelChange'] == 'true')) {
14662
 
            $ff += 1 << 26;
14663
 
        }
14664
 
        $opt['ff'] = $ff;
14665
 
        // defaultValue: The default value of a field - that is, the value that the field is set to when the form is reset.
14666
 
        if (isset($prop['defaultValue'])) {
14667
 
            $opt['dv'] = $prop['defaultValue'];
14668
 
        }
14669
 
        $f = 4; // default value for annotation flags
14670
 
        // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
14671
 
        if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) {
14672
 
            $f += 1 << 6;
14673
 
        }
14674
 
        // display: Controls whether the field is hidden or visible on screen and in print.
14675
 
        if (isset($prop['display'])) {
14676
 
            if ($prop['display'] == 'display.visible') {
14677
 
                //
14678
 
            } elseif ($prop['display'] == 'display.hidden') {
14679
 
                $f += 1 << 1;
14680
 
            } elseif ($prop['display'] == 'display.noPrint') {
14681
 
                $f -= 1 << 2;
14682
 
            } elseif ($prop['display'] == 'display.noView') {
14683
 
                $f += 1 << 5;
14684
 
            }
14685
 
        }
14686
 
        $opt['f'] = $f;
14687
 
        // currentValueIndices: Reads and writes single or multiple values of a list box or combo box.
14688
 
        if (isset($prop['currentValueIndices']) AND is_array($prop['currentValueIndices'])) {
14689
 
            $opt['i'] = $prop['currentValueIndices'];
14690
 
        }
14691
 
        // value: The value of the field data that the user has entered.
14692
 
        if (isset($prop['value'])) {
14693
 
            if (is_array($prop['value'])) {
14694
 
                $opt['opt'] = array();
14695
 
                foreach ($prop['value'] AS $key => $optval) {
14696
 
                    // exportValues: An array of strings representing the export values for the field.
14697
 
                    if (isset($prop['exportValues'][$key])) {
14698
 
                        $opt['opt'][$key] = array($prop['exportValues'][$key], $prop['value'][$key]);
14699
 
                    } else {
14700
 
                        $opt['opt'][$key] = $prop['value'][$key];
14701
 
                    }
14702
 
                }
14703
 
            } else {
14704
 
                $opt['v'] = $prop['value'];
14705
 
            }
14706
 
        }
14707
 
        // richValue: This property specifies the text contents and formatting of a rich text field.
14708
 
        if (isset($prop['richValue'])) {
14709
 
            $opt['rv'] = $prop['richValue'];
14710
 
        }
14711
 
        // submitName: If nonempty, used during form submission instead of name. Only applicable if submitting in HTML format (that is, URL-encoded).
14712
 
        if (isset($prop['submitName'])) {
14713
 
            $opt['tm'] = $prop['submitName'];
14714
 
        }
14715
 
        // name: Fully qualified field name.
14716
 
        if (isset($prop['name'])) {
14717
 
            $opt['t'] = $prop['name'];
14718
 
        }
14719
 
        // userName: The user name (short description string) of the field.
14720
 
        if (isset($prop['userName'])) {
14721
 
            $opt['tu'] = $prop['userName'];
14722
 
        }
14723
 
        // highlight: Defines how a button reacts when a user clicks it.
14724
 
        if (isset($prop['highlight'])) {
14725
 
            switch ($prop['highlight']) {
14726
 
                case 'none':
14727
 
                case 'highlight.n': {
14728
 
                    $opt['h'] = 'N';
14729
 
                    break;
14730
 
                }
14731
 
                case 'invert':
14732
 
                case 'highlight.i': {
14733
 
                    $opt['h'] = 'i';
14734
 
                    break;
14735
 
                }
14736
 
                case 'push':
14737
 
                case 'highlight.p': {
14738
 
                    $opt['h'] = 'P';
14739
 
                    break;
14740
 
                }
14741
 
                case 'outline':
14742
 
                case 'highlight.o': {
14743
 
                    $opt['h'] = 'O';
14744
 
                    break;
14745
 
                }
14746
 
            }
14747
 
        }
14748
 
        // Unsupported options:
14749
 
        // - calcOrderIndex: Changes the calculation order of fields in the document.
14750
 
        // - delay: Delays the redrawing of a field's appearance.
14751
 
        // - defaultStyle: This property defines the default style attributes for the form field.
14752
 
        // - style: Allows the user to set the glyph style of a check box or radio button.
14753
 
        // - textColor, textFont, textSize
14754
 
        return $opt;
14755
 
    }
14756
 
 
14757
 
    /**
14758
 
     * Set default properties for form fields.
14759
 
     * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
14760
 
     * @public
14761
 
     * @author Nicola Asuni
14762
 
     * @since 4.8.000 (2009-09-06)
14763
 
     */
14764
 
    public function setFormDefaultProp($prop=array()) {
14765
 
        $this->default_form_prop = $prop;
14766
 
    }
14767
 
 
14768
 
    /**
14769
 
     * Return the default properties for form fields.
14770
 
     * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
14771
 
     * @public
14772
 
     * @author Nicola Asuni
14773
 
     * @since 4.8.000 (2009-09-06)
14774
 
     */
14775
 
    public function getFormDefaultProp() {
14776
 
        return $this->default_form_prop;
14777
 
    }
14778
 
 
14779
 
    /**
14780
 
     * Creates a text field
14781
 
     * @param $name (string) field name
14782
 
     * @param $w (float) Width of the rectangle
14783
 
     * @param $h (float) Height of the rectangle
14784
 
     * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
14785
 
     * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
14786
 
     * @param $x (float) Abscissa of the upper-left corner of the rectangle
14787
 
     * @param $y (float) Ordinate of the upper-left corner of the rectangle
14788
 
     * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
14789
 
     * @public
14790
 
     * @author Nicola Asuni
14791
 
     * @since 4.8.000 (2009-09-07)
14792
 
     */
14793
 
    public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
14794
 
        if ($x === '') {
14795
 
            $x = $this->x;
14796
 
        }
14797
 
        if ($y === '') {
14798
 
            $y = $this->y;
14799
 
        }
14800
 
        // check page for no-write regions and adapt page margins if necessary
14801
 
        $this->checkPageRegions($h, $x, $y);
14802
 
        if ($js) {
14803
 
            $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
14804
 
            return;
14805
 
        }
14806
 
        // get default style
14807
 
        $prop = array_merge($this->getFormDefaultProp(), $prop);
14808
 
        // get annotation data
14809
 
        $popt = $this->getAnnotOptFromJSProp($prop);
14810
 
        // set default appearance stream
14811
 
        $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
14812
 
        $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
14813
 
        $popt['da'] = $fontstyle;
14814
 
        $popt['ap'] = array();
14815
 
        $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q';
14816
 
        // merge options
14817
 
        $opt = array_merge($popt, $opt);
14818
 
        // remove some conflicting options
14819
 
        unset($opt['bs']);
14820
 
        // set remaining annotation data
14821
 
        $opt['Subtype'] = 'Widget';
14822
 
        $opt['ft'] = 'Tx';
14823
 
        $opt['t'] = $name;
14824
 
        /*
14825
 
        Additional annotation's parameters (check _putannotsobj() method):
14826
 
        //$opt['f']
14827
 
        //$opt['ap']
14828
 
        //$opt['as']
14829
 
        //$opt['bs']
14830
 
        //$opt['be']
14831
 
        //$opt['c']
14832
 
        //$opt['border']
14833
 
        //$opt['h']
14834
 
        //$opt['mk']
14835
 
        //$opt['mk']['r']
14836
 
        //$opt['mk']['bc']
14837
 
        //$opt['mk']['bg']
14838
 
        //$opt['mk']['ca']
14839
 
        //$opt['mk']['rc']
14840
 
        //$opt['mk']['ac']
14841
 
        //$opt['mk']['i']
14842
 
        //$opt['mk']['ri']
14843
 
        //$opt['mk']['ix']
14844
 
        //$opt['mk']['if']
14845
 
        //$opt['mk']['if']['sw']
14846
 
        //$opt['mk']['if']['s']
14847
 
        //$opt['mk']['if']['a']
14848
 
        //$opt['mk']['if']['fb']
14849
 
        //$opt['mk']['tp']
14850
 
        //$opt['tu']
14851
 
        //$opt['tm']
14852
 
        //$opt['ff']
14853
 
        //$opt['v']
14854
 
        //$opt['dv']
14855
 
        //$opt['a']
14856
 
        //$opt['aa']
14857
 
        //$opt['q']
14858
 
        */
14859
 
        $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
14860
 
        if ($this->rtl) {
14861
 
            $this->x -= $w;
14862
 
        } else {
14863
 
            $this->x += $w;
14864
 
        }
14865
 
    }
14866
 
 
14867
 
    /**
14868
 
     * Creates a RadioButton field
14869
 
     * @param $name (string) field name
14870
 
     * @param $w (int) width
14871
 
     * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
14872
 
     * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
14873
 
     * @param $onvalue (string) value to be returned if selected.
14874
 
     * @param $checked (boolean) define the initial state.
14875
 
     * @param $x (float) Abscissa of the upper-left corner of the rectangle
14876
 
     * @param $y (float) Ordinate of the upper-left corner of the rectangle
14877
 
     * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
14878
 
     * @public
14879
 
     * @author Nicola Asuni
14880
 
     * @since 4.8.000 (2009-09-07)
14881
 
     */
14882
 
    public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
14883
 
        if ($x === '') {
14884
 
            $x = $this->x;
14885
 
        }
14886
 
        if ($y === '') {
14887
 
            $y = $this->y;
14888
 
        }
14889
 
        // check page for no-write regions and adapt page margins if necessary
14890
 
        $this->checkPageRegions($w, $x, $y);
14891
 
        if ($js) {
14892
 
            $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
14893
 
            return;
14894
 
        }
14895
 
        if ($this->empty_string($onvalue)) {
14896
 
            $onvalue = 'On';
14897
 
        }
14898
 
        if ($checked) {
14899
 
            $defval = $onvalue;
14900
 
        } else {
14901
 
            $defval = 'Off';
14902
 
        }
14903
 
        // set data for parent group
14904
 
        if (!isset($this->radiobutton_groups[$this->page])) {
14905
 
            $this->radiobutton_groups[$this->page] = array();
14906
 
        }
14907
 
        if (!isset($this->radiobutton_groups[$this->page][$name])) {
14908
 
            $this->radiobutton_groups[$this->page][$name] = array();
14909
 
            ++$this->n;
14910
 
            $this->radiobutton_groups[$this->page][$name]['n'] = $this->n;
14911
 
            $this->radio_groups[] = $this->n;
14912
 
            $kid = ($this->n + 2);
14913
 
        } else {
14914
 
            $kid = ($this->n + 1);
14915
 
        }
14916
 
        // save object ID to be added on Kids entry on parent object
14917
 
        $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval);
14918
 
        // get default style
14919
 
        $prop = array_merge($this->getFormDefaultProp(), $prop);
14920
 
        $prop['NoToggleToOff'] = 'true';
14921
 
        $prop['Radio'] = 'true';
14922
 
        $prop['borderStyle'] = 'inset';
14923
 
        // get annotation data
14924
 
        $popt = $this->getAnnotOptFromJSProp($prop);
14925
 
        // set additional default values
14926
 
        $font = 'zapfdingbats';
14927
 
        $this->AddFont($font);
14928
 
        $tmpfont = $this->getFontBuffer($font);
14929
 
        $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
14930
 
        $fontstyle = sprintf('/F%d %.2F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
14931
 
        $popt['da'] = $fontstyle;
14932
 
        $popt['ap'] = array();
14933
 
        $popt['ap']['n'] = array();
14934
 
        $popt['ap']['n'][$onvalue] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q';
14935
 
        $popt['ap']['n']['Off'] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q';
14936
 
        if (!isset($popt['mk'])) {
14937
 
            $popt['mk'] = array();
14938
 
        }
14939
 
        $popt['mk']['ca'] = '(l)';
14940
 
        // merge options
14941
 
        $opt = array_merge($popt, $opt);
14942
 
        // set remaining annotation data
14943
 
        $opt['Subtype'] = 'Widget';
14944
 
        $opt['ft'] = 'Btn';
14945
 
        if ($checked) {
14946
 
            $opt['v'] = array('/'.$onvalue);
14947
 
            $opt['as'] = $onvalue;
14948
 
        } else {
14949
 
            $opt['as'] = 'Off';
14950
 
        }
14951
 
        $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
14952
 
        if ($this->rtl) {
14953
 
            $this->x -= $w;
14954
 
        } else {
14955
 
            $this->x += $w;
14956
 
        }
14957
 
    }
14958
 
 
14959
 
    /**
14960
 
     * Creates a List-box field
14961
 
     * @param $name (string) field name
14962
 
     * @param $w (int) width
14963
 
     * @param $h (int) height
14964
 
     * @param $values (array) array containing the list of values.
14965
 
     * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
14966
 
     * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
14967
 
     * @param $x (float) Abscissa of the upper-left corner of the rectangle
14968
 
     * @param $y (float) Ordinate of the upper-left corner of the rectangle
14969
 
     * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
14970
 
     * @public
14971
 
     * @author Nicola Asuni
14972
 
     * @since 4.8.000 (2009-09-07)
14973
 
     */
14974
 
    public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
14975
 
        if ($x === '') {
14976
 
            $x = $this->x;
14977
 
        }
14978
 
        if ($y === '') {
14979
 
            $y = $this->y;
14980
 
        }
14981
 
        // check page for no-write regions and adapt page margins if necessary
14982
 
        $this->checkPageRegions($h, $x, $y);
14983
 
        if ($js) {
14984
 
            $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
14985
 
            $s = '';
14986
 
            foreach ($values as $value) {
14987
 
                $s .= "'".addslashes($value)."',";
14988
 
            }
14989
 
            $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
14990
 
            return;
14991
 
        }
14992
 
        // get default style
14993
 
        $prop = array_merge($this->getFormDefaultProp(), $prop);
14994
 
        // get annotation data
14995
 
        $popt = $this->getAnnotOptFromJSProp($prop);
14996
 
        // set additional default values
14997
 
        $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
14998
 
        $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
14999
 
        $popt['da'] = $fontstyle;
15000
 
        $popt['ap'] = array();
15001
 
        $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q';
15002
 
        // merge options
15003
 
        $opt = array_merge($popt, $opt);
15004
 
        // set remaining annotation data
15005
 
        $opt['Subtype'] = 'Widget';
15006
 
        $opt['ft'] = 'Ch';
15007
 
        $opt['t'] = $name;
15008
 
        $opt['opt'] = $values;
15009
 
        $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
15010
 
        if ($this->rtl) {
15011
 
            $this->x -= $w;
15012
 
        } else {
15013
 
            $this->x += $w;
15014
 
        }
15015
 
    }
15016
 
 
15017
 
    /**
15018
 
     * Creates a Combo-box field
15019
 
     * @param $name (string) field name
15020
 
     * @param $w (int) width
15021
 
     * @param $h (int) height
15022
 
     * @param $values (array) array containing the list of values.
15023
 
     * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
15024
 
     * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
15025
 
     * @param $x (float) Abscissa of the upper-left corner of the rectangle
15026
 
     * @param $y (float) Ordinate of the upper-left corner of the rectangle
15027
 
     * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
15028
 
     * @public
15029
 
     * @author Nicola Asuni
15030
 
     * @since 4.8.000 (2009-09-07)
15031
 
     */
15032
 
    public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
15033
 
        if ($x === '') {
15034
 
            $x = $this->x;
15035
 
        }
15036
 
        if ($y === '') {
15037
 
            $y = $this->y;
15038
 
        }
15039
 
        // check page for no-write regions and adapt page margins if necessary
15040
 
        $this->checkPageRegions($h, $x, $y);
15041
 
        if ($js) {
15042
 
            $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
15043
 
            $s = '';
15044
 
            foreach ($values as $value) {
15045
 
                $s .= "'".addslashes($value)."',";
15046
 
            }
15047
 
            $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
15048
 
            return;
15049
 
        }
15050
 
        // get default style
15051
 
        $prop = array_merge($this->getFormDefaultProp(), $prop);
15052
 
        $prop['Combo'] = true;
15053
 
        // get annotation data
15054
 
        $popt = $this->getAnnotOptFromJSProp($prop);
15055
 
        // set additional default options
15056
 
        $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
15057
 
        $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
15058
 
        $popt['da'] = $fontstyle;
15059
 
        $popt['ap'] = array();
15060
 
        $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q';
15061
 
        // merge options
15062
 
        $opt = array_merge($popt, $opt);
15063
 
        // set remaining annotation data
15064
 
        $opt['Subtype'] = 'Widget';
15065
 
        $opt['ft'] = 'Ch';
15066
 
        $opt['t'] = $name;
15067
 
        $opt['opt'] = $values;
15068
 
        $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
15069
 
        if ($this->rtl) {
15070
 
            $this->x -= $w;
15071
 
        } else {
15072
 
            $this->x += $w;
15073
 
        }
15074
 
    }
15075
 
 
15076
 
    /**
15077
 
     * Creates a CheckBox field
15078
 
     * @param $name (string) field name
15079
 
     * @param $w (int) width
15080
 
     * @param $checked (boolean) define the initial state.
15081
 
     * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
15082
 
     * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
15083
 
     * @param $onvalue (string) value to be returned if selected.
15084
 
     * @param $x (float) Abscissa of the upper-left corner of the rectangle
15085
 
     * @param $y (float) Ordinate of the upper-left corner of the rectangle
15086
 
     * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
15087
 
     * @public
15088
 
     * @author Nicola Asuni
15089
 
     * @since 4.8.000 (2009-09-07)
15090
 
     */
15091
 
    public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
15092
 
        if ($x === '') {
15093
 
            $x = $this->x;
15094
 
        }
15095
 
        if ($y === '') {
15096
 
            $y = $this->y;
15097
 
        }
15098
 
        // check page for no-write regions and adapt page margins if necessary
15099
 
        $this->checkPageRegions($w, $x, $y);
15100
 
        if ($js) {
15101
 
            $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
15102
 
            return;
15103
 
        }
15104
 
        if (!isset($prop['value'])) {
15105
 
            $prop['value'] = array('Yes');
15106
 
        }
15107
 
        // get default style
15108
 
        $prop = array_merge($this->getFormDefaultProp(), $prop);
15109
 
        $prop['borderStyle'] = 'inset';
15110
 
        // get annotation data
15111
 
        $popt = $this->getAnnotOptFromJSProp($prop);
15112
 
        // set additional default options
15113
 
        $font = 'zapfdingbats';
15114
 
        $this->AddFont($font);
15115
 
        $tmpfont = $this->getFontBuffer($font);
15116
 
        $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
15117
 
        $fontstyle = sprintf('/F%d %.2F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
15118
 
        $popt['da'] = $fontstyle;
15119
 
        $popt['ap'] = array();
15120
 
        $popt['ap']['n'] = array();
15121
 
        $popt['ap']['n']['Yes'] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q';
15122
 
        $popt['ap']['n']['Off'] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q';
15123
 
        // merge options
15124
 
        $opt = array_merge($popt, $opt);
15125
 
        // set remaining annotation data
15126
 
        $opt['Subtype'] = 'Widget';
15127
 
        $opt['ft'] = 'Btn';
15128
 
        $opt['t'] = $name;
15129
 
        $opt['opt'] = array($onvalue);
15130
 
        if ($checked) {
15131
 
            $opt['v'] = array('/0');
15132
 
            $opt['as'] = 'Yes';
15133
 
        } else {
15134
 
            $opt['v'] = array('/Off');
15135
 
            $opt['as'] = 'Off';
15136
 
        }
15137
 
        $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
15138
 
        if ($this->rtl) {
15139
 
            $this->x -= $w;
15140
 
        } else {
15141
 
            $this->x += $w;
15142
 
        }
15143
 
    }
15144
 
 
15145
 
    /**
15146
 
     * Creates a button field
15147
 
     * @param $name (string) field name
15148
 
     * @param $w (int) width
15149
 
     * @param $h (int) height
15150
 
     * @param $caption (string) caption.
15151
 
     * @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.
15152
 
     * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
15153
 
     * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
15154
 
     * @param $x (float) Abscissa of the upper-left corner of the rectangle
15155
 
     * @param $y (float) Ordinate of the upper-left corner of the rectangle
15156
 
     * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
15157
 
     * @public
15158
 
     * @author Nicola Asuni
15159
 
     * @since 4.8.000 (2009-09-07)
15160
 
     */
15161
 
    public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
15162
 
        if ($x === '') {
15163
 
            $x = $this->x;
15164
 
        }
15165
 
        if ($y === '') {
15166
 
            $y = $this->y;
15167
 
        }
15168
 
        // check page for no-write regions and adapt page margins if necessary
15169
 
        $this->checkPageRegions($h, $x, $y);
15170
 
        if ($js) {
15171
 
            $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
15172
 
            $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
15173
 
            $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
15174
 
            $this->javascript .= 'f'.$name.".highlight='push';\n";
15175
 
            $this->javascript .= 'f'.$name.".print=false;\n";
15176
 
            return;
15177
 
        }
15178
 
        // get default style
15179
 
        $prop = array_merge($this->getFormDefaultProp(), $prop);
15180
 
        $prop['Pushbutton'] = 'true';
15181
 
        $prop['highlight'] = 'push';
15182
 
        $prop['display'] = 'display.noPrint';
15183
 
        // get annotation data
15184
 
        $popt = $this->getAnnotOptFromJSProp($prop);
15185
 
        $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
15186
 
        $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
15187
 
        $popt['da'] = $fontstyle;
15188
 
        $popt['ap'] = array();
15189
 
        $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q';
15190
 
        // set additional default options
15191
 
        if (!isset($popt['mk'])) {
15192
 
            $popt['mk'] = array();
15193
 
        }
15194
 
        $ann_obj_id = ($this->n + 1);
15195
 
        if (!empty($action) AND !is_array($action)) {
15196
 
            $ann_obj_id = ($this->n + 2);
15197
 
        }
15198
 
        $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
15199
 
        $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
15200
 
        $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
15201
 
        // merge options
15202
 
        $opt = array_merge($popt, $opt);
15203
 
        // set remaining annotation data
15204
 
        $opt['Subtype'] = 'Widget';
15205
 
        $opt['ft'] = 'Btn';
15206
 
        $opt['t'] = $caption;
15207
 
        $opt['v'] = $name;
15208
 
        if (!empty($action)) {
15209
 
            if (is_array($action)) {
15210
 
                // form action options as on section 12.7.5 of PDF32000_2008.
15211
 
                $opt['aa'] = '/D <<';
15212
 
                $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
15213
 
                foreach ($action AS $key => $val) {
15214
 
                    if (($key == 'S') AND in_array($val, $bmode)) {
15215
 
                        $opt['aa'] .= ' /S /'.$val;
15216
 
                    } elseif (($key == 'F') AND (!empty($val))) {
15217
 
                        $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
15218
 
                    } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
15219
 
                        $opt['aa'] .= ' /Fields [';
15220
 
                        foreach ($val AS $field) {
15221
 
                            $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
15222
 
                        }
15223
 
                        $opt['aa'] .= ']';
15224
 
                    } elseif (($key == 'Flags')) {
15225
 
                        $ff = 0;
15226
 
                        if (is_array($val)) {
15227
 
                            foreach ($val AS $flag) {
15228
 
                                switch ($flag) {
15229
 
                                    case 'Include/Exclude': {
15230
 
                                        $ff += 1 << 0;
15231
 
                                        break;
15232
 
                                    }
15233
 
                                    case 'IncludeNoValueFields': {
15234
 
                                        $ff += 1 << 1;
15235
 
                                        break;
15236
 
                                    }
15237
 
                                    case 'ExportFormat': {
15238
 
                                        $ff += 1 << 2;
15239
 
                                        break;
15240
 
                                    }
15241
 
                                    case 'GetMethod': {
15242
 
                                        $ff += 1 << 3;
15243
 
                                        break;
15244
 
                                    }
15245
 
                                    case 'SubmitCoordinates': {
15246
 
                                        $ff += 1 << 4;
15247
 
                                        break;
15248
 
                                    }
15249
 
                                    case 'XFDF': {
15250
 
                                        $ff += 1 << 5;
15251
 
                                        break;
15252
 
                                    }
15253
 
                                    case 'IncludeAppendSaves': {
15254
 
                                        $ff += 1 << 6;
15255
 
                                        break;
15256
 
                                    }
15257
 
                                    case 'IncludeAnnotations': {
15258
 
                                        $ff += 1 << 7;
15259
 
                                        break;
15260
 
                                    }
15261
 
                                    case 'SubmitPDF': {
15262
 
                                        $ff += 1 << 8;
15263
 
                                        break;
15264
 
                                    }
15265
 
                                    case 'CanonicalFormat': {
15266
 
                                        $ff += 1 << 9;
15267
 
                                        break;
15268
 
                                    }
15269
 
                                    case 'ExclNonUserAnnots': {
15270
 
                                        $ff += 1 << 10;
15271
 
                                        break;
15272
 
                                    }
15273
 
                                    case 'ExclFKey': {
15274
 
                                        $ff += 1 << 11;
15275
 
                                        break;
15276
 
                                    }
15277
 
                                    case 'EmbedForm': {
15278
 
                                        $ff += 1 << 13;
15279
 
                                        break;
15280
 
                                    }
15281
 
                                }
15282
 
                            }
15283
 
                        } else {
15284
 
                            $ff = intval($val);
15285
 
                        }
15286
 
                        $opt['aa'] .= ' /Flags '.$ff;
15287
 
                    }
15288
 
                }
15289
 
                $opt['aa'] .= ' >>';
15290
 
            } else {
15291
 
                // Javascript action or raw action command
15292
 
                $js_obj_id = $this->addJavascriptObject($action);
15293
 
                $opt['aa'] = '/D '.$js_obj_id.' 0 R';
15294
 
            }
15295
 
        }
15296
 
        $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
15297
 
        if ($this->rtl) {
15298
 
            $this->x -= $w;
15299
 
        } else {
15300
 
            $this->x += $w;
15301
 
        }
15302
 
    }
15303
 
 
15304
 
    // --- END FORMS FIELDS ------------------------------------------------
15305
 
 
15306
 
    /**
15307
 
     * Add certification signature (DocMDP or UR3)
15308
 
     * You can set only one signature type
15309
 
     * @protected
15310
 
     * @author Nicola Asuni
15311
 
     * @since 4.6.008 (2009-05-07)
15312
 
     */
15313
 
    protected function _putsignature() {
15314
 
        if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
15315
 
            return;
15316
 
        }
15317
 
        $out = $this->_getobj($this->sig_obj_id + 1)."\n";
15318
 
        $out .= '<< /Type /Sig';
15319
 
        $out .= ' /Filter /Adobe.PPKLite';
15320
 
        $out .= ' /SubFilter /adbe.pkcs7.detached';
15321
 
        $out .= ' '.$this->byterange_string;
15322
 
        $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>';
15323
 
        $out .= ' /Reference ['; // array of signature reference dictionaries
15324
 
        $out .= ' << /Type /SigRef';
15325
 
        if ($this->signature_data['cert_type'] > 0) {
15326
 
            $out .= ' /TransformMethod /DocMDP';
15327
 
            $out .= ' /TransformParams <<';
15328
 
            $out .= ' /Type /TransformParams';
15329
 
            $out .= ' /V /1.2';
15330
 
            $out .= ' /P '.$this->signature_data['cert_type'];
15331
 
        } else {
15332
 
            $out .= ' /TransformMethod /UR3';
15333
 
            $out .= ' /TransformParams <<';
15334
 
            $out .= ' /Type /TransformParams';
15335
 
            $out .= ' /V /2.2';
15336
 
            if (!$this->empty_string($this->ur['document'])) {
15337
 
                $out .= ' /Document['.$this->ur['document'].']';
15338
 
            }
15339
 
            if (!$this->empty_string($this->ur['form'])) {
15340
 
                $out .= ' /Form['.$this->ur['form'].']';
15341
 
            }
15342
 
            if (!$this->empty_string($this->ur['signature'])) {
15343
 
                $out .= ' /Signature['.$this->ur['signature'].']';
15344
 
            }
15345
 
            if (!$this->empty_string($this->ur['annots'])) {
15346
 
                $out .= ' /Annots['.$this->ur['annots'].']';
15347
 
            }
15348
 
            if (!$this->empty_string($this->ur['ef'])) {
15349
 
                $out .= ' /EF['.$this->ur['ef'].']';
15350
 
            }
15351
 
            if (!$this->empty_string($this->ur['formex'])) {
15352
 
                $out .= ' /FormEX['.$this->ur['formex'].']';
15353
 
            }
15354
 
        }
15355
 
        $out .= ' >>'; // close TransformParams
15356
 
        // optional digest data (values must be calculated and replaced later)
15357
 
        //$out .= ' /Data ********** 0 R';
15358
 
        //$out .= ' /DigestMethod/MD5';
15359
 
        //$out .= ' /DigestLocation[********** 34]';
15360
 
        //$out .= ' /DigestValue<********************************>';
15361
 
        $out .= ' >>';
15362
 
        $out .= ' ]'; // end of reference
15363
 
        if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) {
15364
 
            $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name']);
15365
 
        }
15366
 
        if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) {
15367
 
            $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location']);
15368
 
        }
15369
 
        if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) {
15370
 
            $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason']);
15371
 
        }
15372
 
        if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) {
15373
 
            $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo']);
15374
 
        }
15375
 
        $out .= ' /M '.$this->_datestring();
15376
 
        $out .= ' >>';
15377
 
        $out .= "\n".'endobj';
15378
 
        $this->_out($out);
15379
 
    }
15380
 
 
15381
 
    /**
15382
 
     * Set User's Rights for PDF Reader
15383
 
     * WARNING: This is experimental and currently do not work.
15384
 
     * Check the PDF Reference 8.7.1 Transform Methods,
15385
 
     * Table 8.105 Entries in the UR transform parameters dictionary
15386
 
     * @param $enable (boolean) if true enable user's rights on PDF reader
15387
 
     * @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.
15388
 
     * @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.
15389
 
     * @param $form (string) Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
15390
 
     * @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.
15391
 
     * @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
15392
 
     Names specifying additional embedded-files-related usage rights for the document.
15393
 
     * @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.
15394
 
     * @public
15395
 
     * @author Nicola Asuni
15396
 
     * @since 2.9.000 (2008-03-26)
15397
 
     */
15398
 
    public function setUserRights(
15399
 
            $enable=true,
15400
 
            $document='/FullSave',
15401
 
            $annots='/Create/Delete/Modify/Copy/Import/Export',
15402
 
            $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
15403
 
            $signature='/Modify',
15404
 
            $ef='/Create/Delete/Modify/Import',
15405
 
            $formex='') {
15406
 
        $this->ur['enabled'] = $enable;
15407
 
        $this->ur['document'] = $document;
15408
 
        $this->ur['annots'] = $annots;
15409
 
        $this->ur['form'] = $form;
15410
 
        $this->ur['signature'] = $signature;
15411
 
        $this->ur['ef'] = $ef;
15412
 
        $this->ur['formex'] = $formex;
15413
 
        if (!$this->sign) {
15414
 
            $this->setSignature('', '', '', '', 0, array());
15415
 
        }
15416
 
    }
15417
 
 
15418
 
    /**
15419
 
     * Enable document signature (requires the OpenSSL Library).
15420
 
     * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
15421
 
     * To create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
15422
 
     * To export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
15423
 
     * To convert pfx certificate to pem: openssl pkcs12 -in tcpdf.pfx -out tcpdf.crt -nodes
15424
 
     * @param $signing_cert (mixed) signing certificate (string or filename prefixed with 'file://')
15425
 
     * @param $private_key (mixed) private key (string or filename prefixed with 'file://')
15426
 
     * @param $private_key_password (string) password
15427
 
     * @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.
15428
 
     * @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.
15429
 
     * @param $info (array) array of option information: Name, Location, Reason, ContactInfo.
15430
 
     * @public
15431
 
     * @author Nicola Asuni
15432
 
     * @since 4.6.005 (2009-04-24)
15433
 
     */
15434
 
    public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
15435
 
        // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
15436
 
        // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
15437
 
        // to convert pfx certificate to pem: openssl
15438
 
        //     OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
15439
 
        $this->sign = true;
15440
 
        ++$this->n;
15441
 
        $this->sig_obj_id = $this->n; // signature widget
15442
 
        ++$this->n; // signature object ($this->sig_obj_id + 1)
15443
 
        $this->signature_data = array();
15444
 
        if (strlen($signing_cert) == 0) {
15445
 
            $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.crt';
15446
 
            $private_key_password = 'tcpdfdemo';
15447
 
        }
15448
 
        if (strlen($private_key) == 0) {
15449
 
            $private_key = $signing_cert;
15450
 
        }
15451
 
        $this->signature_data['signcert'] = $signing_cert;
15452
 
        $this->signature_data['privkey'] = $private_key;
15453
 
        $this->signature_data['password'] = $private_key_password;
15454
 
        $this->signature_data['extracerts'] = $extracerts;
15455
 
        $this->signature_data['cert_type'] = $cert_type;
15456
 
        $this->signature_data['info'] = $info;
15457
 
    }
15458
 
 
15459
 
    /**
15460
 
     * Set the digital signature appearance (a cliccable rectangle area to get signature properties)
15461
 
     * @param $x (float) Abscissa of the upper-left corner.
15462
 
     * @param $y (float) Ordinate of the upper-left corner.
15463
 
     * @param $w (float) Width of the signature area.
15464
 
     * @param $h (float) Height of the signature area.
15465
 
     * @param $page (int) option page number (if < 0 the current page is used).
15466
 
     * @public
15467
 
     * @author Nicola Asuni
15468
 
     * @since 5.3.011 (2010-06-17)
15469
 
     */
15470
 
    public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1) {
15471
 
        if (($page < 1) OR ($page > $this->numpages)) {
15472
 
            $this->signature_appearance['page'] = $this->page;
15473
 
        } else {
15474
 
            $this->signature_appearance['page'] = intval($page);
15475
 
        }
15476
 
        $a = $x * $this->k;
15477
 
        $b = $this->pagedim[($this->signature_appearance['page'])]['h'] - (($y + $h) * $this->k);
15478
 
        $c = $w * $this->k;
15479
 
        $d = $h * $this->k;
15480
 
        $this->signature_appearance['rect'] = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b+$d);
15481
 
    }
15482
 
 
15483
 
    /**
15484
 
     * Create a new page group.
15485
 
     * NOTE: call this function before calling AddPage()
15486
 
     * @param $page (int) starting group page (leave empty for next page).
15487
 
     * @public
15488
 
     * @since 3.0.000 (2008-03-27)
15489
 
     */
15490
 
    public function startPageGroup($page='') {
15491
 
        if (empty($page)) {
15492
 
            $page = $this->page + 1;
15493
 
        }
15494
 
        $this->newpagegroup[$page] = true;
15495
 
    }
15496
 
 
15497
 
    /**
15498
 
     * Defines a string alias for the total number of pages. It will be substituted as the document is closed.
15499
 
     * @param $numalias (string) The alias.
15500
 
     * @since 1.4
15501
 
     * @see getAliasNbPages(), PageNo(), Footer()
15502
 
     * @public
15503
 
     */
15504
 
    public function AliasNbPages($numalias="{nb}") {
15505
 
        $this->AliasNbPages = $numalias;
15506
 
    }
15507
 
 
15508
 
    /**
15509
 
     * Returns the string alias used for the total number of pages.
15510
 
     * If the current font is unicode type, the returned string is surrounded by additional curly braces.
15511
 
     * @return string
15512
 
     * @since 4.0.018 (2008-08-08)
15513
 
     * @see AliasNbPages(), PageNo(), Footer()
15514
 
     * @public
15515
 
     */
15516
 
    public function getAliasNbPages() {
15517
 
        if ($this->isUnicodeFont()) {
15518
 
            return '{'.$this->AliasNbPages.'}';
15519
 
        }
15520
 
        return $this->AliasNbPages;
15521
 
    }
15522
 
 
15523
 
    /**
15524
 
     * Defines a string alias for the page number. It will be substituted as the document is closed.
15525
 
     * @param $numalias (string) The alias.
15526
 
     * @since 4.5.000 (2009-01-02)
15527
 
     * @see getAliasNbPages(), PageNo(), Footer()
15528
 
     * @public
15529
 
     */
15530
 
    public function AliasNumPage($numalias="{pnb}") {
15531
 
        $this->AliasNumPage = $numalias;
15532
 
    }
15533
 
 
15534
 
    /**
15535
 
     * Returns the string alias used for the page number.
15536
 
     * If the current font is unicode type, the returned string is surrounded by additional curly braces.
15537
 
     * @return string
15538
 
     * @since 4.5.000 (2009-01-02)
15539
 
     * @see AliasNbPages(), PageNo(), Footer()
15540
 
     * @public
15541
 
     */
15542
 
    public function getAliasNumPage() {
15543
 
        if ($this->isUnicodeFont()) {
15544
 
            return '{'.$this->AliasNumPage.'}';
15545
 
        }
15546
 
        return $this->AliasNumPage;
15547
 
    }
15548
 
 
15549
 
    /**
15550
 
     * Return the current page in the group.
15551
 
     * @return current page in the group
15552
 
     * @public
15553
 
     * @since 3.0.000 (2008-03-27)
15554
 
     */
15555
 
    public function getGroupPageNo() {
15556
 
        return $this->pagegroups[$this->currpagegroup];
15557
 
    }
15558
 
 
15559
 
    /**
15560
 
     * Returns the current group page number formatted as a string.
15561
 
     * @public
15562
 
     * @since 4.3.003 (2008-11-18)
15563
 
     * @see PaneNo(), formatPageNumber()
15564
 
     */
15565
 
    public function getGroupPageNoFormatted() {
15566
 
        return $this->formatPageNumber($this->getGroupPageNo());
15567
 
    }
15568
 
 
15569
 
    /**
15570
 
     * Return the alias of the current page group
15571
 
     * If the current font is unicode type, the returned string is surrounded by additional curly braces.
15572
 
     * (will be replaced by the total number of pages in this group).
15573
 
     * @return alias of the current page group
15574
 
     * @public
15575
 
     * @since 3.0.000 (2008-03-27)
15576
 
     */
15577
 
    public function getPageGroupAlias() {
15578
 
        if ($this->isUnicodeFont()) {
15579
 
            return '{'.$this->currpagegroup.'}';
15580
 
        }
15581
 
        return $this->currpagegroup;
15582
 
    }
15583
 
 
15584
 
    /**
15585
 
     * Return the alias for the page number on the current page group
15586
 
     * If the current font is unicode type, the returned string is surrounded by additional curly braces.
15587
 
     * (will be replaced by the total number of pages in this group).
15588
 
     * @return alias of the current page group
15589
 
     * @public
15590
 
     * @since 4.5.000 (2009-01-02)
15591
 
     */
15592
 
    public function getPageNumGroupAlias() {
15593
 
        if ($this->isUnicodeFont()) {
15594
 
            return '{'.str_replace('{nb', '{pnb', $this->currpagegroup).'}';
15595
 
        }
15596
 
        return str_replace('{nb', '{pnb', $this->currpagegroup);
15597
 
    }
15598
 
 
15599
 
    /**
15600
 
     * Format the page numbers.
15601
 
     * This method can be overriden for custom formats.
15602
 
     * @param $num (int) page number
15603
 
     * @protected
15604
 
     * @since 4.2.005 (2008-11-06)
15605
 
     */
15606
 
    protected function formatPageNumber($num) {
15607
 
        return number_format((float)$num, 0, '', '.');
15608
 
    }
15609
 
 
15610
 
    /**
15611
 
     * Format the page numbers on the Table Of Content.
15612
 
     * This method can be overriden for custom formats.
15613
 
     * @param $num (int) page number
15614
 
     * @protected
15615
 
     * @since 4.5.001 (2009-01-04)
15616
 
     * @see addTOC(), addHTMLTOC()
15617
 
     */
15618
 
    protected function formatTOCPageNumber($num) {
15619
 
        return number_format((float)$num, 0, '', '.');
15620
 
    }
15621
 
 
15622
 
    /**
15623
 
     * Returns the current page number formatted as a string.
15624
 
     * @public
15625
 
     * @since 4.2.005 (2008-11-06)
15626
 
     * @see PaneNo(), formatPageNumber()
15627
 
     */
15628
 
    public function PageNoFormatted() {
15629
 
        return $this->formatPageNumber($this->PageNo());
15630
 
    }
15631
 
 
15632
 
    /**
15633
 
     * Put visibility settings.
15634
 
     * @protected
15635
 
     * @since 3.0.000 (2008-03-27)
15636
 
     */
15637
 
    protected function _putocg() {
15638
 
        $this->n_ocg_print = $this->_newobj();
15639
 
        $this->_out('<< /Type /OCG /Name '.$this->_textstring('print', $this->n_ocg_print).' /Usage << /Print <</PrintState /ON>> /View <</ViewState /OFF>> >> >>'."\n".'endobj');
15640
 
        $this->n_ocg_view = $this->_newobj();
15641
 
        $this->_out('<< /Type /OCG /Name '.$this->_textstring('view', $this->n_ocg_view).' /Usage << /Print <</PrintState /OFF>> /View <</ViewState /ON>> >> >>'."\n".'endobj');
15642
 
    }
15643
 
 
15644
 
    /**
15645
 
     * Set the visibility of the successive elements.
15646
 
     * This can be useful, for instance, to put a background
15647
 
     * image or color that will show on screen but won't print.
15648
 
     * @param $v (string) visibility mode. Legal values are: all, print, screen.
15649
 
     * @public
15650
 
     * @since 3.0.000 (2008-03-27)
15651
 
     */
15652
 
    public function setVisibility($v) {
15653
 
        if ($this->openMarkedContent) {
15654
 
            // close existing open marked-content
15655
 
            $this->_out('EMC');
15656
 
            $this->openMarkedContent = false;
15657
 
        }
15658
 
        switch($v) {
15659
 
            case 'print': {
15660
 
                $this->_out('/OC /OC1 BDC');
15661
 
                $this->openMarkedContent = true;
15662
 
                break;
15663
 
            }
15664
 
            case 'screen': {
15665
 
                $this->_out('/OC /OC2 BDC');
15666
 
                $this->openMarkedContent = true;
15667
 
                break;
15668
 
            }
15669
 
            case 'all': {
15670
 
                $this->_out('');
15671
 
                break;
15672
 
            }
15673
 
            default: {
15674
 
                $this->Error('Incorrect visibility: '.$v);
15675
 
                break;
15676
 
            }
15677
 
        }
15678
 
        $this->visibility = $v;
15679
 
    }
15680
 
 
15681
 
    /**
15682
 
     * Add transparency parameters to the current extgstate
15683
 
     * @param $parms (array) parameters
15684
 
     * @return the number of extgstates
15685
 
     * @protected
15686
 
     * @since 3.0.000 (2008-03-27)
15687
 
     */
15688
 
    protected function addExtGState($parms) {
15689
 
        $n = count($this->extgstates) + 1;
15690
 
        // check if this ExtGState already exist
15691
 
        for ($i = 1; $i < $n; ++$i) {
15692
 
            if ($this->extgstates[$i]['parms'] == $parms) {
15693
 
                // return reference to existing ExtGState
15694
 
                return $i;
15695
 
            }
15696
 
        }
15697
 
        $this->extgstates[$n]['parms'] = $parms;
15698
 
        return $n;
15699
 
    }
15700
 
 
15701
 
    /**
15702
 
     * Add an extgstate
15703
 
     * @param $gs (array) extgstate
15704
 
     * @protected
15705
 
     * @since 3.0.000 (2008-03-27)
15706
 
     */
15707
 
    protected function setExtGState($gs) {
15708
 
        $this->_out(sprintf('/GS%d gs', $gs));
15709
 
    }
15710
 
 
15711
 
    /**
15712
 
     * Put extgstates for object transparency
15713
 
     * @protected
15714
 
     * @since 3.0.000 (2008-03-27)
15715
 
     */
15716
 
    protected function _putextgstates() {
15717
 
        $ne = count($this->extgstates);
15718
 
        for ($i = 1; $i <= $ne; ++$i) {
15719
 
            $this->extgstates[$i]['n'] = $this->_newobj();
15720
 
            $out = '<< /Type /ExtGState';
15721
 
            foreach ($this->extgstates[$i]['parms'] as $k => $v) {
15722
 
                if (is_float($v)) {
15723
 
                    $v = sprintf('%.2F', $v);
15724
 
                }
15725
 
                $out .= ' /'.$k.' '.$v;
15726
 
            }
15727
 
            $out .= ' >>';
15728
 
            $out .= "\n".'endobj';
15729
 
            $this->_out($out);
15730
 
        }
15731
 
    }
15732
 
 
15733
 
    /**
15734
 
     * Set alpha for stroking (CA) and non-stroking (ca) operations.
15735
 
     * @param $alpha (float) real value from 0 (transparent) to 1 (opaque)
15736
 
     * @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
15737
 
     * @public
15738
 
     * @since 3.0.000 (2008-03-27)
15739
 
     */
15740
 
    public function setAlpha($alpha, $bm='Normal') {
15741
 
        $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm, 'AIS' => 'false'));
15742
 
        $this->setExtGState($gs);
15743
 
    }
15744
 
 
15745
 
    /**
15746
 
     * Set the default JPEG compression quality (1-100)
15747
 
     * @param $quality (int) JPEG quality, integer between 1 and 100
15748
 
     * @public
15749
 
     * @since 3.0.000 (2008-03-27)
15750
 
     */
15751
 
    public function setJPEGQuality($quality) {
15752
 
        if (($quality < 1) OR ($quality > 100)) {
15753
 
            $quality = 75;
15754
 
        }
15755
 
        $this->jpeg_quality = intval($quality);
15756
 
    }
15757
 
 
15758
 
    /**
15759
 
     * Set the default number of columns in a row for HTML tables.
15760
 
     * @param $cols (int) number of columns
15761
 
     * @public
15762
 
     * @since 3.0.014 (2008-06-04)
15763
 
     */
15764
 
    public function setDefaultTableColumns($cols=4) {
15765
 
        $this->default_table_columns = intval($cols);
15766
 
    }
15767
 
 
15768
 
    /**
15769
 
     * Set the height of the cell (line height) respect the font height.
15770
 
     * @param $h (int) cell proportion respect font height (typical value = 1.25).
15771
 
     * @public
15772
 
     * @since 3.0.014 (2008-06-04)
15773
 
     */
15774
 
    public function setCellHeightRatio($h) {
15775
 
        $this->cell_height_ratio = $h;
15776
 
    }
15777
 
 
15778
 
    /**
15779
 
     * return the height of cell repect font height.
15780
 
     * @public
15781
 
     * @since 4.0.012 (2008-07-24)
15782
 
     */
15783
 
    public function getCellHeightRatio() {
15784
 
        return $this->cell_height_ratio;
15785
 
    }
15786
 
 
15787
 
    /**
15788
 
     * Set the PDF version (check PDF reference for valid values).
15789
 
     * @param $version (string) PDF document version.
15790
 
     * @public
15791
 
     * @since 3.1.000 (2008-06-09)
15792
 
     */
15793
 
    public function setPDFVersion($version='1.7') {
15794
 
        $this->PDFVersion = $version;
15795
 
    }
15796
 
 
15797
 
    /**
15798
 
     * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
15799
 
     * (see Section 8.1 of PDF reference, "Viewer Preferences").
15800
 
     * <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>
15801
 
     * @param $preferences (array) array of options.
15802
 
     * @author Nicola Asuni
15803
 
     * @public
15804
 
     * @since 3.1.000 (2008-06-09)
15805
 
     */
15806
 
    public function setViewerPreferences($preferences) {
15807
 
        $this->viewer_preferences = $preferences;
15808
 
    }
15809
 
 
15810
 
    /**
15811
 
     * Paints color transition registration bars
15812
 
     * @param $x (float) abscissa of the top left corner of the rectangle.
15813
 
     * @param $y (float) ordinate of the top left corner of the rectangle.
15814
 
     * @param $w (float) width of the rectangle.
15815
 
     * @param $h (float) height of the rectangle.
15816
 
     * @param $transition (boolean) if true prints tcolor transitions to white.
15817
 
     * @param $vertical (boolean) if true prints bar vertically.
15818
 
     * @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.
15819
 
     * @author Nicola Asuni
15820
 
     * @since 4.9.000 (2010-03-26)
15821
 
     * @public
15822
 
     */
15823
 
    public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
15824
 
        $bars = explode(',', $colors);
15825
 
        $numbars = count($bars); // number of bars to print
15826
 
        // set bar measures
15827
 
        if ($vertical) {
15828
 
            $coords = array(0, 0, 0, 1);
15829
 
            $wb = $w / $numbars; // bar width
15830
 
            $hb = $h; // bar height
15831
 
            $xd = $wb; // delta x
15832
 
            $yd = 0; // delta y
15833
 
        } else {
15834
 
            $coords = array(1, 0, 0, 0);
15835
 
            $wb = $w; // bar width
15836
 
            $hb = $h / $numbars; // bar height
15837
 
            $xd = 0; // delta x
15838
 
            $yd = $hb; // delta y
15839
 
        }
15840
 
        $xb = $x;
15841
 
        $yb = $y;
15842
 
        foreach ($bars as $col) {
15843
 
            switch ($col) {
15844
 
                // set transition colors
15845
 
                case 'A': { // BLACK
15846
 
                    $col_a = array(255);
15847
 
                    $col_b = array(0);
15848
 
                    break;
15849
 
                }
15850
 
                case 'W': { // WHITE
15851
 
                    $col_a = array(0);
15852
 
                    $col_b = array(255);
15853
 
                    break;
15854
 
                }
15855
 
                case 'R': { // R
15856
 
                    $col_a = array(255,255,255);
15857
 
                    $col_b = array(255,0,0);
15858
 
                    break;
15859
 
                }
15860
 
                case 'G': { // G
15861
 
                    $col_a = array(255,255,255);
15862
 
                    $col_b = array(0,255,0);
15863
 
                    break;
15864
 
                }
15865
 
                case 'B': { // B
15866
 
                    $col_a = array(255,255,255);
15867
 
                    $col_b = array(0,0,255);
15868
 
                    break;
15869
 
                }
15870
 
                case 'C': { // C
15871
 
                    $col_a = array(0,0,0,0);
15872
 
                    $col_b = array(100,0,0,0);
15873
 
                    break;
15874
 
                }
15875
 
                case 'M': { // M
15876
 
                    $col_a = array(0,0,0,0);
15877
 
                    $col_b = array(0,100,0,0);
15878
 
                    break;
15879
 
                }
15880
 
                case 'Y': { // Y
15881
 
                    $col_a = array(0,0,0,0);
15882
 
                    $col_b = array(0,0,100,0);
15883
 
                    break;
15884
 
                }
15885
 
                case 'K': { // K
15886
 
                    $col_a = array(0,0,0,0);
15887
 
                    $col_b = array(0,0,0,100);
15888
 
                    break;
15889
 
                }
15890
 
                default: { // GRAY
15891
 
                    $col_a = array(255);
15892
 
                    $col_b = array(0);
15893
 
                    break;
15894
 
                }
15895
 
            }
15896
 
            if ($transition) {
15897
 
                // color gradient
15898
 
                $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
15899
 
            } else {
15900
 
                // color rectangle
15901
 
                $this->SetFillColorArray($col_b);
15902
 
                $this->Rect($xb, $yb, $wb, $hb, 'F', array());
15903
 
            }
15904
 
            $xb += $xd;
15905
 
            $yb += $yd;
15906
 
        }
15907
 
    }
15908
 
 
15909
 
    /**
15910
 
     * Paints crop mark
15911
 
     * @param $x (float) abscissa of the crop mark center.
15912
 
     * @param $y (float) ordinate of the crop mark center.
15913
 
     * @param $w (float) width of the crop mark.
15914
 
     * @param $h (float) height of the crop mark.
15915
 
     * @param $type (string) type of crop mark, one sybol per type separated by comma: A = top left, B = top right, C = bottom left, D = bottom right.
15916
 
     * @param $color (array) crop mark color (default black).
15917
 
     * @author Nicola Asuni
15918
 
     * @since 4.9.000 (2010-03-26)
15919
 
     * @public
15920
 
     */
15921
 
    public function cropMark($x, $y, $w, $h, $type='A,B,C,D', $color=array(0,0,0)) {
15922
 
        $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
15923
 
        $crops = explode(',', $type);
15924
 
        $numcrops = count($crops); // number of crop marks to print
15925
 
        $dw = $w / 4; // horizontal space to leave before the intersection point
15926
 
        $dh = $h / 4; // vertical space to leave before the intersection point
15927
 
        foreach ($crops as $crop) {
15928
 
            switch ($crop) {
15929
 
                case 'A': {
15930
 
                    $x1 = $x;
15931
 
                    $y1 = $y - $h;
15932
 
                    $x2 = $x;
15933
 
                    $y2 = $y - $dh;
15934
 
                    $x3 = $x - $w;
15935
 
                    $y3 = $y;
15936
 
                    $x4 = $x - $dw;
15937
 
                    $y4 = $y;
15938
 
                    break;
15939
 
                }
15940
 
                case 'B': {
15941
 
                    $x1 = $x;
15942
 
                    $y1 = $y - $h;
15943
 
                    $x2 = $x;
15944
 
                    $y2 = $y - $dh;
15945
 
                    $x3 = $x + $dw;
15946
 
                    $y3 = $y;
15947
 
                    $x4 = $x + $w;
15948
 
                    $y4 = $y;
15949
 
                    break;
15950
 
                }
15951
 
                case 'C': {
15952
 
                    $x1 = $x - $w;
15953
 
                    $y1 = $y;
15954
 
                    $x2 = $x - $dw;
15955
 
                    $y2 = $y;
15956
 
                    $x3 = $x;
15957
 
                    $y3 = $y + $dh;
15958
 
                    $x4 = $x;
15959
 
                    $y4 = $y + $h;
15960
 
                    break;
15961
 
                }
15962
 
                case 'D': {
15963
 
                    $x1 = $x + $dw;
15964
 
                    $y1 = $y;
15965
 
                    $x2 = $x + $w;
15966
 
                    $y2 = $y;
15967
 
                    $x3 = $x;
15968
 
                    $y3 = $y + $dh;
15969
 
                    $x4 = $x;
15970
 
                    $y4 = $y + $h;
15971
 
                    break;
15972
 
                }
15973
 
            }
15974
 
            $this->Line($x1, $y1, $x2, $y2);
15975
 
            $this->Line($x3, $y3, $x4, $y4);
15976
 
        }
15977
 
    }
15978
 
 
15979
 
    /**
15980
 
     * Paints a registration mark
15981
 
     * @param $x (float) abscissa of the registration mark center.
15982
 
     * @param $y (float) ordinate of the registration mark center.
15983
 
     * @param $r (float) radius of the crop mark.
15984
 
     * @param $double (boolean) if true print two concentric crop marks.
15985
 
     * @param $cola (array) crop mark color (default black).
15986
 
     * @param $colb (array) second crop mark color.
15987
 
     * @author Nicola Asuni
15988
 
     * @since 4.9.000 (2010-03-26)
15989
 
     * @public
15990
 
     */
15991
 
    public function registrationMark($x, $y, $r, $double=false, $cola=array(0,0,0), $colb=array(255,255,255)) {
15992
 
        $line_style = array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
15993
 
        $this->SetFillColorArray($cola);
15994
 
        $this->PieSector($x, $y, $r, 90, 180, 'F');
15995
 
        $this->PieSector($x, $y, $r, 270, 360, 'F');
15996
 
        $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
15997
 
        if ($double) {
15998
 
            $r2 = $r * 0.5;
15999
 
            $this->SetFillColorArray($colb);
16000
 
            $this->PieSector($x, $y, $r2, 90, 180, 'F');
16001
 
            $this->PieSector($x, $y, $r2, 270, 360, 'F');
16002
 
            $this->SetFillColorArray($cola);
16003
 
            $this->PieSector($x, $y, $r2, 0, 90, 'F');
16004
 
            $this->PieSector($x, $y, $r2, 180, 270, 'F');
16005
 
            $this->Circle($x, $y, $r2, 0, 360, 'C', $line_style, array(), 8);
16006
 
        }
16007
 
    }
16008
 
 
16009
 
    /**
16010
 
     * Paints a linear colour gradient.
16011
 
     * @param $x (float) abscissa of the top left corner of the rectangle.
16012
 
     * @param $y (float) ordinate of the top left corner of the rectangle.
16013
 
     * @param $w (float) width of the rectangle.
16014
 
     * @param $h (float) height of the rectangle.
16015
 
     * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
16016
 
     * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
16017
 
     * @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).
16018
 
     * @author Andreas W�rmser, Nicola Asuni
16019
 
     * @since 3.1.000 (2008-06-09)
16020
 
     * @public
16021
 
     */
16022
 
    public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
16023
 
        $this->Clip($x, $y, $w, $h);
16024
 
        $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
16025
 
    }
16026
 
 
16027
 
    /**
16028
 
     * Paints a radial colour gradient.
16029
 
     * @param $x (float) abscissa of the top left corner of the rectangle.
16030
 
     * @param $y (float) ordinate of the top left corner of the rectangle.
16031
 
     * @param $w (float) width of the rectangle.
16032
 
     * @param $h (float) height of the rectangle.
16033
 
     * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
16034
 
     * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
16035
 
     * @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.
16036
 
     * @author Andreas W�rmser, Nicola Asuni
16037
 
     * @since 3.1.000 (2008-06-09)
16038
 
     * @public
16039
 
     */
16040
 
    public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
16041
 
        $this->Clip($x, $y, $w, $h);
16042
 
        $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
16043
 
    }
16044
 
 
16045
 
    /**
16046
 
     * Paints a coons patch mesh.
16047
 
     * @param $x (float) abscissa of the top left corner of the rectangle.
16048
 
     * @param $y (float) ordinate of the top left corner of the rectangle.
16049
 
     * @param $w (float) width of the rectangle.
16050
 
     * @param $h (float) height of the rectangle.
16051
 
     * @param $col1 (array) first color (lower left corner) (RGB components).
16052
 
     * @param $col2 (array) second color (lower right corner) (RGB components).
16053
 
     * @param $col3 (array) third color (upper right corner) (RGB components).
16054
 
     * @param $col4 (array) fourth color (upper left corner) (RGB components).
16055
 
     * @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>
16056
 
     * @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
16057
 
     * @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
16058
 
     * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
16059
 
     * @author Andreas W�rmser, Nicola Asuni
16060
 
     * @since 3.1.000 (2008-06-09)
16061
 
     * @public
16062
 
     */
16063
 
    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) {
16064
 
        $this->Clip($x, $y, $w, $h);
16065
 
        $n = count($this->gradients) + 1;
16066
 
        $this->gradients[$n] = array();
16067
 
        $this->gradients[$n]['type'] = 6; //coons patch mesh
16068
 
        $this->gradients[$n]['coords'] = array();
16069
 
        $this->gradients[$n]['antialias'] = $antialias;
16070
 
        $this->gradients[$n]['colors'] = array();
16071
 
        $this->gradients[$n]['transparency'] = false;
16072
 
        //check the coords array if it is the simple array or the multi patch array
16073
 
        if (!isset($coords[0]['f'])) {
16074
 
            //simple array -> convert to multi patch array
16075
 
            if (!isset($col1[1])) {
16076
 
                $col1[1] = $col1[2] = $col1[0];
16077
 
            }
16078
 
            if (!isset($col2[1])) {
16079
 
                $col2[1] = $col2[2] = $col2[0];
16080
 
            }
16081
 
            if (!isset($col3[1])) {
16082
 
                $col3[1] = $col3[2] = $col3[0];
16083
 
            }
16084
 
            if (!isset($col4[1])) {
16085
 
                $col4[1] = $col4[2] = $col4[0];
16086
 
            }
16087
 
            $patch_array[0]['f'] = 0;
16088
 
            $patch_array[0]['points'] = $coords;
16089
 
            $patch_array[0]['colors'][0]['r'] = $col1[0];
16090
 
            $patch_array[0]['colors'][0]['g'] = $col1[1];
16091
 
            $patch_array[0]['colors'][0]['b'] = $col1[2];
16092
 
            $patch_array[0]['colors'][1]['r'] = $col2[0];
16093
 
            $patch_array[0]['colors'][1]['g'] = $col2[1];
16094
 
            $patch_array[0]['colors'][1]['b'] = $col2[2];
16095
 
            $patch_array[0]['colors'][2]['r'] = $col3[0];
16096
 
            $patch_array[0]['colors'][2]['g'] = $col3[1];
16097
 
            $patch_array[0]['colors'][2]['b'] = $col3[2];
16098
 
            $patch_array[0]['colors'][3]['r'] = $col4[0];
16099
 
            $patch_array[0]['colors'][3]['g'] = $col4[1];
16100
 
            $patch_array[0]['colors'][3]['b'] = $col4[2];
16101
 
        } else {
16102
 
            //multi patch array
16103
 
            $patch_array = $coords;
16104
 
        }
16105
 
        $bpcd = 65535; //16 bits per coordinate
16106
 
        //build the data stream
16107
 
        $this->gradients[$n]['stream'] = '';
16108
 
        $count_patch = count($patch_array);
16109
 
        for ($i=0; $i < $count_patch; ++$i) {
16110
 
            $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
16111
 
            $count_points = count($patch_array[$i]['points']);
16112
 
            for ($j=0; $j < $count_points; ++$j) {
16113
 
                //each point as 16 bit
16114
 
                $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
16115
 
                if ($patch_array[$i]['points'][$j] < 0) {
16116
 
                    $patch_array[$i]['points'][$j] = 0;
16117
 
                }
16118
 
                if ($patch_array[$i]['points'][$j] > $bpcd) {
16119
 
                    $patch_array[$i]['points'][$j] = $bpcd;
16120
 
                }
16121
 
                $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
16122
 
                $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
16123
 
            }
16124
 
            $count_cols = count($patch_array[$i]['colors']);
16125
 
            for ($j=0; $j < $count_cols; ++$j) {
16126
 
                //each color component as 8 bit
16127
 
                $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
16128
 
                $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
16129
 
                $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
16130
 
            }
16131
 
        }
16132
 
        //paint the gradient
16133
 
        $this->_out('/Sh'.$n.' sh');
16134
 
        //restore previous Graphic State
16135
 
        $this->_out('Q');
16136
 
    }
16137
 
 
16138
 
    /**
16139
 
     * Set a rectangular clipping area.
16140
 
     * @param $x (float) abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
16141
 
     * @param $y (float) ordinate of the top left corner of the rectangle.
16142
 
     * @param $w (float) width of the rectangle.
16143
 
     * @param $h (float) height of the rectangle.
16144
 
     * @author Andreas W�rmser, Nicola Asuni
16145
 
     * @since 3.1.000 (2008-06-09)
16146
 
     * @protected
16147
 
     */
16148
 
    protected function Clip($x, $y, $w, $h) {
16149
 
        if ($this->rtl) {
16150
 
            $x = $this->w - $x - $w;
16151
 
        }
16152
 
        //save current Graphic State
16153
 
        $s = 'q';
16154
 
        //set clipping area
16155
 
        $s .= sprintf(' %.2F %.2F %.2F %.2F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
16156
 
        //set up transformation matrix for gradient
16157
 
        $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
16158
 
        $this->_out($s);
16159
 
    }
16160
 
 
16161
 
    /**
16162
 
     * Output gradient.
16163
 
     * @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)
16164
 
     * @param $coords (array) array of coordinates.
16165
 
     * @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).
16166
 
     * @param $background (array) An array of colour components appropriate to the colour space, specifying a single background colour value.
16167
 
     * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
16168
 
     * @author Nicola Asuni
16169
 
     * @since 3.1.000 (2008-06-09)
16170
 
     * @public
16171
 
     */
16172
 
    public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
16173
 
        $n = count($this->gradients) + 1;
16174
 
        $this->gradients[$n] = array();
16175
 
        $this->gradients[$n]['type'] = $type;
16176
 
        $this->gradients[$n]['coords'] = $coords;
16177
 
        $this->gradients[$n]['antialias'] = $antialias;
16178
 
        $this->gradients[$n]['colors'] = array();
16179
 
        $this->gradients[$n]['transparency'] = false;
16180
 
        // color space
16181
 
        $numcolspace = count($stops[0]['color']);
16182
 
        $bcolor = array_values($background);
16183
 
        switch($numcolspace) {
16184
 
            case 4: { // CMYK
16185
 
                $this->gradients[$n]['colspace'] = 'DeviceCMYK';
16186
 
                if (!empty($background)) {
16187
 
                    $this->gradients[$n]['background'] = sprintf('%.3F %.3F %.3F %.3F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
16188
 
                }
16189
 
                break;
16190
 
            }
16191
 
            case 3: { // RGB
16192
 
                $this->gradients[$n]['colspace'] = 'DeviceRGB';
16193
 
                if (!empty($background)) {
16194
 
                    $this->gradients[$n]['background'] = sprintf('%.3F %.3F %.3F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
16195
 
                }
16196
 
                break;
16197
 
            }
16198
 
            case 1: { // Gray scale
16199
 
                $this->gradients[$n]['colspace'] = 'DeviceGray';
16200
 
                if (!empty($background)) {
16201
 
                    $this->gradients[$n]['background'] = sprintf('%.3F', $bcolor[0]/255);
16202
 
                }
16203
 
                break;
16204
 
            }
16205
 
        }
16206
 
        $num_stops = count($stops);
16207
 
        $last_stop_id = $num_stops - 1;
16208
 
        foreach ($stops as $key => $stop) {
16209
 
            $this->gradients[$n]['colors'][$key] = array();
16210
 
            // offset represents a location along the gradient vector
16211
 
            if (isset($stop['offset'])) {
16212
 
                $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset'];
16213
 
            } else {
16214
 
                if ($key == 0) {
16215
 
                    $this->gradients[$n]['colors'][$key]['offset'] = 0;
16216
 
                } elseif ($key == $last_stop_id) {
16217
 
                    $this->gradients[$n]['colors'][$key]['offset'] = 1;
16218
 
                } else {
16219
 
                    $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
16220
 
                    $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep;
16221
 
                }
16222
 
            }
16223
 
            if (isset($stop['opacity'])) {
16224
 
                $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity'];
16225
 
                if ($stop['opacity'] < 1) {
16226
 
                    $this->gradients[$n]['transparency'] = true;
16227
 
                }
16228
 
            } else {
16229
 
                $this->gradients[$n]['colors'][$key]['opacity'] = 1;
16230
 
            }
16231
 
            // exponent for the exponential interpolation function
16232
 
            if (isset($stop['exponent'])) {
16233
 
                $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent'];
16234
 
            } else {
16235
 
                $this->gradients[$n]['colors'][$key]['exponent'] = 1;
16236
 
            }
16237
 
            // set colors
16238
 
            $color = array_values($stop['color']);
16239
 
            switch($numcolspace) {
16240
 
                case 4: { // CMYK
16241
 
                    $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F %.3F %.3F %.3F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
16242
 
                    break;
16243
 
                }
16244
 
                case 3: { // RGB
16245
 
                    $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F %.3F %.3F', $color[0]/255, $color[1]/255, $color[2]/255);
16246
 
                    break;
16247
 
                }
16248
 
                case 1: { // Gray scale
16249
 
                    $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F', $color[0]/255);
16250
 
                    break;
16251
 
                }
16252
 
            }
16253
 
        }
16254
 
        if ($this->gradients[$n]['transparency']) {
16255
 
            // paint luminosity gradient
16256
 
            $this->_out('/TGS'.$n.' gs');
16257
 
        }
16258
 
        //paint the gradient
16259
 
        $this->_out('/Sh'.$n.' sh');
16260
 
        //restore previous Graphic State
16261
 
        $this->_out('Q');
16262
 
    }
16263
 
 
16264
 
    /**
16265
 
     * Output gradient shaders.
16266
 
     * @author Nicola Asuni
16267
 
     * @since 3.1.000 (2008-06-09)
16268
 
     * @protected
16269
 
     */
16270
 
    function _putshaders() {
16271
 
        $idt = count($this->gradients); //index for transparency gradients
16272
 
        foreach ($this->gradients as $id => $grad) {
16273
 
            if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
16274
 
                $fc = $this->_newobj();
16275
 
                $out = '<<';
16276
 
                $out .= ' /FunctionType 3';
16277
 
                $out .= ' /Domain [0 1]';
16278
 
                $functions = '';
16279
 
                $bounds = '';
16280
 
                $encode = '';
16281
 
                $i = 1;
16282
 
                $num_cols = count($grad['colors']);
16283
 
                $lastcols = $num_cols - 1;
16284
 
                for ($i = 1; $i < $num_cols; ++$i) {
16285
 
                    $functions .= ($fc + $i).' 0 R ';
16286
 
                    if ($i < $lastcols) {
16287
 
                        $bounds .= sprintf('%.3F ', $grad['colors'][$i]['offset']);
16288
 
                    }
16289
 
                    $encode .= '0 1 ';
16290
 
                }
16291
 
                $out .= ' /Functions ['.trim($functions).']';
16292
 
                $out .= ' /Bounds ['.trim($bounds).']';
16293
 
                $out .= ' /Encode ['.trim($encode).']';
16294
 
                $out .= ' >>';
16295
 
                $out .= "\n".'endobj';
16296
 
                $this->_out($out);
16297
 
                for ($i = 1; $i < $num_cols; ++$i) {
16298
 
                    $this->_newobj();
16299
 
                    $out = '<<';
16300
 
                    $out .= ' /FunctionType 2';
16301
 
                    $out .= ' /Domain [0 1]';
16302
 
                    $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
16303
 
                    $out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
16304
 
                    $out .= ' /N '.$grad['colors'][$i]['exponent'];
16305
 
                    $out .= ' >>';
16306
 
                    $out .= "\n".'endobj';
16307
 
                    $this->_out($out);
16308
 
                }
16309
 
                // set transparency fuctions
16310
 
                if ($grad['transparency']) {
16311
 
                    $ft = $this->_newobj();
16312
 
                    $out = '<<';
16313
 
                    $out .= ' /FunctionType 3';
16314
 
                    $out .= ' /Domain [0 1]';
16315
 
                    $functions = '';
16316
 
                    $i = 1;
16317
 
                    $num_cols = count($grad['colors']);
16318
 
                    for ($i = 1; $i < $num_cols; ++$i) {
16319
 
                        $functions .= ($ft + $i).' 0 R ';
16320
 
                    }
16321
 
                    $out .= ' /Functions ['.trim($functions).']';
16322
 
                    $out .= ' /Bounds ['.trim($bounds).']';
16323
 
                    $out .= ' /Encode ['.trim($encode).']';
16324
 
                    $out .= ' >>';
16325
 
                    $out .= "\n".'endobj';
16326
 
                    $this->_out($out);
16327
 
                    for ($i = 1; $i < $num_cols; ++$i) {
16328
 
                        $this->_newobj();
16329
 
                        $out = '<<';
16330
 
                        $out .= ' /FunctionType 2';
16331
 
                        $out .= ' /Domain [0 1]';
16332
 
                        $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
16333
 
                        $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
16334
 
                        $out .= ' /N '.$grad['colors'][$i]['exponent'];
16335
 
                        $out .= ' >>';
16336
 
                        $out .= "\n".'endobj';
16337
 
                        $this->_out($out);
16338
 
                    }
16339
 
                }
16340
 
            }
16341
 
            // set shading object
16342
 
            $this->_newobj();
16343
 
            $out = '<< /ShadingType '.$grad['type'];
16344
 
            if (isset($grad['colspace'])) {
16345
 
                $out .= ' /ColorSpace /'.$grad['colspace'];
16346
 
            } else {
16347
 
                $out .= ' /ColorSpace /DeviceRGB';
16348
 
            }
16349
 
            if (isset($grad['background']) AND !empty($grad['background'])) {
16350
 
                $out .= ' /Background ['.$grad['background'].']';
16351
 
            }
16352
 
            if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
16353
 
                $out .= ' /AntiAlias true';
16354
 
            }
16355
 
            if ($grad['type'] == 2) {
16356
 
                $out .= ' '.sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
16357
 
                $out .= ' /Domain [0 1]';
16358
 
                $out .= ' /Function '.$fc.' 0 R';
16359
 
                $out .= ' /Extend [true true]';
16360
 
                $out .= ' >>';
16361
 
            } elseif ($grad['type'] == 3) {
16362
 
                //x0, y0, r0, x1, y1, r1
16363
 
                //at this this time radius of inner circle is 0
16364
 
                $out .= ' '.sprintf('/Coords [%.3F %.3F 0 %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
16365
 
                $out .= ' /Domain [0 1]';
16366
 
                $out .= ' /Function '.$fc.' 0 R';
16367
 
                $out .= ' /Extend [true true]';
16368
 
                $out .= ' >>';
16369
 
            } elseif ($grad['type'] == 6) {
16370
 
                $out .= ' /BitsPerCoordinate 16';
16371
 
                $out .= ' /BitsPerComponent 8';
16372
 
                $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
16373
 
                $out .= ' /BitsPerFlag 8';
16374
 
                $stream = $this->_getrawstream($grad['stream']);
16375
 
                $out .= ' /Length '.strlen($stream);
16376
 
                $out .= ' >>';
16377
 
                $out .= ' stream'."\n".$stream."\n".'endstream';
16378
 
            }
16379
 
            $out .= "\n".'endobj';
16380
 
            $this->_out($out);
16381
 
            if ($grad['transparency']) {
16382
 
                $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
16383
 
                $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
16384
 
            }
16385
 
            $this->gradients[$id]['id'] = $this->n;
16386
 
            // set pattern object
16387
 
            $this->_newobj();
16388
 
            $out = '<< /Type /Pattern /PatternType 2';
16389
 
            $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R';
16390
 
            $out .= ' >>';
16391
 
            $out .= "\n".'endobj';
16392
 
            $this->_out($out);
16393
 
            $this->gradients[$id]['pattern'] = $this->n;
16394
 
            // set shading and pattern for transparency mask
16395
 
            if ($grad['transparency']) {
16396
 
                // luminosity pattern
16397
 
                $idgs = $id + $idt;
16398
 
                $this->_newobj();
16399
 
                $this->_out($shading_transparency);
16400
 
                $this->gradients[$idgs]['id'] = $this->n;
16401
 
                $this->_newobj();
16402
 
                $out = '<< /Type /Pattern /PatternType 2';
16403
 
                $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R';
16404
 
                $out .= ' >>';
16405
 
                $out .= "\n".'endobj';
16406
 
                $this->_out($out);
16407
 
                $this->gradients[$idgs]['pattern'] = $this->n;
16408
 
                // luminosity XObject
16409
 
                $oid = $this->_newobj();
16410
 
                $this->xobjects['LX'.$oid] = array('n' => $oid);
16411
 
                $filter = '';
16412
 
                $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q';
16413
 
                if ($this->compress) {
16414
 
                    $filter = ' /Filter /FlateDecode';
16415
 
                    $stream = gzcompress($stream);
16416
 
                }
16417
 
                $stream = $this->_getrawstream($stream);
16418
 
                $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
16419
 
                $out .= ' /Length '.strlen($stream);
16420
 
                $rect = sprintf('%.2F %.2F', $this->wPt, $this->hPt);
16421
 
                $out .= ' /BBox [0 0 '.$rect.']';
16422
 
                $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
16423
 
                $out .= ' /Resources <<';
16424
 
                $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
16425
 
                $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>';
16426
 
                $out .= ' >>';
16427
 
                $out .= ' >> ';
16428
 
                $out .= ' stream'."\n".$stream."\n".'endstream';
16429
 
                $out .= "\n".'endobj';
16430
 
                $this->_out($out);
16431
 
                // SMask
16432
 
                $this->_newobj();
16433
 
                $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj';
16434
 
                $this->_out($out);
16435
 
                // ExtGState
16436
 
                $this->_newobj();
16437
 
                $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj';
16438
 
                $this->_out($out);
16439
 
                $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id);
16440
 
            }
16441
 
        }
16442
 
    }
16443
 
 
16444
 
    /**
16445
 
     * Draw the sector of a circle.
16446
 
     * It can be used for instance to render pie charts.
16447
 
     * @param $xc (float) abscissa of the center.
16448
 
     * @param $yc (float) ordinate of the center.
16449
 
     * @param $r (float) radius.
16450
 
     * @param $a (float) start angle (in degrees).
16451
 
     * @param $b (float) end angle (in degrees).
16452
 
     * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
16453
 
     * @param $cw: (float) indicates whether to go clockwise (default: true).
16454
 
     * @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.
16455
 
     * @author Maxime Delorme, Nicola Asuni
16456
 
     * @since 3.1.000 (2008-06-09)
16457
 
     * @public
16458
 
     */
16459
 
    public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
16460
 
        $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
16461
 
    }
16462
 
 
16463
 
    /**
16464
 
     * Draw the sector of an ellipse.
16465
 
     * It can be used for instance to render pie charts.
16466
 
     * @param $xc (float) abscissa of the center.
16467
 
     * @param $yc (float) ordinate of the center.
16468
 
     * @param $rx (float) the x-axis radius.
16469
 
     * @param $ry (float) the y-axis radius.
16470
 
     * @param $a (float) start angle (in degrees).
16471
 
     * @param $b (float) end angle (in degrees).
16472
 
     * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
16473
 
     * @param $cw: (float) indicates whether to go clockwise.
16474
 
     * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock).
16475
 
     * @param $nc (integer) Number of curves used to draw a 90 degrees portion of arc.
16476
 
     * @author Maxime Delorme, Nicola Asuni
16477
 
     * @since 3.1.000 (2008-06-09)
16478
 
     * @public
16479
 
     */
16480
 
    public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
16481
 
        if ($this->rtl) {
16482
 
            $xc = $this->w - $xc;
16483
 
        }
16484
 
        $op = $this->getPathPaintOperator($style);
16485
 
        if ($op == 'f') {
16486
 
            $line_style = array();
16487
 
        }
16488
 
        if ($cw) {
16489
 
            $d = $b;
16490
 
            $b = 360 - $a + $o;
16491
 
            $a = 360 - $d + $o;
16492
 
        } else {
16493
 
            $b += $o;
16494
 
            $a += $o;
16495
 
        }
16496
 
        $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
16497
 
        $this->_out($op);
16498
 
    }
16499
 
 
16500
 
    /**
16501
 
     * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
16502
 
     * NOTE: EPS is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
16503
 
     * Only vector drawing is supported, not text or bitmap.
16504
 
     * 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).
16505
 
     * @param $file (string) Name of the file containing the image or a '@' character followed by the EPS/AI data string.
16506
 
     * @param $x (float) Abscissa of the upper-left corner.
16507
 
     * @param $y (float) Ordinate of the upper-left corner.
16508
 
     * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
16509
 
     * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
16510
 
     * @param $link (mixed) URL or identifier returned by AddLink().
16511
 
     * @param $useBoundingBox (boolean) specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
16512
 
     * @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>
16513
 
     * @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>
16514
 
     * @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)))
16515
 
     * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
16516
 
     * @param $fixoutvals (boolean) if true remove values outside the bounding box.
16517
 
     * @author Valentin Schmidt, Nicola Asuni
16518
 
     * @since 3.1.000 (2008-06-09)
16519
 
     * @public
16520
 
     */
16521
 
    public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
16522
 
        if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
16523
 
            // convert EPS to raster image using GD or ImageMagick libraries
16524
 
            return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
16525
 
        }
16526
 
        if ($x === '') {
16527
 
            $x = $this->x;
16528
 
        }
16529
 
        if ($y === '') {
16530
 
            $y = $this->y;
16531
 
        }
16532
 
        // check page for no-write regions and adapt page margins if necessary
16533
 
        $this->checkPageRegions($h, $x, $y);
16534
 
        $k = $this->k;
16535
 
        if ($file{0} === '@') { // image from string
16536
 
            $data = substr($file, 1);
16537
 
        } else { // EPS/AI file
16538
 
            $data = file_get_contents($file);
16539
 
        }
16540
 
        if ($data === false) {
16541
 
            $this->Error('EPS file not found: '.$file);
16542
 
        }
16543
 
        $regs = array();
16544
 
        // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
16545
 
        preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
16546
 
        if (count($regs) > 1) {
16547
 
            $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
16548
 
            if (strpos($version_str, 'Adobe Illustrator') !== false) {
16549
 
                $versexp = explode(' ', $version_str);
16550
 
                $version = (float)array_pop($versexp);
16551
 
                if ($version >= 9) {
16552
 
                    $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
16553
 
                }
16554
 
            }
16555
 
        }
16556
 
        // strip binary bytes in front of PS-header
16557
 
        $start = strpos($data, '%!PS-Adobe');
16558
 
        if ($start > 0) {
16559
 
            $data = substr($data, $start);
16560
 
        }
16561
 
        // find BoundingBox params
16562
 
        preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
16563
 
        if (count($regs) > 1) {
16564
 
            list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
16565
 
        } else {
16566
 
            $this->Error('No BoundingBox found in EPS/AI file: '.$file);
16567
 
        }
16568
 
        $start = strpos($data, '%%EndSetup');
16569
 
        if ($start === false) {
16570
 
            $start = strpos($data, '%%EndProlog');
16571
 
        }
16572
 
        if ($start === false) {
16573
 
            $start = strpos($data, '%%BoundingBox');
16574
 
        }
16575
 
        $data = substr($data, $start);
16576
 
        $end = strpos($data, '%%PageTrailer');
16577
 
        if ($end===false) {
16578
 
            $end = strpos($data, 'showpage');
16579
 
        }
16580
 
        if ($end) {
16581
 
            $data = substr($data, 0, $end);
16582
 
        }
16583
 
        // calculate image width and height on document
16584
 
        if (($w <= 0) AND ($h <= 0)) {
16585
 
            $w = ($x2 - $x1) / $k;
16586
 
            $h = ($y2 - $y1) / $k;
16587
 
        } elseif ($w <= 0) {
16588
 
            $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
16589
 
        } elseif ($h <= 0) {
16590
 
            $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
16591
 
        }
16592
 
        // fit the image on available space
16593
 
        $this->fitBlock($w, $h, $x, $y, $fitonpage);
16594
 
        if ($this->rasterize_vector_images) {
16595
 
            // convert EPS to raster image using GD or ImageMagick libraries
16596
 
            return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
16597
 
        }
16598
 
        // set scaling factors
16599
 
        $scale_x = $w / (($x2 - $x1) / $k);
16600
 
        $scale_y = $h / (($y2 - $y1) / $k);
16601
 
        // set alignment
16602
 
        $this->img_rb_y = $y + $h;
16603
 
        // set alignment
16604
 
        if ($this->rtl) {
16605
 
            if ($palign == 'L') {
16606
 
                $ximg = $this->lMargin;
16607
 
            } elseif ($palign == 'C') {
16608
 
                $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
16609
 
            } elseif ($palign == 'R') {
16610
 
                $ximg = $this->w - $this->rMargin - $w;
16611
 
            } else {
16612
 
                $ximg = $x - $w;
16613
 
            }
16614
 
            $this->img_rb_x = $ximg;
16615
 
        } else {
16616
 
            if ($palign == 'L') {
16617
 
                $ximg = $this->lMargin;
16618
 
            } elseif ($palign == 'C') {
16619
 
                $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
16620
 
            } elseif ($palign == 'R') {
16621
 
                $ximg = $this->w - $this->rMargin - $w;
16622
 
            } else {
16623
 
                $ximg = $x;
16624
 
            }
16625
 
            $this->img_rb_x = $ximg + $w;
16626
 
        }
16627
 
        if ($useBoundingBox) {
16628
 
            $dx = $ximg * $k - $x1;
16629
 
            $dy = $y * $k - $y1;
16630
 
        } else {
16631
 
            $dx = $ximg * $k;
16632
 
            $dy = $y * $k;
16633
 
        }
16634
 
        // save the current graphic state
16635
 
        $this->_out('q'.$this->epsmarker);
16636
 
        // translate
16637
 
        $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
16638
 
        // scale
16639
 
        if (isset($scale_x)) {
16640
 
            $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
16641
 
        }
16642
 
        // handle pc/unix/mac line endings
16643
 
        $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY);
16644
 
        $u=0;
16645
 
        $cnt = count($lines);
16646
 
        for ($i=0; $i < $cnt; ++$i) {
16647
 
            $line = $lines[$i];
16648
 
            if (($line == '') OR ($line{0} == '%')) {
16649
 
                continue;
16650
 
            }
16651
 
            $len = strlen($line);
16652
 
            // check for spot color names
16653
 
            $color_name = '';
16654
 
            if (strcasecmp('x', substr(trim($line), -1)) == 0) {
16655
 
                if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) {
16656
 
                    // extract spot color name
16657
 
                    $color_name = $matches[0];
16658
 
                    // remove color name from string
16659
 
                    $line = str_replace(' '.$color_name, '', $line);
16660
 
                    // remove pharentesis from color name
16661
 
                    $color_name = substr($color_name, 1, -1);
16662
 
                }
16663
 
            }
16664
 
            $chunks = explode(' ', $line);
16665
 
            $cmd = trim(array_pop($chunks));
16666
 
            // RGB
16667
 
            if (($cmd == 'Xa') OR ($cmd == 'XA')) {
16668
 
                $b = array_pop($chunks);
16669
 
                $g = array_pop($chunks);
16670
 
                $r = array_pop($chunks);
16671
 
                $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!
16672
 
                continue;
16673
 
            }
16674
 
            $skip = false;
16675
 
            if ($fixoutvals) {
16676
 
                // check for values outside the bounding box
16677
 
                switch ($cmd) {
16678
 
                    case 'm':
16679
 
                    case 'l':
16680
 
                    case 'L': {
16681
 
                        // skip values outside bounding box
16682
 
                        foreach ($chunks as $key => $val) {
16683
 
                            if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) {
16684
 
                                $skip = true;
16685
 
                            } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) {
16686
 
                                $skip = true;
16687
 
                            }
16688
 
                        }
16689
 
                    }
16690
 
                }
16691
 
            }
16692
 
            switch ($cmd) {
16693
 
                case 'm':
16694
 
                case 'l':
16695
 
                case 'v':
16696
 
                case 'y':
16697
 
                case 'c':
16698
 
                case 'k':
16699
 
                case 'K':
16700
 
                case 'g':
16701
 
                case 'G':
16702
 
                case 's':
16703
 
                case 'S':
16704
 
                case 'J':
16705
 
                case 'j':
16706
 
                case 'w':
16707
 
                case 'M':
16708
 
                case 'd':
16709
 
                case 'n': {
16710
 
                    if ($skip) {
16711
 
                        break;
16712
 
                    }
16713
 
                    $this->_out($line);
16714
 
                    break;
16715
 
                }
16716
 
                case 'x': {// custom fill color
16717
 
                    if (empty($color_name)) {
16718
 
                        // CMYK color
16719
 
                        list($col_c, $col_m, $col_y, $col_k) = $chunks;
16720
 
                        $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
16721
 
                    } else {
16722
 
                        // Spot Color (CMYK + tint)
16723
 
                        list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
16724
 
                        $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
16725
 
                        $color_cmd = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t));
16726
 
                        $this->_out($color_cmd);
16727
 
                    }
16728
 
                    break;
16729
 
                }
16730
 
                case 'X': { // custom stroke color
16731
 
                    if (empty($color_name)) {
16732
 
                        // CMYK color
16733
 
                        list($col_c, $col_m, $col_y, $col_k) = $chunks;
16734
 
                        $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
16735
 
                    } else {
16736
 
                        // Spot Color (CMYK + tint)
16737
 
                        list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
16738
 
                        $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
16739
 
                        $color_cmd = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t));
16740
 
                        $this->_out($color_cmd);
16741
 
                    }
16742
 
                    break;
16743
 
                }
16744
 
                case 'Y':
16745
 
                case 'N':
16746
 
                case 'V':
16747
 
                case 'L':
16748
 
                case 'C': {
16749
 
                    if ($skip) {
16750
 
                        break;
16751
 
                    }
16752
 
                    $line{$len-1} = strtolower($cmd);
16753
 
                    $this->_out($line);
16754
 
                    break;
16755
 
                }
16756
 
                case 'b':
16757
 
                case 'B': {
16758
 
                    $this->_out($cmd . '*');
16759
 
                    break;
16760
 
                }
16761
 
                case 'f':
16762
 
                case 'F': {
16763
 
                    if ($u > 0) {
16764
 
                        $isU = false;
16765
 
                        $max = min(($i + 5), $cnt);
16766
 
                        for ($j = ($i + 1); $j < $max; ++$j) {
16767
 
                            $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
16768
 
                        }
16769
 
                        if ($isU) {
16770
 
                            $this->_out('f*');
16771
 
                        }
16772
 
                    } else {
16773
 
                        $this->_out('f*');
16774
 
                    }
16775
 
                    break;
16776
 
                }
16777
 
                case '*u': {
16778
 
                    ++$u;
16779
 
                    break;
16780
 
                }
16781
 
                case '*U': {
16782
 
                    --$u;
16783
 
                    break;
16784
 
                }
16785
 
            }
16786
 
        }
16787
 
        // restore previous graphic state
16788
 
        $this->_out($this->epsmarker.'Q');
16789
 
        if (!empty($border)) {
16790
 
            $bx = $this->x;
16791
 
            $by = $this->y;
16792
 
            $this->x = $ximg;
16793
 
            if ($this->rtl) {
16794
 
                $this->x += $w;
16795
 
            }
16796
 
            $this->y = $y;
16797
 
            $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
16798
 
            $this->x = $bx;
16799
 
            $this->y = $by;
16800
 
        }
16801
 
        if ($link) {
16802
 
            $this->Link($ximg, $y, $w, $h, $link, 0);
16803
 
        }
16804
 
        // set pointer to align the next text/objects
16805
 
        switch($align) {
16806
 
            case 'T':{
16807
 
                $this->y = $y;
16808
 
                $this->x = $this->img_rb_x;
16809
 
                break;
16810
 
            }
16811
 
            case 'M':{
16812
 
                $this->y = $y + round($h/2);
16813
 
                $this->x = $this->img_rb_x;
16814
 
                break;
16815
 
            }
16816
 
            case 'B':{
16817
 
                $this->y = $this->img_rb_y;
16818
 
                $this->x = $this->img_rb_x;
16819
 
                break;
16820
 
            }
16821
 
            case 'N':{
16822
 
                $this->SetY($this->img_rb_y);
16823
 
                break;
16824
 
            }
16825
 
            default:{
16826
 
                break;
16827
 
            }
16828
 
        }
16829
 
        $this->endlinex = $this->img_rb_x;
16830
 
    }
16831
 
 
16832
 
    /**
16833
 
     * Set document barcode.
16834
 
     * @param $bc (string) barcode
16835
 
     * @public
16836
 
     */
16837
 
    public function setBarcode($bc='') {
16838
 
        $this->barcode = $bc;
16839
 
    }
16840
 
 
16841
 
    /**
16842
 
     * Get current barcode.
16843
 
     * @return string
16844
 
     * @public
16845
 
     * @since 4.0.012 (2008-07-24)
16846
 
     */
16847
 
    public function getBarcode() {
16848
 
        return $this->barcode;
16849
 
    }
16850
 
 
16851
 
    /**
16852
 
     * Print a Linear Barcode.
16853
 
     * @param $code (string) code to print
16854
 
     * @param $type (string) type of barcode (see barcodes.php for supported formats).
16855
 
     * @param $x (int) x position in user units (empty string = current x position)
16856
 
     * @param $y (int) y position in user units (empty string = current y position)
16857
 
     * @param $w (int) width in user units (empty string = remaining page width)
16858
 
     * @param $h (int) height in user units (empty string = remaining page height)
16859
 
     * @param $xres (float) width of the smallest bar in user units (empty string = default value = 0.4mm)
16860
 
     * @param $style (array) array of options:<ul>
16861
 
     * <li>boolean $style['border'] if true prints a border</li>
16862
 
     * <li>int $style['padding'] padding to leave around the barcode in user units (set to 'auto' for automatic padding)</li>
16863
 
     * <li>int $style['hpadding'] horizontal padding in user units (set to 'auto' for automatic padding)</li>
16864
 
     * <li>int $style['vpadding'] vertical padding in user units (set to 'auto' for automatic padding)</li>
16865
 
     * <li>array $style['fgcolor'] color array for bars and text</li>
16866
 
     * <li>mixed $style['bgcolor'] color array for background (set to false for transparent)</li>
16867
 
     * <li>boolean $style['text'] if true prints text below the barcode</li>
16868
 
     * <li>string $style['label'] override default label</li>
16869
 
     * <li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li>
16870
 
     * <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>
16871
 
     * <li>string $style['position'] horizontal position of the containing barcode cell on the page: L = left margin; C = center; R = right margin.</li>
16872
 
     * <li>string $style['align'] horizontal position of the barcode on the containing rectangle: L = left; C = center; R = right.</li>
16873
 
     * <li>string $style['stretch'] if true stretch the barcode to best fit the available width, otherwise uses $xres resolution for a single bar.</li>
16874
 
     * <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>
16875
 
     * <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>
16876
 
     * @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>
16877
 
     * @author Nicola Asuni
16878
 
     * @since 3.1.000 (2008-06-09)
16879
 
     * @public
16880
 
     */
16881
 
    public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') {
16882
 
        if ($this->empty_string(trim($code))) {
16883
 
            return;
16884
 
        }
16885
 
        require_once(dirname(__FILE__).'/barcodes.php');
16886
 
        // save current graphic settings
16887
 
        $gvars = $this->getGraphicVars();
16888
 
        // create new barcode object
16889
 
        $barcodeobj = new TCPDFBarcode($code, $type);
16890
 
        $arrcode = $barcodeobj->getBarcodeArray();
16891
 
        if ($arrcode === false) {
16892
 
            $this->Error('Error in 1D barcode string');
16893
 
        }
16894
 
        // set default values
16895
 
        if (!isset($style['position'])) {
16896
 
            $style['position'] = '';
16897
 
        } elseif ($style['position'] == 'S') {
16898
 
            // keep this for backward compatibility
16899
 
            $style['position'] = '';
16900
 
            $style['stretch'] = true;
16901
 
        }
16902
 
        if (!isset($style['fitwidth'])) {
16903
 
            if (!isset($style['stretch'])) {
16904
 
                $style['fitwidth'] = true;
16905
 
            } else {
16906
 
                $style['fitwidth'] = false;
16907
 
            }
16908
 
        }
16909
 
        if ($style['fitwidth']) {
16910
 
            // disable stretch
16911
 
            $style['stretch'] = false;
16912
 
        }
16913
 
        if (!isset($style['stretch'])) {
16914
 
            if (($w === '') OR ($w <= 0)) {
16915
 
                $style['stretch'] = false;
16916
 
            } else {
16917
 
                $style['stretch'] = true;
16918
 
            }
16919
 
        }
16920
 
        if (!isset($style['fgcolor'])) {
16921
 
            $style['fgcolor'] = array(0,0,0); // default black
16922
 
        }
16923
 
        if (!isset($style['bgcolor'])) {
16924
 
            $style['bgcolor'] = false; // default transparent
16925
 
        }
16926
 
        if (!isset($style['border'])) {
16927
 
            $style['border'] = false;
16928
 
        }
16929
 
        $fontsize = 0;
16930
 
        if (!isset($style['text'])) {
16931
 
            $style['text'] = false;
16932
 
        }
16933
 
        if ($style['text'] AND isset($style['font'])) {
16934
 
            if (isset($style['fontsize'])) {
16935
 
                $fontsize = $style['fontsize'];
16936
 
            }
16937
 
            $this->SetFont($style['font'], '', $fontsize);
16938
 
        }
16939
 
        if (!isset($style['stretchtext'])) {
16940
 
            $style['stretchtext'] = 4;
16941
 
        }
16942
 
        if ($x === '') {
16943
 
            $x = $this->x;
16944
 
        }
16945
 
        if ($y === '') {
16946
 
            $y = $this->y;
16947
 
        }
16948
 
        // check page for no-write regions and adapt page margins if necessary
16949
 
        $this->checkPageRegions($h, $x, $y);
16950
 
        if (($w === '') OR ($w <= 0)) {
16951
 
            if ($this->rtl) {
16952
 
                $w = $x - $this->lMargin;
16953
 
            } else {
16954
 
                $w = $this->w - $this->rMargin - $x;
16955
 
            }
16956
 
        }
16957
 
        // padding
16958
 
        if (!isset($style['padding'])) {
16959
 
            $padding = 0;
16960
 
        } elseif ($style['padding'] === 'auto') {
16961
 
            $padding = 10 * ($w / ($arrcode['maxw'] + 20));
16962
 
        } else {
16963
 
            $padding = floatval($style['padding']);
16964
 
        }
16965
 
        // horizontal padding
16966
 
        if (!isset($style['hpadding'])) {
16967
 
            $hpadding = $padding;
16968
 
        } elseif ($style['hpadding'] === 'auto') {
16969
 
            $hpadding = 10 * ($w / ($arrcode['maxw'] + 20));
16970
 
        } else {
16971
 
            $hpadding = floatval($style['hpadding']);
16972
 
        }
16973
 
        // vertical padding
16974
 
        if (!isset($style['vpadding'])) {
16975
 
            $vpadding = $padding;
16976
 
        } elseif ($style['vpadding'] === 'auto') {
16977
 
            $vpadding = ($hpadding / 2);
16978
 
        } else {
16979
 
            $vpadding = floatval($style['vpadding']);
16980
 
        }
16981
 
        // calculate xres (single bar width)
16982
 
        $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
16983
 
        if ($style['stretch']) {
16984
 
            $xres = $max_xres;
16985
 
        } else {
16986
 
            if ($this->empty_string($xres)) {
16987
 
                $xres = (0.141 * $this->k); // default bar width = 0.4 mm
16988
 
            }
16989
 
            if ($xres > $max_xres) {
16990
 
                // correct xres to fit on $w
16991
 
                $xres = $max_xres;
16992
 
            }
16993
 
            if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
16994
 
                OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
16995
 
                $hpadding = 10 * $xres;
16996
 
                if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
16997
 
                    $vpadding = ($hpadding / 2);
16998
 
                }
16999
 
            }
17000
 
        }
17001
 
        if ($style['fitwidth']) {
17002
 
            $wold = $w;
17003
 
            $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding));
17004
 
            if (isset($style['cellfitalign'])) {
17005
 
                switch ($style['cellfitalign']) {
17006
 
                    case 'L': {
17007
 
                        if ($this->rtl) {
17008
 
                            $x -= ($wold - $w);
17009
 
                        }
17010
 
                        break;
17011
 
                    }
17012
 
                    case 'R': {
17013
 
                        if (!$this->rtl) {
17014
 
                            $x += ($wold - $w);
17015
 
                        }
17016
 
                        break;
17017
 
                    }
17018
 
                    case 'C': {
17019
 
                        if ($this->rtl) {
17020
 
                            $x -= (($wold - $w) / 2);
17021
 
                        } else {
17022
 
                            $x += (($wold - $w) / 2);
17023
 
                        }
17024
 
                        break;
17025
 
                    }
17026
 
                    default : {
17027
 
                        break;
17028
 
                    }
17029
 
                }
17030
 
            }
17031
 
        }
17032
 
        $text_height = ($this->cell_height_ratio * $fontsize / $this->k);
17033
 
        // height
17034
 
        if (($h === '') OR ($h <= 0)) {
17035
 
            // set default height
17036
 
            $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height;
17037
 
        }
17038
 
        $barh = $h - $text_height - (2 * $vpadding);
17039
 
        if ($barh <=0) {
17040
 
            // try to reduce font or padding to fit barcode on available height
17041
 
            if ($text_height > $h) {
17042
 
                $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio));
17043
 
                $text_height = ($this->cell_height_ratio * $fontsize / $this->k);
17044
 
                $this->SetFont($style['font'], '', $fontsize);
17045
 
            }
17046
 
            if ($vpadding > 0) {
17047
 
                $vpadding = (($h - $text_height) / 4);
17048
 
            }
17049
 
            $barh = $h - $text_height - (2 * $vpadding);
17050
 
        }
17051
 
        // fit the barcode on available space
17052
 
        $this->fitBlock($w, $h, $x, $y, false);
17053
 
        // set alignment
17054
 
        $this->img_rb_y = $y + $h;
17055
 
        // set alignment
17056
 
        if ($this->rtl) {
17057
 
            if ($style['position'] == 'L') {
17058
 
                $xpos = $this->lMargin;
17059
 
            } elseif ($style['position'] == 'C') {
17060
 
                $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
17061
 
            } elseif ($style['position'] == 'R') {
17062
 
                $xpos = $this->w - $this->rMargin - $w;
17063
 
            } else {
17064
 
                $xpos = $x - $w;
17065
 
            }
17066
 
            $this->img_rb_x = $xpos;
17067
 
        } else {
17068
 
            if ($style['position'] == 'L') {
17069
 
                $xpos = $this->lMargin;
17070
 
            } elseif ($style['position'] == 'C') {
17071
 
                $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
17072
 
            } elseif ($style['position'] == 'R') {
17073
 
                $xpos = $this->w - $this->rMargin - $w;
17074
 
            } else {
17075
 
                $xpos = $x;
17076
 
            }
17077
 
            $this->img_rb_x = $xpos + $w;
17078
 
        }
17079
 
        $xpos_rect = $xpos;
17080
 
        if (!isset($style['align'])) {
17081
 
            $style['align'] = 'C';
17082
 
        }
17083
 
        switch ($style['align']) {
17084
 
            case 'L': {
17085
 
                $xpos = $xpos_rect + $hpadding;
17086
 
                break;
17087
 
            }
17088
 
            case 'R': {
17089
 
                $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding;
17090
 
                break;
17091
 
            }
17092
 
            case 'C':
17093
 
            default : {
17094
 
                $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2);
17095
 
                break;
17096
 
            }
17097
 
        }
17098
 
        $xpos_text = $xpos;
17099
 
        // barcode is always printed in LTR direction
17100
 
        $tempRTL = $this->rtl;
17101
 
        $this->rtl = false;
17102
 
        // print background color
17103
 
        if ($style['bgcolor']) {
17104
 
            $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
17105
 
        } elseif ($style['border']) {
17106
 
            $this->Rect($xpos_rect, $y, $w, $h, 'D');
17107
 
        }
17108
 
        // set foreground color
17109
 
        $this->SetDrawColorArray($style['fgcolor']);
17110
 
        $this->SetTextColorArray($style['fgcolor']);
17111
 
        // print bars
17112
 
        foreach ($arrcode['bcode'] as $k => $v) {
17113
 
            $bw = ($v['w'] * $xres);
17114
 
            if ($v['t']) {
17115
 
                // draw a vertical bar
17116
 
                $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']);
17117
 
                $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
17118
 
            }
17119
 
            $xpos += $bw;
17120
 
        }
17121
 
        // print text
17122
 
        if ($style['text']) {
17123
 
            if (isset($style['label']) AND !$this->empty_string($style['label'])) {
17124
 
                $label = $style['label'];
17125
 
            } else {
17126
 
                $label = $code;
17127
 
            }
17128
 
            $txtwidth = ($arrcode['maxw'] * $xres);
17129
 
            if ($this->GetStringWidth($label) > $txtwidth) {
17130
 
                $style['stretchtext'] = 2;
17131
 
            }
17132
 
            // print text
17133
 
            $this->x = $xpos_text;
17134
 
            $this->y = $y + $vpadding + $barh;
17135
 
            $cellpadding = $this->cell_padding;
17136
 
            $this->SetCellPadding(0);
17137
 
            $this->Cell($txtwidth, '', $label, 0, 0, 'C', 0, '', $style['stretchtext'], false, 'T', 'T');
17138
 
            $this->cell_padding = $cellpadding;
17139
 
        }
17140
 
        // restore original direction
17141
 
        $this->rtl = $tempRTL;
17142
 
        // restore previous settings
17143
 
        $this->setGraphicVars($gvars);
17144
 
        // set pointer to align the next text/objects
17145
 
        switch($align) {
17146
 
            case 'T':{
17147
 
                $this->y = $y;
17148
 
                $this->x = $this->img_rb_x;
17149
 
                break;
17150
 
            }
17151
 
            case 'M':{
17152
 
                $this->y = $y + round($h / 2);
17153
 
                $this->x = $this->img_rb_x;
17154
 
                break;
17155
 
            }
17156
 
            case 'B':{
17157
 
                $this->y = $this->img_rb_y;
17158
 
                $this->x = $this->img_rb_x;
17159
 
                break;
17160
 
            }
17161
 
            case 'N':{
17162
 
                $this->SetY($this->img_rb_y);
17163
 
                break;
17164
 
            }
17165
 
            default:{
17166
 
                break;
17167
 
            }
17168
 
        }
17169
 
        $this->endlinex = $this->img_rb_x;
17170
 
    }
17171
 
 
17172
 
    /**
17173
 
     * This function is DEPRECATED, please use the new write1DBarcode() function.
17174
 
     * @param $x (int) x position in user units
17175
 
     * @param $y (int) y position in user units
17176
 
     * @param $w (int) width in user units
17177
 
     * @param $h (int) height position in user units
17178
 
     * @param $type (string) type of barcode
17179
 
     * @param $style (string) barcode style
17180
 
     * @param $font (string) font for text
17181
 
     * @param $xres (int) x resolution
17182
 
     * @param $code (string) code to print
17183
 
     * @deprecated deprecated since version 3.1.000 (2008-06-10)
17184
 
     * @public
17185
 
     * @see write1DBarcode()
17186
 
     */
17187
 
    public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
17188
 
        // convert old settings for the new write1DBarcode() function.
17189
 
        $xres = 1 / $xres;
17190
 
        $newstyle = array(
17191
 
            'position' => '',
17192
 
            'align' => '',
17193
 
            'stretch' => false,
17194
 
            'fitwidth' => false,
17195
 
            'cellfitalign' => '',
17196
 
            'border' => false,
17197
 
            'padding' => 0,
17198
 
            'fgcolor' => array(0,0,0),
17199
 
            'bgcolor' => false,
17200
 
            'text' => true,
17201
 
            'font' => $font,
17202
 
            'fontsize' => 8,
17203
 
            'stretchtext' => 4
17204
 
        );
17205
 
        if ($style & 1) {
17206
 
            $newstyle['border'] = true;
17207
 
        }
17208
 
        if ($style & 2) {
17209
 
            $newstyle['bgcolor'] = false;
17210
 
        }
17211
 
        if ($style & 4) {
17212
 
            $newstyle['position'] = 'C';
17213
 
        } elseif ($style & 8) {
17214
 
            $newstyle['position'] = 'L';
17215
 
        } elseif ($style & 16) {
17216
 
            $newstyle['position'] = 'R';
17217
 
        }
17218
 
        if ($style & 128) {
17219
 
            $newstyle['text'] = true;
17220
 
        }
17221
 
        if ($style & 256) {
17222
 
            $newstyle['stretchtext'] = 4;
17223
 
        }
17224
 
        $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
17225
 
    }
17226
 
 
17227
 
    /**
17228
 
     * Print 2D Barcode.
17229
 
     * @param $code (string) code to print
17230
 
     * @param $type (string) type of barcode (see 2dbarcodes.php for supported formats).
17231
 
     * @param $x (int) x position in user units
17232
 
     * @param $y (int) y position in user units
17233
 
     * @param $w (int) width in user units
17234
 
     * @param $h (int) height in user units
17235
 
     * @param $style (array) array of options:<ul>
17236
 
     * <li>boolean $style['border'] if true prints a border around the barcode</li>
17237
 
     * <li>int $style['padding'] padding to leave around the barcode in barcode units (set to 'auto' for automatic padding)</li>
17238
 
     * <li>int $style['hpadding'] horizontal padding in barcode units (set to 'auto' for automatic padding)</li>
17239
 
     * <li>int $style['vpadding'] vertical padding in barcode units (set to 'auto' for automatic padding)</li>
17240
 
     * <li>int $style['module_width'] width of a single module in points</li>
17241
 
     * <li>int $style['module_height'] height of a single module in points</li>
17242
 
     * <li>array $style['fgcolor'] color array for bars and text</li>
17243
 
     * <li>mixed $style['bgcolor'] color array for background or false for transparent</li>
17244
 
     * <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>
17245
 
     * <li>$style['module_height'] height of a single module in points</li></ul>
17246
 
     * @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>
17247
 
     * @param $distort (boolean) if true distort the barcode to fit width and height, otherwise preserve aspect ratio
17248
 
     * @author Nicola Asuni
17249
 
     * @since 4.5.037 (2009-04-07)
17250
 
     * @public
17251
 
     */
17252
 
    public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) {
17253
 
        if ($this->empty_string(trim($code))) {
17254
 
            return;
17255
 
        }
17256
 
        require_once(dirname(__FILE__).'/2dbarcodes.php');
17257
 
        // save current graphic settings
17258
 
        $gvars = $this->getGraphicVars();
17259
 
        // create new barcode object
17260
 
        $barcodeobj = new TCPDF2DBarcode($code, $type);
17261
 
        $arrcode = $barcodeobj->getBarcodeArray();
17262
 
        if (($arrcode === false) OR empty($arrcode)) {
17263
 
            $this->Error('Error in 2D barcode string');
17264
 
        }
17265
 
        // set default values
17266
 
        if (!isset($style['position'])) {
17267
 
            $style['position'] = '';
17268
 
        }
17269
 
        if (!isset($style['fgcolor'])) {
17270
 
            $style['fgcolor'] = array(0,0,0); // default black
17271
 
        }
17272
 
        if (!isset($style['bgcolor'])) {
17273
 
            $style['bgcolor'] = false; // default transparent
17274
 
        }
17275
 
        if (!isset($style['border'])) {
17276
 
            $style['border'] = false;
17277
 
        }
17278
 
        // padding
17279
 
        if (!isset($style['padding'])) {
17280
 
            $style['padding'] = 0;
17281
 
        } elseif ($style['padding'] === 'auto') {
17282
 
            $style['padding'] = 4;
17283
 
        }
17284
 
        if (!isset($style['hpadding'])) {
17285
 
            $style['hpadding'] = $style['padding'];
17286
 
        } elseif ($style['hpadding'] === 'auto') {
17287
 
            $style['hpadding'] = 4;
17288
 
        }
17289
 
        if (!isset($style['vpadding'])) {
17290
 
            $style['vpadding'] = $style['padding'];
17291
 
        } elseif ($style['vpadding'] === 'auto') {
17292
 
            $style['vpadding'] = 4;
17293
 
        }
17294
 
        // cell (module) dimension
17295
 
        if (!isset($style['module_width'])) {
17296
 
            $style['module_width'] = 1; // width of a single module in points
17297
 
        }
17298
 
        if (!isset($style['module_height'])) {
17299
 
            $style['module_height'] = 1; // height of a single module in points
17300
 
        }
17301
 
        if ($x === '') {
17302
 
            $x = $this->x;
17303
 
        }
17304
 
        if ($y === '') {
17305
 
            $y = $this->y;
17306
 
        }
17307
 
        // check page for no-write regions and adapt page margins if necessary
17308
 
        $this->checkPageRegions($h, $x, $y);
17309
 
        // number of barcode columns and rows
17310
 
        $rows = $arrcode['num_rows'];
17311
 
        $cols = $arrcode['num_cols'];
17312
 
        // module width and height
17313
 
        $mw = $style['module_width'];
17314
 
        $mh = $style['module_height'];
17315
 
        // get max dimensions
17316
 
        if ($this->rtl) {
17317
 
            $maxw = $x - $this->lMargin;
17318
 
        } else {
17319
 
            $maxw = $this->w - $this->rMargin - $x;
17320
 
        }
17321
 
        $maxh = ($this->h - $this->tMargin - $this->bMargin);
17322
 
        $ratioHW = ($rows * $mh) / ($cols * $mw);
17323
 
        $ratioWH = ($cols * $mw) / ($rows * $mh);
17324
 
        if (!$distort) {
17325
 
            if (($maxw * $ratioHW) > $maxh) {
17326
 
                $maxw = $maxh * $ratioWH;
17327
 
            }
17328
 
            if (($maxh * $ratioWH) > $maxw) {
17329
 
                $maxh = $maxw * $ratioHW;
17330
 
            }
17331
 
        }
17332
 
        // set maximum dimesions
17333
 
        if ($w > $maxw) {
17334
 
            $w = $maxw;
17335
 
        }
17336
 
        if ($h > $maxh) {
17337
 
            $h = $maxh;
17338
 
        }
17339
 
        $hpad = (2 * $style['hpadding']);
17340
 
        $vpad = (2 * $style['vpadding']);
17341
 
        // set dimensions
17342
 
        if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) {
17343
 
            $w = ($cols + $hpad) * ($mw / $this->k);
17344
 
            $h = ($rows + $vpad) * ($mh / $this->k);
17345
 
        } elseif (($w === '') OR ($w <= 0)) {
17346
 
            $w = $h * $ratioWH;
17347
 
        } elseif (($h === '') OR ($h <= 0)) {
17348
 
            $h = $w * $ratioHW;
17349
 
        }
17350
 
        // barcode size (excluding padding)
17351
 
        $bw = ($w * $cols) / ($cols + $hpad);
17352
 
        $bh = ($h * $rows) / ($rows + $vpad);
17353
 
        // dimension of single barcode cell unit
17354
 
        $cw = $bw / $cols;
17355
 
        $ch = $bh / $rows;
17356
 
        if (!$distort) {
17357
 
            if (($cw / $ch) > ($mw / $mh)) {
17358
 
                // correct horizontal distortion
17359
 
                $cw = $ch * $mw / $mh;
17360
 
                $bw = $cw * $cols;
17361
 
                $style['hpadding'] = ($w - $bw) / (2 * $cw);
17362
 
            } else {
17363
 
                // correct vertical distortion
17364
 
                $ch = $cw * $mh / $mw;
17365
 
                $bh = $ch * $rows;
17366
 
                $style['vpadding'] = ($h - $bh) / (2 * $ch);
17367
 
            }
17368
 
        }
17369
 
        // fit the barcode on available space
17370
 
        $this->fitBlock($w, $h, $x, $y, false);
17371
 
        // set alignment
17372
 
        $this->img_rb_y = $y + $h;
17373
 
        // set alignment
17374
 
        if ($this->rtl) {
17375
 
            if ($style['position'] == 'L') {
17376
 
                $xpos = $this->lMargin;
17377
 
            } elseif ($style['position'] == 'C') {
17378
 
                $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
17379
 
            } elseif ($style['position'] == 'R') {
17380
 
                $xpos = $this->w - $this->rMargin - $w;
17381
 
            } else {
17382
 
                $xpos = $x - $w;
17383
 
            }
17384
 
            $this->img_rb_x = $xpos;
17385
 
        } else {
17386
 
            if ($style['position'] == 'L') {
17387
 
                $xpos = $this->lMargin;
17388
 
            } elseif ($style['position'] == 'C') {
17389
 
                $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
17390
 
            } elseif ($style['position'] == 'R') {
17391
 
                $xpos = $this->w - $this->rMargin - $w;
17392
 
            } else {
17393
 
                $xpos = $x;
17394
 
            }
17395
 
            $this->img_rb_x = $xpos + $w;
17396
 
        }
17397
 
        $xstart = $xpos + ($style['hpadding'] * $cw);
17398
 
        $ystart = $y + ($style['vpadding'] * $ch);
17399
 
        // barcode is always printed in LTR direction
17400
 
        $tempRTL = $this->rtl;
17401
 
        $this->rtl = false;
17402
 
        // print background color
17403
 
        if ($style['bgcolor']) {
17404
 
            $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
17405
 
        } elseif ($style['border']) {
17406
 
            $this->Rect($xpos, $y, $w, $h, 'D');
17407
 
        }
17408
 
        // set foreground color
17409
 
        $this->SetDrawColorArray($style['fgcolor']);
17410
 
        // print barcode cells
17411
 
        // for each row
17412
 
        for ($r = 0; $r < $rows; ++$r) {
17413
 
            $xr = $xstart;
17414
 
            // for each column
17415
 
            for ($c = 0; $c < $cols; ++$c) {
17416
 
                if ($arrcode['bcode'][$r][$c] == 1) {
17417
 
                    // draw a single barcode cell
17418
 
                    $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
17419
 
                }
17420
 
                $xr += $cw;
17421
 
            }
17422
 
            $ystart += $ch;
17423
 
        }
17424
 
        // restore original direction
17425
 
        $this->rtl = $tempRTL;
17426
 
        // restore previous settings
17427
 
        $this->setGraphicVars($gvars);
17428
 
        // set pointer to align the next text/objects
17429
 
        switch($align) {
17430
 
            case 'T':{
17431
 
                $this->y = $y;
17432
 
                $this->x = $this->img_rb_x;
17433
 
                break;
17434
 
            }
17435
 
            case 'M':{
17436
 
                $this->y = $y + round($h/2);
17437
 
                $this->x = $this->img_rb_x;
17438
 
                break;
17439
 
            }
17440
 
            case 'B':{
17441
 
                $this->y = $this->img_rb_y;
17442
 
                $this->x = $this->img_rb_x;
17443
 
                break;
17444
 
            }
17445
 
            case 'N':{
17446
 
                $this->SetY($this->img_rb_y);
17447
 
                break;
17448
 
            }
17449
 
            default:{
17450
 
                break;
17451
 
            }
17452
 
        }
17453
 
        $this->endlinex = $this->img_rb_x;
17454
 
    }
17455
 
 
17456
 
    /**
17457
 
     * Returns an array containing current margins:
17458
 
     * <ul>
17459
 
            <li>$ret['left'] = left margin</li>
17460
 
            <li>$ret['right'] = right margin</li>
17461
 
            <li>$ret['top'] = top margin</li>
17462
 
            <li>$ret['bottom'] = bottom margin</li>
17463
 
            <li>$ret['header'] = header margin</li>
17464
 
            <li>$ret['footer'] = footer margin</li>
17465
 
            <li>$ret['cell'] = cell padding array</li>
17466
 
            <li>$ret['padding_left'] = cell left padding</li>
17467
 
            <li>$ret['padding_top'] = cell top padding</li>
17468
 
            <li>$ret['padding_right'] = cell right padding</li>
17469
 
            <li>$ret['padding_bottom'] = cell bottom padding</li>
17470
 
     * </ul>
17471
 
     * @return array containing all margins measures
17472
 
     * @public
17473
 
     * @since 3.2.000 (2008-06-23)
17474
 
     */
17475
 
    public function getMargins() {
17476
 
        $ret = array(
17477
 
            'left' => $this->lMargin,
17478
 
            'right' => $this->rMargin,
17479
 
            'top' => $this->tMargin,
17480
 
            'bottom' => $this->bMargin,
17481
 
            'header' => $this->header_margin,
17482
 
            'footer' => $this->footer_margin,
17483
 
            'cell' => $this->cell_padding,
17484
 
            'padding_left' => $this->cell_padding['L'],
17485
 
            'padding_top' => $this->cell_padding['T'],
17486
 
            'padding_right' => $this->cell_padding['R'],
17487
 
            'padding_bottom' => $this->cell_padding['B']
17488
 
        );
17489
 
        return $ret;
17490
 
    }
17491
 
 
17492
 
    /**
17493
 
     * Returns an array containing original margins:
17494
 
     * <ul>
17495
 
            <li>$ret['left'] = left margin</li>
17496
 
            <li>$ret['right'] = right margin</li>
17497
 
     * </ul>
17498
 
     * @return array containing all margins measures
17499
 
     * @public
17500
 
     * @since 4.0.012 (2008-07-24)
17501
 
     */
17502
 
    public function getOriginalMargins() {
17503
 
        $ret = array(
17504
 
            'left' => $this->original_lMargin,
17505
 
            'right' => $this->original_rMargin
17506
 
        );
17507
 
        return $ret;
17508
 
    }
17509
 
 
17510
 
    /**
17511
 
     * Returns the current font size.
17512
 
     * @return current font size
17513
 
     * @public
17514
 
     * @since 3.2.000 (2008-06-23)
17515
 
     */
17516
 
    public function getFontSize() {
17517
 
        return $this->FontSize;
17518
 
    }
17519
 
 
17520
 
    /**
17521
 
     * Returns the current font size in points unit.
17522
 
     * @return current font size in points unit
17523
 
     * @public
17524
 
     * @since 3.2.000 (2008-06-23)
17525
 
     */
17526
 
    public function getFontSizePt() {
17527
 
        return $this->FontSizePt;
17528
 
    }
17529
 
 
17530
 
    /**
17531
 
     * Returns the current font family name.
17532
 
     * @return string current font family name
17533
 
     * @public
17534
 
     * @since 4.3.008 (2008-12-05)
17535
 
     */
17536
 
    public function getFontFamily() {
17537
 
        return $this->FontFamily;
17538
 
    }
17539
 
 
17540
 
    /**
17541
 
     * Returns the current font style.
17542
 
     * @return string current font style
17543
 
     * @public
17544
 
     * @since 4.3.008 (2008-12-05)
17545
 
     */
17546
 
    public function getFontStyle() {
17547
 
        return $this->FontStyle;
17548
 
    }
17549
 
 
17550
 
    /**
17551
 
     * Cleanup HTML code (requires HTML Tidy library).
17552
 
     * @param $html (string) htmlcode to fix
17553
 
     * @param $default_css (string) CSS commands to add
17554
 
     * @param $tagvs (array) parameters for setHtmlVSpace method
17555
 
     * @param $tidy_options (array) options for tidy_parse_string function
17556
 
     * @return string XHTML code cleaned up
17557
 
     * @author Nicola Asuni
17558
 
     * @public
17559
 
     * @since 5.9.017 (2010-11-16)
17560
 
     * @see setHtmlVSpace()
17561
 
     */
17562
 
    public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') {
17563
 
        // configure parameters for HTML Tidy
17564
 
        if ($tidy_options === '') {
17565
 
            $tidy_options = array (
17566
 
                'clean' => 1,
17567
 
                'drop-empty-paras' => 0,
17568
 
                'drop-proprietary-attributes' => 1,
17569
 
                'fix-backslash' => 1,
17570
 
                'hide-comments' => 1,
17571
 
                'join-styles' => 1,
17572
 
                'lower-literals' => 1,
17573
 
                'merge-divs' => 1,
17574
 
                'merge-spans' => 1,
17575
 
                'output-xhtml' => 1,
17576
 
                'word-2000' => 1,
17577
 
                'wrap' => 0,
17578
 
                'output-bom' => 0,
17579
 
                //'char-encoding' => 'utf8',
17580
 
                //'input-encoding' => 'utf8',
17581
 
                //'output-encoding' => 'utf8'
17582
 
            );
17583
 
        }
17584
 
        // clean up the HTML code
17585
 
        $tidy = tidy_parse_string($html, $tidy_options);
17586
 
        // fix the HTML
17587
 
        $tidy->cleanRepair();
17588
 
        // get the CSS part
17589
 
        $tidy_head = tidy_get_head($tidy);
17590
 
        $css = $tidy_head->value;
17591
 
        $css = preg_replace('/<style([^>]+)>/ims', '<style>', $css);
17592
 
        $css = preg_replace('/<\/style>(.*)<style>/ims', "\n", $css);
17593
 
        $css = str_replace('/*<![CDATA[*/', '', $css);
17594
 
        $css = str_replace('/*]]>*/', '', $css);
17595
 
        preg_match('/<style>(.*)<\/style>/ims', $css, $matches);
17596
 
        $css = strtolower($matches[1]);
17597
 
        // include default css
17598
 
        $css = '<style>'.$default_css.$css.'</style>';
17599
 
        // get the body part
17600
 
        $tidy_body = tidy_get_body($tidy);
17601
 
        $html = $tidy_body->value;
17602
 
        // fix some self-closing tags
17603
 
        $html = str_replace('<br>', '<br />', $html);
17604
 
        // remove some empty tag blocks
17605
 
        $html = preg_replace('/<div([^\>]*)><\/div>/', '', $html);
17606
 
        $html = preg_replace('/<p([^\>]*)><\/p>/', '', $html);
17607
 
        if ($tagvs !== '') {
17608
 
            // set vertical space for some XHTML tags
17609
 
            $this->setHtmlVSpace($tagvs);
17610
 
        }
17611
 
        // return the cleaned XHTML code + CSS
17612
 
        return $css.$html;
17613
 
    }
17614
 
 
17615
 
    /**
17616
 
     * Extracts the CSS properties from a CSS string.
17617
 
     * @param $cssdata (string) string containing CSS definitions.
17618
 
     * @return An array where the keys are the CSS selectors and the values are the CSS properties.
17619
 
     * @author Nicola Asuni
17620
 
     * @since 5.1.000 (2010-05-25)
17621
 
     * @protected
17622
 
     */
17623
 
    protected function extractCSSproperties($cssdata) {
17624
 
        if (empty($cssdata)) {
17625
 
            return array();
17626
 
        }
17627
 
        // remove comments
17628
 
        $cssdata = preg_replace('/\/\*[^\*]*\*\//', '', $cssdata);
17629
 
        // remove newlines and multiple spaces
17630
 
        $cssdata = preg_replace('/[\s]+/', ' ', $cssdata);
17631
 
        // remove some spaces
17632
 
        $cssdata = preg_replace('/[\s]*([;:\{\}]{1})[\s]*/', '\\1', $cssdata);
17633
 
        // remove empty blocks
17634
 
        $cssdata = preg_replace('/([^\}\{]+)\{\}/', '', $cssdata);
17635
 
        // replace media type parenthesis
17636
 
        $cssdata = preg_replace('/@media[\s]+([^\{]*)\{/i', '@media \\1�', $cssdata);
17637
 
        $cssdata = preg_replace('/\}\}/si', '}�', $cssdata);
17638
 
        // trim string
17639
 
        $cssdata = trim($cssdata);
17640
 
        // find media blocks (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
17641
 
        $cssblocks = array();
17642
 
        $matches = array();
17643
 
        if (preg_match_all('/@media[\s]+([^\�]*)�([^�]*)�/i', $cssdata, $matches) > 0) {
17644
 
            foreach ($matches[1] as $key => $type) {
17645
 
                $cssblocks[$type] = $matches[2][$key];
17646
 
            }
17647
 
            // remove media blocks
17648
 
            $cssdata = preg_replace('/@media[\s]+([^\�]*)�([^�]*)�/i', '', $cssdata);
17649
 
        }
17650
 
        // keep 'all' and 'print' media, other media types are discarded
17651
 
        if (isset($cssblocks['all']) AND !empty($cssblocks['all'])) {
17652
 
            $cssdata .= $cssblocks['all'];
17653
 
        }
17654
 
        if (isset($cssblocks['print']) AND !empty($cssblocks['print'])) {
17655
 
            $cssdata .= $cssblocks['print'];
17656
 
        }
17657
 
        // reset css blocks array
17658
 
        $cssblocks = array();
17659
 
        $matches = array();
17660
 
        // explode css data string into array
17661
 
        if (substr($cssdata, -1) == '}') {
17662
 
            // remove last parethesis
17663
 
            $cssdata = substr($cssdata, 0, -1);
17664
 
        }
17665
 
        $matches = explode('}', $cssdata);
17666
 
        foreach ($matches as $key => $block) {
17667
 
            // index 0 contains the CSS selector, index 1 contains CSS properties
17668
 
            $cssblocks[$key] = explode('{', $block);
17669
 
            if (!isset($cssblocks[$key][1])) {
17670
 
                // remove empty definitions
17671
 
                unset($cssblocks[$key]);
17672
 
            }
17673
 
        }
17674
 
        // split groups of selectors (comma-separated list of selectors)
17675
 
        foreach ($cssblocks as $key => $block) {
17676
 
            if (strpos($block[0], ',') > 0) {
17677
 
                $selectors = explode(',', $block[0]);
17678
 
                foreach ($selectors as $sel) {
17679
 
                    $cssblocks[] = array(0 => trim($sel), 1 => $block[1]);
17680
 
                }
17681
 
                unset($cssblocks[$key]);
17682
 
            }
17683
 
        }
17684
 
        // covert array to selector => properties
17685
 
        $cssdata = array();
17686
 
        foreach ($cssblocks as $block) {
17687
 
            $selector = $block[0];
17688
 
            // calculate selector's specificity
17689
 
            $matches = array();
17690
 
            $a = 0; // the declaration is not from is a 'style' attribute
17691
 
            $b = intval(preg_match_all('/[\#]/', $selector, $matches)); // number of ID attributes
17692
 
            $c = intval(preg_match_all('/[\[\.]/', $selector, $matches)); // number of other attributes
17693
 
            $c += intval(preg_match_all('/[\:]link|visited|hover|active|focus|target|lang|enabled|disabled|checked|indeterminate|root|nth|first|last|only|empty|contains|not/i', $selector, $matches)); // number of pseudo-classes
17694
 
            $d = intval(preg_match_all('/[\>\+\~\s]{1}[a-zA-Z0-9\*]+/', ' '.$selector, $matches)); // number of element names
17695
 
            $d += intval(preg_match_all('/[\:][\:]/', $selector, $matches)); // number of pseudo-elements
17696
 
            $specificity = $a.$b.$c.$d;
17697
 
            // add specificity to the beginning of the selector
17698
 
            $cssdata[$specificity.' '.$selector] = $block[1];
17699
 
        }
17700
 
        // sort selectors alphabetically to account for specificity
17701
 
        ksort($cssdata, SORT_STRING);
17702
 
        // return array
17703
 
        return $cssdata;
17704
 
    }
17705
 
 
17706
 
    /**
17707
 
     * Returns true if the CSS selector is valid for the selected HTML tag
17708
 
     * @param $dom (array) array of HTML tags and properties
17709
 
     * @param $key (int) key of the current HTML tag
17710
 
     * @param $selector (string) CSS selector string
17711
 
     * @return true if the selector is valid, false otherwise
17712
 
     * @protected
17713
 
     * @since 5.1.000 (2010-05-25)
17714
 
     */
17715
 
    protected function isValidCSSSelectorForTag($dom, $key, $selector) {
17716
 
        $valid = false; // value to be returned
17717
 
        $tag = $dom[$key]['value'];
17718
 
        $class = array();
17719
 
        if (isset($dom[$key]['attribute']['class']) AND !empty($dom[$key]['attribute']['class'])) {
17720
 
            $class = explode(' ', strtolower($dom[$key]['attribute']['class']));
17721
 
        }
17722
 
        $id = '';
17723
 
        if (isset($dom[$key]['attribute']['id']) AND !empty($dom[$key]['attribute']['id'])) {
17724
 
            $id = strtolower($dom[$key]['attribute']['id']);
17725
 
        }
17726
 
        $selector = preg_replace('/([\>\+\~\s]{1})([\.]{1})([^\>\+\~\s]*)/si', '\\1*.\\3', $selector);
17727
 
        $matches = array();
17728
 
        if (preg_match_all('/([\>\+\~\s]{1})([a-zA-Z0-9\*]+)([^\>\+\~\s]*)/si', $selector, $matches, PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE) > 0) {
17729
 
            $parentop = array_pop($matches[1]);
17730
 
            $operator = $parentop[0];
17731
 
            $offset = $parentop[1];
17732
 
            $lasttag = array_pop($matches[2]);
17733
 
            $lasttag = strtolower(trim($lasttag[0]));
17734
 
            if (($lasttag == '*') OR ($lasttag == $tag)) {
17735
 
                // the last element on selector is our tag or 'any tag'
17736
 
                $attrib = array_pop($matches[3]);
17737
 
                $attrib = strtolower(trim($attrib[0]));
17738
 
                if (!empty($attrib)) {
17739
 
                    // check if matches class, id, attribute, pseudo-class or pseudo-element
17740
 
                    switch ($attrib{0}) {
17741
 
                        case '.': { // class
17742
 
                            if (in_array(substr($attrib, 1), $class)) {
17743
 
                                $valid = true;
17744
 
                            }
17745
 
                            break;
17746
 
                        }
17747
 
                        case '#': { // ID
17748
 
                            if (substr($attrib, 1) == $id) {
17749
 
                                $valid = true;
17750
 
                            }
17751
 
                            break;
17752
 
                        }
17753
 
                        case '[': { // attribute
17754
 
                            $attrmatch = array();
17755
 
                            if (preg_match('/\[([a-zA-Z0-9]*)[\s]*([\~\^\$\*\|\=]*)[\s]*["]?([^"\]]*)["]?\]/i', $attrib, $attrmatch) > 0) {
17756
 
                                $att = strtolower($attrmatch[1]);
17757
 
                                $val = $attrmatch[3];
17758
 
                                if (isset($dom[$key]['attribute'][$att])) {
17759
 
                                    switch ($attrmatch[2]) {
17760
 
                                        case '=': {
17761
 
                                            if ($dom[$key]['attribute'][$att] == $val) {
17762
 
                                                $valid = true;
17763
 
                                            }
17764
 
                                            break;
17765
 
                                        }
17766
 
                                        case '~=': {
17767
 
                                            if (in_array($val, explode(' ', $dom[$key]['attribute'][$att]))) {
17768
 
                                                $valid = true;
17769
 
                                            }
17770
 
                                            break;
17771
 
                                        }
17772
 
                                        case '^=': {
17773
 
                                            if ($val == substr($dom[$key]['attribute'][$att], 0, strlen($val))) {
17774
 
                                                $valid = true;
17775
 
                                            }
17776
 
                                            break;
17777
 
                                        }
17778
 
                                        case '$=': {
17779
 
                                            if ($val == substr($dom[$key]['attribute'][$att], -strlen($val))) {
17780
 
                                                $valid = true;
17781
 
                                            }
17782
 
                                            break;
17783
 
                                        }
17784
 
                                        case '*=': {
17785
 
                                            if (strpos($dom[$key]['attribute'][$att], $val) !== false) {
17786
 
                                                $valid = true;
17787
 
                                            }
17788
 
                                            break;
17789
 
                                        }
17790
 
                                        case '|=': {
17791
 
                                            if ($dom[$key]['attribute'][$att] == $val) {
17792
 
                                                $valid = true;
17793
 
                                            } elseif (preg_match('/'.$val.'[\-]{1}/i', $dom[$key]['attribute'][$att]) > 0) {
17794
 
                                                $valid = true;
17795
 
                                            }
17796
 
                                            break;
17797
 
                                        }
17798
 
                                        default: {
17799
 
                                            $valid = true;
17800
 
                                        }
17801
 
                                    }
17802
 
                                }
17803
 
                            }
17804
 
                            break;
17805
 
                        }
17806
 
                        case ':': { // pseudo-class or pseudo-element
17807
 
                            if ($attrib{1} == ':') { // pseudo-element
17808
 
                                // pseudo-elements are not supported!
17809
 
                                // (::first-line, ::first-letter, ::before, ::after)
17810
 
                            } else { // pseudo-class
17811
 
                                // pseudo-classes are not supported!
17812
 
                                // (:root, :nth-child(n), :nth-last-child(n), :nth-of-type(n), :nth-last-of-type(n), :first-child, :last-child, :first-of-type, :last-of-type, :only-child, :only-of-type, :empty, :link, :visited, :active, :hover, :focus, :target, :lang(fr), :enabled, :disabled, :checked)
17813
 
                            }
17814
 
                            break;
17815
 
                        }
17816
 
                    } // end of switch
17817
 
                } else {
17818
 
                    $valid = true;
17819
 
                }
17820
 
                if ($valid AND ($offset > 0)) {
17821
 
                    $valid = false;
17822
 
                    // check remaining selector part
17823
 
                    $selector = substr($selector, 0, $offset);
17824
 
                    switch ($operator) {
17825
 
                        case ' ': { // descendant of an element
17826
 
                            while ($dom[$key]['parent'] > 0) {
17827
 
                                if ($this->isValidCSSSelectorForTag($dom, $dom[$key]['parent'], $selector)) {
17828
 
                                    $valid = true;
17829
 
                                    break;
17830
 
                                } else {
17831
 
                                    $key = $dom[$key]['parent'];
17832
 
                                }
17833
 
                            }
17834
 
                            break;
17835
 
                        }
17836
 
                        case '>': { // child of an element
17837
 
                            $valid = $this->isValidCSSSelectorForTag($dom, $dom[$key]['parent'], $selector);
17838
 
                            break;
17839
 
                        }
17840
 
                        case '+': { // immediately preceded by an element
17841
 
                            for ($i = ($key - 1); $i > $dom[$key]['parent']; --$i) {
17842
 
                                if ($dom[$i]['tag'] AND $dom[$i]['opening']) {
17843
 
                                    $valid = $this->isValidCSSSelectorForTag($dom, $i, $selector);
17844
 
                                    break;
17845
 
                                }
17846
 
                            }
17847
 
                            break;
17848
 
                        }
17849
 
                        case '~': { // preceded by an element
17850
 
                            for ($i = ($key - 1); $i > $dom[$key]['parent']; --$i) {
17851
 
                                if ($dom[$i]['tag'] AND $dom[$i]['opening']) {
17852
 
                                    if ($this->isValidCSSSelectorForTag($dom, $i, $selector)) {
17853
 
                                        break;
17854
 
                                    }
17855
 
                                }
17856
 
                            }
17857
 
                            break;
17858
 
                        }
17859
 
                    }
17860
 
                }
17861
 
            }
17862
 
        }
17863
 
        return $valid;
17864
 
    }
17865
 
 
17866
 
    /**
17867
 
     * Returns the styles that apply for the selected HTML tag.
17868
 
     * @param $dom (array) array of HTML tags and properties
17869
 
     * @param $key (int) key of the current HTML tag
17870
 
     * @param $css (array) array of CSS properties
17871
 
     * @return string containing CSS properties
17872
 
     * @protected
17873
 
     * @since 5.1.000 (2010-05-25)
17874
 
     */
17875
 
    protected function getTagStyleFromCSS($dom, $key, $css) {
17876
 
        $tagstyle = ''; // style to be returned
17877
 
        // get all styles that apply
17878
 
        foreach($css as $selector => $style) {
17879
 
            // remove specificity
17880
 
            $selector = substr($selector, strpos($selector, ' '));
17881
 
            // check if this selector apply to current tag
17882
 
            if ($this->isValidCSSSelectorForTag($dom, $key, $selector)) {
17883
 
                // apply style
17884
 
                $tagstyle .= ';'.$style;
17885
 
            }
17886
 
        }
17887
 
        if (isset($dom[$key]['attribute']['style'])) {
17888
 
            // attach inline style (latest properties have high priority)
17889
 
            $tagstyle .= ';'.$dom[$key]['attribute']['style'];
17890
 
        }
17891
 
        // remove multiple semicolons
17892
 
        $tagstyle = preg_replace('/[;]+/', ';', $tagstyle);
17893
 
        return $tagstyle;
17894
 
    }
17895
 
 
17896
 
    /**
17897
 
     * Returns the border width from CSS property
17898
 
     * @param $width (string) border width
17899
 
     * @return int with in user units
17900
 
     * @protected
17901
 
     * @since 5.7.000 (2010-08-02)
17902
 
     */
17903
 
    protected function getCSSBorderWidth($width) {
17904
 
        if ($width == 'thin') {
17905
 
            $width = (2 / $this->k);
17906
 
        } elseif ($width == 'medium') {
17907
 
            $width = (4 / $this->k);
17908
 
        } elseif ($width == 'thick') {
17909
 
            $width = (6 / $this->k);
17910
 
        } else {
17911
 
            $width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
17912
 
        }
17913
 
        return $width;
17914
 
    }
17915
 
 
17916
 
    /**
17917
 
     * Returns the border dash style from CSS property
17918
 
     * @param $style (string) border style to convert
17919
 
     * @return int sash style (return -1 in case of none or hidden border)
17920
 
     * @protected
17921
 
     * @since 5.7.000 (2010-08-02)
17922
 
     */
17923
 
    protected function getCSSBorderDashStyle($style) {
17924
 
        switch (strtolower($style)) {
17925
 
            case 'none':
17926
 
            case 'hidden': {
17927
 
                $dash = -1;
17928
 
                break;
17929
 
            }
17930
 
            case 'dotted': {
17931
 
                $dash = 1;
17932
 
                break;
17933
 
            }
17934
 
            case 'dashed': {
17935
 
                $dash = 3;
17936
 
                break;
17937
 
            }
17938
 
            case 'double':
17939
 
            case 'groove':
17940
 
            case 'ridge':
17941
 
            case 'inset':
17942
 
            case 'outset':
17943
 
            case 'solid':
17944
 
            default: {
17945
 
                $dash = 0;
17946
 
                break;
17947
 
            }
17948
 
        }
17949
 
        return $dash;
17950
 
    }
17951
 
 
17952
 
    /**
17953
 
     * Returns the border style array from CSS border properties
17954
 
     * @param $cssborder (string) border properties
17955
 
     * @return array containing border properties
17956
 
     * @protected
17957
 
     * @since 5.7.000 (2010-08-02)
17958
 
     */
17959
 
    protected function getCSSBorderStyle($cssborder) {
17960
 
        $bprop = preg_split('/[\s]+/', trim($cssborder));
17961
 
        $border = array(); // value to be returned
17962
 
        switch (count($bprop)) {
17963
 
            case 3: {
17964
 
                $width = $bprop[0];
17965
 
                $style = $bprop[1];
17966
 
                $color = $bprop[2];
17967
 
                break;
17968
 
            }
17969
 
            case 2: {
17970
 
                $width = 'medium';
17971
 
                $style = $bprop[0];
17972
 
                $color = $bprop[1];
17973
 
                break;
17974
 
            }
17975
 
            case 1: {
17976
 
                $width = 'medium';
17977
 
                $style = $bprop[0];
17978
 
                $color = 'black';
17979
 
                break;
17980
 
            }
17981
 
            default: {
17982
 
                $width = 'medium';
17983
 
                $style = 'solid';
17984
 
                $color = 'black';
17985
 
                break;
17986
 
            }
17987
 
        }
17988
 
        if ($style == 'none') {
17989
 
            return array();
17990
 
        }
17991
 
        $border['cap'] = 'square';
17992
 
        $border['join'] = 'miter';
17993
 
        $border['dash'] = $this->getCSSBorderDashStyle($style);
17994
 
        if ($border['dash'] < 0) {
17995
 
            return array();
17996
 
        }
17997
 
        $border['width'] = $this->getCSSBorderWidth($width);
17998
 
        $border['color'] = $this->convertHTMLColorToDec($color);
17999
 
        return $border;
18000
 
    }
18001
 
 
18002
 
    /**
18003
 
     * Get the internal Cell padding from CSS attribute.
18004
 
     * @param $csspadding (string) padding properties
18005
 
     * @param $width (float) width of the containing element
18006
 
     * @return array of cell paddings
18007
 
     * @public
18008
 
     * @since 5.9.000 (2010-10-04)
18009
 
     */
18010
 
    public function getCSSPadding($csspadding, $width=0) {
18011
 
        $padding = preg_split('/[\s]+/', trim($csspadding));
18012
 
        $cell_padding = array(); // value to be returned
18013
 
        switch (count($padding)) {
18014
 
            case 4: {
18015
 
                $cell_padding['T'] = $padding[0];
18016
 
                $cell_padding['R'] = $padding[1];
18017
 
                $cell_padding['B'] = $padding[2];
18018
 
                $cell_padding['L'] = $padding[3];
18019
 
                break;
18020
 
            }
18021
 
            case 3: {
18022
 
                $cell_padding['T'] = $padding[0];
18023
 
                $cell_padding['R'] = $padding[1];
18024
 
                $cell_padding['B'] = $padding[2];
18025
 
                $cell_padding['L'] = $padding[1];
18026
 
                break;
18027
 
            }
18028
 
            case 2: {
18029
 
                $cell_padding['T'] = $padding[0];
18030
 
                $cell_padding['R'] = $padding[1];
18031
 
                $cell_padding['B'] = $padding[0];
18032
 
                $cell_padding['L'] = $padding[1];
18033
 
                break;
18034
 
            }
18035
 
            case 1: {
18036
 
                $cell_padding['T'] = $padding[0];
18037
 
                $cell_padding['R'] = $padding[0];
18038
 
                $cell_padding['B'] = $padding[0];
18039
 
                $cell_padding['L'] = $padding[0];
18040
 
                break;
18041
 
            }
18042
 
            default: {
18043
 
                return $this->cell_padding;
18044
 
            }
18045
 
        }
18046
 
        if ($width == 0) {
18047
 
            $width = $this->w - $this->lMargin - $this->rMargin;
18048
 
        }
18049
 
        $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
18050
 
        $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
18051
 
        $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
18052
 
        $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
18053
 
        return $cell_padding;
18054
 
    }
18055
 
 
18056
 
    /**
18057
 
     * Get the internal Cell margin from CSS attribute.
18058
 
     * @param $cssmargin (string) margin properties
18059
 
     * @param $width (float) width of the containing element
18060
 
     * @return array of cell margins
18061
 
     * @public
18062
 
     * @since 5.9.000 (2010-10-04)
18063
 
     */
18064
 
    public function getCSSMargin($cssmargin, $width=0) {
18065
 
        $margin = preg_split('/[\s]+/', trim($cssmargin));
18066
 
        $cell_margin = array(); // value to be returned
18067
 
        switch (count($margin)) {
18068
 
            case 4: {
18069
 
                $cell_margin['T'] = $margin[0];
18070
 
                $cell_margin['R'] = $margin[1];
18071
 
                $cell_margin['B'] = $margin[2];
18072
 
                $cell_margin['L'] = $margin[3];
18073
 
                break;
18074
 
            }
18075
 
            case 3: {
18076
 
                $cell_margin['T'] = $margin[0];
18077
 
                $cell_margin['R'] = $margin[1];
18078
 
                $cell_margin['B'] = $margin[2];
18079
 
                $cell_margin['L'] = $margin[1];
18080
 
                break;
18081
 
            }
18082
 
            case 2: {
18083
 
                $cell_margin['T'] = $margin[0];
18084
 
                $cell_margin['R'] = $margin[1];
18085
 
                $cell_margin['B'] = $margin[0];
18086
 
                $cell_margin['L'] = $margin[1];
18087
 
                break;
18088
 
            }
18089
 
            case 1: {
18090
 
                $cell_margin['T'] = $margin[0];
18091
 
                $cell_margin['R'] = $margin[0];
18092
 
                $cell_margin['B'] = $margin[0];
18093
 
                $cell_margin['L'] = $margin[0];
18094
 
                break;
18095
 
            }
18096
 
            default: {
18097
 
                return $this->cell_margin;
18098
 
            }
18099
 
        }
18100
 
        if ($width == 0) {
18101
 
            $width = $this->w - $this->lMargin - $this->rMargin;
18102
 
        }
18103
 
        $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
18104
 
        $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
18105
 
        $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
18106
 
        $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
18107
 
        return $cell_margin;
18108
 
    }
18109
 
 
18110
 
    /**
18111
 
     * Get the border-spacing from CSS attribute.
18112
 
     * @param $cssbspace (string) border-spacing CSS properties
18113
 
     * @param $width (float) width of the containing element
18114
 
     * @return array of border spacings
18115
 
     * @public
18116
 
     * @since 5.9.010 (2010-10-27)
18117
 
     */
18118
 
    public function getCSSBorderMargin($cssbspace, $width=0) {
18119
 
        $space = preg_split('/[\s]+/', trim($cssbspace));
18120
 
        $border_spacing = array(); // value to be returned
18121
 
        switch (count($space)) {
18122
 
            case 2: {
18123
 
                $border_spacing['H'] = $space[0];
18124
 
                $border_spacing['V'] = $space[1];
18125
 
                break;
18126
 
            }
18127
 
            case 1: {
18128
 
                $border_spacing['H'] = $space[0];
18129
 
                $border_spacing['V'] = $space[0];
18130
 
                break;
18131
 
            }
18132
 
            default: {
18133
 
                return array('H' => 0, 'V' => 0);
18134
 
            }
18135
 
        }
18136
 
        if ($width == 0) {
18137
 
            $width = $this->w - $this->lMargin - $this->rMargin;
18138
 
        }
18139
 
        $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
18140
 
        $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
18141
 
        return $border_spacing;
18142
 
    }
18143
 
 
18144
 
    /**
18145
 
     * Returns the letter-spacing value from CSS value
18146
 
     * @param $spacing (string) letter-spacing value
18147
 
     * @param $parent (float) font spacing (tracking/kerning) value of the parent element
18148
 
     * @return float quantity to increases or decreases the space between characters in a text.
18149
 
     * @protected
18150
 
     * @since 5.9.000 (2010-10-02)
18151
 
     */
18152
 
    protected function getCSSFontSpacing($spacing, $parent=0) {
18153
 
        $val = 0; // value to be returned
18154
 
        $spacing = trim($spacing);
18155
 
        switch ($spacing) {
18156
 
            case 'normal': {
18157
 
                $val = 0;
18158
 
                break;
18159
 
            }
18160
 
            case 'inherit': {
18161
 
                if ($parent == 'normal') {
18162
 
                    $val = 0;
18163
 
                } else {
18164
 
                    $val = $parent;
18165
 
                }
18166
 
                break;
18167
 
            }
18168
 
            default: {
18169
 
                $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
18170
 
            }
18171
 
        }
18172
 
        return $val;
18173
 
    }
18174
 
 
18175
 
    /**
18176
 
     * Returns the percentage of font stretching from CSS value
18177
 
     * @param $stretch (string) stretch mode
18178
 
     * @param $parent (float) stretch value of the parent element
18179
 
     * @return float font stretching percentage
18180
 
     * @protected
18181
 
     * @since 5.9.000 (2010-10-02)
18182
 
     */
18183
 
    protected function getCSSFontStretching($stretch, $parent=100) {
18184
 
        $val = 100; // value to be returned
18185
 
        $stretch = trim($stretch);
18186
 
        switch ($stretch) {
18187
 
            case 'ultra-condensed': {
18188
 
                $val = 40;
18189
 
                break;
18190
 
            }
18191
 
            case 'extra-condensed': {
18192
 
                $val = 55;
18193
 
                break;
18194
 
            }
18195
 
            case 'condensed': {
18196
 
                $val = 70;
18197
 
                break;
18198
 
            }
18199
 
            case 'semi-condensed': {
18200
 
                $val = 85;
18201
 
                break;
18202
 
            }
18203
 
            case 'normal': {
18204
 
                $val = 100;
18205
 
                break;
18206
 
            }
18207
 
            case 'semi-expanded': {
18208
 
                $val = 115;
18209
 
                break;
18210
 
            }
18211
 
            case 'expanded': {
18212
 
                $val = 130;
18213
 
                break;
18214
 
            }
18215
 
            case 'extra-expanded': {
18216
 
                $val = 145;
18217
 
                break;
18218
 
            }
18219
 
            case 'ultra-expanded': {
18220
 
                $val = 160;
18221
 
                break;
18222
 
            }
18223
 
            case 'wider': {
18224
 
                $val = $parent + 10;
18225
 
                break;
18226
 
            }
18227
 
            case 'narrower': {
18228
 
                $val = $parent - 10;
18229
 
                break;
18230
 
            }
18231
 
            case 'inherit': {
18232
 
                if ($parent == 'normal') {
18233
 
                    $val = 100;
18234
 
                } else {
18235
 
                    $val = $parent;
18236
 
                }
18237
 
                break;
18238
 
            }
18239
 
            default: {
18240
 
                $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
18241
 
            }
18242
 
        }
18243
 
        return $val;
18244
 
    }
18245
 
 
18246
 
    /**
18247
 
     * Returns the HTML DOM array.
18248
 
     * @param $html (string) html code
18249
 
     * @return array
18250
 
     * @protected
18251
 
     * @since 3.2.000 (2008-06-20)
18252
 
     */
18253
 
    protected function getHtmlDomArray($html) {
18254
 
        // array of CSS styles ( selector => properties).
18255
 
        $css = array();
18256
 
        // get CSS array defined at previous call
18257
 
        $matches = array();
18258
 
        if (preg_match_all('/<cssarray>([^\<]*)<\/cssarray>/isU', $html, $matches) > 0) {
18259
 
            if (isset($matches[1][0])) {
18260
 
                $css = array_merge($css, unserialize($this->unhtmlentities($matches[1][0])));
18261
 
            }
18262
 
            $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
18263
 
        }
18264
 
        // extract external CSS files
18265
 
        $matches = array();
18266
 
        if (preg_match_all('/<link([^\>]*)>/isU', $html, $matches) > 0) {
18267
 
            foreach ($matches[1] as $key => $link) {
18268
 
                $type = array();
18269
 
                if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
18270
 
                    $type = array();
18271
 
                    preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
18272
 
                    // get 'all' and 'print' media, other media types are discarded
18273
 
                    // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
18274
 
                    if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
18275
 
                        $type = array();
18276
 
                        if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
18277
 
                            // read CSS data file
18278
 
                            $cssdata = file_get_contents(trim($type[1]));
18279
 
                            $css = array_merge($css, $this->extractCSSproperties($cssdata));
18280
 
                        }
18281
 
                    }
18282
 
                }
18283
 
            }
18284
 
        }
18285
 
        // extract style tags
18286
 
        $matches = array();
18287
 
        if (preg_match_all('/<style([^\>]*)>([^\<]*)<\/style>/isU', $html, $matches) > 0) {
18288
 
            foreach ($matches[1] as $key => $media) {
18289
 
                $type = array();
18290
 
                preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
18291
 
                // get 'all' and 'print' media, other media types are discarded
18292
 
                // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
18293
 
                if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
18294
 
                    $cssdata = $matches[2][$key];
18295
 
                    $css = array_merge($css, $this->extractCSSproperties($cssdata));
18296
 
                }
18297
 
            }
18298
 
        }
18299
 
        // create a special tag to contain the CSS array (used for table content)
18300
 
        $csstagarray = '<cssarray>'.htmlentities(serialize($css)).'</cssarray>';
18301
 
        // remove head and style blocks
18302
 
        $html = preg_replace('/<head([^\>]*)>(.*?)<\/head>/siU', '', $html);
18303
 
        $html = preg_replace('/<style([^\>]*)>([^\<]*)<\/style>/isU', '', $html);
18304
 
        // define block tags
18305
 
        $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
18306
 
        // define self-closing tags
18307
 
        $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
18308
 
        // remove all unsupported tags (the line below lists all supported tags)
18309
 
        $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>');
18310
 
        //replace some blank characters
18311
 
        $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
18312
 
        $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);
18313
 
        $html = preg_replace('@(\r\n|\r)@', "\n", $html);
18314
 
        $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
18315
 
        $html = strtr($html, $repTable);
18316
 
        $offset = 0;
18317
 
        while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
18318
 
            $html_a = substr($html, 0, $offset);
18319
 
            $html_b = substr($html, $offset, ($pos - $offset + 6));
18320
 
            while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
18321
 
                // preserve newlines on <pre> tag
18322
 
                $html_b = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
18323
 
            }
18324
 
            while (preg_match("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) {
18325
 
                // preserve spaces on <pre> tag
18326
 
                $html_b = preg_replace("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2&nbsp;\\3</pre>", $html_b);
18327
 
            }
18328
 
            $html = $html_a.$html_b.substr($html, $pos + 6);
18329
 
            $offset = strlen($html_a.$html_b);
18330
 
        }
18331
 
        $offset = 0;
18332
 
        while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
18333
 
            $html_a = substr($html, 0, $offset);
18334
 
            $html_b = substr($html, $offset, ($pos - $offset + 11));
18335
 
            while (preg_match("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
18336
 
                // preserve newlines on <textarea> tag
18337
 
                $html_b = preg_replace("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
18338
 
                $html_b = preg_replace("'<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
18339
 
            }
18340
 
            $html = $html_a.$html_b.substr($html, $pos + 11);
18341
 
            $offset = strlen($html_a.$html_b);
18342
 
        }
18343
 
        $html = preg_replace('/([\s]*)<option/si', '<option', $html);
18344
 
        $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
18345
 
        $offset = 0;
18346
 
        while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
18347
 
            $html_a = substr($html, 0, $offset);
18348
 
            $html_b = substr($html, $offset, ($pos - $offset + 9));
18349
 
            while (preg_match("'<option([^\>]*)>(.*?)</option>'si", $html_b)) {
18350
 
                $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
18351
 
                $html_b = preg_replace("'<option([^\>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
18352
 
            }
18353
 
            $html = $html_a.$html_b.substr($html, $pos + 9);
18354
 
            $offset = strlen($html_a.$html_b);
18355
 
        }
18356
 
        if (preg_match("'</select'si", $html)) {
18357
 
            $html = preg_replace("'<select([^\>]*)>'si", "<select\\1 opt=\"", $html);
18358
 
            $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
18359
 
        }
18360
 
        $html = str_replace("\n", ' ', $html);
18361
 
        // restore textarea newlines
18362
 
        $html = str_replace('<TBR>', "\n", $html);
18363
 
        // remove extra spaces from code
18364
 
        $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
18365
 
        $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html);
18366
 
        $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
18367
 
        $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html);
18368
 
        $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);
18369
 
        $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
18370
 
        $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
18371
 
        $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html);
18372
 
        $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
18373
 
        $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
18374
 
        $html = preg_replace('/<textarea([^\>]*)>([^\<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
18375
 
        $html = preg_replace('/<li([^\>]*)><\/li>/', '<li\\1>&nbsp;</li>', $html);
18376
 
        $html = preg_replace('/<li([^\>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1">&nbsp;</font><img', $html);
18377
 
        $html = preg_replace('/<([^\>\/]*)>[\s]/', '<\\1>&nbsp;', $html); // preserve some spaces
18378
 
        $html = preg_replace('/[\s]<\/([^\>]*)>/', '&nbsp;</\\1>', $html); // preserve some spaces
18379
 
        $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space
18380
 
        // trim string
18381
 
        $html = $this->stringTrim($html);
18382
 
        // pattern for generic tag
18383
 
        $tagpattern = '/(<[^>]+>)/';
18384
 
        // explodes the string
18385
 
        $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
18386
 
        // count elements
18387
 
        $maxel = count($a);
18388
 
        $elkey = 0;
18389
 
        $key = 0;
18390
 
        // create an array of elements
18391
 
        $dom = array();
18392
 
        $dom[$key] = array();
18393
 
        // set inheritable properties fot the first void element
18394
 
        // 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
18395
 
        $dom[$key]['tag'] = false;
18396
 
        $dom[$key]['block'] = false;
18397
 
        $dom[$key]['value'] = '';
18398
 
        $dom[$key]['parent'] = 0;
18399
 
        $dom[$key]['fontname'] = $this->FontFamily;
18400
 
        $dom[$key]['fontstyle'] = $this->FontStyle;
18401
 
        $dom[$key]['fontsize'] = $this->FontSizePt;
18402
 
        $dom[$key]['font-stretch'] = $this->font_stretching;
18403
 
        $dom[$key]['letter-spacing'] = $this->font_spacing;
18404
 
        $dom[$key]['stroke'] = $this->textstrokewidth;
18405
 
        $dom[$key]['fill'] = (($this->textrendermode % 2) == 0);
18406
 
        $dom[$key]['clip'] = ($this->textrendermode > 3);
18407
 
        $dom[$key]['line-height'] = $this->cell_height_ratio;
18408
 
        $dom[$key]['bgcolor'] = false;
18409
 
        $dom[$key]['fgcolor'] = $this->fgcolor; // color
18410
 
        $dom[$key]['strokecolor'] = $this->strokecolor;
18411
 
        $dom[$key]['align'] = '';
18412
 
        $dom[$key]['listtype'] = '';
18413
 
        $dom[$key]['text-indent'] = 0;
18414
 
        $dom[$key]['border'] = array();
18415
 
        $dom[$key]['dir'] = $this->rtl?'rtl':'ltr';
18416
 
        $thead = false; // true when we are inside the THEAD tag
18417
 
        ++$key;
18418
 
        $level = array();
18419
 
        array_push($level, 0); // root
18420
 
        while ($elkey < $maxel) {
18421
 
            $dom[$key] = array();
18422
 
            $element = $a[$elkey];
18423
 
            $dom[$key]['elkey'] = $elkey;
18424
 
            if (preg_match($tagpattern, $element)) {
18425
 
                // html tag
18426
 
                $element = substr($element, 1, -1);
18427
 
                // get tag name
18428
 
                preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
18429
 
                $tagname = strtolower($tag[1]);
18430
 
                // check if we are inside a table header
18431
 
                if ($tagname == 'thead') {
18432
 
                    if ($element{0} == '/') {
18433
 
                        $thead = false;
18434
 
                    } else {
18435
 
                        $thead = true;
18436
 
                    }
18437
 
                    ++$elkey;
18438
 
                    continue;
18439
 
                }
18440
 
                $dom[$key]['tag'] = true;
18441
 
                $dom[$key]['value'] = $tagname;
18442
 
                if (in_array($dom[$key]['value'], $blocktags)) {
18443
 
                    $dom[$key]['block'] = true;
18444
 
                } else {
18445
 
                    $dom[$key]['block'] = false;
18446
 
                }
18447
 
                if ($element{0} == '/') {
18448
 
                    // *** closing html tag
18449
 
                    $dom[$key]['opening'] = false;
18450
 
                    $dom[$key]['parent'] = end($level);
18451
 
                    array_pop($level);
18452
 
                    $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
18453
 
                    $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
18454
 
                    $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
18455
 
                    $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
18456
 
                    $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
18457
 
                    $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
18458
 
                    $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
18459
 
                    $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
18460
 
                    $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
18461
 
                    $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
18462
 
                    $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
18463
 
                    $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
18464
 
                    $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
18465
 
                    $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
18466
 
                    if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
18467
 
                        $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
18468
 
                    }
18469
 
                    // set the number of columns in table tag
18470
 
                    if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
18471
 
                        $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
18472
 
                    }
18473
 
                    if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
18474
 
                        $dom[($dom[$key]['parent'])]['content'] = $csstagarray;
18475
 
                        for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
18476
 
                            $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
18477
 
                        }
18478
 
                        $key = $i;
18479
 
                        // mark nested tables
18480
 
                        $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
18481
 
                        // remove thead sections from nested tables
18482
 
                        $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
18483
 
                        $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
18484
 
                    }
18485
 
                    // store header rows on a new table
18486
 
                    if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
18487
 
                        if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
18488
 
                            $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
18489
 
                        }
18490
 
                        for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
18491
 
                            $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
18492
 
                        }
18493
 
                        if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
18494
 
                            $dom[($dom[$key]['parent'])]['attribute'] = array();
18495
 
                        }
18496
 
                        // header elements must be always contained in a single page
18497
 
                        $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
18498
 
                    }
18499
 
                    if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) {
18500
 
                        // remove the nobr attributes from the table header
18501
 
                        $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
18502
 
                        $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
18503
 
                    }
18504
 
                } else {
18505
 
                    // *** opening or self-closing html tag
18506
 
                    $dom[$key]['opening'] = true;
18507
 
                    $dom[$key]['parent'] = end($level);
18508
 
                    if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
18509
 
                        // self-closing tag
18510
 
                        $dom[$key]['self'] = true;
18511
 
                    } else {
18512
 
                        // opening tag
18513
 
                        array_push($level, $key);
18514
 
                        $dom[$key]['self'] = false;
18515
 
                    }
18516
 
                    // copy some values from parent
18517
 
                    $parentkey = 0;
18518
 
                    if ($key > 0) {
18519
 
                        $parentkey = $dom[$key]['parent'];
18520
 
                        $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
18521
 
                        $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
18522
 
                        $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
18523
 
                        $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
18524
 
                        $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
18525
 
                        $dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
18526
 
                        $dom[$key]['fill'] = $dom[$parentkey]['fill'];
18527
 
                        $dom[$key]['clip'] = $dom[$parentkey]['clip'];
18528
 
                        $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
18529
 
                        $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
18530
 
                        $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
18531
 
                        $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
18532
 
                        $dom[$key]['align'] = $dom[$parentkey]['align'];
18533
 
                        $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
18534
 
                        $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
18535
 
                        $dom[$key]['border'] = array();
18536
 
                        $dom[$key]['dir'] = $dom[$parentkey]['dir'];
18537
 
                    }
18538
 
                    // get attributes
18539
 
                    preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER);
18540
 
                    $dom[$key]['attribute'] = array(); // reset attribute array
18541
 
                    while (list($id, $name) = each($attr_array[1])) {
18542
 
                        $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
18543
 
                    }
18544
 
                    if (!empty($css)) {
18545
 
                        // merge eternal CSS style to current style
18546
 
                        $dom[$key]['attribute']['style'] = $this->getTagStyleFromCSS($dom, $key, $css);
18547
 
                    }
18548
 
                    // split style attributes
18549
 
                    if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
18550
 
                        // get style attributes
18551
 
                        preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
18552
 
                        $dom[$key]['style'] = array(); // reset style attribute array
18553
 
                        while (list($id, $name) = each($style_array[1])) {
18554
 
                            // in case of duplicate attribute the last replace the previous
18555
 
                            $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
18556
 
                        }
18557
 
                        // --- get some style attributes ---
18558
 
                        // text direction
18559
 
                        if (isset($dom[$key]['style']['direction'])) {
18560
 
                            $dom[$key]['dir'] = $dom[$key]['style']['direction'];
18561
 
                        }
18562
 
                        // font family
18563
 
                        if (isset($dom[$key]['style']['font-family'])) {
18564
 
                            $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
18565
 
                        }
18566
 
                        // list-style-type
18567
 
                        if (isset($dom[$key]['style']['list-style-type'])) {
18568
 
                            $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
18569
 
                            if ($dom[$key]['listtype'] == 'inherit') {
18570
 
                                $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
18571
 
                            }
18572
 
                        }
18573
 
                        // text-indent
18574
 
                        if (isset($dom[$key]['style']['text-indent'])) {
18575
 
                            $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
18576
 
                            if ($dom[$key]['text-indent'] == 'inherit') {
18577
 
                                $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
18578
 
                            }
18579
 
                        }
18580
 
                        // font size
18581
 
                        if (isset($dom[$key]['style']['font-size'])) {
18582
 
                            $fsize = trim($dom[$key]['style']['font-size']);
18583
 
                            switch ($fsize) {
18584
 
                                // absolute-size
18585
 
                                case 'xx-small': {
18586
 
                                    $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
18587
 
                                    break;
18588
 
                                }
18589
 
                                case 'x-small': {
18590
 
                                    $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
18591
 
                                    break;
18592
 
                                }
18593
 
                                case 'small': {
18594
 
                                    $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
18595
 
                                    break;
18596
 
                                }
18597
 
                                case 'medium': {
18598
 
                                    $dom[$key]['fontsize'] = $dom[0]['fontsize'];
18599
 
                                    break;
18600
 
                                }
18601
 
                                case 'large': {
18602
 
                                    $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
18603
 
                                    break;
18604
 
                                }
18605
 
                                case 'x-large': {
18606
 
                                    $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
18607
 
                                    break;
18608
 
                                }
18609
 
                                case 'xx-large': {
18610
 
                                    $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
18611
 
                                    break;
18612
 
                                }
18613
 
                                // relative-size
18614
 
                                case 'smaller': {
18615
 
                                    $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3;
18616
 
                                    break;
18617
 
                                }
18618
 
                                case 'larger': {
18619
 
                                    $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3;
18620
 
                                    break;
18621
 
                                }
18622
 
                                default: {
18623
 
                                    $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true);
18624
 
                                }
18625
 
                            }
18626
 
                        }
18627
 
                        // font-stretch
18628
 
                        if (isset($dom[$key]['style']['font-stretch'])) {
18629
 
                            $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
18630
 
                        }
18631
 
                        // letter-spacing
18632
 
                        if (isset($dom[$key]['style']['letter-spacing'])) {
18633
 
                            $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
18634
 
                        }
18635
 
                        // line-height
18636
 
                        if (isset($dom[$key]['style']['line-height'])) {
18637
 
                            $lineheight = trim($dom[$key]['style']['line-height']);
18638
 
                            switch ($lineheight) {
18639
 
                                // A normal line height. This is default
18640
 
                                case 'normal': {
18641
 
                                    $dom[$key]['line-height'] = $dom[0]['line-height'];
18642
 
                                    break;
18643
 
                                }
18644
 
                                default: {
18645
 
                                    if (is_numeric($lineheight)) {
18646
 
                                        $lineheight = $lineheight * 100;
18647
 
                                    }
18648
 
                                    $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
18649
 
                                }
18650
 
                            }
18651
 
                        }
18652
 
                        // font style
18653
 
                        if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == 'b')) {
18654
 
                            $dom[$key]['fontstyle'] .= 'B';
18655
 
                        }
18656
 
                        if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) {
18657
 
                            $dom[$key]['fontstyle'] .= 'I';
18658
 
                        }
18659
 
                        // font color
18660
 
                        if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) {
18661
 
                            $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
18662
 
                        } elseif ($dom[$key]['value'] == 'a') {
18663
 
                            $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
18664
 
                        }
18665
 
                        // background color
18666
 
                        if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) {
18667
 
                            $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
18668
 
                        }
18669
 
                        // text-decoration
18670
 
                        if (isset($dom[$key]['style']['text-decoration'])) {
18671
 
                            $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
18672
 
                            foreach ($decors as $dec) {
18673
 
                                $dec = trim($dec);
18674
 
                                if (!$this->empty_string($dec)) {
18675
 
                                    if ($dec{0} == 'u') {
18676
 
                                        // underline
18677
 
                                        $dom[$key]['fontstyle'] .= 'U';
18678
 
                                    } elseif ($dec{0} == 'l') {
18679
 
                                        // line-trough
18680
 
                                        $dom[$key]['fontstyle'] .= 'D';
18681
 
                                    } elseif ($dec{0} == 'o') {
18682
 
                                        // overline
18683
 
                                        $dom[$key]['fontstyle'] .= 'O';
18684
 
                                    }
18685
 
                                }
18686
 
                            }
18687
 
                        } elseif ($dom[$key]['value'] == 'a') {
18688
 
                            $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
18689
 
                        }
18690
 
                        // check for width attribute
18691
 
                        if (isset($dom[$key]['style']['width'])) {
18692
 
                            $dom[$key]['width'] = $dom[$key]['style']['width'];
18693
 
                        }
18694
 
                        // check for height attribute
18695
 
                        if (isset($dom[$key]['style']['height'])) {
18696
 
                            $dom[$key]['height'] = $dom[$key]['style']['height'];
18697
 
                        }
18698
 
                        // check for text alignment
18699
 
                        if (isset($dom[$key]['style']['text-align'])) {
18700
 
                            $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
18701
 
                        }
18702
 
                        // check for CSS border properties
18703
 
                        if (isset($dom[$key]['style']['border'])) {
18704
 
                            $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
18705
 
                            if (!empty($borderstyle)) {
18706
 
                                $dom[$key]['border']['LTRB'] = $borderstyle;
18707
 
                            }
18708
 
                        }
18709
 
                        if (isset($dom[$key]['style']['border-color'])) {
18710
 
                            $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
18711
 
                            if (isset($brd_colors[3])) {
18712
 
                                $dom[$key]['border']['L']['color'] = $this->convertHTMLColorToDec($brd_colors[3]);
18713
 
                            }
18714
 
                            if (isset($brd_colors[1])) {
18715
 
                                $dom[$key]['border']['R']['color'] = $this->convertHTMLColorToDec($brd_colors[1]);
18716
 
                            }
18717
 
                            if (isset($brd_colors[0])) {
18718
 
                                $dom[$key]['border']['T']['color'] = $this->convertHTMLColorToDec($brd_colors[0]);
18719
 
                            }
18720
 
                            if (isset($brd_colors[2])) {
18721
 
                                $dom[$key]['border']['B']['color'] = $this->convertHTMLColorToDec($brd_colors[2]);
18722
 
                            }
18723
 
                        }
18724
 
                        if (isset($dom[$key]['style']['border-width'])) {
18725
 
                            $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
18726
 
                            if (isset($brd_widths[3])) {
18727
 
                                $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
18728
 
                            }
18729
 
                            if (isset($brd_widths[1])) {
18730
 
                                $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
18731
 
                            }
18732
 
                            if (isset($brd_widths[0])) {
18733
 
                                $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
18734
 
                            }
18735
 
                            if (isset($brd_widths[2])) {
18736
 
                                $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
18737
 
                            }
18738
 
                        }
18739
 
                        if (isset($dom[$key]['style']['border-style'])) {
18740
 
                            $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
18741
 
                            if (isset($brd_styles[3])) {
18742
 
                                $dom[$key]['border']['L']['cap'] = 'square';
18743
 
                                $dom[$key]['border']['L']['join'] = 'miter';
18744
 
                                $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
18745
 
                                if ($dom[$key]['border']['L']['dash'] < 0) {
18746
 
                                    $dom[$key]['border']['L'] = array();
18747
 
                                }
18748
 
                            }
18749
 
                            if (isset($brd_styles[1])) {
18750
 
                                $dom[$key]['border']['R']['cap'] = 'square';
18751
 
                                $dom[$key]['border']['R']['join'] = 'miter';
18752
 
                                $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
18753
 
                                if ($dom[$key]['border']['R']['dash'] < 0) {
18754
 
                                    $dom[$key]['border']['R'] = array();
18755
 
                                }
18756
 
                            }
18757
 
                            if (isset($brd_styles[0])) {
18758
 
                                $dom[$key]['border']['T']['cap'] = 'square';
18759
 
                                $dom[$key]['border']['T']['join'] = 'miter';
18760
 
                                $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
18761
 
                                if ($dom[$key]['border']['T']['dash'] < 0) {
18762
 
                                    $dom[$key]['border']['T'] = array();
18763
 
                                }
18764
 
                            }
18765
 
                            if (isset($brd_styles[2])) {
18766
 
                                $dom[$key]['border']['B']['cap'] = 'square';
18767
 
                                $dom[$key]['border']['B']['join'] = 'miter';
18768
 
                                $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
18769
 
                                if ($dom[$key]['border']['B']['dash'] < 0) {
18770
 
                                    $dom[$key]['border']['B'] = array();
18771
 
                                }
18772
 
                            }
18773
 
                        }
18774
 
                        $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
18775
 
                        foreach ($cellside as $bsk => $bsv) {
18776
 
                            if (isset($dom[$key]['style']['border-'.$bsv])) {
18777
 
                                $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
18778
 
                                if (!empty($borderstyle)) {
18779
 
                                    $dom[$key]['border'][$bsk] = $borderstyle;
18780
 
                                }
18781
 
                            }
18782
 
                            if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
18783
 
                                $dom[$key]['border'][$bsk]['color'] = $this->convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color']);
18784
 
                            }
18785
 
                            if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
18786
 
                                $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
18787
 
                            }
18788
 
                            if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
18789
 
                                $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
18790
 
                                if ($dom[$key]['border'][$bsk]['dash'] < 0) {
18791
 
                                    $dom[$key]['border'][$bsk] = array();
18792
 
                                }
18793
 
                            }
18794
 
                        }
18795
 
                        // check for CSS padding properties
18796
 
                        if (isset($dom[$key]['style']['padding'])) {
18797
 
                            $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
18798
 
                        } else {
18799
 
                            $dom[$key]['padding'] = $this->cell_padding;
18800
 
                        }
18801
 
                        foreach ($cellside as $psk => $psv) {
18802
 
                            if (isset($dom[$key]['style']['padding-'.$psv])) {
18803
 
                                $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
18804
 
                            }
18805
 
                        }
18806
 
                        // check for CSS margin properties
18807
 
                        if (isset($dom[$key]['style']['margin'])) {
18808
 
                            $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
18809
 
                        } else {
18810
 
                            $dom[$key]['margin'] = $this->cell_margin;
18811
 
                        }
18812
 
                        foreach ($cellside as $psk => $psv) {
18813
 
                            if (isset($dom[$key]['style']['margin-'.$psv])) {
18814
 
                                $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
18815
 
                            }
18816
 
                        }
18817
 
                        // check for CSS border-spacing properties
18818
 
                        if (isset($dom[$key]['style']['border-spacing'])) {
18819
 
                            $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
18820
 
                        }
18821
 
                        // page-break-inside
18822
 
                        if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
18823
 
                            $dom[$key]['attribute']['nobr'] = 'true';
18824
 
                        }
18825
 
                        // page-break-before
18826
 
                        if (isset($dom[$key]['style']['page-break-before'])) {
18827
 
                            if ($dom[$key]['style']['page-break-before'] == 'always') {
18828
 
                                $dom[$key]['attribute']['pagebreak'] = 'true';
18829
 
                            } elseif ($dom[$key]['style']['page-break-before'] == 'left') {
18830
 
                                $dom[$key]['attribute']['pagebreak'] = 'left';
18831
 
                            } elseif ($dom[$key]['style']['page-break-before'] == 'right') {
18832
 
                                $dom[$key]['attribute']['pagebreak'] = 'right';
18833
 
                            }
18834
 
                        }
18835
 
                        // page-break-after
18836
 
                        if (isset($dom[$key]['style']['page-break-after'])) {
18837
 
                            if ($dom[$key]['style']['page-break-after'] == 'always') {
18838
 
                                $dom[$key]['attribute']['pagebreakafter'] = 'true';
18839
 
                            } elseif ($dom[$key]['style']['page-break-after'] == 'left') {
18840
 
                                $dom[$key]['attribute']['pagebreakafter'] = 'left';
18841
 
                            } elseif ($dom[$key]['style']['page-break-after'] == 'right') {
18842
 
                                $dom[$key]['attribute']['pagebreakafter'] = 'right';
18843
 
                            }
18844
 
                        }
18845
 
                    }
18846
 
                    if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
18847
 
                        $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
18848
 
                        if (!empty($borderstyle)) {
18849
 
                            $dom[$key]['border']['LTRB'] = $borderstyle;
18850
 
                        }
18851
 
                    }
18852
 
                    // check for font tag
18853
 
                    if ($dom[$key]['value'] == 'font') {
18854
 
                        // font family
18855
 
                        if (isset($dom[$key]['attribute']['face'])) {
18856
 
                            $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
18857
 
                        }
18858
 
                        // font size
18859
 
                        if (isset($dom[$key]['attribute']['size'])) {
18860
 
                            if ($key > 0) {
18861
 
                                if ($dom[$key]['attribute']['size']{0} == '+') {
18862
 
                                    $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
18863
 
                                } elseif ($dom[$key]['attribute']['size']{0} == '-') {
18864
 
                                    $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
18865
 
                                } else {
18866
 
                                    $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
18867
 
                                }
18868
 
                            } else {
18869
 
                                $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
18870
 
                            }
18871
 
                        }
18872
 
                    }
18873
 
                    // force natural alignment for lists
18874
 
                    if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
18875
 
                        AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
18876
 
                        if ($this->rtl) {
18877
 
                            $dom[$key]['align'] = 'R';
18878
 
                        } else {
18879
 
                            $dom[$key]['align'] = 'L';
18880
 
                        }
18881
 
                    }
18882
 
                    if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
18883
 
                        if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
18884
 
                            $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
18885
 
                        }
18886
 
                    }
18887
 
                    if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
18888
 
                        $dom[$key]['fontstyle'] .= 'B';
18889
 
                    }
18890
 
                    if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
18891
 
                        $dom[$key]['fontstyle'] .= 'I';
18892
 
                    }
18893
 
                    if ($dom[$key]['value'] == 'u') {
18894
 
                        $dom[$key]['fontstyle'] .= 'U';
18895
 
                    }
18896
 
                    if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
18897
 
                        $dom[$key]['fontstyle'] .= 'D';
18898
 
                    }
18899
 
                    if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
18900
 
                        $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
18901
 
                    }
18902
 
                    if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
18903
 
                        $dom[$key]['fontname'] = $this->default_monospaced_font;
18904
 
                    }
18905
 
                    if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
18906
 
                        // headings h1, h2, h3, h4, h5, h6
18907
 
                        if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
18908
 
                            $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
18909
 
                            $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
18910
 
                        }
18911
 
                        if (!isset($dom[$key]['style']['font-weight'])) {
18912
 
                            $dom[$key]['fontstyle'] .= 'B';
18913
 
                        }
18914
 
                    }
18915
 
                    if (($dom[$key]['value'] == 'table')) {
18916
 
                        $dom[$key]['rows'] = 0; // number of rows
18917
 
                        $dom[$key]['trids'] = array(); // IDs of TR elements
18918
 
                        $dom[$key]['thead'] = ''; // table header rows
18919
 
                    }
18920
 
                    if (($dom[$key]['value'] == 'tr')) {
18921
 
                        $dom[$key]['cols'] = 0;
18922
 
                        if ($thead) {
18923
 
                            $dom[$key]['thead'] = true;
18924
 
                            // rows on thead block are printed as a separate table
18925
 
                        } else {
18926
 
                            $dom[$key]['thead'] = false;
18927
 
                            // store the number of rows on table element
18928
 
                            ++$dom[($dom[$key]['parent'])]['rows'];
18929
 
                            // store the TR elements IDs on table element
18930
 
                            array_push($dom[($dom[$key]['parent'])]['trids'], $key);
18931
 
                        }
18932
 
                    }
18933
 
                    if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
18934
 
                        if (isset($dom[$key]['attribute']['colspan'])) {
18935
 
                            $colspan = intval($dom[$key]['attribute']['colspan']);
18936
 
                        } else {
18937
 
                            $colspan = 1;
18938
 
                        }
18939
 
                        $dom[$key]['attribute']['colspan'] = $colspan;
18940
 
                        $dom[($dom[$key]['parent'])]['cols'] += $colspan;
18941
 
                    }
18942
 
                    // text direction
18943
 
                    if (isset($dom[$key]['attribute']['dir'])) {
18944
 
                        $dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
18945
 
                    }
18946
 
                    // set foreground color attribute
18947
 
                    if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) {
18948
 
                        $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
18949
 
                    } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
18950
 
                        $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
18951
 
                    }
18952
 
                    // set background color attribute
18953
 
                    if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) {
18954
 
                        $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
18955
 
                    }
18956
 
                    // set stroke color attribute
18957
 
                    if (isset($dom[$key]['attribute']['strokecolor']) AND (!$this->empty_string($dom[$key]['attribute']['strokecolor']))) {
18958
 
                        $dom[$key]['strokecolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['strokecolor']);
18959
 
                    }
18960
 
                    // check for width attribute
18961
 
                    if (isset($dom[$key]['attribute']['width'])) {
18962
 
                        $dom[$key]['width'] = $dom[$key]['attribute']['width'];
18963
 
                    }
18964
 
                    // check for height attribute
18965
 
                    if (isset($dom[$key]['attribute']['height'])) {
18966
 
                        $dom[$key]['height'] = $dom[$key]['attribute']['height'];
18967
 
                    }
18968
 
                    // check for text alignment
18969
 
                    if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
18970
 
                        $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
18971
 
                    }
18972
 
                    // check for text rendering mode (the following attributes do not exist in HTML)
18973
 
                    if (isset($dom[$key]['attribute']['stroke'])) {
18974
 
                        // font stroke width
18975
 
                        $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
18976
 
                    }
18977
 
                    if (isset($dom[$key]['attribute']['fill'])) {
18978
 
                        // font fill
18979
 
                        if ($dom[$key]['attribute']['fill'] == 'true') {
18980
 
                            $dom[$key]['fill'] = true;
18981
 
                        } else {
18982
 
                            $dom[$key]['fill'] = false;
18983
 
                        }
18984
 
                    }
18985
 
                    if (isset($dom[$key]['attribute']['clip'])) {
18986
 
                        // clipping mode
18987
 
                        if ($dom[$key]['attribute']['clip'] == 'true') {
18988
 
                            $dom[$key]['clip'] = true;
18989
 
                        } else {
18990
 
                            $dom[$key]['clip'] = false;
18991
 
                        }
18992
 
                    }
18993
 
                } // end opening tag
18994
 
            } else {
18995
 
                // text
18996
 
                $dom[$key]['tag'] = false;
18997
 
                $dom[$key]['block'] = false;
18998
 
                $element = str_replace('$nbsp;', $this->unichr(160), $element);
18999
 
                $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
19000
 
                $dom[$key]['parent'] = end($level);
19001
 
                $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
19002
 
            }
19003
 
            ++$elkey;
19004
 
            ++$key;
19005
 
        }
19006
 
        return $dom;
19007
 
    }
19008
 
 
19009
 
    /**
19010
 
     * Returns the string used to find spaces
19011
 
     * @return string
19012
 
     * @protected
19013
 
     * @author Nicola Asuni
19014
 
     * @since 4.8.024 (2010-01-15)
19015
 
     */
19016
 
    protected function getSpaceString() {
19017
 
        $spacestr = chr(32);
19018
 
        if ($this->isUnicodeFont()) {
19019
 
            $spacestr = chr(0).chr(32);
19020
 
        }
19021
 
        return $spacestr;
19022
 
    }
19023
 
 
19024
 
    /**
19025
 
     * Prints a cell (rectangular area) with optional borders, background color and html text string.
19026
 
     * 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 />
19027
 
     * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
19028
 
     * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
19029
 
     * @param $h (float) Cell minimum height. The cell extends automatically if needed.
19030
 
     * @param $x (float) upper-left corner X coordinate
19031
 
     * @param $y (float) upper-left corner Y coordinate
19032
 
     * @param $html (string) html text to print. Default value: empty string.
19033
 
     * @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)))
19034
 
     * @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>
 
157
        // private properties
 
158
 
 
159
        /**
 
160
         * Current TCPDF version.
 
161
         * @private
 
162
         */
 
163
        private $tcpdf_version = '5.9.145';
 
164
 
 
165
        // Protected properties
 
166
 
 
167
        /**
 
168
         * Current page number.
 
169
         * @protected
 
170
         */
 
171
        protected $page;
 
172
 
 
173
        /**
 
174
         * Current object number.
 
175
         * @protected
 
176
         */
 
177
        protected $n;
 
178
 
 
179
        /**
 
180
         * Array of object offsets.
 
181
         * @protected
 
182
         */
 
183
        protected $offsets;
 
184
 
 
185
        /**
 
186
         * Buffer holding in-memory PDF.
 
187
         * @protected
 
188
         */
 
189
        protected $buffer;
 
190
 
 
191
        /**
 
192
         * Array containing pages.
 
193
         * @protected
 
194
         */
 
195
        protected $pages = array();
 
196
 
 
197
        /**
 
198
         * Current document state.
 
199
         * @protected
 
200
         */
 
201
        protected $state;
 
202
 
 
203
        /**
 
204
         * Compression flag.
 
205
         * @protected
 
206
         */
 
207
        protected $compress;
 
208
 
 
209
        /**
 
210
         * Current page orientation (P = Portrait, L = Landscape).
 
211
         * @protected
 
212
         */
 
213
        protected $CurOrientation;
 
214
 
 
215
        /**
 
216
         * Page dimensions.
 
217
         * @protected
 
218
         */
 
219
        protected $pagedim = array();
 
220
 
 
221
        /**
 
222
         * Scale factor (number of points in user unit).
 
223
         * @protected
 
224
         */
 
225
        protected $k;
 
226
 
 
227
        /**
 
228
         * Width of page format in points.
 
229
         * @protected
 
230
         */
 
231
        protected $fwPt;
 
232
 
 
233
        /**
 
234
         * Height of page format in points.
 
235
         * @protected
 
236
         */
 
237
        protected $fhPt;
 
238
 
 
239
        /**
 
240
         * Current width of page in points.
 
241
         * @protected
 
242
         */
 
243
        protected $wPt;
 
244
 
 
245
        /**
 
246
         * Current height of page in points.
 
247
         * @protected
 
248
         */
 
249
        protected $hPt;
 
250
 
 
251
        /**
 
252
         * Current width of page in user unit.
 
253
         * @protected
 
254
         */
 
255
        protected $w;
 
256
 
 
257
        /**
 
258
         * Current height of page in user unit.
 
259
         * @protected
 
260
         */
 
261
        protected $h;
 
262
 
 
263
        /**
 
264
         * Left margin.
 
265
         * @protected
 
266
         */
 
267
        protected $lMargin;
 
268
 
 
269
        /**
 
270
         * Top margin.
 
271
         * @protected
 
272
         */
 
273
        protected $tMargin;
 
274
 
 
275
        /**
 
276
         * Right margin.
 
277
         * @protected
 
278
         */
 
279
        protected $rMargin;
 
280
 
 
281
        /**
 
282
         * Page break margin.
 
283
         * @protected
 
284
         */
 
285
        protected $bMargin;
 
286
 
 
287
        /**
 
288
         * Array of cell internal paddings ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
 
289
         * @since 5.9.000 (2010-10-03)
 
290
         * @protected
 
291
         */
 
292
        protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
 
293
 
 
294
        /**
 
295
         * Array of cell margins ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
 
296
         * @since 5.9.000 (2010-10-04)
 
297
         * @protected
 
298
         */
 
299
        protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
 
300
 
 
301
        /**
 
302
         * Current horizontal position in user unit for cell positioning.
 
303
         * @protected
 
304
         */
 
305
        protected $x;
 
306
 
 
307
        /**
 
308
         * Current vertical position in user unit for cell positioning.
 
309
         * @protected
 
310
         */
 
311
        protected $y;
 
312
 
 
313
        /**
 
314
         * Height of last cell printed.
 
315
         * @protected
 
316
         */
 
317
        protected $lasth;
 
318
 
 
319
        /**
 
320
         * Line width in user unit.
 
321
         * @protected
 
322
         */
 
323
        protected $LineWidth;
 
324
 
 
325
        /**
 
326
         * Array of standard font names.
 
327
         * @protected
 
328
         */
 
329
        protected $CoreFonts;
 
330
 
 
331
        /**
 
332
         * Array of used fonts.
 
333
         * @protected
 
334
         */
 
335
        protected $fonts = array();
 
336
 
 
337
        /**
 
338
         * Array of font files.
 
339
         * @protected
 
340
         */
 
341
        protected $FontFiles = array();
 
342
 
 
343
        /**
 
344
         * Array of encoding differences.
 
345
         * @protected
 
346
         */
 
347
        protected $diffs = array();
 
348
 
 
349
        /**
 
350
         * Array of used images.
 
351
         * @protected
 
352
         */
 
353
        protected $images = array();
 
354
 
 
355
        /**
 
356
         * Array of Annotations in pages.
 
357
         * @protected
 
358
         */
 
359
        protected $PageAnnots = array();
 
360
 
 
361
        /**
 
362
         * Array of internal links.
 
363
         * @protected
 
364
         */
 
365
        protected $links = array();
 
366
 
 
367
        /**
 
368
         * Current font family.
 
369
         * @protected
 
370
         */
 
371
        protected $FontFamily;
 
372
 
 
373
        /**
 
374
         * Current font style.
 
375
         * @protected
 
376
         */
 
377
        protected $FontStyle;
 
378
 
 
379
        /**
 
380
         * Current font ascent (distance between font top and baseline).
 
381
         * @protected
 
382
         * @since 2.8.000 (2007-03-29)
 
383
         */
 
384
        protected $FontAscent;
 
385
 
 
386
        /**
 
387
         * Current font descent (distance between font bottom and baseline).
 
388
         * @protected
 
389
         * @since 2.8.000 (2007-03-29)
 
390
         */
 
391
        protected $FontDescent;
 
392
 
 
393
        /**
 
394
         * Underlining flag.
 
395
         * @protected
 
396
         */
 
397
        protected $underline;
 
398
 
 
399
        /**
 
400
         * Overlining flag.
 
401
         * @protected
 
402
         */
 
403
        protected $overline;
 
404
 
 
405
        /**
 
406
         * Current font info.
 
407
         * @protected
 
408
         */
 
409
        protected $CurrentFont;
 
410
 
 
411
        /**
 
412
         * Current font size in points.
 
413
         * @protected
 
414
         */
 
415
        protected $FontSizePt;
 
416
 
 
417
        /**
 
418
         * Current font size in user unit.
 
419
         * @protected
 
420
         */
 
421
        protected $FontSize;
 
422
 
 
423
        /**
 
424
         * Commands for drawing color.
 
425
         * @protected
 
426
         */
 
427
        protected $DrawColor;
 
428
 
 
429
        /**
 
430
         * Commands for filling color.
 
431
         * @protected
 
432
         */
 
433
        protected $FillColor;
 
434
 
 
435
        /**
 
436
         * Commands for text color.
 
437
         * @protected
 
438
         */
 
439
        protected $TextColor;
 
440
 
 
441
        /**
 
442
         * Indicates whether fill and text colors are different.
 
443
         * @protected
 
444
         */
 
445
        protected $ColorFlag;
 
446
 
 
447
        /**
 
448
         * Automatic page breaking.
 
449
         * @protected
 
450
         */
 
451
        protected $AutoPageBreak;
 
452
 
 
453
        /**
 
454
         * Threshold used to trigger page breaks.
 
455
         * @protected
 
456
         */
 
457
        protected $PageBreakTrigger;
 
458
 
 
459
        /**
 
460
         * Flag set when processing page header.
 
461
         * @protected
 
462
         */
 
463
        protected $InHeader = false;
 
464
 
 
465
        /**
 
466
         * Flag set when processing page footer.
 
467
         * @protected
 
468
         */
 
469
        protected $InFooter = false;
 
470
 
 
471
        /**
 
472
         * Zoom display mode.
 
473
         * @protected
 
474
         */
 
475
        protected $ZoomMode;
 
476
 
 
477
        /**
 
478
         * Layout display mode.
 
479
         * @protected
 
480
         */
 
481
        protected $LayoutMode;
 
482
 
 
483
        /**
 
484
         * If true set the document information dictionary in Unicode.
 
485
         * @protected
 
486
         */
 
487
        protected $docinfounicode = true;
 
488
 
 
489
        /**
 
490
         * Document title.
 
491
         * @protected
 
492
         */
 
493
        protected $title = '';
 
494
 
 
495
        /**
 
496
         * Document subject.
 
497
         * @protected
 
498
         */
 
499
        protected $subject = '';
 
500
 
 
501
        /**
 
502
         * Document author.
 
503
         * @protected
 
504
         */
 
505
        protected $author = '';
 
506
 
 
507
        /**
 
508
         * Document keywords.
 
509
         * @protected
 
510
         */
 
511
        protected $keywords = '';
 
512
 
 
513
        /**
 
514
         * Document creator.
 
515
         * @protected
 
516
         */
 
517
        protected $creator = '';
 
518
 
 
519
        /**
 
520
         * Starting page number.
 
521
         * @protected
 
522
         */
 
523
        protected $starting_page_number = 1;
 
524
 
 
525
        /**
 
526
         * String alias for total number of pages.
 
527
         * @protected
 
528
         */
 
529
        protected $alias_tot_pages = '{:ptp:}';
 
530
 
 
531
        /**
 
532
         * String alias for page number.
 
533
         * @protected
 
534
         */
 
535
        protected $alias_num_page = '{:pnp:}';
 
536
 
 
537
        /**
 
538
         * String alias for total number of pages in a single group.
 
539
         * @protected
 
540
         */
 
541
        protected $alias_group_tot_pages = '{:ptg:}';
 
542
 
 
543
        /**
 
544
         * String alias for group page number.
 
545
         * @protected
 
546
         */
 
547
        protected $alias_group_num_page = '{:png:}';
 
548
 
 
549
        /**
 
550
         * String alias for right shift compensation used to correctly align page numbers on the right.
 
551
         * @protected
 
552
         */
 
553
        protected $alias_right_shift = '{rsc:';
 
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
         * Object containing unicode data.
 
589
         * @since 5.9.004 (2010-10-18)
 
590
         * @author Nicola Asuni
 
591
         * @protected
 
592
         */
 
593
        protected $unicode;
 
594
 
 
595
        /**
 
596
         * Object containing font encoding maps.
 
597
         * @since 5.9.123 (2011-10-01)
 
598
         * @author Nicola Asuni
 
599
         * @protected
 
600
         */
 
601
        protected $encmaps;
 
602
 
 
603
        /**
 
604
         * PDF version.
 
605
         * @since 1.5.3
 
606
         * @protected
 
607
         */
 
608
        protected $PDFVersion = '1.7';
 
609
 
 
610
        /**
 
611
         * ID of the stored default header template (-1 = not set).
 
612
         * @protected
 
613
         */
 
614
        protected $header_xobjid = -1;
 
615
 
 
616
        /**
 
617
         * If true reset the Header Xobject template at each page
 
618
         * @protected
 
619
         */
 
620
        protected $header_xobj_autoreset = false;
 
621
 
 
622
        /**
 
623
         * Minimum distance between header and top page margin.
 
624
         * @protected
 
625
         */
 
626
        protected $header_margin;
 
627
 
 
628
        /**
 
629
         * Minimum distance between footer and bottom page margin.
 
630
         * @protected
 
631
         */
 
632
        protected $footer_margin;
 
633
 
 
634
        /**
 
635
         * Original left margin value.
 
636
         * @protected
 
637
         * @since 1.53.0.TC013
 
638
         */
 
639
        protected $original_lMargin;
 
640
 
 
641
        /**
 
642
         * Original right margin value.
 
643
         * @protected
 
644
         * @since 1.53.0.TC013
 
645
         */
 
646
        protected $original_rMargin;
 
647
 
 
648
        /**
 
649
         * Default font used on page header.
 
650
         * @protected
 
651
         */
 
652
        protected $header_font;
 
653
 
 
654
        /**
 
655
         * Default font used on page footer.
 
656
         * @protected
 
657
         */
 
658
        protected $footer_font;
 
659
 
 
660
        /**
 
661
         * Language templates.
 
662
         * @protected
 
663
         */
 
664
        protected $l;
 
665
 
 
666
        /**
 
667
         * Barcode to print on page footer (only if set).
 
668
         * @protected
 
669
         */
 
670
        protected $barcode = false;
 
671
 
 
672
        /**
 
673
         * Boolean flag to print/hide page header.
 
674
         * @protected
 
675
         */
 
676
        protected $print_header = true;
 
677
 
 
678
        /**
 
679
         * Boolean flag to print/hide page footer.
 
680
         * @protected
 
681
         */
 
682
        protected $print_footer = true;
 
683
 
 
684
        /**
 
685
         * Header image logo.
 
686
         * @protected
 
687
         */
 
688
        protected $header_logo = '';
 
689
 
 
690
        /**
 
691
         * Width of header image logo in user units.
 
692
         * @protected
 
693
         */
 
694
        protected $header_logo_width = 30;
 
695
 
 
696
        /**
 
697
         * Title to be printed on default page header.
 
698
         * @protected
 
699
         */
 
700
        protected $header_title = '';
 
701
 
 
702
        /**
 
703
         * String to pring on page header after title.
 
704
         * @protected
 
705
         */
 
706
        protected $header_string = '';
 
707
 
 
708
        /**
 
709
         * Default number of columns for html table.
 
710
         * @protected
 
711
         */
 
712
        protected $default_table_columns = 4;
 
713
 
 
714
        // variables for html parser
 
715
 
 
716
        /**
 
717
         * HTML PARSER: array to store current link and rendering styles.
 
718
         * @protected
 
719
         */
 
720
        protected $HREF = array();
 
721
 
 
722
        /**
 
723
         * List of available fonts on filesystem.
 
724
         * @protected
 
725
         */
 
726
        protected $fontlist = array();
 
727
 
 
728
        /**
 
729
         * Current foreground color.
 
730
         * @protected
 
731
         */
 
732
        protected $fgcolor;
 
733
 
 
734
        /**
 
735
         * HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
 
736
         * @protected
 
737
         */
 
738
        protected $listordered = array();
 
739
 
 
740
        /**
 
741
         * HTML PARSER: array count list items on nested lists.
 
742
         * @protected
 
743
         */
 
744
        protected $listcount = array();
 
745
 
 
746
        /**
 
747
         * HTML PARSER: current list nesting level.
 
748
         * @protected
 
749
         */
 
750
        protected $listnum = 0;
 
751
 
 
752
        /**
 
753
         * HTML PARSER: indent amount for lists.
 
754
         * @protected
 
755
         */
 
756
        protected $listindent = 0;
 
757
 
 
758
        /**
 
759
         * HTML PARSER: current list indententation level.
 
760
         * @protected
 
761
         */
 
762
        protected $listindentlevel = 0;
 
763
 
 
764
        /**
 
765
         * Current background color.
 
766
         * @protected
 
767
         */
 
768
        protected $bgcolor;
 
769
 
 
770
        /**
 
771
         * Temporary font size in points.
 
772
         * @protected
 
773
         */
 
774
        protected $tempfontsize = 10;
 
775
 
 
776
        /**
 
777
         * Spacer string for LI tags.
 
778
         * @protected
 
779
         */
 
780
        protected $lispacer = '';
 
781
 
 
782
        /**
 
783
         * Default encoding.
 
784
         * @protected
 
785
         * @since 1.53.0.TC010
 
786
         */
 
787
        protected $encoding = 'UTF-8';
 
788
 
 
789
        /**
 
790
         * PHP internal encoding.
 
791
         * @protected
 
792
         * @since 1.53.0.TC016
 
793
         */
 
794
        protected $internal_encoding;
 
795
 
 
796
        /**
 
797
         * Boolean flag to indicate if the document language is Right-To-Left.
 
798
         * @protected
 
799
         * @since 2.0.000
 
800
         */
 
801
        protected $rtl = false;
 
802
 
 
803
        /**
 
804
         * Boolean flag used to force RTL or LTR string direction.
 
805
         * @protected
 
806
         * @since 2.0.000
 
807
         */
 
808
        protected $tmprtl = false;
 
809
 
 
810
        // --- Variables used for document encryption:
 
811
 
 
812
        /**
 
813
         * IBoolean flag indicating whether document is protected.
 
814
         * @protected
 
815
         * @since 2.0.000 (2008-01-02)
 
816
         */
 
817
        protected $encrypted;
 
818
 
 
819
        /**
 
820
         * Array containing encryption settings.
 
821
         * @protected
 
822
         * @since 5.0.005 (2010-05-11)
 
823
         */
 
824
        protected $encryptdata = array();
 
825
 
 
826
        /**
 
827
         * Last RC4 key encrypted (cached for optimisation).
 
828
         * @protected
 
829
         * @since 2.0.000 (2008-01-02)
 
830
         */
 
831
        protected $last_enc_key;
 
832
 
 
833
        /**
 
834
         * Last RC4 computed key.
 
835
         * @protected
 
836
         * @since 2.0.000 (2008-01-02)
 
837
         */
 
838
        protected $last_enc_key_c;
 
839
 
 
840
        /**
 
841
         * Encryption padding string.
 
842
         * @protected
 
843
         */
 
844
        protected $enc_padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
 
845
 
 
846
        /**
 
847
         * File ID (used on document trailer).
 
848
         * @protected
 
849
         * @since 5.0.005 (2010-05-12)
 
850
         */
 
851
        protected $file_id;
 
852
 
 
853
        // --- bookmark ---
 
854
 
 
855
        /**
 
856
         * Outlines for bookmark.
 
857
         * @protected
 
858
         * @since 2.1.002 (2008-02-12)
 
859
         */
 
860
        protected $outlines = array();
 
861
 
 
862
        /**
 
863
         * Outline root for bookmark.
 
864
         * @protected
 
865
         * @since 2.1.002 (2008-02-12)
 
866
         */
 
867
        protected $OutlineRoot;
 
868
 
 
869
        // --- javascript and form ---
 
870
 
 
871
        /**
 
872
         * Javascript code.
 
873
         * @protected
 
874
         * @since 2.1.002 (2008-02-12)
 
875
         */
 
876
        protected $javascript = '';
 
877
 
 
878
        /**
 
879
         * Javascript counter.
 
880
         * @protected
 
881
         * @since 2.1.002 (2008-02-12)
 
882
         */
 
883
        protected $n_js;
 
884
 
 
885
        /**
 
886
         * line trough state
 
887
         * @protected
 
888
         * @since 2.8.000 (2008-03-19)
 
889
         */
 
890
        protected $linethrough;
 
891
 
 
892
        /**
 
893
         * Array with additional document-wide usage rights for the document.
 
894
         * @protected
 
895
         * @since 5.8.014 (2010-08-23)
 
896
         */
 
897
        protected $ur = array();
 
898
 
 
899
        /**
 
900
         * DPI (Dot Per Inch) Document Resolution (do not change).
 
901
         * @protected
 
902
         * @since 3.0.000 (2008-03-27)
 
903
         */
 
904
        protected $dpi = 72;
 
905
 
 
906
        /**
 
907
         * Array of page numbers were a new page group was started (the page numbers are the keys of the array).
 
908
         * @protected
 
909
         * @since 3.0.000 (2008-03-27)
 
910
         */
 
911
        protected $newpagegroup = array();
 
912
 
 
913
        /**
 
914
         * Array that contains the number of pages in each page group.
 
915
         * @protected
 
916
         * @since 3.0.000 (2008-03-27)
 
917
         */
 
918
        protected $pagegroups = array();
 
919
 
 
920
        /**
 
921
         * Current page group number.
 
922
         * @protected
 
923
         * @since 3.0.000 (2008-03-27)
 
924
         */
 
925
        protected $currpagegroup = 0;
 
926
 
 
927
        /**
 
928
         * Array of transparency objects and parameters.
 
929
         * @protected
 
930
         * @since 3.0.000 (2008-03-27)
 
931
         */
 
932
        protected $extgstates;
 
933
 
 
934
        /**
 
935
         * Set the default JPEG compression quality (1-100).
 
936
         * @protected
 
937
         * @since 3.0.000 (2008-03-27)
 
938
         */
 
939
        protected $jpeg_quality;
 
940
 
 
941
        /**
 
942
         * Default cell height ratio.
 
943
         * @protected
 
944
         * @since 3.0.014 (2008-05-23)
 
945
         */
 
946
        protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
 
947
 
 
948
        /**
 
949
         * PDF viewer preferences.
 
950
         * @protected
 
951
         * @since 3.1.000 (2008-06-09)
 
952
         */
 
953
        protected $viewer_preferences;
 
954
 
 
955
        /**
 
956
         * A name object specifying how the document should be displayed when opened.
 
957
         * @protected
 
958
         * @since 3.1.000 (2008-06-09)
 
959
         */
 
960
        protected $PageMode;
 
961
 
 
962
        /**
 
963
         * Array for storing gradient information.
 
964
         * @protected
 
965
         * @since 3.1.000 (2008-06-09)
 
966
         */
 
967
        protected $gradients = array();
 
968
 
 
969
        /**
 
970
         * Array used to store positions inside the pages buffer (keys are the page numbers).
 
971
         * @protected
 
972
         * @since 3.2.000 (2008-06-26)
 
973
         */
 
974
        protected $intmrk = array();
 
975
 
 
976
        /**
 
977
         * Array used to store positions inside the pages buffer (keys are the page numbers).
 
978
         * @protected
 
979
         * @since 5.7.000 (2010-08-03)
 
980
         */
 
981
        protected $bordermrk = array();
 
982
 
 
983
        /**
 
984
         * Array used to store page positions to track empty pages (keys are the page numbers).
 
985
         * @protected
 
986
         * @since 5.8.007 (2010-08-18)
 
987
         */
 
988
        protected $emptypagemrk = array();
 
989
 
 
990
        /**
 
991
         * Array used to store content positions inside the pages buffer (keys are the page numbers).
 
992
         * @protected
 
993
         * @since 4.6.021 (2009-07-20)
 
994
         */
 
995
        protected $cntmrk = array();
 
996
 
 
997
        /**
 
998
         * Array used to store footer positions of each page.
 
999
         * @protected
 
1000
         * @since 3.2.000 (2008-07-01)
 
1001
         */
 
1002
        protected $footerpos = array();
 
1003
 
 
1004
        /**
 
1005
         * Array used to store footer length of each page.
 
1006
         * @protected
 
1007
         * @since 4.0.014 (2008-07-29)
 
1008
         */
 
1009
        protected $footerlen = array();
 
1010
 
 
1011
        /**
 
1012
         * Boolean flag to indicate if a new line is created.
 
1013
         * @protected
 
1014
         * @since 3.2.000 (2008-07-01)
 
1015
         */
 
1016
        protected $newline = true;
 
1017
 
 
1018
        /**
 
1019
         * End position of the latest inserted line.
 
1020
         * @protected
 
1021
         * @since 3.2.000 (2008-07-01)
 
1022
         */
 
1023
        protected $endlinex = 0;
 
1024
 
 
1025
        /**
 
1026
         * PDF string for width value of the last line.
 
1027
         * @protected
 
1028
         * @since 4.0.006 (2008-07-16)
 
1029
         */
 
1030
        protected $linestyleWidth = '';
 
1031
 
 
1032
        /**
 
1033
         * PDF string for CAP value of the last line.
 
1034
         * @protected
 
1035
         * @since 4.0.006 (2008-07-16)
 
1036
         */
 
1037
        protected $linestyleCap = '0 J';
 
1038
 
 
1039
        /**
 
1040
         * PDF string for join value of the last line.
 
1041
         * @protected
 
1042
         * @since 4.0.006 (2008-07-16)
 
1043
         */
 
1044
        protected $linestyleJoin = '0 j';
 
1045
 
 
1046
        /**
 
1047
         * PDF string for dash value of the last line.
 
1048
         * @protected
 
1049
         * @since 4.0.006 (2008-07-16)
 
1050
         */
 
1051
        protected $linestyleDash = '[] 0 d';
 
1052
 
 
1053
        /**
 
1054
         * Boolean flag to indicate if marked-content sequence is open.
 
1055
         * @protected
 
1056
         * @since 4.0.013 (2008-07-28)
 
1057
         */
 
1058
        protected $openMarkedContent = false;
 
1059
 
 
1060
        /**
 
1061
         * Count the latest inserted vertical spaces on HTML.
 
1062
         * @protected
 
1063
         * @since 4.0.021 (2008-08-24)
 
1064
         */
 
1065
        protected $htmlvspace = 0;
 
1066
 
 
1067
        /**
 
1068
         * Array of Spot colors.
 
1069
         * @protected
 
1070
         * @since 4.0.024 (2008-09-12)
 
1071
         */
 
1072
        protected $spot_colors = array();
 
1073
 
 
1074
        /**
 
1075
         * Symbol used for HTML unordered list items.
 
1076
         * @protected
 
1077
         * @since 4.0.028 (2008-09-26)
 
1078
         */
 
1079
        protected $lisymbol = '';
 
1080
 
 
1081
        /**
 
1082
         * String used to mark the beginning and end of EPS image blocks.
 
1083
         * @protected
 
1084
         * @since 4.1.000 (2008-10-18)
 
1085
         */
 
1086
        protected $epsmarker = 'x#!#EPS#!#x';
 
1087
 
 
1088
        /**
 
1089
         * Array of transformation matrix.
 
1090
         * @protected
 
1091
         * @since 4.2.000 (2008-10-29)
 
1092
         */
 
1093
        protected $transfmatrix = array();
 
1094
 
 
1095
        /**
 
1096
         * Current key for transformation matrix.
 
1097
         * @protected
 
1098
         * @since 4.8.005 (2009-09-17)
 
1099
         */
 
1100
        protected $transfmatrix_key = 0;
 
1101
 
 
1102
        /**
 
1103
         * Booklet mode for double-sided pages.
 
1104
         * @protected
 
1105
         * @since 4.2.000 (2008-10-29)
 
1106
         */
 
1107
        protected $booklet = false;
 
1108
 
 
1109
        /**
 
1110
         * Epsilon value used for float calculations.
 
1111
         * @protected
 
1112
         * @since 4.2.000 (2008-10-29)
 
1113
         */
 
1114
        protected $feps = 0.005;
 
1115
 
 
1116
        /**
 
1117
         * Array used for custom vertical spaces for HTML tags.
 
1118
         * @protected
 
1119
         * @since 4.2.001 (2008-10-30)
 
1120
         */
 
1121
        protected $tagvspaces = array();
 
1122
 
 
1123
        /**
 
1124
         * HTML PARSER: custom indent amount for lists. Negative value means disabled.
 
1125
         * @protected
 
1126
         * @since 4.2.007 (2008-11-12)
 
1127
         */
 
1128
        protected $customlistindent = -1;
 
1129
 
 
1130
        /**
 
1131
         * Boolean flag to indicate if the border of the cell sides that cross the page should be removed.
 
1132
         * @protected
 
1133
         * @since 4.2.010 (2008-11-14)
 
1134
         */
 
1135
        protected $opencell = true;
 
1136
 
 
1137
        /**
 
1138
         * Array of files to embedd.
 
1139
         * @protected
 
1140
         * @since 4.4.000 (2008-12-07)
 
1141
         */
 
1142
        protected $embeddedfiles = array();
 
1143
 
 
1144
        /**
 
1145
         * Boolean flag to indicate if we are inside a PRE tag.
 
1146
         * @protected
 
1147
         * @since 4.4.001 (2008-12-08)
 
1148
         */
 
1149
        protected $premode = false;
 
1150
 
 
1151
        /**
 
1152
         * Array used to store positions of graphics transformation blocks inside the page buffer.
 
1153
         * keys are the page numbers
 
1154
         * @protected
 
1155
         * @since 4.4.002 (2008-12-09)
 
1156
         */
 
1157
        protected $transfmrk = array();
 
1158
 
 
1159
        /**
 
1160
         * Default color for html links.
 
1161
         * @protected
 
1162
         * @since 4.4.003 (2008-12-09)
 
1163
         */
 
1164
        protected $htmlLinkColorArray = array(0, 0, 255);
 
1165
 
 
1166
        /**
 
1167
         * Default font style to add to html links.
 
1168
         * @protected
 
1169
         * @since 4.4.003 (2008-12-09)
 
1170
         */
 
1171
        protected $htmlLinkFontStyle = 'U';
 
1172
 
 
1173
        /**
 
1174
         * Counts the number of pages.
 
1175
         * @protected
 
1176
         * @since 4.5.000 (2008-12-31)
 
1177
         */
 
1178
        protected $numpages = 0;
 
1179
 
 
1180
        /**
 
1181
         * Array containing page lengths in bytes.
 
1182
         * @protected
 
1183
         * @since 4.5.000 (2008-12-31)
 
1184
         */
 
1185
        protected $pagelen = array();
 
1186
 
 
1187
        /**
 
1188
         * Counts the number of pages.
 
1189
         * @protected
 
1190
         * @since 4.5.000 (2008-12-31)
 
1191
         */
 
1192
        protected $numimages = 0;
 
1193
 
 
1194
        /**
 
1195
         * Store the image keys.
 
1196
         * @protected
 
1197
         * @since 4.5.000 (2008-12-31)
 
1198
         */
 
1199
        protected $imagekeys = array();
 
1200
 
 
1201
        /**
 
1202
         * Length of the buffer in bytes.
 
1203
         * @protected
 
1204
         * @since 4.5.000 (2008-12-31)
 
1205
         */
 
1206
        protected $bufferlen = 0;
 
1207
 
 
1208
        /**
 
1209
         * If true enables disk caching.
 
1210
         * @protected
 
1211
         * @since 4.5.000 (2008-12-31)
 
1212
         */
 
1213
        protected $diskcache = false;
 
1214
 
 
1215
        /**
 
1216
         * Counts the number of fonts.
 
1217
         * @protected
 
1218
         * @since 4.5.000 (2009-01-02)
 
1219
         */
 
1220
        protected $numfonts = 0;
 
1221
 
 
1222
        /**
 
1223
         * Store the font keys.
 
1224
         * @protected
 
1225
         * @since 4.5.000 (2009-01-02)
 
1226
         */
 
1227
        protected $fontkeys = array();
 
1228
 
 
1229
        /**
 
1230
         * Store the font object IDs.
 
1231
         * @protected
 
1232
         * @since 4.8.001 (2009-09-09)
 
1233
         */
 
1234
        protected $font_obj_ids = array();
 
1235
 
 
1236
        /**
 
1237
         * Store the fage status (true when opened, false when closed).
 
1238
         * @protected
 
1239
         * @since 4.5.000 (2009-01-02)
 
1240
         */
 
1241
        protected $pageopen = array();
 
1242
 
 
1243
        /**
 
1244
         * Default monospace font.
 
1245
         * @protected
 
1246
         * @since 4.5.025 (2009-03-10)
 
1247
         */
 
1248
        protected $default_monospaced_font = 'courier';
 
1249
 
 
1250
        /**
 
1251
         * Cloned copy of the current class object.
 
1252
         * @protected
 
1253
         * @since 4.5.029 (2009-03-19)
 
1254
         */
 
1255
        protected $objcopy;
 
1256
 
 
1257
        /**
 
1258
         * Array used to store the lengths of cache files.
 
1259
         * @protected
 
1260
         * @since 4.5.029 (2009-03-19)
 
1261
         */
 
1262
        protected $cache_file_length = array();
 
1263
 
 
1264
        /**
 
1265
         * Table header content to be repeated on each new page.
 
1266
         * @protected
 
1267
         * @since 4.5.030 (2009-03-20)
 
1268
         */
 
1269
        protected $thead = '';
 
1270
 
 
1271
        /**
 
1272
         * Margins used for table header.
 
1273
         * @protected
 
1274
         * @since 4.5.030 (2009-03-20)
 
1275
         */
 
1276
        protected $theadMargins = array();
 
1277
 
 
1278
        /**
 
1279
         * Cache array for UTF8StringToArray() method.
 
1280
         * @protected
 
1281
         * @since 4.5.037 (2009-04-07)
 
1282
         */
 
1283
        protected $cache_UTF8StringToArray = array();
 
1284
 
 
1285
        /**
 
1286
         * Maximum size of cache array used for UTF8StringToArray() method.
 
1287
         * @protected
 
1288
         * @since 4.5.037 (2009-04-07)
 
1289
         */
 
1290
        protected $cache_maxsize_UTF8StringToArray = 8;
 
1291
 
 
1292
        /**
 
1293
         * Current size of cache array used for UTF8StringToArray() method.
 
1294
         * @protected
 
1295
         * @since 4.5.037 (2009-04-07)
 
1296
         */
 
1297
        protected $cache_size_UTF8StringToArray = 0;
 
1298
 
 
1299
        /**
 
1300
         * Boolean flag to enable document digital signature.
 
1301
         * @protected
 
1302
         * @since 4.6.005 (2009-04-24)
 
1303
         */
 
1304
        protected $sign = false;
 
1305
 
 
1306
        /**
 
1307
         * Digital signature data.
 
1308
         * @protected
 
1309
         * @since 4.6.005 (2009-04-24)
 
1310
         */
 
1311
        protected $signature_data = array();
 
1312
 
 
1313
        /**
 
1314
         * Digital signature max length.
 
1315
         * @protected
 
1316
         * @since 4.6.005 (2009-04-24)
 
1317
         */
 
1318
        protected $signature_max_length = 11742;
 
1319
 
 
1320
        /**
 
1321
         * Data for digital signature appearance.
 
1322
         * @protected
 
1323
         * @since 5.3.011 (2010-06-16)
 
1324
         */
 
1325
        protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
 
1326
 
 
1327
        /**
 
1328
         * Array of empty digital signature appearances.
 
1329
         * @protected
 
1330
         * @since 5.9.101 (2011-07-06)
 
1331
         */
 
1332
        protected $empty_signature_appearance = array();
 
1333
 
 
1334
        /**
 
1335
         * Regular expression used to find blank characters (required for word-wrapping).
 
1336
         * @protected
 
1337
         * @since 4.6.006 (2009-04-28)
 
1338
         */
 
1339
        protected $re_spaces = '/[^\S\xa0]/';
 
1340
 
 
1341
        /**
 
1342
         * Array of $re_spaces parts.
 
1343
         * @protected
 
1344
         * @since 5.5.011 (2010-07-09)
 
1345
         */
 
1346
        protected $re_space = array('p' => '[^\S\xa0]', 'm' => '');
 
1347
 
 
1348
        /**
 
1349
         * Digital signature object ID.
 
1350
         * @protected
 
1351
         * @since 4.6.022 (2009-06-23)
 
1352
         */
 
1353
        protected $sig_obj_id = 0;
 
1354
 
 
1355
        /**
 
1356
         * ByteRange placemark used during digital signature process.
 
1357
         * @protected
 
1358
         * @since 4.6.028 (2009-08-25)
 
1359
         */
 
1360
        protected $byterange_string = '/ByteRange[0 ********** ********** **********]';
 
1361
 
 
1362
        /**
 
1363
         * Placemark used during digital signature process.
 
1364
         * @protected
 
1365
         * @since 4.6.028 (2009-08-25)
 
1366
         */
 
1367
        protected $sig_annot_ref = '***SIGANNREF*** 0 R';
 
1368
 
 
1369
        /**
 
1370
         * ID of page objects.
 
1371
         * @protected
 
1372
         * @since 4.7.000 (2009-08-29)
 
1373
         */
 
1374
        protected $page_obj_id = array();
 
1375
 
 
1376
        /**
 
1377
         * List of form annotations IDs.
 
1378
         * @protected
 
1379
         * @since 4.8.000 (2009-09-07)
 
1380
         */
 
1381
        protected $form_obj_id = array();
 
1382
 
 
1383
        /**
 
1384
         * 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.
 
1385
         * @protected
 
1386
         * @since 4.8.000 (2009-09-07)
 
1387
         */
 
1388
        protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
 
1389
 
 
1390
        /**
 
1391
         * Javascript objects array.
 
1392
         * @protected
 
1393
         * @since 4.8.000 (2009-09-07)
 
1394
         */
 
1395
        protected $js_objects = array();
 
1396
 
 
1397
        /**
 
1398
         * Current form action (used during XHTML rendering).
 
1399
         * @protected
 
1400
         * @since 4.8.000 (2009-09-07)
 
1401
         */
 
1402
        protected $form_action = '';
 
1403
 
 
1404
        /**
 
1405
         * Current form encryption type (used during XHTML rendering).
 
1406
         * @protected
 
1407
         * @since 4.8.000 (2009-09-07)
 
1408
         */
 
1409
        protected $form_enctype = 'application/x-www-form-urlencoded';
 
1410
 
 
1411
        /**
 
1412
         * Current method to submit forms.
 
1413
         * @protected
 
1414
         * @since 4.8.000 (2009-09-07)
 
1415
         */
 
1416
        protected $form_mode = 'post';
 
1417
 
 
1418
        /**
 
1419
         * List of fonts used on form fields (fontname => fontkey).
 
1420
         * @protected
 
1421
         * @since 4.8.001 (2009-09-09)
 
1422
         */
 
1423
        protected $annotation_fonts = array();
 
1424
 
 
1425
        /**
 
1426
         * List of radio buttons parent objects.
 
1427
         * @protected
 
1428
         * @since 4.8.001 (2009-09-09)
 
1429
         */
 
1430
        protected $radiobutton_groups = array();
 
1431
 
 
1432
        /**
 
1433
         * List of radio group objects IDs.
 
1434
         * @protected
 
1435
         * @since 4.8.001 (2009-09-09)
 
1436
         */
 
1437
        protected $radio_groups = array();
 
1438
 
 
1439
        /**
 
1440
         * Text indentation value (used for text-indent CSS attribute).
 
1441
         * @protected
 
1442
         * @since 4.8.006 (2009-09-23)
 
1443
         */
 
1444
        protected $textindent = 0;
 
1445
 
 
1446
        /**
 
1447
         * Store page number when startTransaction() is called.
 
1448
         * @protected
 
1449
         * @since 4.8.006 (2009-09-23)
 
1450
         */
 
1451
        protected $start_transaction_page = 0;
 
1452
 
 
1453
        /**
 
1454
         * Store Y position when startTransaction() is called.
 
1455
         * @protected
 
1456
         * @since 4.9.001 (2010-03-28)
 
1457
         */
 
1458
        protected $start_transaction_y = 0;
 
1459
 
 
1460
        /**
 
1461
         * True when we are printing the thead section on a new page.
 
1462
         * @protected
 
1463
         * @since 4.8.027 (2010-01-25)
 
1464
         */
 
1465
        protected $inthead = false;
 
1466
 
 
1467
        /**
 
1468
         * Array of column measures (width, space, starting Y position).
 
1469
         * @protected
 
1470
         * @since 4.9.001 (2010-03-28)
 
1471
         */
 
1472
        protected $columns = array();
 
1473
 
 
1474
        /**
 
1475
         * Number of colums.
 
1476
         * @protected
 
1477
         * @since 4.9.001 (2010-03-28)
 
1478
         */
 
1479
        protected $num_columns = 1;
 
1480
 
 
1481
        /**
 
1482
         * Current column number.
 
1483
         * @protected
 
1484
         * @since 4.9.001 (2010-03-28)
 
1485
         */
 
1486
        protected $current_column = 0;
 
1487
 
 
1488
        /**
 
1489
         * Starting page for columns.
 
1490
         * @protected
 
1491
         * @since 4.9.001 (2010-03-28)
 
1492
         */
 
1493
        protected $column_start_page = 0;
 
1494
 
 
1495
        /**
 
1496
         * Maximum page and column selected.
 
1497
         * @protected
 
1498
         * @since 5.8.000 (2010-08-11)
 
1499
         */
 
1500
        protected $maxselcol = array('page' => 0, 'column' => 0);
 
1501
 
 
1502
        /**
 
1503
         * Array of: X difference between table cell x start and starting page margin, cellspacing, cellpadding.
 
1504
         * @protected
 
1505
         * @since 5.8.000 (2010-08-11)
 
1506
         */
 
1507
        protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
 
1508
 
 
1509
        /**
 
1510
         * 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.
 
1511
         * @protected
 
1512
         * @since 4.9.008 (2010-04-03)
 
1513
         */
 
1514
        protected $textrendermode = 0;
 
1515
 
 
1516
        /**
 
1517
         * Text stroke width in doc units.
 
1518
         * @protected
 
1519
         * @since 4.9.008 (2010-04-03)
 
1520
         */
 
1521
        protected $textstrokewidth = 0;
 
1522
 
 
1523
        /**
 
1524
         * Current stroke color.
 
1525
         * @protected
 
1526
         * @since 4.9.008 (2010-04-03)
 
1527
         */
 
1528
        protected $strokecolor;
 
1529
 
 
1530
        /**
 
1531
         * Default unit of measure for document.
 
1532
         * @protected
 
1533
         * @since 5.0.000 (2010-04-22)
 
1534
         */
 
1535
        protected $pdfunit = 'mm';
 
1536
 
 
1537
        /**
 
1538
         * Boolean flag true when we are on TOC (Table Of Content) page.
 
1539
         * @protected
 
1540
         */
 
1541
        protected $tocpage = false;
 
1542
 
 
1543
        /**
 
1544
         * Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick library.
 
1545
         * @protected
 
1546
         * @since 5.0.000 (2010-04-26)
 
1547
         */
 
1548
        protected $rasterize_vector_images = false;
 
1549
 
 
1550
        /**
 
1551
         * Boolean flag: if true enables font subsetting by default.
 
1552
         * @protected
 
1553
         * @since 5.3.002 (2010-06-07)
 
1554
         */
 
1555
        protected $font_subsetting = true;
 
1556
 
 
1557
        /**
 
1558
         * Array of default graphic settings.
 
1559
         * @protected
 
1560
         * @since 5.5.008 (2010-07-02)
 
1561
         */
 
1562
        protected $default_graphic_vars = array();
 
1563
 
 
1564
        /**
 
1565
         * Array of XObjects.
 
1566
         * @protected
 
1567
         * @since 5.8.014 (2010-08-23)
 
1568
         */
 
1569
        protected $xobjects = array();
 
1570
 
 
1571
        /**
 
1572
         * Boolean value true when we are inside an XObject.
 
1573
         * @protected
 
1574
         * @since 5.8.017 (2010-08-24)
 
1575
         */
 
1576
        protected $inxobj = false;
 
1577
 
 
1578
        /**
 
1579
         * Current XObject ID.
 
1580
         * @protected
 
1581
         * @since 5.8.017 (2010-08-24)
 
1582
         */
 
1583
        protected $xobjid = '';
 
1584
 
 
1585
        /**
 
1586
         * Percentage of character stretching.
 
1587
         * @protected
 
1588
         * @since 5.9.000 (2010-09-29)
 
1589
         */
 
1590
        protected $font_stretching = 100;
 
1591
 
 
1592
        /**
 
1593
         * Increases or decreases the space between characters in a text by the specified amount (tracking/kerning).
 
1594
         * @protected
 
1595
         * @since 5.9.000 (2010-09-29)
 
1596
         */
 
1597
        protected $font_spacing = 0;
 
1598
 
 
1599
        /**
 
1600
         * Array of no-write regions.
 
1601
         * ('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)
 
1602
         * @protected
 
1603
         * @since 5.9.003 (2010-10-14)
 
1604
         */
 
1605
        protected $page_regions = array();
 
1606
 
 
1607
        /**
 
1608
         * Array containing HTML color names and values.
 
1609
         * @protected
 
1610
         * @since 5.9.004 (2010-10-18)
 
1611
         */
 
1612
        protected $webcolor = array();
 
1613
 
 
1614
        /**
 
1615
         * Array containing spot color names and values.
 
1616
         * @protected
 
1617
         * @since 5.9.012 (2010-11-11)
 
1618
         */
 
1619
        protected $spotcolor = array();
 
1620
 
 
1621
        /**
 
1622
         * Array of PDF layers data.
 
1623
         * @protected
 
1624
         * @since 5.9.102 (2011-07-13)
 
1625
         */
 
1626
        protected $pdflayers = array();
 
1627
 
 
1628
        /**
 
1629
         * A dictionary of names and corresponding destinations (Dests key on document Catalog).
 
1630
         * @protected
 
1631
         * @since 5.9.097 (2011-06-23)
 
1632
         */
 
1633
        protected $dests = array();
 
1634
 
 
1635
        /**
 
1636
         * Object ID for Named Destinations
 
1637
         * @protected
 
1638
         * @since 5.9.097 (2011-06-23)
 
1639
         */
 
1640
        protected $n_dests;
 
1641
 
 
1642
        /**
 
1643
         * Directory used for the last SVG image.
 
1644
         * @protected
 
1645
         * @since 5.0.000 (2010-05-05)
 
1646
         */
 
1647
        protected $svgdir = '';
 
1648
 
 
1649
        /**
 
1650
         *  Deafult unit of measure for SVG.
 
1651
         * @protected
 
1652
         * @since 5.0.000 (2010-05-02)
 
1653
         */
 
1654
        protected $svgunit = 'px';
 
1655
 
 
1656
        /**
 
1657
         * Array of SVG gradients.
 
1658
         * @protected
 
1659
         * @since 5.0.000 (2010-05-02)
 
1660
         */
 
1661
        protected $svggradients = array();
 
1662
 
 
1663
        /**
 
1664
         * ID of last SVG gradient.
 
1665
         * @protected
 
1666
         * @since 5.0.000 (2010-05-02)
 
1667
         */
 
1668
        protected $svggradientid = 0;
 
1669
 
 
1670
        /**
 
1671
         * Boolean value true when in SVG defs group.
 
1672
         * @protected
 
1673
         * @since 5.0.000 (2010-05-02)
 
1674
         */
 
1675
        protected $svgdefsmode = false;
 
1676
 
 
1677
        /**
 
1678
         * Array of SVG defs.
 
1679
         * @protected
 
1680
         * @since 5.0.000 (2010-05-02)
 
1681
         */
 
1682
        protected $svgdefs = array();
 
1683
 
 
1684
        /**
 
1685
         * Boolean value true when in SVG clipPath tag.
 
1686
         * @protected
 
1687
         * @since 5.0.000 (2010-04-26)
 
1688
         */
 
1689
        protected $svgclipmode = false;
 
1690
 
 
1691
        /**
 
1692
         * Array of SVG clipPath commands.
 
1693
         * @protected
 
1694
         * @since 5.0.000 (2010-05-02)
 
1695
         */
 
1696
        protected $svgclippaths = array();
 
1697
 
 
1698
        /**
 
1699
         * Array of SVG clipPath tranformation matrix.
 
1700
         * @protected
 
1701
         * @since 5.8.022 (2010-08-31)
 
1702
         */
 
1703
        protected $svgcliptm = array();
 
1704
 
 
1705
        /**
 
1706
         * ID of last SVG clipPath.
 
1707
         * @protected
 
1708
         * @since 5.0.000 (2010-05-02)
 
1709
         */
 
1710
        protected $svgclipid = 0;
 
1711
 
 
1712
        /**
 
1713
         * SVG text.
 
1714
         * @protected
 
1715
         * @since 5.0.000 (2010-05-02)
 
1716
         */
 
1717
        protected $svgtext = '';
 
1718
 
 
1719
        /**
 
1720
         * SVG text properties.
 
1721
         * @protected
 
1722
         * @since 5.8.013 (2010-08-23)
 
1723
         */
 
1724
        protected $svgtextmode = array();
 
1725
 
 
1726
        /**
 
1727
         * Array of hinheritable SVG properties.
 
1728
         * @protected
 
1729
         * @since 5.0.000 (2010-05-02)
 
1730
         */
 
1731
        protected $svginheritprop = array('clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cursor', 'direction', 'fill', 'fill-opacity', 'fill-rule', 'font', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'image-rendering', 'kerning', 'letter-spacing', 'marker', 'marker-end', 'marker-mid', 'marker-start', 'pointer-events', 'shape-rendering', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-rendering', 'visibility', 'word-spacing', 'writing-mode');
 
1732
 
 
1733
        /**
 
1734
         * Array of SVG properties.
 
1735
         * @protected
 
1736
         * @since 5.0.000 (2010-05-02)
 
1737
         */
 
1738
        protected $svgstyles = array(array(
 
1739
                'alignment-baseline' => 'auto',
 
1740
                'baseline-shift' => 'baseline',
 
1741
                'clip' => 'auto',
 
1742
                'clip-path' => 'none',
 
1743
                'clip-rule' => 'nonzero',
 
1744
                'color' => 'black',
 
1745
                'color-interpolation' => 'sRGB',
 
1746
                'color-interpolation-filters' => 'linearRGB',
 
1747
                'color-profile' => 'auto',
 
1748
                'color-rendering' => 'auto',
 
1749
                'cursor' => 'auto',
 
1750
                'direction' => 'ltr',
 
1751
                'display' => 'inline',
 
1752
                'dominant-baseline' => 'auto',
 
1753
                'enable-background' => 'accumulate',
 
1754
                'fill' => 'black',
 
1755
                'fill-opacity' => 1,
 
1756
                'fill-rule' => 'nonzero',
 
1757
                'filter' => 'none',
 
1758
                'flood-color' => 'black',
 
1759
                'flood-opacity' => 1,
 
1760
                'font' => '',
 
1761
                'font-family' => 'helvetica',
 
1762
                'font-size' => 'medium',
 
1763
                'font-size-adjust' => 'none',
 
1764
                'font-stretch' => 'normal',
 
1765
                'font-style' => 'normal',
 
1766
                'font-variant' => 'normal',
 
1767
                'font-weight' => 'normal',
 
1768
                'glyph-orientation-horizontal' => '0deg',
 
1769
                'glyph-orientation-vertical' => 'auto',
 
1770
                'image-rendering' => 'auto',
 
1771
                'kerning' => 'auto',
 
1772
                'letter-spacing' => 'normal',
 
1773
                'lighting-color' => 'white',
 
1774
                'marker' => '',
 
1775
                'marker-end' => 'none',
 
1776
                'marker-mid' => 'none',
 
1777
                'marker-start' => 'none',
 
1778
                'mask' => 'none',
 
1779
                'opacity' => 1,
 
1780
                'overflow' => 'auto',
 
1781
                'pointer-events' => 'visiblePainted',
 
1782
                'shape-rendering' => 'auto',
 
1783
                'stop-color' => 'black',
 
1784
                'stop-opacity' => 1,
 
1785
                'stroke' => 'none',
 
1786
                'stroke-dasharray' => 'none',
 
1787
                'stroke-dashoffset' => 0,
 
1788
                'stroke-linecap' => 'butt',
 
1789
                'stroke-linejoin' => 'miter',
 
1790
                'stroke-miterlimit' => 4,
 
1791
                'stroke-opacity' => 1,
 
1792
                'stroke-width' => 1,
 
1793
                'text-anchor' => 'start',
 
1794
                'text-decoration' => 'none',
 
1795
                'text-rendering' => 'auto',
 
1796
                'unicode-bidi' => 'normal',
 
1797
                'visibility' => 'visible',
 
1798
                'word-spacing' => 'normal',
 
1799
                'writing-mode' => 'lr-tb',
 
1800
                'text-color' => 'black',
 
1801
                'transfmatrix' => array(1, 0, 0, 1, 0, 0)
 
1802
                ));
 
1803
 
 
1804
        /**
 
1805
         * If true force sRGB color profile for all document.
 
1806
         * @protected
 
1807
         * @since 5.9.121 (2011-09-28)
 
1808
         */
 
1809
        protected $force_srgb = false;
 
1810
 
 
1811
        /**
 
1812
         * If true set the document to PDF/A mode.
 
1813
         * @protected
 
1814
         * @since 5.9.121 (2011-09-27)
 
1815
         */
 
1816
        protected $pdfa_mode = false;
 
1817
 
 
1818
        /**
 
1819
         * Document creation date
 
1820
         * @protected
 
1821
         * @since 5.9.121 (2011-09-28)
 
1822
         */
 
1823
        protected $doc_date;
 
1824
 
 
1825
        /**
 
1826
         * Custom XMP data.
 
1827
         * @protected
 
1828
         * @since 5.9.128 (2011-10-06)
 
1829
         */
 
1830
        protected $custom_xmp = '';
 
1831
 
 
1832
        //------------------------------------------------------------
 
1833
        // METHODS
 
1834
        //------------------------------------------------------------
 
1835
 
 
1836
        /**
 
1837
         * This is the class constructor.
 
1838
         * It allows to set up the page format, the orientation and the measure unit used in all the methods (except for the font sizes).
 
1839
         * @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>
 
1840
         * @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.
 
1841
         * @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().
 
1842
         * @param $unicode (boolean) TRUE means that the input text is unicode (default = true)
 
1843
         * @param $encoding (string) Charset encoding; default is UTF-8.
 
1844
         * @param $diskcache (boolean) If TRUE reduce the RAM memory usage by caching temporary data on filesystem (slower).
 
1845
         * @param $pdfa (boolean) If TRUE set the document to PDF/A mode.
 
1846
         * @public
 
1847
         * @see getPageSizeFromFormat(), setPageFormat()
 
1848
         */
 
1849
        public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) {
 
1850
                /* Set internal character encoding to ASCII */
 
1851
                if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
 
1852
                        $this->internal_encoding = mb_internal_encoding();
 
1853
                        mb_internal_encoding('ASCII');
 
1854
                }
 
1855
                // get array of HTML colors
 
1856
                require(dirname(__FILE__).'/htmlcolors.php');
 
1857
                $this->webcolor = $webcolor;
 
1858
                // get array of custom spot colors
 
1859
                if (file_exists(dirname(__FILE__).'/spotcolors.php')) {
 
1860
                        require(dirname(__FILE__).'/spotcolors.php');
 
1861
                        $this->spotcolor = $spotcolor;
 
1862
                } else {
 
1863
                        $this->spotcolor = array();
 
1864
                }
 
1865
                require_once(dirname(__FILE__).'/unicode_data.php');
 
1866
                $this->unicode = new TCPDF_UNICODE_DATA();
 
1867
                require_once(dirname(__FILE__).'/encodings_maps.php');
 
1868
                $this->encmaps = new TCPDF_ENCODING_MAPS();
 
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 = '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
                // internal cell padding
 
1938
                $cpadding = $margin / 10;
 
1939
                $this->setCellPaddings($cpadding, 0, $cpadding, 0);
 
1940
                // cell margins
 
1941
                $this->setCellMargins(0, 0, 0, 0);
 
1942
                // line width (0.2 mm)
 
1943
                $this->LineWidth = 0.57 / $this->k;
 
1944
                $this->linestyleWidth = sprintf('%.2F w', ($this->LineWidth * $this->k));
 
1945
                $this->linestyleCap = '0 J';
 
1946
                $this->linestyleJoin = '0 j';
 
1947
                $this->linestyleDash = '[] 0 d';
 
1948
                // automatic page break
 
1949
                $this->SetAutoPageBreak(true, (2 * $margin));
 
1950
                // full width display mode
 
1951
                $this->SetDisplayMode('fullwidth');
 
1952
                // compression
 
1953
                $this->SetCompression();
 
1954
                // set default PDF version number
 
1955
                $this->setPDFVersion();
 
1956
                $this->encoding = $encoding;
 
1957
                $this->HREF = array();
 
1958
                $this->getFontsList();
 
1959
                $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
 
1960
                $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0);
 
1961
                $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
 
1962
                $this->extgstates = array();
 
1963
                // user's rights
 
1964
                $this->sign = false;
 
1965
                $this->ur['enabled'] = false;
 
1966
                $this->ur['document'] = '/FullSave';
 
1967
                $this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export';
 
1968
                $this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
 
1969
                $this->ur['signature'] = '/Modify';
 
1970
                $this->ur['ef'] = '/Create/Delete/Modify/Import';
 
1971
                $this->ur['formex'] = '';
 
1972
                $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
 
1973
                $this->empty_signature_appearance = array();
 
1974
                // set default JPEG quality
 
1975
                $this->jpeg_quality = 75;
 
1976
                // initialize some settings
 
1977
                $this->utf8Bidi(array(''), '');
 
1978
                // set default font
 
1979
                $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
 
1980
                // check if PCRE Unicode support is enabled
 
1981
                if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
 
1982
                        // PCRE unicode support is turned ON
 
1983
                        // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
 
1984
                        // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
 
1985
                        // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
 
1986
                        //$this->setSpacesRE('/[^\S\P{Z}\P{Lo}\xa0]/u');
 
1987
                        $this->setSpacesRE('/[^\S\P{Z}\xa0]/u');
 
1988
                } else {
 
1989
                        // PCRE unicode support is turned OFF
 
1990
                        $this->setSpacesRE('/[^\S\xa0]/');
 
1991
                }
 
1992
                $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
 
1993
                // set file ID for trailer
 
1994
                $this->file_id = md5($this->getRandomSeed('TCPDF'.$orientation.$unit.$format.$encoding));
 
1995
                // set document date
 
1996
                $this->doc_date = substr_replace(date('YmdHisO'), '\'', (0 - 2), 0).'\'';
 
1997
                // get default graphic vars
 
1998
                $this->default_graphic_vars = $this->getGraphicVars();
 
1999
                $this->header_xobj_autoreset = false;
 
2000
                $this->custom_xmp = '';
 
2001
        }
 
2002
 
 
2003
        /**
 
2004
         * Default destructor.
 
2005
         * @public
 
2006
         * @since 1.53.0.TC016
 
2007
         */
 
2008
        public function __destruct() {
 
2009
                // restore internal encoding
 
2010
                if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
 
2011
                        mb_internal_encoding($this->internal_encoding);
 
2012
                }
 
2013
                // unset all class variables
 
2014
                $this->_destroy(true);
 
2015
        }
 
2016
 
 
2017
        /**
 
2018
         * Return the current TCPDF version.
 
2019
         * @return TCPDF version string
 
2020
         * @public
 
2021
         * @since 5.9.012 (2010-11-10)
 
2022
         */
 
2023
        public function getTCPDFVersion() {
 
2024
                return $this->tcpdf_version;
 
2025
        }
 
2026
 
 
2027
        /**
 
2028
         * Set the units of measure for the document.
 
2029
         * @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.
 
2030
         * @public
 
2031
         * @since 3.0.015 (2008-06-06)
 
2032
         */
 
2033
        public function setPageUnit($unit) {
 
2034
                $unit = strtolower($unit);
 
2035
                //Set scale factor
 
2036
                switch ($unit) {
 
2037
                        // points
 
2038
                        case 'px':
 
2039
                        case 'pt': {
 
2040
                                $this->k = 1;
 
2041
                                break;
 
2042
                        }
 
2043
                        // millimeters
 
2044
                        case 'mm': {
 
2045
                                $this->k = $this->dpi / 25.4;
 
2046
                                break;
 
2047
                        }
 
2048
                        // centimeters
 
2049
                        case 'cm': {
 
2050
                                $this->k = $this->dpi / 2.54;
 
2051
                                break;
 
2052
                        }
 
2053
                        // inches
 
2054
                        case 'in': {
 
2055
                                $this->k = $this->dpi;
 
2056
                                break;
 
2057
                        }
 
2058
                        // unsupported unit
 
2059
                        default : {
 
2060
                                $this->Error('Incorrect unit: '.$unit);
 
2061
                                break;
 
2062
                        }
 
2063
                }
 
2064
                $this->pdfunit = $unit;
 
2065
                if (isset($this->CurOrientation)) {
 
2066
                        $this->setPageOrientation($this->CurOrientation);
 
2067
                }
 
2068
        }
 
2069
 
 
2070
        /**
 
2071
         * Get page dimensions from format name.
 
2072
         * @param $format (mixed) The format name. It can be: <ul>
 
2073
         * <li><b>ISO 216 A Series + 2 SIS 014711 extensions</b></li>
 
2074
         * <li>A0 (841x1189 mm ; 33.11x46.81 in)</li>
 
2075
         * <li>A1 (594x841 mm ; 23.39x33.11 in)</li>
 
2076
         * <li>A2 (420x594 mm ; 16.54x23.39 in)</li>
 
2077
         * <li>A3 (297x420 mm ; 11.69x16.54 in)</li>
 
2078
         * <li>A4 (210x297 mm ; 8.27x11.69 in)</li>
 
2079
         * <li>A5 (148x210 mm ; 5.83x8.27 in)</li>
 
2080
         * <li>A6 (105x148 mm ; 4.13x5.83 in)</li>
 
2081
         * <li>A7 (74x105 mm ; 2.91x4.13 in)</li>
 
2082
         * <li>A8 (52x74 mm ; 2.05x2.91 in)</li>
 
2083
         * <li>A9 (37x52 mm ; 1.46x2.05 in)</li>
 
2084
         * <li>A10 (26x37 mm ; 1.02x1.46 in)</li>
 
2085
         * <li>A11 (18x26 mm ; 0.71x1.02 in)</li>
 
2086
         * <li>A12 (13x18 mm ; 0.51x0.71 in)</li>
 
2087
         * <li><b>ISO 216 B Series + 2 SIS 014711 extensions</b></li>
 
2088
         * <li>B0 (1000x1414 mm ; 39.37x55.67 in)</li>
 
2089
         * <li>B1 (707x1000 mm ; 27.83x39.37 in)</li>
 
2090
         * <li>B2 (500x707 mm ; 19.69x27.83 in)</li>
 
2091
         * <li>B3 (353x500 mm ; 13.90x19.69 in)</li>
 
2092
         * <li>B4 (250x353 mm ; 9.84x13.90 in)</li>
 
2093
         * <li>B5 (176x250 mm ; 6.93x9.84 in)</li>
 
2094
         * <li>B6 (125x176 mm ; 4.92x6.93 in)</li>
 
2095
         * <li>B7 (88x125 mm ; 3.46x4.92 in)</li>
 
2096
         * <li>B8 (62x88 mm ; 2.44x3.46 in)</li>
 
2097
         * <li>B9 (44x62 mm ; 1.73x2.44 in)</li>
 
2098
         * <li>B10 (31x44 mm ; 1.22x1.73 in)</li>
 
2099
         * <li>B11 (22x31 mm ; 0.87x1.22 in)</li>
 
2100
         * <li>B12 (15x22 mm ; 0.59x0.87 in)</li>
 
2101
         * <li><b>ISO 216 C Series + 2 SIS 014711 extensions + 2 EXTENSION</b></li>
 
2102
         * <li>C0 (917x1297 mm ; 36.10x51.06 in)</li>
 
2103
         * <li>C1 (648x917 mm ; 25.51x36.10 in)</li>
 
2104
         * <li>C2 (458x648 mm ; 18.03x25.51 in)</li>
 
2105
         * <li>C3 (324x458 mm ; 12.76x18.03 in)</li>
 
2106
         * <li>C4 (229x324 mm ; 9.02x12.76 in)</li>
 
2107
         * <li>C5 (162x229 mm ; 6.38x9.02 in)</li>
 
2108
         * <li>C6 (114x162 mm ; 4.49x6.38 in)</li>
 
2109
         * <li>C7 (81x114 mm ; 3.19x4.49 in)</li>
 
2110
         * <li>C8 (57x81 mm ; 2.24x3.19 in)</li>
 
2111
         * <li>C9 (40x57 mm ; 1.57x2.24 in)</li>
 
2112
         * <li>C10 (28x40 mm ; 1.10x1.57 in)</li>
 
2113
         * <li>C11 (20x28 mm ; 0.79x1.10 in)</li>
 
2114
         * <li>C12 (14x20 mm ; 0.55x0.79 in)</li>
 
2115
         * <li>C76 (81x162 mm ; 3.19x6.38 in)</li>
 
2116
         * <li>DL (110x220 mm ; 4.33x8.66 in)</li>
 
2117
         * <li><b>SIS 014711 E Series</b></li>
 
2118
         * <li>E0 (879x1241 mm ; 34.61x48.86 in)</li>
 
2119
         * <li>E1 (620x879 mm ; 24.41x34.61 in)</li>
 
2120
         * <li>E2 (440x620 mm ; 17.32x24.41 in)</li>
 
2121
         * <li>E3 (310x440 mm ; 12.20x17.32 in)</li>
 
2122
         * <li>E4 (220x310 mm ; 8.66x12.20 in)</li>
 
2123
         * <li>E5 (155x220 mm ; 6.10x8.66 in)</li>
 
2124
         * <li>E6 (110x155 mm ; 4.33x6.10 in)</li>
 
2125
         * <li>E7 (78x110 mm ; 3.07x4.33 in)</li>
 
2126
         * <li>E8 (55x78 mm ; 2.17x3.07 in)</li>
 
2127
         * <li>E9 (39x55 mm ; 1.54x2.17 in)</li>
 
2128
         * <li>E10 (27x39 mm ; 1.06x1.54 in)</li>
 
2129
         * <li>E11 (19x27 mm ; 0.75x1.06 in)</li>
 
2130
         * <li>E12 (13x19 mm ; 0.51x0.75 in)</li>
 
2131
         * <li><b>SIS 014711 G Series</b></li>
 
2132
         * <li>G0 (958x1354 mm ; 37.72x53.31 in)</li>
 
2133
         * <li>G1 (677x958 mm ; 26.65x37.72 in)</li>
 
2134
         * <li>G2 (479x677 mm ; 18.86x26.65 in)</li>
 
2135
         * <li>G3 (338x479 mm ; 13.31x18.86 in)</li>
 
2136
         * <li>G4 (239x338 mm ; 9.41x13.31 in)</li>
 
2137
         * <li>G5 (169x239 mm ; 6.65x9.41 in)</li>
 
2138
         * <li>G6 (119x169 mm ; 4.69x6.65 in)</li>
 
2139
         * <li>G7 (84x119 mm ; 3.31x4.69 in)</li>
 
2140
         * <li>G8 (59x84 mm ; 2.32x3.31 in)</li>
 
2141
         * <li>G9 (42x59 mm ; 1.65x2.32 in)</li>
 
2142
         * <li>G10 (29x42 mm ; 1.14x1.65 in)</li>
 
2143
         * <li>G11 (21x29 mm ; 0.83x1.14 in)</li>
 
2144
         * <li>G12 (14x21 mm ; 0.55x0.83 in)</li>
 
2145
         * <li><b>ISO Press</b></li>
 
2146
         * <li>RA0 (860x1220 mm ; 33.86x48.03 in)</li>
 
2147
         * <li>RA1 (610x860 mm ; 24.02x33.86 in)</li>
 
2148
         * <li>RA2 (430x610 mm ; 16.93x24.02 in)</li>
 
2149
         * <li>RA3 (305x430 mm ; 12.01x16.93 in)</li>
 
2150
         * <li>RA4 (215x305 mm ; 8.46x12.01 in)</li>
 
2151
         * <li>SRA0 (900x1280 mm ; 35.43x50.39 in)</li>
 
2152
         * <li>SRA1 (640x900 mm ; 25.20x35.43 in)</li>
 
2153
         * <li>SRA2 (450x640 mm ; 17.72x25.20 in)</li>
 
2154
         * <li>SRA3 (320x450 mm ; 12.60x17.72 in)</li>
 
2155
         * <li>SRA4 (225x320 mm ; 8.86x12.60 in)</li>
 
2156
         * <li><b>German DIN 476</b></li>
 
2157
         * <li>4A0 (1682x2378 mm ; 66.22x93.62 in)</li>
 
2158
         * <li>2A0 (1189x1682 mm ; 46.81x66.22 in)</li>
 
2159
         * <li><b>Variations on the ISO Standard</b></li>
 
2160
         * <li>A2_EXTRA (445x619 mm ; 17.52x24.37 in)</li>
 
2161
         * <li>A3+ (329x483 mm ; 12.95x19.02 in)</li>
 
2162
         * <li>A3_EXTRA (322x445 mm ; 12.68x17.52 in)</li>
 
2163
         * <li>A3_SUPER (305x508 mm ; 12.01x20.00 in)</li>
 
2164
         * <li>SUPER_A3 (305x487 mm ; 12.01x19.17 in)</li>
 
2165
         * <li>A4_EXTRA (235x322 mm ; 9.25x12.68 in)</li>
 
2166
         * <li>A4_SUPER (229x322 mm ; 9.02x12.68 in)</li>
 
2167
         * <li>SUPER_A4 (227x356 mm ; 8.94x14.02 in)</li>
 
2168
         * <li>A4_LONG (210x348 mm ; 8.27x13.70 in)</li>
 
2169
         * <li>F4 (210x330 mm ; 8.27x12.99 in)</li>
 
2170
         * <li>SO_B5_EXTRA (202x276 mm ; 7.95x10.87 in)</li>
 
2171
         * <li>A5_EXTRA (173x235 mm ; 6.81x9.25 in)</li>
 
2172
         * <li><b>ANSI Series</b></li>
 
2173
         * <li>ANSI_E (864x1118 mm ; 34.00x44.00 in)</li>
 
2174
         * <li>ANSI_D (559x864 mm ; 22.00x34.00 in)</li>
 
2175
         * <li>ANSI_C (432x559 mm ; 17.00x22.00 in)</li>
 
2176
         * <li>ANSI_B (279x432 mm ; 11.00x17.00 in)</li>
 
2177
         * <li>ANSI_A (216x279 mm ; 8.50x11.00 in)</li>
 
2178
         * <li><b>Traditional 'Loose' North American Paper Sizes</b></li>
 
2179
         * <li>LEDGER, USLEDGER (432x279 mm ; 17.00x11.00 in)</li>
 
2180
         * <li>TABLOID, USTABLOID, BIBLE, ORGANIZERK (279x432 mm ; 11.00x17.00 in)</li>
 
2181
         * <li>LETTER, USLETTER, ORGANIZERM (216x279 mm ; 8.50x11.00 in)</li>
 
2182
         * <li>LEGAL, USLEGAL (216x356 mm ; 8.50x14.00 in)</li>
 
2183
         * <li>GLETTER, GOVERNMENTLETTER (203x267 mm ; 8.00x10.50 in)</li>
 
2184
         * <li>JLEGAL, JUNIORLEGAL (203x127 mm ; 8.00x5.00 in)</li>
 
2185
         * <li><b>Other North American Paper Sizes</b></li>
 
2186
         * <li>QUADDEMY (889x1143 mm ; 35.00x45.00 in)</li>
 
2187
         * <li>SUPER_B (330x483 mm ; 13.00x19.00 in)</li>
 
2188
         * <li>QUARTO (229x279 mm ; 9.00x11.00 in)</li>
 
2189
         * <li>FOLIO, GOVERNMENTLEGAL (216x330 mm ; 8.50x13.00 in)</li>
 
2190
         * <li>EXECUTIVE, MONARCH (184x267 mm ; 7.25x10.50 in)</li>
 
2191
         * <li>MEMO, STATEMENT, ORGANIZERL (140x216 mm ; 5.50x8.50 in)</li>
 
2192
         * <li>FOOLSCAP (210x330 mm ; 8.27x13.00 in)</li>
 
2193
         * <li>COMPACT (108x171 mm ; 4.25x6.75 in)</li>
 
2194
         * <li>ORGANIZERJ (70x127 mm ; 2.75x5.00 in)</li>
 
2195
         * <li><b>Canadian standard CAN 2-9.60M</b></li>
 
2196
         * <li>P1 (560x860 mm ; 22.05x33.86 in)</li>
 
2197
         * <li>P2 (430x560 mm ; 16.93x22.05 in)</li>
 
2198
         * <li>P3 (280x430 mm ; 11.02x16.93 in)</li>
 
2199
         * <li>P4 (215x280 mm ; 8.46x11.02 in)</li>
 
2200
         * <li>P5 (140x215 mm ; 5.51x8.46 in)</li>
 
2201
         * <li>P6 (107x140 mm ; 4.21x5.51 in)</li>
 
2202
         * <li><b>North American Architectural Sizes</b></li>
 
2203
         * <li>ARCH_E (914x1219 mm ; 36.00x48.00 in)</li>
 
2204
         * <li>ARCH_E1 (762x1067 mm ; 30.00x42.00 in)</li>
 
2205
         * <li>ARCH_D (610x914 mm ; 24.00x36.00 in)</li>
 
2206
         * <li>ARCH_C, BROADSHEET (457x610 mm ; 18.00x24.00 in)</li>
 
2207
         * <li>ARCH_B (305x457 mm ; 12.00x18.00 in)</li>
 
2208
         * <li>ARCH_A (229x305 mm ; 9.00x12.00 in)</li>
 
2209
         * <li><b>Announcement Envelopes</b></li>
 
2210
         * <li>ANNENV_A2 (111x146 mm ; 4.37x5.75 in)</li>
 
2211
         * <li>ANNENV_A6 (121x165 mm ; 4.75x6.50 in)</li>
 
2212
         * <li>ANNENV_A7 (133x184 mm ; 5.25x7.25 in)</li>
 
2213
         * <li>ANNENV_A8 (140x206 mm ; 5.50x8.12 in)</li>
 
2214
         * <li>ANNENV_A10 (159x244 mm ; 6.25x9.62 in)</li>
 
2215
         * <li>ANNENV_SLIM (98x225 mm ; 3.87x8.87 in)</li>
 
2216
         * <li><b>Commercial Envelopes</b></li>
 
2217
         * <li>COMMENV_N6_1/4 (89x152 mm ; 3.50x6.00 in)</li>
 
2218
         * <li>COMMENV_N6_3/4 (92x165 mm ; 3.62x6.50 in)</li>
 
2219
         * <li>COMMENV_N8 (98x191 mm ; 3.87x7.50 in)</li>
 
2220
         * <li>COMMENV_N9 (98x225 mm ; 3.87x8.87 in)</li>
 
2221
         * <li>COMMENV_N10 (105x241 mm ; 4.12x9.50 in)</li>
 
2222
         * <li>COMMENV_N11 (114x263 mm ; 4.50x10.37 in)</li>
 
2223
         * <li>COMMENV_N12 (121x279 mm ; 4.75x11.00 in)</li>
 
2224
         * <li>COMMENV_N14 (127x292 mm ; 5.00x11.50 in)</li>
 
2225
         * <li><b>Catalogue Envelopes</b></li>
 
2226
         * <li>CATENV_N1 (152x229 mm ; 6.00x9.00 in)</li>
 
2227
         * <li>CATENV_N1_3/4 (165x241 mm ; 6.50x9.50 in)</li>
 
2228
         * <li>CATENV_N2 (165x254 mm ; 6.50x10.00 in)</li>
 
2229
         * <li>CATENV_N3 (178x254 mm ; 7.00x10.00 in)</li>
 
2230
         * <li>CATENV_N6 (191x267 mm ; 7.50x10.50 in)</li>
 
2231
         * <li>CATENV_N7 (203x279 mm ; 8.00x11.00 in)</li>
 
2232
         * <li>CATENV_N8 (210x286 mm ; 8.25x11.25 in)</li>
 
2233
         * <li>CATENV_N9_1/2 (216x267 mm ; 8.50x10.50 in)</li>
 
2234
         * <li>CATENV_N9_3/4 (222x286 mm ; 8.75x11.25 in)</li>
 
2235
         * <li>CATENV_N10_1/2 (229x305 mm ; 9.00x12.00 in)</li>
 
2236
         * <li>CATENV_N12_1/2 (241x318 mm ; 9.50x12.50 in)</li>
 
2237
         * <li>CATENV_N13_1/2 (254x330 mm ; 10.00x13.00 in)</li>
 
2238
         * <li>CATENV_N14_1/4 (286x311 mm ; 11.25x12.25 in)</li>
 
2239
         * <li>CATENV_N14_1/2 (292x368 mm ; 11.50x14.50 in)</li>
 
2240
         * <li><b>Japanese (JIS P 0138-61) Standard B-Series</b></li>
 
2241
         * <li>JIS_B0 (1030x1456 mm ; 40.55x57.32 in)</li>
 
2242
         * <li>JIS_B1 (728x1030 mm ; 28.66x40.55 in)</li>
 
2243
         * <li>JIS_B2 (515x728 mm ; 20.28x28.66 in)</li>
 
2244
         * <li>JIS_B3 (364x515 mm ; 14.33x20.28 in)</li>
 
2245
         * <li>JIS_B4 (257x364 mm ; 10.12x14.33 in)</li>
 
2246
         * <li>JIS_B5 (182x257 mm ; 7.17x10.12 in)</li>
 
2247
         * <li>JIS_B6 (128x182 mm ; 5.04x7.17 in)</li>
 
2248
         * <li>JIS_B7 (91x128 mm ; 3.58x5.04 in)</li>
 
2249
         * <li>JIS_B8 (64x91 mm ; 2.52x3.58 in)</li>
 
2250
         * <li>JIS_B9 (45x64 mm ; 1.77x2.52 in)</li>
 
2251
         * <li>JIS_B10 (32x45 mm ; 1.26x1.77 in)</li>
 
2252
         * <li>JIS_B11 (22x32 mm ; 0.87x1.26 in)</li>
 
2253
         * <li>JIS_B12 (16x22 mm ; 0.63x0.87 in)</li>
 
2254
         * <li><b>PA Series</b></li>
 
2255
         * <li>PA0 (840x1120 mm ; 33.07x44.09 in)</li>
 
2256
         * <li>PA1 (560x840 mm ; 22.05x33.07 in)</li>
 
2257
         * <li>PA2 (420x560 mm ; 16.54x22.05 in)</li>
 
2258
         * <li>PA3 (280x420 mm ; 11.02x16.54 in)</li>
 
2259
         * <li>PA4 (210x280 mm ; 8.27x11.02 in)</li>
 
2260
         * <li>PA5 (140x210 mm ; 5.51x8.27 in)</li>
 
2261
         * <li>PA6 (105x140 mm ; 4.13x5.51 in)</li>
 
2262
         * <li>PA7 (70x105 mm ; 2.76x4.13 in)</li>
 
2263
         * <li>PA8 (52x70 mm ; 2.05x2.76 in)</li>
 
2264
         * <li>PA9 (35x52 mm ; 1.38x2.05 in)</li>
 
2265
         * <li>PA10 (26x35 mm ; 1.02x1.38 in)</li>
 
2266
         * <li><b>Standard Photographic Print Sizes</b></li>
 
2267
         * <li>PASSPORT_PHOTO (35x45 mm ; 1.38x1.77 in)</li>
 
2268
         * <li>E (82x120 mm ; 3.25x4.72 in)</li>
 
2269
         * <li>3R, L (89x127 mm ; 3.50x5.00 in)</li>
 
2270
         * <li>4R, KG (102x152 mm ; 4.02x5.98 in)</li>
 
2271
         * <li>4D (120x152 mm ; 4.72x5.98 in)</li>
 
2272
         * <li>5R, 2L (127x178 mm ; 5.00x7.01 in)</li>
 
2273
         * <li>6R, 8P (152x203 mm ; 5.98x7.99 in)</li>
 
2274
         * <li>8R, 6P (203x254 mm ; 7.99x10.00 in)</li>
 
2275
         * <li>S8R, 6PW (203x305 mm ; 7.99x12.01 in)</li>
 
2276
         * <li>10R, 4P (254x305 mm ; 10.00x12.01 in)</li>
 
2277
         * <li>S10R, 4PW (254x381 mm ; 10.00x15.00 in)</li>
 
2278
         * <li>11R (279x356 mm ; 10.98x14.02 in)</li>
 
2279
         * <li>S11R (279x432 mm ; 10.98x17.01 in)</li>
 
2280
         * <li>12R (305x381 mm ; 12.01x15.00 in)</li>
 
2281
         * <li>S12R (305x456 mm ; 12.01x17.95 in)</li>
 
2282
         * <li><b>Common Newspaper Sizes</b></li>
 
2283
         * <li>NEWSPAPER_BROADSHEET (750x600 mm ; 29.53x23.62 in)</li>
 
2284
         * <li>NEWSPAPER_BERLINER (470x315 mm ; 18.50x12.40 in)</li>
 
2285
         * <li>NEWSPAPER_COMPACT, NEWSPAPER_TABLOID (430x280 mm ; 16.93x11.02 in)</li>
 
2286
         * <li><b>Business Cards</b></li>
 
2287
         * <li>CREDIT_CARD, BUSINESS_CARD, BUSINESS_CARD_ISO7810 (54x86 mm ; 2.13x3.37 in)</li>
 
2288
         * <li>BUSINESS_CARD_ISO216 (52x74 mm ; 2.05x2.91 in)</li>
 
2289
         * <li>BUSINESS_CARD_IT, BUSINESS_CARD_UK, BUSINESS_CARD_FR, BUSINESS_CARD_DE, BUSINESS_CARD_ES (55x85 mm ; 2.17x3.35 in)</li>
 
2290
         * <li>BUSINESS_CARD_US, BUSINESS_CARD_CA (51x89 mm ; 2.01x3.50 in)</li>
 
2291
         * <li>BUSINESS_CARD_JP (55x91 mm ; 2.17x3.58 in)</li>
 
2292
         * <li>BUSINESS_CARD_HK (54x90 mm ; 2.13x3.54 in)</li>
 
2293
         * <li>BUSINESS_CARD_AU, BUSINESS_CARD_DK, BUSINESS_CARD_SE (55x90 mm ; 2.17x3.54 in)</li>
 
2294
         * <li>BUSINESS_CARD_RU, BUSINESS_CARD_CZ, BUSINESS_CARD_FI, BUSINESS_CARD_HU, BUSINESS_CARD_IL (50x90 mm ; 1.97x3.54 in)</li>
 
2295
         * <li><b>Billboards</b></li>
 
2296
         * <li>4SHEET (1016x1524 mm ; 40.00x60.00 in)</li>
 
2297
         * <li>6SHEET (1200x1800 mm ; 47.24x70.87 in)</li>
 
2298
         * <li>12SHEET (3048x1524 mm ; 120.00x60.00 in)</li>
 
2299
         * <li>16SHEET (2032x3048 mm ; 80.00x120.00 in)</li>
 
2300
         * <li>32SHEET (4064x3048 mm ; 160.00x120.00 in)</li>
 
2301
         * <li>48SHEET (6096x3048 mm ; 240.00x120.00 in)</li>
 
2302
         * <li>64SHEET (8128x3048 mm ; 320.00x120.00 in)</li>
 
2303
         * <li>96SHEET (12192x3048 mm ; 480.00x120.00 in)</li>
 
2304
         * <li><b>Old Imperial English (some are still used in USA)</b></li>
 
2305
         * <li>EN_EMPEROR (1219x1829 mm ; 48.00x72.00 in)</li>
 
2306
         * <li>EN_ANTIQUARIAN (787x1346 mm ; 31.00x53.00 in)</li>
 
2307
         * <li>EN_GRAND_EAGLE (730x1067 mm ; 28.75x42.00 in)</li>
 
2308
         * <li>EN_DOUBLE_ELEPHANT (679x1016 mm ; 26.75x40.00 in)</li>
 
2309
         * <li>EN_ATLAS (660x864 mm ; 26.00x34.00 in)</li>
 
2310
         * <li>EN_COLOMBIER (597x876 mm ; 23.50x34.50 in)</li>
 
2311
         * <li>EN_ELEPHANT (584x711 mm ; 23.00x28.00 in)</li>
 
2312
         * <li>EN_DOUBLE_DEMY (572x902 mm ; 22.50x35.50 in)</li>
 
2313
         * <li>EN_IMPERIAL (559x762 mm ; 22.00x30.00 in)</li>
 
2314
         * <li>EN_PRINCESS (546x711 mm ; 21.50x28.00 in)</li>
 
2315
         * <li>EN_CARTRIDGE (533x660 mm ; 21.00x26.00 in)</li>
 
2316
         * <li>EN_DOUBLE_LARGE_POST (533x838 mm ; 21.00x33.00 in)</li>
 
2317
         * <li>EN_ROYAL (508x635 mm ; 20.00x25.00 in)</li>
 
2318
         * <li>EN_SHEET, EN_HALF_POST (495x597 mm ; 19.50x23.50 in)</li>
 
2319
         * <li>EN_SUPER_ROYAL (483x686 mm ; 19.00x27.00 in)</li>
 
2320
         * <li>EN_DOUBLE_POST (483x775 mm ; 19.00x30.50 in)</li>
 
2321
         * <li>EN_MEDIUM (445x584 mm ; 17.50x23.00 in)</li>
 
2322
         * <li>EN_DEMY (445x572 mm ; 17.50x22.50 in)</li>
 
2323
         * <li>EN_LARGE_POST (419x533 mm ; 16.50x21.00 in)</li>
 
2324
         * <li>EN_COPY_DRAUGHT (406x508 mm ; 16.00x20.00 in)</li>
 
2325
         * <li>EN_POST (394x489 mm ; 15.50x19.25 in)</li>
 
2326
         * <li>EN_CROWN (381x508 mm ; 15.00x20.00 in)</li>
 
2327
         * <li>EN_PINCHED_POST (375x470 mm ; 14.75x18.50 in)</li>
 
2328
         * <li>EN_BRIEF (343x406 mm ; 13.50x16.00 in)</li>
 
2329
         * <li>EN_FOOLSCAP (343x432 mm ; 13.50x17.00 in)</li>
 
2330
         * <li>EN_SMALL_FOOLSCAP (337x419 mm ; 13.25x16.50 in)</li>
 
2331
         * <li>EN_POTT (318x381 mm ; 12.50x15.00 in)</li>
 
2332
         * <li><b>Old Imperial Belgian</b></li>
 
2333
         * <li>BE_GRAND_AIGLE (700x1040 mm ; 27.56x40.94 in)</li>
 
2334
         * <li>BE_COLOMBIER (620x850 mm ; 24.41x33.46 in)</li>
 
2335
         * <li>BE_DOUBLE_CARRE (620x920 mm ; 24.41x36.22 in)</li>
 
2336
         * <li>BE_ELEPHANT (616x770 mm ; 24.25x30.31 in)</li>
 
2337
         * <li>BE_PETIT_AIGLE (600x840 mm ; 23.62x33.07 in)</li>
 
2338
         * <li>BE_GRAND_JESUS (550x730 mm ; 21.65x28.74 in)</li>
 
2339
         * <li>BE_JESUS (540x730 mm ; 21.26x28.74 in)</li>
 
2340
         * <li>BE_RAISIN (500x650 mm ; 19.69x25.59 in)</li>
 
2341
         * <li>BE_GRAND_MEDIAN (460x605 mm ; 18.11x23.82 in)</li>
 
2342
         * <li>BE_DOUBLE_POSTE (435x565 mm ; 17.13x22.24 in)</li>
 
2343
         * <li>BE_COQUILLE (430x560 mm ; 16.93x22.05 in)</li>
 
2344
         * <li>BE_PETIT_MEDIAN (415x530 mm ; 16.34x20.87 in)</li>
 
2345
         * <li>BE_RUCHE (360x460 mm ; 14.17x18.11 in)</li>
 
2346
         * <li>BE_PROPATRIA (345x430 mm ; 13.58x16.93 in)</li>
 
2347
         * <li>BE_LYS (317x397 mm ; 12.48x15.63 in)</li>
 
2348
         * <li>BE_POT (307x384 mm ; 12.09x15.12 in)</li>
 
2349
         * <li>BE_ROSETTE (270x347 mm ; 10.63x13.66 in)</li>
 
2350
         * <li><b>Old Imperial French</b></li>
 
2351
         * <li>FR_UNIVERS (1000x1300 mm ; 39.37x51.18 in)</li>
 
2352
         * <li>FR_DOUBLE_COLOMBIER (900x1260 mm ; 35.43x49.61 in)</li>
 
2353
         * <li>FR_GRANDE_MONDE (900x1260 mm ; 35.43x49.61 in)</li>
 
2354
         * <li>FR_DOUBLE_SOLEIL (800x1200 mm ; 31.50x47.24 in)</li>
 
2355
         * <li>FR_DOUBLE_JESUS (760x1120 mm ; 29.92x44.09 in)</li>
 
2356
         * <li>FR_GRAND_AIGLE (750x1060 mm ; 29.53x41.73 in)</li>
 
2357
         * <li>FR_PETIT_AIGLE (700x940 mm ; 27.56x37.01 in)</li>
 
2358
         * <li>FR_DOUBLE_RAISIN (650x1000 mm ; 25.59x39.37 in)</li>
 
2359
         * <li>FR_JOURNAL (650x940 mm ; 25.59x37.01 in)</li>
 
2360
         * <li>FR_COLOMBIER_AFFICHE (630x900 mm ; 24.80x35.43 in)</li>
 
2361
         * <li>FR_DOUBLE_CAVALIER (620x920 mm ; 24.41x36.22 in)</li>
 
2362
         * <li>FR_CLOCHE (600x800 mm ; 23.62x31.50 in)</li>
 
2363
         * <li>FR_SOLEIL (600x800 mm ; 23.62x31.50 in)</li>
 
2364
         * <li>FR_DOUBLE_CARRE (560x900 mm ; 22.05x35.43 in)</li>
 
2365
         * <li>FR_DOUBLE_COQUILLE (560x880 mm ; 22.05x34.65 in)</li>
 
2366
         * <li>FR_JESUS (560x760 mm ; 22.05x29.92 in)</li>
 
2367
         * <li>FR_RAISIN (500x650 mm ; 19.69x25.59 in)</li>
 
2368
         * <li>FR_CAVALIER (460x620 mm ; 18.11x24.41 in)</li>
 
2369
         * <li>FR_DOUBLE_COURONNE (460x720 mm ; 18.11x28.35 in)</li>
 
2370
         * <li>FR_CARRE (450x560 mm ; 17.72x22.05 in)</li>
 
2371
         * <li>FR_COQUILLE (440x560 mm ; 17.32x22.05 in)</li>
 
2372
         * <li>FR_DOUBLE_TELLIERE (440x680 mm ; 17.32x26.77 in)</li>
 
2373
         * <li>FR_DOUBLE_CLOCHE (400x600 mm ; 15.75x23.62 in)</li>
 
2374
         * <li>FR_DOUBLE_POT (400x620 mm ; 15.75x24.41 in)</li>
 
2375
         * <li>FR_ECU (400x520 mm ; 15.75x20.47 in)</li>
 
2376
         * <li>FR_COURONNE (360x460 mm ; 14.17x18.11 in)</li>
 
2377
         * <li>FR_TELLIERE (340x440 mm ; 13.39x17.32 in)</li>
 
2378
         * <li>FR_POT (310x400 mm ; 12.20x15.75 in)</li>
 
2379
         * </ul>
 
2380
         * @return array containing page width and height in points
 
2381
         * @public
 
2382
         * @since 5.0.010 (2010-05-17)
 
2383
         */
 
2384
        public function getPageSizeFromFormat($format) {
 
2385
                // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 25.4 mm)
 
2386
                switch (strtoupper($format)) {
 
2387
                        // ISO 216 A Series + 2 SIS 014711 extensions
 
2388
                        case 'A0' : {$pf = array( 2383.937, 3370.394); break;}
 
2389
                        case 'A1' : {$pf = array( 1683.780, 2383.937); break;}
 
2390
                        case 'A2' : {$pf = array( 1190.551, 1683.780); break;}
 
2391
                        case 'A3' : {$pf = array(  841.890, 1190.551); break;}
 
2392
                        case 'A4' : {$pf = array(  595.276,  841.890); break;}
 
2393
                        case 'A5' : {$pf = array(  419.528,  595.276); break;}
 
2394
                        case 'A6' : {$pf = array(  297.638,  419.528); break;}
 
2395
                        case 'A7' : {$pf = array(  209.764,  297.638); break;}
 
2396
                        case 'A8' : {$pf = array(  147.402,  209.764); break;}
 
2397
                        case 'A9' : {$pf = array(  104.882,  147.402); break;}
 
2398
                        case 'A10': {$pf = array(   73.701,  104.882); break;}
 
2399
                        case 'A11': {$pf = array(   51.024,   73.701); break;}
 
2400
                        case 'A12': {$pf = array(   36.850,   51.024); break;}
 
2401
                        // ISO 216 B Series + 2 SIS 014711 extensions
 
2402
                        case 'B0' : {$pf = array( 2834.646, 4008.189); break;}
 
2403
                        case 'B1' : {$pf = array( 2004.094, 2834.646); break;}
 
2404
                        case 'B2' : {$pf = array( 1417.323, 2004.094); break;}
 
2405
                        case 'B3' : {$pf = array( 1000.630, 1417.323); break;}
 
2406
                        case 'B4' : {$pf = array(  708.661, 1000.630); break;}
 
2407
                        case 'B5' : {$pf = array(  498.898,  708.661); break;}
 
2408
                        case 'B6' : {$pf = array(  354.331,  498.898); break;}
 
2409
                        case 'B7' : {$pf = array(  249.449,  354.331); break;}
 
2410
                        case 'B8' : {$pf = array(  175.748,  249.449); break;}
 
2411
                        case 'B9' : {$pf = array(  124.724,  175.748); break;}
 
2412
                        case 'B10': {$pf = array(   87.874,  124.724); break;}
 
2413
                        case 'B11': {$pf = array(   62.362,   87.874); break;}
 
2414
                        case 'B12': {$pf = array(   42.520,   62.362); break;}
 
2415
                        // ISO 216 C Series + 2 SIS 014711 extensions + 2 EXTENSION
 
2416
                        case 'C0' : {$pf = array( 2599.370, 3676.535); break;}
 
2417
                        case 'C1' : {$pf = array( 1836.850, 2599.370); break;}
 
2418
                        case 'C2' : {$pf = array( 1298.268, 1836.850); break;}
 
2419
                        case 'C3' : {$pf = array(  918.425, 1298.268); break;}
 
2420
                        case 'C4' : {$pf = array(  649.134,  918.425); break;}
 
2421
                        case 'C5' : {$pf = array(  459.213,  649.134); break;}
 
2422
                        case 'C6' : {$pf = array(  323.150,  459.213); break;}
 
2423
                        case 'C7' : {$pf = array(  229.606,  323.150); break;}
 
2424
                        case 'C8' : {$pf = array(  161.575,  229.606); break;}
 
2425
                        case 'C9' : {$pf = array(  113.386,  161.575); break;}
 
2426
                        case 'C10': {$pf = array(   79.370,  113.386); break;}
 
2427
                        case 'C11': {$pf = array(   56.693,   79.370); break;}
 
2428
                        case 'C12': {$pf = array(   39.685,   56.693); break;}
 
2429
                        case 'C76': {$pf = array(  229.606,  459.213); break;}
 
2430
                        case 'DL' : {$pf = array(  311.811,  623.622); break;}
 
2431
                        // SIS 014711 E Series
 
2432
                        case 'E0' : {$pf = array( 2491.654, 3517.795); break;}
 
2433
                        case 'E1' : {$pf = array( 1757.480, 2491.654); break;}
 
2434
                        case 'E2' : {$pf = array( 1247.244, 1757.480); break;}
 
2435
                        case 'E3' : {$pf = array(  878.740, 1247.244); break;}
 
2436
                        case 'E4' : {$pf = array(  623.622,  878.740); break;}
 
2437
                        case 'E5' : {$pf = array(  439.370,  623.622); break;}
 
2438
                        case 'E6' : {$pf = array(  311.811,  439.370); break;}
 
2439
                        case 'E7' : {$pf = array(  221.102,  311.811); break;}
 
2440
                        case 'E8' : {$pf = array(  155.906,  221.102); break;}
 
2441
                        case 'E9' : {$pf = array(  110.551,  155.906); break;}
 
2442
                        case 'E10': {$pf = array(   76.535,  110.551); break;}
 
2443
                        case 'E11': {$pf = array(   53.858,   76.535); break;}
 
2444
                        case 'E12': {$pf = array(   36.850,   53.858); break;}
 
2445
                        // SIS 014711 G Series
 
2446
                        case 'G0' : {$pf = array( 2715.591, 3838.110); break;}
 
2447
                        case 'G1' : {$pf = array( 1919.055, 2715.591); break;}
 
2448
                        case 'G2' : {$pf = array( 1357.795, 1919.055); break;}
 
2449
                        case 'G3' : {$pf = array(  958.110, 1357.795); break;}
 
2450
                        case 'G4' : {$pf = array(  677.480,  958.110); break;}
 
2451
                        case 'G5' : {$pf = array(  479.055,  677.480); break;}
 
2452
                        case 'G6' : {$pf = array(  337.323,  479.055); break;}
 
2453
                        case 'G7' : {$pf = array(  238.110,  337.323); break;}
 
2454
                        case 'G8' : {$pf = array(  167.244,  238.110); break;}
 
2455
                        case 'G9' : {$pf = array(  119.055,  167.244); break;}
 
2456
                        case 'G10': {$pf = array(   82.205,  119.055); break;}
 
2457
                        case 'G11': {$pf = array(   59.528,   82.205); break;}
 
2458
                        case 'G12': {$pf = array(   39.685,   59.528); break;}
 
2459
                        // ISO Press
 
2460
                        case 'RA0': {$pf = array( 2437.795, 3458.268); break;}
 
2461
                        case 'RA1': {$pf = array( 1729.134, 2437.795); break;}
 
2462
                        case 'RA2': {$pf = array( 1218.898, 1729.134); break;}
 
2463
                        case 'RA3': {$pf = array(  864.567, 1218.898); break;}
 
2464
                        case 'RA4': {$pf = array(  609.449,  864.567); break;}
 
2465
                        case 'SRA0': {$pf = array( 2551.181, 3628.346); break;}
 
2466
                        case 'SRA1': {$pf = array( 1814.173, 2551.181); break;}
 
2467
                        case 'SRA2': {$pf = array( 1275.591, 1814.173); break;}
 
2468
                        case 'SRA3': {$pf = array(  907.087, 1275.591); break;}
 
2469
                        case 'SRA4': {$pf = array(  637.795,  907.087); break;}
 
2470
                        // German  DIN 476
 
2471
                        case '4A0': {$pf = array( 4767.874, 6740.787); break;}
 
2472
                        case '2A0': {$pf = array( 3370.394, 4767.874); break;}
 
2473
                        // Variations on the ISO Standard
 
2474
                        case 'A2_EXTRA'   : {$pf = array( 1261.417, 1754.646); break;}
 
2475
                        case 'A3+'        : {$pf = array(  932.598, 1369.134); break;}
 
2476
                        case 'A3_EXTRA'   : {$pf = array(  912.756, 1261.417); break;}
 
2477
                        case 'A3_SUPER'   : {$pf = array(  864.567, 1440.000); break;}
 
2478
                        case 'SUPER_A3'   : {$pf = array(  864.567, 1380.472); break;}
 
2479
                        case 'A4_EXTRA'   : {$pf = array(  666.142,  912.756); break;}
 
2480
                        case 'A4_SUPER'   : {$pf = array(  649.134,  912.756); break;}
 
2481
                        case 'SUPER_A4'   : {$pf = array(  643.465, 1009.134); break;}
 
2482
                        case 'A4_LONG'    : {$pf = array(  595.276,  986.457); break;}
 
2483
                        case 'F4'         : {$pf = array(  595.276,  935.433); break;}
 
2484
                        case 'SO_B5_EXTRA': {$pf = array(  572.598,  782.362); break;}
 
2485
                        case 'A5_EXTRA'   : {$pf = array(  490.394,  666.142); break;}
 
2486
                        // ANSI Series
 
2487
                        case 'ANSI_E': {$pf = array( 2448.000, 3168.000); break;}
 
2488
                        case 'ANSI_D': {$pf = array( 1584.000, 2448.000); break;}
 
2489
                        case 'ANSI_C': {$pf = array( 1224.000, 1584.000); break;}
 
2490
                        case 'ANSI_B': {$pf = array(  792.000, 1224.000); break;}
 
2491
                        case 'ANSI_A': {$pf = array(  612.000,  792.000); break;}
 
2492
                        // Traditional 'Loose' North American Paper Sizes
 
2493
                        case 'USLEDGER':
 
2494
                        case 'LEDGER' : {$pf = array( 1224.000,  792.000); break;}
 
2495
                        case 'ORGANIZERK':
 
2496
                        case 'BIBLE':
 
2497
                        case 'USTABLOID':
 
2498
                        case 'TABLOID': {$pf = array(  792.000, 1224.000); break;}
 
2499
                        case 'ORGANIZERM':
 
2500
                        case 'USLETTER':
 
2501
                        case 'LETTER' : {$pf = array(  612.000,  792.000); break;}
 
2502
                        case 'USLEGAL':
 
2503
                        case 'LEGAL'  : {$pf = array(  612.000, 1008.000); break;}
 
2504
                        case 'GOVERNMENTLETTER':
 
2505
                        case 'GLETTER': {$pf = array(  576.000,  756.000); break;}
 
2506
                        case 'JUNIORLEGAL':
 
2507
                        case 'JLEGAL' : {$pf = array(  576.000,  360.000); break;}
 
2508
                        // Other North American Paper Sizes
 
2509
                        case 'QUADDEMY': {$pf = array( 2520.000, 3240.000); break;}
 
2510
                        case 'SUPER_B': {$pf = array(  936.000, 1368.000); break;}
 
2511
                        case 'QUARTO': {$pf = array(  648.000,  792.000); break;}
 
2512
                        case 'GOVERNMENTLEGAL':
 
2513
                        case 'FOLIO': {$pf = array(  612.000,  936.000); break;}
 
2514
                        case 'MONARCH':
 
2515
                        case 'EXECUTIVE': {$pf = array(  522.000,  756.000); break;}
 
2516
                        case 'ORGANIZERL':
 
2517
                        case 'STATEMENT':
 
2518
                        case 'MEMO': {$pf = array(  396.000,  612.000); break;}
 
2519
                        case 'FOOLSCAP': {$pf = array(  595.440,  936.000); break;}
 
2520
                        case 'COMPACT': {$pf = array(  306.000,  486.000); break;}
 
2521
                        case 'ORGANIZERJ': {$pf = array(  198.000,  360.000); break;}
 
2522
                        // Canadian standard CAN 2-9.60M
 
2523
                        case 'P1': {$pf = array( 1587.402, 2437.795); break;}
 
2524
                        case 'P2': {$pf = array( 1218.898, 1587.402); break;}
 
2525
                        case 'P3': {$pf = array(  793.701, 1218.898); break;}
 
2526
                        case 'P4': {$pf = array(  609.449,  793.701); break;}
 
2527
                        case 'P5': {$pf = array(  396.850,  609.449); break;}
 
2528
                        case 'P6': {$pf = array(  303.307,  396.850); break;}
 
2529
                        // North American Architectural Sizes
 
2530
                        case 'ARCH_E' : {$pf = array( 2592.000, 3456.000); break;}
 
2531
                        case 'ARCH_E1': {$pf = array( 2160.000, 3024.000); break;}
 
2532
                        case 'ARCH_D' : {$pf = array( 1728.000, 2592.000); break;}
 
2533
                        case 'BROADSHEET':
 
2534
                        case 'ARCH_C' : {$pf = array( 1296.000, 1728.000); break;}
 
2535
                        case 'ARCH_B' : {$pf = array(  864.000, 1296.000); break;}
 
2536
                        case 'ARCH_A' : {$pf = array(  648.000,  864.000); break;}
 
2537
                        // --- North American Envelope Sizes ---
 
2538
                        //   - Announcement Envelopes
 
2539
                        case 'ANNENV_A2'  : {$pf = array(  314.640,  414.000); break;}
 
2540
                        case 'ANNENV_A6'  : {$pf = array(  342.000,  468.000); break;}
 
2541
                        case 'ANNENV_A7'  : {$pf = array(  378.000,  522.000); break;}
 
2542
                        case 'ANNENV_A8'  : {$pf = array(  396.000,  584.640); break;}
 
2543
                        case 'ANNENV_A10' : {$pf = array(  450.000,  692.640); break;}
 
2544
                        case 'ANNENV_SLIM': {$pf = array(  278.640,  638.640); break;}
 
2545
                        //   - Commercial Envelopes
 
2546
                        case 'COMMENV_N6_1/4': {$pf = array(  252.000,  432.000); break;}
 
2547
                        case 'COMMENV_N6_3/4': {$pf = array(  260.640,  468.000); break;}
 
2548
                        case 'COMMENV_N8'    : {$pf = array(  278.640,  540.000); break;}
 
2549
                        case 'COMMENV_N9'    : {$pf = array(  278.640,  638.640); break;}
 
2550
                        case 'COMMENV_N10'   : {$pf = array(  296.640,  684.000); break;}
 
2551
                        case 'COMMENV_N11'   : {$pf = array(  324.000,  746.640); break;}
 
2552
                        case 'COMMENV_N12'   : {$pf = array(  342.000,  792.000); break;}
 
2553
                        case 'COMMENV_N14'   : {$pf = array(  360.000,  828.000); break;}
 
2554
                        //   - Catalogue Envelopes
 
2555
                        case 'CATENV_N1'     : {$pf = array(  432.000,  648.000); break;}
 
2556
                        case 'CATENV_N1_3/4' : {$pf = array(  468.000,  684.000); break;}
 
2557
                        case 'CATENV_N2'     : {$pf = array(  468.000,  720.000); break;}
 
2558
                        case 'CATENV_N3'     : {$pf = array(  504.000,  720.000); break;}
 
2559
                        case 'CATENV_N6'     : {$pf = array(  540.000,  756.000); break;}
 
2560
                        case 'CATENV_N7'     : {$pf = array(  576.000,  792.000); break;}
 
2561
                        case 'CATENV_N8'     : {$pf = array(  594.000,  810.000); break;}
 
2562
                        case 'CATENV_N9_1/2' : {$pf = array(  612.000,  756.000); break;}
 
2563
                        case 'CATENV_N9_3/4' : {$pf = array(  630.000,  810.000); break;}
 
2564
                        case 'CATENV_N10_1/2': {$pf = array(  648.000,  864.000); break;}
 
2565
                        case 'CATENV_N12_1/2': {$pf = array(  684.000,  900.000); break;}
 
2566
                        case 'CATENV_N13_1/2': {$pf = array(  720.000,  936.000); break;}
 
2567
                        case 'CATENV_N14_1/4': {$pf = array(  810.000,  882.000); break;}
 
2568
                        case 'CATENV_N14_1/2': {$pf = array(  828.000, 1044.000); break;}
 
2569
                        // Japanese (JIS P 0138-61) Standard B-Series
 
2570
                        case 'JIS_B0' : {$pf = array( 2919.685, 4127.244); break;}
 
2571
                        case 'JIS_B1' : {$pf = array( 2063.622, 2919.685); break;}
 
2572
                        case 'JIS_B2' : {$pf = array( 1459.843, 2063.622); break;}
 
2573
                        case 'JIS_B3' : {$pf = array( 1031.811, 1459.843); break;}
 
2574
                        case 'JIS_B4' : {$pf = array(  728.504, 1031.811); break;}
 
2575
                        case 'JIS_B5' : {$pf = array(  515.906,  728.504); break;}
 
2576
                        case 'JIS_B6' : {$pf = array(  362.835,  515.906); break;}
 
2577
                        case 'JIS_B7' : {$pf = array(  257.953,  362.835); break;}
 
2578
                        case 'JIS_B8' : {$pf = array(  181.417,  257.953); break;}
 
2579
                        case 'JIS_B9' : {$pf = array(  127.559,  181.417); break;}
 
2580
                        case 'JIS_B10': {$pf = array(   90.709,  127.559); break;}
 
2581
                        case 'JIS_B11': {$pf = array(   62.362,   90.709); break;}
 
2582
                        case 'JIS_B12': {$pf = array(   45.354,   62.362); break;}
 
2583
                        // PA Series
 
2584
                        case 'PA0' : {$pf = array( 2381.102, 3174.803,); break;}
 
2585
                        case 'PA1' : {$pf = array( 1587.402, 2381.102); break;}
 
2586
                        case 'PA2' : {$pf = array( 1190.551, 1587.402); break;}
 
2587
                        case 'PA3' : {$pf = array(  793.701, 1190.551); break;}
 
2588
                        case 'PA4' : {$pf = array(  595.276,  793.701); break;}
 
2589
                        case 'PA5' : {$pf = array(  396.850,  595.276); break;}
 
2590
                        case 'PA6' : {$pf = array(  297.638,  396.850); break;}
 
2591
                        case 'PA7' : {$pf = array(  198.425,  297.638); break;}
 
2592
                        case 'PA8' : {$pf = array(  147.402,  198.425); break;}
 
2593
                        case 'PA9' : {$pf = array(   99.213,  147.402); break;}
 
2594
                        case 'PA10': {$pf = array(   73.701,   99.213); break;}
 
2595
                        // Standard Photographic Print Sizes
 
2596
                        case 'PASSPORT_PHOTO': {$pf = array(   99.213,  127.559); break;}
 
2597
                        case 'E'   : {$pf = array(  233.858,  340.157); break;}
 
2598
                        case 'L':
 
2599
                        case '3R'  : {$pf = array(  252.283,  360.000); break;}
 
2600
                        case 'KG':
 
2601
                        case '4R'  : {$pf = array(  289.134,  430.866); break;}
 
2602
                        case '4D'  : {$pf = array(  340.157,  430.866); break;}
 
2603
                        case '2L':
 
2604
                        case '5R'  : {$pf = array(  360.000,  504.567); break;}
 
2605
                        case '8P':
 
2606
                        case '6R'  : {$pf = array(  430.866,  575.433); break;}
 
2607
                        case '6P':
 
2608
                        case '8R'  : {$pf = array(  575.433,  720.000); break;}
 
2609
                        case '6PW':
 
2610
                        case 'S8R' : {$pf = array(  575.433,  864.567); break;}
 
2611
                        case '4P':
 
2612
                        case '10R' : {$pf = array(  720.000,  864.567); break;}
 
2613
                        case '4PW':
 
2614
                        case 'S10R': {$pf = array(  720.000, 1080.000); break;}
 
2615
                        case '11R' : {$pf = array(  790.866, 1009.134); break;}
 
2616
                        case 'S11R': {$pf = array(  790.866, 1224.567); break;}
 
2617
                        case '12R' : {$pf = array(  864.567, 1080.000); break;}
 
2618
                        case 'S12R': {$pf = array(  864.567, 1292.598); break;}
 
2619
                        // Common Newspaper Sizes
 
2620
                        case 'NEWSPAPER_BROADSHEET': {$pf = array( 2125.984, 1700.787); break;}
 
2621
                        case 'NEWSPAPER_BERLINER'  : {$pf = array( 1332.283,  892.913); break;}
 
2622
                        case 'NEWSPAPER_TABLOID':
 
2623
                        case 'NEWSPAPER_COMPACT'   : {$pf = array( 1218.898,  793.701); break;}
 
2624
                        // Business Cards
 
2625
                        case 'CREDIT_CARD':
 
2626
                        case 'BUSINESS_CARD':
 
2627
                        case 'BUSINESS_CARD_ISO7810': {$pf = array(  153.014,  242.646); break;}
 
2628
                        case 'BUSINESS_CARD_ISO216' : {$pf = array(  147.402,  209.764); break;}
 
2629
                        case 'BUSINESS_CARD_IT':
 
2630
                        case 'BUSINESS_CARD_UK':
 
2631
                        case 'BUSINESS_CARD_FR':
 
2632
                        case 'BUSINESS_CARD_DE':
 
2633
                        case 'BUSINESS_CARD_ES'     : {$pf = array(  155.906,  240.945); break;}
 
2634
                        case 'BUSINESS_CARD_CA':
 
2635
                        case 'BUSINESS_CARD_US'     : {$pf = array(  144.567,  252.283); break;}
 
2636
                        case 'BUSINESS_CARD_JP'     : {$pf = array(  155.906,  257.953); break;}
 
2637
                        case 'BUSINESS_CARD_HK'     : {$pf = array(  153.071,  255.118); break;}
 
2638
                        case 'BUSINESS_CARD_AU':
 
2639
                        case 'BUSINESS_CARD_DK':
 
2640
                        case 'BUSINESS_CARD_SE'     : {$pf = array(  155.906,  255.118); break;}
 
2641
                        case 'BUSINESS_CARD_RU':
 
2642
                        case 'BUSINESS_CARD_CZ':
 
2643
                        case 'BUSINESS_CARD_FI':
 
2644
                        case 'BUSINESS_CARD_HU':
 
2645
                        case 'BUSINESS_CARD_IL'     : {$pf = array(  141.732,  255.118); break;}
 
2646
                        // Billboards
 
2647
                        case '4SHEET' : {$pf = array( 2880.000, 4320.000); break;}
 
2648
                        case '6SHEET' : {$pf = array( 3401.575, 5102.362); break;}
 
2649
                        case '12SHEET': {$pf = array( 8640.000, 4320.000); break;}
 
2650
                        case '16SHEET': {$pf = array( 5760.000, 8640.000); break;}
 
2651
                        case '32SHEET': {$pf = array(11520.000, 8640.000); break;}
 
2652
                        case '48SHEET': {$pf = array(17280.000, 8640.000); break;}
 
2653
                        case '64SHEET': {$pf = array(23040.000, 8640.000); break;}
 
2654
                        case '96SHEET': {$pf = array(34560.000, 8640.000); break;}
 
2655
                        // Old European Sizes
 
2656
                        //   - Old Imperial English Sizes
 
2657
                        case 'EN_EMPEROR'          : {$pf = array( 3456.000, 5184.000); break;}
 
2658
                        case 'EN_ANTIQUARIAN'      : {$pf = array( 2232.000, 3816.000); break;}
 
2659
                        case 'EN_GRAND_EAGLE'      : {$pf = array( 2070.000, 3024.000); break;}
 
2660
                        case 'EN_DOUBLE_ELEPHANT'  : {$pf = array( 1926.000, 2880.000); break;}
 
2661
                        case 'EN_ATLAS'            : {$pf = array( 1872.000, 2448.000); break;}
 
2662
                        case 'EN_COLOMBIER'        : {$pf = array( 1692.000, 2484.000); break;}
 
2663
                        case 'EN_ELEPHANT'         : {$pf = array( 1656.000, 2016.000); break;}
 
2664
                        case 'EN_DOUBLE_DEMY'      : {$pf = array( 1620.000, 2556.000); break;}
 
2665
                        case 'EN_IMPERIAL'         : {$pf = array( 1584.000, 2160.000); break;}
 
2666
                        case 'EN_PRINCESS'         : {$pf = array( 1548.000, 2016.000); break;}
 
2667
                        case 'EN_CARTRIDGE'        : {$pf = array( 1512.000, 1872.000); break;}
 
2668
                        case 'EN_DOUBLE_LARGE_POST': {$pf = array( 1512.000, 2376.000); break;}
 
2669
                        case 'EN_ROYAL'            : {$pf = array( 1440.000, 1800.000); break;}
 
2670
                        case 'EN_SHEET':
 
2671
                        case 'EN_HALF_POST'        : {$pf = array( 1404.000, 1692.000); break;}
 
2672
                        case 'EN_SUPER_ROYAL'      : {$pf = array( 1368.000, 1944.000); break;}
 
2673
                        case 'EN_DOUBLE_POST'      : {$pf = array( 1368.000, 2196.000); break;}
 
2674
                        case 'EN_MEDIUM'           : {$pf = array( 1260.000, 1656.000); break;}
 
2675
                        case 'EN_DEMY'             : {$pf = array( 1260.000, 1620.000); break;}
 
2676
                        case 'EN_LARGE_POST'       : {$pf = array( 1188.000, 1512.000); break;}
 
2677
                        case 'EN_COPY_DRAUGHT'     : {$pf = array( 1152.000, 1440.000); break;}
 
2678
                        case 'EN_POST'             : {$pf = array( 1116.000, 1386.000); break;}
 
2679
                        case 'EN_CROWN'            : {$pf = array( 1080.000, 1440.000); break;}
 
2680
                        case 'EN_PINCHED_POST'     : {$pf = array( 1062.000, 1332.000); break;}
 
2681
                        case 'EN_BRIEF'            : {$pf = array(  972.000, 1152.000); break;}
 
2682
                        case 'EN_FOOLSCAP'         : {$pf = array(  972.000, 1224.000); break;}
 
2683
                        case 'EN_SMALL_FOOLSCAP'   : {$pf = array(  954.000, 1188.000); break;}
 
2684
                        case 'EN_POTT'             : {$pf = array(  900.000, 1080.000); break;}
 
2685
                        //   - Old Imperial Belgian Sizes
 
2686
                        case 'BE_GRAND_AIGLE' : {$pf = array( 1984.252, 2948.031); break;}
 
2687
                        case 'BE_COLOMBIER'   : {$pf = array( 1757.480, 2409.449); break;}
 
2688
                        case 'BE_DOUBLE_CARRE': {$pf = array( 1757.480, 2607.874); break;}
 
2689
                        case 'BE_ELEPHANT'    : {$pf = array( 1746.142, 2182.677); break;}
 
2690
                        case 'BE_PETIT_AIGLE' : {$pf = array( 1700.787, 2381.102); break;}
 
2691
                        case 'BE_GRAND_JESUS' : {$pf = array( 1559.055, 2069.291); break;}
 
2692
                        case 'BE_JESUS'       : {$pf = array( 1530.709, 2069.291); break;}
 
2693
                        case 'BE_RAISIN'      : {$pf = array( 1417.323, 1842.520); break;}
 
2694
                        case 'BE_GRAND_MEDIAN': {$pf = array( 1303.937, 1714.961); break;}
 
2695
                        case 'BE_DOUBLE_POSTE': {$pf = array( 1233.071, 1601.575); break;}
 
2696
                        case 'BE_COQUILLE'    : {$pf = array( 1218.898, 1587.402); break;}
 
2697
                        case 'BE_PETIT_MEDIAN': {$pf = array( 1176.378, 1502.362); break;}
 
2698
                        case 'BE_RUCHE'       : {$pf = array( 1020.472, 1303.937); break;}
 
2699
                        case 'BE_PROPATRIA'   : {$pf = array(  977.953, 1218.898); break;}
 
2700
                        case 'BE_LYS'         : {$pf = array(  898.583, 1125.354); break;}
 
2701
                        case 'BE_POT'         : {$pf = array(  870.236, 1088.504); break;}
 
2702
                        case 'BE_ROSETTE'     : {$pf = array(  765.354,  983.622); break;}
 
2703
                        //   - Old Imperial French Sizes
 
2704
                        case 'FR_UNIVERS'          : {$pf = array( 2834.646, 3685.039); break;}
 
2705
                        case 'FR_DOUBLE_COLOMBIER' : {$pf = array( 2551.181, 3571.654); break;}
 
2706
                        case 'FR_GRANDE_MONDE'     : {$pf = array( 2551.181, 3571.654); break;}
 
2707
                        case 'FR_DOUBLE_SOLEIL'    : {$pf = array( 2267.717, 3401.575); break;}
 
2708
                        case 'FR_DOUBLE_JESUS'     : {$pf = array( 2154.331, 3174.803); break;}
 
2709
                        case 'FR_GRAND_AIGLE'      : {$pf = array( 2125.984, 3004.724); break;}
 
2710
                        case 'FR_PETIT_AIGLE'      : {$pf = array( 1984.252, 2664.567); break;}
 
2711
                        case 'FR_DOUBLE_RAISIN'    : {$pf = array( 1842.520, 2834.646); break;}
 
2712
                        case 'FR_JOURNAL'          : {$pf = array( 1842.520, 2664.567); break;}
 
2713
                        case 'FR_COLOMBIER_AFFICHE': {$pf = array( 1785.827, 2551.181); break;}
 
2714
                        case 'FR_DOUBLE_CAVALIER'  : {$pf = array( 1757.480, 2607.874); break;}
 
2715
                        case 'FR_CLOCHE'           : {$pf = array( 1700.787, 2267.717); break;}
 
2716
                        case 'FR_SOLEIL'           : {$pf = array( 1700.787, 2267.717); break;}
 
2717
                        case 'FR_DOUBLE_CARRE'     : {$pf = array( 1587.402, 2551.181); break;}
 
2718
                        case 'FR_DOUBLE_COQUILLE'  : {$pf = array( 1587.402, 2494.488); break;}
 
2719
                        case 'FR_JESUS'            : {$pf = array( 1587.402, 2154.331); break;}
 
2720
                        case 'FR_RAISIN'           : {$pf = array( 1417.323, 1842.520); break;}
 
2721
                        case 'FR_CAVALIER'         : {$pf = array( 1303.937, 1757.480); break;}
 
2722
                        case 'FR_DOUBLE_COURONNE'  : {$pf = array( 1303.937, 2040.945); break;}
 
2723
                        case 'FR_CARRE'            : {$pf = array( 1275.591, 1587.402); break;}
 
2724
                        case 'FR_COQUILLE'         : {$pf = array( 1247.244, 1587.402); break;}
 
2725
                        case 'FR_DOUBLE_TELLIERE'  : {$pf = array( 1247.244, 1927.559); break;}
 
2726
                        case 'FR_DOUBLE_CLOCHE'    : {$pf = array( 1133.858, 1700.787); break;}
 
2727
                        case 'FR_DOUBLE_POT'       : {$pf = array( 1133.858, 1757.480); break;}
 
2728
                        case 'FR_ECU'              : {$pf = array( 1133.858, 1474.016); break;}
 
2729
                        case 'FR_COURONNE'         : {$pf = array( 1020.472, 1303.937); break;}
 
2730
                        case 'FR_TELLIERE'         : {$pf = array(  963.780, 1247.244); break;}
 
2731
                        case 'FR_POT'              : {$pf = array(  878.740, 1133.858); break;}
 
2732
                        // DEFAULT ISO A4
 
2733
                        default: {$pf = array(  595.276,  841.890); break;}
 
2734
                }
 
2735
                return $pf;
 
2736
        }
 
2737
 
 
2738
        /**
 
2739
         * Change the format of the current page
 
2740
         * @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>
 
2741
         * <li>['format'] = page format name (one of the above);</li>
 
2742
         * <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>
 
2743
         * <li>['PZ'] : The page's preferred zoom (magnification) factor.</li>
 
2744
         * <li>['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed:</li>
 
2745
         * <li>['MediaBox']['llx'] : lower-left x coordinate in points</li>
 
2746
         * <li>['MediaBox']['lly'] : lower-left y coordinate in points</li>
 
2747
         * <li>['MediaBox']['urx'] : upper-right x coordinate in points</li>
 
2748
         * <li>['MediaBox']['ury'] : upper-right y coordinate in points</li>
 
2749
         * <li>['CropBox'] : the visible region of default user space:</li>
 
2750
         * <li>['CropBox']['llx'] : lower-left x coordinate in points</li>
 
2751
         * <li>['CropBox']['lly'] : lower-left y coordinate in points</li>
 
2752
         * <li>['CropBox']['urx'] : upper-right x coordinate in points</li>
 
2753
         * <li>['CropBox']['ury'] : upper-right y coordinate in points</li>
 
2754
         * <li>['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment:</li>
 
2755
         * <li>['BleedBox']['llx'] : lower-left x coordinate in points</li>
 
2756
         * <li>['BleedBox']['lly'] : lower-left y coordinate in points</li>
 
2757
         * <li>['BleedBox']['urx'] : upper-right x coordinate in points</li>
 
2758
         * <li>['BleedBox']['ury'] : upper-right y coordinate in points</li>
 
2759
         * <li>['TrimBox'] : the intended dimensions of the finished page after trimming:</li>
 
2760
         * <li>['TrimBox']['llx'] : lower-left x coordinate in points</li>
 
2761
         * <li>['TrimBox']['lly'] : lower-left y coordinate in points</li>
 
2762
         * <li>['TrimBox']['urx'] : upper-right x coordinate in points</li>
 
2763
         * <li>['TrimBox']['ury'] : upper-right y coordinate in points</li>
 
2764
         * <li>['ArtBox'] : the extent of the page's meaningful content:</li>
 
2765
         * <li>['ArtBox']['llx'] : lower-left x coordinate in points</li>
 
2766
         * <li>['ArtBox']['lly'] : lower-left y coordinate in points</li>
 
2767
         * <li>['ArtBox']['urx'] : upper-right x coordinate in points</li>
 
2768
         * <li>['ArtBox']['ury'] : upper-right y coordinate in points</li>
 
2769
         * <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>
 
2770
         * <li>['BoxColorInfo'][BOXTYPE]['C'] : an array of three numbers in the range 0-255, representing the components in the DeviceRGB colour space.</li>
 
2771
         * <li>['BoxColorInfo'][BOXTYPE]['W'] : the guideline width in default user units</li>
 
2772
         * <li>['BoxColorInfo'][BOXTYPE]['S'] : the guideline style: S = Solid; D = Dashed</li>
 
2773
         * <li>['BoxColorInfo'][BOXTYPE]['D'] : dash array defining a pattern of dashes and gaps to be used in drawing dashed guidelines</li>
 
2774
         * <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>
 
2775
         * <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>
 
2776
         * <li>['trans']['S'] : transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li>
 
2777
         * <li>['trans']['D'] : The duration of the transition effect, in seconds.</li>
 
2778
         * <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>
 
2779
         * <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>
 
2780
         * <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>
 
2781
         * <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>
 
2782
         * <li>['trans']['B'] : (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li>
 
2783
         * </ul>
 
2784
         * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul>
 
2785
         * <li>P or Portrait (default)</li>
 
2786
         * <li>L or Landscape</li>
 
2787
         * <li>'' (empty string) for automatic orientation</li>
 
2788
         * </ul>
 
2789
         * @protected
 
2790
         * @since 3.0.015 (2008-06-06)
 
2791
         * @see getPageSizeFromFormat()
 
2792
         */
 
2793
        protected function setPageFormat($format, $orientation='P') {
 
2794
                if (!empty($format) AND isset($this->pagedim[$this->page])) {
 
2795
                        // remove inherited values
 
2796
                        unset($this->pagedim[$this->page]);
 
2797
                }
 
2798
                if (is_string($format)) {
 
2799
                        // get page measures from format name
 
2800
                        $pf = $this->getPageSizeFromFormat($format);
 
2801
                        $this->fwPt = $pf[0];
 
2802
                        $this->fhPt = $pf[1];
 
2803
                } else {
 
2804
                        // the boundaries of the physical medium on which the page shall be displayed or printed
 
2805
                        if (isset($format['MediaBox'])) {
 
2806
                                $this->setPageBoxes($this->page, 'MediaBox', $format['MediaBox']['llx'], $format['MediaBox']['lly'], $format['MediaBox']['urx'], $format['MediaBox']['ury'], false);
 
2807
                                $this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k);
 
2808
                                $this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k);
 
2809
                        } else {
 
2810
                                if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) {
 
2811
                                        $pf = array(($format[0] * $this->k), ($format[1] * $this->k));
 
2812
                                } else {
 
2813
                                        if (!isset($format['format'])) {
 
2814
                                                // default value
 
2815
                                                $format['format'] = 'A4';
 
2816
                                        }
 
2817
                                        $pf = $this->getPageSizeFromFormat($format['format']);
 
2818
                                }
 
2819
                                $this->fwPt = $pf[0];
 
2820
                                $this->fhPt = $pf[1];
 
2821
                                $this->setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true);
 
2822
                        }
 
2823
                        // the visible region of default user space
 
2824
                        if (isset($format['CropBox'])) {
 
2825
                                $this->setPageBoxes($this->page, 'CropBox', $format['CropBox']['llx'], $format['CropBox']['lly'], $format['CropBox']['urx'], $format['CropBox']['ury'], false);
 
2826
                        }
 
2827
                        // the region to which the contents of the page shall be clipped when output in a production environment
 
2828
                        if (isset($format['BleedBox'])) {
 
2829
                                $this->setPageBoxes($this->page, 'BleedBox', $format['BleedBox']['llx'], $format['BleedBox']['lly'], $format['BleedBox']['urx'], $format['BleedBox']['ury'], false);
 
2830
                        }
 
2831
                        // the intended dimensions of the finished page after trimming
 
2832
                        if (isset($format['TrimBox'])) {
 
2833
                                $this->setPageBoxes($this->page, 'TrimBox', $format['TrimBox']['llx'], $format['TrimBox']['lly'], $format['TrimBox']['urx'], $format['TrimBox']['ury'], false);
 
2834
                        }
 
2835
                        // the page's meaningful content (including potential white space)
 
2836
                        if (isset($format['ArtBox'])) {
 
2837
                                $this->setPageBoxes($this->page, 'ArtBox', $format['ArtBox']['llx'], $format['ArtBox']['lly'], $format['ArtBox']['urx'], $format['ArtBox']['ury'], false);
 
2838
                        }
 
2839
                        // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries
 
2840
                        if (isset($format['BoxColorInfo'])) {
 
2841
                                $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo'];
 
2842
                        }
 
2843
                        if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) {
 
2844
                                // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
 
2845
                                $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']);
 
2846
                        }
 
2847
                        if (isset($format['PZ'])) {
 
2848
                                // The page's preferred zoom (magnification) factor
 
2849
                                $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']);
 
2850
                        }
 
2851
                        if (isset($format['trans'])) {
 
2852
                                // The style and duration of the visual transition to use when moving from another page to the given page during a presentation
 
2853
                                if (isset($format['trans']['Dur'])) {
 
2854
                                        // The page's display duration
 
2855
                                        $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']);
 
2856
                                }
 
2857
                                $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade');
 
2858
                                if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) {
 
2859
                                        // The transition style that shall be used when moving to this page from another during a presentation
 
2860
                                        $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S'];
 
2861
                                        $valid_effect = array('Split', 'Blinds');
 
2862
                                        $valid_vals = array('H', 'V');
 
2863
                                        if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) {
 
2864
                                                $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm'];
 
2865
                                        }
 
2866
                                        $valid_effect = array('Split', 'Box', 'Fly');
 
2867
                                        $valid_vals = array('I', 'O');
 
2868
                                        if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) {
 
2869
                                                $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M'];
 
2870
                                        }
 
2871
                                        $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push');
 
2872
                                        if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) {
 
2873
                                                if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe'))
 
2874
                                                        OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter'))
 
2875
                                                        OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) {
 
2876
                                                        $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']);
 
2877
                                                }
 
2878
                                        }
 
2879
                                        if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
 
2880
                                                $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']);
 
2881
                                        }
 
2882
                                        if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
 
2883
                                                $this->pagedim[$this->page]['trans']['B'] = 'true';
 
2884
                                        }
 
2885
                                } else {
 
2886
                                        $this->pagedim[$this->page]['trans']['S'] = 'R';
 
2887
                                }
 
2888
                                if (isset($format['trans']['D'])) {
 
2889
                                        // The duration of the transition effect, in seconds
 
2890
                                        $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']);
 
2891
                                } else {
 
2892
                                        $this->pagedim[$this->page]['trans']['D'] = 1;
 
2893
                                }
 
2894
                        }
 
2895
                }
 
2896
                $this->setPageOrientation($orientation);
 
2897
        }
 
2898
 
 
2899
        /**
 
2900
         * Set page boundaries.
 
2901
         * @param $page (int) page number
 
2902
         * @param $type (string) valid values are: <ul><li>'MediaBox' : the boundaries of the physical medium on which the page shall be displayed or printed;</li><li>'CropBox' : the visible region of default user space;</li><li>'BleedBox' : the region to which the contents of the page shall be clipped when output in a production environment;</li><li>'TrimBox' : the intended dimensions of the finished page after trimming;</li><li>'ArtBox' : the page's meaningful content (including potential white space).</li></ul>
 
2903
         * @param $llx (float) lower-left x coordinate in user units
 
2904
         * @param $lly (float) lower-left y coordinate in user units
 
2905
         * @param $urx (float) upper-right x coordinate in user units
 
2906
         * @param $ury (float) upper-right y coordinate in user units
 
2907
         * @param $points (boolean) if true uses user units as unit of measure, otherwise uses PDF points
 
2908
         * @public
 
2909
         * @since 5.0.010 (2010-05-17)
 
2910
         */
 
2911
        public function setPageBoxes($page, $type, $llx, $lly, $urx, $ury, $points=false) {
 
2912
                if (!isset($this->pagedim[$page])) {
 
2913
                        // initialize array
 
2914
                        $this->pagedim[$page] = array();
 
2915
                }
 
2916
                $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
 
2917
                if (!in_array($type, $pageboxes)) {
 
2918
                        return;
 
2919
                }
 
2920
                if ($points) {
 
2921
                        $k = 1;
 
2922
                } else {
 
2923
                        $k = $this->k;
 
2924
                }
 
2925
                $this->pagedim[$page][$type]['llx'] = ($llx * $k);
 
2926
                $this->pagedim[$page][$type]['lly'] = ($lly * $k);
 
2927
                $this->pagedim[$page][$type]['urx'] = ($urx * $k);
 
2928
                $this->pagedim[$page][$type]['ury'] = ($ury * $k);
 
2929
        }
 
2930
 
 
2931
        /**
 
2932
         * Swap X and Y coordinates of page boxes (change page boxes orientation).
 
2933
         * @param $page (int) page number
 
2934
         * @protected
 
2935
         * @since 5.0.010 (2010-05-17)
 
2936
         */
 
2937
        protected function swapPageBoxCoordinates($page) {
 
2938
                $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
 
2939
                foreach ($pageboxes as $type) {
 
2940
                        // swap X and Y coordinates
 
2941
                        if (isset($this->pagedim[$page][$type])) {
 
2942
                                $tmp = $this->pagedim[$page][$type]['llx'];
 
2943
                                $this->pagedim[$page][$type]['llx'] = $this->pagedim[$page][$type]['lly'];
 
2944
                                $this->pagedim[$page][$type]['lly'] = $tmp;
 
2945
                                $tmp = $this->pagedim[$page][$type]['urx'];
 
2946
                                $this->pagedim[$page][$type]['urx'] = $this->pagedim[$page][$type]['ury'];
 
2947
                                $this->pagedim[$page][$type]['ury'] = $tmp;
 
2948
                        }
 
2949
                }
 
2950
        }
 
2951
 
 
2952
        /**
 
2953
         * Set page orientation.
 
2954
         * @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>
 
2955
         * @param $autopagebreak (boolean) Boolean indicating if auto-page-break mode should be on or off.
 
2956
         * @param $bottommargin (float) bottom margin of the page.
 
2957
         * @public
 
2958
         * @since 3.0.015 (2008-06-06)
 
2959
         */
 
2960
        public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
 
2961
                if (!isset($this->pagedim[$this->page]['MediaBox'])) {
 
2962
                        // the boundaries of the physical medium on which the page shall be displayed or printed
 
2963
                        $this->setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true);
 
2964
                }
 
2965
                if (!isset($this->pagedim[$this->page]['CropBox'])) {
 
2966
                        // the visible region of default user space
 
2967
                        $this->setPageBoxes($this->page, 'CropBox', $this->pagedim[$this->page]['MediaBox']['llx'], $this->pagedim[$this->page]['MediaBox']['lly'], $this->pagedim[$this->page]['MediaBox']['urx'], $this->pagedim[$this->page]['MediaBox']['ury'], true);
 
2968
                }
 
2969
                if (!isset($this->pagedim[$this->page]['BleedBox'])) {
 
2970
                        // the region to which the contents of the page shall be clipped when output in a production environment
 
2971
                        $this->setPageBoxes($this->page, 'BleedBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true);
 
2972
                }
 
2973
                if (!isset($this->pagedim[$this->page]['TrimBox'])) {
 
2974
                        // the intended dimensions of the finished page after trimming
 
2975
                        $this->setPageBoxes($this->page, 'TrimBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true);
 
2976
                }
 
2977
                if (!isset($this->pagedim[$this->page]['ArtBox'])) {
 
2978
                        // the page's meaningful content (including potential white space)
 
2979
                        $this->setPageBoxes($this->page, 'ArtBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true);
 
2980
                }
 
2981
                if (!isset($this->pagedim[$this->page]['Rotate'])) {
 
2982
                        // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
 
2983
                        $this->pagedim[$this->page]['Rotate'] = 0;
 
2984
                }
 
2985
                if (!isset($this->pagedim[$this->page]['PZ'])) {
 
2986
                        // The page's preferred zoom (magnification) factor
 
2987
                        $this->pagedim[$this->page]['PZ'] = 1;
 
2988
                }
 
2989
                if ($this->fwPt > $this->fhPt) {
 
2990
                        // landscape
 
2991
                        $default_orientation = 'L';
 
2992
                } else {
 
2993
                        // portrait
 
2994
                        $default_orientation = 'P';
 
2995
                }
 
2996
                $valid_orientations = array('P', 'L');
 
2997
                if (empty($orientation)) {
 
2998
                        $orientation = $default_orientation;
 
2999
                } else {
 
3000
                        $orientation = strtoupper($orientation{0});
 
3001
                }
 
3002
                if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
 
3003
                        $this->CurOrientation = $orientation;
 
3004
                        $this->wPt = $this->fhPt;
 
3005
                        $this->hPt = $this->fwPt;
 
3006
                } else {
 
3007
                        $this->CurOrientation = $default_orientation;
 
3008
                        $this->wPt = $this->fwPt;
 
3009
                        $this->hPt = $this->fhPt;
 
3010
                }
 
3011
                if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){
 
3012
                        // swap X and Y coordinates (change page orientation)
 
3013
                        $this->swapPageBoxCoordinates($this->page);
 
3014
                }
 
3015
                $this->w = $this->wPt / $this->k;
 
3016
                $this->h = $this->hPt / $this->k;
 
3017
                if ($this->empty_string($autopagebreak)) {
 
3018
                        if (isset($this->AutoPageBreak)) {
 
3019
                                $autopagebreak = $this->AutoPageBreak;
 
3020
                        } else {
 
3021
                                $autopagebreak = true;
 
3022
                        }
 
3023
                }
 
3024
                if ($this->empty_string($bottommargin)) {
 
3025
                        if (isset($this->bMargin)) {
 
3026
                                $bottommargin = $this->bMargin;
 
3027
                        } else {
 
3028
                                // default value = 2 cm
 
3029
                                $bottommargin = 2 * 28.35 / $this->k;
 
3030
                        }
 
3031
                }
 
3032
                $this->SetAutoPageBreak($autopagebreak, $bottommargin);
 
3033
                // store page dimensions
 
3034
                $this->pagedim[$this->page]['w'] = $this->wPt;
 
3035
                $this->pagedim[$this->page]['h'] = $this->hPt;
 
3036
                $this->pagedim[$this->page]['wk'] = $this->w;
 
3037
                $this->pagedim[$this->page]['hk'] = $this->h;
 
3038
                $this->pagedim[$this->page]['tm'] = $this->tMargin;
 
3039
                $this->pagedim[$this->page]['bm'] = $bottommargin;
 
3040
                $this->pagedim[$this->page]['lm'] = $this->lMargin;
 
3041
                $this->pagedim[$this->page]['rm'] = $this->rMargin;
 
3042
                $this->pagedim[$this->page]['pb'] = $autopagebreak;
 
3043
                $this->pagedim[$this->page]['or'] = $this->CurOrientation;
 
3044
                $this->pagedim[$this->page]['olm'] = $this->original_lMargin;
 
3045
                $this->pagedim[$this->page]['orm'] = $this->original_rMargin;
 
3046
        }
 
3047
 
 
3048
        /**
 
3049
         * Set regular expression to detect withespaces or word separators.
 
3050
         * The pattern delimiter must be the forward-slash character "/".
 
3051
         * Some example patterns are:
 
3052
         * <pre>
 
3053
         * Non-Unicode or missing PCRE unicode support: "/[^\S\xa0]/"
 
3054
         * Unicode and PCRE unicode support: "/[^\S\P{Z}\xa0]/u"
 
3055
         * Unicode and PCRE unicode support in Chinese mode: "/[^\S\P{Z}\P{Lo}\xa0]/u"
 
3056
         * if PCRE unicode support is turned ON ("\P" is the negate class of "\p"):
 
3057
         * "\p{Z}" or "\p{Separator}": any kind of Unicode whitespace or invisible separator.
 
3058
         * "\p{Lo}" or "\p{Other_Letter}": a Unicode letter or ideograph that does not have lowercase and uppercase variants.
 
3059
         * "\p{Lo}" is needed for Chinese characters because are packed next to each other without spaces in between.
 
3060
         * </pre>
 
3061
         * @param $re (string) regular expression (leave empty for default).
 
3062
         * @public
 
3063
         * @since 4.6.016 (2009-06-15)
 
3064
         */
 
3065
        public function setSpacesRE($re='/[^\S\xa0]/') {
 
3066
                $this->re_spaces = $re;
 
3067
                $re_parts = explode('/', $re);
 
3068
                // get pattern parts
 
3069
                $this->re_space = array();
 
3070
                if (isset($re_parts[1]) AND !empty($re_parts[1])) {
 
3071
                        $this->re_space['p'] = $re_parts[1];
 
3072
                } else {
 
3073
                        $this->re_space['p'] = '[\s]';
 
3074
                }
 
3075
                // set pattern modifiers
 
3076
                if (isset($re_parts[2]) AND !empty($re_parts[2])) {
 
3077
                        $this->re_space['m'] = $re_parts[2];
 
3078
                } else {
 
3079
                        $this->re_space['m'] = '';
 
3080
                }
 
3081
        }
 
3082
 
 
3083
        /**
 
3084
         * Enable or disable Right-To-Left language mode
 
3085
         * @param $enable (Boolean) if true enable Right-To-Left language mode.
 
3086
         * @param $resetx (Boolean) if true reset the X position on direction change.
 
3087
         * @public
 
3088
         * @since 2.0.000 (2008-01-03)
 
3089
         */
 
3090
        public function setRTL($enable, $resetx=true) {
 
3091
                $enable = $enable ? true : false;
 
3092
                $resetx = ($resetx AND ($enable != $this->rtl));
 
3093
                $this->rtl = $enable;
 
3094
                $this->tmprtl = false;
 
3095
                if ($resetx) {
 
3096
                        $this->Ln(0);
 
3097
                }
 
3098
        }
 
3099
 
 
3100
        /**
 
3101
         * Return the RTL status
 
3102
         * @return boolean
 
3103
         * @public
 
3104
         * @since 4.0.012 (2008-07-24)
 
3105
         */
 
3106
        public function getRTL() {
 
3107
                return $this->rtl;
 
3108
        }
 
3109
 
 
3110
        /**
 
3111
         * Force temporary RTL language direction
 
3112
         * @param $mode (mixed) can be false, 'L' for LTR or 'R' for RTL
 
3113
         * @public
 
3114
         * @since 2.1.000 (2008-01-09)
 
3115
         */
 
3116
        public function setTempRTL($mode) {
 
3117
                $newmode = false;
 
3118
                switch (strtoupper($mode)) {
 
3119
                        case 'LTR':
 
3120
                        case 'L': {
 
3121
                                if ($this->rtl) {
 
3122
                                        $newmode = 'L';
 
3123
                                }
 
3124
                                break;
 
3125
                        }
 
3126
                        case 'RTL':
 
3127
                        case 'R': {
 
3128
                                if (!$this->rtl) {
 
3129
                                        $newmode = 'R';
 
3130
                                }
 
3131
                                break;
 
3132
                        }
 
3133
                        case false:
 
3134
                        default: {
 
3135
                                $newmode = false;
 
3136
                                break;
 
3137
                        }
 
3138
                }
 
3139
                $this->tmprtl = $newmode;
 
3140
        }
 
3141
 
 
3142
        /**
 
3143
         * Return the current temporary RTL status
 
3144
         * @return boolean
 
3145
         * @public
 
3146
         * @since 4.8.014 (2009-11-04)
 
3147
         */
 
3148
        public function isRTLTextDir() {
 
3149
                return ($this->rtl OR ($this->tmprtl == 'R'));
 
3150
        }
 
3151
 
 
3152
        /**
 
3153
         * Set the last cell height.
 
3154
         * @param $h (float) cell height.
 
3155
         * @author Nicola Asuni
 
3156
         * @public
 
3157
         * @since 1.53.0.TC034
 
3158
         */
 
3159
        public function setLastH($h) {
 
3160
                $this->lasth = $h;
 
3161
        }
 
3162
 
 
3163
        /**
 
3164
         * Reset the last cell height.
 
3165
         * @public
 
3166
         * @since 5.9.000 (2010-10-03)
 
3167
         */
 
3168
        public function resetLastH() {
 
3169
                $this->lasth = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
 
3170
        }
 
3171
 
 
3172
        /**
 
3173
         * Get the last cell height.
 
3174
         * @return last cell height
 
3175
         * @public
 
3176
         * @since 4.0.017 (2008-08-05)
 
3177
         */
 
3178
        public function getLastH() {
 
3179
                return $this->lasth;
 
3180
        }
 
3181
 
 
3182
        /**
 
3183
         * Set the adjusting factor to convert pixels to user units.
 
3184
         * @param $scale (float) adjusting factor to convert pixels to user units.
 
3185
         * @author Nicola Asuni
 
3186
         * @public
 
3187
         * @since 1.5.2
 
3188
         */
 
3189
        public function setImageScale($scale) {
 
3190
                $this->imgscale = $scale;
 
3191
        }
 
3192
 
 
3193
        /**
 
3194
         * Returns the adjusting factor to convert pixels to user units.
 
3195
         * @return float adjusting factor to convert pixels to user units.
 
3196
         * @author Nicola Asuni
 
3197
         * @public
 
3198
         * @since 1.5.2
 
3199
         */
 
3200
        public function getImageScale() {
 
3201
                return $this->imgscale;
 
3202
        }
 
3203
 
 
3204
        /**
 
3205
         * Returns an array of page dimensions:
 
3206
         * <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>
 
3207
         * @param $pagenum (int) page number (empty = current page)
 
3208
         * @return array of page dimensions.
 
3209
         * @author Nicola Asuni
 
3210
         * @public
 
3211
         * @since 4.5.027 (2009-03-16)
 
3212
         */
 
3213
        public function getPageDimensions($pagenum='') {
 
3214
                if (empty($pagenum)) {
 
3215
                        $pagenum = $this->page;
 
3216
                }
 
3217
                return $this->pagedim[$pagenum];
 
3218
        }
 
3219
 
 
3220
        /**
 
3221
         * Returns the page width in units.
 
3222
         * @param $pagenum (int) page number (empty = current page)
 
3223
         * @return int page width.
 
3224
         * @author Nicola Asuni
 
3225
         * @public
 
3226
         * @since 1.5.2
 
3227
         * @see getPageDimensions()
 
3228
         */
 
3229
        public function getPageWidth($pagenum='') {
 
3230
                if (empty($pagenum)) {
 
3231
                        return $this->w;
 
3232
                }
 
3233
                return $this->pagedim[$pagenum]['w'];
 
3234
        }
 
3235
 
 
3236
        /**
 
3237
         * Returns the page height in units.
 
3238
         * @param $pagenum (int) page number (empty = current page)
 
3239
         * @return int page height.
 
3240
         * @author Nicola Asuni
 
3241
         * @public
 
3242
         * @since 1.5.2
 
3243
         * @see getPageDimensions()
 
3244
         */
 
3245
        public function getPageHeight($pagenum='') {
 
3246
                if (empty($pagenum)) {
 
3247
                        return $this->h;
 
3248
                }
 
3249
                return $this->pagedim[$pagenum]['h'];
 
3250
        }
 
3251
 
 
3252
        /**
 
3253
         * Returns the page break margin.
 
3254
         * @param $pagenum (int) page number (empty = current page)
 
3255
         * @return int page break margin.
 
3256
         * @author Nicola Asuni
 
3257
         * @public
 
3258
         * @since 1.5.2
 
3259
         * @see getPageDimensions()
 
3260
         */
 
3261
        public function getBreakMargin($pagenum='') {
 
3262
                if (empty($pagenum)) {
 
3263
                        return $this->bMargin;
 
3264
                }
 
3265
                return $this->pagedim[$pagenum]['bm'];
 
3266
        }
 
3267
 
 
3268
        /**
 
3269
         * Returns the scale factor (number of points in user unit).
 
3270
         * @return int scale factor.
 
3271
         * @author Nicola Asuni
 
3272
         * @public
 
3273
         * @since 1.5.2
 
3274
         */
 
3275
        public function getScaleFactor() {
 
3276
                return $this->k;
 
3277
        }
 
3278
 
 
3279
        /**
 
3280
         * Defines the left, top and right margins.
 
3281
         * @param $left (float) Left margin.
 
3282
         * @param $top (float) Top margin.
 
3283
         * @param $right (float) Right margin. Default value is the left one.
 
3284
         * @param $keepmargins (boolean) if true overwrites the default page margins
 
3285
         * @public
 
3286
         * @since 1.0
 
3287
         * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
 
3288
         */
 
3289
        public function SetMargins($left, $top, $right=-1, $keepmargins=false) {
 
3290
                //Set left, top and right margins
 
3291
                $this->lMargin = $left;
 
3292
                $this->tMargin = $top;
 
3293
                if ($right == -1) {
 
3294
                        $right = $left;
 
3295
                }
 
3296
                $this->rMargin = $right;
 
3297
                if ($keepmargins) {
 
3298
                        // overwrite original values
 
3299
                        $this->original_lMargin = $this->lMargin;
 
3300
                        $this->original_rMargin = $this->rMargin;
 
3301
                }
 
3302
        }
 
3303
 
 
3304
        /**
 
3305
         * 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.
 
3306
         * @param $margin (float) The margin.
 
3307
         * @public
 
3308
         * @since 1.4
 
3309
         * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
 
3310
         */
 
3311
        public function SetLeftMargin($margin) {
 
3312
                //Set left margin
 
3313
                $this->lMargin = $margin;
 
3314
                if (($this->page > 0) AND ($this->x < $margin)) {
 
3315
                        $this->x = $margin;
 
3316
                }
 
3317
        }
 
3318
 
 
3319
        /**
 
3320
         * Defines the top margin. The method can be called before creating the first page.
 
3321
         * @param $margin (float) The margin.
 
3322
         * @public
 
3323
         * @since 1.5
 
3324
         * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
 
3325
         */
 
3326
        public function SetTopMargin($margin) {
 
3327
                //Set top margin
 
3328
                $this->tMargin = $margin;
 
3329
                if (($this->page > 0) AND ($this->y < $margin)) {
 
3330
                        $this->y = $margin;
 
3331
                }
 
3332
        }
 
3333
 
 
3334
        /**
 
3335
         * Defines the right margin. The method can be called before creating the first page.
 
3336
         * @param $margin (float) The margin.
 
3337
         * @public
 
3338
         * @since 1.5
 
3339
         * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
 
3340
         */
 
3341
        public function SetRightMargin($margin) {
 
3342
                $this->rMargin = $margin;
 
3343
                if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
 
3344
                        $this->x = $this->w - $margin;
 
3345
                }
 
3346
        }
 
3347
 
 
3348
        /**
 
3349
         * Set the same internal Cell padding for top, right, bottom, left-
 
3350
         * @param $pad (float) internal padding.
 
3351
         * @public
 
3352
         * @since 2.1.000 (2008-01-09)
 
3353
         * @see getCellPaddings(), setCellPaddings()
 
3354
         */
 
3355
        public function SetCellPadding($pad) {
 
3356
                if ($pad >= 0) {
 
3357
                        $this->cell_padding['L'] = $pad;
 
3358
                        $this->cell_padding['T'] = $pad;
 
3359
                        $this->cell_padding['R'] = $pad;
 
3360
                        $this->cell_padding['B'] = $pad;
 
3361
                }
 
3362
        }
 
3363
 
 
3364
        /**
 
3365
         * Set the internal Cell paddings.
 
3366
         * @param $left (float) left padding
 
3367
         * @param $top (float) top padding
 
3368
         * @param $right (float) right padding
 
3369
         * @param $bottom (float) bottom padding
 
3370
         * @public
 
3371
         * @since 5.9.000 (2010-10-03)
 
3372
         * @see getCellPaddings(), SetCellPadding()
 
3373
         */
 
3374
        public function setCellPaddings($left='', $top='', $right='', $bottom='') {
 
3375
                if (($left !== '') AND ($left >= 0)) {
 
3376
                        $this->cell_padding['L'] = $left;
 
3377
                }
 
3378
                if (($top !== '') AND ($top >= 0)) {
 
3379
                        $this->cell_padding['T'] = $top;
 
3380
                }
 
3381
                if (($right !== '') AND ($right >= 0)) {
 
3382
                        $this->cell_padding['R'] = $right;
 
3383
                }
 
3384
                if (($bottom !== '') AND ($bottom >= 0)) {
 
3385
                        $this->cell_padding['B'] = $bottom;
 
3386
                }
 
3387
        }
 
3388
 
 
3389
        /**
 
3390
         * Get the internal Cell padding array.
 
3391
         * @return array of padding values
 
3392
         * @public
 
3393
         * @since 5.9.000 (2010-10-03)
 
3394
         * @see setCellPaddings(), SetCellPadding()
 
3395
         */
 
3396
        public function getCellPaddings() {
 
3397
                return $this->cell_padding;
 
3398
        }
 
3399
 
 
3400
        /**
 
3401
         * Set the internal Cell margins.
 
3402
         * @param $left (float) left margin
 
3403
         * @param $top (float) top margin
 
3404
         * @param $right (float) right margin
 
3405
         * @param $bottom (float) bottom margin
 
3406
         * @public
 
3407
         * @since 5.9.000 (2010-10-03)
 
3408
         * @see getCellMargins()
 
3409
         */
 
3410
        public function setCellMargins($left='', $top='', $right='', $bottom='') {
 
3411
                if (($left !== '') AND ($left >= 0)) {
 
3412
                        $this->cell_margin['L'] = $left;
 
3413
                }
 
3414
                if (($top !== '') AND ($top >= 0)) {
 
3415
                        $this->cell_margin['T'] = $top;
 
3416
                }
 
3417
                if (($right !== '') AND ($right >= 0)) {
 
3418
                        $this->cell_margin['R'] = $right;
 
3419
                }
 
3420
                if (($bottom !== '') AND ($bottom >= 0)) {
 
3421
                        $this->cell_margin['B'] = $bottom;
 
3422
                }
 
3423
        }
 
3424
 
 
3425
        /**
 
3426
         * Get the internal Cell margin array.
 
3427
         * @return array of margin values
 
3428
         * @public
 
3429
         * @since 5.9.000 (2010-10-03)
 
3430
         * @see setCellMargins()
 
3431
         */
 
3432
        public function getCellMargins() {
 
3433
                return $this->cell_margin;
 
3434
        }
 
3435
 
 
3436
        /**
 
3437
         * Adjust the internal Cell padding array to take account of the line width.
 
3438
         * @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)))
 
3439
         * @return array of adjustments
 
3440
         * @public
 
3441
         * @since 5.9.000 (2010-10-03)
 
3442
         */
 
3443
        protected function adjustCellPadding($brd=0) {
 
3444
                if (empty($brd)) {
 
3445
                        return;
 
3446
                }
 
3447
                if (is_string($brd)) {
 
3448
                        // convert string to array
 
3449
                        $slen = strlen($brd);
 
3450
                        $newbrd = array();
 
3451
                        for ($i = 0; $i < $slen; ++$i) {
 
3452
                                $newbrd[$brd[$i]] = true;
 
3453
                        }
 
3454
                        $brd = $newbrd;
 
3455
                } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) {
 
3456
                        $brd = array('LRTB' => true);
 
3457
                }
 
3458
                if (!is_array($brd)) {
 
3459
                        return;
 
3460
                }
 
3461
                // store current cell padding
 
3462
                $cp = $this->cell_padding;
 
3463
                // select border mode
 
3464
                if (isset($brd['mode'])) {
 
3465
                        $mode = $brd['mode'];
 
3466
                        unset($brd['mode']);
 
3467
                } else {
 
3468
                        $mode = 'normal';
 
3469
                }
 
3470
                // process borders
 
3471
                foreach ($brd as $border => $style) {
 
3472
                        $line_width = $this->LineWidth;
 
3473
                        if (is_array($style) AND isset($style['width'])) {
 
3474
                                // get border width
 
3475
                                $line_width = $style['width'];
 
3476
                        }
 
3477
                        $adj = 0; // line width inside the cell
 
3478
                        switch ($mode) {
 
3479
                                case 'ext': {
 
3480
                                        $adj = 0;
 
3481
                                        break;
 
3482
                                }
 
3483
                                case 'int': {
 
3484
                                        $adj = $line_width;
 
3485
                                        break;
 
3486
                                }
 
3487
                                case 'normal':
 
3488
                                default: {
 
3489
                                        $adj = ($line_width / 2);
 
3490
                                        break;
 
3491
                                }
 
3492
                        }
 
3493
                        // correct internal cell padding if required to avoid overlap between text and lines
 
3494
                        if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) {
 
3495
                                $this->cell_padding['T'] = $adj;
 
3496
                        }
 
3497
                        if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) {
 
3498
                                $this->cell_padding['R'] = $adj;
 
3499
                        }
 
3500
                        if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) {
 
3501
                                $this->cell_padding['B'] = $adj;
 
3502
                        }
 
3503
                        if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) {
 
3504
                                $this->cell_padding['L'] = $adj;
 
3505
                        }
 
3506
                }
 
3507
                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']));
 
3508
        }
 
3509
 
 
3510
        /**
 
3511
         * 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.
 
3512
         * @param $auto (boolean) Boolean indicating if mode should be on or off.
 
3513
         * @param $margin (float) Distance from the bottom of the page.
 
3514
         * @public
 
3515
         * @since 1.0
 
3516
         * @see Cell(), MultiCell(), AcceptPageBreak()
 
3517
         */
 
3518
        public function SetAutoPageBreak($auto, $margin=0) {
 
3519
                $this->AutoPageBreak = $auto ? true : false;
 
3520
                $this->bMargin = $margin;
 
3521
                $this->PageBreakTrigger = $this->h - $margin;
 
3522
        }
 
3523
 
 
3524
        /**
 
3525
         * Return the auto-page-break mode (true or false).
 
3526
         * @return boolean auto-page-break mode
 
3527
         * @public
 
3528
         * @since 5.9.088
 
3529
         */
 
3530
        public function getAutoPageBreak() {
 
3531
                return $this->AutoPageBreak;
 
3532
        }
 
3533
 
 
3534
        /**
 
3535
         * Defines the way the document is to be displayed by the viewer.
 
3536
         * @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>
 
3537
         * @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>
 
3538
         * @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>
 
3539
         * @public
 
3540
         * @since 1.2
 
3541
         */
 
3542
        public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
 
3543
                if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
 
3544
                        $this->ZoomMode = $zoom;
 
3545
                } else {
 
3546
                        $this->Error('Incorrect zoom display mode: '.$zoom);
 
3547
                }
 
3548
                switch ($layout) {
 
3549
                        case 'default':
 
3550
                        case 'single':
 
3551
                        case 'SinglePage': {
 
3552
                                $this->LayoutMode = 'SinglePage';
 
3553
                                break;
 
3554
                        }
 
3555
                        case 'continuous':
 
3556
                        case 'OneColumn': {
 
3557
                                $this->LayoutMode = 'OneColumn';
 
3558
                                break;
 
3559
                        }
 
3560
                        case 'two':
 
3561
                        case 'TwoColumnLeft': {
 
3562
                                $this->LayoutMode = 'TwoColumnLeft';
 
3563
                                break;
 
3564
                        }
 
3565
                        case 'TwoColumnRight': {
 
3566
                                $this->LayoutMode = 'TwoColumnRight';
 
3567
                                break;
 
3568
                        }
 
3569
                        case 'TwoPageLeft': {
 
3570
                                $this->LayoutMode = 'TwoPageLeft';
 
3571
                                break;
 
3572
                        }
 
3573
                        case 'TwoPageRight': {
 
3574
                                $this->LayoutMode = 'TwoPageRight';
 
3575
                                break;
 
3576
                        }
 
3577
                        default: {
 
3578
                                $this->LayoutMode = 'SinglePage';
 
3579
                        }
 
3580
                }
 
3581
                switch ($mode) {
 
3582
                        case 'UseNone': {
 
3583
                                $this->PageMode = 'UseNone';
 
3584
                                break;
 
3585
                        }
 
3586
                        case 'UseOutlines': {
 
3587
                                $this->PageMode = 'UseOutlines';
 
3588
                                break;
 
3589
                        }
 
3590
                        case 'UseThumbs': {
 
3591
                                $this->PageMode = 'UseThumbs';
 
3592
                                break;
 
3593
                        }
 
3594
                        case 'FullScreen': {
 
3595
                                $this->PageMode = 'FullScreen';
 
3596
                                break;
 
3597
                        }
 
3598
                        case 'UseOC': {
 
3599
                                $this->PageMode = 'UseOC';
 
3600
                                break;
 
3601
                        }
 
3602
                        case '': {
 
3603
                                $this->PageMode = 'UseAttachments';
 
3604
                                break;
 
3605
                        }
 
3606
                        default: {
 
3607
                                $this->PageMode = 'UseNone';
 
3608
                        }
 
3609
                }
 
3610
        }
 
3611
 
 
3612
        /**
 
3613
         * 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.
 
3614
         * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
 
3615
         * @param $compress (boolean) Boolean indicating if compression must be enabled.
 
3616
         * @public
 
3617
         * @since 1.4
 
3618
         */
 
3619
        public function SetCompression($compress=true) {
 
3620
                if (function_exists('gzcompress')) {
 
3621
                        $this->compress = $compress ? true : false;
 
3622
                } else {
 
3623
                        $this->compress = false;
 
3624
                }
 
3625
        }
 
3626
 
 
3627
        /**
 
3628
         * Set flag to force sRGB_IEC61966-2.1 black scaled ICC color profile for the whole document.
 
3629
         * @param $mode (boolean) If true force sRGB output intent.
 
3630
         * @public
 
3631
         * @since 5.9.121 (2011-09-28)
 
3632
         */
 
3633
        public function setSRGBmode($mode=false) {
 
3634
                $this->force_srgb = $mode ? true : false;
 
3635
        }
 
3636
 
 
3637
        /**
 
3638
         * Turn on/off Unicode mode for document information dictionary (meta tags).
 
3639
         * This has effect only when unicode mode is set to false.
 
3640
         * @param $unicode (boolean) if true set the meta information in Unicode
 
3641
         * @since 5.9.027 (2010-12-01)
 
3642
         * @public
 
3643
         */
 
3644
        public function SetDocInfoUnicode($unicode=true) {
 
3645
                $this->docinfounicode = $unicode ? true : false;
 
3646
        }
 
3647
 
 
3648
        /**
 
3649
         * Defines the title of the document.
 
3650
         * @param $title (string) The title.
 
3651
         * @public
 
3652
         * @since 1.2
 
3653
         * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
 
3654
         */
 
3655
        public function SetTitle($title) {
 
3656
                $this->title = $title;
 
3657
        }
 
3658
 
 
3659
        /**
 
3660
         * Defines the subject of the document.
 
3661
         * @param $subject (string) The subject.
 
3662
         * @public
 
3663
         * @since 1.2
 
3664
         * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
 
3665
         */
 
3666
        public function SetSubject($subject) {
 
3667
                $this->subject = $subject;
 
3668
        }
 
3669
 
 
3670
        /**
 
3671
         * Defines the author of the document.
 
3672
         * @param $author (string) The name of the author.
 
3673
         * @public
 
3674
         * @since 1.2
 
3675
         * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
 
3676
         */
 
3677
        public function SetAuthor($author) {
 
3678
                $this->author = $author;
 
3679
        }
 
3680
 
 
3681
        /**
 
3682
         * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
 
3683
         * @param $keywords (string) The list of keywords.
 
3684
         * @public
 
3685
         * @since 1.2
 
3686
         * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
 
3687
         */
 
3688
        public function SetKeywords($keywords) {
 
3689
                $this->keywords = $keywords;
 
3690
        }
 
3691
 
 
3692
        /**
 
3693
         * Defines the creator of the document. This is typically the name of the application that generates the PDF.
 
3694
         * @param $creator (string) The name of the creator.
 
3695
         * @public
 
3696
         * @since 1.2
 
3697
         * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
 
3698
         */
 
3699
        public function SetCreator($creator) {
 
3700
                $this->creator = $creator;
 
3701
        }
 
3702
 
 
3703
        /**
 
3704
         * 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.
 
3705
         * 2004-06-11 :: Nicola Asuni : changed bold tag with strong
 
3706
         * @param $msg (string) The error message
 
3707
         * @public
 
3708
         * @since 1.0
 
3709
         */
 
3710
        public function Error($msg) {
 
3711
                // unset all class variables
 
3712
                $this->_destroy(true);
 
3713
                // exit program and print error
 
3714
                die('<strong>TCPDF ERROR: </strong>'.$msg);
 
3715
        }
 
3716
 
 
3717
        /**
 
3718
         * This method begins the generation of the PDF document.
 
3719
         * It is not necessary to call it explicitly because AddPage() does it automatically.
 
3720
         * Note: no page is created by this method
 
3721
         * @public
 
3722
         * @since 1.0
 
3723
         * @see AddPage(), Close()
 
3724
         */
 
3725
        public function Open() {
 
3726
                $this->state = 1;
 
3727
        }
 
3728
 
 
3729
        /**
 
3730
         * Terminates the PDF document.
 
3731
         * It is not necessary to call this method explicitly because Output() does it automatically.
 
3732
         * If the document contains no page, AddPage() is called to prevent from getting an invalid document.
 
3733
         * @public
 
3734
         * @since 1.0
 
3735
         * @see Open(), Output()
 
3736
         */
 
3737
        public function Close() {
 
3738
                if ($this->state == 3) {
 
3739
                        return;
 
3740
                }
 
3741
                if ($this->page == 0) {
 
3742
                        $this->AddPage();
 
3743
                }
 
3744
                $this->endLayer();
 
3745
                // save current graphic settings
 
3746
                $gvars = $this->getGraphicVars();
 
3747
                $this->setEqualColumns();
 
3748
                $this->lastpage(true);
 
3749
                $this->SetAutoPageBreak(false);
 
3750
                $this->x = 0;
 
3751
                $this->y = $this->h - (1 / $this->k);
 
3752
                $this->lMargin = 0;
 
3753
                $this->_out('q');
 
3754
                $this->SetFont('helvetica', '', 1);
 
3755
                $this->setTextRenderingMode(0, false, false);
 
3756
                $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";
 
3757
                $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
 
3758
                $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
 
3759
                $this->_out('Q');
 
3760
                // restore graphic settings
 
3761
                $this->setGraphicVars($gvars);
 
3762
                // close page
 
3763
                $this->endPage();
 
3764
                // close document
 
3765
                $this->_enddoc();
 
3766
                // unset all class variables (except critical ones)
 
3767
                $this->_destroy(false);
 
3768
        }
 
3769
 
 
3770
        /**
 
3771
         * Move pointer at the specified document page and update page dimensions.
 
3772
         * @param $pnum (int) page number (1 ... numpages)
 
3773
         * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
 
3774
         * @public
 
3775
         * @since 2.1.000 (2008-01-07)
 
3776
         * @see getPage(), lastpage(), getNumPages()
 
3777
         */
 
3778
        public function setPage($pnum, $resetmargins=false) {
 
3779
                if (($pnum == $this->page) AND ($this->state == 2)) {
 
3780
                        return;
 
3781
                }
 
3782
                if (($pnum > 0) AND ($pnum <= $this->numpages)) {
 
3783
                        $this->state = 2;
 
3784
                        // save current graphic settings
 
3785
                        //$gvars = $this->getGraphicVars();
 
3786
                        $oldpage = $this->page;
 
3787
                        $this->page = $pnum;
 
3788
                        $this->wPt = $this->pagedim[$this->page]['w'];
 
3789
                        $this->hPt = $this->pagedim[$this->page]['h'];
 
3790
                        $this->w = $this->pagedim[$this->page]['wk'];
 
3791
                        $this->h = $this->pagedim[$this->page]['hk'];
 
3792
                        $this->tMargin = $this->pagedim[$this->page]['tm'];
 
3793
                        $this->bMargin = $this->pagedim[$this->page]['bm'];
 
3794
                        $this->original_lMargin = $this->pagedim[$this->page]['olm'];
 
3795
                        $this->original_rMargin = $this->pagedim[$this->page]['orm'];
 
3796
                        $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
 
3797
                        $this->CurOrientation = $this->pagedim[$this->page]['or'];
 
3798
                        $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
 
3799
                        // restore graphic settings
 
3800
                        //$this->setGraphicVars($gvars);
 
3801
                        if ($resetmargins) {
 
3802
                                $this->lMargin = $this->pagedim[$this->page]['olm'];
 
3803
                                $this->rMargin = $this->pagedim[$this->page]['orm'];
 
3804
                                $this->SetY($this->tMargin);
 
3805
                        } else {
 
3806
                                // account for booklet mode
 
3807
                                if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
 
3808
                                        $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
 
3809
                                        $this->lMargin += $deltam;
 
3810
                                        $this->rMargin -= $deltam;
 
3811
                                }
 
3812
                        }
 
3813
                } else {
 
3814
                        $this->Error('Wrong page number on setPage() function: '.$pnum);
 
3815
                }
 
3816
        }
 
3817
 
 
3818
        /**
 
3819
         * Reset pointer to the last document page.
 
3820
         * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
 
3821
         * @public
 
3822
         * @since 2.0.000 (2008-01-04)
 
3823
         * @see setPage(), getPage(), getNumPages()
 
3824
         */
 
3825
        public function lastPage($resetmargins=false) {
 
3826
                $this->setPage($this->getNumPages(), $resetmargins);
 
3827
        }
 
3828
 
 
3829
        /**
 
3830
         * Get current document page number.
 
3831
         * @return int page number
 
3832
         * @public
 
3833
         * @since 2.1.000 (2008-01-07)
 
3834
         * @see setPage(), lastpage(), getNumPages()
 
3835
         */
 
3836
        public function getPage() {
 
3837
                return $this->page;
 
3838
        }
 
3839
 
 
3840
        /**
 
3841
         * Get the total number of insered pages.
 
3842
         * @return int number of pages
 
3843
         * @public
 
3844
         * @since 2.1.000 (2008-01-07)
 
3845
         * @see setPage(), getPage(), lastpage()
 
3846
         */
 
3847
        public function getNumPages() {
 
3848
                return $this->numpages;
 
3849
        }
 
3850
 
 
3851
        /**
 
3852
         * Adds a new TOC (Table Of Content) page to the document.
 
3853
         * @param $orientation (string) page orientation.
 
3854
         * @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().
 
3855
         * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
 
3856
         * @public
 
3857
         * @since 5.0.001 (2010-05-06)
 
3858
         * @see AddPage(), startPage(), endPage(), endTOCPage()
 
3859
         */
 
3860
        public function addTOCPage($orientation='', $format='', $keepmargins=false) {
 
3861
                $this->AddPage($orientation, $format, $keepmargins, true);
 
3862
        }
 
3863
 
 
3864
        /**
 
3865
         * Terminate the current TOC (Table Of Content) page
 
3866
         * @public
 
3867
         * @since 5.0.001 (2010-05-06)
 
3868
         * @see AddPage(), startPage(), endPage(), addTOCPage()
 
3869
         */
 
3870
        public function endTOCPage() {
 
3871
                $this->endPage(true);
 
3872
        }
 
3873
 
 
3874
        /**
 
3875
         * 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).
 
3876
         * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards.
 
3877
         * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
 
3878
         * @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().
 
3879
         * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
 
3880
         * @param $tocpage (boolean) if true set the tocpage state to true (the added page will be used to display Table Of Content).
 
3881
         * @public
 
3882
         * @since 1.0
 
3883
         * @see startPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
 
3884
         */
 
3885
        public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
 
3886
                if ($this->inxobj) {
 
3887
                        // we are inside an XObject template
 
3888
                        return;
 
3889
                }
 
3890
                if (!isset($this->original_lMargin) OR $keepmargins) {
 
3891
                        $this->original_lMargin = $this->lMargin;
 
3892
                }
 
3893
                if (!isset($this->original_rMargin) OR $keepmargins) {
 
3894
                        $this->original_rMargin = $this->rMargin;
 
3895
                }
 
3896
                // terminate previous page
 
3897
                $this->endPage();
 
3898
                // start new page
 
3899
                $this->startPage($orientation, $format, $tocpage);
 
3900
        }
 
3901
 
 
3902
        /**
 
3903
         * Terminate the current page
 
3904
         * @param $tocpage (boolean) if true set the tocpage state to false (end the page used to display Table Of Content).
 
3905
         * @public
 
3906
         * @since 4.2.010 (2008-11-14)
 
3907
         * @see AddPage(), startPage(), addTOCPage(), endTOCPage()
 
3908
         */
 
3909
        public function endPage($tocpage=false) {
 
3910
                // check if page is already closed
 
3911
                if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
 
3912
                        return;
 
3913
                }
 
3914
                // print page footer
 
3915
                $this->setFooter();
 
3916
                // close page
 
3917
                $this->_endpage();
 
3918
                // mark page as closed
 
3919
                $this->pageopen[$this->page] = false;
 
3920
                if ($tocpage) {
 
3921
                        $this->tocpage = false;
 
3922
                }
 
3923
        }
 
3924
 
 
3925
        /**
 
3926
         * Starts a new page to the document. The page must be closed using the endPage() function.
 
3927
         * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
 
3928
         * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
 
3929
         * @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().
 
3930
         * @param $tocpage (boolean) if true the page is designated to contain the Table-Of-Content.
 
3931
         * @since 4.2.010 (2008-11-14)
 
3932
         * @see AddPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
 
3933
         * @public
 
3934
         */
 
3935
        public function startPage($orientation='', $format='', $tocpage=false) {
 
3936
                if ($tocpage) {
 
3937
                        $this->tocpage = true;
 
3938
                }
 
3939
                // move page numbers of documents to be attached
 
3940
                if ($this->tocpage) {
 
3941
                        // move reference to unexistent pages (used for page attachments)
 
3942
                        // adjust outlines
 
3943
                        $tmpoutlines = $this->outlines;
 
3944
                        foreach ($tmpoutlines as $key => $outline) {
 
3945
                                if ($outline['p'] > $this->numpages) {
 
3946
                                        $this->outlines[$key]['p'] = ($outline['p'] + 1);
 
3947
                                }
 
3948
                        }
 
3949
                        // adjust dests
 
3950
                        $tmpdests = $this->dests;
 
3951
                        foreach ($tmpdests as $key => $dest) {
 
3952
                                if ($dest['p'] > $this->numpages) {
 
3953
                                        $this->dests[$key]['p'] = ($dest['p'] + 1);
 
3954
                                }
 
3955
                        }
 
3956
                        // adjust links
 
3957
                        $tmplinks = $this->links;
 
3958
                        foreach ($tmplinks as $key => $link) {
 
3959
                                if ($link[0] > $this->numpages) {
 
3960
                                        $this->links[$key][0] = ($link[0] + 1);
 
3961
                                }
 
3962
                        }
 
3963
                }
 
3964
                if ($this->numpages > $this->page) {
 
3965
                        // this page has been already added
 
3966
                        $this->setPage($this->page + 1);
 
3967
                        $this->SetY($this->tMargin);
 
3968
                        return;
 
3969
                }
 
3970
                // start a new page
 
3971
                if ($this->state == 0) {
 
3972
                        $this->Open();
 
3973
                }
 
3974
                ++$this->numpages;
 
3975
                $this->swapMargins($this->booklet);
 
3976
                // save current graphic settings
 
3977
                $gvars = $this->getGraphicVars();
 
3978
                // start new page
 
3979
                $this->_beginpage($orientation, $format);
 
3980
                // mark page as open
 
3981
                $this->pageopen[$this->page] = true;
 
3982
                // restore graphic settings
 
3983
                $this->setGraphicVars($gvars);
 
3984
                // mark this point
 
3985
                $this->setPageMark();
 
3986
                // print page header
 
3987
                $this->setHeader();
 
3988
                // restore graphic settings
 
3989
                $this->setGraphicVars($gvars);
 
3990
                // mark this point
 
3991
                $this->setPageMark();
 
3992
                // print table header (if any)
 
3993
                $this->setTableHeader();
 
3994
                // set mark for empty page check
 
3995
                $this->emptypagemrk[$this->page]= $this->pagelen[$this->page];
 
3996
        }
 
3997
 
 
3998
        /**
 
3999
         * Set start-writing mark on current page stream used to put borders and fills.
 
4000
         * Borders and fills are always created after content and inserted on the position marked by this method.
 
4001
         * This function must be called after calling Image() function for a background image.
 
4002
         * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
 
4003
         * @public
 
4004
         * @since 4.0.016 (2008-07-30)
 
4005
         */
 
4006
        public function setPageMark() {
 
4007
                $this->intmrk[$this->page] = $this->pagelen[$this->page];
 
4008
                $this->bordermrk[$this->page] = $this->intmrk[$this->page];
 
4009
                $this->setContentMark();
 
4010
        }
 
4011
 
 
4012
        /**
 
4013
         * Set start-writing mark on selected page.
 
4014
         * Borders and fills are always created after content and inserted on the position marked by this method.
 
4015
         * @param $page (int) page number (default is the current page)
 
4016
         * @protected
 
4017
         * @since 4.6.021 (2009-07-20)
 
4018
         */
 
4019
        protected function setContentMark($page=0) {
 
4020
                if ($page <= 0) {
 
4021
                        $page = $this->page;
 
4022
                }
 
4023
                if (isset($this->footerlen[$page])) {
 
4024
                        $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
 
4025
                } else {
 
4026
                        $this->cntmrk[$page] = $this->pagelen[$page];
 
4027
                }
 
4028
        }
 
4029
 
 
4030
        /**
 
4031
         * Set header data.
 
4032
         * @param $ln (string) header image logo
 
4033
         * @param $lw (string) header image logo width in mm
 
4034
         * @param $ht (string) string to print as title on document header
 
4035
         * @param $hs (string) string to print on document header
 
4036
         * @public
 
4037
         */
 
4038
        public function setHeaderData($ln='', $lw=0, $ht='', $hs='') {
 
4039
                $this->header_logo = $ln;
 
4040
                $this->header_logo_width = $lw;
 
4041
                $this->header_title = $ht;
 
4042
                $this->header_string = $hs;
 
4043
        }
 
4044
 
 
4045
        /**
 
4046
         * Returns header data:
 
4047
         * <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>
 
4048
         * @return array()
 
4049
         * @public
 
4050
         * @since 4.0.012 (2008-07-24)
 
4051
         */
 
4052
        public function getHeaderData() {
 
4053
                $ret = array();
 
4054
                $ret['logo'] = $this->header_logo;
 
4055
                $ret['logo_width'] = $this->header_logo_width;
 
4056
                $ret['title'] = $this->header_title;
 
4057
                $ret['string'] = $this->header_string;
 
4058
                return $ret;
 
4059
        }
 
4060
 
 
4061
        /**
 
4062
         * Set header margin.
 
4063
         * (minimum distance between header and top page margin)
 
4064
         * @param $hm (int) distance in user units
 
4065
         * @public
 
4066
         */
 
4067
        public function setHeaderMargin($hm=10) {
 
4068
                $this->header_margin = $hm;
 
4069
        }
 
4070
 
 
4071
        /**
 
4072
         * Returns header margin in user units.
 
4073
         * @return float
 
4074
         * @since 4.0.012 (2008-07-24)
 
4075
         * @public
 
4076
         */
 
4077
        public function getHeaderMargin() {
 
4078
                return $this->header_margin;
 
4079
        }
 
4080
 
 
4081
        /**
 
4082
         * Set footer margin.
 
4083
         * (minimum distance between footer and bottom page margin)
 
4084
         * @param $fm (int) distance in user units
 
4085
         * @public
 
4086
         */
 
4087
        public function setFooterMargin($fm=10) {
 
4088
                $this->footer_margin = $fm;
 
4089
        }
 
4090
 
 
4091
        /**
 
4092
         * Returns footer margin in user units.
 
4093
         * @return float
 
4094
         * @since 4.0.012 (2008-07-24)
 
4095
         * @public
 
4096
         */
 
4097
        public function getFooterMargin() {
 
4098
                return $this->footer_margin;
 
4099
        }
 
4100
        /**
 
4101
         * Set a flag to print page header.
 
4102
         * @param $val (boolean) set to true to print the page header (default), false otherwise.
 
4103
         * @public
 
4104
         */
 
4105
        public function setPrintHeader($val=true) {
 
4106
                $this->print_header = $val ? true : false;
 
4107
        }
 
4108
 
 
4109
        /**
 
4110
         * Set a flag to print page footer.
 
4111
         * @param $val (boolean) set to true to print the page footer (default), false otherwise.
 
4112
         * @public
 
4113
         */
 
4114
        public function setPrintFooter($val=true) {
 
4115
                $this->print_footer = $val ? true : false;
 
4116
        }
 
4117
 
 
4118
        /**
 
4119
         * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
 
4120
         * @return float
 
4121
         * @public
 
4122
         */
 
4123
        public function getImageRBX() {
 
4124
                return $this->img_rb_x;
 
4125
        }
 
4126
 
 
4127
        /**
 
4128
         * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
 
4129
         * @return float
 
4130
         * @public
 
4131
         */
 
4132
        public function getImageRBY() {
 
4133
                return $this->img_rb_y;
 
4134
        }
 
4135
 
 
4136
        /**
 
4137
         * Reset the xobject template used by Header() method.
 
4138
         * @public
 
4139
         */
 
4140
        public function resetHeaderTemplate() {
 
4141
                $this->header_xobjid = -1;
 
4142
        }
 
4143
 
 
4144
        /**
 
4145
         * Set a flag to automatically reset the xobject template used by Header() method at each page.
 
4146
         * @param $val (boolean) set to true to reset Header xobject template at each page, false otherwise.
 
4147
         * @public
 
4148
         */
 
4149
        public function setHeaderTemplateAutoreset($val=true) {
 
4150
                $this->header_xobj_autoreset = $val ? true : false;
 
4151
        }
 
4152
 
 
4153
        /**
 
4154
         * This method is used to render the page header.
 
4155
         * It is automatically called by AddPage() and could be overwritten in your own inherited class.
 
4156
         * @public
 
4157
         */
 
4158
        public function Header() {
 
4159
                if ($this->header_xobjid < 0) {
 
4160
                        // start a new XObject Template
 
4161
                        $this->header_xobjid = $this->startTemplate($this->w, $this->tMargin);
 
4162
                        $headerfont = $this->getHeaderFont();
 
4163
                        $headerdata = $this->getHeaderData();
 
4164
                        $this->y = $this->header_margin;
 
4165
                        if ($this->rtl) {
 
4166
                                $this->x = $this->w - $this->original_rMargin;
 
4167
                        } else {
 
4168
                                $this->x = $this->original_lMargin;
 
4169
                        }
 
4170
                        if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
 
4171
                                $imgtype = $this->getImageFileType(K_PATH_IMAGES.$headerdata['logo']);
 
4172
                                if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
 
4173
                                        $this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
 
4174
                                } elseif ($imgtype == 'svg') {
 
4175
                                        $this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
 
4176
                                } else {
 
4177
                                        $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
 
4178
                                }
 
4179
                                $imgy = $this->getImageRBY();
 
4180
                        } else {
 
4181
                                $imgy = $this->y;
 
4182
                        }
 
4183
                        $cell_height = round(($this->cell_height_ratio * $headerfont[2]) / $this->k, 2);
 
4184
                        // set starting margin for text data cell
 
4185
                        if ($this->getRTL()) {
 
4186
                                $header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1);
 
4187
                        } else {
 
4188
                                $header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1);
 
4189
                        }
 
4190
                        $cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1);
 
4191
                        $this->SetTextColor(0, 0, 0);
 
4192
                        // header title
 
4193
                        $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
 
4194
                        $this->SetX($header_x);
 
4195
                        $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
 
4196
                        // header string
 
4197
                        $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
 
4198
                        $this->SetX($header_x);
 
4199
                        $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false);
 
4200
                        // print an ending header line
 
4201
                        $this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
 
4202
                        $this->SetY((2.835 / $this->k) + max($imgy, $this->y));
 
4203
                        if ($this->rtl) {
 
4204
                                $this->SetX($this->original_rMargin);
 
4205
                        } else {
 
4206
                                $this->SetX($this->original_lMargin);
 
4207
                        }
 
4208
                        $this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C');
 
4209
                        $this->endTemplate();
 
4210
                }
 
4211
                // print header template
 
4212
                $x = 0;
 
4213
                $dx = 0;
 
4214
                if ($this->booklet AND (($this->page % 2) == 0)) {
 
4215
                        // adjust margins for booklet mode
 
4216
                        $dx = ($this->original_lMargin - $this->original_rMargin);
 
4217
                }
 
4218
                if ($this->rtl) {
 
4219
                        $x = $this->w + $dx;
 
4220
                } else {
 
4221
                        $x = 0 + $dx;
 
4222
                }
 
4223
                $this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false);
 
4224
                if ($this->header_xobj_autoreset) {
 
4225
                        // reset header xobject template at each page
 
4226
                        $this->header_xobjid = -1;
 
4227
                }
 
4228
        }
 
4229
 
 
4230
        /**
 
4231
         * This method is used to render the page footer.
 
4232
         * It is automatically called by AddPage() and could be overwritten in your own inherited class.
 
4233
         * @public
 
4234
         */
 
4235
        public function Footer() {
 
4236
                $cur_y = $this->y;
 
4237
                $this->SetTextColor(0, 0, 0);
 
4238
                //set style for cell border
 
4239
                $line_width = 0.85 / $this->k;
 
4240
                $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
 
4241
                //print document barcode
 
4242
                $barcode = $this->getBarcode();
 
4243
                if (!empty($barcode)) {
 
4244
                        $this->Ln($line_width);
 
4245
                        $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3);
 
4246
                        $style = array(
 
4247
                                'position' => $this->rtl?'R':'L',
 
4248
                                'align' => $this->rtl?'R':'L',
 
4249
                                'stretch' => false,
 
4250
                                'fitwidth' => true,
 
4251
                                'cellfitalign' => '',
 
4252
                                'border' => false,
 
4253
                                'padding' => 0,
 
4254
                                'fgcolor' => array(0,0,0),
 
4255
                                'bgcolor' => false,
 
4256
                                'text' => false
 
4257
                        );
 
4258
                        $this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, '');
 
4259
                }
 
4260
                if (empty($this->pagegroups)) {
 
4261
                        $pagenumtxt = $this->l['w_page'].' '.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
 
4262
                } else {
 
4263
                        $pagenumtxt = $this->l['w_page'].' '.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
 
4264
                }
 
4265
                $this->SetY($cur_y);
 
4266
                //Print page number
 
4267
                if ($this->getRTL()) {
 
4268
                        $this->SetX($this->original_rMargin);
 
4269
                        $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
 
4270
                } else {
 
4271
                        $this->SetX($this->original_lMargin);
 
4272
                        $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R');
 
4273
                }
 
4274
        }
 
4275
 
 
4276
        /**
 
4277
         * This method is used to render the page header.
 
4278
         * @protected
 
4279
         * @since 4.0.012 (2008-07-24)
 
4280
         */
 
4281
        protected function setHeader() {
 
4282
                if (!$this->print_header) {
 
4283
                        return;
 
4284
                }
 
4285
                $this->InHeader = true;
 
4286
                $this->setGraphicVars($this->default_graphic_vars);
 
4287
                $temp_thead = $this->thead;
 
4288
                $temp_theadMargins = $this->theadMargins;
 
4289
                $lasth = $this->lasth;
 
4290
                $this->_out('q');
 
4291
                $this->rMargin = $this->original_rMargin;
 
4292
                $this->lMargin = $this->original_lMargin;
 
4293
                $this->SetCellPadding(0);
 
4294
                //set current position
 
4295
                if ($this->rtl) {
 
4296
                        $this->SetXY($this->original_rMargin, $this->header_margin);
 
4297
                } else {
 
4298
                        $this->SetXY($this->original_lMargin, $this->header_margin);
 
4299
                }
 
4300
                $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
 
4301
                $this->Header();
 
4302
                //restore position
 
4303
                if ($this->rtl) {
 
4304
                        $this->SetXY($this->original_rMargin, $this->tMargin);
 
4305
                } else {
 
4306
                        $this->SetXY($this->original_lMargin, $this->tMargin);
 
4307
                }
 
4308
                $this->_out('Q');
 
4309
                $this->lasth = $lasth;
 
4310
                $this->thead = $temp_thead;
 
4311
                $this->theadMargins = $temp_theadMargins;
 
4312
                $this->newline = false;
 
4313
                $this->InHeader = false;
 
4314
        }
 
4315
 
 
4316
        /**
 
4317
         * This method is used to render the page footer.
 
4318
         * @protected
 
4319
         * @since 4.0.012 (2008-07-24)
 
4320
         */
 
4321
        protected function setFooter() {
 
4322
                //Page footer
 
4323
                $this->InFooter = true;
 
4324
                // save current graphic settings
 
4325
                $gvars = $this->getGraphicVars();
 
4326
                // mark this point
 
4327
                $this->footerpos[$this->page] = $this->pagelen[$this->page];
 
4328
                $this->_out("\n");
 
4329
                if ($this->print_footer) {
 
4330
                        $this->setGraphicVars($this->default_graphic_vars);
 
4331
                        $this->current_column = 0;
 
4332
                        $this->num_columns = 1;
 
4333
                        $temp_thead = $this->thead;
 
4334
                        $temp_theadMargins = $this->theadMargins;
 
4335
                        $lasth = $this->lasth;
 
4336
                        $this->_out('q');
 
4337
                        $this->rMargin = $this->original_rMargin;
 
4338
                        $this->lMargin = $this->original_lMargin;
 
4339
                        $this->SetCellPadding(0);
 
4340
                        //set current position
 
4341
                        $footer_y = $this->h - $this->footer_margin;
 
4342
                        if ($this->rtl) {
 
4343
                                $this->SetXY($this->original_rMargin, $footer_y);
 
4344
                        } else {
 
4345
                                $this->SetXY($this->original_lMargin, $footer_y);
 
4346
                        }
 
4347
                        $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
 
4348
                        $this->Footer();
 
4349
                        //restore position
 
4350
                        if ($this->rtl) {
 
4351
                                $this->SetXY($this->original_rMargin, $this->tMargin);
 
4352
                        } else {
 
4353
                                $this->SetXY($this->original_lMargin, $this->tMargin);
 
4354
                        }
 
4355
                        $this->_out('Q');
 
4356
                        $this->lasth = $lasth;
 
4357
                        $this->thead = $temp_thead;
 
4358
                        $this->theadMargins = $temp_theadMargins;
 
4359
                }
 
4360
                // restore graphic settings
 
4361
                $this->setGraphicVars($gvars);
 
4362
                $this->current_column = $gvars['current_column'];
 
4363
                $this->num_columns = $gvars['num_columns'];
 
4364
                // calculate footer length
 
4365
                $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
 
4366
                $this->InFooter = false;
 
4367
        }
 
4368
 
 
4369
        /**
 
4370
         * Check if we are on the page body (excluding page header and footer).
 
4371
         * @return true if we are not in page header nor in page footer, false otherwise.
 
4372
         * @protected
 
4373
         * @since 5.9.091 (2011-06-15)
 
4374
         */
 
4375
        protected function inPageBody() {
 
4376
                return (($this->InHeader === false) AND ($this->InFooter === false));
 
4377
        }
 
4378
 
 
4379
        /**
 
4380
         * This method is used to render the table header on new page (if any).
 
4381
         * @protected
 
4382
         * @since 4.5.030 (2009-03-25)
 
4383
         */
 
4384
        protected function setTableHeader() {
 
4385
                if ($this->num_columns > 1) {
 
4386
                        // multi column mode
 
4387
                        return;
 
4388
                }
 
4389
                if (isset($this->theadMargins['top'])) {
 
4390
                        // restore the original top-margin
 
4391
                        $this->tMargin = $this->theadMargins['top'];
 
4392
                        $this->pagedim[$this->page]['tm'] = $this->tMargin;
 
4393
                        $this->y = $this->tMargin;
 
4394
                }
 
4395
                if (!$this->empty_string($this->thead) AND (!$this->inthead)) {
 
4396
                        // set margins
 
4397
                        $prev_lMargin = $this->lMargin;
 
4398
                        $prev_rMargin = $this->rMargin;
 
4399
                        $prev_cell_padding = $this->cell_padding;
 
4400
                        $this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']);
 
4401
                        $this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']);
 
4402
                        $this->cell_padding = $this->theadMargins['cell_padding'];
 
4403
                        if ($this->rtl) {
 
4404
                                $this->x = $this->w - $this->rMargin;
 
4405
                        } else {
 
4406
                                $this->x = $this->lMargin;
 
4407
                        }
 
4408
                        // account for special "cell" mode
 
4409
                        if ($this->theadMargins['cell']) {
 
4410
                                if ($this->rtl) {
 
4411
                                        $this->x -= $this->cell_padding['R'];
 
4412
                                } else {
 
4413
                                        $this->x += $this->cell_padding['L'];
 
4414
                                }
 
4415
                        }
 
4416
                        // print table header
 
4417
                        $this->writeHTML($this->thead, false, false, false, false, '');
 
4418
                        // set new top margin to skip the table headers
 
4419
                        if (!isset($this->theadMargins['top'])) {
 
4420
                                $this->theadMargins['top'] = $this->tMargin;
 
4421
                        }
 
4422
                        // store end of header position
 
4423
                        if (!isset($this->columns[0]['th'])) {
 
4424
                                $this->columns[0]['th'] = array();
 
4425
                        }
 
4426
                        $this->columns[0]['th']['\''.$this->page.'\''] = $this->y;
 
4427
                        $this->tMargin = $this->y;
 
4428
                        $this->pagedim[$this->page]['tm'] = $this->tMargin;
 
4429
                        $this->lasth = 0;
 
4430
                        $this->lMargin = $prev_lMargin;
 
4431
                        $this->rMargin = $prev_rMargin;
 
4432
                        $this->cell_padding = $prev_cell_padding;
 
4433
                }
 
4434
        }
 
4435
 
 
4436
        /**
 
4437
         * Returns the current page number.
 
4438
         * @return int page number
 
4439
         * @public
 
4440
         * @since 1.0
 
4441
         * @see getAliasNbPages()
 
4442
         */
 
4443
        public function PageNo() {
 
4444
                return $this->page;
 
4445
        }
 
4446
 
 
4447
        /**
 
4448
         * Defines a new spot color.
 
4449
         * It can be expressed in RGB components or gray scale.
 
4450
         * The method can be called before the first page is created and the value is retained from page to page.
 
4451
         * @param $name (string) Full name of the spot color.
 
4452
         * @param $c (float) Cyan color for CMYK. Value between 0 and 100.
 
4453
         * @param $m (float) Magenta color for CMYK. Value between 0 and 100.
 
4454
         * @param $y (float) Yellow color for CMYK. Value between 0 and 100.
 
4455
         * @param $k (float) Key (Black) color for CMYK. Value between 0 and 100.
 
4456
         * @public
 
4457
         * @since 4.0.024 (2008-09-12)
 
4458
         * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
 
4459
         */
 
4460
        public function AddSpotColor($name, $c, $m, $y, $k) {
 
4461
                if (!isset($this->spot_colors[$name])) {
 
4462
                        $i = (1 + count($this->spot_colors));
 
4463
                        $this->spot_colors[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i);
 
4464
                }
 
4465
        }
 
4466
 
 
4467
        /**
 
4468
         * Return the Spot color array.
 
4469
         * @param $name (string) Name of the spot color.
 
4470
         * @return (array) Spot color array or false if not defined.
 
4471
         * @public
 
4472
         * @since 5.9.125 (2011-10-03)
 
4473
         */
 
4474
        public function getSpotColor($name) {
 
4475
                if (isset($this->spot_colors[$name])) {
 
4476
                        return $this->spot_colors[$name];
 
4477
                }
 
4478
                $color = preg_replace('/[\s]*/', '', $name); // remove extra spaces
 
4479
                $color = strtolower($color);
 
4480
                if (isset($this->spotcolor[$color])) {
 
4481
                        $this->AddSpotColor($this->spotcolor[$color][4], $this->spotcolor[$color][0], $this->spotcolor[$color][1], $this->spotcolor[$color][2], $this->spotcolor[$color][3]);
 
4482
                        return $this->spot_colors[$this->spotcolor[$color][4]];
 
4483
                }
 
4484
                return false;
 
4485
        }
 
4486
 
 
4487
        /**
 
4488
         * Set the spot color for the specified type ('draw', 'fill', 'text').
 
4489
         * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
 
4490
         * @param $name (string) Name of the spot color.
 
4491
         * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
 
4492
         * @return (string) PDF color command.
 
4493
         * @public
 
4494
         * @since 5.9.125 (2011-10-03)
 
4495
         */
 
4496
        public function setSpotColor($type, $name, $tint=100) {
 
4497
                $spotcolor = $this->getSpotColor($name);
 
4498
                if ($spotcolor === false) {
 
4499
                        $this->Error('Undefined spot color: '.$name.', you must add it on the spotcolors.php file.');
 
4500
                }
 
4501
                $tint = (max(0, min(100, $tint)) / 100);
 
4502
                $pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']);
 
4503
                switch ($type) {
 
4504
                        case 'draw': {
 
4505
                                $pdfcolor .= sprintf('CS %.3F SCN', $tint);
 
4506
                                $this->DrawColor = $pdfcolor;
 
4507
                                $this->strokecolor = $spotcolor;
 
4508
                                break;
 
4509
                        }
 
4510
                        case 'fill': {
 
4511
                                $pdfcolor .= sprintf('cs %.3F scn', $tint);
 
4512
                                $this->FillColor = $pdfcolor;
 
4513
                                $this->bgcolor = $spotcolor;
 
4514
                                break;
 
4515
                        }
 
4516
                        case 'text': {
 
4517
                                $pdfcolor .= sprintf('cs %.3F scn', $tint);
 
4518
                                $this->TextColor = $pdfcolor;
 
4519
                                $this->fgcolor = $spotcolor;
 
4520
                                break;
 
4521
                        }
 
4522
                }
 
4523
                $this->ColorFlag = ($this->FillColor != $this->TextColor);
 
4524
                if ($this->page > 0) {
 
4525
                        $this->_out($pdfcolor);
 
4526
                }
 
4527
                if ($this->inxobj) {
 
4528
                        // we are inside an XObject template
 
4529
                        $this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name];
 
4530
                }
 
4531
                return $pdfcolor;
 
4532
        }
 
4533
 
 
4534
        /**
 
4535
         * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
 
4536
         * @param $name (string) Name of the spot color.
 
4537
         * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
 
4538
         * @public
 
4539
         * @since 4.0.024 (2008-09-12)
 
4540
         * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
 
4541
         */
 
4542
        public function SetDrawSpotColor($name, $tint=100) {
 
4543
                $this->setSpotColor('draw', $name, $tint);
 
4544
        }
 
4545
 
 
4546
        /**
 
4547
         * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
 
4548
         * @param $name (string) Name of the spot color.
 
4549
         * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
 
4550
         * @public
 
4551
         * @since 4.0.024 (2008-09-12)
 
4552
         * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
 
4553
         */
 
4554
        public function SetFillSpotColor($name, $tint=100) {
 
4555
                $this->setSpotColor('fill', $name, $tint);
 
4556
        }
 
4557
 
 
4558
        /**
 
4559
         * Defines the spot color used for text.
 
4560
         * @param $name (string) Name of the spot color.
 
4561
         * @param $tint (int) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
 
4562
         * @public
 
4563
         * @since 4.0.024 (2008-09-12)
 
4564
         * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
 
4565
         */
 
4566
        public function SetTextSpotColor($name, $tint=100) {
 
4567
                $this->setSpotColor('text', $name, $tint);
 
4568
        }
 
4569
 
 
4570
        /**
 
4571
         * Set the color array for the specified type ('draw', 'fill', 'text').
 
4572
         * It can be expressed in RGB, CMYK or GRAY SCALE components.
 
4573
         * The method can be called before the first page is created and the value is retained from page to page.
 
4574
         * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
 
4575
         * @param $color (array) Array of colors (1=gray, 3=RGB, 4=CMYK or 5=spotcolor=CMYK+name values).
 
4576
         * @param $ret (boolean) If true do not send the PDF command.
 
4577
         * @return (string) The PDF command or empty string.
 
4578
         * @public
 
4579
         * @since 3.1.000 (2008-06-11)
 
4580
         */
 
4581
        public function setColorArray($type, $color, $ret=false) {
 
4582
                if (is_array($color)) {
 
4583
                        $color = array_values($color);
 
4584
                        // component: grey, RGB red or CMYK cyan
 
4585
                        $c = isset($color[0]) ? $color[0] : -1;
 
4586
                        // component: RGB green or CMYK magenta
 
4587
                        $m = isset($color[1]) ? $color[1] : -1;
 
4588
                        // component: RGB blue or CMYK yellow
 
4589
                        $y = isset($color[2]) ? $color[2] : -1;
 
4590
                        // component: CMYK black
 
4591
                        $k = isset($color[3]) ? $color[3] : -1;
 
4592
                        // color name
 
4593
                        $name = isset($color[4]) ? $color[4] : '';
 
4594
                        if ($c >= 0) {
 
4595
                                return $this->setColor($type, $c, $m, $y, $k, $ret, $name);
 
4596
                        }
 
4597
                }
 
4598
                return '';
 
4599
        }
 
4600
 
 
4601
        /**
 
4602
         * Defines the color used for all drawing operations (lines, rectangles and cell borders).
 
4603
         * It can be expressed in RGB, CMYK or GRAY SCALE components.
 
4604
         * The method can be called before the first page is created and the value is retained from page to page.
 
4605
         * @param $color (array) Array of colors (1, 3 or 4 values).
 
4606
         * @param $ret (boolean) If true do not send the PDF command.
 
4607
         * @return string the PDF command
 
4608
         * @public
 
4609
         * @since 3.1.000 (2008-06-11)
 
4610
         * @see SetDrawColor()
 
4611
         */
 
4612
        public function SetDrawColorArray($color, $ret=false) {
 
4613
                return $this->setColorArray('draw', $color, $ret);
 
4614
        }
 
4615
 
 
4616
        /**
 
4617
         * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
 
4618
         * It can be expressed in RGB, CMYK or GRAY SCALE components.
 
4619
         * The method can be called before the first page is created and the value is retained from page to page.
 
4620
         * @param $color (array) Array of colors (1, 3 or 4 values).
 
4621
         * @param $ret (boolean) If true do not send the PDF command.
 
4622
         * @public
 
4623
         * @since 3.1.000 (2008-6-11)
 
4624
         * @see SetFillColor()
 
4625
         */
 
4626
        public function SetFillColorArray($color, $ret=false) {
 
4627
                return $this->setColorArray('fill', $color, $ret);
 
4628
        }
 
4629
 
 
4630
        /**
 
4631
         * Defines the color used for text. It can be expressed in RGB components or gray scale.
 
4632
         * The method can be called before the first page is created and the value is retained from page to page.
 
4633
         * @param $color (array) Array of colors (1, 3 or 4 values).
 
4634
         * @param $ret (boolean) If true do not send the PDF command.
 
4635
         * @public
 
4636
         * @since 3.1.000 (2008-6-11)
 
4637
         * @see SetFillColor()
 
4638
         */
 
4639
        public function SetTextColorArray($color, $ret=false) {
 
4640
                return $this->setColorArray('text', $color, $ret);
 
4641
        }
 
4642
 
 
4643
        /**
 
4644
         * Defines the color used by the specified type ('draw', 'fill', 'text').
 
4645
         * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
 
4646
         * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
 
4647
         * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
 
4648
         * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
 
4649
         * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
 
4650
         * @param $ret (boolean) If true do not send the command.
 
4651
         * @param $name (string) spot color name (if any)
 
4652
         * @return (string) The PDF command or empty string.
 
4653
         * @public
 
4654
         * @since 5.9.125 (2011-10-03)
 
4655
         */
 
4656
        public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
 
4657
                // set default values
 
4658
                if (!is_numeric($col1)) {
 
4659
                        $col1 = 0;
 
4660
                }
 
4661
                if (!is_numeric($col2)) {
 
4662
                        $col2 = -1;
 
4663
                }
 
4664
                if (!is_numeric($col3)) {
 
4665
                        $col3 = -1;
 
4666
                }
 
4667
                if (!is_numeric($col4)) {
 
4668
                        $col4 = -1;
 
4669
                }
 
4670
                // set color by case
 
4671
                $suffix = '';
 
4672
                if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
 
4673
                        // Grey scale
 
4674
                        $col1 = max(0, min(255, $col1));
 
4675
                        $intcolor = array('G' => $col1);
 
4676
                        $pdfcolor = sprintf('%.3F ', ($col1 / 255));
 
4677
                        $suffix = 'g';
 
4678
                } elseif ($col4 == -1) {
 
4679
                        // RGB
 
4680
                        $col1 = max(0, min(255, $col1));
 
4681
                        $col2 = max(0, min(255, $col2));
 
4682
                        $col3 = max(0, min(255, $col3));
 
4683
                        $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
 
4684
                        $pdfcolor = sprintf('%.3F %.3F %.3F ', ($col1 / 255), ($col2 / 255), ($col3 / 255));
 
4685
                        $suffix = 'rg';
 
4686
                } else {
 
4687
                        $col1 = max(0, min(100, $col1));
 
4688
                        $col2 = max(0, min(100, $col2));
 
4689
                        $col3 = max(0, min(100, $col3));
 
4690
                        $col4 = max(0, min(100, $col4));
 
4691
                        if (empty($name)) {
 
4692
                                // CMYK
 
4693
                                $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
 
4694
                                $pdfcolor = sprintf('%.3F %.3F %.3F %.3F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
 
4695
                                $suffix = 'k';
 
4696
                        } else {
 
4697
                                // SPOT COLOR
 
4698
                                $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
 
4699
                                $this->AddSpotColor($name, $col1, $col2, $col3, $col4);
 
4700
                                $pdfcolor = $this->setSpotColor($type, $name, 100);
 
4701
                        }
 
4702
                }
 
4703
                switch ($type) {
 
4704
                        case 'draw': {
 
4705
                                $pdfcolor .= strtoupper($suffix);
 
4706
                                $this->DrawColor = $pdfcolor;
 
4707
                                $this->strokecolor = $intcolor;
 
4708
                                break;
 
4709
                        }
 
4710
                        case 'fill': {
 
4711
                                $pdfcolor .= $suffix;
 
4712
                                $this->FillColor = $pdfcolor;
 
4713
                                $this->bgcolor = $intcolor;
 
4714
                                break;
 
4715
                        }
 
4716
                        case 'text': {
 
4717
                                $pdfcolor .= $suffix;
 
4718
                                $this->TextColor = $pdfcolor;
 
4719
                                $this->fgcolor = $intcolor;
 
4720
                                break;
 
4721
                        }
 
4722
                }
 
4723
                $this->ColorFlag = ($this->FillColor != $this->TextColor);
 
4724
                if (($type != 'text') AND ($this->page > 0)) {
 
4725
                        if (!$ret) {
 
4726
                                $this->_out($pdfcolor);
 
4727
                        }
 
4728
                        return $pdfcolor;
 
4729
                }
 
4730
                return '';
 
4731
        }
 
4732
 
 
4733
        /**
 
4734
         * Convert a color array into a string representation.
 
4735
         * @param $c (array) Array of colors.
 
4736
         * @return (string) The color array representation.
 
4737
         * @protected
 
4738
         * @since 5.9.137 (2011-12-01)
 
4739
         */
 
4740
        protected function getColorStringFromArray($c) {
 
4741
                $c = array_values($c);
 
4742
                $color = '[';
 
4743
                switch (count($c)) {
 
4744
                        case 4: {
 
4745
                                // CMYK
 
4746
                                $color .= sprintf('%.3F %.3F %.3F %.3F', (max(0, min(100, floatval($c[0]))) / 100), (max(0, min(100, floatval($c[1]))) / 100), (max(0, min(100, floatval($c[2]))) / 100), (max(0, min(100, floatval($c[3]))) / 100));
 
4747
                                break;
 
4748
                        }
 
4749
                        case 3: {
 
4750
                                // RGB
 
4751
                                $color .= sprintf('%.3F %.3F %.3F', (max(0, min(255, floatval($c[0]))) / 255), (max(0, min(255, floatval($c[1]))) / 255), (max(0, min(255, floatval($c[2]))) / 255));
 
4752
                                break;
 
4753
                        }
 
4754
                        case 1: {
 
4755
                                // grayscale
 
4756
                                $color .= sprintf('%.3F', (max(0, min(255, floatval($c[0]))) / 255));
 
4757
                                break;
 
4758
                        }
 
4759
                }
 
4760
                $color .= ']';
 
4761
                return $color;
 
4762
        }
 
4763
 
 
4764
        /**
 
4765
         * 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.
 
4766
         * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
 
4767
         * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
 
4768
         * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
 
4769
         * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
 
4770
         * @param $ret (boolean) If true do not send the command.
 
4771
         * @param $name (string) spot color name (if any)
 
4772
         * @return string the PDF command
 
4773
         * @public
 
4774
         * @since 1.3
 
4775
         * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
 
4776
         */
 
4777
        public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
 
4778
                return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name);
 
4779
        }
 
4780
 
 
4781
        /**
 
4782
         * 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.
 
4783
         * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
 
4784
         * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
 
4785
         * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
 
4786
         * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
 
4787
         * @param $ret (boolean) If true do not send the command.
 
4788
         * @param $name (string) Spot color name (if any).
 
4789
         * @return (string) The PDF command.
 
4790
         * @public
 
4791
         * @since 1.3
 
4792
         * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
 
4793
         */
 
4794
        public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
 
4795
                return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name);
 
4796
        }
 
4797
 
 
4798
        /**
 
4799
         * 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.
 
4800
         * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
 
4801
         * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
 
4802
         * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
 
4803
         * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
 
4804
         * @param $ret (boolean) If true do not send the command.
 
4805
         * @param $name (string) Spot color name (if any).
 
4806
         * @return (string) Empty string.
 
4807
         * @public
 
4808
         * @since 1.3
 
4809
         * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
 
4810
         */
 
4811
        public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
 
4812
                return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name);
 
4813
        }
 
4814
 
 
4815
        /**
 
4816
         * Returns the length of a string in user unit. A font must be selected.<br>
 
4817
         * @param $s (string) The string whose length is to be computed
 
4818
         * @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.
 
4819
         * @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-trough</li><li>O: overline</li></ul> or any combination. The default value is regular.
 
4820
         * @param $fontsize (float) Font size in points. The default value is the current size.
 
4821
         * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
 
4822
         * @return mixed int total string length or array of characted widths
 
4823
         * @author Nicola Asuni
 
4824
         * @public
 
4825
         * @since 1.2
 
4826
         */
 
4827
        public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
 
4828
                return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize, $getarray);
 
4829
        }
 
4830
 
 
4831
        /**
 
4832
         * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br>
 
4833
         * @param $sa (string) The array of chars whose total length is to be computed
 
4834
         * @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.
 
4835
         * @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 trough</li><li>O: overline</li></ul> or any combination. The default value is regular.
 
4836
         * @param $fontsize (float) Font size in points. The default value is the current size.
 
4837
         * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
 
4838
         * @return mixed int total string length or array of characted widths
 
4839
         * @author Nicola Asuni
 
4840
         * @public
 
4841
         * @since 2.4.000 (2008-03-06)
 
4842
         */
 
4843
        public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
 
4844
                // store current values
 
4845
                if (!$this->empty_string($fontname)) {
 
4846
                        $prev_FontFamily = $this->FontFamily;
 
4847
                        $prev_FontStyle = $this->FontStyle;
 
4848
                        $prev_FontSizePt = $this->FontSizePt;
 
4849
                        $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false);
 
4850
                }
 
4851
                // convert UTF-8 array to Latin1 if required
 
4852
                $sa = $this->UTF8ArrToLatin1($sa);
 
4853
                $w = 0; // total width
 
4854
                $wa = array(); // array of characters widths
 
4855
                foreach ($sa as $ck => $char) {
 
4856
                        // character width
 
4857
                        $cw = $this->GetCharWidth($char, isset($sa[($ck + 1)]));
 
4858
                        $wa[] = $cw;
 
4859
                        $w += $cw;
 
4860
                }
 
4861
                // restore previous values
 
4862
                if (!$this->empty_string($fontname)) {
 
4863
                        $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false);
 
4864
                }
 
4865
                if ($getarray) {
 
4866
                        return $wa;
 
4867
                }
 
4868
                return $w;
 
4869
        }
 
4870
 
 
4871
        /**
 
4872
         * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking/kerning).
 
4873
         * @param $char (int) The char code whose length is to be returned
 
4874
         * @param $notlast (boolean) set to false for the latest character on string, true otherwise (default)
 
4875
         * @return float char width
 
4876
         * @author Nicola Asuni
 
4877
         * @public
 
4878
         * @since 2.4.000 (2008-03-06)
 
4879
         */
 
4880
        public function GetCharWidth($char, $notlast=true) {
 
4881
                // get raw width
 
4882
                $chw = $this->getRawCharWidth($char);
 
4883
                if (($this->font_spacing != 0) AND $notlast) {
 
4884
                        // increase/decrease font spacing
 
4885
                        $chw += $this->font_spacing;
 
4886
                }
 
4887
                if ($this->font_stretching != 100) {
 
4888
                        // fixed stretching mode
 
4889
                        $chw *= ($this->font_stretching / 100);
 
4890
                }
 
4891
                return $chw;
 
4892
        }
 
4893
 
 
4894
        /**
 
4895
         * Returns the length of the char in user unit for the current font.
 
4896
         * @param $char (int) The char code whose length is to be returned
 
4897
         * @return float char width
 
4898
         * @author Nicola Asuni
 
4899
         * @public
 
4900
         * @since 5.9.000 (2010-09-28)
 
4901
         */
 
4902
        public function getRawCharWidth($char) {
 
4903
                if ($char == 173) {
 
4904
                        // SHY character will not be printed
 
4905
                        return (0);
 
4906
                }
 
4907
                if (isset($this->CurrentFont['cw'][$char])) {
 
4908
                        $w = $this->CurrentFont['cw'][$char];
 
4909
                } elseif (isset($this->CurrentFont['dw'])) {
 
4910
                        // default width
 
4911
                        $w = $this->CurrentFont['dw'];
 
4912
                } elseif (isset($this->CurrentFont['cw'][32])) {
 
4913
                        // default width
 
4914
                        $w = $this->CurrentFont['cw'][32];
 
4915
                } else {
 
4916
                        $w = 600;
 
4917
                }
 
4918
                return ($w * $this->FontSize / 1000);
 
4919
        }
 
4920
 
 
4921
        /**
 
4922
         * Returns the numbero of characters in a string.
 
4923
         * @param $s (string) The input string.
 
4924
         * @return int number of characters
 
4925
         * @public
 
4926
         * @since 2.0.0001 (2008-01-07)
 
4927
         */
 
4928
        public function GetNumChars($s) {
 
4929
                if ($this->isUnicodeFont()) {
 
4930
                        return count($this->UTF8StringToArray($s));
 
4931
                }
 
4932
                return strlen($s);
 
4933
        }
 
4934
 
 
4935
        /**
 
4936
         * Fill the list of available fonts ($this->fontlist).
 
4937
         * @protected
 
4938
         * @since 4.0.013 (2008-07-28)
 
4939
         */
 
4940
        protected function getFontsList() {
 
4941
                $fontsdir = opendir($this->_getfontpath());
 
4942
                while (($file = readdir($fontsdir)) !== false) {
 
4943
                        if (substr($file, -4) == '.php') {
 
4944
                                array_push($this->fontlist, strtolower(basename($file, '.php')));
 
4945
                        }
 
4946
                }
 
4947
                closedir($fontsdir);
 
4948
        }
 
4949
 
 
4950
        /**
 
4951
         * Imports a TrueType, Type1, core, or CID0 font and makes it available.
 
4952
         * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
 
4953
         * 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.
 
4954
         * @param $family (string) Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
 
4955
         * @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>
 
4956
         * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
 
4957
         * @return array containing the font data, or false in case of error.
 
4958
         * @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.
 
4959
         * @public
 
4960
         * @since 1.5
 
4961
         * @see SetFont(), setFontSubsetting()
 
4962
         */
 
4963
        public function AddFont($family, $style='', $fontfile='', $subset='default') {
 
4964
                if ($subset === 'default') {
 
4965
                        $subset = $this->font_subsetting;
 
4966
                }
 
4967
                if ($this->pdfa_mode) {
 
4968
                        $subset = false;
 
4969
                }
 
4970
                if ($this->empty_string($family)) {
 
4971
                        if (!$this->empty_string($this->FontFamily)) {
 
4972
                                $family = $this->FontFamily;
 
4973
                        } else {
 
4974
                                $this->Error('Empty font family');
 
4975
                        }
 
4976
                }
 
4977
                // move embedded styles on $style
 
4978
                if (substr($family, -1) == 'I') {
 
4979
                        $style .= 'I';
 
4980
                        $family = substr($family, 0, -1);
 
4981
                }
 
4982
                if (substr($family, -1) == 'B') {
 
4983
                        $style .= 'B';
 
4984
                        $family = substr($family, 0, -1);
 
4985
                }
 
4986
                // normalize family name
 
4987
                $family = strtolower($family);
 
4988
                if ((!$this->isunicode) AND ($family == 'arial')) {
 
4989
                        $family = 'helvetica';
 
4990
                }
 
4991
                if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
 
4992
                        $style = '';
 
4993
                }
 
4994
                if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) {
 
4995
                        // all fonts must be embedded
 
4996
                        $family = 'pdfa'.$family;
 
4997
                }
 
4998
                $tempstyle = strtoupper($style);
 
4999
                $style = '';
 
5000
                // underline
 
5001
                if (strpos($tempstyle, 'U') !== false) {
 
5002
                        $this->underline = true;
 
5003
                } else {
 
5004
                        $this->underline = false;
 
5005
                }
 
5006
                // line-through (deleted)
 
5007
                if (strpos($tempstyle, 'D') !== false) {
 
5008
                        $this->linethrough = true;
 
5009
                } else {
 
5010
                        $this->linethrough = false;
 
5011
                }
 
5012
                // overline
 
5013
                if (strpos($tempstyle, 'O') !== false) {
 
5014
                        $this->overline = true;
 
5015
                } else {
 
5016
                        $this->overline = false;
 
5017
                }
 
5018
                // bold
 
5019
                if (strpos($tempstyle, 'B') !== false) {
 
5020
                        $style .= 'B';
 
5021
                }
 
5022
                // oblique
 
5023
                if (strpos($tempstyle, 'I') !== false) {
 
5024
                        $style .= 'I';
 
5025
                }
 
5026
                $bistyle = $style;
 
5027
                $fontkey = $family.$style;
 
5028
                $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : '');
 
5029
                $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
 
5030
                // check if the font has been already added
 
5031
                $fb = $this->getFontBuffer($fontkey);
 
5032
                if ($fb !== false) {
 
5033
                        if ($this->inxobj) {
 
5034
                                // we are inside an XObject template
 
5035
                                $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i'];
 
5036
                        }
 
5037
                        return $fontdata;
 
5038
                }
 
5039
                if (isset($type)) {
 
5040
                        unset($type);
 
5041
                }
 
5042
                if (isset($cw)) {
 
5043
                        unset($cw);
 
5044
                }
 
5045
                // get specified font directory (if any)
 
5046
                $fontdir = false;
 
5047
                if (!$this->empty_string($fontfile)) {
 
5048
                        $fontdir = dirname($fontfile);
 
5049
                        if ($this->empty_string($fontdir) OR ($fontdir == '.')) {
 
5050
                                $fontdir = '';
 
5051
                        } else {
 
5052
                                $fontdir .= '/';
 
5053
                        }
 
5054
                }
 
5055
                $missing_style = false; // true when the font style variation is missing
 
5056
                // search and include font file
 
5057
                if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) {
 
5058
                        // build a standard filenames for specified font
 
5059
                        $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php';
 
5060
                        // search files on various directories
 
5061
                        if (($fontdir !== false) AND file_exists($fontdir.$tmp_fontfile)) {
 
5062
                                $fontfile = $fontdir.$tmp_fontfile;
 
5063
                        } elseif (file_exists($this->_getfontpath().$tmp_fontfile)) {
 
5064
                                $fontfile = $this->_getfontpath().$tmp_fontfile;
 
5065
                        } elseif (file_exists($tmp_fontfile)) {
 
5066
                                $fontfile = $tmp_fontfile;
 
5067
                        } elseif (!$this->empty_string($style)) {
 
5068
                                $missing_style = true;
 
5069
                                // try to remove the style part
 
5070
                                $tmp_fontfile = str_replace(' ', '', $family).'.php';
 
5071
                                if (($fontdir !== false) AND file_exists($fontdir.$tmp_fontfile)) {
 
5072
                                        $fontfile = $fontdir.$tmp_fontfile;
 
5073
                                } elseif (file_exists($this->_getfontpath().$tmp_fontfile)) {
 
5074
                                        $fontfile = $this->_getfontpath().$tmp_fontfile;
 
5075
                                } else {
 
5076
                                        $fontfile = $tmp_fontfile;
 
5077
                                }
 
5078
                        }
 
5079
                }
 
5080
                // include font file
 
5081
                if (file_exists($fontfile)) {
 
5082
                        include($fontfile);
 
5083
                } else {
 
5084
                        $this->Error('Could not include font definition file: '.$family.'');
 
5085
                }
 
5086
                // check font parameters
 
5087
                if ((!isset($type)) OR (!isset($cw))) {
 
5088
                        $this->Error('The font definition file has a bad format: '.$fontfile.'');
 
5089
                }
 
5090
                // SET default parameters
 
5091
                if (!isset($file) OR $this->empty_string($file)) {
 
5092
                        $file = '';
 
5093
                }
 
5094
                if (!isset($enc) OR $this->empty_string($enc)) {
 
5095
                        $enc = '';
 
5096
                }
 
5097
                if (!isset($cidinfo) OR $this->empty_string($cidinfo)) {
 
5098
                        $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0);
 
5099
                        $cidinfo['uni2cid'] = array();
 
5100
                }
 
5101
                if (!isset($ctg) OR $this->empty_string($ctg)) {
 
5102
                        $ctg = '';
 
5103
                }
 
5104
                if (!isset($desc) OR $this->empty_string($desc)) {
 
5105
                        $desc = array();
 
5106
                }
 
5107
                if (!isset($up) OR $this->empty_string($up)) {
 
5108
                        $up = -100;
 
5109
                }
 
5110
                if (!isset($ut) OR $this->empty_string($ut)) {
 
5111
                        $ut = 50;
 
5112
                }
 
5113
                if (!isset($cw) OR $this->empty_string($cw)) {
 
5114
                        $cw = array();
 
5115
                }
 
5116
                if (!isset($dw) OR $this->empty_string($dw)) {
 
5117
                        // set default width
 
5118
                        if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
 
5119
                                $dw = $desc['MissingWidth'];
 
5120
                        } elseif (isset($cw[32])) {
 
5121
                                $dw = $cw[32];
 
5122
                        } else {
 
5123
                                $dw = 600;
 
5124
                        }
 
5125
                }
 
5126
                ++$this->numfonts;
 
5127
                if ($type == 'core') {
 
5128
                        $name = $this->CoreFonts[$fontkey];
 
5129
                        $subset = false;
 
5130
                } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
 
5131
                        $subset = false;
 
5132
                } elseif ($type == 'TrueTypeUnicode') {
 
5133
                        $enc = 'Identity-H';
 
5134
                } elseif ($type == 'cidfont0') {
 
5135
                        if ($this->pdfa_mode) {
 
5136
                                $this->Error('All fonts must be embedded in PDF/A mode!');
 
5137
                        }
 
5138
                } else {
 
5139
                        $this->Error('Unknow font type: '.$type.'');
 
5140
                }
 
5141
                // set name if unset
 
5142
                if (!isset($name) OR empty($name)) {
 
5143
                        $name = $fontkey;
 
5144
                }
 
5145
                // create artificial font style variations if missing (only works with non-embedded fonts)
 
5146
                if (($type != 'core') AND $missing_style) {
 
5147
                        // style variations
 
5148
                        $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
 
5149
                        $name .= $styles[$bistyle];
 
5150
                        // artificial bold
 
5151
                        if (strpos($bistyle, 'B') !== false) {
 
5152
                                if (isset($desc['StemV'])) {
 
5153
                                        // from normal to bold
 
5154
                                        $desc['StemV'] = round($desc['StemV'] * 1.75);
 
5155
                                } else {
 
5156
                                        // bold
 
5157
                                        $desc['StemV'] = 123;
 
5158
                                }
 
5159
                        }
 
5160
                        // artificial italic
 
5161
                        if (strpos($bistyle, 'I') !== false) {
 
5162
                                if (isset($desc['ItalicAngle'])) {
 
5163
                                        $desc['ItalicAngle'] -= 11;
 
5164
                                } else {
 
5165
                                        $desc['ItalicAngle'] = -11;
 
5166
                                }
 
5167
                                if (isset($desc['Flags'])) {
 
5168
                                        $desc['Flags'] |= 64; //bit 7
 
5169
                                } else {
 
5170
                                        $desc['Flags'] = 64;
 
5171
                                }
 
5172
                        }
 
5173
                }
 
5174
                // initialize subsetchars to contain default ASCII values (0-255)
 
5175
                $subsetchars = array_fill(0, 256, true);
 
5176
                $this->setFontBuffer($fontkey, array('fontkey' => $fontkey, 'i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc, 'cidinfo' => $cidinfo, 'file' => $file, 'ctg' => $ctg, 'subset' => $subset, 'subsetchars' => $subsetchars));
 
5177
                if ($this->inxobj) {
 
5178
                        // we are inside an XObject template
 
5179
                        $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts;
 
5180
                }
 
5181
                if (isset($diff) AND (!empty($diff))) {
 
5182
                        //Search existing encodings
 
5183
                        $d = 0;
 
5184
                        $nb = count($this->diffs);
 
5185
                        for ($i=1; $i <= $nb; ++$i) {
 
5186
                                if ($this->diffs[$i] == $diff) {
 
5187
                                        $d = $i;
 
5188
                                        break;
 
5189
                                }
 
5190
                        }
 
5191
                        if ($d == 0) {
 
5192
                                $d = $nb + 1;
 
5193
                                $this->diffs[$d] = $diff;
 
5194
                        }
 
5195
                        $this->setFontSubBuffer($fontkey, 'diff', $d);
 
5196
                }
 
5197
                if (!$this->empty_string($file)) {
 
5198
                        if (!isset($this->FontFiles[$file])) {
 
5199
                                if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
 
5200
                                        $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
 
5201
                                } elseif ($type != 'core') {
 
5202
                                        $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
 
5203
                                }
 
5204
                        } else {
 
5205
                                // update fontkeys that are sharing this font file
 
5206
                                $this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset);
 
5207
                                if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) {
 
5208
                                        $this->FontFiles[$file]['fontkeys'][] = $fontkey;
 
5209
                                }
 
5210
                        }
 
5211
                }
 
5212
                return $fontdata;
 
5213
        }
 
5214
 
 
5215
        /**
 
5216
         * Sets the font used to print character strings.
 
5217
         * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
 
5218
         * The method can be called before the first page is created and the font is retained from page to page.
 
5219
         * If you just wish to change the current font size, it is simpler to call SetFontSize().
 
5220
         * 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 />
 
5221
         * @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.
 
5222
         * @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 trough</li><li>O: overline</li></ul> or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats basic fonts or other fonts when not defined.
 
5223
         * @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
 
5224
         * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
 
5225
         * @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.
 
5226
         * @param $out (boolean) if true output the font size command, otherwise only set the font properties.
 
5227
         * @author Nicola Asuni
 
5228
         * @public
 
5229
         * @since 1.0
 
5230
         * @see AddFont(), SetFontSize()
 
5231
         */
 
5232
        public function SetFont($family, $style='', $size=0, $fontfile='', $subset='default', $out=true) {
 
5233
                //Select a font; size given in points
 
5234
                if ($size == 0) {
 
5235
                        $size = $this->FontSizePt;
 
5236
                }
 
5237
                // try to add font (if not already added)
 
5238
                $fontdata = $this->AddFont($family, $style, $fontfile, $subset);
 
5239
                $this->FontFamily = $fontdata['family'];
 
5240
                $this->FontStyle = $fontdata['style'];
 
5241
                $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
 
5242
                $this->SetFontSize($size, $out);
 
5243
        }
 
5244
 
 
5245
        /**
 
5246
         * Defines the size of the current font.
 
5247
         * @param $size (float) The font size in points.
 
5248
         * @param $out (boolean) if true output the font size command, otherwise only set the font properties.
 
5249
         * @public
 
5250
         * @since 1.0
 
5251
         * @see SetFont()
 
5252
         */
 
5253
        public function SetFontSize($size, $out=true) {
 
5254
                // font size in points
 
5255
                $this->FontSizePt = $size;
 
5256
                // font size in user units
 
5257
                $this->FontSize = $size / $this->k;
 
5258
                // calculate some font metrics
 
5259
                if (isset($this->CurrentFont['desc']['FontBBox'])) {
 
5260
                        $bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
 
5261
                        $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
 
5262
                } else {
 
5263
                        $font_height = $size * 1.219;
 
5264
                }
 
5265
                if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
 
5266
                        $font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000);
 
5267
                }
 
5268
                if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) {
 
5269
                        $font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000);
 
5270
                }
 
5271
                if (!isset($font_ascent) AND !isset($font_descent)) {
 
5272
                        // core font
 
5273
                        $font_ascent = 0.76 * $font_height;
 
5274
                        $font_descent = $font_height - $font_ascent;
 
5275
                } elseif (!isset($font_descent)) {
 
5276
                        $font_descent = $font_height - $font_ascent;
 
5277
                } elseif (!isset($font_ascent)) {
 
5278
                        $font_ascent = $font_height - $font_descent;
 
5279
                }
 
5280
                $this->FontAscent = ($font_ascent / $this->k);
 
5281
                $this->FontDescent = ($font_descent / $this->k);
 
5282
                if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i']))) {
 
5283
                        $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
 
5284
                }
 
5285
        }
 
5286
 
 
5287
        /**
 
5288
         * Return the font descent value
 
5289
         * @param $font (string) font name
 
5290
         * @param $style (string) font style
 
5291
         * @param $size (float) The size (in points)
 
5292
         * @return int font descent
 
5293
         * @public
 
5294
         * @author Nicola Asuni
 
5295
         * @since 4.9.003 (2010-03-30)
 
5296
         */
 
5297
        public function getFontDescent($font, $style='', $size=0) {
 
5298
                $fontdata = $this->AddFont($font, $style);
 
5299
                $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
 
5300
                if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
 
5301
                        $descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
 
5302
                } else {
 
5303
                        $descent = 1.219 * 0.24 * $size;
 
5304
                }
 
5305
                return ($descent / $this->k);
 
5306
        }
 
5307
 
 
5308
        /**
 
5309
         * Return the font ascent value
 
5310
         * @param $font (string) font name
 
5311
         * @param $style (string) font style
 
5312
         * @param $size (float) The size (in points)
 
5313
         * @return int font ascent
 
5314
         * @public
 
5315
         * @author Nicola Asuni
 
5316
         * @since 4.9.003 (2010-03-30)
 
5317
         */
 
5318
        public function getFontAscent($font, $style='', $size=0) {
 
5319
                $fontdata = $this->AddFont($font, $style);
 
5320
                $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
 
5321
                if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
 
5322
                        $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
 
5323
                } else {
 
5324
                        $ascent = 1.219 * 0.76 * $size;
 
5325
                }
 
5326
                return ($ascent / $this->k);
 
5327
        }
 
5328
 
 
5329
        /**
 
5330
         * Defines the default monospaced font.
 
5331
         * @param $font (string) Font name.
 
5332
         * @public
 
5333
         * @since 4.5.025
 
5334
         */
 
5335
        public function SetDefaultMonospacedFont($font) {
 
5336
                $this->default_monospaced_font = $font;
 
5337
        }
 
5338
 
 
5339
        /**
 
5340
         * 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 />
 
5341
         * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
 
5342
         * @public
 
5343
         * @since 1.5
 
5344
         * @see Cell(), Write(), Image(), Link(), SetLink()
 
5345
         */
 
5346
        public function AddLink() {
 
5347
                //Create a new internal link
 
5348
                $n = count($this->links) + 1;
 
5349
                $this->links[$n] = array(0, 0);
 
5350
                return $n;
 
5351
        }
 
5352
 
 
5353
        /**
 
5354
         * Defines the page and position a link points to.
 
5355
         * @param $link (int) The link identifier returned by AddLink()
 
5356
         * @param $y (float) Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
 
5357
         * @param $page (int) Number of target page; -1 indicates the current page. This is the default value
 
5358
         * @public
 
5359
         * @since 1.5
 
5360
         * @see AddLink()
 
5361
         */
 
5362
        public function SetLink($link, $y=0, $page=-1) {
 
5363
                if ($y == -1) {
 
5364
                        $y = $this->y;
 
5365
                }
 
5366
                if ($page == -1) {
 
5367
                        $page = $this->page;
 
5368
                }
 
5369
                $this->links[$link] = array($page, $y);
 
5370
        }
 
5371
 
 
5372
        /**
 
5373
         * Puts a link on a rectangular area of the page.
 
5374
         * 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.
 
5375
         * @param $x (float) Abscissa of the upper-left corner of the rectangle
 
5376
         * @param $y (float) Ordinate of the upper-left corner of the rectangle
 
5377
         * @param $w (float) Width of the rectangle
 
5378
         * @param $h (float) Height of the rectangle
 
5379
         * @param $link (mixed) URL or identifier returned by AddLink()
 
5380
         * @param $spaces (int) number of spaces on the text to link
 
5381
         * @public
 
5382
         * @since 1.5
 
5383
         * @see AddLink(), Annotation(), Cell(), Write(), Image()
 
5384
         */
 
5385
        public function Link($x, $y, $w, $h, $link, $spaces=0) {
 
5386
                $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
 
5387
        }
 
5388
 
 
5389
        /**
 
5390
         * Puts a markup annotation on a rectangular area of the page.
 
5391
         * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!!
 
5392
         * @param $x (float) Abscissa of the upper-left corner of the rectangle
 
5393
         * @param $y (float) Ordinate of the upper-left corner of the rectangle
 
5394
         * @param $w (float) Width of the rectangle
 
5395
         * @param $h (float) Height of the rectangle
 
5396
         * @param $text (string) annotation text or alternate content
 
5397
         * @param $opt (array) array of options (see section 8.4 of PDF reference 1.7).
 
5398
         * @param $spaces (int) number of spaces on the text to link
 
5399
         * @public
 
5400
         * @since 4.0.018 (2008-08-06)
 
5401
         */
 
5402
        public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
 
5403
                if ($this->inxobj) {
 
5404
                        // store parameters for later use on template
 
5405
                        $this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
 
5406
                        return;
 
5407
                }
 
5408
                if ($x === '') {
 
5409
                        $x = $this->x;
 
5410
                }
 
5411
                if ($y === '') {
 
5412
                        $y = $this->y;
 
5413
                }
 
5414
                // check page for no-write regions and adapt page margins if necessary
 
5415
                list($x, $y) = $this->checkPageRegions($h, $x, $y);
 
5416
                // recalculate coordinates to account for graphic transformations
 
5417
                if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) {
 
5418
                        for ($i=$this->transfmatrix_key; $i > 0; --$i) {
 
5419
                                $maxid = count($this->transfmatrix[$i]) - 1;
 
5420
                                for ($j=$maxid; $j >= 0; --$j) {
 
5421
                                        $ctm = $this->transfmatrix[$i][$j];
 
5422
                                        if (isset($ctm['a'])) {
 
5423
                                                $x = $x * $this->k;
 
5424
                                                $y = ($this->h - $y) * $this->k;
 
5425
                                                $w = $w * $this->k;
 
5426
                                                $h = $h * $this->k;
 
5427
                                                // top left
 
5428
                                                $xt = $x;
 
5429
                                                $yt = $y;
 
5430
                                                $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
 
5431
                                                $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
 
5432
                                                // top right
 
5433
                                                $xt = $x + $w;
 
5434
                                                $yt = $y;
 
5435
                                                $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
 
5436
                                                $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
 
5437
                                                // bottom left
 
5438
                                                $xt = $x;
 
5439
                                                $yt = $y - $h;
 
5440
                                                $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
 
5441
                                                $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
 
5442
                                                // bottom right
 
5443
                                                $xt = $x + $w;
 
5444
                                                $yt = $y - $h;
 
5445
                                                $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
 
5446
                                                $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
 
5447
                                                // new coordinates (rectangle area)
 
5448
                                                $x = min($x1, $x2, $x3, $x4);
 
5449
                                                $y = max($y1, $y2, $y3, $y4);
 
5450
                                                $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
 
5451
                                                $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
 
5452
                                                $x = $x / $this->k;
 
5453
                                                $y = $this->h - ($y / $this->k);
 
5454
                                        }
 
5455
                                }
 
5456
                        }
 
5457
                }
 
5458
                if ($this->page <= 0) {
 
5459
                        $page = 1;
 
5460
                } else {
 
5461
                        $page = $this->page;
 
5462
                }
 
5463
                if (!isset($this->PageAnnots[$page])) {
 
5464
                        $this->PageAnnots[$page] = array();
 
5465
                }
 
5466
                ++$this->n;
 
5467
                $this->PageAnnots[$page][] = array('n' => $this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
 
5468
                if (!$this->pdfa_mode) {
 
5469
                        if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!$this->empty_string($opt['FS'])) AND file_exists($opt['FS']) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
 
5470
                                ++$this->n;
 
5471
                                $this->embeddedfiles[basename($opt['FS'])] = array('n' => $this->n, 'file' => $opt['FS']);
 
5472
                        }
 
5473
                }
 
5474
                // Add widgets annotation's icons
 
5475
                if (isset($opt['mk']['i']) AND file_exists($opt['mk']['i'])) {
 
5476
                        $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
 
5477
                }
 
5478
                if (isset($opt['mk']['ri']) AND file_exists($opt['mk']['ri'])) {
 
5479
                        $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
 
5480
                }
 
5481
                if (isset($opt['mk']['ix']) AND file_exists($opt['mk']['ix'])) {
 
5482
                        $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
 
5483
                }
 
5484
        }
 
5485
 
 
5486
        /**
 
5487
         * Embedd the attached files.
 
5488
         * @since 4.4.000 (2008-12-07)
 
5489
         * @protected
 
5490
         * @see Annotation()
 
5491
         */
 
5492
        protected function _putEmbeddedFiles() {
 
5493
                if ($this->pdfa_mode) {
 
5494
                        // embedded files are not allowed in PDF/A mode
 
5495
                        return;
 
5496
                }
 
5497
                reset($this->embeddedfiles);
 
5498
                foreach ($this->embeddedfiles as $filename => $filedata) {
 
5499
                        $data = file_get_contents($filedata['file']);
 
5500
                        $filter = '';
 
5501
                        if ($this->compress) {
 
5502
                                $data = gzcompress($data);
 
5503
                                $filter = ' /Filter /FlateDecode';
 
5504
                        }
 
5505
                        $stream = $this->_getrawstream($data, $filedata['n']);
 
5506
                        $out = $this->_getobj($filedata['n'])."\n";
 
5507
                        $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' >>';
 
5508
                        $out .= ' stream'."\n".$stream."\n".'endstream';
 
5509
                        $out .= "\n".'endobj';
 
5510
                        $this->_out($out);
 
5511
                }
 
5512
        }
 
5513
 
 
5514
        /**
 
5515
         * Prints a text cell at the specified position.
 
5516
         * This method allows to place a string precisely on the page.
 
5517
         * @param $x (float) Abscissa of the cell origin
 
5518
         * @param $y (float) Ordinate of the cell origin
 
5519
         * @param $txt (string) String to print
 
5520
         * @param $fstroke (int) outline size in user units (false = disable)
 
5521
         * @param $fclip (boolean) if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
 
5522
         * @param $ffill (boolean) if true fills the text
 
5523
         * @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)))
 
5524
         * @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.
 
5525
         * @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>
 
5526
         * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
 
5527
         * @param $link (mixed) URL or identifier returned by AddLink().
 
5528
         * @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.
 
5529
         * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
 
5530
         * @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>
 
5531
         * @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>
 
5532
         * @param $rtloff (boolean) if true uses the page top-left corner as origin of axis for $x and $y initial position.
 
5533
         * @public
 
5534
         * @since 1.0
 
5535
         * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell()
 
5536
         */
 
5537
        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) {
 
5538
                $textrendermode = $this->textrendermode;
 
5539
                $textstrokewidth = $this->textstrokewidth;
 
5540
                $this->setTextRenderingMode($fstroke, $ffill, $fclip);
 
5541
                $this->SetXY($x, $y, $rtloff);
 
5542
                $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
 
5543
                // restore previous rendering mode
 
5544
                $this->textrendermode = $textrendermode;
 
5545
                $this->textstrokewidth = $textstrokewidth;
 
5546
        }
 
5547
 
 
5548
        /**
 
5549
         * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
 
5550
         * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
 
5551
         * This method is called automatically and should not be called directly by the application.
 
5552
         * @return boolean
 
5553
         * @public
 
5554
         * @since 1.4
 
5555
         * @see SetAutoPageBreak()
 
5556
         */
 
5557
        public function AcceptPageBreak() {
 
5558
                if ($this->num_columns > 1) {
 
5559
                        // multi column mode
 
5560
                        if ($this->current_column < ($this->num_columns - 1)) {
 
5561
                                // go to next column
 
5562
                                $this->selectColumn($this->current_column + 1);
 
5563
                        } else {
 
5564
                                // add a new page
 
5565
                                $this->AddPage();
 
5566
                                // set first column
 
5567
                                $this->selectColumn(0);
 
5568
                        }
 
5569
                        // avoid page breaking from checkPageBreak()
 
5570
                        return false;
 
5571
                }
 
5572
                return $this->AutoPageBreak;
 
5573
        }
 
5574
 
 
5575
        /**
 
5576
         * Add page if needed.
 
5577
         * @param $h (float) Cell height. Default value: 0.
 
5578
         * @param $y (mixed) starting y position, leave empty for current position.
 
5579
         * @param $addpage (boolean) if true add a page, otherwise only return the true/false state
 
5580
         * @return boolean true in case of page break, false otherwise.
 
5581
         * @since 3.2.000 (2008-07-01)
 
5582
         * @protected
 
5583
         */
 
5584
        protected function checkPageBreak($h=0, $y='', $addpage=true) {
 
5585
                if ($this->empty_string($y)) {
 
5586
                        $y = $this->y;
 
5587
                }
 
5588
                $current_page = $this->page;
 
5589
                if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) {
 
5590
                        if ($addpage) {
 
5591
                                //Automatic page break
 
5592
                                $x = $this->x;
 
5593
                                $this->AddPage($this->CurOrientation);
 
5594
                                $this->y = $this->tMargin;
 
5595
                                $oldpage = $this->page - 1;
 
5596
                                if ($this->rtl) {
 
5597
                                        if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
 
5598
                                                $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
 
5599
                                        } else {
 
5600
                                                $this->x = $x;
 
5601
                                        }
 
5602
                                } else {
 
5603
                                        if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
 
5604
                                                $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
 
5605
                                        } else {
 
5606
                                                $this->x = $x;
 
5607
                                        }
 
5608
                                }
 
5609
                        }
 
5610
                        return true;
 
5611
                }
 
5612
                if ($current_page != $this->page) {
 
5613
                        // account for columns mode
 
5614
                        return true;
 
5615
                }
 
5616
                return false;
 
5617
        }
 
5618
 
 
5619
        /**
 
5620
         * Removes SHY characters from text.
 
5621
         * Unicode Data:<ul>
 
5622
         * <li>Name : SOFT HYPHEN, commonly abbreviated as SHY</li>
 
5623
         * <li>HTML Entity (decimal): "&amp;#173;"</li>
 
5624
         * <li>HTML Entity (hex): "&amp;#xad;"</li>
 
5625
         * <li>HTML Entity (named): "&amp;shy;"</li>
 
5626
         * <li>How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173]</li>
 
5627
         * <li>UTF-8 (hex): 0xC2 0xAD (c2ad)</li>
 
5628
         * <li>UTF-8 character: chr(194).chr(173)</li>
 
5629
         * </ul>
 
5630
         * @param $txt (string) input string
 
5631
         * @return string without SHY characters.
 
5632
         * @public
 
5633
         * @since (4.5.019) 2009-02-28
 
5634
         */
 
5635
        public function removeSHY($txt='') {
 
5636
                $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt);
 
5637
                if (!$this->isunicode) {
 
5638
                        $txt = preg_replace('/([\\xad]{1})/', '', $txt);
 
5639
                }
 
5640
                return $txt;
 
5641
        }
 
5642
 
 
5643
        /**
 
5644
         * 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 />
 
5645
         * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
 
5646
         * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
 
5647
         * @param $h (float) Cell height. Default value: 0.
 
5648
         * @param $txt (string) String to print. Default value: empty string.
 
5649
         * @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)))
 
5650
         * @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.
 
5651
         * @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>
 
5652
         * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
 
5653
         * @param $link (mixed) URL or identifier returned by AddLink().
 
5654
         * @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.
 
5655
         * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
 
5656
         * @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>
 
5657
         * @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>
 
5658
         * @public
 
5659
         * @since 1.0
 
5660
         * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
 
5661
         */
 
5662
        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') {
 
5663
                $prev_cell_margin = $this->cell_margin;
 
5664
                $prev_cell_padding = $this->cell_padding;
 
5665
                $this->adjustCellPadding($border);
 
5666
                if (!$ignore_min_height) {
 
5667
                        $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
 
5668
                        if ($h < $min_cell_height) {
 
5669
                                $h = $min_cell_height;
 
5670
                        }
 
5671
                }
 
5672
                $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']);
 
5673
                $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
 
5674
                $this->cell_padding = $prev_cell_padding;
 
5675
                $this->cell_margin = $prev_cell_margin;
 
5676
        }
 
5677
 
 
5678
        /**
 
5679
         * 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 />
 
5680
         * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
 
5681
         * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
 
5682
         * @param $h (float) Cell height. Default value: 0.
 
5683
         * @param $txt (string) String to print. Default value: empty string.
 
5684
         * @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)))
 
5685
         * @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.
 
5686
         * @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>
 
5687
         * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
 
5688
         * @param $link (mixed) URL or identifier returned by AddLink().
 
5689
         * @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.
 
5690
         * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
 
5691
         * @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>
 
5692
         * @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>
 
5693
         * @return string containing cell code
 
5694
         * @protected
 
5695
         * @since 1.0
 
5696
         * @see Cell()
 
5697
         */
 
5698
        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') {
 
5699
                // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space
 
5700
                $txt = str_replace($this->unichr(160), ' ', $txt);
 
5701
                $prev_cell_margin = $this->cell_margin;
 
5702
                $prev_cell_padding = $this->cell_padding;
 
5703
                $txt = $this->removeSHY($txt);
 
5704
                $rs = ''; //string to be returned
 
5705
                $this->adjustCellPadding($border);
 
5706
                if (!$ignore_min_height) {
 
5707
                        $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
 
5708
                        if ($h < $min_cell_height) {
 
5709
                                $h = $min_cell_height;
 
5710
                        }
 
5711
                }
 
5712
                $k = $this->k;
 
5713
                // check page for no-write regions and adapt page margins if necessary
 
5714
                list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
 
5715
                if ($this->rtl) {
 
5716
                        $x = $this->x - $this->cell_margin['R'];
 
5717
                } else {
 
5718
                        $x = $this->x + $this->cell_margin['L'];
 
5719
                }
 
5720
                $y = $this->y + $this->cell_margin['T'];
 
5721
                $prev_font_stretching = $this->font_stretching;
 
5722
                $prev_font_spacing = $this->font_spacing;
 
5723
                // cell vertical alignment
 
5724
                switch ($calign) {
 
5725
                        case 'A': {
 
5726
                                // font top
 
5727
                                switch ($valign) {
 
5728
                                        case 'T': {
 
5729
                                                // top
 
5730
                                                $y -= $this->cell_padding['T'];
 
5731
                                                break;
 
5732
                                        }
 
5733
                                        case 'B': {
 
5734
                                                // bottom
 
5735
                                                $y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent);
 
5736
                                                break;
 
5737
                                        }
 
5738
                                        default:
 
5739
                                        case 'C':
 
5740
                                        case 'M': {
 
5741
                                                // center
 
5742
                                                $y -= (($h - $this->FontAscent - $this->FontDescent) / 2);
 
5743
                                                break;
 
5744
                                        }
 
5745
                                }
 
5746
                                break;
 
5747
                        }
 
5748
                        case 'L': {
 
5749
                                // font baseline
 
5750
                                switch ($valign) {
 
5751
                                        case 'T': {
 
5752
                                                // top
 
5753
                                                $y -= ($this->cell_padding['T'] + $this->FontAscent);
 
5754
                                                break;
 
5755
                                        }
 
5756
                                        case 'B': {
 
5757
                                                // bottom
 
5758
                                                $y -= ($h - $this->cell_padding['B'] - $this->FontDescent);
 
5759
                                                break;
 
5760
                                        }
 
5761
                                        default:
 
5762
                                        case 'C':
 
5763
                                        case 'M': {
 
5764
                                                // center
 
5765
                                                $y -= (($h + $this->FontAscent - $this->FontDescent) / 2);
 
5766
                                                break;
 
5767
                                        }
 
5768
                                }
 
5769
                                break;
 
5770
                        }
 
5771
                        case 'D': {
 
5772
                                // font bottom
 
5773
                                switch ($valign) {
 
5774
                                        case 'T': {
 
5775
                                                // top
 
5776
                                                $y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent);
 
5777
                                                break;
 
5778
                                        }
 
5779
                                        case 'B': {
 
5780
                                                // bottom
 
5781
                                                $y -= ($h - $this->cell_padding['B']);
 
5782
                                                break;
 
5783
                                        }
 
5784
                                        default:
 
5785
                                        case 'C':
 
5786
                                        case 'M': {
 
5787
                                                // center
 
5788
                                                $y -= (($h + $this->FontAscent + $this->FontDescent) / 2);
 
5789
                                                break;
 
5790
                                        }
 
5791
                                }
 
5792
                                break;
 
5793
                        }
 
5794
                        case 'B': {
 
5795
                                // cell bottom
 
5796
                                $y -= $h;
 
5797
                                break;
 
5798
                        }
 
5799
                        case 'C':
 
5800
                        case 'M': {
 
5801
                                // cell center
 
5802
                                $y -= ($h / 2);
 
5803
                                break;
 
5804
                        }
 
5805
                        default:
 
5806
                        case 'T': {
 
5807
                                // cell top
 
5808
                                break;
 
5809
                        }
 
5810
                }
 
5811
                // text vertical alignment
 
5812
                switch ($valign) {
 
5813
                        case 'T': {
 
5814
                                // top
 
5815
                                $yt = $y + $this->cell_padding['T'];
 
5816
                                break;
 
5817
                        }
 
5818
                        case 'B': {
 
5819
                                // bottom
 
5820
                                $yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent;
 
5821
                                break;
 
5822
                        }
 
5823
                        default:
 
5824
                        case 'C':
 
5825
                        case 'M': {
 
5826
                                // center
 
5827
                                $yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2);
 
5828
                                break;
 
5829
                        }
 
5830
                }
 
5831
                $basefonty = $yt + $this->FontAscent;
 
5832
                if ($this->empty_string($w) OR ($w <= 0)) {
 
5833
                        if ($this->rtl) {
 
5834
                                $w = $x - $this->lMargin;
 
5835
                        } else {
 
5836
                                $w = $this->w - $this->rMargin - $x;
 
5837
                        }
 
5838
                }
 
5839
                $s = '';
 
5840
                // fill and borders
 
5841
                if (is_string($border) AND (strlen($border) == 4)) {
 
5842
                        // full border
 
5843
                        $border = 1;
 
5844
                }
 
5845
                if ($fill OR ($border == 1)) {
 
5846
                        if ($fill) {
 
5847
                                $op = ($border == 1) ? 'B' : 'f';
 
5848
                        } else {
 
5849
                                $op = 'S';
 
5850
                        }
 
5851
                        if ($this->rtl) {
 
5852
                                $xk = (($x - $w) * $k);
 
5853
                        } else {
 
5854
                                $xk = ($x * $k);
 
5855
                        }
 
5856
                        $s .= sprintf('%.2F %.2F %.2F %.2F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op);
 
5857
                }
 
5858
                // draw borders
 
5859
                $s .= $this->getCellBorder($x, $y, $w, $h, $border);
 
5860
                if ($txt != '') {
 
5861
                        $txt2 = $txt;
 
5862
                        if ($this->isunicode) {
 
5863
                                if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
 
5864
                                        $txt2 = $this->UTF8ToLatin1($txt2);
 
5865
                                } else {
 
5866
                                        $unicode = $this->UTF8StringToArray($txt); // array of UTF-8 unicode values
 
5867
                                        $unicode = $this->utf8Bidi($unicode, '', $this->tmprtl);
 
5868
                                        if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) {
 
5869
                                                // ---- Fix for bug #2977340 "Incorrect Thai characters position arrangement" ----
 
5870
                                                // NOTE: this doesn't work with HTML justification
 
5871
                                                // Symbols that could overlap on the font top (only works in LTR)
 
5872
                                                $topchar = array(3611, 3613, 3615, 3650, 3651, 3652); // chars that extends on top
 
5873
                                                $topsym = array(3633, 3636, 3637, 3638, 3639, 3655, 3656, 3657, 3658, 3659, 3660, 3661, 3662); // symbols with top position
 
5874
                                                $numchars = count($unicode); // number of chars
 
5875
                                                $unik = 0;
 
5876
                                                $uniblock = array();
 
5877
                                                $uniblock[$unik] = array();
 
5878
                                                $uniblock[$unik][] = $unicode[0];
 
5879
                                                // resolve overlapping conflicts by splitting the string in several parts
 
5880
                                                for ($i = 1; $i < $numchars; ++$i) {
 
5881
                                                        // check if symbols overlaps at top
 
5882
                                                        if (in_array($unicode[$i], $topsym) AND (in_array($unicode[($i - 1)], $topsym) OR in_array($unicode[($i - 1)], $topchar))) {
 
5883
                                                                // move symbols to another array
 
5884
                                                                ++$unik;
 
5885
                                                                $uniblock[$unik] = array();
 
5886
                                                                $uniblock[$unik][] = $unicode[$i];
 
5887
                                                                ++$unik;
 
5888
                                                                $uniblock[$unik] = array();
 
5889
                                                                $unicode[$i] = 0x200b; // Unicode Character 'ZERO WIDTH SPACE' (DEC:8203, U+200B)
 
5890
                                                        } else {
 
5891
                                                                $uniblock[$unik][] = $unicode[$i];
 
5892
                                                        }
 
5893
                                                }
 
5894
                                                // ---- END OF Fix for bug #2977340
 
5895
                                        }
 
5896
                                        $txt2 = $this->arrUTF8ToUTF16BE($unicode, false);
 
5897
                                }
 
5898
                        }
 
5899
                        $txt2 = $this->_escape($txt2);
 
5900
                        // get current text width (considering general font stretching and spacing)
 
5901
                        $txwidth = $this->GetStringWidth($txt);
 
5902
                        $width = $txwidth;
 
5903
                        // check for stretch mode
 
5904
                        if ($stretch > 0) {
 
5905
                                // calculate ratio between cell width and text width
 
5906
                                if ($width <= 0) {
 
5907
                                        $ratio = 1;
 
5908
                                } else {
 
5909
                                        $ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width);
 
5910
                                }
 
5911
                                // check if stretching is required
 
5912
                                if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) {
 
5913
                                        // the text will be stretched to fit cell width
 
5914
                                        if ($stretch > 2) {
 
5915
                                                // set new character spacing
 
5916
                                                $this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100));
 
5917
                                        } else {
 
5918
                                                // set new horizontal stretching
 
5919
                                                $this->font_stretching *= $ratio;
 
5920
                                        }
 
5921
                                        // recalculate text width (the text fills the entire cell)
 
5922
                                        $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
 
5923
                                        // reset alignment
 
5924
                                        $align = '';
 
5925
                                }
 
5926
                        }
 
5927
                        if ($this->font_stretching != 100) {
 
5928
                                // apply font stretching
 
5929
                                $rs .= sprintf('BT %.2F Tz ET ', $this->font_stretching);
 
5930
                        }
 
5931
                        if ($this->font_spacing != 0) {
 
5932
                                // increase/decrease font spacing
 
5933
                                $rs .= sprintf('BT %.2F Tc ET ', ($this->font_spacing * $this->k));
 
5934
                        }
 
5935
                        if ($this->ColorFlag AND ($this->textrendermode < 4)) {
 
5936
                                $s .= 'q '.$this->TextColor.' ';
 
5937
                        }
 
5938
                        // rendering mode
 
5939
                        $s .= sprintf('BT %d Tr %.2F w ET ', $this->textrendermode, $this->textstrokewidth);
 
5940
                        // count number of spaces
 
5941
                        $ns = substr_count($txt, chr(32));
 
5942
                        // Justification
 
5943
                        $spacewidth = 0;
 
5944
                        if (($align == 'J') AND ($ns > 0)) {
 
5945
                                if ($this->isUnicodeFont()) {
 
5946
                                        // get string width without spaces
 
5947
                                        $width = $this->GetStringWidth(str_replace(' ', '', $txt));
 
5948
                                        // calculate average space width
 
5949
                                        $spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / $this->FontSize;
 
5950
                                        if ($this->font_stretching != 100) {
 
5951
                                                // word spacing is affected by stretching
 
5952
                                                $spacewidth /= ($this->font_stretching / 100);
 
5953
                                        }
 
5954
                                        // set word position to be used with TJ operator
 
5955
                                        $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%.3F', $spacewidth).' (', $txt2);
 
5956
                                        $unicode_justification = true;
 
5957
                                } else {
 
5958
                                        // get string width
 
5959
                                        $width = $txwidth;
 
5960
                                        // new space width
 
5961
                                        $spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k;
 
5962
                                        if ($this->font_stretching != 100) {
 
5963
                                                // word spacing (Tw) is affected by stretching
 
5964
                                                $spacewidth /= ($this->font_stretching / 100);
 
5965
                                        }
 
5966
                                        // set word spacing
 
5967
                                        $rs .= sprintf('BT %.3F Tw ET ', $spacewidth);
 
5968
                                }
 
5969
                                $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
 
5970
                        }
 
5971
                        // replace carriage return characters
 
5972
                        $txt2 = str_replace("\r", ' ', $txt2);
 
5973
                        switch ($align) {
 
5974
                                case 'C': {
 
5975
                                        $dx = ($w - $width) / 2;
 
5976
                                        break;
 
5977
                                }
 
5978
                                case 'R': {
 
5979
                                        if ($this->rtl) {
 
5980
                                                $dx = $this->cell_padding['R'];
 
5981
                                        } else {
 
5982
                                                $dx = $w - $width - $this->cell_padding['R'];
 
5983
                                        }
 
5984
                                        break;
 
5985
                                }
 
5986
                                case 'L': {
 
5987
                                        if ($this->rtl) {
 
5988
                                                $dx = $w - $width - $this->cell_padding['L'];
 
5989
                                        } else {
 
5990
                                                $dx = $this->cell_padding['L'];
 
5991
                                        }
 
5992
                                        break;
 
5993
                                }
 
5994
                                case 'J':
 
5995
                                default: {
 
5996
                                        if ($this->rtl) {
 
5997
                                                $dx = $this->cell_padding['R'];
 
5998
                                        } else {
 
5999
                                                $dx = $this->cell_padding['L'];
 
6000
                                        }
 
6001
                                        break;
 
6002
                                }
 
6003
                        }
 
6004
                        if ($this->rtl) {
 
6005
                                $xdx = $x - $dx - $width;
 
6006
                        } else {
 
6007
                                $xdx = $x + $dx;
 
6008
                        }
 
6009
                        $xdk = $xdx * $k;
 
6010
                        // print text
 
6011
                        $s .= sprintf('BT %.2F %.2F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
 
6012
                        if (isset($uniblock)) {
 
6013
                                // print overlapping characters as separate string
 
6014
                                $xshift = 0; // horizontal shift
 
6015
                                $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k);
 
6016
                                $spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1));
 
6017
                                foreach ($uniblock as $uk => $uniarr) {
 
6018
                                        if (($uk % 2) == 0) {
 
6019
                                                // x space to skip
 
6020
                                                if ($spacewidth != 0) {
 
6021
                                                        // justification shift
 
6022
                                                        $xshift += (count(array_keys($uniarr, 32)) * $spw);
 
6023
                                                }
 
6024
                                                $xshift += $this->GetArrStringWidth($uniarr); // + shift justification
 
6025
                                        } else {
 
6026
                                                // character to print
 
6027
                                                $topchr = $this->arrUTF8ToUTF16BE($uniarr, false);
 
6028
                                                $topchr = $this->_escape($topchr);
 
6029
                                                $s .= sprintf(' BT %.2F %.2F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr);
 
6030
                                        }
 
6031
                                }
 
6032
                        }
 
6033
                        if ($this->underline) {
 
6034
                                $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
 
6035
                        }
 
6036
                        if ($this->linethrough) {
 
6037
                                $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
 
6038
                        }
 
6039
                        if ($this->overline) {
 
6040
                                $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
 
6041
                        }
 
6042
                        if ($this->ColorFlag AND ($this->textrendermode < 4)) {
 
6043
                                $s .= ' Q';
 
6044
                        }
 
6045
                        if ($link) {
 
6046
                                $this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns);
 
6047
                        }
 
6048
                }
 
6049
                // output cell
 
6050
                if ($s) {
 
6051
                        // output cell
 
6052
                        $rs .= $s;
 
6053
                        if ($this->font_spacing != 0) {
 
6054
                                // reset font spacing mode
 
6055
                                $rs .= ' BT 0 Tc ET';
 
6056
                        }
 
6057
                        if ($this->font_stretching != 100) {
 
6058
                                // reset font stretching mode
 
6059
                                $rs .= ' BT 100 Tz ET';
 
6060
                        }
 
6061
                }
 
6062
                // reset word spacing
 
6063
                if (!$this->isUnicodeFont() AND ($align == 'J')) {
 
6064
                        $rs .= ' BT 0 Tw ET';
 
6065
                }
 
6066
                // reset stretching and spacing
 
6067
                $this->font_stretching = $prev_font_stretching;
 
6068
                $this->font_spacing = $prev_font_spacing;
 
6069
                $this->lasth = $h;
 
6070
                if ($ln > 0) {
 
6071
                        //Go to the beginning of the next line
 
6072
                        $this->y = $y + $h + $this->cell_margin['B'];
 
6073
                        if ($ln == 1) {
 
6074
                                if ($this->rtl) {
 
6075
                                        $this->x = $this->w - $this->rMargin;
 
6076
                                } else {
 
6077
                                        $this->x = $this->lMargin;
 
6078
                                }
 
6079
                        }
 
6080
                } else {
 
6081
                        // go left or right by case
 
6082
                        if ($this->rtl) {
 
6083
                                $this->x = $x - $w - $this->cell_margin['L'];
 
6084
                        } else {
 
6085
                                $this->x = $x + $w + $this->cell_margin['R'];
 
6086
                        }
 
6087
                }
 
6088
                $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
 
6089
                $rs = $gstyles.$rs;
 
6090
                $this->cell_padding = $prev_cell_padding;
 
6091
                $this->cell_margin = $prev_cell_margin;
 
6092
                return $rs;
 
6093
        }
 
6094
 
 
6095
        /**
 
6096
         * Returns the code to draw the cell border
 
6097
         * @param $x (float) X coordinate.
 
6098
         * @param $y (float) Y coordinate.
 
6099
         * @param $w (float) Cell width.
 
6100
         * @param $h (float) Cell height.
 
6101
         * @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)))
 
6102
         * @return string containing cell border code
 
6103
         * @protected
 
6104
         * @see SetLineStyle()
 
6105
         * @since 5.7.000 (2010-08-02)
 
6106
         */
 
6107
        protected function getCellBorder($x, $y, $w, $h, $brd) {
 
6108
                $s = ''; // string to be returned
 
6109
                if (empty($brd)) {
 
6110
                        return $s;
 
6111
                }
 
6112
                if ($brd == 1) {
 
6113
                        $brd = array('LRTB' => true);
 
6114
                }
 
6115
                // calculate coordinates for border
 
6116
                $k = $this->k;
 
6117
                if ($this->rtl) {
 
6118
                        $xeL = ($x - $w) * $k;
 
6119
                        $xeR = $x * $k;
 
6120
                } else {
 
6121
                        $xeL = $x * $k;
 
6122
                        $xeR = ($x + $w) * $k;
 
6123
                }
 
6124
                $yeL = (($this->h - ($y + $h)) * $k);
 
6125
                $yeT = (($this->h - $y) * $k);
 
6126
                $xeT = $xeL;
 
6127
                $xeB = $xeR;
 
6128
                $yeR = $yeT;
 
6129
                $yeB = $yeL;
 
6130
                if (is_string($brd)) {
 
6131
                        // convert string to array
 
6132
                        $slen = strlen($brd);
 
6133
                        $newbrd = array();
 
6134
                        for ($i = 0; $i < $slen; ++$i) {
 
6135
                                $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
 
6136
                        }
 
6137
                        $brd = $newbrd;
 
6138
                }
 
6139
                if (isset($brd['mode'])) {
 
6140
                        $mode = $brd['mode'];
 
6141
                        unset($brd['mode']);
 
6142
                } else {
 
6143
                        $mode = 'normal';
 
6144
                }
 
6145
                foreach ($brd as $border => $style) {
 
6146
                        if (is_array($style) AND !empty($style)) {
 
6147
                                // apply border style
 
6148
                                $prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' ';
 
6149
                                $s .= $this->SetLineStyle($style, true)."\n";
 
6150
                        }
 
6151
                        switch ($mode) {
 
6152
                                case 'ext': {
 
6153
                                        $off = (($this->LineWidth / 2) * $k);
 
6154
                                        $xL = $xeL - $off;
 
6155
                                        $xR = $xeR + $off;
 
6156
                                        $yT = $yeT + $off;
 
6157
                                        $yL = $yeL - $off;
 
6158
                                        $xT = $xL;
 
6159
                                        $xB = $xR;
 
6160
                                        $yR = $yT;
 
6161
                                        $yB = $yL;
 
6162
                                        $w += $this->LineWidth;
 
6163
                                        $h += $this->LineWidth;
 
6164
                                        break;
 
6165
                                }
 
6166
                                case 'int': {
 
6167
                                        $off = ($this->LineWidth / 2) * $k;
 
6168
                                        $xL = $xeL + $off;
 
6169
                                        $xR = $xeR - $off;
 
6170
                                        $yT = $yeT - $off;
 
6171
                                        $yL = $yeL + $off;
 
6172
                                        $xT = $xL;
 
6173
                                        $xB = $xR;
 
6174
                                        $yR = $yT;
 
6175
                                        $yB = $yL;
 
6176
                                        $w -= $this->LineWidth;
 
6177
                                        $h -= $this->LineWidth;
 
6178
                                        break;
 
6179
                                }
 
6180
                                case 'normal':
 
6181
                                default: {
 
6182
                                        $xL = $xeL;
 
6183
                                        $xT = $xeT;
 
6184
                                        $xB = $xeB;
 
6185
                                        $xR = $xeR;
 
6186
                                        $yL = $yeL;
 
6187
                                        $yT = $yeT;
 
6188
                                        $yB = $yeB;
 
6189
                                        $yR = $yeR;
 
6190
                                        break;
 
6191
                                }
 
6192
                        }
 
6193
                        // draw borders by case
 
6194
                        if (strlen($border) == 4) {
 
6195
                                $s .= sprintf('%.2F %.2F %.2F %.2F re S ', $xT, $yT, ($w * $k), (-$h * $k));
 
6196
                        } elseif (strlen($border) == 3) {
 
6197
                                if (strpos($border,'B') === false) { // LTR
 
6198
                                        $s .= sprintf('%.2F %.2F m ', $xL, $yL);
 
6199
                                        $s .= sprintf('%.2F %.2F l ', $xT, $yT);
 
6200
                                        $s .= sprintf('%.2F %.2F l ', $xR, $yR);
 
6201
                                        $s .= sprintf('%.2F %.2F l ', $xB, $yB);
 
6202
                                        $s .= 'S ';
 
6203
                                } elseif (strpos($border,'L') === false) { // TRB
 
6204
                                        $s .= sprintf('%.2F %.2F m ', $xT, $yT);
 
6205
                                        $s .= sprintf('%.2F %.2F l ', $xR, $yR);
 
6206
                                        $s .= sprintf('%.2F %.2F l ', $xB, $yB);
 
6207
                                        $s .= sprintf('%.2F %.2F l ', $xL, $yL);
 
6208
                                        $s .= 'S ';
 
6209
                                } elseif (strpos($border,'T') === false) { // RBL
 
6210
                                        $s .= sprintf('%.2F %.2F m ', $xR, $yR);
 
6211
                                        $s .= sprintf('%.2F %.2F l ', $xB, $yB);
 
6212
                                        $s .= sprintf('%.2F %.2F l ', $xL, $yL);
 
6213
                                        $s .= sprintf('%.2F %.2F l ', $xT, $yT);
 
6214
                                        $s .= 'S ';
 
6215
                                } elseif (strpos($border,'R') === false) { // BLT
 
6216
                                        $s .= sprintf('%.2F %.2F m ', $xB, $yB);
 
6217
                                        $s .= sprintf('%.2F %.2F l ', $xL, $yL);
 
6218
                                        $s .= sprintf('%.2F %.2F l ', $xT, $yT);
 
6219
                                        $s .= sprintf('%.2F %.2F l ', $xR, $yR);
 
6220
                                        $s .= 'S ';
 
6221
                                }
 
6222
                        } elseif (strlen($border) == 2) {
 
6223
                                if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
 
6224
                                        $s .= sprintf('%.2F %.2F m ', $xL, $yL);
 
6225
                                        $s .= sprintf('%.2F %.2F l ', $xT, $yT);
 
6226
                                        $s .= sprintf('%.2F %.2F l ', $xR, $yR);
 
6227
                                        $s .= 'S ';
 
6228
                                } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
 
6229
                                        $s .= sprintf('%.2F %.2F m ', $xT, $yT);
 
6230
                                        $s .= sprintf('%.2F %.2F l ', $xR, $yR);
 
6231
                                        $s .= sprintf('%.2F %.2F l ', $xB, $yB);
 
6232
                                        $s .= 'S ';
 
6233
                                } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
 
6234
                                        $s .= sprintf('%.2F %.2F m ', $xR, $yR);
 
6235
                                        $s .= sprintf('%.2F %.2F l ', $xB, $yB);
 
6236
                                        $s .= sprintf('%.2F %.2F l ', $xL, $yL);
 
6237
                                        $s .= 'S ';
 
6238
                                } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
 
6239
                                        $s .= sprintf('%.2F %.2F m ', $xB, $yB);
 
6240
                                        $s .= sprintf('%.2F %.2F l ', $xL, $yL);
 
6241
                                        $s .= sprintf('%.2F %.2F l ', $xT, $yT);
 
6242
                                        $s .= 'S ';
 
6243
                                } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
 
6244
                                        $s .= sprintf('%.2F %.2F m ', $xL, $yL);
 
6245
                                        $s .= sprintf('%.2F %.2F l ', $xT, $yT);
 
6246
                                        $s .= 'S ';
 
6247
                                        $s .= sprintf('%.2F %.2F m ', $xR, $yR);
 
6248
                                        $s .= sprintf('%.2F %.2F l ', $xB, $yB);
 
6249
                                        $s .= 'S ';
 
6250
                                } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
 
6251
                                        $s .= sprintf('%.2F %.2F m ', $xT, $yT);
 
6252
                                        $s .= sprintf('%.2F %.2F l ', $xR, $yR);
 
6253
                                        $s .= 'S ';
 
6254
                                        $s .= sprintf('%.2F %.2F m ', $xB, $yB);
 
6255
                                        $s .= sprintf('%.2F %.2F l ', $xL, $yL);
 
6256
                                        $s .= 'S ';
 
6257
                                }
 
6258
                        } else { // strlen($border) == 1
 
6259
                                if (strpos($border,'L') !== false) { // L
 
6260
                                        $s .= sprintf('%.2F %.2F m ', $xL, $yL);
 
6261
                                        $s .= sprintf('%.2F %.2F l ', $xT, $yT);
 
6262
                                        $s .= 'S ';
 
6263
                                } elseif (strpos($border,'T') !== false) { // T
 
6264
                                        $s .= sprintf('%.2F %.2F m ', $xT, $yT);
 
6265
                                        $s .= sprintf('%.2F %.2F l ', $xR, $yR);
 
6266
                                        $s .= 'S ';
 
6267
                                } elseif (strpos($border,'R') !== false) { // R
 
6268
                                        $s .= sprintf('%.2F %.2F m ', $xR, $yR);
 
6269
                                        $s .= sprintf('%.2F %.2F l ', $xB, $yB);
 
6270
                                        $s .= 'S ';
 
6271
                                } elseif (strpos($border,'B') !== false) { // B
 
6272
                                        $s .= sprintf('%.2F %.2F m ', $xB, $yB);
 
6273
                                        $s .= sprintf('%.2F %.2F l ', $xL, $yL);
 
6274
                                        $s .= 'S ';
 
6275
                                }
 
6276
                        }
 
6277
                        if (is_array($style) AND !empty($style)) {
 
6278
                                // reset border style to previous value
 
6279
                                $s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n";
 
6280
                        }
 
6281
                }
 
6282
                return $s;
 
6283
        }
 
6284
 
 
6285
        /**
 
6286
         * This method allows printing text with line breaks.
 
6287
         * 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 />
 
6288
         * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
 
6289
         * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
 
6290
         * @param $h (float) Cell minimum height. The cell extends automatically if needed.
 
6291
         * @param $txt (string) String to print
 
6292
         * @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)))
 
6293
         * @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>
 
6294
         * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
 
6295
         * @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>
 
6296
         * @param $x (float) x position in user units
 
6297
         * @param $y (float) y position in user units
 
6298
         * @param $reseth (boolean) if true reset the last cell height (default true).
 
6299
         * @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.
 
6300
         * @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.
 
6301
         * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
 
6302
         * @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.
 
6303
         * @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.
 
6304
         * @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).
 
6305
         * @return int Return the number of cells or 1 for html mode.
 
6306
         * @public
 
6307
         * @since 1.3
 
6308
         * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
 
6309
         */
 
6310
        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) {
 
6311
                $prev_cell_margin = $this->cell_margin;
 
6312
                $prev_cell_padding = $this->cell_padding;
 
6313
                // adjust internal padding
 
6314
                $this->adjustCellPadding($border);
 
6315
                $mc_padding = $this->cell_padding;
 
6316
                $mc_margin = $this->cell_margin;
 
6317
                $this->cell_padding['T'] = 0;
 
6318
                $this->cell_padding['B'] = 0;
 
6319
                $this->setCellMargins(0, 0, 0, 0);
 
6320
                if ($this->empty_string($this->lasth) OR $reseth) {
 
6321
                        // reset row height
 
6322
                        $this->resetLastH();
 
6323
                }
 
6324
                if (!$this->empty_string($y)) {
 
6325
                        $this->SetY($y);
 
6326
                } else {
 
6327
                        $y = $this->GetY();
 
6328
                }
 
6329
                $resth = 0;
 
6330
                if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) {
 
6331
                        // spit cell in more pages/columns
 
6332
                        $newh = ($this->PageBreakTrigger - $y);
 
6333
                        $resth = ($h - $newh); // cell to be printed on the next page/column
 
6334
                        $h = $newh;
 
6335
                }
 
6336
                // get current page number
 
6337
                $startpage = $this->page;
 
6338
                // get current column
 
6339
                $startcolumn = $this->current_column;
 
6340
                if (!$this->empty_string($x)) {
 
6341
                        $this->SetX($x);
 
6342
                } else {
 
6343
                        $x = $this->GetX();
 
6344
                }
 
6345
                // check page for no-write regions and adapt page margins if necessary
 
6346
                list($x, $y) = $this->checkPageRegions(0, $x, $y);
 
6347
                // apply margins
 
6348
                $oy = $y + $mc_margin['T'];
 
6349
                if ($this->rtl) {
 
6350
                        $ox = $this->w - $x - $mc_margin['R'];
 
6351
                } else {
 
6352
                        $ox = $x + $mc_margin['L'];
 
6353
                }
 
6354
                $this->x = $ox;
 
6355
                $this->y = $oy;
 
6356
                // set width
 
6357
                if ($this->empty_string($w) OR ($w <= 0)) {
 
6358
                        if ($this->rtl) {
 
6359
                                $w = $this->x - $this->lMargin - $mc_margin['L'];
 
6360
                        } else {
 
6361
                                $w = $this->w - $this->x - $this->rMargin - $mc_margin['R'];
 
6362
                        }
 
6363
                }
 
6364
                // store original margin values
 
6365
                $lMargin = $this->lMargin;
 
6366
                $rMargin = $this->rMargin;
 
6367
                if ($this->rtl) {
 
6368
                        $this->rMargin = $this->w - $this->x;
 
6369
                        $this->lMargin = $this->x - $w;
 
6370
                } else {
 
6371
                        $this->lMargin = $this->x;
 
6372
                        $this->rMargin = $this->w - $this->x - $w;
 
6373
                }
 
6374
                if ($autopadding) {
 
6375
                        // add top padding
 
6376
                        $this->y += $mc_padding['T'];
 
6377
                }
 
6378
                if ($ishtml) { // ******* Write HTML text
 
6379
                        $this->writeHTML($txt, true, false, $reseth, true, $align);
 
6380
                        $nl = 1;
 
6381
                } else { // ******* Write simple text
 
6382
                        $prev_FontSizePt = $this->FontSizePt;
 
6383
                        // vertical alignment
 
6384
                        if ($maxh > 0) {
 
6385
                                // get text height
 
6386
                                $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
 
6387
                                if ($fitcell) {
 
6388
                                        // try to reduce font size to fit text on cell (use a quick search algorithm)
 
6389
                                        $fmin = 1;
 
6390
                                        $fmax = $this->FontSizePt;
 
6391
                                        $prev_text_height = $text_height;
 
6392
                                        $maxit = 100; // max number of iterations
 
6393
                                        while ($maxit > 0) {
 
6394
                                                $fmid = (($fmax + $fmin) / 2);
 
6395
                                                $this->SetFontSize($fmid, false);
 
6396
                                                $this->resetLastH();
 
6397
                                                $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
 
6398
                                                if (($text_height == $maxh) OR (($text_height < $maxh) AND ($fmin >= ($fmax - 0.01)))) {
 
6399
                                                        break;
 
6400
                                                } elseif ($text_height < $maxh) {
 
6401
                                                        $fmin = $fmid;
 
6402
                                                } else {
 
6403
                                                        $fmax = $fmid;
 
6404
                                                }
 
6405
                                                --$maxit;
 
6406
                                        }
 
6407
                                        $this->SetFontSize($this->FontSizePt);
 
6408
                                }
 
6409
                                if ($text_height < $maxh) {
 
6410
                                        if ($valign == 'M') {
 
6411
                                                // text vertically centered
 
6412
                                                $this->y += (($maxh - $text_height) / 2);
 
6413
                                        } elseif ($valign == 'B') {
 
6414
                                                // text vertically aligned on bottom
 
6415
                                                $this->y += ($maxh - $text_height);
 
6416
                                        }
 
6417
                                }
 
6418
                        }
 
6419
                        $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
 
6420
                        if ($fitcell) {
 
6421
                                // restore font size
 
6422
                                $this->SetFontSize($prev_FontSizePt);
 
6423
                        }
 
6424
                }
 
6425
                if ($autopadding) {
 
6426
                        // add bottom padding
 
6427
                        $this->y += $mc_padding['B'];
 
6428
                }
 
6429
                // Get end-of-text Y position
 
6430
                $currentY = $this->y;
 
6431
                // get latest page number
 
6432
                $endpage = $this->page;
 
6433
                if ($resth > 0) {
 
6434
                        $skip = ($endpage - $startpage);
 
6435
                        $tmpresth = $resth;
 
6436
                        while ($tmpresth > 0) {
 
6437
                                if ($skip <= 0) {
 
6438
                                        // add a page (or trig AcceptPageBreak() for multicolumn mode)
 
6439
                                        $this->checkPageBreak($this->PageBreakTrigger + 1);
 
6440
                                }
 
6441
                                if ($this->num_columns > 1) {
 
6442
                                        $tmpresth -= ($this->h - $this->y - $this->bMargin);
 
6443
                                } else {
 
6444
                                        $tmpresth -= ($this->h - $this->tMargin - $this->bMargin);
 
6445
                                }
 
6446
                                --$skip;
 
6447
                        }
 
6448
                        $currentY = $this->y;
 
6449
                        $endpage = $this->page;
 
6450
                }
 
6451
                // get latest column
 
6452
                $endcolumn = $this->current_column;
 
6453
                if ($this->num_columns == 0) {
 
6454
                        $this->num_columns = 1;
 
6455
                }
 
6456
                // get border modes
 
6457
                $border_start = $this->getBorderMode($border, $position='start');
 
6458
                $border_end = $this->getBorderMode($border, $position='end');
 
6459
                $border_middle = $this->getBorderMode($border, $position='middle');
 
6460
                // design borders around HTML cells.
 
6461
                for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
 
6462
                        $ccode = '';
 
6463
                        $this->setPage($page);
 
6464
                        if ($this->num_columns < 2) {
 
6465
                                // single-column mode
 
6466
                                $this->SetX($x);
 
6467
                                $this->y = $this->tMargin;
 
6468
                        }
 
6469
                        // account for margin changes
 
6470
                        if ($page > $startpage) {
 
6471
                                if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
 
6472
                                        $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
 
6473
                                } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
 
6474
                                        $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
 
6475
                                }
 
6476
                        }
 
6477
                        if ($startpage == $endpage) {
 
6478
                                // single page
 
6479
                                for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
 
6480
                                        $this->selectColumn($column);
 
6481
                                        if ($this->rtl) {
 
6482
                                                $this->x -= $mc_margin['R'];
 
6483
                                        } else {
 
6484
                                                $this->x += $mc_margin['L'];
 
6485
                                        }
 
6486
                                        if ($startcolumn == $endcolumn) { // single column
 
6487
                                                $cborder = $border;
 
6488
                                                $h = max($h, ($currentY - $oy));
 
6489
                                                $this->y = $oy;
 
6490
                                        } elseif ($column == $startcolumn) { // first column
 
6491
                                                $cborder = $border_start;
 
6492
                                                $this->y = $oy;
 
6493
                                                $h = $this->h - $this->y - $this->bMargin;
 
6494
                                        } elseif ($column == $endcolumn) { // end column
 
6495
                                                $cborder = $border_end;
 
6496
                                                $h = $currentY - $this->y;
 
6497
                                                if ($resth > $h) {
 
6498
                                                        $h = $resth;
 
6499
                                                }
 
6500
                                        } else { // middle column
 
6501
                                                $cborder = $border_middle;
 
6502
                                                $h = $this->h - $this->y - $this->bMargin;
 
6503
                                                $resth -= $h;
 
6504
                                        }
 
6505
                                        $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
 
6506
                                } // end for each column
 
6507
                        } elseif ($page == $startpage) { // first page
 
6508
                                for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
 
6509
                                        $this->selectColumn($column);
 
6510
                                        if ($this->rtl) {
 
6511
                                                $this->x -= $mc_margin['R'];
 
6512
                                        } else {
 
6513
                                                $this->x += $mc_margin['L'];
 
6514
                                        }
 
6515
                                        if ($column == $startcolumn) { // first column
 
6516
                                                $cborder = $border_start;
 
6517
                                                $this->y = $oy;
 
6518
                                                $h = $this->h - $this->y - $this->bMargin;
 
6519
                                        } else { // middle column
 
6520
                                                $cborder = $border_middle;
 
6521
                                                $h = $this->h - $this->y - $this->bMargin;
 
6522
                                                $resth -= $h;
 
6523
                                        }
 
6524
                                        $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
 
6525
                                } // end for each column
 
6526
                        } elseif ($page == $endpage) { // last page
 
6527
                                for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
 
6528
                                        $this->selectColumn($column);
 
6529
                                        if ($this->rtl) {
 
6530
                                                $this->x -= $mc_margin['R'];
 
6531
                                        } else {
 
6532
                                                $this->x += $mc_margin['L'];
 
6533
                                        }
 
6534
                                        if ($column == $endcolumn) {
 
6535
                                                // end column
 
6536
                                                $cborder = $border_end;
 
6537
                                                $h = $currentY - $this->y;
 
6538
                                                if ($resth > $h) {
 
6539
                                                        $h = $resth;
 
6540
                                                }
 
6541
                                        } else {
 
6542
                                                // middle column
 
6543
                                                $cborder = $border_middle;
 
6544
                                                $h = $this->h - $this->y - $this->bMargin;
 
6545
                                                $resth -= $h;
 
6546
                                        }
 
6547
                                        $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
 
6548
                                } // end for each column
 
6549
                        } else { // middle page
 
6550
                                for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
 
6551
                                        $this->selectColumn($column);
 
6552
                                        if ($this->rtl) {
 
6553
                                                $this->x -= $mc_margin['R'];
 
6554
                                        } else {
 
6555
                                                $this->x += $mc_margin['L'];
 
6556
                                        }
 
6557
                                        $cborder = $border_middle;
 
6558
                                        $h = $this->h - $this->y - $this->bMargin;
 
6559
                                        $resth -= $h;
 
6560
                                        $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
 
6561
                                } // end for each column
 
6562
                        }
 
6563
                        if ($cborder OR $fill) {
 
6564
                                $offsetlen = strlen($ccode);
 
6565
                                // draw border and fill
 
6566
                                if ($this->inxobj) {
 
6567
                                        // we are inside an XObject template
 
6568
                                        if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
 
6569
                                                $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
 
6570
                                                $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
 
6571
                                                $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
 
6572
                                        } else {
 
6573
                                                $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
 
6574
                                                $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
 
6575
                                        }
 
6576
                                        $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
 
6577
                                        $pstart = substr($pagebuff, 0, $pagemark);
 
6578
                                        $pend = substr($pagebuff, $pagemark);
 
6579
                                        $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
 
6580
                                } else {
 
6581
                                        if (end($this->transfmrk[$this->page]) !== false) {
 
6582
                                                $pagemarkkey = key($this->transfmrk[$this->page]);
 
6583
                                                $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
 
6584
                                                $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen;
 
6585
                                        } elseif ($this->InFooter) {
 
6586
                                                $pagemark = $this->footerpos[$this->page];
 
6587
                                                $this->footerpos[$this->page] += $offsetlen;
 
6588
                                        } else {
 
6589
                                                $pagemark = $this->intmrk[$this->page];
 
6590
                                                $this->intmrk[$this->page] += $offsetlen;
 
6591
                                        }
 
6592
                                        $pagebuff = $this->getPageBuffer($this->page);
 
6593
                                        $pstart = substr($pagebuff, 0, $pagemark);
 
6594
                                        $pend = substr($pagebuff, $pagemark);
 
6595
                                        $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
 
6596
                                }
 
6597
                        }
 
6598
                } // end for each page
 
6599
                // Get end-of-cell Y position
 
6600
                $currentY = $this->GetY();
 
6601
                // restore previous values
 
6602
                if ($this->num_columns > 1) {
 
6603
                        $this->selectColumn();
 
6604
                } else {
 
6605
                        // restore original margins
 
6606
                        $this->lMargin = $lMargin;
 
6607
                        $this->rMargin = $rMargin;
 
6608
                        if ($this->page > $startpage) {
 
6609
                                // check for margin variations between pages (i.e. booklet mode)
 
6610
                                $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']);
 
6611
                                $dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']);
 
6612
                                if (($dl != 0) OR ($dr != 0)) {
 
6613
                                        $this->lMargin += $dl;
 
6614
                                        $this->rMargin += $dr;
 
6615
                                }
 
6616
                        }
 
6617
                }
 
6618
                if ($ln > 0) {
 
6619
                        //Go to the beginning of the next line
 
6620
                        $this->SetY($currentY + $mc_margin['B']);
 
6621
                        if ($ln == 2) {
 
6622
                                $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
 
6623
                        }
 
6624
                } else {
 
6625
                        // go left or right by case
 
6626
                        $this->setPage($startpage);
 
6627
                        $this->y = $y;
 
6628
                        $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
 
6629
                }
 
6630
                $this->setContentMark();
 
6631
                $this->cell_padding = $prev_cell_padding;
 
6632
                $this->cell_margin = $prev_cell_margin;
 
6633
                return $nl;
 
6634
        }
 
6635
 
 
6636
        /**
 
6637
         * Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages)
 
6638
         * @param $brd (mixed) Indicates if borders must be drawn around the cell block. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
 
6639
         * @param $position (string) multicell position: 'start', 'middle', 'end'
 
6640
         * @return border mode array
 
6641
         * @protected
 
6642
         * @since 4.4.002 (2008-12-09)
 
6643
         */
 
6644
        protected function getBorderMode($brd, $position='start') {
 
6645
                if ((!$this->opencell) OR empty($brd)) {
 
6646
                        return $brd;
 
6647
                }
 
6648
                if ($brd == 1) {
 
6649
                        $brd = 'LTRB';
 
6650
                }
 
6651
                if (is_string($brd)) {
 
6652
                        // convert string to array
 
6653
                        $slen = strlen($brd);
 
6654
                        $newbrd = array();
 
6655
                        for ($i = 0; $i < $slen; ++$i) {
 
6656
                                $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
 
6657
                        }
 
6658
                        $brd = $newbrd;
 
6659
                }
 
6660
                foreach ($brd as $border => $style) {
 
6661
                        switch ($position) {
 
6662
                                case 'start': {
 
6663
                                        if (strpos($border, 'B') !== false) {
 
6664
                                                // remove bottom line
 
6665
                                                $newkey = str_replace('B', '', $border);
 
6666
                                                if (strlen($newkey) > 0) {
 
6667
                                                        $brd[$newkey] = $style;
 
6668
                                                }
 
6669
                                                unset($brd[$border]);
 
6670
                                        }
 
6671
                                        break;
 
6672
                                }
 
6673
                                case 'middle': {
 
6674
                                        if (strpos($border, 'B') !== false) {
 
6675
                                                // remove bottom line
 
6676
                                                $newkey = str_replace('B', '', $border);
 
6677
                                                if (strlen($newkey) > 0) {
 
6678
                                                        $brd[$newkey] = $style;
 
6679
                                                }
 
6680
                                                unset($brd[$border]);
 
6681
                                                $border = $newkey;
 
6682
                                        }
 
6683
                                        if (strpos($border, 'T') !== false) {
 
6684
                                                // remove bottom line
 
6685
                                                $newkey = str_replace('T', '', $border);
 
6686
                                                if (strlen($newkey) > 0) {
 
6687
                                                        $brd[$newkey] = $style;
 
6688
                                                }
 
6689
                                                unset($brd[$border]);
 
6690
                                        }
 
6691
                                        break;
 
6692
                                }
 
6693
                                case 'end': {
 
6694
                                        if (strpos($border, 'T') !== false) {
 
6695
                                                // remove bottom line
 
6696
                                                $newkey = str_replace('T', '', $border);
 
6697
                                                if (strlen($newkey) > 0) {
 
6698
                                                        $brd[$newkey] = $style;
 
6699
                                                }
 
6700
                                                unset($brd[$border]);
 
6701
                                        }
 
6702
                                        break;
 
6703
                                }
 
6704
                        }
 
6705
                }
 
6706
                return $brd;
 
6707
        }
 
6708
 
 
6709
        /**
 
6710
         * This method return the estimated number of lines for print a simple text string using Multicell() method.
 
6711
         * @param $txt (string) String for calculating his height
 
6712
         * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
 
6713
         * @param $reseth (boolean) if true reset the last cell height (default false).
 
6714
         * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
 
6715
         * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
 
6716
         * @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)))
 
6717
         * @return float Return the minimal height needed for multicell method for printing the $txt param.
 
6718
         * @author Alexander Escalona Fern�ndez, Nicola Asuni
 
6719
         * @public
 
6720
         * @since 4.5.011
 
6721
         */
 
6722
        public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
 
6723
                if ($txt === '') {
 
6724
                        // empty string
 
6725
                        return 1;
 
6726
                }
 
6727
                // adjust internal padding
 
6728
                $prev_cell_padding = $this->cell_padding;
 
6729
                $prev_lasth = $this->lasth;
 
6730
                if (is_array($cellpadding)) {
 
6731
                        $this->cell_padding = $cellpadding;
 
6732
                }
 
6733
                $this->adjustCellPadding($border);
 
6734
                if ($this->empty_string($w) OR ($w <= 0)) {
 
6735
                        if ($this->rtl) {
 
6736
                                $w = $this->x - $this->lMargin;
 
6737
                        } else {
 
6738
                                $w = $this->w - $this->rMargin - $this->x;
 
6739
                        }
 
6740
                }
 
6741
                $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
 
6742
                if ($reseth) {
 
6743
                        // reset row height
 
6744
                        $this->resetLastH();
 
6745
                }
 
6746
                $lines = 1;
 
6747
                $sum = 0;
 
6748
                $chars = $this->utf8Bidi($this->UTF8StringToArray($txt), $txt, $this->tmprtl);
 
6749
                $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
 
6750
                $length = count($chars);
 
6751
                $lastSeparator = -1;
 
6752
                for ($i = 0; $i < $length; ++$i) {
 
6753
                        $charWidth = $charsWidth[$i];
 
6754
                        if (preg_match($this->re_spaces, $this->unichr($chars[$i]))) {
 
6755
                                $lastSeparator = $i;
 
6756
                        }
 
6757
                        if ((($sum + $charWidth) > $wmax) OR ($chars[$i] == 10)) {
 
6758
                                ++$lines;
 
6759
                                if ($chars[$i] == 10) {
 
6760
                                        $lastSeparator = -1;
 
6761
                                        $sum = 0;
 
6762
                                } elseif ($lastSeparator != -1) {
 
6763
                                        $i = $lastSeparator;
 
6764
                                        $lastSeparator = -1;
 
6765
                                        $sum = 0;
 
6766
                                } else {
 
6767
                                        $sum = $charWidth;
 
6768
                                }
 
6769
                        } else {
 
6770
                                $sum += $charWidth;
 
6771
                        }
 
6772
                }
 
6773
                if ($chars[($length - 1)] == 10) {
 
6774
                        --$lines;
 
6775
                }
 
6776
                $this->cell_padding = $prev_cell_padding;
 
6777
                $this->lasth = $prev_lasth;
 
6778
                return $lines;
 
6779
        }
 
6780
 
 
6781
        /**
 
6782
         * This method return the estimated height needed for printing a simple text string using the Multicell() method.
 
6783
         * Generally, if you want to know the exact height for a block of content you can use the following alternative technique:
 
6784
         * @pre
 
6785
         *  // store current object
 
6786
         *  $pdf->startTransaction();
 
6787
         *  // store starting values
 
6788
         *  $start_y = $pdf->GetY();
 
6789
         *  $start_page = $pdf->getPage();
 
6790
         *  // call your printing functions with your parameters
 
6791
         *  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
6792
         *  $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);
 
6793
         *  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
6794
         *  // get the new Y
 
6795
         *  $end_y = $pdf->GetY();
 
6796
         *  $end_page = $pdf->getPage();
 
6797
         *  // calculate height
 
6798
         *  $height = 0;
 
6799
         *  if ($end_page == $start_page) {
 
6800
         *      $height = $end_y - $start_y;
 
6801
         *  } else {
 
6802
         *      for ($page=$start_page; $page <= $end_page; ++$page) {
 
6803
         *              $this->setPage($page);
 
6804
         *              if ($page == $start_page) {
 
6805
         *                      // first page
 
6806
         *                      $height = $this->h - $start_y - $this->bMargin;
 
6807
         *              } elseif ($page == $end_page) {
 
6808
         *                      // last page
 
6809
         *                      $height = $end_y - $this->tMargin;
 
6810
         *              } else {
 
6811
         *                      $height = $this->h - $this->tMargin - $this->bMargin;
 
6812
         *              }
 
6813
         *      }
 
6814
         *  }
 
6815
         *  // restore previous object
 
6816
         *  $pdf = $pdf->rollbackTransaction();
 
6817
         *
 
6818
         * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
 
6819
         * @param $txt (string) String for calculating his height
 
6820
         * @param $reseth (boolean) if true reset the last cell height (default false).
 
6821
         * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
 
6822
         * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
 
6823
         * @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)))
 
6824
         * @return float Return the minimal height needed for multicell method for printing the $txt param.
 
6825
         * @author Nicola Asuni, Alexander Escalona Fern�ndez
 
6826
         * @public
 
6827
         */
 
6828
        public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
 
6829
                // adjust internal padding
 
6830
                $prev_cell_padding = $this->cell_padding;
 
6831
                $prev_lasth = $this->lasth;
 
6832
                if (is_array($cellpadding)) {
 
6833
                        $this->cell_padding = $cellpadding;
 
6834
                }
 
6835
                $this->adjustCellPadding($border);
 
6836
                $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
 
6837
                $height = $lines * ($this->FontSize * $this->cell_height_ratio);
 
6838
                if ($autopadding) {
 
6839
                        // add top and bottom padding
 
6840
                        $height += ($this->cell_padding['T'] + $this->cell_padding['B']);
 
6841
                }
 
6842
                $this->cell_padding = $prev_cell_padding;
 
6843
                $this->lasth = $prev_lasth;
 
6844
                return $height;
 
6845
        }
 
6846
 
 
6847
        /**
 
6848
         * This method prints text from the current position.<br />
 
6849
         * @param $h (float) Line height
 
6850
         * @param $txt (string) String to print
 
6851
         * @param $link (mixed) URL or identifier returned by AddLink()
 
6852
         * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
 
6853
         * @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>
 
6854
         * @param $ln (boolean) if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
 
6855
         * @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.
 
6856
         * @param $firstline (boolean) if true prints only the first line and return the remaining string.
 
6857
         * @param $firstblock (boolean) if true the string is the starting of a line.
 
6858
         * @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.
 
6859
         * @param $wadj (float) first line width will be reduced by this amount (used in HTML mode).
 
6860
         * @param $margin (array) margin array of the parent container
 
6861
         * @return mixed Return the number of cells or the remaining string if $firstline = true.
 
6862
         * @public
 
6863
         * @since 1.5
 
6864
         */
 
6865
        public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') {
 
6866
                // check page for no-write regions and adapt page margins if necessary
 
6867
                list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
 
6868
                if (strlen($txt) == 0) {
 
6869
                        // fix empty text
 
6870
                        $txt = ' ';
 
6871
                }
 
6872
                if ($margin === '') {
 
6873
                        // set default margins
 
6874
                        $margin = $this->cell_margin;
 
6875
                }
 
6876
                // remove carriage returns
 
6877
                $s = str_replace("\r", '', $txt);
 
6878
                // check if string contains arabic text
 
6879
                if (preg_match($this->unicode->uni_RE_PATTERN_ARABIC, $s)) {
 
6880
                        $arabic = true;
 
6881
                } else {
 
6882
                        $arabic = false;
 
6883
                }
 
6884
                // check if string contains RTL text
 
6885
                if ($arabic OR ($this->tmprtl == 'R') OR preg_match($this->unicode->uni_RE_PATTERN_RTL, $s)) {
 
6886
                        $rtlmode = true;
 
6887
                } else {
 
6888
                        $rtlmode = false;
 
6889
                }
 
6890
                // get a char width
 
6891
                $chrwidth = $this->GetCharWidth(46); // dot character
 
6892
                // get array of unicode values
 
6893
                $chars = $this->UTF8StringToArray($s);
 
6894
                // get array of chars
 
6895
                $uchars = $this->UTF8ArrayToUniArray($chars);
 
6896
                // get the number of characters
 
6897
                $nb = count($chars);
 
6898
                // replacement for SHY character (minus symbol)
 
6899
                $shy_replacement = 45;
 
6900
                $shy_replacement_char = $this->unichr($shy_replacement);
 
6901
                // widht for SHY replacement
 
6902
                $shy_replacement_width = $this->GetCharWidth($shy_replacement);
 
6903
                // max Y
 
6904
                $maxy = $this->y + $maxh - $h - $this->cell_padding['T'] - $this->cell_padding['B'];
 
6905
                // page width
 
6906
                $pw = $w = $this->w - $this->lMargin - $this->rMargin;
 
6907
                // calculate remaining line width ($w)
 
6908
                if ($this->rtl) {
 
6909
                        $w = $this->x - $this->lMargin;
 
6910
                } else {
 
6911
                        $w = $this->w - $this->rMargin - $this->x;
 
6912
                }
 
6913
                // max column width
 
6914
                $wmax = $w - $wadj;
 
6915
                if (!$firstline) {
 
6916
                        $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']);
 
6917
                }
 
6918
                if ((!$firstline) AND (($chrwidth > $wmax) OR ($this->GetCharWidth($chars[0]) > $wmax))) {
 
6919
                        // a single character do not fit on column
 
6920
                        return '';
 
6921
                }
 
6922
                // minimum row height
 
6923
                $row_height = max($h, $this->FontSize * $this->cell_height_ratio);
 
6924
                $start_page = $this->page;
 
6925
                $i = 0; // character position
 
6926
                $j = 0; // current starting position
 
6927
                $sep = -1; // position of the last blank space
 
6928
                $shy = false; // true if the last blank is a soft hypen (SHY)
 
6929
                $l = 0; // current string length
 
6930
                $nl = 0; //number of lines
 
6931
                $linebreak = false;
 
6932
                $pc = 0; // previous character
 
6933
                // for each character
 
6934
                while ($i < $nb) {
 
6935
                        if (($maxh > 0) AND ($this->y >= $maxy) ) {
 
6936
                                break;
 
6937
                        }
 
6938
                        //Get the current character
 
6939
                        $c = $chars[$i];
 
6940
                        if ($c == 10) { // 10 = "\n" = new line
 
6941
                                //Explicit line break
 
6942
                                if ($align == 'J') {
 
6943
                                        if ($this->rtl) {
 
6944
                                                $talign = 'R';
 
6945
                                        } else {
 
6946
                                                $talign = 'L';
 
6947
                                        }
 
6948
                                } else {
 
6949
                                        $talign = $align;
 
6950
                                }
 
6951
                                $tmpstr = $this->UniArrSubString($uchars, $j, $i);
 
6952
                                if ($firstline) {
 
6953
                                        $startx = $this->x;
 
6954
                                        $tmparr = array_slice($chars, $j, ($i - $j));
 
6955
                                        if ($rtlmode) {
 
6956
                                                $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
 
6957
                                        }
 
6958
                                        $linew = $this->GetArrStringWidth($tmparr);
 
6959
                                        unset($tmparr);
 
6960
                                        if ($this->rtl) {
 
6961
                                                $this->endlinex = $startx - $linew;
 
6962
                                        } else {
 
6963
                                                $this->endlinex = $startx + $linew;
 
6964
                                        }
 
6965
                                        $w = $linew;
 
6966
                                        $tmpcellpadding = $this->cell_padding;
 
6967
                                        if ($maxh == 0) {
 
6968
                                                $this->SetCellPadding(0);
 
6969
                                        }
 
6970
                                }
 
6971
                                if ($firstblock AND $this->isRTLTextDir()) {
 
6972
                                        $tmpstr = $this->stringRightTrim($tmpstr);
 
6973
                                }
 
6974
                                // Skip newlines at the begining of a page or column
 
6975
                                if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) {
 
6976
                                        $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
 
6977
                                }
 
6978
                                unset($tmpstr);
 
6979
                                if ($firstline) {
 
6980
                                        $this->cell_padding = $tmpcellpadding;
 
6981
                                        return ($this->UniArrSubString($uchars, $i));
 
6982
                                }
 
6983
                                ++$nl;
 
6984
                                $j = $i + 1;
 
6985
                                $l = 0;
 
6986
                                $sep = -1;
 
6987
                                $shy = false;
 
6988
                                // account for margin changes
 
6989
                                if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
 
6990
                                        $this->AcceptPageBreak();
 
6991
                                        if ($this->rtl) {
 
6992
                                                $this->x -= $margin['R'];
 
6993
                                        } else {
 
6994
                                                $this->x += $margin['L'];
 
6995
                                        }
 
6996
                                        $this->lMargin += $margin['L'];
 
6997
                                        $this->rMargin += $margin['R'];
 
6998
                                }
 
6999
                                $w = $this->getRemainingWidth();
 
7000
                                $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
 
7001
                        } else {
 
7002
                                // 160 is the non-breaking space.
 
7003
                                // 173 is SHY (Soft Hypen).
 
7004
                                // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
 
7005
                                // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
 
7006
                                // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
 
7007
                                if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) {
 
7008
                                        // update last blank space position
 
7009
                                        $sep = $i;
 
7010
                                        // check if is a SHY
 
7011
                                        if ($c == 173) {
 
7012
                                                $shy = true;
 
7013
                                                if ($pc == 45) {
 
7014
                                                        $tmp_shy_replacement_width = 0;
 
7015
                                                        $tmp_shy_replacement_char = '';
 
7016
                                                } else {
 
7017
                                                        $tmp_shy_replacement_width = $shy_replacement_width;
 
7018
                                                        $tmp_shy_replacement_char = $shy_replacement_char;
 
7019
                                                }
 
7020
                                        } else {
 
7021
                                                $shy = false;
 
7022
                                        }
 
7023
                                }
 
7024
                                // update string length
 
7025
                                if ($this->isUnicodeFont() AND ($arabic)) {
 
7026
                                        // with bidirectional algorithm some chars may be changed affecting the line length
 
7027
                                        // *** very slow ***
 
7028
                                        $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl));
 
7029
                                } else {
 
7030
                                        $l += $this->GetCharWidth($c);
 
7031
                                }
 
7032
                                if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) ) {
 
7033
                                        // we have reached the end of column
 
7034
                                        if ($sep == -1) {
 
7035
                                                // check if the line was already started
 
7036
                                                if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth)))
 
7037
                                                        OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) {
 
7038
                                                        // print a void cell and go to next line
 
7039
                                                        $this->Cell($w, $h, '', 0, 1);
 
7040
                                                        $linebreak = true;
 
7041
                                                        if ($firstline) {
 
7042
                                                                return ($this->UniArrSubString($uchars, $j));
 
7043
                                                        }
 
7044
                                                } else {
 
7045
                                                        // truncate the word because do not fit on column
 
7046
                                                        $tmpstr = $this->UniArrSubString($uchars, $j, $i);
 
7047
                                                        if ($firstline) {
 
7048
                                                                $startx = $this->x;
 
7049
                                                                $tmparr = array_slice($chars, $j, ($i - $j));
 
7050
                                                                if ($rtlmode) {
 
7051
                                                                        $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
 
7052
                                                                }
 
7053
                                                                $linew = $this->GetArrStringWidth($tmparr);
 
7054
                                                                unset($tmparr);
 
7055
                                                                if ($this->rtl) {
 
7056
                                                                        $this->endlinex = $startx - $linew;
 
7057
                                                                } else {
 
7058
                                                                        $this->endlinex = $startx + $linew;
 
7059
                                                                }
 
7060
                                                                $w = $linew;
 
7061
                                                                $tmpcellpadding = $this->cell_padding;
 
7062
                                                                if ($maxh == 0) {
 
7063
                                                                        $this->SetCellPadding(0);
 
7064
                                                                }
 
7065
                                                        }
 
7066
                                                        if ($firstblock AND $this->isRTLTextDir()) {
 
7067
                                                                $tmpstr = $this->stringRightTrim($tmpstr);
 
7068
                                                        }
 
7069
                                                        $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
 
7070
                                                        unset($tmpstr);
 
7071
                                                        if ($firstline) {
 
7072
                                                                $this->cell_padding = $tmpcellpadding;
 
7073
                                                                return ($this->UniArrSubString($uchars, $i));
 
7074
                                                        }
 
7075
                                                        $j = $i;
 
7076
                                                        --$i;
 
7077
                                                }
 
7078
                                        } else {
 
7079
                                                // word wrapping
 
7080
                                                if ($this->rtl AND (!$firstblock) AND ($sep < $i)) {
 
7081
                                                        $endspace = 1;
 
7082
                                                } else {
 
7083
                                                        $endspace = 0;
 
7084
                                                }
 
7085
                                                // check the length of the next string
 
7086
                                                $strrest = $this->UniArrSubString($uchars, ($sep + $endspace));
 
7087
                                                $nextstr = preg_split('/'.$this->re_space['p'].'/'.$this->re_space['m'], $this->stringTrim($strrest));
 
7088
                                                if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) {
 
7089
                                                        // truncate the word because do not fit on a full page width
 
7090
                                                        $tmpstr = $this->UniArrSubString($uchars, $j, $i);
 
7091
                                                        if ($firstline) {
 
7092
                                                                $startx = $this->x;
 
7093
                                                                $tmparr = array_slice($chars, $j, ($i - $j));
 
7094
                                                                if ($rtlmode) {
 
7095
                                                                        $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
 
7096
                                                                }
 
7097
                                                                $linew = $this->GetArrStringWidth($tmparr);
 
7098
                                                                unset($tmparr);
 
7099
                                                                if ($this->rtl) {
 
7100
                                                                        $this->endlinex = $startx - $linew;
 
7101
                                                                } else {
 
7102
                                                                        $this->endlinex = $startx + $linew;
 
7103
                                                                }
 
7104
                                                                $w = $linew;
 
7105
                                                                $tmpcellpadding = $this->cell_padding;
 
7106
                                                                if ($maxh == 0) {
 
7107
                                                                        $this->SetCellPadding(0);
 
7108
                                                                }
 
7109
                                                        }
 
7110
                                                        if ($firstblock AND $this->isRTLTextDir()) {
 
7111
                                                                $tmpstr = $this->stringRightTrim($tmpstr);
 
7112
                                                        }
 
7113
                                                        $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
 
7114
                                                        unset($tmpstr);
 
7115
                                                        if ($firstline) {
 
7116
                                                                $this->cell_padding = $tmpcellpadding;
 
7117
                                                                return ($this->UniArrSubString($uchars, $i));
 
7118
                                                        }
 
7119
                                                        $j = $i;
 
7120
                                                        --$i;
 
7121
                                                } else {
 
7122
                                                        // word wrapping
 
7123
                                                        if ($shy) {
 
7124
                                                                // add hypen (minus symbol) at the end of the line
 
7125
                                                                $shy_width = $tmp_shy_replacement_width;
 
7126
                                                                if ($this->rtl) {
 
7127
                                                                        $shy_char_left = $tmp_shy_replacement_char;
 
7128
                                                                        $shy_char_right = '';
 
7129
                                                                } else {
 
7130
                                                                        $shy_char_left = '';
 
7131
                                                                        $shy_char_right = $tmp_shy_replacement_char;
 
7132
                                                                }
 
7133
                                                        } else {
 
7134
                                                                $shy_width = 0;
 
7135
                                                                $shy_char_left = '';
 
7136
                                                                $shy_char_right = '';
 
7137
                                                        }
 
7138
                                                        $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace));
 
7139
                                                        if ($firstline) {
 
7140
                                                                $startx = $this->x;
 
7141
                                                                $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j));
 
7142
                                                                if ($rtlmode) {
 
7143
                                                                        $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
 
7144
                                                                }
 
7145
                                                                $linew = $this->GetArrStringWidth($tmparr);
 
7146
                                                                unset($tmparr);
 
7147
                                                                if ($this->rtl) {
 
7148
                                                                        $this->endlinex = $startx - $linew - $shy_width;
 
7149
                                                                } else {
 
7150
                                                                        $this->endlinex = $startx + $linew + $shy_width;
 
7151
                                                                }
 
7152
                                                                $w = $linew;
 
7153
                                                                $tmpcellpadding = $this->cell_padding;
 
7154
                                                                if ($maxh == 0) {
 
7155
                                                                        $this->SetCellPadding(0);
 
7156
                                                                }
 
7157
                                                        }
 
7158
                                                        // print the line
 
7159
                                                        if ($firstblock AND $this->isRTLTextDir()) {
 
7160
                                                                $tmpstr = $this->stringRightTrim($tmpstr);
 
7161
                                                        }
 
7162
                                                        $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
 
7163
                                                        unset($tmpstr);
 
7164
                                                        if ($firstline) {
 
7165
                                                                // return the remaining text
 
7166
                                                                $this->cell_padding = $tmpcellpadding;
 
7167
                                                                return ($this->UniArrSubString($uchars, ($sep + $endspace)));
 
7168
                                                        }
 
7169
                                                        $i = $sep;
 
7170
                                                        $sep = -1;
 
7171
                                                        $shy = false;
 
7172
                                                        $j = ($i+1);
 
7173
                                                }
 
7174
                                        }
 
7175
                                        // account for margin changes
 
7176
                                        if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
 
7177
                                                $this->AcceptPageBreak();
 
7178
                                                if ($this->rtl) {
 
7179
                                                        $this->x -= $margin['R'];
 
7180
                                                } else {
 
7181
                                                        $this->x += $margin['L'];
 
7182
                                                }
 
7183
                                                $this->lMargin += $margin['L'];
 
7184
                                                $this->rMargin += $margin['R'];
 
7185
                                        }
 
7186
                                        $w = $this->getRemainingWidth();
 
7187
                                        $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
 
7188
                                        if ($linebreak) {
 
7189
                                                $linebreak = false;
 
7190
                                        } else {
 
7191
                                                ++$nl;
 
7192
                                                $l = 0;
 
7193
                                        }
 
7194
                                }
 
7195
                        }
 
7196
                        // save last character
 
7197
                        $pc = $c;
 
7198
                        ++$i;
 
7199
                } // end while i < nb
 
7200
                // print last substring (if any)
 
7201
                if ($l > 0) {
 
7202
                        switch ($align) {
 
7203
                                case 'J':
 
7204
                                case 'C': {
 
7205
                                        $w = $w;
 
7206
                                        break;
 
7207
                                }
 
7208
                                case 'L': {
 
7209
                                        if ($this->rtl) {
 
7210
                                                $w = $w;
 
7211
                                        } else {
 
7212
                                                $w = $l;
 
7213
                                        }
 
7214
                                        break;
 
7215
                                }
 
7216
                                case 'R': {
 
7217
                                        if ($this->rtl) {
 
7218
                                                $w = $l;
 
7219
                                        } else {
 
7220
                                                $w = $w;
 
7221
                                        }
 
7222
                                        break;
 
7223
                                }
 
7224
                                default: {
 
7225
                                        $w = $l;
 
7226
                                        break;
 
7227
                                }
 
7228
                        }
 
7229
                        $tmpstr = $this->UniArrSubString($uchars, $j, $nb);
 
7230
                        if ($firstline) {
 
7231
                                $startx = $this->x;
 
7232
                                $tmparr = array_slice($chars, $j, ($nb - $j));
 
7233
                                if ($rtlmode) {
 
7234
                                        $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
 
7235
                                }
 
7236
                                $linew = $this->GetArrStringWidth($tmparr);
 
7237
                                unset($tmparr);
 
7238
                                if ($this->rtl) {
 
7239
                                        $this->endlinex = $startx - $linew;
 
7240
                                } else {
 
7241
                                        $this->endlinex = $startx + $linew;
 
7242
                                }
 
7243
                                $w = $linew;
 
7244
                                $tmpcellpadding = $this->cell_padding;
 
7245
                                if ($maxh == 0) {
 
7246
                                        $this->SetCellPadding(0);
 
7247
                                }
 
7248
                        }
 
7249
                        if ($firstblock AND $this->isRTLTextDir()) {
 
7250
                                $tmpstr = $this->stringRightTrim($tmpstr);
 
7251
                        }
 
7252
                        $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
 
7253
                        unset($tmpstr);
 
7254
                        if ($firstline) {
 
7255
                                $this->cell_padding = $tmpcellpadding;
 
7256
                                return ($this->UniArrSubString($uchars, $nb));
 
7257
                        }
 
7258
                        ++$nl;
 
7259
                }
 
7260
                if ($firstline) {
 
7261
                        return '';
 
7262
                }
 
7263
                return $nl;
 
7264
        }
 
7265
 
 
7266
        /**
 
7267
         * Returns the remaining width between the current position and margins.
 
7268
         * @return int Return the remaining width
 
7269
         * @protected
 
7270
         */
 
7271
        protected function getRemainingWidth() {
 
7272
                list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y);
 
7273
                if ($this->rtl) {
 
7274
                        return ($this->x - $this->lMargin);
 
7275
                } else {
 
7276
                        return ($this->w - $this->rMargin - $this->x);
 
7277
                }
 
7278
        }
 
7279
 
 
7280
        /**
 
7281
         * Extract a slice of the $strarr array and return it as string.
 
7282
         * @param $strarr (string) The input array of characters.
 
7283
         * @param $start (int) the starting element of $strarr.
 
7284
         * @param $end (int) first element that will not be returned.
 
7285
         * @return Return part of a string
 
7286
         * @public
 
7287
         */
 
7288
        public function UTF8ArrSubString($strarr, $start='', $end='') {
 
7289
                if (strlen($start) == 0) {
 
7290
                        $start = 0;
 
7291
                }
 
7292
                if (strlen($end) == 0) {
 
7293
                        $end = count($strarr);
 
7294
                }
 
7295
                $string = '';
 
7296
                for ($i=$start; $i < $end; ++$i) {
 
7297
                        $string .= $this->unichr($strarr[$i]);
 
7298
                }
 
7299
                return $string;
 
7300
        }
 
7301
 
 
7302
        /**
 
7303
         * Extract a slice of the $uniarr array and return it as string.
 
7304
         * @param $uniarr (string) The input array of characters.
 
7305
         * @param $start (int) the starting element of $strarr.
 
7306
         * @param $end (int) first element that will not be returned.
 
7307
         * @return Return part of a string
 
7308
         * @public
 
7309
         * @since 4.5.037 (2009-04-07)
 
7310
         */
 
7311
        public function UniArrSubString($uniarr, $start='', $end='') {
 
7312
                if (strlen($start) == 0) {
 
7313
                        $start = 0;
 
7314
                }
 
7315
                if (strlen($end) == 0) {
 
7316
                        $end = count($uniarr);
 
7317
                }
 
7318
                $string = '';
 
7319
                for ($i=$start; $i < $end; ++$i) {
 
7320
                        $string .= $uniarr[$i];
 
7321
                }
 
7322
                return $string;
 
7323
        }
 
7324
 
 
7325
        /**
 
7326
         * Convert an array of UTF8 values to array of unicode characters
 
7327
         * @param $ta (string) The input array of UTF8 values.
 
7328
         * @return Return array of unicode characters
 
7329
         * @public
 
7330
         * @since 4.5.037 (2009-04-07)
 
7331
         */
 
7332
        public function UTF8ArrayToUniArray($ta) {
 
7333
                return array_map(array($this, 'unichr'), $ta);
 
7334
        }
 
7335
 
 
7336
        /**
 
7337
         * Returns the unicode caracter specified by UTF-8 value
 
7338
         * @param $c (int) UTF-8 value
 
7339
         * @return Returns the specified character.
 
7340
         * @author Miguel Perez, Nicola Asuni
 
7341
         * @public
 
7342
         * @since 2.3.000 (2008-03-05)
 
7343
         */
 
7344
        public function unichr($c) {
 
7345
                if (!$this->isunicode) {
 
7346
                        return chr($c);
 
7347
                } elseif ($c <= 0x7F) {
 
7348
                        // one byte
 
7349
                        return chr($c);
 
7350
                } elseif ($c <= 0x7FF) {
 
7351
                        // two bytes
 
7352
                        return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
 
7353
                } elseif ($c <= 0xFFFF) {
 
7354
                        // three bytes
 
7355
                        return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
 
7356
                } elseif ($c <= 0x10FFFF) {
 
7357
                        // four bytes
 
7358
                        return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
 
7359
                } else {
 
7360
                        return '';
 
7361
                }
 
7362
        }
 
7363
 
 
7364
        /**
 
7365
         * Return the image type given the file name or array returned by getimagesize() function.
 
7366
         * @param $imgfile (string) image file name
 
7367
         * @param $iminfo (array) array of image information returned by getimagesize() function.
 
7368
         * @return string image type
 
7369
         * @since 4.8.017 (2009-11-27)
 
7370
         */
 
7371
        public function getImageFileType($imgfile, $iminfo=array()) {
 
7372
                $type = '';
 
7373
                if (isset($iminfo['mime']) AND !empty($iminfo['mime'])) {
 
7374
                        $mime = explode('/', $iminfo['mime']);
 
7375
                        if ((count($mime) > 1) AND ($mime[0] == 'image') AND (!empty($mime[1]))) {
 
7376
                                $type = strtolower(trim($mime[1]));
 
7377
                        }
 
7378
                }
 
7379
                if (empty($type)) {
 
7380
                        $fileinfo = pathinfo($imgfile);
 
7381
                        if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
 
7382
                                $type = strtolower(trim($fileinfo['extension']));
 
7383
                        }
 
7384
                }
 
7385
                if ($type == 'jpg') {
 
7386
                        $type = 'jpeg';
 
7387
                }
 
7388
                return $type;
 
7389
        }
 
7390
 
 
7391
        /**
 
7392
         * Set the block dimensions accounting for page breaks and page/column fitting
 
7393
         * @param $w (float) width
 
7394
         * @param $h (float) height
 
7395
         * @param $x (float) X coordinate
 
7396
         * @param $y (float) Y coodiante
 
7397
         * @param $fitonpage (boolean) if true the block is resized to not exceed page dimensions.
 
7398
         * @return array($w, $h, $x, $y)
 
7399
         * @protected
 
7400
         * @since 5.5.009 (2010-07-05)
 
7401
         */
 
7402
        protected function fitBlock($w, $h, $x, $y, $fitonpage=false) {
 
7403
                if ($w <= 0) {
 
7404
                        // set maximum width
 
7405
                        $w = ($this->w - $this->lMargin - $this->rMargin);
 
7406
                }
 
7407
                if ($h <= 0) {
 
7408
                        // set maximum height
 
7409
                        $h = ($this->PageBreakTrigger - $this->tMargin);
 
7410
                }
 
7411
                // resize the block to be vertically contained on a single page or single column
 
7412
                if ($fitonpage OR $this->AutoPageBreak) {
 
7413
                        $ratio_wh = ($w / $h);
 
7414
                        if ($h > ($this->PageBreakTrigger - $this->tMargin)) {
 
7415
                                $h = $this->PageBreakTrigger - $this->tMargin;
 
7416
                                $w = ($h * $ratio_wh);
 
7417
                        }
 
7418
                        // resize the block to be horizontally contained on a single page or single column
 
7419
                        if ($fitonpage) {
 
7420
                                $maxw = ($this->w - $this->lMargin - $this->rMargin);
 
7421
                                if ($w > $maxw) {
 
7422
                                        $w = $maxw;
 
7423
                                        $h = ($w / $ratio_wh);
 
7424
                                }
 
7425
                        }
 
7426
                }
 
7427
                // Check whether we need a new page or new column first as this does not fit
 
7428
                $prev_x = $this->x;
 
7429
                $prev_y = $this->y;
 
7430
                if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) {
 
7431
                        $y = $this->y;
 
7432
                        if ($this->rtl) {
 
7433
                                $x += ($prev_x - $this->x);
 
7434
                        } else {
 
7435
                                $x += ($this->x - $prev_x);
 
7436
                        }
 
7437
                        $this->newline = true;
 
7438
                }
 
7439
                // resize the block to be contained on the remaining available page or column space
 
7440
                if ($fitonpage) {
 
7441
                        $ratio_wh = ($w / $h);
 
7442
                        if (($y + $h) > $this->PageBreakTrigger) {
 
7443
                                $h = $this->PageBreakTrigger - $y;
 
7444
                                $w = ($h * $ratio_wh);
 
7445
                        }
 
7446
                        if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) {
 
7447
                                $w = $this->w - $this->rMargin - $x;
 
7448
                                $h = ($w / $ratio_wh);
 
7449
                        } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) {
 
7450
                                $w = $x - $this->lMargin;
 
7451
                                $h = ($w / $ratio_wh);
 
7452
                        }
 
7453
                }
 
7454
                return array($w, $h, $x, $y);
 
7455
        }
 
7456
 
 
7457
        /**
 
7458
         * Puts an image in the page.
 
7459
         * The upper-left corner must be given.
 
7460
         * The dimensions can be specified in different ways:<ul>
 
7461
         * <li>explicit width and height (expressed in user unit)</li>
 
7462
         * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
 
7463
         * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
 
7464
         * 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;
 
7465
         * The format can be specified explicitly or inferred from the file extension.<br />
 
7466
         * It is possible to put a link on the image.<br />
 
7467
         * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
 
7468
         * @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').
 
7469
         * @param $x (float) Abscissa of the upper-left corner (LTR) or upper-right corner (RTL).
 
7470
         * @param $y (float) Ordinate of the upper-left corner (LTR) or upper-right corner (RTL).
 
7471
         * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
 
7472
         * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
 
7473
         * @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.
 
7474
         * @param $link (mixed) URL or identifier returned by AddLink().
 
7475
         * @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>
 
7476
         * @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).
 
7477
         * @param $dpi (int) dot-per-inch resolution used on resize
 
7478
         * @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>
 
7479
         * @param $ismask (boolean) true if this image is a mask, false otherwise
 
7480
         * @param $imgmask (mixed) image object returned by this function or false
 
7481
         * @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)))
 
7482
         * @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).
 
7483
         * @param $hidden (boolean) If true do not display the image.
 
7484
         * @param $fitonpage (boolean) If true the image is resized to not exceed page dimensions.
 
7485
         * @param $alt (boolean) If true the image will be added as alternative and not directly printed (the ID of the image will be returned).
 
7486
         * @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.
 
7487
         * @return image information
 
7488
         * @public
 
7489
         * @since 1.1
 
7490
         */
 
7491
        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()) {
 
7492
                if ($x === '') {
 
7493
                        $x = $this->x;
 
7494
                }
 
7495
                if ($y === '') {
 
7496
                        $y = $this->y;
 
7497
                }
 
7498
                // check page for no-write regions and adapt page margins if necessary
 
7499
                list($x, $y) = $this->checkPageRegions($h, $x, $y);
 
7500
                $cached_file = false; // true when the file is cached
 
7501
                $exurl = ''; // external streams
 
7502
                // check if we are passing an image as file or string
 
7503
                if ($file[0] === '@') {
 
7504
                        // image from string
 
7505
                        $imgdata = substr($file, 1);
 
7506
                        $file = K_PATH_CACHE.'img_'.md5($imgdata);
 
7507
                        $fp = fopen($file, 'w');
 
7508
                        fwrite($fp, $imgdata);
 
7509
                        fclose($fp);
 
7510
                        unset($imgdata);
 
7511
                        $cached_file = true;
 
7512
                        $imsize = @getimagesize($file);
 
7513
                        if ($imsize === FALSE) {
 
7514
                                unlink($file);
 
7515
                                $cached_file = false;
 
7516
                        }
 
7517
                } else { // image file
 
7518
                        if ($file{0} === '*') {
 
7519
                                // image as external stream
 
7520
                                $file = substr($file, 1);
 
7521
                                $exurl = $file;
 
7522
                        }
 
7523
                        // check if is local file
 
7524
                        if (!@file_exists($file)) {
 
7525
                                // encode spaces on filename (file is probably an URL)
 
7526
                                $file = str_replace(' ', '%20', $file);
 
7527
                        }
 
7528
                        if (@file_exists($file)) {
 
7529
                                // get image dimensions
 
7530
                                $imsize = @getimagesize($file);
 
7531
                        } else {
 
7532
                                $imsize = false;
 
7533
                        }
 
7534
                        if ($imsize === FALSE) {
 
7535
                                if (function_exists('curl_init')) {
 
7536
                                        // try to get remote file data using cURL
 
7537
                                        $cs = curl_init(); // curl session
 
7538
                                        curl_setopt($cs, CURLOPT_URL, $file);
 
7539
                                        curl_setopt($cs, CURLOPT_BINARYTRANSFER, true);
 
7540
                                        curl_setopt($cs, CURLOPT_FAILONERROR, true);
 
7541
                                        curl_setopt($cs, CURLOPT_RETURNTRANSFER, true);
 
7542
                                        curl_setopt($cs, CURLOPT_FOLLOWLOCATION, true);
 
7543
                                        curl_setopt($cs, CURLOPT_CONNECTTIMEOUT, 5);
 
7544
                                        curl_setopt($cs, CURLOPT_TIMEOUT, 30);
 
7545
                                        curl_setopt($cs, CURLOPT_SSL_VERIFYPEER, false);
 
7546
                                        curl_setopt($cs, CURLOPT_SSL_VERIFYHOST, false);
 
7547
                                        curl_setopt($cs, CURLOPT_USERAGENT, 'TCPDF');
 
7548
                                        $imgdata = curl_exec($cs);
 
7549
                                        curl_close($cs);
 
7550
                                        if ($imgdata !== FALSE) {
 
7551
                                                // copy image to cache
 
7552
                                                $file = K_PATH_CACHE.'img_'.md5($imgdata);
 
7553
                                                $fp = fopen($file, 'w');
 
7554
                                                fwrite($fp, $imgdata);
 
7555
                                                fclose($fp);
 
7556
                                                unset($imgdata);
 
7557
                                                $cached_file = true;
 
7558
                                                $imsize = @getimagesize($file);
 
7559
                                                if ($imsize === FALSE) {
 
7560
                                                        unlink($file);
 
7561
                                                        $cached_file = false;
 
7562
                                                }
 
7563
                                        }
 
7564
                                } elseif (($w > 0) AND ($h > 0)) {
 
7565
                                        // get measures from specified data
 
7566
                                        $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
 
7567
                                        $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
 
7568
                                        $imsize = array($pw, $ph);
 
7569
                                }
 
7570
                        }
 
7571
                }
 
7572
                if ($imsize === FALSE) {
 
7573
                        if (substr($file, 0, -34) == K_PATH_CACHE.'msk') { // mask file
 
7574
                                // get measures from specified data
 
7575
                                $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
 
7576
                                $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
 
7577
                                $imsize = array($pw, $ph);
 
7578
                        } else {
 
7579
                                $this->Error('[Image] Unable to get image: '.$file);
 
7580
                        }
 
7581
                }
 
7582
                // get original image width and height in pixels
 
7583
                list($pixw, $pixh) = $imsize;
 
7584
                // calculate image width and height on document
 
7585
                if (($w <= 0) AND ($h <= 0)) {
 
7586
                        // convert image size to document unit
 
7587
                        $w = $this->pixelsToUnits($pixw);
 
7588
                        $h = $this->pixelsToUnits($pixh);
 
7589
                } elseif ($w <= 0) {
 
7590
                        $w = $h * $pixw / $pixh;
 
7591
                } elseif ($h <= 0) {
 
7592
                        $h = $w * $pixh / $pixw;
 
7593
                } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
 
7594
                        if (strlen($fitbox) !== 2) {
 
7595
                                // set default alignment
 
7596
                                $fitbox = '--';
 
7597
                        }
 
7598
                        // scale image dimensions proportionally to fit within the ($w, $h) box
 
7599
                        if ((($w * $pixh) / ($h * $pixw)) < 1) {
 
7600
                                // store current height
 
7601
                                $oldh = $h;
 
7602
                                // calculate new height
 
7603
                                $h = $w * $pixh / $pixw;
 
7604
                                // height difference
 
7605
                                $hdiff = ($oldh - $h);
 
7606
                                // vertical alignment
 
7607
                                switch (strtoupper($fitbox{1})) {
 
7608
                                        case 'T': {
 
7609
                                                break;
 
7610
                                        }
 
7611
                                        case 'M': {
 
7612
                                                $y += ($hdiff / 2);
 
7613
                                                break;
 
7614
                                        }
 
7615
                                        case 'B': {
 
7616
                                                $y += $hdiff;
 
7617
                                                break;
 
7618
                                        }
 
7619
                                }
 
7620
                        } else {
 
7621
                                // store current width
 
7622
                                $oldw = $w;
 
7623
                                // calculate new width
 
7624
                                $w = $h * $pixw / $pixh;
 
7625
                                // width difference
 
7626
                                $wdiff = ($oldw - $w);
 
7627
                                // horizontal alignment
 
7628
                                switch (strtoupper($fitbox{0})) {
 
7629
                                        case 'L': {
 
7630
                                                if ($this->rtl) {
 
7631
                                                        $x -= $wdiff;
 
7632
                                                }
 
7633
                                                break;
 
7634
                                        }
 
7635
                                        case 'C': {
 
7636
                                                if ($this->rtl) {
 
7637
                                                        $x -= ($wdiff / 2);
 
7638
                                                } else {
 
7639
                                                        $x += ($wdiff / 2);
 
7640
                                                }
 
7641
                                                break;
 
7642
                                        }
 
7643
                                        case 'R': {
 
7644
                                                if (!$this->rtl) {
 
7645
                                                        $x += $wdiff;
 
7646
                                                }
 
7647
                                                break;
 
7648
                                        }
 
7649
                                }
 
7650
                        }
 
7651
                }
 
7652
                // fit the image on available space
 
7653
                list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
 
7654
                // calculate new minimum dimensions in pixels
 
7655
                $neww = round($w * $this->k * $dpi / $this->dpi);
 
7656
                $newh = round($h * $this->k * $dpi / $this->dpi);
 
7657
                // check if resize is necessary (resize is used only to reduce the image)
 
7658
                $newsize = ($neww * $newh);
 
7659
                $pixsize = ($pixw * $pixh);
 
7660
                if (intval($resize) == 2) {
 
7661
                        $resize = true;
 
7662
                } elseif ($newsize >= $pixsize) {
 
7663
                        $resize = false;
 
7664
                }
 
7665
                // check if image has been already added on document
 
7666
                $newimage = true;
 
7667
                if (in_array($file, $this->imagekeys)) {
 
7668
                        $newimage = false;
 
7669
                        // get existing image data
 
7670
                        $info = $this->getImageBuffer($file);
 
7671
                        if (substr($file, 0, -34) != K_PATH_CACHE.'msk') {
 
7672
                                // check if the newer image is larger
 
7673
                                $oldsize = ($info['w'] * $info['h']);
 
7674
                                if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
 
7675
                                        $newimage = true;
 
7676
                                }
 
7677
                        }
 
7678
                } elseif (substr($file, 0, -34) != K_PATH_CACHE.'msk') {
 
7679
                        // check for cached images with alpha channel
 
7680
                        $filehash = md5($file);
 
7681
                        $tempfile_plain = K_PATH_CACHE.'mskp_'.$filehash;
 
7682
                        $tempfile_alpha = K_PATH_CACHE.'mska_'.$filehash;
 
7683
                        if (in_array($tempfile_plain, $this->imagekeys)) {
 
7684
                                // get existing image data
 
7685
                                $info = $this->getImageBuffer($tempfile_plain);
 
7686
                                // check if the newer image is larger
 
7687
                                $oldsize = ($info['w'] * $info['h']);
 
7688
                                if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
 
7689
                                        $newimage = true;
 
7690
                                } else {
 
7691
                                        $newimage = false;
 
7692
                                        // embed mask image
 
7693
                                        $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
 
7694
                                        // embed image, masked with previously embedded mask
 
7695
                                        return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
 
7696
                                }
 
7697
                        }
 
7698
                }
 
7699
                if ($newimage) {
 
7700
                        //First use of image, get info
 
7701
                        $type = strtolower($type);
 
7702
                        if ($type == '') {
 
7703
                                $type = $this->getImageFileType($file, $imsize);
 
7704
                        } elseif ($type == 'jpg') {
 
7705
                                $type = 'jpeg';
 
7706
                        }
 
7707
                        $mqr = $this->get_mqr();
 
7708
                        $this->set_mqr(false);
 
7709
                        // Specific image handlers
 
7710
                        $mtd = '_parse'.$type;
 
7711
                        // GD image handler function
 
7712
                        $gdfunction = 'imagecreatefrom'.$type;
 
7713
                        $info = false;
 
7714
                        if ((method_exists($this, $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
 
7715
                                // TCPDF image functions
 
7716
                                $info = $this->$mtd($file);
 
7717
                                if ($info == 'pngalpha') {
 
7718
                                        return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
 
7719
                                }
 
7720
                        }
 
7721
                        if (!$info) {
 
7722
                                if (function_exists($gdfunction)) {
 
7723
                                        // GD library
 
7724
                                        $img = $gdfunction($file);
 
7725
                                        if ($resize) {
 
7726
                                                $imgr = imagecreatetruecolor($neww, $newh);
 
7727
                                                if (($type == 'gif') OR ($type == 'png')) {
 
7728
                                                        $imgr = $this->_setGDImageTransparency($imgr, $img);
 
7729
                                                }
 
7730
                                                imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
 
7731
                                                if (($type == 'gif') OR ($type == 'png')) {
 
7732
                                                        $info = $this->_toPNG($imgr);
 
7733
                                                } else {
 
7734
                                                        $info = $this->_toJPEG($imgr);
 
7735
                                                }
 
7736
                                        } else {
 
7737
                                                if (($type == 'gif') OR ($type == 'png')) {
 
7738
                                                        $info = $this->_toPNG($img);
 
7739
                                                } else {
 
7740
                                                        $info = $this->_toJPEG($img);
 
7741
                                                }
 
7742
                                        }
 
7743
                                } elseif (extension_loaded('imagick')) {
 
7744
                                        // ImageMagick library
 
7745
                                        $img = new Imagick();
 
7746
                                        if ($type == 'SVG') {
 
7747
                                                // get SVG file content
 
7748
                                                $svgimg = file_get_contents($file);
 
7749
                                                // get width and height
 
7750
                                                $regs = array();
 
7751
                                                if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) {
 
7752
                                                        $svgtag = $regs[1];
 
7753
                                                        $tmp = array();
 
7754
                                                        if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
 
7755
                                                                $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
 
7756
                                                                $owu = sprintf('%.3F', ($ow * $dpi / 72)).$this->pdfunit;
 
7757
                                                                $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
 
7758
                                                        } else {
 
7759
                                                                $ow = $w;
 
7760
                                                        }
 
7761
                                                        $tmp = array();
 
7762
                                                        if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
 
7763
                                                                $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
 
7764
                                                                $ohu = sprintf('%.3F', ($oh * $dpi / 72)).$this->pdfunit;
 
7765
                                                                $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
 
7766
                                                        } else {
 
7767
                                                                $oh = $h;
 
7768
                                                        }
 
7769
                                                        $tmp = array();
 
7770
                                                        if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
 
7771
                                                                $vbw = ($ow * $this->imgscale * $this->k);
 
7772
                                                                $vbh = ($oh * $this->imgscale * $this->k);
 
7773
                                                                $vbox = sprintf(' viewBox="0 0 %.3F %.3F" ', $vbw, $vbh);
 
7774
                                                                $svgtag = $vbox.$svgtag;
 
7775
                                                        }
 
7776
                                                        $svgimg = preg_replace('/<svg([^\>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
 
7777
                                                }
 
7778
                                                $img->readImageBlob($svgimg);
 
7779
                                        } else {
 
7780
                                                $img->readImage($file);
 
7781
                                        }
 
7782
                                        if ($resize) {
 
7783
                                                $img->resizeImage($neww, $newh, 10, 1, false);
 
7784
                                        }
 
7785
                                        $img->setCompressionQuality($this->jpeg_quality);
 
7786
                                        $img->setImageFormat('jpeg');
 
7787
                                        $tempname = tempnam(K_PATH_CACHE, 'jpg_');
 
7788
                                        $img->writeImage($tempname);
 
7789
                                        $info = $this->_parsejpeg($tempname);
 
7790
                                        unlink($tempname);
 
7791
                                        $img->destroy();
 
7792
                                } else {
 
7793
                                        return;
 
7794
                                }
 
7795
                        }
 
7796
                        if ($info === false) {
 
7797
                                //If false, we cannot process image
 
7798
                                return;
 
7799
                        }
 
7800
                        $this->set_mqr($mqr);
 
7801
                        if ($ismask) {
 
7802
                                // force grayscale
 
7803
                                $info['cs'] = 'DeviceGray';
 
7804
                        }
 
7805
                        $info['i'] = $this->numimages;
 
7806
                        if (!in_array($file, $this->imagekeys)) {
 
7807
                                ++$info['i'];
 
7808
                        }
 
7809
                        if ($imgmask !== false) {
 
7810
                                $info['masked'] = $imgmask;
 
7811
                        }
 
7812
                        if (!empty($exurl)) {
 
7813
                                $info['exurl'] = $exurl;
 
7814
                        }
 
7815
                        // array of alternative images
 
7816
                        $info['altimgs'] = $altimgs;
 
7817
                        // add image to document
 
7818
                        $this->setImageBuffer($file, $info);
 
7819
                }
 
7820
                if ($cached_file) {
 
7821
                        // remove cached file
 
7822
                        unlink($file);
 
7823
                }
 
7824
                // set alignment
 
7825
                $this->img_rb_y = $y + $h;
 
7826
                // set alignment
 
7827
                if ($this->rtl) {
 
7828
                        if ($palign == 'L') {
 
7829
                                $ximg = $this->lMargin;
 
7830
                        } elseif ($palign == 'C') {
 
7831
                                $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
 
7832
                        } elseif ($palign == 'R') {
 
7833
                                $ximg = $this->w - $this->rMargin - $w;
 
7834
                        } else {
 
7835
                                $ximg = $x - $w;
 
7836
                        }
 
7837
                        $this->img_rb_x = $ximg;
 
7838
                } else {
 
7839
                        if ($palign == 'L') {
 
7840
                                $ximg = $this->lMargin;
 
7841
                        } elseif ($palign == 'C') {
 
7842
                                $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
 
7843
                        } elseif ($palign == 'R') {
 
7844
                                $ximg = $this->w - $this->rMargin - $w;
 
7845
                        } else {
 
7846
                                $ximg = $x;
 
7847
                        }
 
7848
                        $this->img_rb_x = $ximg + $w;
 
7849
                }
 
7850
                if ($ismask OR $hidden) {
 
7851
                        // image is not displayed
 
7852
                        return $info['i'];
 
7853
                }
 
7854
                $xkimg = $ximg * $this->k;
 
7855
                if (!$alt) {
 
7856
                        // only non-alternative immages will be set
 
7857
                        $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%u Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i']));
 
7858
                }
 
7859
                if (!empty($border)) {
 
7860
                        $bx = $this->x;
 
7861
                        $by = $this->y;
 
7862
                        $this->x = $ximg;
 
7863
                        if ($this->rtl) {
 
7864
                                $this->x += $w;
 
7865
                        }
 
7866
                        $this->y = $y;
 
7867
                        $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
 
7868
                        $this->x = $bx;
 
7869
                        $this->y = $by;
 
7870
                }
 
7871
                if ($link) {
 
7872
                        $this->Link($ximg, $y, $w, $h, $link, 0);
 
7873
                }
 
7874
                // set pointer to align the next text/objects
 
7875
                switch($align) {
 
7876
                        case 'T': {
 
7877
                                $this->y = $y;
 
7878
                                $this->x = $this->img_rb_x;
 
7879
                                break;
 
7880
                        }
 
7881
                        case 'M': {
 
7882
                                $this->y = $y + round($h/2);
 
7883
                                $this->x = $this->img_rb_x;
 
7884
                                break;
 
7885
                        }
 
7886
                        case 'B': {
 
7887
                                $this->y = $this->img_rb_y;
 
7888
                                $this->x = $this->img_rb_x;
 
7889
                                break;
 
7890
                        }
 
7891
                        case 'N': {
 
7892
                                $this->SetY($this->img_rb_y);
 
7893
                                break;
 
7894
                        }
 
7895
                        default:{
 
7896
                                break;
 
7897
                        }
 
7898
                }
 
7899
                $this->endlinex = $this->img_rb_x;
 
7900
                if ($this->inxobj) {
 
7901
                        // we are inside an XObject template
 
7902
                        $this->xobjects[$this->xobjid]['images'][] = $info['i'];
 
7903
                }
 
7904
                return $info['i'];
 
7905
        }
 
7906
 
 
7907
        /**
 
7908
         * Sets the current active configuration setting of magic_quotes_runtime (if the set_magic_quotes_runtime function exist)
 
7909
         * @param $mqr (boolean) FALSE for off, TRUE for on.
 
7910
         * @since 4.6.025 (2009-08-17)
 
7911
         */
 
7912
        public function set_mqr($mqr) {
 
7913
                if (!defined('PHP_VERSION_ID')) {
 
7914
                        $version = PHP_VERSION;
 
7915
                        define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4}));
 
7916
                }
 
7917
                if (PHP_VERSION_ID < 50300) {
 
7918
                        @set_magic_quotes_runtime($mqr);
 
7919
                }
 
7920
        }
 
7921
 
 
7922
        /**
 
7923
         * Gets the current active configuration setting of magic_quotes_runtime (if the get_magic_quotes_runtime function exist)
 
7924
         * @return Returns 0 if magic quotes runtime is off or get_magic_quotes_runtime doesn't exist, 1 otherwise.
 
7925
         * @since 4.6.025 (2009-08-17)
 
7926
         */
 
7927
        public function get_mqr() {
 
7928
                if (!defined('PHP_VERSION_ID')) {
 
7929
                        $version = PHP_VERSION;
 
7930
                        define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4}));
 
7931
                }
 
7932
                if (PHP_VERSION_ID < 50300) {
 
7933
                        return @get_magic_quotes_runtime();
 
7934
                }
 
7935
                return 0;
 
7936
        }
 
7937
 
 
7938
        /**
 
7939
         * Convert the loaded image to a JPEG and then return a structure for the PDF creator.
 
7940
         * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
 
7941
         * @param $image (image) Image object.
 
7942
         * return image JPEG image object.
 
7943
         * @protected
 
7944
         */
 
7945
        protected function _toJPEG($image) {
 
7946
                $tempname = tempnam(K_PATH_CACHE, 'jpg_');
 
7947
                imagejpeg($image, $tempname, $this->jpeg_quality);
 
7948
                imagedestroy($image);
 
7949
                $retvars = $this->_parsejpeg($tempname);
 
7950
                // tidy up by removing temporary image
 
7951
                unlink($tempname);
 
7952
                return $retvars;
 
7953
        }
 
7954
 
 
7955
        /**
 
7956
         * Convert the loaded image to a PNG and then return a structure for the PDF creator.
 
7957
         * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
 
7958
         * @param $image (image) Image object.
 
7959
         * return image PNG image object.
 
7960
         * @protected
 
7961
         * @since 4.9.016 (2010-04-20)
 
7962
         */
 
7963
        protected function _toPNG($image) {
 
7964
                // set temporary image file name
 
7965
                $tempname = tempnam(K_PATH_CACHE, 'jpg_');
 
7966
                // turn off interlaced mode
 
7967
                imageinterlace($image, 0);
 
7968
                // create temporary PNG image
 
7969
                imagepng($image, $tempname);
 
7970
                // remove image from memory
 
7971
                imagedestroy($image);
 
7972
                // get PNG image data
 
7973
                $retvars = $this->_parsepng($tempname);
 
7974
                // tidy up by removing temporary image
 
7975
                unlink($tempname);
 
7976
                return $retvars;
 
7977
        }
 
7978
 
 
7979
        /**
 
7980
         * Set the transparency for the given GD image.
 
7981
         * @param $new_image (image) GD image object
 
7982
         * @param $image (image) GD image object.
 
7983
         * return GD image object.
 
7984
         * @protected
 
7985
         * @since 4.9.016 (2010-04-20)
 
7986
         */
 
7987
        protected function _setGDImageTransparency($new_image, $image) {
 
7988
                // transparency index
 
7989
                $tid = imagecolortransparent($image);
 
7990
                // default transparency color
 
7991
                $tcol = array('red' => 255, 'green' => 255, 'blue' => 255);
 
7992
                if ($tid >= 0) {
 
7993
                        // get the colors for the transparency index
 
7994
                        $tcol = imagecolorsforindex($image, $tid);
 
7995
                }
 
7996
                $tid = imagecolorallocate($new_image, $tcol['red'], $tcol['green'], $tcol['blue']);
 
7997
                imagefill($new_image, 0, 0, $tid);
 
7998
                imagecolortransparent($new_image, $tid);
 
7999
                return $new_image;
 
8000
        }
 
8001
 
 
8002
        /**
 
8003
         * Extract info from a JPEG file without using the GD library.
 
8004
         * @param $file (string) image file to parse
 
8005
         * @return array structure containing the image data
 
8006
         * @protected
 
8007
         */
 
8008
        protected function _parsejpeg($file) {
 
8009
                $a = getimagesize($file);
 
8010
                if (empty($a)) {
 
8011
                        $this->Error('Missing or incorrect image file: '.$file);
 
8012
                }
 
8013
                if ($a[2] != 2) {
 
8014
                        $this->Error('Not a JPEG file: '.$file);
 
8015
                }
 
8016
                // bits per pixel
 
8017
                $bpc = isset($a['bits']) ? intval($a['bits']) : 8;
 
8018
                // number of image channels
 
8019
                if (!isset($a['channels'])) {
 
8020
                        $channels = 3;
 
8021
                } else {
 
8022
                        $channels = intval($a['channels']);
 
8023
                }
 
8024
                // default colour space
 
8025
                switch ($channels) {
 
8026
                        case 1: {
 
8027
                                $colspace = 'DeviceGray';
 
8028
                                break;
 
8029
                        }
 
8030
                        case 3: {
 
8031
                                $colspace = 'DeviceRGB';
 
8032
                                break;
 
8033
                        }
 
8034
                        case 4: {
 
8035
                                $colspace = 'DeviceCMYK';
 
8036
                                break;
 
8037
                        }
 
8038
                        default: {
 
8039
                                $channels = 3;
 
8040
                                $colspace = 'DeviceRGB';
 
8041
                                break;
 
8042
                        }
 
8043
                }
 
8044
                // get file content
 
8045
                $data = file_get_contents($file);
 
8046
                // check for embedded ICC profile
 
8047
                $icc = array();
 
8048
                $offset = 0;
 
8049
                while (($pos = strpos($data, "ICC_PROFILE\0", $offset)) !== false) {
 
8050
                        // get ICC sequence length
 
8051
                        $length = ($this->_getUSHORT($data, ($pos - 2)) - 16);
 
8052
                        // marker sequence number
 
8053
                        $msn = max(1, ord($data[($pos + 12)]));
 
8054
                        // number of markers (total of APP2 used)
 
8055
                        $nom = max(1, ord($data[($pos + 13)]));
 
8056
                        // get sequence segment
 
8057
                        $icc[($msn - 1)] = substr($data, ($pos + 14), $length);
 
8058
                        // move forward to next sequence
 
8059
                        $offset = ($pos + 14 + $length);
 
8060
                }
 
8061
                // order and compact ICC segments
 
8062
                if (count($icc) > 0) {
 
8063
                        ksort($icc);
 
8064
                        $icc = implode('', $icc);
 
8065
                        if ((ord($icc{36}) != 0x61) OR (ord($icc{37}) != 0x63) OR (ord($icc{38}) != 0x73) OR (ord($icc{39}) != 0x70)) {
 
8066
                                // invalid ICC profile
 
8067
                                $icc = false;
 
8068
                        }
 
8069
                } else {
 
8070
                        $icc = false;
 
8071
                }
 
8072
                return array('w' => $a[0], 'h' => $a[1], 'ch' => $channels, 'icc' => $icc, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
 
8073
        }
 
8074
 
 
8075
        /**
 
8076
         * Extract info from a PNG file without using the GD library.
 
8077
         * @param $file (string) image file to parse
 
8078
         * @return array structure containing the image data
 
8079
         * @protected
 
8080
         */
 
8081
        protected function _parsepng($file) {
 
8082
                $f = fopen($file, 'rb');
 
8083
                if ($f === false) {
 
8084
                        $this->Error('Can\'t open image file: '.$file);
 
8085
                }
 
8086
                //Check signature
 
8087
                if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
 
8088
                        $this->Error('Not a PNG file: '.$file);
 
8089
                }
 
8090
                //Read header chunk
 
8091
                fread($f, 4);
 
8092
                if (fread($f, 4) != 'IHDR') {
 
8093
                        $this->Error('Incorrect PNG file: '.$file);
 
8094
                }
 
8095
                $w = $this->_freadint($f);
 
8096
                $h = $this->_freadint($f);
 
8097
                $bpc = ord(fread($f, 1));
 
8098
                if ($bpc > 8) {
 
8099
                        //$this->Error('16-bit depth not supported: '.$file);
 
8100
                        fclose($f);
 
8101
                        return false;
 
8102
                }
 
8103
                $ct = ord(fread($f, 1));
 
8104
                if ($ct == 0) {
 
8105
                        $colspace = 'DeviceGray';
 
8106
                } elseif ($ct == 2) {
 
8107
                        $colspace = 'DeviceRGB';
 
8108
                } elseif ($ct == 3) {
 
8109
                        $colspace = 'Indexed';
 
8110
                } else {
 
8111
                        // alpha channel
 
8112
                        fclose($f);
 
8113
                        return 'pngalpha';
 
8114
                }
 
8115
                if (ord(fread($f, 1)) != 0) {
 
8116
                        //$this->Error('Unknown compression method: '.$file);
 
8117
                        fclose($f);
 
8118
                        return false;
 
8119
                }
 
8120
                if (ord(fread($f, 1)) != 0) {
 
8121
                        //$this->Error('Unknown filter method: '.$file);
 
8122
                        fclose($f);
 
8123
                        return false;
 
8124
                }
 
8125
                if (ord(fread($f, 1)) != 0) {
 
8126
                        //$this->Error('Interlacing not supported: '.$file);
 
8127
                        fclose($f);
 
8128
                        return false;
 
8129
                }
 
8130
                fread($f, 4);
 
8131
                $channels = ($ct == 2 ? 3 : 1);
 
8132
                $parms = '/DecodeParms << /Predictor 15 /Colors '.$channels.' /BitsPerComponent '.$bpc.' /Columns '.$w.' >>';
 
8133
                //Scan chunks looking for palette, transparency and image data
 
8134
                $pal = '';
 
8135
                $trns = '';
 
8136
                $data = '';
 
8137
                $icc = false;
 
8138
                do {
 
8139
                        $n = $this->_freadint($f);
 
8140
                        $type = fread($f, 4);
 
8141
                        if ($type == 'PLTE') {
 
8142
                                // read palette
 
8143
                                $pal = $this->rfread($f, $n);
 
8144
                                fread($f, 4);
 
8145
                        } elseif ($type == 'tRNS') {
 
8146
                                // read transparency info
 
8147
                                $t = $this->rfread($f, $n);
 
8148
                                if ($ct == 0) {
 
8149
                                        $trns = array(ord($t{1}));
 
8150
                                } elseif ($ct == 2) {
 
8151
                                        $trns = array(ord($t{1}), ord($t{3}), ord($t{5}));
 
8152
                                } else {
 
8153
                                        $pos = strpos($t, chr(0));
 
8154
                                        if ($pos !== false) {
 
8155
                                                $trns = array($pos);
 
8156
                                        }
 
8157
                                }
 
8158
                                fread($f, 4);
 
8159
                        } elseif ($type == 'IDAT') {
 
8160
                                // read image data block
 
8161
                                $data .= $this->rfread($f, $n);
 
8162
                                fread($f, 4);
 
8163
                        } elseif ($type == 'iCCP') {
 
8164
                                // skip profile name and null separator
 
8165
                                $len = 0;
 
8166
                                while ((ord(fread($f, 1)) > 0) AND ($len < 80)) {
 
8167
                                        ++$len;
 
8168
                                }
 
8169
                                // get compression method
 
8170
                                if (ord(fread($f, 1)) != 0) {
 
8171
                                        //$this->Error('Unknown filter method: '.$file);
 
8172
                                        fclose($f);
 
8173
                                        return false;
 
8174
                                }
 
8175
                                // read ICC Color Profile
 
8176
                                $icc = $this->rfread($f, ($n - $len - 2));
 
8177
                                // decompress profile
 
8178
                                $icc = gzuncompress($icc);
 
8179
                                fread($f, 4);
 
8180
                        } elseif ($type == 'IEND') {
 
8181
                                break;
 
8182
                        } else {
 
8183
                                $this->rfread($f, $n + 4);
 
8184
                        }
 
8185
                } while ($n);
 
8186
                if (($colspace == 'Indexed') AND (empty($pal))) {
 
8187
                        //$this->Error('Missing palette in '.$file);
 
8188
                        fclose($f);
 
8189
                        return false;
 
8190
                }
 
8191
                fclose($f);
 
8192
                return array('w' => $w, 'h' => $h, 'ch' => $channels, 'icc' => $icc, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
 
8193
        }
 
8194
 
 
8195
        /**
 
8196
         * Binary-safe and URL-safe file read.
 
8197
         * Reads up to length bytes from the file pointer referenced by handle. Reading stops as soon as one of the following conditions is met: length bytes have been read; EOF (end of file) is reached.
 
8198
         * @param $handle (resource)
 
8199
         * @param $length (int)
 
8200
         * @return Returns the read string or FALSE in case of error.
 
8201
         * @author Nicola Asuni
 
8202
         * @protected
 
8203
         * @since 4.5.027 (2009-03-16)
 
8204
         */
 
8205
        protected function rfread($handle, $length) {
 
8206
                $data = fread($handle, $length);
 
8207
                if ($data === false) {
 
8208
                        return false;
 
8209
                }
 
8210
                $rest = $length - strlen($data);
 
8211
                if ($rest > 0) {
 
8212
                        $data .= $this->rfread($handle, $rest);
 
8213
                }
 
8214
                return $data;
 
8215
        }
 
8216
 
 
8217
        /**
 
8218
         * Extract info from a PNG image with alpha channel using the GD library.
 
8219
         * @param $file (string) Name of the file containing the image.
 
8220
         * @param $x (float) Abscissa of the upper-left corner.
 
8221
         * @param $y (float) Ordinate of the upper-left corner.
 
8222
         * @param $wpx (float) Original width of the image in pixels.
 
8223
         * @param $hpx (float) original height of the image in pixels.
 
8224
         * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
 
8225
         * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
 
8226
         * @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.
 
8227
         * @param $link (mixed) URL or identifier returned by AddLink().
 
8228
         * @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>
 
8229
         * @param $resize (boolean) If true resize (reduce) the image to fit $w and $h (requires GD library).
 
8230
         * @param $dpi (int) dot-per-inch resolution used on resize
 
8231
         * @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>
 
8232
         * @param $filehash (string) File hash used to build unique file names.
 
8233
         * @author Nicola Asuni
 
8234
         * @protected
 
8235
         * @since 4.3.007 (2008-12-04)
 
8236
         * @see Image()
 
8237
         */
 
8238
        protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
 
8239
                if (empty($filehash)) {
 
8240
                        $filehash = md5($file);
 
8241
                }
 
8242
                // create temp image file (without alpha channel)
 
8243
                $tempfile_plain = K_PATH_CACHE.'mskp_'.$filehash;
 
8244
                // create temp alpha file
 
8245
                $tempfile_alpha = K_PATH_CACHE.'mska_'.$filehash;
 
8246
                if (extension_loaded('imagick')) { // ImageMagick extension
 
8247
                        // ImageMagick library
 
8248
                        $img = new Imagick();
 
8249
                        $img->readImage($file);
 
8250
                        // clone image object
 
8251
                        $imga = $img->clone();
 
8252
                        // extract alpha channel
 
8253
                        $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
 
8254
                        $img->negateImage(true);
 
8255
                        $img->setImageFormat('png');
 
8256
                        $img->writeImage($tempfile_alpha);
 
8257
                        // remove alpha channel
 
8258
                        $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
 
8259
                        $imga->setImageFormat('png');
 
8260
                        $imga->writeImage($tempfile_plain);
 
8261
                } elseif (function_exists('imagecreatefrompng')) { // GD extension
 
8262
                        // generate images
 
8263
                        $img = imagecreatefrompng($file);
 
8264
                        $imgalpha = imagecreate($wpx, $hpx);
 
8265
                        // generate gray scale palette (0 -> 255)
 
8266
                        for ($c = 0; $c < 256; ++$c) {
 
8267
                                ImageColorAllocate($imgalpha, $c, $c, $c);
 
8268
                        }
 
8269
                        // extract alpha channel
 
8270
                        for ($xpx = 0; $xpx < $wpx; ++$xpx) {
 
8271
                                for ($ypx = 0; $ypx < $hpx; ++$ypx) {
 
8272
                                        $color = imagecolorat($img, $xpx, $ypx);
 
8273
                                        $alpha = ($color >> 24); // shifts off the first 24 bits (where 8x3 are used for each color), and returns the remaining 7 allocated bits (commonly used for alpha)
 
8274
                                        $alpha = (((127 - $alpha) / 127) * 255); // GD alpha is only 7 bit (0 -> 127)
 
8275
                                        $alpha = $this->getGDgamma($alpha); // correct gamma
 
8276
                                        imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
 
8277
                                }
 
8278
                        }
 
8279
                        imagepng($imgalpha, $tempfile_alpha);
 
8280
                        imagedestroy($imgalpha);
 
8281
                        // extract image without alpha channel
 
8282
                        $imgplain = imagecreatetruecolor($wpx, $hpx);
 
8283
                        imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
 
8284
                        imagepng($imgplain, $tempfile_plain);
 
8285
                        imagedestroy($imgplain);
 
8286
                } else {
 
8287
                        $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
 
8288
                }
 
8289
                // embed mask image
 
8290
                $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
 
8291
                // embed image, masked with previously embedded mask
 
8292
                $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
 
8293
                // remove temp files
 
8294
                unlink($tempfile_alpha);
 
8295
                unlink($tempfile_plain);
 
8296
        }
 
8297
 
 
8298
        /**
 
8299
         * Correct the gamma value to be used with GD library
 
8300
         * @param $v (float) the gamma value to be corrected
 
8301
         * @protected
 
8302
         * @since 4.3.007 (2008-12-04)
 
8303
         */
 
8304
        protected function getGDgamma($v) {
 
8305
                return (pow(($v / 255), 2.2) * 255);
 
8306
        }
 
8307
 
 
8308
        /**
 
8309
         * Performs a line break.
 
8310
         * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
 
8311
         * @param $h (float) The height of the break. By default, the value equals the height of the last printed cell.
 
8312
         * @param $cell (boolean) if true add the current left (or right o for RTL) padding to the X coordinate
 
8313
         * @public
 
8314
         * @since 1.0
 
8315
         * @see Cell()
 
8316
         */
 
8317
        public function Ln($h='', $cell=false) {
 
8318
                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'])) {
 
8319
                        // revove vertical space from the top of the column
 
8320
                        return;
 
8321
                }
 
8322
                if ($cell) {
 
8323
                        if ($this->rtl) {
 
8324
                                $cellpadding = $this->cell_padding['R'];
 
8325
                        } else {
 
8326
                                $cellpadding = $this->cell_padding['L'];
 
8327
                        }
 
8328
                } else {
 
8329
                        $cellpadding = 0;
 
8330
                }
 
8331
                if ($this->rtl) {
 
8332
                        $this->x = $this->w - $this->rMargin - $cellpadding;
 
8333
                } else {
 
8334
                        $this->x = $this->lMargin + $cellpadding;
 
8335
                }
 
8336
                if (is_string($h)) {
 
8337
                        $this->y += $this->lasth;
 
8338
                } else {
 
8339
                        $this->y += $h;
 
8340
                }
 
8341
                $this->newline = true;
 
8342
        }
 
8343
 
 
8344
        /**
 
8345
         * Returns the relative X value of current position.
 
8346
         * The value is relative to the left border for LTR languages and to the right border for RTL languages.
 
8347
         * @return float
 
8348
         * @public
 
8349
         * @since 1.2
 
8350
         * @see SetX(), GetY(), SetY()
 
8351
         */
 
8352
        public function GetX() {
 
8353
                //Get x position
 
8354
                if ($this->rtl) {
 
8355
                        return ($this->w - $this->x);
 
8356
                } else {
 
8357
                        return $this->x;
 
8358
                }
 
8359
        }
 
8360
 
 
8361
        /**
 
8362
         * Returns the absolute X value of current position.
 
8363
         * @return float
 
8364
         * @public
 
8365
         * @since 1.2
 
8366
         * @see SetX(), GetY(), SetY()
 
8367
         */
 
8368
        public function GetAbsX() {
 
8369
                return $this->x;
 
8370
        }
 
8371
 
 
8372
        /**
 
8373
         * Returns the ordinate of the current position.
 
8374
         * @return float
 
8375
         * @public
 
8376
         * @since 1.0
 
8377
         * @see SetY(), GetX(), SetX()
 
8378
         */
 
8379
        public function GetY() {
 
8380
                return $this->y;
 
8381
        }
 
8382
 
 
8383
        /**
 
8384
         * Defines the abscissa of the current position.
 
8385
         * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
 
8386
         * @param $x (float) The value of the abscissa.
 
8387
         * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
 
8388
         * @public
 
8389
         * @since 1.2
 
8390
         * @see GetX(), GetY(), SetY(), SetXY()
 
8391
         */
 
8392
        public function SetX($x, $rtloff=false) {
 
8393
                if (!$rtloff AND $this->rtl) {
 
8394
                        if ($x >= 0) {
 
8395
                                $this->x = $this->w - $x;
 
8396
                        } else {
 
8397
                                $this->x = abs($x);
 
8398
                        }
 
8399
                } else {
 
8400
                        if ($x >= 0) {
 
8401
                                $this->x = $x;
 
8402
                        } else {
 
8403
                                $this->x = $this->w + $x;
 
8404
                        }
 
8405
                }
 
8406
                if ($this->x < 0) {
 
8407
                        $this->x = 0;
 
8408
                }
 
8409
                if ($this->x > $this->w) {
 
8410
                        $this->x = $this->w;
 
8411
                }
 
8412
        }
 
8413
 
 
8414
        /**
 
8415
         * Moves the current abscissa back to the left margin and sets the ordinate.
 
8416
         * If the passed value is negative, it is relative to the bottom of the page.
 
8417
         * @param $y (float) The value of the ordinate.
 
8418
         * @param $resetx (bool) if true (default) reset the X position.
 
8419
         * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
 
8420
         * @public
 
8421
         * @since 1.0
 
8422
         * @see GetX(), GetY(), SetY(), SetXY()
 
8423
         */
 
8424
        public function SetY($y, $resetx=true, $rtloff=false) {
 
8425
                if ($resetx) {
 
8426
                        //reset x
 
8427
                        if (!$rtloff AND $this->rtl) {
 
8428
                                $this->x = $this->w - $this->rMargin;
 
8429
                        } else {
 
8430
                                $this->x = $this->lMargin;
 
8431
                        }
 
8432
                }
 
8433
                if ($y >= 0) {
 
8434
                        $this->y = $y;
 
8435
                } else {
 
8436
                        $this->y = $this->h + $y;
 
8437
                }
 
8438
                if ($this->y < 0) {
 
8439
                        $this->y = 0;
 
8440
                }
 
8441
                if ($this->y > $this->h) {
 
8442
                        $this->y = $this->h;
 
8443
                }
 
8444
        }
 
8445
 
 
8446
        /**
 
8447
         * Defines the abscissa and ordinate of the current position.
 
8448
         * If the passed values are negative, they are relative respectively to the right and bottom of the page.
 
8449
         * @param $x (float) The value of the abscissa.
 
8450
         * @param $y (float) The value of the ordinate.
 
8451
         * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
 
8452
         * @public
 
8453
         * @since 1.2
 
8454
         * @see SetX(), SetY()
 
8455
         */
 
8456
        public function SetXY($x, $y, $rtloff=false) {
 
8457
                $this->SetY($y, false, $rtloff);
 
8458
                $this->SetX($x, $rtloff);
 
8459
        }
 
8460
 
 
8461
        /**
 
8462
         * Ouput input data and compress it if possible.
 
8463
         * @param $data (string) Data to output.
 
8464
         * @param $length (int) Data length in bytes.
 
8465
         * @protected
 
8466
         * @since 5.9.086
 
8467
         */
 
8468
        protected function sendOutputData($data, $length) {
 
8469
                if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) OR empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {
 
8470
                        // the content length may vary if the server is using compression
 
8471
                        header('Content-Length: '.$length);
 
8472
                }
 
8473
                echo $data;
 
8474
        }
 
8475
 
 
8476
        /**
 
8477
         * Send the document to a given destination: string, local file or browser.
 
8478
         * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
 
8479
         * The method first calls Close() if necessary to terminate the document.
 
8480
         * @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.
 
8481
         * @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>
 
8482
         * @public
 
8483
         * @since 1.0
 
8484
         * @see Close()
 
8485
         */
 
8486
        public function Output($name='doc.pdf', $dest='I') {
 
8487
                //Output PDF to some destination
 
8488
                //Finish document if necessary
 
8489
                if ($this->state < 3) {
 
8490
                        $this->Close();
 
8491
                }
 
8492
                //Normalize parameters
 
8493
                if (is_bool($dest)) {
 
8494
                        $dest = $dest ? 'D' : 'F';
 
8495
                }
 
8496
                $dest = strtoupper($dest);
 
8497
                if ($dest{0} != 'F') {
 
8498
                        $name = preg_replace('/[\s]+/', '_', $name);
 
8499
                        $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
 
8500
                }
 
8501
                if ($this->sign) {
 
8502
                        // *** apply digital signature to the document ***
 
8503
                        // get the document content
 
8504
                        $pdfdoc = $this->getBuffer();
 
8505
                        // remove last newline
 
8506
                        $pdfdoc = substr($pdfdoc, 0, -1);
 
8507
                        // Remove the original buffer
 
8508
                        if (isset($this->diskcache) AND $this->diskcache) {
 
8509
                                // remove buffer file from cache
 
8510
                                unlink($this->buffer);
 
8511
                        }
 
8512
                        unset($this->buffer);
 
8513
                        // remove filler space
 
8514
                        $byterange_string_len = strlen($this->byterange_string);
 
8515
                        // define the ByteRange
 
8516
                        $byte_range = array();
 
8517
                        $byte_range[0] = 0;
 
8518
                        $byte_range[1] = strpos($pdfdoc, $this->byterange_string) + $byterange_string_len + 10;
 
8519
                        $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
 
8520
                        $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
 
8521
                        $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
 
8522
                        // replace the ByteRange
 
8523
                        $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
 
8524
                        $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
 
8525
                        $pdfdoc = str_replace($this->byterange_string, $byterange, $pdfdoc);
 
8526
                        // write the document to a temporary folder
 
8527
                        $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_');
 
8528
                        $f = fopen($tempdoc, 'wb');
 
8529
                        if (!$f) {
 
8530
                                $this->Error('Unable to create temporary file: '.$tempdoc);
 
8531
                        }
 
8532
                        $pdfdoc_length = strlen($pdfdoc);
 
8533
                        fwrite($f, $pdfdoc, $pdfdoc_length);
 
8534
                        fclose($f);
 
8535
                        // get digital signature via openssl library
 
8536
                        $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_');
 
8537
                        if (empty($this->signature_data['extracerts'])) {
 
8538
                                openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
 
8539
                        } else {
 
8540
                                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']);
 
8541
                        }
 
8542
                        unlink($tempdoc);
 
8543
                        // read signature
 
8544
                        $signature = file_get_contents($tempsign);
 
8545
                        unlink($tempsign);
 
8546
                        // extract signature
 
8547
                        $signature = substr($signature, $pdfdoc_length);
 
8548
                        $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
 
8549
                        $tmparr = explode("\n\n", $signature);
 
8550
                        $signature = $tmparr[1];
 
8551
                        unset($tmparr);
 
8552
                        // decode signature
 
8553
                        $signature = base64_decode(trim($signature));
 
8554
                        // convert signature to hex
 
8555
                        $signature = current(unpack('H*', $signature));
 
8556
                        $signature = str_pad($signature, $this->signature_max_length, '0');
 
8557
                        // disable disk caching
 
8558
                        $this->diskcache = false;
 
8559
                        // Add signature to the document
 
8560
                        $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
 
8561
                        $this->bufferlen = strlen($this->buffer);
 
8562
                }
 
8563
                switch($dest) {
 
8564
                        case 'I': {
 
8565
                                // Send PDF to the standard output
 
8566
                                if (ob_get_contents()) {
 
8567
                                        $this->Error('Some data has already been output, can\'t send PDF file');
 
8568
                                }
 
8569
                                if (php_sapi_name() != 'cli') {
 
8570
                                        // send output to a browser
 
8571
                                        header('Content-Type: application/pdf');
 
8572
                                        if (headers_sent()) {
 
8573
                                                $this->Error('Some data has already been output to browser, can\'t send PDF file');
 
8574
                                        }
 
8575
                                        header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
 
8576
                                        //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
 
8577
                                        header('Pragma: public');
 
8578
                                        header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
 
8579
                                        header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
 
8580
                                        header('Content-Disposition: inline; filename="'.basename($name).'";');
 
8581
                                        $this->sendOutputData($this->getBuffer(), $this->bufferlen);
 
8582
                                } else {
 
8583
                                        echo $this->getBuffer();
 
8584
                                }
 
8585
                                break;
 
8586
                        }
 
8587
                        case 'D': {
 
8588
                                // download PDF as file
 
8589
                                if (ob_get_contents()) {
 
8590
                                        $this->Error('Some data has already been output, can\'t send PDF file');
 
8591
                                }
 
8592
                                header('Content-Description: File Transfer');
 
8593
                                if (headers_sent()) {
 
8594
                                        $this->Error('Some data has already been output to browser, can\'t send PDF file');
 
8595
                                }
 
8596
                                header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
 
8597
                                //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
 
8598
                                header('Pragma: public');
 
8599
                                header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
 
8600
                                header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
 
8601
                                // force download dialog
 
8602
                                if (strpos(php_sapi_name(), 'cgi') === false) {
 
8603
                                        header('Content-Type: application/force-download');
 
8604
                                        header('Content-Type: application/octet-stream', false);
 
8605
                                        header('Content-Type: application/download', false);
 
8606
                                        header('Content-Type: application/pdf', false);
 
8607
                                } else {
 
8608
                                        header('Content-Type: application/pdf');
 
8609
                                }
 
8610
                                // use the Content-Disposition header to supply a recommended filename
 
8611
                                header('Content-Disposition: attachment; filename="'.basename($name).'";');
 
8612
                                header('Content-Transfer-Encoding: binary');
 
8613
                                $this->sendOutputData($this->getBuffer(), $this->bufferlen);
 
8614
                                break;
 
8615
                        }
 
8616
                        case 'F':
 
8617
                        case 'FI':
 
8618
                        case 'FD': {
 
8619
                                // save PDF to a local file
 
8620
                                if ($this->diskcache) {
 
8621
                                        copy($this->buffer, $name);
 
8622
                                } else {
 
8623
                                        $f = fopen($name, 'wb');
 
8624
                                        if (!$f) {
 
8625
                                                $this->Error('Unable to create output file: '.$name);
 
8626
                                        }
 
8627
                                        fwrite($f, $this->getBuffer(), $this->bufferlen);
 
8628
                                        fclose($f);
 
8629
                                }
 
8630
                                if ($dest == 'FI') {
 
8631
                                        // send headers to browser
 
8632
                                        header('Content-Type: application/pdf');
 
8633
                                        header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
 
8634
                                        //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
 
8635
                                        header('Pragma: public');
 
8636
                                        header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
 
8637
                                        header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
 
8638
                                        header('Content-Disposition: inline; filename="'.basename($name).'";');
 
8639
                                        $this->sendOutputData(file_get_contents($name), filesize($name));
 
8640
                                } elseif ($dest == 'FD') {
 
8641
                                        // send headers to browser
 
8642
                                        if (ob_get_contents()) {
 
8643
                                                $this->Error('Some data has already been output, can\'t send PDF file');
 
8644
                                        }
 
8645
                                        header('Content-Description: File Transfer');
 
8646
                                        if (headers_sent()) {
 
8647
                                                $this->Error('Some data has already been output to browser, can\'t send PDF file');
 
8648
                                        }
 
8649
                                        header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
 
8650
                                        header('Pragma: public');
 
8651
                                        header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
 
8652
                                        header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
 
8653
                                        // force download dialog
 
8654
                                        if (strpos(php_sapi_name(), 'cgi') === false) {
 
8655
                                                header('Content-Type: application/force-download');
 
8656
                                                header('Content-Type: application/octet-stream', false);
 
8657
                                                header('Content-Type: application/download', false);
 
8658
                                                header('Content-Type: application/pdf', false);
 
8659
                                        } else {
 
8660
                                                header('Content-Type: application/pdf');
 
8661
                                        }
 
8662
                                        // use the Content-Disposition header to supply a recommended filename
 
8663
                                        header('Content-Disposition: attachment; filename="'.basename($name).'";');
 
8664
                                        header('Content-Transfer-Encoding: binary');
 
8665
                                        $this->sendOutputData(file_get_contents($name), filesize($name));
 
8666
                                }
 
8667
                                break;
 
8668
                        }
 
8669
                        case 'E': {
 
8670
                                // return PDF as base64 mime multi-part email attachment (RFC 2045)
 
8671
                                $retval = 'Content-Type: application/pdf;'."\r\n";
 
8672
                                $retval .= ' name="'.$name.'"'."\r\n";
 
8673
                                $retval .= 'Content-Transfer-Encoding: base64'."\r\n";
 
8674
                                $retval .= 'Content-Disposition: attachment;'."\r\n";
 
8675
                                $retval .= ' filename="'.$name.'"'."\r\n\r\n";
 
8676
                                $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
 
8677
                                return $retval;
 
8678
                        }
 
8679
                        case 'S': {
 
8680
                                // returns PDF as a string
 
8681
                                return $this->getBuffer();
 
8682
                        }
 
8683
                        default: {
 
8684
                                $this->Error('Incorrect output destination: '.$dest);
 
8685
                        }
 
8686
                }
 
8687
                return '';
 
8688
        }
 
8689
 
 
8690
        /**
 
8691
         * Unset all class variables except the following critical variables: internal_encoding, state, bufferlen, buffer and diskcache.
 
8692
         * @param $destroyall (boolean) if true destroys all class variables, otherwise preserves critical variables.
 
8693
         * @param $preserve_objcopy (boolean) if true preserves the objcopy variable
 
8694
         * @public
 
8695
         * @since 4.5.016 (2009-02-24)
 
8696
         */
 
8697
        public function _destroy($destroyall=false, $preserve_objcopy=false) {
 
8698
                if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) {
 
8699
                        // remove buffer file from cache
 
8700
                        unlink($this->buffer);
 
8701
                }
 
8702
                foreach (array_keys(get_object_vars($this)) as $val) {
 
8703
                        if ($destroyall OR (
 
8704
                                ($val != 'internal_encoding')
 
8705
                                AND ($val != 'state')
 
8706
                                AND ($val != 'bufferlen')
 
8707
                                AND ($val != 'buffer')
 
8708
                                AND ($val != 'diskcache')
 
8709
                                AND ($val != 'sign')
 
8710
                                AND ($val != 'signature_data')
 
8711
                                AND ($val != 'signature_max_length')
 
8712
                                AND ($val != 'byterange_string')
 
8713
                                )) {
 
8714
                                if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) {
 
8715
                                        unset($this->$val);
 
8716
                                }
 
8717
                        }
 
8718
                }
 
8719
        }
 
8720
 
 
8721
        /**
 
8722
         * Check for locale-related bug
 
8723
         * @protected
 
8724
         */
 
8725
        protected function _dochecks() {
 
8726
                //Check for locale-related bug
 
8727
                if (1.1 == 1) {
 
8728
                        $this->Error('Don\'t alter the locale before including class file');
 
8729
                }
 
8730
                //Check for decimal separator
 
8731
                if (sprintf('%.1F', 1.0) != '1.0') {
 
8732
                        setlocale(LC_NUMERIC, 'C');
 
8733
                }
 
8734
        }
 
8735
 
 
8736
        /**
 
8737
         * Return fonts path
 
8738
         * @return string
 
8739
         * @protected
 
8740
         */
 
8741
        protected function _getfontpath() {
 
8742
                if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
 
8743
                        define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
 
8744
                }
 
8745
                return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
 
8746
        }
 
8747
 
 
8748
        /**
 
8749
         * Return an array containing variations for the basic page number alias.
 
8750
         * @param $a (string) Base alias.
 
8751
         * @return array of page number aliases
 
8752
         * @protected
 
8753
         */
 
8754
        protected function getInternalPageNumberAliases($a= '') {
 
8755
                $alias = array();
 
8756
                // build array of Unicode + ASCII variants (the order is important)
 
8757
                $alias = array('u' => array(), 'a' => array());
 
8758
                $u = '{'.$a.'}';
 
8759
                $alias['u'][] = $this->_escape($u);
 
8760
                if ($this->isunicode) {
 
8761
                        $alias['u'][] = $this->_escape($this->UTF8ToLatin1($u));
 
8762
                        $alias['u'][] = $this->_escape($this->utf8StrRev($u, false, $this->tmprtl));
 
8763
                        $alias['a'][] = $this->_escape($this->UTF8ToLatin1($a));
 
8764
                        $alias['a'][] = $this->_escape($this->utf8StrRev($a, false, $this->tmprtl));
 
8765
                }
 
8766
                $alias['a'][] = $this->_escape($a);
 
8767
                return $alias;
 
8768
        }
 
8769
 
 
8770
        /**
 
8771
         * Return an array containing all internal page aliases.
 
8772
         * @return array of page number aliases
 
8773
         * @protected
 
8774
         */
 
8775
        protected function getAllInternalPageNumberAliases() {
 
8776
                $basic_alias = array($this->alias_tot_pages, $this->alias_num_page, $this->alias_group_tot_pages, $this->alias_group_num_page, $this->alias_right_shift);
 
8777
                $pnalias = array();
 
8778
                foreach($basic_alias as $k => $a) {
 
8779
                        $pnalias[$k] = $this->getInternalPageNumberAliases($a);
 
8780
                }
 
8781
                return $pnalias;
 
8782
        }
 
8783
 
 
8784
        /**
 
8785
         * Replace page number aliases with number.
 
8786
         * @param $page (string) Page content.
 
8787
         * @param $replace (array) Array of replacements (array keys are replacement strings, values are alias arrays).
 
8788
         * @param $diff (int) If passed, this will be set to the total char number difference between alias and replacements.
 
8789
         * @return replaced page content and updated $diff parameter as array.
 
8790
         * @protected
 
8791
         */
 
8792
        protected function replacePageNumAliases($page, $replace, $diff=0) {
 
8793
                foreach ($replace as $rep) {
 
8794
                        foreach ($rep[3] as $a) {
 
8795
                                if (strpos($page, $a) !== false) {
 
8796
                                        $page = str_replace($a, $rep[0], $page);
 
8797
                                        $diff += ($rep[2] - $rep[1]);
 
8798
                                }
 
8799
                        }
 
8800
                }
 
8801
                return array($page, $diff);
 
8802
        }
 
8803
 
 
8804
        /**
 
8805
         * Replace right shift page number aliases with spaces to correct right alignment.
 
8806
         * This works perfectly only when using monospaced fonts.
 
8807
         * @param $page (string) Page content.
 
8808
         * @param $aliases (array) Array of page aliases.
 
8809
         * @param $diff (int) initial difference to add.
 
8810
         * @return replaced page content.
 
8811
         * @protected
 
8812
         */
 
8813
        protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
 
8814
                foreach ($aliases as $type => $alias) {
 
8815
                        foreach ($alias as $a) {
 
8816
                                // find position of compensation factor
 
8817
                                $startnum = (strpos($a, ':') + 1);
 
8818
                                $a = substr($a, 0, $startnum);
 
8819
                                if (($pos = strpos($page, $a)) !== false) {
 
8820
                                        // end of alias
 
8821
                                        $endnum = strpos($page, '}', $pos);
 
8822
                                        // string to be replaced
 
8823
                                        $aa = substr($page, $pos, ($endnum - $pos + 1));
 
8824
                                        // get compensation factor
 
8825
                                        $ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum));
 
8826
                                        $ratio = preg_replace('/[^0-9\.]/', '', $ratio);
 
8827
                                        $ratio = floatval($ratio);
 
8828
                                        if ($type == 'u') {
 
8829
                                                $chrdiff = floor(($diff + 12) * $ratio);
 
8830
                                                $shift = str_repeat(' ', $chrdiff);
 
8831
                                                $shift = $this->UTF8ToUTF16BE($shift, false);
 
8832
                                        } else {
 
8833
                                                $chrdiff = floor(($diff + 11) * $ratio);
 
8834
                                                $shift = str_repeat(' ', $chrdiff);
 
8835
                                        }
 
8836
                                        $page = str_replace($aa, $shift, $page);
 
8837
                                }
 
8838
                        }
 
8839
                }
 
8840
                return $page;
 
8841
        }
 
8842
 
 
8843
        /**
 
8844
         * Output pages (and replace page number aliases).
 
8845
         * @protected
 
8846
         */
 
8847
        protected function _putpages() {
 
8848
                $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
 
8849
                // get internal aliases for page numbers
 
8850
                $pnalias = $this->getAllInternalPageNumberAliases();
 
8851
                $num_pages = $this->numpages;
 
8852
                $ptpa = $this->formatPageNumber(($this->starting_page_number + $num_pages - 1));
 
8853
                $ptpu = $this->UTF8ToUTF16BE($ptpa, false);
 
8854
                $ptp_num_chars = $this->GetNumChars($ptpa);
 
8855
                $pagegroupnum = 0;
 
8856
                $groupnum = 0;
 
8857
                $ptgu = 1;
 
8858
                $ptga = 1;
 
8859
                for ($n = 1; $n <= $num_pages; ++$n) {
 
8860
                        // get current page
 
8861
                        $temppage = $this->getPageBuffer($n);
 
8862
                        $pagelen = strlen($temppage);
 
8863
                        // set replacements for total pages number
 
8864
                        $pnpa = $this->formatPageNumber(($this->starting_page_number + $n - 1));
 
8865
                        $pnpu = $this->UTF8ToUTF16BE($pnpa, false);
 
8866
                        $pnp_num_chars = $this->GetNumChars($pnpa);
 
8867
                        $pdiff = 0; // difference used for right shift alignment of page numbers
 
8868
                        $gdiff = 0; // difference used for right shift alignment of page group numbers
 
8869
                        if (!empty($this->pagegroups)) {
 
8870
                                if (isset($this->newpagegroup[$n])) {
 
8871
                                        $pagegroupnum = 0;
 
8872
                                        ++$groupnum;
 
8873
                                        $ptga = $this->formatPageNumber($this->pagegroups[$groupnum]);
 
8874
                                        $ptgu = $this->UTF8ToUTF16BE($ptga, false);
 
8875
                                        $ptg_num_chars = $this->GetNumChars($ptga);
 
8876
                                }
 
8877
                                ++$pagegroupnum;
 
8878
                                $pnga = $this->formatPageNumber($pagegroupnum);
 
8879
                                $pngu = $this->UTF8ToUTF16BE($pnga, false);
 
8880
                                $png_num_chars = $this->GetNumChars($pnga);
 
8881
                                // replace page numbers
 
8882
                                $replace = array();
 
8883
                                $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
 
8884
                                $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
 
8885
                                $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
 
8886
                                $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
 
8887
                                list($temppage, $gdiff) = $this->replacePageNumAliases($temppage, $replace, $gdiff);
 
8888
                        }
 
8889
                        // replace page numbers
 
8890
                        $replace = array();
 
8891
                        $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
 
8892
                        $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
 
8893
                        $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
 
8894
                        $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
 
8895
                        list($temppage, $pdiff) = $this->replacePageNumAliases($temppage, $replace, $pdiff);
 
8896
                        // replace right shift alias
 
8897
                        $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
 
8898
                        // replace EPS marker
 
8899
                        $temppage = str_replace($this->epsmarker, '', $temppage);
 
8900
                        //Page
 
8901
                        $this->page_obj_id[$n] = $this->_newobj();
 
8902
                        $out = '<<';
 
8903
                        $out .= ' /Type /Page';
 
8904
                        $out .= ' /Parent 1 0 R';
 
8905
                        $out .= ' /LastModified '.$this->_datestring();
 
8906
                        $out .= ' /Resources 2 0 R';
 
8907
                        $boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
 
8908
                        foreach ($boxes as $box) {
 
8909
                                $out .= ' /'.$box;
 
8910
                                $out .= sprintf(' [%.2F %.2F %.2F %.2F]', $this->pagedim[$n][$box]['llx'], $this->pagedim[$n][$box]['lly'], $this->pagedim[$n][$box]['urx'], $this->pagedim[$n][$box]['ury']);
 
8911
                        }
 
8912
                        if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) {
 
8913
                                $out .= ' /BoxColorInfo <<';
 
8914
                                foreach ($boxes as $box) {
 
8915
                                        if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) {
 
8916
                                                $out .= ' /'.$box.' <<';
 
8917
                                                if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) {
 
8918
                                                        $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C'];
 
8919
                                                        $out .= ' /C [';
 
8920
                                                        $out .= sprintf(' %.3F %.3F %.3F', $color[0]/255, $color[1]/255, $color[2]/255);
 
8921
                                                        $out .= ' ]';
 
8922
                                                }
 
8923
                                                if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) {
 
8924
                                                        $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k);
 
8925
                                                }
 
8926
                                                if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) {
 
8927
                                                        $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S'];
 
8928
                                                }
 
8929
                                                if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) {
 
8930
                                                        $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D'];
 
8931
                                                        $out .= ' /D [';
 
8932
                                                        foreach ($dashes as $dash) {
 
8933
                                                                $out .= sprintf(' %.3F', ($dash * $this->k));
 
8934
                                                        }
 
8935
                                                        $out .= ' ]';
 
8936
                                                }
 
8937
                                                $out .= ' >>';
 
8938
                                        }
 
8939
                                }
 
8940
                                $out .= ' >>';
 
8941
                        }
 
8942
                        $out .= ' /Contents '.($this->n + 1).' 0 R';
 
8943
                        $out .= ' /Rotate '.$this->pagedim[$n]['Rotate'];
 
8944
                        if (!$this->pdfa_mode) {
 
8945
                                $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
 
8946
                        }
 
8947
                        if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) {
 
8948
                                // page transitions
 
8949
                                if (isset($this->pagedim[$n]['trans']['Dur'])) {
 
8950
                                        $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur'];
 
8951
                                }
 
8952
                                $out .= ' /Trans <<';
 
8953
                                $out .= ' /Type /Trans';
 
8954
                                if (isset($this->pagedim[$n]['trans']['S'])) {
 
8955
                                        $out .= ' /S /'.$this->pagedim[$n]['trans']['S'];
 
8956
                                }
 
8957
                                if (isset($this->pagedim[$n]['trans']['D'])) {
 
8958
                                        $out .= ' /D '.$this->pagedim[$n]['trans']['D'];
 
8959
                                }
 
8960
                                if (isset($this->pagedim[$n]['trans']['Dm'])) {
 
8961
                                        $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm'];
 
8962
                                }
 
8963
                                if (isset($this->pagedim[$n]['trans']['M'])) {
 
8964
                                        $out .= ' /M /'.$this->pagedim[$n]['trans']['M'];
 
8965
                                }
 
8966
                                if (isset($this->pagedim[$n]['trans']['Di'])) {
 
8967
                                        $out .= ' /Di '.$this->pagedim[$n]['trans']['Di'];
 
8968
                                }
 
8969
                                if (isset($this->pagedim[$n]['trans']['SS'])) {
 
8970
                                        $out .= ' /SS '.$this->pagedim[$n]['trans']['SS'];
 
8971
                                }
 
8972
                                if (isset($this->pagedim[$n]['trans']['B'])) {
 
8973
                                        $out .= ' /B '.$this->pagedim[$n]['trans']['B'];
 
8974
                                }
 
8975
                                $out .= ' >>';
 
8976
                        }
 
8977
                        $out .= $this->_getannotsrefs($n);
 
8978
                        $out .= ' /PZ '.$this->pagedim[$n]['PZ'];
 
8979
                        $out .= ' >>';
 
8980
                        $out .= "\n".'endobj';
 
8981
                        $this->_out($out);
 
8982
                        //Page content
 
8983
                        $p = ($this->compress) ? gzcompress($temppage) : $temppage;
 
8984
                        $this->_newobj();
 
8985
                        $p = $this->_getrawstream($p);
 
8986
                        $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
 
8987
                        if ($this->diskcache) {
 
8988
                                // remove temporary files
 
8989
                                unlink($this->pages[$n]);
 
8990
                        }
 
8991
                }
 
8992
                //Pages root
 
8993
                $out = $this->_getobj(1)."\n";
 
8994
                $out .= '<< /Type /Pages /Kids [';
 
8995
                foreach($this->page_obj_id as $page_obj) {
 
8996
                        $out .= ' '.$page_obj.' 0 R';
 
8997
                }
 
8998
                $out .= ' ] /Count '.$num_pages.' >>';
 
8999
                $out .= "\n".'endobj';
 
9000
                $this->_out($out);
 
9001
        }
 
9002
 
 
9003
        /**
 
9004
         * Output references to page annotations
 
9005
         * @param $n (int) page number
 
9006
         * @protected
 
9007
         * @author Nicola Asuni
 
9008
         * @since 4.7.000 (2008-08-29)
 
9009
         * @deprecated
 
9010
         */
 
9011
        protected function _putannotsrefs($n) {
 
9012
                $this->_out($this->_getannotsrefs($n));
 
9013
        }
 
9014
 
 
9015
        /**
 
9016
         * Get references to page annotations.
 
9017
         * @param $n (int) page number
 
9018
         * @return string
 
9019
         * @protected
 
9020
         * @author Nicola Asuni
 
9021
         * @since 5.0.010 (2010-05-17)
 
9022
         */
 
9023
        protected function _getannotsrefs($n) {
 
9024
                if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
 
9025
                        return '';
 
9026
                }
 
9027
                $out = ' /Annots [';
 
9028
                if (isset($this->PageAnnots[$n])) {
 
9029
                        foreach ($this->PageAnnots[$n] as $key => $val) {
 
9030
                                if (!in_array($val['n'], $this->radio_groups)) {
 
9031
                                        $out .= ' '.$val['n'].' 0 R';
 
9032
                                }
 
9033
                        }
 
9034
                        // add radiobutton groups
 
9035
                        if (isset($this->radiobutton_groups[$n])) {
 
9036
                                foreach ($this->radiobutton_groups[$n] as $key => $data) {
 
9037
                                        if (isset($data['n'])) {
 
9038
                                                $out .= ' '.$data['n'].' 0 R';
 
9039
                                        }
 
9040
                                }
 
9041
                        }
 
9042
                }
 
9043
                if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
 
9044
                        // set reference for signature object
 
9045
                        $out .= ' '.$this->sig_obj_id.' 0 R';
 
9046
                }
 
9047
                if (!empty($this->empty_signature_appearance)) {
 
9048
                        foreach ($this->empty_signature_appearance as $esa) {
 
9049
                                if ($esa['page'] == $n) {
 
9050
                                        // set reference for empty signature objects
 
9051
                                        $out .= ' '.$esa['objid'].' 0 R';
 
9052
                                }
 
9053
                        }
 
9054
                }
 
9055
                $out .= ' ]';
 
9056
                return $out;
 
9057
        }
 
9058
 
 
9059
        /**
 
9060
         * Output annotations objects for all pages.
 
9061
         * !!! THIS METHOD IS NOT YET COMPLETED !!!
 
9062
         * See section 12.5 of PDF 32000_2008 reference.
 
9063
         * @protected
 
9064
         * @author Nicola Asuni
 
9065
         * @since 4.0.018 (2008-08-06)
 
9066
         */
 
9067
        protected function _putannotsobjs() {
 
9068
                // reset object counter
 
9069
                for ($n=1; $n <= $this->numpages; ++$n) {
 
9070
                        if (isset($this->PageAnnots[$n])) {
 
9071
                                // set page annotations
 
9072
                                foreach ($this->PageAnnots[$n] as $key => $pl) {
 
9073
                                        $annot_obj_id = $this->PageAnnots[$n][$key]['n'];
 
9074
                                        // create annotation object for grouping radiobuttons
 
9075
                                        if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
 
9076
                                                $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n'];
 
9077
                                                $annots = '<<';
 
9078
                                                $annots .= ' /Type /Annot';
 
9079
                                                $annots .= ' /Subtype /Widget';
 
9080
                                                $annots .= ' /Rect [0 0 0 0]';
 
9081
                                                if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) {
 
9082
                                                        // read only
 
9083
                                                        $annots .= ' /F 68';
 
9084
                                                        $annots .= ' /Ff 49153';
 
9085
                                                } else {
 
9086
                                                        $annots .= ' /F 4'; // default print for PDF/A
 
9087
                                                        $annots .= ' /Ff 49152';
 
9088
                                                }
 
9089
                                                $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
 
9090
                                                $annots .= ' /FT /Btn';
 
9091
                                                $annots .= ' /Kids [';
 
9092
                                                foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) {
 
9093
                                                        if (isset($data['kid'])) {
 
9094
                                                                $annots .= ' '.$data['kid'].' 0 R';
 
9095
                                                                if ($data['def'] !== 'Off') {
 
9096
                                                                        $defval = $data['def'];
 
9097
                                                                }
 
9098
                                                        }
 
9099
                                                }
 
9100
                                                $annots .= ' ]';
 
9101
                                                if (isset($defval)) {
 
9102
                                                        $annots .= ' /V /'.$defval;
 
9103
                                                }
 
9104
                                                $annots .= ' >>';
 
9105
                                                $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
 
9106
                                                $this->form_obj_id[] = $radio_button_obj_id;
 
9107
                                                // store object id to be used on Parent entry of Kids
 
9108
                                                $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id;
 
9109
                                        }
 
9110
                                        $formfield = false;
 
9111
                                        $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
 
9112
                                        $a = $pl['x'] * $this->k;
 
9113
                                        $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k);
 
9114
                                        $c = $pl['w'] * $this->k;
 
9115
                                        $d = $pl['h'] * $this->k;
 
9116
                                        $rect = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b+$d);
 
9117
                                        // create new annotation object
 
9118
                                        $annots = '<</Type /Annot';
 
9119
                                        $annots .= ' /Subtype /'.$pl['opt']['subtype'];
 
9120
                                        $annots .= ' /Rect ['.$rect.']';
 
9121
                                        $ft = array('Btn', 'Tx', 'Ch', 'Sig');
 
9122
                                        if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
 
9123
                                                $annots .= ' /FT /'.$pl['opt']['ft'];
 
9124
                                                $formfield = true;
 
9125
                                        }
 
9126
                                        $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
 
9127
                                        $annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
 
9128
                                        $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
 
9129
                                        $annots .= ' /M '.$this->_datestring($annot_obj_id);
 
9130
                                        if (isset($pl['opt']['f'])) {
 
9131
                                                $fval = 0;
 
9132
                                                if (is_array($pl['opt']['f'])) {
 
9133
                                                        foreach ($pl['opt']['f'] as $f) {
 
9134
                                                                switch (strtolower($f)) {
 
9135
                                                                        case 'invisible': {
 
9136
                                                                                $fval += 1 << 0;
 
9137
                                                                                break;
 
9138
                                                                        }
 
9139
                                                                        case 'hidden': {
 
9140
                                                                                $fval += 1 << 1;
 
9141
                                                                                break;
 
9142
                                                                        }
 
9143
                                                                        case 'print': {
 
9144
                                                                                $fval += 1 << 2;
 
9145
                                                                                break;
 
9146
                                                                        }
 
9147
                                                                        case 'nozoom': {
 
9148
                                                                                $fval += 1 << 3;
 
9149
                                                                                break;
 
9150
                                                                        }
 
9151
                                                                        case 'norotate': {
 
9152
                                                                                $fval += 1 << 4;
 
9153
                                                                                break;
 
9154
                                                                        }
 
9155
                                                                        case 'noview': {
 
9156
                                                                                $fval += 1 << 5;
 
9157
                                                                                break;
 
9158
                                                                        }
 
9159
                                                                        case 'readonly': {
 
9160
                                                                                $fval += 1 << 6;
 
9161
                                                                                break;
 
9162
                                                                        }
 
9163
                                                                        case 'locked': {
 
9164
                                                                                $fval += 1 << 8;
 
9165
                                                                                break;
 
9166
                                                                        }
 
9167
                                                                        case 'togglenoview': {
 
9168
                                                                                $fval += 1 << 9;
 
9169
                                                                                break;
 
9170
                                                                        }
 
9171
                                                                        case 'lockedcontents': {
 
9172
                                                                                $fval += 1 << 10;
 
9173
                                                                                break;
 
9174
                                                                        }
 
9175
                                                                        default: {
 
9176
                                                                                break;
 
9177
                                                                        }
 
9178
                                                                }
 
9179
                                                        }
 
9180
                                                } else {
 
9181
                                                        $fval = intval($pl['opt']['f']);
 
9182
                                                }
 
9183
                                        } else {
 
9184
                                                $fval = 4;
 
9185
                                        }
 
9186
                                        if ($this->pdfa_mode) {
 
9187
                                                // force print flag for PDF/A mode
 
9188
                                                $fval |= 4;
 
9189
                                        }
 
9190
                                        $annots .= ' /F '.intval($fval);
 
9191
                                        if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
 
9192
                                                $annots .= ' /AS /'.$pl['opt']['as'];
 
9193
                                        }
 
9194
                                        if (isset($pl['opt']['ap'])) {
 
9195
                                                // appearance stream
 
9196
                                                $annots .= ' /AP <<';
 
9197
                                                if (is_array($pl['opt']['ap'])) {
 
9198
                                                        foreach ($pl['opt']['ap'] as $apmode => $apdef) {
 
9199
                                                                // $apmode can be: n = normal; r = rollover; d = down;
 
9200
                                                                $annots .= ' /'.strtoupper($apmode);
 
9201
                                                                if (is_array($apdef)) {
 
9202
                                                                        $annots .= ' <<';
 
9203
                                                                        foreach ($apdef as $apstate => $stream) {
 
9204
                                                                                // reference to XObject that define the appearance for this mode-state
 
9205
                                                                                $apsobjid = $this->_putAPXObject($c, $d, $stream);
 
9206
                                                                                $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
 
9207
                                                                        }
 
9208
                                                                        $annots .= ' >>';
 
9209
                                                                } else {
 
9210
                                                                        // reference to XObject that define the appearance for this mode
 
9211
                                                                        $apsobjid = $this->_putAPXObject($c, $d, $apdef);
 
9212
                                                                        $annots .= ' '.$apsobjid.' 0 R';
 
9213
                                                                }
 
9214
                                                        }
 
9215
                                                } else {
 
9216
                                                        $annots .= $pl['opt']['ap'];
 
9217
                                                }
 
9218
                                                $annots .= ' >>';
 
9219
                                        }
 
9220
                                        if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
 
9221
                                                $annots .= ' /BS <<';
 
9222
                                                $annots .= ' /Type /Border';
 
9223
                                                if (isset($pl['opt']['bs']['w'])) {
 
9224
                                                        $annots .= ' /W '.intval($pl['opt']['bs']['w']);
 
9225
                                                }
 
9226
                                                $bstyles = array('S', 'D', 'B', 'I', 'U');
 
9227
                                                if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
 
9228
                                                        $annots .= ' /S /'.$pl['opt']['bs']['s'];
 
9229
                                                }
 
9230
                                                if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
 
9231
                                                        $annots .= ' /D [';
 
9232
                                                        foreach ($pl['opt']['bs']['d'] as $cord) {
 
9233
                                                                $annots .= ' '.intval($cord);
 
9234
                                                        }
 
9235
                                                        $annots .= ']';
 
9236
                                                }
 
9237
                                                $annots .= ' >>';
 
9238
                                        } else {
 
9239
                                                $annots .= ' /Border [';
 
9240
                                                if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
 
9241
                                                        $annots .= intval($pl['opt']['border'][0]).' ';
 
9242
                                                        $annots .= intval($pl['opt']['border'][1]).' ';
 
9243
                                                        $annots .= intval($pl['opt']['border'][2]);
 
9244
                                                        if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
 
9245
                                                                $annots .= ' [';
 
9246
                                                                foreach ($pl['opt']['border'][3] as $dash) {
 
9247
                                                                        $annots .= intval($dash).' ';
 
9248
                                                                }
 
9249
                                                                $annots .= ']';
 
9250
                                                        }
 
9251
                                                } else {
 
9252
                                                        $annots .= '0 0 0';
 
9253
                                                }
 
9254
                                                $annots .= ']';
 
9255
                                        }
 
9256
                                        if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
 
9257
                                                $annots .= ' /BE <<';
 
9258
                                                $bstyles = array('S', 'C');
 
9259
                                                if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
 
9260
                                                        $annots .= ' /S /'.$pl['opt']['bs']['s'];
 
9261
                                                } else {
 
9262
                                                        $annots .= ' /S /S';
 
9263
                                                }
 
9264
                                                if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
 
9265
                                                        $annots .= ' /I '.sprintf(' %.4F', $pl['opt']['be']['i']);
 
9266
                                                }
 
9267
                                                $annots .= '>>';
 
9268
                                        }
 
9269
                                        if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
 
9270
                                                $annots .= ' /C '.$this->getColorStringFromArray($pl['opt']['c']);
 
9271
                                        }
 
9272
                                        //$annots .= ' /StructParent ';
 
9273
                                        //$annots .= ' /OC ';
 
9274
                                        $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
 
9275
                                        if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
 
9276
                                                // this is a markup type
 
9277
                                                if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
 
9278
                                                        $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
 
9279
                                                }
 
9280
                                                //$annots .= ' /Popup ';
 
9281
                                                if (isset($pl['opt']['ca'])) {
 
9282
                                                        $annots .= ' /CA '.sprintf('%.4F', floatval($pl['opt']['ca']));
 
9283
                                                }
 
9284
                                                if (isset($pl['opt']['rc'])) {
 
9285
                                                        $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
 
9286
                                                }
 
9287
                                                $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id);
 
9288
                                                //$annots .= ' /IRT ';
 
9289
                                                if (isset($pl['opt']['subj'])) {
 
9290
                                                        $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
 
9291
                                                }
 
9292
                                                //$annots .= ' /RT ';
 
9293
                                                //$annots .= ' /IT ';
 
9294
                                                //$annots .= ' /ExData ';
 
9295
                                        }
 
9296
                                        $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
 
9297
                                        // Annotation types
 
9298
                                        switch (strtolower($pl['opt']['subtype'])) {
 
9299
                                                case 'text': {
 
9300
                                                        if (isset($pl['opt']['open'])) {
 
9301
                                                                $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
 
9302
                                                        }
 
9303
                                                        $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
 
9304
                                                        if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
 
9305
                                                                $annots .= ' /Name /'.$pl['opt']['name'];
 
9306
                                                        } else {
 
9307
                                                                $annots .= ' /Name /Note';
 
9308
                                                        }
 
9309
                                                        $statemodels = array('Marked', 'Review');
 
9310
                                                        if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
 
9311
                                                                $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
 
9312
                                                        } else {
 
9313
                                                                $pl['opt']['statemodel'] = 'Marked';
 
9314
                                                                $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
 
9315
                                                        }
 
9316
                                                        if ($pl['opt']['statemodel'] == 'Marked') {
 
9317
                                                                $states = array('Accepted', 'Unmarked');
 
9318
                                                        } else {
 
9319
                                                                $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
 
9320
                                                        }
 
9321
                                                        if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
 
9322
                                                                $annots .= ' /State /'.$pl['opt']['state'];
 
9323
                                                        } else {
 
9324
                                                                if ($pl['opt']['statemodel'] == 'Marked') {
 
9325
                                                                        $annots .= ' /State /Unmarked';
 
9326
                                                                } else {
 
9327
                                                                        $annots .= ' /State /None';
 
9328
                                                                }
 
9329
                                                        }
 
9330
                                                        break;
 
9331
                                                }
 
9332
                                                case 'link': {
 
9333
                                                        if (is_string($pl['txt'])) {
 
9334
                                                                // external URI link
 
9335
                                                                $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
 
9336
                                                        } else {
 
9337
                                                                // internal link
 
9338
                                                                $l = $this->links[$pl['txt']];
 
9339
                                                                if (isset($this->page_obj_id[($l[0])])) {
 
9340
                                                                        $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %.2F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
 
9341
                                                                }
 
9342
                                                        }
 
9343
                                                        $hmodes = array('N', 'I', 'O', 'P');
 
9344
                                                        if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
 
9345
                                                                $annots .= ' /H /'.$pl['opt']['h'];
 
9346
                                                        } else {
 
9347
                                                                $annots .= ' /H /I';
 
9348
                                                        }
 
9349
                                                        //$annots .= ' /PA ';
 
9350
                                                        //$annots .= ' /Quadpoints ';
 
9351
                                                        break;
 
9352
                                                }
 
9353
                                                case 'freetext': {
 
9354
                                                        if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
 
9355
                                                                $annots .= ' /DA ('.$pl['opt']['da'].')';
 
9356
                                                        }
 
9357
                                                        if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
 
9358
                                                                $annots .= ' /Q '.intval($pl['opt']['q']);
 
9359
                                                        }
 
9360
                                                        if (isset($pl['opt']['rc'])) {
 
9361
                                                                $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
 
9362
                                                        }
 
9363
                                                        if (isset($pl['opt']['ds'])) {
 
9364
                                                                $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
 
9365
                                                        }
 
9366
                                                        if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
 
9367
                                                                $annots .= ' /CL [';
 
9368
                                                                foreach ($pl['opt']['cl'] as $cl) {
 
9369
                                                                        $annots .= sprintf('%.4F ', $cl * $this->k);
 
9370
                                                                }
 
9371
                                                                $annots .= ']';
 
9372
                                                        }
 
9373
                                                        $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
 
9374
                                                        if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
 
9375
                                                                $annots .= ' /IT /'.$pl['opt']['it'];
 
9376
                                                        }
 
9377
                                                        if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
 
9378
                                                                $l = $pl['opt']['rd'][0] * $this->k;
 
9379
                                                                $r = $pl['opt']['rd'][1] * $this->k;
 
9380
                                                                $t = $pl['opt']['rd'][2] * $this->k;
 
9381
                                                                $b = $pl['opt']['rd'][3] * $this->k;
 
9382
                                                                $annots .= ' /RD ['.sprintf('%.2F %.2F %.2F %.2F', $l, $r, $t, $b).']';
 
9383
                                                        }
 
9384
                                                        if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
 
9385
                                                                $annots .= ' /LE /'.$pl['opt']['le'];
 
9386
                                                        }
 
9387
                                                        break;
 
9388
                                                }
 
9389
                                                case 'line': {
 
9390
                                                        break;
 
9391
                                                }
 
9392
                                                case 'square': {
 
9393
                                                        break;
 
9394
                                                }
 
9395
                                                case 'circle': {
 
9396
                                                        break;
 
9397
                                                }
 
9398
                                                case 'polygon': {
 
9399
                                                        break;
 
9400
                                                }
 
9401
                                                case 'polyline': {
 
9402
                                                        break;
 
9403
                                                }
 
9404
                                                case 'highlight': {
 
9405
                                                        break;
 
9406
                                                }
 
9407
                                                case 'underline': {
 
9408
                                                        break;
 
9409
                                                }
 
9410
                                                case 'squiggly': {
 
9411
                                                        break;
 
9412
                                                }
 
9413
                                                case 'strikeout': {
 
9414
                                                        break;
 
9415
                                                }
 
9416
                                                case 'stamp': {
 
9417
                                                        break;
 
9418
                                                }
 
9419
                                                case 'caret': {
 
9420
                                                        break;
 
9421
                                                }
 
9422
                                                case 'ink': {
 
9423
                                                        break;
 
9424
                                                }
 
9425
                                                case 'popup': {
 
9426
                                                        break;
 
9427
                                                }
 
9428
                                                case 'fileattachment': {
 
9429
                                                        if ($this->pdfa_mode) {
 
9430
                                                                // embedded files are not allowed in PDF/A mode
 
9431
                                                                break;
 
9432
                                                        }
 
9433
                                                        if (!isset($pl['opt']['fs'])) {
 
9434
                                                                break;
 
9435
                                                        }
 
9436
                                                        $filename = basename($pl['opt']['fs']);
 
9437
                                                        if (isset($this->embeddedfiles[$filename]['n'])) {
 
9438
                                                                $annots .= ' /FS <</Type /Filespec /F '.$this->_datastring($filename, $annot_obj_id).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
 
9439
                                                                $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
 
9440
                                                                if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
 
9441
                                                                        $annots .= ' /Name /'.$pl['opt']['name'];
 
9442
                                                                } else {
 
9443
                                                                        $annots .= ' /Name /PushPin';
 
9444
                                                                }
 
9445
                                                        }
 
9446
                                                        break;
 
9447
                                                }
 
9448
                                                case 'sound': {
 
9449
                                                        if (!isset($pl['opt']['fs'])) {
 
9450
                                                                break;
 
9451
                                                        }
 
9452
                                                        $filename = basename($pl['opt']['fs']);
 
9453
                                                        if (isset($this->embeddedfiles[$filename]['n'])) {
 
9454
                                                                // ... TO BE COMPLETED ...
 
9455
                                                                // /R /C /B /E /CO /CP
 
9456
                                                                $annots .= ' /Sound <</Type /Filespec /F '.$this->_datastring($filename, $annot_obj_id).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
 
9457
                                                                $iconsapp = array('Speaker', 'Mic');
 
9458
                                                                if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
 
9459
                                                                        $annots .= ' /Name /'.$pl['opt']['name'];
 
9460
                                                                } else {
 
9461
                                                                        $annots .= ' /Name /Speaker';
 
9462
                                                                }
 
9463
                                                        }
 
9464
                                                        break;
 
9465
                                                }
 
9466
                                                case 'movie': {
 
9467
                                                        break;
 
9468
                                                }
 
9469
                                                case 'widget': {
 
9470
                                                        $hmode = array('N', 'I', 'O', 'P', 'T');
 
9471
                                                        if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
 
9472
                                                                $annots .= ' /H /'.$pl['opt']['h'];
 
9473
                                                        }
 
9474
                                                        if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
 
9475
                                                                $annots .= ' /MK <<';
 
9476
                                                                if (isset($pl['opt']['mk']['r'])) {
 
9477
                                                                        $annots .= ' /R '.$pl['opt']['mk']['r'];
 
9478
                                                                }
 
9479
                                                                if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
 
9480
                                                                        $annots .= ' /BC '.$this->getColorStringFromArray($pl['opt']['mk']['bc']);
 
9481
                                                                }
 
9482
                                                                if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
 
9483
                                                                        $annots .= ' /BG '.$this->getColorStringFromArray($pl['opt']['mk']['bg']);
 
9484
                                                                }
 
9485
                                                                if (isset($pl['opt']['mk']['ca'])) {
 
9486
                                                                        $annots .= ' /CA '.$pl['opt']['mk']['ca'];
 
9487
                                                                }
 
9488
                                                                if (isset($pl['opt']['mk']['rc'])) {
 
9489
                                                                        $annots .= ' /RC '.$pl['opt']['mk']['rc'];
 
9490
                                                                }
 
9491
                                                                if (isset($pl['opt']['mk']['ac'])) {
 
9492
                                                                        $annots .= ' /AC '.$pl['opt']['mk']['ac'];
 
9493
                                                                }
 
9494
                                                                if (isset($pl['opt']['mk']['i'])) {
 
9495
                                                                        $info = $this->getImageBuffer($pl['opt']['mk']['i']);
 
9496
                                                                        if ($info !== false) {
 
9497
                                                                                $annots .= ' /I '.$info['n'].' 0 R';
 
9498
                                                                        }
 
9499
                                                                }
 
9500
                                                                if (isset($pl['opt']['mk']['ri'])) {
 
9501
                                                                        $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
 
9502
                                                                        if ($info !== false) {
 
9503
                                                                                $annots .= ' /RI '.$info['n'].' 0 R';
 
9504
                                                                        }
 
9505
                                                                }
 
9506
                                                                if (isset($pl['opt']['mk']['ix'])) {
 
9507
                                                                        $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
 
9508
                                                                        if ($info !== false) {
 
9509
                                                                                $annots .= ' /IX '.$info['n'].' 0 R';
 
9510
                                                                        }
 
9511
                                                                }
 
9512
                                                                if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
 
9513
                                                                        $annots .= ' /IF <<';
 
9514
                                                                        $if_sw = array('A', 'B', 'S', 'N');
 
9515
                                                                        if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
 
9516
                                                                                $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
 
9517
                                                                        }
 
9518
                                                                        $if_s = array('A', 'P');
 
9519
                                                                        if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
 
9520
                                                                                $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
 
9521
                                                                        }
 
9522
                                                                        if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
 
9523
                                                                                $annots .= sprintf(' /A [%.2F %.2F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
 
9524
                                                                        }
 
9525
                                                                        if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
 
9526
                                                                                $annots .= ' /FB true';
 
9527
                                                                        }
 
9528
                                                                        $annots .= '>>';
 
9529
                                                                }
 
9530
                                                                if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
 
9531
                                                                        $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
 
9532
                                                                }
 
9533
                                                                $annots .= '>>';
 
9534
                                                        } // end MK
 
9535
                                                        // --- Entries for field dictionaries ---
 
9536
                                                        if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
 
9537
                                                                // set parent
 
9538
                                                                $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
 
9539
                                                        }
 
9540
                                                        if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
 
9541
                                                                $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
 
9542
                                                        }
 
9543
                                                        if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
 
9544
                                                                $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
 
9545
                                                        }
 
9546
                                                        if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
 
9547
                                                                $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
 
9548
                                                        }
 
9549
                                                        if (isset($pl['opt']['ff'])) {
 
9550
                                                                if (is_array($pl['opt']['ff'])) {
 
9551
                                                                        // array of bit settings
 
9552
                                                                        $flag = 0;
 
9553
                                                                        foreach($pl['opt']['ff'] as $val) {
 
9554
                                                                                $flag += 1 << ($val - 1);
 
9555
                                                                        }
 
9556
                                                                } else {
 
9557
                                                                        $flag = intval($pl['opt']['ff']);
 
9558
                                                                }
 
9559
                                                                $annots .= ' /Ff '.$flag;
 
9560
                                                        }
 
9561
                                                        if (isset($pl['opt']['maxlen'])) {
 
9562
                                                                $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
 
9563
                                                        }
 
9564
                                                        if (isset($pl['opt']['v'])) {
 
9565
                                                                $annots .= ' /V';
 
9566
                                                                if (is_array($pl['opt']['v'])) {
 
9567
                                                                        foreach ($pl['opt']['v'] AS $optval) {
 
9568
                                                                                if (is_float($optval)) {
 
9569
                                                                                        $optval = sprintf('%.2F', $optval);
 
9570
                                                                                }
 
9571
                                                                                $annots .= ' '.$optval;
 
9572
                                                                        }
 
9573
                                                                } else {
 
9574
                                                                        $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
 
9575
                                                                }
 
9576
                                                        }
 
9577
                                                        if (isset($pl['opt']['dv'])) {
 
9578
                                                                $annots .= ' /DV';
 
9579
                                                                if (is_array($pl['opt']['dv'])) {
 
9580
                                                                        foreach ($pl['opt']['dv'] AS $optval) {
 
9581
                                                                                if (is_float($optval)) {
 
9582
                                                                                        $optval = sprintf('%.2F', $optval);
 
9583
                                                                                }
 
9584
                                                                                $annots .= ' '.$optval;
 
9585
                                                                        }
 
9586
                                                                } else {
 
9587
                                                                        $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
 
9588
                                                                }
 
9589
                                                        }
 
9590
                                                        if (isset($pl['opt']['rv'])) {
 
9591
                                                                $annots .= ' /RV';
 
9592
                                                                if (is_array($pl['opt']['rv'])) {
 
9593
                                                                        foreach ($pl['opt']['rv'] AS $optval) {
 
9594
                                                                                if (is_float($optval)) {
 
9595
                                                                                        $optval = sprintf('%.2F', $optval);
 
9596
                                                                                }
 
9597
                                                                                $annots .= ' '.$optval;
 
9598
                                                                        }
 
9599
                                                                } else {
 
9600
                                                                        $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
 
9601
                                                                }
 
9602
                                                        }
 
9603
                                                        if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
 
9604
                                                                $annots .= ' /A << '.$pl['opt']['a'].' >>';
 
9605
                                                        }
 
9606
                                                        if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
 
9607
                                                                $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
 
9608
                                                        }
 
9609
                                                        if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
 
9610
                                                                $annots .= ' /DA ('.$pl['opt']['da'].')';
 
9611
                                                        }
 
9612
                                                        if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
 
9613
                                                                $annots .= ' /Q '.intval($pl['opt']['q']);
 
9614
                                                        }
 
9615
                                                        if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
 
9616
                                                                $annots .= ' /Opt [';
 
9617
                                                                foreach($pl['opt']['opt'] AS $copt) {
 
9618
                                                                        if (is_array($copt)) {
 
9619
                                                                                $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
 
9620
                                                                        } else {
 
9621
                                                                                $annots .= ' '.$this->_textstring($copt, $annot_obj_id);
 
9622
                                                                        }
 
9623
                                                                }
 
9624
                                                                $annots .= ']';
 
9625
                                                        }
 
9626
                                                        if (isset($pl['opt']['ti'])) {
 
9627
                                                                $annots .= ' /TI '.intval($pl['opt']['ti']);
 
9628
                                                        }
 
9629
                                                        if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
 
9630
                                                                $annots .= ' /I [';
 
9631
                                                                foreach($pl['opt']['i'] AS $copt) {
 
9632
                                                                        $annots .= intval($copt).' ';
 
9633
                                                                }
 
9634
                                                                $annots .= ']';
 
9635
                                                        }
 
9636
                                                        break;
 
9637
                                                }
 
9638
                                                case 'screen': {
 
9639
                                                        break;
 
9640
                                                }
 
9641
                                                case 'printermark': {
 
9642
                                                        break;
 
9643
                                                }
 
9644
                                                case 'trapnet': {
 
9645
                                                        break;
 
9646
                                                }
 
9647
                                                case 'watermark': {
 
9648
                                                        break;
 
9649
                                                }
 
9650
                                                case '3d': {
 
9651
                                                        break;
 
9652
                                                }
 
9653
                                                default: {
 
9654
                                                        break;
 
9655
                                                }
 
9656
                                        }
 
9657
                                        $annots .= '>>';
 
9658
                                        // create new annotation object
 
9659
                                        $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
 
9660
                                        if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) {
 
9661
                                                // store reference of form object
 
9662
                                                $this->form_obj_id[] = $annot_obj_id;
 
9663
                                        }
 
9664
                                }
 
9665
                        }
 
9666
                } // end for each page
 
9667
        }
 
9668
 
 
9669
        /**
 
9670
         * Put appearance streams XObject used to define annotation's appearance states.
 
9671
         * @param $w (int) annotation width
 
9672
         * @param $h (int) annotation height
 
9673
         * @param $stream (string) appearance stream
 
9674
         * @return int object ID
 
9675
         * @protected
 
9676
         * @since 4.8.001 (2009-09-09)
 
9677
         */
 
9678
        protected function _putAPXObject($w=0, $h=0, $stream='') {
 
9679
                $stream = trim($stream);
 
9680
                $out = $this->_getobj()."\n";
 
9681
                $this->xobjects['AX'.$this->n] = array('n' => $this->n);
 
9682
                $out .= '<<';
 
9683
                $out .= ' /Type /XObject';
 
9684
                $out .= ' /Subtype /Form';
 
9685
                $out .= ' /FormType 1';
 
9686
                if ($this->compress) {
 
9687
                        $stream = gzcompress($stream);
 
9688
                        $out .= ' /Filter /FlateDecode';
 
9689
                }
 
9690
                $rect = sprintf('%.2F %.2F', $w, $h);
 
9691
                $out .= ' /BBox [0 0 '.$rect.']';
 
9692
                $out .= ' /Matrix [1 0 0 1 0 0]';
 
9693
                $out .= ' /Resources 2 0 R';
 
9694
                $stream = $this->_getrawstream($stream);
 
9695
                $out .= ' /Length '.strlen($stream);
 
9696
                $out .= ' >>';
 
9697
                $out .= ' stream'."\n".$stream."\n".'endstream';
 
9698
                $out .= "\n".'endobj';
 
9699
                $this->_out($out);
 
9700
                return $this->n;
 
9701
        }
 
9702
 
 
9703
        /**
 
9704
         * Get ULONG from string (Big Endian 32-bit unsigned integer).
 
9705
         * @param $str (string) string from where to extract value
 
9706
         * @param $offset (int) point from where to read the data
 
9707
         * @return int 32 bit value
 
9708
         * @author Nicola Asuni
 
9709
         * @protected
 
9710
         * @since 5.2.000 (2010-06-02)
 
9711
         */
 
9712
        protected function _getULONG($str, $offset) {
 
9713
                $v = unpack('Ni', substr($str, $offset, 4));
 
9714
                return $v['i'];
 
9715
        }
 
9716
 
 
9717
        /**
 
9718
         * Get USHORT from string (Big Endian 16-bit unsigned integer).
 
9719
         * @param $str (string) string from where to extract value
 
9720
         * @param $offset (int) point from where to read the data
 
9721
         * @return int 16 bit value
 
9722
         * @author Nicola Asuni
 
9723
         * @protected
 
9724
         * @since 5.2.000 (2010-06-02)
 
9725
         */
 
9726
        protected function _getUSHORT($str, $offset) {
 
9727
                $v = unpack('ni', substr($str, $offset, 2));
 
9728
                return $v['i'];
 
9729
        }
 
9730
 
 
9731
        /**
 
9732
         * Get SHORT from string (Big Endian 16-bit signed integer).
 
9733
         * @param $str (string) string from where to extract value
 
9734
         * @param $offset (int) point from where to read the data
 
9735
         * @return int 16 bit value
 
9736
         * @author Nicola Asuni
 
9737
         * @protected
 
9738
         * @since 5.2.000 (2010-06-02)
 
9739
         */
 
9740
        protected function _getSHORT($str, $offset) {
 
9741
                $v = unpack('si', substr($str, $offset, 2));
 
9742
                return $v['i'];
 
9743
        }
 
9744
 
 
9745
        /**
 
9746
         * Get FWORD from string (Big Endian 16-bit signed integer).
 
9747
         * @param $str (string) string from where to extract value
 
9748
         * @param $offset (int) point from where to read the data
 
9749
         * @return int 16 bit value
 
9750
         * @author Nicola Asuni
 
9751
         * @protected
 
9752
         * @since 5.9.123 (2011-09-30)
 
9753
         */
 
9754
        protected function _getFWORD($str, $offset) {
 
9755
                $v = $this->_getUSHORT($str, $offset);
 
9756
                if ($v > 0x7fff) {
 
9757
                        $v -= 0x10000;
 
9758
                }
 
9759
                return $v;
 
9760
        }
 
9761
 
 
9762
        /**
 
9763
         * Get UFWORD from string (Big Endian 16-bit unsigned integer).
 
9764
         * @param $str (string) string from where to extract value
 
9765
         * @param $offset (int) point from where to read the data
 
9766
         * @return int 16 bit value
 
9767
         * @author Nicola Asuni
 
9768
         * @protected
 
9769
         * @since 5.9.123 (2011-09-30)
 
9770
         */
 
9771
        protected function _getUFWORD($str, $offset) {
 
9772
                $v = $this->_getUSHORT($str, $offset);
 
9773
                return $v;
 
9774
        }
 
9775
 
 
9776
        /**
 
9777
         * Get FIXED from string (32-bit signed fixed-point number (16.16).
 
9778
         * @param $str (string) string from where to extract value
 
9779
         * @param $offset (int) point from where to read the data
 
9780
         * @return int 16 bit value
 
9781
         * @author Nicola Asuni
 
9782
         * @protected
 
9783
         * @since 5.9.123 (2011-09-30)
 
9784
         */
 
9785
        protected function _getFIXED($str, $offset) {
 
9786
                // mantissa
 
9787
                $m = $this->_getFWORD($str, $offset);
 
9788
                // fraction
 
9789
                $f = $this->_getUSHORT($str, ($offset + 2));
 
9790
                $v = floatval(''.$m.'.'.$f.'');
 
9791
                return $v;
 
9792
        }
 
9793
 
 
9794
        /**
 
9795
         * Get BYTE from string (8-bit unsigned integer).
 
9796
         * @param $str (string) String from where to extract value.
 
9797
         * @param $offset (int) Point from where to read the data.
 
9798
         * @return int 8 bit value
 
9799
         * @author Nicola Asuni
 
9800
         * @protected
 
9801
         * @since 5.2.000 (2010-06-02)
 
9802
         */
 
9803
        protected function _getBYTE($str, $offset) {
 
9804
                $v = unpack('Ci', substr($str, $offset, 1));
 
9805
                return $v['i'];
 
9806
        }
 
9807
        /**
 
9808
         * Update the CIDToGIDMap string with a new value.
 
9809
         * @param $map (string) CIDToGIDMap.
 
9810
         * @param $cid (int) CID value.
 
9811
         * @param $gid (int) GID value.
 
9812
         * @return (string) CIDToGIDMap.
 
9813
         * @author Nicola Asuni
 
9814
         * @protected
 
9815
         * @since 5.9.123 (2011-09-29)
 
9816
         */
 
9817
        protected function updateCIDtoGIDmap($map, $cid, $gid) {
 
9818
                if (($cid >= 0) AND ($cid <= 0xFFFF) AND ($gid >= 0)) {
 
9819
                        if ($gid > 0xFFFF) {
 
9820
                                $gid -= 0x10000;
 
9821
                        }
 
9822
                        $map[($cid * 2)] = chr($gid >> 8);
 
9823
                        $map[(($cid * 2) + 1)] = chr($gid & 0xFF);
 
9824
                }
 
9825
                return $map;
 
9826
        }
 
9827
 
 
9828
        /**
 
9829
         * Convert and add the selected TrueType or Type1 font to the fonts folder (that must be writeable).
 
9830
         * @param $fontfile (string) TrueType or Type1 font file (full path).
 
9831
         * @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.
 
9832
         * @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.
 
9833
         * @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.
 
9834
         * @param $outpath (string) Output path for generated font files (must be writeable by the web server). Leave empty for default font folder.
 
9835
         * @return (string) TCPDF font name.
 
9836
         * @author Nicola Asuni
 
9837
         * @public
 
9838
         * @since 5.9.123 (2010-09-30)
 
9839
         */
 
9840
        public function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='') {
 
9841
                if (!file_exists($fontfile)) {
 
9842
                        $this->Error('Could not find file: '.$fontfile.'');
 
9843
                }
 
9844
                // font metrics
 
9845
                $fmetric = array();
 
9846
                // build new font name for TCPDF compatibility
 
9847
                $font_path_parts = pathinfo($fontfile);
 
9848
                if (!isset($font_path_parts['filename'])) {
 
9849
                        $font_path_parts['filename'] = substr($font_path_parts['basename'], 0, -(strlen($font_path_parts['extension']) + 1));
 
9850
                }
 
9851
                $font_name = strtolower($font_path_parts['filename']);
 
9852
                $font_name = preg_replace('/[^a-z0-9_]/', '', $font_name);
 
9853
                $search  = array('bold', 'oblique', 'italic', 'regular');
 
9854
                $replace = array('b', 'i', 'i', '');
 
9855
                $font_name = str_replace($search, $replace, $font_name);
 
9856
                if (empty($font_name)) {
 
9857
                        // set generic name
 
9858
                        $font_name = 'tcpdffont';
 
9859
                }
 
9860
                // set output path
 
9861
                if (empty($outpath)) {
 
9862
                        $outpath = $this->_getfontpath();
 
9863
                }
 
9864
                // check if this font already exist
 
9865
                if (file_exists($outpath.$font_name.'.php')) {
 
9866
                        // this font already exist (delete it from fonts folder to rebuild it)
 
9867
                        return $font_name;
 
9868
                }
 
9869
                $fmetric['file'] = $font_name.'.z';
 
9870
                $fmetric['ctg'] = $font_name.'.ctg.z';
 
9871
                // get font data
 
9872
                $font = file_get_contents($fontfile);
 
9873
                $fmetric['originalsize'] = strlen($font);
 
9874
                // autodetect font type
 
9875
                if (empty($fonttype)) {
 
9876
                        if ($this->_getULONG($font, 0) == 0x10000) {
 
9877
                                // True Type (Unicode or not)
 
9878
                                $fonttype = 'TrueTypeUnicode';
 
9879
                        } elseif (substr($font, 0, 4) == 'OTTO') {
 
9880
                                // Open Type (Unicode or not)
 
9881
                                $this->Error('Unsupported font format: OpenType with CFF data.');
 
9882
                        } else {
 
9883
                                // Type 1
 
9884
                                $fonttype = 'Type1';
 
9885
                        }
 
9886
                }
 
9887
                // set font type
 
9888
                switch ($fonttype) {
 
9889
                        case 'CID0CT':
 
9890
                        case 'CID0CS':
 
9891
                        case 'CID0KR':
 
9892
                        case 'CID0JP': {
 
9893
                                $fmetric['type'] = 'cidfont0';
 
9894
                                break;
 
9895
                        }
 
9896
                        case 'Type1': {
 
9897
                                $fmetric['type'] = 'Type1';
 
9898
                                if (empty($enc) AND (($flags & 4) == 0)) {
 
9899
                                        $enc = 'cp1252';
 
9900
                                }
 
9901
                                break;
 
9902
                        }
 
9903
                        case 'TrueType': {
 
9904
                                $fmetric['type'] = 'TrueType';
 
9905
                                break;
 
9906
                        }
 
9907
                        case 'TrueTypeUnicode':
 
9908
                        default: {
 
9909
                                $fmetric['type'] = 'TrueTypeUnicode';
 
9910
                                break;
 
9911
                        }
 
9912
                }
 
9913
                // set encoding maps (if any)
 
9914
                $fmetric['enc'] = preg_replace('/[^A-Za-z0-9_\-]/', '', $enc);
 
9915
                $fmetric['diff'] = '';
 
9916
                if (($fmetric['type'] == 'TrueType') OR ($fmetric['type'] == 'Type1')) {
 
9917
                        if (!empty($enc) AND ($enc != 'cp1252') AND isset($this->encmaps->encmap[$enc])) {
 
9918
                                // build differences from reference encoding
 
9919
                                $enc_ref = $this->encmaps->encmap['cp1252'];
 
9920
                                $enc_target = $this->encmaps->encmap[$enc];
 
9921
                                $last = 0;
 
9922
                                for ($i = 32; $i <= 255; ++$i) {
 
9923
                                        if ($enc_target != $enc_ref[$i]) {
 
9924
                                                if ($i != ($last + 1)) {
 
9925
                                                        $fmetric['diff'] .= $i.' ';
 
9926
                                                }
 
9927
                                                $last = $i;
 
9928
                                                $fmetric['diff'] .= '/'.$enc_target[$i].' ';
 
9929
                                        }
 
9930
                                }
 
9931
                        }
 
9932
                }
 
9933
                // parse the font by type
 
9934
                if ($fmetric['type'] == 'Type1') {
 
9935
                        // ---------- TYPE 1 ----------
 
9936
                        // read first segment
 
9937
                        $a = unpack('Cmarker/Ctype/Vsize', substr($font, 0, 6));
 
9938
                        if ($a['marker'] != 128) {
 
9939
                                $this->Error('Font file is not a valid binary Type1');
 
9940
                        }
 
9941
                        $fmetric['size1'] = $a['size'];
 
9942
                        $data = substr($font, 6, $fmetric['size1']);
 
9943
                        // read second segment
 
9944
                        $a = unpack('Cmarker/Ctype/Vsize', substr($font, (6 + $fmetric['size1']), 6));
 
9945
                        if ($a['marker'] != 128) {
 
9946
                                $this->Error('Font file is not a valid binary Type1');
 
9947
                        }
 
9948
                        $fmetric['size2'] = $a['size'];
 
9949
                        $encrypted = substr($font, (12 + $fmetric['size1']), $fmetric['size2']);
 
9950
                        $data .= $encrypted;
 
9951
                        // store compressed font
 
9952
                        $fp = fopen($outpath.$fmetric['file'], 'wb');
 
9953
                        fwrite($fp, gzcompress($data));
 
9954
                        fclose($fp);
 
9955
                        // get font info
 
9956
                        $fmetric['Flags'] = $flags;
 
9957
                        preg_match ('#/FullName[\s]*\(([^\)]*)#', $font, $matches);
 
9958
                        $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $matches[1]);
 
9959
                        preg_match('#/FontBBox[\s]*{([^}]*)#', $font, $matches);
 
9960
                        $fmetric['bbox'] = trim($matches[1]);
 
9961
                        $bv = explode(' ', $fmetric['bbox']);
 
9962
                        $fmetric['Ascent'] = intval($bv[3]);
 
9963
                        $fmetric['Descent'] = intval($bv[1]);
 
9964
                        preg_match('#/ItalicAngle[\s]*([0-9\+\-]*)#', $font, $matches);
 
9965
                        $fmetric['italicAngle'] = intval($matches[1]);
 
9966
                        if ($fmetric['italicAngle'] != 0) {
 
9967
                                $fmetric['Flags'] |= 64;
 
9968
                        }
 
9969
                        preg_match('#/UnderlinePosition[\s]*([0-9\+\-]*)#', $font, $matches);
 
9970
                        $fmetric['underlinePosition'] = intval($matches[1]);
 
9971
                        preg_match('#/UnderlineThickness[\s]*([0-9\+\-]*)#', $font, $matches);
 
9972
                        $fmetric['underlineThickness'] = intval($matches[1]);
 
9973
                        preg_match('#/isFixedPitch[\s]*([^\s]*)#', $font, $matches);
 
9974
                        if ($matches[1] == 'true') {
 
9975
                                $fmetric['Flags'] |= 1;
 
9976
                        }
 
9977
                        // get internal map
 
9978
                        $imap = array();
 
9979
                        if (preg_match_all('#dup[\s]([0-9]+)[\s]*/([^\s]*)[\s]put#sU', $font, $fmap, PREG_SET_ORDER) > 0) {
 
9980
                                foreach ($fmap as $v) {
 
9981
                                        $imap[$v[2]] = $v[1];
 
9982
                                }
 
9983
                        }
 
9984
                        // decrypt eexec encrypted part
 
9985
                        $r = 55665; // eexec encryption constant
 
9986
                        $c1 = 52845;
 
9987
                        $c2 = 22719;
 
9988
                        $elen = strlen($encrypted);
 
9989
                        $eplain = '';
 
9990
                        for ($i = 0; $i < $elen; ++$i) {
 
9991
                                $chr = ord($encrypted[$i]);
 
9992
                                $eplain .= chr($chr ^ ($r >> 8));
 
9993
                                $r = ((($chr + $r) * $c1 + $c2) % 65536);
 
9994
                        }
 
9995
                        if (preg_match('#/ForceBold[\s]*([^\s]*)#', $eplain, $matches) > 0) {
 
9996
                                if ($matches[1] == 'true') {
 
9997
                                        $fmetric['Flags'] |= 0x40000;
 
9998
                                }
 
9999
                        }
 
10000
                        if (preg_match('#/StdVW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
 
10001
                                $fmetric['StemV'] = intval($matches[1]);
 
10002
                        } else {
 
10003
                                $fmetric['StemV'] = 70;
 
10004
                        }
 
10005
                        if (preg_match('#/StdHW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
 
10006
                                $fmetric['StemH'] = intval($matches[1]);
 
10007
                        } else {
 
10008
                                $fmetric['StemH'] = 30;
 
10009
                        }
 
10010
                        if (preg_match('#/BlueValues[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
 
10011
                                $bv = explode(' ', $matches[1]);
 
10012
                                if (count($bv) >= 6) {
 
10013
                                        $v1 = intval($bv[2]);
 
10014
                                        $v2 = intval($bv[4]);
 
10015
                                        if ($v1 <= $v2) {
 
10016
                                                $fmetric['XHeight'] = $v1;
 
10017
                                                $fmetric['CapHeight'] = $v2;
 
10018
                                        } else {
 
10019
                                                $fmetric['XHeight'] = $v2;
 
10020
                                                $fmetric['CapHeight'] = $v1;
 
10021
                                        }
 
10022
                                } else {
 
10023
                                        $fmetric['XHeight'] = 450;
 
10024
                                        $fmetric['CapHeight'] = 700;
 
10025
                                }
 
10026
                        } else {
 
10027
                                $fmetric['XHeight'] = 450;
 
10028
                                $fmetric['CapHeight'] = 700;
 
10029
                        }
 
10030
                        // get the number of random bytes at the beginning of charstrings
 
10031
                        if (preg_match('#/lenIV[\s]*([0-9]*)#', $eplain, $matches) > 0) {
 
10032
                                $lenIV = intval($matches[1]);
 
10033
                        } else {
 
10034
                                $lenIV = 4;
 
10035
                        }
 
10036
                        $fmetric['Leading'] = 0;
 
10037
                        // get charstring data
 
10038
                        $eplain = substr($eplain, (strpos($eplain, '/CharStrings') + 1));
 
10039
                        preg_match_all('#/([A-Za-z0-9\.]*)[\s][0-9]+[\s]RD[\s](.*)[\s]ND#sU', $eplain, $matches, PREG_SET_ORDER);
 
10040
                        if (!empty($enc) AND isset($this->encmaps->encmap[$enc])) {
 
10041
                                $enc_map = $this->encmaps->encmap[$enc];
 
10042
                        } else {
 
10043
                                $enc_map = false;
 
10044
                        }
 
10045
                        $fmetric['cw'] = '';
 
10046
                        $fmetric['MaxWidth'] = 0;
 
10047
                        $cwidths = array();
 
10048
                        foreach ($matches as $k => $v) {
 
10049
                                $cid = 0;
 
10050
                                if (isset($imap[$v[1]])) {
 
10051
                                        $cid = $imap[$v[1]];
 
10052
                                } elseif ($enc_map !== false) {
 
10053
                                        $cid = array_search($v[1], $enc_map);
 
10054
                                        if ($cid === false) {
 
10055
                                                $cid = 0;
 
10056
                                        } elseif ($cid > 1000) {
 
10057
                                                $cid -= 1000;
 
10058
                                        }
 
10059
                                }
 
10060
                                // decrypt charstring encrypted part
 
10061
                                $r = 4330; // charstring encryption constant
 
10062
                                $c1 = 52845;
 
10063
                                $c2 = 22719;
 
10064
                                $cd = $v[2];
 
10065
                                $clen = strlen($cd);
 
10066
                                $ccom = array();
 
10067
                                for ($i = 0; $i < $clen; ++$i) {
 
10068
                                        $chr = ord($cd[$i]);
 
10069
                                        $ccom[] = ($chr ^ ($r >> 8));
 
10070
                                        $r = ((($chr + $r) * $c1 + $c2) % 65536);
 
10071
                                }
 
10072
                                // decode numbers
 
10073
                                $cdec = array();
 
10074
                                $ck = 0;
 
10075
                                $i = $lenIV;
 
10076
                                while ($i < $clen) {
 
10077
                                        if ($ccom[$i] < 32) {
 
10078
                                                $cdec[$ck] = $ccom[$i];
 
10079
                                                if (($ck > 0) AND ($cdec[$ck] == 13)) {
 
10080
                                                        // hsbw command: update width
 
10081
                                                        $cwidths[$cid] = $cdec[($ck - 1)];
 
10082
                                                }
 
10083
                                                ++$i;
 
10084
                                        } elseif (($ccom[$i] >= 32) AND ($ccom[$i] <= 246)) {
 
10085
                                                $cdec[$ck] = ($ccom[$i] - 139);
 
10086
                                                ++$i;
 
10087
                                        } elseif (($ccom[$i] >= 247) AND ($ccom[$i] <= 250)) {
 
10088
                                                $cdec[$ck] = ((($ccom[$i] - 247) * 256) + $ccom[($i + 1)] + 108);
 
10089
                                                $i += 2;
 
10090
                                        } elseif (($ccom[$i] >= 251) AND ($ccom[$i] <= 254)) {
 
10091
                                                $cdec[$ck] = ((-($ccom[$i] - 251) * 256) - $ccom[($i + 1)] - 108);
 
10092
                                                $i += 2;
 
10093
                                        } elseif ($ccom[$i] == 255) {
 
10094
                                                $sval = chr($ccom[($i + 1)]).chr($ccom[($i + 2)]).chr($ccom[($i + 3)]).chr($ccom[($i + 4)]);
 
10095
                                                $vsval = unpack('li', $sval);
 
10096
                                                $cdec[$ck] = $vsval['i'];
 
10097
                                                $i += 5;
 
10098
                                        }
 
10099
                                        ++$ck;
 
10100
                                }
 
10101
                        } // end for each matches
 
10102
                        $fmetric['MissingWidth'] = $cwidths[0];
 
10103
                        $fmetric['MaxWidth'] = $fmetric['MissingWidth'];
 
10104
                        $fmetric['AvgWidth'] = 0;
 
10105
                        // set chars widths
 
10106
                        for ($cid = 0; $cid <= 255; ++$cid) {
 
10107
                                if (isset($cwidths[$cid])) {
 
10108
                                        if ($cwidths[$cid] > $fmetric['MaxWidth']) {
 
10109
                                                $fmetric['MaxWidth'] = $cwidths[$cid];
 
10110
                                        }
 
10111
                                        $fmetric['AvgWidth'] += $cwidths[$cid];
 
10112
                                        $fmetric['cw'] .= ','.$cid.'=>'.$cwidths[$cid];
 
10113
                                } else {
 
10114
                                        $fmetric['cw'] .= ','.$cid.'=>'.$fmetric['MissingWidth'];
 
10115
                                }
 
10116
                        }
 
10117
                        $fmetric['AvgWidth'] = round($fmetric['AvgWidth'] / count($cwidths));
 
10118
                } else {
 
10119
                        // ---------- TRUE TYPE ----------
 
10120
                        if ($fmetric['type'] != 'cidfont0') {
 
10121
                                // store compressed font
 
10122
                                $fp = fopen($outpath.$fmetric['file'], 'wb');
 
10123
                                fwrite($fp, gzcompress($font));
 
10124
                                fclose($fp);
 
10125
                        }
 
10126
                        $offset = 0; // offset position of the font data
 
10127
                        if ($this->_getULONG($font, $offset) != 0x10000) {
 
10128
                                // sfnt version must be 0x00010000 for TrueType version 1.0.
 
10129
                                return $font;
 
10130
                        }
 
10131
                        $offset += 4;
 
10132
                        // get number of tables
 
10133
                        $numTables = $this->_getUSHORT($font, $offset);
 
10134
                        $offset += 2;
 
10135
                        // skip searchRange, entrySelector and rangeShift
 
10136
                        $offset += 6;
 
10137
                        // tables array
 
10138
                        $table = array();
 
10139
                        // ---------- get tables ----------
 
10140
                        for ($i = 0; $i < $numTables; ++$i) {
 
10141
                                // get table info
 
10142
                                $tag = substr($font, $offset, 4);
 
10143
                                $offset += 4;
 
10144
                                $table[$tag] = array();
 
10145
                                $table[$tag]['checkSum'] = $this->_getULONG($font, $offset);
 
10146
                                $offset += 4;
 
10147
                                $table[$tag]['offset'] = $this->_getULONG($font, $offset);
 
10148
                                $offset += 4;
 
10149
                                $table[$tag]['length'] = $this->_getULONG($font, $offset);
 
10150
                                $offset += 4;
 
10151
                        }
 
10152
                        // check magicNumber
 
10153
                        $offset = $table['head']['offset'] + 12;
 
10154
                        if ($this->_getULONG($font, $offset) != 0x5F0F3CF5) {
 
10155
                                // magicNumber must be 0x5F0F3CF5
 
10156
                                return $font;
 
10157
                        }
 
10158
                        $offset += 4;
 
10159
                        $offset += 2; // skip flags
 
10160
                        // get FUnits
 
10161
                        $fmetric['unitsPerEm'] = $this->_getUSHORT($font, $offset);
 
10162
                        $offset += 2;
 
10163
                        // units ratio constant
 
10164
                        $urk = (1000 / $fmetric['unitsPerEm']);
 
10165
                        $offset += 16; // skip created, modified
 
10166
                        $xMin = round($this->_getFWORD($font, $offset) * $urk);
 
10167
                        $offset += 2;
 
10168
                        $yMin = round($this->_getFWORD($font, $offset) * $urk);
 
10169
                        $offset += 2;
 
10170
                        $xMax = round($this->_getFWORD($font, $offset) * $urk);
 
10171
                        $offset += 2;
 
10172
                        $yMax = round($this->_getFWORD($font, $offset) * $urk);
 
10173
                        $offset += 2;
 
10174
                        $fmetric['bbox'] = ''.$xMin.' '.$yMin.' '.$xMax.' '.$yMax.'';
 
10175
                        $macStyle = $this->_getUSHORT($font, $offset);
 
10176
                        $offset += 2;
 
10177
                        // PDF font flags
 
10178
                        $fmetric['Flags'] = $flags;
 
10179
                        if (($macStyle & 2) == 2) {
 
10180
                                // italic flag
 
10181
                                $fmetric['Flags'] |= 64;
 
10182
                        }
 
10183
                        // get offset mode (indexToLocFormat : 0 = short, 1 = long)
 
10184
                        $offset = $table['head']['offset'] + 50;
 
10185
                        $short_offset = ($this->_getSHORT($font, $offset) == 0);
 
10186
                        $offset += 2;
 
10187
                        // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table
 
10188
                        $indexToLoc = array();
 
10189
                        $offset = $table['loca']['offset'];
 
10190
                        if ($short_offset) {
 
10191
                                // short version
 
10192
                                $tot_num_glyphs = ($table['loca']['length'] / 2); // numGlyphs + 1
 
10193
                                for ($i = 0; $i < $tot_num_glyphs; ++$i) {
 
10194
                                        $indexToLoc[$i] = $this->_getUSHORT($font, $offset) * 2;
 
10195
                                        $offset += 2;
 
10196
                                }
 
10197
                        } else {
 
10198
                                // long version
 
10199
                                $tot_num_glyphs = ($table['loca']['length'] / 4); // numGlyphs + 1
 
10200
                                for ($i = 0; $i < $tot_num_glyphs; ++$i) {
 
10201
                                        $indexToLoc[$i] = $this->_getULONG($font, $offset);
 
10202
                                        $offset += 4;
 
10203
                                }
 
10204
                        }
 
10205
                        // get glyphs indexes of chars from cmap table
 
10206
                        $offset = $table['cmap']['offset'] + 2;
 
10207
                        $numEncodingTables = $this->_getUSHORT($font, $offset);
 
10208
                        $offset += 2;
 
10209
                        $encodingTables = array();
 
10210
                        for ($i = 0; $i < $numEncodingTables; ++$i) {
 
10211
                                $encodingTables[$i]['platformID'] = $this->_getUSHORT($font, $offset);
 
10212
                                $offset += 2;
 
10213
                                $encodingTables[$i]['encodingID'] = $this->_getUSHORT($font, $offset);
 
10214
                                $offset += 2;
 
10215
                                $encodingTables[$i]['offset'] = $this->_getULONG($font, $offset);
 
10216
                                $offset += 4;
 
10217
                        }
 
10218
                        // ---------- get os/2 metrics ----------
 
10219
                        $offset = $table['OS/2']['offset'];
 
10220
                        $offset += 2; // skip version
 
10221
                        // xAvgCharWidth
 
10222
                        $fmetric['AvgWidth'] = round($this->_getFWORD($font, $offset) * $urk);
 
10223
                        $offset += 2;
 
10224
                        // usWeightClass
 
10225
                        $usWeightClass = round($this->_getUFWORD($font, $offset) * $urk);
 
10226
                        // estimate StemV and StemH (400 = usWeightClass for Normal - Regular font)
 
10227
                        $fmetric['StemV'] = round((70 * $usWeightClass) / 400);
 
10228
                        $fmetric['StemH'] = round((30 * $usWeightClass) / 400);
 
10229
                        $offset += 2;
 
10230
                        $offset += 2; // usWidthClass
 
10231
                        $fsType = $this->_getSHORT($font, $offset);
 
10232
                        $offset += 2;
 
10233
                        if ($fsType == 2) {
 
10234
                                $this->Error('This Font cannot be modified, embedded or exchanged in any manner without first obtaining permission of the legal owner.');
 
10235
                        }
 
10236
                        // ---------- get font name ----------
 
10237
                        $fmetric['name'] = '';
 
10238
                        $offset = $table['name']['offset'];
 
10239
                        $offset += 2; // skip Format selector (=0).
 
10240
                        // Number of NameRecords that follow n.
 
10241
                        $numNameRecords = $this->_getUSHORT($font, $offset);
 
10242
                        $offset += 2;
 
10243
                        // Offset to start of string storage (from start of table).
 
10244
                        $stringStorageOffset = $this->_getUSHORT($font, $offset);
 
10245
                        $offset += 2;
 
10246
                        for ($i = 0; $i < $numNameRecords; ++$i) {
 
10247
                                $offset += 6; // skip Platform ID, Platform-specific encoding ID, Language ID.
 
10248
                                // Name ID.
 
10249
                                $nameID = $this->_getUSHORT($font, $offset);
 
10250
                                $offset += 2;
 
10251
                                if ($nameID == 6) {
 
10252
                                        // String length (in bytes).
 
10253
                                        $stringLength = $this->_getUSHORT($font, $offset);
 
10254
                                        $offset += 2;
 
10255
                                        // String offset from start of storage area (in bytes).
 
10256
                                        $stringOffset = $this->_getUSHORT($font, $offset);
 
10257
                                        $offset += 2;
 
10258
                                        $offset = ($table['name']['offset'] + $stringStorageOffset + $stringOffset);
 
10259
                                        $fmetric['name'] = substr($font, $offset, $stringLength);
 
10260
                                        $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $fmetric['name']);
 
10261
                                        break;
 
10262
                                } else {
 
10263
                                        $offset += 4; // skip String length, String offset
 
10264
                                }
 
10265
                        }
 
10266
                        if (empty($fmetric['name'])) {
 
10267
                                $fmetric['name'] = $font_name;
 
10268
                        }
 
10269
                        // ---------- get post data ----------
 
10270
                        $offset = $table['post']['offset'];
 
10271
                        $offset += 4; // skip Format Type
 
10272
                        $fmetric['italicAngle'] = $this->_getFIXED($font, $offset);
 
10273
                        $offset += 4;
 
10274
                        $fmetric['underlinePosition'] = round($this->_getFWORD($font, $offset) * $urk);
 
10275
                        $offset += 2;
 
10276
                        $fmetric['underlineThickness'] = round($this->_getFWORD($font, $offset) * $urk);
 
10277
                        $offset += 2;
 
10278
                        $isFixedPitch = ($this->_getULONG($font, $offset) == 0) ? false : true;
 
10279
                        $offset += 2;
 
10280
                        if ($isFixedPitch) {
 
10281
                                $fmetric['Flags'] |= 1;
 
10282
                        }
 
10283
                        // ---------- get hhea data ----------
 
10284
                        $offset = $table['hhea']['offset'];
 
10285
                        $offset += 4; // skip Table version number
 
10286
                        // Ascender
 
10287
                        $fmetric['Ascent'] = round($this->_getFWORD($font, $offset) * $urk);
 
10288
                        $offset += 2;
 
10289
                        // Descender
 
10290
                        $fmetric['Descent'] = round($this->_getFWORD($font, $offset) * $urk);
 
10291
                        $offset += 2;
 
10292
                        // LineGap
 
10293
                        $fmetric['Leading'] = round($this->_getFWORD($font, $offset) * $urk);
 
10294
                        $offset += 2;
 
10295
                        // advanceWidthMax
 
10296
                        $fmetric['MaxWidth'] = round($this->_getUFWORD($font, $offset) * $urk);
 
10297
                        $offset += 2;
 
10298
                        $offset += 22; // skip some values
 
10299
                        // get the number of hMetric entries in hmtx table
 
10300
                        $numberOfHMetrics = $this->_getUSHORT($font, $offset);
 
10301
                        // ---------- get maxp data ----------
 
10302
                        $offset = $table['maxp']['offset'];
 
10303
                        $offset += 4; // skip Table version number
 
10304
                        // get the the number of glyphs in the font.
 
10305
                        $numGlyphs = $this->_getUSHORT($font, $offset);
 
10306
                        // ---------- get CIDToGIDMap ----------
 
10307
                        $ctg = array();
 
10308
                        foreach ($encodingTables as $enctable) {
 
10309
                                if (($enctable['platformID'] == 3) AND ($enctable['encodingID'] == 0)) {
 
10310
                                        $modesymbol = true;
 
10311
                                } else {
 
10312
                                        $modesymbol = false;
 
10313
                                }
 
10314
                                $offset = $table['cmap']['offset'] + $enctable['offset'];
 
10315
                                $format = $this->_getUSHORT($font, $offset);
 
10316
                                $offset += 2;
 
10317
                                switch ($format) {
 
10318
                                        case 0: { // Format 0: Byte encoding table
 
10319
                                                $offset += 4; // skip length and version/language
 
10320
                                                for ($c = 0; $c < 256; ++$c) {
 
10321
                                                        $g = $this->_getBYTE($font, $offset);
 
10322
                                                        $ctg[$c] = $g;
 
10323
                                                        ++$offset;
 
10324
                                                }
 
10325
                                                break;
 
10326
                                        }
 
10327
                                        case 2: { // Format 2: High-byte mapping through table
 
10328
                                                $offset += 4; // skip length and version/language
 
10329
                                                $numSubHeaders = 0;
 
10330
                                                for ($i = 0; $i < 256; ++$i) {
 
10331
                                                        // Array that maps high bytes to subHeaders: value is subHeader index * 8.
 
10332
                                                        $subHeaderKeys[$i] = ($this->_getUSHORT($font, $offset) / 8);
 
10333
                                                        $offset += 2;
 
10334
                                                        if ($numSubHeaders < $subHeaderKeys[$i]) {
 
10335
                                                                $numSubHeaders = $subHeaderKeys[$i];
 
10336
                                                        }
 
10337
                                                }
 
10338
                                                // the number of subHeaders is equal to the max of subHeaderKeys + 1
 
10339
                                                ++$numSubHeaders;
 
10340
                                                // read subHeader structures
 
10341
                                                $subHeaders = array();
 
10342
                                                $numGlyphIndexArray = 0;
 
10343
                                                for ($k = 0; $k < $numSubHeaders; ++$k) {
 
10344
                                                        $subHeaders[$k]['firstCode'] = $this->_getUSHORT($font, $offset);
 
10345
                                                        $offset += 2;
 
10346
                                                        $subHeaders[$k]['entryCount'] = $this->_getUSHORT($font, $offset);
 
10347
                                                        $offset += 2;
 
10348
                                                        $subHeaders[$k]['idDelta'] = $this->_getSHORT($font, $offset);
 
10349
                                                        $offset += 2;
 
10350
                                                        $subHeaders[$k]['idRangeOffset'] = $this->_getUSHORT($font, $offset);
 
10351
                                                        $offset += 2;
 
10352
                                                        $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8));
 
10353
                                                        $subHeaders[$k]['idRangeOffset'] /= 2;
 
10354
                                                        $numGlyphIndexArray += $subHeaders[$k]['entryCount'];
 
10355
                                                }
 
10356
                                                for ($k = 0; $k < $numGlyphIndexArray; ++$k) {
 
10357
                                                        $glyphIndexArray[$k] = $this->_getUSHORT($font, $offset);
 
10358
                                                        $offset += 2;
 
10359
                                                }
 
10360
                                                for ($i = 0; $i < 256; ++$i) {
 
10361
                                                        $k = $subHeaderKeys[$i];
 
10362
                                                        if ($k == 0) {
 
10363
                                                                // one byte code
 
10364
                                                                $c = $i;
 
10365
                                                                $g = $glyphIndexArray[0];
 
10366
                                                                $ctg[$c] = $g;
 
10367
                                                        } else {
 
10368
                                                                // two bytes code
 
10369
                                                                $start_byte = $subHeaders[$k]['firstCode'];
 
10370
                                                                $end_byte = $start_byte + $subHeaders[$k]['entryCount'];
 
10371
                                                                for ($j = $start_byte; $j < $end_byte; ++$j) {
 
10372
                                                                        // combine high and low bytes
 
10373
                                                                        $c = (($i << 8) + $j);
 
10374
                                                                        $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']);
 
10375
                                                                        $g = $glyphIndexArray[$idRangeOffset];
 
10376
                                                                        $g += ($idDelta[$k] - 65536);
 
10377
                                                                        if ($g < 0) {
 
10378
                                                                                $g = 0;
 
10379
                                                                        }
 
10380
                                                                        $ctg[$c] = $g;
 
10381
                                                                }
 
10382
                                                        }
 
10383
                                                }
 
10384
                                                break;
 
10385
                                        }
 
10386
                                        case 4: { // Format 4: Segment mapping to delta values
 
10387
                                                $length = $this->_getUSHORT($font, $offset);
 
10388
                                                $offset += 2;
 
10389
                                                $offset += 2; // skip version/language
 
10390
                                                $segCount = ($this->_getUSHORT($font, $offset) / 2);
 
10391
                                                $offset += 2;
 
10392
                                                $offset += 6; // skip searchRange, entrySelector, rangeShift
 
10393
                                                $endCount = array(); // array of end character codes for each segment
 
10394
                                                for ($k = 0; $k < $segCount; ++$k) {
 
10395
                                                        $endCount[$k] = $this->_getUSHORT($font, $offset);
 
10396
                                                        $offset += 2;
 
10397
                                                }
 
10398
                                                $offset += 2; // skip reservedPad
 
10399
                                                $startCount = array(); // array of start character codes for each segment
 
10400
                                                for ($k = 0; $k < $segCount; ++$k) {
 
10401
                                                        $startCount[$k] = $this->_getUSHORT($font, $offset);
 
10402
                                                        $offset += 2;
 
10403
                                                }
 
10404
                                                $idDelta = array(); // delta for all character codes in segment
 
10405
                                                for ($k = 0; $k < $segCount; ++$k) {
 
10406
                                                        $idDelta[$k] = $this->_getUSHORT($font, $offset);
 
10407
                                                        $offset += 2;
 
10408
                                                }
 
10409
                                                $idRangeOffset = array(); // Offsets into glyphIdArray or 0
 
10410
                                                for ($k = 0; $k < $segCount; ++$k) {
 
10411
                                                        $idRangeOffset[$k] = $this->_getUSHORT($font, $offset);
 
10412
                                                        $offset += 2;
 
10413
                                                }
 
10414
                                                $gidlen = ($length / 2) - 8 - (4 * $segCount);
 
10415
                                                $glyphIdArray = array(); // glyph index array
 
10416
                                                for ($k = 0; $k < $gidlen; ++$k) {
 
10417
                                                        $glyphIdArray[$k] = $this->_getUSHORT($font, $offset);
 
10418
                                                        $offset += 2;
 
10419
                                                }
 
10420
                                                for ($k = 0; $k < $segCount; ++$k) {
 
10421
                                                        for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) {
 
10422
                                                                if ($idRangeOffset[$k] == 0) {
 
10423
                                                                        $g = $c;
 
10424
                                                                } else {
 
10425
                                                                        $gid = (($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k));
 
10426
                                                                        $g = $glyphIdArray[$gid];
 
10427
                                                                }
 
10428
                                                                $g += ($idDelta[$k] - 65536);
 
10429
                                                                if ($g < 0) {
 
10430
                                                                        $g = 0;
 
10431
                                                                }
 
10432
                                                                $ctg[$c] = $g;
 
10433
                                                        }
 
10434
                                                }
 
10435
                                                break;
 
10436
                                        }
 
10437
                                        case 6: { // Format 6: Trimmed table mapping
 
10438
                                                $offset += 4; // skip length and version/language
 
10439
                                                $firstCode = $this->_getUSHORT($font, $offset);
 
10440
                                                $offset += 2;
 
10441
                                                $entryCount = $this->_getUSHORT($font, $offset);
 
10442
                                                $offset += 2;
 
10443
                                                for ($k = 0; $k < $entryCount; ++$k) {
 
10444
                                                        $c = ($k + $firstCode);
 
10445
                                                        $g = $this->_getUSHORT($font, $offset);
 
10446
                                                        $ctg[$c] = $g;
 
10447
                                                        $offset += 2;
 
10448
                                                }
 
10449
                                                break;
 
10450
                                        }
 
10451
                                        case 8: { // Format 8: Mixed 16-bit and 32-bit coverage
 
10452
                                                $offset += 10; // skip reserved, length and version/language
 
10453
                                                for ($k = 0; $k < 8192; ++$k) {
 
10454
                                                        $is32[$k] = $this->_getBYTE($font, $offset);
 
10455
                                                        ++$offset;
 
10456
                                                }
 
10457
                                                $nGroups = $this->_getULONG($font, $offset);
 
10458
                                                $offset += 4;
 
10459
                                                for ($i = 0; $i < $nGroups; ++$i) {
 
10460
                                                        $startCharCode = $this->_getULONG($font, $offset);
 
10461
                                                        $offset += 4;
 
10462
                                                        $endCharCode = $this->_getULONG($font, $offset);
 
10463
                                                        $offset += 4;
 
10464
                                                        $startGlyphID = $this->_getULONG($font, $offset);
 
10465
                                                        $offset += 4;
 
10466
                                                        for ($k = $startCharCode; $k <= $endCharCode; ++$k) {
 
10467
                                                                $is32idx = floor($c / 8);
 
10468
                                                                if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) {
 
10469
                                                                        $c = $k;
 
10470
                                                                } else {
 
10471
                                                                        // 32 bit format
 
10472
                                                                        // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4)
 
10473
                                                                        //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232
 
10474
                                                                        //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888
 
10475
                                                                        $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888;
 
10476
                                                                }
 
10477
                                                                $ctg[$c] = $g;
 
10478
                                                                ++$startGlyphID;
 
10479
                                                        }
 
10480
                                                }
 
10481
                                                break;
 
10482
                                        }
 
10483
                                        case 10: { // Format 10: Trimmed array
 
10484
                                                $offset += 10; // skip reserved, length and version/language
 
10485
                                                $startCharCode = $this->_getULONG($font, $offset);
 
10486
                                                $offset += 4;
 
10487
                                                $numChars = $this->_getULONG($font, $offset);
 
10488
                                                $offset += 4;
 
10489
                                                for ($k = 0; $k < $numChars; ++$k) {
 
10490
                                                        $c = ($k + $startCharCode);
 
10491
                                                        $g = $this->_getUSHORT($font, $offset);
 
10492
                                                        $ctg[$c] = $g;
 
10493
                                                        $offset += 2;
 
10494
                                                }
 
10495
                                                break;
 
10496
                                        }
 
10497
                                        case 12: { // Format 12: Segmented coverage
 
10498
                                                $offset += 10; // skip length and version/language
 
10499
                                                $nGroups = $this->_getULONG($font, $offset);
 
10500
                                                $offset += 4;
 
10501
                                                for ($k = 0; $k < $nGroups; ++$k) {
 
10502
                                                        $startCharCode = $this->_getULONG($font, $offset);
 
10503
                                                        $offset += 4;
 
10504
                                                        $endCharCode = $this->_getULONG($font, $offset);
 
10505
                                                        $offset += 4;
 
10506
                                                        $startGlyphCode = $this->_getULONG($font, $offset);
 
10507
                                                        $offset += 4;
 
10508
                                                        for ($c = $startCharCode; $c <= $endCharCode; ++$c) {
 
10509
                                                                $ctg[$c] = $startGlyphCode;
 
10510
                                                                ++$startGlyphCode;
 
10511
                                                        }
 
10512
                                                }
 
10513
                                                break;
 
10514
                                        }
 
10515
                                        case 13: { // Format 13: Many-to-one range mappings
 
10516
                                                // to be implemented ...
 
10517
                                                break;
 
10518
                                        }
 
10519
                                        case 14: { // Format 14: Unicode Variation Sequences
 
10520
                                                // to be implemented ...
 
10521
                                                break;
 
10522
                                        }
 
10523
                                }
 
10524
                        }
 
10525
                        if (!isset($ctg[0])) {
 
10526
                                $ctg[0] = 0;
 
10527
                        }
 
10528
                        // get xHeight (height of x)
 
10529
                        $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[120]] + 4);
 
10530
                        $yMin = $this->_getFWORD($font, $offset);
 
10531
                        $offset += 4;
 
10532
                        $yMax = $this->_getFWORD($font, $offset);
 
10533
                        $offset += 2;
 
10534
                        $fmetric['XHeight'] = round(($yMax - $yMin) * $urk);
 
10535
                        // get CapHeight (height of H)
 
10536
                        $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[72]] + 4);
 
10537
                        $yMin = $this->_getFWORD($font, $offset);
 
10538
                        $offset += 4;
 
10539
                        $yMax = $this->_getFWORD($font, $offset);
 
10540
                        $offset += 2;
 
10541
                        $fmetric['CapHeight'] = round(($yMax - $yMin) * $urk);
 
10542
                        // ceate widths array
 
10543
                        $cw = array();
 
10544
                        $offset = $table['hmtx']['offset'];
 
10545
                        for ($i = 0 ; $i < $numberOfHMetrics; ++$i) {
 
10546
                                $cw[$i] = round($this->_getUFWORD($font, $offset) * $urk);
 
10547
                                $offset += 4; // skip lsb
 
10548
                        }
 
10549
                        if ($numberOfHMetrics < $numGlyphs) {
 
10550
                                // fill missing widths with the last value
 
10551
                                $cw = array_pad($cw, $numGlyphs, $cw[($numberOfHMetrics - 1)]);
 
10552
                        }
 
10553
                        $fmetric['MissingWidth'] = $cw[0];
 
10554
                        $fmetric['cw'] = '';
 
10555
                        for ($cid = 0; $cid <= 65535; ++$cid) {
 
10556
                                if (isset($ctg[$cid]) AND isset($cw[$ctg[$cid]])) {
 
10557
                                        $fmetric['cw'] .= ','.$cid.'=>'.$cw[$ctg[$cid]];
 
10558
                                }
 
10559
                        }
 
10560
                } // end of true type
 
10561
                if (($fmetric['type'] == 'TrueTypeUnicode') AND (count($ctg) == 256)) {
 
10562
                        $fmetric['type'] == 'TrueType';
 
10563
                }
 
10564
                // ---------- create php font file ----------
 
10565
                $pfile = '<'.'?'.'php'."\n";
 
10566
                $pfile .= '// TCPDF FONT FILE DESCRIPTION'."\n";
 
10567
                $pfile .= '$type=\''.$fmetric['type'].'\';'."\n";
 
10568
                $pfile .= '$name=\''.$fmetric['name'].'\';'."\n";
 
10569
                $pfile .= '$up='.$fmetric['underlinePosition'].';'."\n";
 
10570
                $pfile .= '$ut='.$fmetric['underlineThickness'].';'."\n";
 
10571
                if ($fmetric['MissingWidth'] > 0) {
 
10572
                        $pfile .= '$dw='.$fmetric['MissingWidth'].';'."\n";
 
10573
                } else {
 
10574
                        $pfile .= '$dw='.$fmetric['AvgWidth'].';'."\n";
 
10575
                }
 
10576
                $pfile .= '$diff=\''.$fmetric['diff'].'\';'."\n";
 
10577
                if ($fmetric['type'] == 'Type1') {
 
10578
                        // Type 1
 
10579
                        $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n";
 
10580
                        $pfile .= '$file=\''.$fmetric['file'].'\';'."\n";
 
10581
                        $pfile .= '$size1='.$fmetric['size1'].';'."\n";
 
10582
                        $pfile .= '$size2='.$fmetric['size2'].';'."\n";
 
10583
                } else {
 
10584
                        $pfile .= '$originalsize='.$fmetric['originalsize'].';'."\n";
 
10585
                        if ($fmetric['type'] == 'cidfont0') {
 
10586
                                // CID-0
 
10587
                                switch ($fonttype) {
 
10588
                                        case 'CID0JP': {
 
10589
                                                $pfile .= '// Japanese'."\n";
 
10590
                                                $pfile .= '$enc=\'UniJIS-UTF16-H\';'."\n";
 
10591
                                                $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Japan1\',\'Supplement\'=>5);'."\n";
 
10592
                                                $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n";
 
10593
                                                break;
 
10594
                                        }
 
10595
                                        case 'CID0KR': {
 
10596
                                                $pfile .= '// Korean'."\n";
 
10597
                                                $pfile .= '$enc=\'UniKS-UTF16-H\';'."\n";
 
10598
                                                $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Korea1\',\'Supplement\'=>0);'."\n";
 
10599
                                                $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ak12.php\');'."\n";
 
10600
                                                break;
 
10601
                                        }
 
10602
                                        case 'CID0CS': {
 
10603
                                                $pfile .= '// Chinese Simplified'."\n";
 
10604
                                                $pfile .= '$enc=\'UniGB-UTF16-H\';'."\n";
 
10605
                                                $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'GB1\',\'Supplement\'=>2);'."\n";
 
10606
                                                $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ag15.php\');'."\n";
 
10607
                                                break;
 
10608
                                        }
 
10609
                                        case 'CID0CT':
 
10610
                                        default: {
 
10611
                                                $pfile .= '// Chinese Traditional'."\n";
 
10612
                                                $pfile .= '$enc=\'UniCNS-UTF16-H\';'."\n";
 
10613
                                                $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'CNS1\',\'Supplement\'=>0);'."\n";
 
10614
                                                $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n";
 
10615
                                                break;
 
10616
                                        }
 
10617
                                }
 
10618
                        } else {
 
10619
                                // TrueType
 
10620
                                $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n";
 
10621
                                $pfile .= '$file=\''.$fmetric['file'].'\';'."\n";
 
10622
                                $pfile .= '$ctg=\''.$fmetric['ctg'].'\';'."\n";
 
10623
                                // create CIDToGIDMap
 
10624
                                $cidtogidmap = str_pad('', 131072, "\x00"); // (256 * 256 * 2) = 131072
 
10625
                                foreach ($ctg as $cid => $gid) {
 
10626
                                        $cidtogidmap = $this->updateCIDtoGIDmap($cidtogidmap, $cid, $ctg[$cid]);
 
10627
                                }
 
10628
                                // store compressed CIDToGIDMap
 
10629
                                $fp = fopen($outpath.$fmetric['ctg'], 'wb');
 
10630
                                fwrite($fp, gzcompress($cidtogidmap));
 
10631
                                fclose($fp);
 
10632
                        }
 
10633
                }
 
10634
                $pfile .= '$desc=array(';
 
10635
                $pfile .= '\'Flags\'=>'.$fmetric['Flags'].',';
 
10636
                $pfile .= '\'FontBBox\'=>\'['.$fmetric['bbox'].']\',';
 
10637
                $pfile .= '\'ItalicAngle\'=>'.$fmetric['italicAngle'].',';
 
10638
                $pfile .= '\'Ascent\'=>'.$fmetric['Ascent'].',';
 
10639
                $pfile .= '\'Descent\'=>'.$fmetric['Descent'].',';
 
10640
                $pfile .= '\'Leading\'=>'.$fmetric['Leading'].',';
 
10641
                $pfile .= '\'CapHeight\'=>'.$fmetric['CapHeight'].',';
 
10642
                $pfile .= '\'XHeight\'=>'.$fmetric['XHeight'].',';
 
10643
                $pfile .= '\'StemV\'=>'.$fmetric['StemV'].',';
 
10644
                $pfile .= '\'StemH\'=>'.$fmetric['StemH'].',';
 
10645
                $pfile .= '\'AvgWidth\'=>'.$fmetric['AvgWidth'].',';
 
10646
                $pfile .= '\'MaxWidth\'=>'.$fmetric['MaxWidth'].',';
 
10647
                $pfile .= '\'MissingWidth\'=>'.$fmetric['MissingWidth'].'';
 
10648
                $pfile .= ');'."\n";
 
10649
                $pfile .= '$cw=array('.substr($fmetric['cw'], 1).');'."\n";
 
10650
                $pfile .= '// --- EOF ---'."\n";
 
10651
                // store file
 
10652
                $fp = fopen($outpath.$font_name.'.php', 'w');
 
10653
                fwrite($fp, $pfile);
 
10654
                fclose($fp);
 
10655
                // return TCPDF font name
 
10656
                return $font_name;
 
10657
        }
 
10658
 
 
10659
        /**
 
10660
         * Returns a subset of the TrueType font data without the unused glyphs.
 
10661
         * @param $font (string) TrueType font data.
 
10662
         * @param $subsetchars (array) Array of used characters (the glyphs to keep).
 
10663
         * @return (string) A subset of TrueType font data without the unused glyphs.
 
10664
         * @author Nicola Asuni
 
10665
         * @protected
 
10666
         * @since 5.2.000 (2010-06-02)
 
10667
         */
 
10668
        protected function _getTrueTypeFontSubset($font, $subsetchars) {
 
10669
                ksort($subsetchars);
 
10670
                $offset = 0; // offset position of the font data
 
10671
                if ($this->_getULONG($font, $offset) != 0x10000) {
 
10672
                        // sfnt version must be 0x00010000 for TrueType version 1.0.
 
10673
                        return $font;
 
10674
                }
 
10675
                $offset += 4;
 
10676
                // get number of tables
 
10677
                $numTables = $this->_getUSHORT($font, $offset);
 
10678
                $offset += 2;
 
10679
                // skip searchRange, entrySelector and rangeShift
 
10680
                $offset += 6;
 
10681
                // tables array
 
10682
                $table = array();
 
10683
                // for each table
 
10684
                for ($i = 0; $i < $numTables; ++$i) {
 
10685
                        // get table info
 
10686
                        $tag = substr($font, $offset, 4);
 
10687
                        $offset += 4;
 
10688
                        $table[$tag] = array();
 
10689
                        $table[$tag]['checkSum'] = $this->_getULONG($font, $offset);
 
10690
                        $offset += 4;
 
10691
                        $table[$tag]['offset'] = $this->_getULONG($font, $offset);
 
10692
                        $offset += 4;
 
10693
                        $table[$tag]['length'] = $this->_getULONG($font, $offset);
 
10694
                        $offset += 4;
 
10695
                }
 
10696
                // check magicNumber
 
10697
                $offset = $table['head']['offset'] + 12;
 
10698
                if ($this->_getULONG($font, $offset) != 0x5F0F3CF5) {
 
10699
                        // magicNumber must be 0x5F0F3CF5
 
10700
                        return $font;
 
10701
                }
 
10702
                $offset += 4;
 
10703
                // get offset mode (indexToLocFormat : 0 = short, 1 = long)
 
10704
                $offset = $table['head']['offset'] + 50;
 
10705
                $short_offset = ($this->_getSHORT($font, $offset) == 0);
 
10706
                $offset += 2;
 
10707
                // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table
 
10708
                $indexToLoc = array();
 
10709
                $offset = $table['loca']['offset'];
 
10710
                if ($short_offset) {
 
10711
                        // short version
 
10712
                        $tot_num_glyphs = ($table['loca']['length'] / 2); // numGlyphs + 1
 
10713
                        for ($i = 0; $i < $tot_num_glyphs; ++$i) {
 
10714
                                $indexToLoc[$i] = $this->_getUSHORT($font, $offset) * 2;
 
10715
                                $offset += 2;
 
10716
                        }
 
10717
                } else {
 
10718
                        // long version
 
10719
                        $tot_num_glyphs = ($table['loca']['length'] / 4); // numGlyphs + 1
 
10720
                        for ($i = 0; $i < $tot_num_glyphs; ++$i) {
 
10721
                                $indexToLoc[$i] = $this->_getULONG($font, $offset);
 
10722
                                $offset += 4;
 
10723
                        }
 
10724
                }
 
10725
                // get glyphs indexes of chars from cmap table
 
10726
                $subsetglyphs = array(); // glyph IDs on key
 
10727
                $subsetglyphs[0] = true; // character codes that do not correspond to any glyph in the font should be mapped to glyph index 0
 
10728
                $offset = $table['cmap']['offset'] + 2;
 
10729
                $numEncodingTables = $this->_getUSHORT($font, $offset);
 
10730
                $offset += 2;
 
10731
                $encodingTables = array();
 
10732
                for ($i = 0; $i < $numEncodingTables; ++$i) {
 
10733
                        $encodingTables[$i]['platformID'] = $this->_getUSHORT($font, $offset);
 
10734
                        $offset += 2;
 
10735
                        $encodingTables[$i]['encodingID'] = $this->_getUSHORT($font, $offset);
 
10736
                        $offset += 2;
 
10737
                        $encodingTables[$i]['offset'] = $this->_getULONG($font, $offset);
 
10738
                        $offset += 4;
 
10739
                }
 
10740
                foreach ($encodingTables as $enctable) {
 
10741
                        if (($enctable['platformID'] == 3) AND ($enctable['encodingID'] == 0)) {
 
10742
                                $modesymbol = true;
 
10743
                        } else {
 
10744
                                $modesymbol = false;
 
10745
                        }
 
10746
                        $offset = $table['cmap']['offset'] + $enctable['offset'];
 
10747
                        $format = $this->_getUSHORT($font, $offset);
 
10748
                        $offset += 2;
 
10749
                        switch ($format) {
 
10750
                                case 0: { // Format 0: Byte encoding table
 
10751
                                        $offset += 4; // skip length and version/language
 
10752
                                        for ($c = 0; $c < 256; ++$c) {
 
10753
                                                if (isset($subsetchars[$c])) {
 
10754
                                                        $g = $this->_getBYTE($font, $offset);
 
10755
                                                        $subsetglyphs[$g] = true;
 
10756
                                                }
 
10757
                                                ++$offset;
 
10758
                                        }
 
10759
                                        break;
 
10760
                                }
 
10761
                                case 2: { // Format 2: High-byte mapping through table
 
10762
                                        $offset += 4; // skip length and version/language
 
10763
                                        $numSubHeaders = 0;
 
10764
                                        for ($i = 0; $i < 256; ++$i) {
 
10765
                                                // Array that maps high bytes to subHeaders: value is subHeader index * 8.
 
10766
                                                $subHeaderKeys[$i] = ($this->_getUSHORT($font, $offset) / 8);
 
10767
                                                $offset += 2;
 
10768
                                                if ($numSubHeaders < $subHeaderKeys[$i]) {
 
10769
                                                        $numSubHeaders = $subHeaderKeys[$i];
 
10770
                                                }
 
10771
                                        }
 
10772
                                        // the number of subHeaders is equal to the max of subHeaderKeys + 1
 
10773
                                        ++$numSubHeaders;
 
10774
                                        // read subHeader structures
 
10775
                                        $subHeaders = array();
 
10776
                                        $numGlyphIndexArray = 0;
 
10777
                                        for ($k = 0; $k < $numSubHeaders; ++$k) {
 
10778
                                                $subHeaders[$k]['firstCode'] = $this->_getUSHORT($font, $offset);
 
10779
                                                $offset += 2;
 
10780
                                                $subHeaders[$k]['entryCount'] = $this->_getUSHORT($font, $offset);
 
10781
                                                $offset += 2;
 
10782
                                                $subHeaders[$k]['idDelta'] = $this->_getSHORT($font, $offset);
 
10783
                                                $offset += 2;
 
10784
                                                $subHeaders[$k]['idRangeOffset'] = $this->_getUSHORT($font, $offset);
 
10785
                                                $offset += 2;
 
10786
                                                $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8));
 
10787
                                                $subHeaders[$k]['idRangeOffset'] /= 2;
 
10788
                                                $numGlyphIndexArray += $subHeaders[$k]['entryCount'];
 
10789
                                        }
 
10790
                                        for ($k = 0; $k < $numGlyphIndexArray; ++$k) {
 
10791
                                                $glyphIndexArray[$k] = $this->_getUSHORT($font, $offset);
 
10792
                                                $offset += 2;
 
10793
                                        }
 
10794
                                        for ($i = 0; $i < 256; ++$i) {
 
10795
                                                $k = $subHeaderKeys[$i];
 
10796
                                                if ($k == 0) {
 
10797
                                                        // one byte code
 
10798
                                                        $c = $i;
 
10799
                                                        if (isset($subsetchars[$c])) {
 
10800
                                                                $g = $glyphIndexArray[0];
 
10801
                                                                $subsetglyphs[$g] = true;
 
10802
                                                        }
 
10803
                                                } else {
 
10804
                                                        // two bytes code
 
10805
                                                        $start_byte = $subHeaders[$k]['firstCode'];
 
10806
                                                        $end_byte = $start_byte + $subHeaders[$k]['entryCount'];
 
10807
                                                        for ($j = $start_byte; $j < $end_byte; ++$j) {
 
10808
                                                                // combine high and low bytes
 
10809
                                                                $c = (($i << 8) + $j);
 
10810
                                                                if (isset($subsetchars[$c])) {
 
10811
                                                                        $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']);
 
10812
                                                                        $g = $glyphIndexArray[$idRangeOffset];
 
10813
                                                                        $g += ($idDelta[$k] - 65536);
 
10814
                                                                        if ($g < 0) {
 
10815
                                                                                $g = 0;
 
10816
                                                                        }
 
10817
                                                                        $subsetglyphs[$g] = true;
 
10818
                                                                }
 
10819
                                                        }
 
10820
                                                }
 
10821
                                        }
 
10822
                                        break;
 
10823
                                }
 
10824
                                case 4: { // Format 4: Segment mapping to delta values
 
10825
                                        $length = $this->_getUSHORT($font, $offset);
 
10826
                                        $offset += 2;
 
10827
                                        $offset += 2; // skip version/language
 
10828
                                        $segCount = ($this->_getUSHORT($font, $offset) / 2);
 
10829
                                        $offset += 2;
 
10830
                                        $offset += 6; // skip searchRange, entrySelector, rangeShift
 
10831
                                        $endCount = array(); // array of end character codes for each segment
 
10832
                                        for ($k = 0; $k < $segCount; ++$k) {
 
10833
                                                $endCount[$k] = $this->_getUSHORT($font, $offset);
 
10834
                                                $offset += 2;
 
10835
                                        }
 
10836
                                        $offset += 2; // skip reservedPad
 
10837
                                        $startCount = array(); // array of start character codes for each segment
 
10838
                                        for ($k = 0; $k < $segCount; ++$k) {
 
10839
                                                $startCount[$k] = $this->_getUSHORT($font, $offset);
 
10840
                                                $offset += 2;
 
10841
                                        }
 
10842
                                        $idDelta = array(); // delta for all character codes in segment
 
10843
                                        for ($k = 0; $k < $segCount; ++$k) {
 
10844
                                                $idDelta[$k] = $this->_getUSHORT($font, $offset);
 
10845
                                                $offset += 2;
 
10846
                                        }
 
10847
                                        $idRangeOffset = array(); // Offsets into glyphIdArray or 0
 
10848
                                        for ($k = 0; $k < $segCount; ++$k) {
 
10849
                                                $idRangeOffset[$k] = $this->_getUSHORT($font, $offset);
 
10850
                                                $offset += 2;
 
10851
                                        }
 
10852
                                        $gidlen = ($length / 2) - 8 - (4 * $segCount);
 
10853
                                        $glyphIdArray = array(); // glyph index array
 
10854
                                        for ($k = 0; $k < $gidlen; ++$k) {
 
10855
                                                $glyphIdArray[$k] = $this->_getUSHORT($font, $offset);
 
10856
                                                $offset += 2;
 
10857
                                        }
 
10858
                                        for ($k = 0; $k < $segCount; ++$k) {
 
10859
                                                for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) {
 
10860
                                                        if (isset($subsetchars[$c])) {
 
10861
                                                                if ($idRangeOffset[$k] == 0) {
 
10862
                                                                        $g = $c;
 
10863
                                                                } else {
 
10864
                                                                        $gid = (($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k));
 
10865
                                                                        $g = $glyphIdArray[$gid];
 
10866
                                                                }
 
10867
                                                                $g += ($idDelta[$k] - 65536);
 
10868
                                                                if ($g < 0) {
 
10869
                                                                        $g = 0;
 
10870
                                                                }
 
10871
                                                                $subsetglyphs[$g] = true;
 
10872
                                                        }
 
10873
                                                }
 
10874
                                        }
 
10875
                                        break;
 
10876
                                }
 
10877
                                case 6: { // Format 6: Trimmed table mapping
 
10878
                                        $offset += 4; // skip length and version/language
 
10879
                                        $firstCode = $this->_getUSHORT($font, $offset);
 
10880
                                        $offset += 2;
 
10881
                                        $entryCount = $this->_getUSHORT($font, $offset);
 
10882
                                        $offset += 2;
 
10883
                                        for ($k = 0; $k < $entryCount; ++$k) {
 
10884
                                                $c = ($k + $firstCode);
 
10885
                                                if (isset($subsetchars[$c])) {
 
10886
                                                        $g = $this->_getUSHORT($font, $offset);
 
10887
                                                        $subsetglyphs[$g] = true;
 
10888
                                                }
 
10889
                                                $offset += 2;
 
10890
                                        }
 
10891
                                        break;
 
10892
                                }
 
10893
                                case 8: { // Format 8: Mixed 16-bit and 32-bit coverage
 
10894
                                        $offset += 10; // skip reserved, length and version/language
 
10895
                                        for ($k = 0; $k < 8192; ++$k) {
 
10896
                                                $is32[$k] = $this->_getBYTE($font, $offset);
 
10897
                                                ++$offset;
 
10898
                                        }
 
10899
                                        $nGroups = $this->_getULONG($font, $offset);
 
10900
                                        $offset += 4;
 
10901
                                        for ($i = 0; $i < $nGroups; ++$i) {
 
10902
                                                $startCharCode = $this->_getULONG($font, $offset);
 
10903
                                                $offset += 4;
 
10904
                                                $endCharCode = $this->_getULONG($font, $offset);
 
10905
                                                $offset += 4;
 
10906
                                                $startGlyphID = $this->_getULONG($font, $offset);
 
10907
                                                $offset += 4;
 
10908
                                                for ($k = $startCharCode; $k <= $endCharCode; ++$k) {
 
10909
                                                        $is32idx = floor($c / 8);
 
10910
                                                        if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) {
 
10911
                                                                $c = $k;
 
10912
                                                        } else {
 
10913
                                                                // 32 bit format
 
10914
                                                                // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4)
 
10915
                                                                //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232
 
10916
                                                                //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888
 
10917
                                                                $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888;
 
10918
                                                        }
 
10919
                                                        if (isset($subsetchars[$c])) {
 
10920
                                                                $subsetglyphs[$startGlyphID] = true;
 
10921
                                                        }
 
10922
                                                        ++$startGlyphID;
 
10923
                                                }
 
10924
                                        }
 
10925
                                        break;
 
10926
                                }
 
10927
                                case 10: { // Format 10: Trimmed array
 
10928
                                        $offset += 10; // skip reserved, length and version/language
 
10929
                                        $startCharCode = $this->_getULONG($font, $offset);
 
10930
                                        $offset += 4;
 
10931
                                        $numChars = $this->_getULONG($font, $offset);
 
10932
                                        $offset += 4;
 
10933
                                        for ($k = 0; $k < $numChars; ++$k) {
 
10934
                                                $c = ($k + $startCharCode);
 
10935
                                                if (isset($subsetchars[$c])) {
 
10936
                                                        $g = $this->_getUSHORT($font, $offset);
 
10937
                                                        $subsetglyphs[$g] = true;
 
10938
                                                }
 
10939
                                                $offset += 2;
 
10940
                                        }
 
10941
                                        break;
 
10942
                                }
 
10943
                                case 12: { // Format 12: Segmented coverage
 
10944
                                        $offset += 10; // skip length and version/language
 
10945
                                        $nGroups = $this->_getULONG($font, $offset);
 
10946
                                        $offset += 4;
 
10947
                                        for ($k = 0; $k < $nGroups; ++$k) {
 
10948
                                                $startCharCode = $this->_getULONG($font, $offset);
 
10949
                                                $offset += 4;
 
10950
                                                $endCharCode = $this->_getULONG($font, $offset);
 
10951
                                                $offset += 4;
 
10952
                                                $startGlyphCode = $this->_getULONG($font, $offset);
 
10953
                                                $offset += 4;
 
10954
                                                for ($c = $startCharCode; $c <= $endCharCode; ++$c) {
 
10955
                                                        if (isset($subsetchars[$c])) {
 
10956
                                                                $subsetglyphs[$startGlyphCode] = true;
 
10957
                                                        }
 
10958
                                                        ++$startGlyphCode;
 
10959
                                                }
 
10960
                                        }
 
10961
                                        break;
 
10962
                                }
 
10963
                                case 13: { // Format 13: Many-to-one range mappings
 
10964
                                        // to be implemented ...
 
10965
                                        break;
 
10966
                                }
 
10967
                                case 14: { // Format 14: Unicode Variation Sequences
 
10968
                                        // to be implemented ...
 
10969
                                        break;
 
10970
                                }
 
10971
                        }
 
10972
                }
 
10973
                // include all parts of composite glyphs
 
10974
                $new_sga = $subsetglyphs;
 
10975
                while (!empty($new_sga)) {
 
10976
                        $sga = $new_sga;
 
10977
                        $new_sga = array();
 
10978
                        foreach ($sga as $key => $val) {
 
10979
                                if (isset($indexToLoc[$key])) {
 
10980
                                        $offset = ($table['glyf']['offset'] + $indexToLoc[$key]);
 
10981
                                        $numberOfContours = $this->_getSHORT($font, $offset);
 
10982
                                        $offset += 2;
 
10983
                                        if ($numberOfContours < 0) { // composite glyph
 
10984
                                                $offset += 8; // skip xMin, yMin, xMax, yMax
 
10985
                                                do {
 
10986
                                                        $flags = $this->_getUSHORT($font, $offset);
 
10987
                                                        $offset += 2;
 
10988
                                                        $glyphIndex = $this->_getUSHORT($font, $offset);
 
10989
                                                        $offset += 2;
 
10990
                                                        if (!isset($subsetglyphs[$glyphIndex])) {
 
10991
                                                                // add missing glyphs
 
10992
                                                                $new_sga[$glyphIndex] = true;
 
10993
                                                        }
 
10994
                                                        // skip some bytes by case
 
10995
                                                        if ($flags & 1) {
 
10996
                                                                $offset += 4;
 
10997
                                                        } else {
 
10998
                                                                $offset += 2;
 
10999
                                                        }
 
11000
                                                        if ($flags & 8) {
 
11001
                                                                $offset += 2;
 
11002
                                                        } elseif ($flags & 64) {
 
11003
                                                                $offset += 4;
 
11004
                                                        } elseif ($flags & 128) {
 
11005
                                                                $offset += 8;
 
11006
                                                        }
 
11007
                                                } while ($flags & 32);
 
11008
                                        }
 
11009
                                }
 
11010
                        }
 
11011
                        $subsetglyphs += $new_sga;
 
11012
                }
 
11013
                // sort glyphs by key (and remove duplicates)
 
11014
                ksort($subsetglyphs);
 
11015
                // build new glyf and loca tables
 
11016
                $glyf = '';
 
11017
                $loca = '';
 
11018
                $offset = 0;
 
11019
                $glyf_offset = $table['glyf']['offset'];
 
11020
                for ($i = 0; $i < $tot_num_glyphs; ++$i) {
 
11021
                        if (isset($subsetglyphs[$i])) {
 
11022
                                $length = ($indexToLoc[($i + 1)] - $indexToLoc[$i]);
 
11023
                                $glyf .= substr($font, ($glyf_offset + $indexToLoc[$i]), $length);
 
11024
                        } else {
 
11025
                                $length = 0;
 
11026
                        }
 
11027
                        if ($short_offset) {
 
11028
                                $loca .= pack('n', ($offset / 2));
 
11029
                        } else {
 
11030
                                $loca .= pack('N', $offset);
 
11031
                        }
 
11032
                        $offset += $length;
 
11033
                }
 
11034
                // array of table names to preserve (loca and glyf tables will be added later)
 
11035
                // the cmap table is not needed and shall not be present, since the mapping from character codes to glyph descriptions is provided separately
 
11036
                $table_names = array ('head', 'hhea', 'hmtx', 'maxp', 'cvt ', 'fpgm', 'prep'); // minimum required table names
 
11037
                // get the tables to preserve
 
11038
                $offset = 12;
 
11039
                foreach ($table as $tag => $val) {
 
11040
                        if (in_array($tag, $table_names)) {
 
11041
                                $table[$tag]['data'] = substr($font, $table[$tag]['offset'], $table[$tag]['length']);
 
11042
                                if ($tag == 'head') {
 
11043
                                        // set the checkSumAdjustment to 0
 
11044
                                        $table[$tag]['data'] = substr($table[$tag]['data'], 0, 8)."\x0\x0\x0\x0".substr($table[$tag]['data'], 12);
 
11045
                                }
 
11046
                                $pad = 4 - ($table[$tag]['length'] % 4);
 
11047
                                if ($pad != 4) {
 
11048
                                        // the length of a table must be a multiple of four bytes
 
11049
                                        $table[$tag]['length'] += $pad;
 
11050
                                        $table[$tag]['data'] .= str_repeat("\x0", $pad);
 
11051
                                }
 
11052
                                $table[$tag]['offset'] = $offset;
 
11053
                                $offset += $table[$tag]['length'];
 
11054
                                // check sum is not changed (so keep the following line commented)
 
11055
                                //$table[$tag]['checkSum'] = $this->_getTTFtableChecksum($table[$tag]['data'], $table[$tag]['length']);
 
11056
                        } else {
 
11057
                                unset($table[$tag]);
 
11058
                        }
 
11059
                }
 
11060
                // add loca
 
11061
                $table['loca']['data'] = $loca;
 
11062
                $table['loca']['length'] = strlen($loca);
 
11063
                $pad = 4 - ($table['loca']['length'] % 4);
 
11064
                if ($pad != 4) {
 
11065
                        // the length of a table must be a multiple of four bytes
 
11066
                        $table['loca']['length'] += $pad;
 
11067
                        $table['loca']['data'] .= str_repeat("\x0", $pad);
 
11068
                }
 
11069
                $table['loca']['offset'] = $offset;
 
11070
                $table['loca']['checkSum'] = $this->_getTTFtableChecksum($table['loca']['data'], $table['loca']['length']);
 
11071
                $offset += $table['loca']['length'];
 
11072
                // add glyf
 
11073
                $table['glyf']['data'] = $glyf;
 
11074
                $table['glyf']['length'] = strlen($glyf);
 
11075
                $pad = 4 - ($table['glyf']['length'] % 4);
 
11076
                if ($pad != 4) {
 
11077
                        // the length of a table must be a multiple of four bytes
 
11078
                        $table['glyf']['length'] += $pad;
 
11079
                        $table['glyf']['data'] .= str_repeat("\x0", $pad);
 
11080
                }
 
11081
                $table['glyf']['offset'] = $offset;
 
11082
                $table['glyf']['checkSum'] = $this->_getTTFtableChecksum($table['glyf']['data'], $table['glyf']['length']);
 
11083
                // rebuild font
 
11084
                $font = '';
 
11085
                $font .= pack('N', 0x10000); // sfnt version
 
11086
                $numTables = count($table);
 
11087
                $font .= pack('n', $numTables); // numTables
 
11088
                $entrySelector = floor(log($numTables, 2));
 
11089
                $searchRange = pow(2, $entrySelector) * 16;
 
11090
                $rangeShift = ($numTables * 16) - $searchRange;
 
11091
                $font .= pack('n', $searchRange); // searchRange
 
11092
                $font .= pack('n', $entrySelector); // entrySelector
 
11093
                $font .= pack('n', $rangeShift); // rangeShift
 
11094
                $offset = ($numTables * 16);
 
11095
                foreach ($table as $tag => $data) {
 
11096
                        $font .= $tag; // tag
 
11097
                        $font .= pack('N', $data['checkSum']); // checkSum
 
11098
                        $font .= pack('N', ($data['offset'] + $offset)); // offset
 
11099
                        $font .= pack('N', $data['length']); // length
 
11100
                }
 
11101
                foreach ($table as $data) {
 
11102
                        $font .= $data['data'];
 
11103
                }
 
11104
                // set checkSumAdjustment on head table
 
11105
                $checkSumAdjustment = 0xB1B0AFBA - $this->_getTTFtableChecksum($font, strlen($font));
 
11106
                $font = substr($font, 0, $table['head']['offset'] + 8).pack('N', $checkSumAdjustment).substr($font, $table['head']['offset'] + 12);
 
11107
                return $font;
 
11108
        }
 
11109
 
 
11110
        /**
 
11111
         * Returs the checksum of a TTF table.
 
11112
         * @param $table (string) table to check
 
11113
         * @param $length (int) length of table in bytes
 
11114
         * @return int checksum
 
11115
         * @author Nicola Asuni
 
11116
         * @protected
 
11117
         * @since 5.2.000 (2010-06-02)
 
11118
         */
 
11119
        protected function _getTTFtableChecksum($table, $length) {
 
11120
                $sum = 0;
 
11121
                $tlen = ($length / 4);
 
11122
                $offset = 0;
 
11123
                for ($i = 0; $i < $tlen; ++$i) {
 
11124
                        $v = unpack('Ni', substr($table, $offset, 4));
 
11125
                        $sum += $v['i'];
 
11126
                        $offset += 4;
 
11127
                }
 
11128
                $sum = unpack('Ni', pack('N', $sum));
 
11129
                return $sum['i'];
 
11130
        }
 
11131
 
 
11132
        /**
 
11133
         * Outputs font widths
 
11134
         * @param $font (array) font data
 
11135
         * @param $cidoffset (int) offset for CID values
 
11136
         * @return PDF command string for font widths
 
11137
         * @author Nicola Asuni
 
11138
         * @protected
 
11139
         * @since 4.4.000 (2008-12-07)
 
11140
         */
 
11141
        protected function _putfontwidths($font, $cidoffset=0) {
 
11142
                ksort($font['cw']);
 
11143
                $rangeid = 0;
 
11144
                $range = array();
 
11145
                $prevcid = -2;
 
11146
                $prevwidth = -1;
 
11147
                $interval = false;
 
11148
                // for each character
 
11149
                foreach ($font['cw'] as $cid => $width) {
 
11150
                        $cid -= $cidoffset;
 
11151
                        if ($font['subset'] AND ($cid > 255) AND (!isset($font['subsetchars'][$cid]))) {
 
11152
                                // ignore the unused characters (font subsetting)
 
11153
                                continue;
 
11154
                        }
 
11155
                        if ($width != $font['dw']) {
 
11156
                                if ($cid == ($prevcid + 1)) {
 
11157
                                        // consecutive CID
 
11158
                                        if ($width == $prevwidth) {
 
11159
                                                if ($width == $range[$rangeid][0]) {
 
11160
                                                        $range[$rangeid][] = $width;
 
11161
                                                } else {
 
11162
                                                        array_pop($range[$rangeid]);
 
11163
                                                        // new range
 
11164
                                                        $rangeid = $prevcid;
 
11165
                                                        $range[$rangeid] = array();
 
11166
                                                        $range[$rangeid][] = $prevwidth;
 
11167
                                                        $range[$rangeid][] = $width;
 
11168
                                                }
 
11169
                                                $interval = true;
 
11170
                                                $range[$rangeid]['interval'] = true;
 
11171
                                        } else {
 
11172
                                                if ($interval) {
 
11173
                                                        // new range
 
11174
                                                        $rangeid = $cid;
 
11175
                                                        $range[$rangeid] = array();
 
11176
                                                        $range[$rangeid][] = $width;
 
11177
                                                } else {
 
11178
                                                        $range[$rangeid][] = $width;
 
11179
                                                }
 
11180
                                                $interval = false;
 
11181
                                        }
 
11182
                                } else {
 
11183
                                        // new range
 
11184
                                        $rangeid = $cid;
 
11185
                                        $range[$rangeid] = array();
 
11186
                                        $range[$rangeid][] = $width;
 
11187
                                        $interval = false;
 
11188
                                }
 
11189
                                $prevcid = $cid;
 
11190
                                $prevwidth = $width;
 
11191
                        }
 
11192
                }
 
11193
                // optimize ranges
 
11194
                $prevk = -1;
 
11195
                $nextk = -1;
 
11196
                $prevint = false;
 
11197
                foreach ($range as $k => $ws) {
 
11198
                        $cws = count($ws);
 
11199
                        if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
 
11200
                                if (isset($range[$k]['interval'])) {
 
11201
                                        unset($range[$k]['interval']);
 
11202
                                }
 
11203
                                $range[$prevk] = array_merge($range[$prevk], $range[$k]);
 
11204
                                unset($range[$k]);
 
11205
                        } else {
 
11206
                                $prevk = $k;
 
11207
                        }
 
11208
                        $nextk = $k + $cws;
 
11209
                        if (isset($ws['interval'])) {
 
11210
                                if ($cws > 3) {
 
11211
                                        $prevint = true;
 
11212
                                } else {
 
11213
                                        $prevint = false;
 
11214
                                }
 
11215
                                unset($range[$k]['interval']);
 
11216
                                --$nextk;
 
11217
                        } else {
 
11218
                                $prevint = false;
 
11219
                        }
 
11220
                }
 
11221
                // output data
 
11222
                $w = '';
 
11223
                foreach ($range as $k => $ws) {
 
11224
                        if (count(array_count_values($ws)) == 1) {
 
11225
                                // interval mode is more compact
 
11226
                                $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0];
 
11227
                        } else {
 
11228
                                // range mode
 
11229
                                $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
 
11230
                        }
 
11231
                }
 
11232
                return '/W ['.$w.' ]';
 
11233
        }
 
11234
 
 
11235
        /**
 
11236
         * Output fonts.
 
11237
         * @author Nicola Asuni
 
11238
         * @protected
 
11239
         */
 
11240
        protected function _putfonts() {
 
11241
                $nf = $this->n;
 
11242
                foreach ($this->diffs as $diff) {
 
11243
                        //Encodings
 
11244
                        $this->_newobj();
 
11245
                        $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
 
11246
                }
 
11247
                $mqr = $this->get_mqr();
 
11248
                $this->set_mqr(false);
 
11249
                foreach ($this->FontFiles as $file => $info) {
 
11250
                        // search and get font file to embedd
 
11251
                        $fontdir = $info['fontdir'];
 
11252
                        $file = strtolower($file);
 
11253
                        $fontfile = '';
 
11254
                        // search files on various directories
 
11255
                        if (($fontdir !== false) AND file_exists($fontdir.$file)) {
 
11256
                                $fontfile = $fontdir.$file;
 
11257
                        } elseif (file_exists($this->_getfontpath().$file)) {
 
11258
                                $fontfile = $this->_getfontpath().$file;
 
11259
                        } elseif (file_exists($file)) {
 
11260
                                $fontfile = $file;
 
11261
                        }
 
11262
                        if (!$this->empty_string($fontfile)) {
 
11263
                                $font = file_get_contents($fontfile);
 
11264
                                $compressed = (substr($file, -2) == '.z');
 
11265
                                if ((!$compressed) AND (isset($info['length2']))) {
 
11266
                                        $header = (ord($font{0}) == 128);
 
11267
                                        if ($header) {
 
11268
                                                // strip first binary header
 
11269
                                                $font = substr($font, 6);
 
11270
                                        }
 
11271
                                        if ($header AND (ord($font[$info['length1']]) == 128)) {
 
11272
                                                // strip second binary header
 
11273
                                                $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
 
11274
                                        }
 
11275
                                } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
 
11276
                                        if ($compressed) {
 
11277
                                                // uncompress font
 
11278
                                                $font = gzuncompress($font);
 
11279
                                        }
 
11280
                                        // merge subset characters
 
11281
                                        $subsetchars = array(); // used chars
 
11282
                                        foreach ($info['fontkeys'] as $fontkey) {
 
11283
                                                $fontinfo = $this->getFontBuffer($fontkey);
 
11284
                                                $subsetchars += $fontinfo['subsetchars'];
 
11285
                                        }
 
11286
                                        // rebuild a font subset
 
11287
                                        $font = $this->_getTrueTypeFontSubset($font, $subsetchars);
 
11288
                                        // calculate new font length
 
11289
                                        $info['length1'] = strlen($font);
 
11290
                                        if ($compressed) {
 
11291
                                                // recompress font
 
11292
                                                $font = gzcompress($font);
 
11293
                                        }
 
11294
                                }
 
11295
                                $this->_newobj();
 
11296
                                $this->FontFiles[$file]['n'] = $this->n;
 
11297
                                $stream = $this->_getrawstream($font);
 
11298
                                $out = '<< /Length '.strlen($stream);
 
11299
                                if ($compressed) {
 
11300
                                        $out .= ' /Filter /FlateDecode';
 
11301
                                }
 
11302
                                $out .= ' /Length1 '.$info['length1'];
 
11303
                                if (isset($info['length2'])) {
 
11304
                                        $out .= ' /Length2 '.$info['length2'].' /Length3 0';
 
11305
                                }
 
11306
                                $out .= ' >>';
 
11307
                                $out .= ' stream'."\n".$stream."\n".'endstream';
 
11308
                                $out .= "\n".'endobj';
 
11309
                                $this->_out($out);
 
11310
                        }
 
11311
                }
 
11312
                $this->set_mqr($mqr);
 
11313
                foreach ($this->fontkeys as $k) {
 
11314
                        //Font objects
 
11315
                        $font = $this->getFontBuffer($k);
 
11316
                        $type = $font['type'];
 
11317
                        $name = $font['name'];
 
11318
                        if ($type == 'core') {
 
11319
                                // standard core font
 
11320
                                $out = $this->_getobj($this->font_obj_ids[$k])."\n";
 
11321
                                $out .= '<</Type /Font';
 
11322
                                $out .= ' /Subtype /Type1';
 
11323
                                $out .= ' /BaseFont /'.$name;
 
11324
                                $out .= ' /Name /F'.$font['i'];
 
11325
                                if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
 
11326
                                        $out .= ' /Encoding /WinAnsiEncoding';
 
11327
                                }
 
11328
                                if ($k == 'helvetica') {
 
11329
                                        // add default font for annotations
 
11330
                                        $this->annotation_fonts[$k] = $font['i'];
 
11331
                                }
 
11332
                                $out .= ' >>';
 
11333
                                $out .= "\n".'endobj';
 
11334
                                $this->_out($out);
 
11335
                        } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
 
11336
                                // additional Type1 or TrueType font
 
11337
                                $out = $this->_getobj($this->font_obj_ids[$k])."\n";
 
11338
                                $out .= '<</Type /Font';
 
11339
                                $out .= ' /Subtype /'.$type;
 
11340
                                $out .= ' /BaseFont /'.$name;
 
11341
                                $out .= ' /Name /F'.$font['i'];
 
11342
                                $out .= ' /FirstChar 32 /LastChar 255';
 
11343
                                $out .= ' /Widths '.($this->n + 1).' 0 R';
 
11344
                                $out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
 
11345
                                if ($font['enc']) {
 
11346
                                        if (isset($font['diff'])) {
 
11347
                                                $out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
 
11348
                                        } else {
 
11349
                                                $out .= ' /Encoding /WinAnsiEncoding';
 
11350
                                        }
 
11351
                                }
 
11352
                                $out .= ' >>';
 
11353
                                $out .= "\n".'endobj';
 
11354
                                $this->_out($out);
 
11355
                                // Widths
 
11356
                                $this->_newobj();
 
11357
                                $s = '[';
 
11358
                                for ($i = 32; $i < 256; ++$i) {
 
11359
                                        $s .= $font['cw'][$i].' ';
 
11360
                                }
 
11361
                                $s .= ']';
 
11362
                                $s .= "\n".'endobj';
 
11363
                                $this->_out($s);
 
11364
                                //Descriptor
 
11365
                                $this->_newobj();
 
11366
                                $s = '<</Type /FontDescriptor /FontName /'.$name;
 
11367
                                foreach ($font['desc'] as $fdk => $fdv) {
 
11368
                                        if (is_float($fdv)) {
 
11369
                                                $fdv = sprintf('%.3F', $fdv);
 
11370
                                        }
 
11371
                                        $s .= ' /'.$fdk.' '.$fdv.'';
 
11372
                                }
 
11373
                                if (!$this->empty_string($font['file'])) {
 
11374
                                        $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
 
11375
                                }
 
11376
                                $s .= '>>';
 
11377
                                $s .= "\n".'endobj';
 
11378
                                $this->_out($s);
 
11379
                        } else {
 
11380
                                // additional types
 
11381
                                $mtd = '_put'.strtolower($type);
 
11382
                                if (!method_exists($this, $mtd)) {
 
11383
                                        $this->Error('Unsupported font type: '.$type);
 
11384
                                }
 
11385
                                $this->$mtd($font);
 
11386
                        }
 
11387
                }
 
11388
        }
 
11389
 
 
11390
        /**
 
11391
         * Adds unicode fonts.<br>
 
11392
         * Based on PDF Reference 1.3 (section 5)
 
11393
         * @param $font (array) font data
 
11394
         * @protected
 
11395
         * @author Nicola Asuni
 
11396
         * @since 1.52.0.TC005 (2005-01-05)
 
11397
         */
 
11398
        protected function _puttruetypeunicode($font) {
 
11399
                $fontname = '';
 
11400
                if ($font['subset']) {
 
11401
                        // change name for font subsetting
 
11402
                        $subtag = sprintf('%06u', $font['i']);
 
11403
                        $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
 
11404
                        $fontname .= $subtag.'+';
 
11405
                }
 
11406
                $fontname .= $font['name'];
 
11407
                // Type0 Font
 
11408
                // A composite font composed of other fonts, organized hierarchically
 
11409
                $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
 
11410
                $out .= '<< /Type /Font';
 
11411
                $out .= ' /Subtype /Type0';
 
11412
                $out .= ' /BaseFont /'.$fontname;
 
11413
                $out .= ' /Name /F'.$font['i'];
 
11414
                $out .= ' /Encoding /'.$font['enc'];
 
11415
                $out .= ' /ToUnicode '.($this->n + 1).' 0 R';
 
11416
                $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]';
 
11417
                $out .= ' >>';
 
11418
                $out .= "\n".'endobj';
 
11419
                $this->_out($out);
 
11420
                // ToUnicode map for Identity-H
 
11421
                $stream = "/CIDInit /ProcSet findresource begin\n";
 
11422
                $stream .= "12 dict begin\n";
 
11423
                $stream .= "begincmap\n";
 
11424
                $stream .= "/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n";
 
11425
                $stream .= "/CMapName /Adobe-Identity-UCS def\n";
 
11426
                $stream .= "/CMapType 2 def\n";
 
11427
                $stream .= "/WMode 0 def\n";
 
11428
                $stream .= "1 begincodespacerange\n";
 
11429
                $stream .= "<0000> <FFFF>\n";
 
11430
                $stream .= "endcodespacerange\n";
 
11431
                $stream .= "100 beginbfrange\n";
 
11432
                $stream .= "<0000> <00ff> <0000>\n";
 
11433
                $stream .= "<0100> <01ff> <0100>\n";
 
11434
                $stream .= "<0200> <02ff> <0200>\n";
 
11435
                $stream .= "<0300> <03ff> <0300>\n";
 
11436
                $stream .= "<0400> <04ff> <0400>\n";
 
11437
                $stream .= "<0500> <05ff> <0500>\n";
 
11438
                $stream .= "<0600> <06ff> <0600>\n";
 
11439
                $stream .= "<0700> <07ff> <0700>\n";
 
11440
                $stream .= "<0800> <08ff> <0800>\n";
 
11441
                $stream .= "<0900> <09ff> <0900>\n";
 
11442
                $stream .= "<0a00> <0aff> <0a00>\n";
 
11443
                $stream .= "<0b00> <0bff> <0b00>\n";
 
11444
                $stream .= "<0c00> <0cff> <0c00>\n";
 
11445
                $stream .= "<0d00> <0dff> <0d00>\n";
 
11446
                $stream .= "<0e00> <0eff> <0e00>\n";
 
11447
                $stream .= "<0f00> <0fff> <0f00>\n";
 
11448
                $stream .= "<1000> <10ff> <1000>\n";
 
11449
                $stream .= "<1100> <11ff> <1100>\n";
 
11450
                $stream .= "<1200> <12ff> <1200>\n";
 
11451
                $stream .= "<1300> <13ff> <1300>\n";
 
11452
                $stream .= "<1400> <14ff> <1400>\n";
 
11453
                $stream .= "<1500> <15ff> <1500>\n";
 
11454
                $stream .= "<1600> <16ff> <1600>\n";
 
11455
                $stream .= "<1700> <17ff> <1700>\n";
 
11456
                $stream .= "<1800> <18ff> <1800>\n";
 
11457
                $stream .= "<1900> <19ff> <1900>\n";
 
11458
                $stream .= "<1a00> <1aff> <1a00>\n";
 
11459
                $stream .= "<1b00> <1bff> <1b00>\n";
 
11460
                $stream .= "<1c00> <1cff> <1c00>\n";
 
11461
                $stream .= "<1d00> <1dff> <1d00>\n";
 
11462
                $stream .= "<1e00> <1eff> <1e00>\n";
 
11463
                $stream .= "<1f00> <1fff> <1f00>\n";
 
11464
                $stream .= "<2000> <20ff> <2000>\n";
 
11465
                $stream .= "<2100> <21ff> <2100>\n";
 
11466
                $stream .= "<2200> <22ff> <2200>\n";
 
11467
                $stream .= "<2300> <23ff> <2300>\n";
 
11468
                $stream .= "<2400> <24ff> <2400>\n";
 
11469
                $stream .= "<2500> <25ff> <2500>\n";
 
11470
                $stream .= "<2600> <26ff> <2600>\n";
 
11471
                $stream .= "<2700> <27ff> <2700>\n";
 
11472
                $stream .= "<2800> <28ff> <2800>\n";
 
11473
                $stream .= "<2900> <29ff> <2900>\n";
 
11474
                $stream .= "<2a00> <2aff> <2a00>\n";
 
11475
                $stream .= "<2b00> <2bff> <2b00>\n";
 
11476
                $stream .= "<2c00> <2cff> <2c00>\n";
 
11477
                $stream .= "<2d00> <2dff> <2d00>\n";
 
11478
                $stream .= "<2e00> <2eff> <2e00>\n";
 
11479
                $stream .= "<2f00> <2fff> <2f00>\n";
 
11480
                $stream .= "<3000> <30ff> <3000>\n";
 
11481
                $stream .= "<3100> <31ff> <3100>\n";
 
11482
                $stream .= "<3200> <32ff> <3200>\n";
 
11483
                $stream .= "<3300> <33ff> <3300>\n";
 
11484
                $stream .= "<3400> <34ff> <3400>\n";
 
11485
                $stream .= "<3500> <35ff> <3500>\n";
 
11486
                $stream .= "<3600> <36ff> <3600>\n";
 
11487
                $stream .= "<3700> <37ff> <3700>\n";
 
11488
                $stream .= "<3800> <38ff> <3800>\n";
 
11489
                $stream .= "<3900> <39ff> <3900>\n";
 
11490
                $stream .= "<3a00> <3aff> <3a00>\n";
 
11491
                $stream .= "<3b00> <3bff> <3b00>\n";
 
11492
                $stream .= "<3c00> <3cff> <3c00>\n";
 
11493
                $stream .= "<3d00> <3dff> <3d00>\n";
 
11494
                $stream .= "<3e00> <3eff> <3e00>\n";
 
11495
                $stream .= "<3f00> <3fff> <3f00>\n";
 
11496
                $stream .= "<4000> <40ff> <4000>\n";
 
11497
                $stream .= "<4100> <41ff> <4100>\n";
 
11498
                $stream .= "<4200> <42ff> <4200>\n";
 
11499
                $stream .= "<4300> <43ff> <4300>\n";
 
11500
                $stream .= "<4400> <44ff> <4400>\n";
 
11501
                $stream .= "<4500> <45ff> <4500>\n";
 
11502
                $stream .= "<4600> <46ff> <4600>\n";
 
11503
                $stream .= "<4700> <47ff> <4700>\n";
 
11504
                $stream .= "<4800> <48ff> <4800>\n";
 
11505
                $stream .= "<4900> <49ff> <4900>\n";
 
11506
                $stream .= "<4a00> <4aff> <4a00>\n";
 
11507
                $stream .= "<4b00> <4bff> <4b00>\n";
 
11508
                $stream .= "<4c00> <4cff> <4c00>\n";
 
11509
                $stream .= "<4d00> <4dff> <4d00>\n";
 
11510
                $stream .= "<4e00> <4eff> <4e00>\n";
 
11511
                $stream .= "<4f00> <4fff> <4f00>\n";
 
11512
                $stream .= "<5000> <50ff> <5000>\n";
 
11513
                $stream .= "<5100> <51ff> <5100>\n";
 
11514
                $stream .= "<5200> <52ff> <5200>\n";
 
11515
                $stream .= "<5300> <53ff> <5300>\n";
 
11516
                $stream .= "<5400> <54ff> <5400>\n";
 
11517
                $stream .= "<5500> <55ff> <5500>\n";
 
11518
                $stream .= "<5600> <56ff> <5600>\n";
 
11519
                $stream .= "<5700> <57ff> <5700>\n";
 
11520
                $stream .= "<5800> <58ff> <5800>\n";
 
11521
                $stream .= "<5900> <59ff> <5900>\n";
 
11522
                $stream .= "<5a00> <5aff> <5a00>\n";
 
11523
                $stream .= "<5b00> <5bff> <5b00>\n";
 
11524
                $stream .= "<5c00> <5cff> <5c00>\n";
 
11525
                $stream .= "<5d00> <5dff> <5d00>\n";
 
11526
                $stream .= "<5e00> <5eff> <5e00>\n";
 
11527
                $stream .= "<5f00> <5fff> <5f00>\n";
 
11528
                $stream .= "<6000> <60ff> <6000>\n";
 
11529
                $stream .= "<6100> <61ff> <6100>\n";
 
11530
                $stream .= "<6200> <62ff> <6200>\n";
 
11531
                $stream .= "<6300> <63ff> <6300>\n";
 
11532
                $stream .= "endbfrange\n";
 
11533
                $stream .= "100 beginbfrange\n";
 
11534
                $stream .= "<6400> <64ff> <6400>\n";
 
11535
                $stream .= "<6500> <65ff> <6500>\n";
 
11536
                $stream .= "<6600> <66ff> <6600>\n";
 
11537
                $stream .= "<6700> <67ff> <6700>\n";
 
11538
                $stream .= "<6800> <68ff> <6800>\n";
 
11539
                $stream .= "<6900> <69ff> <6900>\n";
 
11540
                $stream .= "<6a00> <6aff> <6a00>\n";
 
11541
                $stream .= "<6b00> <6bff> <6b00>\n";
 
11542
                $stream .= "<6c00> <6cff> <6c00>\n";
 
11543
                $stream .= "<6d00> <6dff> <6d00>\n";
 
11544
                $stream .= "<6e00> <6eff> <6e00>\n";
 
11545
                $stream .= "<6f00> <6fff> <6f00>\n";
 
11546
                $stream .= "<7000> <70ff> <7000>\n";
 
11547
                $stream .= "<7100> <71ff> <7100>\n";
 
11548
                $stream .= "<7200> <72ff> <7200>\n";
 
11549
                $stream .= "<7300> <73ff> <7300>\n";
 
11550
                $stream .= "<7400> <74ff> <7400>\n";
 
11551
                $stream .= "<7500> <75ff> <7500>\n";
 
11552
                $stream .= "<7600> <76ff> <7600>\n";
 
11553
                $stream .= "<7700> <77ff> <7700>\n";
 
11554
                $stream .= "<7800> <78ff> <7800>\n";
 
11555
                $stream .= "<7900> <79ff> <7900>\n";
 
11556
                $stream .= "<7a00> <7aff> <7a00>\n";
 
11557
                $stream .= "<7b00> <7bff> <7b00>\n";
 
11558
                $stream .= "<7c00> <7cff> <7c00>\n";
 
11559
                $stream .= "<7d00> <7dff> <7d00>\n";
 
11560
                $stream .= "<7e00> <7eff> <7e00>\n";
 
11561
                $stream .= "<7f00> <7fff> <7f00>\n";
 
11562
                $stream .= "<8000> <80ff> <8000>\n";
 
11563
                $stream .= "<8100> <81ff> <8100>\n";
 
11564
                $stream .= "<8200> <82ff> <8200>\n";
 
11565
                $stream .= "<8300> <83ff> <8300>\n";
 
11566
                $stream .= "<8400> <84ff> <8400>\n";
 
11567
                $stream .= "<8500> <85ff> <8500>\n";
 
11568
                $stream .= "<8600> <86ff> <8600>\n";
 
11569
                $stream .= "<8700> <87ff> <8700>\n";
 
11570
                $stream .= "<8800> <88ff> <8800>\n";
 
11571
                $stream .= "<8900> <89ff> <8900>\n";
 
11572
                $stream .= "<8a00> <8aff> <8a00>\n";
 
11573
                $stream .= "<8b00> <8bff> <8b00>\n";
 
11574
                $stream .= "<8c00> <8cff> <8c00>\n";
 
11575
                $stream .= "<8d00> <8dff> <8d00>\n";
 
11576
                $stream .= "<8e00> <8eff> <8e00>\n";
 
11577
                $stream .= "<8f00> <8fff> <8f00>\n";
 
11578
                $stream .= "<9000> <90ff> <9000>\n";
 
11579
                $stream .= "<9100> <91ff> <9100>\n";
 
11580
                $stream .= "<9200> <92ff> <9200>\n";
 
11581
                $stream .= "<9300> <93ff> <9300>\n";
 
11582
                $stream .= "<9400> <94ff> <9400>\n";
 
11583
                $stream .= "<9500> <95ff> <9500>\n";
 
11584
                $stream .= "<9600> <96ff> <9600>\n";
 
11585
                $stream .= "<9700> <97ff> <9700>\n";
 
11586
                $stream .= "<9800> <98ff> <9800>\n";
 
11587
                $stream .= "<9900> <99ff> <9900>\n";
 
11588
                $stream .= "<9a00> <9aff> <9a00>\n";
 
11589
                $stream .= "<9b00> <9bff> <9b00>\n";
 
11590
                $stream .= "<9c00> <9cff> <9c00>\n";
 
11591
                $stream .= "<9d00> <9dff> <9d00>\n";
 
11592
                $stream .= "<9e00> <9eff> <9e00>\n";
 
11593
                $stream .= "<9f00> <9fff> <9f00>\n";
 
11594
                $stream .= "<a000> <a0ff> <a000>\n";
 
11595
                $stream .= "<a100> <a1ff> <a100>\n";
 
11596
                $stream .= "<a200> <a2ff> <a200>\n";
 
11597
                $stream .= "<a300> <a3ff> <a300>\n";
 
11598
                $stream .= "<a400> <a4ff> <a400>\n";
 
11599
                $stream .= "<a500> <a5ff> <a500>\n";
 
11600
                $stream .= "<a600> <a6ff> <a600>\n";
 
11601
                $stream .= "<a700> <a7ff> <a700>\n";
 
11602
                $stream .= "<a800> <a8ff> <a800>\n";
 
11603
                $stream .= "<a900> <a9ff> <a900>\n";
 
11604
                $stream .= "<aa00> <aaff> <aa00>\n";
 
11605
                $stream .= "<ab00> <abff> <ab00>\n";
 
11606
                $stream .= "<ac00> <acff> <ac00>\n";
 
11607
                $stream .= "<ad00> <adff> <ad00>\n";
 
11608
                $stream .= "<ae00> <aeff> <ae00>\n";
 
11609
                $stream .= "<af00> <afff> <af00>\n";
 
11610
                $stream .= "<b000> <b0ff> <b000>\n";
 
11611
                $stream .= "<b100> <b1ff> <b100>\n";
 
11612
                $stream .= "<b200> <b2ff> <b200>\n";
 
11613
                $stream .= "<b300> <b3ff> <b300>\n";
 
11614
                $stream .= "<b400> <b4ff> <b400>\n";
 
11615
                $stream .= "<b500> <b5ff> <b500>\n";
 
11616
                $stream .= "<b600> <b6ff> <b600>\n";
 
11617
                $stream .= "<b700> <b7ff> <b700>\n";
 
11618
                $stream .= "<b800> <b8ff> <b800>\n";
 
11619
                $stream .= "<b900> <b9ff> <b900>\n";
 
11620
                $stream .= "<ba00> <baff> <ba00>\n";
 
11621
                $stream .= "<bb00> <bbff> <bb00>\n";
 
11622
                $stream .= "<bc00> <bcff> <bc00>\n";
 
11623
                $stream .= "<bd00> <bdff> <bd00>\n";
 
11624
                $stream .= "<be00> <beff> <be00>\n";
 
11625
                $stream .= "<bf00> <bfff> <bf00>\n";
 
11626
                $stream .= "<c000> <c0ff> <c000>\n";
 
11627
                $stream .= "<c100> <c1ff> <c100>\n";
 
11628
                $stream .= "<c200> <c2ff> <c200>\n";
 
11629
                $stream .= "<c300> <c3ff> <c300>\n";
 
11630
                $stream .= "<c400> <c4ff> <c400>\n";
 
11631
                $stream .= "<c500> <c5ff> <c500>\n";
 
11632
                $stream .= "<c600> <c6ff> <c600>\n";
 
11633
                $stream .= "<c700> <c7ff> <c700>\n";
 
11634
                $stream .= "endbfrange\n";
 
11635
                $stream .= "56 beginbfrange\n";
 
11636
                $stream .= "<c800> <c8ff> <c800>\n";
 
11637
                $stream .= "<c900> <c9ff> <c900>\n";
 
11638
                $stream .= "<ca00> <caff> <ca00>\n";
 
11639
                $stream .= "<cb00> <cbff> <cb00>\n";
 
11640
                $stream .= "<cc00> <ccff> <cc00>\n";
 
11641
                $stream .= "<cd00> <cdff> <cd00>\n";
 
11642
                $stream .= "<ce00> <ceff> <ce00>\n";
 
11643
                $stream .= "<cf00> <cfff> <cf00>\n";
 
11644
                $stream .= "<d000> <d0ff> <d000>\n";
 
11645
                $stream .= "<d100> <d1ff> <d100>\n";
 
11646
                $stream .= "<d200> <d2ff> <d200>\n";
 
11647
                $stream .= "<d300> <d3ff> <d300>\n";
 
11648
                $stream .= "<d400> <d4ff> <d400>\n";
 
11649
                $stream .= "<d500> <d5ff> <d500>\n";
 
11650
                $stream .= "<d600> <d6ff> <d600>\n";
 
11651
                $stream .= "<d700> <d7ff> <d700>\n";
 
11652
                $stream .= "<d800> <d8ff> <d800>\n";
 
11653
                $stream .= "<d900> <d9ff> <d900>\n";
 
11654
                $stream .= "<da00> <daff> <da00>\n";
 
11655
                $stream .= "<db00> <dbff> <db00>\n";
 
11656
                $stream .= "<dc00> <dcff> <dc00>\n";
 
11657
                $stream .= "<dd00> <ddff> <dd00>\n";
 
11658
                $stream .= "<de00> <deff> <de00>\n";
 
11659
                $stream .= "<df00> <dfff> <df00>\n";
 
11660
                $stream .= "<e000> <e0ff> <e000>\n";
 
11661
                $stream .= "<e100> <e1ff> <e100>\n";
 
11662
                $stream .= "<e200> <e2ff> <e200>\n";
 
11663
                $stream .= "<e300> <e3ff> <e300>\n";
 
11664
                $stream .= "<e400> <e4ff> <e400>\n";
 
11665
                $stream .= "<e500> <e5ff> <e500>\n";
 
11666
                $stream .= "<e600> <e6ff> <e600>\n";
 
11667
                $stream .= "<e700> <e7ff> <e700>\n";
 
11668
                $stream .= "<e800> <e8ff> <e800>\n";
 
11669
                $stream .= "<e900> <e9ff> <e900>\n";
 
11670
                $stream .= "<ea00> <eaff> <ea00>\n";
 
11671
                $stream .= "<eb00> <ebff> <eb00>\n";
 
11672
                $stream .= "<ec00> <ecff> <ec00>\n";
 
11673
                $stream .= "<ed00> <edff> <ed00>\n";
 
11674
                $stream .= "<ee00> <eeff> <ee00>\n";
 
11675
                $stream .= "<ef00> <efff> <ef00>\n";
 
11676
                $stream .= "<f000> <f0ff> <f000>\n";
 
11677
                $stream .= "<f100> <f1ff> <f100>\n";
 
11678
                $stream .= "<f200> <f2ff> <f200>\n";
 
11679
                $stream .= "<f300> <f3ff> <f300>\n";
 
11680
                $stream .= "<f400> <f4ff> <f400>\n";
 
11681
                $stream .= "<f500> <f5ff> <f500>\n";
 
11682
                $stream .= "<f600> <f6ff> <f600>\n";
 
11683
                $stream .= "<f700> <f7ff> <f700>\n";
 
11684
                $stream .= "<f800> <f8ff> <f800>\n";
 
11685
                $stream .= "<f900> <f9ff> <f900>\n";
 
11686
                $stream .= "<fa00> <faff> <fa00>\n";
 
11687
                $stream .= "<fb00> <fbff> <fb00>\n";
 
11688
                $stream .= "<fc00> <fcff> <fc00>\n";
 
11689
                $stream .= "<fd00> <fdff> <fd00>\n";
 
11690
                $stream .= "<fe00> <feff> <fe00>\n";
 
11691
                $stream .= "<ff00> <ffff> <ff00>\n";
 
11692
                $stream .= "endbfrange\n";
 
11693
                $stream .= "endcmap\n";
 
11694
                $stream .= "CMapName currentdict /CMap defineresource pop\n";
 
11695
                $stream .= "end\n";
 
11696
                $stream .= "end";
 
11697
                // ToUnicode Object
 
11698
                $this->_newobj();
 
11699
                $stream = ($this->compress) ? gzcompress($stream) : $stream;
 
11700
                $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
 
11701
                $stream = $this->_getrawstream($stream);
 
11702
                $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
 
11703
                // CIDFontType2
 
11704
                // A CIDFont whose glyph descriptions are based on TrueType font technology
 
11705
                $oid = $this->_newobj();
 
11706
                $out = '<< /Type /Font';
 
11707
                $out .= ' /Subtype /CIDFontType2';
 
11708
                $out .= ' /BaseFont /'.$fontname;
 
11709
                // A dictionary containing entries that define the character collection of the CIDFont.
 
11710
                $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
 
11711
                $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
 
11712
                $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
 
11713
                $out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
 
11714
                $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
 
11715
                $out .= ' /DW '.$font['dw']; // default width
 
11716
                $out .= "\n".$this->_putfontwidths($font, 0);
 
11717
                if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
 
11718
                        $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R';
 
11719
                }
 
11720
                $out .= ' >>';
 
11721
                $out .= "\n".'endobj';
 
11722
                $this->_out($out);
 
11723
                // Font descriptor
 
11724
                // A font descriptor describing the CIDFont default metrics other than its glyph widths
 
11725
                $this->_newobj();
 
11726
                $out = '<< /Type /FontDescriptor';
 
11727
                $out .= ' /FontName /'.$fontname;
 
11728
                foreach ($font['desc'] as $key => $value) {
 
11729
                        if (is_float($value)) {
 
11730
                                $value = sprintf('%.3F', $value);
 
11731
                        }
 
11732
                        $out .= ' /'.$key.' '.$value;
 
11733
                }
 
11734
                $fontdir = false;
 
11735
                if (!$this->empty_string($font['file'])) {
 
11736
                        // A stream containing a TrueType font
 
11737
                        $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
 
11738
                        $fontdir = $this->FontFiles[$font['file']]['fontdir'];
 
11739
                }
 
11740
                $out .= ' >>';
 
11741
                $out .= "\n".'endobj';
 
11742
                $this->_out($out);
 
11743
                if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
 
11744
                        $this->_newobj();
 
11745
                        // Embed CIDToGIDMap
 
11746
                        // A specification of the mapping from CIDs to glyph indices
 
11747
                        // search and get CTG font file to embedd
 
11748
                        $ctgfile = strtolower($font['ctg']);
 
11749
                        // search and get ctg font file to embedd
 
11750
                        $fontfile = '';
 
11751
                        // search files on various directories
 
11752
                        if (($fontdir !== false) AND file_exists($fontdir.$ctgfile)) {
 
11753
                                $fontfile = $fontdir.$ctgfile;
 
11754
                        } elseif (file_exists($this->_getfontpath().$ctgfile)) {
 
11755
                                $fontfile = $this->_getfontpath().$ctgfile;
 
11756
                        } elseif (file_exists($ctgfile)) {
 
11757
                                $fontfile = $ctgfile;
 
11758
                        }
 
11759
                        if ($this->empty_string($fontfile)) {
 
11760
                                $this->Error('Font file not found: '.$ctgfile);
 
11761
                        }
 
11762
                        $stream = $this->_getrawstream(file_get_contents($fontfile));
 
11763
                        $out = '<< /Length '.strlen($stream).'';
 
11764
                        if (substr($fontfile, -2) == '.z') { // check file extension
 
11765
                                // Decompresses data encoded using the public-domain
 
11766
                                // zlib/deflate compression method, reproducing the
 
11767
                                // original text or binary data
 
11768
                                $out .= ' /Filter /FlateDecode';
 
11769
                        }
 
11770
                        $out .= ' >>';
 
11771
                        $out .= ' stream'."\n".$stream."\n".'endstream';
 
11772
                        $out .= "\n".'endobj';
 
11773
                        $this->_out($out);
 
11774
                }
 
11775
        }
 
11776
 
 
11777
        /**
 
11778
         * Output CID-0 fonts.
 
11779
         * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format
 
11780
         * @param $font (array) font data
 
11781
         * @protected
 
11782
         * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
 
11783
         * @since 3.2.000 (2008-06-23)
 
11784
         */
 
11785
        protected function _putcidfont0($font) {
 
11786
                $cidoffset = 0;
 
11787
                if (!isset($font['cw'][1])) {
 
11788
                        $cidoffset = 31;
 
11789
                }
 
11790
                if (isset($font['cidinfo']['uni2cid'])) {
 
11791
                        // convert unicode to cid.
 
11792
                        $uni2cid = $font['cidinfo']['uni2cid'];
 
11793
                        $cw = array();
 
11794
                        foreach ($font['cw'] as $uni => $width) {
 
11795
                                if (isset($uni2cid[$uni])) {
 
11796
                                        $cw[($uni2cid[$uni] + $cidoffset)] = $width;
 
11797
                                } elseif ($uni < 256) {
 
11798
                                        $cw[$uni] = $width;
 
11799
                                } // else unknown character
 
11800
                        }
 
11801
                        $font = array_merge($font, array('cw' => $cw));
 
11802
                }
 
11803
                $name = $font['name'];
 
11804
                $enc = $font['enc'];
 
11805
                if ($enc) {
 
11806
                        $longname = $name.'-'.$enc;
 
11807
                } else {
 
11808
                        $longname = $name;
 
11809
                }
 
11810
                $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
 
11811
                $out .= '<</Type /Font';
 
11812
                $out .= ' /Subtype /Type0';
 
11813
                $out .= ' /BaseFont /'.$longname;
 
11814
                $out .= ' /Name /F'.$font['i'];
 
11815
                if ($enc) {
 
11816
                        $out .= ' /Encoding /'.$enc;
 
11817
                }
 
11818
                $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
 
11819
                $out .= ' >>';
 
11820
                $out .= "\n".'endobj';
 
11821
                $this->_out($out);
 
11822
                $oid = $this->_newobj();
 
11823
                $out = '<</Type /Font';
 
11824
                $out .= ' /Subtype /CIDFontType0';
 
11825
                $out .= ' /BaseFont /'.$name;
 
11826
                $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
 
11827
                $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
 
11828
                $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
 
11829
                $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
 
11830
                $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
 
11831
                $out .= ' /DW '.$font['dw'];
 
11832
                $out .= "\n".$this->_putfontwidths($font, $cidoffset);
 
11833
                $out .= ' >>';
 
11834
                $out .= "\n".'endobj';
 
11835
                $this->_out($out);
 
11836
                $this->_newobj();
 
11837
                $s = '<</Type /FontDescriptor /FontName /'.$name;
 
11838
                foreach ($font['desc'] as $k => $v) {
 
11839
                        if ($k != 'Style') {
 
11840
                                if (is_float($v)) {
 
11841
                                        $v = sprintf('%.3F', $v);
 
11842
                                }
 
11843
                                $s .= ' /'.$k.' '.$v.'';
 
11844
                        }
 
11845
                }
 
11846
                $s .= '>>';
 
11847
                $s .= "\n".'endobj';
 
11848
                $this->_out($s);
 
11849
        }
 
11850
 
 
11851
        /**
 
11852
         * Output images.
 
11853
         * @protected
 
11854
         */
 
11855
        protected function _putimages() {
 
11856
                $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
 
11857
                foreach ($this->imagekeys as $file) {
 
11858
                        $info = $this->getImageBuffer($file);
 
11859
                        // set object for alternate images array
 
11860
                        if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
 
11861
                                $altoid = $this->_newobj();
 
11862
                                $out = '[';
 
11863
                                foreach ($info['altimgs'] as $altimage) {
 
11864
                                        if (isset($this->xobjects['I'.$altimage[0]]['n'])) {
 
11865
                                                $out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R';
 
11866
                                                $out .= ' /DefaultForPrinting';
 
11867
                                                if ($altimage[1] === true) {
 
11868
                                                        $out .= ' true';
 
11869
                                                } else {
 
11870
                                                        $out .= ' false';
 
11871
                                                }
 
11872
                                                $out .= ' >>';
 
11873
                                        }
 
11874
                                }
 
11875
                                $out .= ' ]';
 
11876
                                $out .= "\n".'endobj';
 
11877
                                $this->_out($out);
 
11878
                        }
 
11879
                        // set image object
 
11880
                        $oid = $this->_newobj();
 
11881
                        $this->xobjects['I'.$info['i']] = array('n' => $oid);
 
11882
                        $this->setImageSubBuffer($file, 'n', $this->n);
 
11883
                        $out = '<</Type /XObject';
 
11884
                        $out .= ' /Subtype /Image';
 
11885
                        $out .= ' /Width '.$info['w'];
 
11886
                        $out .= ' /Height '.$info['h'];
 
11887
                        if (array_key_exists('masked', $info)) {
 
11888
                                $out .= ' /SMask '.($this->n - 1).' 0 R';
 
11889
                        }
 
11890
                        // set color space
 
11891
                        $icc = false;
 
11892
                        if (isset($info['icc']) AND ($info['icc'] !== false)) {
 
11893
                                // ICC Colour Space
 
11894
                                $icc = true;
 
11895
                                $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]';
 
11896
                        } elseif ($info['cs'] == 'Indexed') {
 
11897
                                // Indexed Colour Space
 
11898
                                $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
 
11899
                        } else {
 
11900
                                // Device Colour Space
 
11901
                                $out .= ' /ColorSpace /'.$info['cs'];
 
11902
                        }
 
11903
                        if ($info['cs'] == 'DeviceCMYK') {
 
11904
                                $out .= ' /Decode [1 0 1 0 1 0 1 0]';
 
11905
                        }
 
11906
                        $out .= ' /BitsPerComponent '.$info['bpc'];
 
11907
                        if (isset($altoid) AND ($altoid > 0)) {
 
11908
                                // reference to alternate images dictionary
 
11909
                                $out .= ' /Alternates '.$altoid.' 0 R';
 
11910
                        }
 
11911
                        if (isset($info['exurl']) AND !empty($info['exurl'])) {
 
11912
                                // external stream
 
11913
                                $out .= ' /Length 0';
 
11914
                                $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
 
11915
                                if (isset($info['f'])) {
 
11916
                                        $out .= ' /FFilter /'.$info['f'];
 
11917
                                }
 
11918
                                $out .= ' >>';
 
11919
                                $out .= ' stream'."\n".'endstream';
 
11920
                        } else {
 
11921
                                if (isset($info['f'])) {
 
11922
                                        $out .= ' /Filter /'.$info['f'];
 
11923
                                }
 
11924
                                if (isset($info['parms'])) {
 
11925
                                        $out .= ' '.$info['parms'];
 
11926
                                }
 
11927
                                if (isset($info['trns']) AND is_array($info['trns'])) {
 
11928
                                        $trns = '';
 
11929
                                        $count_info = count($info['trns']);
 
11930
                                        for ($i=0; $i < $count_info; ++$i) {
 
11931
                                                $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
 
11932
                                        }
 
11933
                                        $out .= ' /Mask ['.$trns.']';
 
11934
                                }
 
11935
                                $stream = $this->_getrawstream($info['data']);
 
11936
                                $out .= ' /Length '.strlen($stream).' >>';
 
11937
                                $out .= ' stream'."\n".$stream."\n".'endstream';
 
11938
                        }
 
11939
                        $out .= "\n".'endobj';
 
11940
                        $this->_out($out);
 
11941
                        if ($icc) {
 
11942
                                // ICC colour profile
 
11943
                                $this->_newobj();
 
11944
                                $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc'];
 
11945
                                $icc = $this->_getrawstream($icc);
 
11946
                                $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
 
11947
                        } elseif ($info['cs'] == 'Indexed') {
 
11948
                                // colour palette
 
11949
                                $this->_newobj();
 
11950
                                $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
 
11951
                                $pal = $this->_getrawstream($pal);
 
11952
                                $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
 
11953
                        }
 
11954
                }
 
11955
        }
 
11956
 
 
11957
        /**
 
11958
         * Output Form XObjects Templates.
 
11959
         * @author Nicola Asuni
 
11960
         * @since 5.8.017 (2010-08-24)
 
11961
         * @protected
 
11962
         * @see startTemplate(), endTemplate(), printTemplate()
 
11963
         */
 
11964
        protected function _putxobjects() {
 
11965
                foreach ($this->xobjects as $key => $data) {
 
11966
                        if (isset($data['outdata'])) {
 
11967
                                $stream = trim($data['outdata']);
 
11968
                                $out = $this->_getobj($data['n'])."\n";
 
11969
                                $out .= '<<';
 
11970
                                $out .= ' /Type /XObject';
 
11971
                                $out .= ' /Subtype /Form';
 
11972
                                $out .= ' /FormType 1';
 
11973
                                if ($this->compress) {
 
11974
                                        $stream = gzcompress($stream);
 
11975
                                        $out .= ' /Filter /FlateDecode';
 
11976
                                }
 
11977
                                $out .= sprintf(' /BBox [%.2F %.2F %.2F %.2F]', ($data['x'] * $this->k), (-$data['y'] * $this->k), (($data['w'] + $data['x']) * $this->k), (($data['h'] - $data['y']) * $this->k));
 
11978
                                $out .= ' /Matrix [1 0 0 1 0 0]';
 
11979
                                $out .= ' /Resources <<';
 
11980
                                $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
 
11981
                                if (!$this->pdfa_mode) {
 
11982
                                        // transparency
 
11983
                                        if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
 
11984
                                                $out .= ' /ExtGState <<';
 
11985
                                                foreach ($data['extgstates'] as $k => $extgstate) {
 
11986
                                                        if (isset($this->extgstates[$k]['name'])) {
 
11987
                                                                $out .= ' /'.$this->extgstates[$k]['name'];
 
11988
                                                        } else {
 
11989
                                                                $out .= ' /GS'.$k;
 
11990
                                                        }
 
11991
                                                        $out .= ' '.$this->extgstates[$k]['n'].' 0 R';
 
11992
                                                }
 
11993
                                                $out .= ' >>';
 
11994
                                        }
 
11995
                                        if (isset($data['gradients']) AND !empty($data['gradients'])) {
 
11996
                                                $gp = '';
 
11997
                                                $gs = '';
 
11998
                                                foreach ($data['gradients'] as $id => $grad) {
 
11999
                                                        // gradient patterns
 
12000
                                                        $gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R';
 
12001
                                                        // gradient shadings
 
12002
                                                        $gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R';
 
12003
                                                }
 
12004
                                                $out .= ' /Pattern <<'.$gp.' >>';
 
12005
                                                $out .= ' /Shading <<'.$gs.' >>';
 
12006
                                        }
 
12007
                                }
 
12008
                                // spot colors
 
12009
                                if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
 
12010
                                        $out .= ' /ColorSpace <<';
 
12011
                                        foreach ($data['spot_colors'] as $name => $color) {
 
12012
                                                $out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R';
 
12013
                                        }
 
12014
                                        $out .= ' >>';
 
12015
                                }
 
12016
                                // fonts
 
12017
                                if (!empty($data['fonts'])) {
 
12018
                                        $out .= ' /Font <<';
 
12019
                                        foreach ($data['fonts'] as $fontkey => $fontid) {
 
12020
                                                $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
 
12021
                                        }
 
12022
                                        $out .= ' >>';
 
12023
                                }
 
12024
                                // images or nested xobjects
 
12025
                                if (!empty($data['images']) OR !empty($data['xobjects'])) {
 
12026
                                        $out .= ' /XObject <<';
 
12027
                                        foreach ($data['images'] as $imgid) {
 
12028
                                                $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R';
 
12029
                                        }
 
12030
                                        foreach ($data['xobjects'] as $sub_id => $sub_objid) {
 
12031
                                                $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
 
12032
                                        }
 
12033
                                        $out .= ' >>';
 
12034
                                }
 
12035
                                $out .= ' >>'; //end resources
 
12036
                                if (isset($data['group']) AND ($data['group'] !== false)) {
 
12037
                                        // set transparency group
 
12038
                                        $out .= ' /Group << /Type /Group /S /Transparency';
 
12039
                                        if (is_array($data['group'])) {
 
12040
                                                if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
 
12041
                                                        $out .= ' /CS /'.$data['group']['CS'];
 
12042
                                                }
 
12043
                                                if (isset($data['group']['I'])) {
 
12044
                                                        $out .= ' /I /'.($data['group']['I']===true?'true':'false');
 
12045
                                                }
 
12046
                                                if (isset($data['group']['K'])) {
 
12047
                                                        $out .= ' /K /'.($data['group']['K']===true?'true':'false');
 
12048
                                                }
 
12049
                                        }
 
12050
                                        $out .= ' >>';
 
12051
                                }
 
12052
                                $stream = $this->_getrawstream($stream, $data['n']);
 
12053
                                $out .= ' /Length '.strlen($stream);
 
12054
                                $out .= ' >>';
 
12055
                                $out .= ' stream'."\n".$stream."\n".'endstream';
 
12056
                                $out .= "\n".'endobj';
 
12057
                                $this->_out($out);
 
12058
                        }
 
12059
                }
 
12060
        }
 
12061
 
 
12062
        /**
 
12063
         * Output Spot Colors Resources.
 
12064
         * @protected
 
12065
         * @since 4.0.024 (2008-09-12)
 
12066
         */
 
12067
        protected function _putspotcolors() {
 
12068
                foreach ($this->spot_colors as $name => $color) {
 
12069
                        $this->_newobj();
 
12070
                        $this->spot_colors[$name]['n'] = $this->n;
 
12071
                        $out = '[/Separation /'.str_replace(' ', '#20', $name);
 
12072
                        $out .= ' /DeviceCMYK <<';
 
12073
                        $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
 
12074
                        $out .= ' '.sprintf('/C1 [%.4F %.4F %.4F %.4F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100));
 
12075
                        $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
 
12076
                        $out .= "\n".'endobj';
 
12077
                        $this->_out($out);
 
12078
                }
 
12079
        }
 
12080
 
 
12081
        /**
 
12082
         * Return XObjects Dictionary.
 
12083
         * @return string XObjects dictionary
 
12084
         * @protected
 
12085
         * @since 5.8.014 (2010-08-23)
 
12086
         */
 
12087
        protected function _getxobjectdict() {
 
12088
                $out = '';
 
12089
                foreach ($this->xobjects as $id => $objid) {
 
12090
                        $out .= ' /'.$id.' '.$objid['n'].' 0 R';
 
12091
                }
 
12092
                return $out;
 
12093
        }
 
12094
 
 
12095
        /**
 
12096
         * Output Resources Dictionary.
 
12097
         * @protected
 
12098
         */
 
12099
        protected function _putresourcedict() {
 
12100
                $out = $this->_getobj(2)."\n";
 
12101
                $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
 
12102
                $out .= ' /Font <<';
 
12103
                foreach ($this->fontkeys as $fontkey) {
 
12104
                        $font = $this->getFontBuffer($fontkey);
 
12105
                        $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
 
12106
                }
 
12107
                $out .= ' >>';
 
12108
                $out .= ' /XObject <<';
 
12109
                $out .= $this->_getxobjectdict();
 
12110
                $out .= ' >>';
 
12111
                // layers
 
12112
                if (!empty($this->pdflayers)) {
 
12113
                        $out .= ' /Properties <<';
 
12114
                        foreach ($this->pdflayers as $layer) {
 
12115
                                $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
 
12116
                        }
 
12117
                        $out .= ' >>';
 
12118
                }
 
12119
                if (!$this->pdfa_mode) {
 
12120
                        // transparency
 
12121
                        if (isset($this->extgstates) AND !empty($this->extgstates)) {
 
12122
                                $out .= ' /ExtGState <<';
 
12123
                                foreach ($this->extgstates as $k => $extgstate) {
 
12124
                                        if (isset($extgstate['name'])) {
 
12125
                                                $out .= ' /'.$extgstate['name'];
 
12126
                                        } else {
 
12127
                                                $out .= ' /GS'.$k;
 
12128
                                        }
 
12129
                                        $out .= ' '.$extgstate['n'].' 0 R';
 
12130
                                }
 
12131
                                $out .= ' >>';
 
12132
                        }
 
12133
                        if (isset($this->gradients) AND !empty($this->gradients)) {
 
12134
                                $gp = '';
 
12135
                                $gs = '';
 
12136
                                foreach ($this->gradients as $id => $grad) {
 
12137
                                        // gradient patterns
 
12138
                                        $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
 
12139
                                        // gradient shadings
 
12140
                                        $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
 
12141
                                }
 
12142
                                $out .= ' /Pattern <<'.$gp.' >>';
 
12143
                                $out .= ' /Shading <<'.$gs.' >>';
 
12144
                        }
 
12145
                }
 
12146
                // spot colors
 
12147
                if (isset($this->spot_colors) AND !empty($this->spot_colors)) {
 
12148
                        $out .= ' /ColorSpace <<';
 
12149
                        foreach ($this->spot_colors as $color) {
 
12150
                                $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
 
12151
                        }
 
12152
                        $out .= ' >>';
 
12153
                }
 
12154
                $out .= ' >>';
 
12155
                $out .= "\n".'endobj';
 
12156
                $this->_out($out);
 
12157
        }
 
12158
 
 
12159
        /**
 
12160
         * Output Resources.
 
12161
         * @protected
 
12162
         */
 
12163
        protected function _putresources() {
 
12164
                $this->_putextgstates();
 
12165
                $this->_putocg();
 
12166
                $this->_putfonts();
 
12167
                $this->_putimages();
 
12168
                $this->_putspotcolors();
 
12169
                $this->_putshaders();
 
12170
                $this->_putxobjects();
 
12171
                $this->_putresourcedict();
 
12172
                $this->_putdests();
 
12173
                $this->_putbookmarks();
 
12174
                $this->_putEmbeddedFiles();
 
12175
                $this->_putannotsobjs();
 
12176
                $this->_putjavascript();
 
12177
                $this->_putencryption();
 
12178
        }
 
12179
 
 
12180
        /**
 
12181
         * Adds some Metadata information (Document Information Dictionary)
 
12182
         * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference)
 
12183
         * @return int object id
 
12184
         * @protected
 
12185
         */
 
12186
        protected function _putinfo() {
 
12187
                $oid = $this->_newobj();
 
12188
                $out = '<<';
 
12189
                // store current isunicode value
 
12190
                $prev_isunicode = $this->isunicode;
 
12191
                if ($this->docinfounicode) {
 
12192
                        $this->isunicode = true;
 
12193
                }
 
12194
                if (!$this->empty_string($this->title)) {
 
12195
                        // The document's title.
 
12196
                        $out .= ' /Title '.$this->_textstring($this->title, $oid);
 
12197
                }
 
12198
                if (!$this->empty_string($this->author)) {
 
12199
                        // The name of the person who created the document.
 
12200
                        $out .= ' /Author '.$this->_textstring($this->author, $oid);
 
12201
                }
 
12202
                if (!$this->empty_string($this->subject)) {
 
12203
                        // The subject of the document.
 
12204
                        $out .= ' /Subject '.$this->_textstring($this->subject, $oid);
 
12205
                }
 
12206
                if (!$this->empty_string($this->keywords)) {
 
12207
                        // Keywords associated with the document.
 
12208
                        $out .= ' /Keywords '.$this->_textstring($this->keywords.' TCPDF', $oid);
 
12209
                }
 
12210
                if (!$this->empty_string($this->creator)) {
 
12211
                        // 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.
 
12212
                        $out .= ' /Creator '.$this->_textstring($this->creator, $oid);
 
12213
                }
 
12214
                // restore previous isunicode value
 
12215
                $this->isunicode = $prev_isunicode;
 
12216
                // default producer
 
12217
                $out .= ' /Producer '.$this->_textstring("\x54\x43\x50\x44\x46\x20".$this->tcpdf_version."\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29", $oid);
 
12218
                // The date and time the document was created, in human-readable form
 
12219
                $out .= ' /CreationDate '.$this->_datestring();
 
12220
                // The date and time the document was most recently modified, in human-readable form
 
12221
                $out .= ' /ModDate '.$this->_datestring();
 
12222
                // A name object indicating whether the document has been modified to include trapping information
 
12223
                $out .= ' /Trapped /False';
 
12224
                $out .= ' >>';
 
12225
                $out .= "\n".'endobj';
 
12226
                $this->_out($out);
 
12227
                return $oid;
 
12228
        }
 
12229
 
 
12230
        /**
 
12231
         * Set additional XMP data to be added on the default XMP data just before the end of "x:xmpmeta" tag.
 
12232
         * IMPORTANT: This data is added as-is without controls, so you have to validate your data before using this method!
 
12233
         * @param $xmp (string) Custom XMP data.
 
12234
         * @since 5.9.128 (2011-10-06)
 
12235
         * @public
 
12236
         */
 
12237
        public function setExtraXMP($xmp) {
 
12238
                $this->custom_xmp = $xmp;
 
12239
        }
 
12240
 
 
12241
        /**
 
12242
         * Put XMP data object and return ID.
 
12243
         * @return (int) The object ID.
 
12244
         * @since 5.9.121 (2011-09-28)
 
12245
         * @protected
 
12246
         */
 
12247
        protected function _putXMP() {
 
12248
                $oid = $this->_newobj();
 
12249
                // store current isunicode value
 
12250
                $prev_isunicode = $this->isunicode;
 
12251
                $this->isunicode = true;
 
12252
                $prev_encrypted = $this->encrypted;
 
12253
                $this->encrypted = false;
 
12254
                // set XMP data
 
12255
                $xmp = '<?xpacket begin="'.$this->unichr(0xfeff).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
 
12256
                $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";
 
12257
                $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
 
12258
                $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
 
12259
                $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
 
12260
                $xmp .= "\t\t\t".'<dc:title>'."\n";
 
12261
                $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
 
12262
                $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.$this->_escapeXML($this->title).'</rdf:li>'."\n";
 
12263
                $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
 
12264
                $xmp .= "\t\t\t".'</dc:title>'."\n";
 
12265
                $xmp .= "\t\t\t".'<dc:creator>'."\n";
 
12266
                $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
 
12267
                $xmp .= "\t\t\t\t\t".'<rdf:li>'.$this->_escapeXML($this->author).'</rdf:li>'."\n";
 
12268
                $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
 
12269
                $xmp .= "\t\t\t".'</dc:creator>'."\n";
 
12270
                $xmp .= "\t\t\t".'<dc:description>'."\n";
 
12271
                $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
 
12272
                $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.$this->_escapeXML($this->subject).'</rdf:li>'."\n";
 
12273
                $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
 
12274
                $xmp .= "\t\t\t".'</dc:description>'."\n";
 
12275
                $xmp .= "\t\t\t".'<dc:subject>'."\n";
 
12276
                $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
 
12277
                $xmp .= "\t\t\t\t\t".'<rdf:li>'.$this->_escapeXML($this->keywords).'</rdf:li>'."\n";
 
12278
                $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
 
12279
                $xmp .= "\t\t\t".'</dc:subject>'."\n";
 
12280
                $xmp .= "\t\t".'</rdf:Description>'."\n";
 
12281
                // convert date format
 
12282
                $docdate = substr($this->doc_date, 0, 4).'-'.substr($this->doc_date, 4, 2).'-'.substr($this->doc_date, 6, 2);
 
12283
                $docdate .= 'T'.substr($this->doc_date, 8, 2).':'.substr($this->doc_date, 10, 2).':'.substr($this->doc_date, 12, 2);
 
12284
                $docdate .= '+'.substr($this->doc_date, 15, 2).':'.substr($this->doc_date, 18, 2);
 
12285
                $docdate = $this->_escapeXML($docdate);
 
12286
                $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
 
12287
                $xmp .= "\t\t\t".'<xmp:CreateDate>'.$docdate.'</xmp:CreateDate>'."\n";
 
12288
                $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n";
 
12289
                $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docdate.'</xmp:ModifyDate>'."\n";
 
12290
                $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$docdate.'</xmp:MetadataDate>'."\n";
 
12291
                $xmp .= "\t\t".'</rdf:Description>'."\n";
 
12292
                $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
 
12293
                $xmp .= "\t\t\t".'<pdf:Keywords>'.$this->_escapeXML($this->keywords).' TCPDF</pdf:Keywords>'."\n";
 
12294
                $xmp .= "\t\t\t".'<pdf:Producer>'.$this->_escapeXML("\x54\x43\x50\x44\x46\x20".$this->tcpdf_version."\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29").'</pdf:Producer>'."\n";
 
12295
                $xmp .= "\t\t".'</rdf:Description>'."\n";
 
12296
                $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
 
12297
                $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);
 
12298
                $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
 
12299
                $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
 
12300
                $xmp .= "\t\t".'</rdf:Description>'."\n";
 
12301
                if ($this->pdfa_mode) {
 
12302
                        $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
 
12303
                        $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n";
 
12304
                        $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
 
12305
                        $xmp .= "\t\t".'</rdf:Description>'."\n";
 
12306
                }
 
12307
                // XMP extension schemas
 
12308
                $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";
 
12309
                $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
 
12310
                $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
 
12311
                $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
 
12312
                $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
 
12313
                $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
 
12314
                $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
 
12315
                $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
 
12316
                $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
 
12317
                $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
 
12318
                $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
 
12319
                $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
 
12320
                $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
 
12321
                $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
 
12322
                $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
 
12323
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
 
12324
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
 
12325
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
 
12326
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
 
12327
                $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
 
12328
                $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
 
12329
                $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
 
12330
                $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
 
12331
                $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
 
12332
                $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
 
12333
                $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
 
12334
                $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
 
12335
                $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
 
12336
                $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
 
12337
                $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
 
12338
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
 
12339
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n";
 
12340
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
 
12341
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n";
 
12342
                $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
 
12343
                $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
 
12344
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
 
12345
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
 
12346
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n";
 
12347
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
 
12348
                $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
 
12349
                $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
 
12350
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
 
12351
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
 
12352
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
 
12353
                $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
 
12354
                $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
 
12355
                $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
 
12356
                $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
 
12357
                $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
 
12358
                $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
 
12359
                $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
 
12360
                $xmp .= "\t\t".'</rdf:Description>'."\n";
 
12361
                $xmp .= "\t".'</rdf:RDF>'."\n";
 
12362
                $xmp .= $this->custom_xmp;
 
12363
                $xmp .= '</x:xmpmeta>'."\n";
 
12364
                $xmp .= '<?xpacket end="w"?>';
 
12365
                $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
 
12366
                // restore previous isunicode value
 
12367
                $this->isunicode = $prev_isunicode;
 
12368
                $this->encrypted = $prev_encrypted;
 
12369
                $this->_out($out);
 
12370
                return $oid;
 
12371
        }
 
12372
 
 
12373
        /**
 
12374
         * Output Catalog.
 
12375
         * @return int object id
 
12376
         * @protected
 
12377
         */
 
12378
        protected function _putcatalog() {
 
12379
                // put XMP
 
12380
                $xmpobj = $this->_putXMP();
 
12381
                // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile
 
12382
                if ($this->pdfa_mode OR $this->force_srgb) {
 
12383
                        $iccobj = $this->_newobj();
 
12384
                        $icc = file_get_contents(dirname(__FILE__).'/sRGB.icc');
 
12385
                        $filter = '';
 
12386
                        if ($this->compress) {
 
12387
                                $filter = ' /Filter /FlateDecode';
 
12388
                                $icc = gzcompress($icc);
 
12389
                        }
 
12390
                        $icc = $this->_getrawstream($icc);
 
12391
                        $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
 
12392
                }
 
12393
                // start catalog
 
12394
                $oid = $this->_newobj();
 
12395
                $out = '<< /Type /Catalog';
 
12396
                $out .= ' /Version /'.$this->PDFVersion;
 
12397
                //$out .= ' /Extensions <<>>';
 
12398
                $out .= ' /Pages 1 0 R';
 
12399
                //$out .= ' /PageLabels ' //...;
 
12400
                $out .= ' /Names <<';
 
12401
                if ((!$this->pdfa_mode) AND ((!empty($this->javascript)) OR (!empty($this->js_objects)))) {
 
12402
                        $out .= ' /JavaScript '.($this->n_js).' 0 R';
 
12403
                }
 
12404
                $out .= ' >>';
 
12405
                if (!empty($this->dests)) {
 
12406
                        $out .= ' /Dests '.$this->n_dests.' 0 R';
 
12407
                }
 
12408
                $out .= $this->_putviewerpreferences();
 
12409
                if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) {
 
12410
                        $out .= ' /PageLayout /'.$this->LayoutMode;
 
12411
                }
 
12412
                if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) {
 
12413
                        $out .= ' /PageMode /'.$this->PageMode;
 
12414
                }
 
12415
                if (count($this->outlines) > 0) {
 
12416
                        $out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
 
12417
                        $out .= ' /PageMode /UseOutlines';
 
12418
                }
 
12419
                //$out .= ' /Threads []';
 
12420
                if ($this->ZoomMode == 'fullpage') {
 
12421
                        $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]';
 
12422
                } elseif ($this->ZoomMode == 'fullwidth') {
 
12423
                        $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]';
 
12424
                } elseif ($this->ZoomMode == 'real') {
 
12425
                        $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]';
 
12426
                } elseif (!is_string($this->ZoomMode)) {
 
12427
                        $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %.2F]', ($this->ZoomMode / 100));
 
12428
                }
 
12429
                //$out .= ' /AA <<>>';
 
12430
                //$out .= ' /URI <<>>';
 
12431
                $out .= ' /Metadata '.$xmpobj.' 0 R';
 
12432
                //$out .= ' /StructTreeRoot <<>>';
 
12433
                //$out .= ' /MarkInfo <<>>';
 
12434
                if (isset($this->l['a_meta_language'])) {
 
12435
                        $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid);
 
12436
                }
 
12437
                //$out .= ' /SpiderInfo <<>>';
 
12438
                // set OutputIntent to sRGB IEC61966-2.1 if required
 
12439
                if ($this->pdfa_mode OR $this->force_srgb) {
 
12440
                        $out .= ' /OutputIntents [<<';
 
12441
                        $out .= ' /Type /OutputIntent';
 
12442
                        $out .= ' /S /GTS_PDFA1';
 
12443
                        $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
 
12444
                        $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
 
12445
                        $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
 
12446
                        $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
 
12447
                        $out .= ' /DestOutputProfile '.$iccobj.' 0 R';
 
12448
                        $out .= ' >>]';
 
12449
                }
 
12450
                //$out .= ' /PieceInfo <<>>';
 
12451
                if (!empty($this->pdflayers)) {
 
12452
                        $lyrobjs = '';
 
12453
                        $lyrobjs_print = '';
 
12454
                        $lyrobjs_view = '';
 
12455
                        foreach ($this->pdflayers as $layer) {
 
12456
                                $lyrobjs .= ' '.$layer['objid'].' 0 R';
 
12457
                                if ($layer['print']) {
 
12458
                                        $lyrobjs_print .= ' '.$layer['objid'].' 0 R';
 
12459
                                }
 
12460
                                if ($layer['view']) {
 
12461
                                        $lyrobjs_view .= ' '.$layer['objid'].' 0 R';
 
12462
                                }
 
12463
                        }
 
12464
                        $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
 
12465
                        $out .= ' /D <<';
 
12466
                        $out .= ' /Name '.$this->_textstring('Layers', $oid);
 
12467
                        $out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
 
12468
                        $out .= ' /BaseState /ON';
 
12469
                        $out .= ' /ON ['.$lyrobjs_print.']';
 
12470
                        $out .= ' /OFF ['.$lyrobjs_view.']';
 
12471
                        $out .= ' /Intent /View';
 
12472
                        $out .= ' /AS [';
 
12473
                        $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
 
12474
                        $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
 
12475
                        $out .= ' ]';
 
12476
                        $out .= ' /Order ['.$lyrobjs.']';
 
12477
                        $out .= ' /ListMode /AllPages';
 
12478
                        //$out .= ' /RBGroups ['..']';
 
12479
                        //$out .= ' /Locked ['..']';
 
12480
                        $out .= ' >>';
 
12481
                        $out .= ' >>';
 
12482
                }
 
12483
                // AcroForm
 
12484
                if (!empty($this->form_obj_id) OR ($this->sign AND isset($this->signature_data['cert_type']))) {
 
12485
                        $out .= ' /AcroForm <<';
 
12486
                        $objrefs = '';
 
12487
                        if ($this->sign AND isset($this->signature_data['cert_type'])) {
 
12488
                                // set reference for signature object
 
12489
                                $objrefs .= $this->sig_obj_id.' 0 R';
 
12490
                        }
 
12491
                        if (!empty($this->empty_signature_appearance)) {
 
12492
                                foreach ($this->empty_signature_appearance as $esa) {
 
12493
                                        // set reference for empty signature objects
 
12494
                                        $objrefs .= ' '.$esa['objid'].' 0 R';
 
12495
                                }
 
12496
                        }
 
12497
                        if (!empty($this->form_obj_id)) {
 
12498
                                foreach($this->form_obj_id as $objid) {
 
12499
                                        $objrefs .= ' '.$objid.' 0 R';
 
12500
                                }
 
12501
                        }
 
12502
                        $out .= ' /Fields ['.$objrefs.']';
 
12503
                        // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
 
12504
                        $out .= ' /NeedAppearances false';
 
12505
                        if ($this->sign AND isset($this->signature_data['cert_type'])) {
 
12506
                                if ($this->signature_data['cert_type'] > 0) {
 
12507
                                        $out .= ' /SigFlags 3';
 
12508
                                } else {
 
12509
                                        $out .= ' /SigFlags 1';
 
12510
                                }
 
12511
                        }
 
12512
                        //$out .= ' /CO ';
 
12513
                        if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
 
12514
                                $out .= ' /DR <<';
 
12515
                                $out .= ' /Font <<';
 
12516
                                foreach ($this->annotation_fonts as $fontkey => $fontid) {
 
12517
                                        $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
 
12518
                                }
 
12519
                                $out .= ' >> >>';
 
12520
                        }
 
12521
                        $font = $this->getFontBuffer('helvetica');
 
12522
                        $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
 
12523
                        $out .= ' /Q '.(($this->rtl)?'2':'0');
 
12524
                        //$out .= ' /XFA ';
 
12525
                        $out .= ' >>';
 
12526
                        // signatures
 
12527
                        if ($this->sign AND isset($this->signature_data['cert_type'])) {
 
12528
                                if ($this->signature_data['cert_type'] > 0) {
 
12529
                                        $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>';
 
12530
                                } else {
 
12531
                                        $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>';
 
12532
                                }
 
12533
                        }
 
12534
                }
 
12535
                //$out .= ' /Legal <<>>';
 
12536
                //$out .= ' /Requirements []';
 
12537
                //$out .= ' /Collection <<>>';
 
12538
                //$out .= ' /NeedsRendering true';
 
12539
                $out .= ' >>';
 
12540
                $out .= "\n".'endobj';
 
12541
                $this->_out($out);
 
12542
                return $oid;
 
12543
        }
 
12544
 
 
12545
        /**
 
12546
         * Output viewer preferences.
 
12547
         * @return string for viewer preferences
 
12548
         * @author Nicola asuni
 
12549
         * @since 3.1.000 (2008-06-09)
 
12550
         * @protected
 
12551
         */
 
12552
        protected function _putviewerpreferences() {
 
12553
                $out = ' /ViewerPreferences <<';
 
12554
                if ($this->rtl) {
 
12555
                        $out .= ' /Direction /R2L';
 
12556
                } else {
 
12557
                        $out .= ' /Direction /L2R';
 
12558
                }
 
12559
                if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
 
12560
                        $out .= ' /HideToolbar true';
 
12561
                }
 
12562
                if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
 
12563
                        $out .= ' /HideMenubar true';
 
12564
                }
 
12565
                if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
 
12566
                        $out .= ' /HideWindowUI true';
 
12567
                }
 
12568
                if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
 
12569
                        $out .= ' /FitWindow true';
 
12570
                }
 
12571
                if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
 
12572
                        $out .= ' /CenterWindow true';
 
12573
                }
 
12574
                if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
 
12575
                        $out .= ' /DisplayDocTitle true';
 
12576
                }
 
12577
                if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
 
12578
                        $out .= ' /NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'];
 
12579
                }
 
12580
                if (isset($this->viewer_preferences['ViewArea'])) {
 
12581
                        $out .= ' /ViewArea /'.$this->viewer_preferences['ViewArea'];
 
12582
                }
 
12583
                if (isset($this->viewer_preferences['ViewClip'])) {
 
12584
                        $out .= ' /ViewClip /'.$this->viewer_preferences['ViewClip'];
 
12585
                }
 
12586
                if (isset($this->viewer_preferences['PrintArea'])) {
 
12587
                        $out .= ' /PrintArea /'.$this->viewer_preferences['PrintArea'];
 
12588
                }
 
12589
                if (isset($this->viewer_preferences['PrintClip'])) {
 
12590
                        $out .= ' /PrintClip /'.$this->viewer_preferences['PrintClip'];
 
12591
                }
 
12592
                if (isset($this->viewer_preferences['PrintScaling'])) {
 
12593
                        $out .= ' /PrintScaling /'.$this->viewer_preferences['PrintScaling'];
 
12594
                }
 
12595
                if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) {
 
12596
                        $out .= ' /Duplex /'.$this->viewer_preferences['Duplex'];
 
12597
                }
 
12598
                if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
 
12599
                        if ($this->viewer_preferences['PickTrayByPDFSize']) {
 
12600
                                $out .= ' /PickTrayByPDFSize true';
 
12601
                        } else {
 
12602
                                $out .= ' /PickTrayByPDFSize false';
 
12603
                        }
 
12604
                }
 
12605
                if (isset($this->viewer_preferences['PrintPageRange'])) {
 
12606
                        $PrintPageRangeNum = '';
 
12607
                        foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
 
12608
                                $PrintPageRangeNum .= ' '.($v - 1).'';
 
12609
                        }
 
12610
                        $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
 
12611
                }
 
12612
                if (isset($this->viewer_preferences['NumCopies'])) {
 
12613
                        $out .= ' /NumCopies '.intval($this->viewer_preferences['NumCopies']);
 
12614
                }
 
12615
                $out .= ' >>';
 
12616
                return $out;
 
12617
        }
 
12618
 
 
12619
        /**
 
12620
         * Output PDF File Header (7.5.2).
 
12621
         * @protected
 
12622
         */
 
12623
        protected function _putheader() {
 
12624
                $this->_out('%PDF-'.$this->PDFVersion);
 
12625
                $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
 
12626
        }
 
12627
 
 
12628
        /**
 
12629
         * Output end of document (EOF).
 
12630
         * @protected
 
12631
         */
 
12632
        protected function _enddoc() {
 
12633
                $this->state = 1;
 
12634
                $this->_putheader();
 
12635
                $this->_putpages();
 
12636
                $this->_putresources();
 
12637
                // empty signature fields
 
12638
                if (!empty($this->empty_signature_appearance)) {
 
12639
                        foreach ($this->empty_signature_appearance as $key => $esa) {
 
12640
                                // widget annotation for empty signature
 
12641
                                $out = $this->_getobj($esa['objid'])."\n";
 
12642
                                $out .= '<< /Type /Annot';
 
12643
                                $out .= ' /Subtype /Widget';
 
12644
                                $out .= ' /Rect ['.$esa['rect'].']';
 
12645
                                $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page
 
12646
                                $out .= ' /F 4';
 
12647
                                $out .= ' /FT /Sig';
 
12648
                                $signame = sprintf('Signature_%03d', ($key + 1));
 
12649
                                $out .= ' /T '.$this->_textstring($signame, $esa['objid']);
 
12650
                                $out .= ' /Ff 0';
 
12651
                                $out .= ' >>';
 
12652
                                $out .= "\n".'endobj';
 
12653
                                $this->_out($out);
 
12654
                        }
 
12655
                }
 
12656
                // Signature
 
12657
                if ($this->sign AND isset($this->signature_data['cert_type'])) {
 
12658
                        // widget annotation for signature
 
12659
                        $out = $this->_getobj($this->sig_obj_id)."\n";
 
12660
                        $out .= '<< /Type /Annot';
 
12661
                        $out .= ' /Subtype /Widget';
 
12662
                        $out .= ' /Rect ['.$this->signature_appearance['rect'].']';
 
12663
                        $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page
 
12664
                        $out .= ' /F 4';
 
12665
                        $out .= ' /FT /Sig';
 
12666
                        $out .= ' /T '.$this->_textstring('Signature_000', $this->sig_obj_id);
 
12667
                        $out .= ' /Ff 0';
 
12668
                        $out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
 
12669
                        $out .= ' >>';
 
12670
                        $out .= "\n".'endobj';
 
12671
                        $this->_out($out);
 
12672
                        // signature
 
12673
                        $this->_putsignature();
 
12674
                }
 
12675
                // Info
 
12676
                $objid_info = $this->_putinfo();
 
12677
                // Catalog
 
12678
                $objid_catalog = $this->_putcatalog();
 
12679
                // Cross-ref
 
12680
                $o = $this->bufferlen;
 
12681
                // XREF section
 
12682
                $this->_out('xref');
 
12683
                $this->_out('0 '.($this->n + 1));
 
12684
                $this->_out('0000000000 65535 f ');
 
12685
                for ($i=1; $i <= $this->n; ++$i) {
 
12686
                        if (!isset($this->offsets[$i]) AND ($i > 1)) {
 
12687
                                $this->offsets[$i] = $this->offsets[($i - 1)];
 
12688
                        }
 
12689
                        $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
 
12690
                }
 
12691
                // TRAILER
 
12692
                $out = 'trailer'."\n";
 
12693
                $out .= '<<';
 
12694
                $out .= ' /Size '.($this->n + 1);
 
12695
                $out .= ' /Root '.$objid_catalog.' 0 R';
 
12696
                $out .= ' /Info '.$objid_info.' 0 R';
 
12697
                if ($this->encrypted) {
 
12698
                        $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R';
 
12699
                }
 
12700
                $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]';
 
12701
                $out .= ' >>';
 
12702
                $this->_out($out);
 
12703
                $this->_out('startxref');
 
12704
                $this->_out($o);
 
12705
                $this->_out('%%EOF');
 
12706
                $this->state = 3; // end-of-doc
 
12707
                if ($this->diskcache) {
 
12708
                        // remove temporary files used for images
 
12709
                        foreach ($this->imagekeys as $key) {
 
12710
                                // remove temporary files
 
12711
                                unlink($this->images[$key]);
 
12712
                        }
 
12713
                        foreach ($this->fontkeys as $key) {
 
12714
                                // remove temporary files
 
12715
                                unlink($this->fonts[$key]);
 
12716
                        }
 
12717
                }
 
12718
        }
 
12719
 
 
12720
        /**
 
12721
         * Initialize a new page.
 
12722
         * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
 
12723
         * @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().
 
12724
         * @protected
 
12725
         * @see getPageSizeFromFormat(), setPageFormat()
 
12726
         */
 
12727
        protected function _beginpage($orientation='', $format='') {
 
12728
                ++$this->page;
 
12729
                $this->setPageBuffer($this->page, '');
 
12730
                // initialize array for graphics tranformation positions inside a page buffer
 
12731
                $this->transfmrk[$this->page] = array();
 
12732
                $this->state = 2;
 
12733
                if ($this->empty_string($orientation)) {
 
12734
                        if (isset($this->CurOrientation)) {
 
12735
                                $orientation = $this->CurOrientation;
 
12736
                        } elseif ($this->fwPt > $this->fhPt) {
 
12737
                                // landscape
 
12738
                                $orientation = 'L';
 
12739
                        } else {
 
12740
                                // portrait
 
12741
                                $orientation = 'P';
 
12742
                        }
 
12743
                }
 
12744
                if ($this->empty_string($format)) {
 
12745
                        $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)];
 
12746
                        $this->setPageOrientation($orientation);
 
12747
                } else {
 
12748
                        $this->setPageFormat($format, $orientation);
 
12749
                }
 
12750
                if ($this->rtl) {
 
12751
                        $this->x = $this->w - $this->rMargin;
 
12752
                } else {
 
12753
                        $this->x = $this->lMargin;
 
12754
                }
 
12755
                $this->y = $this->tMargin;
 
12756
                if (isset($this->newpagegroup[$this->page])) {
 
12757
                        // start a new group
 
12758
                        $this->currpagegroup = $this->newpagegroup[$this->page];
 
12759
                        $this->pagegroups[$this->currpagegroup] = 1;
 
12760
                } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
 
12761
                        ++$this->pagegroups[$this->currpagegroup];
 
12762
                }
 
12763
        }
 
12764
 
 
12765
        /**
 
12766
         * Mark end of page.
 
12767
         * @protected
 
12768
         */
 
12769
        protected function _endpage() {
 
12770
                $this->setVisibility('all');
 
12771
                $this->state = 1;
 
12772
        }
 
12773
 
 
12774
        /**
 
12775
         * Begin a new object and return the object number.
 
12776
         * @return int object number
 
12777
         * @protected
 
12778
         */
 
12779
        protected function _newobj() {
 
12780
                $this->_out($this->_getobj());
 
12781
                return $this->n;
 
12782
        }
 
12783
 
 
12784
        /**
 
12785
         * Return the starting object string for the selected object ID.
 
12786
         * @param $objid (int) Object ID (leave empty to get a new ID).
 
12787
         * @return string the starting object string
 
12788
         * @protected
 
12789
         * @since 5.8.009 (2010-08-20)
 
12790
         */
 
12791
        protected function _getobj($objid='') {
 
12792
                if ($objid === '') {
 
12793
                        ++$this->n;
 
12794
                        $objid = $this->n;
 
12795
                }
 
12796
                $this->offsets[$objid] = $this->bufferlen;
 
12797
                return $objid.' 0 obj';
 
12798
        }
 
12799
 
 
12800
        /**
 
12801
         * Underline text.
 
12802
         * @param $x (int) X coordinate
 
12803
         * @param $y (int) Y coordinate
 
12804
         * @param $txt (string) text to underline
 
12805
         * @protected
 
12806
         */
 
12807
        protected function _dounderline($x, $y, $txt) {
 
12808
                $w = $this->GetStringWidth($txt);
 
12809
                return $this->_dounderlinew($x, $y, $w);
 
12810
        }
 
12811
 
 
12812
        /**
 
12813
         * Underline for rectangular text area.
 
12814
         * @param $x (int) X coordinate
 
12815
         * @param $y (int) Y coordinate
 
12816
         * @param $w (int) width to underline
 
12817
         * @protected
 
12818
         * @since 4.8.008 (2009-09-29)
 
12819
         */
 
12820
        protected function _dounderlinew($x, $y, $w) {
 
12821
                $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
 
12822
                return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew);
 
12823
        }
 
12824
 
 
12825
        /**
 
12826
         * Line through text.
 
12827
         * @param $x (int) X coordinate
 
12828
         * @param $y (int) Y coordinate
 
12829
         * @param $txt (string) text to linethrough
 
12830
         * @protected
 
12831
         */
 
12832
        protected function _dolinethrough($x, $y, $txt) {
 
12833
                $w = $this->GetStringWidth($txt);
 
12834
                return $this->_dolinethroughw($x, $y, $w);
 
12835
        }
 
12836
 
 
12837
        /**
 
12838
         * Line through for rectangular text area.
 
12839
         * @param $x (int) X coordinate
 
12840
         * @param $y (int) Y coordinate
 
12841
         * @param $w (int) line length (width)
 
12842
         * @protected
 
12843
         * @since 4.9.008 (2009-09-29)
 
12844
         */
 
12845
        protected function _dolinethroughw($x, $y, $w) {
 
12846
                $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
 
12847
                return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew);
 
12848
        }
 
12849
 
 
12850
        /**
 
12851
         * Overline text.
 
12852
         * @param $x (int) X coordinate
 
12853
         * @param $y (int) Y coordinate
 
12854
         * @param $txt (string) text to overline
 
12855
         * @protected
 
12856
         * @since 4.9.015 (2010-04-19)
 
12857
         */
 
12858
        protected function _dooverline($x, $y, $txt) {
 
12859
                $w = $this->GetStringWidth($txt);
 
12860
                return $this->_dooverlinew($x, $y, $w);
 
12861
        }
 
12862
 
 
12863
        /**
 
12864
         * Overline for rectangular text area.
 
12865
         * @param $x (int) X coordinate
 
12866
         * @param $y (int) Y coordinate
 
12867
         * @param $w (int) width to overline
 
12868
         * @protected
 
12869
         * @since 4.9.015 (2010-04-19)
 
12870
         */
 
12871
        protected function _dooverlinew($x, $y, $w) {
 
12872
                $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
 
12873
                return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew);
 
12874
 
 
12875
        }
 
12876
 
 
12877
        /**
 
12878
         * Read a 4-byte (32 bit) integer from file.
 
12879
         * @param $f (string) file name.
 
12880
         * @return 4-byte integer
 
12881
         * @protected
 
12882
         */
 
12883
        protected function _freadint($f) {
 
12884
                $a = unpack('Ni', fread($f, 4));
 
12885
                return $a['i'];
 
12886
        }
 
12887
 
 
12888
        /**
 
12889
         * Add "\" before "\", "(" and ")"
 
12890
         * @param $s (string) string to escape.
 
12891
         * @return string escaped string.
 
12892
         * @protected
 
12893
         */
 
12894
        protected function _escape($s) {
 
12895
                // the chr(13) substitution fixes the Bugs item #1421290.
 
12896
                return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
 
12897
        }
 
12898
 
 
12899
        /**
 
12900
         * Format a data string for meta information
 
12901
         * @param $s (string) data string to escape.
 
12902
         * @param $n (int) object ID
 
12903
         * @return string escaped string.
 
12904
         * @protected
 
12905
         */
 
12906
        protected function _datastring($s, $n=0) {
 
12907
                if ($n == 0) {
 
12908
                        $n = $this->n;
 
12909
                }
 
12910
                $s = $this->_encrypt_data($n, $s);
 
12911
                return '('. $this->_escape($s).')';
 
12912
        }
 
12913
 
 
12914
        /**
 
12915
         * Returns a formatted date for meta information
 
12916
         * @param $n (int) object ID
 
12917
         * @return string escaped date string.
 
12918
         * @protected
 
12919
         * @since 4.6.028 (2009-08-25)
 
12920
         */
 
12921
        protected function _datestring($n=0) {
 
12922
                return $this->_datastring('D:'.$this->doc_date, $n);
 
12923
        }
 
12924
 
 
12925
        /**
 
12926
         * Format a text string for meta information
 
12927
         * @param $s (string) string to escape.
 
12928
         * @param $n (int) object ID
 
12929
         * @return string escaped string.
 
12930
         * @protected
 
12931
         */
 
12932
        protected function _textstring($s, $n=0) {
 
12933
                if ($this->isunicode) {
 
12934
                        //Convert string to UTF-16BE
 
12935
                        $s = $this->UTF8ToUTF16BE($s, true);
 
12936
                }
 
12937
                return $this->_datastring($s, $n);
 
12938
        }
 
12939
 
 
12940
        /**
 
12941
         * THIS METHOD IS DEPRECATED
 
12942
         * Format a text string
 
12943
         * @param $s (string) string to escape.
 
12944
         * @return string escaped string.
 
12945
         * @protected
 
12946
         * @deprecated
 
12947
         */
 
12948
        protected function _escapetext($s) {
 
12949
                if ($this->isunicode) {
 
12950
                        if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
 
12951
                                $s = $this->UTF8ToLatin1($s);
 
12952
                        } else {
 
12953
                                //Convert string to UTF-16BE and reverse RTL language
 
12954
                                $s = $this->utf8StrRev($s, false, $this->tmprtl);
 
12955
                        }
 
12956
                }
 
12957
                return $this->_escape($s);
 
12958
        }
 
12959
 
 
12960
        /**
 
12961
        * Escape some special characters (&lt; &gt; &amp;) for XML output.
 
12962
        * @param $str (string) Input string to convert.
 
12963
        * @return converted string
 
12964
        * @since 5.9.121 (2011-09-28)
 
12965
        * @protected
 
12966
        */
 
12967
        protected function _escapeXML($str) {
 
12968
                $replaceTable = array("\0" => '', '&' => '&amp;', '<' => '&lt;', '>' => '&gt;');
 
12969
                $str = strtr($str, $replaceTable);
 
12970
                return $str;
 
12971
        }
 
12972
 
 
12973
        /**
 
12974
         * get raw output stream.
 
12975
         * @param $s (string) string to output.
 
12976
         * @param $n (int) object reference for encryption mode
 
12977
         * @protected
 
12978
         * @author Nicola Asuni
 
12979
         * @since 5.5.000 (2010-06-22)
 
12980
         */
 
12981
        protected function _getrawstream($s, $n=0) {
 
12982
                if ($n <= 0) {
 
12983
                        // default to current object
 
12984
                        $n = $this->n;
 
12985
                }
 
12986
                return $this->_encrypt_data($n, $s);
 
12987
        }
 
12988
 
 
12989
        /**
 
12990
         * Format output stream (DEPRECATED).
 
12991
         * @param $s (string) string to output.
 
12992
         * @param $n (int) object reference for encryption mode
 
12993
         * @protected
 
12994
         * @deprecated
 
12995
         */
 
12996
        protected function _getstream($s, $n=0) {
 
12997
                return 'stream'."\n".$this->_getrawstream($s, $n)."\n".'endstream';
 
12998
        }
 
12999
 
 
13000
        /**
 
13001
         * Output a stream (DEPRECATED).
 
13002
         * @param $s (string) string to output.
 
13003
         * @param $n (int) object reference for encryption mode
 
13004
         * @protected
 
13005
         * @deprecated
 
13006
         */
 
13007
        protected function _putstream($s, $n=0) {
 
13008
                $this->_out($this->_getstream($s, $n));
 
13009
        }
 
13010
 
 
13011
        /**
 
13012
         * Output a string to the document.
 
13013
         * @param $s (string) string to output.
 
13014
         * @protected
 
13015
         */
 
13016
        protected function _out($s) {
 
13017
                if ($this->state == 2) {
 
13018
                        if ($this->inxobj) {
 
13019
                                // we are inside an XObject template
 
13020
                                $this->xobjects[$this->xobjid]['outdata'] .= $s."\n";
 
13021
                        } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
 
13022
                                // puts data before page footer
 
13023
                                $pagebuff = $this->getPageBuffer($this->page);
 
13024
                                $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
 
13025
                                $footer = substr($pagebuff, -$this->footerlen[$this->page]);
 
13026
                                $this->setPageBuffer($this->page, $page.$s."\n".$footer);
 
13027
                                // update footer position
 
13028
                                $this->footerpos[$this->page] += strlen($s."\n");
 
13029
                        } else {
 
13030
                                $this->setPageBuffer($this->page, $s."\n", true);
 
13031
                        }
 
13032
                } else {
 
13033
                        $this->setBuffer($s."\n");
 
13034
                }
 
13035
        }
 
13036
 
 
13037
        /**
 
13038
         * Converts UTF-8 strings to codepoints array.<br>
 
13039
         * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>
 
13040
         * Based on: http://www.faqs.org/rfcs/rfc3629.html
 
13041
         * <pre>
 
13042
         *    Char. number range  |        UTF-8 octet sequence
 
13043
         *       (hexadecimal)    |              (binary)
 
13044
         *    --------------------+-----------------------------------------------
 
13045
         *    0000 0000-0000 007F | 0xxxxxxx
 
13046
         *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
 
13047
         *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
 
13048
         *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
 
13049
         *    ---------------------------------------------------------------------
 
13050
         *
 
13051
         *   ABFN notation:
 
13052
         *   ---------------------------------------------------------------------
 
13053
         *   UTF8-octets = *( UTF8-char )
 
13054
         *   UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
 
13055
         *   UTF8-1      = %x00-7F
 
13056
         *   UTF8-2      = %xC2-DF UTF8-tail
 
13057
         *
 
13058
         *   UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
 
13059
         *                 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
 
13060
         *   UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
 
13061
         *                 %xF4 %x80-8F 2( UTF8-tail )
 
13062
         *   UTF8-tail   = %x80-BF
 
13063
         *   ---------------------------------------------------------------------
 
13064
         * </pre>
 
13065
         * @param $str (string) string to process.
 
13066
         * @return array containing codepoints (UTF-8 characters values)
 
13067
         * @protected
 
13068
         * @author Nicola Asuni
 
13069
         * @since 1.53.0.TC005 (2005-01-05)
 
13070
         */
 
13071
        protected function UTF8StringToArray($str) {
 
13072
                // build a unique string key
 
13073
                $strkey = md5($str);
 
13074
                if (isset($this->cache_UTF8StringToArray[$strkey])) {
 
13075
                        // return cached value
 
13076
                        $chrarray = $this->cache_UTF8StringToArray[$strkey]['s'];
 
13077
                        if (!isset($this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']])) {
 
13078
                                if ($this->isunicode) {
 
13079
                                        foreach ($chrarray as $chr) {
 
13080
                                                // store this char for font subsetting
 
13081
                                                $this->CurrentFont['subsetchars'][$chr] = true;
 
13082
                                        }
 
13083
                                        // update font subsetchars
 
13084
                                        $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
 
13085
                                }
 
13086
                                $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true;
 
13087
                        }
 
13088
                        return $chrarray;
 
13089
                }
 
13090
                // check cache size
 
13091
                if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) {
 
13092
                        // remove first element
 
13093
                        array_shift($this->cache_UTF8StringToArray);
 
13094
                }
 
13095
                // new cache array for selected string
 
13096
                $this->cache_UTF8StringToArray[$strkey] = array('s' => array(), 'f' => array());
 
13097
                ++$this->cache_size_UTF8StringToArray;
 
13098
                if (!$this->isunicode) {
 
13099
                        // split string into array of equivalent codes
 
13100
                        $strarr = array();
 
13101
                        $strlen = strlen($str);
 
13102
                        for ($i=0; $i < $strlen; ++$i) {
 
13103
                                $strarr[] = ord($str[$i]);
 
13104
                        }
 
13105
                        // insert new value on cache
 
13106
                        $this->cache_UTF8StringToArray[$strkey]['s'] = $strarr;
 
13107
                        $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true;
 
13108
                        return $strarr;
 
13109
                }
 
13110
                $unichar = -1; // last unicode char
 
13111
                $unicode = array(); // array containing unicode values
 
13112
                $bytes  = array(); // array containing single character byte sequences
 
13113
                $numbytes = 1; // number of octetc needed to represent the UTF-8 character
 
13114
                $str .= ''; // force $str to be a string
 
13115
                $length = strlen($str);
 
13116
                for ($i = 0; $i < $length; ++$i) {
 
13117
                        $char = ord($str[$i]); // get one string character at time
 
13118
                        if (count($bytes) == 0) { // get starting octect
 
13119
                                if ($char <= 0x7F) {
 
13120
                                        $unichar = $char; // use the character "as is" because is ASCII
 
13121
                                        $numbytes = 1;
 
13122
                                } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
 
13123
                                        $bytes[] = ($char - 0xC0) << 0x06;
 
13124
                                        $numbytes = 2;
 
13125
                                } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
 
13126
                                        $bytes[] = ($char - 0xE0) << 0x0C;
 
13127
                                        $numbytes = 3;
 
13128
                                } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
 
13129
                                        $bytes[] = ($char - 0xF0) << 0x12;
 
13130
                                        $numbytes = 4;
 
13131
                                } else {
 
13132
                                        // use replacement character for other invalid sequences
 
13133
                                        $unichar = 0xFFFD;
 
13134
                                        $bytes = array();
 
13135
                                        $numbytes = 1;
 
13136
                                }
 
13137
                        } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
 
13138
                                $bytes[] = $char - 0x80;
 
13139
                                if (count($bytes) == $numbytes) {
 
13140
                                        // compose UTF-8 bytes to a single unicode value
 
13141
                                        $char = $bytes[0];
 
13142
                                        for ($j = 1; $j < $numbytes; ++$j) {
 
13143
                                                $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
 
13144
                                        }
 
13145
                                        if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
 
13146
                                                /* The definition of UTF-8 prohibits encoding character numbers between
 
13147
                                                U+D800 and U+DFFF, which are reserved for use with the UTF-16
 
13148
                                                encoding form (as surrogate pairs) and do not directly represent
 
13149
                                                characters. */
 
13150
                                                $unichar = 0xFFFD; // use replacement character
 
13151
                                        } else {
 
13152
                                                $unichar = $char; // add char to array
 
13153
                                        }
 
13154
                                        // reset data for next char
 
13155
                                        $bytes = array();
 
13156
                                        $numbytes = 1;
 
13157
                                }
 
13158
                        } else {
 
13159
                                // use replacement character for other invalid sequences
 
13160
                                $unichar = 0xFFFD;
 
13161
                                $bytes = array();
 
13162
                                $numbytes = 1;
 
13163
                        }
 
13164
                        if ($unichar >= 0) {
 
13165
                                // insert unicode value into array
 
13166
                                $unicode[] = $unichar;
 
13167
                                // store this char for font subsetting
 
13168
                                $this->CurrentFont['subsetchars'][$unichar] = true;
 
13169
                                $unichar = -1;
 
13170
                        }
 
13171
                }
 
13172
                // update font subsetchars
 
13173
                $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
 
13174
                // insert new value on cache
 
13175
                $this->cache_UTF8StringToArray[$strkey]['s'] = $unicode;
 
13176
                $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true;
 
13177
                return $unicode;
 
13178
        }
 
13179
 
 
13180
        /**
 
13181
         * Converts UTF-8 strings to UTF16-BE.<br>
 
13182
         * @param $str (string) string to process.
 
13183
         * @param $setbom (boolean) if true set the Byte Order Mark (BOM = 0xFEFF)
 
13184
         * @return string
 
13185
         * @author Nicola Asuni
 
13186
         * @since 1.53.0.TC005 (2005-01-05)
 
13187
         * @see UTF8StringToArray(), arrUTF8ToUTF16BE()
 
13188
         * @protected
 
13189
         */
 
13190
        protected function UTF8ToUTF16BE($str, $setbom=false) {
 
13191
                if (!$this->isunicode) {
 
13192
                        return $str; // string is not in unicode
 
13193
                }
 
13194
                $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
 
13195
                return $this->arrUTF8ToUTF16BE($unicode, $setbom);
 
13196
        }
 
13197
 
 
13198
        /**
 
13199
         * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br>
 
13200
         * @param $str (string) string to process.
 
13201
         * @return string
 
13202
         * @author Andrew Whitehead, Nicola Asuni
 
13203
         * @protected
 
13204
         * @since 3.2.000 (2008-06-23)
 
13205
         */
 
13206
        protected function UTF8ToLatin1($str) {
 
13207
                if (!$this->isunicode) {
 
13208
                        return $str; // string is not in unicode
 
13209
                }
 
13210
                $outstr = ''; // string to be returned
 
13211
                $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
 
13212
                foreach ($unicode as $char) {
 
13213
                        if ($char < 256) {
 
13214
                                $outstr .= chr($char);
 
13215
                        } elseif (array_key_exists($char, $this->unicode->uni_utf8tolatin)) {
 
13216
                                // map from UTF-8
 
13217
                                $outstr .= chr($this->unicode->uni_utf8tolatin[$char]);
 
13218
                        } elseif ($char == 0xFFFD) {
 
13219
                                // skip
 
13220
                        } else {
 
13221
                                $outstr .= '?';
 
13222
                        }
 
13223
                }
 
13224
                return $outstr;
 
13225
        }
 
13226
 
 
13227
        /**
 
13228
         * Converts UTF-8 characters array to array of Latin1 characters<br>
 
13229
         * @param $unicode (array) array containing UTF-8 unicode values
 
13230
         * @return array
 
13231
         * @author Nicola Asuni
 
13232
         * @protected
 
13233
         * @since 4.8.023 (2010-01-15)
 
13234
         */
 
13235
        protected function UTF8ArrToLatin1($unicode) {
 
13236
                if ((!$this->isunicode) OR $this->isUnicodeFont()) {
 
13237
                        return $unicode;
 
13238
                }
 
13239
                $outarr = array(); // array to be returned
 
13240
                foreach ($unicode as $char) {
 
13241
                        if ($char < 256) {
 
13242
                                $outarr[] = $char;
 
13243
                        } elseif (array_key_exists($char, $this->unicode->uni_utf8tolatin)) {
 
13244
                                // map from UTF-8
 
13245
                                $outarr[] = $this->unicode->uni_utf8tolatin[$char];
 
13246
                        } elseif ($char == 0xFFFD) {
 
13247
                                // skip
 
13248
                        } else {
 
13249
                                $outarr[] = 63; // '?' character
 
13250
                        }
 
13251
                }
 
13252
                return $outarr;
 
13253
        }
 
13254
 
 
13255
        /**
 
13256
         * Converts array of UTF-8 characters to UTF16-BE string.<br>
 
13257
         * Based on: http://www.faqs.org/rfcs/rfc2781.html
 
13258
         * <pre>
 
13259
         *   Encoding UTF-16:
 
13260
         *
 
13261
         *   Encoding of a single character from an ISO 10646 character value to
 
13262
         *    UTF-16 proceeds as follows. Let U be the character number, no greater
 
13263
         *    than 0x10FFFF.
 
13264
         *
 
13265
         *    1) If U < 0x10000, encode U as a 16-bit unsigned integer and
 
13266
         *       terminate.
 
13267
         *
 
13268
         *    2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
 
13269
         *       U' must be less than or equal to 0xFFFFF. That is, U' can be
 
13270
         *       represented in 20 bits.
 
13271
         *
 
13272
         *    3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
 
13273
         *       0xDC00, respectively. These integers each have 10 bits free to
 
13274
         *       encode the character value, for a total of 20 bits.
 
13275
         *
 
13276
         *    4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
 
13277
         *       bits of W1 and the 10 low-order bits of U' to the 10 low-order
 
13278
         *       bits of W2. Terminate.
 
13279
         *
 
13280
         *    Graphically, steps 2 through 4 look like:
 
13281
         *    U' = yyyyyyyyyyxxxxxxxxxx
 
13282
         *    W1 = 110110yyyyyyyyyy
 
13283
         *    W2 = 110111xxxxxxxxxx
 
13284
         * </pre>
 
13285
         * @param $unicode (array) array containing UTF-8 unicode values
 
13286
         * @param $setbom (boolean) if true set the Byte Order Mark (BOM = 0xFEFF)
 
13287
         * @return string
 
13288
         * @protected
 
13289
         * @author Nicola Asuni
 
13290
         * @since 2.1.000 (2008-01-08)
 
13291
         * @see UTF8ToUTF16BE()
 
13292
         */
 
13293
        protected function arrUTF8ToUTF16BE($unicode, $setbom=false) {
 
13294
                $outstr = ''; // string to be returned
 
13295
                if ($setbom) {
 
13296
                        $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
 
13297
                }
 
13298
                foreach ($unicode as $char) {
 
13299
                        if ($char == 0x200b) {
 
13300
                                // skip Unicode Character 'ZERO WIDTH SPACE' (DEC:8203, U+200B)
 
13301
                        } elseif ($char == 0xFFFD) {
 
13302
                                $outstr .= "\xFF\xFD"; // replacement character
 
13303
                        } elseif ($char < 0x10000) {
 
13304
                                $outstr .= chr($char >> 0x08);
 
13305
                                $outstr .= chr($char & 0xFF);
 
13306
                        } else {
 
13307
                                $char -= 0x10000;
 
13308
                                $w1 = 0xD800 | ($char >> 0x0a);
 
13309
                                $w2 = 0xDC00 | ($char & 0x3FF);
 
13310
                                $outstr .= chr($w1 >> 0x08);
 
13311
                                $outstr .= chr($w1 & 0xFF);
 
13312
                                $outstr .= chr($w2 >> 0x08);
 
13313
                                $outstr .= chr($w2 & 0xFF);
 
13314
                        }
 
13315
                }
 
13316
                return $outstr;
 
13317
        }
 
13318
        // ====================================================
 
13319
 
 
13320
        /**
 
13321
         * Set header font.
 
13322
         * @param $font (array) font
 
13323
         * @public
 
13324
         * @since 1.1
 
13325
         */
 
13326
        public function setHeaderFont($font) {
 
13327
                $this->header_font = $font;
 
13328
        }
 
13329
 
 
13330
        /**
 
13331
         * Get header font.
 
13332
         * @return array()
 
13333
         * @public
 
13334
         * @since 4.0.012 (2008-07-24)
 
13335
         */
 
13336
        public function getHeaderFont() {
 
13337
                return $this->header_font;
 
13338
        }
 
13339
 
 
13340
        /**
 
13341
         * Set footer font.
 
13342
         * @param $font (array) font
 
13343
         * @public
 
13344
         * @since 1.1
 
13345
         */
 
13346
        public function setFooterFont($font) {
 
13347
                $this->footer_font = $font;
 
13348
        }
 
13349
 
 
13350
        /**
 
13351
         * Get Footer font.
 
13352
         * @return array()
 
13353
         * @public
 
13354
         * @since 4.0.012 (2008-07-24)
 
13355
         */
 
13356
        public function getFooterFont() {
 
13357
                return $this->footer_font;
 
13358
        }
 
13359
 
 
13360
        /**
 
13361
         * Set language array.
 
13362
         * @param $language (array)
 
13363
         * @public
 
13364
         * @since 1.1
 
13365
         */
 
13366
        public function setLanguageArray($language) {
 
13367
                $this->l = $language;
 
13368
                if (isset($this->l['a_meta_dir'])) {
 
13369
                        $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
 
13370
                } else {
 
13371
                        $this->rtl = false;
 
13372
                }
 
13373
        }
 
13374
 
 
13375
        /**
 
13376
         * Returns the PDF data.
 
13377
         * @public
 
13378
         */
 
13379
        public function getPDFData() {
 
13380
                if ($this->state < 3) {
 
13381
                        $this->Close();
 
13382
                }
 
13383
                return $this->buffer;
 
13384
        }
 
13385
 
 
13386
        /**
 
13387
         * Output anchor link.
 
13388
         * @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;)
 
13389
         * @param $name (string) link name
 
13390
         * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
 
13391
         * @param $firstline (boolean) if true prints only the first line and return the remaining string.
 
13392
         * @param $color (array) array of RGB text color
 
13393
         * @param $style (string) font style (U, D, B, I)
 
13394
         * @param $firstblock (boolean) if true the string is the starting of a line.
 
13395
         * @return the number of cells used or the remaining text if $firstline = true;
 
13396
         * @public
 
13397
         */
 
13398
        public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
 
13399
                if (!$this->empty_string($url) AND ($url{0} == '#')) {
 
13400
                        // convert url to internal link
 
13401
                        $lnkdata = explode(',', $url);
 
13402
                        if (isset($lnkdata[0])) {
 
13403
                                $page = intval(substr($lnkdata[0], 1));
 
13404
                                if (empty($page) OR ($page <= 0)) {
 
13405
                                        $page = $this->page;
 
13406
                                }
 
13407
                                if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
 
13408
                                        $lnky = floatval($lnkdata[1]);
 
13409
                                } else {
 
13410
                                        $lnky = 0;
 
13411
                                }
 
13412
                                $url = $this->AddLink();
 
13413
                                $this->SetLink($url, $lnky, $page);
 
13414
                        }
 
13415
                }
 
13416
                // store current settings
 
13417
                $prevcolor = $this->fgcolor;
 
13418
                $prevstyle = $this->FontStyle;
 
13419
                if (empty($color)) {
 
13420
                        $this->SetTextColorArray($this->htmlLinkColorArray);
 
13421
                } else {
 
13422
                        $this->SetTextColorArray($color);
 
13423
                }
 
13424
                if ($style == -1) {
 
13425
                        $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
 
13426
                } else {
 
13427
                        $this->SetFont('', $this->FontStyle.$style);
 
13428
                }
 
13429
                $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
 
13430
                // restore settings
 
13431
                $this->SetFont('', $prevstyle);
 
13432
                $this->SetTextColorArray($prevcolor);
 
13433
                return $ret;
 
13434
        }
 
13435
 
 
13436
        /**
 
13437
         * Returns an array (RGB or CMYK) from an html color name, or a six-digit (i.e. #3FE5AA), or three-digit (i.e. #7FF) hexadecimal color, or a javascript color array, or javascript color name.
 
13438
         * @param $hcolor (string) HTML color.
 
13439
         * @param $defcol (array) Color to return in case of error.
 
13440
         * @return array RGB or CMYK color, or false in case of error.
 
13441
         * @public
 
13442
         */
 
13443
        public function convertHTMLColorToDec($hcolor='#FFFFFF', $defcol=array('R'=>128,'G'=>128,'B'=>128)) {
 
13444
                $color = preg_replace('/[\s]*/', '', $hcolor); // remove extra spaces
 
13445
                $color = strtolower($color);
 
13446
                // check for javascript color array syntax
 
13447
                if (strpos($color, '[') !== false) {
 
13448
                        if (preg_match('/[\[][\"\'](t|g|rgb|cmyk)[\"\'][\,]?([0-9\.]*)[\,]?([0-9\.]*)[\,]?([0-9\.]*)[\,]?([0-9\.]*)[\]]/', $color, $m) > 0) {
 
13449
                                $returncolor = array();
 
13450
                                switch ($m[1]) {
 
13451
                                        case 'cmyk': {
 
13452
                                                // RGB
 
13453
                                                $returncolor['C'] = max(0, min(100, (floatval($m[2]) * 100)));
 
13454
                                                $returncolor['M'] = max(0, min(100, (floatval($m[3]) * 100)));
 
13455
                                                $returncolor['Y'] = max(0, min(100, (floatval($m[4]) * 100)));
 
13456
                                                $returncolor['K'] = max(0, min(100, (floatval($m[5]) * 100)));
 
13457
                                                break;
 
13458
                                        }
 
13459
                                        case 'rgb': {
 
13460
                                                // RGB
 
13461
                                                $returncolor['R'] = max(0, min(255, (floatval($m[2]) * 255)));
 
13462
                                                $returncolor['G'] = max(0, min(255, (floatval($m[3]) * 255)));
 
13463
                                                $returncolor['B'] = max(0, min(255, (floatval($m[4]) * 255)));
 
13464
                                                break;
 
13465
                                        }
 
13466
                                        case 'g': {
 
13467
                                                // grayscale
 
13468
                                                $returncolor['G'] = max(0, min(255, (floatval($m[2]) * 255)));
 
13469
                                                break;
 
13470
                                        }
 
13471
                                        case 't':
 
13472
                                        default: {
 
13473
                                                // transparent (empty array)
 
13474
                                                break;
 
13475
                                        }
 
13476
                                }
 
13477
                                return $returncolor;
 
13478
                        }
 
13479
                } elseif (($dotpos = strpos($color, '.')) !== false) {
 
13480
                        // remove class parent (i.e.: color.red)
 
13481
                        $color = substr($color, ($dotpos + 1));
 
13482
                        if ($color == 'transparent') {
 
13483
                                // transparent (empty array)
 
13484
                                return array();
 
13485
                        }
 
13486
                }
 
13487
                if (strlen($color) == 0) {
 
13488
                        return $defcol;
 
13489
                }
 
13490
                // RGB ARRAY
 
13491
                if (substr($color, 0, 3) == 'rgb') {
 
13492
                        $codes = substr($color, 4);
 
13493
                        $codes = str_replace(')', '', $codes);
 
13494
                        $returncolor = explode(',', $codes);
 
13495
                        foreach ($returncolor as $key => $val) {
 
13496
                                if (strpos($val, '%') > 0) {
 
13497
                                        // percentage
 
13498
                                        $returncolor[$key] = (255 * intval($val) / 100);
 
13499
                                } else {
 
13500
                                        $returncolor[$key] = intval($val);
 
13501
                                }
 
13502
                                // normalize value
 
13503
                                $returncolor[$key] = max(0, min(255, $returncolor[$key]));
 
13504
                        }
 
13505
                        return $returncolor;
 
13506
                }
 
13507
                // CMYK ARRAY
 
13508
                if (substr($color, 0, 4) == 'cmyk') {
 
13509
                        $codes = substr($color, 5);
 
13510
                        $codes = str_replace(')', '', $codes);
 
13511
                        $returncolor = explode(',', $codes);
 
13512
                        foreach ($returncolor as $key => $val) {
 
13513
                                if (strpos($val, '%') !== false) {
 
13514
                                        // percentage
 
13515
                                        $returncolor[$key] = (100 * intval($val) / 100);
 
13516
                                } else {
 
13517
                                        $returncolor[$key] = intval($val);
 
13518
                                }
 
13519
                                // normalize value
 
13520
                                $returncolor[$key] = max(0, min(100, $returncolor[$key]));
 
13521
                        }
 
13522
                        return $returncolor;
 
13523
                }
 
13524
                if ($color{0} != '#') {
 
13525
                        // COLOR NAME
 
13526
                        if (isset($this->webcolor[$color])) {
 
13527
                                // web color
 
13528
                                $color_code = $this->webcolor[$color];
 
13529
                        } else {
 
13530
                                // spot color
 
13531
                                $returncolor = $this->getSpotColor($color);
 
13532
                                if ($returncolor === false) {
 
13533
                                        $returncolor = $defcol;
 
13534
                                }
 
13535
                                return $returncolor;
 
13536
                        }
 
13537
                } else {
 
13538
                        $color_code = substr($color, 1);
 
13539
                }
 
13540
                // HEXADECIMAL REPRESENTATION
 
13541
                switch (strlen($color_code)) {
 
13542
                        case 3: {
 
13543
                                // 3-digit RGB hexadecimal representation
 
13544
                                $r = substr($color_code, 0, 1);
 
13545
                                $g = substr($color_code, 1, 1);
 
13546
                                $b = substr($color_code, 2, 1);
 
13547
                                $returncolor = array();
 
13548
                                $returncolor['R'] = max(0, min(255, hexdec($r.$r)));
 
13549
                                $returncolor['G'] = max(0, min(255, hexdec($g.$g)));
 
13550
                                $returncolor['B'] = max(0, min(255, hexdec($b.$b)));
 
13551
                                break;
 
13552
                        }
 
13553
                        case 6: {
 
13554
                                // 6-digit RGB hexadecimal representation
 
13555
                                $returncolor = array();
 
13556
                                $returncolor['R'] = max(0, min(255, hexdec(substr($color_code, 0, 2))));
 
13557
                                $returncolor['G'] = max(0, min(255, hexdec(substr($color_code, 2, 2))));
 
13558
                                $returncolor['B'] = max(0, min(255, hexdec(substr($color_code, 4, 2))));
 
13559
                                break;
 
13560
                        }
 
13561
                        case 8: {
 
13562
                                // 8-digit CMYK hexadecimal representation
 
13563
                                $returncolor = array();
 
13564
                                $returncolor['C'] = max(0, min(100, round(hexdec(substr($color_code, 0, 2)) / 2.55)));
 
13565
                                $returncolor['M'] = max(0, min(100, round(hexdec(substr($color_code, 2, 2)) / 2.55)));
 
13566
                                $returncolor['Y'] = max(0, min(100, round(hexdec(substr($color_code, 4, 2)) / 2.55)));
 
13567
                                $returncolor['K'] = max(0, min(100, round(hexdec(substr($color_code, 6, 2)) / 2.55)));
 
13568
                                break;
 
13569
                        }
 
13570
                        default: {
 
13571
                                $returncolor = $defcol;
 
13572
                                break;
 
13573
                        }
 
13574
                }
 
13575
                return $returncolor;
 
13576
        }
 
13577
 
 
13578
        /**
 
13579
         * Converts pixels to User's Units.
 
13580
         * @param $px (int) pixels
 
13581
         * @return float value in user's unit
 
13582
         * @public
 
13583
         * @see setImageScale(), getImageScale()
 
13584
         */
 
13585
        public function pixelsToUnits($px) {
 
13586
                return ($px / ($this->imgscale * $this->k));
 
13587
        }
 
13588
 
 
13589
        /**
 
13590
         * Reverse function for htmlentities.
 
13591
         * Convert entities in UTF-8.
 
13592
         * @param $text_to_convert (string) Text to convert.
 
13593
         * @return string converted text string
 
13594
         * @public
 
13595
         */
 
13596
        public function unhtmlentities($text_to_convert) {
 
13597
                return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
 
13598
        }
 
13599
 
 
13600
        // ENCRYPTION METHODS ----------------------------------
 
13601
 
 
13602
        /**
 
13603
         * Returns a string containing random data to be used as a seed for encryption methods.
 
13604
         * @param $seed (string) starting seed value
 
13605
         * @return string containing random data
 
13606
         * @author Nicola Asuni
 
13607
         * @since 5.9.006 (2010-10-19)
 
13608
         * @protected
 
13609
         */
 
13610
        protected function getRandomSeed($seed='') {
 
13611
                $seed .= microtime();
 
13612
                if (function_exists('openssl_random_pseudo_bytes') AND (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) {
 
13613
                        // this is not used on windows systems because it is very slow for a know bug
 
13614
                        $seed .= openssl_random_pseudo_bytes(512);
 
13615
                } else {
 
13616
                        for ($i = 0; $i < 23; ++$i) {
 
13617
                                $seed .= uniqid('', true);
 
13618
                        }
 
13619
                }
 
13620
                $seed .= uniqid('', true);
 
13621
                $seed .= rand();
 
13622
                $seed .= getmypid();
 
13623
                $seed .= __FILE__;
 
13624
                $seed .= $this->bufferlen;
 
13625
                if (isset($_SERVER['REMOTE_ADDR'])) {
 
13626
                        $seed .= $_SERVER['REMOTE_ADDR'];
 
13627
                }
 
13628
                if (isset($_SERVER['HTTP_USER_AGENT'])) {
 
13629
                        $seed .= $_SERVER['HTTP_USER_AGENT'];
 
13630
                }
 
13631
                if (isset($_SERVER['HTTP_ACCEPT'])) {
 
13632
                        $seed .= $_SERVER['HTTP_ACCEPT'];
 
13633
                }
 
13634
                if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
 
13635
                        $seed .= $_SERVER['HTTP_ACCEPT_ENCODING'];
 
13636
                }
 
13637
                if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
 
13638
                        $seed .= $_SERVER['HTTP_ACCEPT_LANGUAGE'];
 
13639
                }
 
13640
                if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) {
 
13641
                        $seed .= $_SERVER['HTTP_ACCEPT_CHARSET'];
 
13642
                }
 
13643
                $seed .= rand();
 
13644
                $seed .= uniqid('', true);
 
13645
                $seed .= microtime();
 
13646
                return $seed;
 
13647
        }
 
13648
 
 
13649
        /**
 
13650
         * Compute encryption key depending on object number where the encrypted data is stored.
 
13651
         * This is used for all strings and streams without crypt filter specifier.
 
13652
         * @param $n (int) object number
 
13653
         * @return int object key
 
13654
         * @protected
 
13655
         * @author Nicola Asuni
 
13656
         * @since 2.0.000 (2008-01-02)
 
13657
         */
 
13658
        protected function _objectkey($n) {
 
13659
                $objkey = $this->encryptdata['key'].pack('VXxx', $n);
 
13660
                if ($this->encryptdata['mode'] == 2) { // AES-128
 
13661
                        // AES padding
 
13662
                        $objkey .= "\x73\x41\x6C\x54"; // sAlT
 
13663
                }
 
13664
                $objkey = substr($this->_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5));
 
13665
                $objkey = substr($objkey, 0, 16);
 
13666
                return $objkey;
 
13667
        }
 
13668
 
 
13669
        /**
 
13670
         * Encrypt the input string.
 
13671
         * @param $n (int) object number
 
13672
         * @param $s (string) data string to encrypt
 
13673
         * @return encrypted string
 
13674
         * @protected
 
13675
         * @author Nicola Asuni
 
13676
         * @since 5.0.005 (2010-05-11)
 
13677
         */
 
13678
        protected function _encrypt_data($n, $s) {
 
13679
                if (!$this->encrypted) {
 
13680
                        return $s;
 
13681
                }
 
13682
                switch ($this->encryptdata['mode']) {
 
13683
                        case 0:   // RC4-40
 
13684
                        case 1: { // RC4-128
 
13685
                                $s = $this->_RC4($this->_objectkey($n), $s);
 
13686
                                break;
 
13687
                        }
 
13688
                        case 2: { // AES-128
 
13689
                                $s = $this->_AES($this->_objectkey($n), $s);
 
13690
                                break;
 
13691
                        }
 
13692
                        case 3: { // AES-256
 
13693
                                $s = $this->_AES($this->encryptdata['key'], $s);
 
13694
                                break;
 
13695
                        }
 
13696
                }
 
13697
                return $s;
 
13698
        }
 
13699
 
 
13700
        /**
 
13701
         * Put encryption on PDF document.
 
13702
         * @protected
 
13703
         * @author Nicola Asuni
 
13704
         * @since 2.0.000 (2008-01-02)
 
13705
         */
 
13706
        protected function _putencryption() {
 
13707
                if (!$this->encrypted) {
 
13708
                        return;
 
13709
                }
 
13710
                $this->encryptdata['objid'] = $this->_newobj();
 
13711
                $out = '<<';
 
13712
                if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) {
 
13713
                        $this->encryptdata['Filter'] = 'Standard';
 
13714
                }
 
13715
                $out .= ' /Filter /'.$this->encryptdata['Filter'];
 
13716
                if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) {
 
13717
                        $out .= ' /SubFilter /'.$this->encryptdata['SubFilter'];
 
13718
                }
 
13719
                if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) {
 
13720
                        $this->encryptdata['V'] = 1;
 
13721
                }
 
13722
                // V is a code specifying the algorithm to be used in encrypting and decrypting the document
 
13723
                $out .= ' /V '.$this->encryptdata['V'];
 
13724
                if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) {
 
13725
                        // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
 
13726
                        $out .= ' /Length '.$this->encryptdata['Length'];
 
13727
                } else {
 
13728
                        $out .= ' /Length 40';
 
13729
                }
 
13730
                if ($this->encryptdata['V'] >= 4) {
 
13731
                        if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) {
 
13732
                                $this->encryptdata['StmF'] = 'Identity';
 
13733
                        }
 
13734
                        if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) {
 
13735
                                // The name of the crypt filter that shall be used when decrypting all strings in the document.
 
13736
                                $this->encryptdata['StrF'] = 'Identity';
 
13737
                        }
 
13738
                        // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
 
13739
                        if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) {
 
13740
                                $out .= ' /CF <<';
 
13741
                                $out .= ' /'.$this->encryptdata['StmF'].' <<';
 
13742
                                $out .= ' /Type /CryptFilter';
 
13743
                                if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) {
 
13744
                                        // The method used
 
13745
                                        $out .= ' /CFM /'.$this->encryptdata['CF']['CFM'];
 
13746
                                        if ($this->encryptdata['pubkey']) {
 
13747
                                                $out .= ' /Recipients [';
 
13748
                                                foreach ($this->encryptdata['Recipients'] as $rec) {
 
13749
                                                        $out .= ' <'.$rec.'>';
 
13750
                                                }
 
13751
                                                $out .= ' ]';
 
13752
                                                if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) {
 
13753
                                                        $out .= ' /EncryptMetadata false';
 
13754
                                                } else {
 
13755
                                                        $out .= ' /EncryptMetadata true';
 
13756
                                                }
 
13757
                                        }
 
13758
                                } else {
 
13759
                                        $out .= ' /CFM /None';
 
13760
                                }
 
13761
                                if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) {
 
13762
                                        // The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
 
13763
                                        $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent'];
 
13764
                                } else {
 
13765
                                        $out .= ' /AuthEvent /DocOpen';
 
13766
                                }
 
13767
                                if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) {
 
13768
                                        // The bit length of the encryption key.
 
13769
                                        $out .= ' /Length '.$this->encryptdata['CF']['Length'];
 
13770
                                }
 
13771
                                $out .= ' >> >>';
 
13772
                        }
 
13773
                        // The name of the crypt filter that shall be used by default when decrypting streams.
 
13774
                        $out .= ' /StmF /'.$this->encryptdata['StmF'];
 
13775
                        // The name of the crypt filter that shall be used when decrypting all strings in the document.
 
13776
                        $out .= ' /StrF /'.$this->encryptdata['StrF'];
 
13777
                        if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) {
 
13778
                                // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
 
13779
                                $out .= ' /EFF /'.$this->encryptdata[''];
 
13780
                        }
 
13781
                }
 
13782
                // Additional encryption dictionary entries for the standard security handler
 
13783
                if ($this->encryptdata['pubkey']) {
 
13784
                        if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) {
 
13785
                                $out .= ' /Recipients [';
 
13786
                                foreach ($this->encryptdata['Recipients'] as $rec) {
 
13787
                                        $out .= ' <'.$rec.'>';
 
13788
                                }
 
13789
                                $out .= ' ]';
 
13790
                        }
 
13791
                } else {
 
13792
                        $out .= ' /R';
 
13793
                        if ($this->encryptdata['V'] == 5) { // AES-256
 
13794
                                $out .= ' 5';
 
13795
                                $out .= ' /OE ('.$this->_escape($this->encryptdata['OE']).')';
 
13796
                                $out .= ' /UE ('.$this->_escape($this->encryptdata['UE']).')';
 
13797
                                $out .= ' /Perms ('.$this->_escape($this->encryptdata['perms']).')';
 
13798
                        } elseif ($this->encryptdata['V'] == 4) { // AES-128
 
13799
                                $out .= ' 4';
 
13800
                        } elseif ($this->encryptdata['V'] < 2) { // RC-40
 
13801
                                $out .= ' 2';
 
13802
                        } else { // RC-128
 
13803
                                $out .= ' 3';
 
13804
                        }
 
13805
                        $out .= ' /O ('.$this->_escape($this->encryptdata['O']).')';
 
13806
                        $out .= ' /U ('.$this->_escape($this->encryptdata['U']).')';
 
13807
                        $out .= ' /P '.$this->encryptdata['P'];
 
13808
                        if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) {
 
13809
                                $out .= ' /EncryptMetadata false';
 
13810
                        } else {
 
13811
                                $out .= ' /EncryptMetadata true';
 
13812
                        }
 
13813
                }
 
13814
                $out .= ' >>';
 
13815
                $out .= "\n".'endobj';
 
13816
                $this->_out($out);
 
13817
        }
 
13818
 
 
13819
        /**
 
13820
         * Returns the input text encrypted using RC4 algorithm and the specified key.
 
13821
         * RC4 is the standard encryption algorithm used in PDF format
 
13822
         * @param $key (string) encryption key
 
13823
         * @param $text (String) input text to be encrypted
 
13824
         * @return String encrypted text
 
13825
         * @protected
 
13826
         * @since 2.0.000 (2008-01-02)
 
13827
         * @author Klemen Vodopivec, Nicola Asuni
 
13828
         */
 
13829
        protected function _RC4($key, $text) {
 
13830
                if (function_exists('mcrypt_decrypt') AND ($out = @mcrypt_decrypt(MCRYPT_ARCFOUR, $key, $text, MCRYPT_MODE_STREAM, ''))) {
 
13831
                        // try to use mcrypt function if exist
 
13832
                        return $out;
 
13833
                }
 
13834
                if ($this->last_enc_key != $key) {
 
13835
                        $k = str_repeat($key, ((256 / strlen($key)) + 1));
 
13836
                        $rc4 = range(0, 255);
 
13837
                        $j = 0;
 
13838
                        for ($i = 0; $i < 256; ++$i) {
 
13839
                                $t = $rc4[$i];
 
13840
                                $j = ($j + $t + ord($k[$i])) % 256;
 
13841
                                $rc4[$i] = $rc4[$j];
 
13842
                                $rc4[$j] = $t;
 
13843
                        }
 
13844
                        $this->last_enc_key = $key;
 
13845
                        $this->last_enc_key_c = $rc4;
 
13846
                } else {
 
13847
                        $rc4 = $this->last_enc_key_c;
 
13848
                }
 
13849
                $len = strlen($text);
 
13850
                $a = 0;
 
13851
                $b = 0;
 
13852
                $out = '';
 
13853
                for ($i = 0; $i < $len; ++$i) {
 
13854
                        $a = ($a + 1) % 256;
 
13855
                        $t = $rc4[$a];
 
13856
                        $b = ($b + $t) % 256;
 
13857
                        $rc4[$a] = $rc4[$b];
 
13858
                        $rc4[$b] = $t;
 
13859
                        $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
 
13860
                        $out .= chr(ord($text[$i]) ^ $k);
 
13861
                }
 
13862
                return $out;
 
13863
        }
 
13864
 
 
13865
        /**
 
13866
         * Returns the input text exrypted using AES algorithm and the specified key.
 
13867
         * This method requires mcrypt.
 
13868
         * @param $key (string) encryption key
 
13869
         * @param $text (String) input text to be encrypted
 
13870
         * @return String encrypted text
 
13871
         * @protected
 
13872
         * @author Nicola Asuni
 
13873
         * @since 5.0.005 (2010-05-11)
 
13874
         */
 
13875
        protected function _AES($key, $text) {
 
13876
                // padding (RFC 2898, PKCS #5: Password-Based Cryptography Specification Version 2.0)
 
13877
                $padding = 16 - (strlen($text) % 16);
 
13878
                $text .= str_repeat(chr($padding), $padding);
 
13879
                $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
 
13880
                $text = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv);
 
13881
                $text = $iv.$text;
 
13882
                return $text;
 
13883
        }
 
13884
 
 
13885
        /**
 
13886
         * Encrypts a string using MD5 and returns it's value as a binary string.
 
13887
         * @param $str (string) input string
 
13888
         * @return String MD5 encrypted binary string
 
13889
         * @protected
 
13890
         * @since 2.0.000 (2008-01-02)
 
13891
         * @author Klemen Vodopivec
 
13892
         */
 
13893
        protected function _md5_16($str) {
 
13894
                return pack('H*', md5($str));
 
13895
        }
 
13896
 
 
13897
        /**
 
13898
         * Compute U value (used for encryption)
 
13899
         * @return string U value
 
13900
         * @protected
 
13901
         * @since 2.0.000 (2008-01-02)
 
13902
         * @author Nicola Asuni
 
13903
         */
 
13904
        protected function _Uvalue() {
 
13905
                if ($this->encryptdata['mode'] == 0) { // RC4-40
 
13906
                        return $this->_RC4($this->encryptdata['key'], $this->enc_padding);
 
13907
                } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
 
13908
                        $tmp = $this->_md5_16($this->enc_padding.$this->encryptdata['fileid']);
 
13909
                        $enc = $this->_RC4($this->encryptdata['key'], $tmp);
 
13910
                        $len = strlen($tmp);
 
13911
                        for ($i = 1; $i <= 19; ++$i) {
 
13912
                                $ek = '';
 
13913
                                for ($j = 0; $j < $len; ++$j) {
 
13914
                                        $ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i);
 
13915
                                }
 
13916
                                $enc = $this->_RC4($ek, $enc);
 
13917
                        }
 
13918
                        $enc .= str_repeat("\x00", 16);
 
13919
                        return substr($enc, 0, 32);
 
13920
                } elseif ($this->encryptdata['mode'] == 3) { // AES-256
 
13921
                        $seed = $this->_md5_16($this->getRandomSeed());
 
13922
                        // User Validation Salt
 
13923
                        $this->encryptdata['UVS'] = substr($seed, 0, 8);
 
13924
                        // User Key Salt
 
13925
                        $this->encryptdata['UKS'] = substr($seed, 8, 16);
 
13926
                        return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS'];
 
13927
                }
 
13928
        }
 
13929
 
 
13930
        /**
 
13931
         * Compute UE value (used for encryption)
 
13932
         * @return string UE value
 
13933
         * @protected
 
13934
         * @since 5.9.006 (2010-10-19)
 
13935
         * @author Nicola Asuni
 
13936
         */
 
13937
        protected function _UEvalue() {
 
13938
                $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true);
 
13939
                $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
 
13940
                return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
 
13941
        }
 
13942
 
 
13943
        /**
 
13944
         * Compute O value (used for encryption)
 
13945
         * @return string O value
 
13946
         * @protected
 
13947
         * @since 2.0.000 (2008-01-02)
 
13948
         * @author Nicola Asuni
 
13949
         */
 
13950
        protected function _Ovalue() {
 
13951
                if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
 
13952
                        $tmp = $this->_md5_16($this->encryptdata['owner_password']);
 
13953
                        if ($this->encryptdata['mode'] > 0) {
 
13954
                                for ($i = 0; $i < 50; ++$i) {
 
13955
                                        $tmp = $this->_md5_16($tmp);
 
13956
                                }
 
13957
                        }
 
13958
                        $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
 
13959
                        $enc = $this->_RC4($owner_key, $this->encryptdata['user_password']);
 
13960
                        if ($this->encryptdata['mode'] > 0) {
 
13961
                                $len = strlen($owner_key);
 
13962
                                for ($i = 1; $i <= 19; ++$i) {
 
13963
                                        $ek = '';
 
13964
                                        for ($j = 0; $j < $len; ++$j) {
 
13965
                                                $ek .= chr(ord($owner_key[$j]) ^ $i);
 
13966
                                        }
 
13967
                                        $enc = $this->_RC4($ek, $enc);
 
13968
                                }
 
13969
                        }
 
13970
                        return $enc;
 
13971
                } elseif ($this->encryptdata['mode'] == 3) { // AES-256
 
13972
                        $seed = $this->_md5_16($this->getRandomSeed());
 
13973
                        // Owner Validation Salt
 
13974
                        $this->encryptdata['OVS'] = substr($seed, 0, 8);
 
13975
                        // Owner Key Salt
 
13976
                        $this->encryptdata['OKS'] = substr($seed, 8, 16);
 
13977
                        return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS'];
 
13978
                }
 
13979
        }
 
13980
 
 
13981
        /**
 
13982
         * Compute OE value (used for encryption)
 
13983
         * @return string OE value
 
13984
         * @protected
 
13985
         * @since 5.9.006 (2010-10-19)
 
13986
         * @author Nicola Asuni
 
13987
         */
 
13988
        protected function _OEvalue() {
 
13989
                $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true);
 
13990
                $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
 
13991
                return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
 
13992
        }
 
13993
 
 
13994
        /**
 
13995
         * Convert password for AES-256 encryption mode
 
13996
         * @param $password (string) password
 
13997
         * @return string password
 
13998
         * @protected
 
13999
         * @since 5.9.006 (2010-10-19)
 
14000
         * @author Nicola Asuni
 
14001
         */
 
14002
        protected function _fixAES256Password($password) {
 
14003
                $psw = ''; // password to be returned
 
14004
                $psw_array = $this->utf8Bidi($this->UTF8StringToArray($password), $password, $this->rtl);
 
14005
                foreach ($psw_array as $c) {
 
14006
                        $psw .= $this->unichr($c);
 
14007
                }
 
14008
                return substr($psw, 0, 127);
 
14009
        }
 
14010
 
 
14011
        /**
 
14012
         * Compute encryption key
 
14013
         * @protected
 
14014
         * @since 2.0.000 (2008-01-02)
 
14015
         * @author Nicola Asuni
 
14016
         */
 
14017
        protected function _generateencryptionkey() {
 
14018
                $keybytelen = ($this->encryptdata['Length'] / 8);
 
14019
                if (!$this->encryptdata['pubkey']) { // standard mode
 
14020
                        if ($this->encryptdata['mode'] == 3) { // AES-256
 
14021
                                // generate 256 bit random key
 
14022
                                $this->encryptdata['key'] = substr(hash('sha256', $this->getRandomSeed(), true), 0, $keybytelen);
 
14023
                                // truncate passwords
 
14024
                                $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']);
 
14025
                                $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']);
 
14026
                                // Compute U value
 
14027
                                $this->encryptdata['U'] = $this->_Uvalue();
 
14028
                                // Compute UE value
 
14029
                                $this->encryptdata['UE'] = $this->_UEvalue();
 
14030
                                // Compute O value
 
14031
                                $this->encryptdata['O'] = $this->_Ovalue();
 
14032
                                // Compute OE value
 
14033
                                $this->encryptdata['OE'] = $this->_OEvalue();
 
14034
                                // Compute P value
 
14035
                                $this->encryptdata['P'] = $this->encryptdata['protection'];
 
14036
                                // Computing the encryption dictionary's Perms (permissions) value
 
14037
                                $perms = $this->getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
 
14038
                                $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
 
14039
                                if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8
 
14040
                                        $perms .= 'F';
 
14041
                                } else {
 
14042
                                        $perms .= 'T';
 
14043
                                }
 
14044
                                $perms .= 'adb'; // bytes 9-11
 
14045
                                $perms .= 'nick'; // bytes 12-15
 
14046
                                $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB));
 
14047
                                $this->encryptdata['perms'] = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->encryptdata['key'], $perms, MCRYPT_MODE_ECB, $iv);
 
14048
                        } else { // RC4-40, RC4-128, AES-128
 
14049
                                // Pad passwords
 
14050
                                $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].$this->enc_padding, 0, 32);
 
14051
                                $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].$this->enc_padding, 0, 32);
 
14052
                                // Compute O value
 
14053
                                $this->encryptdata['O'] = $this->_Ovalue();
 
14054
                                // get default permissions (reverse byte order)
 
14055
                                $permissions = $this->getEncPermissionsString($this->encryptdata['protection']);
 
14056
                                // Compute encryption key
 
14057
                                $tmp = $this->_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']);
 
14058
                                if ($this->encryptdata['mode'] > 0) {
 
14059
                                        for ($i = 0; $i < 50; ++$i) {
 
14060
                                                $tmp = $this->_md5_16(substr($tmp, 0, $keybytelen));
 
14061
                                        }
 
14062
                                }
 
14063
                                $this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
 
14064
                                // Compute U value
 
14065
                                $this->encryptdata['U'] = $this->_Uvalue();
 
14066
                                // Compute P value
 
14067
                                $this->encryptdata['P'] = $this->encryptdata['protection'];
 
14068
                        }
 
14069
                } else { // Public-Key mode
 
14070
                        // random 20-byte seed
 
14071
                        $seed = sha1($this->getRandomSeed(), true);
 
14072
                        $recipient_bytes = '';
 
14073
                        foreach ($this->encryptdata['pubkeys'] as $pubkey) {
 
14074
                                // for each public certificate
 
14075
                                if (isset($pubkey['p'])) {
 
14076
                                        $pkprotection = $this->getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
 
14077
                                } else {
 
14078
                                        $pkprotection = $this->encryptdata['protection'];
 
14079
                                }
 
14080
                                // get default permissions (reverse byte order)
 
14081
                                $pkpermissions = $this->getEncPermissionsString($pkprotection);
 
14082
                                // envelope data
 
14083
                                $envelope = $seed.$pkpermissions;
 
14084
                                // write the envelope data to a temporary file
 
14085
                                $tempkeyfile = tempnam(K_PATH_CACHE, 'tmpkey_');
 
14086
                                $f = fopen($tempkeyfile, 'wb');
 
14087
                                if (!$f) {
 
14088
                                        $this->Error('Unable to create temporary key file: '.$tempkeyfile);
 
14089
                                }
 
14090
                                $envelope_length = strlen($envelope);
 
14091
                                fwrite($f, $envelope, $envelope_length);
 
14092
                                fclose($f);
 
14093
                                $tempencfile = tempnam(K_PATH_CACHE, 'tmpenc_');
 
14094
                                if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) {
 
14095
                                        $this->Error('Unable to encrypt the file: '.$tempkeyfile);
 
14096
                                }
 
14097
                                unlink($tempkeyfile);
 
14098
                                // read encryption signature
 
14099
                                $signature = file_get_contents($tempencfile, false, null, $envelope_length);
 
14100
                                unlink($tempencfile);
 
14101
                                // extract signature
 
14102
                                $signature = substr($signature, strpos($signature, 'Content-Disposition'));
 
14103
                                $tmparr = explode("\n\n", $signature);
 
14104
                                $signature = trim($tmparr[1]);
 
14105
                                unset($tmparr);
 
14106
                                // decode signature
 
14107
                                $signature = base64_decode($signature);
 
14108
                                // convert signature to hex
 
14109
                                $hexsignature = current(unpack('H*', $signature));
 
14110
                                // store signature on recipients array
 
14111
                                $this->encryptdata['Recipients'][] = $hexsignature;
 
14112
                                // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
 
14113
                                $recipient_bytes .= $signature;
 
14114
                        }
 
14115
                        // calculate encryption key
 
14116
                        if ($this->encryptdata['mode'] == 3) { // AES-256
 
14117
                                $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
 
14118
                        } else { // RC4-40, RC4-128, AES-128
 
14119
                                $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
 
14120
                        }
 
14121
                }
 
14122
        }
 
14123
 
 
14124
        /**
 
14125
         * Return the premission code used on encryption (P value).
 
14126
         * @param $permissions (Array) the set of permissions (specify the ones you want to block).
 
14127
         * @param $mode (int) encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit.
 
14128
         * @protected
 
14129
         * @since 5.0.005 (2010-05-12)
 
14130
         * @author Nicola Asuni
 
14131
         */
 
14132
        protected function getUserPermissionCode($permissions, $mode=0) {
 
14133
                $options = array(
 
14134
                        'owner' => 2, // bit 2 -- inverted logic: cleared by default
 
14135
                        'print' => 4, // bit 3
 
14136
                        'modify' => 8, // bit 4
 
14137
                        'copy' => 16, // bit 5
 
14138
                        'annot-forms' => 32, // bit 6
 
14139
                        'fill-forms' => 256, // bit 9
 
14140
                        'extract' => 512, // bit 10
 
14141
                        'assemble' => 1024,// bit 11
 
14142
                        'print-high' => 2048 // bit 12
 
14143
                        );
 
14144
                $protection = 2147422012; // 32 bit: (01111111 11111111 00001111 00111100)
 
14145
                foreach ($permissions as $permission) {
 
14146
                        if (!isset($options[$permission])) {
 
14147
                                $this->Error('Incorrect permission: '.$permission);
 
14148
                        }
 
14149
                        if (($mode > 0) OR ($options[$permission] <= 32)) {
 
14150
                                // set only valid permissions
 
14151
                                if ($options[$permission] == 2) {
 
14152
                                        // the logic for bit 2 is inverted (cleared by default)
 
14153
                                        $protection += $options[$permission];
 
14154
                                } else {
 
14155
                                        $protection -= $options[$permission];
 
14156
                                }
 
14157
                        }
 
14158
                }
 
14159
                return $protection;
 
14160
        }
 
14161
 
 
14162
        /**
 
14163
         * Set document protection
 
14164
         * Remark: the protection against modification is for people who have the full Acrobat product.
 
14165
         * 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.
 
14166
         * 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.
 
14167
         * @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>
 
14168
         * @param $user_pass (String) user password. Empty by default.
 
14169
         * @param $owner_pass (String) owner password. If not specified, a random value is used.
 
14170
         * @param $mode (int) encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit.
 
14171
         * @param $pubkeys (String) array of recipients containing public-key certificates ('c') and permissions ('p'). For example: array(array('c' => 'file://../tcpdf.crt', 'p' => array('print')))
 
14172
         * @public
 
14173
         * @since 2.0.000 (2008-01-02)
 
14174
         * @author Nicola Asuni
 
14175
         */
 
14176
        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) {
 
14177
                if ($this->pdfa_mode) {
 
14178
                        // encryption is not allowed in PDF/A mode
 
14179
                        return;
 
14180
                }
 
14181
                $this->encryptdata['protection'] = $this->getUserPermissionCode($permissions, $mode);
 
14182
                if (($pubkeys !== null) AND (is_array($pubkeys))) {
 
14183
                        // public-key mode
 
14184
                        $this->encryptdata['pubkeys'] = $pubkeys;
 
14185
                        if ($mode == 0) {
 
14186
                                // public-Key Security requires at least 128 bit
 
14187
                                $mode = 1;
 
14188
                        }
 
14189
                        if (!function_exists('openssl_pkcs7_encrypt')) {
 
14190
                                $this->Error('Public-Key Security requires openssl library.');
 
14191
                        }
 
14192
                        // Set Public-Key filter (availabe are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
 
14193
                        $this->encryptdata['pubkey'] = true;
 
14194
                        $this->encryptdata['Filter'] = 'Adobe.PubSec';
 
14195
                        $this->encryptdata['StmF'] = 'DefaultCryptFilter';
 
14196
                        $this->encryptdata['StrF'] = 'DefaultCryptFilter';
 
14197
                } else {
 
14198
                        // standard mode (password mode)
 
14199
                        $this->encryptdata['pubkey'] = false;
 
14200
                        $this->encryptdata['Filter'] = 'Standard';
 
14201
                        $this->encryptdata['StmF'] = 'StdCF';
 
14202
                        $this->encryptdata['StrF'] = 'StdCF';
 
14203
                }
 
14204
                if ($mode > 1) { // AES
 
14205
                        if (!extension_loaded('mcrypt')) {
 
14206
                                $this->Error('AES encryption requires mcrypt library (http://www.php.net/manual/en/mcrypt.requirements.php).');
 
14207
                        }
 
14208
                        if (mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) {
 
14209
                                $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
 
14210
                        }
 
14211
                        if (($mode == 3) AND !function_exists('hash')) {
 
14212
                                // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
 
14213
                                $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
 
14214
                        }
 
14215
                }
 
14216
                if ($owner_pass === null) {
 
14217
                        $owner_pass = md5($this->getRandomSeed());
 
14218
                }
 
14219
                $this->encryptdata['user_password'] = $user_pass;
 
14220
                $this->encryptdata['owner_password'] = $owner_pass;
 
14221
                $this->encryptdata['mode'] = $mode;
 
14222
                switch ($mode) {
 
14223
                        case 0: { // RC4 40 bit
 
14224
                                $this->encryptdata['V'] = 1;
 
14225
                                $this->encryptdata['Length'] = 40;
 
14226
                                $this->encryptdata['CF']['CFM'] = 'V2';
 
14227
                                break;
 
14228
                        }
 
14229
                        case 1: { // RC4 128 bit
 
14230
                                $this->encryptdata['V'] = 2;
 
14231
                                $this->encryptdata['Length'] = 128;
 
14232
                                $this->encryptdata['CF']['CFM'] = 'V2';
 
14233
                                if ($this->encryptdata['pubkey']) {
 
14234
                                        $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4';
 
14235
                                        $this->encryptdata['Recipients'] = array();
 
14236
                                }
 
14237
                                break;
 
14238
                        }
 
14239
                        case 2: { // AES 128 bit
 
14240
                                $this->encryptdata['V'] = 4;
 
14241
                                $this->encryptdata['Length'] = 128;
 
14242
                                $this->encryptdata['CF']['CFM'] = 'AESV2';
 
14243
                                $this->encryptdata['CF']['Length'] = 128;
 
14244
                                if ($this->encryptdata['pubkey']) {
 
14245
                                        $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
 
14246
                                        $this->encryptdata['Recipients'] = array();
 
14247
                                }
 
14248
                                break;
 
14249
                        }
 
14250
                        case 3: { // AES 256 bit
 
14251
                                $this->encryptdata['V'] = 5;
 
14252
                                $this->encryptdata['Length'] = 256;
 
14253
                                $this->encryptdata['CF']['CFM'] = 'AESV3';
 
14254
                                $this->encryptdata['CF']['Length'] = 256;
 
14255
                                if ($this->encryptdata['pubkey']) {
 
14256
                                        $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
 
14257
                                        $this->encryptdata['Recipients'] = array();
 
14258
                                }
 
14259
                                break;
 
14260
                        }
 
14261
                }
 
14262
                $this->encrypted = true;
 
14263
                $this->encryptdata['fileid'] = $this->convertHexStringToString($this->file_id);
 
14264
                $this->_generateencryptionkey();
 
14265
        }
 
14266
 
 
14267
        /**
 
14268
         * Convert hexadecimal string to string
 
14269
         * @param $bs (string) byte-string to convert
 
14270
         * @return String
 
14271
         * @protected
 
14272
         * @since 5.0.005 (2010-05-12)
 
14273
         * @author Nicola Asuni
 
14274
         */
 
14275
        protected function convertHexStringToString($bs) {
 
14276
                $string = ''; // string to be returned
 
14277
                $bslength = strlen($bs);
 
14278
                if (($bslength % 2) != 0) {
 
14279
                        // padding
 
14280
                        $bs .= '0';
 
14281
                        ++$bslength;
 
14282
                }
 
14283
                for ($i = 0; $i < $bslength; $i += 2) {
 
14284
                        $string .= chr(hexdec($bs[$i].$bs[($i + 1)]));
 
14285
                }
 
14286
                return $string;
 
14287
        }
 
14288
 
 
14289
        /**
 
14290
         * Convert string to hexadecimal string (byte string)
 
14291
         * @param $s (string) string to convert
 
14292
         * @return byte string
 
14293
         * @protected
 
14294
         * @since 5.0.010 (2010-05-17)
 
14295
         * @author Nicola Asuni
 
14296
         */
 
14297
        protected function convertStringToHexString($s) {
 
14298
                $bs = '';
 
14299
                $chars = preg_split('//', $s, -1, PREG_SPLIT_NO_EMPTY);
 
14300
                foreach ($chars as $c) {
 
14301
                        $bs .= sprintf('%02s', dechex(ord($c)));
 
14302
                }
 
14303
                return $bs;
 
14304
        }
 
14305
 
 
14306
        /**
 
14307
         * Convert encryption P value to a string of bytes, low-order byte first.
 
14308
         * @param $protection (string) 32bit encryption permission value (P value)
 
14309
         * @return String
 
14310
         * @protected
 
14311
         * @since 5.0.005 (2010-05-12)
 
14312
         * @author Nicola Asuni
 
14313
         */
 
14314
        protected function getEncPermissionsString($protection) {
 
14315
                $binprot = sprintf('%032b', $protection);
 
14316
                $str = chr(bindec(substr($binprot, 24, 8)));
 
14317
                $str .= chr(bindec(substr($binprot, 16, 8)));
 
14318
                $str .= chr(bindec(substr($binprot, 8, 8)));
 
14319
                $str .= chr(bindec(substr($binprot, 0, 8)));
 
14320
                return $str;
 
14321
        }
 
14322
 
 
14323
        // END OF ENCRYPTION FUNCTIONS -------------------------
 
14324
 
 
14325
        // START TRANSFORMATIONS SECTION -----------------------
 
14326
 
 
14327
        /**
 
14328
         * Starts a 2D tranformation saving current graphic state.
 
14329
         * This function must be called before scaling, mirroring, translation, rotation and skewing.
 
14330
         * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
 
14331
         * @public
 
14332
         * @since 2.1.000 (2008-01-07)
 
14333
         * @see StartTransform(), StopTransform()
 
14334
         */
 
14335
        public function StartTransform() {
 
14336
                $this->_out('q');
 
14337
                if ($this->inxobj) {
 
14338
                        // we are inside an XObject template
 
14339
                        $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']);
 
14340
                } else {
 
14341
                        $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
 
14342
                }
 
14343
                ++$this->transfmatrix_key;
 
14344
                $this->transfmatrix[$this->transfmatrix_key] = array();
 
14345
        }
 
14346
 
 
14347
        /**
 
14348
         * Stops a 2D tranformation restoring previous graphic state.
 
14349
         * This function must be called after scaling, mirroring, translation, rotation and skewing.
 
14350
         * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
 
14351
         * @public
 
14352
         * @since 2.1.000 (2008-01-07)
 
14353
         * @see StartTransform(), StopTransform()
 
14354
         */
 
14355
        public function StopTransform() {
 
14356
                $this->_out('Q');
 
14357
                if (isset($this->transfmatrix[$this->transfmatrix_key])) {
 
14358
                        array_pop($this->transfmatrix[$this->transfmatrix_key]);
 
14359
                        --$this->transfmatrix_key;
 
14360
                }
 
14361
                if ($this->inxobj) {
 
14362
                        // we are inside an XObject template
 
14363
                        array_pop($this->xobjects[$this->xobjid]['transfmrk']);
 
14364
                } else {
 
14365
                        array_pop($this->transfmrk[$this->page]);
 
14366
                }
 
14367
        }
 
14368
        /**
 
14369
         * Horizontal Scaling.
 
14370
         * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
 
14371
         * @param $x (int) abscissa of the scaling center. Default is current x position
 
14372
         * @param $y (int) ordinate of the scaling center. Default is current y position
 
14373
         * @public
 
14374
         * @since 2.1.000 (2008-01-07)
 
14375
         * @see StartTransform(), StopTransform()
 
14376
         */
 
14377
        public function ScaleX($s_x, $x='', $y='') {
 
14378
                $this->Scale($s_x, 100, $x, $y);
 
14379
        }
 
14380
 
 
14381
        /**
 
14382
         * Vertical Scaling.
 
14383
         * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
 
14384
         * @param $x (int) abscissa of the scaling center. Default is current x position
 
14385
         * @param $y (int) ordinate of the scaling center. Default is current y position
 
14386
         * @public
 
14387
         * @since 2.1.000 (2008-01-07)
 
14388
         * @see StartTransform(), StopTransform()
 
14389
         */
 
14390
        public function ScaleY($s_y, $x='', $y='') {
 
14391
                $this->Scale(100, $s_y, $x, $y);
 
14392
        }
 
14393
 
 
14394
        /**
 
14395
         * Vertical and horizontal proportional Scaling.
 
14396
         * @param $s (float) scaling factor for width and height as percent. 0 is not allowed.
 
14397
         * @param $x (int) abscissa of the scaling center. Default is current x position
 
14398
         * @param $y (int) ordinate of the scaling center. Default is current y position
 
14399
         * @public
 
14400
         * @since 2.1.000 (2008-01-07)
 
14401
         * @see StartTransform(), StopTransform()
 
14402
         */
 
14403
        public function ScaleXY($s, $x='', $y='') {
 
14404
                $this->Scale($s, $s, $x, $y);
 
14405
        }
 
14406
 
 
14407
        /**
 
14408
         * Vertical and horizontal non-proportional Scaling.
 
14409
         * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
 
14410
         * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
 
14411
         * @param $x (int) abscissa of the scaling center. Default is current x position
 
14412
         * @param $y (int) ordinate of the scaling center. Default is current y position
 
14413
         * @public
 
14414
         * @since 2.1.000 (2008-01-07)
 
14415
         * @see StartTransform(), StopTransform()
 
14416
         */
 
14417
        public function Scale($s_x, $s_y, $x='', $y='') {
 
14418
                if ($x === '') {
 
14419
                        $x = $this->x;
 
14420
                }
 
14421
                if ($y === '') {
 
14422
                        $y = $this->y;
 
14423
                }
 
14424
                if (($s_x == 0) OR ($s_y == 0)) {
 
14425
                        $this->Error('Please do not use values equal to zero for scaling');
 
14426
                }
 
14427
                $y = ($this->h - $y) * $this->k;
 
14428
                $x *= $this->k;
 
14429
                //calculate elements of transformation matrix
 
14430
                $s_x /= 100;
 
14431
                $s_y /= 100;
 
14432
                $tm = array();
 
14433
                $tm[0] = $s_x;
 
14434
                $tm[1] = 0;
 
14435
                $tm[2] = 0;
 
14436
                $tm[3] = $s_y;
 
14437
                $tm[4] = $x * (1 - $s_x);
 
14438
                $tm[5] = $y * (1 - $s_y);
 
14439
                //scale the coordinate system
 
14440
                $this->Transform($tm);
 
14441
        }
 
14442
 
 
14443
        /**
 
14444
         * Horizontal Mirroring.
 
14445
         * @param $x (int) abscissa of the point. Default is current x position
 
14446
         * @public
 
14447
         * @since 2.1.000 (2008-01-07)
 
14448
         * @see StartTransform(), StopTransform()
 
14449
         */
 
14450
        public function MirrorH($x='') {
 
14451
                $this->Scale(-100, 100, $x);
 
14452
        }
 
14453
 
 
14454
        /**
 
14455
         * Verical Mirroring.
 
14456
         * @param $y (int) ordinate of the point. Default is current y position
 
14457
         * @public
 
14458
         * @since 2.1.000 (2008-01-07)
 
14459
         * @see StartTransform(), StopTransform()
 
14460
         */
 
14461
        public function MirrorV($y='') {
 
14462
                $this->Scale(100, -100, '', $y);
 
14463
        }
 
14464
 
 
14465
        /**
 
14466
         * Point reflection mirroring.
 
14467
         * @param $x (int) abscissa of the point. Default is current x position
 
14468
         * @param $y (int) ordinate of the point. Default is current y position
 
14469
         * @public
 
14470
         * @since 2.1.000 (2008-01-07)
 
14471
         * @see StartTransform(), StopTransform()
 
14472
         */
 
14473
        public function MirrorP($x='',$y='') {
 
14474
                $this->Scale(-100, -100, $x, $y);
 
14475
        }
 
14476
 
 
14477
        /**
 
14478
         * Reflection against a straight line through point (x, y) with the gradient angle (angle).
 
14479
         * @param $angle (float) gradient angle of the straight line. Default is 0 (horizontal line).
 
14480
         * @param $x (int) abscissa of the point. Default is current x position
 
14481
         * @param $y (int) ordinate of the point. Default is current y position
 
14482
         * @public
 
14483
         * @since 2.1.000 (2008-01-07)
 
14484
         * @see StartTransform(), StopTransform()
 
14485
         */
 
14486
        public function MirrorL($angle=0, $x='',$y='') {
 
14487
                $this->Scale(-100, 100, $x, $y);
 
14488
                $this->Rotate(-2*($angle-90), $x, $y);
 
14489
        }
 
14490
 
 
14491
        /**
 
14492
         * Translate graphic object horizontally.
 
14493
         * @param $t_x (int) movement to the right (or left for RTL)
 
14494
         * @public
 
14495
         * @since 2.1.000 (2008-01-07)
 
14496
         * @see StartTransform(), StopTransform()
 
14497
         */
 
14498
        public function TranslateX($t_x) {
 
14499
                $this->Translate($t_x, 0);
 
14500
        }
 
14501
 
 
14502
        /**
 
14503
         * Translate graphic object vertically.
 
14504
         * @param $t_y (int) movement to the bottom
 
14505
         * @public
 
14506
         * @since 2.1.000 (2008-01-07)
 
14507
         * @see StartTransform(), StopTransform()
 
14508
         */
 
14509
        public function TranslateY($t_y) {
 
14510
                $this->Translate(0, $t_y);
 
14511
        }
 
14512
 
 
14513
        /**
 
14514
         * Translate graphic object horizontally and vertically.
 
14515
         * @param $t_x (int) movement to the right
 
14516
         * @param $t_y (int) movement to the bottom
 
14517
         * @public
 
14518
         * @since 2.1.000 (2008-01-07)
 
14519
         * @see StartTransform(), StopTransform()
 
14520
         */
 
14521
        public function Translate($t_x, $t_y) {
 
14522
                //calculate elements of transformation matrix
 
14523
                $tm = array();
 
14524
                $tm[0] = 1;
 
14525
                $tm[1] = 0;
 
14526
                $tm[2] = 0;
 
14527
                $tm[3] = 1;
 
14528
                $tm[4] = $t_x * $this->k;
 
14529
                $tm[5] = -$t_y * $this->k;
 
14530
                //translate the coordinate system
 
14531
                $this->Transform($tm);
 
14532
        }
 
14533
 
 
14534
        /**
 
14535
         * Rotate object.
 
14536
         * @param $angle (float) angle in degrees for counter-clockwise rotation
 
14537
         * @param $x (int) abscissa of the rotation center. Default is current x position
 
14538
         * @param $y (int) ordinate of the rotation center. Default is current y position
 
14539
         * @public
 
14540
         * @since 2.1.000 (2008-01-07)
 
14541
         * @see StartTransform(), StopTransform()
 
14542
         */
 
14543
        public function Rotate($angle, $x='', $y='') {
 
14544
                if ($x === '') {
 
14545
                        $x = $this->x;
 
14546
                }
 
14547
                if ($y === '') {
 
14548
                        $y = $this->y;
 
14549
                }
 
14550
                $y = ($this->h - $y) * $this->k;
 
14551
                $x *= $this->k;
 
14552
                //calculate elements of transformation matrix
 
14553
                $tm = array();
 
14554
                $tm[0] = cos(deg2rad($angle));
 
14555
                $tm[1] = sin(deg2rad($angle));
 
14556
                $tm[2] = -$tm[1];
 
14557
                $tm[3] = $tm[0];
 
14558
                $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
 
14559
                $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
 
14560
                //rotate the coordinate system around ($x,$y)
 
14561
                $this->Transform($tm);
 
14562
        }
 
14563
 
 
14564
        /**
 
14565
         * Skew horizontally.
 
14566
         * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
 
14567
         * @param $x (int) abscissa of the skewing center. default is current x position
 
14568
         * @param $y (int) ordinate of the skewing center. default is current y position
 
14569
         * @public
 
14570
         * @since 2.1.000 (2008-01-07)
 
14571
         * @see StartTransform(), StopTransform()
 
14572
         */
 
14573
        public function SkewX($angle_x, $x='', $y='') {
 
14574
                $this->Skew($angle_x, 0, $x, $y);
 
14575
        }
 
14576
 
 
14577
        /**
 
14578
         * Skew vertically.
 
14579
         * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
 
14580
         * @param $x (int) abscissa of the skewing center. default is current x position
 
14581
         * @param $y (int) ordinate of the skewing center. default is current y position
 
14582
         * @public
 
14583
         * @since 2.1.000 (2008-01-07)
 
14584
         * @see StartTransform(), StopTransform()
 
14585
         */
 
14586
        public function SkewY($angle_y, $x='', $y='') {
 
14587
                $this->Skew(0, $angle_y, $x, $y);
 
14588
        }
 
14589
 
 
14590
        /**
 
14591
         * Skew.
 
14592
         * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
 
14593
         * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
 
14594
         * @param $x (int) abscissa of the skewing center. default is current x position
 
14595
         * @param $y (int) ordinate of the skewing center. default is current y position
 
14596
         * @public
 
14597
         * @since 2.1.000 (2008-01-07)
 
14598
         * @see StartTransform(), StopTransform()
 
14599
         */
 
14600
        public function Skew($angle_x, $angle_y, $x='', $y='') {
 
14601
                if ($x === '') {
 
14602
                        $x = $this->x;
 
14603
                }
 
14604
                if ($y === '') {
 
14605
                        $y = $this->y;
 
14606
                }
 
14607
                if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
 
14608
                        $this->Error('Please use values between -90 and +90 degrees for Skewing.');
 
14609
                }
 
14610
                $x *= $this->k;
 
14611
                $y = ($this->h - $y) * $this->k;
 
14612
                //calculate elements of transformation matrix
 
14613
                $tm = array();
 
14614
                $tm[0] = 1;
 
14615
                $tm[1] = tan(deg2rad($angle_y));
 
14616
                $tm[2] = tan(deg2rad($angle_x));
 
14617
                $tm[3] = 1;
 
14618
                $tm[4] = -$tm[2] * $y;
 
14619
                $tm[5] = -$tm[1] * $x;
 
14620
                //skew the coordinate system
 
14621
                $this->Transform($tm);
 
14622
        }
 
14623
 
 
14624
        /**
 
14625
         * Apply graphic transformations.
 
14626
         * @param $tm (array) transformation matrix
 
14627
         * @protected
 
14628
         * @since 2.1.000 (2008-01-07)
 
14629
         * @see StartTransform(), StopTransform()
 
14630
         */
 
14631
        protected function Transform($tm) {
 
14632
                $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
 
14633
                // add tranformation matrix
 
14634
                $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
 
14635
                // update transformation mark
 
14636
                if ($this->inxobj) {
 
14637
                        // we are inside an XObject template
 
14638
                        if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
 
14639
                                $key = key($this->xobjects[$this->xobjid]['transfmrk']);
 
14640
                                $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']);
 
14641
                        }
 
14642
                } elseif (end($this->transfmrk[$this->page]) !== false) {
 
14643
                        $key = key($this->transfmrk[$this->page]);
 
14644
                        $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
 
14645
                }
 
14646
        }
 
14647
 
 
14648
        // END TRANSFORMATIONS SECTION -------------------------
 
14649
 
 
14650
        // START GRAPHIC FUNCTIONS SECTION ---------------------
 
14651
        // The following section is based on the code provided by David Hernandez Sanz
 
14652
 
 
14653
        /**
 
14654
         * 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.
 
14655
         * @param $width (float) The width.
 
14656
         * @public
 
14657
         * @since 1.0
 
14658
         * @see Line(), Rect(), Cell(), MultiCell()
 
14659
         */
 
14660
        public function SetLineWidth($width) {
 
14661
                //Set line width
 
14662
                $this->LineWidth = $width;
 
14663
                $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k));
 
14664
                if ($this->page > 0) {
 
14665
                        $this->_out($this->linestyleWidth);
 
14666
                }
 
14667
        }
 
14668
 
 
14669
        /**
 
14670
         * Returns the current the line width.
 
14671
         * @return int Line width
 
14672
         * @public
 
14673
         * @since 2.1.000 (2008-01-07)
 
14674
         * @see Line(), SetLineWidth()
 
14675
         */
 
14676
        public function GetLineWidth() {
 
14677
                return $this->LineWidth;
 
14678
        }
 
14679
 
 
14680
        /**
 
14681
         * Set line style.
 
14682
         * @param $style (array) Line style. Array with keys among the following:
 
14683
         * <ul>
 
14684
         *       <li>width (float): Width of the line in user units.</li>
 
14685
         *       <li>cap (string): Type of cap to put on the line. Possible values are:
 
14686
         * butt, round, square. The difference between "square" and "butt" is that
 
14687
         * "square" projects a flat end past the end of the line.</li>
 
14688
         *       <li>join (string): Type of join. Possible values are: miter, round,
 
14689
         * bevel.</li>
 
14690
         *       <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
 
14691
         * series of length values, which are the lengths of the on and off dashes.
 
14692
         * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
 
14693
         * 1 off, 2 on, 1 off, ...</li>
 
14694
         *       <li>phase (integer): Modifier on the dash pattern which is used to shift
 
14695
         * the point at which the pattern starts.</li>
 
14696
         *       <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K).</li>
 
14697
         * </ul>
 
14698
         * @param $ret (boolean) if true do not send the command.
 
14699
         * @return string the PDF command
 
14700
         * @public
 
14701
         * @since 2.1.000 (2008-01-08)
 
14702
         */
 
14703
        public function SetLineStyle($style, $ret=false) {
 
14704
                $s = ''; // string to be returned
 
14705
                if (!is_array($style)) {
 
14706
                        return;
 
14707
                }
 
14708
                if (isset($style['width'])) {
 
14709
                        $this->LineWidth = $style['width'];
 
14710
                        $this->linestyleWidth = sprintf('%.2F w', ($style['width'] * $this->k));
 
14711
                        $s .= $this->linestyleWidth.' ';
 
14712
                }
 
14713
                if (isset($style['cap'])) {
 
14714
                        $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
 
14715
                        if (isset($ca[$style['cap']])) {
 
14716
                                $this->linestyleCap = $ca[$style['cap']].' J';
 
14717
                                $s .= $this->linestyleCap.' ';
 
14718
                        }
 
14719
                }
 
14720
                if (isset($style['join'])) {
 
14721
                        $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
 
14722
                        if (isset($ja[$style['join']])) {
 
14723
                                $this->linestyleJoin = $ja[$style['join']].' j';
 
14724
                                $s .= $this->linestyleJoin.' ';
 
14725
                        }
 
14726
                }
 
14727
                if (isset($style['dash'])) {
 
14728
                        $dash_string = '';
 
14729
                        if ($style['dash']) {
 
14730
                                if (preg_match('/^.+,/', $style['dash']) > 0) {
 
14731
                                        $tab = explode(',', $style['dash']);
 
14732
                                } else {
 
14733
                                        $tab = array($style['dash']);
 
14734
                                }
 
14735
                                $dash_string = '';
 
14736
                                foreach ($tab as $i => $v) {
 
14737
                                        if ($i) {
 
14738
                                                $dash_string .= ' ';
 
14739
                                        }
 
14740
                                        $dash_string .= sprintf('%.2F', $v);
 
14741
                                }
 
14742
                        }
 
14743
                        if (!isset($style['phase']) OR !$style['dash']) {
 
14744
                                $style['phase'] = 0;
 
14745
                        }
 
14746
                        $this->linestyleDash = sprintf('[%s] %.2F d', $dash_string, $style['phase']);
 
14747
                        $s .= $this->linestyleDash.' ';
 
14748
                }
 
14749
                if (isset($style['color'])) {
 
14750
                        $s .= $this->SetDrawColorArray($style['color'], true).' ';
 
14751
                }
 
14752
                if (!$ret) {
 
14753
                        $this->_out($s);
 
14754
                }
 
14755
                return $s;
 
14756
        }
 
14757
 
 
14758
        /**
 
14759
         * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment.
 
14760
         * @param $x (float) Abscissa of point.
 
14761
         * @param $y (float) Ordinate of point.
 
14762
         * @protected
 
14763
         * @since 2.1.000 (2008-01-08)
 
14764
         */
 
14765
        protected function _outPoint($x, $y) {
 
14766
                $this->_out(sprintf('%.2F %.2F m', $x * $this->k, ($this->h - $y) * $this->k));
 
14767
        }
 
14768
 
 
14769
        /**
 
14770
         * Append a straight line segment from the current point to the point (x, y).
 
14771
         * The new current point shall be (x, y).
 
14772
         * @param $x (float) Abscissa of end point.
 
14773
         * @param $y (float) Ordinate of end point.
 
14774
         * @protected
 
14775
         * @since 2.1.000 (2008-01-08)
 
14776
         */
 
14777
        protected function _outLine($x, $y) {
 
14778
                $this->_out(sprintf('%.2F %.2F l', $x * $this->k, ($this->h - $y) * $this->k));
 
14779
        }
 
14780
 
 
14781
        /**
 
14782
         * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space.
 
14783
         * @param $x (float) Abscissa of upper-left corner.
 
14784
         * @param $y (float) Ordinate of upper-left corner.
 
14785
         * @param $w (float) Width.
 
14786
         * @param $h (float) Height.
 
14787
         * @param $op (string) options
 
14788
         * @protected
 
14789
         * @since 2.1.000 (2008-01-08)
 
14790
         */
 
14791
        protected function _outRect($x, $y, $w, $h, $op) {
 
14792
                $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x * $this->k, ($this->h - $y) * $this->k, $w * $this->k, -$h * $this->k, $op));
 
14793
        }
 
14794
 
 
14795
        /**
 
14796
         * 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.
 
14797
         * The new current point shall be (x3, y3).
 
14798
         * @param $x1 (float) Abscissa of control point 1.
 
14799
         * @param $y1 (float) Ordinate of control point 1.
 
14800
         * @param $x2 (float) Abscissa of control point 2.
 
14801
         * @param $y2 (float) Ordinate of control point 2.
 
14802
         * @param $x3 (float) Abscissa of end point.
 
14803
         * @param $y3 (float) Ordinate of end point.
 
14804
         * @protected
 
14805
         * @since 2.1.000 (2008-01-08)
 
14806
         */
 
14807
        protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
 
14808
                $this->_out(sprintf('%.2F %.2F %.2F %.2F %.2F %.2F c', $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
 
14809
        }
 
14810
 
 
14811
        /**
 
14812
         * 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.
 
14813
         * The new current point shall be (x3, y3).
 
14814
         * @param $x2 (float) Abscissa of control point 2.
 
14815
         * @param $y2 (float) Ordinate of control point 2.
 
14816
         * @param $x3 (float) Abscissa of end point.
 
14817
         * @param $y3 (float) Ordinate of end point.
 
14818
         * @protected
 
14819
         * @since 4.9.019 (2010-04-26)
 
14820
         */
 
14821
        protected function _outCurveV($x2, $y2, $x3, $y3) {
 
14822
                $this->_out(sprintf('%.2F %.2F %.2F %.2F v', $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
 
14823
        }
 
14824
 
 
14825
        /**
 
14826
         * 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.
 
14827
         * The new current point shall be (x3, y3).
 
14828
         * @param $x1 (float) Abscissa of control point 1.
 
14829
         * @param $y1 (float) Ordinate of control point 1.
 
14830
         * @param $x3 (float) Abscissa of end point.
 
14831
         * @param $y3 (float) Ordinate of end point.
 
14832
         * @protected
 
14833
         * @since 2.1.000 (2008-01-08)
 
14834
         */
 
14835
        protected function _outCurveY($x1, $y1, $x3, $y3) {
 
14836
                $this->_out(sprintf('%.2F %.2F %.2F %.2F y', $x1 * $this->k, ($this->h - $y1) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
 
14837
        }
 
14838
 
 
14839
        /**
 
14840
         * Draws a line between two points.
 
14841
         * @param $x1 (float) Abscissa of first point.
 
14842
         * @param $y1 (float) Ordinate of first point.
 
14843
         * @param $x2 (float) Abscissa of second point.
 
14844
         * @param $y2 (float) Ordinate of second point.
 
14845
         * @param $style (array) Line style. Array like for SetLineStyle(). Default value: default line style (empty array).
 
14846
         * @public
 
14847
         * @since 1.0
 
14848
         * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
 
14849
         */
 
14850
        public function Line($x1, $y1, $x2, $y2, $style=array()) {
 
14851
                if (is_array($style)) {
 
14852
                        $this->SetLineStyle($style);
 
14853
                }
 
14854
                $this->_outPoint($x1, $y1);
 
14855
                $this->_outLine($x2, $y2);
 
14856
                $this->_out('S');
 
14857
        }
 
14858
 
 
14859
        /**
 
14860
         * Draws a rectangle.
 
14861
         * @param $x (float) Abscissa of upper-left corner.
 
14862
         * @param $y (float) Ordinate of upper-left corner.
 
14863
         * @param $w (float) Width.
 
14864
         * @param $h (float) Height.
 
14865
         * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
 
14866
         * @param $border_style (array) Border style of rectangle. Array with keys among the following:
 
14867
         * <ul>
 
14868
         *       <li>all: Line style of all borders. Array like for SetLineStyle().</li>
 
14869
         *       <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for SetLineStyle().</li>
 
14870
         * </ul>
 
14871
         * If a key is not present or is null, not draws the border. Default value: default line style (empty array).
 
14872
         * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
 
14873
         * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
 
14874
         * @public
 
14875
         * @since 1.0
 
14876
         * @see SetLineStyle()
 
14877
         */
 
14878
        public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
 
14879
                if (!(false === strpos($style, 'F')) AND !empty($fill_color)) {
 
14880
                        $this->SetFillColorArray($fill_color);
 
14881
                }
 
14882
                $op = $this->getPathPaintOperator($style);
 
14883
                if ((!$border_style) OR (isset($border_style['all']))) {
 
14884
                        if (isset($border_style['all']) AND $border_style['all']) {
 
14885
                                $this->SetLineStyle($border_style['all']);
 
14886
                                $border_style = array();
 
14887
                        }
 
14888
                }
 
14889
                $this->_outRect($x, $y, $w, $h, $op);
 
14890
                if ($border_style) {
 
14891
                        $border_style2 = array();
 
14892
                        foreach ($border_style as $line => $value) {
 
14893
                                $length = strlen($line);
 
14894
                                for ($i = 0; $i < $length; ++$i) {
 
14895
                                        $border_style2[$line[$i]] = $value;
 
14896
                                }
 
14897
                        }
 
14898
                        $border_style = $border_style2;
 
14899
                        if (isset($border_style['L']) AND $border_style['L']) {
 
14900
                                $this->Line($x, $y, $x, $y + $h, $border_style['L']);
 
14901
                        }
 
14902
                        if (isset($border_style['T']) AND $border_style['T']) {
 
14903
                                $this->Line($x, $y, $x + $w, $y, $border_style['T']);
 
14904
                        }
 
14905
                        if (isset($border_style['R']) AND $border_style['R']) {
 
14906
                                $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
 
14907
                        }
 
14908
                        if (isset($border_style['B']) AND $border_style['B']) {
 
14909
                                $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
 
14910
                        }
 
14911
                }
 
14912
        }
 
14913
 
 
14914
        /**
 
14915
         * Draws a Bezier curve.
 
14916
         * The Bezier curve is a tangent to the line between the control points at
 
14917
         * either end of the curve.
 
14918
         * @param $x0 (float) Abscissa of start point.
 
14919
         * @param $y0 (float) Ordinate of start point.
 
14920
         * @param $x1 (float) Abscissa of control point 1.
 
14921
         * @param $y1 (float) Ordinate of control point 1.
 
14922
         * @param $x2 (float) Abscissa of control point 2.
 
14923
         * @param $y2 (float) Ordinate of control point 2.
 
14924
         * @param $x3 (float) Abscissa of end point.
 
14925
         * @param $y3 (float) Ordinate of end point.
 
14926
         * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
 
14927
         * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
 
14928
         * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
 
14929
         * @public
 
14930
         * @see SetLineStyle()
 
14931
         * @since 2.1.000 (2008-01-08)
 
14932
         */
 
14933
        public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
 
14934
                if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
 
14935
                        $this->SetFillColorArray($fill_color);
 
14936
                }
 
14937
                $op = $this->getPathPaintOperator($style);
 
14938
                if ($line_style) {
 
14939
                        $this->SetLineStyle($line_style);
 
14940
                }
 
14941
                $this->_outPoint($x0, $y0);
 
14942
                $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
 
14943
                $this->_out($op);
 
14944
        }
 
14945
 
 
14946
        /**
 
14947
         * Draws a poly-Bezier curve.
 
14948
         * Each Bezier curve segment is a tangent to the line between the control points at
 
14949
         * either end of the curve.
 
14950
         * @param $x0 (float) Abscissa of start point.
 
14951
         * @param $y0 (float) Ordinate of start point.
 
14952
         * @param $segments (float) An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
 
14953
         * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
 
14954
         * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
 
14955
         * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
 
14956
         * @public
 
14957
         * @see SetLineStyle()
 
14958
         * @since 3.0008 (2008-05-12)
 
14959
         */
 
14960
        public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
 
14961
                if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
 
14962
                        $this->SetFillColorArray($fill_color);
 
14963
                }
 
14964
                $op = $this->getPathPaintOperator($style);
 
14965
                if ($op == 'f') {
 
14966
                        $line_style = array();
 
14967
                }
 
14968
                if ($line_style) {
 
14969
                        $this->SetLineStyle($line_style);
 
14970
                }
 
14971
                $this->_outPoint($x0, $y0);
 
14972
                foreach ($segments as $segment) {
 
14973
                        list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
 
14974
                        $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
 
14975
                }
 
14976
                $this->_out($op);
 
14977
        }
 
14978
 
 
14979
        /**
 
14980
         * Draws an ellipse.
 
14981
         * An ellipse is formed from n Bezier curves.
 
14982
         * @param $x0 (float) Abscissa of center point.
 
14983
         * @param $y0 (float) Ordinate of center point.
 
14984
         * @param $rx (float) Horizontal radius.
 
14985
         * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
 
14986
         * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
 
14987
         * @param $astart: (float) Angle start of draw line. Default value: 0.
 
14988
         * @param $afinish: (float) Angle finish of draw line. Default value: 360.
 
14989
         * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
 
14990
         * @param $line_style (array) Line style of ellipse. Array like for SetLineStyle(). Default value: default line style (empty array).
 
14991
         * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
 
14992
         * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
 
14993
         * @author Nicola Asuni
 
14994
         * @public
 
14995
         * @since 2.1.000 (2008-01-08)
 
14996
         */
 
14997
        public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
 
14998
                if ($this->empty_string($ry) OR ($ry == 0)) {
 
14999
                        $ry = $rx;
 
15000
                }
 
15001
                if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
 
15002
                        $this->SetFillColorArray($fill_color);
 
15003
                }
 
15004
                $op = $this->getPathPaintOperator($style);
 
15005
                if ($op == 'f') {
 
15006
                        $line_style = array();
 
15007
                }
 
15008
                if ($line_style) {
 
15009
                        $this->SetLineStyle($line_style);
 
15010
                }
 
15011
                $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
 
15012
                $this->_out($op);
 
15013
        }
 
15014
 
 
15015
        /**
 
15016
         * Append an elliptical arc to the current path.
 
15017
         * An ellipse is formed from n Bezier curves.
 
15018
         * @param $xc (float) Abscissa of center point.
 
15019
         * @param $yc (float) Ordinate of center point.
 
15020
         * @param $rx (float) Horizontal radius.
 
15021
         * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
 
15022
         * @param $xang: (float) Angle between the X-axis and the major axis of the ellipse. Default value: 0.
 
15023
         * @param $angs: (float) Angle start of draw line. Default value: 0.
 
15024
         * @param $angf: (float) Angle finish of draw line. Default value: 360.
 
15025
         * @param $pie (boolean) if true do not mark the border point (used to draw pie sectors).
 
15026
         * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
 
15027
         * @param $startpoint (boolean) if true output a starting point.
 
15028
         * @param $ccw (boolean) if true draws in counter-clockwise.
 
15029
         * @param $svg (boolean) if true the angles are in svg mode (already calculated).
 
15030
         * @return array bounding box coordinates (x min, y min, x max, y max)
 
15031
         * @author Nicola Asuni
 
15032
         * @protected
 
15033
         * @since 4.9.019 (2010-04-26)
 
15034
         */
 
15035
        protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
 
15036
                $k = $this->k;
 
15037
                if ($nc < 2) {
 
15038
                        $nc = 2;
 
15039
                }
 
15040
                $xmin = 2147483647;
 
15041
                $ymin = 2147483647;
 
15042
                $xmax = 0;
 
15043
                $ymax = 0;
 
15044
                if ($pie) {
 
15045
                        // center of the arc
 
15046
                        $this->_outPoint($xc, $yc);
 
15047
                }
 
15048
                $xang = deg2rad((float) $xang);
 
15049
                $angs = deg2rad((float) $angs);
 
15050
                $angf = deg2rad((float) $angf);
 
15051
                if ($svg) {
 
15052
                        $as = $angs;
 
15053
                        $af = $angf;
 
15054
                } else {
 
15055
                        $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
 
15056
                        $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
 
15057
                }
 
15058
                if ($as < 0) {
 
15059
                        $as += (2 * M_PI);
 
15060
                }
 
15061
                if ($af < 0) {
 
15062
                        $af += (2 * M_PI);
 
15063
                }
 
15064
                if ($ccw AND ($as > $af)) {
 
15065
                        // reverse rotation
 
15066
                        $as -= (2 * M_PI);
 
15067
                } elseif (!$ccw AND ($as < $af)) {
 
15068
                        // reverse rotation
 
15069
                        $af -= (2 * M_PI);
 
15070
                }
 
15071
                $total_angle = ($af - $as);
 
15072
                if ($nc < 2) {
 
15073
                        $nc = 2;
 
15074
                }
 
15075
                // total arcs to draw
 
15076
                $nc *= (2 * abs($total_angle) / M_PI);
 
15077
                $nc = round($nc) + 1;
 
15078
                // angle of each arc
 
15079
                $arcang = ($total_angle / $nc);
 
15080
                // center point in PDF coordinates
 
15081
                $x0 = $xc;
 
15082
                $y0 = ($this->h - $yc);
 
15083
                // starting angle
 
15084
                $ang = $as;
 
15085
                $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
 
15086
                $cos_xang = cos($xang);
 
15087
                $sin_xang = sin($xang);
 
15088
                $cos_ang = cos($ang);
 
15089
                $sin_ang = sin($ang);
 
15090
                // first arc point
 
15091
                $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
 
15092
                $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
 
15093
                // first Bezier control point
 
15094
                $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
 
15095
                $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
 
15096
                if ($pie) {
 
15097
                        // line from center to arc starting point
 
15098
                        $this->_outLine($px1, $this->h - $py1);
 
15099
                } elseif ($startpoint) {
 
15100
                        // arc starting point
 
15101
                        $this->_outPoint($px1, $this->h - $py1);
 
15102
                }
 
15103
                // draw arcs
 
15104
                for ($i = 1; $i <= $nc; ++$i) {
 
15105
                        // starting angle
 
15106
                        $ang = $as + ($i * $arcang);
 
15107
                        if ($i == $nc) {
 
15108
                                $ang = $af;
 
15109
                        }
 
15110
                        $cos_ang = cos($ang);
 
15111
                        $sin_ang = sin($ang);
 
15112
                        // second arc point
 
15113
                        $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
 
15114
                        $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
 
15115
                        // second Bezier control point
 
15116
                        $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
 
15117
                        $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
 
15118
                        // draw arc
 
15119
                        $cx1 = ($px1 + $qx1);
 
15120
                        $cy1 = ($this->h - ($py1 + $qy1));
 
15121
                        $cx2 = ($px2 - $qx2);
 
15122
                        $cy2 = ($this->h - ($py2 - $qy2));
 
15123
                        $cx3 = $px2;
 
15124
                        $cy3 = ($this->h - $py2);
 
15125
                        $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
 
15126
                        // get bounding box coordinates
 
15127
                        $xmin = min($xmin, $cx1, $cx2, $cx3);
 
15128
                        $ymin = min($ymin, $cy1, $cy2, $cy3);
 
15129
                        $xmax = max($xmax, $cx1, $cx2, $cx3);
 
15130
                        $ymax = max($ymax, $cy1, $cy2, $cy3);
 
15131
                        // move to next point
 
15132
                        $px1 = $px2;
 
15133
                        $py1 = $py2;
 
15134
                        $qx1 = $qx2;
 
15135
                        $qy1 = $qy2;
 
15136
                }
 
15137
                if ($pie) {
 
15138
                        $this->_outLine($xc, $yc);
 
15139
                        // get bounding box coordinates
 
15140
                        $xmin = min($xmin, $xc);
 
15141
                        $ymin = min($ymin, $yc);
 
15142
                        $xmax = max($xmax, $xc);
 
15143
                        $ymax = max($ymax, $yc);
 
15144
                }
 
15145
                return array($xmin, $ymin, $xmax, $ymax);
 
15146
        }
 
15147
 
 
15148
        /**
 
15149
         * Draws a circle.
 
15150
         * A circle is formed from n Bezier curves.
 
15151
         * @param $x0 (float) Abscissa of center point.
 
15152
         * @param $y0 (float) Ordinate of center point.
 
15153
         * @param $r (float) Radius.
 
15154
         * @param $angstr: (float) Angle start of draw line. Default value: 0.
 
15155
         * @param $angend: (float) Angle finish of draw line. Default value: 360.
 
15156
         * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
 
15157
         * @param $line_style (array) Line style of circle. Array like for SetLineStyle(). Default value: default line style (empty array).
 
15158
         * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
 
15159
         * @param $nc (integer) Number of curves used to draw a 90 degrees portion of circle.
 
15160
         * @public
 
15161
         * @since 2.1.000 (2008-01-08)
 
15162
         */
 
15163
        public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
 
15164
                $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
 
15165
        }
 
15166
 
 
15167
        /**
 
15168
         * Draws a polygonal line
 
15169
         * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
 
15170
         * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
 
15171
         * @param $line_style (array) Line style of polygon. Array with keys among the following:
 
15172
         * <ul>
 
15173
         *       <li>all: Line style of all lines. Array like for SetLineStyle().</li>
 
15174
         *       <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
 
15175
         * </ul>
 
15176
         * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
 
15177
         * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
 
15178
         * @since 4.8.003 (2009-09-15)
 
15179
         * @public
 
15180
         */
 
15181
        public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
 
15182
                $this->Polygon($p, $style, $line_style, $fill_color, false);
 
15183
        }
 
15184
 
 
15185
        /**
 
15186
         * Draws a polygon.
 
15187
         * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
 
15188
         * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
 
15189
         * @param $line_style (array) Line style of polygon. Array with keys among the following:
 
15190
         * <ul>
 
15191
         *       <li>all: Line style of all lines. Array like for SetLineStyle().</li>
 
15192
         *       <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
 
15193
         * </ul>
 
15194
         * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
 
15195
         * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
 
15196
         * @param $closed (boolean) if true the polygon is closes, otherwise will remain open
 
15197
         * @public
 
15198
         * @since 2.1.000 (2008-01-08)
 
15199
         */
 
15200
        public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
 
15201
                $nc = count($p); // number of coordinates
 
15202
                $np = $nc / 2; // number of points
 
15203
                if ($closed) {
 
15204
                        // close polygon by adding the first 2 points at the end (one line)
 
15205
                        for ($i = 0; $i < 4; ++$i) {
 
15206
                                $p[$nc + $i] = $p[$i];
 
15207
                        }
 
15208
                        // copy style for the last added line
 
15209
                        if (isset($line_style[0])) {
 
15210
                                $line_style[$np] = $line_style[0];
 
15211
                        }
 
15212
                        $nc += 4;
 
15213
                }
 
15214
                if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
 
15215
                        $this->SetFillColorArray($fill_color);
 
15216
                }
 
15217
                $op = $this->getPathPaintOperator($style);
 
15218
                if ($op == 'f') {
 
15219
                        $line_style = array();
 
15220
                }
 
15221
                $draw = true;
 
15222
                if ($line_style) {
 
15223
                        if (isset($line_style['all'])) {
 
15224
                                $this->SetLineStyle($line_style['all']);
 
15225
                        } else {
 
15226
                                $draw = false;
 
15227
                                if ($op == 'B') {
 
15228
                                        // draw fill
 
15229
                                        $op = 'f';
 
15230
                                        $this->_outPoint($p[0], $p[1]);
 
15231
                                        for ($i = 2; $i < $nc; $i = $i + 2) {
 
15232
                                                $this->_outLine($p[$i], $p[$i + 1]);
 
15233
                                        }
 
15234
                                        $this->_out($op);
 
15235
                                }
 
15236
                                // draw outline
 
15237
                                $this->_outPoint($p[0], $p[1]);
 
15238
                                for ($i = 2; $i < $nc; $i = $i + 2) {
 
15239
                                        $line_num = ($i / 2) - 1;
 
15240
                                        if (isset($line_style[$line_num])) {
 
15241
                                                if ($line_style[$line_num] != 0) {
 
15242
                                                        if (is_array($line_style[$line_num])) {
 
15243
                                                                $this->_out('S');
 
15244
                                                                $this->SetLineStyle($line_style[$line_num]);
 
15245
                                                                $this->_outPoint($p[$i - 2], $p[$i - 1]);
 
15246
                                                                $this->_outLine($p[$i], $p[$i + 1]);
 
15247
                                                                $this->_out('S');
 
15248
                                                                $this->_outPoint($p[$i], $p[$i + 1]);
 
15249
                                                        } else {
 
15250
                                                                $this->_outLine($p[$i], $p[$i + 1]);
 
15251
                                                        }
 
15252
                                                }
 
15253
                                        } else {
 
15254
                                                $this->_outLine($p[$i], $p[$i + 1]);
 
15255
                                        }
 
15256
                                }
 
15257
                                $this->_out($op);
 
15258
                        }
 
15259
                }
 
15260
                if ($draw) {
 
15261
                        $this->_outPoint($p[0], $p[1]);
 
15262
                        for ($i = 2; $i < $nc; $i = $i + 2) {
 
15263
                                $this->_outLine($p[$i], $p[$i + 1]);
 
15264
                        }
 
15265
                        $this->_out($op);
 
15266
                }
 
15267
        }
 
15268
 
 
15269
        /**
 
15270
         * Draws a regular polygon.
 
15271
         * @param $x0 (float) Abscissa of center point.
 
15272
         * @param $y0 (float) Ordinate of center point.
 
15273
         * @param $r: (float) Radius of inscribed circle.
 
15274
         * @param $ns (integer) Number of sides.
 
15275
         * @param $angle (float) Angle oriented (anti-clockwise). Default value: 0.
 
15276
         * @param $draw_circle (boolean) Draw inscribed circle or not. Default value: false.
 
15277
         * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
 
15278
         * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
 
15279
         * <ul>
 
15280
         *       <li>all: Line style of all sides. Array like for SetLineStyle().</li>
 
15281
         *       <li>0 to ($ns - 1): Line style of each side. Array like for SetLineStyle().</li>
 
15282
         * </ul>
 
15283
         * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
 
15284
         * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
 
15285
         * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
 
15286
         * <ul>
 
15287
         *       <li>D or empty string: Draw (default).</li>
 
15288
         *       <li>F: Fill.</li>
 
15289
         *       <li>DF or FD: Draw and fill.</li>
 
15290
         *       <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
 
15291
         *       <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
 
15292
         * </ul>
 
15293
         * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
 
15294
         * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
 
15295
         * @public
 
15296
         * @since 2.1.000 (2008-01-08)
 
15297
         */
 
15298
        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()) {
 
15299
                if (3 > $ns) {
 
15300
                        $ns = 3;
 
15301
                }
 
15302
                if ($draw_circle) {
 
15303
                        $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
 
15304
                }
 
15305
                $p = array();
 
15306
                for ($i = 0; $i < $ns; ++$i) {
 
15307
                        $a = $angle + ($i * 360 / $ns);
 
15308
                        $a_rad = deg2rad((float) $a);
 
15309
                        $p[] = $x0 + ($r * sin($a_rad));
 
15310
                        $p[] = $y0 + ($r * cos($a_rad));
 
15311
                }
 
15312
                $this->Polygon($p, $style, $line_style, $fill_color);
 
15313
        }
 
15314
 
 
15315
        /**
 
15316
         * Draws a star polygon
 
15317
         * @param $x0 (float) Abscissa of center point.
 
15318
         * @param $y0 (float) Ordinate of center point.
 
15319
         * @param $r (float) Radius of inscribed circle.
 
15320
         * @param $nv (integer) Number of vertices.
 
15321
         * @param $ng (integer) Number of gap (if ($ng % $nv = 1) then is a regular polygon).
 
15322
         * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
 
15323
         * @param $draw_circle: (boolean) Draw inscribed circle or not. Default value is false.
 
15324
         * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
 
15325
         * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
 
15326
         * <ul>
 
15327
         *       <li>all: Line style of all sides. Array like for
 
15328
         * SetLineStyle().</li>
 
15329
         *       <li>0 to (n - 1): Line style of each side. Array like for SetLineStyle().</li>
 
15330
         * </ul>
 
15331
         * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
 
15332
         * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
 
15333
         * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
 
15334
         * <ul>
 
15335
         *       <li>D or empty string: Draw (default).</li>
 
15336
         *       <li>F: Fill.</li>
 
15337
         *       <li>DF or FD: Draw and fill.</li>
 
15338
         *       <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
 
15339
         *       <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
 
15340
         * </ul>
 
15341
         * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
 
15342
         * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
 
15343
         * @public
 
15344
         * @since 2.1.000 (2008-01-08)
 
15345
         */
 
15346
        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()) {
 
15347
                if ($nv < 2) {
 
15348
                        $nv = 2;
 
15349
                }
 
15350
                if ($draw_circle) {
 
15351
                        $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
 
15352
                }
 
15353
                $p2 = array();
 
15354
                $visited = array();
 
15355
                for ($i = 0; $i < $nv; ++$i) {
 
15356
                        $a = $angle + ($i * 360 / $nv);
 
15357
                        $a_rad = deg2rad((float) $a);
 
15358
                        $p2[] = $x0 + ($r * sin($a_rad));
 
15359
                        $p2[] = $y0 + ($r * cos($a_rad));
 
15360
                        $visited[] = false;
 
15361
                }
 
15362
                $p = array();
 
15363
                $i = 0;
 
15364
                do {
 
15365
                        $p[] = $p2[$i * 2];
 
15366
                        $p[] = $p2[($i * 2) + 1];
 
15367
                        $visited[$i] = true;
 
15368
                        $i += $ng;
 
15369
                        $i %= $nv;
 
15370
                } while (!$visited[$i]);
 
15371
                $this->Polygon($p, $style, $line_style, $fill_color);
 
15372
        }
 
15373
 
 
15374
        /**
 
15375
         * Draws a rounded rectangle.
 
15376
         * @param $x (float) Abscissa of upper-left corner.
 
15377
         * @param $y (float) Ordinate of upper-left corner.
 
15378
         * @param $w (float) Width.
 
15379
         * @param $h (float) Height.
 
15380
         * @param $r (float) the radius of the circle used to round off the corners of the rectangle.
 
15381
         * @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 left, top right, bottom right and bottom left. Default value: all rounded corner ("1111").
 
15382
         * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
 
15383
         * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
 
15384
         * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
 
15385
         * @public
 
15386
         * @since 2.1.000 (2008-01-08)
 
15387
         */
 
15388
        public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
 
15389
                $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
 
15390
        }
 
15391
 
 
15392
        /**
 
15393
         * Draws a rounded rectangle.
 
15394
         * @param $x (float) Abscissa of upper-left corner.
 
15395
         * @param $y (float) Ordinate of upper-left corner.
 
15396
         * @param $w (float) Width.
 
15397
         * @param $h (float) Height.
 
15398
         * @param $rx (float) the x-axis radius of the ellipse used to round off the corners of the rectangle.
 
15399
         * @param $ry (float) the y-axis radius of the ellipse used to round off the corners of the rectangle.
 
15400
         * @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 left, top right, bottom right and bottom left. Default value: all rounded corner ("1111").
 
15401
         * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
 
15402
         * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
 
15403
         * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
 
15404
         * @public
 
15405
         * @since 4.9.019 (2010-04-22)
 
15406
         */
 
15407
        public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
 
15408
                if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
 
15409
                        // Not rounded
 
15410
                        $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
 
15411
                        return;
 
15412
                }
 
15413
                // Rounded
 
15414
                if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
 
15415
                        $this->SetFillColorArray($fill_color);
 
15416
                }
 
15417
                $op = $this->getPathPaintOperator($style);
 
15418
                if ($op == 'f') {
 
15419
                        $border_style = array();
 
15420
                }
 
15421
                if ($border_style) {
 
15422
                        $this->SetLineStyle($border_style);
 
15423
                }
 
15424
                $MyArc = 4 / 3 * (sqrt(2) - 1);
 
15425
                $this->_outPoint($x + $rx, $y);
 
15426
                $xc = $x + $w - $rx;
 
15427
                $yc = $y + $ry;
 
15428
                $this->_outLine($xc, $y);
 
15429
                if ($round_corner[0]) {
 
15430
                        $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
 
15431
                } else {
 
15432
                        $this->_outLine($x + $w, $y);
 
15433
                }
 
15434
                $xc = $x + $w - $rx;
 
15435
                $yc = $y + $h - $ry;
 
15436
                $this->_outLine($x + $w, $yc);
 
15437
                if ($round_corner[1]) {
 
15438
                        $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
 
15439
                } else {
 
15440
                        $this->_outLine($x + $w, $y + $h);
 
15441
                }
 
15442
                $xc = $x + $rx;
 
15443
                $yc = $y + $h - $ry;
 
15444
                $this->_outLine($xc, $y + $h);
 
15445
                if ($round_corner[2]) {
 
15446
                        $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
 
15447
                } else {
 
15448
                        $this->_outLine($x, $y + $h);
 
15449
                }
 
15450
                $xc = $x + $rx;
 
15451
                $yc = $y + $ry;
 
15452
                $this->_outLine($x, $yc);
 
15453
                if ($round_corner[3]) {
 
15454
                        $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
 
15455
                } else {
 
15456
                        $this->_outLine($x, $y);
 
15457
                        $this->_outLine($x + $rx, $y);
 
15458
                }
 
15459
                $this->_out($op);
 
15460
        }
 
15461
 
 
15462
        /**
 
15463
         * Draws a grahic arrow.
 
15464
         * @param $x0 (float) Abscissa of first point.
 
15465
         * @param $y0 (float) Ordinate of first point.
 
15466
         * @param $x1 (float) Abscissa of second point.
 
15467
         * @param $y1 (float) Ordinate of second point.
 
15468
         * @param $head_style (int) (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead)
 
15469
         * @param $arm_size (float) length of arrowhead arms
 
15470
         * @param $arm_angle (int) angle between an arm and the shaft
 
15471
         * @author Piotr Galecki, Nicola Asuni, Andy Meier
 
15472
         * @since 4.6.018 (2009-07-10)
 
15473
         */
 
15474
        public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
 
15475
                // getting arrow direction angle
 
15476
                // 0 deg angle is when both arms go along X axis. angle grows clockwise.
 
15477
                $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
 
15478
                if ($dir_angle < 0) {
 
15479
                        $dir_angle += (2 * M_PI);
 
15480
                }
 
15481
                $arm_angle = deg2rad($arm_angle);
 
15482
                $sx1 = $x1;
 
15483
                $sy1 = $y1;
 
15484
                if ($head_style > 0) {
 
15485
                        // calculate the stopping point for the arrow shaft
 
15486
                        $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
 
15487
                        $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
 
15488
                }
 
15489
                // main arrow line / shaft
 
15490
                $this->Line($x0, $y0, $sx1, $sy1);
 
15491
                // left arrowhead arm tip
 
15492
                $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
 
15493
                $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
 
15494
                // right arrowhead arm tip
 
15495
                $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
 
15496
                $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
 
15497
                $mode = 'D';
 
15498
                $style = array();
 
15499
                switch ($head_style) {
 
15500
                        case 0: {
 
15501
                                // draw only arrowhead arms
 
15502
                                $mode = 'D';
 
15503
                                $style = array(1, 1, 0);
 
15504
                                break;
 
15505
                        }
 
15506
                        case 1: {
 
15507
                                // draw closed arrowhead, but no fill
 
15508
                                $mode = 'D';
 
15509
                                break;
 
15510
                        }
 
15511
                        case 2: {
 
15512
                                // closed and filled arrowhead
 
15513
                                $mode = 'DF';
 
15514
                                break;
 
15515
                        }
 
15516
                        case 3: {
 
15517
                                // filled arrowhead
 
15518
                                $mode = 'F';
 
15519
                                break;
 
15520
                        }
 
15521
                }
 
15522
                $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
 
15523
        }
 
15524
 
 
15525
        // END GRAPHIC FUNCTIONS SECTION -----------------------
 
15526
 
 
15527
        // BIDIRECTIONAL TEXT SECTION --------------------------
 
15528
 
 
15529
        /**
 
15530
         * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
 
15531
         * @param $str (string) string to manipulate.
 
15532
         * @param $setbom (bool) if true set the Byte Order Mark (BOM = 0xFEFF)
 
15533
         * @param $forcertl (bool) if true forces RTL text direction
 
15534
         * @return string
 
15535
         * @protected
 
15536
         * @author Nicola Asuni
 
15537
         * @since 2.1.000 (2008-01-08)
 
15538
         */
 
15539
        protected function utf8StrRev($str, $setbom=false, $forcertl=false) {
 
15540
                return $this->utf8StrArrRev($this->UTF8StringToArray($str), $str, $setbom, $forcertl);
 
15541
        }
 
15542
 
 
15543
        /**
 
15544
         * Reverse the RLT substrings array using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
 
15545
         * @param $arr (array) array of unicode values.
 
15546
         * @param $str (string) string to manipulate (or empty value).
 
15547
         * @param $setbom (bool) if true set the Byte Order Mark (BOM = 0xFEFF)
 
15548
         * @param $forcertl (bool) if true forces RTL text direction
 
15549
         * @return string
 
15550
         * @protected
 
15551
         * @author Nicola Asuni
 
15552
         * @since 4.9.000 (2010-03-27)
 
15553
         */
 
15554
        protected function utf8StrArrRev($arr, $str='', $setbom=false, $forcertl=false) {
 
15555
                return $this->arrUTF8ToUTF16BE($this->utf8Bidi($arr, $str, $forcertl), $setbom);
 
15556
        }
 
15557
 
 
15558
        /**
 
15559
         * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
 
15560
         * @param $ta (array) array of characters composing the string.
 
15561
         * @param $str (string) string to process
 
15562
         * @param $forcertl (bool) if 'R' forces RTL, if 'L' forces LTR
 
15563
         * @return array of unicode chars
 
15564
         * @author Nicola Asuni
 
15565
         * @protected
 
15566
         * @since 2.4.000 (2008-03-06)
 
15567
         */
 
15568
        protected function utf8Bidi($ta, $str='', $forcertl=false) {
 
15569
                // paragraph embedding level
 
15570
                $pel = 0;
 
15571
                // max level
 
15572
                $maxlevel = 0;
 
15573
                if ($this->empty_string($str)) {
 
15574
                        // create string from array
 
15575
                        $str = $this->UTF8ArrSubString($ta);
 
15576
                }
 
15577
                // check if string contains arabic text
 
15578
                if (preg_match($this->unicode->uni_RE_PATTERN_ARABIC, $str)) {
 
15579
                        $arabic = true;
 
15580
                } else {
 
15581
                        $arabic = false;
 
15582
                }
 
15583
                // check if string contains RTL text
 
15584
                if (!($forcertl OR $arabic OR preg_match($this->unicode->uni_RE_PATTERN_RTL, $str))) {
 
15585
                        return $ta;
 
15586
                }
 
15587
 
 
15588
                // get number of chars
 
15589
                $numchars = count($ta);
 
15590
 
 
15591
                if ($forcertl == 'R') {
 
15592
                        $pel = 1;
 
15593
                } elseif ($forcertl == 'L') {
 
15594
                        $pel = 0;
 
15595
                } else {
 
15596
                        // P2. In each paragraph, find the first character of type L, AL, or R.
 
15597
                        // P3. If a character is found in P2 and it is of type AL or R, then set the paragraph embedding level to one; otherwise, set it to zero.
 
15598
                        for ($i=0; $i < $numchars; ++$i) {
 
15599
                                $type = $this->unicode->uni_type[$ta[$i]];
 
15600
                                if ($type == 'L') {
 
15601
                                        $pel = 0;
 
15602
                                        break;
 
15603
                                } elseif (($type == 'AL') OR ($type == 'R')) {
 
15604
                                        $pel = 1;
 
15605
                                        break;
 
15606
                                }
 
15607
                        }
 
15608
                }
 
15609
 
 
15610
                // Current Embedding Level
 
15611
                $cel = $pel;
 
15612
                // directional override status
 
15613
                $dos = 'N';
 
15614
                $remember = array();
 
15615
                // start-of-level-run
 
15616
                $sor = $pel % 2 ? 'R' : 'L';
 
15617
                $eor = $sor;
 
15618
 
 
15619
                // Array of characters data
 
15620
                $chardata = Array();
 
15621
 
 
15622
                // X1. Begin by setting the current embedding level to the paragraph embedding level. Set the directional override status to neutral. Process each character iteratively, applying rules X2 through X9. Only embedding levels from 0 to 61 are valid in this phase.
 
15623
                // In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
 
15624
                for ($i=0; $i < $numchars; ++$i) {
 
15625
                        if ($ta[$i] == $this->unicode->uni_RLE) {
 
15626
                                // X2. With each RLE, compute the least greater odd embedding level.
 
15627
                                //      a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
 
15628
                                //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
 
15629
                                $next_level = $cel + ($cel % 2) + 1;
 
15630
                                if ($next_level < 62) {
 
15631
                                        $remember[] = array('num' => $this->unicode->uni_RLE, 'cel' => $cel, 'dos' => $dos);
 
15632
                                        $cel = $next_level;
 
15633
                                        $dos = 'N';
 
15634
                                        $sor = $eor;
 
15635
                                        $eor = $cel % 2 ? 'R' : 'L';
 
15636
                                }
 
15637
                        } elseif ($ta[$i] == $this->unicode->uni_LRE) {
 
15638
                                // X3. With each LRE, compute the least greater even embedding level.
 
15639
                                //      a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
 
15640
                                //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
 
15641
                                $next_level = $cel + 2 - ($cel % 2);
 
15642
                                if ( $next_level < 62 ) {
 
15643
                                        $remember[] = array('num' => $this->unicode->uni_LRE, 'cel' => $cel, 'dos' => $dos);
 
15644
                                        $cel = $next_level;
 
15645
                                        $dos = 'N';
 
15646
                                        $sor = $eor;
 
15647
                                        $eor = $cel % 2 ? 'R' : 'L';
 
15648
                                }
 
15649
                        } elseif ($ta[$i] == $this->unicode->uni_RLO) {
 
15650
                                // X4. With each RLO, compute the least greater odd embedding level.
 
15651
                                //      a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to right-to-left.
 
15652
                                //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
 
15653
                                $next_level = $cel + ($cel % 2) + 1;
 
15654
                                if ($next_level < 62) {
 
15655
                                        $remember[] = array('num' => $this->unicode->uni_RLO, 'cel' => $cel, 'dos' => $dos);
 
15656
                                        $cel = $next_level;
 
15657
                                        $dos = 'R';
 
15658
                                        $sor = $eor;
 
15659
                                        $eor = $cel % 2 ? 'R' : 'L';
 
15660
                                }
 
15661
                        } elseif ($ta[$i] == $this->unicode->uni_LRO) {
 
15662
                                // X5. With each LRO, compute the least greater even embedding level.
 
15663
                                //      a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to left-to-right.
 
15664
                                //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
 
15665
                                $next_level = $cel + 2 - ($cel % 2);
 
15666
                                if ( $next_level < 62 ) {
 
15667
                                        $remember[] = array('num' => $this->unicode->uni_LRO, 'cel' => $cel, 'dos' => $dos);
 
15668
                                        $cel = $next_level;
 
15669
                                        $dos = 'L';
 
15670
                                        $sor = $eor;
 
15671
                                        $eor = $cel % 2 ? 'R' : 'L';
 
15672
                                }
 
15673
                        } elseif ($ta[$i] == $this->unicode->uni_PDF) {
 
15674
                                // X7. With each PDF, determine the matching embedding or override code. If there was a valid matching code, restore (pop) the last remembered (pushed) embedding level and directional override.
 
15675
                                if (count($remember)) {
 
15676
                                        $last = count($remember ) - 1;
 
15677
                                        if (($remember[$last]['num'] == $this->unicode->uni_RLE) OR
 
15678
                                                ($remember[$last]['num'] == $this->unicode->uni_LRE) OR
 
15679
                                                ($remember[$last]['num'] == $this->unicode->uni_RLO) OR
 
15680
                                                ($remember[$last]['num'] == $this->unicode->uni_LRO)) {
 
15681
                                                $match = array_pop($remember);
 
15682
                                                $cel = $match['cel'];
 
15683
                                                $dos = $match['dos'];
 
15684
                                                $sor = $eor;
 
15685
                                                $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
 
15686
                                        }
 
15687
                                }
 
15688
                        } elseif (($ta[$i] != $this->unicode->uni_RLE) AND
 
15689
                                                         ($ta[$i] != $this->unicode->uni_LRE) AND
 
15690
                                                         ($ta[$i] != $this->unicode->uni_RLO) AND
 
15691
                                                         ($ta[$i] != $this->unicode->uni_LRO) AND
 
15692
                                                         ($ta[$i] != $this->unicode->uni_PDF)) {
 
15693
                                // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
 
15694
                                //      a. Set the level of the current character to the current embedding level.
 
15695
                                //      b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
 
15696
                                if ($dos != 'N') {
 
15697
                                        $chardir = $dos;
 
15698
                                } else {
 
15699
                                        if (isset($this->unicode->uni_type[$ta[$i]])) {
 
15700
                                                $chardir = $this->unicode->uni_type[$ta[$i]];
 
15701
                                        } else {
 
15702
                                                $chardir = 'L';
 
15703
                                        }
 
15704
                                }
 
15705
                                // stores string characters and other information
 
15706
                                $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
 
15707
                        }
 
15708
                } // end for each char
 
15709
 
 
15710
                // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
 
15711
                // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
 
15712
                // X10. The remaining rules are applied to each run of characters at the same level. For each run, determine the start-of-level-run (sor) and end-of-level-run (eor) type, either L or R. This depends on the higher of the two levels on either side of the boundary (at the start or end of the paragraph, the level of the 'other' run is the base embedding level). If the higher level is odd, the type is R; otherwise, it is L.
 
15713
 
 
15714
                // 3.3.3 Resolving Weak Types
 
15715
                // Weak types are now resolved one level run at a time. At level run boundaries where the type of the character on the other side of the boundary is required, the type assigned to sor or eor is used.
 
15716
                // Nonspacing marks are now resolved based on the previous characters.
 
15717
                $numchars = count($chardata);
 
15718
 
 
15719
                // W1. Examine each nonspacing mark (NSM) in the level run, and change the type of the NSM to the type of the previous character. If the NSM is at the start of the level run, it will get the type of sor.
 
15720
                $prevlevel = -1; // track level changes
 
15721
                $levcount = 0; // counts consecutive chars at the same level
 
15722
                for ($i=0; $i < $numchars; ++$i) {
 
15723
                        if ($chardata[$i]['type'] == 'NSM') {
 
15724
                                if ($levcount) {
 
15725
                                        $chardata[$i]['type'] = $chardata[$i]['sor'];
 
15726
                                } elseif ($i > 0) {
 
15727
                                        $chardata[$i]['type'] = $chardata[($i-1)]['type'];
 
15728
                                }
 
15729
                        }
 
15730
                        if ($chardata[$i]['level'] != $prevlevel) {
 
15731
                                $levcount = 0;
 
15732
                        } else {
 
15733
                                ++$levcount;
 
15734
                        }
 
15735
                        $prevlevel = $chardata[$i]['level'];
 
15736
                }
 
15737
 
 
15738
                // W2. Search backward from each instance of a European number until the first strong type (R, L, AL, or sor) is found. If an AL is found, change the type of the European number to Arabic number.
 
15739
                $prevlevel = -1;
 
15740
                $levcount = 0;
 
15741
                for ($i=0; $i < $numchars; ++$i) {
 
15742
                        if ($chardata[$i]['char'] == 'EN') {
 
15743
                                for ($j=$levcount; $j >= 0; $j--) {
 
15744
                                        if ($chardata[$j]['type'] == 'AL') {
 
15745
                                                $chardata[$i]['type'] = 'AN';
 
15746
                                        } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
 
15747
                                                break;
 
15748
                                        }
 
15749
                                }
 
15750
                        }
 
15751
                        if ($chardata[$i]['level'] != $prevlevel) {
 
15752
                                $levcount = 0;
 
15753
                        } else {
 
15754
                                ++$levcount;
 
15755
                        }
 
15756
                        $prevlevel = $chardata[$i]['level'];
 
15757
                }
 
15758
 
 
15759
                // W3. Change all ALs to R.
 
15760
                for ($i=0; $i < $numchars; ++$i) {
 
15761
                        if ($chardata[$i]['type'] == 'AL') {
 
15762
                                $chardata[$i]['type'] = 'R';
 
15763
                        }
 
15764
                }
 
15765
 
 
15766
                // W4. A single European separator between two European numbers changes to a European number. A single common separator between two numbers of the same type changes to that type.
 
15767
                $prevlevel = -1;
 
15768
                $levcount = 0;
 
15769
                for ($i=0; $i < $numchars; ++$i) {
 
15770
                        if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
 
15771
                                if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
 
15772
                                        $chardata[$i]['type'] = 'EN';
 
15773
                                } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
 
15774
                                        $chardata[$i]['type'] = 'EN';
 
15775
                                } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
 
15776
                                        $chardata[$i]['type'] = 'AN';
 
15777
                                }
 
15778
                        }
 
15779
                        if ($chardata[$i]['level'] != $prevlevel) {
 
15780
                                $levcount = 0;
 
15781
                        } else {
 
15782
                                ++$levcount;
 
15783
                        }
 
15784
                        $prevlevel = $chardata[$i]['level'];
 
15785
                }
 
15786
 
 
15787
                // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
 
15788
                $prevlevel = -1;
 
15789
                $levcount = 0;
 
15790
                for ($i=0; $i < $numchars; ++$i) {
 
15791
                        if ($chardata[$i]['type'] == 'ET') {
 
15792
                                if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
 
15793
                                        $chardata[$i]['type'] = 'EN';
 
15794
                                } else {
 
15795
                                        $j = $i+1;
 
15796
                                        while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
 
15797
                                                if ($chardata[$j]['type'] == 'EN') {
 
15798
                                                        $chardata[$i]['type'] = 'EN';
 
15799
                                                        break;
 
15800
                                                } elseif ($chardata[$j]['type'] != 'ET') {
 
15801
                                                        break;
 
15802
                                                }
 
15803
                                                ++$j;
 
15804
                                        }
 
15805
                                }
 
15806
                        }
 
15807
                        if ($chardata[$i]['level'] != $prevlevel) {
 
15808
                                $levcount = 0;
 
15809
                        } else {
 
15810
                                ++$levcount;
 
15811
                        }
 
15812
                        $prevlevel = $chardata[$i]['level'];
 
15813
                }
 
15814
 
 
15815
                // W6. Otherwise, separators and terminators change to Other Neutral.
 
15816
                $prevlevel = -1;
 
15817
                $levcount = 0;
 
15818
                for ($i=0; $i < $numchars; ++$i) {
 
15819
                        if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
 
15820
                                $chardata[$i]['type'] = 'ON';
 
15821
                        }
 
15822
                        if ($chardata[$i]['level'] != $prevlevel) {
 
15823
                                $levcount = 0;
 
15824
                        } else {
 
15825
                                ++$levcount;
 
15826
                        }
 
15827
                        $prevlevel = $chardata[$i]['level'];
 
15828
                }
 
15829
 
 
15830
                //W7. Search backward from each instance of a European number until the first strong type (R, L, or sor) is found. If an L is found, then change the type of the European number to L.
 
15831
                $prevlevel = -1;
 
15832
                $levcount = 0;
 
15833
                for ($i=0; $i < $numchars; ++$i) {
 
15834
                        if ($chardata[$i]['char'] == 'EN') {
 
15835
                                for ($j=$levcount; $j >= 0; $j--) {
 
15836
                                        if ($chardata[$j]['type'] == 'L') {
 
15837
                                                $chardata[$i]['type'] = 'L';
 
15838
                                        } elseif ($chardata[$j]['type'] == 'R') {
 
15839
                                                break;
 
15840
                                        }
 
15841
                                }
 
15842
                        }
 
15843
                        if ($chardata[$i]['level'] != $prevlevel) {
 
15844
                                $levcount = 0;
 
15845
                        } else {
 
15846
                                ++$levcount;
 
15847
                        }
 
15848
                        $prevlevel = $chardata[$i]['level'];
 
15849
                }
 
15850
 
 
15851
                // N1. A sequence of neutrals takes the direction of the surrounding strong text if the text on both sides has the same direction. European and Arabic numbers act as if they were R in terms of their influence on neutrals. Start-of-level-run (sor) and end-of-level-run (eor) are used at level run boundaries.
 
15852
                $prevlevel = -1;
 
15853
                $levcount = 0;
 
15854
                for ($i=0; $i < $numchars; ++$i) {
 
15855
                        if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
 
15856
                                if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
 
15857
                                        $chardata[$i]['type'] = 'L';
 
15858
                                } elseif (($chardata[$i]['type'] == 'N') AND
 
15859
                                 (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
 
15860
                                 (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
 
15861
                                        $chardata[$i]['type'] = 'R';
 
15862
                                } elseif ($chardata[$i]['type'] == 'N') {
 
15863
                                        // N2. Any remaining neutrals take the embedding direction
 
15864
                                        $chardata[$i]['type'] = $chardata[$i]['sor'];
 
15865
                                }
 
15866
                        } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
 
15867
                                // first char
 
15868
                                if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
 
15869
                                        $chardata[$i]['type'] = 'L';
 
15870
                                } elseif (($chardata[$i]['type'] == 'N') AND
 
15871
                                 (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
 
15872
                                 (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
 
15873
                                        $chardata[$i]['type'] = 'R';
 
15874
                                } elseif ($chardata[$i]['type'] == 'N') {
 
15875
                                        // N2. Any remaining neutrals take the embedding direction
 
15876
                                        $chardata[$i]['type'] = $chardata[$i]['sor'];
 
15877
                                }
 
15878
                        } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
 
15879
                                //last char
 
15880
                                if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
 
15881
                                        $chardata[$i]['type'] = 'L';
 
15882
                                } elseif (($chardata[$i]['type'] == 'N') AND
 
15883
                                 (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
 
15884
                                 (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
 
15885
                                        $chardata[$i]['type'] = 'R';
 
15886
                                } elseif ($chardata[$i]['type'] == 'N') {
 
15887
                                        // N2. Any remaining neutrals take the embedding direction
 
15888
                                        $chardata[$i]['type'] = $chardata[$i]['sor'];
 
15889
                                }
 
15890
                        } elseif ($chardata[$i]['type'] == 'N') {
 
15891
                                // N2. Any remaining neutrals take the embedding direction
 
15892
                                $chardata[$i]['type'] = $chardata[$i]['sor'];
 
15893
                        }
 
15894
                        if ($chardata[$i]['level'] != $prevlevel) {
 
15895
                                $levcount = 0;
 
15896
                        } else {
 
15897
                                ++$levcount;
 
15898
                        }
 
15899
                        $prevlevel = $chardata[$i]['level'];
 
15900
                }
 
15901
 
 
15902
                // I1. For all characters with an even (left-to-right) embedding direction, those of type R go up one level and those of type AN or EN go up two levels.
 
15903
                // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
 
15904
                for ($i=0; $i < $numchars; ++$i) {
 
15905
                        $odd = $chardata[$i]['level'] % 2;
 
15906
                        if ($odd) {
 
15907
                                if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
 
15908
                                        $chardata[$i]['level'] += 1;
 
15909
                                }
 
15910
                        } else {
 
15911
                                if ($chardata[$i]['type'] == 'R') {
 
15912
                                        $chardata[$i]['level'] += 1;
 
15913
                                } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
 
15914
                                        $chardata[$i]['level'] += 2;
 
15915
                                }
 
15916
                        }
 
15917
                        $maxlevel = max($chardata[$i]['level'],$maxlevel);
 
15918
                }
 
15919
 
 
15920
                // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
 
15921
                //      1. Segment separators,
 
15922
                //      2. Paragraph separators,
 
15923
                //      3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
 
15924
                //      4. Any sequence of white space characters at the end of the line.
 
15925
                for ($i=0; $i < $numchars; ++$i) {
 
15926
                        if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
 
15927
                                $chardata[$i]['level'] = $pel;
 
15928
                        } elseif ($chardata[$i]['type'] == 'WS') {
 
15929
                                $j = $i+1;
 
15930
                                while ($j < $numchars) {
 
15931
                                        if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
 
15932
                                                (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
 
15933
                                                $chardata[$i]['level'] = $pel;
 
15934
                                                break;
 
15935
                                        } elseif ($chardata[$j]['type'] != 'WS') {
 
15936
                                                break;
 
15937
                                        }
 
15938
                                        ++$j;
 
15939
                                }
 
15940
                        }
 
15941
                }
 
15942
 
 
15943
                // Arabic Shaping
 
15944
                // Cursively connected scripts, such as Arabic or Syriac, require the selection of positional character shapes that depend on adjacent characters. Shaping is logically applied after the Bidirectional Algorithm is used and is limited to characters within the same directional run.
 
15945
                if ($arabic) {
 
15946
                        $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
 
15947
                        $alfletter = array(1570,1571,1573,1575);
 
15948
                        $chardata2 = $chardata;
 
15949
                        $laaletter = false;
 
15950
                        $charAL = array();
 
15951
                        $x = 0;
 
15952
                        for ($i=0; $i < $numchars; ++$i) {
 
15953
                                if (($this->unicode->uni_type[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
 
15954
                                        $charAL[$x] = $chardata[$i];
 
15955
                                        $charAL[$x]['i'] = $i;
 
15956
                                        $chardata[$i]['x'] = $x;
 
15957
                                        ++$x;
 
15958
                                }
 
15959
                        }
 
15960
                        $numAL = $x;
 
15961
                        for ($i=0; $i < $numchars; ++$i) {
 
15962
                                $thischar = $chardata[$i];
 
15963
                                if ($i > 0) {
 
15964
                                        $prevchar = $chardata[($i-1)];
 
15965
                                } else {
 
15966
                                        $prevchar = false;
 
15967
                                }
 
15968
                                if (($i+1) < $numchars) {
 
15969
                                        $nextchar = $chardata[($i+1)];
 
15970
                                } else {
 
15971
                                        $nextchar = false;
 
15972
                                }
 
15973
                                if ($this->unicode->uni_type[$thischar['char']] == 'AL') {
 
15974
                                        $x = $thischar['x'];
 
15975
                                        if ($x > 0) {
 
15976
                                                $prevchar = $charAL[($x-1)];
 
15977
                                        } else {
 
15978
                                                $prevchar = false;
 
15979
                                        }
 
15980
                                        if (($x+1) < $numAL) {
 
15981
                                                $nextchar = $charAL[($x+1)];
 
15982
                                        } else {
 
15983
                                                $nextchar = false;
 
15984
                                        }
 
15985
                                        // if laa letter
 
15986
                                        if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
 
15987
                                                $arabicarr = $this->unicode->uni_laa_array;
 
15988
                                                $laaletter = true;
 
15989
                                                if ($x > 1) {
 
15990
                                                        $prevchar = $charAL[($x-2)];
 
15991
                                                } else {
 
15992
                                                        $prevchar = false;
 
15993
                                                }
 
15994
                                        } else {
 
15995
                                                $arabicarr = $this->unicode->uni_arabicsubst;
 
15996
                                                $laaletter = false;
 
15997
                                        }
 
15998
                                        if (($prevchar !== false) AND ($nextchar !== false) AND
 
15999
                                                (($this->unicode->uni_type[$prevchar['char']] == 'AL') OR ($this->unicode->uni_type[$prevchar['char']] == 'NSM')) AND
 
16000
                                                (($this->unicode->uni_type[$nextchar['char']] == 'AL') OR ($this->unicode->uni_type[$nextchar['char']] == 'NSM')) AND
 
16001
                                                ($prevchar['type'] == $thischar['type']) AND
 
16002
                                                ($nextchar['type'] == $thischar['type']) AND
 
16003
                                                ($nextchar['char'] != 1567)) {
 
16004
                                                if (in_array($prevchar['char'], $endedletter)) {
 
16005
                                                        if (isset($arabicarr[$thischar['char']][2])) {
 
16006
                                                                // initial
 
16007
                                                                $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
 
16008
                                                        }
 
16009
                                                } else {
 
16010
                                                        if (isset($arabicarr[$thischar['char']][3])) {
 
16011
                                                                // medial
 
16012
                                                                $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
 
16013
                                                        }
 
16014
                                                }
 
16015
                                        } elseif (($nextchar !== false) AND
 
16016
                                                (($this->unicode->uni_type[$nextchar['char']] == 'AL') OR ($this->unicode->uni_type[$nextchar['char']] == 'NSM')) AND
 
16017
                                                ($nextchar['type'] == $thischar['type']) AND
 
16018
                                                ($nextchar['char'] != 1567)) {
 
16019
                                                if (isset($arabicarr[$chardata[$i]['char']][2])) {
 
16020
                                                        // initial
 
16021
                                                        $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
 
16022
                                                }
 
16023
                                        } elseif ((($prevchar !== false) AND
 
16024
                                                (($this->unicode->uni_type[$prevchar['char']] == 'AL') OR ($this->unicode->uni_type[$prevchar['char']] == 'NSM')) AND
 
16025
                                                ($prevchar['type'] == $thischar['type'])) OR
 
16026
                                                (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
 
16027
                                                // final
 
16028
                                                if (($i > 1) AND ($thischar['char'] == 1607) AND
 
16029
                                                        ($chardata[$i-1]['char'] == 1604) AND
 
16030
                                                        ($chardata[$i-2]['char'] == 1604)) {
 
16031
                                                        //Allah Word
 
16032
                                                        // mark characters to delete with false
 
16033
                                                        $chardata2[$i-2]['char'] = false;
 
16034
                                                        $chardata2[$i-1]['char'] = false;
 
16035
                                                        $chardata2[$i]['char'] = 65010;
 
16036
                                                } else {
 
16037
                                                        if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
 
16038
                                                                if (isset($arabicarr[$thischar['char']][0])) {
 
16039
                                                                        // isolated
 
16040
                                                                        $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
 
16041
                                                                }
 
16042
                                                        } else {
 
16043
                                                                if (isset($arabicarr[$thischar['char']][1])) {
 
16044
                                                                        // final
 
16045
                                                                        $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
 
16046
                                                                }
 
16047
                                                        }
 
16048
                                                }
 
16049
                                        } elseif (isset($arabicarr[$thischar['char']][0])) {
 
16050
                                                // isolated
 
16051
                                                $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
 
16052
                                        }
 
16053
                                        // if laa letter
 
16054
                                        if ($laaletter) {
 
16055
                                                // mark characters to delete with false
 
16056
                                                $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
 
16057
                                        }
 
16058
                                } // end if AL (Arabic Letter)
 
16059
                        } // end for each char
 
16060
                        /*
 
16061
                         * Combining characters that can occur with Arabic Shadda (0651 HEX, 1617 DEC) are replaced.
 
16062
                         * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
 
16063
                         */
 
16064
                        for ($i = 0; $i < ($numchars-1); ++$i) {
 
16065
                                if (($chardata2[$i]['char'] == 1617) AND (isset($this->unicode->uni_diacritics[($chardata2[$i+1]['char'])]))) {
 
16066
                                        // check if the subtitution font is defined on current font
 
16067
                                        if (isset($this->CurrentFont['cw'][($this->unicode->uni_diacritics[($chardata2[$i+1]['char'])])])) {
 
16068
                                                $chardata2[$i]['char'] = false;
 
16069
                                                $chardata2[$i+1]['char'] = $this->unicode->uni_diacritics[($chardata2[$i+1]['char'])];
 
16070
                                        }
 
16071
                                }
 
16072
                        }
 
16073
                        // remove marked characters
 
16074
                        foreach ($chardata2 as $key => $value) {
 
16075
                                if ($value['char'] === false) {
 
16076
                                        unset($chardata2[$key]);
 
16077
                                }
 
16078
                        }
 
16079
                        $chardata = array_values($chardata2);
 
16080
                        $numchars = count($chardata);
 
16081
                        unset($chardata2);
 
16082
                        unset($arabicarr);
 
16083
                        unset($laaletter);
 
16084
                        unset($charAL);
 
16085
                }
 
16086
 
 
16087
                // L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher.
 
16088
                for ($j=$maxlevel; $j > 0; $j--) {
 
16089
                        $ordarray = Array();
 
16090
                        $revarr = Array();
 
16091
                        $onlevel = false;
 
16092
                        for ($i=0; $i < $numchars; ++$i) {
 
16093
                                if ($chardata[$i]['level'] >= $j) {
 
16094
                                        $onlevel = true;
 
16095
                                        if (isset($this->unicode->uni_mirror[$chardata[$i]['char']])) {
 
16096
                                                // L4. A character is depicted by a mirrored glyph if and only if (a) the resolved directionality of that character is R, and (b) the Bidi_Mirrored property value of that character is true.
 
16097
                                                $chardata[$i]['char'] = $this->unicode->uni_mirror[$chardata[$i]['char']];
 
16098
                                        }
 
16099
                                        $revarr[] = $chardata[$i];
 
16100
                                } else {
 
16101
                                        if ($onlevel) {
 
16102
                                                $revarr = array_reverse($revarr);
 
16103
                                                $ordarray = array_merge($ordarray, $revarr);
 
16104
                                                $revarr = Array();
 
16105
                                                $onlevel = false;
 
16106
                                        }
 
16107
                                        $ordarray[] = $chardata[$i];
 
16108
                                }
 
16109
                        }
 
16110
                        if ($onlevel) {
 
16111
                                $revarr = array_reverse($revarr);
 
16112
                                $ordarray = array_merge($ordarray, $revarr);
 
16113
                        }
 
16114
                        $chardata = $ordarray;
 
16115
                }
 
16116
 
 
16117
                $ordarray = array();
 
16118
                for ($i=0; $i < $numchars; ++$i) {
 
16119
                        $ordarray[] = $chardata[$i]['char'];
 
16120
                        // store char values for subsetting
 
16121
                        $this->CurrentFont['subsetchars'][$chardata[$i]['char']] = true;
 
16122
                }
 
16123
                // update font subsetchars
 
16124
                $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
 
16125
                return $ordarray;
 
16126
        }
 
16127
 
 
16128
        // END OF BIDIRECTIONAL TEXT SECTION -------------------
 
16129
 
 
16130
        /**
 
16131
         * Encode a name object.
 
16132
         * @param $name (string) Name object to encode.
 
16133
         * @return (string) Encoded name object.
 
16134
         * @protected
 
16135
         * @author Nicola Asuni
 
16136
         * @since 5.9.097 (2011-06-23)
 
16137
         */
 
16138
        protected function encodeNameObject($name) {
 
16139
                $escname = '';
 
16140
                $length = strlen($name);
 
16141
                for ($i = 0; $i < $length; ++$i) {
 
16142
                        $chr = $name[$i];
 
16143
                        if (preg_match('/[0-9a-zA-Z]/', $chr) == 1) {
 
16144
                                $escname .= $chr;
 
16145
                        } else {
 
16146
                                $escname .= sprintf('#%02X', ord($chr));
 
16147
                        }
 
16148
                }
 
16149
                return $escname;
 
16150
        }
 
16151
 
 
16152
        /**
 
16153
         * Add a Named Destination.
 
16154
         * NOTE: destination names are unique, so only last entry will be saved.
 
16155
         * @param $name (string) Destination name.
 
16156
         * @param $y (float) Y position in user units of the destiantion on the selected page (default = -1 = current position; 0 = page start;).
 
16157
         * @param $page (int) Target page number (leave empty for current page).
 
16158
         * @return (string) Stripped named destination identifier or false in case of error.
 
16159
         * @public
 
16160
         * @author Christian Deligant, Nicola Asuni
 
16161
         * @since 5.9.097 (2011-06-23)
 
16162
         */
 
16163
        public function setDestination($name, $y=-1, $page='') {
 
16164
                // remove unsupported characters
 
16165
                $name = $this->encodeNameObject($name);
 
16166
                if ($this->empty_string($name)) {
 
16167
                        return false;
 
16168
                }
 
16169
                if ($y == -1) {
 
16170
                        $y = $this->GetY();
 
16171
                }
 
16172
                if (empty($page)) {
 
16173
                        $page = $this->PageNo();
 
16174
                        if (empty($page)) {
 
16175
                                return;
 
16176
                        }
 
16177
                }
 
16178
                $this->dests[$name] = array('y' => $y, 'p' => $page);
 
16179
                return $name;
 
16180
        }
 
16181
 
 
16182
        /**
 
16183
         * Return the Named Destination array.
 
16184
         * @return (array) Named Destination array.
 
16185
         * @public
 
16186
         * @author Nicola Asuni
 
16187
         * @since 5.9.097 (2011-06-23)
 
16188
         */
 
16189
        public function getDestination() {
 
16190
                return $this->dests;
 
16191
        }
 
16192
 
 
16193
        /**
 
16194
         * Create a javascript PDF string.
 
16195
         * @protected
 
16196
         * @author Johannes G�ntert, Nicola Asuni
 
16197
         * @since 5.9.098 (2011-06-23)
 
16198
         */
 
16199
        protected function _putdests() {
 
16200
                if (empty($this->dests)) {
 
16201
                        return;
 
16202
                }
 
16203
                $this->n_dests = $this->_newobj();
 
16204
                $out = ' <<';
 
16205
                foreach($this->dests as $name => $o) {
 
16206
                        $out .= ' /'.$name.' '.sprintf('[%u 0 R /XYZ 0 %.2F null]', $this->page_obj_id[($o['p'])], ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
 
16207
                }
 
16208
                $out .= ' >>';
 
16209
                $out .= "\n".'endobj';
 
16210
                $this->_out($out);
 
16211
        }
 
16212
 
 
16213
        /**
 
16214
         * Adds a bookmark - alias for Bookmark().
 
16215
         * @param $txt (string) Bookmark description.
 
16216
         * @param $level (int) Bookmark level (minimum value is 0).
 
16217
         * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
 
16218
         * @param $page (int) Target page number (leave empty for current page).
 
16219
         * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic.
 
16220
         * @param $color (array) RGB color array (values from 0 to 255).
 
16221
         * @public
 
16222
         */
 
16223
        public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0)) {
 
16224
                $this->Bookmark($txt, $level, $y, $page, $style, $color);
 
16225
        }
 
16226
 
 
16227
        /**
 
16228
         * Adds a bookmark.
 
16229
         * @param $txt (string) Bookmark description.
 
16230
         * @param $level (int) Bookmark level (minimum value is 0).
 
16231
         * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
 
16232
         * @param $page (int) Target page number (leave empty for current page).
 
16233
         * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic.
 
16234
         * @param $color (array) RGB color array (values from 0 to 255).
 
16235
         * @public
 
16236
         * @author Olivier Plathey, Nicola Asuni
 
16237
         * @since 2.1.002 (2008-02-12)
 
16238
         */
 
16239
        public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0)) {
 
16240
                if ($level < 0) {
 
16241
                        $level = 0;
 
16242
                }
 
16243
                if (isset($this->outlines[0])) {
 
16244
                        $lastoutline = end($this->outlines);
 
16245
                        $maxlevel = $lastoutline['l'] + 1;
 
16246
                } else {
 
16247
                        $maxlevel = 0;
 
16248
                }
 
16249
                if ($level > $maxlevel) {
 
16250
                        $level = $maxlevel;
 
16251
                }
 
16252
                if ($y == -1) {
 
16253
                        $y = $this->GetY();
 
16254
                }
 
16255
                if (empty($page)) {
 
16256
                        $page = $this->PageNo();
 
16257
                        if (empty($page)) {
 
16258
                                return;
 
16259
                        }
 
16260
                }
 
16261
                $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $page, 's' => strtoupper($style), 'c' => $color);
 
16262
        }
 
16263
 
 
16264
        /**
 
16265
         * Sort bookmarks for page and key.
 
16266
         * @protected
 
16267
         * @since 5.9.119 (2011-09-19)
 
16268
         */
 
16269
        protected function sortBookmarks() {
 
16270
                // get sorting columns
 
16271
                $outline_p = array();
 
16272
                $outline_y = array();
 
16273
                foreach ($this->outlines as $key => $row) {
 
16274
                        $outline_p[$key] = $row['p'];
 
16275
                        $outline_k[$key] = $key;
 
16276
                }
 
16277
                // sort outlines by page and original position
 
16278
                array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines);
 
16279
        }
 
16280
 
 
16281
        /**
 
16282
         * Create a bookmark PDF string.
 
16283
         * @protected
 
16284
         * @author Olivier Plathey, Nicola Asuni
 
16285
         * @since 2.1.002 (2008-02-12)
 
16286
         */
 
16287
        protected function _putbookmarks() {
 
16288
                $nb = count($this->outlines);
 
16289
                if ($nb == 0) {
 
16290
                        return;
 
16291
                }
 
16292
                // sort bookmarks
 
16293
                $this->sortBookmarks();
 
16294
                $lru = array();
 
16295
                $level = 0;
 
16296
                foreach ($this->outlines as $i => $o) {
 
16297
                        if ($o['l'] > 0) {
 
16298
                                $parent = $lru[($o['l'] - 1)];
 
16299
                                //Set parent and last pointers
 
16300
                                $this->outlines[$i]['parent'] = $parent;
 
16301
                                $this->outlines[$parent]['last'] = $i;
 
16302
                                if ($o['l'] > $level) {
 
16303
                                        //Level increasing: set first pointer
 
16304
                                        $this->outlines[$parent]['first'] = $i;
 
16305
                                }
 
16306
                        } else {
 
16307
                                $this->outlines[$i]['parent'] = $nb;
 
16308
                        }
 
16309
                        if (($o['l'] <= $level) AND ($i > 0)) {
 
16310
                                //Set prev and next pointers
 
16311
                                $prev = $lru[$o['l']];
 
16312
                                $this->outlines[$prev]['next'] = $i;
 
16313
                                $this->outlines[$i]['prev'] = $prev;
 
16314
                        }
 
16315
                        $lru[$o['l']] = $i;
 
16316
                        $level = $o['l'];
 
16317
                }
 
16318
                //Outline items
 
16319
                $n = $this->n + 1;
 
16320
                $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';
 
16321
                foreach ($this->outlines as $i => $o) {
 
16322
                        $oid = $this->_newobj();
 
16323
                        // covert HTML title to string
 
16324
                        $title = preg_replace($nltags, "\n", $o['t']);
 
16325
                        $title = preg_replace("/[\r]+/si", '', $title);
 
16326
                        $title = preg_replace("/[\n]+/si", "\n", $title);
 
16327
                        $title = strip_tags($title);
 
16328
                        $title = $this->stringTrim($title);
 
16329
                        $out = '<</Title '.$this->_textstring($title, $oid);
 
16330
                        $out .= ' /Parent '.($n + $o['parent']).' 0 R';
 
16331
                        if (isset($o['prev'])) {
 
16332
                                $out .= ' /Prev '.($n + $o['prev']).' 0 R';
 
16333
                        }
 
16334
                        if (isset($o['next'])) {
 
16335
                                $out .= ' /Next '.($n + $o['next']).' 0 R';
 
16336
                        }
 
16337
                        if (isset($o['first'])) {
 
16338
                                $out .= ' /First '.($n + $o['first']).' 0 R';
 
16339
                        }
 
16340
                        if (isset($o['last'])) {
 
16341
                                $out .= ' /Last '.($n + $o['last']).' 0 R';
 
16342
                        }
 
16343
                        if (isset($this->page_obj_id[($o['p'])])) {
 
16344
                                $out .= ' '.sprintf('/Dest [%u 0 R /XYZ 0 %.2F null]', $this->page_obj_id[($o['p'])], ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
 
16345
                        }
 
16346
                        // set font style
 
16347
                        $style = 0;
 
16348
                        if (!empty($o['s'])) {
 
16349
                                // bold
 
16350
                                if (strpos($o['s'], 'B') !== false) {
 
16351
                                        $style |= 2;
 
16352
                                }
 
16353
                                // oblique
 
16354
                                if (strpos($o['s'], 'I') !== false) {
 
16355
                                        $style |= 1;
 
16356
                                }
 
16357
                        }
 
16358
                        $out .= sprintf(' /F %d', $style);
 
16359
                        // set bookmark color
 
16360
                        if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
 
16361
                                $color = array_values($o['c']);
 
16362
                                $out .= sprintf(' /C [%.3F %.3F %.3F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
 
16363
                        } else {
 
16364
                                // black
 
16365
                                $out .= ' /C [0.0 0.0 0.0]';
 
16366
                        }
 
16367
                        $out .= ' /Count 0'; // normally closed item
 
16368
                        $out .= ' >>';
 
16369
                        $out .= "\n".'endobj';
 
16370
                        $this->_out($out);
 
16371
                }
 
16372
                //Outline root
 
16373
                $this->OutlineRoot = $this->_newobj();
 
16374
                $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj');
 
16375
        }
 
16376
 
 
16377
        // --- JAVASCRIPT ------------------------------------------------------
 
16378
 
 
16379
        /**
 
16380
         * Adds a javascript
 
16381
         * @param $script (string) Javascript code
 
16382
         * @public
 
16383
         * @author Johannes G�ntert, Nicola Asuni
 
16384
         * @since 2.1.002 (2008-02-12)
 
16385
         */
 
16386
        public function IncludeJS($script) {
 
16387
                $this->javascript .= $script;
 
16388
        }
 
16389
 
 
16390
        /**
 
16391
         * Adds a javascript object and return object ID
 
16392
         * @param $script (string) Javascript code
 
16393
         * @param $onload (boolean) if true executes this object when opening the document
 
16394
         * @return int internal object ID
 
16395
         * @public
 
16396
         * @author Nicola Asuni
 
16397
         * @since 4.8.000 (2009-09-07)
 
16398
         */
 
16399
        public function addJavascriptObject($script, $onload=false) {
 
16400
                if ($this->pdfa_mode) {
 
16401
                        // javascript is not allowed in PDF/A mode
 
16402
                        return false;
 
16403
                }
 
16404
                ++$this->n;
 
16405
                $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload);
 
16406
                return $this->n;
 
16407
        }
 
16408
 
 
16409
        /**
 
16410
         * Create a javascript PDF string.
 
16411
         * @protected
 
16412
         * @author Johannes G�ntert, Nicola Asuni
 
16413
         * @since 2.1.002 (2008-02-12)
 
16414
         */
 
16415
        protected function _putjavascript() {
 
16416
                if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) {
 
16417
                        return;
 
16418
                }
 
16419
                if (strpos($this->javascript, 'this.addField') > 0) {
 
16420
                        if (!$this->ur['enabled']) {
 
16421
                                //$this->setUserRights();
 
16422
                        }
 
16423
                        // the following two lines are used to avoid form fields duplication after saving
 
16424
                        // The addField method only works when releasing user rights (UR3)
 
16425
                        $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
 
16426
                        $jsb = "getField('tcpdfdocsaved').value='saved';";
 
16427
                        $this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
 
16428
                }
 
16429
                $this->n_js = $this->_newobj();
 
16430
                $out = ' << /Names [';
 
16431
                if (!empty($this->javascript)) {
 
16432
                        $out .= ' (EmbeddedJS) '.($this->n + 1).' 0 R';
 
16433
                }
 
16434
                if (!empty($this->js_objects)) {
 
16435
                        foreach ($this->js_objects as $key => $val) {
 
16436
                                if ($val['onload']) {
 
16437
                                        $out .= ' (JS'.$key.') '.$key.' 0 R';
 
16438
                                }
 
16439
                        }
 
16440
                }
 
16441
                $out .= ' ] >>';
 
16442
                $out .= "\n".'endobj';
 
16443
                $this->_out($out);
 
16444
                // default Javascript object
 
16445
                if (!empty($this->javascript)) {
 
16446
                        $obj_id = $this->_newobj();
 
16447
                        $out = '<< /S /JavaScript';
 
16448
                        $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id);
 
16449
                        $out .= ' >>';
 
16450
                        $out .= "\n".'endobj';
 
16451
                        $this->_out($out);
 
16452
                }
 
16453
                // additional Javascript objects
 
16454
                if (!empty($this->js_objects)) {
 
16455
                        foreach ($this->js_objects as $key => $val) {
 
16456
                                $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
 
16457
                                $this->_out($out);
 
16458
                        }
 
16459
                }
 
16460
        }
 
16461
 
 
16462
        /**
 
16463
         * Convert color to javascript color.
 
16464
         * @param $color (string) color name or "#RRGGBB"
 
16465
         * @protected
 
16466
         * @author Denis Van Nuffelen, Nicola Asuni
 
16467
         * @since 2.1.002 (2008-02-12)
 
16468
         */
 
16469
        protected function _JScolor($color) {
 
16470
                static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
 
16471
                if (substr($color,0,1) == '#') {
 
16472
                        return sprintf("['RGB',%.3F,%.3F,%.3F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
 
16473
                }
 
16474
                if (!in_array($color,$aColors)) {
 
16475
                        $this->Error('Invalid color: '.$color);
 
16476
                }
 
16477
                return 'color.'.$color;
 
16478
        }
 
16479
 
 
16480
        /**
 
16481
         * Adds a javascript form field.
 
16482
         * @param $type (string) field type
 
16483
         * @param $name (string) field name
 
16484
         * @param $x (int) horizontal position
 
16485
         * @param $y (int) vertical position
 
16486
         * @param $w (int) width
 
16487
         * @param $h (int) height
 
16488
         * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
 
16489
         * @protected
 
16490
         * @author Denis Van Nuffelen, Nicola Asuni
 
16491
         * @since 2.1.002 (2008-02-12)
 
16492
         */
 
16493
        protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
 
16494
                if ($this->rtl) {
 
16495
                        $x = $x - $w;
 
16496
                }
 
16497
                // the followind avoid fields duplication after saving the document
 
16498
                $this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {";
 
16499
                $k = $this->k;
 
16500
                $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%u,[%.2F,%.2F,%.2F,%.2F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n";
 
16501
                $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
 
16502
                while (list($key, $val) = each($prop)) {
 
16503
                        if (strcmp(substr($key, -5), 'Color') == 0) {
 
16504
                                $val = $this->_JScolor($val);
 
16505
                        } else {
 
16506
                                $val = "'".$val."'";
 
16507
                        }
 
16508
                        $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
 
16509
                }
 
16510
                if ($this->rtl) {
 
16511
                        $this->x -= $w;
 
16512
                } else {
 
16513
                        $this->x += $w;
 
16514
                }
 
16515
                $this->javascript .= '}';
 
16516
        }
 
16517
 
 
16518
        // --- FORM FIELDS -----------------------------------------------------
 
16519
 
 
16520
        /**
 
16521
         * Convert JavaScript form fields properties array to Annotation Properties array.
 
16522
         * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
 
16523
         * @return array of annotation properties
 
16524
         * @protected
 
16525
         * @author Nicola Asuni
 
16526
         * @since 4.8.000 (2009-09-06)
 
16527
         */
 
16528
        protected function getAnnotOptFromJSProp($prop) {
 
16529
                if (isset($prop['aopt']) AND is_array($prop['aopt'])) {
 
16530
                        // the annotation options area lready defined
 
16531
                        return $prop['aopt'];
 
16532
                }
 
16533
                $opt = array(); // value to be returned
 
16534
                // alignment: Controls how the text is laid out within the text field.
 
16535
                if (isset($prop['alignment'])) {
 
16536
                        switch ($prop['alignment']) {
 
16537
                                case 'left': {
 
16538
                                        $opt['q'] = 0;
 
16539
                                        break;
 
16540
                                }
 
16541
                                case 'center': {
 
16542
                                        $opt['q'] = 1;
 
16543
                                        break;
 
16544
                                }
 
16545
                                case 'right': {
 
16546
                                        $opt['q'] = 2;
 
16547
                                        break;
 
16548
                                }
 
16549
                                default: {
 
16550
                                        $opt['q'] = ($this->rtl)?2:0;
 
16551
                                        break;
 
16552
                                }
 
16553
                        }
 
16554
                }
 
16555
                // lineWidth: Specifies the thickness of the border when stroking the perimeter of a field's rectangle.
 
16556
                if (isset($prop['lineWidth'])) {
 
16557
                        $linewidth = intval($prop['lineWidth']);
 
16558
                } else {
 
16559
                        $linewidth = 1;
 
16560
                }
 
16561
                // borderStyle: The border style for a field.
 
16562
                if (isset($prop['borderStyle'])) {
 
16563
                        switch ($prop['borderStyle']) {
 
16564
                                case 'border.d':
 
16565
                                case 'dashed': {
 
16566
                                        $opt['border'] = array(0, 0, $linewidth, array(3, 2));
 
16567
                                        $opt['bs'] = array('w'=>$linewidth, 's'=>'D', 'd'=>array(3, 2));
 
16568
                                        break;
 
16569
                                }
 
16570
                                case 'border.b':
 
16571
                                case 'beveled': {
 
16572
                                        $opt['border'] = array(0, 0, $linewidth);
 
16573
                                        $opt['bs'] = array('w'=>$linewidth, 's'=>'B');
 
16574
                                        break;
 
16575
                                }
 
16576
                                case 'border.i':
 
16577
                                case 'inset': {
 
16578
                                        $opt['border'] = array(0, 0, $linewidth);
 
16579
                                        $opt['bs'] = array('w'=>$linewidth, 's'=>'I');
 
16580
                                        break;
 
16581
                                }
 
16582
                                case 'border.u':
 
16583
                                case 'underline': {
 
16584
                                        $opt['border'] = array(0, 0, $linewidth);
 
16585
                                        $opt['bs'] = array('w'=>$linewidth, 's'=>'U');
 
16586
                                        break;
 
16587
                                }
 
16588
                                case 'border.s':
 
16589
                                case 'solid': {
 
16590
                                        $opt['border'] = array(0, 0, $linewidth);
 
16591
                                        $opt['bs'] = array('w'=>$linewidth, 's'=>'S');
 
16592
                                        break;
 
16593
                                }
 
16594
                                default: {
 
16595
                                        break;
 
16596
                                }
 
16597
                        }
 
16598
                }
 
16599
                if (isset($prop['border']) AND is_array($prop['border'])) {
 
16600
                        $opt['border'] = $prop['border'];
 
16601
                }
 
16602
                if (!isset($opt['mk'])) {
 
16603
                        $opt['mk'] = array();
 
16604
                }
 
16605
                if (!isset($opt['mk']['if'])) {
 
16606
                        $opt['mk']['if'] = array();
 
16607
                }
 
16608
                $opt['mk']['if']['a'] = array(0.5, 0.5);
 
16609
                // buttonAlignX: Controls how space is distributed from the left of the button face with respect to the icon.
 
16610
                if (isset($prop['buttonAlignX'])) {
 
16611
                        $opt['mk']['if']['a'][0] = $prop['buttonAlignX'];
 
16612
                }
 
16613
                // buttonAlignY: Controls how unused space is distributed from the bottom of the button face with respect to the icon.
 
16614
                if (isset($prop['buttonAlignY'])) {
 
16615
                        $opt['mk']['if']['a'][1] = $prop['buttonAlignY'];
 
16616
                }
 
16617
                // buttonFitBounds: If true, the extent to which the icon may be scaled is set to the bounds of the button field.
 
16618
                if (isset($prop['buttonFitBounds']) AND ($prop['buttonFitBounds'] == 'true')) {
 
16619
                        $opt['mk']['if']['fb'] = true;
 
16620
                }
 
16621
                // buttonScaleHow: Controls how the icon is scaled (if necessary) to fit inside the button face.
 
16622
                if (isset($prop['buttonScaleHow'])) {
 
16623
                        switch ($prop['buttonScaleHow']) {
 
16624
                                case 'scaleHow.proportional': {
 
16625
                                        $opt['mk']['if']['s'] = 'P';
 
16626
                                        break;
 
16627
                                }
 
16628
                                case 'scaleHow.anamorphic': {
 
16629
                                        $opt['mk']['if']['s'] = 'A';
 
16630
                                        break;
 
16631
                                }
 
16632
                        }
 
16633
                }
 
16634
                // buttonScaleWhen: Controls when an icon is scaled to fit inside the button face.
 
16635
                if (isset($prop['buttonScaleWhen'])) {
 
16636
                        switch ($prop['buttonScaleWhen']) {
 
16637
                                case 'scaleWhen.always': {
 
16638
                                        $opt['mk']['if']['sw'] = 'A';
 
16639
                                        break;
 
16640
                                }
 
16641
                                case 'scaleWhen.never': {
 
16642
                                        $opt['mk']['if']['sw'] = 'N';
 
16643
                                        break;
 
16644
                                }
 
16645
                                case 'scaleWhen.tooBig': {
 
16646
                                        $opt['mk']['if']['sw'] = 'B';
 
16647
                                        break;
 
16648
                                }
 
16649
                                case 'scaleWhen.tooSmall': {
 
16650
                                        $opt['mk']['if']['sw'] = 'S';
 
16651
                                        break;
 
16652
                                }
 
16653
                        }
 
16654
                }
 
16655
                // buttonPosition: Controls how the text and the icon of the button are positioned with respect to each other within the button face.
 
16656
                if (isset($prop['buttonPosition'])) {
 
16657
                        switch ($prop['buttonPosition']) {
 
16658
                                case 0:
 
16659
                                case 'position.textOnly': {
 
16660
                                        $opt['mk']['tp'] = 0;
 
16661
                                        break;
 
16662
                                }
 
16663
                                case 1:
 
16664
                                case 'position.iconOnly': {
 
16665
                                        $opt['mk']['tp'] = 1;
 
16666
                                        break;
 
16667
                                }
 
16668
                                case 2:
 
16669
                                case 'position.iconTextV': {
 
16670
                                        $opt['mk']['tp'] = 2;
 
16671
                                        break;
 
16672
                                }
 
16673
                                case 3:
 
16674
                                case 'position.textIconV': {
 
16675
                                        $opt['mk']['tp'] = 3;
 
16676
                                        break;
 
16677
                                }
 
16678
                                case 4:
 
16679
                                case 'position.iconTextH': {
 
16680
                                        $opt['mk']['tp'] = 4;
 
16681
                                        break;
 
16682
                                }
 
16683
                                case 5:
 
16684
                                case 'position.textIconH': {
 
16685
                                        $opt['mk']['tp'] = 5;
 
16686
                                        break;
 
16687
                                }
 
16688
                                case 6:
 
16689
                                case 'position.overlay': {
 
16690
                                        $opt['mk']['tp'] = 6;
 
16691
                                        break;
 
16692
                                }
 
16693
                        }
 
16694
                }
 
16695
                // fillColor: Specifies the background color for a field.
 
16696
                if (isset($prop['fillColor'])) {
 
16697
                        if (is_array($prop['fillColor'])) {
 
16698
                                $opt['mk']['bg'] = $prop['fillColor'];
 
16699
                        } else {
 
16700
                                $opt['mk']['bg'] = $this->convertHTMLColorToDec($prop['fillColor']);
 
16701
                        }
 
16702
                }
 
16703
                // strokeColor: Specifies the stroke color for a field that is used to stroke the rectangle of the field with a line as large as the line width.
 
16704
                if (isset($prop['strokeColor'])) {
 
16705
                        if (is_array($prop['strokeColor'])) {
 
16706
                                $opt['mk']['bc'] = $prop['strokeColor'];
 
16707
                        } else {
 
16708
                                $opt['mk']['bc'] = $this->convertHTMLColorToDec($prop['strokeColor']);
 
16709
                        }
 
16710
                }
 
16711
                // rotation: The rotation of a widget in counterclockwise increments.
 
16712
                if (isset($prop['rotation'])) {
 
16713
                        $opt['mk']['r'] = $prop['rotation'];
 
16714
                }
 
16715
                // charLimit: Limits the number of characters that a user can type into a text field.
 
16716
                if (isset($prop['charLimit'])) {
 
16717
                        $opt['maxlen'] = intval($prop['charLimit']);
 
16718
                }
 
16719
                if (!isset($ff)) {
 
16720
                        $ff = 0; // default value
 
16721
                }
 
16722
                // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
 
16723
                if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) {
 
16724
                        $ff += 1 << 0;
 
16725
                }
 
16726
                // required: Specifies whether a field requires a value.
 
16727
                if (isset($prop['required']) AND ($prop['required'] == 'true')) {
 
16728
                        $ff += 1 << 1;
 
16729
                }
 
16730
                // multiline: Controls how text is wrapped within the field.
 
16731
                if (isset($prop['multiline']) AND ($prop['multiline'] == 'true')) {
 
16732
                        $ff += 1 << 12;
 
16733
                }
 
16734
                // password: Specifies whether the field should display asterisks when data is entered in the field.
 
16735
                if (isset($prop['password']) AND ($prop['password'] == 'true')) {
 
16736
                        $ff += 1 << 13;
 
16737
                }
 
16738
                // NoToggleToOff: If set, exactly one radio button shall be selected at all times; selecting the currently selected button has no effect.
 
16739
                if (isset($prop['NoToggleToOff']) AND ($prop['NoToggleToOff'] == 'true')) {
 
16740
                        $ff += 1 << 14;
 
16741
                }
 
16742
                // Radio: If set, the field is a set of radio buttons.
 
16743
                if (isset($prop['Radio']) AND ($prop['Radio'] == 'true')) {
 
16744
                        $ff += 1 << 15;
 
16745
                }
 
16746
                // Pushbutton: If set, the field is a pushbutton that does not retain a permanent value.
 
16747
                if (isset($prop['Pushbutton']) AND ($prop['Pushbutton'] == 'true')) {
 
16748
                        $ff += 1 << 16;
 
16749
                }
 
16750
                // Combo: If set, the field is a combo box; if clear, the field is a list box.
 
16751
                if (isset($prop['Combo']) AND ($prop['Combo'] == 'true')) {
 
16752
                        $ff += 1 << 17;
 
16753
                }
 
16754
                // editable: Controls whether a combo box is editable.
 
16755
                if (isset($prop['editable']) AND ($prop['editable'] == 'true')) {
 
16756
                        $ff += 1 << 18;
 
16757
                }
 
16758
                // Sort: If set, the field's option items shall be sorted alphabetically.
 
16759
                if (isset($prop['Sort']) AND ($prop['Sort'] == 'true')) {
 
16760
                        $ff += 1 << 19;
 
16761
                }
 
16762
                // fileSelect: If true, sets the file-select flag in the Options tab of the text field (Field is Used for File Selection).
 
16763
                if (isset($prop['fileSelect']) AND ($prop['fileSelect'] == 'true')) {
 
16764
                        $ff += 1 << 20;
 
16765
                }
 
16766
                // multipleSelection: If true, indicates that a list box allows a multiple selection of items.
 
16767
                if (isset($prop['multipleSelection']) AND ($prop['multipleSelection'] == 'true')) {
 
16768
                        $ff += 1 << 21;
 
16769
                }
 
16770
                // doNotSpellCheck: If true, spell checking is not performed on this editable text field.
 
16771
                if (isset($prop['doNotSpellCheck']) AND ($prop['doNotSpellCheck'] == 'true')) {
 
16772
                        $ff += 1 << 22;
 
16773
                }
 
16774
                // doNotScroll: If true, the text field does not scroll and the user, therefore, is limited by the rectangular region designed for the field.
 
16775
                if (isset($prop['doNotScroll']) AND ($prop['doNotScroll'] == 'true')) {
 
16776
                        $ff += 1 << 23;
 
16777
                }
 
16778
                // comb: If set to true, the field background is drawn as series of boxes (one for each character in the value of the field) and each character of the content is drawn within those boxes. The number of boxes drawn is determined from the charLimit property. It applies only to text fields. The setter will also raise if any of the following field properties are also set multiline, password, and fileSelect. A side-effect of setting this property is that the doNotScroll property is also set.
 
16779
                if (isset($prop['comb']) AND ($prop['comb'] == 'true')) {
 
16780
                        $ff += 1 << 24;
 
16781
                }
 
16782
                // radiosInUnison: If false, even if a group of radio buttons have the same name and export value, they behave in a mutually exclusive fashion, like HTML radio buttons.
 
16783
                if (isset($prop['radiosInUnison']) AND ($prop['radiosInUnison'] == 'true')) {
 
16784
                        $ff += 1 << 25;
 
16785
                }
 
16786
                // richText: If true, the field allows rich text formatting.
 
16787
                if (isset($prop['richText']) AND ($prop['richText'] == 'true')) {
 
16788
                        $ff += 1 << 25;
 
16789
                }
 
16790
                // commitOnSelChange: Controls whether a field value is committed after a selection change.
 
16791
                if (isset($prop['commitOnSelChange']) AND ($prop['commitOnSelChange'] == 'true')) {
 
16792
                        $ff += 1 << 26;
 
16793
                }
 
16794
                $opt['ff'] = $ff;
 
16795
                // defaultValue: The default value of a field - that is, the value that the field is set to when the form is reset.
 
16796
                if (isset($prop['defaultValue'])) {
 
16797
                        $opt['dv'] = $prop['defaultValue'];
 
16798
                }
 
16799
                $f = 4; // default value for annotation flags
 
16800
                // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
 
16801
                if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) {
 
16802
                        $f += 1 << 6;
 
16803
                }
 
16804
                // display: Controls whether the field is hidden or visible on screen and in print.
 
16805
                if (isset($prop['display'])) {
 
16806
                        if ($prop['display'] == 'display.visible') {
 
16807
                                //
 
16808
                        } elseif ($prop['display'] == 'display.hidden') {
 
16809
                                $f += 1 << 1;
 
16810
                        } elseif ($prop['display'] == 'display.noPrint') {
 
16811
                                $f -= 1 << 2;
 
16812
                        } elseif ($prop['display'] == 'display.noView') {
 
16813
                                $f += 1 << 5;
 
16814
                        }
 
16815
                }
 
16816
                $opt['f'] = $f;
 
16817
                // currentValueIndices: Reads and writes single or multiple values of a list box or combo box.
 
16818
                if (isset($prop['currentValueIndices']) AND is_array($prop['currentValueIndices'])) {
 
16819
                        $opt['i'] = $prop['currentValueIndices'];
 
16820
                }
 
16821
                // value: The value of the field data that the user has entered.
 
16822
                if (isset($prop['value'])) {
 
16823
                        if (is_array($prop['value'])) {
 
16824
                                $opt['opt'] = array();
 
16825
                                foreach ($prop['value'] AS $key => $optval) {
 
16826
                                        // exportValues: An array of strings representing the export values for the field.
 
16827
                                        if (isset($prop['exportValues'][$key])) {
 
16828
                                                $opt['opt'][$key] = array($prop['exportValues'][$key], $prop['value'][$key]);
 
16829
                                        } else {
 
16830
                                                $opt['opt'][$key] = $prop['value'][$key];
 
16831
                                        }
 
16832
                                }
 
16833
                        } else {
 
16834
                                $opt['v'] = $prop['value'];
 
16835
                        }
 
16836
                }
 
16837
                // richValue: This property specifies the text contents and formatting of a rich text field.
 
16838
                if (isset($prop['richValue'])) {
 
16839
                        $opt['rv'] = $prop['richValue'];
 
16840
                }
 
16841
                // submitName: If nonempty, used during form submission instead of name. Only applicable if submitting in HTML format (that is, URL-encoded).
 
16842
                if (isset($prop['submitName'])) {
 
16843
                        $opt['tm'] = $prop['submitName'];
 
16844
                }
 
16845
                // name: Fully qualified field name.
 
16846
                if (isset($prop['name'])) {
 
16847
                        $opt['t'] = $prop['name'];
 
16848
                }
 
16849
                // userName: The user name (short description string) of the field.
 
16850
                if (isset($prop['userName'])) {
 
16851
                        $opt['tu'] = $prop['userName'];
 
16852
                }
 
16853
                // highlight: Defines how a button reacts when a user clicks it.
 
16854
                if (isset($prop['highlight'])) {
 
16855
                        switch ($prop['highlight']) {
 
16856
                                case 'none':
 
16857
                                case 'highlight.n': {
 
16858
                                        $opt['h'] = 'N';
 
16859
                                        break;
 
16860
                                }
 
16861
                                case 'invert':
 
16862
                                case 'highlight.i': {
 
16863
                                        $opt['h'] = 'i';
 
16864
                                        break;
 
16865
                                }
 
16866
                                case 'push':
 
16867
                                case 'highlight.p': {
 
16868
                                        $opt['h'] = 'P';
 
16869
                                        break;
 
16870
                                }
 
16871
                                case 'outline':
 
16872
                                case 'highlight.o': {
 
16873
                                        $opt['h'] = 'O';
 
16874
                                        break;
 
16875
                                }
 
16876
                        }
 
16877
                }
 
16878
                // Unsupported options:
 
16879
                // - calcOrderIndex: Changes the calculation order of fields in the document.
 
16880
                // - delay: Delays the redrawing of a field's appearance.
 
16881
                // - defaultStyle: This property defines the default style attributes for the form field.
 
16882
                // - style: Allows the user to set the glyph style of a check box or radio button.
 
16883
                // - textColor, textFont, textSize
 
16884
                return $opt;
 
16885
        }
 
16886
 
 
16887
        /**
 
16888
         * Set default properties for form fields.
 
16889
         * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
 
16890
         * @public
 
16891
         * @author Nicola Asuni
 
16892
         * @since 4.8.000 (2009-09-06)
 
16893
         */
 
16894
        public function setFormDefaultProp($prop=array()) {
 
16895
                $this->default_form_prop = $prop;
 
16896
        }
 
16897
 
 
16898
        /**
 
16899
         * Return the default properties for form fields.
 
16900
         * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
 
16901
         * @public
 
16902
         * @author Nicola Asuni
 
16903
         * @since 4.8.000 (2009-09-06)
 
16904
         */
 
16905
        public function getFormDefaultProp() {
 
16906
                return $this->default_form_prop;
 
16907
        }
 
16908
 
 
16909
        /**
 
16910
         * Creates a text field
 
16911
         * @param $name (string) field name
 
16912
         * @param $w (float) Width of the rectangle
 
16913
         * @param $h (float) Height of the rectangle
 
16914
         * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
 
16915
         * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
 
16916
         * @param $x (float) Abscissa of the upper-left corner of the rectangle
 
16917
         * @param $y (float) Ordinate of the upper-left corner of the rectangle
 
16918
         * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
 
16919
         * @public
 
16920
         * @author Nicola Asuni
 
16921
         * @since 4.8.000 (2009-09-07)
 
16922
         */
 
16923
        public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
 
16924
                if ($x === '') {
 
16925
                        $x = $this->x;
 
16926
                }
 
16927
                if ($y === '') {
 
16928
                        $y = $this->y;
 
16929
                }
 
16930
                // check page for no-write regions and adapt page margins if necessary
 
16931
                list($x, $y) = $this->checkPageRegions($h, $x, $y);
 
16932
                if ($js) {
 
16933
                        $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
 
16934
                        return;
 
16935
                }
 
16936
                // get default style
 
16937
                $prop = array_merge($this->getFormDefaultProp(), $prop);
 
16938
                // get annotation data
 
16939
                $popt = $this->getAnnotOptFromJSProp($prop);
 
16940
                // set default appearance stream
 
16941
                $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
 
16942
                $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
 
16943
                $popt['da'] = $fontstyle;
 
16944
                // build appearance stream
 
16945
                $popt['ap'] = array();
 
16946
                $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
 
16947
                $text = '';
 
16948
                if (isset($prop['value']) AND !empty($prop['value'])) {
 
16949
                        $text = $prop['value'];
 
16950
                } elseif (isset($opt['v']) AND !empty($opt['v'])) {
 
16951
                        $text = $opt['v'];
 
16952
                }
 
16953
                $tmpid = $this->startTemplate($w, $h, false);
 
16954
                $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
 
16955
                $this->endTemplate();
 
16956
                --$this->n;
 
16957
                $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
 
16958
                unset($this->xobjects[$tmpid]);
 
16959
                $popt['ap']['n'] .= 'Q EMC';
 
16960
                // merge options
 
16961
                $opt = array_merge($popt, $opt);
 
16962
                // remove some conflicting options
 
16963
                unset($opt['bs']);
 
16964
                // set remaining annotation data
 
16965
                $opt['Subtype'] = 'Widget';
 
16966
                $opt['ft'] = 'Tx';
 
16967
                $opt['t'] = $name;
 
16968
                // Additional annotation's parameters (check _putannotsobj() method):
 
16969
                //$opt['f']
 
16970
                //$opt['as']
 
16971
                //$opt['bs']
 
16972
                //$opt['be']
 
16973
                //$opt['c']
 
16974
                //$opt['border']
 
16975
                //$opt['h']
 
16976
                //$opt['mk'];
 
16977
                //$opt['mk']['r']
 
16978
                //$opt['mk']['bc'];
 
16979
                //$opt['mk']['bg'];
 
16980
                unset($opt['mk']['ca']);
 
16981
                unset($opt['mk']['rc']);
 
16982
                unset($opt['mk']['ac']);
 
16983
                unset($opt['mk']['i']);
 
16984
                unset($opt['mk']['ri']);
 
16985
                unset($opt['mk']['ix']);
 
16986
                unset($opt['mk']['if']);
 
16987
                //$opt['mk']['if']['sw'];
 
16988
                //$opt['mk']['if']['s'];
 
16989
                //$opt['mk']['if']['a'];
 
16990
                //$opt['mk']['if']['fb'];
 
16991
                unset($opt['mk']['tp']);
 
16992
                //$opt['tu']
 
16993
                //$opt['tm']
 
16994
                //$opt['ff']
 
16995
                //$opt['v']
 
16996
                //$opt['dv']
 
16997
                //$opt['a']
 
16998
                //$opt['aa']
 
16999
                //$opt['q']
 
17000
                $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
 
17001
                if ($this->rtl) {
 
17002
                        $this->x -= $w;
 
17003
                } else {
 
17004
                        $this->x += $w;
 
17005
                }
 
17006
        }
 
17007
 
 
17008
        /**
 
17009
         * Creates a RadioButton field.
 
17010
         * @param $name (string) Field name.
 
17011
         * @param $w (int) Width or the radio button.
 
17012
         * @param $prop (array) Javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
 
17013
         * @param $opt (array) Annotation parameters. Possible values are described on official PDF32000_2008 reference.
 
17014
         * @param $onvalue (string) Value to be returned if selected.
 
17015
         * @param $checked (boolean) Define the initial state.
 
17016
         * @param $x (float) Abscissa of the upper-left corner of the rectangle
 
17017
         * @param $y (float) Ordinate of the upper-left corner of the rectangle
 
17018
         * @param $js (boolean) If true put the field using JavaScript (requires Acrobat Writer to be rendered).
 
17019
         * @public
 
17020
         * @author Nicola Asuni
 
17021
         * @since 4.8.000 (2009-09-07)
 
17022
         */
 
17023
        public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
 
17024
                if ($x === '') {
 
17025
                        $x = $this->x;
 
17026
                }
 
17027
                if ($y === '') {
 
17028
                        $y = $this->y;
 
17029
                }
 
17030
                // check page for no-write regions and adapt page margins if necessary
 
17031
                list($x, $y) = $this->checkPageRegions($w, $x, $y);
 
17032
                if ($js) {
 
17033
                        $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
 
17034
                        return;
 
17035
                }
 
17036
                if ($this->empty_string($onvalue)) {
 
17037
                        $onvalue = 'On';
 
17038
                }
 
17039
                if ($checked) {
 
17040
                        $defval = $onvalue;
 
17041
                } else {
 
17042
                        $defval = 'Off';
 
17043
                }
 
17044
                // set font
 
17045
                $font = 'zapfdingbats';
 
17046
                $this->AddFont($font);
 
17047
                $tmpfont = $this->getFontBuffer($font);
 
17048
                // set data for parent group
 
17049
                if (!isset($this->radiobutton_groups[$this->page])) {
 
17050
                        $this->radiobutton_groups[$this->page] = array();
 
17051
                }
 
17052
                if (!isset($this->radiobutton_groups[$this->page][$name])) {
 
17053
                        $this->radiobutton_groups[$this->page][$name] = array();
 
17054
                        ++$this->n;
 
17055
                        $this->radiobutton_groups[$this->page][$name]['n'] = $this->n;
 
17056
                        $this->radio_groups[] = $this->n;
 
17057
                }
 
17058
                $kid = ($this->n + 1);
 
17059
                // save object ID to be added on Kids entry on parent object
 
17060
                $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval);
 
17061
                // get default style
 
17062
                $prop = array_merge($this->getFormDefaultProp(), $prop);
 
17063
                $prop['NoToggleToOff'] = 'true';
 
17064
                $prop['Radio'] = 'true';
 
17065
                $prop['borderStyle'] = 'inset';
 
17066
                // get annotation data
 
17067
                $popt = $this->getAnnotOptFromJSProp($prop);
 
17068
                // set additional default options
 
17069
                $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
 
17070
                $fontstyle = sprintf('/F%d %.2F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
 
17071
                $popt['da'] = $fontstyle;
 
17072
                // build appearance stream
 
17073
                $popt['ap'] = array();
 
17074
                $popt['ap']['n'] = array();
 
17075
                $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
 
17076
                $popt['ap']['n'][$onvalue] = sprintf('q %s BT /F%d %.2F Tf %.2F %.2F Td ('.chr(108).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, 0, $fy);
 
17077
                $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %.2F Tf %.2F %.2F Td ('.chr(109).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, 0, $fy);
 
17078
                if (!isset($popt['mk'])) {
 
17079
                        $popt['mk'] = array();
 
17080
                }
 
17081
                $popt['mk']['ca'] = '(l)';
 
17082
                // merge options
 
17083
                $opt = array_merge($popt, $opt);
 
17084
                // set remaining annotation data
 
17085
                $opt['Subtype'] = 'Widget';
 
17086
                $opt['ft'] = 'Btn';
 
17087
                if ($checked) {
 
17088
                        $opt['v'] = array('/'.$onvalue);
 
17089
                        $opt['as'] = $onvalue;
 
17090
                } else {
 
17091
                        $opt['as'] = 'Off';
 
17092
                }
 
17093
                // store readonly flag
 
17094
                if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) {
 
17095
                        $this->radiobutton_groups[$this->page][$name]['#readonly#'] = false;
 
17096
                }
 
17097
                $this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64);
 
17098
                $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
 
17099
                if ($this->rtl) {
 
17100
                        $this->x -= $w;
 
17101
                } else {
 
17102
                        $this->x += $w;
 
17103
                }
 
17104
        }
 
17105
 
 
17106
        /**
 
17107
         * Creates a List-box field
 
17108
         * @param $name (string) field name
 
17109
         * @param $w (int) width
 
17110
         * @param $h (int) height
 
17111
         * @param $values (array) array containing the list of values.
 
17112
         * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
 
17113
         * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
 
17114
         * @param $x (float) Abscissa of the upper-left corner of the rectangle
 
17115
         * @param $y (float) Ordinate of the upper-left corner of the rectangle
 
17116
         * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
 
17117
         * @public
 
17118
         * @author Nicola Asuni
 
17119
         * @since 4.8.000 (2009-09-07)
 
17120
         */
 
17121
        public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
 
17122
                if ($x === '') {
 
17123
                        $x = $this->x;
 
17124
                }
 
17125
                if ($y === '') {
 
17126
                        $y = $this->y;
 
17127
                }
 
17128
                // check page for no-write regions and adapt page margins if necessary
 
17129
                list($x, $y) = $this->checkPageRegions($h, $x, $y);
 
17130
                if ($js) {
 
17131
                        $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
 
17132
                        $s = '';
 
17133
                        foreach ($values as $value) {
 
17134
                                $s .= '\''.addslashes($value).'\',';
 
17135
                        }
 
17136
                        $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
 
17137
                        return;
 
17138
                }
 
17139
                // get default style
 
17140
                $prop = array_merge($this->getFormDefaultProp(), $prop);
 
17141
                // get annotation data
 
17142
                $popt = $this->getAnnotOptFromJSProp($prop);
 
17143
                // set additional default values
 
17144
                $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
 
17145
                $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
 
17146
                $popt['da'] = $fontstyle;
 
17147
                // build appearance stream
 
17148
                $popt['ap'] = array();
 
17149
                $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
 
17150
                $text = '';
 
17151
                foreach($values as $item) {
 
17152
                        $text .= $item."\n";
 
17153
                }
 
17154
                $tmpid = $this->startTemplate($w, $h, false);
 
17155
                $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
 
17156
                $this->endTemplate();
 
17157
                --$this->n;
 
17158
                $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
 
17159
                unset($this->xobjects[$tmpid]);
 
17160
                $popt['ap']['n'] .= 'Q EMC';
 
17161
                // merge options
 
17162
                $opt = array_merge($popt, $opt);
 
17163
                // set remaining annotation data
 
17164
                $opt['Subtype'] = 'Widget';
 
17165
                $opt['ft'] = 'Ch';
 
17166
                $opt['t'] = $name;
 
17167
                $opt['opt'] = $values;
 
17168
                unset($opt['mk']['ca']);
 
17169
                unset($opt['mk']['rc']);
 
17170
                unset($opt['mk']['ac']);
 
17171
                unset($opt['mk']['i']);
 
17172
                unset($opt['mk']['ri']);
 
17173
                unset($opt['mk']['ix']);
 
17174
                unset($opt['mk']['if']);
 
17175
                unset($opt['mk']['tp']);
 
17176
                $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
 
17177
                if ($this->rtl) {
 
17178
                        $this->x -= $w;
 
17179
                } else {
 
17180
                        $this->x += $w;
 
17181
                }
 
17182
        }
 
17183
 
 
17184
        /**
 
17185
         * Creates a Combo-box field
 
17186
         * @param $name (string) field name
 
17187
         * @param $w (int) width
 
17188
         * @param $h (int) height
 
17189
         * @param $values (array) array containing the list of values.
 
17190
         * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
 
17191
         * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
 
17192
         * @param $x (float) Abscissa of the upper-left corner of the rectangle
 
17193
         * @param $y (float) Ordinate of the upper-left corner of the rectangle
 
17194
         * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
 
17195
         * @public
 
17196
         * @author Nicola Asuni
 
17197
         * @since 4.8.000 (2009-09-07)
 
17198
         */
 
17199
        public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
 
17200
                if ($x === '') {
 
17201
                        $x = $this->x;
 
17202
                }
 
17203
                if ($y === '') {
 
17204
                        $y = $this->y;
 
17205
                }
 
17206
                // check page for no-write regions and adapt page margins if necessary
 
17207
                list($x, $y) = $this->checkPageRegions($h, $x, $y);
 
17208
                if ($js) {
 
17209
                        $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
 
17210
                        $s = '';
 
17211
                        foreach ($values as $value) {
 
17212
                                $s .= "'".addslashes($value)."',";
 
17213
                        }
 
17214
                        $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
 
17215
                        return;
 
17216
                }
 
17217
                // get default style
 
17218
                $prop = array_merge($this->getFormDefaultProp(), $prop);
 
17219
                $prop['Combo'] = true;
 
17220
                // get annotation data
 
17221
                $popt = $this->getAnnotOptFromJSProp($prop);
 
17222
                // set additional default options
 
17223
                $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
 
17224
                $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
 
17225
                $popt['da'] = $fontstyle;
 
17226
                // build appearance stream
 
17227
                $popt['ap'] = array();
 
17228
                $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
 
17229
                $text = '';
 
17230
                foreach($values as $item) {
 
17231
                        $text .= $item[1]."\n";
 
17232
                }
 
17233
                $tmpid = $this->startTemplate($w, $h, false);
 
17234
                $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
 
17235
                $this->endTemplate();
 
17236
                --$this->n;
 
17237
                $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
 
17238
                unset($this->xobjects[$tmpid]);
 
17239
                $popt['ap']['n'] .= 'Q EMC';
 
17240
                // merge options
 
17241
                $opt = array_merge($popt, $opt);
 
17242
                // set remaining annotation data
 
17243
                $opt['Subtype'] = 'Widget';
 
17244
                $opt['ft'] = 'Ch';
 
17245
                $opt['t'] = $name;
 
17246
                $opt['opt'] = $values;
 
17247
                unset($opt['mk']['ca']);
 
17248
                unset($opt['mk']['rc']);
 
17249
                unset($opt['mk']['ac']);
 
17250
                unset($opt['mk']['i']);
 
17251
                unset($opt['mk']['ri']);
 
17252
                unset($opt['mk']['ix']);
 
17253
                unset($opt['mk']['if']);
 
17254
                unset($opt['mk']['tp']);
 
17255
                $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
 
17256
                if ($this->rtl) {
 
17257
                        $this->x -= $w;
 
17258
                } else {
 
17259
                        $this->x += $w;
 
17260
                }
 
17261
        }
 
17262
 
 
17263
        /**
 
17264
         * Creates a CheckBox field
 
17265
         * @param $name (string) field name
 
17266
         * @param $w (int) width
 
17267
         * @param $checked (boolean) define the initial state.
 
17268
         * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
 
17269
         * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
 
17270
         * @param $onvalue (string) value to be returned if selected.
 
17271
         * @param $x (float) Abscissa of the upper-left corner of the rectangle
 
17272
         * @param $y (float) Ordinate of the upper-left corner of the rectangle
 
17273
         * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
 
17274
         * @public
 
17275
         * @author Nicola Asuni
 
17276
         * @since 4.8.000 (2009-09-07)
 
17277
         */
 
17278
        public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
 
17279
                if ($x === '') {
 
17280
                        $x = $this->x;
 
17281
                }
 
17282
                if ($y === '') {
 
17283
                        $y = $this->y;
 
17284
                }
 
17285
                // check page for no-write regions and adapt page margins if necessary
 
17286
                list($x, $y) = $this->checkPageRegions($w, $x, $y);
 
17287
                if ($js) {
 
17288
                        $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
 
17289
                        return;
 
17290
                }
 
17291
                if (!isset($prop['value'])) {
 
17292
                        $prop['value'] = array('Yes');
 
17293
                }
 
17294
                // get default style
 
17295
                $prop = array_merge($this->getFormDefaultProp(), $prop);
 
17296
                $prop['borderStyle'] = 'inset';
 
17297
                // get annotation data
 
17298
                $popt = $this->getAnnotOptFromJSProp($prop);
 
17299
                // set additional default options
 
17300
                $font = 'zapfdingbats';
 
17301
                $this->AddFont($font);
 
17302
                $tmpfont = $this->getFontBuffer($font);
 
17303
                $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
 
17304
                $fontstyle = sprintf('/F%d %.2F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
 
17305
                $popt['da'] = $fontstyle;
 
17306
                // build appearance stream
 
17307
                $popt['ap'] = array();
 
17308
                $popt['ap']['n'] = array();
 
17309
                $fy = ((($tmpfont['desc']['Ascent'] + $tmpfont['desc']['Descent']) * $this->FontSizePt) / (1000 * $this->k));
 
17310
                $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
 
17311
                $popt['ap']['n']['Yes'] = sprintf('q %s BT /F%d %.2F Tf %.2F %.2F Td ('.chr(110).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, 0, $fy);
 
17312
                $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %.2F Tf %.2F %.2F Td ('.chr(111).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, 0, $fy);
 
17313
                // merge options
 
17314
                $opt = array_merge($popt, $opt);
 
17315
                // set remaining annotation data
 
17316
                $opt['Subtype'] = 'Widget';
 
17317
                $opt['ft'] = 'Btn';
 
17318
                $opt['t'] = $name;
 
17319
                if ($this->empty_string($onvalue)) {
 
17320
                        $onvalue = 'Yes';
 
17321
                }
 
17322
                $opt['opt'] = array($onvalue);
 
17323
                if ($checked) {
 
17324
                        $opt['v'] = array('/Yes');
 
17325
                        $opt['as'] = 'Yes';
 
17326
                } else {
 
17327
                        $opt['v'] = array('/Off');
 
17328
                        $opt['as'] = 'Off';
 
17329
                }
 
17330
                $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
 
17331
                if ($this->rtl) {
 
17332
                        $this->x -= $w;
 
17333
                } else {
 
17334
                        $this->x += $w;
 
17335
                }
 
17336
        }
 
17337
 
 
17338
        /**
 
17339
         * Creates a button field
 
17340
         * @param $name (string) field name
 
17341
         * @param $w (int) width
 
17342
         * @param $h (int) height
 
17343
         * @param $caption (string) caption.
 
17344
         * @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.
 
17345
         * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
 
17346
         * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
 
17347
         * @param $x (float) Abscissa of the upper-left corner of the rectangle
 
17348
         * @param $y (float) Ordinate of the upper-left corner of the rectangle
 
17349
         * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
 
17350
         * @public
 
17351
         * @author Nicola Asuni
 
17352
         * @since 4.8.000 (2009-09-07)
 
17353
         */
 
17354
        public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
 
17355
                if ($x === '') {
 
17356
                        $x = $this->x;
 
17357
                }
 
17358
                if ($y === '') {
 
17359
                        $y = $this->y;
 
17360
                }
 
17361
                // check page for no-write regions and adapt page margins if necessary
 
17362
                list($x, $y) = $this->checkPageRegions($h, $x, $y);
 
17363
                if ($js) {
 
17364
                        $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
 
17365
                        $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
 
17366
                        $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
 
17367
                        $this->javascript .= 'f'.$name.".highlight='push';\n";
 
17368
                        $this->javascript .= 'f'.$name.".print=false;\n";
 
17369
                        return;
 
17370
                }
 
17371
                // get default style
 
17372
                $prop = array_merge($this->getFormDefaultProp(), $prop);
 
17373
                $prop['Pushbutton'] = 'true';
 
17374
                $prop['highlight'] = 'push';
 
17375
                $prop['display'] = 'display.noPrint';
 
17376
                // get annotation data
 
17377
                $popt = $this->getAnnotOptFromJSProp($prop);
 
17378
                $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
 
17379
                $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
 
17380
                $popt['da'] = $fontstyle;
 
17381
                // build appearance stream
 
17382
                $popt['ap'] = array();
 
17383
                $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
 
17384
                $tmpid = $this->startTemplate($w, $h, false);
 
17385
                $bw = (2 / $this->k); // border width
 
17386
                $border = array(
 
17387
                        'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
 
17388
                        'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
 
17389
                        'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
 
17390
                        'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
 
17391
                $this->SetFillColor(204);
 
17392
                $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
 
17393
                $this->endTemplate();
 
17394
                --$this->n;
 
17395
                $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
 
17396
                unset($this->xobjects[$tmpid]);
 
17397
                $popt['ap']['n'] .= 'Q EMC';
 
17398
                // set additional default options
 
17399
                if (!isset($popt['mk'])) {
 
17400
                        $popt['mk'] = array();
 
17401
                }
 
17402
                $ann_obj_id = ($this->n + 1);
 
17403
                if (!empty($action) AND !is_array($action)) {
 
17404
                        $ann_obj_id = ($this->n + 2);
 
17405
                }
 
17406
                $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
 
17407
                $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
 
17408
                $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
 
17409
                // merge options
 
17410
                $opt = array_merge($popt, $opt);
 
17411
                // set remaining annotation data
 
17412
                $opt['Subtype'] = 'Widget';
 
17413
                $opt['ft'] = 'Btn';
 
17414
                $opt['t'] = $caption;
 
17415
                $opt['v'] = $name;
 
17416
                if (!empty($action)) {
 
17417
                        if (is_array($action)) {
 
17418
                                // form action options as on section 12.7.5 of PDF32000_2008.
 
17419
                                $opt['aa'] = '/D <<';
 
17420
                                $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
 
17421
                                foreach ($action AS $key => $val) {
 
17422
                                        if (($key == 'S') AND in_array($val, $bmode)) {
 
17423
                                                $opt['aa'] .= ' /S /'.$val;
 
17424
                                        } elseif (($key == 'F') AND (!empty($val))) {
 
17425
                                                $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
 
17426
                                        } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
 
17427
                                                $opt['aa'] .= ' /Fields [';
 
17428
                                                foreach ($val AS $field) {
 
17429
                                                        $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
 
17430
                                                }
 
17431
                                                $opt['aa'] .= ']';
 
17432
                                        } elseif (($key == 'Flags')) {
 
17433
                                                $ff = 0;
 
17434
                                                if (is_array($val)) {
 
17435
                                                        foreach ($val AS $flag) {
 
17436
                                                                switch ($flag) {
 
17437
                                                                        case 'Include/Exclude': {
 
17438
                                                                                $ff += 1 << 0;
 
17439
                                                                                break;
 
17440
                                                                        }
 
17441
                                                                        case 'IncludeNoValueFields': {
 
17442
                                                                                $ff += 1 << 1;
 
17443
                                                                                break;
 
17444
                                                                        }
 
17445
                                                                        case 'ExportFormat': {
 
17446
                                                                                $ff += 1 << 2;
 
17447
                                                                                break;
 
17448
                                                                        }
 
17449
                                                                        case 'GetMethod': {
 
17450
                                                                                $ff += 1 << 3;
 
17451
                                                                                break;
 
17452
                                                                        }
 
17453
                                                                        case 'SubmitCoordinates': {
 
17454
                                                                                $ff += 1 << 4;
 
17455
                                                                                break;
 
17456
                                                                        }
 
17457
                                                                        case 'XFDF': {
 
17458
                                                                                $ff += 1 << 5;
 
17459
                                                                                break;
 
17460
                                                                        }
 
17461
                                                                        case 'IncludeAppendSaves': {
 
17462
                                                                                $ff += 1 << 6;
 
17463
                                                                                break;
 
17464
                                                                        }
 
17465
                                                                        case 'IncludeAnnotations': {
 
17466
                                                                                $ff += 1 << 7;
 
17467
                                                                                break;
 
17468
                                                                        }
 
17469
                                                                        case 'SubmitPDF': {
 
17470
                                                                                $ff += 1 << 8;
 
17471
                                                                                break;
 
17472
                                                                        }
 
17473
                                                                        case 'CanonicalFormat': {
 
17474
                                                                                $ff += 1 << 9;
 
17475
                                                                                break;
 
17476
                                                                        }
 
17477
                                                                        case 'ExclNonUserAnnots': {
 
17478
                                                                                $ff += 1 << 10;
 
17479
                                                                                break;
 
17480
                                                                        }
 
17481
                                                                        case 'ExclFKey': {
 
17482
                                                                                $ff += 1 << 11;
 
17483
                                                                                break;
 
17484
                                                                        }
 
17485
                                                                        case 'EmbedForm': {
 
17486
                                                                                $ff += 1 << 13;
 
17487
                                                                                break;
 
17488
                                                                        }
 
17489
                                                                }
 
17490
                                                        }
 
17491
                                                } else {
 
17492
                                                        $ff = intval($val);
 
17493
                                                }
 
17494
                                                $opt['aa'] .= ' /Flags '.$ff;
 
17495
                                        }
 
17496
                                }
 
17497
                                $opt['aa'] .= ' >>';
 
17498
                        } else {
 
17499
                                // Javascript action or raw action command
 
17500
                                $js_obj_id = $this->addJavascriptObject($action);
 
17501
                                $opt['aa'] = '/D '.$js_obj_id.' 0 R';
 
17502
                        }
 
17503
                }
 
17504
                $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
 
17505
                if ($this->rtl) {
 
17506
                        $this->x -= $w;
 
17507
                } else {
 
17508
                        $this->x += $w;
 
17509
                }
 
17510
        }
 
17511
 
 
17512
        // --- END FORMS FIELDS ------------------------------------------------
 
17513
 
 
17514
        /**
 
17515
         * Add certification signature (DocMDP or UR3)
 
17516
         * You can set only one signature type
 
17517
         * @protected
 
17518
         * @author Nicola Asuni
 
17519
         * @since 4.6.008 (2009-05-07)
 
17520
         */
 
17521
        protected function _putsignature() {
 
17522
                if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
 
17523
                        return;
 
17524
                }
 
17525
                $sigobjid = ($this->sig_obj_id + 1);
 
17526
                $out = $this->_getobj($sigobjid)."\n";
 
17527
                $out .= '<< /Type /Sig';
 
17528
                $out .= ' /Filter /Adobe.PPKLite';
 
17529
                $out .= ' /SubFilter /adbe.pkcs7.detached';
 
17530
                $out .= ' '.$this->byterange_string;
 
17531
                $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>';
 
17532
                $out .= ' /Reference ['; // array of signature reference dictionaries
 
17533
                $out .= ' << /Type /SigRef';
 
17534
                if ($this->signature_data['cert_type'] > 0) {
 
17535
                        $out .= ' /TransformMethod /DocMDP';
 
17536
                        $out .= ' /TransformParams <<';
 
17537
                        $out .= ' /Type /TransformParams';
 
17538
                        $out .= ' /P '.$this->signature_data['cert_type'];
 
17539
                        $out .= ' /V /1.2';
 
17540
                } else {
 
17541
                        $out .= ' /TransformMethod /UR3';
 
17542
                        $out .= ' /TransformParams <<';
 
17543
                        $out .= ' /Type /TransformParams';
 
17544
                        $out .= ' /V /2.2';
 
17545
                        if (!$this->empty_string($this->ur['document'])) {
 
17546
                                $out .= ' /Document['.$this->ur['document'].']';
 
17547
                        }
 
17548
                        if (!$this->empty_string($this->ur['form'])) {
 
17549
                                $out .= ' /Form['.$this->ur['form'].']';
 
17550
                        }
 
17551
                        if (!$this->empty_string($this->ur['signature'])) {
 
17552
                                $out .= ' /Signature['.$this->ur['signature'].']';
 
17553
                        }
 
17554
                        if (!$this->empty_string($this->ur['annots'])) {
 
17555
                                $out .= ' /Annots['.$this->ur['annots'].']';
 
17556
                        }
 
17557
                        if (!$this->empty_string($this->ur['ef'])) {
 
17558
                                $out .= ' /EF['.$this->ur['ef'].']';
 
17559
                        }
 
17560
                        if (!$this->empty_string($this->ur['formex'])) {
 
17561
                                $out .= ' /FormEX['.$this->ur['formex'].']';
 
17562
                        }
 
17563
                }
 
17564
                $out .= ' >>'; // close TransformParams
 
17565
                // optional digest data (values must be calculated and replaced later)
 
17566
                //$out .= ' /Data ********** 0 R';
 
17567
                //$out .= ' /DigestMethod/MD5';
 
17568
                //$out .= ' /DigestLocation[********** 34]';
 
17569
                //$out .= ' /DigestValue<********************************>';
 
17570
                $out .= ' >>';
 
17571
                $out .= ' ]'; // end of reference
 
17572
                if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) {
 
17573
                        $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid);
 
17574
                }
 
17575
                if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) {
 
17576
                        $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid);
 
17577
                }
 
17578
                if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) {
 
17579
                        $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid);
 
17580
                }
 
17581
                if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) {
 
17582
                        $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid);
 
17583
                }
 
17584
                $out .= ' /M '.$this->_datestring($sigobjid);
 
17585
                $out .= ' >>';
 
17586
                $out .= "\n".'endobj';
 
17587
                $this->_out($out);
 
17588
        }
 
17589
 
 
17590
        /**
 
17591
         * Set User's Rights for PDF Reader
 
17592
         * WARNING: This is experimental and currently do not work.
 
17593
         * Check the PDF Reference 8.7.1 Transform Methods,
 
17594
         * Table 8.105 Entries in the UR transform parameters dictionary
 
17595
         * @param $enable (boolean) if true enable user's rights on PDF reader
 
17596
         * @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.
 
17597
         * @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.
 
17598
         * @param $form (string) Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
 
17599
         * @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.
 
17600
         * @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
 
17601
         Names specifying additional embedded-files-related usage rights for the document.
 
17602
         * @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.
 
17603
         * @public
 
17604
         * @author Nicola Asuni
 
17605
         * @since 2.9.000 (2008-03-26)
 
17606
         */
 
17607
        public function setUserRights(
 
17608
                        $enable=true,
 
17609
                        $document='/FullSave',
 
17610
                        $annots='/Create/Delete/Modify/Copy/Import/Export',
 
17611
                        $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
 
17612
                        $signature='/Modify',
 
17613
                        $ef='/Create/Delete/Modify/Import',
 
17614
                        $formex='') {
 
17615
                $this->ur['enabled'] = $enable;
 
17616
                $this->ur['document'] = $document;
 
17617
                $this->ur['annots'] = $annots;
 
17618
                $this->ur['form'] = $form;
 
17619
                $this->ur['signature'] = $signature;
 
17620
                $this->ur['ef'] = $ef;
 
17621
                $this->ur['formex'] = $formex;
 
17622
                if (!$this->sign) {
 
17623
                        $this->setSignature('', '', '', '', 0, array());
 
17624
                }
 
17625
        }
 
17626
 
 
17627
        /**
 
17628
         * Enable document signature (requires the OpenSSL Library).
 
17629
         * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
 
17630
         * To create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
 
17631
         * To export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
 
17632
         * To convert pfx certificate to pem: openssl pkcs12 -in tcpdf.pfx -out tcpdf.crt -nodes
 
17633
         * @param $signing_cert (mixed) signing certificate (string or filename prefixed with 'file://')
 
17634
         * @param $private_key (mixed) private key (string or filename prefixed with 'file://')
 
17635
         * @param $private_key_password (string) password
 
17636
         * @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.
 
17637
         * @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.
 
17638
         * @param $info (array) array of option information: Name, Location, Reason, ContactInfo.
 
17639
         * @public
 
17640
         * @author Nicola Asuni
 
17641
         * @since 4.6.005 (2009-04-24)
 
17642
         */
 
17643
        public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
 
17644
                // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
 
17645
                // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
 
17646
                // to convert pfx certificate to pem: openssl
 
17647
                //     OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
 
17648
                $this->sign = true;
 
17649
                ++$this->n;
 
17650
                $this->sig_obj_id = $this->n; // signature widget
 
17651
                ++$this->n; // signature object ($this->sig_obj_id + 1)
 
17652
                $this->signature_data = array();
 
17653
                if (strlen($signing_cert) == 0) {
 
17654
                        $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.crt';
 
17655
                        $private_key_password = 'tcpdfdemo';
 
17656
                }
 
17657
                if (strlen($private_key) == 0) {
 
17658
                        $private_key = $signing_cert;
 
17659
                }
 
17660
                $this->signature_data['signcert'] = $signing_cert;
 
17661
                $this->signature_data['privkey'] = $private_key;
 
17662
                $this->signature_data['password'] = $private_key_password;
 
17663
                $this->signature_data['extracerts'] = $extracerts;
 
17664
                $this->signature_data['cert_type'] = $cert_type;
 
17665
                $this->signature_data['info'] = $info;
 
17666
        }
 
17667
 
 
17668
        /**
 
17669
         * Set the digital signature appearance (a cliccable rectangle area to get signature properties)
 
17670
         * @param $x (float) Abscissa of the upper-left corner.
 
17671
         * @param $y (float) Ordinate of the upper-left corner.
 
17672
         * @param $w (float) Width of the signature area.
 
17673
         * @param $h (float) Height of the signature area.
 
17674
         * @param $page (int) option page number (if < 0 the current page is used).
 
17675
         * @public
 
17676
         * @author Nicola Asuni
 
17677
         * @since 5.3.011 (2010-06-17)
 
17678
         */
 
17679
        public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1) {
 
17680
                $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page);
 
17681
        }
 
17682
 
 
17683
        /**
 
17684
         * Add an empty digital signature appearance (a cliccable rectangle area to get signature properties)
 
17685
         * @param $x (float) Abscissa of the upper-left corner.
 
17686
         * @param $y (float) Ordinate of the upper-left corner.
 
17687
         * @param $w (float) Width of the signature area.
 
17688
         * @param $h (float) Height of the signature area.
 
17689
         * @param $page (int) option page number (if < 0 the current page is used).
 
17690
         * @public
 
17691
         * @author Nicola Asuni
 
17692
         * @since 5.9.101 (2011-07-06)
 
17693
         */
 
17694
        public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1) {
 
17695
                ++$this->n;
 
17696
                $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page);
 
17697
        }
 
17698
 
 
17699
        /**
 
17700
         * Get the array that defines the signature appearance (page and rectangle coordinates).
 
17701
         * @param $x (float) Abscissa of the upper-left corner.
 
17702
         * @param $y (float) Ordinate of the upper-left corner.
 
17703
         * @param $w (float) Width of the signature area.
 
17704
         * @param $h (float) Height of the signature area.
 
17705
         * @param $page (int) option page number (if < 0 the current page is used).
 
17706
         * @return (array) Array defining page and rectangle coordinates of signature appearance.
 
17707
         * @protected
 
17708
         * @author Nicola Asuni
 
17709
         * @since 5.9.101 (2011-07-06)
 
17710
         */
 
17711
        protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1) {
 
17712
                $sigapp = array();
 
17713
                if (($page < 1) OR ($page > $this->numpages)) {
 
17714
                        $sigapp['page'] = $this->page;
 
17715
                } else {
 
17716
                        $sigapp['page'] = intval($page);
 
17717
                }
 
17718
                $a = $x * $this->k;
 
17719
                $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k);
 
17720
                $c = $w * $this->k;
 
17721
                $d = $h * $this->k;
 
17722
                $sigapp['rect'] = sprintf('%.2F %.2F %.2F %.2F', $a, $b, ($a + $c), ($b + $d));
 
17723
                return $sigapp;
 
17724
        }
 
17725
 
 
17726
        /**
 
17727
         * Create a new page group.
 
17728
         * NOTE: call this function before calling AddPage()
 
17729
         * @param $page (int) starting group page (leave empty for next page).
 
17730
         * @public
 
17731
         * @since 3.0.000 (2008-03-27)
 
17732
         */
 
17733
        public function startPageGroup($page='') {
 
17734
                if (empty($page)) {
 
17735
                        $page = $this->page + 1;
 
17736
                }
 
17737
                $this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1;
 
17738
        }
 
17739
 
 
17740
        /**
 
17741
         * This method is DEPRECATED and doesn't have any effect.
 
17742
         * Please remove any reference to this method.
 
17743
         * @param $s (string) Empty parameter.
 
17744
         * @deprecated deprecated since version 5.9.089 (2011-06-13)
 
17745
         * @public
 
17746
         */
 
17747
        public function AliasNbPages($s='') {}
 
17748
 
 
17749
        /**
 
17750
         * This method is DEPRECATED and doesn't have any effect.
 
17751
         * Please remove any reference to this method.
 
17752
         * @param $s (string) Empty parameter.
 
17753
         * @deprecated deprecated since version 5.9.089 (2011-06-13)
 
17754
         * @public
 
17755
         */
 
17756
        public function AliasNumPage($s='') {}
 
17757
 
 
17758
        /**
 
17759
         * Set the starting page number.
 
17760
         * @param $num (int) Starting page number.
 
17761
         * @since 5.9.093 (2011-06-16)
 
17762
         * @public
 
17763
         */
 
17764
        public function setStartingPageNumber($num=1) {
 
17765
                $this->starting_page_number = max(0, intval($num));
 
17766
        }
 
17767
 
 
17768
        /**
 
17769
         * Returns the string alias used right align page numbers.
 
17770
         * If the current font is unicode type, the returned string wil contain an additional open curly brace.
 
17771
         * @return string
 
17772
         * @since 5.9.099 (2011-06-27)
 
17773
         * @public
 
17774
         */
 
17775
        public function getAliasRightShift() {
 
17776
                // calculate aproximatively the ratio between widths of aliases and replacements.
 
17777
                $ref = '{'.$this->alias_right_shift.'}{'.$this->alias_tot_pages.'}{'.$this->alias_num_page.'}';
 
17778
                $rep = str_repeat(' ', $this->GetNumChars($ref));
 
17779
                $wdiff = max(1, ($this->GetStringWidth($ref) / $this->GetStringWidth($rep)));
 
17780
                $sdiff = sprintf('%.3F', $wdiff);
 
17781
                $alias = $this->alias_right_shift.$sdiff.'}';
 
17782
                if ($this->isUnicodeFont()) {
 
17783
                        $alias = '{'.$alias;
 
17784
                }
 
17785
                return $alias;
 
17786
        }
 
17787
 
 
17788
        /**
 
17789
         * Returns the string alias used for the total number of pages.
 
17790
         * If the current font is unicode type, the returned string is surrounded by additional curly braces.
 
17791
         * This alias will be replaced by the total number of pages in the document.
 
17792
         * @return string
 
17793
         * @since 4.0.018 (2008-08-08)
 
17794
         * @public
 
17795
         */
 
17796
        public function getAliasNbPages() {
 
17797
                if ($this->isUnicodeFont()) {
 
17798
                        return '{'.$this->alias_tot_pages.'}';
 
17799
                }
 
17800
                return $this->alias_tot_pages;
 
17801
        }
 
17802
 
 
17803
        /**
 
17804
         * Returns the string alias used for the page number.
 
17805
         * If the current font is unicode type, the returned string is surrounded by additional curly braces.
 
17806
         * This alias will be replaced by the page number.
 
17807
         * @return string
 
17808
         * @since 4.5.000 (2009-01-02)
 
17809
         * @public
 
17810
         */
 
17811
        public function getAliasNumPage() {
 
17812
                if ($this->isUnicodeFont()) {
 
17813
                        return '{'.$this->alias_num_page.'}';
 
17814
                }
 
17815
                return $this->alias_num_page;
 
17816
        }
 
17817
 
 
17818
        /**
 
17819
         * Return the alias for the total number of pages in the current page group.
 
17820
         * If the current font is unicode type, the returned string is surrounded by additional curly braces.
 
17821
         * This alias will be replaced by the total number of pages in this group.
 
17822
         * @return alias of the current page group
 
17823
         * @public
 
17824
         * @since 3.0.000 (2008-03-27)
 
17825
         */
 
17826
        public function getPageGroupAlias() {
 
17827
                if ($this->isUnicodeFont()) {
 
17828
                        return '{'.$this->alias_group_tot_pages.'}';
 
17829
                }
 
17830
                return $this->alias_group_tot_pages;
 
17831
        }
 
17832
 
 
17833
        /**
 
17834
         * Return the alias for the page number on the current page group.
 
17835
         * If the current font is unicode type, the returned string is surrounded by additional curly braces.
 
17836
         * This alias will be replaced by the page number (relative to the belonging group).
 
17837
         * @return alias of the current page group
 
17838
         * @public
 
17839
         * @since 4.5.000 (2009-01-02)
 
17840
         */
 
17841
        public function getPageNumGroupAlias() {
 
17842
                if ($this->isUnicodeFont()) {
 
17843
                        return '{'.$this->alias_group_num_page.'}';
 
17844
                }
 
17845
                return $this->alias_group_num_page;
 
17846
        }
 
17847
 
 
17848
        /**
 
17849
         * Return the current page in the group.
 
17850
         * @return current page in the group
 
17851
         * @public
 
17852
         * @since 3.0.000 (2008-03-27)
 
17853
         */
 
17854
        public function getGroupPageNo() {
 
17855
                return $this->pagegroups[$this->currpagegroup];
 
17856
        }
 
17857
 
 
17858
        /**
 
17859
         * Returns the current group page number formatted as a string.
 
17860
         * @public
 
17861
         * @since 4.3.003 (2008-11-18)
 
17862
         * @see PaneNo(), formatPageNumber()
 
17863
         */
 
17864
        public function getGroupPageNoFormatted() {
 
17865
                return $this->formatPageNumber($this->getGroupPageNo());
 
17866
        }
 
17867
 
 
17868
        /**
 
17869
         * Format the page numbers.
 
17870
         * This method can be overriden for custom formats.
 
17871
         * @param $num (int) page number
 
17872
         * @protected
 
17873
         * @since 4.2.005 (2008-11-06)
 
17874
         */
 
17875
        protected function formatPageNumber($num) {
 
17876
                return number_format((float)$num, 0, '', '.');
 
17877
        }
 
17878
 
 
17879
        /**
 
17880
         * Format the page numbers on the Table Of Content.
 
17881
         * This method can be overriden for custom formats.
 
17882
         * @param $num (int) page number
 
17883
         * @protected
 
17884
         * @since 4.5.001 (2009-01-04)
 
17885
         * @see addTOC(), addHTMLTOC()
 
17886
         */
 
17887
        protected function formatTOCPageNumber($num) {
 
17888
                return number_format((float)$num, 0, '', '.');
 
17889
        }
 
17890
 
 
17891
        /**
 
17892
         * Returns the current page number formatted as a string.
 
17893
         * @public
 
17894
         * @since 4.2.005 (2008-11-06)
 
17895
         * @see PaneNo(), formatPageNumber()
 
17896
         */
 
17897
        public function PageNoFormatted() {
 
17898
                return $this->formatPageNumber($this->PageNo());
 
17899
        }
 
17900
 
 
17901
        /**
 
17902
         * Put pdf layers.
 
17903
         * @protected
 
17904
         * @since 3.0.000 (2008-03-27)
 
17905
         */
 
17906
        protected function _putocg() {
 
17907
                if (empty($this->pdflayers)) {
 
17908
                        return;
 
17909
                }
 
17910
                foreach ($this->pdflayers as $key => $layer) {
 
17911
                         $this->pdflayers[$key]['objid'] = $this->_newobj();
 
17912
                         $out = '<< /Type /OCG';
 
17913
                         $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']);
 
17914
                         $out .= ' /Usage <<';
 
17915
                         $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>';
 
17916
                         $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>';
 
17917
                         $out .= ' >> >>';
 
17918
                         $out .= "\n".'endobj';
 
17919
                         $this->_out($out);
 
17920
                }
 
17921
        }
 
17922
 
 
17923
        /**
 
17924
         * Start a new pdf layer.
 
17925
         * @param $name (string) Layer name (only a-z letters and numbers). Leave empty for automatic name.
 
17926
         * @param $print (boolean) Set to true to print this layer.
 
17927
         * @param $view (boolean) Set to true to view this layer.
 
17928
         * @public
 
17929
         * @since 5.9.102 (2011-07-13)
 
17930
         */
 
17931
        public function startLayer($name='', $print=true, $view=true) {
 
17932
                $layer = sprintf('LYR%03d', (count($this->pdflayers) + 1));
 
17933
                if (empty($name)) {
 
17934
                        $name = $layer;
 
17935
                } else {
 
17936
                        $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
 
17937
                }
 
17938
                $this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view);
 
17939
                $this->openMarkedContent = true;
 
17940
                $this->_out('/OC /'.$layer.' BDC');
 
17941
        }
 
17942
 
 
17943
        /**
 
17944
         * End the current PDF layer.
 
17945
         * @public
 
17946
         * @since 5.9.102 (2011-07-13)
 
17947
         */
 
17948
        public function endLayer() {
 
17949
                if ($this->openMarkedContent) {
 
17950
                        // close existing open marked-content layer
 
17951
                        $this->_out('EMC');
 
17952
                        $this->openMarkedContent = false;
 
17953
                }
 
17954
        }
 
17955
 
 
17956
        /**
 
17957
         * Set the visibility of the successive elements.
 
17958
         * This can be useful, for instance, to put a background
 
17959
         * image or color that will show on screen but won't print.
 
17960
         * @param $v (string) visibility mode. Legal values are: all, print, screen or view.
 
17961
         * @public
 
17962
         * @since 3.0.000 (2008-03-27)
 
17963
         */
 
17964
        public function setVisibility($v) {
 
17965
                $this->endLayer();
 
17966
                switch($v) {
 
17967
                        case 'print': {
 
17968
                                $this->startLayer('Print', true, false);
 
17969
                                break;
 
17970
                        }
 
17971
                        case 'view':
 
17972
                        case 'screen': {
 
17973
                                $this->startLayer('View', false, true);
 
17974
                                break;
 
17975
                        }
 
17976
                        case 'all': {
 
17977
                                $this->_out('');
 
17978
                                break;
 
17979
                        }
 
17980
                        default: {
 
17981
                                $this->Error('Incorrect visibility: '.$v);
 
17982
                                break;
 
17983
                        }
 
17984
                }
 
17985
        }
 
17986
 
 
17987
        /**
 
17988
         * Add transparency parameters to the current extgstate
 
17989
         * @param $parms (array) parameters
 
17990
         * @return the number of extgstates
 
17991
         * @protected
 
17992
         * @since 3.0.000 (2008-03-27)
 
17993
         */
 
17994
        protected function addExtGState($parms) {
 
17995
                if ($this->pdfa_mode) {
 
17996
                        // transparencies are not allowed in PDF/A mode
 
17997
                        return;
 
17998
                }
 
17999
                // check if this ExtGState already exist
 
18000
                foreach ($this->extgstates as $i => $ext) {
 
18001
                        if ($ext['parms'] == $parms) {
 
18002
                                if ($this->inxobj) {
 
18003
                                        // we are inside an XObject template
 
18004
                                        $this->xobjects[$this->xobjid]['extgstates'][$i] = $ext;
 
18005
                                }
 
18006
                                // return reference to existing ExtGState
 
18007
                                return $i;
 
18008
                        }
 
18009
                }
 
18010
                $n = (count($this->extgstates) + 1);
 
18011
                $this->extgstates[$n] = array('parms' => $parms);
 
18012
                if ($this->inxobj) {
 
18013
                        // we are inside an XObject template
 
18014
                        $this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n];
 
18015
                }
 
18016
                return $n;
 
18017
        }
 
18018
 
 
18019
        /**
 
18020
         * Add an extgstate
 
18021
         * @param $gs (array) extgstate
 
18022
         * @protected
 
18023
         * @since 3.0.000 (2008-03-27)
 
18024
         */
 
18025
        protected function setExtGState($gs) {
 
18026
                if ($this->pdfa_mode) {
 
18027
                        // transparency is not allowed in PDF/A mode
 
18028
                        return;
 
18029
                }
 
18030
                $this->_out(sprintf('/GS%d gs', $gs));
 
18031
        }
 
18032
 
 
18033
        /**
 
18034
         * Put extgstates for object transparency
 
18035
         * @protected
 
18036
         * @since 3.0.000 (2008-03-27)
 
18037
         */
 
18038
        protected function _putextgstates() {
 
18039
                if ($this->pdfa_mode) {
 
18040
                        // transparencies are not allowed in PDF/A mode
 
18041
                        return;
 
18042
                }
 
18043
                foreach ($this->extgstates as $i => $ext) {
 
18044
                        $this->extgstates[$i]['n'] = $this->_newobj();
 
18045
                        $out = '<< /Type /ExtGState';
 
18046
                        foreach ($ext['parms'] as $k => $v) {
 
18047
                                if (is_float($v)) {
 
18048
                                        $v = sprintf('%.2F', $v);
 
18049
                                }
 
18050
                                $out .= ' /'.$k.' '.$v;
 
18051
                        }
 
18052
                        $out .= ' >>';
 
18053
                        $out .= "\n".'endobj';
 
18054
                        $this->_out($out);
 
18055
                }
 
18056
        }
 
18057
 
 
18058
        /**
 
18059
         * Set alpha for stroking (CA) and non-stroking (ca) operations.
 
18060
         * @param $alpha (float) real value from 0 (transparent) to 1 (opaque)
 
18061
         * @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
 
18062
         * @public
 
18063
         * @since 3.0.000 (2008-03-27)
 
18064
         */
 
18065
        public function setAlpha($alpha, $bm='Normal') {
 
18066
                if ($this->pdfa_mode) {
 
18067
                        // transparency is not allowed in PDF/A mode
 
18068
                        return;
 
18069
                }
 
18070
                $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm, 'AIS' => 'false'));
 
18071
                $this->setExtGState($gs);
 
18072
        }
 
18073
 
 
18074
        /**
 
18075
         * Set the default JPEG compression quality (1-100)
 
18076
         * @param $quality (int) JPEG quality, integer between 1 and 100
 
18077
         * @public
 
18078
         * @since 3.0.000 (2008-03-27)
 
18079
         */
 
18080
        public function setJPEGQuality($quality) {
 
18081
                if (($quality < 1) OR ($quality > 100)) {
 
18082
                        $quality = 75;
 
18083
                }
 
18084
                $this->jpeg_quality = intval($quality);
 
18085
        }
 
18086
 
 
18087
        /**
 
18088
         * Set the default number of columns in a row for HTML tables.
 
18089
         * @param $cols (int) number of columns
 
18090
         * @public
 
18091
         * @since 3.0.014 (2008-06-04)
 
18092
         */
 
18093
        public function setDefaultTableColumns($cols=4) {
 
18094
                $this->default_table_columns = intval($cols);
 
18095
        }
 
18096
 
 
18097
        /**
 
18098
         * Set the height of the cell (line height) respect the font height.
 
18099
         * @param $h (int) cell proportion respect font height (typical value = 1.25).
 
18100
         * @public
 
18101
         * @since 3.0.014 (2008-06-04)
 
18102
         */
 
18103
        public function setCellHeightRatio($h) {
 
18104
                $this->cell_height_ratio = $h;
 
18105
        }
 
18106
 
 
18107
        /**
 
18108
         * return the height of cell repect font height.
 
18109
         * @public
 
18110
         * @since 4.0.012 (2008-07-24)
 
18111
         */
 
18112
        public function getCellHeightRatio() {
 
18113
                return $this->cell_height_ratio;
 
18114
        }
 
18115
 
 
18116
        /**
 
18117
         * Set the PDF version (check PDF reference for valid values).
 
18118
         * @param $version (string) PDF document version.
 
18119
         * @public
 
18120
         * @since 3.1.000 (2008-06-09)
 
18121
         */
 
18122
        public function setPDFVersion($version='1.7') {
 
18123
                if ($this->pdfa_mode) {
 
18124
                        // PDF/A mode
 
18125
                        $this->PDFVersion = '1.4';
 
18126
                } else {
 
18127
                        $this->PDFVersion = $version;
 
18128
                }
 
18129
        }
 
18130
 
 
18131
        /**
 
18132
         * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
 
18133
         * (see Section 8.1 of PDF reference, "Viewer Preferences").
 
18134
         * <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>
 
18135
         * @param $preferences (array) array of options.
 
18136
         * @author Nicola Asuni
 
18137
         * @public
 
18138
         * @since 3.1.000 (2008-06-09)
 
18139
         */
 
18140
        public function setViewerPreferences($preferences) {
 
18141
                $this->viewer_preferences = $preferences;
 
18142
        }
 
18143
 
 
18144
        /**
 
18145
         * Paints color transition registration bars
 
18146
         * @param $x (float) abscissa of the top left corner of the rectangle.
 
18147
         * @param $y (float) ordinate of the top left corner of the rectangle.
 
18148
         * @param $w (float) width of the rectangle.
 
18149
         * @param $h (float) height of the rectangle.
 
18150
         * @param $transition (boolean) if true prints tcolor transitions to white.
 
18151
         * @param $vertical (boolean) if true prints bar vertically.
 
18152
         * @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.
 
18153
         * @author Nicola Asuni
 
18154
         * @since 4.9.000 (2010-03-26)
 
18155
         * @public
 
18156
         */
 
18157
        public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
 
18158
                $bars = explode(',', $colors);
 
18159
                $numbars = count($bars); // number of bars to print
 
18160
                // set bar measures
 
18161
                if ($vertical) {
 
18162
                        $coords = array(0, 0, 0, 1);
 
18163
                        $wb = $w / $numbars; // bar width
 
18164
                        $hb = $h; // bar height
 
18165
                        $xd = $wb; // delta x
 
18166
                        $yd = 0; // delta y
 
18167
                } else {
 
18168
                        $coords = array(1, 0, 0, 0);
 
18169
                        $wb = $w; // bar width
 
18170
                        $hb = $h / $numbars; // bar height
 
18171
                        $xd = 0; // delta x
 
18172
                        $yd = $hb; // delta y
 
18173
                }
 
18174
                $xb = $x;
 
18175
                $yb = $y;
 
18176
                foreach ($bars as $col) {
 
18177
                        switch ($col) {
 
18178
                                // set transition colors
 
18179
                                case 'A': { // BLACK
 
18180
                                        $col_a = array(255);
 
18181
                                        $col_b = array(0);
 
18182
                                        break;
 
18183
                                }
 
18184
                                case 'W': { // WHITE
 
18185
                                        $col_a = array(0);
 
18186
                                        $col_b = array(255);
 
18187
                                        break;
 
18188
                                }
 
18189
                                case 'R': { // R
 
18190
                                        $col_a = array(255,255,255);
 
18191
                                        $col_b = array(255,0,0);
 
18192
                                        break;
 
18193
                                }
 
18194
                                case 'G': { // G
 
18195
                                        $col_a = array(255,255,255);
 
18196
                                        $col_b = array(0,255,0);
 
18197
                                        break;
 
18198
                                }
 
18199
                                case 'B': { // B
 
18200
                                        $col_a = array(255,255,255);
 
18201
                                        $col_b = array(0,0,255);
 
18202
                                        break;
 
18203
                                }
 
18204
                                case 'C': { // C
 
18205
                                        $col_a = array(0,0,0,0);
 
18206
                                        $col_b = array(100,0,0,0);
 
18207
                                        break;
 
18208
                                }
 
18209
                                case 'M': { // M
 
18210
                                        $col_a = array(0,0,0,0);
 
18211
                                        $col_b = array(0,100,0,0);
 
18212
                                        break;
 
18213
                                }
 
18214
                                case 'Y': { // Y
 
18215
                                        $col_a = array(0,0,0,0);
 
18216
                                        $col_b = array(0,0,100,0);
 
18217
                                        break;
 
18218
                                }
 
18219
                                case 'K': { // K
 
18220
                                        $col_a = array(0,0,0,0);
 
18221
                                        $col_b = array(0,0,0,100);
 
18222
                                        break;
 
18223
                                }
 
18224
                                default: { // GRAY
 
18225
                                        $col_a = array(255);
 
18226
                                        $col_b = array(0);
 
18227
                                        break;
 
18228
                                }
 
18229
                        }
 
18230
                        if ($transition) {
 
18231
                                // color gradient
 
18232
                                $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
 
18233
                        } else {
 
18234
                                // color rectangle
 
18235
                                $this->SetFillColorArray($col_b);
 
18236
                                $this->Rect($xb, $yb, $wb, $hb, 'F', array());
 
18237
                        }
 
18238
                        $xb += $xd;
 
18239
                        $yb += $yd;
 
18240
                }
 
18241
        }
 
18242
 
 
18243
        /**
 
18244
         * Paints crop marks.
 
18245
         * @param $x (float) abscissa of the crop mark center.
 
18246
         * @param $y (float) ordinate of the crop mark center.
 
18247
         * @param $w (float) width of the crop mark.
 
18248
         * @param $h (float) height of the crop mark.
 
18249
         * @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.
 
18250
         * @param $color (array) crop mark color (default black).
 
18251
         * @author Nicola Asuni
 
18252
         * @since 4.9.000 (2010-03-26)
 
18253
         * @public
 
18254
         */
 
18255
        public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(0,0,0)) {
 
18256
                $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
 
18257
                $type = strtoupper($type);
 
18258
                $type = preg_replace('/[^A-Z\-\,]*/', '', $type);
 
18259
                // split type in single components
 
18260
                $type = str_replace('-', ',', $type);
 
18261
                $type = str_replace('TL', 'T,L', $type);
 
18262
                $type = str_replace('TR', 'T,R', $type);
 
18263
                $type = str_replace('BL', 'F,L', $type);
 
18264
                $type = str_replace('BR', 'F,R', $type);
 
18265
                $type = str_replace('A', 'T,L', $type);
 
18266
                $type = str_replace('B', 'T,R', $type);
 
18267
                $type = str_replace('T,RO', 'BO', $type);
 
18268
                $type = str_replace('C', 'F,L', $type);
 
18269
                $type = str_replace('D', 'F,R', $type);
 
18270
                $crops = explode(',', strtoupper($type));
 
18271
                // remove duplicates
 
18272
                $crops = array_unique($crops);
 
18273
                $dw = ($w / 4); // horizontal space to leave before the intersection point
 
18274
                $dh = ($h / 4); // vertical space to leave before the intersection point
 
18275
                foreach ($crops as $crop) {
 
18276
                        switch ($crop) {
 
18277
                                case 'T':
 
18278
                                case 'TOP': {
 
18279
                                        $x1 = $x;
 
18280
                                        $y1 = ($y - $h);
 
18281
                                        $x2 = $x;
 
18282
                                        $y2 = ($y - $dh);
 
18283
                                        break;
 
18284
                                }
 
18285
                                case 'F':
 
18286
                                case 'BOTTOM': {
 
18287
                                        $x1 = $x;
 
18288
                                        $y1 = ($y + $dh);
 
18289
                                        $x2 = $x;
 
18290
                                        $y2 = ($y + $h);
 
18291
                                        break;
 
18292
                                }
 
18293
                                case 'L':
 
18294
                                case 'LEFT': {
 
18295
                                        $x1 = ($x - $w);
 
18296
                                        $y1 = $y;
 
18297
                                        $x2 = ($x - $dw);
 
18298
                                        $y2 = $y;
 
18299
                                        break;
 
18300
                                }
 
18301
                                case 'R':
 
18302
                                case 'RIGHT': {
 
18303
                                        $x1 = ($x + $dw);
 
18304
                                        $y1 = $y;
 
18305
                                        $x2 = ($x + $w);
 
18306
                                        $y2 = $y;
 
18307
                                        break;
 
18308
                                }
 
18309
                        }
 
18310
                        $this->Line($x1, $y1, $x2, $y2);
 
18311
                }
 
18312
        }
 
18313
 
 
18314
        /**
 
18315
         * Paints a registration mark
 
18316
         * @param $x (float) abscissa of the registration mark center.
 
18317
         * @param $y (float) ordinate of the registration mark center.
 
18318
         * @param $r (float) radius of the crop mark.
 
18319
         * @param $double (boolean) if true print two concentric crop marks.
 
18320
         * @param $cola (array) crop mark color (default black).
 
18321
         * @param $colb (array) second crop mark color.
 
18322
         * @author Nicola Asuni
 
18323
         * @since 4.9.000 (2010-03-26)
 
18324
         * @public
 
18325
         */
 
18326
        public function registrationMark($x, $y, $r, $double=false, $cola=array(0,0,0), $colb=array(255,255,255)) {
 
18327
                $line_style = array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
 
18328
                $this->SetFillColorArray($cola);
 
18329
                $this->PieSector($x, $y, $r, 90, 180, 'F');
 
18330
                $this->PieSector($x, $y, $r, 270, 360, 'F');
 
18331
                $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
 
18332
                if ($double) {
 
18333
                        $r2 = $r * 0.5;
 
18334
                        $this->SetFillColorArray($colb);
 
18335
                        $this->PieSector($x, $y, $r2, 90, 180, 'F');
 
18336
                        $this->PieSector($x, $y, $r2, 270, 360, 'F');
 
18337
                        $this->SetFillColorArray($cola);
 
18338
                        $this->PieSector($x, $y, $r2, 0, 90, 'F');
 
18339
                        $this->PieSector($x, $y, $r2, 180, 270, 'F');
 
18340
                        $this->Circle($x, $y, $r2, 0, 360, 'C', $line_style, array(), 8);
 
18341
                }
 
18342
        }
 
18343
 
 
18344
        /**
 
18345
         * Paints a linear colour gradient.
 
18346
         * @param $x (float) abscissa of the top left corner of the rectangle.
 
18347
         * @param $y (float) ordinate of the top left corner of the rectangle.
 
18348
         * @param $w (float) width of the rectangle.
 
18349
         * @param $h (float) height of the rectangle.
 
18350
         * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
 
18351
         * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
 
18352
         * @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).
 
18353
         * @author Andreas W�rmser, Nicola Asuni
 
18354
         * @since 3.1.000 (2008-06-09)
 
18355
         * @public
 
18356
         */
 
18357
        public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
 
18358
                $this->Clip($x, $y, $w, $h);
 
18359
                $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
 
18360
        }
 
18361
 
 
18362
        /**
 
18363
         * Paints a radial colour gradient.
 
18364
         * @param $x (float) abscissa of the top left corner of the rectangle.
 
18365
         * @param $y (float) ordinate of the top left corner of the rectangle.
 
18366
         * @param $w (float) width of the rectangle.
 
18367
         * @param $h (float) height of the rectangle.
 
18368
         * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
 
18369
         * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
 
18370
         * @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.
 
18371
         * @author Andreas W�rmser, Nicola Asuni
 
18372
         * @since 3.1.000 (2008-06-09)
 
18373
         * @public
 
18374
         */
 
18375
        public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
 
18376
                $this->Clip($x, $y, $w, $h);
 
18377
                $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
 
18378
        }
 
18379
 
 
18380
        /**
 
18381
         * Paints a coons patch mesh.
 
18382
         * @param $x (float) abscissa of the top left corner of the rectangle.
 
18383
         * @param $y (float) ordinate of the top left corner of the rectangle.
 
18384
         * @param $w (float) width of the rectangle.
 
18385
         * @param $h (float) height of the rectangle.
 
18386
         * @param $col1 (array) first color (lower left corner) (RGB components).
 
18387
         * @param $col2 (array) second color (lower right corner) (RGB components).
 
18388
         * @param $col3 (array) third color (upper right corner) (RGB components).
 
18389
         * @param $col4 (array) fourth color (upper left corner) (RGB components).
 
18390
         * @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>
 
18391
         * @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
 
18392
         * @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
 
18393
         * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
 
18394
         * @author Andreas W�rmser, Nicola Asuni
 
18395
         * @since 3.1.000 (2008-06-09)
 
18396
         * @public
 
18397
         */
 
18398
        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) {
 
18399
                if ($this->pdfa_mode) {
 
18400
                        return;
 
18401
                }
 
18402
                $this->Clip($x, $y, $w, $h);
 
18403
                $n = count($this->gradients) + 1;
 
18404
                $this->gradients[$n] = array();
 
18405
                $this->gradients[$n]['type'] = 6; //coons patch mesh
 
18406
                $this->gradients[$n]['coords'] = array();
 
18407
                $this->gradients[$n]['antialias'] = $antialias;
 
18408
                $this->gradients[$n]['colors'] = array();
 
18409
                $this->gradients[$n]['transparency'] = false;
 
18410
                //check the coords array if it is the simple array or the multi patch array
 
18411
                if (!isset($coords[0]['f'])) {
 
18412
                        //simple array -> convert to multi patch array
 
18413
                        if (!isset($col1[1])) {
 
18414
                                $col1[1] = $col1[2] = $col1[0];
 
18415
                        }
 
18416
                        if (!isset($col2[1])) {
 
18417
                                $col2[1] = $col2[2] = $col2[0];
 
18418
                        }
 
18419
                        if (!isset($col3[1])) {
 
18420
                                $col3[1] = $col3[2] = $col3[0];
 
18421
                        }
 
18422
                        if (!isset($col4[1])) {
 
18423
                                $col4[1] = $col4[2] = $col4[0];
 
18424
                        }
 
18425
                        $patch_array[0]['f'] = 0;
 
18426
                        $patch_array[0]['points'] = $coords;
 
18427
                        $patch_array[0]['colors'][0]['r'] = $col1[0];
 
18428
                        $patch_array[0]['colors'][0]['g'] = $col1[1];
 
18429
                        $patch_array[0]['colors'][0]['b'] = $col1[2];
 
18430
                        $patch_array[0]['colors'][1]['r'] = $col2[0];
 
18431
                        $patch_array[0]['colors'][1]['g'] = $col2[1];
 
18432
                        $patch_array[0]['colors'][1]['b'] = $col2[2];
 
18433
                        $patch_array[0]['colors'][2]['r'] = $col3[0];
 
18434
                        $patch_array[0]['colors'][2]['g'] = $col3[1];
 
18435
                        $patch_array[0]['colors'][2]['b'] = $col3[2];
 
18436
                        $patch_array[0]['colors'][3]['r'] = $col4[0];
 
18437
                        $patch_array[0]['colors'][3]['g'] = $col4[1];
 
18438
                        $patch_array[0]['colors'][3]['b'] = $col4[2];
 
18439
                } else {
 
18440
                        //multi patch array
 
18441
                        $patch_array = $coords;
 
18442
                }
 
18443
                $bpcd = 65535; //16 bits per coordinate
 
18444
                //build the data stream
 
18445
                $this->gradients[$n]['stream'] = '';
 
18446
                $count_patch = count($patch_array);
 
18447
                for ($i=0; $i < $count_patch; ++$i) {
 
18448
                        $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
 
18449
                        $count_points = count($patch_array[$i]['points']);
 
18450
                        for ($j=0; $j < $count_points; ++$j) {
 
18451
                                //each point as 16 bit
 
18452
                                $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
 
18453
                                if ($patch_array[$i]['points'][$j] < 0) {
 
18454
                                        $patch_array[$i]['points'][$j] = 0;
 
18455
                                }
 
18456
                                if ($patch_array[$i]['points'][$j] > $bpcd) {
 
18457
                                        $patch_array[$i]['points'][$j] = $bpcd;
 
18458
                                }
 
18459
                                $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
 
18460
                                $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
 
18461
                        }
 
18462
                        $count_cols = count($patch_array[$i]['colors']);
 
18463
                        for ($j=0; $j < $count_cols; ++$j) {
 
18464
                                //each color component as 8 bit
 
18465
                                $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
 
18466
                                $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
 
18467
                                $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
 
18468
                        }
 
18469
                }
 
18470
                //paint the gradient
 
18471
                $this->_out('/Sh'.$n.' sh');
 
18472
                //restore previous Graphic State
 
18473
                $this->_out('Q');
 
18474
                if ($this->inxobj) {
 
18475
                        // we are inside an XObject template
 
18476
                        $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
 
18477
                }
 
18478
        }
 
18479
 
 
18480
        /**
 
18481
         * Set a rectangular clipping area.
 
18482
         * @param $x (float) abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
 
18483
         * @param $y (float) ordinate of the top left corner of the rectangle.
 
18484
         * @param $w (float) width of the rectangle.
 
18485
         * @param $h (float) height of the rectangle.
 
18486
         * @author Andreas W�rmser, Nicola Asuni
 
18487
         * @since 3.1.000 (2008-06-09)
 
18488
         * @protected
 
18489
         */
 
18490
        protected function Clip($x, $y, $w, $h) {
 
18491
                if ($this->rtl) {
 
18492
                        $x = $this->w - $x - $w;
 
18493
                }
 
18494
                //save current Graphic State
 
18495
                $s = 'q';
 
18496
                //set clipping area
 
18497
                $s .= sprintf(' %.2F %.2F %.2F %.2F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
 
18498
                //set up transformation matrix for gradient
 
18499
                $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
 
18500
                $this->_out($s);
 
18501
        }
 
18502
 
 
18503
        /**
 
18504
         * Output gradient.
 
18505
         * @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)
 
18506
         * @param $coords (array) array of coordinates.
 
18507
         * @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).
 
18508
         * @param $background (array) An array of colour components appropriate to the colour space, specifying a single background colour value.
 
18509
         * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
 
18510
         * @author Nicola Asuni
 
18511
         * @since 3.1.000 (2008-06-09)
 
18512
         * @public
 
18513
         */
 
18514
        public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
 
18515
                if ($this->pdfa_mode) {
 
18516
                        return;
 
18517
                }
 
18518
                $n = count($this->gradients) + 1;
 
18519
                $this->gradients[$n] = array();
 
18520
                $this->gradients[$n]['type'] = $type;
 
18521
                $this->gradients[$n]['coords'] = $coords;
 
18522
                $this->gradients[$n]['antialias'] = $antialias;
 
18523
                $this->gradients[$n]['colors'] = array();
 
18524
                $this->gradients[$n]['transparency'] = false;
 
18525
                // color space
 
18526
                $numcolspace = count($stops[0]['color']);
 
18527
                $bcolor = array_values($background);
 
18528
                switch($numcolspace) {
 
18529
                        case 4: { // CMYK
 
18530
                                $this->gradients[$n]['colspace'] = 'DeviceCMYK';
 
18531
                                if (!empty($background)) {
 
18532
                                        $this->gradients[$n]['background'] = sprintf('%.3F %.3F %.3F %.3F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
 
18533
                                }
 
18534
                                break;
 
18535
                        }
 
18536
                        case 3: { // RGB
 
18537
                                $this->gradients[$n]['colspace'] = 'DeviceRGB';
 
18538
                                if (!empty($background)) {
 
18539
                                        $this->gradients[$n]['background'] = sprintf('%.3F %.3F %.3F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
 
18540
                                }
 
18541
                                break;
 
18542
                        }
 
18543
                        case 1: { // Gray scale
 
18544
                                $this->gradients[$n]['colspace'] = 'DeviceGray';
 
18545
                                if (!empty($background)) {
 
18546
                                        $this->gradients[$n]['background'] = sprintf('%.3F', $bcolor[0]/255);
 
18547
                                }
 
18548
                                break;
 
18549
                        }
 
18550
                }
 
18551
                $num_stops = count($stops);
 
18552
                $last_stop_id = $num_stops - 1;
 
18553
                foreach ($stops as $key => $stop) {
 
18554
                        $this->gradients[$n]['colors'][$key] = array();
 
18555
                        // offset represents a location along the gradient vector
 
18556
                        if (isset($stop['offset'])) {
 
18557
                                $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset'];
 
18558
                        } else {
 
18559
                                if ($key == 0) {
 
18560
                                        $this->gradients[$n]['colors'][$key]['offset'] = 0;
 
18561
                                } elseif ($key == $last_stop_id) {
 
18562
                                        $this->gradients[$n]['colors'][$key]['offset'] = 1;
 
18563
                                } else {
 
18564
                                        $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
 
18565
                                        $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep;
 
18566
                                }
 
18567
                        }
 
18568
                        if (isset($stop['opacity'])) {
 
18569
                                $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity'];
 
18570
                                if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) {
 
18571
                                        $this->gradients[$n]['transparency'] = true;
 
18572
                                }
 
18573
                        } else {
 
18574
                                $this->gradients[$n]['colors'][$key]['opacity'] = 1;
 
18575
                        }
 
18576
                        // exponent for the exponential interpolation function
 
18577
                        if (isset($stop['exponent'])) {
 
18578
                                $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent'];
 
18579
                        } else {
 
18580
                                $this->gradients[$n]['colors'][$key]['exponent'] = 1;
 
18581
                        }
 
18582
                        // set colors
 
18583
                        $color = array_values($stop['color']);
 
18584
                        switch($numcolspace) {
 
18585
                                case 4: { // CMYK
 
18586
                                        $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F %.3F %.3F %.3F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
 
18587
                                        break;
 
18588
                                }
 
18589
                                case 3: { // RGB
 
18590
                                        $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F %.3F %.3F', $color[0]/255, $color[1]/255, $color[2]/255);
 
18591
                                        break;
 
18592
                                }
 
18593
                                case 1: { // Gray scale
 
18594
                                        $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F', $color[0]/255);
 
18595
                                        break;
 
18596
                                }
 
18597
                        }
 
18598
                }
 
18599
                if ($this->gradients[$n]['transparency']) {
 
18600
                        // paint luminosity gradient
 
18601
                        $this->_out('/TGS'.$n.' gs');
 
18602
                }
 
18603
                //paint the gradient
 
18604
                $this->_out('/Sh'.$n.' sh');
 
18605
                //restore previous Graphic State
 
18606
                $this->_out('Q');
 
18607
                if ($this->inxobj) {
 
18608
                        // we are inside an XObject template
 
18609
                        $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
 
18610
                }
 
18611
        }
 
18612
 
 
18613
        /**
 
18614
         * Output gradient shaders.
 
18615
         * @author Nicola Asuni
 
18616
         * @since 3.1.000 (2008-06-09)
 
18617
         * @protected
 
18618
         */
 
18619
        function _putshaders() {
 
18620
                if ($this->pdfa_mode) {
 
18621
                        return;
 
18622
                }
 
18623
                $idt = count($this->gradients); //index for transparency gradients
 
18624
                foreach ($this->gradients as $id => $grad) {
 
18625
                        if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
 
18626
                                $fc = $this->_newobj();
 
18627
                                $out = '<<';
 
18628
                                $out .= ' /FunctionType 3';
 
18629
                                $out .= ' /Domain [0 1]';
 
18630
                                $functions = '';
 
18631
                                $bounds = '';
 
18632
                                $encode = '';
 
18633
                                $i = 1;
 
18634
                                $num_cols = count($grad['colors']);
 
18635
                                $lastcols = $num_cols - 1;
 
18636
                                for ($i = 1; $i < $num_cols; ++$i) {
 
18637
                                        $functions .= ($fc + $i).' 0 R ';
 
18638
                                        if ($i < $lastcols) {
 
18639
                                                $bounds .= sprintf('%.3F ', $grad['colors'][$i]['offset']);
 
18640
                                        }
 
18641
                                        $encode .= '0 1 ';
 
18642
                                }
 
18643
                                $out .= ' /Functions ['.trim($functions).']';
 
18644
                                $out .= ' /Bounds ['.trim($bounds).']';
 
18645
                                $out .= ' /Encode ['.trim($encode).']';
 
18646
                                $out .= ' >>';
 
18647
                                $out .= "\n".'endobj';
 
18648
                                $this->_out($out);
 
18649
                                for ($i = 1; $i < $num_cols; ++$i) {
 
18650
                                        $this->_newobj();
 
18651
                                        $out = '<<';
 
18652
                                        $out .= ' /FunctionType 2';
 
18653
                                        $out .= ' /Domain [0 1]';
 
18654
                                        $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
 
18655
                                        $out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
 
18656
                                        $out .= ' /N '.$grad['colors'][$i]['exponent'];
 
18657
                                        $out .= ' >>';
 
18658
                                        $out .= "\n".'endobj';
 
18659
                                        $this->_out($out);
 
18660
                                }
 
18661
                                // set transparency fuctions
 
18662
                                if ($grad['transparency']) {
 
18663
                                        $ft = $this->_newobj();
 
18664
                                        $out = '<<';
 
18665
                                        $out .= ' /FunctionType 3';
 
18666
                                        $out .= ' /Domain [0 1]';
 
18667
                                        $functions = '';
 
18668
                                        $i = 1;
 
18669
                                        $num_cols = count($grad['colors']);
 
18670
                                        for ($i = 1; $i < $num_cols; ++$i) {
 
18671
                                                $functions .= ($ft + $i).' 0 R ';
 
18672
                                        }
 
18673
                                        $out .= ' /Functions ['.trim($functions).']';
 
18674
                                        $out .= ' /Bounds ['.trim($bounds).']';
 
18675
                                        $out .= ' /Encode ['.trim($encode).']';
 
18676
                                        $out .= ' >>';
 
18677
                                        $out .= "\n".'endobj';
 
18678
                                        $this->_out($out);
 
18679
                                        for ($i = 1; $i < $num_cols; ++$i) {
 
18680
                                                $this->_newobj();
 
18681
                                                $out = '<<';
 
18682
                                                $out .= ' /FunctionType 2';
 
18683
                                                $out .= ' /Domain [0 1]';
 
18684
                                                $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
 
18685
                                                $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
 
18686
                                                $out .= ' /N '.$grad['colors'][$i]['exponent'];
 
18687
                                                $out .= ' >>';
 
18688
                                                $out .= "\n".'endobj';
 
18689
                                                $this->_out($out);
 
18690
                                        }
 
18691
                                }
 
18692
                        }
 
18693
                        // set shading object
 
18694
                        $this->_newobj();
 
18695
                        $out = '<< /ShadingType '.$grad['type'];
 
18696
                        if (isset($grad['colspace'])) {
 
18697
                                $out .= ' /ColorSpace /'.$grad['colspace'];
 
18698
                        } else {
 
18699
                                $out .= ' /ColorSpace /DeviceRGB';
 
18700
                        }
 
18701
                        if (isset($grad['background']) AND !empty($grad['background'])) {
 
18702
                                $out .= ' /Background ['.$grad['background'].']';
 
18703
                        }
 
18704
                        if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
 
18705
                                $out .= ' /AntiAlias true';
 
18706
                        }
 
18707
                        if ($grad['type'] == 2) {
 
18708
                                $out .= ' '.sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
 
18709
                                $out .= ' /Domain [0 1]';
 
18710
                                $out .= ' /Function '.$fc.' 0 R';
 
18711
                                $out .= ' /Extend [true true]';
 
18712
                                $out .= ' >>';
 
18713
                        } elseif ($grad['type'] == 3) {
 
18714
                                //x0, y0, r0, x1, y1, r1
 
18715
                                //at this this time radius of inner circle is 0
 
18716
                                $out .= ' '.sprintf('/Coords [%.3F %.3F 0 %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
 
18717
                                $out .= ' /Domain [0 1]';
 
18718
                                $out .= ' /Function '.$fc.' 0 R';
 
18719
                                $out .= ' /Extend [true true]';
 
18720
                                $out .= ' >>';
 
18721
                        } elseif ($grad['type'] == 6) {
 
18722
                                $out .= ' /BitsPerCoordinate 16';
 
18723
                                $out .= ' /BitsPerComponent 8';
 
18724
                                $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
 
18725
                                $out .= ' /BitsPerFlag 8';
 
18726
                                $stream = $this->_getrawstream($grad['stream']);
 
18727
                                $out .= ' /Length '.strlen($stream);
 
18728
                                $out .= ' >>';
 
18729
                                $out .= ' stream'."\n".$stream."\n".'endstream';
 
18730
                        }
 
18731
                        $out .= "\n".'endobj';
 
18732
                        $this->_out($out);
 
18733
                        if ($grad['transparency']) {
 
18734
                                $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
 
18735
                                $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
 
18736
                        }
 
18737
                        $this->gradients[$id]['id'] = $this->n;
 
18738
                        // set pattern object
 
18739
                        $this->_newobj();
 
18740
                        $out = '<< /Type /Pattern /PatternType 2';
 
18741
                        $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R';
 
18742
                        $out .= ' >>';
 
18743
                        $out .= "\n".'endobj';
 
18744
                        $this->_out($out);
 
18745
                        $this->gradients[$id]['pattern'] = $this->n;
 
18746
                        // set shading and pattern for transparency mask
 
18747
                        if ($grad['transparency']) {
 
18748
                                // luminosity pattern
 
18749
                                $idgs = $id + $idt;
 
18750
                                $this->_newobj();
 
18751
                                $this->_out($shading_transparency);
 
18752
                                $this->gradients[$idgs]['id'] = $this->n;
 
18753
                                $this->_newobj();
 
18754
                                $out = '<< /Type /Pattern /PatternType 2';
 
18755
                                $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R';
 
18756
                                $out .= ' >>';
 
18757
                                $out .= "\n".'endobj';
 
18758
                                $this->_out($out);
 
18759
                                $this->gradients[$idgs]['pattern'] = $this->n;
 
18760
                                // luminosity XObject
 
18761
                                $oid = $this->_newobj();
 
18762
                                $this->xobjects['LX'.$oid] = array('n' => $oid);
 
18763
                                $filter = '';
 
18764
                                $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q';
 
18765
                                if ($this->compress) {
 
18766
                                        $filter = ' /Filter /FlateDecode';
 
18767
                                        $stream = gzcompress($stream);
 
18768
                                }
 
18769
                                $stream = $this->_getrawstream($stream);
 
18770
                                $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
 
18771
                                $out .= ' /Length '.strlen($stream);
 
18772
                                $rect = sprintf('%.2F %.2F', $this->wPt, $this->hPt);
 
18773
                                $out .= ' /BBox [0 0 '.$rect.']';
 
18774
                                $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
 
18775
                                $out .= ' /Resources <<';
 
18776
                                $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
 
18777
                                $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>';
 
18778
                                $out .= ' >>';
 
18779
                                $out .= ' >> ';
 
18780
                                $out .= ' stream'."\n".$stream."\n".'endstream';
 
18781
                                $out .= "\n".'endobj';
 
18782
                                $this->_out($out);
 
18783
                                // SMask
 
18784
                                $this->_newobj();
 
18785
                                $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj';
 
18786
                                $this->_out($out);
 
18787
                                // ExtGState
 
18788
                                $this->_newobj();
 
18789
                                $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj';
 
18790
                                $this->_out($out);
 
18791
                                $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id);
 
18792
                        }
 
18793
                }
 
18794
        }
 
18795
 
 
18796
        /**
 
18797
         * Draw the sector of a circle.
 
18798
         * It can be used for instance to render pie charts.
 
18799
         * @param $xc (float) abscissa of the center.
 
18800
         * @param $yc (float) ordinate of the center.
 
18801
         * @param $r (float) radius.
 
18802
         * @param $a (float) start angle (in degrees).
 
18803
         * @param $b (float) end angle (in degrees).
 
18804
         * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
 
18805
         * @param $cw: (float) indicates whether to go clockwise (default: true).
 
18806
         * @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.
 
18807
         * @author Maxime Delorme, Nicola Asuni
 
18808
         * @since 3.1.000 (2008-06-09)
 
18809
         * @public
 
18810
         */
 
18811
        public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
 
18812
                $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
 
18813
        }
 
18814
 
 
18815
        /**
 
18816
         * Draw the sector of an ellipse.
 
18817
         * It can be used for instance to render pie charts.
 
18818
         * @param $xc (float) abscissa of the center.
 
18819
         * @param $yc (float) ordinate of the center.
 
18820
         * @param $rx (float) the x-axis radius.
 
18821
         * @param $ry (float) the y-axis radius.
 
18822
         * @param $a (float) start angle (in degrees).
 
18823
         * @param $b (float) end angle (in degrees).
 
18824
         * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
 
18825
         * @param $cw: (float) indicates whether to go clockwise.
 
18826
         * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock).
 
18827
         * @param $nc (integer) Number of curves used to draw a 90 degrees portion of arc.
 
18828
         * @author Maxime Delorme, Nicola Asuni
 
18829
         * @since 3.1.000 (2008-06-09)
 
18830
         * @public
 
18831
         */
 
18832
        public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
 
18833
                if ($this->rtl) {
 
18834
                        $xc = $this->w - $xc;
 
18835
                }
 
18836
                $op = $this->getPathPaintOperator($style);
 
18837
                if ($op == 'f') {
 
18838
                        $line_style = array();
 
18839
                }
 
18840
                if ($cw) {
 
18841
                        $d = $b;
 
18842
                        $b = 360 - $a + $o;
 
18843
                        $a = 360 - $d + $o;
 
18844
                } else {
 
18845
                        $b += $o;
 
18846
                        $a += $o;
 
18847
                }
 
18848
                $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
 
18849
                $this->_out($op);
 
18850
        }
 
18851
 
 
18852
        /**
 
18853
         * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
 
18854
         * NOTE: EPS is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
 
18855
         * Only vector drawing is supported, not text or bitmap.
 
18856
         * 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).
 
18857
         * @param $file (string) Name of the file containing the image or a '@' character followed by the EPS/AI data string.
 
18858
         * @param $x (float) Abscissa of the upper-left corner.
 
18859
         * @param $y (float) Ordinate of the upper-left corner.
 
18860
         * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
 
18861
         * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
 
18862
         * @param $link (mixed) URL or identifier returned by AddLink().
 
18863
         * @param $useBoundingBox (boolean) specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
 
18864
         * @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>
 
18865
         * @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>
 
18866
         * @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)))
 
18867
         * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
 
18868
         * @param $fixoutvals (boolean) if true remove values outside the bounding box.
 
18869
         * @author Valentin Schmidt, Nicola Asuni
 
18870
         * @since 3.1.000 (2008-06-09)
 
18871
         * @public
 
18872
         */
 
18873
        public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
 
18874
                if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
 
18875
                        // convert EPS to raster image using GD or ImageMagick libraries
 
18876
                        return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
 
18877
                }
 
18878
                if ($x === '') {
 
18879
                        $x = $this->x;
 
18880
                }
 
18881
                if ($y === '') {
 
18882
                        $y = $this->y;
 
18883
                }
 
18884
                // check page for no-write regions and adapt page margins if necessary
 
18885
                list($x, $y) = $this->checkPageRegions($h, $x, $y);
 
18886
                $k = $this->k;
 
18887
                if ($file{0} === '@') { // image from string
 
18888
                        $data = substr($file, 1);
 
18889
                } else { // EPS/AI file
 
18890
                        $data = file_get_contents($file);
 
18891
                }
 
18892
                if ($data === false) {
 
18893
                        $this->Error('EPS file not found: '.$file);
 
18894
                }
 
18895
                $regs = array();
 
18896
                // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
 
18897
                preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
 
18898
                if (count($regs) > 1) {
 
18899
                        $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
 
18900
                        if (strpos($version_str, 'Adobe Illustrator') !== false) {
 
18901
                                $versexp = explode(' ', $version_str);
 
18902
                                $version = (float)array_pop($versexp);
 
18903
                                if ($version >= 9) {
 
18904
                                        $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
 
18905
                                }
 
18906
                        }
 
18907
                }
 
18908
                // strip binary bytes in front of PS-header
 
18909
                $start = strpos($data, '%!PS-Adobe');
 
18910
                if ($start > 0) {
 
18911
                        $data = substr($data, $start);
 
18912
                }
 
18913
                // find BoundingBox params
 
18914
                preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
 
18915
                if (count($regs) > 1) {
 
18916
                        list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
 
18917
                } else {
 
18918
                        $this->Error('No BoundingBox found in EPS/AI file: '.$file);
 
18919
                }
 
18920
                $start = strpos($data, '%%EndSetup');
 
18921
                if ($start === false) {
 
18922
                        $start = strpos($data, '%%EndProlog');
 
18923
                }
 
18924
                if ($start === false) {
 
18925
                        $start = strpos($data, '%%BoundingBox');
 
18926
                }
 
18927
                $data = substr($data, $start);
 
18928
                $end = strpos($data, '%%PageTrailer');
 
18929
                if ($end===false) {
 
18930
                        $end = strpos($data, 'showpage');
 
18931
                }
 
18932
                if ($end) {
 
18933
                        $data = substr($data, 0, $end);
 
18934
                }
 
18935
                // calculate image width and height on document
 
18936
                if (($w <= 0) AND ($h <= 0)) {
 
18937
                        $w = ($x2 - $x1) / $k;
 
18938
                        $h = ($y2 - $y1) / $k;
 
18939
                } elseif ($w <= 0) {
 
18940
                        $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
 
18941
                } elseif ($h <= 0) {
 
18942
                        $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
 
18943
                }
 
18944
                // fit the image on available space
 
18945
                list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
 
18946
                if ($this->rasterize_vector_images) {
 
18947
                        // convert EPS to raster image using GD or ImageMagick libraries
 
18948
                        return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
 
18949
                }
 
18950
                // set scaling factors
 
18951
                $scale_x = $w / (($x2 - $x1) / $k);
 
18952
                $scale_y = $h / (($y2 - $y1) / $k);
 
18953
                // set alignment
 
18954
                $this->img_rb_y = $y + $h;
 
18955
                // set alignment
 
18956
                if ($this->rtl) {
 
18957
                        if ($palign == 'L') {
 
18958
                                $ximg = $this->lMargin;
 
18959
                        } elseif ($palign == 'C') {
 
18960
                                $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
 
18961
                        } elseif ($palign == 'R') {
 
18962
                                $ximg = $this->w - $this->rMargin - $w;
 
18963
                        } else {
 
18964
                                $ximg = $x - $w;
 
18965
                        }
 
18966
                        $this->img_rb_x = $ximg;
 
18967
                } else {
 
18968
                        if ($palign == 'L') {
 
18969
                                $ximg = $this->lMargin;
 
18970
                        } elseif ($palign == 'C') {
 
18971
                                $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
 
18972
                        } elseif ($palign == 'R') {
 
18973
                                $ximg = $this->w - $this->rMargin - $w;
 
18974
                        } else {
 
18975
                                $ximg = $x;
 
18976
                        }
 
18977
                        $this->img_rb_x = $ximg + $w;
 
18978
                }
 
18979
                if ($useBoundingBox) {
 
18980
                        $dx = $ximg * $k - $x1;
 
18981
                        $dy = $y * $k - $y1;
 
18982
                } else {
 
18983
                        $dx = $ximg * $k;
 
18984
                        $dy = $y * $k;
 
18985
                }
 
18986
                // save the current graphic state
 
18987
                $this->_out('q'.$this->epsmarker);
 
18988
                // translate
 
18989
                $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
 
18990
                // scale
 
18991
                if (isset($scale_x)) {
 
18992
                        $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
 
18993
                }
 
18994
                // handle pc/unix/mac line endings
 
18995
                $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY);
 
18996
                $u=0;
 
18997
                $cnt = count($lines);
 
18998
                for ($i=0; $i < $cnt; ++$i) {
 
18999
                        $line = $lines[$i];
 
19000
                        if (($line == '') OR ($line{0} == '%')) {
 
19001
                                continue;
 
19002
                        }
 
19003
                        $len = strlen($line);
 
19004
                        // check for spot color names
 
19005
                        $color_name = '';
 
19006
                        if (strcasecmp('x', substr(trim($line), -1)) == 0) {
 
19007
                                if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) {
 
19008
                                        // extract spot color name
 
19009
                                        $color_name = $matches[0];
 
19010
                                        // remove color name from string
 
19011
                                        $line = str_replace(' '.$color_name, '', $line);
 
19012
                                        // remove pharentesis from color name
 
19013
                                        $color_name = substr($color_name, 1, -1);
 
19014
                                }
 
19015
                        }
 
19016
                        $chunks = explode(' ', $line);
 
19017
                        $cmd = trim(array_pop($chunks));
 
19018
                        // RGB
 
19019
                        if (($cmd == 'Xa') OR ($cmd == 'XA')) {
 
19020
                                $b = array_pop($chunks);
 
19021
                                $g = array_pop($chunks);
 
19022
                                $r = array_pop($chunks);
 
19023
                                $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!
 
19024
                                continue;
 
19025
                        }
 
19026
                        $skip = false;
 
19027
                        if ($fixoutvals) {
 
19028
                                // check for values outside the bounding box
 
19029
                                switch ($cmd) {
 
19030
                                        case 'm':
 
19031
                                        case 'l':
 
19032
                                        case 'L': {
 
19033
                                                // skip values outside bounding box
 
19034
                                                foreach ($chunks as $key => $val) {
 
19035
                                                        if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) {
 
19036
                                                                $skip = true;
 
19037
                                                        } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) {
 
19038
                                                                $skip = true;
 
19039
                                                        }
 
19040
                                                }
 
19041
                                        }
 
19042
                                }
 
19043
                        }
 
19044
                        switch ($cmd) {
 
19045
                                case 'm':
 
19046
                                case 'l':
 
19047
                                case 'v':
 
19048
                                case 'y':
 
19049
                                case 'c':
 
19050
                                case 'k':
 
19051
                                case 'K':
 
19052
                                case 'g':
 
19053
                                case 'G':
 
19054
                                case 's':
 
19055
                                case 'S':
 
19056
                                case 'J':
 
19057
                                case 'j':
 
19058
                                case 'w':
 
19059
                                case 'M':
 
19060
                                case 'd':
 
19061
                                case 'n': {
 
19062
                                        if ($skip) {
 
19063
                                                break;
 
19064
                                        }
 
19065
                                        $this->_out($line);
 
19066
                                        break;
 
19067
                                }
 
19068
                                case 'x': {// custom fill color
 
19069
                                        if (empty($color_name)) {
 
19070
                                                // CMYK color
 
19071
                                                list($col_c, $col_m, $col_y, $col_k) = $chunks;
 
19072
                                                $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
 
19073
                                        } else {
 
19074
                                                // Spot Color (CMYK + tint)
 
19075
                                                list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
 
19076
                                                $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
 
19077
                                                $color_cmd = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t));
 
19078
                                                $this->_out($color_cmd);
 
19079
                                        }
 
19080
                                        break;
 
19081
                                }
 
19082
                                case 'X': { // custom stroke color
 
19083
                                        if (empty($color_name)) {
 
19084
                                                // CMYK color
 
19085
                                                list($col_c, $col_m, $col_y, $col_k) = $chunks;
 
19086
                                                $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
 
19087
                                        } else {
 
19088
                                                // Spot Color (CMYK + tint)
 
19089
                                                list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
 
19090
                                                $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
 
19091
                                                $color_cmd = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t));
 
19092
                                                $this->_out($color_cmd);
 
19093
                                        }
 
19094
                                        break;
 
19095
                                }
 
19096
                                case 'Y':
 
19097
                                case 'N':
 
19098
                                case 'V':
 
19099
                                case 'L':
 
19100
                                case 'C': {
 
19101
                                        if ($skip) {
 
19102
                                                break;
 
19103
                                        }
 
19104
                                        $line[($len - 1)] = strtolower($cmd);
 
19105
                                        $this->_out($line);
 
19106
                                        break;
 
19107
                                }
 
19108
                                case 'b':
 
19109
                                case 'B': {
 
19110
                                        $this->_out($cmd . '*');
 
19111
                                        break;
 
19112
                                }
 
19113
                                case 'f':
 
19114
                                case 'F': {
 
19115
                                        if ($u > 0) {
 
19116
                                                $isU = false;
 
19117
                                                $max = min(($i + 5), $cnt);
 
19118
                                                for ($j = ($i + 1); $j < $max; ++$j) {
 
19119
                                                        $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
 
19120
                                                }
 
19121
                                                if ($isU) {
 
19122
                                                        $this->_out('f*');
 
19123
                                                }
 
19124
                                        } else {
 
19125
                                                $this->_out('f*');
 
19126
                                        }
 
19127
                                        break;
 
19128
                                }
 
19129
                                case '*u': {
 
19130
                                        ++$u;
 
19131
                                        break;
 
19132
                                }
 
19133
                                case '*U': {
 
19134
                                        --$u;
 
19135
                                        break;
 
19136
                                }
 
19137
                        }
 
19138
                }
 
19139
                // restore previous graphic state
 
19140
                $this->_out($this->epsmarker.'Q');
 
19141
                if (!empty($border)) {
 
19142
                        $bx = $this->x;
 
19143
                        $by = $this->y;
 
19144
                        $this->x = $ximg;
 
19145
                        if ($this->rtl) {
 
19146
                                $this->x += $w;
 
19147
                        }
 
19148
                        $this->y = $y;
 
19149
                        $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
 
19150
                        $this->x = $bx;
 
19151
                        $this->y = $by;
 
19152
                }
 
19153
                if ($link) {
 
19154
                        $this->Link($ximg, $y, $w, $h, $link, 0);
 
19155
                }
 
19156
                // set pointer to align the next text/objects
 
19157
                switch($align) {
 
19158
                        case 'T':{
 
19159
                                $this->y = $y;
 
19160
                                $this->x = $this->img_rb_x;
 
19161
                                break;
 
19162
                        }
 
19163
                        case 'M':{
 
19164
                                $this->y = $y + round($h/2);
 
19165
                                $this->x = $this->img_rb_x;
 
19166
                                break;
 
19167
                        }
 
19168
                        case 'B':{
 
19169
                                $this->y = $this->img_rb_y;
 
19170
                                $this->x = $this->img_rb_x;
 
19171
                                break;
 
19172
                        }
 
19173
                        case 'N':{
 
19174
                                $this->SetY($this->img_rb_y);
 
19175
                                break;
 
19176
                        }
 
19177
                        default:{
 
19178
                                break;
 
19179
                        }
 
19180
                }
 
19181
                $this->endlinex = $this->img_rb_x;
 
19182
        }
 
19183
 
 
19184
        /**
 
19185
         * Set document barcode.
 
19186
         * @param $bc (string) barcode
 
19187
         * @public
 
19188
         */
 
19189
        public function setBarcode($bc='') {
 
19190
                $this->barcode = $bc;
 
19191
        }
 
19192
 
 
19193
        /**
 
19194
         * Get current barcode.
 
19195
         * @return string
 
19196
         * @public
 
19197
         * @since 4.0.012 (2008-07-24)
 
19198
         */
 
19199
        public function getBarcode() {
 
19200
                return $this->barcode;
 
19201
        }
 
19202
 
 
19203
        /**
 
19204
         * Print a Linear Barcode.
 
19205
         * @param $code (string) code to print
 
19206
         * @param $type (string) type of barcode (see barcodes.php for supported formats).
 
19207
         * @param $x (int) x position in user units (empty string = current x position)
 
19208
         * @param $y (int) y position in user units (empty string = current y position)
 
19209
         * @param $w (int) width in user units (empty string = remaining page width)
 
19210
         * @param $h (int) height in user units (empty string = remaining page height)
 
19211
         * @param $xres (float) width of the smallest bar in user units (empty string = default value = 0.4mm)
 
19212
         * @param $style (array) array of options:<ul>
 
19213
         * <li>boolean $style['border'] if true prints a border</li>
 
19214
         * <li>int $style['padding'] padding to leave around the barcode in user units (set to 'auto' for automatic padding)</li>
 
19215
         * <li>int $style['hpadding'] horizontal padding in user units (set to 'auto' for automatic padding)</li>
 
19216
         * <li>int $style['vpadding'] vertical padding in user units (set to 'auto' for automatic padding)</li>
 
19217
         * <li>array $style['fgcolor'] color array for bars and text</li>
 
19218
         * <li>mixed $style['bgcolor'] color array for background (set to false for transparent)</li>
 
19219
         * <li>boolean $style['text'] if true prints text below the barcode</li>
 
19220
         * <li>string $style['label'] override default label</li>
 
19221
         * <li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li>
 
19222
         * <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>
 
19223
         * <li>string $style['position'] horizontal position of the containing barcode cell on the page: L = left margin; C = center; R = right margin.</li>
 
19224
         * <li>string $style['align'] horizontal position of the barcode on the containing rectangle: L = left; C = center; R = right.</li>
 
19225
         * <li>string $style['stretch'] if true stretch the barcode to best fit the available width, otherwise uses $xres resolution for a single bar.</li>
 
19226
         * <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>
 
19227
         * <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>
 
19228
         * @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>
 
19229
         * @author Nicola Asuni
 
19230
         * @since 3.1.000 (2008-06-09)
 
19231
         * @public
 
19232
         */
 
19233
        public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') {
 
19234
                if ($this->empty_string(trim($code))) {
 
19235
                        return;
 
19236
                }
 
19237
                require_once(dirname(__FILE__).'/barcodes.php');
 
19238
                // save current graphic settings
 
19239
                $gvars = $this->getGraphicVars();
 
19240
                // create new barcode object
 
19241
                $barcodeobj = new TCPDFBarcode($code, $type);
 
19242
                $arrcode = $barcodeobj->getBarcodeArray();
 
19243
                if ($arrcode === false) {
 
19244
                        $this->Error('Error in 1D barcode string');
 
19245
                }
 
19246
                // set default values
 
19247
                if (!isset($style['position'])) {
 
19248
                        $style['position'] = '';
 
19249
                } elseif ($style['position'] == 'S') {
 
19250
                        // keep this for backward compatibility
 
19251
                        $style['position'] = '';
 
19252
                        $style['stretch'] = true;
 
19253
                }
 
19254
                if (!isset($style['fitwidth'])) {
 
19255
                        if (!isset($style['stretch'])) {
 
19256
                                $style['fitwidth'] = true;
 
19257
                        } else {
 
19258
                                $style['fitwidth'] = false;
 
19259
                        }
 
19260
                }
 
19261
                if ($style['fitwidth']) {
 
19262
                        // disable stretch
 
19263
                        $style['stretch'] = false;
 
19264
                }
 
19265
                if (!isset($style['stretch'])) {
 
19266
                        if (($w === '') OR ($w <= 0)) {
 
19267
                                $style['stretch'] = false;
 
19268
                        } else {
 
19269
                                $style['stretch'] = true;
 
19270
                        }
 
19271
                }
 
19272
                if (!isset($style['fgcolor'])) {
 
19273
                        $style['fgcolor'] = array(0,0,0); // default black
 
19274
                }
 
19275
                if (!isset($style['bgcolor'])) {
 
19276
                        $style['bgcolor'] = false; // default transparent
 
19277
                }
 
19278
                if (!isset($style['border'])) {
 
19279
                        $style['border'] = false;
 
19280
                }
 
19281
                $fontsize = 0;
 
19282
                if (!isset($style['text'])) {
 
19283
                        $style['text'] = false;
 
19284
                }
 
19285
                if ($style['text'] AND isset($style['font'])) {
 
19286
                        if (isset($style['fontsize'])) {
 
19287
                                $fontsize = $style['fontsize'];
 
19288
                        }
 
19289
                        $this->SetFont($style['font'], '', $fontsize);
 
19290
                }
 
19291
                if (!isset($style['stretchtext'])) {
 
19292
                        $style['stretchtext'] = 4;
 
19293
                }
 
19294
                if ($x === '') {
 
19295
                        $x = $this->x;
 
19296
                }
 
19297
                if ($y === '') {
 
19298
                        $y = $this->y;
 
19299
                }
 
19300
                // check page for no-write regions and adapt page margins if necessary
 
19301
                list($x, $y) = $this->checkPageRegions($h, $x, $y);
 
19302
                if (($w === '') OR ($w <= 0)) {
 
19303
                        if ($this->rtl) {
 
19304
                                $w = $x - $this->lMargin;
 
19305
                        } else {
 
19306
                                $w = $this->w - $this->rMargin - $x;
 
19307
                        }
 
19308
                }
 
19309
                // padding
 
19310
                if (!isset($style['padding'])) {
 
19311
                        $padding = 0;
 
19312
                } elseif ($style['padding'] === 'auto') {
 
19313
                        $padding = 10 * ($w / ($arrcode['maxw'] + 20));
 
19314
                } else {
 
19315
                        $padding = floatval($style['padding']);
 
19316
                }
 
19317
                // horizontal padding
 
19318
                if (!isset($style['hpadding'])) {
 
19319
                        $hpadding = $padding;
 
19320
                } elseif ($style['hpadding'] === 'auto') {
 
19321
                        $hpadding = 10 * ($w / ($arrcode['maxw'] + 20));
 
19322
                } else {
 
19323
                        $hpadding = floatval($style['hpadding']);
 
19324
                }
 
19325
                // vertical padding
 
19326
                if (!isset($style['vpadding'])) {
 
19327
                        $vpadding = $padding;
 
19328
                } elseif ($style['vpadding'] === 'auto') {
 
19329
                        $vpadding = ($hpadding / 2);
 
19330
                } else {
 
19331
                        $vpadding = floatval($style['vpadding']);
 
19332
                }
 
19333
                // calculate xres (single bar width)
 
19334
                $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
 
19335
                if ($style['stretch']) {
 
19336
                        $xres = $max_xres;
 
19337
                } else {
 
19338
                        if ($this->empty_string($xres)) {
 
19339
                                $xres = (0.141 * $this->k); // default bar width = 0.4 mm
 
19340
                        }
 
19341
                        if ($xres > $max_xres) {
 
19342
                                // correct xres to fit on $w
 
19343
                                $xres = $max_xres;
 
19344
                        }
 
19345
                        if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
 
19346
                                OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
 
19347
                                $hpadding = 10 * $xres;
 
19348
                                if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
 
19349
                                        $vpadding = ($hpadding / 2);
 
19350
                                }
 
19351
                        }
 
19352
                }
 
19353
                if ($style['fitwidth']) {
 
19354
                        $wold = $w;
 
19355
                        $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding));
 
19356
                        if (isset($style['cellfitalign'])) {
 
19357
                                switch ($style['cellfitalign']) {
 
19358
                                        case 'L': {
 
19359
                                                if ($this->rtl) {
 
19360
                                                        $x -= ($wold - $w);
 
19361
                                                }
 
19362
                                                break;
 
19363
                                        }
 
19364
                                        case 'R': {
 
19365
                                                if (!$this->rtl) {
 
19366
                                                        $x += ($wold - $w);
 
19367
                                                }
 
19368
                                                break;
 
19369
                                        }
 
19370
                                        case 'C': {
 
19371
                                                if ($this->rtl) {
 
19372
                                                        $x -= (($wold - $w) / 2);
 
19373
                                                } else {
 
19374
                                                        $x += (($wold - $w) / 2);
 
19375
                                                }
 
19376
                                                break;
 
19377
                                        }
 
19378
                                        default : {
 
19379
                                                break;
 
19380
                                        }
 
19381
                                }
 
19382
                        }
 
19383
                }
 
19384
                $text_height = ($this->cell_height_ratio * $fontsize / $this->k);
 
19385
                // height
 
19386
                if (($h === '') OR ($h <= 0)) {
 
19387
                        // set default height
 
19388
                        $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height;
 
19389
                }
 
19390
                $barh = $h - $text_height - (2 * $vpadding);
 
19391
                if ($barh <=0) {
 
19392
                        // try to reduce font or padding to fit barcode on available height
 
19393
                        if ($text_height > $h) {
 
19394
                                $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio));
 
19395
                                $text_height = ($this->cell_height_ratio * $fontsize / $this->k);
 
19396
                                $this->SetFont($style['font'], '', $fontsize);
 
19397
                        }
 
19398
                        if ($vpadding > 0) {
 
19399
                                $vpadding = (($h - $text_height) / 4);
 
19400
                        }
 
19401
                        $barh = $h - $text_height - (2 * $vpadding);
 
19402
                }
 
19403
                // fit the barcode on available space
 
19404
                list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
 
19405
                // set alignment
 
19406
                $this->img_rb_y = $y + $h;
 
19407
                // set alignment
 
19408
                if ($this->rtl) {
 
19409
                        if ($style['position'] == 'L') {
 
19410
                                $xpos = $this->lMargin;
 
19411
                        } elseif ($style['position'] == 'C') {
 
19412
                                $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
 
19413
                        } elseif ($style['position'] == 'R') {
 
19414
                                $xpos = $this->w - $this->rMargin - $w;
 
19415
                        } else {
 
19416
                                $xpos = $x - $w;
 
19417
                        }
 
19418
                        $this->img_rb_x = $xpos;
 
19419
                } else {
 
19420
                        if ($style['position'] == 'L') {
 
19421
                                $xpos = $this->lMargin;
 
19422
                        } elseif ($style['position'] == 'C') {
 
19423
                                $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
 
19424
                        } elseif ($style['position'] == 'R') {
 
19425
                                $xpos = $this->w - $this->rMargin - $w;
 
19426
                        } else {
 
19427
                                $xpos = $x;
 
19428
                        }
 
19429
                        $this->img_rb_x = $xpos + $w;
 
19430
                }
 
19431
                $xpos_rect = $xpos;
 
19432
                if (!isset($style['align'])) {
 
19433
                        $style['align'] = 'C';
 
19434
                }
 
19435
                switch ($style['align']) {
 
19436
                        case 'L': {
 
19437
                                $xpos = $xpos_rect + $hpadding;
 
19438
                                break;
 
19439
                        }
 
19440
                        case 'R': {
 
19441
                                $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding;
 
19442
                                break;
 
19443
                        }
 
19444
                        case 'C':
 
19445
                        default : {
 
19446
                                $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2);
 
19447
                                break;
 
19448
                        }
 
19449
                }
 
19450
                $xpos_text = $xpos;
 
19451
                // barcode is always printed in LTR direction
 
19452
                $tempRTL = $this->rtl;
 
19453
                $this->rtl = false;
 
19454
                // print background color
 
19455
                if ($style['bgcolor']) {
 
19456
                        $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
 
19457
                } elseif ($style['border']) {
 
19458
                        $this->Rect($xpos_rect, $y, $w, $h, 'D');
 
19459
                }
 
19460
                // set foreground color
 
19461
                $this->SetDrawColorArray($style['fgcolor']);
 
19462
                $this->SetTextColorArray($style['fgcolor']);
 
19463
                // print bars
 
19464
                foreach ($arrcode['bcode'] as $k => $v) {
 
19465
                        $bw = ($v['w'] * $xres);
 
19466
                        if ($v['t']) {
 
19467
                                // draw a vertical bar
 
19468
                                $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']);
 
19469
                                $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
 
19470
                        }
 
19471
                        $xpos += $bw;
 
19472
                }
 
19473
                // print text
 
19474
                if ($style['text']) {
 
19475
                        if (isset($style['label']) AND !$this->empty_string($style['label'])) {
 
19476
                                $label = $style['label'];
 
19477
                        } else {
 
19478
                                $label = $code;
 
19479
                        }
 
19480
                        $txtwidth = ($arrcode['maxw'] * $xres);
 
19481
                        if ($this->GetStringWidth($label) > $txtwidth) {
 
19482
                                $style['stretchtext'] = 2;
 
19483
                        }
 
19484
                        // print text
 
19485
                        $this->x = $xpos_text;
 
19486
                        $this->y = $y + $vpadding + $barh;
 
19487
                        $cellpadding = $this->cell_padding;
 
19488
                        $this->SetCellPadding(0);
 
19489
                        $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
 
19490
                        $this->cell_padding = $cellpadding;
 
19491
                }
 
19492
                // restore original direction
 
19493
                $this->rtl = $tempRTL;
 
19494
                // restore previous settings
 
19495
                $this->setGraphicVars($gvars);
 
19496
                // set pointer to align the next text/objects
 
19497
                switch($align) {
 
19498
                        case 'T':{
 
19499
                                $this->y = $y;
 
19500
                                $this->x = $this->img_rb_x;
 
19501
                                break;
 
19502
                        }
 
19503
                        case 'M':{
 
19504
                                $this->y = $y + round($h / 2);
 
19505
                                $this->x = $this->img_rb_x;
 
19506
                                break;
 
19507
                        }
 
19508
                        case 'B':{
 
19509
                                $this->y = $this->img_rb_y;
 
19510
                                $this->x = $this->img_rb_x;
 
19511
                                break;
 
19512
                        }
 
19513
                        case 'N':{
 
19514
                                $this->SetY($this->img_rb_y);
 
19515
                                break;
 
19516
                        }
 
19517
                        default:{
 
19518
                                break;
 
19519
                        }
 
19520
                }
 
19521
                $this->endlinex = $this->img_rb_x;
 
19522
        }
 
19523
 
 
19524
        /**
 
19525
         * This function is DEPRECATED, please use the new write1DBarcode() function.
 
19526
         * @param $x (int) x position in user units
 
19527
         * @param $y (int) y position in user units
 
19528
         * @param $w (int) width in user units
 
19529
         * @param $h (int) height position in user units
 
19530
         * @param $type (string) type of barcode
 
19531
         * @param $style (string) barcode style
 
19532
         * @param $font (string) font for text
 
19533
         * @param $xres (int) x resolution
 
19534
         * @param $code (string) code to print
 
19535
         * @deprecated deprecated since version 3.1.000 (2008-06-10)
 
19536
         * @public
 
19537
         * @see write1DBarcode()
 
19538
         */
 
19539
        public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
 
19540
                // convert old settings for the new write1DBarcode() function.
 
19541
                $xres = 1 / $xres;
 
19542
                $newstyle = array(
 
19543
                        'position' => '',
 
19544
                        'align' => '',
 
19545
                        'stretch' => false,
 
19546
                        'fitwidth' => false,
 
19547
                        'cellfitalign' => '',
 
19548
                        'border' => false,
 
19549
                        'padding' => 0,
 
19550
                        'fgcolor' => array(0,0,0),
 
19551
                        'bgcolor' => false,
 
19552
                        'text' => true,
 
19553
                        'font' => $font,
 
19554
                        'fontsize' => 8,
 
19555
                        'stretchtext' => 4
 
19556
                );
 
19557
                if ($style & 1) {
 
19558
                        $newstyle['border'] = true;
 
19559
                }
 
19560
                if ($style & 2) {
 
19561
                        $newstyle['bgcolor'] = false;
 
19562
                }
 
19563
                if ($style & 4) {
 
19564
                        $newstyle['position'] = 'C';
 
19565
                } elseif ($style & 8) {
 
19566
                        $newstyle['position'] = 'L';
 
19567
                } elseif ($style & 16) {
 
19568
                        $newstyle['position'] = 'R';
 
19569
                }
 
19570
                if ($style & 128) {
 
19571
                        $newstyle['text'] = true;
 
19572
                }
 
19573
                if ($style & 256) {
 
19574
                        $newstyle['stretchtext'] = 4;
 
19575
                }
 
19576
                $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
 
19577
        }
 
19578
 
 
19579
        /**
 
19580
         * Print 2D Barcode.
 
19581
         * @param $code (string) code to print
 
19582
         * @param $type (string) type of barcode (see 2dbarcodes.php for supported formats).
 
19583
         * @param $x (int) x position in user units
 
19584
         * @param $y (int) y position in user units
 
19585
         * @param $w (int) width in user units
 
19586
         * @param $h (int) height in user units
 
19587
         * @param $style (array) array of options:<ul>
 
19588
         * <li>boolean $style['border'] if true prints a border around the barcode</li>
 
19589
         * <li>int $style['padding'] padding to leave around the barcode in barcode units (set to 'auto' for automatic padding)</li>
 
19590
         * <li>int $style['hpadding'] horizontal padding in barcode units (set to 'auto' for automatic padding)</li>
 
19591
         * <li>int $style['vpadding'] vertical padding in barcode units (set to 'auto' for automatic padding)</li>
 
19592
         * <li>int $style['module_width'] width of a single module in points</li>
 
19593
         * <li>int $style['module_height'] height of a single module in points</li>
 
19594
         * <li>array $style['fgcolor'] color array for bars and text</li>
 
19595
         * <li>mixed $style['bgcolor'] color array for background or false for transparent</li>
 
19596
         * <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>
 
19597
         * <li>$style['module_height'] height of a single module in points</li></ul>
 
19598
         * @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>
 
19599
         * @param $distort (boolean) if true distort the barcode to fit width and height, otherwise preserve aspect ratio
 
19600
         * @author Nicola Asuni
 
19601
         * @since 4.5.037 (2009-04-07)
 
19602
         * @public
 
19603
         */
 
19604
        public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) {
 
19605
                if ($this->empty_string(trim($code))) {
 
19606
                        return;
 
19607
                }
 
19608
                require_once(dirname(__FILE__).'/2dbarcodes.php');
 
19609
                // save current graphic settings
 
19610
                $gvars = $this->getGraphicVars();
 
19611
                // create new barcode object
 
19612
                $barcodeobj = new TCPDF2DBarcode($code, $type);
 
19613
                $arrcode = $barcodeobj->getBarcodeArray();
 
19614
                if (($arrcode === false) OR empty($arrcode)) {
 
19615
                        $this->Error('Error in 2D barcode string');
 
19616
                }
 
19617
                // set default values
 
19618
                if (!isset($style['position'])) {
 
19619
                        $style['position'] = '';
 
19620
                }
 
19621
                if (!isset($style['fgcolor'])) {
 
19622
                        $style['fgcolor'] = array(0,0,0); // default black
 
19623
                }
 
19624
                if (!isset($style['bgcolor'])) {
 
19625
                        $style['bgcolor'] = false; // default transparent
 
19626
                }
 
19627
                if (!isset($style['border'])) {
 
19628
                        $style['border'] = false;
 
19629
                }
 
19630
                // padding
 
19631
                if (!isset($style['padding'])) {
 
19632
                        $style['padding'] = 0;
 
19633
                } elseif ($style['padding'] === 'auto') {
 
19634
                        $style['padding'] = 4;
 
19635
                }
 
19636
                if (!isset($style['hpadding'])) {
 
19637
                        $style['hpadding'] = $style['padding'];
 
19638
                } elseif ($style['hpadding'] === 'auto') {
 
19639
                        $style['hpadding'] = 4;
 
19640
                }
 
19641
                if (!isset($style['vpadding'])) {
 
19642
                        $style['vpadding'] = $style['padding'];
 
19643
                } elseif ($style['vpadding'] === 'auto') {
 
19644
                        $style['vpadding'] = 4;
 
19645
                }
 
19646
                $hpad = (2 * $style['hpadding']);
 
19647
                $vpad = (2 * $style['vpadding']);
 
19648
                // cell (module) dimension
 
19649
                if (!isset($style['module_width'])) {
 
19650
                        $style['module_width'] = 1; // width of a single module in points
 
19651
                }
 
19652
                if (!isset($style['module_height'])) {
 
19653
                        $style['module_height'] = 1; // height of a single module in points
 
19654
                }
 
19655
                if ($x === '') {
 
19656
                        $x = $this->x;
 
19657
                }
 
19658
                if ($y === '') {
 
19659
                        $y = $this->y;
 
19660
                }
 
19661
                // check page for no-write regions and adapt page margins if necessary
 
19662
                list($x, $y) = $this->checkPageRegions($h, $x, $y);
 
19663
                // number of barcode columns and rows
 
19664
                $rows = $arrcode['num_rows'];
 
19665
                $cols = $arrcode['num_cols'];
 
19666
                // module width and height
 
19667
                $mw = $style['module_width'];
 
19668
                $mh = $style['module_height'];
 
19669
                // get max dimensions
 
19670
                if ($this->rtl) {
 
19671
                        $maxw = $x - $this->lMargin;
 
19672
                } else {
 
19673
                        $maxw = $this->w - $this->rMargin - $x;
 
19674
                }
 
19675
                $maxh = ($this->h - $this->tMargin - $this->bMargin);
 
19676
                $ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad));
 
19677
                $ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad));
 
19678
                if (!$distort) {
 
19679
                        if (($maxw * $ratioHW) > $maxh) {
 
19680
                                $maxw = $maxh * $ratioWH;
 
19681
                        }
 
19682
                        if (($maxh * $ratioWH) > $maxw) {
 
19683
                                $maxh = $maxw * $ratioHW;
 
19684
                        }
 
19685
                }
 
19686
                // set maximum dimesions
 
19687
                if ($w > $maxw) {
 
19688
                        $w = $maxw;
 
19689
                }
 
19690
                if ($h > $maxh) {
 
19691
                        $h = $maxh;
 
19692
                }
 
19693
                // set dimensions
 
19694
                if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) {
 
19695
                        $w = ($cols + $hpad) * ($mw / $this->k);
 
19696
                        $h = ($rows + $vpad) * ($mh / $this->k);
 
19697
                } elseif (($w === '') OR ($w <= 0)) {
 
19698
                        $w = $h * $ratioWH;
 
19699
                } elseif (($h === '') OR ($h <= 0)) {
 
19700
                        $h = $w * $ratioHW;
 
19701
                }
 
19702
                // barcode size (excluding padding)
 
19703
                $bw = ($w * $cols) / ($cols + $hpad);
 
19704
                $bh = ($h * $rows) / ($rows + $vpad);
 
19705
                // dimension of single barcode cell unit
 
19706
                $cw = $bw / $cols;
 
19707
                $ch = $bh / $rows;
 
19708
                if (!$distort) {
 
19709
                        if (($cw / $ch) > ($mw / $mh)) {
 
19710
                                // correct horizontal distortion
 
19711
                                $cw = $ch * $mw / $mh;
 
19712
                                $bw = $cw * $cols;
 
19713
                                $style['hpadding'] = ($w - $bw) / (2 * $cw);
 
19714
                        } else {
 
19715
                                // correct vertical distortion
 
19716
                                $ch = $cw * $mh / $mw;
 
19717
                                $bh = $ch * $rows;
 
19718
                                $style['vpadding'] = ($h - $bh) / (2 * $ch);
 
19719
                        }
 
19720
                }
 
19721
                // fit the barcode on available space
 
19722
                list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
 
19723
                // set alignment
 
19724
                $this->img_rb_y = $y + $h;
 
19725
                // set alignment
 
19726
                if ($this->rtl) {
 
19727
                        if ($style['position'] == 'L') {
 
19728
                                $xpos = $this->lMargin;
 
19729
                        } elseif ($style['position'] == 'C') {
 
19730
                                $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
 
19731
                        } elseif ($style['position'] == 'R') {
 
19732
                                $xpos = $this->w - $this->rMargin - $w;
 
19733
                        } else {
 
19734
                                $xpos = $x - $w;
 
19735
                        }
 
19736
                        $this->img_rb_x = $xpos;
 
19737
                } else {
 
19738
                        if ($style['position'] == 'L') {
 
19739
                                $xpos = $this->lMargin;
 
19740
                        } elseif ($style['position'] == 'C') {
 
19741
                                $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
 
19742
                        } elseif ($style['position'] == 'R') {
 
19743
                                $xpos = $this->w - $this->rMargin - $w;
 
19744
                        } else {
 
19745
                                $xpos = $x;
 
19746
                        }
 
19747
                        $this->img_rb_x = $xpos + $w;
 
19748
                }
 
19749
                $xstart = $xpos + ($style['hpadding'] * $cw);
 
19750
                $ystart = $y + ($style['vpadding'] * $ch);
 
19751
                // barcode is always printed in LTR direction
 
19752
                $tempRTL = $this->rtl;
 
19753
                $this->rtl = false;
 
19754
                // print background color
 
19755
                if ($style['bgcolor']) {
 
19756
                        $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
 
19757
                } elseif ($style['border']) {
 
19758
                        $this->Rect($xpos, $y, $w, $h, 'D');
 
19759
                }
 
19760
                // set foreground color
 
19761
                $this->SetDrawColorArray($style['fgcolor']);
 
19762
                // print barcode cells
 
19763
                // for each row
 
19764
                for ($r = 0; $r < $rows; ++$r) {
 
19765
                        $xr = $xstart;
 
19766
                        // for each column
 
19767
                        for ($c = 0; $c < $cols; ++$c) {
 
19768
                                if ($arrcode['bcode'][$r][$c] == 1) {
 
19769
                                        // draw a single barcode cell
 
19770
                                        $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
 
19771
                                }
 
19772
                                $xr += $cw;
 
19773
                        }
 
19774
                        $ystart += $ch;
 
19775
                }
 
19776
                // restore original direction
 
19777
                $this->rtl = $tempRTL;
 
19778
                // restore previous settings
 
19779
                $this->setGraphicVars($gvars);
 
19780
                // set pointer to align the next text/objects
 
19781
                switch($align) {
 
19782
                        case 'T':{
 
19783
                                $this->y = $y;
 
19784
                                $this->x = $this->img_rb_x;
 
19785
                                break;
 
19786
                        }
 
19787
                        case 'M':{
 
19788
                                $this->y = $y + round($h/2);
 
19789
                                $this->x = $this->img_rb_x;
 
19790
                                break;
 
19791
                        }
 
19792
                        case 'B':{
 
19793
                                $this->y = $this->img_rb_y;
 
19794
                                $this->x = $this->img_rb_x;
 
19795
                                break;
 
19796
                        }
 
19797
                        case 'N':{
 
19798
                                $this->SetY($this->img_rb_y);
 
19799
                                break;
 
19800
                        }
 
19801
                        default:{
 
19802
                                break;
 
19803
                        }
 
19804
                }
 
19805
                $this->endlinex = $this->img_rb_x;
 
19806
        }
 
19807
 
 
19808
        /**
 
19809
         * Returns an array containing current margins:
 
19810
         * <ul>
 
19811
                        <li>$ret['left'] = left margin</li>
 
19812
                        <li>$ret['right'] = right margin</li>
 
19813
                        <li>$ret['top'] = top margin</li>
 
19814
                        <li>$ret['bottom'] = bottom margin</li>
 
19815
                        <li>$ret['header'] = header margin</li>
 
19816
                        <li>$ret['footer'] = footer margin</li>
 
19817
                        <li>$ret['cell'] = cell padding array</li>
 
19818
                        <li>$ret['padding_left'] = cell left padding</li>
 
19819
                        <li>$ret['padding_top'] = cell top padding</li>
 
19820
                        <li>$ret['padding_right'] = cell right padding</li>
 
19821
                        <li>$ret['padding_bottom'] = cell bottom padding</li>
 
19822
         * </ul>
 
19823
         * @return array containing all margins measures
 
19824
         * @public
 
19825
         * @since 3.2.000 (2008-06-23)
 
19826
         */
 
19827
        public function getMargins() {
 
19828
                $ret = array(
 
19829
                        'left' => $this->lMargin,
 
19830
                        'right' => $this->rMargin,
 
19831
                        'top' => $this->tMargin,
 
19832
                        'bottom' => $this->bMargin,
 
19833
                        'header' => $this->header_margin,
 
19834
                        'footer' => $this->footer_margin,
 
19835
                        'cell' => $this->cell_padding,
 
19836
                        'padding_left' => $this->cell_padding['L'],
 
19837
                        'padding_top' => $this->cell_padding['T'],
 
19838
                        'padding_right' => $this->cell_padding['R'],
 
19839
                        'padding_bottom' => $this->cell_padding['B']
 
19840
                );
 
19841
                return $ret;
 
19842
        }
 
19843
 
 
19844
        /**
 
19845
         * Returns an array containing original margins:
 
19846
         * <ul>
 
19847
                        <li>$ret['left'] = left margin</li>
 
19848
                        <li>$ret['right'] = right margin</li>
 
19849
         * </ul>
 
19850
         * @return array containing all margins measures
 
19851
         * @public
 
19852
         * @since 4.0.012 (2008-07-24)
 
19853
         */
 
19854
        public function getOriginalMargins() {
 
19855
                $ret = array(
 
19856
                        'left' => $this->original_lMargin,
 
19857
                        'right' => $this->original_rMargin
 
19858
                );
 
19859
                return $ret;
 
19860
        }
 
19861
 
 
19862
        /**
 
19863
         * Returns the current font size.
 
19864
         * @return current font size
 
19865
         * @public
 
19866
         * @since 3.2.000 (2008-06-23)
 
19867
         */
 
19868
        public function getFontSize() {
 
19869
                return $this->FontSize;
 
19870
        }
 
19871
 
 
19872
        /**
 
19873
         * Returns the current font size in points unit.
 
19874
         * @return current font size in points unit
 
19875
         * @public
 
19876
         * @since 3.2.000 (2008-06-23)
 
19877
         */
 
19878
        public function getFontSizePt() {
 
19879
                return $this->FontSizePt;
 
19880
        }
 
19881
 
 
19882
        /**
 
19883
         * Returns the current font family name.
 
19884
         * @return string current font family name
 
19885
         * @public
 
19886
         * @since 4.3.008 (2008-12-05)
 
19887
         */
 
19888
        public function getFontFamily() {
 
19889
                return $this->FontFamily;
 
19890
        }
 
19891
 
 
19892
        /**
 
19893
         * Returns the current font style.
 
19894
         * @return string current font style
 
19895
         * @public
 
19896
         * @since 4.3.008 (2008-12-05)
 
19897
         */
 
19898
        public function getFontStyle() {
 
19899
                return $this->FontStyle;
 
19900
        }
 
19901
 
 
19902
        /**
 
19903
         * Cleanup HTML code (requires HTML Tidy library).
 
19904
         * @param $html (string) htmlcode to fix
 
19905
         * @param $default_css (string) CSS commands to add
 
19906
         * @param $tagvs (array) parameters for setHtmlVSpace method
 
19907
         * @param $tidy_options (array) options for tidy_parse_string function
 
19908
         * @return string XHTML code cleaned up
 
19909
         * @author Nicola Asuni
 
19910
         * @public
 
19911
         * @since 5.9.017 (2010-11-16)
 
19912
         * @see setHtmlVSpace()
 
19913
         */
 
19914
        public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') {
 
19915
                // configure parameters for HTML Tidy
 
19916
                if ($tidy_options === '') {
 
19917
                        $tidy_options = array (
 
19918
                                'clean' => 1,
 
19919
                                'drop-empty-paras' => 0,
 
19920
                                'drop-proprietary-attributes' => 1,
 
19921
                                'fix-backslash' => 1,
 
19922
                                'hide-comments' => 1,
 
19923
                                'join-styles' => 1,
 
19924
                                'lower-literals' => 1,
 
19925
                                'merge-divs' => 1,
 
19926
                                'merge-spans' => 1,
 
19927
                                'output-xhtml' => 1,
 
19928
                                'word-2000' => 1,
 
19929
                                'wrap' => 0,
 
19930
                                'output-bom' => 0,
 
19931
                                //'char-encoding' => 'utf8',
 
19932
                                //'input-encoding' => 'utf8',
 
19933
                                //'output-encoding' => 'utf8'
 
19934
                        );
 
19935
                }
 
19936
                // clean up the HTML code
 
19937
                $tidy = tidy_parse_string($html, $tidy_options);
 
19938
                // fix the HTML
 
19939
                $tidy->cleanRepair();
 
19940
                // get the CSS part
 
19941
                $tidy_head = tidy_get_head($tidy);
 
19942
                $css = $tidy_head->value;
 
19943
                $css = preg_replace('/<style([^>]+)>/ims', '<style>', $css);
 
19944
                $css = preg_replace('/<\/style>(.*)<style>/ims', "\n", $css);
 
19945
                $css = str_replace('/*<![CDATA[*/', '', $css);
 
19946
                $css = str_replace('/*]]>*/', '', $css);
 
19947
                preg_match('/<style>(.*)<\/style>/ims', $css, $matches);
 
19948
                if (isset($matches[1])) {
 
19949
                        $css = strtolower($matches[1]);
 
19950
                } else {
 
19951
                        $css = '';
 
19952
                }
 
19953
                // include default css
 
19954
                $css = '<style>'.$default_css.$css.'</style>';
 
19955
                // get the body part
 
19956
                $tidy_body = tidy_get_body($tidy);
 
19957
                $html = $tidy_body->value;
 
19958
                // fix some self-closing tags
 
19959
                $html = str_replace('<br>', '<br />', $html);
 
19960
                // remove some empty tag blocks
 
19961
                $html = preg_replace('/<div([^\>]*)><\/div>/', '', $html);
 
19962
                $html = preg_replace('/<p([^\>]*)><\/p>/', '', $html);
 
19963
                if ($tagvs !== '') {
 
19964
                        // set vertical space for some XHTML tags
 
19965
                        $this->setHtmlVSpace($tagvs);
 
19966
                }
 
19967
                // return the cleaned XHTML code + CSS
 
19968
                return $css.$html;
 
19969
        }
 
19970
 
 
19971
        /**
 
19972
         * Extracts the CSS properties from a CSS string.
 
19973
         * @param $cssdata (string) string containing CSS definitions.
 
19974
         * @return An array where the keys are the CSS selectors and the values are the CSS properties.
 
19975
         * @author Nicola Asuni
 
19976
         * @since 5.1.000 (2010-05-25)
 
19977
         * @protected
 
19978
         */
 
19979
        protected function extractCSSproperties($cssdata) {
 
19980
                if (empty($cssdata)) {
 
19981
                        return array();
 
19982
                }
 
19983
                // remove comments
 
19984
                $cssdata = preg_replace('/\/\*[^\*]*\*\//', '', $cssdata);
 
19985
                // remove newlines and multiple spaces
 
19986
                $cssdata = preg_replace('/[\s]+/', ' ', $cssdata);
 
19987
                // remove some spaces
 
19988
                $cssdata = preg_replace('/[\s]*([;:\{\}]{1})[\s]*/', '\\1', $cssdata);
 
19989
                // remove empty blocks
 
19990
                $cssdata = preg_replace('/([^\}\{]+)\{\}/', '', $cssdata);
 
19991
                // replace media type parenthesis
 
19992
                $cssdata = preg_replace('/@media[\s]+([^\{]*)\{/i', '@media \\1�', $cssdata);
 
19993
                $cssdata = preg_replace('/\}\}/si', '}�', $cssdata);
 
19994
                // trim string
 
19995
                $cssdata = trim($cssdata);
 
19996
                // find media blocks (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
 
19997
                $cssblocks = array();
 
19998
                $matches = array();
 
19999
                if (preg_match_all('/@media[\s]+([^\�]*)�([^�]*)�/i', $cssdata, $matches) > 0) {
 
20000
                        foreach ($matches[1] as $key => $type) {
 
20001
                                $cssblocks[$type] = $matches[2][$key];
 
20002
                        }
 
20003
                        // remove media blocks
 
20004
                        $cssdata = preg_replace('/@media[\s]+([^\�]*)�([^�]*)�/i', '', $cssdata);
 
20005
                }
 
20006
                // keep 'all' and 'print' media, other media types are discarded
 
20007
                if (isset($cssblocks['all']) AND !empty($cssblocks['all'])) {
 
20008
                        $cssdata .= $cssblocks['all'];
 
20009
                }
 
20010
                if (isset($cssblocks['print']) AND !empty($cssblocks['print'])) {
 
20011
                        $cssdata .= $cssblocks['print'];
 
20012
                }
 
20013
                // reset css blocks array
 
20014
                $cssblocks = array();
 
20015
                $matches = array();
 
20016
                // explode css data string into array
 
20017
                if (substr($cssdata, -1) == '}') {
 
20018
                        // remove last parethesis
 
20019
                        $cssdata = substr($cssdata, 0, -1);
 
20020
                }
 
20021
                $matches = explode('}', $cssdata);
 
20022
                foreach ($matches as $key => $block) {
 
20023
                        // index 0 contains the CSS selector, index 1 contains CSS properties
 
20024
                        $cssblocks[$key] = explode('{', $block);
 
20025
                        if (!isset($cssblocks[$key][1])) {
 
20026
                                // remove empty definitions
 
20027
                                unset($cssblocks[$key]);
 
20028
                        }
 
20029
                }
 
20030
                // split groups of selectors (comma-separated list of selectors)
 
20031
                foreach ($cssblocks as $key => $block) {
 
20032
                        if (strpos($block[0], ',') > 0) {
 
20033
                                $selectors = explode(',', $block[0]);
 
20034
                                foreach ($selectors as $sel) {
 
20035
                                        $cssblocks[] = array(0 => trim($sel), 1 => $block[1]);
 
20036
                                }
 
20037
                                unset($cssblocks[$key]);
 
20038
                        }
 
20039
                }
 
20040
                // covert array to selector => properties
 
20041
                $cssdata = array();
 
20042
                foreach ($cssblocks as $block) {
 
20043
                        $selector = $block[0];
 
20044
                        // calculate selector's specificity
 
20045
                        $matches = array();
 
20046
                        $a = 0; // the declaration is not from is a 'style' attribute
 
20047
                        $b = intval(preg_match_all('/[\#]/', $selector, $matches)); // number of ID attributes
 
20048
                        $c = intval(preg_match_all('/[\[\.]/', $selector, $matches)); // number of other attributes
 
20049
                        $c += intval(preg_match_all('/[\:]link|visited|hover|active|focus|target|lang|enabled|disabled|checked|indeterminate|root|nth|first|last|only|empty|contains|not/i', $selector, $matches)); // number of pseudo-classes
 
20050
                        $d = intval(preg_match_all('/[\>\+\~\s]{1}[a-zA-Z0-9]+/', ' '.$selector, $matches)); // number of element names
 
20051
                        $d += intval(preg_match_all('/[\:][\:]/', $selector, $matches)); // number of pseudo-elements
 
20052
                        $specificity = $a.$b.$c.$d;
 
20053
                        // add specificity to the beginning of the selector
 
20054
                        $cssdata[$specificity.' '.$selector] = $block[1];
 
20055
                }
 
20056
                // sort selectors alphabetically to account for specificity
 
20057
                ksort($cssdata, SORT_STRING);
 
20058
                // return array
 
20059
                return $cssdata;
 
20060
        }
 
20061
 
 
20062
        /**
 
20063
         * Returns true if the CSS selector is valid for the selected HTML tag
 
20064
         * @param $dom (array) array of HTML tags and properties
 
20065
         * @param $key (int) key of the current HTML tag
 
20066
         * @param $selector (string) CSS selector string
 
20067
         * @return true if the selector is valid, false otherwise
 
20068
         * @protected
 
20069
         * @since 5.1.000 (2010-05-25)
 
20070
         */
 
20071
        protected function isValidCSSSelectorForTag($dom, $key, $selector) {
 
20072
                $valid = false; // value to be returned
 
20073
                $tag = $dom[$key]['value'];
 
20074
                $class = array();
 
20075
                if (isset($dom[$key]['attribute']['class']) AND !empty($dom[$key]['attribute']['class'])) {
 
20076
                        $class = explode(' ', strtolower($dom[$key]['attribute']['class']));
 
20077
                }
 
20078
                $id = '';
 
20079
                if (isset($dom[$key]['attribute']['id']) AND !empty($dom[$key]['attribute']['id'])) {
 
20080
                        $id = strtolower($dom[$key]['attribute']['id']);
 
20081
                }
 
20082
                $selector = preg_replace('/([\>\+\~\s]{1})([\.]{1})([^\>\+\~\s]*)/si', '\\1*.\\3', $selector);
 
20083
                $matches = array();
 
20084
                if (preg_match_all('/([\>\+\~\s]{1})([a-zA-Z0-9\*]+)([^\>\+\~\s]*)/si', $selector, $matches, PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE) > 0) {
 
20085
                        $parentop = array_pop($matches[1]);
 
20086
                        $operator = $parentop[0];
 
20087
                        $offset = $parentop[1];
 
20088
                        $lasttag = array_pop($matches[2]);
 
20089
                        $lasttag = strtolower(trim($lasttag[0]));
 
20090
                        if (($lasttag == '*') OR ($lasttag == $tag)) {
 
20091
                                // the last element on selector is our tag or 'any tag'
 
20092
                                $attrib = array_pop($matches[3]);
 
20093
                                $attrib = strtolower(trim($attrib[0]));
 
20094
                                if (!empty($attrib)) {
 
20095
                                        // check if matches class, id, attribute, pseudo-class or pseudo-element
 
20096
                                        switch ($attrib{0}) {
 
20097
                                                case '.': { // class
 
20098
                                                        if (in_array(substr($attrib, 1), $class)) {
 
20099
                                                                $valid = true;
 
20100
                                                        }
 
20101
                                                        break;
 
20102
                                                }
 
20103
                                                case '#': { // ID
 
20104
                                                        if (substr($attrib, 1) == $id) {
 
20105
                                                                $valid = true;
 
20106
                                                        }
 
20107
                                                        break;
 
20108
                                                }
 
20109
                                                case '[': { // attribute
 
20110
                                                        $attrmatch = array();
 
20111
                                                        if (preg_match('/\[([a-zA-Z0-9]*)[\s]*([\~\^\$\*\|\=]*)[\s]*["]?([^"\]]*)["]?\]/i', $attrib, $attrmatch) > 0) {
 
20112
                                                                $att = strtolower($attrmatch[1]);
 
20113
                                                                $val = $attrmatch[3];
 
20114
                                                                if (isset($dom[$key]['attribute'][$att])) {
 
20115
                                                                        switch ($attrmatch[2]) {
 
20116
                                                                                case '=': {
 
20117
                                                                                        if ($dom[$key]['attribute'][$att] == $val) {
 
20118
                                                                                                $valid = true;
 
20119
                                                                                        }
 
20120
                                                                                        break;
 
20121
                                                                                }
 
20122
                                                                                case '~=': {
 
20123
                                                                                        if (in_array($val, explode(' ', $dom[$key]['attribute'][$att]))) {
 
20124
                                                                                                $valid = true;
 
20125
                                                                                        }
 
20126
                                                                                        break;
 
20127
                                                                                }
 
20128
                                                                                case '^=': {
 
20129
                                                                                        if ($val == substr($dom[$key]['attribute'][$att], 0, strlen($val))) {
 
20130
                                                                                                $valid = true;
 
20131
                                                                                        }
 
20132
                                                                                        break;
 
20133
                                                                                }
 
20134
                                                                                case '$=': {
 
20135
                                                                                        if ($val == substr($dom[$key]['attribute'][$att], -strlen($val))) {
 
20136
                                                                                                $valid = true;
 
20137
                                                                                        }
 
20138
                                                                                        break;
 
20139
                                                                                }
 
20140
                                                                                case '*=': {
 
20141
                                                                                        if (strpos($dom[$key]['attribute'][$att], $val) !== false) {
 
20142
                                                                                                $valid = true;
 
20143
                                                                                        }
 
20144
                                                                                        break;
 
20145
                                                                                }
 
20146
                                                                                case '|=': {
 
20147
                                                                                        if ($dom[$key]['attribute'][$att] == $val) {
 
20148
                                                                                                $valid = true;
 
20149
                                                                                        } elseif (preg_match('/'.$val.'[\-]{1}/i', $dom[$key]['attribute'][$att]) > 0) {
 
20150
                                                                                                $valid = true;
 
20151
                                                                                        }
 
20152
                                                                                        break;
 
20153
                                                                                }
 
20154
                                                                                default: {
 
20155
                                                                                        $valid = true;
 
20156
                                                                                }
 
20157
                                                                        }
 
20158
                                                                }
 
20159
                                                        }
 
20160
                                                        break;
 
20161
                                                }
 
20162
                                                case ':': { // pseudo-class or pseudo-element
 
20163
                                                        if ($attrib{1} == ':') { // pseudo-element
 
20164
                                                                // pseudo-elements are not supported!
 
20165
                                                                // (::first-line, ::first-letter, ::before, ::after)
 
20166
                                                        } else { // pseudo-class
 
20167
                                                                // pseudo-classes are not supported!
 
20168
                                                                // (:root, :nth-child(n), :nth-last-child(n), :nth-of-type(n), :nth-last-of-type(n), :first-child, :last-child, :first-of-type, :last-of-type, :only-child, :only-of-type, :empty, :link, :visited, :active, :hover, :focus, :target, :lang(fr), :enabled, :disabled, :checked)
 
20169
                                                        }
 
20170
                                                        break;
 
20171
                                                }
 
20172
                                        } // end of switch
 
20173
                                } else {
 
20174
                                        $valid = true;
 
20175
                                }
 
20176
                                if ($valid AND ($offset > 0)) {
 
20177
                                        $valid = false;
 
20178
                                        // check remaining selector part
 
20179
                                        $selector = substr($selector, 0, $offset);
 
20180
                                        switch ($operator) {
 
20181
                                                case ' ': { // descendant of an element
 
20182
                                                        while ($dom[$key]['parent'] > 0) {
 
20183
                                                                if ($this->isValidCSSSelectorForTag($dom, $dom[$key]['parent'], $selector)) {
 
20184
                                                                        $valid = true;
 
20185
                                                                        break;
 
20186
                                                                } else {
 
20187
                                                                        $key = $dom[$key]['parent'];
 
20188
                                                                }
 
20189
                                                        }
 
20190
                                                        break;
 
20191
                                                }
 
20192
                                                case '>': { // child of an element
 
20193
                                                        $valid = $this->isValidCSSSelectorForTag($dom, $dom[$key]['parent'], $selector);
 
20194
                                                        break;
 
20195
                                                }
 
20196
                                                case '+': { // immediately preceded by an element
 
20197
                                                        for ($i = ($key - 1); $i > $dom[$key]['parent']; --$i) {
 
20198
                                                                if ($dom[$i]['tag'] AND $dom[$i]['opening']) {
 
20199
                                                                        $valid = $this->isValidCSSSelectorForTag($dom, $i, $selector);
 
20200
                                                                        break;
 
20201
                                                                }
 
20202
                                                        }
 
20203
                                                        break;
 
20204
                                                }
 
20205
                                                case '~': { // preceded by an element
 
20206
                                                        for ($i = ($key - 1); $i > $dom[$key]['parent']; --$i) {
 
20207
                                                                if ($dom[$i]['tag'] AND $dom[$i]['opening']) {
 
20208
                                                                        if ($this->isValidCSSSelectorForTag($dom, $i, $selector)) {
 
20209
                                                                                break;
 
20210
                                                                        }
 
20211
                                                                }
 
20212
                                                        }
 
20213
                                                        break;
 
20214
                                                }
 
20215
                                        }
 
20216
                                }
 
20217
                        }
 
20218
                }
 
20219
                return $valid;
 
20220
        }
 
20221
 
 
20222
        /**
 
20223
         * Returns the styles array that apply for the selected HTML tag.
 
20224
         * @param $dom (array) array of HTML tags and properties
 
20225
         * @param $key (int) key of the current HTML tag
 
20226
         * @param $css (array) array of CSS properties
 
20227
         * @return array containing CSS properties
 
20228
         * @protected
 
20229
         * @since 5.1.000 (2010-05-25)
 
20230
         */
 
20231
        protected function getCSSdataArray($dom, $key, $css) {
 
20232
                $cssarray = array(); // style to be returned
 
20233
                // get parent CSS selectors
 
20234
                $selectors = array();
 
20235
                if (isset($dom[($dom[$key]['parent'])]['csssel'])) {
 
20236
                        $selectors = $dom[($dom[$key]['parent'])]['csssel'];
 
20237
                }
 
20238
                // get all styles that apply
 
20239
                foreach($css as $selector => $style) {
 
20240
                        $pos = strpos($selector, ' ');
 
20241
                        // get specificity
 
20242
                        $specificity = substr($selector, 0, $pos);
 
20243
                        // remove specificity
 
20244
                        $selector = substr($selector, $pos);
 
20245
                        // check if this selector apply to current tag
 
20246
                        if ($this->isValidCSSSelectorForTag($dom, $key, $selector)) {
 
20247
                                if (!in_array($selector, $selectors)) {
 
20248
                                        // add style if not already added on parent selector
 
20249
                                        $cssarray[] = array('k' => $selector, 's' => $specificity, 'c' => $style);
 
20250
                                        $selectors[] = $selector;
 
20251
                                }
 
20252
                        }
 
20253
                }
 
20254
                if (isset($dom[$key]['attribute']['style'])) {
 
20255
                        // attach inline style (latest properties have high priority)
 
20256
                        $cssarray[] = array('k' => '', 's' => '1000', 'c' => $dom[$key]['attribute']['style']);
 
20257
                }
 
20258
                // order the css array to account for specificity
 
20259
                $cssordered = array();
 
20260
                foreach ($cssarray as $key => $val) {
 
20261
                        $skey = sprintf('%04d', $key);
 
20262
                        $cssordered[$val['s'].'_'.$skey] = $val;
 
20263
                }
 
20264
                // sort selectors alphabetically to account for specificity
 
20265
                ksort($cssordered, SORT_STRING);
 
20266
                return array($selectors, $cssordered);
 
20267
        }
 
20268
 
 
20269
        /**
 
20270
         * Compact CSS data array into single string.
 
20271
         * @param $css (array) array of CSS properties
 
20272
         * @return string containing merged CSS properties
 
20273
         * @protected
 
20274
         * @since 5.9.070 (2011-04-19)
 
20275
         */
 
20276
        protected function getTagStyleFromCSSarray($css) {
 
20277
                $tagstyle = ''; // value to be returned
 
20278
                foreach ($css as $style) {
 
20279
                        // split single css commands
 
20280
                        $csscmds = explode(';', $style['c']);
 
20281
                        foreach ($csscmds as $cmd) {
 
20282
                                if (!empty($cmd)) {
 
20283
                                        $pos = strpos($cmd, ':');
 
20284
                                        if ($pos !== false) {
 
20285
                                                $cmd = substr($cmd, 0, ($pos + 1));
 
20286
                                                if (strpos($tagstyle, $cmd) !== false) {
 
20287
                                                        // remove duplicate commands (last commands have high priority)
 
20288
                                                        $tagstyle = preg_replace('/'.$cmd.'[^;]+/i', '', $tagstyle);
 
20289
                                                }
 
20290
                                        }
 
20291
                                }
 
20292
                        }
 
20293
                        $tagstyle .= ';'.$style['c'];
 
20294
                }
 
20295
                // remove multiple semicolons
 
20296
                $tagstyle = preg_replace('/[;]+/', ';', $tagstyle);
 
20297
                return $tagstyle;
 
20298
        }
 
20299
 
 
20300
        /**
 
20301
         * Returns the border width from CSS property
 
20302
         * @param $width (string) border width
 
20303
         * @return int with in user units
 
20304
         * @protected
 
20305
         * @since 5.7.000 (2010-08-02)
 
20306
         */
 
20307
        protected function getCSSBorderWidth($width) {
 
20308
                if ($width == 'thin') {
 
20309
                        $width = (2 / $this->k);
 
20310
                } elseif ($width == 'medium') {
 
20311
                        $width = (4 / $this->k);
 
20312
                } elseif ($width == 'thick') {
 
20313
                        $width = (6 / $this->k);
 
20314
                } else {
 
20315
                        $width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
 
20316
                }
 
20317
                return $width;
 
20318
        }
 
20319
 
 
20320
        /**
 
20321
         * Returns the border dash style from CSS property
 
20322
         * @param $style (string) border style to convert
 
20323
         * @return int sash style (return -1 in case of none or hidden border)
 
20324
         * @protected
 
20325
         * @since 5.7.000 (2010-08-02)
 
20326
         */
 
20327
        protected function getCSSBorderDashStyle($style) {
 
20328
                switch (strtolower($style)) {
 
20329
                        case 'none':
 
20330
                        case 'hidden': {
 
20331
                                $dash = -1;
 
20332
                                break;
 
20333
                        }
 
20334
                        case 'dotted': {
 
20335
                                $dash = 1;
 
20336
                                break;
 
20337
                        }
 
20338
                        case 'dashed': {
 
20339
                                $dash = 3;
 
20340
                                break;
 
20341
                        }
 
20342
                        case 'double':
 
20343
                        case 'groove':
 
20344
                        case 'ridge':
 
20345
                        case 'inset':
 
20346
                        case 'outset':
 
20347
                        case 'solid':
 
20348
                        default: {
 
20349
                                $dash = 0;
 
20350
                                break;
 
20351
                        }
 
20352
                }
 
20353
                return $dash;
 
20354
        }
 
20355
 
 
20356
        /**
 
20357
         * Returns the border style array from CSS border properties
 
20358
         * @param $cssborder (string) border properties
 
20359
         * @return array containing border properties
 
20360
         * @protected
 
20361
         * @since 5.7.000 (2010-08-02)
 
20362
         */
 
20363
        protected function getCSSBorderStyle($cssborder) {
 
20364
                $bprop = preg_split('/[\s]+/', trim($cssborder));
 
20365
                $border = array(); // value to be returned
 
20366
                switch (count($bprop)) {
 
20367
                        case 3: {
 
20368
                                $width = $bprop[0];
 
20369
                                $style = $bprop[1];
 
20370
                                $color = $bprop[2];
 
20371
                                break;
 
20372
                        }
 
20373
                        case 2: {
 
20374
                                $width = 'medium';
 
20375
                                $style = $bprop[0];
 
20376
                                $color = $bprop[1];
 
20377
                                break;
 
20378
                        }
 
20379
                        case 1: {
 
20380
                                $width = 'medium';
 
20381
                                $style = $bprop[0];
 
20382
                                $color = 'black';
 
20383
                                break;
 
20384
                        }
 
20385
                        default: {
 
20386
                                $width = 'medium';
 
20387
                                $style = 'solid';
 
20388
                                $color = 'black';
 
20389
                                break;
 
20390
                        }
 
20391
                }
 
20392
                if ($style == 'none') {
 
20393
                        return array();
 
20394
                }
 
20395
                $border['cap'] = 'square';
 
20396
                $border['join'] = 'miter';
 
20397
                $border['dash'] = $this->getCSSBorderDashStyle($style);
 
20398
                if ($border['dash'] < 0) {
 
20399
                        return array();
 
20400
                }
 
20401
                $border['width'] = $this->getCSSBorderWidth($width);
 
20402
                $border['color'] = $this->convertHTMLColorToDec($color);
 
20403
                return $border;
 
20404
        }
 
20405
 
 
20406
        /**
 
20407
         * Get the internal Cell padding from CSS attribute.
 
20408
         * @param $csspadding (string) padding properties
 
20409
         * @param $width (float) width of the containing element
 
20410
         * @return array of cell paddings
 
20411
         * @public
 
20412
         * @since 5.9.000 (2010-10-04)
 
20413
         */
 
20414
        public function getCSSPadding($csspadding, $width=0) {
 
20415
                $padding = preg_split('/[\s]+/', trim($csspadding));
 
20416
                $cell_padding = array(); // value to be returned
 
20417
                switch (count($padding)) {
 
20418
                        case 4: {
 
20419
                                $cell_padding['T'] = $padding[0];
 
20420
                                $cell_padding['R'] = $padding[1];
 
20421
                                $cell_padding['B'] = $padding[2];
 
20422
                                $cell_padding['L'] = $padding[3];
 
20423
                                break;
 
20424
                        }
 
20425
                        case 3: {
 
20426
                                $cell_padding['T'] = $padding[0];
 
20427
                                $cell_padding['R'] = $padding[1];
 
20428
                                $cell_padding['B'] = $padding[2];
 
20429
                                $cell_padding['L'] = $padding[1];
 
20430
                                break;
 
20431
                        }
 
20432
                        case 2: {
 
20433
                                $cell_padding['T'] = $padding[0];
 
20434
                                $cell_padding['R'] = $padding[1];
 
20435
                                $cell_padding['B'] = $padding[0];
 
20436
                                $cell_padding['L'] = $padding[1];
 
20437
                                break;
 
20438
                        }
 
20439
                        case 1: {
 
20440
                                $cell_padding['T'] = $padding[0];
 
20441
                                $cell_padding['R'] = $padding[0];
 
20442
                                $cell_padding['B'] = $padding[0];
 
20443
                                $cell_padding['L'] = $padding[0];
 
20444
                                break;
 
20445
                        }
 
20446
                        default: {
 
20447
                                return $this->cell_padding;
 
20448
                        }
 
20449
                }
 
20450
                if ($width == 0) {
 
20451
                        $width = $this->w - $this->lMargin - $this->rMargin;
 
20452
                }
 
20453
                $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
 
20454
                $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
 
20455
                $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
 
20456
                $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
 
20457
                return $cell_padding;
 
20458
        }
 
20459
 
 
20460
        /**
 
20461
         * Get the internal Cell margin from CSS attribute.
 
20462
         * @param $cssmargin (string) margin properties
 
20463
         * @param $width (float) width of the containing element
 
20464
         * @return array of cell margins
 
20465
         * @public
 
20466
         * @since 5.9.000 (2010-10-04)
 
20467
         */
 
20468
        public function getCSSMargin($cssmargin, $width=0) {
 
20469
                $margin = preg_split('/[\s]+/', trim($cssmargin));
 
20470
                $cell_margin = array(); // value to be returned
 
20471
                switch (count($margin)) {
 
20472
                        case 4: {
 
20473
                                $cell_margin['T'] = $margin[0];
 
20474
                                $cell_margin['R'] = $margin[1];
 
20475
                                $cell_margin['B'] = $margin[2];
 
20476
                                $cell_margin['L'] = $margin[3];
 
20477
                                break;
 
20478
                        }
 
20479
                        case 3: {
 
20480
                                $cell_margin['T'] = $margin[0];
 
20481
                                $cell_margin['R'] = $margin[1];
 
20482
                                $cell_margin['B'] = $margin[2];
 
20483
                                $cell_margin['L'] = $margin[1];
 
20484
                                break;
 
20485
                        }
 
20486
                        case 2: {
 
20487
                                $cell_margin['T'] = $margin[0];
 
20488
                                $cell_margin['R'] = $margin[1];
 
20489
                                $cell_margin['B'] = $margin[0];
 
20490
                                $cell_margin['L'] = $margin[1];
 
20491
                                break;
 
20492
                        }
 
20493
                        case 1: {
 
20494
                                $cell_margin['T'] = $margin[0];
 
20495
                                $cell_margin['R'] = $margin[0];
 
20496
                                $cell_margin['B'] = $margin[0];
 
20497
                                $cell_margin['L'] = $margin[0];
 
20498
                                break;
 
20499
                        }
 
20500
                        default: {
 
20501
                                return $this->cell_margin;
 
20502
                        }
 
20503
                }
 
20504
                if ($width == 0) {
 
20505
                        $width = $this->w - $this->lMargin - $this->rMargin;
 
20506
                }
 
20507
                $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
 
20508
                $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
 
20509
                $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
 
20510
                $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
 
20511
                return $cell_margin;
 
20512
        }
 
20513
 
 
20514
        /**
 
20515
         * Get the border-spacing from CSS attribute.
 
20516
         * @param $cssbspace (string) border-spacing CSS properties
 
20517
         * @param $width (float) width of the containing element
 
20518
         * @return array of border spacings
 
20519
         * @public
 
20520
         * @since 5.9.010 (2010-10-27)
 
20521
         */
 
20522
        public function getCSSBorderMargin($cssbspace, $width=0) {
 
20523
                $space = preg_split('/[\s]+/', trim($cssbspace));
 
20524
                $border_spacing = array(); // value to be returned
 
20525
                switch (count($space)) {
 
20526
                        case 2: {
 
20527
                                $border_spacing['H'] = $space[0];
 
20528
                                $border_spacing['V'] = $space[1];
 
20529
                                break;
 
20530
                        }
 
20531
                        case 1: {
 
20532
                                $border_spacing['H'] = $space[0];
 
20533
                                $border_spacing['V'] = $space[0];
 
20534
                                break;
 
20535
                        }
 
20536
                        default: {
 
20537
                                return array('H' => 0, 'V' => 0);
 
20538
                        }
 
20539
                }
 
20540
                if ($width == 0) {
 
20541
                        $width = $this->w - $this->lMargin - $this->rMargin;
 
20542
                }
 
20543
                $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
 
20544
                $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
 
20545
                return $border_spacing;
 
20546
        }
 
20547
 
 
20548
        /**
 
20549
         * Returns the letter-spacing value from CSS value
 
20550
         * @param $spacing (string) letter-spacing value
 
20551
         * @param $parent (float) font spacing (tracking/kerning) value of the parent element
 
20552
         * @return float quantity to increases or decreases the space between characters in a text.
 
20553
         * @protected
 
20554
         * @since 5.9.000 (2010-10-02)
 
20555
         */
 
20556
        protected function getCSSFontSpacing($spacing, $parent=0) {
 
20557
                $val = 0; // value to be returned
 
20558
                $spacing = trim($spacing);
 
20559
                switch ($spacing) {
 
20560
                        case 'normal': {
 
20561
                                $val = 0;
 
20562
                                break;
 
20563
                        }
 
20564
                        case 'inherit': {
 
20565
                                if ($parent == 'normal') {
 
20566
                                        $val = 0;
 
20567
                                } else {
 
20568
                                        $val = $parent;
 
20569
                                }
 
20570
                                break;
 
20571
                        }
 
20572
                        default: {
 
20573
                                $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
 
20574
                        }
 
20575
                }
 
20576
                return $val;
 
20577
        }
 
20578
 
 
20579
        /**
 
20580
         * Returns the percentage of font stretching from CSS value
 
20581
         * @param $stretch (string) stretch mode
 
20582
         * @param $parent (float) stretch value of the parent element
 
20583
         * @return float font stretching percentage
 
20584
         * @protected
 
20585
         * @since 5.9.000 (2010-10-02)
 
20586
         */
 
20587
        protected function getCSSFontStretching($stretch, $parent=100) {
 
20588
                $val = 100; // value to be returned
 
20589
                $stretch = trim($stretch);
 
20590
                switch ($stretch) {
 
20591
                        case 'ultra-condensed': {
 
20592
                                $val = 40;
 
20593
                                break;
 
20594
                        }
 
20595
                        case 'extra-condensed': {
 
20596
                                $val = 55;
 
20597
                                break;
 
20598
                        }
 
20599
                        case 'condensed': {
 
20600
                                $val = 70;
 
20601
                                break;
 
20602
                        }
 
20603
                        case 'semi-condensed': {
 
20604
                                $val = 85;
 
20605
                                break;
 
20606
                        }
 
20607
                        case 'normal': {
 
20608
                                $val = 100;
 
20609
                                break;
 
20610
                        }
 
20611
                        case 'semi-expanded': {
 
20612
                                $val = 115;
 
20613
                                break;
 
20614
                        }
 
20615
                        case 'expanded': {
 
20616
                                $val = 130;
 
20617
                                break;
 
20618
                        }
 
20619
                        case 'extra-expanded': {
 
20620
                                $val = 145;
 
20621
                                break;
 
20622
                        }
 
20623
                        case 'ultra-expanded': {
 
20624
                                $val = 160;
 
20625
                                break;
 
20626
                        }
 
20627
                        case 'wider': {
 
20628
                                $val = $parent + 10;
 
20629
                                break;
 
20630
                        }
 
20631
                        case 'narrower': {
 
20632
                                $val = $parent - 10;
 
20633
                                break;
 
20634
                        }
 
20635
                        case 'inherit': {
 
20636
                                if ($parent == 'normal') {
 
20637
                                        $val = 100;
 
20638
                                } else {
 
20639
                                        $val = $parent;
 
20640
                                }
 
20641
                                break;
 
20642
                        }
 
20643
                        default: {
 
20644
                                $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
 
20645
                        }
 
20646
                }
 
20647
                return $val;
 
20648
        }
 
20649
 
 
20650
        /**
 
20651
         * Returns the HTML DOM array.
 
20652
         * @param $html (string) html code
 
20653
         * @return array
 
20654
         * @protected
 
20655
         * @since 3.2.000 (2008-06-20)
 
20656
         */
 
20657
        protected function getHtmlDomArray($html) {
 
20658
                // array of CSS styles ( selector => properties).
 
20659
                $css = array();
 
20660
                // get CSS array defined at previous call
 
20661
                $matches = array();
 
20662
                if (preg_match_all('/<cssarray>([^\<]*)<\/cssarray>/isU', $html, $matches) > 0) {
 
20663
                        if (isset($matches[1][0])) {
 
20664
                                $css = array_merge($css, unserialize($this->unhtmlentities($matches[1][0])));
 
20665
                        }
 
20666
                        $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
 
20667
                }
 
20668
                // extract external CSS files
 
20669
                $matches = array();
 
20670
                if (preg_match_all('/<link([^\>]*)>/isU', $html, $matches) > 0) {
 
20671
                        foreach ($matches[1] as $key => $link) {
 
20672
                                $type = array();
 
20673
                                if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
 
20674
                                        $type = array();
 
20675
                                        preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
 
20676
                                        // get 'all' and 'print' media, other media types are discarded
 
20677
                                        // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
 
20678
                                        if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
 
20679
                                                $type = array();
 
20680
                                                if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
 
20681
                                                        // read CSS data file
 
20682
                                                        $cssdata = file_get_contents(trim($type[1]));
 
20683
                                                        $css = array_merge($css, $this->extractCSSproperties($cssdata));
 
20684
                                                }
 
20685
                                        }
 
20686
                                }
 
20687
                        }
 
20688
                }
 
20689
                // extract style tags
 
20690
                $matches = array();
 
20691
                if (preg_match_all('/<style([^\>]*)>([^\<]*)<\/style>/isU', $html, $matches) > 0) {
 
20692
                        foreach ($matches[1] as $key => $media) {
 
20693
                                $type = array();
 
20694
                                preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
 
20695
                                // get 'all' and 'print' media, other media types are discarded
 
20696
                                // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
 
20697
                                if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
 
20698
                                        $cssdata = $matches[2][$key];
 
20699
                                        $css = array_merge($css, $this->extractCSSproperties($cssdata));
 
20700
                                }
 
20701
                        }
 
20702
                }
 
20703
                // create a special tag to contain the CSS array (used for table content)
 
20704
                $csstagarray = '<cssarray>'.htmlentities(serialize($css)).'</cssarray>';
 
20705
                // remove head and style blocks
 
20706
                $html = preg_replace('/<head([^\>]*)>(.*?)<\/head>/siU', '', $html);
 
20707
                $html = preg_replace('/<style([^\>]*)>([^\<]*)<\/style>/isU', '', $html);
 
20708
                // define block tags
 
20709
                $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
 
20710
                // define self-closing tags
 
20711
                $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
 
20712
                // remove all unsupported tags (the line below lists all supported tags)
 
20713
                $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>');
 
20714
                //replace some blank characters
 
20715
                $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
 
20716
                $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);
 
20717
                $html = preg_replace('@(\r\n|\r)@', "\n", $html);
 
20718
                $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
 
20719
                $html = strtr($html, $repTable);
 
20720
                $offset = 0;
 
20721
                while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
 
20722
                        $html_a = substr($html, 0, $offset);
 
20723
                        $html_b = substr($html, $offset, ($pos - $offset + 6));
 
20724
                        while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
 
20725
                                // preserve newlines on <pre> tag
 
20726
                                $html_b = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
 
20727
                        }
 
20728
                        while (preg_match("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) {
 
20729
                                // preserve spaces on <pre> tag
 
20730
                                $html_b = preg_replace("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2&nbsp;\\3</pre>", $html_b);
 
20731
                        }
 
20732
                        $html = $html_a.$html_b.substr($html, $pos + 6);
 
20733
                        $offset = strlen($html_a.$html_b);
 
20734
                }
 
20735
                $offset = 0;
 
20736
                while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
 
20737
                        $html_a = substr($html, 0, $offset);
 
20738
                        $html_b = substr($html, $offset, ($pos - $offset + 11));
 
20739
                        while (preg_match("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
 
20740
                                // preserve newlines on <textarea> tag
 
20741
                                $html_b = preg_replace("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
 
20742
                                $html_b = preg_replace("'<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
 
20743
                        }
 
20744
                        $html = $html_a.$html_b.substr($html, $pos + 11);
 
20745
                        $offset = strlen($html_a.$html_b);
 
20746
                }
 
20747
                $html = preg_replace('/([\s]*)<option/si', '<option', $html);
 
20748
                $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
 
20749
                $offset = 0;
 
20750
                while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
 
20751
                        $html_a = substr($html, 0, $offset);
 
20752
                        $html_b = substr($html, $offset, ($pos - $offset + 9));
 
20753
                        while (preg_match("'<option([^\>]*)>(.*?)</option>'si", $html_b)) {
 
20754
                                $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
 
20755
                                $html_b = preg_replace("'<option([^\>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
 
20756
                        }
 
20757
                        $html = $html_a.$html_b.substr($html, $pos + 9);
 
20758
                        $offset = strlen($html_a.$html_b);
 
20759
                }
 
20760
                if (preg_match("'</select'si", $html)) {
 
20761
                        $html = preg_replace("'<select([^\>]*)>'si", "<select\\1 opt=\"", $html);
 
20762
                        $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
 
20763
                }
 
20764
                $html = str_replace("\n", ' ', $html);
 
20765
                // restore textarea newlines
 
20766
                $html = str_replace('<TBR>', "\n", $html);
 
20767
                // remove extra spaces from code
 
20768
                $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
 
20769
                $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html);
 
20770
                $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
 
20771
                $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html);
 
20772
                $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);
 
20773
                $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
 
20774
                $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
 
20775
                $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html);
 
20776
                $html = preg_replace('/<img([^\>]*)>[\s]+([^\<])/xi', '<img\\1>&nbsp;\\2', $html);
 
20777
                $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
 
20778
                $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
 
20779
                $html = preg_replace('/<textarea([^\>]*)>([^\<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
 
20780
                $html = preg_replace('/<li([^\>]*)><\/li>/', '<li\\1>&nbsp;</li>', $html);
 
20781
                $html = preg_replace('/<li([^\>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1">&nbsp;</font><img', $html);
 
20782
                $html = preg_replace('/<([^\>\/]*)>[\s]/', '<\\1>&nbsp;', $html); // preserve some spaces
 
20783
                $html = preg_replace('/[\s]<\/([^\>]*)>/', '&nbsp;</\\1>', $html); // preserve some spaces
 
20784
                $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space
 
20785
                // trim string
 
20786
                $html = $this->stringTrim($html);
 
20787
                // fix first image tag alignment
 
20788
                $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1);
 
20789
                // pattern for generic tag
 
20790
                $tagpattern = '/(<[^>]+>)/';
 
20791
                // explodes the string
 
20792
                $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
 
20793
                // count elements
 
20794
                $maxel = count($a);
 
20795
                $elkey = 0;
 
20796
                $key = 0;
 
20797
                // create an array of elements
 
20798
                $dom = array();
 
20799
                $dom[$key] = array();
 
20800
                // set inheritable properties fot the first void element
 
20801
                // 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
 
20802
                $dom[$key]['tag'] = false;
 
20803
                $dom[$key]['block'] = false;
 
20804
                $dom[$key]['value'] = '';
 
20805
                $dom[$key]['parent'] = 0;
 
20806
                $dom[$key]['hide'] = false;
 
20807
                $dom[$key]['fontname'] = $this->FontFamily;
 
20808
                $dom[$key]['fontstyle'] = $this->FontStyle;
 
20809
                $dom[$key]['fontsize'] = $this->FontSizePt;
 
20810
                $dom[$key]['font-stretch'] = $this->font_stretching;
 
20811
                $dom[$key]['letter-spacing'] = $this->font_spacing;
 
20812
                $dom[$key]['stroke'] = $this->textstrokewidth;
 
20813
                $dom[$key]['fill'] = (($this->textrendermode % 2) == 0);
 
20814
                $dom[$key]['clip'] = ($this->textrendermode > 3);
 
20815
                $dom[$key]['line-height'] = $this->cell_height_ratio;
 
20816
                $dom[$key]['bgcolor'] = false;
 
20817
                $dom[$key]['fgcolor'] = $this->fgcolor; // color
 
20818
                $dom[$key]['strokecolor'] = $this->strokecolor;
 
20819
                $dom[$key]['align'] = '';
 
20820
                $dom[$key]['listtype'] = '';
 
20821
                $dom[$key]['text-indent'] = 0;
 
20822
                $dom[$key]['border'] = array();
 
20823
                $dom[$key]['dir'] = $this->rtl?'rtl':'ltr';
 
20824
                $thead = false; // true when we are inside the THEAD tag
 
20825
                ++$key;
 
20826
                $level = array();
 
20827
                array_push($level, 0); // root
 
20828
                while ($elkey < $maxel) {
 
20829
                        $dom[$key] = array();
 
20830
                        $element = $a[$elkey];
 
20831
                        $dom[$key]['elkey'] = $elkey;
 
20832
                        if (preg_match($tagpattern, $element)) {
 
20833
                                // html tag
 
20834
                                $element = substr($element, 1, -1);
 
20835
                                // get tag name
 
20836
                                preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
 
20837
                                $tagname = strtolower($tag[1]);
 
20838
                                // check if we are inside a table header
 
20839
                                if ($tagname == 'thead') {
 
20840
                                        if ($element{0} == '/') {
 
20841
                                                $thead = false;
 
20842
                                        } else {
 
20843
                                                $thead = true;
 
20844
                                        }
 
20845
                                        ++$elkey;
 
20846
                                        continue;
 
20847
                                }
 
20848
                                $dom[$key]['tag'] = true;
 
20849
                                $dom[$key]['value'] = $tagname;
 
20850
                                if (in_array($dom[$key]['value'], $blocktags)) {
 
20851
                                        $dom[$key]['block'] = true;
 
20852
                                } else {
 
20853
                                        $dom[$key]['block'] = false;
 
20854
                                }
 
20855
                                if ($element{0} == '/') {
 
20856
                                        // *** closing html tag
 
20857
                                        $dom[$key]['opening'] = false;
 
20858
                                        $dom[$key]['parent'] = end($level);
 
20859
                                        array_pop($level);
 
20860
                                        $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide'];
 
20861
                                        $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
 
20862
                                        $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
 
20863
                                        $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
 
20864
                                        $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
 
20865
                                        $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
 
20866
                                        $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
 
20867
                                        $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
 
20868
                                        $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
 
20869
                                        $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
 
20870
                                        $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
 
20871
                                        $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
 
20872
                                        $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
 
20873
                                        $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
 
20874
                                        $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
 
20875
                                        if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
 
20876
                                                $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
 
20877
                                        }
 
20878
                                        // set the number of columns in table tag
 
20879
                                        if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
 
20880
                                                $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
 
20881
                                        }
 
20882
                                        if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
 
20883
                                                $dom[($dom[$key]['parent'])]['content'] = $csstagarray;
 
20884
                                                for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
 
20885
                                                        $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
 
20886
                                                }
 
20887
                                                $key = $i;
 
20888
                                                // mark nested tables
 
20889
                                                $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
 
20890
                                                // remove thead sections from nested tables
 
20891
                                                $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
 
20892
                                                $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
 
20893
                                        }
 
20894
                                        // store header rows on a new table
 
20895
                                        if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
 
20896
                                                if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
 
20897
                                                        $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
 
20898
                                                }
 
20899
                                                for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
 
20900
                                                        $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
 
20901
                                                }
 
20902
                                                if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
 
20903
                                                        $dom[($dom[$key]['parent'])]['attribute'] = array();
 
20904
                                                }
 
20905
                                                // header elements must be always contained in a single page
 
20906
                                                $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
 
20907
                                        }
 
20908
                                        if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) {
 
20909
                                                // remove the nobr attributes from the table header
 
20910
                                                $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
 
20911
                                                $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
 
20912
                                        }
 
20913
                                } else {
 
20914
                                        // *** opening or self-closing html tag
 
20915
                                        $dom[$key]['opening'] = true;
 
20916
                                        $dom[$key]['parent'] = end($level);
 
20917
                                        if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
 
20918
                                                // self-closing tag
 
20919
                                                $dom[$key]['self'] = true;
 
20920
                                        } else {
 
20921
                                                // opening tag
 
20922
                                                array_push($level, $key);
 
20923
                                                $dom[$key]['self'] = false;
 
20924
                                        }
 
20925
                                        // copy some values from parent
 
20926
                                        $parentkey = 0;
 
20927
                                        if ($key > 0) {
 
20928
                                                $parentkey = $dom[$key]['parent'];
 
20929
                                                $dom[$key]['hide'] = $dom[$parentkey]['hide'];
 
20930
                                                $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
 
20931
                                                $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
 
20932
                                                $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
 
20933
                                                $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
 
20934
                                                $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
 
20935
                                                $dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
 
20936
                                                $dom[$key]['fill'] = $dom[$parentkey]['fill'];
 
20937
                                                $dom[$key]['clip'] = $dom[$parentkey]['clip'];
 
20938
                                                $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
 
20939
                                                $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
 
20940
                                                $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
 
20941
                                                $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
 
20942
                                                $dom[$key]['align'] = $dom[$parentkey]['align'];
 
20943
                                                $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
 
20944
                                                $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
 
20945
                                                $dom[$key]['border'] = array();
 
20946
                                                $dom[$key]['dir'] = $dom[$parentkey]['dir'];
 
20947
                                        }
 
20948
                                        // get attributes
 
20949
                                        preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER);
 
20950
                                        $dom[$key]['attribute'] = array(); // reset attribute array
 
20951
                                        while (list($id, $name) = each($attr_array[1])) {
 
20952
                                                $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
 
20953
                                        }
 
20954
                                        if (!empty($css)) {
 
20955
                                                // merge CSS style to current style
 
20956
                                                list($dom[$key]['csssel'], $dom[$key]['cssdata']) = $this->getCSSdataArray($dom, $key, $css);
 
20957
                                                $dom[$key]['attribute']['style'] = $this->getTagStyleFromCSSarray($dom[$key]['cssdata']);
 
20958
                                        }
 
20959
                                        // split style attributes
 
20960
                                        if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
 
20961
                                                // get style attributes
 
20962
                                                preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
 
20963
                                                $dom[$key]['style'] = array(); // reset style attribute array
 
20964
                                                while (list($id, $name) = each($style_array[1])) {
 
20965
                                                        // in case of duplicate attribute the last replace the previous
 
20966
                                                        $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
 
20967
                                                }
 
20968
                                                // --- get some style attributes ---
 
20969
                                                // text direction
 
20970
                                                if (isset($dom[$key]['style']['direction'])) {
 
20971
                                                        $dom[$key]['dir'] = $dom[$key]['style']['direction'];
 
20972
                                                }
 
20973
                                                // display
 
20974
                                                if (isset($dom[$key]['style']['display'])) {
 
20975
                                                        $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
 
20976
                                                }
 
20977
                                                // font family
 
20978
                                                if (isset($dom[$key]['style']['font-family'])) {
 
20979
                                                        $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
 
20980
                                                }
 
20981
                                                // list-style-type
 
20982
                                                if (isset($dom[$key]['style']['list-style-type'])) {
 
20983
                                                        $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
 
20984
                                                        if ($dom[$key]['listtype'] == 'inherit') {
 
20985
                                                                $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
 
20986
                                                        }
 
20987
                                                }
 
20988
                                                // text-indent
 
20989
                                                if (isset($dom[$key]['style']['text-indent'])) {
 
20990
                                                        $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
 
20991
                                                        if ($dom[$key]['text-indent'] == 'inherit') {
 
20992
                                                                $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
 
20993
                                                        }
 
20994
                                                }
 
20995
                                                // font size
 
20996
                                                if (isset($dom[$key]['style']['font-size'])) {
 
20997
                                                        $fsize = trim($dom[$key]['style']['font-size']);
 
20998
                                                        switch ($fsize) {
 
20999
                                                                // absolute-size
 
21000
                                                                case 'xx-small': {
 
21001
                                                                        $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
 
21002
                                                                        break;
 
21003
                                                                }
 
21004
                                                                case 'x-small': {
 
21005
                                                                        $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
 
21006
                                                                        break;
 
21007
                                                                }
 
21008
                                                                case 'small': {
 
21009
                                                                        $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
 
21010
                                                                        break;
 
21011
                                                                }
 
21012
                                                                case 'medium': {
 
21013
                                                                        $dom[$key]['fontsize'] = $dom[0]['fontsize'];
 
21014
                                                                        break;
 
21015
                                                                }
 
21016
                                                                case 'large': {
 
21017
                                                                        $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
 
21018
                                                                        break;
 
21019
                                                                }
 
21020
                                                                case 'x-large': {
 
21021
                                                                        $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
 
21022
                                                                        break;
 
21023
                                                                }
 
21024
                                                                case 'xx-large': {
 
21025
                                                                        $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
 
21026
                                                                        break;
 
21027
                                                                }
 
21028
                                                                // relative-size
 
21029
                                                                case 'smaller': {
 
21030
                                                                        $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3;
 
21031
                                                                        break;
 
21032
                                                                }
 
21033
                                                                case 'larger': {
 
21034
                                                                        $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3;
 
21035
                                                                        break;
 
21036
                                                                }
 
21037
                                                                default: {
 
21038
                                                                        $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true);
 
21039
                                                                }
 
21040
                                                        }
 
21041
                                                }
 
21042
                                                // font-stretch
 
21043
                                                if (isset($dom[$key]['style']['font-stretch'])) {
 
21044
                                                        $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
 
21045
                                                }
 
21046
                                                // letter-spacing
 
21047
                                                if (isset($dom[$key]['style']['letter-spacing'])) {
 
21048
                                                        $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
 
21049
                                                }
 
21050
                                                // line-height
 
21051
                                                if (isset($dom[$key]['style']['line-height'])) {
 
21052
                                                        $lineheight = trim($dom[$key]['style']['line-height']);
 
21053
                                                        switch ($lineheight) {
 
21054
                                                                // A normal line height. This is default
 
21055
                                                                case 'normal': {
 
21056
                                                                        $dom[$key]['line-height'] = $dom[0]['line-height'];
 
21057
                                                                        break;
 
21058
                                                                }
 
21059
                                                                default: {
 
21060
                                                                        if (is_numeric($lineheight)) {
 
21061
                                                                                $lineheight = $lineheight * 100;
 
21062
                                                                        }
 
21063
                                                                        $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
 
21064
                                                                }
 
21065
                                                        }
 
21066
                                                }
 
21067
                                                // font style
 
21068
                                                if (isset($dom[$key]['style']['font-weight'])) {
 
21069
                                                        if (strtolower($dom[$key]['style']['font-weight']{0}) == 'n') {
 
21070
                                                                if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
 
21071
                                                                        $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
 
21072
                                                                }
 
21073
                                                        } elseif (strtolower($dom[$key]['style']['font-weight']{0}) == 'b') {
 
21074
                                                                $dom[$key]['fontstyle'] .= 'B';
 
21075
                                                        }
 
21076
                                                }
 
21077
                                                if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) {
 
21078
                                                        $dom[$key]['fontstyle'] .= 'I';
 
21079
                                                }
 
21080
                                                // font color
 
21081
                                                if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) {
 
21082
                                                        $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
 
21083
                                                } elseif ($dom[$key]['value'] == 'a') {
 
21084
                                                        $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
 
21085
                                                }
 
21086
                                                // background color
 
21087
                                                if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) {
 
21088
                                                        $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
 
21089
                                                }
 
21090
                                                // text-decoration
 
21091
                                                if (isset($dom[$key]['style']['text-decoration'])) {
 
21092
                                                        $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
 
21093
                                                        foreach ($decors as $dec) {
 
21094
                                                                $dec = trim($dec);
 
21095
                                                                if (!$this->empty_string($dec)) {
 
21096
                                                                        if ($dec{0} == 'u') {
 
21097
                                                                                // underline
 
21098
                                                                                $dom[$key]['fontstyle'] .= 'U';
 
21099
                                                                        } elseif ($dec{0} == 'l') {
 
21100
                                                                                // line-trough
 
21101
                                                                                $dom[$key]['fontstyle'] .= 'D';
 
21102
                                                                        } elseif ($dec{0} == 'o') {
 
21103
                                                                                // overline
 
21104
                                                                                $dom[$key]['fontstyle'] .= 'O';
 
21105
                                                                        }
 
21106
                                                                }
 
21107
                                                        }
 
21108
                                                } elseif ($dom[$key]['value'] == 'a') {
 
21109
                                                        $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
 
21110
                                                }
 
21111
                                                // check for width attribute
 
21112
                                                if (isset($dom[$key]['style']['width'])) {
 
21113
                                                        $dom[$key]['width'] = $dom[$key]['style']['width'];
 
21114
                                                }
 
21115
                                                // check for height attribute
 
21116
                                                if (isset($dom[$key]['style']['height'])) {
 
21117
                                                        $dom[$key]['height'] = $dom[$key]['style']['height'];
 
21118
                                                }
 
21119
                                                // check for text alignment
 
21120
                                                if (isset($dom[$key]['style']['text-align'])) {
 
21121
                                                        $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
 
21122
                                                }
 
21123
                                                // check for CSS border properties
 
21124
                                                if (isset($dom[$key]['style']['border'])) {
 
21125
                                                        $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
 
21126
                                                        if (!empty($borderstyle)) {
 
21127
                                                                $dom[$key]['border']['LTRB'] = $borderstyle;
 
21128
                                                        }
 
21129
                                                }
 
21130
                                                if (isset($dom[$key]['style']['border-color'])) {
 
21131
                                                        $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
 
21132
                                                        if (isset($brd_colors[3])) {
 
21133
                                                                $dom[$key]['border']['L']['color'] = $this->convertHTMLColorToDec($brd_colors[3]);
 
21134
                                                        }
 
21135
                                                        if (isset($brd_colors[1])) {
 
21136
                                                                $dom[$key]['border']['R']['color'] = $this->convertHTMLColorToDec($brd_colors[1]);
 
21137
                                                        }
 
21138
                                                        if (isset($brd_colors[0])) {
 
21139
                                                                $dom[$key]['border']['T']['color'] = $this->convertHTMLColorToDec($brd_colors[0]);
 
21140
                                                        }
 
21141
                                                        if (isset($brd_colors[2])) {
 
21142
                                                                $dom[$key]['border']['B']['color'] = $this->convertHTMLColorToDec($brd_colors[2]);
 
21143
                                                        }
 
21144
                                                }
 
21145
                                                if (isset($dom[$key]['style']['border-width'])) {
 
21146
                                                        $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
 
21147
                                                        if (isset($brd_widths[3])) {
 
21148
                                                                $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
 
21149
                                                        }
 
21150
                                                        if (isset($brd_widths[1])) {
 
21151
                                                                $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
 
21152
                                                        }
 
21153
                                                        if (isset($brd_widths[0])) {
 
21154
                                                                $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
 
21155
                                                        }
 
21156
                                                        if (isset($brd_widths[2])) {
 
21157
                                                                $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
 
21158
                                                        }
 
21159
                                                }
 
21160
                                                if (isset($dom[$key]['style']['border-style'])) {
 
21161
                                                        $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
 
21162
                                                        if (isset($brd_styles[3])) {
 
21163
                                                                $dom[$key]['border']['L']['cap'] = 'square';
 
21164
                                                                $dom[$key]['border']['L']['join'] = 'miter';
 
21165
                                                                $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
 
21166
                                                                if ($dom[$key]['border']['L']['dash'] < 0) {
 
21167
                                                                        $dom[$key]['border']['L'] = array();
 
21168
                                                                }
 
21169
                                                        }
 
21170
                                                        if (isset($brd_styles[1])) {
 
21171
                                                                $dom[$key]['border']['R']['cap'] = 'square';
 
21172
                                                                $dom[$key]['border']['R']['join'] = 'miter';
 
21173
                                                                $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
 
21174
                                                                if ($dom[$key]['border']['R']['dash'] < 0) {
 
21175
                                                                        $dom[$key]['border']['R'] = array();
 
21176
                                                                }
 
21177
                                                        }
 
21178
                                                        if (isset($brd_styles[0])) {
 
21179
                                                                $dom[$key]['border']['T']['cap'] = 'square';
 
21180
                                                                $dom[$key]['border']['T']['join'] = 'miter';
 
21181
                                                                $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
 
21182
                                                                if ($dom[$key]['border']['T']['dash'] < 0) {
 
21183
                                                                        $dom[$key]['border']['T'] = array();
 
21184
                                                                }
 
21185
                                                        }
 
21186
                                                        if (isset($brd_styles[2])) {
 
21187
                                                                $dom[$key]['border']['B']['cap'] = 'square';
 
21188
                                                                $dom[$key]['border']['B']['join'] = 'miter';
 
21189
                                                                $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
 
21190
                                                                if ($dom[$key]['border']['B']['dash'] < 0) {
 
21191
                                                                        $dom[$key]['border']['B'] = array();
 
21192
                                                                }
 
21193
                                                        }
 
21194
                                                }
 
21195
                                                $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
 
21196
                                                foreach ($cellside as $bsk => $bsv) {
 
21197
                                                        if (isset($dom[$key]['style']['border-'.$bsv])) {
 
21198
                                                                $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
 
21199
                                                                if (!empty($borderstyle)) {
 
21200
                                                                        $dom[$key]['border'][$bsk] = $borderstyle;
 
21201
                                                                }
 
21202
                                                        }
 
21203
                                                        if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
 
21204
                                                                $dom[$key]['border'][$bsk]['color'] = $this->convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color']);
 
21205
                                                        }
 
21206
                                                        if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
 
21207
                                                                $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
 
21208
                                                        }
 
21209
                                                        if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
 
21210
                                                                $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
 
21211
                                                                if ($dom[$key]['border'][$bsk]['dash'] < 0) {
 
21212
                                                                        $dom[$key]['border'][$bsk] = array();
 
21213
                                                                }
 
21214
                                                        }
 
21215
                                                }
 
21216
                                                // check for CSS padding properties
 
21217
                                                if (isset($dom[$key]['style']['padding'])) {
 
21218
                                                        $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
 
21219
                                                } else {
 
21220
                                                        $dom[$key]['padding'] = $this->cell_padding;
 
21221
                                                }
 
21222
                                                foreach ($cellside as $psk => $psv) {
 
21223
                                                        if (isset($dom[$key]['style']['padding-'.$psv])) {
 
21224
                                                                $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
 
21225
                                                        }
 
21226
                                                }
 
21227
                                                // check for CSS margin properties
 
21228
                                                if (isset($dom[$key]['style']['margin'])) {
 
21229
                                                        $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
 
21230
                                                } else {
 
21231
                                                        $dom[$key]['margin'] = $this->cell_margin;
 
21232
                                                }
 
21233
                                                foreach ($cellside as $psk => $psv) {
 
21234
                                                        if (isset($dom[$key]['style']['margin-'.$psv])) {
 
21235
                                                                $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
 
21236
                                                        }
 
21237
                                                }
 
21238
                                                // check for CSS border-spacing properties
 
21239
                                                if (isset($dom[$key]['style']['border-spacing'])) {
 
21240
                                                        $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
 
21241
                                                }
 
21242
                                                // page-break-inside
 
21243
                                                if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
 
21244
                                                        $dom[$key]['attribute']['nobr'] = 'true';
 
21245
                                                }
 
21246
                                                // page-break-before
 
21247
                                                if (isset($dom[$key]['style']['page-break-before'])) {
 
21248
                                                        if ($dom[$key]['style']['page-break-before'] == 'always') {
 
21249
                                                                $dom[$key]['attribute']['pagebreak'] = 'true';
 
21250
                                                        } elseif ($dom[$key]['style']['page-break-before'] == 'left') {
 
21251
                                                                $dom[$key]['attribute']['pagebreak'] = 'left';
 
21252
                                                        } elseif ($dom[$key]['style']['page-break-before'] == 'right') {
 
21253
                                                                $dom[$key]['attribute']['pagebreak'] = 'right';
 
21254
                                                        }
 
21255
                                                }
 
21256
                                                // page-break-after
 
21257
                                                if (isset($dom[$key]['style']['page-break-after'])) {
 
21258
                                                        if ($dom[$key]['style']['page-break-after'] == 'always') {
 
21259
                                                                $dom[$key]['attribute']['pagebreakafter'] = 'true';
 
21260
                                                        } elseif ($dom[$key]['style']['page-break-after'] == 'left') {
 
21261
                                                                $dom[$key]['attribute']['pagebreakafter'] = 'left';
 
21262
                                                        } elseif ($dom[$key]['style']['page-break-after'] == 'right') {
 
21263
                                                                $dom[$key]['attribute']['pagebreakafter'] = 'right';
 
21264
                                                        }
 
21265
                                                }
 
21266
                                        }
 
21267
                                        if (isset($dom[$key]['attribute']['display'])) {
 
21268
                                                $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
 
21269
                                        }
 
21270
                                        if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
 
21271
                                                $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
 
21272
                                                if (!empty($borderstyle)) {
 
21273
                                                        $dom[$key]['border']['LTRB'] = $borderstyle;
 
21274
                                                }
 
21275
                                        }
 
21276
                                        // check for font tag
 
21277
                                        if ($dom[$key]['value'] == 'font') {
 
21278
                                                // font family
 
21279
                                                if (isset($dom[$key]['attribute']['face'])) {
 
21280
                                                        $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
 
21281
                                                }
 
21282
                                                // font size
 
21283
                                                if (isset($dom[$key]['attribute']['size'])) {
 
21284
                                                        if ($key > 0) {
 
21285
                                                                if ($dom[$key]['attribute']['size']{0} == '+') {
 
21286
                                                                        $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
 
21287
                                                                } elseif ($dom[$key]['attribute']['size']{0} == '-') {
 
21288
                                                                        $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
 
21289
                                                                } else {
 
21290
                                                                        $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
 
21291
                                                                }
 
21292
                                                        } else {
 
21293
                                                                $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
 
21294
                                                        }
 
21295
                                                }
 
21296
                                        }
 
21297
                                        // force natural alignment for lists
 
21298
                                        if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
 
21299
                                                AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
 
21300
                                                if ($this->rtl) {
 
21301
                                                        $dom[$key]['align'] = 'R';
 
21302
                                                } else {
 
21303
                                                        $dom[$key]['align'] = 'L';
 
21304
                                                }
 
21305
                                        }
 
21306
                                        if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
 
21307
                                                if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
 
21308
                                                        $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
 
21309
                                                }
 
21310
                                        }
 
21311
                                        if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
 
21312
                                                $dom[$key]['fontstyle'] .= 'B';
 
21313
                                        }
 
21314
                                        if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
 
21315
                                                $dom[$key]['fontstyle'] .= 'I';
 
21316
                                        }
 
21317
                                        if ($dom[$key]['value'] == 'u') {
 
21318
                                                $dom[$key]['fontstyle'] .= 'U';
 
21319
                                        }
 
21320
                                        if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
 
21321
                                                $dom[$key]['fontstyle'] .= 'D';
 
21322
                                        }
 
21323
                                        if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
 
21324
                                                $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
 
21325
                                        }
 
21326
                                        if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
 
21327
                                                $dom[$key]['fontname'] = $this->default_monospaced_font;
 
21328
                                        }
 
21329
                                        if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
 
21330
                                                // headings h1, h2, h3, h4, h5, h6
 
21331
                                                if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
 
21332
                                                        $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
 
21333
                                                        $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
 
21334
                                                }
 
21335
                                                if (!isset($dom[$key]['style']['font-weight'])) {
 
21336
                                                        $dom[$key]['fontstyle'] .= 'B';
 
21337
                                                }
 
21338
                                        }
 
21339
                                        if (($dom[$key]['value'] == 'table')) {
 
21340
                                                $dom[$key]['rows'] = 0; // number of rows
 
21341
                                                $dom[$key]['trids'] = array(); // IDs of TR elements
 
21342
                                                $dom[$key]['thead'] = ''; // table header rows
 
21343
                                        }
 
21344
                                        if (($dom[$key]['value'] == 'tr')) {
 
21345
                                                $dom[$key]['cols'] = 0;
 
21346
                                                if ($thead) {
 
21347
                                                        $dom[$key]['thead'] = true;
 
21348
                                                        // rows on thead block are printed as a separate table
 
21349
                                                } else {
 
21350
                                                        $dom[$key]['thead'] = false;
 
21351
                                                        // store the number of rows on table element
 
21352
                                                        ++$dom[($dom[$key]['parent'])]['rows'];
 
21353
                                                        // store the TR elements IDs on table element
 
21354
                                                        array_push($dom[($dom[$key]['parent'])]['trids'], $key);
 
21355
                                                }
 
21356
                                        }
 
21357
                                        if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
 
21358
                                                if (isset($dom[$key]['attribute']['colspan'])) {
 
21359
                                                        $colspan = intval($dom[$key]['attribute']['colspan']);
 
21360
                                                } else {
 
21361
                                                        $colspan = 1;
 
21362
                                                }
 
21363
                                                $dom[$key]['attribute']['colspan'] = $colspan;
 
21364
                                                $dom[($dom[$key]['parent'])]['cols'] += $colspan;
 
21365
                                        }
 
21366
                                        // text direction
 
21367
                                        if (isset($dom[$key]['attribute']['dir'])) {
 
21368
                                                $dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
 
21369
                                        }
 
21370
                                        // set foreground color attribute
 
21371
                                        if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) {
 
21372
                                                $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
 
21373
                                        } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
 
21374
                                                $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
 
21375
                                        }
 
21376
                                        // set background color attribute
 
21377
                                        if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) {
 
21378
                                                $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
 
21379
                                        }
 
21380
                                        // set stroke color attribute
 
21381
                                        if (isset($dom[$key]['attribute']['strokecolor']) AND (!$this->empty_string($dom[$key]['attribute']['strokecolor']))) {
 
21382
                                                $dom[$key]['strokecolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['strokecolor']);
 
21383
                                        }
 
21384
                                        // check for width attribute
 
21385
                                        if (isset($dom[$key]['attribute']['width'])) {
 
21386
                                                $dom[$key]['width'] = $dom[$key]['attribute']['width'];
 
21387
                                        }
 
21388
                                        // check for height attribute
 
21389
                                        if (isset($dom[$key]['attribute']['height'])) {
 
21390
                                                $dom[$key]['height'] = $dom[$key]['attribute']['height'];
 
21391
                                        }
 
21392
                                        // check for text alignment
 
21393
                                        if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
 
21394
                                                $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
 
21395
                                        }
 
21396
                                        // check for text rendering mode (the following attributes do not exist in HTML)
 
21397
                                        if (isset($dom[$key]['attribute']['stroke'])) {
 
21398
                                                // font stroke width
 
21399
                                                $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
 
21400
                                        }
 
21401
                                        if (isset($dom[$key]['attribute']['fill'])) {
 
21402
                                                // font fill
 
21403
                                                if ($dom[$key]['attribute']['fill'] == 'true') {
 
21404
                                                        $dom[$key]['fill'] = true;
 
21405
                                                } else {
 
21406
                                                        $dom[$key]['fill'] = false;
 
21407
                                                }
 
21408
                                        }
 
21409
                                        if (isset($dom[$key]['attribute']['clip'])) {
 
21410
                                                // clipping mode
 
21411
                                                if ($dom[$key]['attribute']['clip'] == 'true') {
 
21412
                                                        $dom[$key]['clip'] = true;
 
21413
                                                } else {
 
21414
                                                        $dom[$key]['clip'] = false;
 
21415
                                                }
 
21416
                                        }
 
21417
                                } // end opening tag
 
21418
                        } else {
 
21419
                                // text
 
21420
                                $dom[$key]['tag'] = false;
 
21421
                                $dom[$key]['block'] = false;
 
21422
                                //$element = str_replace('&nbsp;', $this->unichr(160), $element);
 
21423
                                $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
 
21424
                                $dom[$key]['parent'] = end($level);
 
21425
                                $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
 
21426
                        }
 
21427
                        ++$elkey;
 
21428
                        ++$key;
 
21429
                }
 
21430
                return $dom;
 
21431
        }
 
21432
 
 
21433
        /**
 
21434
         * Returns the string used to find spaces
 
21435
         * @return string
 
21436
         * @protected
 
21437
         * @author Nicola Asuni
 
21438
         * @since 4.8.024 (2010-01-15)
 
21439
         */
 
21440
        protected function getSpaceString() {
 
21441
                $spacestr = chr(32);
 
21442
                if ($this->isUnicodeFont()) {
 
21443
                        $spacestr = chr(0).chr(32);
 
21444
                }
 
21445
                return $spacestr;
 
21446
        }
 
21447
 
 
21448
        /**
 
21449
         * Prints a cell (rectangular area) with optional borders, background color and html text string.
 
21450
         * 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 />
 
21451
         * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
 
21452
         * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
 
21453
         * @param $h (float) Cell minimum height. The cell extends automatically if needed.
 
21454
         * @param $x (float) upper-left corner X coordinate
 
21455
         * @param $y (float) upper-left corner Y coordinate
 
21456
         * @param $html (string) html text to print. Default value: empty string.
 
21457
         * @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)))
 
21458
         * @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>
19035
21459
Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
19036
 
     * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
19037
 
     * @param $reseth (boolean) if true reset the last cell height (default true).
19038
 
     * @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>
19039
 
     * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
19040
 
     * @see Multicell(), writeHTML()
19041
 
     * @public
19042
 
     */
19043
 
    public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
19044
 
        return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0);
19045
 
    }
19046
 
 
19047
 
    /**
19048
 
     * Allows to preserve some HTML formatting (limited support).<br />
19049
 
     * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
19050
 
     * 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
19051
 
     * @param $html (string) text to display
19052
 
     * @param $ln (boolean) if true add a new line after text (default = true)
19053
 
     * @param $fill (boolean) Indicates if the background must be painted (true) or transparent (false).
19054
 
     * @param $reseth (boolean) if true reset the last cell height (default false).
19055
 
     * @param $cell (boolean) if true add the current left (or right for RTL) padding to each Write (default false).
19056
 
     * @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>
19057
 
     * @public
19058
 
     */
19059
 
    public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
19060
 
        $gvars = $this->getGraphicVars();
19061
 
        // store current values
19062
 
        $prev_cell_margin = $this->cell_margin;
19063
 
        $prev_cell_padding = $this->cell_padding;
19064
 
        $prevPage = $this->page;
19065
 
        $prevlMargin = $this->lMargin;
19066
 
        $prevrMargin = $this->rMargin;
19067
 
        $curfontname = $this->FontFamily;
19068
 
        $curfontstyle = $this->FontStyle;
19069
 
        $curfontsize = $this->FontSizePt;
19070
 
        $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
19071
 
        $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
19072
 
        $curfontstretcing = $this->font_stretching;
19073
 
        $curfontkerning = $this->font_spacing;
19074
 
        $this->newline = true;
19075
 
        $newline = true;
19076
 
        $startlinepage = $this->page;
19077
 
        $minstartliney = $this->y;
19078
 
        $maxbottomliney = 0;
19079
 
        $startlinex = $this->x;
19080
 
        $startliney = $this->y;
19081
 
        $yshift = 0;
19082
 
        $loop = 0;
19083
 
        $curpos = 0;
19084
 
        $this_method_vars = array();
19085
 
        $undo = false;
19086
 
        $fontaligned = false;
19087
 
        $reverse_dir = false; // true when the text direction is reversed
19088
 
        $this->premode = false;
19089
 
        if ($this->inxobj) {
19090
 
            // we are inside an XObject template
19091
 
            $pask = count($this->xobjects[$this->xobjid]['annotations']);
19092
 
        } elseif (isset($this->PageAnnots[$this->page])) {
19093
 
            $pask = count($this->PageAnnots[$this->page]);
19094
 
        } else {
19095
 
            $pask = 0;
19096
 
        }
19097
 
        if ($this->inxobj) {
19098
 
            // we are inside an XObject template
19099
 
            $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
19100
 
        } elseif (!$this->InFooter) {
19101
 
            if (isset($this->footerlen[$this->page])) {
19102
 
                $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
19103
 
            } else {
19104
 
                $this->footerpos[$this->page] = $this->pagelen[$this->page];
19105
 
            }
19106
 
            $startlinepos = $this->footerpos[$this->page];
19107
 
        } else {
19108
 
            // we are inside the footer
19109
 
            $startlinepos = $this->pagelen[$this->page];
19110
 
        }
19111
 
        $lalign = $align;
19112
 
        $plalign = $align;
19113
 
        if ($this->rtl) {
19114
 
            $w = $this->x - $this->lMargin;
19115
 
        } else {
19116
 
            $w = $this->w - $this->rMargin - $this->x;
19117
 
        }
19118
 
        $w -= ($this->cell_padding['L'] + $this->cell_padding['R']);
19119
 
        if ($cell) {
19120
 
            if ($this->rtl) {
19121
 
                $this->x -= $this->cell_padding['R'];
19122
 
                $this->lMargin += $this->cell_padding['R'];
19123
 
            } else {
19124
 
                $this->x += $this->cell_padding['L'];
19125
 
                $this->rMargin += $this->cell_padding['L'];
19126
 
            }
19127
 
        }
19128
 
        if ($this->customlistindent >= 0) {
19129
 
            $this->listindent = $this->customlistindent;
19130
 
        } else {
19131
 
            $this->listindent = $this->GetStringWidth('0000');
19132
 
        }
19133
 
        $this->listindentlevel = 0;
19134
 
        // save previous states
19135
 
        $prev_cell_height_ratio = $this->cell_height_ratio;
19136
 
        $prev_listnum = $this->listnum;
19137
 
        $prev_listordered = $this->listordered;
19138
 
        $prev_listcount = $this->listcount;
19139
 
        $prev_lispacer = $this->lispacer;
19140
 
        $this->listnum = 0;
19141
 
        $this->listordered = array();
19142
 
        $this->listcount = array();
19143
 
        $this->lispacer = '';
19144
 
        if (($this->empty_string($this->lasth)) OR ($reseth)) {
19145
 
            // reset row height
19146
 
            $this->resetLastH();
19147
 
        }
19148
 
        $dom = $this->getHtmlDomArray($html);
19149
 
        $maxel = count($dom);
19150
 
        $key = 0;
19151
 
        while ($key < $maxel) {
19152
 
            if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
19153
 
                // check for pagebreak
19154
 
                if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
19155
 
                    // add a page (or trig AcceptPageBreak() for multicolumn mode)
19156
 
                    $this->checkPageBreak($this->PageBreakTrigger + 1);
19157
 
                }
19158
 
                if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19159
 
                    OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19160
 
                    // add a page (or trig AcceptPageBreak() for multicolumn mode)
19161
 
                    $this->checkPageBreak($this->PageBreakTrigger + 1);
19162
 
                }
19163
 
            }
19164
 
            if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
19165
 
                if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
19166
 
                    $dom[$key]['attribute']['nobr'] = false;
19167
 
                } else {
19168
 
                    // store current object
19169
 
                    $this->startTransaction();
19170
 
                    // save this method vars
19171
 
                    $this_method_vars['html'] = $html;
19172
 
                    $this_method_vars['ln'] = $ln;
19173
 
                    $this_method_vars['fill'] = $fill;
19174
 
                    $this_method_vars['reseth'] = $reseth;
19175
 
                    $this_method_vars['cell'] = $cell;
19176
 
                    $this_method_vars['align'] = $align;
19177
 
                    $this_method_vars['gvars'] = $gvars;
19178
 
                    $this_method_vars['prevPage'] = $prevPage;
19179
 
                    $this_method_vars['prev_cell_margin'] = $prev_cell_margin;
19180
 
                    $this_method_vars['prev_cell_padding'] = $prev_cell_padding;
19181
 
                    $this_method_vars['prevlMargin'] = $prevlMargin;
19182
 
                    $this_method_vars['prevrMargin'] = $prevrMargin;
19183
 
                    $this_method_vars['curfontname'] = $curfontname;
19184
 
                    $this_method_vars['curfontstyle'] = $curfontstyle;
19185
 
                    $this_method_vars['curfontsize'] = $curfontsize;
19186
 
                    $this_method_vars['curfontascent'] = $curfontascent;
19187
 
                    $this_method_vars['curfontdescent'] = $curfontdescent;
19188
 
                    $this_method_vars['curfontstretcing'] = $curfontstretcing;
19189
 
                    $this_method_vars['curfontkerning'] = $curfontkerning;
19190
 
                    $this_method_vars['minstartliney'] = $minstartliney;
19191
 
                    $this_method_vars['maxbottomliney'] = $maxbottomliney;
19192
 
                    $this_method_vars['yshift'] = $yshift;
19193
 
                    $this_method_vars['startlinepage'] = $startlinepage;
19194
 
                    $this_method_vars['startlinepos'] = $startlinepos;
19195
 
                    $this_method_vars['startlinex'] = $startlinex;
19196
 
                    $this_method_vars['startliney'] = $startliney;
19197
 
                    $this_method_vars['newline'] = $newline;
19198
 
                    $this_method_vars['loop'] = $loop;
19199
 
                    $this_method_vars['curpos'] = $curpos;
19200
 
                    $this_method_vars['pask'] = $pask;
19201
 
                    $this_method_vars['lalign'] = $lalign;
19202
 
                    $this_method_vars['plalign'] = $plalign;
19203
 
                    $this_method_vars['w'] = $w;
19204
 
                    $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
19205
 
                    $this_method_vars['prev_listnum'] = $prev_listnum;
19206
 
                    $this_method_vars['prev_listordered'] = $prev_listordered;
19207
 
                    $this_method_vars['prev_listcount'] = $prev_listcount;
19208
 
                    $this_method_vars['prev_lispacer'] = $prev_lispacer;
19209
 
                    $this_method_vars['fontaligned'] = $fontaligned;
19210
 
                    $this_method_vars['key'] = $key;
19211
 
                    $this_method_vars['dom'] = $dom;
19212
 
                }
19213
 
            }
19214
 
            // print THEAD block
19215
 
            if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
19216
 
                if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !$this->empty_string($dom[$dom[$key]['parent']]['thead'])) {
19217
 
                    $this->inthead = true;
19218
 
                    // print table header (thead)
19219
 
                    $this->writeHTML($this->thead, false, false, false, false, '');
19220
 
                    // check if we are on a new page or on a new column
19221
 
                    if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) {
19222
 
                        // we are on a new page or on a new column and the total object height is less than the available vertical space.
19223
 
                        // restore previous object
19224
 
                        $this->rollbackTransaction(true);
19225
 
                        // restore previous values
19226
 
                        foreach ($this_method_vars as $vkey => $vval) {
19227
 
                            $$vkey = $vval;
19228
 
                        }
19229
 
                        // disable table header
19230
 
                        $tmp_thead = $this->thead;
19231
 
                        $this->thead = '';
19232
 
                        // add a page (or trig AcceptPageBreak() for multicolumn mode)
19233
 
                        $pre_y = $this->y;
19234
 
                        if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
19235
 
                            // fix for multicolumn mode
19236
 
                            $startliney = $this->y;
19237
 
                        }
19238
 
                        $this->start_transaction_page = $this->page;
19239
 
                        $this->start_transaction_y = $this->y;
19240
 
                        // restore table header
19241
 
                        $this->thead = $tmp_thead;
19242
 
                        // fix table border properties
19243
 
                        if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
19244
 
                            $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
19245
 
                        } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
19246
 
                            $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
19247
 
                        } else {
19248
 
                            $tmp_cellspacing = 0;
19249
 
                        }
19250
 
                        $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page;
19251
 
                        $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column;
19252
 
                        $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing;
19253
 
                        $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']);
19254
 
                        $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset;
19255
 
                        $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset;
19256
 
                        // print table header (thead)
19257
 
                        $this->writeHTML($this->thead, false, false, false, false, '');
19258
 
                    }
19259
 
                }
19260
 
                // move $key index forward to skip THEAD block
19261
 
                while ( ($key < $maxel) AND (!(
19262
 
                    ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
19263
 
                    OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
19264
 
                    ++$key;
19265
 
                }
19266
 
            }
19267
 
            if ($dom[$key]['tag'] OR ($key == 0)) {
19268
 
                if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
19269
 
                    $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
19270
 
                }
19271
 
                // vertically align image in line
19272
 
                if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
19273
 
                    // get image height
19274
 
                    $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], $this->lasth, 'px');
19275
 
                    // check for automatic line break
19276
 
                    $autolinebreak = false;
19277
 
                    if (isset($dom[$key]['width']) AND ($dom[$key]['width'] > 0)) {
19278
 
                        $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], 1, 'px', false);
19279
 
                        if (($this->rtl AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L'])))
19280
 
                            OR (!$this->rtl AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R'])))) {
19281
 
                            // add automatic line break
19282
 
                            $autolinebreak = true;
19283
 
                            $this->Ln('', $cell);
19284
 
                            // go back to evaluate this line break
19285
 
                            --$key;
19286
 
                        }
19287
 
                    }
19288
 
                    if (!$autolinebreak) {
19289
 
                        if (!$this->InFooter) {
19290
 
                            $pre_y = $this->y;
19291
 
                            // check for page break
19292
 
                            if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) {
19293
 
                                // fix for multicolumn mode
19294
 
                                $startliney = $this->y;
19295
 
                            }
19296
 
                        }
19297
 
                        if ($this->page > $startlinepage) {
19298
 
                            // fix line splitted over two pages
19299
 
                            if (isset($this->footerlen[$startlinepage])) {
19300
 
                                $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
19301
 
                            }
19302
 
                            // line to be moved one page forward
19303
 
                            $pagebuff = $this->getPageBuffer($startlinepage);
19304
 
                            $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
19305
 
                            $tstart = substr($pagebuff, 0, $startlinepos);
19306
 
                            $tend = substr($this->getPageBuffer($startlinepage), $curpos);
19307
 
                            // remove line from previous page
19308
 
                            $this->setPageBuffer($startlinepage, $tstart.''.$tend);
19309
 
                            $pagebuff = $this->getPageBuffer($this->page);
19310
 
                            $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
19311
 
                            $tend = substr($pagebuff, $this->cntmrk[$this->page]);
19312
 
                            // add line start to current page
19313
 
                            $yshift = $minstartliney - $this->y;
19314
 
                            if ($fontaligned) {
19315
 
                                $yshift += ($curfontsize / $this->k);
19316
 
                            }
19317
 
                            $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
19318
 
                            $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
19319
 
                            // shift the annotations and links
19320
 
                            if (isset($this->PageAnnots[$this->page])) {
19321
 
                                $next_pask = count($this->PageAnnots[$this->page]);
19322
 
                            } else {
19323
 
                                $next_pask = 0;
19324
 
                            }
19325
 
                            if (isset($this->PageAnnots[$startlinepage])) {
19326
 
                                foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
19327
 
                                    if ($pak >= $pask) {
19328
 
                                        $this->PageAnnots[$this->page][] = $pac;
19329
 
                                        unset($this->PageAnnots[$startlinepage][$pak]);
19330
 
                                        $npak = count($this->PageAnnots[$this->page]) - 1;
19331
 
                                        $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
19332
 
                                    }
19333
 
                                }
19334
 
                            }
19335
 
                            $pask = $next_pask;
19336
 
                            $startlinepos = $this->cntmrk[$this->page];
19337
 
                            $startlinepage = $this->page;
19338
 
                            $startliney = $this->y;
19339
 
                            $this->newline = false;
19340
 
                        }
19341
 
                        $this->y += ((($curfontsize * $this->cell_height_ratio / $this->k) + $curfontascent - $curfontdescent) / 2) - $imgh;
19342
 
                        $minstartliney = min($this->y, $minstartliney);
19343
 
                        $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio));
19344
 
                    }
19345
 
                } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
19346
 
                    // account for different font size
19347
 
                    $pfontname = $curfontname;
19348
 
                    $pfontstyle = $curfontstyle;
19349
 
                    $pfontsize = $curfontsize;
19350
 
                    $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
19351
 
                    $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
19352
 
                    $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
19353
 
                    $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
19354
 
                    $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
19355
 
                    if ( ($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
19356
 
                        OR ($this->cell_height_ratio != $dom[$key]['line-height'])
19357
 
                        OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
19358
 
                        if ((!$this->newline) AND ($key < ($maxel - 1))
19359
 
                            AND ( (is_numeric($fontsize) AND ($fontsize >= 0) AND is_numeric($curfontsize) AND ($curfontsize >= 0) AND ($fontsize != $curfontsize))
19360
 
                                OR ($this->cell_height_ratio != $dom[$key]['line-height']))
19361
 
                                OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
19362
 
                            if ($this->page > $startlinepage) {
19363
 
                                // fix lines splitted over two pages
19364
 
                                if (isset($this->footerlen[$startlinepage])) {
19365
 
                                    $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
19366
 
                                }
19367
 
                                // line to be moved one page forward
19368
 
                                $pagebuff = $this->getPageBuffer($startlinepage);
19369
 
                                $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
19370
 
                                $tstart = substr($pagebuff, 0, $startlinepos);
19371
 
                                $tend = substr($this->getPageBuffer($startlinepage), $curpos);
19372
 
                                // remove line start from previous page
19373
 
                                $this->setPageBuffer($startlinepage, $tstart.''.$tend);
19374
 
                                $pagebuff = $this->getPageBuffer($this->page);
19375
 
                                $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
19376
 
                                $tend = substr($pagebuff, $this->cntmrk[$this->page]);
19377
 
                                // add line start to current page
19378
 
                                $yshift = $minstartliney - $this->y;
19379
 
                                $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
19380
 
                                $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
19381
 
                                // shift the annotations and links
19382
 
                                if (isset($this->PageAnnots[$this->page])) {
19383
 
                                    $next_pask = count($this->PageAnnots[$this->page]);
19384
 
                                } else {
19385
 
                                    $next_pask = 0;
19386
 
                                }
19387
 
                                if (isset($this->PageAnnots[$startlinepage])) {
19388
 
                                    foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
19389
 
                                        if ($pak >= $pask) {
19390
 
                                            $this->PageAnnots[$this->page][] = $pac;
19391
 
                                            unset($this->PageAnnots[$startlinepage][$pak]);
19392
 
                                            $npak = count($this->PageAnnots[$this->page]) - 1;
19393
 
                                            $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
19394
 
                                        }
19395
 
                                    }
19396
 
                                }
19397
 
                                $pask = $next_pask;
19398
 
                                $startlinepos = $this->cntmrk[$this->page];
19399
 
                                $startlinepage = $this->page;
19400
 
                                $startliney = $this->y;
19401
 
                            }
19402
 
                            if (!isset($dom[$key]['line-height'])) {
19403
 
                                $dom[$key]['line-height'] = $this->cell_height_ratio;
19404
 
                            }
19405
 
                            if (!$dom[$key]['block']) {
19406
 
                                $this->y += (((($curfontsize * $this->cell_height_ratio ) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
19407
 
                                if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
19408
 
                                    $minstartliney = min($this->y, $minstartliney);
19409
 
                                    $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney);
19410
 
                                }
19411
 
                            }
19412
 
                            $this->cell_height_ratio = $dom[$key]['line-height'];
19413
 
                            $fontaligned = true;
19414
 
                        }
19415
 
                        $this->SetFont($fontname, $fontstyle, $fontsize);
19416
 
                        // reset row height
19417
 
                        $this->resetLastH();
19418
 
                        $curfontname = $fontname;
19419
 
                        $curfontstyle = $fontstyle;
19420
 
                        $curfontsize = $fontsize;
19421
 
                        $curfontascent = $fontascent;
19422
 
                        $curfontdescent = $fontdescent;
19423
 
                    }
19424
 
                }
19425
 
                // set text rendering mode
19426
 
                $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
19427
 
                $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0);
19428
 
                $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3);
19429
 
                $this->setTextRenderingMode($textstroke, $textfill, $textclip);
19430
 
                if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
19431
 
                    $this->setFontStretching($dom[$key]['font-stretch']);
19432
 
                }
19433
 
                if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
19434
 
                    $this->setFontSpacing($dom[$key]['letter-spacing']);
19435
 
                }
19436
 
                if (($plalign == 'J') AND $dom[$key]['block']) {
19437
 
                    $plalign = '';
19438
 
                }
19439
 
                // get current position on page buffer
19440
 
                $curpos = $this->pagelen[$startlinepage];
19441
 
                if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
19442
 
                    $this->SetFillColorArray($dom[$key]['bgcolor']);
19443
 
                    $wfill = true;
19444
 
                } else {
19445
 
                    $wfill = $fill | false;
19446
 
                }
19447
 
                if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
19448
 
                    $this->SetTextColorArray($dom[$key]['fgcolor']);
19449
 
                }
19450
 
                if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
19451
 
                    $this->SetDrawColorArray($dom[$key]['strokecolor']);
19452
 
                }
19453
 
                if (isset($dom[$key]['align'])) {
19454
 
                    $lalign = $dom[$key]['align'];
19455
 
                }
19456
 
                if ($this->empty_string($lalign)) {
19457
 
                    $lalign = $align;
19458
 
                }
19459
 
            }
19460
 
            // align lines
19461
 
            if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
19462
 
                $newline = true;
19463
 
                $fontaligned = false;
19464
 
                // we are at the beginning of a new line
19465
 
                if (isset($startlinex)) {
19466
 
                    $yshift = $minstartliney - $startliney;
19467
 
                    if (($yshift > 0) OR ($this->page > $startlinepage)) {
19468
 
                        $yshift = 0;
19469
 
                    }
19470
 
                    $t_x = 0;
19471
 
                    // the last line must be shifted to be aligned as requested
19472
 
                    $linew = abs($this->endlinex - $startlinex);
19473
 
                    if ($this->inxobj) {
19474
 
                        // we are inside an XObject template
19475
 
                        $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
19476
 
                        if (isset($opentagpos)) {
19477
 
                            $midpos = $opentagpos;
19478
 
                        } else {
19479
 
                            $midpos = 0;
19480
 
                        }
19481
 
                        if ($midpos > 0) {
19482
 
                            $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
19483
 
                            $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
19484
 
                        } else {
19485
 
                            $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
19486
 
                            $pend = '';
19487
 
                        }
19488
 
                    } else {
19489
 
                        $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
19490
 
                        if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
19491
 
                            $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
19492
 
                            $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
19493
 
                        } elseif (isset($opentagpos)) {
19494
 
                            $midpos = $opentagpos;
19495
 
                        } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
19496
 
                            $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
19497
 
                            $midpos = $this->footerpos[$startlinepage];
19498
 
                        } else {
19499
 
                            $midpos = 0;
19500
 
                        }
19501
 
                        if ($midpos > 0) {
19502
 
                            $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
19503
 
                            $pend = substr($this->getPageBuffer($startlinepage), $midpos);
19504
 
                        } else {
19505
 
                            $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
19506
 
                            $pend = '';
19507
 
                        }
19508
 
                    }
19509
 
                    if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
19510
 
                        // calculate shifting amount
19511
 
                        $tw = $w;
19512
 
                        if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) {
19513
 
                            $tw += $this->cell_padding['R'];
19514
 
                        }
19515
 
                        if ($this->lMargin != $prevlMargin) {
19516
 
                            $tw += ($prevlMargin - $this->lMargin);
19517
 
                        }
19518
 
                        if ($this->rMargin != $prevrMargin) {
19519
 
                            $tw += ($prevrMargin - $this->rMargin);
19520
 
                        }
19521
 
                        $one_space_width = $this->GetStringWidth(chr(32));
19522
 
                        $no = 0; // number of spaces on a line contained on a single block
19523
 
                        if ($this->isRTLTextDir()) { // RTL
19524
 
                            // remove left space if exist
19525
 
                            $pos1 = $this->revstrpos($pmid, '[(');
19526
 
                            if ($pos1 > 0) {
19527
 
                                $pos1 = intval($pos1);
19528
 
                                if ($this->isUnicodeFont()) {
19529
 
                                    $pos2 = intval($this->revstrpos($pmid, '[('.chr(0).chr(32)));
19530
 
                                    $spacelen = 2;
19531
 
                                } else {
19532
 
                                    $pos2 = intval($this->revstrpos($pmid, '[('.chr(32)));
19533
 
                                    $spacelen = 1;
19534
 
                                }
19535
 
                                if ($pos1 == $pos2) {
19536
 
                                    $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
19537
 
                                    if (substr($pmid, $pos1, 4) == '[()]') {
19538
 
                                        $linew -= $one_space_width;
19539
 
                                    } elseif ($pos1 == strpos($pmid, '[(')) {
19540
 
                                        $no = 1;
19541
 
                                    }
19542
 
                                }
19543
 
                            }
19544
 
                        } else { // LTR
19545
 
                            // remove right space if exist
19546
 
                            $pos1 = $this->revstrpos($pmid, ')]');
19547
 
                            if ($pos1 > 0) {
19548
 
                                $pos1 = intval($pos1);
19549
 
                                if ($this->isUnicodeFont()) {
19550
 
                                    $pos2 = intval($this->revstrpos($pmid, chr(0).chr(32).')]')) + 2;
19551
 
                                    $spacelen = 2;
19552
 
                                } else {
19553
 
                                    $pos2 = intval($this->revstrpos($pmid, chr(32).')]')) + 1;
19554
 
                                    $spacelen = 1;
19555
 
                                }
19556
 
                                if ($pos1 == $pos2) {
19557
 
                                    $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
19558
 
                                    $linew -= $one_space_width;
19559
 
                                }
19560
 
                            }
19561
 
                        }
19562
 
                        $mdiff = ($tw - $linew);
19563
 
                        if ($plalign == 'C') {
19564
 
                            if ($this->rtl) {
19565
 
                                $t_x = -($mdiff / 2);
19566
 
                            } else {
19567
 
                                $t_x = ($mdiff / 2);
19568
 
                            }
19569
 
                        } elseif ($plalign == 'R') {
19570
 
                            // right alignment on LTR document
19571
 
                            $t_x = $mdiff;
19572
 
                        } elseif ($plalign == 'L') {
19573
 
                            // left alignment on RTL document
19574
 
                            $t_x = -$mdiff;
19575
 
                        } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
19576
 
                            // Justification
19577
 
                            if ($this->isRTLTextDir()) {
19578
 
                                // align text on the left
19579
 
                                $t_x = -$mdiff;
19580
 
                            }
19581
 
                            $ns = 0; // number of spaces
19582
 
                            $pmidtemp = $pmid;
19583
 
                            // escape special characters
19584
 
                            $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
19585
 
                            $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
19586
 
                            // search spaces
19587
 
                            if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
19588
 
                                $spacestr = $this->getSpaceString();
19589
 
                                $maxkk = count($lnstring[1]) - 1;
19590
 
                                for ($kk=0; $kk <= $maxkk; ++$kk) {
19591
 
                                    // restore special characters
19592
 
                                    $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
19593
 
                                    $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
19594
 
                                    // store number of spaces on the strings
19595
 
                                    $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
19596
 
                                    // count total spaces on line
19597
 
                                    $ns += $lnstring[2][$kk];
19598
 
                                    $lnstring[3][$kk] = $ns;
19599
 
                                }
19600
 
                                if ($ns == 0) {
19601
 
                                    $ns = 1;
19602
 
                                }
19603
 
                                // calculate additional space to add to each existing space
19604
 
                                $spacewidth = ($mdiff / ($ns - $no)) * $this->k;
19605
 
                                $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize;
19606
 
                                if ($this->font_spacing != 0) {
19607
 
                                    // fixed spacing mode
19608
 
                                    $osw = -1000 * $this->font_spacing / $this->FontSize;
19609
 
                                    $spacewidthu += $osw;
19610
 
                                }
19611
 
                                $nsmax = $ns;
19612
 
                                $ns = 0;
19613
 
                                reset($lnstring);
19614
 
                                $offset = 0;
19615
 
                                $strcount = 0;
19616
 
                                $prev_epsposbeg = 0;
19617
 
                                $textpos = 0;
19618
 
                                if ($this->isRTLTextDir()) {
19619
 
                                    $textpos = $this->wPt;
19620
 
                                }
19621
 
                                global $spacew;
19622
 
                                while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
19623
 
                                    // check if we are inside a string section '[( ... )]'
19624
 
                                    $stroffset = strpos($pmid, '[(', $offset);
19625
 
                                    if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
19626
 
                                        // set offset to the end of string section
19627
 
                                        $offset = strpos($pmid, ')]', $stroffset);
19628
 
                                        while (($offset !== false) AND ($pmid{($offset - 1)} == '\\')) {
19629
 
                                            $offset = strpos($pmid, ')]', ($offset + 1));
19630
 
                                        }
19631
 
                                        if ($offset === false) {
19632
 
                                            $this->Error('HTML Justification: malformed PDF code.');
19633
 
                                        }
19634
 
                                        continue;
19635
 
                                    }
19636
 
                                    if ($this->isRTLTextDir()) {
19637
 
                                        $spacew = ($spacewidth * ($nsmax - $ns));
19638
 
                                    } else {
19639
 
                                        $spacew = ($spacewidth * $ns);
19640
 
                                    }
19641
 
                                    $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
19642
 
                                    $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
19643
 
                                    $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q');
19644
 
                                    if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
19645
 
                                        OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
19646
 
                                        // shift EPS images
19647
 
                                        $trx = sprintf('1 0 0 1 %.3F 0 cm', $spacew);
19648
 
                                        $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
19649
 
                                        $pmid_b = substr($pmid, 0, $epsposbeg);
19650
 
                                        $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
19651
 
                                        $pmid_e = substr($pmid, $epsposend);
19652
 
                                        $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
19653
 
                                        $offset = $epsposend;
19654
 
                                        continue;
19655
 
 
19656
 
                                    }
19657
 
                                    $prev_epsposbeg = $epsposbeg;
19658
 
                                    $currentxpos = 0;
19659
 
                                    // shift blocks of code
19660
 
                                    switch ($strpiece[2][0]) {
19661
 
                                        case 'Td':
19662
 
                                        case 'cm':
19663
 
                                        case 'm':
19664
 
                                        case 'l': {
19665
 
                                            // get current X position
19666
 
                                            preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
19667
 
                                            $currentxpos = $xmatches[1];
19668
 
                                            $textpos = $currentxpos;
19669
 
                                            if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
19670
 
                                                $ns = $lnstring[3][$strcount];
19671
 
                                                if ($this->isRTLTextDir()) {
19672
 
                                                    $spacew = ($spacewidth * ($nsmax - $ns));
19673
 
                                                }
19674
 
                                                ++$strcount;
19675
 
                                            }
19676
 
                                            // justify block
19677
 
                                            $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
19678
 
                                                create_function('$matches', 'global $spacew;
19679
 
                                                $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
19680
 
                                                return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1);
19681
 
                                            break;
19682
 
                                        }
19683
 
                                        case 're': {
19684
 
                                            // justify block
19685
 
                                            if (!$this->empty_string($this->lispacer)) {
19686
 
                                                $this->lispacer = '';
19687
 
                                                continue;
19688
 
                                            }
19689
 
                                            preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
19690
 
                                            $currentxpos = $xmatches[1];
19691
 
                                            global $x_diff, $w_diff;
19692
 
                                            $x_diff = 0;
19693
 
                                            $w_diff = 0;
19694
 
                                            if ($this->isRTLTextDir()) { // RTL
19695
 
                                                if ($currentxpos < $textpos) {
19696
 
                                                    $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
19697
 
                                                    $w_diff = ($spacewidth * $lnstring[2][$strcount]);
19698
 
                                                } else {
19699
 
                                                    if ($strcount > 0) {
19700
 
                                                        $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
19701
 
                                                        $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
19702
 
                                                    }
19703
 
                                                }
19704
 
                                            } else { // LTR
19705
 
                                                if ($currentxpos > $textpos) {
19706
 
                                                    if ($strcount > 0) {
19707
 
                                                        $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
19708
 
                                                    }
19709
 
                                                    $w_diff = ($spacewidth * $lnstring[2][$strcount]);
19710
 
                                                } else {
19711
 
                                                    if ($strcount > 1) {
19712
 
                                                        $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
19713
 
                                                    }
19714
 
                                                    if ($strcount > 0) {
19715
 
                                                        $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
19716
 
                                                    }
19717
 
                                                }
19718
 
                                            }
19719
 
                                            $pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x',
19720
 
                                                create_function('$matches', 'global $x_diff, $w_diff;
19721
 
                                                $newx = sprintf("%.2F",(floatval($matches[1]) + $x_diff));
19722
 
                                                $neww = sprintf("%.2F",(floatval($matches[3]) + $w_diff));
19723
 
                                                return "".$newx." ".$matches[2]." ".$neww." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1);
19724
 
                                            break;
19725
 
                                        }
19726
 
                                        case 'c': {
19727
 
                                            // get current X position
19728
 
                                            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);
19729
 
                                            $currentxpos = $xmatches[1];
19730
 
                                            // justify block
19731
 
                                            $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',
19732
 
                                                create_function('$matches', 'global $spacew;
19733
 
                                                $newx1 = sprintf("%.3F",(floatval($matches[1]) + $spacew));
19734
 
                                                $newx2 = sprintf("%.3F",(floatval($matches[3]) + $spacew));
19735
 
                                                $newx3 = sprintf("%.3F",(floatval($matches[5]) + $spacew));
19736
 
                                                return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1);
19737
 
                                            break;
19738
 
                                        }
19739
 
                                    }
19740
 
                                    // shift the annotations and links
19741
 
                                    $cxpos = ($currentxpos / $this->k);
19742
 
                                    $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps);
19743
 
                                    if ($this->inxobj) {
19744
 
                                        // we are inside an XObject template
19745
 
                                        foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
19746
 
                                            if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
19747
 
                                                if ($cxpos > $lmpos) {
19748
 
                                                    $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k);
19749
 
                                                    $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
19750
 
                                                } else {
19751
 
                                                    $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
19752
 
                                                }
19753
 
                                                break;
19754
 
                                            }
19755
 
                                        }
19756
 
                                    } elseif (isset($this->PageAnnots[$this->page])) {
19757
 
                                        foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
19758
 
                                            if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
19759
 
                                                if ($cxpos > $lmpos) {
19760
 
                                                    $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
19761
 
                                                    $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
19762
 
                                                } else {
19763
 
                                                    $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
19764
 
                                                }
19765
 
                                                break;
19766
 
                                            }
19767
 
                                        }
19768
 
                                    }
19769
 
                                } // end of while
19770
 
                                // remove markers
19771
 
                                $pmid = str_replace('x*#!#*x', '', $pmid);
19772
 
                                if ($this->isUnicodeFont()) {
19773
 
                                    // multibyte characters
19774
 
                                    $spacew = $spacewidthu;
19775
 
                                    if ($this->font_stretching != 100) {
19776
 
                                        // word spacing is affected by stretching
19777
 
                                        $spacew /= ($this->font_stretching / 100);
19778
 
                                    }
19779
 
                                    $pmidtemp = $pmid;
19780
 
                                    // escape special characters
19781
 
                                    $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
19782
 
                                    $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
19783
 
                                    $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x",
19784
 
                                                create_function('$matches', 'global $spacew;
19785
 
                                                $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]);
19786
 
                                                $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]);
19787
 
                                                return "[(".str_replace(chr(0).chr(32), ") ".sprintf("%.3F", $spacew)." (", $matches[1]).")]";'), $pmidtemp);
19788
 
                                    if ($this->inxobj) {
19789
 
                                        // we are inside an XObject template
19790
 
                                        $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend;
19791
 
                                    } else {
19792
 
                                        $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
19793
 
                                    }
19794
 
                                    $endlinepos = strlen($pstart."\n".$pmid."\n");
19795
 
                                } else {
19796
 
                                    // non-unicode (single-byte characters)
19797
 
                                    if ($this->font_stretching != 100) {
19798
 
                                        // word spacing (Tw) is affected by stretching
19799
 
                                        $spacewidth /= ($this->font_stretching / 100);
19800
 
                                    }
19801
 
                                    $rs = sprintf('%.3F Tw', $spacewidth);
19802
 
                                    $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
19803
 
                                    if ($this->inxobj) {
19804
 
                                        // we are inside an XObject template
19805
 
                                        $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
19806
 
                                    } else {
19807
 
                                        $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
19808
 
                                    }
19809
 
                                    $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
19810
 
                                }
19811
 
                            }
19812
 
                        } // end of J
19813
 
                    } // end if $startlinex
19814
 
                    if (($t_x != 0) OR ($yshift < 0)) {
19815
 
                        // shift the line
19816
 
                        $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
19817
 
                        $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
19818
 
                        $endlinepos = strlen($pstart);
19819
 
                        if ($this->inxobj) {
19820
 
                            // we are inside an XObject template
19821
 
                            $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
19822
 
                            foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
19823
 
                                if ($pak >= $pask) {
19824
 
                                    $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
19825
 
                                    $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
19826
 
                                }
19827
 
                            }
19828
 
                        } else {
19829
 
                            $this->setPageBuffer($startlinepage, $pstart.$pend);
19830
 
                            // shift the annotations and links
19831
 
                            if (isset($this->PageAnnots[$this->page])) {
19832
 
                                foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
19833
 
                                    if ($pak >= $pask) {
19834
 
                                        $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
19835
 
                                        $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
19836
 
                                    }
19837
 
                                }
19838
 
                            }
19839
 
                        }
19840
 
                        $this->y -= $yshift;
19841
 
                    }
19842
 
                }
19843
 
                $pbrk = $this->checkPageBreak($this->lasth);
19844
 
                $this->newline = false;
19845
 
                $startlinex = $this->x;
19846
 
                $startliney = $this->y;
19847
 
                if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
19848
 
                    $startliney -= ((0.3 * $this->FontSizePt) / $this->k);
19849
 
                } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
19850
 
                    $startliney -= (($this->FontSizePt / 0.7) / $this->k);
19851
 
                } else {
19852
 
                    $minstartliney = $startliney;
19853
 
                    $maxbottomliney = ($this->y + (($fontsize * $this->cell_height_ratio) / $this->k));
19854
 
                }
19855
 
                $startlinepage = $this->page;
19856
 
                if (isset($endlinepos) AND (!$pbrk)) {
19857
 
                    $startlinepos = $endlinepos;
19858
 
                } else {
19859
 
                    if ($this->inxobj) {
19860
 
                        // we are inside an XObject template
19861
 
                        $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
19862
 
                    } elseif (!$this->InFooter) {
19863
 
                        if (isset($this->footerlen[$this->page])) {
19864
 
                            $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
19865
 
                        } else {
19866
 
                            $this->footerpos[$this->page] = $this->pagelen[$this->page];
19867
 
                        }
19868
 
                        $startlinepos = $this->footerpos[$this->page];
19869
 
                    } else {
19870
 
                        $startlinepos = $this->pagelen[$this->page];
19871
 
                    }
19872
 
                }
19873
 
                unset($endlinepos);
19874
 
                $plalign = $lalign;
19875
 
                if (isset($this->PageAnnots[$this->page])) {
19876
 
                    $pask = count($this->PageAnnots[$this->page]);
19877
 
                } else {
19878
 
                    $pask = 0;
19879
 
                }
19880
 
                if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
19881
 
                    AND (isset($this->emptypagemrk[$this->page]))
19882
 
                    AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) {
19883
 
                    $this->SetFont($fontname, $fontstyle, $fontsize);
19884
 
                    if ($wfill) {
19885
 
                        $this->SetFillColorArray($this->bgcolor);
19886
 
                    }
19887
 
                }
19888
 
            } // end newline
19889
 
            if (isset($opentagpos)) {
19890
 
                unset($opentagpos);
19891
 
            }
19892
 
            if ($dom[$key]['tag']) {
19893
 
                if ($dom[$key]['opening']) {
19894
 
                    // get text indentation (if any)
19895
 
                    if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
19896
 
                        $this->textindent = $dom[$key]['text-indent'];
19897
 
                        $this->newline = true;
19898
 
                    }
19899
 
                    // table
19900
 
                    if ($dom[$key]['value'] == 'table') {
19901
 
                        // available page width
19902
 
                        if ($this->rtl) {
19903
 
                            $wtmp = $this->x - $this->lMargin;
19904
 
                        } else {
19905
 
                            $wtmp = $this->w - $this->rMargin - $this->x;
19906
 
                        }
19907
 
                        // get cell spacing
19908
 
                        if (isset($dom[$key]['attribute']['cellspacing'])) {
19909
 
                            $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
19910
 
                            $cellspacing = array('H' => $clsp, 'V' => $clsp);
19911
 
                        } elseif (isset($dom[$key]['border-spacing'])) {
19912
 
                            $cellspacing = $dom[$key]['border-spacing'];
19913
 
                        } else {
19914
 
                            $cellspacing = array('H' => 0, 'V' => 0);
19915
 
                        }
19916
 
                        // table width
19917
 
                        if (isset($dom[$key]['width'])) {
19918
 
                            $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
19919
 
                        } else {
19920
 
                            $table_width = $wtmp;
19921
 
                        }
19922
 
                        $table_width -= (2 * $cellspacing['H']);
19923
 
                        if (!$this->inthead) {
19924
 
                            $this->y += $cellspacing['V'];
19925
 
                        }
19926
 
                        if ($this->rtl) {
19927
 
                            $cellspacingx = -$cellspacing['H'];
19928
 
                        } else {
19929
 
                            $cellspacingx = $cellspacing['H'];
19930
 
                        }
19931
 
                        // total table width without cellspaces
19932
 
                        $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
19933
 
                        // minimum column width
19934
 
                        $table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
19935
 
                        // array of custom column widths
19936
 
                        $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
19937
 
                    }
19938
 
                    // table row
19939
 
                    if ($dom[$key]['value'] == 'tr') {
19940
 
                        // reset column counter
19941
 
                        $colid = 0;
19942
 
                    }
19943
 
                    // table cell
19944
 
                    if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
19945
 
                        $trid = $dom[$key]['parent'];
19946
 
                        $table_el = $dom[$trid]['parent'];
19947
 
                        if (!isset($dom[$table_el]['cols'])) {
19948
 
                            $dom[$table_el]['cols'] = $dom[$trid]['cols'];
19949
 
                        }
19950
 
                        // store border info
19951
 
                        $tdborder = 0;
19952
 
                        if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
19953
 
                            $tdborder = $dom[$key]['border'];
19954
 
                        }
19955
 
                        $colspan = $dom[$key]['attribute']['colspan'];
19956
 
                        $old_cell_padding = $this->cell_padding;
19957
 
                        if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
19958
 
                            $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
19959
 
                            $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
19960
 
                        } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
19961
 
                            $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
19962
 
                        } else {
19963
 
                            $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
19964
 
                        }
19965
 
                        $this->cell_padding = $current_cell_padding;
19966
 
                        if (isset($dom[$key]['height'])) {
19967
 
                            // minimum cell height
19968
 
                            $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
19969
 
                        } else {
19970
 
                            $cellh = 0;
19971
 
                        }
19972
 
                        if (isset($dom[$key]['content'])) {
19973
 
                            $cell_content = $dom[$key]['content'];
19974
 
                        } else {
19975
 
                            $cell_content = '&nbsp;';
19976
 
                        }
19977
 
                        $tagtype = $dom[$key]['value'];
19978
 
                        $parentid = $key;
19979
 
                        while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
19980
 
                            // move $key index forward
19981
 
                            ++$key;
19982
 
                        }
19983
 
                        if (!isset($dom[$trid]['startpage'])) {
19984
 
                            $dom[$trid]['startpage'] = $this->page;
19985
 
                        } else {
19986
 
                            $this->setPage($dom[$trid]['startpage']);
19987
 
                        }
19988
 
                        if (!isset($dom[$trid]['startcolumn'])) {
19989
 
                            $dom[$trid]['startcolumn'] = $this->current_column;
19990
 
                        } elseif ($this->current_column != $dom[$trid]['startcolumn']) {
19991
 
                            $tmpx = $this->x;
19992
 
                            $this->selectColumn($dom[$trid]['startcolumn']);
19993
 
                            $this->x = $tmpx;
19994
 
                        }
19995
 
                        if (!isset($dom[$trid]['starty'])) {
19996
 
                            $dom[$trid]['starty'] = $this->y;
19997
 
                        } else {
19998
 
                            $this->y = $dom[$trid]['starty'];
19999
 
                        }
20000
 
                        if (!isset($dom[$trid]['startx'])) {
20001
 
                            $dom[$trid]['startx'] = $this->x;
20002
 
                            $this->x += $cellspacingx;
20003
 
                        } else {
20004
 
                            $this->x += ($cellspacingx / 2);
20005
 
                        }
20006
 
                        if (isset($dom[$parentid]['attribute']['rowspan'])) {
20007
 
                            $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
20008
 
                        } else {
20009
 
                            $rowspan = 1;
20010
 
                        }
20011
 
                        // skip row-spanned cells started on the previous rows
20012
 
                        if (isset($dom[$table_el]['rowspans'])) {
20013
 
                            $rsk = 0;
20014
 
                            $rskmax = count($dom[$table_el]['rowspans']);
20015
 
                            while ($rsk < $rskmax) {
20016
 
                                $trwsp = $dom[$table_el]['rowspans'][$rsk];
20017
 
                                $rsstartx = $trwsp['startx'];
20018
 
                                $rsendx = $trwsp['endx'];
20019
 
                                // account for margin changes
20020
 
                                if ($trwsp['startpage'] < $this->page) {
20021
 
                                    if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
20022
 
                                        $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
20023
 
                                        $rsstartx -= $dl;
20024
 
                                        $rsendx -= $dl;
20025
 
                                    } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
20026
 
                                        $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
20027
 
                                        $rsstartx += $dl;
20028
 
                                        $rsendx += $dl;
20029
 
                                    }
20030
 
                                }
20031
 
                                if (($trwsp['rowspan'] > 0)
20032
 
                                    AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps))
20033
 
                                    AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps))
20034
 
                                    AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) {
20035
 
                                    // set the starting X position of the current cell
20036
 
                                    $this->x = $rsendx + $cellspacingx;
20037
 
                                    // increment column indicator
20038
 
                                    $colid += $trwsp['colspan'];
20039
 
                                    if (($trwsp['rowspan'] == 1)
20040
 
                                        AND (isset($dom[$trid]['endy']))
20041
 
                                        AND (isset($dom[$trid]['endpage']))
20042
 
                                        AND (isset($dom[$trid]['endcolumn']))
20043
 
                                        AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
20044
 
                                        AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
20045
 
                                        // set ending Y position for row
20046
 
                                        $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
20047
 
                                        $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
20048
 
                                    }
20049
 
                                    $rsk = 0;
20050
 
                                } else {
20051
 
                                    ++$rsk;
20052
 
                                }
20053
 
                            }
20054
 
                        }
20055
 
                        if (isset($dom[$parentid]['width'])) {
20056
 
                            // user specified width
20057
 
                            $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
20058
 
                            $tmpcw = ($cellw / $colspan);
20059
 
                            for ($i = 0; $i < $colspan; ++$i) {
20060
 
                                $table_colwidths[($colid + $i)] = $tmpcw;
20061
 
                            }
20062
 
                        } else {
20063
 
                            // inherit column width
20064
 
                            $cellw = 0;
20065
 
                            for ($i = 0; $i < $colspan; ++$i) {
20066
 
                                $cellw += $table_colwidths[($colid + $i)];
20067
 
                            }
20068
 
                        }
20069
 
                        $cellw += (($colspan - 1) * $cellspacing['H']);
20070
 
                        // increment column indicator
20071
 
                        $colid += $colspan;
20072
 
                        // add rowspan information to table element
20073
 
                        if ($rowspan > 1) {
20074
 
                            $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));
20075
 
                        }
20076
 
                        $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
20077
 
                        if ($rowspan > 1) {
20078
 
                            $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
20079
 
                        }
20080
 
                        // push background colors
20081
 
                        if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
20082
 
                            $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
20083
 
                        }
20084
 
                        // store border info
20085
 
                        if (isset($tdborder) AND !empty($tdborder)) {
20086
 
                            $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
20087
 
                        }
20088
 
                        $prevLastH = $this->lasth;
20089
 
                        // store some info for multicolumn mode
20090
 
                        if ($this->rtl) {
20091
 
                            $this->colxshift['x'] = $this->w - $this->x - $this->rMargin;
20092
 
                        } else {
20093
 
                            $this->colxshift['x'] = $this->x - $this->lMargin;
20094
 
                        }
20095
 
                        $this->colxshift['s'] = $cellspacing;
20096
 
                        $this->colxshift['p'] = $current_cell_padding;
20097
 
                        // ****** write the cell content ******
20098
 
                        $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true);
20099
 
                        // restore some values
20100
 
                        $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
20101
 
                        $this->lasth = $prevLastH;
20102
 
                        $this->cell_padding = $old_cell_padding;
20103
 
                        $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
20104
 
                        // update the end of row position
20105
 
                        if ($rowspan <= 1) {
20106
 
                            if (isset($dom[$trid]['endy'])) {
20107
 
                                if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) {
20108
 
                                    $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
20109
 
                                } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) {
20110
 
                                    $dom[$trid]['endy'] = $this->y;
20111
 
                                }
20112
 
                            } else {
20113
 
                                $dom[$trid]['endy'] = $this->y;
20114
 
                            }
20115
 
                            if (isset($dom[$trid]['endpage'])) {
20116
 
                                $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
20117
 
                            } else {
20118
 
                                $dom[$trid]['endpage'] = $this->page;
20119
 
                            }
20120
 
                            if (isset($dom[$trid]['endcolumn'])) {
20121
 
                                $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']);
20122
 
                            } else {
20123
 
                                $dom[$trid]['endcolumn'] = $this->current_column;
20124
 
                            }
20125
 
                        } else {
20126
 
                            // account for row-spanned cells
20127
 
                            $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
20128
 
                            $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
20129
 
                            $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
20130
 
                            $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column;
20131
 
                        }
20132
 
                        if (isset($dom[$table_el]['rowspans'])) {
20133
 
                            // update endy and endpage on rowspanned cells
20134
 
                            foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
20135
 
                                if ($trwsp['rowspan'] > 0) {
20136
 
                                    if (isset($dom[$trid]['endpage'])) {
20137
 
                                        if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
20138
 
                                            $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
20139
 
                                        } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
20140
 
                                            $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
20141
 
                                            $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
20142
 
                                            $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
20143
 
                                        } else {
20144
 
                                            $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
20145
 
                                        }
20146
 
                                    }
20147
 
                                }
20148
 
                            }
20149
 
                        }
20150
 
                        $this->x += ($cellspacingx / 2);
20151
 
                    } else {
20152
 
                        // opening tag (or self-closing tag)
20153
 
                        if (!isset($opentagpos)) {
20154
 
                            if ($this->inxobj) {
20155
 
                                // we are inside an XObject template
20156
 
                                $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']);
20157
 
                            } elseif (!$this->InFooter) {
20158
 
                                if (isset($this->footerlen[$this->page])) {
20159
 
                                    $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
20160
 
                                } else {
20161
 
                                    $this->footerpos[$this->page] = $this->pagelen[$this->page];
20162
 
                                }
20163
 
                                $opentagpos = $this->footerpos[$this->page];
20164
 
                            }
20165
 
                        }
20166
 
                        $this->openHTMLTagHandler($dom, $key, $cell);
20167
 
                    }
20168
 
                } else { // closing tag
20169
 
                    $prev_numpages = $this->numpages;
20170
 
                    $old_bordermrk = $this->bordermrk[$this->page];
20171
 
                    $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
20172
 
                    if ($this->bordermrk[$this->page] > $old_bordermrk) {
20173
 
                        $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk);
20174
 
                    }
20175
 
                    if ($prev_numpages > $this->numpages) {
20176
 
                        $startlinepage = $this->page;
20177
 
                    }
20178
 
                }
20179
 
            } elseif (strlen($dom[$key]['value']) > 0) {
20180
 
                // print list-item
20181
 
                if (!$this->empty_string($this->lispacer) AND ($this->lispacer != '^')) {
20182
 
                    $this->SetFont($pfontname, $pfontstyle, $pfontsize);
20183
 
                    $this->resetLastH();
20184
 
                    $minstartliney = $this->y;
20185
 
                    $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio));
20186
 
                    $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
20187
 
                    $this->SetFont($curfontname, $curfontstyle, $curfontsize);
20188
 
                    $this->resetLastH();
20189
 
                    if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
20190
 
                        $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
20191
 
                        $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
20192
 
                        $this->y += ((($pfontsize - $curfontsize) * $this->cell_height_ratio / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
20193
 
                        $minstartliney = min($this->y, $minstartliney);
20194
 
                        $maxbottomliney = max(($this->y + (($pfontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney);
20195
 
                    }
20196
 
                }
20197
 
                // text
20198
 
                $this->htmlvspace = 0;
20199
 
                if ((!$this->premode) AND $this->isRTLTextDir()) {
20200
 
                    // reverse spaces order
20201
 
                    $lsp = ''; // left spaces
20202
 
                    $rsp = ''; // right spaces
20203
 
                    if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
20204
 
                        $lsp = $matches[1];
20205
 
                    }
20206
 
                    if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
20207
 
                        $rsp = $matches[1];
20208
 
                    }
20209
 
                    $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
20210
 
                }
20211
 
                if ($newline) {
20212
 
                    if (!$this->premode) {
20213
 
                        $prelen = strlen($dom[$key]['value']);
20214
 
                        if ($this->isRTLTextDir()) {
20215
 
                            // right trim except non-breaking space
20216
 
                            $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
20217
 
                        } else {
20218
 
                            // left trim except non-breaking space
20219
 
                            $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
20220
 
                        }
20221
 
                        $postlen = strlen($dom[$key]['value']);
20222
 
                        if (($postlen == 0) AND ($prelen > 0)) {
20223
 
                            $dom[$key]['trimmed_space'] = true;
20224
 
                        }
20225
 
                    }
20226
 
                    $newline = false;
20227
 
                    $firstblock = true;
20228
 
                } else {
20229
 
                    $firstblock = false;
20230
 
                    // replace empty multiple spaces string with a single space
20231
 
                    $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']);
20232
 
                }
20233
 
                $strrest = '';
20234
 
                if ($this->rtl) {
20235
 
                    $this->x -= $this->textindent;
20236
 
                } else {
20237
 
                    $this->x += $this->textindent;
20238
 
                }
20239
 
                if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
20240
 
                    $strlinelen = $this->GetStringWidth($dom[$key]['value']);
20241
 
                    if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
20242
 
                        // HTML <a> Link
20243
 
                        $hrefcolor = '';
20244
 
                        if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
20245
 
                            $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
20246
 
                        }
20247
 
                        $hrefstyle = -1;
20248
 
                        if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
20249
 
                            $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
20250
 
                        }
20251
 
                        $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
20252
 
                    } else {
20253
 
                        $wadj = 0; // space to leave for block continuity
20254
 
                        $adjblks = 0; // number of blocks
20255
 
                        if ($this->rtl) {
20256
 
                            $cwa = $this->x - $this->lMargin;
20257
 
                        } else {
20258
 
                            $cwa = $this->w - $this->rMargin - $this->x;
20259
 
                        }
20260
 
                        if ($strlinelen < $cwa) {
20261
 
                            // check the next text blocks for continuity
20262
 
                            $nkey = ($key + 1);
20263
 
                            $write_block = true;
20264
 
                            $same_textdir = true;
20265
 
                            $tmp_fontname = $this->FontFamily;
20266
 
                            $tmp_fontstyle = $this->FontStyle;
20267
 
                            $tmp_fontsize = $this->FontSizePt;
20268
 
                            while ($write_block AND isset($dom[$nkey])) {
20269
 
                                if ($dom[$nkey]['tag']) {
20270
 
                                    if ($dom[$nkey]['block']) {
20271
 
                                        // end of block
20272
 
                                        $write_block = false;
20273
 
                                    }
20274
 
                                    $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily;
20275
 
                                    $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle;
20276
 
                                    $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt;
20277
 
                                    $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
20278
 
                                } else {
20279
 
                                    $nextstr = preg_split('/'.$this->re_space['p'].'+/'.$this->re_space['m'], $dom[$nkey]['value']);
20280
 
                                    if (isset($nextstr[0])) {
20281
 
                                        if ($same_textdir) {
20282
 
                                            $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
20283
 
                                        }
20284
 
                                        ++$adjblks;
20285
 
                                    }
20286
 
                                    if (isset($nextstr[1])) {
20287
 
                                        $write_block = false;
20288
 
                                    }
20289
 
                                }
20290
 
                                ++$nkey;
20291
 
                            }
20292
 
                        }
20293
 
                        // check for reversed text direction
20294
 
                        if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) {
20295
 
                            // LTR text on RTL direction or RTL text on LTR direction
20296
 
                            $reverse_dir = true;
20297
 
                            $this->rtl = !$this->rtl;
20298
 
                            $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems
20299
 
                            if ($this->rtl) {
20300
 
                                $this->x += $revshift;
20301
 
                            } else {
20302
 
                                $this->x -= $revshift;
20303
 
                            }
20304
 
                            $xws = $this->x;
20305
 
                        }
20306
 
                        // ****** write only until the end of the line and get the rest ******
20307
 
                        $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
20308
 
                        // restore default direction
20309
 
                        if ($reverse_dir AND ($wadj == 0)) {
20310
 
                            $this->x = $xws;
20311
 
                            $this->rtl = !$this->rtl;
20312
 
                            $reverse_dir = false;
20313
 
                        }
20314
 
                    }
20315
 
                }
20316
 
                $this->textindent = 0;
20317
 
                if (strlen($strrest) > 0) {
20318
 
                    // store the remaining string on the previous $key position
20319
 
                    $this->newline = true;
20320
 
                    if ($strrest == $dom[$key]['value']) {
20321
 
                        // used to avoid infinite loop
20322
 
                        ++$loop;
20323
 
                    } else {
20324
 
                        $loop = 0;
20325
 
                    }
20326
 
                    $dom[$key]['value'] = $strrest;
20327
 
                    if ($cell) {
20328
 
                        if ($this->rtl) {
20329
 
                            $this->x -= $this->cell_padding['R'];
20330
 
                        } else {
20331
 
                            $this->x += $this->cell_padding['L'];
20332
 
                        }
20333
 
                    }
20334
 
                    if ($loop < 3) {
20335
 
                        --$key;
20336
 
                    }
20337
 
                } else {
20338
 
                    $loop = 0;
20339
 
                }
20340
 
            }
20341
 
            ++$key;
20342
 
            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')) {
20343
 
                // check if we are on a new page or on a new column
20344
 
                if ((!$undo) AND ($this->y < $this->start_transaction_y)) {
20345
 
                    // we are on a new page or on a new column and the total object height is less than the available vertical space.
20346
 
                    // restore previous object
20347
 
                    $this->rollbackTransaction(true);
20348
 
                    // restore previous values
20349
 
                    foreach ($this_method_vars as $vkey => $vval) {
20350
 
                        $$vkey = $vval;
20351
 
                    }
20352
 
                    // add a page (or trig AcceptPageBreak() for multicolumn mode)
20353
 
                    $pre_y = $this->y;
20354
 
                    if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
20355
 
                        $startliney = $this->y;
20356
 
                    }
20357
 
                    $undo = true; // avoid infinite loop
20358
 
                } else {
20359
 
                    $undo = false;
20360
 
                }
20361
 
            }
20362
 
        } // end for each $key
20363
 
        // align the last line
20364
 
        if (isset($startlinex)) {
20365
 
            $yshift = $minstartliney - $startliney;
20366
 
            if (($yshift > 0) OR ($this->page > $startlinepage)) {
20367
 
                $yshift = 0;
20368
 
            }
20369
 
            $t_x = 0;
20370
 
            // the last line must be shifted to be aligned as requested
20371
 
            $linew = abs($this->endlinex - $startlinex);
20372
 
            if ($this->inxobj) {
20373
 
                // we are inside an XObject template
20374
 
                $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
20375
 
                if (isset($opentagpos)) {
20376
 
                    $midpos = $opentagpos;
20377
 
                } else {
20378
 
                    $midpos = 0;
20379
 
                }
20380
 
                if ($midpos > 0) {
20381
 
                    $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
20382
 
                    $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
20383
 
                } else {
20384
 
                    $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
20385
 
                    $pend = '';
20386
 
                }
20387
 
            } else {
20388
 
                $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
20389
 
                if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
20390
 
                    $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
20391
 
                    $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
20392
 
                } elseif (isset($opentagpos)) {
20393
 
                    $midpos = $opentagpos;
20394
 
                } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
20395
 
                    $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
20396
 
                    $midpos = $this->footerpos[$startlinepage];
20397
 
                } else {
20398
 
                    $midpos = 0;
20399
 
                }
20400
 
                if ($midpos > 0) {
20401
 
                    $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
20402
 
                    $pend = substr($this->getPageBuffer($startlinepage), $midpos);
20403
 
                } else {
20404
 
                    $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
20405
 
                    $pend = '';
20406
 
                }
20407
 
            }
20408
 
            if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
20409
 
                // calculate shifting amount
20410
 
                $tw = $w;
20411
 
                if ($this->lMargin != $prevlMargin) {
20412
 
                    $tw += ($prevlMargin - $this->lMargin);
20413
 
                }
20414
 
                if ($this->rMargin != $prevrMargin) {
20415
 
                    $tw += ($prevrMargin - $this->rMargin);
20416
 
                }
20417
 
                $one_space_width = $this->GetStringWidth(chr(32));
20418
 
                $no = 0; // number of spaces on a line contained on a single block
20419
 
                if ($this->isRTLTextDir()) { // RTL
20420
 
                    // remove left space if exist
20421
 
                    $pos1 = $this->revstrpos($pmid, '[(');
20422
 
                    if ($pos1 > 0) {
20423
 
                        $pos1 = intval($pos1);
20424
 
                        if ($this->isUnicodeFont()) {
20425
 
                            $pos2 = intval($this->revstrpos($pmid, '[('.chr(0).chr(32)));
20426
 
                            $spacelen = 2;
20427
 
                        } else {
20428
 
                            $pos2 = intval($this->revstrpos($pmid, '[('.chr(32)));
20429
 
                            $spacelen = 1;
20430
 
                        }
20431
 
                        if ($pos1 == $pos2) {
20432
 
                            $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
20433
 
                            if (substr($pmid, $pos1, 4) == '[()]') {
20434
 
                                $linew -= $one_space_width;
20435
 
                            } elseif ($pos1 == strpos($pmid, '[(')) {
20436
 
                                $no = 1;
20437
 
                            }
20438
 
                        }
20439
 
                    }
20440
 
                } else { // LTR
20441
 
                    // remove right space if exist
20442
 
                    $pos1 = $this->revstrpos($pmid, ')]');
20443
 
                    if ($pos1 > 0) {
20444
 
                        $pos1 = intval($pos1);
20445
 
                        if ($this->isUnicodeFont()) {
20446
 
                            $pos2 = intval($this->revstrpos($pmid, chr(0).chr(32).')]')) + 2;
20447
 
                            $spacelen = 2;
20448
 
                        } else {
20449
 
                            $pos2 = intval($this->revstrpos($pmid, chr(32).')]')) + 1;
20450
 
                            $spacelen = 1;
20451
 
                        }
20452
 
                        if ($pos1 == $pos2) {
20453
 
                            $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
20454
 
                            $linew -= $one_space_width;
20455
 
                        }
20456
 
                    }
20457
 
                }
20458
 
                $mdiff = ($tw - $linew);
20459
 
                if ($plalign == 'C') {
20460
 
                    if ($this->rtl) {
20461
 
                        $t_x = -($mdiff / 2);
20462
 
                    } else {
20463
 
                        $t_x = ($mdiff / 2);
20464
 
                    }
20465
 
                } elseif ($plalign == 'R') {
20466
 
                    // right alignment on LTR document
20467
 
                    $t_x = $mdiff;
20468
 
                } elseif ($plalign == 'L') {
20469
 
                    // left alignment on RTL document
20470
 
                    $t_x = -$mdiff;
20471
 
                }
20472
 
            } // end if startlinex
20473
 
            if (($t_x != 0) OR ($yshift < 0)) {
20474
 
                // shift the line
20475
 
                $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
20476
 
                $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
20477
 
                $endlinepos = strlen($pstart);
20478
 
                if ($this->inxobj) {
20479
 
                    // we are inside an XObject template
20480
 
                    $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
20481
 
                    foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
20482
 
                        if ($pak >= $pask) {
20483
 
                            $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
20484
 
                            $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
20485
 
                        }
20486
 
                    }
20487
 
                } else {
20488
 
                    $this->setPageBuffer($startlinepage, $pstart.$pend);
20489
 
                    // shift the annotations and links
20490
 
                    if (isset($this->PageAnnots[$this->page])) {
20491
 
                        foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
20492
 
                            if ($pak >= $pask) {
20493
 
                                $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
20494
 
                                $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
20495
 
                            }
20496
 
                        }
20497
 
                    }
20498
 
                }
20499
 
                $this->y -= $yshift;
20500
 
            }
20501
 
        }
20502
 
        // restore previous values
20503
 
        $this->setGraphicVars($gvars);
20504
 
        if ($this->num_columns > 1) {
20505
 
            $this->selectColumn();
20506
 
        } elseif ($this->page > $prevPage) {
20507
 
            $this->lMargin = $this->pagedim[$this->page]['olm'];
20508
 
            $this->rMargin = $this->pagedim[$this->page]['orm'];
20509
 
        }
20510
 
        // restore previous list state
20511
 
        $this->cell_height_ratio = $prev_cell_height_ratio;
20512
 
        $this->listnum = $prev_listnum;
20513
 
        $this->listordered = $prev_listordered;
20514
 
        $this->listcount = $prev_listcount;
20515
 
        $this->lispacer = $prev_lispacer;
20516
 
        if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
20517
 
            $this->Ln($this->lasth);
20518
 
            if ($this->y < $maxbottomliney) {
20519
 
                $this->y = $maxbottomliney;
20520
 
            }
20521
 
        }
20522
 
        unset($dom);
20523
 
    }
20524
 
 
20525
 
    /**
20526
 
     * Process opening tags.
20527
 
     * @param $dom (array) html dom array
20528
 
     * @param $key (int) current element id
20529
 
     * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
20530
 
     * @protected
20531
 
     */
20532
 
    protected function openHTMLTagHandler(&$dom, $key, $cell) {
20533
 
        $tag = $dom[$key];
20534
 
        $parent = $dom[($dom[$key]['parent'])];
20535
 
        $firsttag = ($key == 1);
20536
 
        // check for text direction attribute
20537
 
        if (isset($tag['dir'])) {
20538
 
            $this->setTempRTL($tag['dir']);
20539
 
        } else {
20540
 
            $this->tmprtl = false;
20541
 
        }
20542
 
        if ($tag['block']) {
20543
 
            $hbz = 0; // distance from y to line bottom
20544
 
            $hb = 0; // vertical space between block tags
20545
 
            // calculate vertical space for block tags
20546
 
            if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) {
20547
 
                $cur_h = $this->tagvspaces[$tag['value']][0]['h'];
20548
 
            } elseif (isset($tag['fontsize'])) {
20549
 
                $cur_h = ($tag['fontsize'] / $this->k) * $this->cell_height_ratio;
20550
 
            } else {
20551
 
                $cur_h = $this->FontSize * $this->cell_height_ratio;
20552
 
            }
20553
 
            if (isset($this->tagvspaces[$tag['value']][0]['n'])) {
20554
 
                $n = $this->tagvspaces[$tag['value']][0]['n'];
20555
 
            } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
20556
 
                $n = 0.6;
20557
 
            } else {
20558
 
                $n = 1;
20559
 
            }
20560
 
            $hb = ($n * $cur_h);
20561
 
            if (($this->htmlvspace <= 0) AND ($n > 0)) {
20562
 
                if (isset($parent['fontsize'])) {
20563
 
                    $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
20564
 
                } else {
20565
 
                    $hbz = $this->FontSize * $this->cell_height_ratio;
20566
 
                }
20567
 
            }
20568
 
        }
20569
 
        // Opening tag
20570
 
        switch($tag['value']) {
20571
 
            case 'table': {
20572
 
                $cp = 0;
20573
 
                $cs = 0;
20574
 
                $dom[$key]['rowspans'] = array();
20575
 
                if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
20576
 
                    // set table header
20577
 
                    if (!$this->empty_string($dom[$key]['thead'])) {
20578
 
                        // set table header
20579
 
                        $this->thead = $dom[$key]['thead'];
20580
 
                        if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
20581
 
                            $this->theadMargins = array();
20582
 
                            $this->theadMargins['cell_padding'] = $this->cell_padding;
20583
 
                            $this->theadMargins['lmargin'] = $this->lMargin;
20584
 
                            $this->theadMargins['rmargin'] = $this->rMargin;
20585
 
                            $this->theadMargins['page'] = $this->page;
20586
 
                        }
20587
 
                    }
20588
 
                }
20589
 
                // store current margins and page
20590
 
                $dom[$key]['old_cell_padding'] = $this->cell_padding;
20591
 
                if (isset($tag['attribute']['cellpadding'])) {
20592
 
                    $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
20593
 
                    $this->SetCellPadding($pad);
20594
 
                } elseif (isset($tag['padding'])) {
20595
 
                    $this->cell_padding = $tag['padding'];
20596
 
                }
20597
 
                if (isset($tag['attribute']['cellspacing'])) {
20598
 
                    $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
20599
 
                } elseif (isset($tag['border-spacing'])) {
20600
 
                    $cs = $tag['border-spacing']['V'];
20601
 
                }
20602
 
                $prev_y = $this->y;
20603
 
                if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) {
20604
 
                    $this->inthead = true;
20605
 
                    // add a page (or trig AcceptPageBreak() for multicolumn mode)
20606
 
                    $this->checkPageBreak($this->PageBreakTrigger + 1);
20607
 
                }
20608
 
                break;
20609
 
            }
20610
 
            case 'tr': {
20611
 
                // array of columns positions
20612
 
                $dom[$key]['cellpos'] = array();
20613
 
                break;
20614
 
            }
20615
 
            case 'hr': {
20616
 
                if ((isset($tag['height'])) AND ($tag['height'] != '')) {
20617
 
                    $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
20618
 
                } else {
20619
 
                    $hrHeight = $this->GetLineWidth();
20620
 
                }
20621
 
                $this->addHTMLVertSpace($hbz, ($hrHeight / 2), $cell, $firsttag);
20622
 
                $x = $this->GetX();
20623
 
                $y = $this->GetY();
20624
 
                $wtmp = $this->w - $this->lMargin - $this->rMargin;
20625
 
                if ($cell) {
20626
 
                    $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']);
20627
 
                }
20628
 
                if ((isset($tag['width'])) AND ($tag['width'] != '')) {
20629
 
                    $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
20630
 
                } else {
20631
 
                    $hrWidth = $wtmp;
20632
 
                }
20633
 
                $prevlinewidth = $this->GetLineWidth();
20634
 
                $this->SetLineWidth($hrHeight);
20635
 
                $this->Line($x, $y, $x + $hrWidth, $y);
20636
 
                $this->SetLineWidth($prevlinewidth);
20637
 
                $this->addHTMLVertSpace(($hrHeight / 2), 0, $cell, !isset($dom[($key + 1)]));
20638
 
                break;
20639
 
            }
20640
 
            case 'a': {
20641
 
                if (array_key_exists('href', $tag['attribute'])) {
20642
 
                    $this->HREF['url'] = $tag['attribute']['href'];
20643
 
                }
20644
 
                break;
20645
 
            }
20646
 
            case 'img': {
20647
 
                if (isset($tag['attribute']['src'])) {
20648
 
                    // replace relative path with real server path
20649
 
                    if (($tag['attribute']['src'][0] == '/') AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
20650
 
                        $findroot = strpos($tag['attribute']['src'], $_SERVER['DOCUMENT_ROOT']);
20651
 
                        if (($findroot === false) OR ($findroot > 1)) {
20652
 
                            $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
20653
 
                        }
20654
 
                    }
20655
 
                    $tag['attribute']['src'] = urldecode($tag['attribute']['src']);
20656
 
                    $type = $this->getImageFileType($tag['attribute']['src']);
20657
 
                    $testscrtype = @parse_url($tag['attribute']['src']);
20658
 
                    if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
20659
 
                        // convert URL to server path
20660
 
                        $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
20661
 
                    }
20662
 
                    if (!isset($tag['width'])) {
20663
 
                        $tag['width'] = 0;
20664
 
                    }
20665
 
                    if (!isset($tag['height'])) {
20666
 
                        $tag['height'] = 0;
20667
 
                    }
20668
 
                    //if (!isset($tag['attribute']['align'])) {
20669
 
                        // the only alignment supported is "bottom"
20670
 
                        // further development is required for other modes.
20671
 
                        $tag['attribute']['align'] = 'bottom';
20672
 
                    //}
20673
 
                    switch($tag['attribute']['align']) {
20674
 
                        case 'top': {
20675
 
                            $align = 'T';
20676
 
                            break;
20677
 
                        }
20678
 
                        case 'middle': {
20679
 
                            $align = 'M';
20680
 
                            break;
20681
 
                        }
20682
 
                        case 'bottom': {
20683
 
                            $align = 'B';
20684
 
                            break;
20685
 
                        }
20686
 
                        default: {
20687
 
                            $align = 'B';
20688
 
                            break;
20689
 
                        }
20690
 
                    }
20691
 
                    $prevy = $this->y;
20692
 
                    $xpos = $this->x;
20693
 
                    // eliminate marker spaces
20694
 
                    if (isset($dom[($key - 1)])) {
20695
 
                        if (($dom[($key - 1)]['value'] == ' ') OR (isset($dom[($key - 1)]['trimmed_space']))) {
20696
 
                            $xpos -= $this->GetStringWidth(chr(32));
20697
 
                        } elseif ($this->rtl AND $dom[($key - 1)]['value'] == '  ') {
20698
 
                            $xpos += (2 * $this->GetStringWidth(chr(32)));
20699
 
                        }
20700
 
                    }
20701
 
                    $imglink = '';
20702
 
                    if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) {
20703
 
                        $imglink = $this->HREF['url'];
20704
 
                        if ($imglink{0} == '#') {
20705
 
                            // convert url to internal link
20706
 
                            $lnkdata = explode(',', $imglink);
20707
 
                            if (isset($lnkdata[0])) {
20708
 
                                $page = intval(substr($lnkdata[0], 1));
20709
 
                                if (empty($page) OR ($page <= 0)) {
20710
 
                                    $page = $this->page;
20711
 
                                }
20712
 
                                if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
20713
 
                                    $lnky = floatval($lnkdata[1]);
20714
 
                                } else {
20715
 
                                    $lnky = 0;
20716
 
                                }
20717
 
                                $imglink = $this->AddLink();
20718
 
                                $this->SetLink($imglink, $lnky, $page);
20719
 
                            }
20720
 
                        }
20721
 
                    }
20722
 
                    $border = 0;
20723
 
                    if (isset($tag['border']) AND !empty($tag['border'])) {
20724
 
                        // currently only support 1 (frame) or a combination of 'LTRB'
20725
 
                        $border = $tag['border'];
20726
 
                    }
20727
 
                    $iw = '';
20728
 
                    if (isset($tag['width'])) {
20729
 
                        $iw = $this->getHTMLUnitToUnits($tag['width'], 1, 'px', false);
20730
 
                    }
20731
 
                    $ih = '';
20732
 
                    if (isset($tag['height'])) {
20733
 
                        $ih = $this->getHTMLUnitToUnits($tag['height'], 1, 'px', false);
20734
 
                    }
20735
 
                    if (($type == 'eps') OR ($type == 'ai')) {
20736
 
                        $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true);
20737
 
                    } elseif ($type == 'svg') {
20738
 
                        $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true);
20739
 
                    } else {
20740
 
                        $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
20741
 
                    }
20742
 
                    switch($align) {
20743
 
                        case 'T': {
20744
 
                            $this->y = $prevy;
20745
 
                            break;
20746
 
                        }
20747
 
                        case 'M': {
20748
 
                            $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
20749
 
                            break;
20750
 
                        }
20751
 
                        case 'B': {
20752
 
                            $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
20753
 
                            break;
20754
 
                        }
20755
 
                    }
20756
 
                }
20757
 
                break;
20758
 
            }
20759
 
            case 'dl': {
20760
 
                ++$this->listnum;
20761
 
                if ($this->listnum == 1) {
20762
 
                    $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
20763
 
                } else {
20764
 
                    $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
20765
 
                }
20766
 
                break;
20767
 
            }
20768
 
            case 'dt': {
20769
 
                $this->addHTMLVertSpace($hbz, 0, $cell, $firsttag);
20770
 
                break;
20771
 
            }
20772
 
            case 'dd': {
20773
 
                if ($this->rtl) {
20774
 
                    $this->rMargin += $this->listindent;
20775
 
                } else {
20776
 
                    $this->lMargin += $this->listindent;
20777
 
                }
20778
 
                ++$this->listindentlevel;
20779
 
                $this->addHTMLVertSpace($hbz, 0, $cell, $firsttag);
20780
 
                break;
20781
 
            }
20782
 
            case 'ul':
20783
 
            case 'ol': {
20784
 
                ++$this->listnum;
20785
 
                if ($tag['value'] == 'ol') {
20786
 
                    $this->listordered[$this->listnum] = true;
20787
 
                } else {
20788
 
                    $this->listordered[$this->listnum] = false;
20789
 
                }
20790
 
                if (isset($tag['attribute']['start'])) {
20791
 
                    $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
20792
 
                } else {
20793
 
                    $this->listcount[$this->listnum] = 0;
20794
 
                }
20795
 
                if ($this->rtl) {
20796
 
                    $this->rMargin += $this->listindent;
20797
 
                    $this->x -= $this->listindent;
20798
 
                } else {
20799
 
                    $this->lMargin += $this->listindent;
20800
 
                    $this->x += $this->listindent;
20801
 
                }
20802
 
                ++$this->listindentlevel;
20803
 
                if ($this->listnum == 1) {
20804
 
                    $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
20805
 
                } else {
20806
 
                    $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
20807
 
                }
20808
 
                break;
20809
 
            }
20810
 
            case 'li': {
20811
 
                $this->addHTMLVertSpace($hbz, 0, $cell, $firsttag);
20812
 
                if ($this->listordered[$this->listnum]) {
20813
 
                    // ordered item
20814
 
                    if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
20815
 
                        $this->lispacer = $parent['attribute']['type'];
20816
 
                    } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
20817
 
                        $this->lispacer = $parent['listtype'];
20818
 
                    } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
20819
 
                        $this->lispacer = $this->lisymbol;
20820
 
                    } else {
20821
 
                        $this->lispacer = '#';
20822
 
                    }
20823
 
                    ++$this->listcount[$this->listnum];
20824
 
                    if (isset($tag['attribute']['value'])) {
20825
 
                        $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
20826
 
                    }
20827
 
                } else {
20828
 
                    // unordered item
20829
 
                    if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
20830
 
                        $this->lispacer = $parent['attribute']['type'];
20831
 
                    } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
20832
 
                        $this->lispacer = $parent['listtype'];
20833
 
                    } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
20834
 
                        $this->lispacer = $this->lisymbol;
20835
 
                    } else {
20836
 
                        $this->lispacer = '!';
20837
 
                    }
20838
 
                }
20839
 
                break;
20840
 
            }
20841
 
            case 'blockquote': {
20842
 
                if ($this->rtl) {
20843
 
                    $this->rMargin += $this->listindent;
20844
 
                } else {
20845
 
                    $this->lMargin += $this->listindent;
20846
 
                }
20847
 
                ++$this->listindentlevel;
20848
 
                $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
20849
 
                break;
20850
 
            }
20851
 
            case 'br': {
20852
 
                $this->addHTMLVertSpace($hbz, 0, $cell, $firsttag);
20853
 
                break;
20854
 
            }
20855
 
            case 'div': {
20856
 
                $this->addHTMLVertSpace($hbz, 0, $cell, $firsttag);
20857
 
                break;
20858
 
            }
20859
 
            case 'p': {
20860
 
                $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
20861
 
                break;
20862
 
            }
20863
 
            case 'pre': {
20864
 
                $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
20865
 
                $this->premode = true;
20866
 
                break;
20867
 
            }
20868
 
            case 'sup': {
20869
 
                $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
20870
 
                break;
20871
 
            }
20872
 
            case 'sub': {
20873
 
                $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
20874
 
                break;
20875
 
            }
20876
 
            case 'h1':
20877
 
            case 'h2':
20878
 
            case 'h3':
20879
 
            case 'h4':
20880
 
            case 'h5':
20881
 
            case 'h6': {
20882
 
                $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
20883
 
                break;
20884
 
            }
20885
 
            // Form fields (since 4.8.000 - 2009-09-07)
20886
 
            case 'form': {
20887
 
                if (isset($tag['attribute']['action'])) {
20888
 
                    $this->form_action = $tag['attribute']['action'];
20889
 
                } else {
20890
 
                    $this->form_action = K_PATH_URL.$_SERVER['SCRIPT_NAME'];
20891
 
                }
20892
 
                if (isset($tag['attribute']['enctype'])) {
20893
 
                    $this->form_enctype = $tag['attribute']['enctype'];
20894
 
                } else {
20895
 
                    $this->form_enctype = 'application/x-www-form-urlencoded';
20896
 
                }
20897
 
                if (isset($tag['attribute']['method'])) {
20898
 
                    $this->form_mode = $tag['attribute']['method'];
20899
 
                } else {
20900
 
                    $this->form_mode = 'post';
20901
 
                }
20902
 
                break;
20903
 
            }
20904
 
            case 'input': {
20905
 
                if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
20906
 
                    $name = $tag['attribute']['name'];
20907
 
                } else {
20908
 
                    break;
20909
 
                }
20910
 
                $prop = array();
20911
 
                $opt = array();
20912
 
                if (isset($tag['attribute']['readonly']) AND !$this->empty_string($tag['attribute']['readonly'])) {
20913
 
                    $prop['readonly'] = true;
20914
 
                }
20915
 
                if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) {
20916
 
                    $value = $tag['attribute']['value'];
20917
 
                }
20918
 
                if (isset($tag['attribute']['maxlength']) AND !$this->empty_string($tag['attribute']['maxlength'])) {
20919
 
                    $opt['maxlen'] = intval($tag['attribute']['value']);
20920
 
                }
20921
 
                $h = $this->FontSize * $this->cell_height_ratio;
20922
 
                if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) {
20923
 
                    $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
20924
 
                } else {
20925
 
                    $w = $h;
20926
 
                }
20927
 
                if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
20928
 
                    $checked = true;
20929
 
                } else {
20930
 
                    $checked = false;
20931
 
                }
20932
 
                switch ($tag['attribute']['type']) {
20933
 
                    case 'text': {
20934
 
                        if (isset($value)) {
20935
 
                            $opt['v'] = $value;
20936
 
                        }
20937
 
                        $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
20938
 
                        break;
20939
 
                    }
20940
 
                    case 'password': {
20941
 
                        if (isset($value)) {
20942
 
                            $opt['v'] = $value;
20943
 
                        }
20944
 
                        $prop['password'] = 'true';
20945
 
                        $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
20946
 
                        break;
20947
 
                    }
20948
 
                    case 'checkbox': {
20949
 
                        $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
20950
 
                        break;
20951
 
                    }
20952
 
                    case 'radio': {
20953
 
                        $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
20954
 
                        break;
20955
 
                    }
20956
 
                    case 'submit': {
20957
 
                        $w = $this->GetStringWidth($value) * 1.5;
20958
 
                        $h *= 1.6;
20959
 
                        $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
20960
 
                        $action = array();
20961
 
                        $action['S'] = 'SubmitForm';
20962
 
                        $action['F'] = $this->form_action;
20963
 
                        if ($this->form_enctype != 'FDF') {
20964
 
                            $action['Flags'] = array('ExportFormat');
20965
 
                        }
20966
 
                        if ($this->form_mode == 'get') {
20967
 
                            $action['Flags'] = array('GetMethod');
20968
 
                        }
20969
 
                        $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
20970
 
                        break;
20971
 
                    }
20972
 
                    case 'reset': {
20973
 
                        $w = $this->GetStringWidth($value) * 1.5;
20974
 
                        $h *= 1.6;
20975
 
                        $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
20976
 
                        $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
20977
 
                        break;
20978
 
                    }
20979
 
                    case 'file': {
20980
 
                        $prop['fileSelect'] = 'true';
20981
 
                        $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
20982
 
                        if (!isset($value)) {
20983
 
                            $value = '*';
20984
 
                        }
20985
 
                        $w = $this->GetStringWidth($value) * 2;
20986
 
                        $h *= 1.2;
20987
 
                        $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
20988
 
                        $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
20989
 
                        $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
20990
 
                        break;
20991
 
                    }
20992
 
                    case 'hidden': {
20993
 
                        if (isset($value)) {
20994
 
                            $opt['v'] = $value;
20995
 
                        }
20996
 
                        $opt['f'] = array('invisible', 'hidden');
20997
 
                        $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
20998
 
                        break;
20999
 
                    }
21000
 
                    case 'image': {
21001
 
                        // THIS TYPE MUST BE FIXED
21002
 
                        if (isset($tag['attribute']['src']) AND !$this->empty_string($tag['attribute']['src'])) {
21003
 
                            $img = $tag['attribute']['src'];
21004
 
                        } else {
21005
 
                            break;
21006
 
                        }
21007
 
                        $value = 'img';
21008
 
                        //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
21009
 
                        if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
21010
 
                            $jsaction = $tag['attribute']['onclick'];
21011
 
                        } else {
21012
 
                            $jsaction = '';
21013
 
                        }
21014
 
                        $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
21015
 
                        break;
21016
 
                    }
21017
 
                    case 'button': {
21018
 
                        $w = $this->GetStringWidth($value) * 1.5;
21019
 
                        $h *= 1.6;
21020
 
                        $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
21021
 
                        if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
21022
 
                            $jsaction = $tag['attribute']['onclick'];
21023
 
                        } else {
21024
 
                            $jsaction = '';
21025
 
                        }
21026
 
                        $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
21027
 
                        break;
21028
 
                    }
21029
 
                }
21030
 
                break;
21031
 
            }
21032
 
            case 'textarea': {
21033
 
                $prop = array();
21034
 
                $opt = array();
21035
 
                if (isset($tag['attribute']['readonly']) AND !$this->empty_string($tag['attribute']['readonly'])) {
21036
 
                    $prop['readonly'] = true;
21037
 
                }
21038
 
                if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
21039
 
                    $name = $tag['attribute']['name'];
21040
 
                } else {
21041
 
                    break;
21042
 
                }
21043
 
                if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) {
21044
 
                    $opt['v'] = $tag['attribute']['value'];
21045
 
                }
21046
 
                if (isset($tag['attribute']['cols']) AND !$this->empty_string($tag['attribute']['cols'])) {
21047
 
                    $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
21048
 
                } else {
21049
 
                    $w = 40;
21050
 
                }
21051
 
                if (isset($tag['attribute']['rows']) AND !$this->empty_string($tag['attribute']['rows'])) {
21052
 
                    $h = intval($tag['attribute']['rows']) * $this->FontSize * $this->cell_height_ratio;
21053
 
                } else {
21054
 
                    $h = 10;
21055
 
                }
21056
 
                $prop['multiline'] = 'true';
21057
 
                $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
21058
 
                break;
21059
 
            }
21060
 
            case 'select': {
21061
 
                $h = $this->FontSize * $this->cell_height_ratio;
21062
 
                if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) {
21063
 
                    $h *= ($tag['attribute']['size'] + 1);
21064
 
                }
21065
 
                $prop = array();
21066
 
                $opt = array();
21067
 
                if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
21068
 
                    $name = $tag['attribute']['name'];
21069
 
                } else {
21070
 
                    break;
21071
 
                }
21072
 
                $w = 0;
21073
 
                if (isset($tag['attribute']['opt']) AND !$this->empty_string($tag['attribute']['opt'])) {
21074
 
                    $options = explode('#!NwL!#', $tag['attribute']['opt']);
21075
 
                    $values = array();
21076
 
                    foreach ($options as $val) {
21077
 
                        if (strpos($val, '#!TaB!#') !== false) {
21078
 
                            $opts = explode('#!TaB!#', $val);
21079
 
                            $values[] = $opts;
21080
 
                            $w = max($w, $this->GetStringWidth($opts[1]));
21081
 
                        } else {
21082
 
                            $values[] = $val;
21083
 
                            $w = max($w, $this->GetStringWidth($val));
21084
 
                        }
21085
 
                    }
21086
 
                } else {
21087
 
                    break;
21088
 
                }
21089
 
                $w *= 2;
21090
 
                if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
21091
 
                    $prop['multipleSelection'] = 'true';
21092
 
                    $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
21093
 
                } else {
21094
 
                    $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
21095
 
                }
21096
 
                break;
21097
 
            }
21098
 
            case 'tcpdf': {
21099
 
                if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
21100
 
                    // Special tag used to call TCPDF methods
21101
 
                    if (isset($tag['attribute']['method'])) {
21102
 
                        $tcpdf_method = $tag['attribute']['method'];
21103
 
                        if (method_exists($this, $tcpdf_method)) {
21104
 
                            if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
21105
 
                                $params = unserialize(urldecode($tag['attribute']['params']));
21106
 
                                call_user_func_array(array($this, $tcpdf_method), $params);
21107
 
                            } else {
21108
 
                                $this->$tcpdf_method();
21109
 
                            }
21110
 
                            $this->newline = true;
21111
 
                        }
21112
 
                    }
21113
 
                }
21114
 
                break;
21115
 
            }
21116
 
            default: {
21117
 
                break;
21118
 
            }
21119
 
        }
21120
 
        // define tags that support borders and background colors
21121
 
        $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
21122
 
        if (in_array($tag['value'], $bordertags)) {
21123
 
            // set border
21124
 
            $dom[$key]['borderposition'] = $this->getBorderStartPosition();
21125
 
        }
21126
 
        if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
21127
 
            $pba = $dom[$key]['attribute']['pagebreakafter'];
21128
 
            // check for pagebreak
21129
 
            if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
21130
 
                // add a page (or trig AcceptPageBreak() for multicolumn mode)
21131
 
                $this->checkPageBreak($this->PageBreakTrigger + 1);
21132
 
            }
21133
 
            if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
21134
 
                OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
21135
 
                // add a page (or trig AcceptPageBreak() for multicolumn mode)
21136
 
                $this->checkPageBreak($this->PageBreakTrigger + 1);
21137
 
            }
21138
 
        }
21139
 
    }
21140
 
 
21141
 
    /**
21142
 
     * Process closing tags.
21143
 
     * @param $dom (array) html dom array
21144
 
     * @param $key (int) current element id
21145
 
     * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
21146
 
     * @param $maxbottomliney (int) maximum y value of current line
21147
 
     * @protected
21148
 
     */
21149
 
    protected function closeHTMLTagHandler(&$dom, $key, $cell, $maxbottomliney=0) {
21150
 
        $tag = $dom[$key];
21151
 
        $parent = $dom[($dom[$key]['parent'])];
21152
 
        $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
21153
 
        $in_table_head = false;
21154
 
        // maximum x position (used to draw borders)
21155
 
        if ($this->rtl) {
21156
 
            $xmax = $this->w;
21157
 
        } else {
21158
 
            $xmax = 0;
21159
 
        }
21160
 
        if ($tag['block']) {
21161
 
            $hbz = 0; // distance from y to line bottom
21162
 
            $hb = 0; // vertical space between block tags
21163
 
            // calculate vertical space for block tags
21164
 
            if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
21165
 
                $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
21166
 
            } elseif (isset($parent['fontsize'])) {
21167
 
                $pre_h = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
21168
 
            } else {
21169
 
                $pre_h = $this->FontSize * $this->cell_height_ratio;
21170
 
            }
21171
 
            if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
21172
 
                $n = $this->tagvspaces[$tag['value']][1]['n'];
21173
 
            } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
21174
 
                $n = 0.6;
21175
 
            } else {
21176
 
                $n = 1;
21177
 
            }
21178
 
            $hb = ($n * $pre_h);
21179
 
            if ($this->y < $maxbottomliney) {
21180
 
                $hbz = ($maxbottomliney - $this->y);
21181
 
            }
21182
 
        }
21183
 
        // Closing tag
21184
 
        switch($tag['value']) {
21185
 
            case 'tr': {
21186
 
                $table_el = $dom[($dom[$key]['parent'])]['parent'];
21187
 
                if (!isset($parent['endy'])) {
21188
 
                    $dom[($dom[$key]['parent'])]['endy'] = $this->y;
21189
 
                    $parent['endy'] = $this->y;
21190
 
                }
21191
 
                if (!isset($parent['endpage'])) {
21192
 
                    $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
21193
 
                    $parent['endpage'] = $this->page;
21194
 
                }
21195
 
                if (!isset($parent['endcolumn'])) {
21196
 
                    $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column;
21197
 
                    $parent['endcolumn'] = $this->current_column;
21198
 
                }
21199
 
                // update row-spanned cells
21200
 
                if (isset($dom[$table_el]['rowspans'])) {
21201
 
                    foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
21202
 
                        $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
21203
 
                        if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
21204
 
                            if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
21205
 
                                $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
21206
 
                            } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
21207
 
                                $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
21208
 
                                $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
21209
 
                                $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
21210
 
                            }
21211
 
                        }
21212
 
                    }
21213
 
                    // report new endy and endpage to the rowspanned cells
21214
 
                    foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
21215
 
                        if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
21216
 
                            $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
21217
 
                            $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
21218
 
                            $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
21219
 
                            $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
21220
 
                            $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
21221
 
                            $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
21222
 
                        }
21223
 
                    }
21224
 
                    // update remaining rowspanned cells
21225
 
                    foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
21226
 
                        if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
21227
 
                            $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
21228
 
                            $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
21229
 
                            $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
21230
 
                        }
21231
 
                    }
21232
 
                }
21233
 
                $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
21234
 
                if ($this->num_columns > 1) {
21235
 
                    $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
21236
 
                }
21237
 
                $this->y = $dom[($dom[$key]['parent'])]['endy'];
21238
 
                if (isset($dom[$table_el]['attribute']['cellspacing'])) {
21239
 
                    $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
21240
 
                } elseif (isset($dom[$table_el]['border-spacing'])) {
21241
 
                    $this->y += $dom[$table_el]['border-spacing']['V'];
21242
 
                }
21243
 
                $this->Ln(0, $cell);
21244
 
                if ($this->current_column == $parent['startcolumn']) {
21245
 
                    $this->x = $parent['startx'];
21246
 
                }
21247
 
                // account for booklet mode
21248
 
                if ($this->page > $parent['startpage']) {
21249
 
                    if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
21250
 
                        $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
21251
 
                    } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
21252
 
                        $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
21253
 
                    }
21254
 
                }
21255
 
                break;
21256
 
            }
21257
 
            case 'tablehead':
21258
 
                // closing tag used for the thead part
21259
 
                $in_table_head = true;
21260
 
                $this->inthead = false;
21261
 
            case 'table': {
21262
 
                $table_el = $parent;
21263
 
                // set default border
21264
 
                if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
21265
 
                    // set default border
21266
 
                    $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
21267
 
                } else {
21268
 
                    $border = 0;
21269
 
                }
21270
 
                $default_border = $border;
21271
 
                // fix bottom line alignment of last line before page break
21272
 
                foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
21273
 
                    // update row-spanned cells
21274
 
                    if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
21275
 
                        foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
21276
 
                            if ($trwsp['trid'] == $trkey) {
21277
 
                                $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
21278
 
                            }
21279
 
                            if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0)) {
21280
 
                                $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
21281
 
                            }
21282
 
                        }
21283
 
                    }
21284
 
                    if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
21285
 
                        $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
21286
 
                        $dom[$prevtrkey]['endy'] = $pgendy;
21287
 
                        // update row-spanned cells
21288
 
                        if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
21289
 
                            foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
21290
 
                                if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] > 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
21291
 
                                    $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
21292
 
                                    $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
21293
 
                                }
21294
 
                            }
21295
 
                        }
21296
 
                    }
21297
 
                    $prevtrkey = $trkey;
21298
 
                    $table_el = $dom[($dom[$key]['parent'])];
21299
 
                }
21300
 
                // for each row
21301
 
                unset($xmax);
21302
 
                foreach ($table_el['trids'] as $j => $trkey) {
21303
 
                    $parent = $dom[$trkey];
21304
 
                    if (!isset($xmax)) {
21305
 
                        $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
21306
 
                    }
21307
 
                    // for each cell on the row
21308
 
                    foreach ($parent['cellpos'] as $k => $cellpos) {
21309
 
                        if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
21310
 
                            $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
21311
 
                            $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
21312
 
                            $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
21313
 
                            $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
21314
 
                            $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
21315
 
                            $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
21316
 
                            $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
21317
 
                        } else {
21318
 
                            $endy = $parent['endy'];
21319
 
                            $startpage = $parent['startpage'];
21320
 
                            $endpage = $parent['endpage'];
21321
 
                            $startcolumn = $parent['startcolumn'];
21322
 
                            $endcolumn = $parent['endcolumn'];
21323
 
                        }
21324
 
                        if ($this->num_columns == 0) {
21325
 
                            $this->num_columns = 1;
21326
 
                        }
21327
 
                        if (isset($cellpos['border'])) {
21328
 
                            $border = $cellpos['border'];
21329
 
                        }
21330
 
                        if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
21331
 
                            $this->SetFillColorArray($cellpos['bgcolor']);
21332
 
                            $fill = true;
21333
 
                        } else {
21334
 
                            $fill = false;
21335
 
                        }
21336
 
                        $x = $cellpos['startx'];
21337
 
                        $y = $parent['starty'];
21338
 
                        $starty = $y;
21339
 
                        $w = abs($cellpos['endx'] - $cellpos['startx']);
21340
 
                        // get border modes
21341
 
                        $border_start = $this->getBorderMode($border, $position='start');
21342
 
                        $border_end = $this->getBorderMode($border, $position='end');
21343
 
                        $border_middle = $this->getBorderMode($border, $position='middle');
21344
 
                        // design borders around HTML cells.
21345
 
                        for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
21346
 
                            $ccode = '';
21347
 
                            $this->setPage($page);
21348
 
                            if ($this->num_columns < 2) {
21349
 
                                // single-column mode
21350
 
                                $this->x = $x;
21351
 
                                $this->y = $this->tMargin;
21352
 
                            }
21353
 
                            // account for margin changes
21354
 
                            if ($page > $startpage) {
21355
 
                                if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
21356
 
                                    $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
21357
 
                                } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
21358
 
                                    $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
21359
 
                                }
21360
 
                            }
21361
 
                            if ($startpage == $endpage) { // single page
21362
 
                                $deltacol = 0;
21363
 
                                $deltath = 0;
21364
 
                                for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
21365
 
                                    $this->selectColumn($column);
21366
 
                                    if ($startcolumn == $endcolumn) { // single column
21367
 
                                        $cborder = $border;
21368
 
                                        $h = $endy - $parent['starty'];
21369
 
                                        $this->y = $y;
21370
 
                                        $this->x = $x;
21371
 
                                    } elseif ($column == $startcolumn) { // first column
21372
 
                                        $cborder = $border_start;
21373
 
                                        $this->y = $starty;
21374
 
                                        $this->x = $x;
21375
 
                                        $h = $this->h - $this->y - $this->bMargin;
21376
 
                                        if ($this->rtl) {
21377
 
                                            $deltacol = $this->x + $this->rMargin - $this->w;
21378
 
                                        } else {
21379
 
                                            $deltacol = $this->x - $this->lMargin;
21380
 
                                        }
21381
 
                                    } elseif ($column == $endcolumn) { // end column
21382
 
                                        $cborder = $border_end;
21383
 
                                        if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
21384
 
                                            $this->y = $this->columns[$column]['th']['\''.$page.'\''];
21385
 
                                        }
21386
 
                                        $this->x += $deltacol;
21387
 
                                        $h = $endy - $this->y;
21388
 
                                    } else { // middle column
21389
 
                                        $cborder = $border_middle;
21390
 
                                        if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
21391
 
                                            $this->y = $this->columns[$column]['th']['\''.$page.'\''];
21392
 
                                        }
21393
 
                                        $this->x += $deltacol;
21394
 
                                        $h = $this->h - $this->y - $this->bMargin;
21395
 
                                    }
21396
 
                                    $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
21397
 
                                } // end for each column
21398
 
                            } elseif ($page == $startpage) { // first page
21399
 
                                $deltacol = 0;
21400
 
                                $deltath = 0;
21401
 
                                for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
21402
 
                                    $this->selectColumn($column);
21403
 
                                    if ($column == $startcolumn) { // first column
21404
 
                                        $cborder = $border_start;
21405
 
                                        $this->y = $starty;
21406
 
                                        $this->x = $x;
21407
 
                                        $h = $this->h - $this->y - $this->bMargin;
21408
 
                                        if ($this->rtl) {
21409
 
                                            $deltacol = $this->x + $this->rMargin - $this->w;
21410
 
                                        } else {
21411
 
                                            $deltacol = $this->x - $this->lMargin;
21412
 
                                        }
21413
 
                                    } else { // middle column
21414
 
                                        $cborder = $border_middle;
21415
 
                                        if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
21416
 
                                            $this->y = $this->columns[$column]['th']['\''.$page.'\''];
21417
 
                                        }
21418
 
                                        $this->x += $deltacol;
21419
 
                                        $h = $this->h - $this->y - $this->bMargin;
21420
 
                                    }
21421
 
                                    $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
21422
 
                                } // end for each column
21423
 
                            } elseif ($page == $endpage) { // last page
21424
 
                                $deltacol = 0;
21425
 
                                $deltath = 0;
21426
 
                                for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
21427
 
                                    $this->selectColumn($column);
21428
 
                                    if ($column == $endcolumn) { // end column
21429
 
                                        $cborder = $border_end;
21430
 
                                        if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
21431
 
                                            $this->y = $this->columns[$column]['th']['\''.$page.'\''];
21432
 
                                        }
21433
 
                                        $this->x += $deltacol;
21434
 
                                        $h = $endy - $this->y;
21435
 
                                    } else { // middle column
21436
 
                                        $cborder = $border_middle;
21437
 
                                        if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
21438
 
                                            $this->y = $this->columns[$column]['th']['\''.$page.'\''];
21439
 
                                        }
21440
 
                                        $this->x += $deltacol;
21441
 
                                        $h = $this->h - $this->y - $this->bMargin;
21442
 
                                    }
21443
 
                                    $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
21444
 
                                } // end for each column
21445
 
                            } else { // middle page
21446
 
                                $deltacol = 0;
21447
 
                                $deltath = 0;
21448
 
                                for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
21449
 
                                    $this->selectColumn($column);
21450
 
                                    $cborder = $border_middle;
21451
 
                                    if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
21452
 
                                        $this->y = $this->columns[$column]['th']['\''.$page.'\''];
21453
 
                                    }
21454
 
                                    $this->x += $deltacol;
21455
 
                                    $h = $this->h - $this->y - $this->bMargin;
21456
 
                                    $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
21457
 
                                } // end for each column
21458
 
                            }
21459
 
                            if ($cborder OR $fill) {
21460
 
                                // draw border and fill
21461
 
                                if ($this->inxobj) {
21462
 
                                    // we are inside an XObject template
21463
 
                                    if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
21464
 
                                        $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
21465
 
                                        $pagemark = &$this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
21466
 
                                    } else {
21467
 
                                        $pagemark = &$this->xobjects[$this->xobjid]['intmrk'];
21468
 
                                    }
21469
 
                                    $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
21470
 
                                    $pstart = substr($pagebuff, 0, $pagemark);
21471
 
                                    $pend = substr($pagebuff, $pagemark);
21472
 
                                    $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
21473
 
                                    $pagemark += strlen($ccode);
21474
 
                                } else {
21475
 
                                    // draw border and fill
21476
 
                                    if (end($this->transfmrk[$this->page]) !== false) {
21477
 
                                        $pagemarkkey = key($this->transfmrk[$this->page]);
21478
 
                                        $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
21479
 
                                    } elseif ($this->InFooter) {
21480
 
                                        $pagemark = &$this->footerpos[$this->page];
21481
 
                                    } else {
21482
 
                                        $pagemark = &$this->intmrk[$this->page];
21483
 
                                    }
21484
 
                                    $pagebuff = $this->getPageBuffer($this->page);
21485
 
                                    $pstart = substr($pagebuff, 0, $pagemark);
21486
 
                                    $pend = substr($pagebuff, $pagemark);
21487
 
                                    $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
21488
 
                                    $pagemark += strlen($ccode);
21489
 
                                }
21490
 
                            }
21491
 
                        } // end for each page
21492
 
                        // restore default border
21493
 
                        $border = $default_border;
21494
 
                    } // end for each cell on the row
21495
 
                    if (isset($table_el['attribute']['cellspacing'])) {
21496
 
                        $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
21497
 
                    } elseif (isset($table_el['border-spacing'])) {
21498
 
                        $this->y += $table_el['border-spacing']['V'];
21499
 
                    }
21500
 
                    $this->Ln(0, $cell);
21501
 
                    $this->x = $parent['startx'];
21502
 
                    if ($endpage > $startpage) {
21503
 
                        if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
21504
 
                            $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
21505
 
                        } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
21506
 
                            $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
21507
 
                        }
21508
 
                    }
21509
 
                }
21510
 
                if (!$in_table_head) { // we are not inside a thead section
21511
 
                    $this->cell_padding = $table_el['old_cell_padding'];
21512
 
                    // reset row height
21513
 
                    $this->resetLastH();
21514
 
                    if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages]) AND ($this->emptypagemrk[$this->numpages] == $this->pagelen[$this->numpages])) {
21515
 
                        // remove last blank page
21516
 
                        $this->deletePage($this->numpages);
21517
 
                    }
21518
 
                    if (isset($this->theadMargins['top'])) {
21519
 
                        // restore top margin
21520
 
                        $this->tMargin = $this->theadMargins['top'];
21521
 
                        $this->pagedim[$this->page]['tm'] = $this->tMargin;
21522
 
                    }
21523
 
                    if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
21524
 
                        // reset main table header
21525
 
                        $this->thead = '';
21526
 
                        $this->theadMargins = array();
21527
 
                    }
21528
 
                }
21529
 
                $parent = $table_el;
21530
 
                break;
21531
 
            }
21532
 
            case 'a': {
21533
 
                $this->HREF = '';
21534
 
                break;
21535
 
            }
21536
 
            case 'sup': {
21537
 
                $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
21538
 
                break;
21539
 
            }
21540
 
            case 'sub': {
21541
 
                $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k));
21542
 
                break;
21543
 
            }
21544
 
            case 'div': {
21545
 
                $this->addHTMLVertSpace($hbz, 0, $cell, false, $lasttag);
21546
 
                break;
21547
 
            }
21548
 
            case 'blockquote': {
21549
 
                if ($this->rtl) {
21550
 
                    $this->rMargin -= $this->listindent;
21551
 
                } else {
21552
 
                    $this->lMargin -= $this->listindent;
21553
 
                }
21554
 
                --$this->listindentlevel;
21555
 
                $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
21556
 
                break;
21557
 
            }
21558
 
            case 'p': {
21559
 
                $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
21560
 
                break;
21561
 
            }
21562
 
            case 'pre': {
21563
 
                $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
21564
 
                $this->premode = false;
21565
 
                break;
21566
 
            }
21567
 
            case 'dl': {
21568
 
                --$this->listnum;
21569
 
                if ($this->listnum <= 0) {
21570
 
                    $this->listnum = 0;
21571
 
                    $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
21572
 
                } else {
21573
 
                    $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
21574
 
                }
21575
 
                $this->resetLastH();
21576
 
                break;
21577
 
            }
21578
 
            case 'dt': {
21579
 
                $this->lispacer = '';
21580
 
                $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
21581
 
                break;
21582
 
            }
21583
 
            case 'dd': {
21584
 
                $this->lispacer = '';
21585
 
                if ($this->rtl) {
21586
 
                    $this->rMargin -= $this->listindent;
21587
 
                } else {
21588
 
                    $this->lMargin -= $this->listindent;
21589
 
                }
21590
 
                --$this->listindentlevel;
21591
 
                $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
21592
 
                break;
21593
 
            }
21594
 
            case 'ul':
21595
 
            case 'ol': {
21596
 
                --$this->listnum;
21597
 
                $this->lispacer = '';
21598
 
                if ($this->rtl) {
21599
 
                    $this->rMargin -= $this->listindent;
21600
 
                } else {
21601
 
                    $this->lMargin -= $this->listindent;
21602
 
                }
21603
 
                --$this->listindentlevel;
21604
 
                if ($this->listnum <= 0) {
21605
 
                    $this->listnum = 0;
21606
 
                    $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
21607
 
                } else {
21608
 
                    $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
21609
 
                }
21610
 
                $this->resetLastH();
21611
 
                break;
21612
 
            }
21613
 
            case 'li': {
21614
 
                $this->lispacer = '';
21615
 
                $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
21616
 
                break;
21617
 
            }
21618
 
            case 'h1':
21619
 
            case 'h2':
21620
 
            case 'h3':
21621
 
            case 'h4':
21622
 
            case 'h5':
21623
 
            case 'h6': {
21624
 
                $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
21625
 
                break;
21626
 
            }
21627
 
            // Form fields (since 4.8.000 - 2009-09-07)
21628
 
            case 'form': {
21629
 
                $this->form_action = '';
21630
 
                $this->form_enctype = 'application/x-www-form-urlencoded';
21631
 
                break;
21632
 
            }
21633
 
            default : {
21634
 
                break;
21635
 
            }
21636
 
        }
21637
 
        // draw border and background (if any)
21638
 
        $this->drawHTMLTagBorder($parent, $xmax);
21639
 
        if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
21640
 
            $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
21641
 
            // check for pagebreak
21642
 
            if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
21643
 
                // add a page (or trig AcceptPageBreak() for multicolumn mode)
21644
 
                $this->checkPageBreak($this->PageBreakTrigger + 1);
21645
 
            }
21646
 
            if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
21647
 
                OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
21648
 
                // add a page (or trig AcceptPageBreak() for multicolumn mode)
21649
 
                $this->checkPageBreak($this->PageBreakTrigger + 1);
21650
 
            }
21651
 
        }
21652
 
        $this->tmprtl = false;
21653
 
    }
21654
 
 
21655
 
    /**
21656
 
     * Add vertical spaces if needed.
21657
 
     * @param $hbz (string) Distance between current y and line bottom.
21658
 
     * @param $hb (string) The height of the break.
21659
 
     * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
21660
 
     * @param $firsttag (boolean) set to true when the tag is the first.
21661
 
     * @param $lasttag (boolean) set to true when the tag is the last.
21662
 
     * @protected
21663
 
     */
21664
 
    protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
21665
 
        if ($firsttag) {
21666
 
            $this->Ln(0, $cell);
21667
 
            $this->htmlvspace = 0;
21668
 
            return;
21669
 
        }
21670
 
        if ($lasttag) {
21671
 
            $this->Ln($hbz, $cell);
21672
 
            $this->htmlvspace = 0;
21673
 
            return;
21674
 
        }
21675
 
        if ($hb < $this->htmlvspace) {
21676
 
            $hd = 0;
21677
 
        } else {
21678
 
            $hd = $hb - $this->htmlvspace;
21679
 
            $this->htmlvspace = $hb;
21680
 
        }
21681
 
        $this->Ln(($hbz + $hd), $cell);
21682
 
    }
21683
 
 
21684
 
    /**
21685
 
     * Return the starting coordinates to draw an html border
21686
 
     * @return array containing top-left border coordinates
21687
 
     * @protected
21688
 
     * @since 5.7.000 (2010-08-03)
21689
 
     */
21690
 
    protected function getBorderStartPosition() {
21691
 
        if ($this->rtl) {
21692
 
            $xmax = $this->lMargin;
21693
 
        } else {
21694
 
            $xmax = $this->w - $this->rMargin;
21695
 
        }
21696
 
        return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax);
21697
 
    }
21698
 
 
21699
 
    /**
21700
 
     * Draw an HTML block border and fill
21701
 
     * @param $tag (array) array of tag properties.
21702
 
     * @param $xmax (int) end X coordinate for border.
21703
 
     * @protected
21704
 
     * @since 5.7.000 (2010-08-03)
21705
 
     */
21706
 
    protected function drawHTMLTagBorder($tag, $xmax) {
21707
 
        if (!isset($tag['borderposition'])) {
21708
 
            // nothing to draw
21709
 
            return;
21710
 
        }
21711
 
        $prev_x = $this->x;
21712
 
        $prev_y = $this->y;
21713
 
        $prev_lasth = $this->lasth;
21714
 
        $border = 0;
21715
 
        $fill = false;
21716
 
        $this->lasth = 0;
21717
 
        if (isset($tag['border']) AND !empty($tag['border'])) {
21718
 
            // get border style
21719
 
            $border = $tag['border'];
21720
 
            if (!$this->empty_string($this->thead) AND (!$this->inthead)) {
21721
 
                // border for table header
21722
 
                $border = $this->getBorderMode($border, $position='middle');
21723
 
            }
21724
 
        }
21725
 
        if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
21726
 
            // get background color
21727
 
            $old_bgcolor = $this->bgcolor;
21728
 
            $this->SetFillColorArray($tag['bgcolor']);
21729
 
            $fill = true;
21730
 
        }
21731
 
        if (!$border AND !$fill) {
21732
 
            // nothing to draw
21733
 
            return;
21734
 
        }
21735
 
        if (isset($tag['attribute']['cellspacing'])) {
21736
 
            $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
21737
 
            $cellspacing = array('H' => $clsp, 'V' => $clsp);
21738
 
        } elseif (isset($tag['border-spacing'])) {
21739
 
            $cellspacing = $tag['border-spacing'];
21740
 
        } else {
21741
 
            $cellspacing = array('H' => 0, 'V' => 0);
21742
 
        }
21743
 
        if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
21744
 
            // draw the border externally respect the sqare edge.
21745
 
            $border['mode'] = 'ext';
21746
 
        }
21747
 
        if ($this->rtl) {
21748
 
            if ($xmax >= $tag['borderposition']['x']) {
21749
 
                $xmax = $tag['borderposition']['xmax'];
21750
 
            }
21751
 
            $w = ($tag['borderposition']['x'] - $xmax);
21752
 
        } else {
21753
 
            if ($xmax <= $tag['borderposition']['x']) {
21754
 
                $xmax = $tag['borderposition']['xmax'];
21755
 
            }
21756
 
            $w = ($xmax - $tag['borderposition']['x']);
21757
 
        }
21758
 
        if ($w <= 0) {
21759
 
            return;
21760
 
        }
21761
 
        $w += $cellspacing['H'];
21762
 
        $startpage = $tag['borderposition']['page'];
21763
 
        $startcolumn = $tag['borderposition']['column'];
21764
 
        $x = $tag['borderposition']['x'];
21765
 
        $y = $tag['borderposition']['y'];
21766
 
        $endpage = $this->page;
21767
 
        $starty = $tag['borderposition']['y'] - $cellspacing['V'];
21768
 
        $currentY = $this->y;
21769
 
        $this->x = $x;
21770
 
        // get latest column
21771
 
        $endcolumn = $this->current_column;
21772
 
        if ($this->num_columns == 0) {
21773
 
            $this->num_columns = 1;
21774
 
        }
21775
 
        // get border modes
21776
 
        $border_start = $this->getBorderMode($border, $position='start');
21777
 
        $border_end = $this->getBorderMode($border, $position='end');
21778
 
        $border_middle = $this->getBorderMode($border, $position='middle');
21779
 
        // temporary disable page regions
21780
 
        $temp_page_regions = $this->page_regions;
21781
 
        $this->page_regions = array();
21782
 
        // design borders around HTML cells.
21783
 
        for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
21784
 
            $ccode = '';
21785
 
            $this->setPage($page);
21786
 
            if ($this->num_columns < 2) {
21787
 
                // single-column mode
21788
 
                $this->x = $x;
21789
 
                $this->y = $this->tMargin;
21790
 
            }
21791
 
            // account for margin changes
21792
 
            if ($page > $startpage) {
21793
 
                if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
21794
 
                    $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
21795
 
                } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
21796
 
                    $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
21797
 
                }
21798
 
            }
21799
 
            if ($startpage == $endpage) {
21800
 
                // single page
21801
 
                for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
21802
 
                    $this->selectColumn($column);
21803
 
                    if ($startcolumn == $endcolumn) { // single column
21804
 
                        $cborder = $border;
21805
 
                        $h = ($currentY - $y) + $cellspacing['V'];
21806
 
                        $this->y = $starty;
21807
 
                    } elseif ($column == $startcolumn) { // first column
21808
 
                        $cborder = $border_start;
21809
 
                        $this->y = $starty;
21810
 
                        $h = $this->h - $this->y - $this->bMargin;
21811
 
                    } elseif ($column == $endcolumn) { // end column
21812
 
                        $cborder = $border_end;
21813
 
                        $h = $currentY - $this->y;
21814
 
                    } else { // middle column
21815
 
                        $cborder = $border_middle;
21816
 
                        $h = $this->h - $this->y - $this->bMargin;
21817
 
                    }
21818
 
                    $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
21819
 
                } // end for each column
21820
 
            } elseif ($page == $startpage) { // first page
21821
 
                for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
21822
 
                    $this->selectColumn($column);
21823
 
                    if ($column == $startcolumn) { // first column
21824
 
                        $cborder = $border_start;
21825
 
                        $this->y = $starty;
21826
 
                        $h = $this->h - $this->y - $this->bMargin;
21827
 
                    } else { // middle column
21828
 
                        $cborder = $border_middle;
21829
 
                        $h = $this->h - $this->y - $this->bMargin;
21830
 
                    }
21831
 
                    $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
21832
 
                } // end for each column
21833
 
            } elseif ($page == $endpage) { // last page
21834
 
                for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
21835
 
                    $this->selectColumn($column);
21836
 
                    if ($column == $endcolumn) {
21837
 
                        // end column
21838
 
                        $cborder = $border_end;
21839
 
                        $h = $currentY - $this->y;
21840
 
                    } else {
21841
 
                        // middle column
21842
 
                        $cborder = $border_middle;
21843
 
                        $h = $this->h - $this->y - $this->bMargin;
21844
 
                    }
21845
 
                    $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
21846
 
                } // end for each column
21847
 
            } else { // middle page
21848
 
                for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
21849
 
                    $this->selectColumn($column);
21850
 
                    $cborder = $border_middle;
21851
 
                    $h = $this->h - $this->y - $this->bMargin;
21852
 
                    $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
21853
 
                } // end for each column
21854
 
            }
21855
 
            if ($cborder OR $fill) {
21856
 
                // draw border and fill
21857
 
                if ($this->inxobj) {
21858
 
                    // we are inside an XObject template
21859
 
                    if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
21860
 
                        $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
21861
 
                        $pagemark = &$this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
21862
 
                    } else {
21863
 
                        $pagemark = &$this->xobjects[$this->xobjid]['intmrk'];
21864
 
                    }
21865
 
                    $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
21866
 
                    $pstart = substr($pagebuff, 0, $pagemark);
21867
 
                    $pend = substr($pagebuff, $pagemark);
21868
 
                    $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
21869
 
                    $pagemark += strlen($ccode);
21870
 
                } else {
21871
 
                    if (end($this->transfmrk[$this->page]) !== false) {
21872
 
                        $pagemarkkey = key($this->transfmrk[$this->page]);
21873
 
                        $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
21874
 
                    } elseif ($this->InFooter) {
21875
 
                        $pagemark = &$this->footerpos[$this->page];
21876
 
                    } else {
21877
 
                        $pagemark = &$this->intmrk[$this->page];
21878
 
                    }
21879
 
                    $pagebuff = $this->getPageBuffer($this->page);
21880
 
                    $pstart = substr($pagebuff, 0, $this->bordermrk[$this->page]);
21881
 
                    $pend = substr($pagebuff, $this->bordermrk[$this->page]);
21882
 
                    $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
21883
 
                    $offsetlen = strlen($ccode);
21884
 
                    $this->bordermrk[$this->page] += $offsetlen;
21885
 
                    $this->cntmrk[$this->page] += $offsetlen;
21886
 
                    $pagemark += $offsetlen;
21887
 
                }
21888
 
            }
21889
 
        } // end for each page
21890
 
        // restore page regions
21891
 
        $this->page_regions = $temp_page_regions;
21892
 
        if (isset($old_bgcolor)) {
21893
 
            // restore background color
21894
 
            $this->SetFillColorArray($old_bgcolor);
21895
 
        }
21896
 
        // restore pointer position
21897
 
        $this->x = $prev_x;
21898
 
        $this->y = $prev_y;
21899
 
        $this->lasth = $prev_lasth;
21900
 
    }
21901
 
 
21902
 
    /**
21903
 
     * Set the default bullet to be used as LI bullet symbol
21904
 
     * @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')
21905
 
     * @public
21906
 
     * @since 4.0.028 (2008-09-26)
21907
 
     */
21908
 
    public function setLIsymbol($symbol='!') {
21909
 
        // check for custom image symbol
21910
 
        if (substr($symbol, 0, 4) == 'img|') {
21911
 
            $this->lisymbol = $symbol;
21912
 
            return;
21913
 
        }
21914
 
        $symbol = strtolower($symbol);
21915
 
        switch ($symbol) {
21916
 
            case '!' :
21917
 
            case '#' :
21918
 
            case 'disc' :
21919
 
            case 'circle' :
21920
 
            case 'square' :
21921
 
            case '1':
21922
 
            case 'decimal':
21923
 
            case 'decimal-leading-zero':
21924
 
            case 'i':
21925
 
            case 'lower-roman':
21926
 
            case 'I':
21927
 
            case 'upper-roman':
21928
 
            case 'a':
21929
 
            case 'lower-alpha':
21930
 
            case 'lower-latin':
21931
 
            case 'A':
21932
 
            case 'upper-alpha':
21933
 
            case 'upper-latin':
21934
 
            case 'lower-greek': {
21935
 
                $this->lisymbol = $symbol;
21936
 
                break;
21937
 
            }
21938
 
            default : {
21939
 
                $this->lisymbol = '';
21940
 
            }
21941
 
        }
21942
 
    }
21943
 
 
21944
 
    /**
21945
 
     * Set the booklet mode for double-sided pages.
21946
 
     * @param $booklet (boolean) true set the booklet mode on, false otherwise.
21947
 
     * @param $inner (float) Inner page margin.
21948
 
     * @param $outer (float) Outer page margin.
21949
 
     * @public
21950
 
     * @since 4.2.000 (2008-10-29)
21951
 
     */
21952
 
    public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
21953
 
        $this->booklet = $booklet;
21954
 
        if ($inner >= 0) {
21955
 
            $this->lMargin = $inner;
21956
 
        }
21957
 
        if ($outer >= 0) {
21958
 
            $this->rMargin = $outer;
21959
 
        }
21960
 
    }
21961
 
 
21962
 
    /**
21963
 
     * Swap the left and right margins.
21964
 
     * @param $reverse (boolean) if true swap left and right margins.
21965
 
     * @protected
21966
 
     * @since 4.2.000 (2008-10-29)
21967
 
     */
21968
 
    protected function swapMargins($reverse=true) {
21969
 
        if ($reverse) {
21970
 
            // swap left and right margins
21971
 
            $mtemp = $this->original_lMargin;
21972
 
            $this->original_lMargin = $this->original_rMargin;
21973
 
            $this->original_rMargin = $mtemp;
21974
 
            $deltam = $this->original_lMargin - $this->original_rMargin;
21975
 
            $this->lMargin += $deltam;
21976
 
            $this->rMargin -= $deltam;
21977
 
        }
21978
 
    }
21979
 
 
21980
 
    /**
21981
 
     * Set the vertical spaces for HTML tags.
21982
 
     * The array must have the following structure (example):
21983
 
     * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1)));
21984
 
     * The first array level contains the tag names,
21985
 
     * the second level contains 0 for opening tags or 1 for closing tags,
21986
 
     * the third level contains the vertical space unit (h) and the number spaces to add (n).
21987
 
     * If the h parameter is not specified, default values are used.
21988
 
     * @param $tagvs (array) array of tags and relative vertical spaces.
21989
 
     * @public
21990
 
     * @since 4.2.001 (2008-10-30)
21991
 
     */
21992
 
    public function setHtmlVSpace($tagvs) {
21993
 
        $this->tagvspaces = $tagvs;
21994
 
    }
21995
 
 
21996
 
    /**
21997
 
     * Set custom width for list indentation.
21998
 
     * @param $width (float) width of the indentation. Use negative value to disable it.
21999
 
     * @public
22000
 
     * @since 4.2.007 (2008-11-12)
22001
 
     */
22002
 
    public function setListIndentWidth($width) {
22003
 
        return $this->customlistindent = floatval($width);
22004
 
    }
22005
 
 
22006
 
    /**
22007
 
     * Set the top/bottom cell sides to be open or closed when the cell cross the page.
22008
 
     * @param $isopen (boolean) if true keeps the top/bottom border open for the cell sides that cross the page.
22009
 
     * @public
22010
 
     * @since 4.2.010 (2008-11-14)
22011
 
     */
22012
 
    public function setOpenCell($isopen) {
22013
 
        $this->opencell = $isopen;
22014
 
    }
22015
 
 
22016
 
    /**
22017
 
     * Set the color and font style for HTML links.
22018
 
     * @param $color (array) RGB array of colors
22019
 
     * @param $fontstyle (string) additional font styles to add
22020
 
     * @public
22021
 
     * @since 4.4.003 (2008-12-09)
22022
 
     */
22023
 
    public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
22024
 
        $this->htmlLinkColorArray = $color;
22025
 
        $this->htmlLinkFontStyle = $fontstyle;
22026
 
    }
22027
 
 
22028
 
    /**
22029
 
     * Convert HTML string containing value and unit of measure to user's units or points.
22030
 
     * @param $htmlval (string) string containing values and unit
22031
 
     * @param $refsize (string) reference value in points
22032
 
     * @param $defaultunit (string) default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
22033
 
     * @param $points (boolean) if true returns points, otherwise returns value in user's units
22034
 
     * @return float value in user's unit or point if $points=true
22035
 
     * @public
22036
 
     * @since 4.4.004 (2008-12-10)
22037
 
     */
22038
 
    public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
22039
 
        $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
22040
 
        $retval = 0;
22041
 
        $value = 0;
22042
 
        $unit = 'px';
22043
 
        $k = $this->k;
22044
 
        if ($points) {
22045
 
            $k = 1;
22046
 
        }
22047
 
        if (in_array($defaultunit, $supportedunits)) {
22048
 
            $unit = $defaultunit;
22049
 
        }
22050
 
        if (is_numeric($htmlval)) {
22051
 
            $value = floatval($htmlval);
22052
 
        } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
22053
 
            $value = floatval($mnum[1]);
22054
 
            if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
22055
 
                if (in_array($munit[1], $supportedunits)) {
22056
 
                    $unit = $munit[1];
22057
 
                }
22058
 
            }
22059
 
        }
22060
 
        switch ($unit) {
22061
 
            // percentage
22062
 
            case '%': {
22063
 
                $retval = (($value * $refsize) / 100);
22064
 
                break;
22065
 
            }
22066
 
            // relative-size
22067
 
            case 'em': {
22068
 
                $retval = ($value * $refsize);
22069
 
                break;
22070
 
            }
22071
 
            // height of lower case 'x' (about half the font-size)
22072
 
            case 'ex': {
22073
 
                $retval = $value * ($refsize / 2);
22074
 
                break;
22075
 
            }
22076
 
            // absolute-size
22077
 
            case 'in': {
22078
 
                $retval = ($value * $this->dpi) / $k;
22079
 
                break;
22080
 
            }
22081
 
            // centimeters
22082
 
            case 'cm': {
22083
 
                $retval = ($value / 2.54 * $this->dpi) / $k;
22084
 
                break;
22085
 
            }
22086
 
            // millimeters
22087
 
            case 'mm': {
22088
 
                $retval = ($value / 25.4 * $this->dpi) / $k;
22089
 
                break;
22090
 
            }
22091
 
            // one pica is 12 points
22092
 
            case 'pc': {
22093
 
                $retval = ($value * 12) / $k;
22094
 
                break;
22095
 
            }
22096
 
            // points
22097
 
            case 'pt': {
22098
 
                $retval = $value / $k;
22099
 
                break;
22100
 
            }
22101
 
            // pixels
22102
 
            case 'px': {
22103
 
                $retval = $this->pixelsToUnits($value);
22104
 
                break;
22105
 
            }
22106
 
        }
22107
 
        return $retval;
22108
 
    }
22109
 
 
22110
 
    /**
22111
 
     * Returns the Roman representation of an integer number
22112
 
     * @param $number (int) number to convert
22113
 
     * @return string roman representation of the specified number
22114
 
     * @since 4.4.004 (2008-12-10)
22115
 
     * @public
22116
 
     */
22117
 
    public function intToRoman($number) {
22118
 
        $roman = '';
22119
 
        while ($number >= 1000) {
22120
 
            $roman .= 'M';
22121
 
            $number -= 1000;
22122
 
        }
22123
 
        while ($number >= 900) {
22124
 
            $roman .= 'CM';
22125
 
            $number -= 900;
22126
 
        }
22127
 
        while ($number >= 500) {
22128
 
            $roman .= 'D';
22129
 
            $number -= 500;
22130
 
        }
22131
 
        while ($number >= 400) {
22132
 
            $roman .= 'CD';
22133
 
            $number -= 400;
22134
 
        }
22135
 
        while ($number >= 100) {
22136
 
            $roman .= 'C';
22137
 
            $number -= 100;
22138
 
        }
22139
 
        while ($number >= 90) {
22140
 
            $roman .= 'XC';
22141
 
            $number -= 90;
22142
 
        }
22143
 
        while ($number >= 50) {
22144
 
            $roman .= 'L';
22145
 
            $number -= 50;
22146
 
        }
22147
 
        while ($number >= 40) {
22148
 
            $roman .= 'XL';
22149
 
            $number -= 40;
22150
 
        }
22151
 
        while ($number >= 10) {
22152
 
            $roman .= 'X';
22153
 
            $number -= 10;
22154
 
        }
22155
 
        while ($number >= 9) {
22156
 
            $roman .= 'IX';
22157
 
            $number -= 9;
22158
 
        }
22159
 
        while ($number >= 5) {
22160
 
            $roman .= 'V';
22161
 
            $number -= 5;
22162
 
        }
22163
 
        while ($number >= 4) {
22164
 
            $roman .= 'IV';
22165
 
            $number -= 4;
22166
 
        }
22167
 
        while ($number >= 1) {
22168
 
            $roman .= 'I';
22169
 
            --$number;
22170
 
        }
22171
 
        return $roman;
22172
 
    }
22173
 
 
22174
 
    /**
22175
 
     * Output an HTML list bullet or ordered item symbol
22176
 
     * @param $listdepth (int) list nesting level
22177
 
     * @param $listtype (string) type of list
22178
 
     * @param $size (float) current font size
22179
 
     * @protected
22180
 
     * @since 4.4.004 (2008-12-10)
22181
 
     */
22182
 
    protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
22183
 
        $size /= $this->k;
22184
 
        $fill = '';
22185
 
        $color = $this->fgcolor;
22186
 
        $width = 0;
22187
 
        $textitem = '';
22188
 
        $tmpx = $this->x;
22189
 
        $lspace = $this->GetStringWidth('  ');
22190
 
        if ($listtype == '^') {
22191
 
            // special symbol used for avoid justification of rect bullet
22192
 
            $this->lispacer = '';
22193
 
            return;
22194
 
        } elseif ($listtype == '!') {
22195
 
            // set default list type for unordered list
22196
 
            $deftypes = array('disc', 'circle', 'square');
22197
 
            $listtype = $deftypes[($listdepth - 1) % 3];
22198
 
        } elseif ($listtype == '#') {
22199
 
            // set default list type for ordered list
22200
 
            $listtype = 'decimal';
22201
 
        } elseif(substr($listtype, 0, 4) == 'img|') {
22202
 
            // custom image type ('img|type|width|height|image.ext')
22203
 
            $img = explode('|', $listtype);
22204
 
            $listtype = 'img';
22205
 
        }
22206
 
        switch ($listtype) {
22207
 
            // unordered types
22208
 
            case 'none': {
22209
 
                break;
22210
 
            }
22211
 
            case 'disc': {
22212
 
                $fill = 'F';
22213
 
            }
22214
 
            case 'circle': {
22215
 
                $fill .= 'D';
22216
 
                $r = $size / 6;
22217
 
                $lspace += (2 * $r);
22218
 
                if ($this->rtl) {
22219
 
                    $this->x += $lspace;
22220
 
                } else {
22221
 
                    $this->x -= $lspace;
22222
 
                }
22223
 
                $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, $fill, array('color'=>$color), $color, 8);
22224
 
                break;
22225
 
            }
22226
 
            case 'square': {
22227
 
                $l = $size / 3;
22228
 
                $lspace += $l;
22229
 
                if ($this->rtl) {;
22230
 
                    $this->x += $lspace;
22231
 
                } else {
22232
 
                    $this->x -= $lspace;
22233
 
                }
22234
 
                $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color);
22235
 
                break;
22236
 
            }
22237
 
            case 'img': {
22238
 
                // 1=>type, 2=>width, 3=>height, 4=>image.ext
22239
 
                $lspace += $img[2];
22240
 
                if ($this->rtl) {;
22241
 
                    $this->x += $lspace;
22242
 
                } else {
22243
 
                    $this->x -= $lspace;
22244
 
                }
22245
 
                $imgtype = strtolower($img[1]);
22246
 
                $prev_y = $this->y;
22247
 
                switch ($imgtype) {
22248
 
                    case 'svg': {
22249
 
                        $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
22250
 
                        break;
22251
 
                    }
22252
 
                    case 'ai':
22253
 
                    case 'eps': {
22254
 
                        $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
22255
 
                        break;
22256
 
                    }
22257
 
                    default: {
22258
 
                        $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);
22259
 
                        break;
22260
 
                    }
22261
 
                }
22262
 
                $this->y = $prev_y;
22263
 
                break;
22264
 
            }
22265
 
            // ordered types
22266
 
            // $this->listcount[$this->listnum];
22267
 
            // $textitem
22268
 
            case '1':
22269
 
            case 'decimal': {
22270
 
                $textitem = $this->listcount[$this->listnum];
22271
 
                break;
22272
 
            }
22273
 
            case 'decimal-leading-zero': {
22274
 
                $textitem = sprintf('%02d', $this->listcount[$this->listnum]);
22275
 
                break;
22276
 
            }
22277
 
            case 'i':
22278
 
            case 'lower-roman': {
22279
 
                $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum]));
22280
 
                break;
22281
 
            }
22282
 
            case 'I':
22283
 
            case 'upper-roman': {
22284
 
                $textitem = $this->intToRoman($this->listcount[$this->listnum]);
22285
 
                break;
22286
 
            }
22287
 
            case 'a':
22288
 
            case 'lower-alpha':
22289
 
            case 'lower-latin': {
22290
 
                $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
22291
 
                break;
22292
 
            }
22293
 
            case 'A':
22294
 
            case 'upper-alpha':
22295
 
            case 'upper-latin': {
22296
 
                $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
22297
 
                break;
22298
 
            }
22299
 
            case 'lower-greek': {
22300
 
                $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1);
22301
 
                break;
22302
 
            }
22303
 
            /*
22304
 
            // Types to be implemented (special handling)
22305
 
            case 'hebrew': {
22306
 
                break;
22307
 
            }
22308
 
            case 'armenian': {
22309
 
                break;
22310
 
            }
22311
 
            case 'georgian': {
22312
 
                break;
22313
 
            }
22314
 
            case 'cjk-ideographic': {
22315
 
                break;
22316
 
            }
22317
 
            case 'hiragana': {
22318
 
                break;
22319
 
            }
22320
 
            case 'katakana': {
22321
 
                break;
22322
 
            }
22323
 
            case 'hiragana-iroha': {
22324
 
                break;
22325
 
            }
22326
 
            case 'katakana-iroha': {
22327
 
                break;
22328
 
            }
22329
 
            */
22330
 
            default: {
22331
 
                $textitem = $this->listcount[$this->listnum];
22332
 
            }
22333
 
        }
22334
 
        if (!$this->empty_string($textitem)) {
22335
 
            // Check whether we need a new page or new column
22336
 
            $prev_y = $this->y;
22337
 
            $h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
22338
 
            if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) {
22339
 
                $tmpx = $this->x;
22340
 
            }
22341
 
            // print ordered item
22342
 
            if ($this->rtl) {
22343
 
                $textitem = '.'.$textitem;
22344
 
            } else {
22345
 
                $textitem = $textitem.'.';
22346
 
            }
22347
 
            $lspace += $this->GetStringWidth($textitem);
22348
 
            if ($this->rtl) {
22349
 
                $this->x += $lspace;
22350
 
            } else {
22351
 
                $this->x -= $lspace;
22352
 
            }
22353
 
            $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
22354
 
        }
22355
 
        $this->x = $tmpx;
22356
 
        $this->lispacer = '^';
22357
 
    }
22358
 
 
22359
 
    /**
22360
 
     * Returns current graphic variables as array.
22361
 
     * @return array of graphic variables
22362
 
     * @protected
22363
 
     * @since 4.2.010 (2008-11-14)
22364
 
     */
22365
 
    protected function getGraphicVars() {
22366
 
        $grapvars = array(
22367
 
            'FontFamily' => $this->FontFamily,
22368
 
            'FontStyle' => $this->FontStyle,
22369
 
            'FontSizePt' => $this->FontSizePt,
22370
 
            'rMargin' => $this->rMargin,
22371
 
            'lMargin' => $this->lMargin,
22372
 
            'cell_padding' => $this->cell_padding,
22373
 
            'cell_margin' => $this->cell_margin,
22374
 
            'LineWidth' => $this->LineWidth,
22375
 
            'linestyleWidth' => $this->linestyleWidth,
22376
 
            'linestyleCap' => $this->linestyleCap,
22377
 
            'linestyleJoin' => $this->linestyleJoin,
22378
 
            'linestyleDash' => $this->linestyleDash,
22379
 
            'textrendermode' => $this->textrendermode,
22380
 
            'textstrokewidth' => $this->textstrokewidth,
22381
 
            'DrawColor' => $this->DrawColor,
22382
 
            'FillColor' => $this->FillColor,
22383
 
            'TextColor' => $this->TextColor,
22384
 
            'ColorFlag' => $this->ColorFlag,
22385
 
            'bgcolor' => $this->bgcolor,
22386
 
            'fgcolor' => $this->fgcolor,
22387
 
            'htmlvspace' => $this->htmlvspace,
22388
 
            'listindent' => $this->listindent,
22389
 
            'listindentlevel' => $this->listindentlevel,
22390
 
            'listnum' => $this->listnum,
22391
 
            'listordered' => $this->listordered,
22392
 
            'listcount' => $this->listcount,
22393
 
            'lispacer' => $this->lispacer,
22394
 
            'cell_height_ratio' => $this->cell_height_ratio,
22395
 
            'font_stretching' => $this->font_stretching,
22396
 
            'font_spacing' => $this->font_spacing,
22397
 
            // extended
22398
 
            'lasth' => $this->lasth,
22399
 
            'tMargin' => $this->tMargin,
22400
 
            'bMargin' => $this->bMargin,
22401
 
            'AutoPageBreak' => $this->AutoPageBreak,
22402
 
            'PageBreakTrigger' => $this->PageBreakTrigger,
22403
 
            'x' => $this->x,
22404
 
            'y' => $this->y,
22405
 
            'w' => $this->w,
22406
 
            'h' => $this->h,
22407
 
            'wPt' => $this->wPt,
22408
 
            'hPt' => $this->hPt,
22409
 
            'fwPt' => $this->fwPt,
22410
 
            'fhPt' => $this->fhPt,
22411
 
            'page' => $this->page,
22412
 
            'current_column' => $this->current_column,
22413
 
            'num_columns' => $this->num_columns
22414
 
            );
22415
 
        return $grapvars;
22416
 
    }
22417
 
 
22418
 
    /**
22419
 
     * Set graphic variables.
22420
 
     * @param $gvars (array) array of graphic variablesto restore
22421
 
     * @param $extended (boolean) if true restore extended graphic variables
22422
 
     * @protected
22423
 
     * @since 4.2.010 (2008-11-14)
22424
 
     */
22425
 
    protected function setGraphicVars($gvars, $extended=false) {
22426
 
        $this->FontFamily = $gvars['FontFamily'];
22427
 
        $this->FontStyle = $gvars['FontStyle'];
22428
 
        $this->FontSizePt = $gvars['FontSizePt'];
22429
 
        $this->rMargin = $gvars['rMargin'];
22430
 
        $this->lMargin = $gvars['lMargin'];
22431
 
        $this->cell_padding = $gvars['cell_padding'];
22432
 
        $this->cell_margin = $gvars['cell_margin'];
22433
 
        $this->LineWidth = $gvars['LineWidth'];
22434
 
        $this->linestyleWidth = $gvars['linestyleWidth'];
22435
 
        $this->linestyleCap = $gvars['linestyleCap'];
22436
 
        $this->linestyleJoin = $gvars['linestyleJoin'];
22437
 
        $this->linestyleDash = $gvars['linestyleDash'];
22438
 
        $this->textrendermode = $gvars['textrendermode'];
22439
 
        $this->textstrokewidth = $gvars['textstrokewidth'];
22440
 
        $this->DrawColor = $gvars['DrawColor'];
22441
 
        $this->FillColor = $gvars['FillColor'];
22442
 
        $this->TextColor = $gvars['TextColor'];
22443
 
        $this->ColorFlag = $gvars['ColorFlag'];
22444
 
        $this->bgcolor = $gvars['bgcolor'];
22445
 
        $this->fgcolor = $gvars['fgcolor'];
22446
 
        $this->htmlvspace = $gvars['htmlvspace'];
22447
 
        $this->listindent = $gvars['listindent'];
22448
 
        $this->listindentlevel = $gvars['listindentlevel'];
22449
 
        $this->listnum = $gvars['listnum'];
22450
 
        $this->listordered = $gvars['listordered'];
22451
 
        $this->listcount = $gvars['listcount'];
22452
 
        $this->lispacer = $gvars['lispacer'];
22453
 
        $this->cell_height_ratio = $gvars['cell_height_ratio'];
22454
 
        $this->font_stretching = $gvars['font_stretching'];
22455
 
        $this->font_spacing = $gvars['font_spacing'];
22456
 
        if ($extended) {
22457
 
            // restore extended values
22458
 
            $this->lasth = $gvars['lasth'];
22459
 
            $this->tMargin = $gvars['tMargin'];
22460
 
            $this->bMargin = $gvars['bMargin'];
22461
 
            $this->AutoPageBreak = $gvars['AutoPageBreak'];
22462
 
            $this->PageBreakTrigger = $gvars['PageBreakTrigger'];
22463
 
            $this->x = $gvars['x'];
22464
 
            $this->y = $gvars['y'];
22465
 
            $this->w = $gvars['w'];
22466
 
            $this->h = $gvars['h'];
22467
 
            $this->wPt = $gvars['wPt'];
22468
 
            $this->hPt = $gvars['hPt'];
22469
 
            $this->fwPt = $gvars['fwPt'];
22470
 
            $this->fhPt = $gvars['fhPt'];
22471
 
            $this->page = $gvars['page'];
22472
 
            $this->current_column = $gvars['current_column'];
22473
 
            $this->num_columns = $gvars['num_columns'];
22474
 
        }
22475
 
        $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
22476
 
        if (!$this->empty_string($this->FontFamily)) {
22477
 
            $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
22478
 
        }
22479
 
    }
22480
 
 
22481
 
    /**
22482
 
     * Returns a temporary filename for caching object on filesystem.
22483
 
     * @param $name (string) prefix to add to filename
22484
 
     * @return string filename.
22485
 
     * @since 4.5.000 (2008-12-31)
22486
 
     * @protected
22487
 
     */
22488
 
    protected function getObjFilename($name) {
22489
 
        return tempnam(K_PATH_CACHE, $name.'_');
22490
 
    }
22491
 
 
22492
 
    /**
22493
 
     * Writes data to a temporary file on filesystem.
22494
 
     * @param $filename (string) file name
22495
 
     * @param $data (mixed) data to write on file
22496
 
     * @param $append (boolean) if true append data, false replace.
22497
 
     * @since 4.5.000 (2008-12-31)
22498
 
     * @protected
22499
 
     */
22500
 
    protected function writeDiskCache($filename, $data, $append=false) {
22501
 
        if ($append) {
22502
 
            $fmode = 'ab+';
22503
 
        } else {
22504
 
            $fmode = 'wb+';
22505
 
        }
22506
 
        $f = @fopen($filename, $fmode);
22507
 
        if (!$f) {
22508
 
            $this->Error('Unable to write cache file: '.$filename);
22509
 
        } else {
22510
 
            fwrite($f, $data);
22511
 
            fclose($f);
22512
 
        }
22513
 
        // update file length (needed for transactions)
22514
 
        if (!isset($this->cache_file_length['_'.$filename])) {
22515
 
            $this->cache_file_length['_'.$filename] = strlen($data);
22516
 
        } else {
22517
 
            $this->cache_file_length['_'.$filename] += strlen($data);
22518
 
        }
22519
 
    }
22520
 
 
22521
 
    /**
22522
 
     * Read data from a temporary file on filesystem.
22523
 
     * @param $filename (string) file name
22524
 
     * @return mixed retrieved data
22525
 
     * @since 4.5.000 (2008-12-31)
22526
 
     * @protected
22527
 
     */
22528
 
    protected function readDiskCache($filename) {
22529
 
        return file_get_contents($filename);
22530
 
    }
22531
 
 
22532
 
    /**
22533
 
     * Set buffer content (always append data).
22534
 
     * @param $data (string) data
22535
 
     * @protected
22536
 
     * @since 4.5.000 (2009-01-02)
22537
 
     */
22538
 
    protected function setBuffer($data) {
22539
 
        $this->bufferlen += strlen($data);
22540
 
        if ($this->diskcache) {
22541
 
            if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
22542
 
                $this->buffer = $this->getObjFilename('buffer');
22543
 
            }
22544
 
            $this->writeDiskCache($this->buffer, $data, true);
22545
 
        } else {
22546
 
            $this->buffer .= $data;
22547
 
        }
22548
 
    }
22549
 
 
22550
 
    /**
22551
 
     * Replace the buffer content
22552
 
     * @param $data (string) data
22553
 
     * @protected
22554
 
     * @since 5.5.000 (2010-06-22)
22555
 
     */
22556
 
    protected function replaceBuffer($data) {
22557
 
        $this->bufferlen = strlen($data);
22558
 
        if ($this->diskcache) {
22559
 
            if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
22560
 
                $this->buffer = $this->getObjFilename('buffer');
22561
 
            }
22562
 
            $this->writeDiskCache($this->buffer, $data, false);
22563
 
        } else {
22564
 
            $this->buffer = $data;
22565
 
        }
22566
 
    }
22567
 
 
22568
 
    /**
22569
 
     * Get buffer content.
22570
 
     * @return string buffer content
22571
 
     * @protected
22572
 
     * @since 4.5.000 (2009-01-02)
22573
 
     */
22574
 
    protected function getBuffer() {
22575
 
        if ($this->diskcache) {
22576
 
            return $this->readDiskCache($this->buffer);
22577
 
        } else {
22578
 
            return $this->buffer;
22579
 
        }
22580
 
    }
22581
 
 
22582
 
    /**
22583
 
     * Set page buffer content.
22584
 
     * @param $page (int) page number
22585
 
     * @param $data (string) page data
22586
 
     * @param $append (boolean) if true append data, false replace.
22587
 
     * @protected
22588
 
     * @since 4.5.000 (2008-12-31)
22589
 
     */
22590
 
    protected function setPageBuffer($page, $data, $append=false) {
22591
 
        if ($this->diskcache) {
22592
 
            if (!isset($this->pages[$page])) {
22593
 
                $this->pages[$page] = $this->getObjFilename('page'.$page);
22594
 
            }
22595
 
            $this->writeDiskCache($this->pages[$page], $data, $append);
22596
 
        } else {
22597
 
            if ($append) {
22598
 
                $this->pages[$page] .= $data;
22599
 
            } else {
22600
 
                $this->pages[$page] = $data;
22601
 
            }
22602
 
        }
22603
 
        if ($append AND isset($this->pagelen[$page])) {
22604
 
            $this->pagelen[$page] += strlen($data);
22605
 
        } else {
22606
 
            $this->pagelen[$page] = strlen($data);
22607
 
        }
22608
 
    }
22609
 
 
22610
 
    /**
22611
 
     * Get page buffer content.
22612
 
     * @param $page (int) page number
22613
 
     * @return string page buffer content or false in case of error
22614
 
     * @protected
22615
 
     * @since 4.5.000 (2008-12-31)
22616
 
     */
22617
 
    protected function getPageBuffer($page) {
22618
 
        if ($this->diskcache) {
22619
 
            return $this->readDiskCache($this->pages[$page]);
22620
 
        } elseif (isset($this->pages[$page])) {
22621
 
            return $this->pages[$page];
22622
 
        }
22623
 
        return false;
22624
 
    }
22625
 
 
22626
 
    /**
22627
 
     * Set image buffer content.
22628
 
     * @param $image (string) image key
22629
 
     * @param $data (array) image data
22630
 
     * @protected
22631
 
     * @since 4.5.000 (2008-12-31)
22632
 
     */
22633
 
    protected function setImageBuffer($image, $data) {
22634
 
        if ($this->diskcache) {
22635
 
            if (!isset($this->images[$image])) {
22636
 
                $this->images[$image] = $this->getObjFilename('image'.$image);
22637
 
            }
22638
 
            $this->writeDiskCache($this->images[$image], serialize($data));
22639
 
        } else {
22640
 
            $this->images[$image] = $data;
22641
 
        }
22642
 
        if (!in_array($image, $this->imagekeys)) {
22643
 
            $this->imagekeys[] = $image;
22644
 
            ++$this->numimages;
22645
 
        }
22646
 
    }
22647
 
 
22648
 
    /**
22649
 
     * Set image buffer content for a specified sub-key.
22650
 
     * @param $image (string) image key
22651
 
     * @param $key (string) image sub-key
22652
 
     * @param $data (array) image data
22653
 
     * @protected
22654
 
     * @since 4.5.000 (2008-12-31)
22655
 
     */
22656
 
    protected function setImageSubBuffer($image, $key, $data) {
22657
 
        if (!isset($this->images[$image])) {
22658
 
            $this->setImageBuffer($image, array());
22659
 
        }
22660
 
        if ($this->diskcache) {
22661
 
            $tmpimg = $this->getImageBuffer($image);
22662
 
            $tmpimg[$key] = $data;
22663
 
            $this->writeDiskCache($this->images[$image], serialize($tmpimg));
22664
 
        } else {
22665
 
            $this->images[$image][$key] = $data;
22666
 
        }
22667
 
    }
22668
 
 
22669
 
    /**
22670
 
     * Get image buffer content.
22671
 
     * @param $image (string) image key
22672
 
     * @return string image buffer content or false in case of error
22673
 
     * @protected
22674
 
     * @since 4.5.000 (2008-12-31)
22675
 
     */
22676
 
    protected function getImageBuffer($image) {
22677
 
        if ($this->diskcache AND isset($this->images[$image])) {
22678
 
            return unserialize($this->readDiskCache($this->images[$image]));
22679
 
        } elseif (isset($this->images[$image])) {
22680
 
            return $this->images[$image];
22681
 
        }
22682
 
        return false;
22683
 
    }
22684
 
 
22685
 
    /**
22686
 
     * Set font buffer content.
22687
 
     * @param $font (string) font key
22688
 
     * @param $data (array) font data
22689
 
     * @protected
22690
 
     * @since 4.5.000 (2009-01-02)
22691
 
     */
22692
 
    protected function setFontBuffer($font, $data) {
22693
 
        if ($this->diskcache) {
22694
 
            if (!isset($this->fonts[$font])) {
22695
 
                $this->fonts[$font] = $this->getObjFilename('font');
22696
 
            }
22697
 
            $this->writeDiskCache($this->fonts[$font], serialize($data));
22698
 
        } else {
22699
 
            $this->fonts[$font] = $data;
22700
 
        }
22701
 
        if (!in_array($font, $this->fontkeys)) {
22702
 
            $this->fontkeys[] = $font;
22703
 
            // store object ID for current font
22704
 
            ++$this->n;
22705
 
            $this->font_obj_ids[$font] = $this->n;
22706
 
            $this->setFontSubBuffer($font, 'n', $this->n);
22707
 
        }
22708
 
    }
22709
 
 
22710
 
    /**
22711
 
     * Set font buffer content.
22712
 
     * @param $font (string) font key
22713
 
     * @param $key (string) font sub-key
22714
 
     * @param $data (array) font data
22715
 
     * @protected
22716
 
     * @since 4.5.000 (2009-01-02)
22717
 
     */
22718
 
    protected function setFontSubBuffer($font, $key, $data) {
22719
 
        if (!isset($this->fonts[$font])) {
22720
 
            $this->setFontBuffer($font, array());
22721
 
        }
22722
 
        if ($this->diskcache) {
22723
 
            $tmpfont = $this->getFontBuffer($font);
22724
 
            $tmpfont[$key] = $data;
22725
 
            $this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
22726
 
        } else {
22727
 
            $this->fonts[$font][$key] = $data;
22728
 
        }
22729
 
    }
22730
 
 
22731
 
    /**
22732
 
     * Get font buffer content.
22733
 
     * @param $font (string) font key
22734
 
     * @return string font buffer content or false in case of error
22735
 
     * @protected
22736
 
     * @since 4.5.000 (2009-01-02)
22737
 
     */
22738
 
    protected function getFontBuffer($font) {
22739
 
        if ($this->diskcache AND isset($this->fonts[$font])) {
22740
 
            return unserialize($this->readDiskCache($this->fonts[$font]));
22741
 
        } elseif (isset($this->fonts[$font])) {
22742
 
            return $this->fonts[$font];
22743
 
        }
22744
 
        return false;
22745
 
    }
22746
 
 
22747
 
    /**
22748
 
     * Move a page to a previous position.
22749
 
     * @param $frompage (int) number of the source page
22750
 
     * @param $topage (int) number of the destination page (must be less than $frompage)
22751
 
     * @return true in case of success, false in case of error.
22752
 
     * @public
22753
 
     * @since 4.5.000 (2009-01-02)
22754
 
     */
22755
 
    public function movePage($frompage, $topage) {
22756
 
        if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
22757
 
            return false;
22758
 
        }
22759
 
        if ($frompage == $this->page) {
22760
 
            // close the page before moving it
22761
 
            $this->endPage();
22762
 
        }
22763
 
        // move all page-related states
22764
 
        $tmppage = $this->pages[$frompage];
22765
 
        $tmppagedim = $this->pagedim[$frompage];
22766
 
        $tmppagelen = $this->pagelen[$frompage];
22767
 
        $tmpintmrk = $this->intmrk[$frompage];
22768
 
        $tmpbordermrk = $this->bordermrk[$frompage];
22769
 
        $tmpcntmrk = $this->cntmrk[$frompage];
22770
 
        if (isset($this->footerpos[$frompage])) {
22771
 
            $tmpfooterpos = $this->footerpos[$frompage];
22772
 
        }
22773
 
        if (isset($this->footerlen[$frompage])) {
22774
 
            $tmpfooterlen = $this->footerlen[$frompage];
22775
 
        }
22776
 
        if (isset($this->transfmrk[$frompage])) {
22777
 
            $tmptransfmrk = $this->transfmrk[$frompage];
22778
 
        }
22779
 
        if (isset($this->PageAnnots[$frompage])) {
22780
 
            $tmpannots = $this->PageAnnots[$frompage];
22781
 
        }
22782
 
        if (isset($this->newpagegroup[$frompage])) {
22783
 
            $tmpnewpagegroup = $this->newpagegroup[$frompage];
22784
 
        }
22785
 
        for ($i = $frompage; $i > $topage; --$i) {
22786
 
            $j = $i - 1;
22787
 
            // shift pages down
22788
 
            $this->pages[$i] = $this->pages[$j];
22789
 
            $this->pagedim[$i] = $this->pagedim[$j];
22790
 
            $this->pagelen[$i] = $this->pagelen[$j];
22791
 
            $this->intmrk[$i] = $this->intmrk[$j];
22792
 
            $this->bordermrk[$i] = $this->bordermrk[$j];
22793
 
            $this->cntmrk[$i] = $this->cntmrk[$j];
22794
 
            if (isset($this->footerpos[$j])) {
22795
 
                $this->footerpos[$i] = $this->footerpos[$j];
22796
 
            } elseif (isset($this->footerpos[$i])) {
22797
 
                unset($this->footerpos[$i]);
22798
 
            }
22799
 
            if (isset($this->footerlen[$j])) {
22800
 
                $this->footerlen[$i] = $this->footerlen[$j];
22801
 
            } elseif (isset($this->footerlen[$i])) {
22802
 
                unset($this->footerlen[$i]);
22803
 
            }
22804
 
            if (isset($this->transfmrk[$j])) {
22805
 
                $this->transfmrk[$i] = $this->transfmrk[$j];
22806
 
            } elseif (isset($this->transfmrk[$i])) {
22807
 
                unset($this->transfmrk[$i]);
22808
 
            }
22809
 
            if (isset($this->PageAnnots[$j])) {
22810
 
                $this->PageAnnots[$i] = $this->PageAnnots[$j];
22811
 
            } elseif (isset($this->PageAnnots[$i])) {
22812
 
                unset($this->PageAnnots[$i]);
22813
 
            }
22814
 
            if (isset($this->newpagegroup[$j])) {
22815
 
                $this->newpagegroup[$i] = $this->newpagegroup[$j];
22816
 
            } elseif (isset($this->newpagegroup[$i])) {
22817
 
                unset($this->newpagegroup[$i]);
22818
 
            }
22819
 
        }
22820
 
        $this->pages[$topage] = $tmppage;
22821
 
        $this->pagedim[$topage] = $tmppagedim;
22822
 
        $this->pagelen[$topage] = $tmppagelen;
22823
 
        $this->intmrk[$topage] = $tmpintmrk;
22824
 
        $this->bordermrk[$topage] = $tmpbordermrk;
22825
 
        $this->cntmrk[$topage] = $tmpcntmrk;
22826
 
        if (isset($tmpfooterpos)) {
22827
 
            $this->footerpos[$topage] = $tmpfooterpos;
22828
 
        } elseif (isset($this->footerpos[$topage])) {
22829
 
            unset($this->footerpos[$topage]);
22830
 
        }
22831
 
        if (isset($tmpfooterlen)) {
22832
 
            $this->footerlen[$topage] = $tmpfooterlen;
22833
 
        } elseif (isset($this->footerlen[$topage])) {
22834
 
            unset($this->footerlen[$topage]);
22835
 
        }
22836
 
        if (isset($tmptransfmrk)) {
22837
 
            $this->transfmrk[$topage] = $tmptransfmrk;
22838
 
        } elseif (isset($this->transfmrk[$topage])) {
22839
 
            unset($this->transfmrk[$topage]);
22840
 
        }
22841
 
        if (isset($tmpannots)) {
22842
 
            $this->PageAnnots[$topage] = $tmpannots;
22843
 
        } elseif (isset($this->PageAnnots[$topage])) {
22844
 
            unset($this->PageAnnots[$topage]);
22845
 
        }
22846
 
        if (isset($tmpnewpagegroup)) {
22847
 
            $this->newpagegroup[$topage] = $tmpnewpagegroup;
22848
 
        } elseif (isset($this->newpagegroup[$topage])) {
22849
 
            unset($this->newpagegroup[$topage]);
22850
 
        }
22851
 
        // adjust outlines
22852
 
        $tmpoutlines = $this->outlines;
22853
 
        foreach ($tmpoutlines as $key => $outline) {
22854
 
            if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
22855
 
                $this->outlines[$key]['p'] = $outline['p'] + 1;
22856
 
            } elseif ($outline['p'] == $frompage) {
22857
 
                $this->outlines[$key]['p'] = $topage;
22858
 
            }
22859
 
        }
22860
 
        // adjust links
22861
 
        $tmplinks = $this->links;
22862
 
        foreach ($tmplinks as $key => $link) {
22863
 
            if (($link[0] >= $topage) AND ($link[0] < $frompage)) {
22864
 
                $this->links[$key][0] = $link[0] + 1;
22865
 
            } elseif ($link[0] == $frompage) {
22866
 
                $this->links[$key][0] = $topage;
22867
 
            }
22868
 
        }
22869
 
        // adjust javascript
22870
 
        $tmpjavascript = $this->javascript;
22871
 
        global $jfrompage, $jtopage;
22872
 
        $jfrompage = $frompage;
22873
 
        $jtopage = $topage;
22874
 
        $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
22875
 
            create_function('$matches', 'global $jfrompage, $jtopage;
22876
 
            $pagenum = intval($matches[3]) + 1;
22877
 
            if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
22878
 
                $newpage = ($pagenum + 1);
22879
 
            } elseif ($pagenum == $jfrompage) {
22880
 
                $newpage = $jtopage;
22881
 
            } else {
22882
 
                $newpage = $pagenum;
22883
 
            }
22884
 
            --$newpage;
22885
 
            return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
22886
 
        // return to last page
22887
 
        $this->lastPage(true);
22888
 
        return true;
22889
 
    }
22890
 
 
22891
 
    /**
22892
 
     * Remove the specified page.
22893
 
     * @param $page (int) page to remove
22894
 
     * @return true in case of success, false in case of error.
22895
 
     * @public
22896
 
     * @since 4.6.004 (2009-04-23)
22897
 
     */
22898
 
    public function deletePage($page) {
22899
 
        if (($page < 1) OR ($page > $this->numpages)) {
22900
 
            return false;
22901
 
        }
22902
 
        // delete current page
22903
 
        unset($this->pages[$page]);
22904
 
        unset($this->pagedim[$page]);
22905
 
        unset($this->pagelen[$page]);
22906
 
        unset($this->intmrk[$page]);
22907
 
        unset($this->bordermrk[$page]);
22908
 
        unset($this->cntmrk[$page]);
22909
 
        if (isset($this->footerpos[$page])) {
22910
 
            unset($this->footerpos[$page]);
22911
 
        }
22912
 
        if (isset($this->footerlen[$page])) {
22913
 
            unset($this->footerlen[$page]);
22914
 
        }
22915
 
        if (isset($this->transfmrk[$page])) {
22916
 
            unset($this->transfmrk[$page]);
22917
 
        }
22918
 
        if (isset($this->PageAnnots[$page])) {
22919
 
            unset($this->PageAnnots[$page]);
22920
 
        }
22921
 
        if (isset($this->newpagegroup[$page])) {
22922
 
            unset($this->newpagegroup[$page]);
22923
 
        }
22924
 
        if (isset($this->pageopen[$page])) {
22925
 
            unset($this->pageopen[$page]);
22926
 
        }
22927
 
        // update remaining pages
22928
 
        for ($i = $page; $i < $this->numpages; ++$i) {
22929
 
            $j = $i + 1;
22930
 
            // shift pages
22931
 
            $this->pages[$i] = $this->pages[$j];
22932
 
            $this->pagedim[$i] = $this->pagedim[$j];
22933
 
            $this->pagelen[$i] = $this->pagelen[$j];
22934
 
            $this->intmrk[$i] = $this->intmrk[$j];
22935
 
            $this->bordermrk[$i] = $this->bordermrk[$j];
22936
 
            $this->cntmrk[$i] = $this->cntmrk[$j];
22937
 
            if (isset($this->footerpos[$j])) {
22938
 
                $this->footerpos[$i] = $this->footerpos[$j];
22939
 
            } elseif (isset($this->footerpos[$i])) {
22940
 
                unset($this->footerpos[$i]);
22941
 
            }
22942
 
            if (isset($this->footerlen[$j])) {
22943
 
                $this->footerlen[$i] = $this->footerlen[$j];
22944
 
            } elseif (isset($this->footerlen[$i])) {
22945
 
                unset($this->footerlen[$i]);
22946
 
            }
22947
 
            if (isset($this->transfmrk[$j])) {
22948
 
                $this->transfmrk[$i] = $this->transfmrk[$j];
22949
 
            } elseif (isset($this->transfmrk[$i])) {
22950
 
                unset($this->transfmrk[$i]);
22951
 
            }
22952
 
            if (isset($this->PageAnnots[$j])) {
22953
 
                $this->PageAnnots[$i] = $this->PageAnnots[$j];
22954
 
            } elseif (isset($this->PageAnnots[$i])) {
22955
 
                unset($this->PageAnnots[$i]);
22956
 
            }
22957
 
            if (isset($this->newpagegroup[$j])) {
22958
 
                $this->newpagegroup[$i] = $this->newpagegroup[$j];
22959
 
            } elseif (isset($this->newpagegroup[$i])) {
22960
 
                unset($this->newpagegroup[$i]);
22961
 
            }
22962
 
            if (isset($this->pageopen[$j])) {
22963
 
                $this->pageopen[$i] = $this->pageopen[$j];
22964
 
            } elseif (isset($this->pageopen[$i])) {
22965
 
                unset($this->pageopen[$i]);
22966
 
            }
22967
 
        }
22968
 
        // remove last page
22969
 
        unset($this->pages[$this->numpages]);
22970
 
        unset($this->pagedim[$this->numpages]);
22971
 
        unset($this->pagelen[$this->numpages]);
22972
 
        unset($this->intmrk[$this->numpages]);
22973
 
        unset($this->bordermrk[$this->numpages]);
22974
 
        unset($this->cntmrk[$this->numpages]);
22975
 
        if (isset($this->footerpos[$this->numpages])) {
22976
 
            unset($this->footerpos[$this->numpages]);
22977
 
        }
22978
 
        if (isset($this->footerlen[$this->numpages])) {
22979
 
            unset($this->footerlen[$this->numpages]);
22980
 
        }
22981
 
        if (isset($this->transfmrk[$this->numpages])) {
22982
 
            unset($this->transfmrk[$this->numpages]);
22983
 
        }
22984
 
        if (isset($this->PageAnnots[$this->numpages])) {
22985
 
            unset($this->PageAnnots[$this->numpages]);
22986
 
        }
22987
 
        if (isset($this->newpagegroup[$this->numpages])) {
22988
 
            unset($this->newpagegroup[$this->numpages]);
22989
 
        }
22990
 
        if (isset($this->pageopen[$this->numpages])) {
22991
 
            unset($this->pageopen[$this->numpages]);
22992
 
        }
22993
 
        --$this->numpages;
22994
 
        $this->page = $this->numpages;
22995
 
        // adjust outlines
22996
 
        $tmpoutlines = $this->outlines;
22997
 
        foreach ($tmpoutlines as $key => $outline) {
22998
 
            if ($outline['p'] > $page) {
22999
 
                $this->outlines[$key]['p'] = $outline['p'] - 1;
23000
 
            } elseif ($outline['p'] == $page) {
23001
 
                unset($this->outlines[$key]);
23002
 
            }
23003
 
        }
23004
 
        // adjust links
23005
 
        $tmplinks = $this->links;
23006
 
        foreach ($tmplinks as $key => $link) {
23007
 
            if ($link[0] > $page) {
23008
 
                $this->links[$key][0] = $link[0] - 1;
23009
 
            } elseif ($link[0] == $page) {
23010
 
                unset($this->links[$key]);
23011
 
            }
23012
 
        }
23013
 
        // adjust javascript
23014
 
        $tmpjavascript = $this->javascript;
23015
 
        global $jpage;
23016
 
        $jpage = $page;
23017
 
        $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
23018
 
            create_function('$matches', 'global $jpage;
23019
 
            $pagenum = intval($matches[3]) + 1;
23020
 
            if ($pagenum >= $jpage) {
23021
 
                $newpage = ($pagenum - 1);
23022
 
            } elseif ($pagenum == $jpage) {
23023
 
                $newpage = 1;
23024
 
            } else {
23025
 
                $newpage = $pagenum;
23026
 
            }
23027
 
            --$newpage;
23028
 
            return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
23029
 
        // return to last page
23030
 
        $this->lastPage(true);
23031
 
        return true;
23032
 
    }
23033
 
 
23034
 
    /**
23035
 
     * Clone the specified page to a new page.
23036
 
     * @param $page (int) number of page to copy (0 = current page)
23037
 
     * @return true in case of success, false in case of error.
23038
 
     * @public
23039
 
     * @since 4.9.015 (2010-04-20)
23040
 
     */
23041
 
    public function copyPage($page=0) {
23042
 
        if ($page == 0) {
23043
 
            // default value
23044
 
            $page = $this->page;
23045
 
        }
23046
 
        if (($page < 1) OR ($page > $this->numpages)) {
23047
 
            return false;
23048
 
        }
23049
 
        if ($page == $this->page) {
23050
 
            // close the page before cloning it
23051
 
            $this->endPage();
23052
 
        }
23053
 
        // copy all page-related states
23054
 
        ++$this->numpages;
23055
 
        $this->page = $this->numpages;
23056
 
        $this->pages[$this->page] = $this->pages[$page];
23057
 
        $this->pagedim[$this->page] = $this->pagedim[$page];
23058
 
        $this->pagelen[$this->page] = $this->pagelen[$page];
23059
 
        $this->intmrk[$this->page] = $this->intmrk[$page];
23060
 
        $this->bordermrk[$this->page] = $this->bordermrk[$page];
23061
 
        $this->cntmrk[$this->page] = $this->cntmrk[$page];
23062
 
        $this->pageopen[$this->page] = false;
23063
 
        if (isset($this->footerpos[$page])) {
23064
 
            $this->footerpos[$this->page] = $this->footerpos[$page];
23065
 
        }
23066
 
        if (isset($this->footerlen[$page])) {
23067
 
            $this->footerlen[$this->page] = $this->footerlen[$page];
23068
 
        }
23069
 
        if (isset($this->transfmrk[$page])) {
23070
 
            $this->transfmrk[$this->page] = $this->transfmrk[$page];
23071
 
        }
23072
 
        if (isset($this->PageAnnots[$page])) {
23073
 
            $this->PageAnnots[$this->page] = $this->PageAnnots[$page];
23074
 
        }
23075
 
        if (isset($this->newpagegroup[$page])) {
23076
 
            $this->newpagegroup[$this->page] = $this->newpagegroup[$page];
23077
 
        }
23078
 
        // copy outlines
23079
 
        $tmpoutlines = $this->outlines;
23080
 
        foreach ($tmpoutlines as $key => $outline) {
23081
 
            if ($outline['p'] == $page) {
23082
 
                $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'y' => $outline['y'], 'p' => $this->page);
23083
 
            }
23084
 
        }
23085
 
        // copy links
23086
 
        $tmplinks = $this->links;
23087
 
        foreach ($tmplinks as $key => $link) {
23088
 
            if ($link[0] == $page) {
23089
 
                $this->links[] = array($this->page, $link[1]);
23090
 
            }
23091
 
        }
23092
 
        // return to last page
23093
 
        $this->lastPage(true);
23094
 
        return true;
23095
 
    }
23096
 
 
23097
 
    /**
23098
 
     * Output a Table of Content Index (TOC).
23099
 
     * Before calling this method you have to open the page using the addTOCPage() method.
23100
 
     * After calling this method you have to call endTOCPage() to close the TOC page.
23101
 
     * You can override this method to achieve different styles.
23102
 
     * @param $page (int) page number where this TOC should be inserted (leave empty for current page).
23103
 
     * @param $numbersfont (string) set the font for page numbers (please use monospaced font for better alignment).
23104
 
     * @param $filler (string) string used to fill the space between text and page number.
23105
 
     * @param $toc_name (string) name to use for TOC bookmark.
23106
 
     * @public
23107
 
     * @author Nicola Asuni
23108
 
     * @since 4.5.000 (2009-01-02)
23109
 
     * @see addTOCPage(), endTOCPage(), addHTMLTOC()
23110
 
     */
23111
 
    public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC') {
23112
 
        $fontsize = $this->FontSizePt;
23113
 
        $fontfamily = $this->FontFamily;
23114
 
        $fontstyle = $this->FontStyle;
23115
 
        $w = $this->w - $this->lMargin - $this->rMargin;
23116
 
        $spacer = $this->GetStringWidth(chr(32)) * 4;
23117
 
        $page_first = $this->getPage();
23118
 
        $lmargin = $this->lMargin;
23119
 
        $rmargin = $this->rMargin;
23120
 
        $x_start = $this->GetX();
23121
 
        $current_page = $this->page;
23122
 
        $current_column = $this->current_column;
23123
 
        if ($this->empty_string($numbersfont)) {
23124
 
            $numbersfont = $this->default_monospaced_font;
23125
 
        }
23126
 
        if ($this->empty_string($filler)) {
23127
 
            $filler = ' ';
23128
 
        }
23129
 
        if ($this->empty_string($page)) {
23130
 
            $gap = ' ';
23131
 
        } else {
23132
 
            $gap = '';
23133
 
            if ($page < 1) {
23134
 
                $page = 1;
23135
 
            }
23136
 
        }
23137
 
        foreach ($this->outlines as $key => $outline) {
23138
 
            if ($this->rtl) {
23139
 
                $aligntext = 'R';
23140
 
                $alignnum = 'L';
23141
 
            } else {
23142
 
                $aligntext = 'L';
23143
 
                $alignnum = 'R';
23144
 
            }
23145
 
            if ($outline['l'] == 0) {
23146
 
                $this->SetFont($fontfamily, $fontstyle.'B', $fontsize);
23147
 
            } else {
23148
 
                $this->SetFont($fontfamily, $fontstyle, $fontsize - $outline['l']);
23149
 
            }
23150
 
            // check for page break
23151
 
            $this->checkPageBreak(($this->FontSize * $this->cell_height_ratio));
23152
 
            // set margins and X position
23153
 
            if (($this->page == $current_page) AND ($this->current_column == $current_column)) {
23154
 
                $this->lMargin = $lmargin;
23155
 
                $this->rMargin = $rmargin;
23156
 
            } else {
23157
 
                if ($this->current_column != $current_column) {
23158
 
                    if ($this->rtl) {
23159
 
                        $x_start = $this->w - $this->columns[$this->current_column]['x'];
23160
 
                    } else {
23161
 
                        $x_start = $this->columns[$this->current_column]['x'];
23162
 
                    }
23163
 
                }
23164
 
                $lmargin = $this->lMargin;
23165
 
                $rmargin = $this->rMargin;
23166
 
                $current_page = $this->page;
23167
 
                $current_column = $this->current_column;
23168
 
            }
23169
 
            $this->SetX($x_start);
23170
 
            $indent = ($spacer * $outline['l']);
23171
 
            if ($this->rtl) {
23172
 
                $this->rMargin += $indent;
23173
 
                $this->x -= $indent;
23174
 
            } else {
23175
 
                $this->lMargin += $indent;
23176
 
                $this->x += $indent;
23177
 
            }
23178
 
            $link = $this->AddLink();
23179
 
            $this->SetLink($link, $outline['y'], $outline['p']);
23180
 
            // write the text
23181
 
            $this->Write(0, $outline['t'], $link, 0, $aligntext, false, 0, false, false, 0);
23182
 
            $this->SetFont($numbersfont, $fontstyle, $fontsize);
23183
 
            if ($this->empty_string($page)) {
23184
 
                $pagenum = $outline['p'];
23185
 
            } else {
23186
 
                // placemark to be replaced with the correct number
23187
 
                $pagenum = '{#'.($outline['p']).'}';
23188
 
                if ($this->isUnicodeFont()) {
23189
 
                    $pagenum = '{'.$pagenum.'}';
23190
 
                }
23191
 
            }
23192
 
            $numwidth = $this->GetStringWidth($pagenum);
23193
 
            if ($this->rtl) {
23194
 
                $tw = $this->x - $this->lMargin;
23195
 
            } else {
23196
 
                $tw = $this->w - $this->rMargin - $this->x;
23197
 
            }
23198
 
            $fw = $tw - $numwidth - $this->GetStringWidth(chr(32));
23199
 
            $numfills = floor($fw / $this->GetStringWidth($filler));
23200
 
            if ($numfills > 0) {
23201
 
                $rowfill = str_repeat($filler, $numfills);
23202
 
            } else {
23203
 
                $rowfill = '';
23204
 
            }
23205
 
            if ($this->rtl) {
23206
 
                $pagenum = $pagenum.$gap.$rowfill.' ';
23207
 
            } else {
23208
 
                $pagenum = ' '.$rowfill.$gap.$pagenum;
23209
 
            }
23210
 
            // write the number
23211
 
            $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
23212
 
        }
23213
 
        $page_last = $this->getPage();
23214
 
        $numpages = $page_last - $page_first + 1;
23215
 
        if (!$this->empty_string($page)) {
23216
 
            for ($p = $page_first; $p <= $page_last; ++$p) {
23217
 
                // get page data
23218
 
                $temppage = $this->getPageBuffer($p);
23219
 
                for ($n = 1; $n <= $this->numpages; ++$n) {
23220
 
                    // update page numbers
23221
 
                    $k = '{#'.$n.'}';
23222
 
                    $ku = '{'.$k.'}';
23223
 
                    $alias_a = $this->_escape($k);
23224
 
                    $alias_au = $this->_escape($ku);
23225
 
                    if ($this->isunicode) {
23226
 
                        $alias_b = $this->_escape($this->UTF8ToLatin1($k));
23227
 
                        $alias_bu = $this->_escape($this->UTF8ToLatin1($ku));
23228
 
                        $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
23229
 
                        $alias_cu = $this->_escape($this->utf8StrRev($ku, false, $this->tmprtl));
23230
 
                    }
23231
 
                    if ($n >= $page) {
23232
 
                        $np = $n + $numpages;
23233
 
                    } else {
23234
 
                        $np = $n;
23235
 
                    }
23236
 
                    $ns = $this->formatTOCPageNumber($np);
23237
 
                    $nu = $ns;
23238
 
                    $sdiff = strlen($k) - strlen($ns) - 1;
23239
 
                    $sdiffu = strlen($ku) - strlen($ns) - 1;
23240
 
                    $sfill = str_repeat($filler, $sdiff);
23241
 
                    $sfillu = str_repeat($filler, $sdiffu);
23242
 
                    if ($this->rtl) {
23243
 
                        $ns = $ns.' '.$sfill;
23244
 
                        $nu = $nu.' '.$sfillu;
23245
 
                    } else {
23246
 
                        $ns = $sfill.' '.$ns;
23247
 
                        $nu = $sfillu.' '.$nu;
23248
 
                    }
23249
 
                    $nu = $this->UTF8ToUTF16BE($nu, false);
23250
 
                    $temppage = str_replace($alias_au, $nu, $temppage);
23251
 
                    if ($this->isunicode) {
23252
 
                        $temppage = str_replace($alias_bu, $nu, $temppage);
23253
 
                        $temppage = str_replace($alias_cu, $nu, $temppage);
23254
 
                        $temppage = str_replace($alias_b, $ns, $temppage);
23255
 
                        $temppage = str_replace($alias_c, $ns, $temppage);
23256
 
                    }
23257
 
                    $temppage = str_replace($alias_a, $ns, $temppage);
23258
 
                }
23259
 
                // save changes
23260
 
                $this->setPageBuffer($p, $temppage);
23261
 
            }
23262
 
            // move pages
23263
 
            $this->Bookmark($toc_name, 0, 0, $page_first);
23264
 
            for ($i = 0; $i < $numpages; ++$i) {
23265
 
                $this->movePage($page_last, $page);
23266
 
            }
23267
 
        }
23268
 
    }
23269
 
 
23270
 
    /**
23271
 
     * Output a Table Of Content Index (TOC) using HTML templates.
23272
 
     * Before calling this method you have to open the page using the addTOCPage() method.
23273
 
     * After calling this method you have to call endTOCPage() to close the TOC page.
23274
 
     * @param $page (int) page number where this TOC should be inserted (leave empty for current page).
23275
 
     * @param $toc_name (string) name to use for TOC bookmark.
23276
 
     * @param $templates (array) array of html templates. Use: "#TOC_DESCRIPTION#" for bookmark title, "#TOC_PAGE_NUMBER#" for page number.
23277
 
     * @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)
23278
 
     * @public
23279
 
     * @author Nicola Asuni
23280
 
     * @since 5.0.001 (2010-05-06)
23281
 
     * @see addTOCPage(), endTOCPage(), addTOC()
23282
 
     */
23283
 
    public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true) {
23284
 
        $prev_htmlLinkColorArray = $this->htmlLinkColorArray;
23285
 
        $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle;
23286
 
        // set new style for link
23287
 
        $this->htmlLinkColorArray = array();
23288
 
        $this->htmlLinkFontStyle = '';
23289
 
        $page_first = $this->getPage();
23290
 
        // get the font type used for numbers in each template
23291
 
        $current_font = $this->FontFamily;
23292
 
        foreach ($templates as $level => $html) {
23293
 
            $dom = $this->getHtmlDomArray($html);
23294
 
            foreach ($dom as $key => $value) {
23295
 
                if ($value['value'] == '#TOC_PAGE_NUMBER#') {
23296
 
                    $this->SetFont($dom[($key - 1)]['fontname']);
23297
 
                    $templates['F'.$level] = $this->isUnicodeFont();
23298
 
                }
23299
 
            }
23300
 
        }
23301
 
        $this->SetFont($current_font);
23302
 
        foreach ($this->outlines as $key => $outline) {
23303
 
            // get HTML template
23304
 
            $row = $templates[$outline['l']];
23305
 
            if ($this->empty_string($page)) {
23306
 
                $pagenum = $outline['p'];
23307
 
            } else {
23308
 
                // placemark to be replaced with the correct number
23309
 
                $pagenum = '{#'.($outline['p']).'}';
23310
 
                if ($templates['F'.$outline['l']]) {
23311
 
                    $pagenum = '{'.$pagenum.'}';
23312
 
                }
23313
 
            }
23314
 
            // replace templates with current values
23315
 
            $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
23316
 
            $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
23317
 
            // add link to page
23318
 
            $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
23319
 
            // write bookmark entry
23320
 
            $this->writeHTML($row, false, false, true, false, '');
23321
 
        }
23322
 
        // restore link styles
23323
 
        $this->htmlLinkColorArray = $prev_htmlLinkColorArray;
23324
 
        $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle;
23325
 
        // move TOC page and replace numbers
23326
 
        $page_last = $this->getPage();
23327
 
        $numpages = $page_last - $page_first + 1;
23328
 
        if (!$this->empty_string($page)) {
23329
 
            for ($p = $page_first; $p <= $page_last; ++$p) {
23330
 
                // get page data
23331
 
                $temppage = $this->getPageBuffer($p);
23332
 
                for ($n = 1; $n <= $this->numpages; ++$n) {
23333
 
                    // update page numbers
23334
 
                    $k = '{#'.$n.'}';
23335
 
                    $ku = '{'.$k.'}';
23336
 
                    $alias_a = $this->_escape($k);
23337
 
                    $alias_au = $this->_escape('{'.$k.'}');
23338
 
                    if ($this->isunicode) {
23339
 
                        $alias_b = $this->_escape($this->UTF8ToLatin1($k));
23340
 
                        $alias_bu = $this->_escape($this->UTF8ToLatin1($ku));
23341
 
                        $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
23342
 
                        $alias_cu = $this->_escape($this->utf8StrRev($ku, false, $this->tmprtl));
23343
 
                    }
23344
 
                    if ($n >= $page) {
23345
 
                        $np = $n + $numpages;
23346
 
                    } else {
23347
 
                        $np = $n;
23348
 
                    }
23349
 
                    $ns = $this->formatTOCPageNumber($np);
23350
 
                    $nu = $ns;
23351
 
                    if ($correct_align) {
23352
 
                        $sdiff = strlen($k) - strlen($ns);
23353
 
                        $sdiffu = strlen($ku) - strlen($ns);
23354
 
                        $sfill = str_repeat(' ', $sdiff);
23355
 
                        $sfillu = str_repeat(' ', $sdiffu);
23356
 
                        if ($this->rtl) {
23357
 
                            $ns = $ns.$sfill;
23358
 
                            $nu = $nu.$sfillu;
23359
 
                        } else {
23360
 
                            $ns = $sfill.$ns;
23361
 
                            $nu = $sfillu.$nu;
23362
 
                        }
23363
 
                    }
23364
 
                    $nu = $this->UTF8ToUTF16BE($nu, false);
23365
 
                    $temppage = str_replace($alias_au, $nu, $temppage);
23366
 
                    if ($this->isunicode) {
23367
 
                        $temppage = str_replace($alias_bu, $nu, $temppage);
23368
 
                        $temppage = str_replace($alias_cu, $nu, $temppage);
23369
 
                        $temppage = str_replace($alias_b, $ns, $temppage);
23370
 
                        $temppage = str_replace($alias_c, $ns, $temppage);
23371
 
                    }
23372
 
                    $temppage = str_replace($alias_a, $ns, $temppage);
23373
 
                }
23374
 
                // save changes
23375
 
                $this->setPageBuffer($p, $temppage);
23376
 
            }
23377
 
            // move pages
23378
 
            $this->Bookmark($toc_name, 0, 0, $page_first);
23379
 
            for ($i = 0; $i < $numpages; ++$i) {
23380
 
                $this->movePage($page_last, $page);
23381
 
            }
23382
 
        }
23383
 
    }
23384
 
 
23385
 
    /**
23386
 
     * Stores a copy of the current TCPDF object used for undo operation.
23387
 
     * @public
23388
 
     * @since 4.5.029 (2009-03-19)
23389
 
     */
23390
 
    public function startTransaction() {
23391
 
        if (isset($this->objcopy)) {
23392
 
            // remove previous copy
23393
 
            $this->commitTransaction();
23394
 
        }
23395
 
        // record current page number and Y position
23396
 
        $this->start_transaction_page = $this->page;
23397
 
        $this->start_transaction_y = $this->y;
23398
 
        // clone current object
23399
 
        $this->objcopy = $this->objclone($this);
23400
 
    }
23401
 
 
23402
 
    /**
23403
 
     * Delete the copy of the current TCPDF object used for undo operation.
23404
 
     * @public
23405
 
     * @since 4.5.029 (2009-03-19)
23406
 
     */
23407
 
    public function commitTransaction() {
23408
 
        if (isset($this->objcopy)) {
23409
 
            $this->objcopy->_destroy(true, true);
23410
 
            unset($this->objcopy);
23411
 
        }
23412
 
    }
23413
 
 
23414
 
    /**
23415
 
     * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction().
23416
 
     * @param $self (boolean) if true restores current class object to previous state without the need of reassignment via the returned value.
23417
 
     * @return TCPDF object.
23418
 
     * @public
23419
 
     * @since 4.5.029 (2009-03-19)
23420
 
     */
23421
 
    public function rollbackTransaction($self=false) {
23422
 
        if (isset($this->objcopy)) {
23423
 
            if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
23424
 
                // truncate files to previous values
23425
 
                foreach ($this->objcopy->cache_file_length as $file => $length) {
23426
 
                    $file = substr($file, 1);
23427
 
                    $handle = fopen($file, 'r+');
23428
 
                    ftruncate($handle, $length);
23429
 
                }
23430
 
            }
23431
 
            $this->_destroy(true, true);
23432
 
            if ($self) {
23433
 
                $objvars = get_object_vars($this->objcopy);
23434
 
                foreach ($objvars as $key => $value) {
23435
 
                    $this->$key = $value;
23436
 
                }
23437
 
            }
23438
 
            return $this->objcopy;
23439
 
        }
23440
 
        return $this;
23441
 
    }
23442
 
 
23443
 
    /**
23444
 
     * Creates a copy of a class object
23445
 
     * @param $object (object) class object to be cloned
23446
 
     * @return cloned object
23447
 
     * @public
23448
 
     * @since 4.5.029 (2009-03-19)
23449
 
     */
23450
 
    public function objclone($object) {
23451
 
        return @clone($object);
23452
 
    }
23453
 
 
23454
 
    /**
23455
 
     * Determine whether a string is empty.
23456
 
     * @param $str (string) string to be checked
23457
 
     * @return boolean true if string is empty
23458
 
     * @public
23459
 
     * @since 4.5.044 (2009-04-16)
23460
 
     */
23461
 
    public function empty_string($str) {
23462
 
        return (is_null($str) OR (is_string($str) AND (strlen($str) == 0)));
23463
 
    }
23464
 
 
23465
 
    /**
23466
 
     * Find position of last occurrence of a substring in a string
23467
 
     * @param $haystack (string) The string to search in.
23468
 
     * @param $needle (string) substring to search.
23469
 
     * @param $offset (int) May be specified to begin searching an arbitrary number of characters into the string.
23470
 
     * @return Returns the position where the needle exists. Returns FALSE if the needle was not found.
23471
 
     * @public
23472
 
     * @since 4.8.038 (2010-03-13)
23473
 
     */
23474
 
    public function revstrpos($haystack, $needle, $offset = 0) {
23475
 
        $length = strlen($haystack);
23476
 
        $offset = ($offset > 0)?($length - $offset):abs($offset);
23477
 
        $pos = strpos(strrev($haystack), strrev($needle), $offset);
23478
 
        return ($pos === false)?false:($length - $pos - strlen($needle));
23479
 
    }
23480
 
 
23481
 
    // --- MULTI COLUMNS METHODS -----------------------
23482
 
 
23483
 
    /**
23484
 
     * Set multiple columns of the same size
23485
 
     * @param $numcols (int) number of columns (set to zero to disable columns mode)
23486
 
     * @param $width (int) column width
23487
 
     * @param $y (int) column starting Y position (leave empty for current Y position)
23488
 
     * @public
23489
 
     * @since 4.9.001 (2010-03-28)
23490
 
     */
23491
 
    public function setEqualColumns($numcols=0, $width=0, $y='') {
23492
 
        $this->columns = array();
23493
 
        if ($numcols < 2) {
23494
 
            $numcols = 0;
23495
 
            $this->columns = array();
23496
 
        } else {
23497
 
            // maximum column width
23498
 
            $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols;
23499
 
            if (($width == 0) OR ($width > $maxwidth)) {
23500
 
                $width = $maxwidth;
23501
 
            }
23502
 
            if ($this->empty_string($y)) {
23503
 
                $y = $this->y;
23504
 
            }
23505
 
            // space between columns
23506
 
            $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1));
23507
 
            // fill the columns array (with, space, starting Y position)
23508
 
            for ($i = 0; $i < $numcols; ++$i) {
23509
 
                $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y);
23510
 
            }
23511
 
        }
23512
 
        $this->num_columns = $numcols;
23513
 
        $this->current_column = 0;
23514
 
        $this->column_start_page = $this->page;
23515
 
    }
23516
 
 
23517
 
    /**
23518
 
     * Set columns array.
23519
 
     * Each column is represented by an array of arrays with the following keys: (w = width, s = space between columns, y = column top position).
23520
 
     * @param $columns (array)
23521
 
     * @public
23522
 
     * @since 4.9.001 (2010-03-28)
23523
 
     */
23524
 
    public function setColumnsArray($columns) {
23525
 
        $this->columns = $columns;
23526
 
        $this->num_columns = count($columns);
23527
 
        $this->current_column = 0;
23528
 
        $this->column_start_page = $this->page;
23529
 
    }
23530
 
 
23531
 
    /**
23532
 
     * Set position at a given column
23533
 
     * @param $col (int) column number (from 0 to getNumberOfColumns()-1); empty string = current column.
23534
 
     * @public
23535
 
     * @since 4.9.001 (2010-03-28)
23536
 
     */
23537
 
    public function selectColumn($col='') {
23538
 
        if (is_string($col)) {
23539
 
            $col = $this->current_column;
23540
 
        } elseif($col >= $this->num_columns) {
23541
 
            $col = 0;
23542
 
        }
23543
 
        $xshift = 0;
23544
 
        $enable_thead = false;
23545
 
        if ($this->num_columns > 1) {
23546
 
            if ($col != $this->current_column) {
23547
 
                // move Y pointer at the top of the column
23548
 
                if ($this->column_start_page == $this->page) {
23549
 
                    $this->y = $this->columns[$col]['y'];
23550
 
                } else {
23551
 
                    $this->y = $this->tMargin;
23552
 
                }
23553
 
                // Avoid to write table headers more than once
23554
 
                if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) {
23555
 
                    $enable_thead = true;
23556
 
                    $this->maxselcol['page'] = $this->page;
23557
 
                    $this->maxselcol['column'] = $col;
23558
 
                }
23559
 
            }
23560
 
            $xshift = $this->colxshift;
23561
 
            // set X position of the current column by case
23562
 
            $listindent = ($this->listindentlevel * $this->listindent);
23563
 
            $colpos = ($col * ($this->columns[$col]['w'] + $this->columns[$col]['s']));
23564
 
            if ($this->rtl) {
23565
 
                $x = $this->w - $this->original_rMargin - $colpos;
23566
 
                $this->rMargin = ($this->w - $x + $listindent);
23567
 
                $this->lMargin = ($x - $this->columns[$col]['w']);
23568
 
                $this->x = $x - $listindent;
23569
 
            } else {
23570
 
                $x = $this->original_lMargin + $colpos;
23571
 
                $this->lMargin = ($x + $listindent);
23572
 
                $this->rMargin = ($this->w - $x - $this->columns[$col]['w']);
23573
 
                $this->x = $x + $listindent;
23574
 
            }
23575
 
            $this->columns[$col]['x'] = $x;
23576
 
        }
23577
 
        $this->current_column = $col;
23578
 
        // fix for HTML mode
23579
 
        $this->newline = true;
23580
 
        // print HTML table header (if any)
23581
 
        if ((!$this->empty_string($this->thead)) AND (!$this->inthead)) {
23582
 
            if ($enable_thead) {
23583
 
                // print table header
23584
 
                $this->writeHTML($this->thead, false, false, false, false, '');
23585
 
                $this->y += $xshift['s']['V'];
23586
 
                // store end of header position
23587
 
                if (!isset($this->columns[$col]['th'])) {
23588
 
                    $this->columns[$col]['th'] = array();
23589
 
                }
23590
 
                $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y;
23591
 
                $this->lasth = 0;
23592
 
            } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) {
23593
 
                $this->y = $this->columns[$col]['th']['\''.$this->page.'\''];
23594
 
            }
23595
 
        }
23596
 
        // account for an html table cell over multiple columns
23597
 
        if ($this->rtl) {
23598
 
            $this->rMargin += $xshift['x'];
23599
 
            $this->x -= ($xshift['x'] + $xshift['p']['R']);
23600
 
        } else {
23601
 
            $this->lMargin += $xshift['x'];
23602
 
            $this->x += $xshift['x'] + $xshift['p']['L'];
23603
 
        }
23604
 
    }
23605
 
 
23606
 
    /**
23607
 
     * Return the current column number
23608
 
     * @return int current column number
23609
 
     * @public
23610
 
     * @since 5.5.011 (2010-07-08)
23611
 
     */
23612
 
    public function getColumn() {
23613
 
        return $this->current_column;
23614
 
    }
23615
 
 
23616
 
    /**
23617
 
     * Return the current number of columns.
23618
 
     * @return int number of columns
23619
 
     * @public
23620
 
     * @since 5.8.018 (2010-08-25)
23621
 
     */
23622
 
    public function getNumberOfColumns() {
23623
 
        return $this->num_columns;
23624
 
    }
23625
 
 
23626
 
    /**
23627
 
     * Serialize an array of parameters to be used with TCPDF tag in HTML code.
23628
 
     * @param $pararray (array) parameters array
23629
 
     * @return sting containing serialized data
23630
 
     * @public
23631
 
     * @since 4.9.006 (2010-04-02)
23632
 
     */
23633
 
    public function serializeTCPDFtagParameters($pararray) {
23634
 
        return urlencode(serialize($pararray));
23635
 
    }
23636
 
 
23637
 
    /**
23638
 
     * Set Text rendering mode.
23639
 
     * @param $stroke (int) outline size in user units (0 = disable).
23640
 
     * @param $fill (boolean) if true fills the text (default).
23641
 
     * @param $clip (boolean) if true activate clipping mode
23642
 
     * @public
23643
 
     * @since 4.9.008 (2009-04-02)
23644
 
     */
23645
 
    public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
23646
 
        // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
23647
 
        // convert text rendering parameters
23648
 
        if ($stroke < 0) {
23649
 
            $stroke = 0;
23650
 
        }
23651
 
        if ($fill === true) {
23652
 
            if ($stroke > 0) {
23653
 
                if ($clip === true) {
23654
 
                    // Fill, then stroke text and add to path for clipping
23655
 
                    $textrendermode = 6;
23656
 
                } else {
23657
 
                    // Fill, then stroke text
23658
 
                    $textrendermode = 2;
23659
 
                }
23660
 
                $textstrokewidth = $stroke;
23661
 
            } else {
23662
 
                if ($clip === true) {
23663
 
                    // Fill text and add to path for clipping
23664
 
                    $textrendermode = 4;
23665
 
                } else {
23666
 
                    // Fill text
23667
 
                    $textrendermode = 0;
23668
 
                }
23669
 
            }
23670
 
        } else {
23671
 
            if ($stroke > 0) {
23672
 
                if ($clip === true) {
23673
 
                    // Stroke text and add to path for clipping
23674
 
                    $textrendermode = 5;
23675
 
                } else {
23676
 
                    // Stroke text
23677
 
                    $textrendermode = 1;
23678
 
                }
23679
 
                $textstrokewidth = $stroke;
23680
 
            } else {
23681
 
                if ($clip === true) {
23682
 
                    // Add text to path for clipping
23683
 
                    $textrendermode = 7;
23684
 
                } else {
23685
 
                    // Neither fill nor stroke text (invisible)
23686
 
                    $textrendermode = 3;
23687
 
                }
23688
 
            }
23689
 
        }
23690
 
        $this->textrendermode = $textrendermode;
23691
 
        $this->textstrokewidth = $stroke * $this->k;
23692
 
    }
23693
 
 
23694
 
    /**
23695
 
     * Returns an array of chars containing soft hyphens.
23696
 
     * @param $word (array) array of chars
23697
 
     * @param $patterns (array) Array of hypenation patterns.
23698
 
     * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm.
23699
 
     * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
23700
 
     * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
23701
 
     * @param $charmin (int) Minimum word lenght to apply the hyphenation algoritm.
23702
 
     * @param $charmax (int) Maximum lenght of broken piece of word.
23703
 
     * @return array text with soft hyphens
23704
 
     * @author Nicola Asuni
23705
 
     * @since 4.9.012 (2010-04-12)
23706
 
     * @protected
23707
 
     */
23708
 
    protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
23709
 
        $hyphenword = array(); // hyphens positions
23710
 
        $numchars = count($word);
23711
 
        if ($numchars <= $charmin) {
23712
 
            return $word;
23713
 
        }
23714
 
        $word_string = $this->UTF8ArrSubString($word);
23715
 
        // some words will be returned as-is
23716
 
        $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})(\]?)$/';
23717
 
        if (preg_match($pattern, $word_string) > 0) {
23718
 
            // email
23719
 
            return $word;
23720
 
        }
23721
 
        $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})(\]?)$/';
23722
 
        if (preg_match($pattern, $word_string) > 0) {
23723
 
            // URL
23724
 
            return $word;
23725
 
        }
23726
 
        if (isset($dictionary[$word_string])) {
23727
 
            return $this->UTF8StringToArray($dictionary[$word_string]);
23728
 
        }
23729
 
        // suround word with '_' characters
23730
 
        $tmpword = array_merge(array(95), $word, array(95));
23731
 
        $tmpnumchars = $numchars + 2;
23732
 
        $maxpos = $tmpnumchars - $charmin;
23733
 
        for ($pos = 0; $pos < $maxpos; ++$pos) {
23734
 
            $imax = min(($tmpnumchars - $pos), $charmax);
23735
 
            for ($i = $charmin; $i <= $imax; ++$i) {
23736
 
                $subword = strtolower($this->UTF8ArrSubString($tmpword, $pos, $pos + $i));
23737
 
                if (isset($patterns[$subword])) {
23738
 
                    $pattern = $this->UTF8StringToArray($patterns[$subword]);
23739
 
                    $pattern_length = count($pattern);
23740
 
                    $digits = 1;
23741
 
                    for ($j = 0; $j < $pattern_length; ++$j) {
23742
 
                        // check if $pattern[$j] is a number
23743
 
                        if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
23744
 
                            if ($j == 0) {
23745
 
                                $zero = $pos - 1;
23746
 
                            } else {
23747
 
                                $zero = $pos + $j - $digits;
23748
 
                            }
23749
 
                            if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] != $pattern[$j])) {
23750
 
                                $hyphenword[$zero] = $this->unichr($pattern[$j]);
23751
 
                            }
23752
 
                            ++$digits;
23753
 
                        }
23754
 
                    }
23755
 
                }
23756
 
            }
23757
 
        }
23758
 
        $inserted = 0;
23759
 
        $maxpos = $numchars - $rightmin;
23760
 
        for($i = $leftmin; $i <= $maxpos; ++$i) {
23761
 
            if(isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) {
23762
 
                // 173 = soft hyphen character
23763
 
                array_splice($word, $i + $inserted, 0, 173);
23764
 
                ++$inserted;
23765
 
            }
23766
 
        }
23767
 
        return $word;
23768
 
    }
23769
 
 
23770
 
    /**
23771
 
     * Returns an array of hyphenation patterns.
23772
 
     * @param $file (string) TEX file containing hypenation patterns. TEX pattrns can be downloaded from http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/
23773
 
     * @return array of hyphenation patterns
23774
 
     * @author Nicola Asuni
23775
 
     * @since 4.9.012 (2010-04-12)
23776
 
     * @public
23777
 
     */
23778
 
    public function getHyphenPatternsFromTEX($file) {
23779
 
        // TEX patterns are available at:
23780
 
        // http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/
23781
 
        $data = file_get_contents($file);
23782
 
        $patterns = array();
23783
 
        // remove comments
23784
 
        $data = preg_replace('/\%[^\n]*/', '', $data);
23785
 
        // extract the patterns part
23786
 
        preg_match('/\\\\patterns\{([^\}]*)\}/i', $data, $matches);
23787
 
        $data = trim(substr($matches[0], 10, -1));
23788
 
        // extract each pattern
23789
 
        $patterns_array = preg_split('/[\s]+/', $data);
23790
 
        // create new language array of patterns
23791
 
        $patterns = array();
23792
 
        foreach($patterns_array as $val) {
23793
 
            if (!$this->empty_string($val)) {
23794
 
                $val = trim($val);
23795
 
                $val = str_replace('\'', '\\\'', $val);
23796
 
                $key = preg_replace('/[0-9]+/', '', $val);
23797
 
                $patterns[$key] = $val;
23798
 
            }
23799
 
        }
23800
 
        return $patterns;
23801
 
    }
23802
 
 
23803
 
    /**
23804
 
     * Returns text with soft hyphens.
23805
 
     * @param $text (string) text to process
23806
 
     * @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/
23807
 
     * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm.
23808
 
     * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
23809
 
     * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
23810
 
     * @param $charmin (int) Minimum word lenght to apply the hyphenation algoritm.
23811
 
     * @param $charmax (int) Maximum lenght of broken piece of word.
23812
 
     * @return array text with soft hyphens
23813
 
     * @author Nicola Asuni
23814
 
     * @since 4.9.012 (2010-04-12)
23815
 
     * @public
23816
 
     */
23817
 
    public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
23818
 
        $text = $this->unhtmlentities($text);
23819
 
        $word = array(); // last word
23820
 
        $txtarr = array(); // text to be returned
23821
 
        $intag = false; // true if we are inside an HTML tag
23822
 
        if (!is_array($patterns)) {
23823
 
            $patterns = $this->getHyphenPatternsFromTEX($patterns);
23824
 
        }
23825
 
        // get array of characters
23826
 
        $unichars = $this->UTF8StringToArray($text);
23827
 
        // for each char
23828
 
        foreach ($unichars as $char) {
23829
 
            if ((!$intag) AND $this->unicode->uni_type[$char] == 'L') {
23830
 
                // letter character
23831
 
                $word[] = $char;
23832
 
            } else {
23833
 
                // other type of character
23834
 
                if (!$this->empty_string($word)) {
23835
 
                    // hypenate the word
23836
 
                    $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
23837
 
                    $word = array();
23838
 
                }
23839
 
                $txtarr[] = $char;
23840
 
                if (chr($char) == '<') {
23841
 
                    // we are inside an HTML tag
23842
 
                    $intag = true;
23843
 
                } elseif ($intag AND (chr($char) == '>')) {
23844
 
                    // end of HTML tag
23845
 
                    $intag = false;
23846
 
                }
23847
 
            }
23848
 
        }
23849
 
        if (!$this->empty_string($word)) {
23850
 
            // hypenate the word
23851
 
            $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
23852
 
        }
23853
 
        // convert char array to string and return
23854
 
        return $this->UTF8ArrSubString($txtarr);
23855
 
    }
23856
 
 
23857
 
    /**
23858
 
     * Enable/disable rasterization of vector images using ImageMagick library.
23859
 
     * @param $mode (boolean) if true enable rasterization, false otherwise.
23860
 
     * @public
23861
 
     * @since 5.0.000 (2010-04-27)
23862
 
     */
23863
 
    public function setRasterizeVectorImages($mode) {
23864
 
        $this->rasterize_vector_images = $mode;
23865
 
    }
23866
 
 
23867
 
    /**
23868
 
     * Get the Path-Painting Operators.
23869
 
     * @param $style (string) Style of rendering. Possible values are:
23870
 
     * <ul>
23871
 
     *   <li>S or D: Stroke the path.</li>
23872
 
     *   <li>s or d: Close and stroke the path.</li>
23873
 
     *   <li>f or F: Fill the path, using the nonzero winding number rule to determine the region to fill.</li>
23874
 
     *   <li>f* or F*: Fill the path, using the even-odd rule to determine the region to fill.</li>
23875
 
     *   <li>B or FD or DF: Fill and then stroke the path, using the nonzero winding number rule to determine the region to fill.</li>
23876
 
     *   <li>B* or F*D or DF*: Fill and then stroke the path, using the even-odd rule to determine the region to fill.</li>
23877
 
     *   <li>b or fd or df: Close, fill, and then stroke the path, using the nonzero winding number rule to determine the region to fill.</li>
23878
 
     *   <li>b or f*d or df*: Close, fill, and then stroke the path, using the even-odd rule to determine the region to fill.</li>
23879
 
     *   <li>CNZ: Clipping mode using the even-odd rule to determine which regions lie inside the clipping path.</li>
23880
 
     *   <li>CEO: Clipping mode using the nonzero winding number rule to determine which regions lie inside the clipping path</li>
23881
 
     *   <li>n: End the path object without filling or stroking it.</li>
23882
 
     * </ul>
23883
 
     * @param $default (string) default style
23884
 
     * @author Nicola Asuni
23885
 
     * @since 5.0.000 (2010-04-30)
23886
 
     * @protected
23887
 
     */
23888
 
    protected function getPathPaintOperator($style, $default='S') {
23889
 
        $op = '';
23890
 
        switch($style) {
23891
 
            case 'S':
23892
 
            case 'D': {
23893
 
                $op = 'S';
23894
 
                break;
23895
 
            }
23896
 
            case 's':
23897
 
            case 'd': {
23898
 
                $op = 's';
23899
 
                break;
23900
 
            }
23901
 
            case 'f':
23902
 
            case 'F': {
23903
 
                $op = 'f';
23904
 
                break;
23905
 
            }
23906
 
            case 'f*':
23907
 
            case 'F*': {
23908
 
                $op = 'f*';
23909
 
                break;
23910
 
            }
23911
 
            case 'B':
23912
 
            case 'FD':
23913
 
            case 'DF': {
23914
 
                $op = 'B';
23915
 
                break;
23916
 
            }
23917
 
            case 'B*':
23918
 
            case 'F*D':
23919
 
            case 'DF*': {
23920
 
                $op = 'B*';
23921
 
                break;
23922
 
            }
23923
 
            case 'b':
23924
 
            case 'fd':
23925
 
            case 'df': {
23926
 
                $op = 'b';
23927
 
                break;
23928
 
            }
23929
 
            case 'b*':
23930
 
            case 'f*d':
23931
 
            case 'df*': {
23932
 
                $op = 'b*';
23933
 
                break;
23934
 
            }
23935
 
            case 'CNZ': {
23936
 
                $op = 'W n';
23937
 
                break;
23938
 
            }
23939
 
            case 'CEO': {
23940
 
                $op = 'W* n';
23941
 
                break;
23942
 
            }
23943
 
            case 'n': {
23944
 
                $op = 'n';
23945
 
                break;
23946
 
            }
23947
 
            default: {
23948
 
                if (!empty($default)) {
23949
 
                    $op = $this->getPathPaintOperator($default, '');
23950
 
                } else {
23951
 
                    $op = '';
23952
 
                }
23953
 
            }
23954
 
        }
23955
 
        return $op;
23956
 
    }
23957
 
 
23958
 
    /**
23959
 
     * Enable or disable default option for font subsetting.
23960
 
     * @param $enable (boolean) if true enable font subsetting by default.
23961
 
     * @author Nicola Asuni
23962
 
     * @public
23963
 
     * @since 5.3.002 (2010-06-07)
23964
 
     */
23965
 
    public function setFontSubsetting($enable=true) {
23966
 
        $this->font_subsetting = $enable ? true : false;
23967
 
    }
23968
 
 
23969
 
    /**
23970
 
     * Return the default option for font subsetting.
23971
 
     * @return boolean default font subsetting state.
23972
 
     * @author Nicola Asuni
23973
 
     * @public
23974
 
     * @since 5.3.002 (2010-06-07)
23975
 
     */
23976
 
    public function getFontSubsetting() {
23977
 
        return $this->font_subsetting;
23978
 
    }
23979
 
 
23980
 
    /**
23981
 
     * Left trim the input string
23982
 
     * @param $str (string) string to trim
23983
 
     * @param $replace (string) string that replace spaces.
23984
 
     * @return left trimmed string
23985
 
     * @author Nicola Asuni
23986
 
     * @public
23987
 
     * @since 5.8.000 (2010-08-11)
23988
 
     */
23989
 
    public function stringLeftTrim($str, $replace='') {
23990
 
        return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str);
23991
 
    }
23992
 
 
23993
 
    /**
23994
 
     * Right trim the input string
23995
 
     * @param $str (string) string to trim
23996
 
     * @param $replace (string) string that replace spaces.
23997
 
     * @return right trimmed string
23998
 
     * @author Nicola Asuni
23999
 
     * @public
24000
 
     * @since 5.8.000 (2010-08-11)
24001
 
     */
24002
 
    public function stringRightTrim($str, $replace='') {
24003
 
        return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str);
24004
 
    }
24005
 
 
24006
 
    /**
24007
 
     * Trim the input string
24008
 
     * @param $str (string) string to trim
24009
 
     * @param $replace (string) string that replace spaces.
24010
 
     * @return trimmed string
24011
 
     * @author Nicola Asuni
24012
 
     * @public
24013
 
     * @since 5.8.000 (2010-08-11)
24014
 
     */
24015
 
    public function stringTrim($str, $replace='') {
24016
 
        $str = $this->stringLeftTrim($str, $replace);
24017
 
        $str = $this->stringRightTrim($str, $replace);
24018
 
        return $str;
24019
 
    }
24020
 
 
24021
 
    /**
24022
 
     * Return true if the current font is unicode type.
24023
 
     * @return true for unicode font, false otherwise.
24024
 
     * @author Nicola Asuni
24025
 
     * @public
24026
 
     * @since 5.8.002 (2010-08-14)
24027
 
     */
24028
 
    public function isUnicodeFont() {
24029
 
        return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0'));
24030
 
    }
24031
 
 
24032
 
    /**
24033
 
     * Return normalized font name
24034
 
     * @param $fontfamily (string) property string containing font family names
24035
 
     * @return string normalized font name
24036
 
     * @author Nicola Asuni
24037
 
     * @public
24038
 
     * @since 5.8.004 (2010-08-17)
24039
 
     */
24040
 
    public function getFontFamilyName($fontfamily) {
24041
 
        // remove spaces and symbols
24042
 
        $fontfamily = preg_replace('/[^a-z0-9\,]/', '', strtolower($fontfamily));
24043
 
        // extract all font names
24044
 
        $fontslist = preg_split('/[,]/', $fontfamily);
24045
 
        // find first valid font name
24046
 
        foreach ($fontslist as $font) {
24047
 
            // replace font variations
24048
 
            $font = preg_replace('/italic$/', 'I', $font);
24049
 
            $font = preg_replace('/oblique$/', 'I', $font);
24050
 
            $font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
24051
 
            // replace common family names and core fonts
24052
 
            $pattern = array();
24053
 
            $replacement = array();
24054
 
            $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
24055
 
            $replacement[] = 'times';
24056
 
            $pattern[] = '/^sansserif/';
24057
 
            $replacement[] = 'helvetica';
24058
 
            $pattern[] = '/^monospace/';
24059
 
            $replacement[] = 'courier';
24060
 
            $font = preg_replace($pattern, $replacement, $font);
24061
 
            if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) {
24062
 
                return $font;
24063
 
            }
24064
 
        }
24065
 
        // return current font as default
24066
 
        return $this->CurrentFont['fontkey'];
24067
 
    }
24068
 
 
24069
 
    /**
24070
 
     * Start a new XObject Template.
24071
 
     * 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).
24072
 
     * 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.
24073
 
     * Note: X,Y coordinates will be reset to 0,0.
24074
 
     * @param $w (int) Template width in user units (empty string or zero = page width less margins)
24075
 
     * @param $h (int) Template height in user units (empty string or zero = page height less margins)
24076
 
     * @return int the XObject Template ID in case of success or false in case of error.
24077
 
     * @author Nicola Asuni
24078
 
     * @public
24079
 
     * @since 5.8.017 (2010-08-24)
24080
 
     * @see endTemplate(), printTemplate()
24081
 
     */
24082
 
    public function startTemplate($w=0, $h=0) {
24083
 
        if ($this->inxobj) {
24084
 
            // we are already inside an XObject template
24085
 
            return false;
24086
 
        }
24087
 
        $this->inxobj = true;
24088
 
        ++$this->n;
24089
 
        // XObject ID
24090
 
        $this->xobjid = 'XT'.$this->n;
24091
 
        // object ID
24092
 
        $this->xobjects[$this->xobjid] = array('n' => $this->n);
24093
 
        // store current graphic state
24094
 
        $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars();
24095
 
        // initialize data
24096
 
        $this->xobjects[$this->xobjid]['intmrk'] = 0;
24097
 
        $this->xobjects[$this->xobjid]['transfmrk'] = array();
24098
 
        $this->xobjects[$this->xobjid]['outdata'] = '';
24099
 
        $this->xobjects[$this->xobjid]['xobjects'] = array();
24100
 
        $this->xobjects[$this->xobjid]['images'] = array();
24101
 
        $this->xobjects[$this->xobjid]['fonts'] = array();
24102
 
        $this->xobjects[$this->xobjid]['annotations'] = array();
24103
 
        // set new environment
24104
 
        $this->num_columns = 1;
24105
 
        $this->current_column = 0;
24106
 
        $this->SetAutoPageBreak(false);
24107
 
        if (($w === '') OR ($w <= 0)) {
24108
 
            $w = $this->w - $this->lMargin - $this->rMargin;
24109
 
        }
24110
 
        if (($h === '') OR ($h <= 0)) {
24111
 
            $h = $this->h - $this->tMargin - $this->bMargin;
24112
 
        }
24113
 
        $this->xobjects[$this->xobjid]['x'] = 0;
24114
 
        $this->xobjects[$this->xobjid]['y'] = 0;
24115
 
        $this->xobjects[$this->xobjid]['w'] = $w;
24116
 
        $this->xobjects[$this->xobjid]['h'] = $h;
24117
 
        $this->w = $w;
24118
 
        $this->h = $h;
24119
 
        $this->wPt = $this->w * $this->k;
24120
 
        $this->hPt = $this->h * $this->k;
24121
 
        $this->fwPt = $this->wPt;
24122
 
        $this->fhPt = $this->hPt;
24123
 
        $this->x = 0;
24124
 
        $this->y = 0;
24125
 
        $this->lMargin = 0;
24126
 
        $this->rMargin = 0;
24127
 
        $this->tMargin = 0;
24128
 
        $this->bMargin = 0;
24129
 
        return $this->xobjid;
24130
 
    }
24131
 
 
24132
 
    /**
24133
 
     * End the current XObject Template started with startTemplate() and restore the previous graphic state.
24134
 
     * 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).
24135
 
     * 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.
24136
 
     * @return int the XObject Template ID in case of success or false in case of error.
24137
 
     * @author Nicola Asuni
24138
 
     * @public
24139
 
     * @since 5.8.017 (2010-08-24)
24140
 
     * @see startTemplate(), printTemplate()
24141
 
     */
24142
 
    public function endTemplate() {
24143
 
        if (!$this->inxobj) {
24144
 
            // we are not inside a template
24145
 
            return false;
24146
 
        }
24147
 
        $this->inxobj = false;
24148
 
        // restore previous graphic state
24149
 
        $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true);
24150
 
        return $this->xobjid;
24151
 
    }
24152
 
 
24153
 
    /**
24154
 
     * Print an XObject Template.
24155
 
     * You can print an XObject Template inside the currently opened Template.
24156
 
     * 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).
24157
 
     * 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.
24158
 
     * @param $id (string) The ID of XObject Template to print.
24159
 
     * @param $x (int) X position in user units (empty string = current x position)
24160
 
     * @param $y (int) Y position in user units (empty string = current y position)
24161
 
     * @param $w (int) Width in user units (zero = remaining page width)
24162
 
     * @param $h (int) Height in user units (zero = remaining page height)
24163
 
     * @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>
24164
 
     * @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>
24165
 
     * @param $fitonpage (boolean) if true the template is resized to not exceed page dimensions.
24166
 
     * @author Nicola Asuni
24167
 
     * @public
24168
 
     * @since 5.8.017 (2010-08-24)
24169
 
     * @see startTemplate(), endTemplate()
24170
 
     */
24171
 
    public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
24172
 
        if (!isset($this->xobjects[$id])) {
24173
 
            $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
24174
 
        }
24175
 
        if ($this->inxobj) {
24176
 
            if ($id == $this->xobjid) {
24177
 
                // close current template
24178
 
                $this->endTemplate();
24179
 
            } else {
24180
 
                // use the template as resource for the template currently opened
24181
 
                $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id];
24182
 
            }
24183
 
        }
24184
 
        // set default values
24185
 
        if ($x === '') {
24186
 
            $x = $this->x;
24187
 
        }
24188
 
        if ($y === '') {
24189
 
            $y = $this->y;
24190
 
        }
24191
 
        // check page for no-write regions and adapt page margins if necessary
24192
 
        $this->checkPageRegions($h, $x, $y);
24193
 
        $ow = $this->xobjects[$id]['w'];
24194
 
        $oh = $this->xobjects[$id]['h'];
24195
 
        // calculate template width and height on document
24196
 
        if (($w <= 0) AND ($h <= 0)) {
24197
 
            $w = $ow;
24198
 
            $h = $oh;
24199
 
        } elseif ($w <= 0) {
24200
 
            $w = $h * $ow / $oh;
24201
 
        } elseif ($h <= 0) {
24202
 
            $h = $w * $oh / $ow;
24203
 
        }
24204
 
        // fit the template on available space
24205
 
        $this->fitBlock($w, $h, $x, $y, $fitonpage);
24206
 
        // set page alignment
24207
 
        $rb_y = $y + $h;
24208
 
        // set alignment
24209
 
        if ($this->rtl) {
24210
 
            if ($palign == 'L') {
24211
 
                $xt = $this->lMargin;
24212
 
            } elseif ($palign == 'C') {
24213
 
                $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
24214
 
            } elseif ($palign == 'R') {
24215
 
                $xt = $this->w - $this->rMargin - $w;
24216
 
            } else {
24217
 
                $xt = $x - $w;
24218
 
            }
24219
 
            $rb_x = $xt;
24220
 
        } else {
24221
 
            if ($palign == 'L') {
24222
 
                $xt = $this->lMargin;
24223
 
            } elseif ($palign == 'C') {
24224
 
                $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
24225
 
            } elseif ($palign == 'R') {
24226
 
                $xt = $this->w - $this->rMargin - $w;
24227
 
            } else {
24228
 
                $xt = $x;
24229
 
            }
24230
 
            $rb_x = $xt + $w;
24231
 
        }
24232
 
        // print XObject Template + Transformation matrix
24233
 
        $this->StartTransform();
24234
 
        // translate and scale
24235
 
        $sx = ($w / $this->xobjects[$id]['w']);
24236
 
        $sy = ($h / $this->xobjects[$id]['h']);
24237
 
        $tm = array();
24238
 
        $tm[0] = $sx;
24239
 
        $tm[1] = 0;
24240
 
        $tm[2] = 0;
24241
 
        $tm[3] = $sy;
24242
 
        $tm[4] = $xt * $this->k;
24243
 
        $tm[5] = ($this->h - $h - $y) * $this->k;
24244
 
        $this->Transform($tm);
24245
 
        // set object
24246
 
        $this->_out('/'.$id.' Do');
24247
 
        $this->StopTransform();
24248
 
        // add annotations
24249
 
        if (!empty($this->xobjects[$id]['annotations'])) {
24250
 
            foreach ($this->xobjects[$id]['annotations'] as $annot) {
24251
 
                // transform original coordinates
24252
 
                $coordlt = $this->getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k)));
24253
 
                $ax = ($coordlt[4] / $this->k);
24254
 
                $ay = ($this->h - $h - ($coordlt[5] / $this->k));
24255
 
                $coordrb = $this->getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k)));
24256
 
                $aw = ($coordrb[4] / $this->k) - $ax;
24257
 
                $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay;
24258
 
                $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
24259
 
            }
24260
 
        }
24261
 
        // set pointer to align the next text/objects
24262
 
        switch($align) {
24263
 
            case 'T': {
24264
 
                $this->y = $y;
24265
 
                $this->x = $rb_x;
24266
 
                break;
24267
 
            }
24268
 
            case 'M': {
24269
 
                $this->y = $y + round($h/2);
24270
 
                $this->x = $rb_x;
24271
 
                break;
24272
 
            }
24273
 
            case 'B': {
24274
 
                $this->y = $rb_y;
24275
 
                $this->x = $rb_x;
24276
 
                break;
24277
 
            }
24278
 
            case 'N': {
24279
 
                $this->SetY($rb_y);
24280
 
                break;
24281
 
            }
24282
 
            default:{
24283
 
                break;
24284
 
            }
24285
 
        }
24286
 
    }
24287
 
 
24288
 
    /**
24289
 
     * Set the percentage of character stretching.
24290
 
     * @param $perc (int) percentage of stretching (100 = no stretching)
24291
 
     * @author Nicola Asuni
24292
 
     * @public
24293
 
     * @since 5.9.000 (2010-09-29)
24294
 
     */
24295
 
    public function setFontStretching($perc=100) {
24296
 
        $this->font_stretching = $perc;
24297
 
    }
24298
 
 
24299
 
    /**
24300
 
     * Get the percentage of character stretching.
24301
 
     * @return float stretching value
24302
 
     * @author Nicola Asuni
24303
 
     * @public
24304
 
     * @since 5.9.000 (2010-09-29)
24305
 
     */
24306
 
    public function getFontStretching() {
24307
 
        return $this->font_stretching;
24308
 
    }
24309
 
 
24310
 
    /**
24311
 
     * Set the amount to increase or decrease the space between characters in a text.
24312
 
     * @param $spacing (float) amount to increase or decrease the space between characters in a text (0 = default spacing)
24313
 
     * @author Nicola Asuni
24314
 
     * @public
24315
 
     * @since 5.9.000 (2010-09-29)
24316
 
     */
24317
 
    public function setFontSpacing($spacing=0) {
24318
 
        $this->font_spacing = $spacing;
24319
 
    }
24320
 
 
24321
 
    /**
24322
 
     * Get the amount to increase or decrease the space between characters in a text.
24323
 
     * @return int font spacing (tracking/kerning) value
24324
 
     * @author Nicola Asuni
24325
 
     * @public
24326
 
     * @since 5.9.000 (2010-09-29)
24327
 
     */
24328
 
    public function getFontSpacing() {
24329
 
        return $this->font_spacing;
24330
 
    }
24331
 
 
24332
 
    /**
24333
 
     * Return an array of no-write page regions
24334
 
     * @return array of no-write page regions
24335
 
     * @author Nicola Asuni
24336
 
     * @public
24337
 
     * @since 5.9.003 (2010-10-13)
24338
 
     * @see setPageRegions(), addPageRegion()
24339
 
     */
24340
 
    public function getPageRegions() {
24341
 
        return $this->page_regions;
24342
 
    }
24343
 
 
24344
 
    /**
24345
 
     * Set no-write regions on page.
24346
 
     * 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.
24347
 
     * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
24348
 
     * You can set multiple regions for the same page.
24349
 
     * @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.
24350
 
     * @author Nicola Asuni
24351
 
     * @public
24352
 
     * @since 5.9.003 (2010-10-13)
24353
 
     * @see addPageRegion(), getPageRegions()
24354
 
     */
24355
 
    public function setPageRegions($regions=array()) {
24356
 
        // empty current regions array
24357
 
        $this->page_regions = array();
24358
 
        // add regions
24359
 
        foreach ($regions as $data) {
24360
 
            $this->addPageRegion($data);
24361
 
        }
24362
 
    }
24363
 
 
24364
 
    /**
24365
 
     * Add a single no-write region on selected page.
24366
 
     * 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.
24367
 
     * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
24368
 
     * You can set multiple regions for the same page.
24369
 
     * @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).
24370
 
     * @author Nicola Asuni
24371
 
     * @public
24372
 
     * @since 5.9.003 (2010-10-13)
24373
 
     * @see setPageRegions(), getPageRegions()
24374
 
     */
24375
 
    public function addPageRegion($region) {
24376
 
        if (!isset($region['page']) OR empty($region['page'])) {
24377
 
            $region['page'] = $this->page;
24378
 
        }
24379
 
        if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
24380
 
            AND isset($region['yt'])  AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
24381
 
            AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
24382
 
            $this->page_regions[] = $region;
24383
 
        }
24384
 
    }
24385
 
 
24386
 
    /**
24387
 
     * Remove a single no-write region.
24388
 
     * @param $key (int) region key
24389
 
     * @author Nicola Asuni
24390
 
     * @public
24391
 
     * @since 5.9.003 (2010-10-13)
24392
 
     * @see setPageRegions(), getPageRegions()
24393
 
     */
24394
 
    public function removePageRegion($key) {
24395
 
        if (isset($this->page_regions[$key])) {
24396
 
            unset($this->page_regions[$key]);
24397
 
        }
24398
 
    }
24399
 
 
24400
 
    /**
24401
 
     * Check page for no-write regions and adapt current coordinates and page margins if necessary.
24402
 
     * 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.
24403
 
     * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
24404
 
     * @param $h (float) height of the text/image/object to print in user units
24405
 
     * @param $x (float) current X coordinate in user units
24406
 
     * @param $y (float) current Y coordinate in user units
24407
 
     * @author Nicola Asuni
24408
 
     * @protected
24409
 
     * @since 5.9.003 (2010-10-13)
24410
 
     */
24411
 
    protected function checkPageRegions($h, &$x, &$y) {
24412
 
        // set default values
24413
 
        if ($x === '') {
24414
 
            $x = &$this->x;
24415
 
        }
24416
 
        if ($y === '') {
24417
 
            $y = &$this->y;
24418
 
        }
24419
 
        if (empty($this->page_regions)) {
24420
 
            // no page regions defined
24421
 
            return;
24422
 
        }
24423
 
        if (empty($h)) {
24424
 
            $h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
24425
 
        }
24426
 
        if ($this->AutoPageBreak AND (!$this->InFooter) AND (($y + $h) > $this->PageBreakTrigger)) {
24427
 
            // the content will be printed on a new page
24428
 
            return;
24429
 
        }
24430
 
        if ($this->num_columns > 1) {
24431
 
            if ($this->rtl) {
24432
 
                $this->lMargin = $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w'];
24433
 
            } else {
24434
 
                $this->rMargin = $this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w'];
24435
 
            }
24436
 
        } else {
24437
 
            if ($this->rtl) {
24438
 
                $this->lMargin = $this->original_lMargin;
24439
 
            } else {
24440
 
                $this->rMargin = $this->original_rMargin;
24441
 
            }
24442
 
        }
24443
 
        // adjust coordinates and page margins
24444
 
        foreach ($this->page_regions as $regid => $regdata) {
24445
 
            if ($regdata['page'] == $this->page) {
24446
 
                // check region boundaries
24447
 
                if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
24448
 
                    // Y is inside the region
24449
 
                    $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
24450
 
                    $yt = max($y, $regdata['yt']);
24451
 
                    $yb = min(($yt + $h), $regdata['yb']);
24452
 
                    $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt'];
24453
 
                    $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt'];
24454
 
                    if ($regdata['side'] == 'L') { // left side
24455
 
                        $new_margin = max($xt, $xb);
24456
 
                        if ($this->lMargin < $new_margin) {
24457
 
                            if ($this->rtl) {
24458
 
                                // adjust left page margin
24459
 
                                $this->lMargin = $new_margin;
24460
 
                            }
24461
 
                            if ($x < $new_margin) {
24462
 
                                // adjust x position
24463
 
                                $x = $new_margin;
24464
 
                                if ($new_margin > ($this->w - $this->rMargin)) {
24465
 
                                    // adjust y position
24466
 
                                    $y = $regdata['yb'] - $h;
24467
 
                                }
24468
 
                            }
24469
 
                        }
24470
 
                    } elseif ($regdata['side'] == 'R') { // right side
24471
 
                        $new_margin = min($xt, $xb);
24472
 
                        if (($this->w - $this->rMargin) > $new_margin) {
24473
 
                            if (!$this->rtl) {
24474
 
                                // adjust right page margin
24475
 
                                $this->rMargin = ($this->w - $new_margin);
24476
 
                            }
24477
 
                            if ($x > $new_margin) {
24478
 
                                // adjust x position
24479
 
                                $x = $new_margin;
24480
 
                                if ($new_margin > $this->lMargin) {
24481
 
                                    // adjust y position
24482
 
                                    $y = $regdata['yb'] - $h;
24483
 
                                }
24484
 
                            }
24485
 
                        }
24486
 
                    }
24487
 
                }
24488
 
            }
24489
 
        }
24490
 
    }
24491
 
 
24492
 
    // -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
24493
 
    // SVG METHODS
24494
 
    // -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
24495
 
 
24496
 
    /**
24497
 
     * Embedd a Scalable Vector Graphics (SVG) image.
24498
 
     * NOTE: SVG standard is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
24499
 
     * @param $file (string) Name of the SVG file or a '@' character followed by the SVG data string.
24500
 
     * @param $x (float) Abscissa of the upper-left corner.
24501
 
     * @param $y (float) Ordinate of the upper-left corner.
24502
 
     * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
24503
 
     * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
24504
 
     * @param $link (mixed) URL or identifier returned by AddLink().
24505
 
     * @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.
24506
 
     * @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>
24507
 
     * @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)))
24508
 
     * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
24509
 
     * @author Nicola Asuni
24510
 
     * @since 5.0.000 (2010-05-02)
24511
 
     * @public
24512
 
     */
24513
 
    public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
24514
 
        if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
24515
 
            // convert SVG to raster image using GD or ImageMagick libraries
24516
 
            return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
24517
 
        }
24518
 
        if ($file{0} === '@') { // image from string
24519
 
            $this->svgdir = '';
24520
 
            $svgdata = substr($file, 1);
24521
 
        } else { // SVG file
24522
 
            $this->svgdir = dirname($file);
24523
 
            $svgdata = file_get_contents($file);
24524
 
        }
24525
 
        if ($svgdata === false) {
24526
 
            $this->Error('SVG file not found: '.$file);
24527
 
        }
24528
 
        if ($x === '') {
24529
 
            $x = $this->x;
24530
 
        }
24531
 
        if ($y === '') {
24532
 
            $y = $this->y;
24533
 
        }
24534
 
        // check page for no-write regions and adapt page margins if necessary
24535
 
        $this->checkPageRegions($h, $x, $y);
24536
 
        $k = $this->k;
24537
 
        $ox = 0;
24538
 
        $oy = 0;
24539
 
        $ow = $w;
24540
 
        $oh = $h;
24541
 
        $aspect_ratio_align = 'xMidYMid';
24542
 
        $aspect_ratio_ms = 'meet';
24543
 
        $regs = array();
24544
 
        // get original image width and height
24545
 
        preg_match('/<svg([^\>]*)>/si', $svgdata, $regs);
24546
 
        if (isset($regs[1]) AND !empty($regs[1])) {
24547
 
            $tmp = array();
24548
 
            if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
24549
 
                $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
24550
 
            }
24551
 
            $tmp = array();
24552
 
            if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
24553
 
                $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
24554
 
            }
24555
 
            $tmp = array();
24556
 
            if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
24557
 
                $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
24558
 
            }
24559
 
            $tmp = array();
24560
 
            if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
24561
 
                $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
24562
 
            }
24563
 
            $tmp = array();
24564
 
            $view_box = array();
24565
 
            if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
24566
 
                if (count($tmp) == 5) {
24567
 
                    array_shift($tmp);
24568
 
                    foreach ($tmp as $key => $val) {
24569
 
                        $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
24570
 
                    }
24571
 
                    $ox = $view_box[0];
24572
 
                    $oy = $view_box[1];
24573
 
                }
24574
 
                // get aspect ratio
24575
 
                $tmp = array();
24576
 
                if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
24577
 
                    $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
24578
 
                    switch (count($aspect_ratio)) {
24579
 
                        case 3: {
24580
 
                            $aspect_ratio_align = $aspect_ratio[1];
24581
 
                            $aspect_ratio_ms = $aspect_ratio[2];
24582
 
                            break;
24583
 
                        }
24584
 
                        case 2: {
24585
 
                            $aspect_ratio_align = $aspect_ratio[0];
24586
 
                            $aspect_ratio_ms = $aspect_ratio[1];
24587
 
                            break;
24588
 
                        }
24589
 
                        case 1: {
24590
 
                            $aspect_ratio_align = $aspect_ratio[0];
24591
 
                            $aspect_ratio_ms = 'meet';
24592
 
                            break;
24593
 
                        }
24594
 
                    }
24595
 
                }
24596
 
            }
24597
 
        }
24598
 
        // calculate image width and height on document
24599
 
        if (($w <= 0) AND ($h <= 0)) {
24600
 
            // convert image size to document unit
24601
 
            $w = $ow;
24602
 
            $h = $oh;
24603
 
        } elseif ($w <= 0) {
24604
 
            $w = $h * $ow / $oh;
24605
 
        } elseif ($h <= 0) {
24606
 
            $h = $w * $oh / $ow;
24607
 
        }
24608
 
        // fit the image on available space
24609
 
        $this->fitBlock($w, $h, $x, $y, $fitonpage);
24610
 
        if ($this->rasterize_vector_images) {
24611
 
            // convert SVG to raster image using GD or ImageMagick libraries
24612
 
            return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
24613
 
        }
24614
 
        // set alignment
24615
 
        $this->img_rb_y = $y + $h;
24616
 
        // set alignment
24617
 
        if ($this->rtl) {
24618
 
            if ($palign == 'L') {
24619
 
                $ximg = $this->lMargin;
24620
 
            } elseif ($palign == 'C') {
24621
 
                $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
24622
 
            } elseif ($palign == 'R') {
24623
 
                $ximg = $this->w - $this->rMargin - $w;
24624
 
            } else {
24625
 
                $ximg = $x - $w;
24626
 
            }
24627
 
            $this->img_rb_x = $ximg;
24628
 
        } else {
24629
 
            if ($palign == 'L') {
24630
 
                $ximg = $this->lMargin;
24631
 
            } elseif ($palign == 'C') {
24632
 
                $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
24633
 
            } elseif ($palign == 'R') {
24634
 
                $ximg = $this->w - $this->rMargin - $w;
24635
 
            } else {
24636
 
                $ximg = $x;
24637
 
            }
24638
 
            $this->img_rb_x = $ximg + $w;
24639
 
        }
24640
 
        // store current graphic vars
24641
 
        $gvars = $this->getGraphicVars();
24642
 
        // store SVG position and scale factors
24643
 
        $svgoffset_x = ($ximg - $ox) * $this->k;
24644
 
        $svgoffset_y = -($y - $oy) * $this->k;
24645
 
        if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
24646
 
            $ow = $view_box[2];
24647
 
            $oh = $view_box[3];
24648
 
        } else {
24649
 
            if ($ow <= 0) {
24650
 
                $ow = $w;
24651
 
            }
24652
 
            if ($oh <= 0) {
24653
 
                $oh = $h;
24654
 
            }
24655
 
        }
24656
 
        $svgscale_x = $w / $ow;
24657
 
        $svgscale_y = $h / $oh;
24658
 
        // scaling and alignment
24659
 
        if ($aspect_ratio_align != 'none') {
24660
 
            // store current scaling values
24661
 
            $svgscale_old_x = $svgscale_x;
24662
 
            $svgscale_old_y = $svgscale_y;
24663
 
            // force uniform scaling
24664
 
            if ($aspect_ratio_ms == 'slice') {
24665
 
                // the entire viewport is covered by the viewBox
24666
 
                if ($svgscale_x > $svgscale_y) {
24667
 
                    $svgscale_y = $svgscale_x;
24668
 
                } elseif ($svgscale_x < $svgscale_y) {
24669
 
                    $svgscale_x = $svgscale_y;
24670
 
                }
24671
 
            } else { // meet
24672
 
                // the entire viewBox is visible within the viewport
24673
 
                if ($svgscale_x < $svgscale_y) {
24674
 
                    $svgscale_y = $svgscale_x;
24675
 
                } elseif ($svgscale_x > $svgscale_y) {
24676
 
                    $svgscale_x = $svgscale_y;
24677
 
                }
24678
 
            }
24679
 
            // correct X alignment
24680
 
            switch (substr($aspect_ratio_align, 1, 3)) {
24681
 
                case 'Min': {
24682
 
                    // do nothing
24683
 
                    break;
24684
 
                }
24685
 
                case 'Max': {
24686
 
                    $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x));
24687
 
                    break;
24688
 
                }
24689
 
                default:
24690
 
                case 'Mid': {
24691
 
                    $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2);
24692
 
                    break;
24693
 
                }
24694
 
            }
24695
 
            // correct Y alignment
24696
 
            switch (substr($aspect_ratio_align, 5)) {
24697
 
                case 'Min': {
24698
 
                    // do nothing
24699
 
                    break;
24700
 
                }
24701
 
                case 'Max': {
24702
 
                    $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y));
24703
 
                    break;
24704
 
                }
24705
 
                default:
24706
 
                case 'Mid': {
24707
 
                    $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2);
24708
 
                    break;
24709
 
                }
24710
 
            }
24711
 
        }
24712
 
        // store current page break mode
24713
 
        $page_break_mode = $this->AutoPageBreak;
24714
 
        $page_break_margin = $this->getBreakMargin();
24715
 
        $cell_padding = $this->cell_padding;
24716
 
        $this->SetCellPadding(0);
24717
 
        $this->SetAutoPageBreak(false);
24718
 
        // save the current graphic state
24719
 
        $this->_out('q'.$this->epsmarker);
24720
 
        // set initial clipping mask
24721
 
        $this->Rect($x, $y, $w, $h, 'CNZ', array(), array());
24722
 
        // scale and translate
24723
 
        $e = $ox * $this->k * (1 - $svgscale_x);
24724
 
        $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y);
24725
 
        $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $svgscale_x, 0, 0, $svgscale_y, $e + $svgoffset_x, $f + $svgoffset_y));
24726
 
        // creates a new XML parser to be used by the other XML functions
24727
 
        $this->parser = xml_parser_create('UTF-8');
24728
 
        // the following function allows to use parser inside object
24729
 
        xml_set_object($this->parser, $this);
24730
 
        // disable case-folding for this XML parser
24731
 
        xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
24732
 
        // sets the element handler functions for the XML parser
24733
 
        xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler');
24734
 
        // sets the character data handler function for the XML parser
24735
 
        xml_set_character_data_handler($this->parser, 'segSVGContentHandler');
24736
 
        // start parsing an XML document
24737
 
        if(!xml_parse($this->parser, $svgdata)) {
24738
 
            $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));
24739
 
            $this->Error($error_message);
24740
 
        }
24741
 
        // free this XML parser
24742
 
        xml_parser_free($this->parser);
24743
 
        // restore previous graphic state
24744
 
        $this->_out($this->epsmarker.'Q');
24745
 
        // restore graphic vars
24746
 
        $this->setGraphicVars($gvars);
24747
 
        $this->lasth = $gvars['lasth'];
24748
 
        if (!empty($border)) {
24749
 
            $bx = $this->x;
24750
 
            $by = $this->y;
24751
 
            $this->x = $ximg;
24752
 
            if ($this->rtl) {
24753
 
                $this->x += $w;
24754
 
            }
24755
 
            $this->y = $y;
24756
 
            $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
24757
 
            $this->x = $bx;
24758
 
            $this->y = $by;
24759
 
        }
24760
 
        if ($link) {
24761
 
            $this->Link($ximg, $y, $w, $h, $link, 0);
24762
 
        }
24763
 
        // set pointer to align the next text/objects
24764
 
        switch($align) {
24765
 
            case 'T':{
24766
 
                $this->y = $y;
24767
 
                $this->x = $this->img_rb_x;
24768
 
                break;
24769
 
            }
24770
 
            case 'M':{
24771
 
                $this->y = $y + round($h/2);
24772
 
                $this->x = $this->img_rb_x;
24773
 
                break;
24774
 
            }
24775
 
            case 'B':{
24776
 
                $this->y = $this->img_rb_y;
24777
 
                $this->x = $this->img_rb_x;
24778
 
                break;
24779
 
            }
24780
 
            case 'N':{
24781
 
                $this->SetY($this->img_rb_y);
24782
 
                break;
24783
 
            }
24784
 
            default:{
24785
 
                // restore pointer to starting position
24786
 
                $this->x = $gvars['x'];
24787
 
                $this->y = $gvars['y'];
24788
 
                $this->page = $gvars['page'];
24789
 
                $this->current_column = $gvars['current_column'];
24790
 
                $this->tMargin = $gvars['tMargin'];
24791
 
                $this->bMargin = $gvars['bMargin'];
24792
 
                $this->w = $gvars['w'];
24793
 
                $this->h = $gvars['h'];
24794
 
                $this->wPt = $gvars['wPt'];
24795
 
                $this->hPt = $gvars['hPt'];
24796
 
                $this->fwPt = $gvars['fwPt'];
24797
 
                $this->fhPt = $gvars['fhPt'];
24798
 
                break;
24799
 
            }
24800
 
        }
24801
 
        $this->endlinex = $this->img_rb_x;
24802
 
        // restore page break
24803
 
        $this->SetAutoPageBreak($page_break_mode, $page_break_margin);
24804
 
        $this->cell_padding = $cell_padding;
24805
 
    }
24806
 
 
24807
 
    /**
24808
 
     * Get the tranformation matrix from SVG transform attribute
24809
 
     * @param $attribute (string) transformation
24810
 
     * @return array of transformations
24811
 
     * @author Nicola Asuni
24812
 
     * @since 5.0.000 (2010-05-02)
24813
 
     * @protected
24814
 
     */
24815
 
    protected function getSVGTransformMatrix($attribute) {
24816
 
        // identity matrix
24817
 
        $tm = array(1, 0, 0, 1, 0, 0);
24818
 
        $transform = array();
24819
 
        if (preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)[\s]*\(([^\)]+)\)/si', $attribute, $transform, PREG_SET_ORDER) > 0) {
24820
 
            foreach ($transform as $key => $data) {
24821
 
                if (!empty($data[2])) {
24822
 
                    $a = 1;
24823
 
                    $b = 0;
24824
 
                    $c = 0;
24825
 
                    $d = 1;
24826
 
                    $e = 0;
24827
 
                    $f = 0;
24828
 
                    $regs = array();
24829
 
                    switch ($data[1]) {
24830
 
                        case 'matrix': {
24831
 
                            if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
24832
 
                                $a = $regs[1];
24833
 
                                $b = $regs[2];
24834
 
                                $c = $regs[3];
24835
 
                                $d = $regs[4];
24836
 
                                $e = $regs[5];
24837
 
                                $f = $regs[6];
24838
 
                            }
24839
 
                            break;
24840
 
                        }
24841
 
                        case 'translate': {
24842
 
                            if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
24843
 
                                $e = $regs[1];
24844
 
                                $f = $regs[2];
24845
 
                            } elseif (preg_match('/([a-z0-9\-\.]+)/si', $data[2], $regs)) {
24846
 
                                $e = $regs[1];
24847
 
                            }
24848
 
                            break;
24849
 
                        }
24850
 
                        case 'scale': {
24851
 
                            if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
24852
 
                                $a = $regs[1];
24853
 
                                $d = $regs[2];
24854
 
                            } elseif (preg_match('/([a-z0-9\-\.]+)/si', $data[2], $regs)) {
24855
 
                                $a = $regs[1];
24856
 
                                $d = $a;
24857
 
                            }
24858
 
                            break;
24859
 
                        }
24860
 
                        case 'rotate': {
24861
 
                            if (preg_match('/([0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
24862
 
                                $ang = deg2rad($regs[1]);
24863
 
                                $x = $regs[2];
24864
 
                                $y = $regs[3];
24865
 
                                $a = cos($ang);
24866
 
                                $b = sin($ang);
24867
 
                                $c = -$b;
24868
 
                                $d = $a;
24869
 
                                $e = ($x * (1 - $a)) - ($y * $c);
24870
 
                                $f = ($y * (1 - $d)) - ($x * $b);
24871
 
                            } elseif (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) {
24872
 
                                $ang = deg2rad($regs[1]);
24873
 
                                $a = cos($ang);
24874
 
                                $b = sin($ang);
24875
 
                                $c = -$b;
24876
 
                                $d = $a;
24877
 
                                $e = 0;
24878
 
                                $f = 0;
24879
 
                            }
24880
 
                            break;
24881
 
                        }
24882
 
                        case 'skewX': {
24883
 
                            if (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) {
24884
 
                                $c = tan(deg2rad($regs[1]));
24885
 
                            }
24886
 
                            break;
24887
 
                        }
24888
 
                        case 'skewY': {
24889
 
                            if (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) {
24890
 
                                $b = tan(deg2rad($regs[1]));
24891
 
                            }
24892
 
                            break;
24893
 
                        }
24894
 
                    }
24895
 
                    $tm = $this->getTransformationMatrixProduct($tm, array($a, $b, $c, $d, $e, $f));
24896
 
                }
24897
 
            }
24898
 
        }
24899
 
        return $tm;
24900
 
    }
24901
 
 
24902
 
    /**
24903
 
     * Get the product of two SVG tranformation matrices
24904
 
     * @param $ta (array) first SVG tranformation matrix
24905
 
     * @param $tb (array) second SVG tranformation matrix
24906
 
     * @return transformation array
24907
 
     * @author Nicola Asuni
24908
 
     * @since 5.0.000 (2010-05-02)
24909
 
     * @protected
24910
 
     */
24911
 
    protected function getTransformationMatrixProduct($ta, $tb) {
24912
 
        $tm = array();
24913
 
        $tm[0] = ($ta[0] * $tb[0]) + ($ta[2] * $tb[1]);
24914
 
        $tm[1] = ($ta[1] * $tb[0]) + ($ta[3] * $tb[1]);
24915
 
        $tm[2] = ($ta[0] * $tb[2]) + ($ta[2] * $tb[3]);
24916
 
        $tm[3] = ($ta[1] * $tb[2]) + ($ta[3] * $tb[3]);
24917
 
        $tm[4] = ($ta[0] * $tb[4]) + ($ta[2] * $tb[5]) + $ta[4];
24918
 
        $tm[5] = ($ta[1] * $tb[4]) + ($ta[3] * $tb[5]) + $ta[5];
24919
 
        return $tm;
24920
 
    }
24921
 
 
24922
 
    /**
24923
 
     * Convert SVG transformation matrix to PDF.
24924
 
     * @param $tm (array) original SVG transformation matrix
24925
 
     * @return array transformation matrix
24926
 
     * @protected
24927
 
     * @since 5.0.000 (2010-05-02)
24928
 
     */
24929
 
    protected function convertSVGtMatrix($tm) {
24930
 
        $a = $tm[0];
24931
 
        $b = -$tm[1];
24932
 
        $c = -$tm[2];
24933
 
        $d = $tm[3];
24934
 
        $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k;
24935
 
        $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k;
24936
 
        $x = 0;
24937
 
        $y = $this->h * $this->k;
24938
 
        $e = ($x * (1 - $a)) - ($y * $c) + $e;
24939
 
        $f = ($y * (1 - $d)) - ($x * $b) + $f;
24940
 
        return array($a, $b, $c, $d, $e, $f);
24941
 
    }
24942
 
 
24943
 
    /**
24944
 
     * Apply SVG graphic transformation matrix.
24945
 
     * @param $tm (array) original SVG transformation matrix
24946
 
     * @protected
24947
 
     * @since 5.0.000 (2010-05-02)
24948
 
     */
24949
 
    protected function SVGTransform($tm) {
24950
 
        $this->Transform($this->convertSVGtMatrix($tm));
24951
 
    }
24952
 
 
24953
 
    /**
24954
 
     * Apply the requested SVG styles (*** TO BE COMPLETED ***)
24955
 
     * @param $svgstyle (array) array of SVG styles to apply
24956
 
     * @param $prevsvgstyle (array) array of previous SVG style
24957
 
     * @param $x (int) X origin of the bounding box
24958
 
     * @param $y (int) Y origin of the bounding box
24959
 
     * @param $w (int) width of the bounding box
24960
 
     * @param $h (int) height of the bounding box
24961
 
     * @param $clip_function (string) clip function
24962
 
     * @param $clip_params (array) array of parameters for clipping function
24963
 
     * @return object style
24964
 
     * @author Nicola Asuni
24965
 
     * @since 5.0.000 (2010-05-02)
24966
 
     * @protected
24967
 
     */
24968
 
    protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
24969
 
        $objstyle = '';
24970
 
        if(!isset($svgstyle['opacity'])) {
24971
 
            return $objstyle;
24972
 
        }
24973
 
        // clip-path
24974
 
        $regs = array();
24975
 
        if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) {
24976
 
            $clip_path = $this->svgclippaths[$regs[1]];
24977
 
            foreach ($clip_path as $cp) {
24978
 
                $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
24979
 
            }
24980
 
        }
24981
 
        // opacity
24982
 
        if ($svgstyle['opacity'] != 1) {
24983
 
            $this->SetAlpha($svgstyle['opacity']);
24984
 
        }
24985
 
        // color
24986
 
        $fill_color = $this->convertHTMLColorToDec($svgstyle['color']);
24987
 
        $this->SetFillColorArray($fill_color);
24988
 
        // text color
24989
 
        $text_color = $this->convertHTMLColorToDec($svgstyle['text-color']);
24990
 
        $this->SetTextColorArray($text_color);
24991
 
        // clip
24992
 
        if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) {
24993
 
            $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0);
24994
 
            $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0);
24995
 
            $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0);
24996
 
            $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0);
24997
 
            $cx = $x + $left;
24998
 
            $cy = $y + $top;
24999
 
            $cw = $w - $left - $right;
25000
 
            $ch = $h - $top - $bottom;
25001
 
            if ($svgstyle['clip-rule'] == 'evenodd') {
25002
 
                $clip_rule = 'CNZ';
25003
 
            } else {
25004
 
                $clip_rule = 'CEO';
25005
 
            }
25006
 
            $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
25007
 
        }
25008
 
        // fill
25009
 
        $regs = array();
25010
 
        if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) {
25011
 
            // gradient
25012
 
            $gradient = $this->svggradients[$regs[1]];
25013
 
            if (isset($gradient['xref'])) {
25014
 
                // reference to another gradient definition
25015
 
                $newgradient = $this->svggradients[$gradient['xref']];
25016
 
                $newgradient['coords'] = $gradient['coords'];
25017
 
                $newgradient['mode'] = $gradient['mode'];
25018
 
                $newgradient['gradientUnits'] = $gradient['gradientUnits'];
25019
 
                if (isset($gradient['gradientTransform'])) {
25020
 
                    $newgradient['gradientTransform'] = $gradient['gradientTransform'];
25021
 
                }
25022
 
                $gradient = $newgradient;
25023
 
            }
25024
 
            //save current Graphic State
25025
 
            $this->_out('q');
25026
 
            //set clipping area
25027
 
            if (!empty($clip_function) AND method_exists($this, $clip_function)) {
25028
 
                $bbox = call_user_func_array(array($this, $clip_function), $clip_params);
25029
 
                if (is_array($bbox) AND (count($bbox) == 4)) {
25030
 
                    list($x, $y, $w, $h) = $bbox;
25031
 
                }
25032
 
            }
25033
 
            if ($gradient['mode'] == 'measure') {
25034
 
                if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
25035
 
                    $gtm = $gradient['gradientTransform'];
25036
 
                    // apply transformation matrix
25037
 
                    $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4];
25038
 
                    $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5];
25039
 
                    $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4];
25040
 
                    $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5];
25041
 
                    if (isset($gradient['coords'][4])) {
25042
 
                        $gradient['coords'][4] = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2));
25043
 
                    }
25044
 
                    $gradient['coords'][0] = $xa;
25045
 
                    $gradient['coords'][1] = $ya;
25046
 
                    $gradient['coords'][2] = $xb;
25047
 
                    $gradient['coords'][3] = $yb;
25048
 
                }
25049
 
                // convert SVG coordinates to user units
25050
 
                $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false);
25051
 
                $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false);
25052
 
                $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false);
25053
 
                $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false);
25054
 
                if (isset($gradient['coords'][4])) {
25055
 
                    $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false);
25056
 
                }
25057
 
                // shift units
25058
 
                if ($gradient['gradientUnits'] == 'objectBoundingBox') {
25059
 
                    // convert to SVG coordinate system
25060
 
                    $gradient['coords'][0] += $x;
25061
 
                    $gradient['coords'][1] += $y;
25062
 
                    $gradient['coords'][2] += $x;
25063
 
                    $gradient['coords'][3] += $y;
25064
 
                }
25065
 
                if ($w <= 0) {
25066
 
                    $w = 1;
25067
 
                }
25068
 
                if ($h <= 0) {
25069
 
                    $h = 1;
25070
 
                }
25071
 
                // calculate percentages
25072
 
                $gradient['coords'][0] = ($gradient['coords'][0] - $x) / $w;
25073
 
                $gradient['coords'][1] = ($gradient['coords'][1] - $y) / $h;
25074
 
                $gradient['coords'][2] = ($gradient['coords'][2] - $x) / $w;
25075
 
                $gradient['coords'][3] = ($gradient['coords'][3] - $y) / $h;
25076
 
                if (isset($gradient['coords'][4])) {
25077
 
                    $gradient['coords'][4] /= $w;
25078
 
                }
25079
 
            } elseif ($gradient['mode'] == 'percentage') {
25080
 
                foreach($gradient['coords'] as $key => $val) {
25081
 
                    $gradient['coords'][$key] = (intval($val) / 100);
25082
 
                }
25083
 
            }
25084
 
            // fix values
25085
 
            foreach($gradient['coords'] as $key => $val) {
25086
 
                if ($val < 0) {
25087
 
                    $gradient['coords'][$key] = 0;
25088
 
                } elseif ($val > 1) {
25089
 
                    $gradient['coords'][$key] = 1;
25090
 
                }
25091
 
            }
25092
 
            if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
25093
 
                // single color (no shading)
25094
 
                $gradient['coords'][0] = 1;
25095
 
                $gradient['coords'][1] = 0;
25096
 
                $gradient['coords'][2] = 0.999;
25097
 
                $gradient['coords'][3] = 0;
25098
 
            }
25099
 
            // swap Y coordinates
25100
 
            $tmp = $gradient['coords'][1];
25101
 
            $gradient['coords'][1] = $gradient['coords'][3];
25102
 
            $gradient['coords'][3] = $tmp;
25103
 
            // set transformation map for gradient
25104
 
            if ($gradient['type'] == 3) {
25105
 
                // gradient is always circular
25106
 
                $cy = $this->h - $y - ($gradient['coords'][1] * ($w + $h));
25107
 
                $this->_out(sprintf('%.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $w*$this->k, $x*$this->k, $cy*$this->k));
25108
 
            } else {
25109
 
                $this->_out(sprintf('%.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k));
25110
 
            }
25111
 
            if (count($gradient['stops']) > 1) {
25112
 
                $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
25113
 
            }
25114
 
        } elseif ($svgstyle['fill'] != 'none') {
25115
 
            $fill_color = $this->convertHTMLColorToDec($svgstyle['fill']);
25116
 
            if ($svgstyle['fill-opacity'] != 1) {
25117
 
                $this->SetAlpha($svgstyle['fill-opacity']);
25118
 
            }
25119
 
            $this->SetFillColorArray($fill_color);
25120
 
            if ($svgstyle['fill-rule'] == 'evenodd') {
25121
 
                $objstyle .= 'F*';
25122
 
            } else {
25123
 
                $objstyle .= 'F';
25124
 
            }
25125
 
        }
25126
 
        // stroke
25127
 
        if ($svgstyle['stroke'] != 'none') {
25128
 
            $stroke_style = array(
25129
 
                'color' => $this->convertHTMLColorToDec($svgstyle['stroke']),
25130
 
                'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false),
25131
 
                'cap' => $svgstyle['stroke-linecap'],
25132
 
                'join' => $svgstyle['stroke-linejoin']
25133
 
                );
25134
 
            if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
25135
 
                $stroke_style['dash'] = $svgstyle['stroke-dasharray'];
25136
 
            }
25137
 
            $this->SetLineStyle($stroke_style);
25138
 
            $objstyle .= 'D';
25139
 
        }
25140
 
        // font
25141
 
        $regs = array();
25142
 
        if (!empty($svgstyle['font'])) {
25143
 
            if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
25144
 
                $font_family = $this->getFontFamilyName($regs[1]);
25145
 
            } else {
25146
 
                $font_family = $svgstyle['font-family'];
25147
 
            }
25148
 
            if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
25149
 
                $font_size = trim($regs[1]);
25150
 
            } else {
25151
 
                $font_size = $svgstyle['font-size'];
25152
 
            }
25153
 
            if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
25154
 
                $font_style = trim($regs[1]);
25155
 
            } else {
25156
 
                $font_style = $svgstyle['font-style'];
25157
 
            }
25158
 
            if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
25159
 
                $font_weight = trim($regs[1]);
25160
 
            } else {
25161
 
                $font_weight = $svgstyle['font-weight'];
25162
 
            }
25163
 
            if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
25164
 
                $font_stretch = trim($regs[1]);
25165
 
            } else {
25166
 
                $font_stretch = $svgstyle['font-stretch'];
25167
 
            }
25168
 
            if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
25169
 
                $font_spacing = trim($regs[1]);
25170
 
            } else {
25171
 
                $font_spacing = $svgstyle['letter-spacing'];
25172
 
            }
25173
 
        } else {
25174
 
            $font_family = $this->getFontFamilyName($svgstyle['font-family']);
25175
 
            $font_size = $svgstyle['font-size'];
25176
 
            $font_style = $svgstyle['font-style'];
25177
 
            $font_weight = $svgstyle['font-weight'];
25178
 
            $font_stretch = $svgstyle['font-stretch'];
25179
 
            $font_spacing = $svgstyle['letter-spacing'];
25180
 
        }
25181
 
        $font_size = $this->getHTMLUnitToUnits($font_size, $prevsvgstyle['font-size'], $this->svgunit, false) * $this->k;
25182
 
        $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
25183
 
        $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
25184
 
        switch ($font_style) {
25185
 
            case 'italic': {
25186
 
                $font_style = 'I';
25187
 
                break;
25188
 
            }
25189
 
            case 'oblique': {
25190
 
                $font_style = 'I';
25191
 
                break;
25192
 
            }
25193
 
            default:
25194
 
            case 'normal': {
25195
 
                $font_style = '';
25196
 
                break;
25197
 
            }
25198
 
        }
25199
 
        switch ($font_weight) {
25200
 
            case 'bold':
25201
 
            case 'bolder': {
25202
 
                $font_style .= 'B';
25203
 
                break;
25204
 
            }
25205
 
        }
25206
 
        switch ($svgstyle['text-decoration']) {
25207
 
            case 'underline': {
25208
 
                $font_style .= 'U';
25209
 
                break;
25210
 
            }
25211
 
            case 'overline': {
25212
 
                $font_style .= 'O';
25213
 
                break;
25214
 
            }
25215
 
            case 'line-through': {
25216
 
                $font_style .= 'D';
25217
 
                break;
25218
 
            }
25219
 
            default:
25220
 
            case 'none': {
25221
 
                break;
25222
 
            }
25223
 
        }
25224
 
        $this->SetFont($font_family, $font_style, $font_size);
25225
 
        $this->setFontStretching($font_stretch);
25226
 
        $this->setFontSpacing($font_spacing);
25227
 
        return $objstyle;
25228
 
    }
25229
 
 
25230
 
    /**
25231
 
     * Draws an SVG path
25232
 
     * @param $d (string) attribute d of the path SVG element
25233
 
     * @param $style (string) Style of rendering. Possible values are:
25234
 
     * <ul>
25235
 
     *     <li>D or empty string: Draw (default).</li>
25236
 
     *     <li>F: Fill.</li>
25237
 
     *     <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
25238
 
     *     <li>DF or FD: Draw and fill.</li>
25239
 
     *     <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
25240
 
     *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
25241
 
     *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
25242
 
     * </ul>
25243
 
     * @return array of container box measures (x, y, w, h)
25244
 
     * @author Nicola Asuni
25245
 
     * @since 5.0.000 (2010-05-02)
25246
 
     * @protected
25247
 
     */
25248
 
    protected function SVGPath($d, $style='') {
25249
 
        // set fill/stroke style
25250
 
        $op = $this->getPathPaintOperator($style, '');
25251
 
        if (empty($op)) {
25252
 
            return;
25253
 
        }
25254
 
        $paths = array();
25255
 
        $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
25256
 
        preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER);
25257
 
        $x = 0;
25258
 
        $y = 0;
25259
 
        $x1 = 0;
25260
 
        $y1 = 0;
25261
 
        $x2 = 0;
25262
 
        $y2 = 0;
25263
 
        $xmin = 2147483647;
25264
 
        $xmax = 0;
25265
 
        $ymin = 2147483647;
25266
 
        $ymax = 0;
25267
 
        $relcoord = false;
25268
 
        // draw curve pieces
25269
 
        foreach ($paths as $key => $val) {
25270
 
            // get curve type
25271
 
            $cmd = trim($val[1]);
25272
 
            if (strtolower($cmd) == $cmd) {
25273
 
                // use relative coordinated instead of absolute
25274
 
                $relcoord = true;
25275
 
                $xoffset = $x;
25276
 
                $yoffset = $y;
25277
 
            } else {
25278
 
                $relcoord = false;
25279
 
                $xoffset = 0;
25280
 
                $yoffset = 0;
25281
 
            }
25282
 
            $params = array();
25283
 
            if (isset($val[2])) {
25284
 
                // get curve parameters
25285
 
                $rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
25286
 
                $params = array();
25287
 
                foreach ($rawparams as $ck => $cp) {
25288
 
                    $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false);
25289
 
                }
25290
 
            }
25291
 
            switch (strtoupper($cmd)) {
25292
 
                case 'M': { // moveto
25293
 
                    foreach ($params as $ck => $cp) {
25294
 
                        if (($ck % 2) == 0) {
25295
 
                            $x = $cp + $xoffset;
25296
 
                        } else {
25297
 
                            $y = $cp + $yoffset;
25298
 
                            if ($ck == 1) {
25299
 
                                $this->_outPoint($x, $y);
25300
 
                            } else {
25301
 
                                $this->_outLine($x, $y);
25302
 
                            }
25303
 
                            $xmin = min($xmin, $x);
25304
 
                            $ymin = min($ymin, $y);
25305
 
                            $xmax = max($xmax, $x);
25306
 
                            $ymax = max($ymax, $y);
25307
 
                            if ($relcoord) {
25308
 
                                $xoffset = $x;
25309
 
                                $yoffset = $y;
25310
 
                            }
25311
 
                        }
25312
 
                    }
25313
 
                    break;
25314
 
                }
25315
 
                case 'L': { // lineto
25316
 
                    foreach ($params as $ck => $cp) {
25317
 
                        if (($ck % 2) == 0) {
25318
 
                            $x = $cp + $xoffset;
25319
 
                        } else {
25320
 
                            $y = $cp + $yoffset;
25321
 
                            $this->_outLine($x, $y);
25322
 
                            $xmin = min($xmin, $x);
25323
 
                            $ymin = min($ymin, $y);
25324
 
                            $xmax = max($xmax, $x);
25325
 
                            $ymax = max($ymax, $y);
25326
 
                            if ($relcoord) {
25327
 
                                $xoffset = $x;
25328
 
                                $yoffset = $y;
25329
 
                            }
25330
 
                        }
25331
 
                    }
25332
 
                    break;
25333
 
                }
25334
 
                case 'H': { // horizontal lineto
25335
 
                    foreach ($params as $ck => $cp) {
25336
 
                        $x = $cp + $xoffset;
25337
 
                        $this->_outLine($x, $y);
25338
 
                        $xmin = min($xmin, $x);
25339
 
                        $xmax = max($xmax, $x);
25340
 
                        if ($relcoord) {
25341
 
                            $xoffset = $x;
25342
 
                        }
25343
 
                    }
25344
 
                    break;
25345
 
                }
25346
 
                case 'V': { // vertical lineto
25347
 
                    foreach ($params as $ck => $cp) {
25348
 
                        $y = $cp + $yoffset;
25349
 
                        $this->_outLine($x, $y);
25350
 
                        $ymin = min($ymin, $y);
25351
 
                        $ymax = max($ymax, $y);
25352
 
                        if ($relcoord) {
25353
 
                            $yoffset = $y;
25354
 
                        }
25355
 
                    }
25356
 
                    break;
25357
 
                }
25358
 
                case 'C': { // curveto
25359
 
                    foreach ($params as $ck => $cp) {
25360
 
                        $params[$ck] = $cp;
25361
 
                        if ((($ck + 1) % 6) == 0) {
25362
 
                            $x1 = $params[($ck - 5)] + $xoffset;
25363
 
                            $y1 = $params[($ck - 4)] + $yoffset;
25364
 
                            $x2 = $params[($ck - 3)] + $xoffset;
25365
 
                            $y2 = $params[($ck - 2)] + $yoffset;
25366
 
                            $x = $params[($ck - 1)] + $xoffset;
25367
 
                            $y = $params[($ck)] + $yoffset;
25368
 
                            $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
25369
 
                            $xmin = min($xmin, $x, $x1, $x2);
25370
 
                            $ymin = min($ymin, $y, $y1, $y2);
25371
 
                            $xmax = max($xmax, $x, $x1, $x2);
25372
 
                            $ymax = max($ymax, $y, $y1, $y2);
25373
 
                            if ($relcoord) {
25374
 
                                $xoffset = $x;
25375
 
                                $yoffset = $y;
25376
 
                            }
25377
 
                        }
25378
 
                    }
25379
 
                    break;
25380
 
                }
25381
 
                case 'S': { // shorthand/smooth curveto
25382
 
                    foreach ($params as $ck => $cp) {
25383
 
                        $params[$ck] = $cp;
25384
 
                        if ((($ck + 1) % 4) == 0) {
25385
 
                            if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
25386
 
                                $x1 = (2 * $x) - $x2;
25387
 
                                $y1 = (2 * $y) - $y2;
25388
 
                            } else {
25389
 
                                $x1 = $x;
25390
 
                                $y1 = $y;
25391
 
                            }
25392
 
                            $x2 = $params[($ck - 3)] + $xoffset;
25393
 
                            $y2 = $params[($ck - 2)] + $yoffset;
25394
 
                            $x = $params[($ck - 1)] + $xoffset;
25395
 
                            $y = $params[($ck)] + $yoffset;
25396
 
                            $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
25397
 
                            $xmin = min($xmin, $x, $x1, $x2);
25398
 
                            $ymin = min($ymin, $y, $y1, $y2);
25399
 
                            $xmax = max($xmax, $x, $x1, $x2);
25400
 
                            $ymax = max($ymax, $y, $y1, $y2);
25401
 
                            if ($relcoord) {
25402
 
                                $xoffset = $x;
25403
 
                                $yoffset = $y;
25404
 
                            }
25405
 
                        }
25406
 
                    }
25407
 
                    break;
25408
 
                }
25409
 
                case 'Q': { // quadratic B�zier curveto
25410
 
                    foreach ($params as $ck => $cp) {
25411
 
                        $params[$ck] = $cp;
25412
 
                        if ((($ck + 1) % 4) == 0) {
25413
 
                            // convert quadratic points to cubic points
25414
 
                            $x1 = $params[($ck - 3)] + $xoffset;
25415
 
                            $y1 = $params[($ck - 2)] + $yoffset;
25416
 
                            $xa = ($x + (2 * $x1)) / 3;
25417
 
                            $ya = ($y + (2 * $y1)) / 3;
25418
 
                            $x = $params[($ck - 1)] + $xoffset;
25419
 
                            $y = $params[($ck)] + $yoffset;
25420
 
                            $xb = ($x + (2 * $x1)) / 3;
25421
 
                            $yb = ($y + (2 * $y1)) / 3;
25422
 
                            $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
25423
 
                            $xmin = min($xmin, $x, $xa, $xb);
25424
 
                            $ymin = min($ymin, $y, $ya, $yb);
25425
 
                            $xmax = max($xmax, $x, $xa, $xb);
25426
 
                            $ymax = max($ymax, $y, $ya, $yb);
25427
 
                            if ($relcoord) {
25428
 
                                $xoffset = $x;
25429
 
                                $yoffset = $y;
25430
 
                            }
25431
 
                        }
25432
 
                    }
25433
 
                    break;
25434
 
                }
25435
 
                case 'T': { // shorthand/smooth quadratic B�zier curveto
25436
 
                    foreach ($params as $ck => $cp) {
25437
 
                        $params[$ck] = $cp;
25438
 
                        if (($ck % 2) != 0) {
25439
 
                            if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
25440
 
                                $x1 = (2 * $x) - $x1;
25441
 
                                $y1 = (2 * $y) - $y1;
25442
 
                            } else {
25443
 
                                $x1 = $x;
25444
 
                                $y1 = $y;
25445
 
                            }
25446
 
                            // convert quadratic points to cubic points
25447
 
                            $xa = ($x + (2 * $x1)) / 3;
25448
 
                            $ya = ($y + (2 * $y1)) / 3;
25449
 
                            $x = $params[($ck - 1)] + $xoffset;
25450
 
                            $y = $params[($ck)] + $yoffset;
25451
 
                            $xb = ($x + (2 * $x1)) / 3;
25452
 
                            $yb = ($y + (2 * $y1)) / 3;
25453
 
                            $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
25454
 
                            $xmin = min($xmin, $x, $xa, $xb);
25455
 
                            $ymin = min($ymin, $y, $ya, $yb);
25456
 
                            $xmax = max($xmax, $x, $xa, $xb);
25457
 
                            $ymax = max($ymax, $y, $ya, $yb);
25458
 
                            if ($relcoord) {
25459
 
                                $xoffset = $x;
25460
 
                                $yoffset = $y;
25461
 
                            }
25462
 
                        }
25463
 
                    }
25464
 
                    break;
25465
 
                }
25466
 
                case 'A': { // elliptical arc
25467
 
                    foreach ($params as $ck => $cp) {
25468
 
                        $params[$ck] = $cp;
25469
 
                        if ((($ck + 1) % 7) == 0) {
25470
 
                            $x0 = $x;
25471
 
                            $y0 = $y;
25472
 
                            $rx = abs($params[($ck - 6)]);
25473
 
                            $ry = abs($params[($ck - 5)]);
25474
 
                            $ang = -$rawparams[($ck - 4)];
25475
 
                            $angle = deg2rad($ang);
25476
 
                            $fa = $rawparams[($ck - 3)]; // large-arc-flag
25477
 
                            $fs = $rawparams[($ck - 2)]; // sweep-flag
25478
 
                            $x = $params[($ck - 1)] + $xoffset;
25479
 
                            $y = $params[$ck] + $yoffset;
25480
 
                            $cos_ang = cos($angle);
25481
 
                            $sin_ang = sin($angle);
25482
 
                            $a = ($x0 - $x) / 2;
25483
 
                            $b = ($y0 - $y) / 2;
25484
 
                            $xa = ($a * $cos_ang) - ($b * $sin_ang);
25485
 
                            $ya = ($a * $sin_ang) + ($b * $cos_ang);
25486
 
                            $rx2 = $rx * $rx;
25487
 
                            $ry2 = $ry * $ry;
25488
 
                            $xa2 = $xa * $xa;
25489
 
                            $ya2 = $ya * $ya;
25490
 
                            $delta = ($xa2 / $rx2) + ($ya2 / $ry2);
25491
 
                            if ($delta > 1) {
25492
 
                                $rx *= sqrt($delta);
25493
 
                                $ry *= sqrt($delta);
25494
 
                                $rx2 = $rx * $rx;
25495
 
                                $ry2 = $ry * $ry;
25496
 
                            }
25497
 
                            $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
25498
 
                            if ($numerator < 0) {
25499
 
                                $root = 0;
25500
 
                            } else {
25501
 
                                $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2)));
25502
 
                            }
25503
 
                            if ($fa == $fs) {
25504
 
                                $root *= -1;
25505
 
                            }
25506
 
                            $cax = $root * (($rx * $ya) / $ry);
25507
 
                            $cay = -$root * (($ry * $xa) / $rx);
25508
 
                            // coordinates of ellipse center
25509
 
                            $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2);
25510
 
                            $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2);
25511
 
                            // get angles
25512
 
                            $angs = $this->getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
25513
 
                            $dang = $this->getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
25514
 
                            if (($fs == 0) AND ($dang > 0)) {
25515
 
                                $dang -= (2 * M_PI);
25516
 
                            } elseif (($fs == 1) AND ($dang < 0)) {
25517
 
                                $dang += (2 * M_PI);
25518
 
                            }
25519
 
                            $angf = $angs - $dang;
25520
 
                            if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
25521
 
                                // reverse angles
25522
 
                                $tmp = $angs;
25523
 
                                $angs = $angf;
25524
 
                                $angf = $tmp;
25525
 
                            }
25526
 
                            $angs = round(rad2deg($angs), 6);
25527
 
                            $angf = round(rad2deg($angf), 6);
25528
 
                            // covent angles to positive values
25529
 
                            if (($angs < 0) AND ($angf < 0)) {
25530
 
                                $angs += 360;
25531
 
                                $angf += 360;
25532
 
                            }
25533
 
                            $pie = false;
25534
 
                            if (($key==0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) {
25535
 
                                $pie = true;
25536
 
                            }
25537
 
                            list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0));
25538
 
                            $xmin = min($xmin, $x, $axmin);
25539
 
                            $ymin = min($ymin, $y, $aymin);
25540
 
                            $xmax = max($xmax, $x, $axmax);
25541
 
                            $ymax = max($ymax, $y, $aymax);
25542
 
                            if ($relcoord) {
25543
 
                                $xoffset = $x;
25544
 
                                $yoffset = $y;
25545
 
                            }
25546
 
                        }
25547
 
                    }
25548
 
                    break;
25549
 
                }
25550
 
                case 'Z': {
25551
 
                    $this->_out('h');
25552
 
                    break;
25553
 
                }
25554
 
            }
25555
 
        } // end foreach
25556
 
        if (!empty($op)) {
25557
 
            $this->_out($op);
25558
 
        }
25559
 
        return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
25560
 
    }
25561
 
 
25562
 
    /**
25563
 
     * Returns the angle in radiants between two vectors
25564
 
     * @param $x1 (int) X coordiante of first vector point
25565
 
     * @param $y1 (int) Y coordiante of first vector point
25566
 
     * @param $x2 (int) X coordiante of second vector point
25567
 
     * @param $y2 (int) Y coordiante of second vector point
25568
 
     * @author Nicola Asuni
25569
 
     * @since 5.0.000 (2010-05-04)
25570
 
     * @protected
25571
 
     */
25572
 
    protected function getVectorsAngle($x1, $y1, $x2, $y2) {
25573
 
        $dprod = ($x1 * $x2) + ($y1 * $y2);
25574
 
        $dist1 = sqrt(($x1 * $x1) + ($y1 * $y1));
25575
 
        $dist2 = sqrt(($x2 * $x2) + ($y2 * $y2));
25576
 
        $angle = acos($dprod / ($dist1 * $dist2));
25577
 
        if (is_nan($angle)) {
25578
 
            $angle = M_PI;
25579
 
        }
25580
 
        if ((($x1 * $y2) - ($x2 * $y1)) < 0) {
25581
 
            $angle *= -1;
25582
 
        }
25583
 
        return $angle;
25584
 
    }
25585
 
 
25586
 
    /**
25587
 
     * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***)
25588
 
     * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
25589
 
     * @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.
25590
 
     * @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.
25591
 
     * @param $ctm (array) tranformation matrix for clipping mode (starting transformation matrix).
25592
 
     * @author Nicola Asuni
25593
 
     * @since 5.0.000 (2010-05-02)
25594
 
     * @protected
25595
 
     */
25596
 
    protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
25597
 
        // check if we are in clip mode
25598
 
        if ($this->svgclipmode) {
25599
 
            $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]);
25600
 
            return;
25601
 
        }
25602
 
        if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
25603
 
            if (!isset($attribs['id'])) {
25604
 
                $attribs['id'] = 'DF_'.(count($this->svgdefs) + 1);
25605
 
            }
25606
 
            $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
25607
 
            return;
25608
 
        }
25609
 
        $clipping = false;
25610
 
        if ($parser == 'clip-path') {
25611
 
            // set clipping mode
25612
 
            $clipping = true;
25613
 
        }
25614
 
        // get styling properties
25615
 
        $prev_svgstyle = $this->svgstyles[(count($this->svgstyles) - 1)]; // previous style
25616
 
        $svgstyle = $this->svgstyles[0]; // set default style
25617
 
        if (isset($attribs['style']) AND !$this->empty_string($attribs['style'])) {
25618
 
            // fix style for regular expression
25619
 
            $attribs['style'] = ';'.$attribs['style'];
25620
 
        }
25621
 
        foreach ($prev_svgstyle as $key => $val) {
25622
 
            if (in_array($key, $this->svginheritprop)) {
25623
 
                // inherit previous value
25624
 
                $svgstyle[$key] = $val;
25625
 
            }
25626
 
            if (isset($attribs[$key]) AND !$this->empty_string($attribs[$key])) {
25627
 
                // specific attribute settings
25628
 
                if ($attribs[$key] == 'inherit') {
25629
 
                    $svgstyle[$key] = $val;
25630
 
                } else {
25631
 
                    $svgstyle[$key] = $attribs[$key];
25632
 
                }
25633
 
            } elseif (isset($attribs['style']) AND !$this->empty_string($attribs['style'])) {
25634
 
                // CSS style syntax
25635
 
                $attrval = array();
25636
 
                if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
25637
 
                    if ($attrval[1] == 'inherit') {
25638
 
                        $svgstyle[$key] = $val;
25639
 
                    } else {
25640
 
                        $svgstyle[$key] = $attrval[1];
25641
 
                    }
25642
 
                }
25643
 
            }
25644
 
        }
25645
 
        // transformation matrix
25646
 
        if (!empty($ctm)) {
25647
 
            $tm = $ctm;
25648
 
        } else {
25649
 
            $tm = $this->svgstyles[(count($this->svgstyles) - 1)]['transfmatrix'];
25650
 
        }
25651
 
        if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
25652
 
            $tm = $this->getTransformationMatrixProduct($tm, $this->getSVGTransformMatrix($attribs['transform']));
25653
 
        }
25654
 
        $svgstyle['transfmatrix'] = $tm;
25655
 
        $invisible = false;
25656
 
        if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
25657
 
            // the current graphics element is invisible (nothing is painted)
25658
 
            $invisible = true;
25659
 
        }
25660
 
        // process tag
25661
 
        switch($name) {
25662
 
            case 'defs': {
25663
 
                $this->svgdefsmode = true;
25664
 
                break;
25665
 
            }
25666
 
            // clipPath
25667
 
            case 'clipPath': {
25668
 
                if ($invisible) {
25669
 
                    break;
25670
 
                }
25671
 
                $this->svgclipmode = true;
25672
 
                if (!isset($attribs['id'])) {
25673
 
                    $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1);
25674
 
                }
25675
 
                $this->svgclipid = $attribs['id'];
25676
 
                $this->svgclippaths[$this->svgclipid] = array();
25677
 
                $this->svgcliptm[$this->svgclipid] = $tm;
25678
 
                break;
25679
 
            }
25680
 
            case 'svg': {
25681
 
                // start of SVG object
25682
 
                break;
25683
 
            }
25684
 
            case 'g': {
25685
 
                // group together related graphics elements
25686
 
                array_push($this->svgstyles, $svgstyle);
25687
 
                $this->StartTransform();
25688
 
                $this->setSVGStyles($svgstyle, $prev_svgstyle);
25689
 
                break;
25690
 
            }
25691
 
            case 'linearGradient': {
25692
 
                if (!isset($attribs['id'])) {
25693
 
                    $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
25694
 
                }
25695
 
                $this->svggradientid = $attribs['id'];
25696
 
                $this->svggradients[$this->svggradientid] = array();
25697
 
                $this->svggradients[$this->svggradientid]['type'] = 2;
25698
 
                $this->svggradients[$this->svggradientid]['stops'] = array();
25699
 
                if (isset($attribs['gradientUnits'])) {
25700
 
                    $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
25701
 
                } else {
25702
 
                    $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
25703
 
                }
25704
 
                //$attribs['spreadMethod']
25705
 
                $x1 = (isset($attribs['x1'])?$attribs['x1']:'0%');
25706
 
                $y1 = (isset($attribs['y1'])?$attribs['y1']:'0%');
25707
 
                $x2 = (isset($attribs['x2'])?$attribs['x2']:'100%');
25708
 
                $y2 = (isset($attribs['y2'])?$attribs['y2']:'0%');
25709
 
                if (substr($x1, -1) != '%') {
25710
 
                    $this->svggradients[$this->svggradientid]['mode'] = 'measure';
25711
 
                } else {
25712
 
                    $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
25713
 
                }
25714
 
                if (isset($attribs['gradientTransform'])) {
25715
 
                    $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']);
25716
 
                }
25717
 
                $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2);
25718
 
                if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
25719
 
                    // gradient is defined on another place
25720
 
                    $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
25721
 
                }
25722
 
                break;
25723
 
            }
25724
 
            case 'radialGradient': {
25725
 
                if (!isset($attribs['id'])) {
25726
 
                    $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
25727
 
                }
25728
 
                $this->svggradientid = $attribs['id'];
25729
 
                $this->svggradients[$this->svggradientid] = array();
25730
 
                $this->svggradients[$this->svggradientid]['type'] = 3;
25731
 
                $this->svggradients[$this->svggradientid]['stops'] = array();
25732
 
                if (isset($attribs['gradientUnits'])) {
25733
 
                    $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
25734
 
                } else {
25735
 
                    $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
25736
 
                }
25737
 
                //$attribs['spreadMethod']
25738
 
                $cx = (isset($attribs['cx'])?$attribs['cx']:0.5);
25739
 
                $cy = (isset($attribs['cy'])?$attribs['cy']:0.5);
25740
 
                $fx = (isset($attribs['fx'])?$attribs['fx']:$cx);
25741
 
                $fy = (isset($attribs['fy'])?$attribs['fy']:$cy);
25742
 
                $r = (isset($attribs['r'])?$attribs['r']:0.5);
25743
 
                if (isset($attribs['cx']) AND (substr($attribs['cx'], -1) != '%')) {
25744
 
                    $this->svggradients[$this->svggradientid]['mode'] = 'measure';
25745
 
                } else {
25746
 
                    $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
25747
 
                }
25748
 
                if (isset($attribs['gradientTransform'])) {
25749
 
                    $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']);
25750
 
                }
25751
 
                $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r);
25752
 
                if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
25753
 
                    // gradient is defined on another place
25754
 
                    $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
25755
 
                }
25756
 
                break;
25757
 
            }
25758
 
            case 'stop': {
25759
 
                // gradient stops
25760
 
                if (substr($attribs['offset'], -1) == '%') {
25761
 
                    $offset = floatval(substr($attribs['offset'], -1)) / 100;
25762
 
                } else {
25763
 
                    $offset = floatval($attribs['offset']);
25764
 
                    if ($offset > 1) {
25765
 
                        $offset /= 100;
25766
 
                    }
25767
 
                }
25768
 
                $stop_color = isset($svgstyle['stop-color'])?$this->convertHTMLColorToDec($svgstyle['stop-color']):'black';
25769
 
                $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
25770
 
                $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
25771
 
                break;
25772
 
            }
25773
 
            // paths
25774
 
            case 'path': {
25775
 
                if ($invisible) {
25776
 
                    break;
25777
 
                }
25778
 
                if (isset($attribs['d'])) {
25779
 
                    $d = trim($attribs['d']);
25780
 
                    if (!empty($d)) {
25781
 
                        if ($clipping) {
25782
 
                            $this->SVGTransform($tm);
25783
 
                            $this->SVGPath($d, 'CNZ');
25784
 
                        } else {
25785
 
                            $this->StartTransform();
25786
 
                            $this->SVGTransform($tm);
25787
 
                            $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, 0, 0, 1, 1, 'SVGPath', array($d, 'CNZ'));
25788
 
                            if (!empty($obstyle)) {
25789
 
                                $this->SVGPath($d, $obstyle);
25790
 
                            }
25791
 
                            $this->StopTransform();
25792
 
                        }
25793
 
                    }
25794
 
                }
25795
 
                break;
25796
 
            }
25797
 
            // shapes
25798
 
            case 'rect': {
25799
 
                if ($invisible) {
25800
 
                    break;
25801
 
                }
25802
 
                $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
25803
 
                $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
25804
 
                $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
25805
 
                $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
25806
 
                $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
25807
 
                $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx);
25808
 
                if ($clipping) {
25809
 
                    $this->SVGTransform($tm);
25810
 
                    $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
25811
 
                } else {
25812
 
                    $this->StartTransform();
25813
 
                    $this->SVGTransform($tm);
25814
 
                    $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
25815
 
                    if (!empty($obstyle)) {
25816
 
                        $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
25817
 
                    }
25818
 
                    $this->StopTransform();
25819
 
                }
25820
 
                break;
25821
 
            }
25822
 
            case 'circle': {
25823
 
                if ($invisible) {
25824
 
                    break;
25825
 
                }
25826
 
                $cx = (isset($attribs['cx'])?$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false):0);
25827
 
                $cy = (isset($attribs['cy'])?$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false):0);
25828
 
                $r = (isset($attribs['r'])?$this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false):0);
25829
 
                $x = $cx - $r;
25830
 
                $y = $cy - $r;
25831
 
                $w = 2 * $r;
25832
 
                $h = $w;
25833
 
                if ($clipping) {
25834
 
                    $this->SVGTransform($tm);
25835
 
                    $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
25836
 
                } else {
25837
 
                    $this->StartTransform();
25838
 
                    $this->SVGTransform($tm);
25839
 
                    $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
25840
 
                    if (!empty($obstyle)) {
25841
 
                        $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
25842
 
                    }
25843
 
                    $this->StopTransform();
25844
 
                }
25845
 
                break;
25846
 
            }
25847
 
            case 'ellipse': {
25848
 
                if ($invisible) {
25849
 
                    break;
25850
 
                }
25851
 
                $cx = (isset($attribs['cx'])?$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false):0);
25852
 
                $cy = (isset($attribs['cy'])?$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false):0);
25853
 
                $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
25854
 
                $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):0);
25855
 
                $x = $cx - $rx;
25856
 
                $y = $cy - $ry;
25857
 
                $w = 2 * $rx;
25858
 
                $h = 2 * $ry;
25859
 
                if ($clipping) {
25860
 
                    $this->SVGTransform($tm);
25861
 
                    $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
25862
 
                } else {
25863
 
                    $this->StartTransform();
25864
 
                    $this->SVGTransform($tm);
25865
 
                    $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
25866
 
                    if (!empty($obstyle)) {
25867
 
                        $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
25868
 
                    }
25869
 
                    $this->StopTransform();
25870
 
                }
25871
 
                break;
25872
 
            }
25873
 
            case 'line': {
25874
 
                if ($invisible) {
25875
 
                    break;
25876
 
                }
25877
 
                $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0);
25878
 
                $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0);
25879
 
                $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0);
25880
 
                $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0);
25881
 
                $x = $x1;
25882
 
                $y = $y1;
25883
 
                $w = abs($x2 - $x1);
25884
 
                $h = abs($y2 - $y1);
25885
 
                if (!$clipping) {
25886
 
                    $this->StartTransform();
25887
 
                    $this->SVGTransform($tm);
25888
 
                    $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
25889
 
                    $this->Line($x1, $y1, $x2, $y2);
25890
 
                    $this->StopTransform();
25891
 
                }
25892
 
                break;
25893
 
            }
25894
 
            case 'polyline':
25895
 
            case 'polygon': {
25896
 
                if ($invisible) {
25897
 
                    break;
25898
 
                }
25899
 
                $points = (isset($attribs['points'])?$attribs['points']:'0 0');
25900
 
                $points = trim($points);
25901
 
                // note that point may use a complex syntax not covered here
25902
 
                $points = preg_split('/[\,\s]+/si', $points);
25903
 
                if (count($points) < 4) {
25904
 
                    break;
25905
 
                }
25906
 
                $p = array();
25907
 
                $xmin = 2147483647;
25908
 
                $xmax = 0;
25909
 
                $ymin = 2147483647;
25910
 
                $ymax = 0;
25911
 
                foreach ($points as $key => $val) {
25912
 
                    $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
25913
 
                    if (($key % 2) == 0) {
25914
 
                        // X coordinate
25915
 
                        $xmin = min($xmin, $p[$key]);
25916
 
                        $xmax = max($xmax, $p[$key]);
25917
 
                    } else {
25918
 
                        // Y coordinate
25919
 
                        $ymin = min($ymin, $p[$key]);
25920
 
                        $ymax = max($ymax, $p[$key]);
25921
 
                    }
25922
 
                }
25923
 
                $x = $xmin;
25924
 
                $y = $ymin;
25925
 
                $w = ($xmax - $xmin);
25926
 
                $h = ($ymax - $ymin);
25927
 
                if ($name == 'polyline') {
25928
 
                    $this->StartTransform();
25929
 
                    $this->SVGTransform($tm);
25930
 
                    $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
25931
 
                    $this->PolyLine($p, 'D', array(), array());
25932
 
                    $this->StopTransform();
25933
 
                } else { // polygon
25934
 
                    if ($clipping) {
25935
 
                        $this->SVGTransform($tm);
25936
 
                        $this->Polygon($p, 'CNZ', array(), array(), true);
25937
 
                    } else {
25938
 
                        $this->StartTransform();
25939
 
                        $this->SVGTransform($tm);
25940
 
                        $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
25941
 
                        if (!empty($obstyle)) {
25942
 
                            $this->Polygon($p, $obstyle, array(), array(), true);
25943
 
                        }
25944
 
                        $this->StopTransform();
25945
 
                    }
25946
 
                }
25947
 
                break;
25948
 
            }
25949
 
            // image
25950
 
            case 'image': {
25951
 
                if ($invisible) {
25952
 
                    break;
25953
 
                }
25954
 
                if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
25955
 
                    break;
25956
 
                }
25957
 
                $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
25958
 
                $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
25959
 
                $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
25960
 
                $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
25961
 
                $img = $attribs['xlink:href'];
25962
 
                if (!$clipping) {
25963
 
                    $this->StartTransform();
25964
 
                    $this->SVGTransform($tm);
25965
 
                    $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
25966
 
                    // fix image path
25967
 
                    if (!$this->empty_string($this->svgdir) AND (($img{0} == '.') OR (basename($img) == $img))) {
25968
 
                        // replace relative path with full server path
25969
 
                        $img = $this->svgdir.'/'.$img;
25970
 
                    }
25971
 
                    if (($img{0} == '/') AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
25972
 
                        $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
25973
 
                        if (($findroot === false) OR ($findroot > 1)) {
25974
 
                            // replace relative path with full server path
25975
 
                            $img = $_SERVER['DOCUMENT_ROOT'].$img;
25976
 
                        }
25977
 
                    }
25978
 
                    $img = urldecode($img);
25979
 
                    $testscrtype = @parse_url($img);
25980
 
                    if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
25981
 
                        // convert URL to server path
25982
 
                        $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img);
25983
 
                    }
25984
 
                    $this->Image($img, $x, $y, $w, $h);
25985
 
                    $this->StopTransform();
25986
 
                }
25987
 
                break;
25988
 
            }
25989
 
            // text
25990
 
            case 'text':
25991
 
            case 'tspan': {
25992
 
                $this->svgtextmode['invisible'] = $invisible;
25993
 
                if ($invisible) {
25994
 
                    break;
25995
 
                }
25996
 
                array_push($this->svgstyles, $svgstyle);
25997
 
                // only basic support - advanced features must be implemented
25998
 
                $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):$this->x);
25999
 
                $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):$this->y);
26000
 
                $svgstyle['text-color'] = $svgstyle['fill'];
26001
 
                $this->svgtext = '';
26002
 
                if (isset($svgstyle['text-anchor'])) {
26003
 
                    $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor'];
26004
 
                } else {
26005
 
                    $this->svgtextmode['text-anchor'] = 'start';
26006
 
                }
26007
 
                if (isset($svgstyle['direction'])) {
26008
 
                    if ($svgstyle['direction'] == 'rtl') {
26009
 
                        $this->svgtextmode['rtl'] = true;
26010
 
                    } else {
26011
 
                        $this->svgtextmode['rtl'] = false;
26012
 
                    }
26013
 
                } else {
26014
 
                    $this->svgtextmode['rtl'] = false;
26015
 
                }
26016
 
                if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
26017
 
                    $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false);
26018
 
                } else {
26019
 
                    $this->svgtextmode['stroke'] = false;
26020
 
                }
26021
 
                $this->StartTransform();
26022
 
                $this->SVGTransform($tm);
26023
 
                $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
26024
 
                $this->x = $x;
26025
 
                $this->y = $y;
26026
 
                break;
26027
 
            }
26028
 
            // use
26029
 
            case 'use': {
26030
 
                if (isset($attribs['xlink:href'])) {
26031
 
                    $use = $this->svgdefs[substr($attribs['xlink:href'], 1)];
26032
 
                    if (isset($attribs['xlink:href'])) {
26033
 
                        unset($attribs['xlink:href']);
26034
 
                    }
26035
 
                    if (isset($attribs['id'])) {
26036
 
                        unset($attribs['id']);
26037
 
                    }
26038
 
                    $attribs = array_merge($use['attribs'], $attribs);
26039
 
                    $this->startSVGElementHandler($parser, $use['name'], $use['attribs']);
26040
 
                }
26041
 
                break;
26042
 
            }
26043
 
            default: {
26044
 
                break;
26045
 
            }
26046
 
        } // end of switch
26047
 
    }
26048
 
 
26049
 
    /**
26050
 
     * Sets the closing SVG element handler function for the XML parser.
26051
 
     * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
26052
 
     * @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.
26053
 
     * @author Nicola Asuni
26054
 
     * @since 5.0.000 (2010-05-02)
26055
 
     * @protected
26056
 
     */
26057
 
    protected function endSVGElementHandler($parser, $name) {
26058
 
        switch($name) {
26059
 
            case 'defs': {
26060
 
                $this->svgdefsmode = false;
26061
 
                break;
26062
 
            }
26063
 
            // clipPath
26064
 
            case 'clipPath': {
26065
 
                $this->svgclipmode = false;
26066
 
                break;
26067
 
            }
26068
 
            case 'g': {
26069
 
                // ungroup: remove last style from array
26070
 
                array_pop($this->svgstyles);
26071
 
                $this->StopTransform();
26072
 
                break;
26073
 
            }
26074
 
            case 'text':
26075
 
            case 'tspan': {
26076
 
                if ($this->svgtextmode['invisible']) {
26077
 
                    // This implementation must be fixed to following the rule:
26078
 
                    // 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.
26079
 
                    break;
26080
 
                }
26081
 
                // print text
26082
 
                $text = $this->stringTrim($this->svgtext);
26083
 
                if ($this->svgtextmode['text-anchor'] != 'start') {
26084
 
                    $textlen = $this->GetStringWidth($text);
26085
 
                    // check if string is RTL text
26086
 
                    if ($this->svgtextmode['text-anchor'] == 'end') {
26087
 
                        if ($this->svgtextmode['rtl']) {
26088
 
                            $this->x += $textlen;
26089
 
                        } else {
26090
 
                            $this->x -= $textlen;
26091
 
                        }
26092
 
                    } elseif ($this->svgtextmode['text-anchor'] == 'middle') {
26093
 
                        if ($this->svgtextmode['rtl']) {
26094
 
                            $this->x += ($textlen / 2);
26095
 
                        } else {
26096
 
                            $this->x -= ($textlen / 2);
26097
 
                        }
26098
 
                    }
26099
 
                }
26100
 
                $textrendermode = $this->textrendermode;
26101
 
                $textstrokewidth = $this->textstrokewidth;
26102
 
                $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false);
26103
 
                $this->Cell(0, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
26104
 
                // restore previous rendering mode
26105
 
                $this->textrendermode = $textrendermode;
26106
 
                $this->textstrokewidth = $textstrokewidth;
26107
 
                $this->svgtext = '';
26108
 
                $this->StopTransform();
26109
 
                array_pop($this->svgstyles);
26110
 
                break;
26111
 
            }
26112
 
            default: {
26113
 
                break;
26114
 
            }
26115
 
        }
26116
 
    }
26117
 
 
26118
 
    /**
26119
 
     * Sets the character data handler function for the XML parser.
26120
 
     * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
26121
 
     * @param $data (string) The second parameter, data, contains the character data as a string.
26122
 
     * @author Nicola Asuni
26123
 
     * @since 5.0.000 (2010-05-02)
26124
 
     * @protected
26125
 
     */
26126
 
    protected function segSVGContentHandler($parser, $data) {
26127
 
        $this->svgtext .= $data;
26128
 
    }
26129
 
 
26130
 
    // --- END SVG METHODS -----------------------------
 
21460
         * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
 
21461
         * @param $reseth (boolean) if true reset the last cell height (default true).
 
21462
         * @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>
 
21463
         * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
 
21464
         * @see Multicell(), writeHTML()
 
21465
         * @public
 
21466
         */
 
21467
        public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
 
21468
                return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
 
21469
        }
 
21470
 
 
21471
        /**
 
21472
         * Allows to preserve some HTML formatting (limited support).<br />
 
21473
         * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
 
21474
         * 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
 
21475
         * @param $html (string) text to display
 
21476
         * @param $ln (boolean) if true add a new line after text (default = true)
 
21477
         * @param $fill (boolean) Indicates if the background must be painted (true) or transparent (false).
 
21478
         * @param $reseth (boolean) if true reset the last cell height (default false).
 
21479
         * @param $cell (boolean) if true add the current left (or right for RTL) padding to each Write (default false).
 
21480
         * @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>
 
21481
         * @public
 
21482
         */
 
21483
        public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
 
21484
                $gvars = $this->getGraphicVars();
 
21485
                // store current values
 
21486
                $prev_cell_margin = $this->cell_margin;
 
21487
                $prev_cell_padding = $this->cell_padding;
 
21488
                $prevPage = $this->page;
 
21489
                $prevlMargin = $this->lMargin;
 
21490
                $prevrMargin = $this->rMargin;
 
21491
                $curfontname = $this->FontFamily;
 
21492
                $curfontstyle = $this->FontStyle;
 
21493
                $curfontsize = $this->FontSizePt;
 
21494
                $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
 
21495
                $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
 
21496
                $curfontstretcing = $this->font_stretching;
 
21497
                $curfontkerning = $this->font_spacing;
 
21498
                $this->newline = true;
 
21499
                $newline = true;
 
21500
                $startlinepage = $this->page;
 
21501
                $minstartliney = $this->y;
 
21502
                $maxbottomliney = 0;
 
21503
                $startlinex = $this->x;
 
21504
                $startliney = $this->y;
 
21505
                $yshift = 0;
 
21506
                $loop = 0;
 
21507
                $curpos = 0;
 
21508
                $this_method_vars = array();
 
21509
                $undo = false;
 
21510
                $fontaligned = false;
 
21511
                $reverse_dir = false; // true when the text direction is reversed
 
21512
                $this->premode = false;
 
21513
                if ($this->inxobj) {
 
21514
                        // we are inside an XObject template
 
21515
                        $pask = count($this->xobjects[$this->xobjid]['annotations']);
 
21516
                } elseif (isset($this->PageAnnots[$this->page])) {
 
21517
                        $pask = count($this->PageAnnots[$this->page]);
 
21518
                } else {
 
21519
                        $pask = 0;
 
21520
                }
 
21521
                if ($this->inxobj) {
 
21522
                        // we are inside an XObject template
 
21523
                        $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
 
21524
                } elseif (!$this->InFooter) {
 
21525
                        if (isset($this->footerlen[$this->page])) {
 
21526
                                $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
 
21527
                        } else {
 
21528
                                $this->footerpos[$this->page] = $this->pagelen[$this->page];
 
21529
                        }
 
21530
                        $startlinepos = $this->footerpos[$this->page];
 
21531
                } else {
 
21532
                        // we are inside the footer
 
21533
                        $startlinepos = $this->pagelen[$this->page];
 
21534
                }
 
21535
                $lalign = $align;
 
21536
                $plalign = $align;
 
21537
                if ($this->rtl) {
 
21538
                        $w = $this->x - $this->lMargin;
 
21539
                } else {
 
21540
                        $w = $this->w - $this->rMargin - $this->x;
 
21541
                }
 
21542
                $w -= ($this->cell_padding['L'] + $this->cell_padding['R']);
 
21543
                if ($cell) {
 
21544
                        if ($this->rtl) {
 
21545
                                $this->x -= $this->cell_padding['R'];
 
21546
                                $this->lMargin += $this->cell_padding['R'];
 
21547
                        } else {
 
21548
                                $this->x += $this->cell_padding['L'];
 
21549
                                $this->rMargin += $this->cell_padding['L'];
 
21550
                        }
 
21551
                }
 
21552
                if ($this->customlistindent >= 0) {
 
21553
                        $this->listindent = $this->customlistindent;
 
21554
                } else {
 
21555
                        $this->listindent = $this->GetStringWidth('000000');
 
21556
                }
 
21557
                $this->listindentlevel = 0;
 
21558
                // save previous states
 
21559
                $prev_cell_height_ratio = $this->cell_height_ratio;
 
21560
                $prev_listnum = $this->listnum;
 
21561
                $prev_listordered = $this->listordered;
 
21562
                $prev_listcount = $this->listcount;
 
21563
                $prev_lispacer = $this->lispacer;
 
21564
                $this->listnum = 0;
 
21565
                $this->listordered = array();
 
21566
                $this->listcount = array();
 
21567
                $this->lispacer = '';
 
21568
                if (($this->empty_string($this->lasth)) OR ($reseth)) {
 
21569
                        // reset row height
 
21570
                        $this->resetLastH();
 
21571
                }
 
21572
                $dom = $this->getHtmlDomArray($html);
 
21573
                $maxel = count($dom);
 
21574
                $key = 0;
 
21575
                $hidden_node_key = -1;
 
21576
                while ($key < $maxel) {
 
21577
                        if ($dom[$key]['tag']) {
 
21578
                                if ($dom[$key]['opening']) {
 
21579
                                        if (($hidden_node_key <= 0) AND $dom[$key]['hide']) {
 
21580
                                                // store the node key
 
21581
                                                $hidden_node_key = $key;
 
21582
                                        }
 
21583
                                } elseif (($hidden_node_key > 0) AND ($dom[$key]['parent'] == $hidden_node_key)) {
 
21584
                                        // we have reached the closing tag of the hidden node
 
21585
                                        $hidden_node_key = 0;
 
21586
                                }
 
21587
                        }
 
21588
                        if ($hidden_node_key >= 0) {
 
21589
                                // skip this node
 
21590
                                ++$key;
 
21591
                                if ($hidden_node_key == 0) {
 
21592
                                        // reset hidden mode
 
21593
                                        $hidden_node_key = -1;
 
21594
                                }
 
21595
                                continue;
 
21596
                        }
 
21597
                        if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
 
21598
                                // check for pagebreak
 
21599
                                if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
 
21600
                                        // add a page (or trig AcceptPageBreak() for multicolumn mode)
 
21601
                                        $this->checkPageBreak($this->PageBreakTrigger + 1);
 
21602
                                        $this->htmlvspace = ($this->PageBreakTrigger + 1);
 
21603
                                }
 
21604
                                if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
 
21605
                                        OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
 
21606
                                        // add a page (or trig AcceptPageBreak() for multicolumn mode)
 
21607
                                        $this->checkPageBreak($this->PageBreakTrigger + 1);
 
21608
                                        $this->htmlvspace = ($this->PageBreakTrigger + 1);
 
21609
                                }
 
21610
                        }
 
21611
                        if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
 
21612
                                if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
 
21613
                                        $dom[$key]['attribute']['nobr'] = false;
 
21614
                                } else {
 
21615
                                        // store current object
 
21616
                                        $this->startTransaction();
 
21617
                                        // save this method vars
 
21618
                                        $this_method_vars['html'] = $html;
 
21619
                                        $this_method_vars['ln'] = $ln;
 
21620
                                        $this_method_vars['fill'] = $fill;
 
21621
                                        $this_method_vars['reseth'] = $reseth;
 
21622
                                        $this_method_vars['cell'] = $cell;
 
21623
                                        $this_method_vars['align'] = $align;
 
21624
                                        $this_method_vars['gvars'] = $gvars;
 
21625
                                        $this_method_vars['prevPage'] = $prevPage;
 
21626
                                        $this_method_vars['prev_cell_margin'] = $prev_cell_margin;
 
21627
                                        $this_method_vars['prev_cell_padding'] = $prev_cell_padding;
 
21628
                                        $this_method_vars['prevlMargin'] = $prevlMargin;
 
21629
                                        $this_method_vars['prevrMargin'] = $prevrMargin;
 
21630
                                        $this_method_vars['curfontname'] = $curfontname;
 
21631
                                        $this_method_vars['curfontstyle'] = $curfontstyle;
 
21632
                                        $this_method_vars['curfontsize'] = $curfontsize;
 
21633
                                        $this_method_vars['curfontascent'] = $curfontascent;
 
21634
                                        $this_method_vars['curfontdescent'] = $curfontdescent;
 
21635
                                        $this_method_vars['curfontstretcing'] = $curfontstretcing;
 
21636
                                        $this_method_vars['curfontkerning'] = $curfontkerning;
 
21637
                                        $this_method_vars['minstartliney'] = $minstartliney;
 
21638
                                        $this_method_vars['maxbottomliney'] = $maxbottomliney;
 
21639
                                        $this_method_vars['yshift'] = $yshift;
 
21640
                                        $this_method_vars['startlinepage'] = $startlinepage;
 
21641
                                        $this_method_vars['startlinepos'] = $startlinepos;
 
21642
                                        $this_method_vars['startlinex'] = $startlinex;
 
21643
                                        $this_method_vars['startliney'] = $startliney;
 
21644
                                        $this_method_vars['newline'] = $newline;
 
21645
                                        $this_method_vars['loop'] = $loop;
 
21646
                                        $this_method_vars['curpos'] = $curpos;
 
21647
                                        $this_method_vars['pask'] = $pask;
 
21648
                                        $this_method_vars['lalign'] = $lalign;
 
21649
                                        $this_method_vars['plalign'] = $plalign;
 
21650
                                        $this_method_vars['w'] = $w;
 
21651
                                        $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
 
21652
                                        $this_method_vars['prev_listnum'] = $prev_listnum;
 
21653
                                        $this_method_vars['prev_listordered'] = $prev_listordered;
 
21654
                                        $this_method_vars['prev_listcount'] = $prev_listcount;
 
21655
                                        $this_method_vars['prev_lispacer'] = $prev_lispacer;
 
21656
                                        $this_method_vars['fontaligned'] = $fontaligned;
 
21657
                                        $this_method_vars['key'] = $key;
 
21658
                                        $this_method_vars['dom'] = $dom;
 
21659
                                }
 
21660
                        }
 
21661
                        // print THEAD block
 
21662
                        if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
 
21663
                                if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !$this->empty_string($dom[$dom[$key]['parent']]['thead'])) {
 
21664
                                        $this->inthead = true;
 
21665
                                        // print table header (thead)
 
21666
                                        $this->writeHTML($this->thead, false, false, false, false, '');
 
21667
                                        // check if we are on a new page or on a new column
 
21668
                                        if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) {
 
21669
                                                // we are on a new page or on a new column and the total object height is less than the available vertical space.
 
21670
                                                // restore previous object
 
21671
                                                $this->rollbackTransaction(true);
 
21672
                                                // restore previous values
 
21673
                                                foreach ($this_method_vars as $vkey => $vval) {
 
21674
                                                        $$vkey = $vval;
 
21675
                                                }
 
21676
                                                // disable table header
 
21677
                                                $tmp_thead = $this->thead;
 
21678
                                                $this->thead = '';
 
21679
                                                // add a page (or trig AcceptPageBreak() for multicolumn mode)
 
21680
                                                $pre_y = $this->y;
 
21681
                                                if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
 
21682
                                                        // fix for multicolumn mode
 
21683
                                                        $startliney = $this->y;
 
21684
                                                }
 
21685
                                                $this->start_transaction_page = $this->page;
 
21686
                                                $this->start_transaction_y = $this->y;
 
21687
                                                // restore table header
 
21688
                                                $this->thead = $tmp_thead;
 
21689
                                                // fix table border properties
 
21690
                                                if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
 
21691
                                                        $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
 
21692
                                                } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
 
21693
                                                        $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
 
21694
                                                } else {
 
21695
                                                        $tmp_cellspacing = 0;
 
21696
                                                }
 
21697
                                                $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page;
 
21698
                                                $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column;
 
21699
                                                $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing;
 
21700
                                                $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']);
 
21701
                                                $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset;
 
21702
                                                $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset;
 
21703
                                                // print table header (thead)
 
21704
                                                $this->writeHTML($this->thead, false, false, false, false, '');
 
21705
                                        }
 
21706
                                }
 
21707
                                // move $key index forward to skip THEAD block
 
21708
                                while ( ($key < $maxel) AND (!(
 
21709
                                        ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
 
21710
                                        OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
 
21711
                                        ++$key;
 
21712
                                }
 
21713
                        }
 
21714
                        if ($dom[$key]['tag'] OR ($key == 0)) {
 
21715
                                if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
 
21716
                                        $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
 
21717
                                }
 
21718
                                // vertically align image in line
 
21719
                                if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
 
21720
                                        // get image height
 
21721
                                        $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], $this->lasth, 'px');
 
21722
                                        $autolinebreak = false;
 
21723
                                        if (isset($dom[$key]['width']) AND ($dom[$key]['width'] > 0)) {
 
21724
                                                $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], 1, 'px', false);
 
21725
                                                if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R']))
 
21726
                                                        AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L'])))
 
21727
                                                        OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) {
 
21728
                                                        // add automatic line break
 
21729
                                                        $autolinebreak = true;
 
21730
                                                        $this->Ln('', $cell);
 
21731
                                                        if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
 
21732
                                                                // go back to evaluate this line break
 
21733
                                                                --$key;
 
21734
                                                        }
 
21735
                                                }
 
21736
                                        }
 
21737
                                        if (!$autolinebreak) {
 
21738
                                                if ($this->inPageBody()) {
 
21739
                                                        $pre_y = $this->y;
 
21740
                                                        // check for page break
 
21741
                                                        if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) {
 
21742
                                                                // fix for multicolumn mode
 
21743
                                                                $startliney = $this->y;
 
21744
                                                        }
 
21745
                                                }
 
21746
                                                if ($this->page > $startlinepage) {
 
21747
                                                        // fix line splitted over two pages
 
21748
                                                        if (isset($this->footerlen[$startlinepage])) {
 
21749
                                                                $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
 
21750
                                                        }
 
21751
                                                        // line to be moved one page forward
 
21752
                                                        $pagebuff = $this->getPageBuffer($startlinepage);
 
21753
                                                        $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
 
21754
                                                        $tstart = substr($pagebuff, 0, $startlinepos);
 
21755
                                                        $tend = substr($this->getPageBuffer($startlinepage), $curpos);
 
21756
                                                        // remove line from previous page
 
21757
                                                        $this->setPageBuffer($startlinepage, $tstart.''.$tend);
 
21758
                                                        $pagebuff = $this->getPageBuffer($this->page);
 
21759
                                                        $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
 
21760
                                                        $tend = substr($pagebuff, $this->cntmrk[$this->page]);
 
21761
                                                        // add line start to current page
 
21762
                                                        $yshift = ($minstartliney - $this->y);
 
21763
                                                        if ($fontaligned) {
 
21764
                                                                $yshift += ($curfontsize / $this->k);
 
21765
                                                        }
 
21766
                                                        $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
 
21767
                                                        $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
 
21768
                                                        // shift the annotations and links
 
21769
                                                        if (isset($this->PageAnnots[$this->page])) {
 
21770
                                                                $next_pask = count($this->PageAnnots[$this->page]);
 
21771
                                                        } else {
 
21772
                                                                $next_pask = 0;
 
21773
                                                        }
 
21774
                                                        if (isset($this->PageAnnots[$startlinepage])) {
 
21775
                                                                foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
 
21776
                                                                        if ($pak >= $pask) {
 
21777
                                                                                $this->PageAnnots[$this->page][] = $pac;
 
21778
                                                                                unset($this->PageAnnots[$startlinepage][$pak]);
 
21779
                                                                                $npak = count($this->PageAnnots[$this->page]) - 1;
 
21780
                                                                                $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
 
21781
                                                                        }
 
21782
                                                                }
 
21783
                                                        }
 
21784
                                                        $pask = $next_pask;
 
21785
                                                        $startlinepos = $this->cntmrk[$this->page];
 
21786
                                                        $startlinepage = $this->page;
 
21787
                                                        $startliney = $this->y;
 
21788
                                                        $this->newline = false;
 
21789
                                                }
 
21790
                                                $this->y += ((($curfontsize * $this->cell_height_ratio / $this->k) + $curfontascent - $curfontdescent) / 2) - $imgh;
 
21791
                                                $minstartliney = min($this->y, $minstartliney);
 
21792
                                                $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio));
 
21793
                                        }
 
21794
                                } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
 
21795
                                        // account for different font size
 
21796
                                        $pfontname = $curfontname;
 
21797
                                        $pfontstyle = $curfontstyle;
 
21798
                                        $pfontsize = $curfontsize;
 
21799
                                        $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
 
21800
                                        $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
 
21801
                                        $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
 
21802
                                        $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
 
21803
                                        $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
 
21804
                                        if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
 
21805
                                                OR ($this->cell_height_ratio != $dom[$key]['line-height'])
 
21806
                                                OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
 
21807
                                                if (($key < ($maxel - 1)) AND (
 
21808
                                                                ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
 
21809
                                                                OR ($this->cell_height_ratio != $dom[$key]['line-height'])
 
21810
                                                                OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize) AND ($fontsize >= 0) AND ($curfontsize >= 0) AND ($fontsize != $curfontsize))
 
21811
                                                        )) {
 
21812
                                                        if ($this->page > $startlinepage) {
 
21813
                                                                // fix lines splitted over two pages
 
21814
                                                                if (isset($this->footerlen[$startlinepage])) {
 
21815
                                                                        $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
 
21816
                                                                }
 
21817
                                                                // line to be moved one page forward
 
21818
                                                                $pagebuff = $this->getPageBuffer($startlinepage);
 
21819
                                                                $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
 
21820
                                                                $tstart = substr($pagebuff, 0, $startlinepos);
 
21821
                                                                $tend = substr($this->getPageBuffer($startlinepage), $curpos);
 
21822
                                                                // remove line start from previous page
 
21823
                                                                $this->setPageBuffer($startlinepage, $tstart.''.$tend);
 
21824
                                                                $pagebuff = $this->getPageBuffer($this->page);
 
21825
                                                                $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
 
21826
                                                                $tend = substr($pagebuff, $this->cntmrk[$this->page]);
 
21827
                                                                // add line start to current page
 
21828
                                                                $yshift = ($minstartliney - $this->y);
 
21829
                                                                $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
 
21830
                                                                $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
 
21831
                                                                // shift the annotations and links
 
21832
                                                                if (isset($this->PageAnnots[$this->page])) {
 
21833
                                                                        $next_pask = count($this->PageAnnots[$this->page]);
 
21834
                                                                } else {
 
21835
                                                                        $next_pask = 0;
 
21836
                                                                }
 
21837
                                                                if (isset($this->PageAnnots[$startlinepage])) {
 
21838
                                                                        foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
 
21839
                                                                                if ($pak >= $pask) {
 
21840
                                                                                        $this->PageAnnots[$this->page][] = $pac;
 
21841
                                                                                        unset($this->PageAnnots[$startlinepage][$pak]);
 
21842
                                                                                        $npak = count($this->PageAnnots[$this->page]) - 1;
 
21843
                                                                                        $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
 
21844
                                                                                }
 
21845
                                                                        }
 
21846
                                                                }
 
21847
                                                                $pask = $next_pask;
 
21848
                                                                $startlinepos = $this->cntmrk[$this->page];
 
21849
                                                                $startlinepage = $this->page;
 
21850
                                                                $startliney = $this->y;
 
21851
                                                        }
 
21852
                                                        if (!isset($dom[$key]['line-height'])) {
 
21853
                                                                $dom[$key]['line-height'] = $this->cell_height_ratio;
 
21854
                                                        }
 
21855
                                                        if (!$dom[$key]['block']) {
 
21856
                                                                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']))) {
 
21857
                                                                        $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
 
21858
                                                                }
 
21859
                                                                if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
 
21860
                                                                        $current_line_align_data = array($key, $minstartliney, $maxbottomliney);
 
21861
                                                                        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)))) {
 
21862
                                                                                $minstartliney = min($this->y, $line_align_data[1]);
 
21863
                                                                                $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $line_align_data[2]);
 
21864
                                                                        } else {
 
21865
                                                                                $minstartliney = min($this->y, $minstartliney);
 
21866
                                                                                $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney);
 
21867
                                                                        }
 
21868
                                                                        $line_align_data = $current_line_align_data;
 
21869
                                                                }
 
21870
                                                        }
 
21871
                                                        $this->cell_height_ratio = $dom[$key]['line-height'];
 
21872
                                                        $fontaligned = true;
 
21873
                                                }
 
21874
                                                $this->SetFont($fontname, $fontstyle, $fontsize);
 
21875
                                                // reset row height
 
21876
                                                $this->resetLastH();
 
21877
                                                $curfontname = $fontname;
 
21878
                                                $curfontstyle = $fontstyle;
 
21879
                                                $curfontsize = $fontsize;
 
21880
                                                $curfontascent = $fontascent;
 
21881
                                                $curfontdescent = $fontdescent;
 
21882
                                        }
 
21883
                                }
 
21884
                                // set text rendering mode
 
21885
                                $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
 
21886
                                $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0);
 
21887
                                $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3);
 
21888
                                $this->setTextRenderingMode($textstroke, $textfill, $textclip);
 
21889
                                if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
 
21890
                                        $this->setFontStretching($dom[$key]['font-stretch']);
 
21891
                                }
 
21892
                                if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
 
21893
                                        $this->setFontSpacing($dom[$key]['letter-spacing']);
 
21894
                                }
 
21895
                                if (($plalign == 'J') AND $dom[$key]['block']) {
 
21896
                                        $plalign = '';
 
21897
                                }
 
21898
                                // get current position on page buffer
 
21899
                                $curpos = $this->pagelen[$startlinepage];
 
21900
                                if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
 
21901
                                        $this->SetFillColorArray($dom[$key]['bgcolor']);
 
21902
                                        $wfill = true;
 
21903
                                } else {
 
21904
                                        $wfill = $fill | false;
 
21905
                                }
 
21906
                                if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
 
21907
                                        $this->SetTextColorArray($dom[$key]['fgcolor']);
 
21908
                                }
 
21909
                                if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
 
21910
                                        $this->SetDrawColorArray($dom[$key]['strokecolor']);
 
21911
                                }
 
21912
                                if (isset($dom[$key]['align'])) {
 
21913
                                        $lalign = $dom[$key]['align'];
 
21914
                                }
 
21915
                                if ($this->empty_string($lalign)) {
 
21916
                                        $lalign = $align;
 
21917
                                }
 
21918
                        }
 
21919
                        // align lines
 
21920
                        if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
 
21921
                                $newline = true;
 
21922
                                $fontaligned = false;
 
21923
                                // we are at the beginning of a new line
 
21924
                                if (isset($startlinex)) {
 
21925
                                        $yshift = ($minstartliney - $startliney);
 
21926
                                        if (($yshift > 0) OR ($this->page > $startlinepage)) {
 
21927
                                                $yshift = 0;
 
21928
                                        }
 
21929
                                        $t_x = 0;
 
21930
                                        // the last line must be shifted to be aligned as requested
 
21931
                                        $linew = abs($this->endlinex - $startlinex);
 
21932
                                        if ($this->inxobj) {
 
21933
                                                // we are inside an XObject template
 
21934
                                                $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
 
21935
                                                if (isset($opentagpos)) {
 
21936
                                                        $midpos = $opentagpos;
 
21937
                                                } else {
 
21938
                                                        $midpos = 0;
 
21939
                                                }
 
21940
                                                if ($midpos > 0) {
 
21941
                                                        $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
 
21942
                                                        $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
 
21943
                                                } else {
 
21944
                                                        $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
 
21945
                                                        $pend = '';
 
21946
                                                }
 
21947
                                        } else {
 
21948
                                                $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
 
21949
                                                if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
 
21950
                                                        $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
 
21951
                                                        $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
 
21952
                                                } elseif (isset($opentagpos)) {
 
21953
                                                        $midpos = $opentagpos;
 
21954
                                                } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
 
21955
                                                        $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
 
21956
                                                        $midpos = $this->footerpos[$startlinepage];
 
21957
                                                } else {
 
21958
                                                        $midpos = 0;
 
21959
                                                }
 
21960
                                                if ($midpos > 0) {
 
21961
                                                        $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
 
21962
                                                        $pend = substr($this->getPageBuffer($startlinepage), $midpos);
 
21963
                                                } else {
 
21964
                                                        $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
 
21965
                                                        $pend = '';
 
21966
                                                }
 
21967
                                        }
 
21968
                                        if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
 
21969
                                                // calculate shifting amount
 
21970
                                                $tw = $w;
 
21971
                                                if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) {
 
21972
                                                        $tw += $this->cell_padding['R'];
 
21973
                                                }
 
21974
                                                if ($this->lMargin != $prevlMargin) {
 
21975
                                                        $tw += ($prevlMargin - $this->lMargin);
 
21976
                                                }
 
21977
                                                if ($this->rMargin != $prevrMargin) {
 
21978
                                                        $tw += ($prevrMargin - $this->rMargin);
 
21979
                                                }
 
21980
                                                $one_space_width = $this->GetStringWidth(chr(32));
 
21981
                                                $no = 0; // number of spaces on a line contained on a single block
 
21982
                                                if ($this->isRTLTextDir()) { // RTL
 
21983
                                                        // remove left space if exist
 
21984
                                                        $pos1 = $this->revstrpos($pmid, '[(');
 
21985
                                                        if ($pos1 > 0) {
 
21986
                                                                $pos1 = intval($pos1);
 
21987
                                                                if ($this->isUnicodeFont()) {
 
21988
                                                                        $pos2 = intval($this->revstrpos($pmid, '[('.chr(0).chr(32)));
 
21989
                                                                        $spacelen = 2;
 
21990
                                                                } else {
 
21991
                                                                        $pos2 = intval($this->revstrpos($pmid, '[('.chr(32)));
 
21992
                                                                        $spacelen = 1;
 
21993
                                                                }
 
21994
                                                                if ($pos1 == $pos2) {
 
21995
                                                                        $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
 
21996
                                                                        if (substr($pmid, $pos1, 4) == '[()]') {
 
21997
                                                                                $linew -= $one_space_width;
 
21998
                                                                        } elseif ($pos1 == strpos($pmid, '[(')) {
 
21999
                                                                                $no = 1;
 
22000
                                                                        }
 
22001
                                                                }
 
22002
                                                        }
 
22003
                                                } else { // LTR
 
22004
                                                        // remove right space if exist
 
22005
                                                        $pos1 = $this->revstrpos($pmid, ')]');
 
22006
                                                        if ($pos1 > 0) {
 
22007
                                                                $pos1 = intval($pos1);
 
22008
                                                                if ($this->isUnicodeFont()) {
 
22009
                                                                        $pos2 = intval($this->revstrpos($pmid, chr(0).chr(32).')]')) + 2;
 
22010
                                                                        $spacelen = 2;
 
22011
                                                                } else {
 
22012
                                                                        $pos2 = intval($this->revstrpos($pmid, chr(32).')]')) + 1;
 
22013
                                                                        $spacelen = 1;
 
22014
                                                                }
 
22015
                                                                if ($pos1 == $pos2) {
 
22016
                                                                        $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
 
22017
                                                                        $linew -= $one_space_width;
 
22018
                                                                }
 
22019
                                                        }
 
22020
                                                }
 
22021
                                                $mdiff = ($tw - $linew);
 
22022
                                                if ($plalign == 'C') {
 
22023
                                                        if ($this->rtl) {
 
22024
                                                                $t_x = -($mdiff / 2);
 
22025
                                                        } else {
 
22026
                                                                $t_x = ($mdiff / 2);
 
22027
                                                        }
 
22028
                                                } elseif ($plalign == 'R') {
 
22029
                                                        // right alignment on LTR document
 
22030
                                                        $t_x = $mdiff;
 
22031
                                                } elseif ($plalign == 'L') {
 
22032
                                                        // left alignment on RTL document
 
22033
                                                        $t_x = -$mdiff;
 
22034
                                                } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
 
22035
                                                        // Justification
 
22036
                                                        if ($this->isRTLTextDir()) {
 
22037
                                                                // align text on the left
 
22038
                                                                $t_x = -$mdiff;
 
22039
                                                        }
 
22040
                                                        $ns = 0; // number of spaces
 
22041
                                                        $pmidtemp = $pmid;
 
22042
                                                        // escape special characters
 
22043
                                                        $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
 
22044
                                                        $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
 
22045
                                                        // search spaces
 
22046
                                                        if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
 
22047
                                                                $spacestr = $this->getSpaceString();
 
22048
                                                                $maxkk = count($lnstring[1]) - 1;
 
22049
                                                                for ($kk=0; $kk <= $maxkk; ++$kk) {
 
22050
                                                                        // restore special characters
 
22051
                                                                        $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
 
22052
                                                                        $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
 
22053
                                                                        // store number of spaces on the strings
 
22054
                                                                        $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
 
22055
                                                                        // count total spaces on line
 
22056
                                                                        $ns += $lnstring[2][$kk];
 
22057
                                                                        $lnstring[3][$kk] = $ns;
 
22058
                                                                }
 
22059
                                                                if ($ns == 0) {
 
22060
                                                                        $ns = 1;
 
22061
                                                                }
 
22062
                                                                // calculate additional space to add to each existing space
 
22063
                                                                $spacewidth = ($mdiff / ($ns - $no)) * $this->k;
 
22064
                                                                $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize;
 
22065
                                                                if ($this->font_spacing != 0) {
 
22066
                                                                        // fixed spacing mode
 
22067
                                                                        $osw = -1000 * $this->font_spacing / $this->FontSize;
 
22068
                                                                        $spacewidthu += $osw;
 
22069
                                                                }
 
22070
                                                                $nsmax = $ns;
 
22071
                                                                $ns = 0;
 
22072
                                                                reset($lnstring);
 
22073
                                                                $offset = 0;
 
22074
                                                                $strcount = 0;
 
22075
                                                                $prev_epsposbeg = 0;
 
22076
                                                                $textpos = 0;
 
22077
                                                                if ($this->isRTLTextDir()) {
 
22078
                                                                        $textpos = $this->wPt;
 
22079
                                                                }
 
22080
                                                                global $spacew;
 
22081
                                                                while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
 
22082
                                                                        // check if we are inside a string section '[( ... )]'
 
22083
                                                                        $stroffset = strpos($pmid, '[(', $offset);
 
22084
                                                                        if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
 
22085
                                                                                // set offset to the end of string section
 
22086
                                                                                $offset = strpos($pmid, ')]', $stroffset);
 
22087
                                                                                while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) {
 
22088
                                                                                        $offset = strpos($pmid, ')]', ($offset + 1));
 
22089
                                                                                }
 
22090
                                                                                if ($offset === false) {
 
22091
                                                                                        $this->Error('HTML Justification: malformed PDF code.');
 
22092
                                                                                }
 
22093
                                                                                continue;
 
22094
                                                                        }
 
22095
                                                                        if ($this->isRTLTextDir()) {
 
22096
                                                                                $spacew = ($spacewidth * ($nsmax - $ns));
 
22097
                                                                        } else {
 
22098
                                                                                $spacew = ($spacewidth * $ns);
 
22099
                                                                        }
 
22100
                                                                        $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
 
22101
                                                                        $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
 
22102
                                                                        $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q');
 
22103
                                                                        if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
 
22104
                                                                                OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
 
22105
                                                                                // shift EPS images
 
22106
                                                                                $trx = sprintf('1 0 0 1 %.3F 0 cm', $spacew);
 
22107
                                                                                $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
 
22108
                                                                                $pmid_b = substr($pmid, 0, $epsposbeg);
 
22109
                                                                                $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
 
22110
                                                                                $pmid_e = substr($pmid, $epsposend);
 
22111
                                                                                $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
 
22112
                                                                                $offset = $epsposend;
 
22113
                                                                                continue;
 
22114
 
 
22115
                                                                        }
 
22116
                                                                        $prev_epsposbeg = $epsposbeg;
 
22117
                                                                        $currentxpos = 0;
 
22118
                                                                        // shift blocks of code
 
22119
                                                                        switch ($strpiece[2][0]) {
 
22120
                                                                                case 'Td':
 
22121
                                                                                case 'cm':
 
22122
                                                                                case 'm':
 
22123
                                                                                case 'l': {
 
22124
                                                                                        // get current X position
 
22125
                                                                                        preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
 
22126
                                                                                        $currentxpos = $xmatches[1];
 
22127
                                                                                        $textpos = $currentxpos;
 
22128
                                                                                        if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
 
22129
                                                                                                $ns = $lnstring[3][$strcount];
 
22130
                                                                                                if ($this->isRTLTextDir()) {
 
22131
                                                                                                        $spacew = ($spacewidth * ($nsmax - $ns));
 
22132
                                                                                                }
 
22133
                                                                                                ++$strcount;
 
22134
                                                                                        }
 
22135
                                                                                        // justify block
 
22136
                                                                                        $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
 
22137
                                                                                                create_function('$matches', 'global $spacew;
 
22138
                                                                                                $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
 
22139
                                                                                                return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1);
 
22140
                                                                                        break;
 
22141
                                                                                }
 
22142
                                                                                case 're': {
 
22143
                                                                                        // justify block
 
22144
                                                                                        if (!$this->empty_string($this->lispacer)) {
 
22145
                                                                                                $this->lispacer = '';
 
22146
                                                                                                continue;
 
22147
                                                                                        }
 
22148
                                                                                        preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
 
22149
                                                                                        $currentxpos = $xmatches[1];
 
22150
                                                                                        global $x_diff, $w_diff;
 
22151
                                                                                        $x_diff = 0;
 
22152
                                                                                        $w_diff = 0;
 
22153
                                                                                        if ($this->isRTLTextDir()) { // RTL
 
22154
                                                                                                if ($currentxpos < $textpos) {
 
22155
                                                                                                        $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
 
22156
                                                                                                        $w_diff = ($spacewidth * $lnstring[2][$strcount]);
 
22157
                                                                                                } else {
 
22158
                                                                                                        if ($strcount > 0) {
 
22159
                                                                                                                $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
 
22160
                                                                                                                $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
 
22161
                                                                                                        }
 
22162
                                                                                                }
 
22163
                                                                                        } else { // LTR
 
22164
                                                                                                if ($currentxpos > $textpos) {
 
22165
                                                                                                        if ($strcount > 0) {
 
22166
                                                                                                                $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
 
22167
                                                                                                        }
 
22168
                                                                                                        $w_diff = ($spacewidth * $lnstring[2][$strcount]);
 
22169
                                                                                                } else {
 
22170
                                                                                                        if ($strcount > 1) {
 
22171
                                                                                                                $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
 
22172
                                                                                                        }
 
22173
                                                                                                        if ($strcount > 0) {
 
22174
                                                                                                                $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
 
22175
                                                                                                        }
 
22176
                                                                                                }
 
22177
                                                                                        }
 
22178
                                                                                        $pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x',
 
22179
                                                                                                create_function('$matches', 'global $x_diff, $w_diff;
 
22180
                                                                                                $newx = sprintf("%.2F",(floatval($matches[1]) + $x_diff));
 
22181
                                                                                                $neww = sprintf("%.2F",(floatval($matches[3]) + $w_diff));
 
22182
                                                                                                return "".$newx." ".$matches[2]." ".$neww." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1);
 
22183
                                                                                        break;
 
22184
                                                                                }
 
22185
                                                                                case 'c': {
 
22186
                                                                                        // get current X position
 
22187
                                                                                        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);
 
22188
                                                                                        $currentxpos = $xmatches[1];
 
22189
                                                                                        // justify block
 
22190
                                                                                        $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',
 
22191
                                                                                                create_function('$matches', 'global $spacew;
 
22192
                                                                                                $newx1 = sprintf("%.3F",(floatval($matches[1]) + $spacew));
 
22193
                                                                                                $newx2 = sprintf("%.3F",(floatval($matches[3]) + $spacew));
 
22194
                                                                                                $newx3 = sprintf("%.3F",(floatval($matches[5]) + $spacew));
 
22195
                                                                                                return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1);
 
22196
                                                                                        break;
 
22197
                                                                                }
 
22198
                                                                        }
 
22199
                                                                        // shift the annotations and links
 
22200
                                                                        $cxpos = ($currentxpos / $this->k);
 
22201
                                                                        $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps);
 
22202
                                                                        if ($this->inxobj) {
 
22203
                                                                                // we are inside an XObject template
 
22204
                                                                                foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
 
22205
                                                                                        if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
 
22206
                                                                                                if ($cxpos > $lmpos) {
 
22207
                                                                                                        $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k);
 
22208
                                                                                                        $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
 
22209
                                                                                                } else {
 
22210
                                                                                                        $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
 
22211
                                                                                                }
 
22212
                                                                                                break;
 
22213
                                                                                        }
 
22214
                                                                                }
 
22215
                                                                        } elseif (isset($this->PageAnnots[$this->page])) {
 
22216
                                                                                foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
 
22217
                                                                                        if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
 
22218
                                                                                                if ($cxpos > $lmpos) {
 
22219
                                                                                                        $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
 
22220
                                                                                                        $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
 
22221
                                                                                                } else {
 
22222
                                                                                                        $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
 
22223
                                                                                                }
 
22224
                                                                                                break;
 
22225
                                                                                        }
 
22226
                                                                                }
 
22227
                                                                        }
 
22228
                                                                } // end of while
 
22229
                                                                // remove markers
 
22230
                                                                $pmid = str_replace('x*#!#*x', '', $pmid);
 
22231
                                                                if ($this->isUnicodeFont()) {
 
22232
                                                                        // multibyte characters
 
22233
                                                                        $spacew = $spacewidthu;
 
22234
                                                                        if ($this->font_stretching != 100) {
 
22235
                                                                                // word spacing is affected by stretching
 
22236
                                                                                $spacew /= ($this->font_stretching / 100);
 
22237
                                                                        }
 
22238
                                                                        $pmidtemp = $pmid;
 
22239
                                                                        // escape special characters
 
22240
                                                                        $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
 
22241
                                                                        $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
 
22242
                                                                        $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x",
 
22243
                                                                                                create_function('$matches', 'global $spacew;
 
22244
                                                                                                $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]);
 
22245
                                                                                                $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]);
 
22246
                                                                                                return "[(".str_replace(chr(0).chr(32), ") ".sprintf("%.3F", $spacew)." (", $matches[1]).")]";'), $pmidtemp);
 
22247
                                                                        if ($this->inxobj) {
 
22248
                                                                                // we are inside an XObject template
 
22249
                                                                                $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend;
 
22250
                                                                        } else {
 
22251
                                                                                $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
 
22252
                                                                        }
 
22253
                                                                        $endlinepos = strlen($pstart."\n".$pmid."\n");
 
22254
                                                                } else {
 
22255
                                                                        // non-unicode (single-byte characters)
 
22256
                                                                        if ($this->font_stretching != 100) {
 
22257
                                                                                // word spacing (Tw) is affected by stretching
 
22258
                                                                                $spacewidth /= ($this->font_stretching / 100);
 
22259
                                                                        }
 
22260
                                                                        $rs = sprintf('%.3F Tw', $spacewidth);
 
22261
                                                                        $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
 
22262
                                                                        if ($this->inxobj) {
 
22263
                                                                                // we are inside an XObject template
 
22264
                                                                                $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
 
22265
                                                                        } else {
 
22266
                                                                                $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
 
22267
                                                                        }
 
22268
                                                                        $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
 
22269
                                                                }
 
22270
                                                        }
 
22271
                                                } // end of J
 
22272
                                        } // end if $startlinex
 
22273
                                        if (($t_x != 0) OR ($yshift < 0)) {
 
22274
                                                // shift the line
 
22275
                                                $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
 
22276
                                                $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
 
22277
                                                $endlinepos = strlen($pstart);
 
22278
                                                if ($this->inxobj) {
 
22279
                                                        // we are inside an XObject template
 
22280
                                                        $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
 
22281
                                                        foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
 
22282
                                                                if ($pak >= $pask) {
 
22283
                                                                        $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
 
22284
                                                                        $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
 
22285
                                                                }
 
22286
                                                        }
 
22287
                                                } else {
 
22288
                                                        $this->setPageBuffer($startlinepage, $pstart.$pend);
 
22289
                                                        // shift the annotations and links
 
22290
                                                        if (isset($this->PageAnnots[$this->page])) {
 
22291
                                                                foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
 
22292
                                                                        if ($pak >= $pask) {
 
22293
                                                                                $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
 
22294
                                                                                $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
 
22295
                                                                        }
 
22296
                                                                }
 
22297
                                                        }
 
22298
                                                }
 
22299
                                                $this->y -= $yshift;
 
22300
                                        }
 
22301
                                }
 
22302
                                $pbrk = $this->checkPageBreak($this->lasth);
 
22303
                                $this->newline = false;
 
22304
                                $startlinex = $this->x;
 
22305
                                $startliney = $this->y;
 
22306
                                if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
 
22307
                                        $startliney -= ((0.3 * $this->FontSizePt) / $this->k);
 
22308
                                } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
 
22309
                                        $startliney -= (($this->FontSizePt / 0.7) / $this->k);
 
22310
                                } else {
 
22311
                                        $minstartliney = $startliney;
 
22312
                                        $maxbottomliney = ($this->y + (($fontsize * $this->cell_height_ratio) / $this->k));
 
22313
                                }
 
22314
                                $startlinepage = $this->page;
 
22315
                                if (isset($endlinepos) AND (!$pbrk)) {
 
22316
                                        $startlinepos = $endlinepos;
 
22317
                                } else {
 
22318
                                        if ($this->inxobj) {
 
22319
                                                // we are inside an XObject template
 
22320
                                                $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
 
22321
                                        } elseif (!$this->InFooter) {
 
22322
                                                if (isset($this->footerlen[$this->page])) {
 
22323
                                                        $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
 
22324
                                                } else {
 
22325
                                                        $this->footerpos[$this->page] = $this->pagelen[$this->page];
 
22326
                                                }
 
22327
                                                $startlinepos = $this->footerpos[$this->page];
 
22328
                                        } else {
 
22329
                                                $startlinepos = $this->pagelen[$this->page];
 
22330
                                        }
 
22331
                                }
 
22332
                                unset($endlinepos);
 
22333
                                $plalign = $lalign;
 
22334
                                if (isset($this->PageAnnots[$this->page])) {
 
22335
                                        $pask = count($this->PageAnnots[$this->page]);
 
22336
                                } else {
 
22337
                                        $pask = 0;
 
22338
                                }
 
22339
                                if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
 
22340
                                        AND (isset($this->emptypagemrk[$this->page]))
 
22341
                                        AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) {
 
22342
                                        $this->SetFont($fontname, $fontstyle, $fontsize);
 
22343
                                        if ($wfill) {
 
22344
                                                $this->SetFillColorArray($this->bgcolor);
 
22345
                                        }
 
22346
                                }
 
22347
                        } // end newline
 
22348
                        if (isset($opentagpos)) {
 
22349
                                unset($opentagpos);
 
22350
                        }
 
22351
                        if ($dom[$key]['tag']) {
 
22352
                                if ($dom[$key]['opening']) {
 
22353
                                        // get text indentation (if any)
 
22354
                                        if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
 
22355
                                                $this->textindent = $dom[$key]['text-indent'];
 
22356
                                                $this->newline = true;
 
22357
                                        }
 
22358
                                        // table
 
22359
                                        if ($dom[$key]['value'] == 'table') {
 
22360
                                                // available page width
 
22361
                                                if ($this->rtl) {
 
22362
                                                        $wtmp = $this->x - $this->lMargin;
 
22363
                                                } else {
 
22364
                                                        $wtmp = $this->w - $this->rMargin - $this->x;
 
22365
                                                }
 
22366
                                                // get cell spacing
 
22367
                                                if (isset($dom[$key]['attribute']['cellspacing'])) {
 
22368
                                                        $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
 
22369
                                                        $cellspacing = array('H' => $clsp, 'V' => $clsp);
 
22370
                                                } elseif (isset($dom[$key]['border-spacing'])) {
 
22371
                                                        $cellspacing = $dom[$key]['border-spacing'];
 
22372
                                                } else {
 
22373
                                                        $cellspacing = array('H' => 0, 'V' => 0);
 
22374
                                                }
 
22375
                                                // table width
 
22376
                                                if (isset($dom[$key]['width'])) {
 
22377
                                                        $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
 
22378
                                                } else {
 
22379
                                                        $table_width = $wtmp;
 
22380
                                                }
 
22381
                                                $table_width -= (2 * $cellspacing['H']);
 
22382
                                                if (!$this->inthead) {
 
22383
                                                        $this->y += $cellspacing['V'];
 
22384
                                                }
 
22385
                                                if ($this->rtl) {
 
22386
                                                        $cellspacingx = -$cellspacing['H'];
 
22387
                                                } else {
 
22388
                                                        $cellspacingx = $cellspacing['H'];
 
22389
                                                }
 
22390
                                                // total table width without cellspaces
 
22391
                                                $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
 
22392
                                                // minimum column width
 
22393
                                                $table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
 
22394
                                                // array of custom column widths
 
22395
                                                $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
 
22396
                                        }
 
22397
                                        // table row
 
22398
                                        if ($dom[$key]['value'] == 'tr') {
 
22399
                                                // reset column counter
 
22400
                                                $colid = 0;
 
22401
                                        }
 
22402
                                        // table cell
 
22403
                                        if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
 
22404
                                                $trid = $dom[$key]['parent'];
 
22405
                                                $table_el = $dom[$trid]['parent'];
 
22406
                                                if (!isset($dom[$table_el]['cols'])) {
 
22407
                                                        $dom[$table_el]['cols'] = $dom[$trid]['cols'];
 
22408
                                                }
 
22409
                                                // store border info
 
22410
                                                $tdborder = 0;
 
22411
                                                if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
 
22412
                                                        $tdborder = $dom[$key]['border'];
 
22413
                                                }
 
22414
                                                $colspan = $dom[$key]['attribute']['colspan'];
 
22415
                                                $old_cell_padding = $this->cell_padding;
 
22416
                                                if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
 
22417
                                                        $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
 
22418
                                                        $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
 
22419
                                                } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
 
22420
                                                        $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
 
22421
                                                } else {
 
22422
                                                        $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
 
22423
                                                }
 
22424
                                                $this->cell_padding = $current_cell_padding;
 
22425
                                                if (isset($dom[$key]['height'])) {
 
22426
                                                        // minimum cell height
 
22427
                                                        $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
 
22428
                                                } else {
 
22429
                                                        $cellh = 0;
 
22430
                                                }
 
22431
                                                if (isset($dom[$key]['content'])) {
 
22432
                                                        $cell_content = stripslashes($dom[$key]['content']);
 
22433
                                                } else {
 
22434
                                                        $cell_content = '&nbsp;';
 
22435
                                                }
 
22436
                                                $tagtype = $dom[$key]['value'];
 
22437
                                                $parentid = $key;
 
22438
                                                while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
 
22439
                                                        // move $key index forward
 
22440
                                                        ++$key;
 
22441
                                                }
 
22442
                                                if (!isset($dom[$trid]['startpage'])) {
 
22443
                                                        $dom[$trid]['startpage'] = $this->page;
 
22444
                                                } else {
 
22445
                                                        $this->setPage($dom[$trid]['startpage']);
 
22446
                                                }
 
22447
                                                if (!isset($dom[$trid]['startcolumn'])) {
 
22448
                                                        $dom[$trid]['startcolumn'] = $this->current_column;
 
22449
                                                } elseif ($this->current_column != $dom[$trid]['startcolumn']) {
 
22450
                                                        $tmpx = $this->x;
 
22451
                                                        $this->selectColumn($dom[$trid]['startcolumn']);
 
22452
                                                        $this->x = $tmpx;
 
22453
                                                }
 
22454
                                                if (!isset($dom[$trid]['starty'])) {
 
22455
                                                        $dom[$trid]['starty'] = $this->y;
 
22456
                                                } else {
 
22457
                                                        $this->y = $dom[$trid]['starty'];
 
22458
                                                }
 
22459
                                                if (!isset($dom[$trid]['startx'])) {
 
22460
                                                        $dom[$trid]['startx'] = $this->x;
 
22461
                                                        $this->x += $cellspacingx;
 
22462
                                                } else {
 
22463
                                                        $this->x += ($cellspacingx / 2);
 
22464
                                                }
 
22465
                                                if (isset($dom[$parentid]['attribute']['rowspan'])) {
 
22466
                                                        $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
 
22467
                                                } else {
 
22468
                                                        $rowspan = 1;
 
22469
                                                }
 
22470
                                                // skip row-spanned cells started on the previous rows
 
22471
                                                if (isset($dom[$table_el]['rowspans'])) {
 
22472
                                                        $rsk = 0;
 
22473
                                                        $rskmax = count($dom[$table_el]['rowspans']);
 
22474
                                                        while ($rsk < $rskmax) {
 
22475
                                                                $trwsp = $dom[$table_el]['rowspans'][$rsk];
 
22476
                                                                $rsstartx = $trwsp['startx'];
 
22477
                                                                $rsendx = $trwsp['endx'];
 
22478
                                                                // account for margin changes
 
22479
                                                                if ($trwsp['startpage'] < $this->page) {
 
22480
                                                                        if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
 
22481
                                                                                $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
 
22482
                                                                                $rsstartx -= $dl;
 
22483
                                                                                $rsendx -= $dl;
 
22484
                                                                        } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
 
22485
                                                                                $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
 
22486
                                                                                $rsstartx += $dl;
 
22487
                                                                                $rsendx += $dl;
 
22488
                                                                        }
 
22489
                                                                }
 
22490
                                                                if (($trwsp['rowspan'] > 0)
 
22491
                                                                        AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps))
 
22492
                                                                        AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps))
 
22493
                                                                        AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) {
 
22494
                                                                        // set the starting X position of the current cell
 
22495
                                                                        $this->x = $rsendx + $cellspacingx;
 
22496
                                                                        // increment column indicator
 
22497
                                                                        $colid += $trwsp['colspan'];
 
22498
                                                                        if (($trwsp['rowspan'] == 1)
 
22499
                                                                                AND (isset($dom[$trid]['endy']))
 
22500
                                                                                AND (isset($dom[$trid]['endpage']))
 
22501
                                                                                AND (isset($dom[$trid]['endcolumn']))
 
22502
                                                                                AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
 
22503
                                                                                AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
 
22504
                                                                                // set ending Y position for row
 
22505
                                                                                $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
 
22506
                                                                                $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
 
22507
                                                                        }
 
22508
                                                                        $rsk = 0;
 
22509
                                                                } else {
 
22510
                                                                        ++$rsk;
 
22511
                                                                }
 
22512
                                                        }
 
22513
                                                }
 
22514
                                                if (isset($dom[$parentid]['width'])) {
 
22515
                                                        // user specified width
 
22516
                                                        $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
 
22517
                                                        $tmpcw = ($cellw / $colspan);
 
22518
                                                        for ($i = 0; $i < $colspan; ++$i) {
 
22519
                                                                $table_colwidths[($colid + $i)] = $tmpcw;
 
22520
                                                        }
 
22521
                                                } else {
 
22522
                                                        // inherit column width
 
22523
                                                        $cellw = 0;
 
22524
                                                        for ($i = 0; $i < $colspan; ++$i) {
 
22525
                                                                $cellw += $table_colwidths[($colid + $i)];
 
22526
                                                        }
 
22527
                                                }
 
22528
                                                $cellw += (($colspan - 1) * $cellspacing['H']);
 
22529
                                                // increment column indicator
 
22530
                                                $colid += $colspan;
 
22531
                                                // add rowspan information to table element
 
22532
                                                if ($rowspan > 1) {
 
22533
                                                        $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));
 
22534
                                                }
 
22535
                                                $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
 
22536
                                                if ($rowspan > 1) {
 
22537
                                                        $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
 
22538
                                                }
 
22539
                                                // push background colors
 
22540
                                                if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
 
22541
                                                        $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
 
22542
                                                }
 
22543
                                                // store border info
 
22544
                                                if (isset($tdborder) AND !empty($tdborder)) {
 
22545
                                                        $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
 
22546
                                                }
 
22547
                                                $prevLastH = $this->lasth;
 
22548
                                                // store some info for multicolumn mode
 
22549
                                                if ($this->rtl) {
 
22550
                                                        $this->colxshift['x'] = $this->w - $this->x - $this->rMargin;
 
22551
                                                } else {
 
22552
                                                        $this->colxshift['x'] = $this->x - $this->lMargin;
 
22553
                                                }
 
22554
                                                $this->colxshift['s'] = $cellspacing;
 
22555
                                                $this->colxshift['p'] = $current_cell_padding;
 
22556
                                                // ****** write the cell content ******
 
22557
                                                $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
 
22558
                                                // restore some values
 
22559
                                                $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
 
22560
                                                $this->lasth = $prevLastH;
 
22561
                                                $this->cell_padding = $old_cell_padding;
 
22562
                                                $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
 
22563
                                                // update the end of row position
 
22564
                                                if ($rowspan <= 1) {
 
22565
                                                        if (isset($dom[$trid]['endy'])) {
 
22566
                                                                if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) {
 
22567
                                                                        $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
 
22568
                                                                } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) {
 
22569
                                                                        $dom[$trid]['endy'] = $this->y;
 
22570
                                                                }
 
22571
                                                        } else {
 
22572
                                                                $dom[$trid]['endy'] = $this->y;
 
22573
                                                        }
 
22574
                                                        if (isset($dom[$trid]['endpage'])) {
 
22575
                                                                $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
 
22576
                                                        } else {
 
22577
                                                                $dom[$trid]['endpage'] = $this->page;
 
22578
                                                        }
 
22579
                                                        if (isset($dom[$trid]['endcolumn'])) {
 
22580
                                                                $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']);
 
22581
                                                        } else {
 
22582
                                                                $dom[$trid]['endcolumn'] = $this->current_column;
 
22583
                                                        }
 
22584
                                                } else {
 
22585
                                                        // account for row-spanned cells
 
22586
                                                        $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
 
22587
                                                        $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
 
22588
                                                        $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
 
22589
                                                        $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column;
 
22590
                                                }
 
22591
                                                if (isset($dom[$table_el]['rowspans'])) {
 
22592
                                                        // update endy and endpage on rowspanned cells
 
22593
                                                        foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
 
22594
                                                                if ($trwsp['rowspan'] > 0) {
 
22595
                                                                        if (isset($dom[$trid]['endpage'])) {
 
22596
                                                                                if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
 
22597
                                                                                        $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
 
22598
                                                                                } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
 
22599
                                                                                        $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
 
22600
                                                                                        $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
 
22601
                                                                                        $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
 
22602
                                                                                } else {
 
22603
                                                                                        $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
 
22604
                                                                                }
 
22605
                                                                        }
 
22606
                                                                }
 
22607
                                                        }
 
22608
                                                }
 
22609
                                                $this->x += ($cellspacingx / 2);
 
22610
                                        } else {
 
22611
                                                // opening tag (or self-closing tag)
 
22612
                                                if (!isset($opentagpos)) {
 
22613
                                                        if ($this->inxobj) {
 
22614
                                                                // we are inside an XObject template
 
22615
                                                                $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']);
 
22616
                                                        } elseif (!$this->InFooter) {
 
22617
                                                                if (isset($this->footerlen[$this->page])) {
 
22618
                                                                        $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
 
22619
                                                                } else {
 
22620
                                                                        $this->footerpos[$this->page] = $this->pagelen[$this->page];
 
22621
                                                                }
 
22622
                                                                $opentagpos = $this->footerpos[$this->page];
 
22623
                                                        }
 
22624
                                                }
 
22625
                                                $dom = $this->openHTMLTagHandler($dom, $key, $cell);
 
22626
                                        }
 
22627
                                } else { // closing tag
 
22628
                                        $prev_numpages = $this->numpages;
 
22629
                                        $old_bordermrk = $this->bordermrk[$this->page];
 
22630
                                        $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
 
22631
                                        if ($this->bordermrk[$this->page] > $old_bordermrk) {
 
22632
                                                $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk);
 
22633
                                        }
 
22634
                                        if ($prev_numpages > $this->numpages) {
 
22635
                                                $startlinepage = $this->page;
 
22636
                                        }
 
22637
                                }
 
22638
                        } elseif (strlen($dom[$key]['value']) > 0) {
 
22639
                                // print list-item
 
22640
                                if (!$this->empty_string($this->lispacer) AND ($this->lispacer != '^')) {
 
22641
                                        $this->SetFont($pfontname, $pfontstyle, $pfontsize);
 
22642
                                        $this->resetLastH();
 
22643
                                        $minstartliney = $this->y;
 
22644
                                        $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio));
 
22645
                                        $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
 
22646
                                        $this->SetFont($curfontname, $curfontstyle, $curfontsize);
 
22647
                                        $this->resetLastH();
 
22648
                                        if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
 
22649
                                                $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
 
22650
                                                $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
 
22651
                                                $this->y += ((($pfontsize - $curfontsize) * $this->cell_height_ratio / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
 
22652
                                                $minstartliney = min($this->y, $minstartliney);
 
22653
                                                $maxbottomliney = max(($this->y + (($pfontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney);
 
22654
                                        }
 
22655
                                }
 
22656
                                // text
 
22657
                                $this->htmlvspace = 0;
 
22658
                                if ((!$this->premode) AND $this->isRTLTextDir()) {
 
22659
                                        // reverse spaces order
 
22660
                                        $lsp = ''; // left spaces
 
22661
                                        $rsp = ''; // right spaces
 
22662
                                        if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
 
22663
                                                $lsp = $matches[1];
 
22664
                                        }
 
22665
                                        if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
 
22666
                                                $rsp = $matches[1];
 
22667
                                        }
 
22668
                                        $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
 
22669
                                }
 
22670
                                if ($newline) {
 
22671
                                        if (!$this->premode) {
 
22672
                                                $prelen = strlen($dom[$key]['value']);
 
22673
                                                if ($this->isRTLTextDir()) {
 
22674
                                                        // right trim except non-breaking space
 
22675
                                                        $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
 
22676
                                                } else {
 
22677
                                                        // left trim except non-breaking space
 
22678
                                                        $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
 
22679
                                                }
 
22680
                                                $postlen = strlen($dom[$key]['value']);
 
22681
                                                if (($postlen == 0) AND ($prelen > 0)) {
 
22682
                                                        $dom[$key]['trimmed_space'] = true;
 
22683
                                                }
 
22684
                                        }
 
22685
                                        $newline = false;
 
22686
                                        $firstblock = true;
 
22687
                                } else {
 
22688
                                        $firstblock = false;
 
22689
                                        // replace empty multiple spaces string with a single space
 
22690
                                        $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']);
 
22691
                                }
 
22692
                                $strrest = '';
 
22693
                                if ($this->rtl) {
 
22694
                                        $this->x -= $this->textindent;
 
22695
                                } else {
 
22696
                                        $this->x += $this->textindent;
 
22697
                                }
 
22698
                                if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
 
22699
                                        $strlinelen = $this->GetStringWidth($dom[$key]['value']);
 
22700
                                        if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
 
22701
                                                // HTML <a> Link
 
22702
                                                $hrefcolor = '';
 
22703
                                                if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
 
22704
                                                        $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
 
22705
                                                }
 
22706
                                                $hrefstyle = -1;
 
22707
                                                if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
 
22708
                                                        $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
 
22709
                                                }
 
22710
                                                $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
 
22711
                                        } else {
 
22712
                                                $wadj = 0; // space to leave for block continuity
 
22713
                                                if ($this->rtl) {
 
22714
                                                        $cwa = $this->x - $this->lMargin;
 
22715
                                                } else {
 
22716
                                                        $cwa = $this->w - $this->rMargin - $this->x;
 
22717
                                                }
 
22718
                                                if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) {
 
22719
                                                        // check the next text blocks for continuity
 
22720
                                                        $nkey = ($key + 1);
 
22721
                                                        $write_block = true;
 
22722
                                                        $same_textdir = true;
 
22723
                                                        $tmp_fontname = $this->FontFamily;
 
22724
                                                        $tmp_fontstyle = $this->FontStyle;
 
22725
                                                        $tmp_fontsize = $this->FontSizePt;
 
22726
                                                        while ($write_block AND isset($dom[$nkey])) {
 
22727
                                                                if ($dom[$nkey]['tag']) {
 
22728
                                                                        if ($dom[$nkey]['block']) {
 
22729
                                                                                // end of block
 
22730
                                                                                $write_block = false;
 
22731
                                                                        }
 
22732
                                                                        $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily;
 
22733
                                                                        $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle;
 
22734
                                                                        $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt;
 
22735
                                                                        $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
 
22736
                                                                } else {
 
22737
                                                                        $nextstr = preg_split('/'.$this->re_space['p'].'+/'.$this->re_space['m'], $dom[$nkey]['value']);
 
22738
                                                                        if (isset($nextstr[0]) AND $same_textdir) {
 
22739
                                                                                $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
 
22740
                                                                        }
 
22741
                                                                        if (isset($nextstr[1])) {
 
22742
                                                                                $write_block = false;
 
22743
                                                                        }
 
22744
                                                                }
 
22745
                                                                ++$nkey;
 
22746
                                                        }
 
22747
                                                }
 
22748
                                                if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) {
 
22749
                                                        $wadj = 0;
 
22750
                                                        $nextstr = preg_split('/'.$this->re_space['p'].'/'.$this->re_space['m'], $dom[$key]['value']);
 
22751
                                                        $numblks = count($nextstr);
 
22752
                                                        if ($numblks > 1) {
 
22753
                                                                // try to split on blank spaces
 
22754
                                                                $wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)]));
 
22755
                                                        }
 
22756
                                                }
 
22757
                                                // check for reversed text direction
 
22758
                                                if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) {
 
22759
                                                        // LTR text on RTL direction or RTL text on LTR direction
 
22760
                                                        $reverse_dir = true;
 
22761
                                                        $this->rtl = !$this->rtl;
 
22762
                                                        $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems
 
22763
                                                        if ($this->rtl) {
 
22764
                                                                $this->x += $revshift;
 
22765
                                                        } else {
 
22766
                                                                $this->x -= $revshift;
 
22767
                                                        }
 
22768
                                                        $xws = $this->x;
 
22769
                                                }
 
22770
                                                // ****** write only until the end of the line and get the rest ******
 
22771
                                                $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
 
22772
                                                // restore default direction
 
22773
                                                if ($reverse_dir AND ($wadj == 0)) {
 
22774
                                                        $this->x = $xws;
 
22775
                                                        $this->rtl = !$this->rtl;
 
22776
                                                        $reverse_dir = false;
 
22777
                                                }
 
22778
                                        }
 
22779
                                }
 
22780
                                $this->textindent = 0;
 
22781
                                if (strlen($strrest) > 0) {
 
22782
                                        // store the remaining string on the previous $key position
 
22783
                                        $this->newline = true;
 
22784
                                        if ($strrest == $dom[$key]['value']) {
 
22785
                                                // used to avoid infinite loop
 
22786
                                                ++$loop;
 
22787
                                        } else {
 
22788
                                                $loop = 0;
 
22789
                                        }
 
22790
                                        $dom[$key]['value'] = $strrest;
 
22791
                                        if ($cell) {
 
22792
                                                if ($this->rtl) {
 
22793
                                                        $this->x -= $this->cell_padding['R'];
 
22794
                                                } else {
 
22795
                                                        $this->x += $this->cell_padding['L'];
 
22796
                                                }
 
22797
                                        }
 
22798
                                        if ($loop < 3) {
 
22799
                                                --$key;
 
22800
                                        }
 
22801
                                } else {
 
22802
                                        $loop = 0;
 
22803
                                }
 
22804
                        }
 
22805
                        ++$key;
 
22806
                        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')) {
 
22807
                                // check if we are on a new page or on a new column
 
22808
                                if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) {
 
22809
                                        // we are on a new page or on a new column and the total object height is less than the available vertical space.
 
22810
                                        // restore previous object
 
22811
                                        $this->rollbackTransaction(true);
 
22812
                                        // restore previous values
 
22813
                                        foreach ($this_method_vars as $vkey => $vval) {
 
22814
                                                $$vkey = $vval;
 
22815
                                        }
 
22816
                                        // add a page (or trig AcceptPageBreak() for multicolumn mode)
 
22817
                                        $pre_y = $this->y;
 
22818
                                        if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
 
22819
                                                $startliney = $this->y;
 
22820
                                        }
 
22821
                                        $undo = true; // avoid infinite loop
 
22822
                                } else {
 
22823
                                        $undo = false;
 
22824
                                }
 
22825
                        }
 
22826
                } // end for each $key
 
22827
                // align the last line
 
22828
                if (isset($startlinex)) {
 
22829
                        $yshift = ($minstartliney - $startliney);
 
22830
                        if (($yshift > 0) OR ($this->page > $startlinepage)) {
 
22831
                                $yshift = 0;
 
22832
                        }
 
22833
                        $t_x = 0;
 
22834
                        // the last line must be shifted to be aligned as requested
 
22835
                        $linew = abs($this->endlinex - $startlinex);
 
22836
                        if ($this->inxobj) {
 
22837
                                // we are inside an XObject template
 
22838
                                $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
 
22839
                                if (isset($opentagpos)) {
 
22840
                                        $midpos = $opentagpos;
 
22841
                                } else {
 
22842
                                        $midpos = 0;
 
22843
                                }
 
22844
                                if ($midpos > 0) {
 
22845
                                        $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
 
22846
                                        $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
 
22847
                                } else {
 
22848
                                        $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
 
22849
                                        $pend = '';
 
22850
                                }
 
22851
                        } else {
 
22852
                                $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
 
22853
                                if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
 
22854
                                        $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
 
22855
                                        $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
 
22856
                                } elseif (isset($opentagpos)) {
 
22857
                                        $midpos = $opentagpos;
 
22858
                                } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
 
22859
                                        $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
 
22860
                                        $midpos = $this->footerpos[$startlinepage];
 
22861
                                } else {
 
22862
                                        $midpos = 0;
 
22863
                                }
 
22864
                                if ($midpos > 0) {
 
22865
                                        $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
 
22866
                                        $pend = substr($this->getPageBuffer($startlinepage), $midpos);
 
22867
                                } else {
 
22868
                                        $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
 
22869
                                        $pend = '';
 
22870
                                }
 
22871
                        }
 
22872
                        if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
 
22873
                                // calculate shifting amount
 
22874
                                $tw = $w;
 
22875
                                if ($this->lMargin != $prevlMargin) {
 
22876
                                        $tw += ($prevlMargin - $this->lMargin);
 
22877
                                }
 
22878
                                if ($this->rMargin != $prevrMargin) {
 
22879
                                        $tw += ($prevrMargin - $this->rMargin);
 
22880
                                }
 
22881
                                $one_space_width = $this->GetStringWidth(chr(32));
 
22882
                                $no = 0; // number of spaces on a line contained on a single block
 
22883
                                if ($this->isRTLTextDir()) { // RTL
 
22884
                                        // remove left space if exist
 
22885
                                        $pos1 = $this->revstrpos($pmid, '[(');
 
22886
                                        if ($pos1 > 0) {
 
22887
                                                $pos1 = intval($pos1);
 
22888
                                                if ($this->isUnicodeFont()) {
 
22889
                                                        $pos2 = intval($this->revstrpos($pmid, '[('.chr(0).chr(32)));
 
22890
                                                        $spacelen = 2;
 
22891
                                                } else {
 
22892
                                                        $pos2 = intval($this->revstrpos($pmid, '[('.chr(32)));
 
22893
                                                        $spacelen = 1;
 
22894
                                                }
 
22895
                                                if ($pos1 == $pos2) {
 
22896
                                                        $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
 
22897
                                                        if (substr($pmid, $pos1, 4) == '[()]') {
 
22898
                                                                $linew -= $one_space_width;
 
22899
                                                        } elseif ($pos1 == strpos($pmid, '[(')) {
 
22900
                                                                $no = 1;
 
22901
                                                        }
 
22902
                                                }
 
22903
                                        }
 
22904
                                } else { // LTR
 
22905
                                        // remove right space if exist
 
22906
                                        $pos1 = $this->revstrpos($pmid, ')]');
 
22907
                                        if ($pos1 > 0) {
 
22908
                                                $pos1 = intval($pos1);
 
22909
                                                if ($this->isUnicodeFont()) {
 
22910
                                                        $pos2 = intval($this->revstrpos($pmid, chr(0).chr(32).')]')) + 2;
 
22911
                                                        $spacelen = 2;
 
22912
                                                } else {
 
22913
                                                        $pos2 = intval($this->revstrpos($pmid, chr(32).')]')) + 1;
 
22914
                                                        $spacelen = 1;
 
22915
                                                }
 
22916
                                                if ($pos1 == $pos2) {
 
22917
                                                        $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
 
22918
                                                        $linew -= $one_space_width;
 
22919
                                                }
 
22920
                                        }
 
22921
                                }
 
22922
                                $mdiff = ($tw - $linew);
 
22923
                                if ($plalign == 'C') {
 
22924
                                        if ($this->rtl) {
 
22925
                                                $t_x = -($mdiff / 2);
 
22926
                                        } else {
 
22927
                                                $t_x = ($mdiff / 2);
 
22928
                                        }
 
22929
                                } elseif ($plalign == 'R') {
 
22930
                                        // right alignment on LTR document
 
22931
                                        $t_x = $mdiff;
 
22932
                                } elseif ($plalign == 'L') {
 
22933
                                        // left alignment on RTL document
 
22934
                                        $t_x = -$mdiff;
 
22935
                                }
 
22936
                        } // end if startlinex
 
22937
                        if (($t_x != 0) OR ($yshift < 0)) {
 
22938
                                // shift the line
 
22939
                                $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
 
22940
                                $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
 
22941
                                $endlinepos = strlen($pstart);
 
22942
                                if ($this->inxobj) {
 
22943
                                        // we are inside an XObject template
 
22944
                                        $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
 
22945
                                        foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
 
22946
                                                if ($pak >= $pask) {
 
22947
                                                        $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
 
22948
                                                        $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
 
22949
                                                }
 
22950
                                        }
 
22951
                                } else {
 
22952
                                        $this->setPageBuffer($startlinepage, $pstart.$pend);
 
22953
                                        // shift the annotations and links
 
22954
                                        if (isset($this->PageAnnots[$this->page])) {
 
22955
                                                foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
 
22956
                                                        if ($pak >= $pask) {
 
22957
                                                                $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
 
22958
                                                                $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
 
22959
                                                        }
 
22960
                                                }
 
22961
                                        }
 
22962
                                }
 
22963
                                $this->y -= $yshift;
 
22964
                                $yshift = 0;
 
22965
                        }
 
22966
                }
 
22967
                // restore previous values
 
22968
                $this->setGraphicVars($gvars);
 
22969
                if ($this->num_columns > 1) {
 
22970
                        $this->selectColumn();
 
22971
                } elseif ($this->page > $prevPage) {
 
22972
                        $this->lMargin = $this->pagedim[$this->page]['olm'];
 
22973
                        $this->rMargin = $this->pagedim[$this->page]['orm'];
 
22974
                }
 
22975
                // restore previous list state
 
22976
                $this->cell_height_ratio = $prev_cell_height_ratio;
 
22977
                $this->listnum = $prev_listnum;
 
22978
                $this->listordered = $prev_listordered;
 
22979
                $this->listcount = $prev_listcount;
 
22980
                $this->lispacer = $prev_lispacer;
 
22981
                if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
 
22982
                        $this->Ln($this->lasth);
 
22983
                        if ($this->y < $maxbottomliney) {
 
22984
                                $this->y = $maxbottomliney;
 
22985
                        }
 
22986
                }
 
22987
                unset($dom);
 
22988
        }
 
22989
 
 
22990
        /**
 
22991
         * Process opening tags.
 
22992
         * @param $dom (array) html dom array
 
22993
         * @param $key (int) current element id
 
22994
         * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
 
22995
         * @return $dom array
 
22996
         * @protected
 
22997
         */
 
22998
        protected function openHTMLTagHandler($dom, $key, $cell) {
 
22999
                $tag = $dom[$key];
 
23000
                $parent = $dom[($dom[$key]['parent'])];
 
23001
                $firsttag = ($key == 1);
 
23002
                // check for text direction attribute
 
23003
                if (isset($tag['dir'])) {
 
23004
                        $this->setTempRTL($tag['dir']);
 
23005
                } else {
 
23006
                        $this->tmprtl = false;
 
23007
                }
 
23008
                if ($tag['block']) {
 
23009
                        $hbz = 0; // distance from y to line bottom
 
23010
                        $hb = 0; // vertical space between block tags
 
23011
                        // calculate vertical space for block tags
 
23012
                        if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) {
 
23013
                                $cur_h = $this->tagvspaces[$tag['value']][0]['h'];
 
23014
                        } elseif (isset($tag['fontsize'])) {
 
23015
                                $cur_h = ($tag['fontsize'] / $this->k) * $this->cell_height_ratio;
 
23016
                        } else {
 
23017
                                $cur_h = $this->FontSize * $this->cell_height_ratio;
 
23018
                        }
 
23019
                        if (isset($this->tagvspaces[$tag['value']][0]['n'])) {
 
23020
                                $n = $this->tagvspaces[$tag['value']][0]['n'];
 
23021
                        } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
 
23022
                                $n = 0.6;
 
23023
                        } else {
 
23024
                                $n = 1;
 
23025
                        }
 
23026
                        if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br')))) {
 
23027
                                $hb = 0;
 
23028
                        } else {
 
23029
                                $hb = ($n * $cur_h);
 
23030
                        }
 
23031
                        if (($this->htmlvspace <= 0) AND ($n > 0)) {
 
23032
                                if (isset($parent['fontsize'])) {
 
23033
                                        $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
 
23034
                                } else {
 
23035
                                        $hbz = $this->FontSize * $this->cell_height_ratio;
 
23036
                                }
 
23037
                        }
 
23038
                }
 
23039
                // Opening tag
 
23040
                switch($tag['value']) {
 
23041
                        case 'table': {
 
23042
                                $cp = 0;
 
23043
                                $cs = 0;
 
23044
                                $dom[$key]['rowspans'] = array();
 
23045
                                if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
 
23046
                                        // set table header
 
23047
                                        if (!$this->empty_string($dom[$key]['thead'])) {
 
23048
                                                // set table header
 
23049
                                                $this->thead = $dom[$key]['thead'];
 
23050
                                                if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
 
23051
                                                        $this->theadMargins = array();
 
23052
                                                        $this->theadMargins['cell_padding'] = $this->cell_padding;
 
23053
                                                        $this->theadMargins['lmargin'] = $this->lMargin;
 
23054
                                                        $this->theadMargins['rmargin'] = $this->rMargin;
 
23055
                                                        $this->theadMargins['page'] = $this->page;
 
23056
                                                        $this->theadMargins['cell'] = $cell;
 
23057
                                                }
 
23058
                                        }
 
23059
                                }
 
23060
                                // store current margins and page
 
23061
                                $dom[$key]['old_cell_padding'] = $this->cell_padding;
 
23062
                                if (isset($tag['attribute']['cellpadding'])) {
 
23063
                                        $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
 
23064
                                        $this->SetCellPadding($pad);
 
23065
                                } elseif (isset($tag['padding'])) {
 
23066
                                        $this->cell_padding = $tag['padding'];
 
23067
                                }
 
23068
                                if (isset($tag['attribute']['cellspacing'])) {
 
23069
                                        $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
 
23070
                                } elseif (isset($tag['border-spacing'])) {
 
23071
                                        $cs = $tag['border-spacing']['V'];
 
23072
                                }
 
23073
                                $prev_y = $this->y;
 
23074
                                if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) {
 
23075
                                        $this->inthead = true;
 
23076
                                        // add a page (or trig AcceptPageBreak() for multicolumn mode)
 
23077
                                        $this->checkPageBreak($this->PageBreakTrigger + 1);
 
23078
                                }
 
23079
                                break;
 
23080
                        }
 
23081
                        case 'tr': {
 
23082
                                // array of columns positions
 
23083
                                $dom[$key]['cellpos'] = array();
 
23084
                                break;
 
23085
                        }
 
23086
                        case 'hr': {
 
23087
                                if ((isset($tag['height'])) AND ($tag['height'] != '')) {
 
23088
                                        $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
 
23089
                                } else {
 
23090
                                        $hrHeight = $this->GetLineWidth();
 
23091
                                }
 
23092
                                $this->addHTMLVertSpace($hbz, ($hrHeight / 2), $cell, $firsttag);
 
23093
                                $x = $this->GetX();
 
23094
                                $y = $this->GetY();
 
23095
                                $wtmp = $this->w - $this->lMargin - $this->rMargin;
 
23096
                                if ($cell) {
 
23097
                                        $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']);
 
23098
                                }
 
23099
                                if ((isset($tag['width'])) AND ($tag['width'] != '')) {
 
23100
                                        $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
 
23101
                                } else {
 
23102
                                        $hrWidth = $wtmp;
 
23103
                                }
 
23104
                                $prevlinewidth = $this->GetLineWidth();
 
23105
                                $this->SetLineWidth($hrHeight);
 
23106
                                $this->Line($x, $y, $x + $hrWidth, $y);
 
23107
                                $this->SetLineWidth($prevlinewidth);
 
23108
                                $this->addHTMLVertSpace(($hrHeight / 2), 0, $cell, !isset($dom[($key + 1)]));
 
23109
                                break;
 
23110
                        }
 
23111
                        case 'a': {
 
23112
                                if (array_key_exists('href', $tag['attribute'])) {
 
23113
                                        $this->HREF['url'] = $tag['attribute']['href'];
 
23114
                                }
 
23115
                                break;
 
23116
                        }
 
23117
                        case 'img': {
 
23118
                                if (isset($tag['attribute']['src'])) {
 
23119
                                        if ($tag['attribute']['src']{0} === '@') {
 
23120
                                                // data stream
 
23121
                                                $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1));
 
23122
                                                $type = '';
 
23123
                                        } else {
 
23124
                                                // check for images without protocol
 
23125
                                                if (preg_match('%^/{2}%', $tag['attribute']['src'])) {
 
23126
                                                        $tag['attribute']['src'] = 'http:'.$tag['attribute']['src'];
 
23127
                                                }
 
23128
                                                // replace relative path with real server path
 
23129
                                                if (($tag['attribute']['src'][0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
 
23130
                                                        $findroot = strpos($tag['attribute']['src'], $_SERVER['DOCUMENT_ROOT']);
 
23131
                                                        if (($findroot === false) OR ($findroot > 1)) {
 
23132
                                                                if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
 
23133
                                                                        $tag['attribute']['src'] = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$tag['attribute']['src'];
 
23134
                                                                } else {
 
23135
                                                                        $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
 
23136
                                                                }
 
23137
                                                        }
 
23138
                                                }
 
23139
                                                $tag['attribute']['src'] = htmlspecialchars_decode(urldecode($tag['attribute']['src']));
 
23140
                                                $type = $this->getImageFileType($tag['attribute']['src']);
 
23141
                                                $testscrtype = @parse_url($tag['attribute']['src']);
 
23142
                                                if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
 
23143
                                                        // convert URL to server path
 
23144
                                                        $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
 
23145
                                                }
 
23146
                                        }
 
23147
                                        if (!isset($tag['width'])) {
 
23148
                                                $tag['width'] = 0;
 
23149
                                        }
 
23150
                                        if (!isset($tag['height'])) {
 
23151
                                                $tag['height'] = 0;
 
23152
                                        }
 
23153
                                        //if (!isset($tag['attribute']['align'])) {
 
23154
                                                // the only alignment supported is "bottom"
 
23155
                                                // further development is required for other modes.
 
23156
                                                $tag['attribute']['align'] = 'bottom';
 
23157
                                        //}
 
23158
                                        switch($tag['attribute']['align']) {
 
23159
                                                case 'top': {
 
23160
                                                        $align = 'T';
 
23161
                                                        break;
 
23162
                                                }
 
23163
                                                case 'middle': {
 
23164
                                                        $align = 'M';
 
23165
                                                        break;
 
23166
                                                }
 
23167
                                                case 'bottom': {
 
23168
                                                        $align = 'B';
 
23169
                                                        break;
 
23170
                                                }
 
23171
                                                default: {
 
23172
                                                        $align = 'B';
 
23173
                                                        break;
 
23174
                                                }
 
23175
                                        }
 
23176
                                        $prevy = $this->y;
 
23177
                                        $xpos = $this->x;
 
23178
                                        $imglink = '';
 
23179
                                        if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) {
 
23180
                                                $imglink = $this->HREF['url'];
 
23181
                                                if ($imglink{0} == '#') {
 
23182
                                                        // convert url to internal link
 
23183
                                                        $lnkdata = explode(',', $imglink);
 
23184
                                                        if (isset($lnkdata[0])) {
 
23185
                                                                $page = intval(substr($lnkdata[0], 1));
 
23186
                                                                if (empty($page) OR ($page <= 0)) {
 
23187
                                                                        $page = $this->page;
 
23188
                                                                }
 
23189
                                                                if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
 
23190
                                                                        $lnky = floatval($lnkdata[1]);
 
23191
                                                                } else {
 
23192
                                                                        $lnky = 0;
 
23193
                                                                }
 
23194
                                                                $imglink = $this->AddLink();
 
23195
                                                                $this->SetLink($imglink, $lnky, $page);
 
23196
                                                        }
 
23197
                                                }
 
23198
                                        }
 
23199
                                        $border = 0;
 
23200
                                        if (isset($tag['border']) AND !empty($tag['border'])) {
 
23201
                                                // currently only support 1 (frame) or a combination of 'LTRB'
 
23202
                                                $border = $tag['border'];
 
23203
                                        }
 
23204
                                        $iw = '';
 
23205
                                        if (isset($tag['width'])) {
 
23206
                                                $iw = $this->getHTMLUnitToUnits($tag['width'], 1, 'px', false);
 
23207
                                        }
 
23208
                                        $ih = '';
 
23209
                                        if (isset($tag['height'])) {
 
23210
                                                $ih = $this->getHTMLUnitToUnits($tag['height'], 1, 'px', false);
 
23211
                                        }
 
23212
                                        if (($type == 'eps') OR ($type == 'ai')) {
 
23213
                                                $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true);
 
23214
                                        } elseif ($type == 'svg') {
 
23215
                                                $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true);
 
23216
                                        } else {
 
23217
                                                $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
 
23218
                                        }
 
23219
                                        switch($align) {
 
23220
                                                case 'T': {
 
23221
                                                        $this->y = $prevy;
 
23222
                                                        break;
 
23223
                                                }
 
23224
                                                case 'M': {
 
23225
                                                        $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
 
23226
                                                        break;
 
23227
                                                }
 
23228
                                                case 'B': {
 
23229
                                                        $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
 
23230
                                                        break;
 
23231
                                                }
 
23232
                                        }
 
23233
                                }
 
23234
                                break;
 
23235
                        }
 
23236
                        case 'dl': {
 
23237
                                ++$this->listnum;
 
23238
                                if ($this->listnum == 1) {
 
23239
                                        $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
 
23240
                                } else {
 
23241
                                        $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
 
23242
                                }
 
23243
                                break;
 
23244
                        }
 
23245
                        case 'dt': {
 
23246
                                $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
 
23247
                                break;
 
23248
                        }
 
23249
                        case 'dd': {
 
23250
                                if ($this->rtl) {
 
23251
                                        $this->rMargin += $this->listindent;
 
23252
                                } else {
 
23253
                                        $this->lMargin += $this->listindent;
 
23254
                                }
 
23255
                                ++$this->listindentlevel;
 
23256
                                $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
 
23257
                                break;
 
23258
                        }
 
23259
                        case 'ul':
 
23260
                        case 'ol': {
 
23261
                                ++$this->listnum;
 
23262
                                if ($tag['value'] == 'ol') {
 
23263
                                        $this->listordered[$this->listnum] = true;
 
23264
                                } else {
 
23265
                                        $this->listordered[$this->listnum] = false;
 
23266
                                }
 
23267
                                if (isset($tag['attribute']['start'])) {
 
23268
                                        $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
 
23269
                                } else {
 
23270
                                        $this->listcount[$this->listnum] = 0;
 
23271
                                }
 
23272
                                if ($this->rtl) {
 
23273
                                        $this->rMargin += $this->listindent;
 
23274
                                        $this->x -= $this->listindent;
 
23275
                                } else {
 
23276
                                        $this->lMargin += $this->listindent;
 
23277
                                        $this->x += $this->listindent;
 
23278
                                }
 
23279
                                ++$this->listindentlevel;
 
23280
                                if ($this->listnum == 1) {
 
23281
                                        if ($key > 1) {
 
23282
                                                $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
 
23283
                                        }
 
23284
                                } else {
 
23285
                                        $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
 
23286
                                }
 
23287
                                break;
 
23288
                        }
 
23289
                        case 'li': {
 
23290
                                if ($key > 2) {
 
23291
                                        $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
 
23292
                                }
 
23293
                                if ($this->listordered[$this->listnum]) {
 
23294
                                        // ordered item
 
23295
                                        if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
 
23296
                                                $this->lispacer = $parent['attribute']['type'];
 
23297
                                        } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
 
23298
                                                $this->lispacer = $parent['listtype'];
 
23299
                                        } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
 
23300
                                                $this->lispacer = $this->lisymbol;
 
23301
                                        } else {
 
23302
                                                $this->lispacer = '#';
 
23303
                                        }
 
23304
                                        ++$this->listcount[$this->listnum];
 
23305
                                        if (isset($tag['attribute']['value'])) {
 
23306
                                                $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
 
23307
                                        }
 
23308
                                } else {
 
23309
                                        // unordered item
 
23310
                                        if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
 
23311
                                                $this->lispacer = $parent['attribute']['type'];
 
23312
                                        } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
 
23313
                                                $this->lispacer = $parent['listtype'];
 
23314
                                        } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
 
23315
                                                $this->lispacer = $this->lisymbol;
 
23316
                                        } else {
 
23317
                                                $this->lispacer = '!';
 
23318
                                        }
 
23319
                                }
 
23320
                                break;
 
23321
                        }
 
23322
                        case 'blockquote': {
 
23323
                                if ($this->rtl) {
 
23324
                                        $this->rMargin += $this->listindent;
 
23325
                                } else {
 
23326
                                        $this->lMargin += $this->listindent;
 
23327
                                }
 
23328
                                ++$this->listindentlevel;
 
23329
                                $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
 
23330
                                break;
 
23331
                        }
 
23332
                        case 'br': {
 
23333
                                $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
 
23334
                                break;
 
23335
                        }
 
23336
                        case 'div': {
 
23337
                                $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
 
23338
                                break;
 
23339
                        }
 
23340
                        case 'p': {
 
23341
                                $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
 
23342
                                break;
 
23343
                        }
 
23344
                        case 'pre': {
 
23345
                                $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
 
23346
                                $this->premode = true;
 
23347
                                break;
 
23348
                        }
 
23349
                        case 'sup': {
 
23350
                                $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
 
23351
                                break;
 
23352
                        }
 
23353
                        case 'sub': {
 
23354
                                $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
 
23355
                                break;
 
23356
                        }
 
23357
                        case 'h1':
 
23358
                        case 'h2':
 
23359
                        case 'h3':
 
23360
                        case 'h4':
 
23361
                        case 'h5':
 
23362
                        case 'h6': {
 
23363
                                $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
 
23364
                                break;
 
23365
                        }
 
23366
                        // Form fields (since 4.8.000 - 2009-09-07)
 
23367
                        case 'form': {
 
23368
                                if (isset($tag['attribute']['action'])) {
 
23369
                                        $this->form_action = $tag['attribute']['action'];
 
23370
                                } else {
 
23371
                                        $this->form_action = K_PATH_URL.$_SERVER['SCRIPT_NAME'];
 
23372
                                }
 
23373
                                if (isset($tag['attribute']['enctype'])) {
 
23374
                                        $this->form_enctype = $tag['attribute']['enctype'];
 
23375
                                } else {
 
23376
                                        $this->form_enctype = 'application/x-www-form-urlencoded';
 
23377
                                }
 
23378
                                if (isset($tag['attribute']['method'])) {
 
23379
                                        $this->form_mode = $tag['attribute']['method'];
 
23380
                                } else {
 
23381
                                        $this->form_mode = 'post';
 
23382
                                }
 
23383
                                break;
 
23384
                        }
 
23385
                        case 'input': {
 
23386
                                if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
 
23387
                                        $name = $tag['attribute']['name'];
 
23388
                                } else {
 
23389
                                        break;
 
23390
                                }
 
23391
                                $prop = array();
 
23392
                                $opt = array();
 
23393
                                if (isset($tag['attribute']['readonly']) AND !$this->empty_string($tag['attribute']['readonly'])) {
 
23394
                                        $prop['readonly'] = true;
 
23395
                                }
 
23396
                                if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) {
 
23397
                                        $value = $tag['attribute']['value'];
 
23398
                                }
 
23399
                                if (isset($tag['attribute']['maxlength']) AND !$this->empty_string($tag['attribute']['maxlength'])) {
 
23400
                                        $opt['maxlen'] = intval($tag['attribute']['value']);
 
23401
                                }
 
23402
                                $h = $this->FontSize * $this->cell_height_ratio;
 
23403
                                if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) {
 
23404
                                        $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
 
23405
                                } else {
 
23406
                                        $w = $h;
 
23407
                                }
 
23408
                                if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
 
23409
                                        $checked = true;
 
23410
                                } else {
 
23411
                                        $checked = false;
 
23412
                                }
 
23413
                                if (isset($tag['align'])) {
 
23414
                                        switch ($tag['align']) {
 
23415
                                                case 'C': {
 
23416
                                                        $opt['q'] = 1;
 
23417
                                                        break;
 
23418
                                                }
 
23419
                                                case 'R': {
 
23420
                                                        $opt['q'] = 2;
 
23421
                                                        break;
 
23422
                                                }
 
23423
                                                case 'L':
 
23424
                                                default: {
 
23425
                                                        break;
 
23426
                                                }
 
23427
                                        }
 
23428
                                }
 
23429
                                switch ($tag['attribute']['type']) {
 
23430
                                        case 'text': {
 
23431
                                                if (isset($value)) {
 
23432
                                                        $opt['v'] = $value;
 
23433
                                                }
 
23434
                                                $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
 
23435
                                                break;
 
23436
                                        }
 
23437
                                        case 'password': {
 
23438
                                                if (isset($value)) {
 
23439
                                                        $opt['v'] = $value;
 
23440
                                                }
 
23441
                                                $prop['password'] = 'true';
 
23442
                                                $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
 
23443
                                                break;
 
23444
                                        }
 
23445
                                        case 'checkbox': {
 
23446
                                                $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
 
23447
                                                break;
 
23448
                                        }
 
23449
                                        case 'radio': {
 
23450
                                                $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
 
23451
                                                break;
 
23452
                                        }
 
23453
                                        case 'submit': {
 
23454
                                                $w = $this->GetStringWidth($value) * 1.5;
 
23455
                                                $h *= 1.6;
 
23456
                                                $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
 
23457
                                                $action = array();
 
23458
                                                $action['S'] = 'SubmitForm';
 
23459
                                                $action['F'] = $this->form_action;
 
23460
                                                if ($this->form_enctype != 'FDF') {
 
23461
                                                        $action['Flags'] = array('ExportFormat');
 
23462
                                                }
 
23463
                                                if ($this->form_mode == 'get') {
 
23464
                                                        $action['Flags'] = array('GetMethod');
 
23465
                                                }
 
23466
                                                $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
 
23467
                                                break;
 
23468
                                        }
 
23469
                                        case 'reset': {
 
23470
                                                $w = $this->GetStringWidth($value) * 1.5;
 
23471
                                                $h *= 1.6;
 
23472
                                                $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
 
23473
                                                $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
 
23474
                                                break;
 
23475
                                        }
 
23476
                                        case 'file': {
 
23477
                                                $prop['fileSelect'] = 'true';
 
23478
                                                $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
 
23479
                                                if (!isset($value)) {
 
23480
                                                        $value = '*';
 
23481
                                                }
 
23482
                                                $w = $this->GetStringWidth($value) * 2;
 
23483
                                                $h *= 1.2;
 
23484
                                                $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
 
23485
                                                $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
 
23486
                                                $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
 
23487
                                                break;
 
23488
                                        }
 
23489
                                        case 'hidden': {
 
23490
                                                if (isset($value)) {
 
23491
                                                        $opt['v'] = $value;
 
23492
                                                }
 
23493
                                                $opt['f'] = array('invisible', 'hidden');
 
23494
                                                $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
 
23495
                                                break;
 
23496
                                        }
 
23497
                                        case 'image': {
 
23498
                                                // THIS TYPE MUST BE FIXED
 
23499
                                                if (isset($tag['attribute']['src']) AND !$this->empty_string($tag['attribute']['src'])) {
 
23500
                                                        $img = $tag['attribute']['src'];
 
23501
                                                } else {
 
23502
                                                        break;
 
23503
                                                }
 
23504
                                                $value = 'img';
 
23505
                                                //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
 
23506
                                                if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
 
23507
                                                        $jsaction = $tag['attribute']['onclick'];
 
23508
                                                } else {
 
23509
                                                        $jsaction = '';
 
23510
                                                }
 
23511
                                                $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
 
23512
                                                break;
 
23513
                                        }
 
23514
                                        case 'button': {
 
23515
                                                $w = $this->GetStringWidth($value) * 1.5;
 
23516
                                                $h *= 1.6;
 
23517
                                                $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
 
23518
                                                if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
 
23519
                                                        $jsaction = $tag['attribute']['onclick'];
 
23520
                                                } else {
 
23521
                                                        $jsaction = '';
 
23522
                                                }
 
23523
                                                $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
 
23524
                                                break;
 
23525
                                        }
 
23526
                                }
 
23527
                                break;
 
23528
                        }
 
23529
                        case 'textarea': {
 
23530
                                $prop = array();
 
23531
                                $opt = array();
 
23532
                                if (isset($tag['attribute']['readonly']) AND !$this->empty_string($tag['attribute']['readonly'])) {
 
23533
                                        $prop['readonly'] = true;
 
23534
                                }
 
23535
                                if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
 
23536
                                        $name = $tag['attribute']['name'];
 
23537
                                } else {
 
23538
                                        break;
 
23539
                                }
 
23540
                                if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) {
 
23541
                                        $opt['v'] = $tag['attribute']['value'];
 
23542
                                }
 
23543
                                if (isset($tag['attribute']['cols']) AND !$this->empty_string($tag['attribute']['cols'])) {
 
23544
                                        $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
 
23545
                                } else {
 
23546
                                        $w = 40;
 
23547
                                }
 
23548
                                if (isset($tag['attribute']['rows']) AND !$this->empty_string($tag['attribute']['rows'])) {
 
23549
                                        $h = intval($tag['attribute']['rows']) * $this->FontSize * $this->cell_height_ratio;
 
23550
                                } else {
 
23551
                                        $h = 10;
 
23552
                                }
 
23553
                                $prop['multiline'] = 'true';
 
23554
                                $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
 
23555
                                break;
 
23556
                        }
 
23557
                        case 'select': {
 
23558
                                $h = $this->FontSize * $this->cell_height_ratio;
 
23559
                                if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) {
 
23560
                                        $h *= ($tag['attribute']['size'] + 1);
 
23561
                                }
 
23562
                                $prop = array();
 
23563
                                $opt = array();
 
23564
                                if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
 
23565
                                        $name = $tag['attribute']['name'];
 
23566
                                } else {
 
23567
                                        break;
 
23568
                                }
 
23569
                                $w = 0;
 
23570
                                if (isset($tag['attribute']['opt']) AND !$this->empty_string($tag['attribute']['opt'])) {
 
23571
                                        $options = explode('#!NwL!#', $tag['attribute']['opt']);
 
23572
                                        $values = array();
 
23573
                                        foreach ($options as $val) {
 
23574
                                                if (strpos($val, '#!TaB!#') !== false) {
 
23575
                                                        $opts = explode('#!TaB!#', $val);
 
23576
                                                        $values[] = $opts;
 
23577
                                                        $w = max($w, $this->GetStringWidth($opts[1]));
 
23578
                                                } else {
 
23579
                                                        $values[] = $val;
 
23580
                                                        $w = max($w, $this->GetStringWidth($val));
 
23581
                                                }
 
23582
                                        }
 
23583
                                } else {
 
23584
                                        break;
 
23585
                                }
 
23586
                                $w *= 2;
 
23587
                                if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
 
23588
                                        $prop['multipleSelection'] = 'true';
 
23589
                                        $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
 
23590
                                } else {
 
23591
                                        $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
 
23592
                                }
 
23593
                                break;
 
23594
                        }
 
23595
                        case 'tcpdf': {
 
23596
                                if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
 
23597
                                        // Special tag used to call TCPDF methods
 
23598
                                        if (isset($tag['attribute']['method'])) {
 
23599
                                                $tcpdf_method = $tag['attribute']['method'];
 
23600
                                                if (method_exists($this, $tcpdf_method)) {
 
23601
                                                        if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
 
23602
                                                                $params = unserialize(urldecode($tag['attribute']['params']));
 
23603
                                                                call_user_func_array(array($this, $tcpdf_method), $params);
 
23604
                                                        } else {
 
23605
                                                                $this->$tcpdf_method();
 
23606
                                                        }
 
23607
                                                        $this->newline = true;
 
23608
                                                }
 
23609
                                        }
 
23610
                                }
 
23611
                                break;
 
23612
                        }
 
23613
                        default: {
 
23614
                                break;
 
23615
                        }
 
23616
                }
 
23617
                // define tags that support borders and background colors
 
23618
                $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
 
23619
                if (in_array($tag['value'], $bordertags)) {
 
23620
                        // set border
 
23621
                        $dom[$key]['borderposition'] = $this->getBorderStartPosition();
 
23622
                }
 
23623
                if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
 
23624
                        $pba = $dom[$key]['attribute']['pagebreakafter'];
 
23625
                        // check for pagebreak
 
23626
                        if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
 
23627
                                // add a page (or trig AcceptPageBreak() for multicolumn mode)
 
23628
                                $this->checkPageBreak($this->PageBreakTrigger + 1);
 
23629
                        }
 
23630
                        if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
 
23631
                                OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
 
23632
                                // add a page (or trig AcceptPageBreak() for multicolumn mode)
 
23633
                                $this->checkPageBreak($this->PageBreakTrigger + 1);
 
23634
                        }
 
23635
                }
 
23636
                return $dom;
 
23637
        }
 
23638
 
 
23639
        /**
 
23640
         * Process closing tags.
 
23641
         * @param $dom (array) html dom array
 
23642
         * @param $key (int) current element id
 
23643
         * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
 
23644
         * @param $maxbottomliney (int) maximum y value of current line
 
23645
         * @return $dom array
 
23646
         * @protected
 
23647
         */
 
23648
        protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
 
23649
                $tag = $dom[$key];
 
23650
                $parent = $dom[($dom[$key]['parent'])];
 
23651
                $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
 
23652
                $in_table_head = false;
 
23653
                // maximum x position (used to draw borders)
 
23654
                if ($this->rtl) {
 
23655
                        $xmax = $this->w;
 
23656
                } else {
 
23657
                        $xmax = 0;
 
23658
                }
 
23659
                if ($tag['block']) {
 
23660
                        $hbz = 0; // distance from y to line bottom
 
23661
                        $hb = 0; // vertical space between block tags
 
23662
                        // calculate vertical space for block tags
 
23663
                        if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
 
23664
                                $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
 
23665
                        } elseif (isset($parent['fontsize'])) {
 
23666
                                $pre_h = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
 
23667
                        } else {
 
23668
                                $pre_h = $this->FontSize * $this->cell_height_ratio;
 
23669
                        }
 
23670
                        if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
 
23671
                                $n = $this->tagvspaces[$tag['value']][1]['n'];
 
23672
                        } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
 
23673
                                $n = 0.6;
 
23674
                        } else {
 
23675
                                $n = 1;
 
23676
                        }
 
23677
                        if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) {
 
23678
                                $hb = 0;
 
23679
                        } else {
 
23680
                                $hb = ($n * $pre_h);
 
23681
                        }
 
23682
                        if ($maxbottomliney > $this->PageBreakTrigger) {
 
23683
                                $hbz = ($this->FontSize * $this->cell_height_ratio);
 
23684
                        } elseif ($this->y < $maxbottomliney) {
 
23685
                                $hbz = ($maxbottomliney - $this->y);
 
23686
                        }
 
23687
                }
 
23688
                // Closing tag
 
23689
                switch($tag['value']) {
 
23690
                        case 'tr': {
 
23691
                                $table_el = $dom[($dom[$key]['parent'])]['parent'];
 
23692
                                if (!isset($parent['endy'])) {
 
23693
                                        $dom[($dom[$key]['parent'])]['endy'] = $this->y;
 
23694
                                        $parent['endy'] = $this->y;
 
23695
                                }
 
23696
                                if (!isset($parent['endpage'])) {
 
23697
                                        $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
 
23698
                                        $parent['endpage'] = $this->page;
 
23699
                                }
 
23700
                                if (!isset($parent['endcolumn'])) {
 
23701
                                        $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column;
 
23702
                                        $parent['endcolumn'] = $this->current_column;
 
23703
                                }
 
23704
                                // update row-spanned cells
 
23705
                                if (isset($dom[$table_el]['rowspans'])) {
 
23706
                                        foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
 
23707
                                                $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
 
23708
                                                if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
 
23709
                                                        if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
 
23710
                                                                $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
 
23711
                                                        } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
 
23712
                                                                $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
 
23713
                                                                $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
 
23714
                                                                $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
 
23715
                                                        }
 
23716
                                                }
 
23717
                                        }
 
23718
                                        // report new endy and endpage to the rowspanned cells
 
23719
                                        foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
 
23720
                                                if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
 
23721
                                                        $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
 
23722
                                                        $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
 
23723
                                                        $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
 
23724
                                                        $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
 
23725
                                                        $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
 
23726
                                                        $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
 
23727
                                                }
 
23728
                                        }
 
23729
                                        // update remaining rowspanned cells
 
23730
                                        foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
 
23731
                                                if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
 
23732
                                                        $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
 
23733
                                                        $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
 
23734
                                                        $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
 
23735
                                                }
 
23736
                                        }
 
23737
                                }
 
23738
                                $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
 
23739
                                if ($this->num_columns > 1) {
 
23740
                                        $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
 
23741
                                }
 
23742
                                $this->y = $dom[($dom[$key]['parent'])]['endy'];
 
23743
                                if (isset($dom[$table_el]['attribute']['cellspacing'])) {
 
23744
                                        $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
 
23745
                                } elseif (isset($dom[$table_el]['border-spacing'])) {
 
23746
                                        $this->y += $dom[$table_el]['border-spacing']['V'];
 
23747
                                }
 
23748
                                $this->Ln(0, $cell);
 
23749
                                if ($this->current_column == $parent['startcolumn']) {
 
23750
                                        $this->x = $parent['startx'];
 
23751
                                }
 
23752
                                // account for booklet mode
 
23753
                                if ($this->page > $parent['startpage']) {
 
23754
                                        if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
 
23755
                                                $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
 
23756
                                        } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
 
23757
                                                $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
 
23758
                                        }
 
23759
                                }
 
23760
                                break;
 
23761
                        }
 
23762
                        case 'tablehead':
 
23763
                                // closing tag used for the thead part
 
23764
                                $in_table_head = true;
 
23765
                                $this->inthead = false;
 
23766
                        case 'table': {
 
23767
                                $table_el = $parent;
 
23768
                                // set default border
 
23769
                                if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
 
23770
                                        // set default border
 
23771
                                        $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
 
23772
                                } else {
 
23773
                                        $border = 0;
 
23774
                                }
 
23775
                                $default_border = $border;
 
23776
                                // fix bottom line alignment of last line before page break
 
23777
                                foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
 
23778
                                        // update row-spanned cells
 
23779
                                        if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
 
23780
                                                foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
 
23781
                                                        if ($trwsp['trid'] == $trkey) {
 
23782
                                                                $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
 
23783
                                                        }
 
23784
                                                        if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0)) {
 
23785
                                                                $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
 
23786
                                                        }
 
23787
                                                }
 
23788
                                        }
 
23789
                                        if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
 
23790
                                                $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
 
23791
                                                $dom[$prevtrkey]['endy'] = $pgendy;
 
23792
                                                // update row-spanned cells
 
23793
                                                if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
 
23794
                                                        foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
 
23795
                                                                if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] > 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
 
23796
                                                                        $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
 
23797
                                                                        $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
 
23798
                                                                }
 
23799
                                                        }
 
23800
                                                }
 
23801
                                        }
 
23802
                                        $prevtrkey = $trkey;
 
23803
                                        $table_el = $dom[($dom[$key]['parent'])];
 
23804
                                }
 
23805
                                // for each row
 
23806
                                if (count($table_el['trids']) > 0) {
 
23807
                                        unset($xmax);
 
23808
                                }
 
23809
                                foreach ($table_el['trids'] as $j => $trkey) {
 
23810
                                        $parent = $dom[$trkey];
 
23811
                                        if (!isset($xmax)) {
 
23812
                                                $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
 
23813
                                        }
 
23814
                                        // for each cell on the row
 
23815
                                        foreach ($parent['cellpos'] as $k => $cellpos) {
 
23816
                                                if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
 
23817
                                                        $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
 
23818
                                                        $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
 
23819
                                                        $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
 
23820
                                                        $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
 
23821
                                                        $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
 
23822
                                                        $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
 
23823
                                                        $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
 
23824
                                                } else {
 
23825
                                                        $endy = $parent['endy'];
 
23826
                                                        $startpage = $parent['startpage'];
 
23827
                                                        $endpage = $parent['endpage'];
 
23828
                                                        $startcolumn = $parent['startcolumn'];
 
23829
                                                        $endcolumn = $parent['endcolumn'];
 
23830
                                                }
 
23831
                                                if ($this->num_columns == 0) {
 
23832
                                                        $this->num_columns = 1;
 
23833
                                                }
 
23834
                                                if (isset($cellpos['border'])) {
 
23835
                                                        $border = $cellpos['border'];
 
23836
                                                }
 
23837
                                                if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
 
23838
                                                        $this->SetFillColorArray($cellpos['bgcolor']);
 
23839
                                                        $fill = true;
 
23840
                                                } else {
 
23841
                                                        $fill = false;
 
23842
                                                }
 
23843
                                                $x = $cellpos['startx'];
 
23844
                                                $y = $parent['starty'];
 
23845
                                                $starty = $y;
 
23846
                                                $w = abs($cellpos['endx'] - $cellpos['startx']);
 
23847
                                                // get border modes
 
23848
                                                $border_start = $this->getBorderMode($border, $position='start');
 
23849
                                                $border_end = $this->getBorderMode($border, $position='end');
 
23850
                                                $border_middle = $this->getBorderMode($border, $position='middle');
 
23851
                                                // design borders around HTML cells.
 
23852
                                                for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
 
23853
                                                        $ccode = '';
 
23854
                                                        $this->setPage($page);
 
23855
                                                        if ($this->num_columns < 2) {
 
23856
                                                                // single-column mode
 
23857
                                                                $this->x = $x;
 
23858
                                                                $this->y = $this->tMargin;
 
23859
                                                        }
 
23860
                                                        // account for margin changes
 
23861
                                                        if ($page > $startpage) {
 
23862
                                                                if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
 
23863
                                                                        $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
 
23864
                                                                } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
 
23865
                                                                        $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
 
23866
                                                                }
 
23867
                                                        }
 
23868
                                                        if ($startpage == $endpage) { // single page
 
23869
                                                                $deltacol = 0;
 
23870
                                                                $deltath = 0;
 
23871
                                                                for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
 
23872
                                                                        $this->selectColumn($column);
 
23873
                                                                        if ($startcolumn == $endcolumn) { // single column
 
23874
                                                                                $cborder = $border;
 
23875
                                                                                $h = $endy - $parent['starty'];
 
23876
                                                                                $this->y = $y;
 
23877
                                                                                $this->x = $x;
 
23878
                                                                        } elseif ($column == $startcolumn) { // first column
 
23879
                                                                                $cborder = $border_start;
 
23880
                                                                                $this->y = $starty;
 
23881
                                                                                $this->x = $x;
 
23882
                                                                                $h = $this->h - $this->y - $this->bMargin;
 
23883
                                                                                if ($this->rtl) {
 
23884
                                                                                        $deltacol = $this->x + $this->rMargin - $this->w;
 
23885
                                                                                } else {
 
23886
                                                                                        $deltacol = $this->x - $this->lMargin;
 
23887
                                                                                }
 
23888
                                                                        } elseif ($column == $endcolumn) { // end column
 
23889
                                                                                $cborder = $border_end;
 
23890
                                                                                if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
 
23891
                                                                                        $this->y = $this->columns[$column]['th']['\''.$page.'\''];
 
23892
                                                                                }
 
23893
                                                                                $this->x += $deltacol;
 
23894
                                                                                $h = $endy - $this->y;
 
23895
                                                                        } else { // middle column
 
23896
                                                                                $cborder = $border_middle;
 
23897
                                                                                if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
 
23898
                                                                                        $this->y = $this->columns[$column]['th']['\''.$page.'\''];
 
23899
                                                                                }
 
23900
                                                                                $this->x += $deltacol;
 
23901
                                                                                $h = $this->h - $this->y - $this->bMargin;
 
23902
                                                                        }
 
23903
                                                                        $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
 
23904
                                                                } // end for each column
 
23905
                                                        } elseif ($page == $startpage) { // first page
 
23906
                                                                $deltacol = 0;
 
23907
                                                                $deltath = 0;
 
23908
                                                                for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
 
23909
                                                                        $this->selectColumn($column);
 
23910
                                                                        if ($column == $startcolumn) { // first column
 
23911
                                                                                $cborder = $border_start;
 
23912
                                                                                $this->y = $starty;
 
23913
                                                                                $this->x = $x;
 
23914
                                                                                $h = $this->h - $this->y - $this->bMargin;
 
23915
                                                                                if ($this->rtl) {
 
23916
                                                                                        $deltacol = $this->x + $this->rMargin - $this->w;
 
23917
                                                                                } else {
 
23918
                                                                                        $deltacol = $this->x - $this->lMargin;
 
23919
                                                                                }
 
23920
                                                                        } else { // middle column
 
23921
                                                                                $cborder = $border_middle;
 
23922
                                                                                if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
 
23923
                                                                                        $this->y = $this->columns[$column]['th']['\''.$page.'\''];
 
23924
                                                                                }
 
23925
                                                                                $this->x += $deltacol;
 
23926
                                                                                $h = $this->h - $this->y - $this->bMargin;
 
23927
                                                                        }
 
23928
                                                                        $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
 
23929
                                                                } // end for each column
 
23930
                                                        } elseif ($page == $endpage) { // last page
 
23931
                                                                $deltacol = 0;
 
23932
                                                                $deltath = 0;
 
23933
                                                                for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
 
23934
                                                                        $this->selectColumn($column);
 
23935
                                                                        if ($column == $endcolumn) { // end column
 
23936
                                                                                $cborder = $border_end;
 
23937
                                                                                if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
 
23938
                                                                                        $this->y = $this->columns[$column]['th']['\''.$page.'\''];
 
23939
                                                                                }
 
23940
                                                                                $this->x += $deltacol;
 
23941
                                                                                $h = $endy - $this->y;
 
23942
                                                                        } else { // middle column
 
23943
                                                                                $cborder = $border_middle;
 
23944
                                                                                if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
 
23945
                                                                                        $this->y = $this->columns[$column]['th']['\''.$page.'\''];
 
23946
                                                                                }
 
23947
                                                                                $this->x += $deltacol;
 
23948
                                                                                $h = $this->h - $this->y - $this->bMargin;
 
23949
                                                                        }
 
23950
                                                                        $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
 
23951
                                                                } // end for each column
 
23952
                                                        } else { // middle page
 
23953
                                                                $deltacol = 0;
 
23954
                                                                $deltath = 0;
 
23955
                                                                for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
 
23956
                                                                        $this->selectColumn($column);
 
23957
                                                                        $cborder = $border_middle;
 
23958
                                                                        if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
 
23959
                                                                                $this->y = $this->columns[$column]['th']['\''.$page.'\''];
 
23960
                                                                        }
 
23961
                                                                        $this->x += $deltacol;
 
23962
                                                                        $h = $this->h - $this->y - $this->bMargin;
 
23963
                                                                        $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
 
23964
                                                                } // end for each column
 
23965
                                                        }
 
23966
                                                        if ($cborder OR $fill) {
 
23967
                                                                $offsetlen = strlen($ccode);
 
23968
                                                                // draw border and fill
 
23969
                                                                if ($this->inxobj) {
 
23970
                                                                        // we are inside an XObject template
 
23971
                                                                        if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
 
23972
                                                                                $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
 
23973
                                                                                $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
 
23974
                                                                                $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
 
23975
                                                                        } else {
 
23976
                                                                                $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
 
23977
                                                                                $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
 
23978
                                                                        }
 
23979
                                                                        $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
 
23980
                                                                        $pstart = substr($pagebuff, 0, $pagemark);
 
23981
                                                                        $pend = substr($pagebuff, $pagemark);
 
23982
                                                                        $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
 
23983
                                                                } else {
 
23984
                                                                        // draw border and fill
 
23985
                                                                        if (end($this->transfmrk[$this->page]) !== false) {
 
23986
                                                                                $pagemarkkey = key($this->transfmrk[$this->page]);
 
23987
                                                                                $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
 
23988
                                                                                $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen;
 
23989
                                                                        } elseif ($this->InFooter) {
 
23990
                                                                                $pagemark = $this->footerpos[$this->page];
 
23991
                                                                                $this->footerpos[$this->page] += $offsetlen;
 
23992
                                                                        } else {
 
23993
                                                                                $pagemark = $this->intmrk[$this->page];
 
23994
                                                                                $this->intmrk[$this->page] += $offsetlen;
 
23995
                                                                        }
 
23996
                                                                        $pagebuff = $this->getPageBuffer($this->page);
 
23997
                                                                        $pstart = substr($pagebuff, 0, $pagemark);
 
23998
                                                                        $pend = substr($pagebuff, $pagemark);
 
23999
                                                                        $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
 
24000
                                                                }
 
24001
                                                        }
 
24002
                                                } // end for each page
 
24003
                                                // restore default border
 
24004
                                                $border = $default_border;
 
24005
                                        } // end for each cell on the row
 
24006
                                        if (isset($table_el['attribute']['cellspacing'])) {
 
24007
                                                $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
 
24008
                                        } elseif (isset($table_el['border-spacing'])) {
 
24009
                                                $this->y += $table_el['border-spacing']['V'];
 
24010
                                        }
 
24011
                                        $this->Ln(0, $cell);
 
24012
                                        $this->x = $parent['startx'];
 
24013
                                        if ($endpage > $startpage) {
 
24014
                                                if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
 
24015
                                                        $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
 
24016
                                                } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
 
24017
                                                        $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
 
24018
                                                }
 
24019
                                        }
 
24020
                                }
 
24021
                                if (!$in_table_head) { // we are not inside a thead section
 
24022
                                        $this->cell_padding = $table_el['old_cell_padding'];
 
24023
                                        // reset row height
 
24024
                                        $this->resetLastH();
 
24025
                                        if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) {
 
24026
                                                $plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]);
 
24027
                                                if (($plendiff > 0) AND ($plendiff < 60)) {
 
24028
                                                        $pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff);
 
24029
                                                        if (substr($pagediff, 0, 5) == 'BT /F') {
 
24030
                                                                // the difference is only a font setting
 
24031
                                                                $plendiff = 0;
 
24032
                                                        }
 
24033
                                                }
 
24034
                                                if ($plendiff == 0) {
 
24035
                                                        // remove last blank page
 
24036
                                                        $this->deletePage($this->numpages);
 
24037
                                                }
 
24038
                                        }
 
24039
                                        if (isset($this->theadMargins['top'])) {
 
24040
                                                // restore top margin
 
24041
                                                $this->tMargin = $this->theadMargins['top'];
 
24042
                                        }
 
24043
                                        if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
 
24044
                                                // reset main table header
 
24045
                                                $this->thead = '';
 
24046
                                                $this->theadMargins = array();
 
24047
                                                $this->pagedim[$this->page]['tm'] = $this->tMargin;
 
24048
                                        }
 
24049
                                }
 
24050
                                $parent = $table_el;
 
24051
                                break;
 
24052
                        }
 
24053
                        case 'a': {
 
24054
                                $this->HREF = '';
 
24055
                                break;
 
24056
                        }
 
24057
                        case 'sup': {
 
24058
                                $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
 
24059
                                break;
 
24060
                        }
 
24061
                        case 'sub': {
 
24062
                                $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k));
 
24063
                                break;
 
24064
                        }
 
24065
                        case 'div': {
 
24066
                                $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
 
24067
                                break;
 
24068
                        }
 
24069
                        case 'blockquote': {
 
24070
                                if ($this->rtl) {
 
24071
                                        $this->rMargin -= $this->listindent;
 
24072
                                } else {
 
24073
                                        $this->lMargin -= $this->listindent;
 
24074
                                }
 
24075
                                --$this->listindentlevel;
 
24076
                                $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
 
24077
                                break;
 
24078
                        }
 
24079
                        case 'p': {
 
24080
                                $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
 
24081
                                break;
 
24082
                        }
 
24083
                        case 'pre': {
 
24084
                                $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
 
24085
                                $this->premode = false;
 
24086
                                break;
 
24087
                        }
 
24088
                        case 'dl': {
 
24089
                                --$this->listnum;
 
24090
                                if ($this->listnum <= 0) {
 
24091
                                        $this->listnum = 0;
 
24092
                                        $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
 
24093
                                } else {
 
24094
                                        $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
 
24095
                                }
 
24096
                                $this->resetLastH();
 
24097
                                break;
 
24098
                        }
 
24099
                        case 'dt': {
 
24100
                                $this->lispacer = '';
 
24101
                                $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
 
24102
                                break;
 
24103
                        }
 
24104
                        case 'dd': {
 
24105
                                $this->lispacer = '';
 
24106
                                if ($this->rtl) {
 
24107
                                        $this->rMargin -= $this->listindent;
 
24108
                                } else {
 
24109
                                        $this->lMargin -= $this->listindent;
 
24110
                                }
 
24111
                                --$this->listindentlevel;
 
24112
                                $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
 
24113
                                break;
 
24114
                        }
 
24115
                        case 'ul':
 
24116
                        case 'ol': {
 
24117
                                --$this->listnum;
 
24118
                                $this->lispacer = '';
 
24119
                                if ($this->rtl) {
 
24120
                                        $this->rMargin -= $this->listindent;
 
24121
                                } else {
 
24122
                                        $this->lMargin -= $this->listindent;
 
24123
                                }
 
24124
                                --$this->listindentlevel;
 
24125
                                if ($this->listnum <= 0) {
 
24126
                                        $this->listnum = 0;
 
24127
                                        $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
 
24128
                                } else {
 
24129
                                        $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
 
24130
                                }
 
24131
                                $this->resetLastH();
 
24132
                                break;
 
24133
                        }
 
24134
                        case 'li': {
 
24135
                                $this->lispacer = '';
 
24136
                                $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
 
24137
                                break;
 
24138
                        }
 
24139
                        case 'h1':
 
24140
                        case 'h2':
 
24141
                        case 'h3':
 
24142
                        case 'h4':
 
24143
                        case 'h5':
 
24144
                        case 'h6': {
 
24145
                                $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
 
24146
                                break;
 
24147
                        }
 
24148
                        // Form fields (since 4.8.000 - 2009-09-07)
 
24149
                        case 'form': {
 
24150
                                $this->form_action = '';
 
24151
                                $this->form_enctype = 'application/x-www-form-urlencoded';
 
24152
                                break;
 
24153
                        }
 
24154
                        default : {
 
24155
                                break;
 
24156
                        }
 
24157
                }
 
24158
                // draw border and background (if any)
 
24159
                $this->drawHTMLTagBorder($parent, $xmax);
 
24160
                if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
 
24161
                        $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
 
24162
                        // check for pagebreak
 
24163
                        if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
 
24164
                                // add a page (or trig AcceptPageBreak() for multicolumn mode)
 
24165
                                $this->checkPageBreak($this->PageBreakTrigger + 1);
 
24166
                        }
 
24167
                        if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
 
24168
                                OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
 
24169
                                // add a page (or trig AcceptPageBreak() for multicolumn mode)
 
24170
                                $this->checkPageBreak($this->PageBreakTrigger + 1);
 
24171
                        }
 
24172
                }
 
24173
                $this->tmprtl = false;
 
24174
                return $dom;
 
24175
        }
 
24176
 
 
24177
        /**
 
24178
         * Add vertical spaces if needed.
 
24179
         * @param $hbz (string) Distance between current y and line bottom.
 
24180
         * @param $hb (string) The height of the break.
 
24181
         * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
 
24182
         * @param $firsttag (boolean) set to true when the tag is the first.
 
24183
         * @param $lasttag (boolean) set to true when the tag is the last.
 
24184
         * @protected
 
24185
         */
 
24186
        protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
 
24187
                if ($firsttag) {
 
24188
                        $this->Ln(0, $cell);
 
24189
                        $this->htmlvspace = 0;
 
24190
                        return;
 
24191
                }
 
24192
                if ($lasttag) {
 
24193
                        $this->Ln($hbz, $cell);
 
24194
                        $this->htmlvspace = 0;
 
24195
                        return;
 
24196
                }
 
24197
                if ($hb < $this->htmlvspace) {
 
24198
                        $hd = 0;
 
24199
                } else {
 
24200
                        $hd = $hb - $this->htmlvspace;
 
24201
                        $this->htmlvspace = $hb;
 
24202
                }
 
24203
                $this->Ln(($hbz + $hd), $cell);
 
24204
        }
 
24205
 
 
24206
        /**
 
24207
         * Return the starting coordinates to draw an html border
 
24208
         * @return array containing top-left border coordinates
 
24209
         * @protected
 
24210
         * @since 5.7.000 (2010-08-03)
 
24211
         */
 
24212
        protected function getBorderStartPosition() {
 
24213
                if ($this->rtl) {
 
24214
                        $xmax = $this->lMargin;
 
24215
                } else {
 
24216
                        $xmax = $this->w - $this->rMargin;
 
24217
                }
 
24218
                return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax);
 
24219
        }
 
24220
 
 
24221
        /**
 
24222
         * Draw an HTML block border and fill
 
24223
         * @param $tag (array) array of tag properties.
 
24224
         * @param $xmax (int) end X coordinate for border.
 
24225
         * @protected
 
24226
         * @since 5.7.000 (2010-08-03)
 
24227
         */
 
24228
        protected function drawHTMLTagBorder($tag, $xmax) {
 
24229
                if (!isset($tag['borderposition'])) {
 
24230
                        // nothing to draw
 
24231
                        return;
 
24232
                }
 
24233
                $prev_x = $this->x;
 
24234
                $prev_y = $this->y;
 
24235
                $prev_lasth = $this->lasth;
 
24236
                $border = 0;
 
24237
                $fill = false;
 
24238
                $this->lasth = 0;
 
24239
                if (isset($tag['border']) AND !empty($tag['border'])) {
 
24240
                        // get border style
 
24241
                        $border = $tag['border'];
 
24242
                        if (!$this->empty_string($this->thead) AND (!$this->inthead)) {
 
24243
                                // border for table header
 
24244
                                $border = $this->getBorderMode($border, $position='middle');
 
24245
                        }
 
24246
                }
 
24247
                if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
 
24248
                        // get background color
 
24249
                        $old_bgcolor = $this->bgcolor;
 
24250
                        $this->SetFillColorArray($tag['bgcolor']);
 
24251
                        $fill = true;
 
24252
                }
 
24253
                if (!$border AND !$fill) {
 
24254
                        // nothing to draw
 
24255
                        return;
 
24256
                }
 
24257
                if (isset($tag['attribute']['cellspacing'])) {
 
24258
                        $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
 
24259
                        $cellspacing = array('H' => $clsp, 'V' => $clsp);
 
24260
                } elseif (isset($tag['border-spacing'])) {
 
24261
                        $cellspacing = $tag['border-spacing'];
 
24262
                } else {
 
24263
                        $cellspacing = array('H' => 0, 'V' => 0);
 
24264
                }
 
24265
                if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
 
24266
                        // draw the border externally respect the sqare edge.
 
24267
                        $border['mode'] = 'ext';
 
24268
                }
 
24269
                if ($this->rtl) {
 
24270
                        if ($xmax >= $tag['borderposition']['x']) {
 
24271
                                $xmax = $tag['borderposition']['xmax'];
 
24272
                        }
 
24273
                        $w = ($tag['borderposition']['x'] - $xmax);
 
24274
                } else {
 
24275
                        if ($xmax <= $tag['borderposition']['x']) {
 
24276
                                $xmax = $tag['borderposition']['xmax'];
 
24277
                        }
 
24278
                        $w = ($xmax - $tag['borderposition']['x']);
 
24279
                }
 
24280
                if ($w <= 0) {
 
24281
                        return;
 
24282
                }
 
24283
                $w += $cellspacing['H'];
 
24284
                $startpage = $tag['borderposition']['page'];
 
24285
                $startcolumn = $tag['borderposition']['column'];
 
24286
                $x = $tag['borderposition']['x'];
 
24287
                $y = $tag['borderposition']['y'];
 
24288
                $endpage = $this->page;
 
24289
                $starty = $tag['borderposition']['y'] - $cellspacing['V'];
 
24290
                $currentY = $this->y;
 
24291
                $this->x = $x;
 
24292
                // get latest column
 
24293
                $endcolumn = $this->current_column;
 
24294
                if ($this->num_columns == 0) {
 
24295
                        $this->num_columns = 1;
 
24296
                }
 
24297
                // get border modes
 
24298
                $border_start = $this->getBorderMode($border, $position='start');
 
24299
                $border_end = $this->getBorderMode($border, $position='end');
 
24300
                $border_middle = $this->getBorderMode($border, $position='middle');
 
24301
                // temporary disable page regions
 
24302
                $temp_page_regions = $this->page_regions;
 
24303
                $this->page_regions = array();
 
24304
                // design borders around HTML cells.
 
24305
                for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
 
24306
                        $ccode = '';
 
24307
                        $this->setPage($page);
 
24308
                        if ($this->num_columns < 2) {
 
24309
                                // single-column mode
 
24310
                                $this->x = $x;
 
24311
                                $this->y = $this->tMargin;
 
24312
                        }
 
24313
                        // account for margin changes
 
24314
                        if ($page > $startpage) {
 
24315
                                if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
 
24316
                                        $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
 
24317
                                } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
 
24318
                                        $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
 
24319
                                }
 
24320
                        }
 
24321
                        if ($startpage == $endpage) {
 
24322
                                // single page
 
24323
                                for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
 
24324
                                        $this->selectColumn($column);
 
24325
                                        if ($startcolumn == $endcolumn) { // single column
 
24326
                                                $cborder = $border;
 
24327
                                                $h = ($currentY - $y) + $cellspacing['V'];
 
24328
                                                $this->y = $starty;
 
24329
                                        } elseif ($column == $startcolumn) { // first column
 
24330
                                                $cborder = $border_start;
 
24331
                                                $this->y = $starty;
 
24332
                                                $h = $this->h - $this->y - $this->bMargin;
 
24333
                                        } elseif ($column == $endcolumn) { // end column
 
24334
                                                $cborder = $border_end;
 
24335
                                                $h = $currentY - $this->y;
 
24336
                                        } else { // middle column
 
24337
                                                $cborder = $border_middle;
 
24338
                                                $h = $this->h - $this->y - $this->bMargin;
 
24339
                                        }
 
24340
                                        $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
 
24341
                                } // end for each column
 
24342
                        } elseif ($page == $startpage) { // first page
 
24343
                                for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
 
24344
                                        $this->selectColumn($column);
 
24345
                                        if ($column == $startcolumn) { // first column
 
24346
                                                $cborder = $border_start;
 
24347
                                                $this->y = $starty;
 
24348
                                                $h = $this->h - $this->y - $this->bMargin;
 
24349
                                        } else { // middle column
 
24350
                                                $cborder = $border_middle;
 
24351
                                                $h = $this->h - $this->y - $this->bMargin;
 
24352
                                        }
 
24353
                                        $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
 
24354
                                } // end for each column
 
24355
                        } elseif ($page == $endpage) { // last page
 
24356
                                for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
 
24357
                                        $this->selectColumn($column);
 
24358
                                        if ($column == $endcolumn) {
 
24359
                                                // end column
 
24360
                                                $cborder = $border_end;
 
24361
                                                $h = $currentY - $this->y;
 
24362
                                        } else {
 
24363
                                                // middle column
 
24364
                                                $cborder = $border_middle;
 
24365
                                                $h = $this->h - $this->y - $this->bMargin;
 
24366
                                        }
 
24367
                                        $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
 
24368
                                } // end for each column
 
24369
                        } else { // middle page
 
24370
                                for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
 
24371
                                        $this->selectColumn($column);
 
24372
                                        $cborder = $border_middle;
 
24373
                                        $h = $this->h - $this->y - $this->bMargin;
 
24374
                                        $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
 
24375
                                } // end for each column
 
24376
                        }
 
24377
                        if ($cborder OR $fill) {
 
24378
                                $offsetlen = strlen($ccode);
 
24379
                                // draw border and fill
 
24380
                                if ($this->inxobj) {
 
24381
                                        // we are inside an XObject template
 
24382
                                        if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
 
24383
                                                $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
 
24384
                                                $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
 
24385
                                                $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
 
24386
                                        } else {
 
24387
                                                $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
 
24388
                                                $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
 
24389
                                        }
 
24390
                                        $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
 
24391
                                        $pstart = substr($pagebuff, 0, $pagemark);
 
24392
                                        $pend = substr($pagebuff, $pagemark);
 
24393
                                        $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
 
24394
                                } else {
 
24395
                                        if (end($this->transfmrk[$this->page]) !== false) {
 
24396
                                                $pagemarkkey = key($this->transfmrk[$this->page]);
 
24397
                                                $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
 
24398
                                                $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen;
 
24399
                                        } elseif ($this->InFooter) {
 
24400
                                                $pagemark = $this->footerpos[$this->page];
 
24401
                                                $this->footerpos[$this->page] += $offsetlen;
 
24402
                                        } else {
 
24403
                                                $pagemark = $this->intmrk[$this->page];
 
24404
                                                $this->intmrk[$this->page] += $offsetlen;
 
24405
                                        }
 
24406
                                        $pagebuff = $this->getPageBuffer($this->page);
 
24407
                                        $pstart = substr($pagebuff, 0, $this->bordermrk[$this->page]);
 
24408
                                        $pend = substr($pagebuff, $this->bordermrk[$this->page]);
 
24409
                                        $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
 
24410
                                        $this->bordermrk[$this->page] += $offsetlen;
 
24411
                                        $this->cntmrk[$this->page] += $offsetlen;
 
24412
                                }
 
24413
                        }
 
24414
                } // end for each page
 
24415
                // restore page regions
 
24416
                $this->page_regions = $temp_page_regions;
 
24417
                if (isset($old_bgcolor)) {
 
24418
                        // restore background color
 
24419
                        $this->SetFillColorArray($old_bgcolor);
 
24420
                }
 
24421
                // restore pointer position
 
24422
                $this->x = $prev_x;
 
24423
                $this->y = $prev_y;
 
24424
                $this->lasth = $prev_lasth;
 
24425
        }
 
24426
 
 
24427
        /**
 
24428
         * Set the default bullet to be used as LI bullet symbol
 
24429
         * @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')
 
24430
         * @public
 
24431
         * @since 4.0.028 (2008-09-26)
 
24432
         */
 
24433
        public function setLIsymbol($symbol='!') {
 
24434
                // check for custom image symbol
 
24435
                if (substr($symbol, 0, 4) == 'img|') {
 
24436
                        $this->lisymbol = $symbol;
 
24437
                        return;
 
24438
                }
 
24439
                $symbol = strtolower($symbol);
 
24440
                switch ($symbol) {
 
24441
                        case '!' :
 
24442
                        case '#' :
 
24443
                        case 'disc' :
 
24444
                        case 'circle' :
 
24445
                        case 'square' :
 
24446
                        case '1':
 
24447
                        case 'decimal':
 
24448
                        case 'decimal-leading-zero':
 
24449
                        case 'i':
 
24450
                        case 'lower-roman':
 
24451
                        case 'I':
 
24452
                        case 'upper-roman':
 
24453
                        case 'a':
 
24454
                        case 'lower-alpha':
 
24455
                        case 'lower-latin':
 
24456
                        case 'A':
 
24457
                        case 'upper-alpha':
 
24458
                        case 'upper-latin':
 
24459
                        case 'lower-greek': {
 
24460
                                $this->lisymbol = $symbol;
 
24461
                                break;
 
24462
                        }
 
24463
                        default : {
 
24464
                                $this->lisymbol = '';
 
24465
                        }
 
24466
                }
 
24467
        }
 
24468
 
 
24469
        /**
 
24470
         * Set the booklet mode for double-sided pages.
 
24471
         * @param $booklet (boolean) true set the booklet mode on, false otherwise.
 
24472
         * @param $inner (float) Inner page margin.
 
24473
         * @param $outer (float) Outer page margin.
 
24474
         * @public
 
24475
         * @since 4.2.000 (2008-10-29)
 
24476
         */
 
24477
        public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
 
24478
                $this->booklet = $booklet;
 
24479
                if ($inner >= 0) {
 
24480
                        $this->lMargin = $inner;
 
24481
                }
 
24482
                if ($outer >= 0) {
 
24483
                        $this->rMargin = $outer;
 
24484
                }
 
24485
        }
 
24486
 
 
24487
        /**
 
24488
         * Swap the left and right margins.
 
24489
         * @param $reverse (boolean) if true swap left and right margins.
 
24490
         * @protected
 
24491
         * @since 4.2.000 (2008-10-29)
 
24492
         */
 
24493
        protected function swapMargins($reverse=true) {
 
24494
                if ($reverse) {
 
24495
                        // swap left and right margins
 
24496
                        $mtemp = $this->original_lMargin;
 
24497
                        $this->original_lMargin = $this->original_rMargin;
 
24498
                        $this->original_rMargin = $mtemp;
 
24499
                        $deltam = $this->original_lMargin - $this->original_rMargin;
 
24500
                        $this->lMargin += $deltam;
 
24501
                        $this->rMargin -= $deltam;
 
24502
                }
 
24503
        }
 
24504
 
 
24505
        /**
 
24506
         * Set the vertical spaces for HTML tags.
 
24507
         * The array must have the following structure (example):
 
24508
         * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1)));
 
24509
         * The first array level contains the tag names,
 
24510
         * the second level contains 0 for opening tags or 1 for closing tags,
 
24511
         * the third level contains the vertical space unit (h) and the number spaces to add (n).
 
24512
         * If the h parameter is not specified, default values are used.
 
24513
         * @param $tagvs (array) array of tags and relative vertical spaces.
 
24514
         * @public
 
24515
         * @since 4.2.001 (2008-10-30)
 
24516
         */
 
24517
        public function setHtmlVSpace($tagvs) {
 
24518
                $this->tagvspaces = $tagvs;
 
24519
        }
 
24520
 
 
24521
        /**
 
24522
         * Set custom width for list indentation.
 
24523
         * @param $width (float) width of the indentation. Use negative value to disable it.
 
24524
         * @public
 
24525
         * @since 4.2.007 (2008-11-12)
 
24526
         */
 
24527
        public function setListIndentWidth($width) {
 
24528
                return $this->customlistindent = floatval($width);
 
24529
        }
 
24530
 
 
24531
        /**
 
24532
         * Set the top/bottom cell sides to be open or closed when the cell cross the page.
 
24533
         * @param $isopen (boolean) if true keeps the top/bottom border open for the cell sides that cross the page.
 
24534
         * @public
 
24535
         * @since 4.2.010 (2008-11-14)
 
24536
         */
 
24537
        public function setOpenCell($isopen) {
 
24538
                $this->opencell = $isopen;
 
24539
        }
 
24540
 
 
24541
        /**
 
24542
         * Set the color and font style for HTML links.
 
24543
         * @param $color (array) RGB array of colors
 
24544
         * @param $fontstyle (string) additional font styles to add
 
24545
         * @public
 
24546
         * @since 4.4.003 (2008-12-09)
 
24547
         */
 
24548
        public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
 
24549
                $this->htmlLinkColorArray = $color;
 
24550
                $this->htmlLinkFontStyle = $fontstyle;
 
24551
        }
 
24552
 
 
24553
        /**
 
24554
         * Convert HTML string containing value and unit of measure to user's units or points.
 
24555
         * @param $htmlval (string) string containing values and unit
 
24556
         * @param $refsize (string) reference value in points
 
24557
         * @param $defaultunit (string) default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
 
24558
         * @param $points (boolean) if true returns points, otherwise returns value in user's units
 
24559
         * @return float value in user's unit or point if $points=true
 
24560
         * @public
 
24561
         * @since 4.4.004 (2008-12-10)
 
24562
         */
 
24563
        public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
 
24564
                $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
 
24565
                $retval = 0;
 
24566
                $value = 0;
 
24567
                $unit = 'px';
 
24568
                $k = $this->k;
 
24569
                if ($points) {
 
24570
                        $k = 1;
 
24571
                }
 
24572
                if (in_array($defaultunit, $supportedunits)) {
 
24573
                        $unit = $defaultunit;
 
24574
                }
 
24575
                if (is_numeric($htmlval)) {
 
24576
                        $value = floatval($htmlval);
 
24577
                } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
 
24578
                        $value = floatval($mnum[1]);
 
24579
                        if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
 
24580
                                if (in_array($munit[1], $supportedunits)) {
 
24581
                                        $unit = $munit[1];
 
24582
                                }
 
24583
                        }
 
24584
                }
 
24585
                switch ($unit) {
 
24586
                        // percentage
 
24587
                        case '%': {
 
24588
                                $retval = (($value * $refsize) / 100);
 
24589
                                break;
 
24590
                        }
 
24591
                        // relative-size
 
24592
                        case 'em': {
 
24593
                                $retval = ($value * $refsize);
 
24594
                                break;
 
24595
                        }
 
24596
                        // height of lower case 'x' (about half the font-size)
 
24597
                        case 'ex': {
 
24598
                                $retval = $value * ($refsize / 2);
 
24599
                                break;
 
24600
                        }
 
24601
                        // absolute-size
 
24602
                        case 'in': {
 
24603
                                $retval = ($value * $this->dpi) / $k;
 
24604
                                break;
 
24605
                        }
 
24606
                        // centimeters
 
24607
                        case 'cm': {
 
24608
                                $retval = ($value / 2.54 * $this->dpi) / $k;
 
24609
                                break;
 
24610
                        }
 
24611
                        // millimeters
 
24612
                        case 'mm': {
 
24613
                                $retval = ($value / 25.4 * $this->dpi) / $k;
 
24614
                                break;
 
24615
                        }
 
24616
                        // one pica is 12 points
 
24617
                        case 'pc': {
 
24618
                                $retval = ($value * 12) / $k;
 
24619
                                break;
 
24620
                        }
 
24621
                        // points
 
24622
                        case 'pt': {
 
24623
                                $retval = $value / $k;
 
24624
                                break;
 
24625
                        }
 
24626
                        // pixels
 
24627
                        case 'px': {
 
24628
                                $retval = $this->pixelsToUnits($value);
 
24629
                                break;
 
24630
                        }
 
24631
                }
 
24632
                return $retval;
 
24633
        }
 
24634
 
 
24635
        /**
 
24636
         * Returns the Roman representation of an integer number
 
24637
         * @param $number (int) number to convert
 
24638
         * @return string roman representation of the specified number
 
24639
         * @since 4.4.004 (2008-12-10)
 
24640
         * @public
 
24641
         */
 
24642
        public function intToRoman($number) {
 
24643
                $roman = '';
 
24644
                while ($number >= 1000) {
 
24645
                        $roman .= 'M';
 
24646
                        $number -= 1000;
 
24647
                }
 
24648
                while ($number >= 900) {
 
24649
                        $roman .= 'CM';
 
24650
                        $number -= 900;
 
24651
                }
 
24652
                while ($number >= 500) {
 
24653
                        $roman .= 'D';
 
24654
                        $number -= 500;
 
24655
                }
 
24656
                while ($number >= 400) {
 
24657
                        $roman .= 'CD';
 
24658
                        $number -= 400;
 
24659
                }
 
24660
                while ($number >= 100) {
 
24661
                        $roman .= 'C';
 
24662
                        $number -= 100;
 
24663
                }
 
24664
                while ($number >= 90) {
 
24665
                        $roman .= 'XC';
 
24666
                        $number -= 90;
 
24667
                }
 
24668
                while ($number >= 50) {
 
24669
                        $roman .= 'L';
 
24670
                        $number -= 50;
 
24671
                }
 
24672
                while ($number >= 40) {
 
24673
                        $roman .= 'XL';
 
24674
                        $number -= 40;
 
24675
                }
 
24676
                while ($number >= 10) {
 
24677
                        $roman .= 'X';
 
24678
                        $number -= 10;
 
24679
                }
 
24680
                while ($number >= 9) {
 
24681
                        $roman .= 'IX';
 
24682
                        $number -= 9;
 
24683
                }
 
24684
                while ($number >= 5) {
 
24685
                        $roman .= 'V';
 
24686
                        $number -= 5;
 
24687
                }
 
24688
                while ($number >= 4) {
 
24689
                        $roman .= 'IV';
 
24690
                        $number -= 4;
 
24691
                }
 
24692
                while ($number >= 1) {
 
24693
                        $roman .= 'I';
 
24694
                        --$number;
 
24695
                }
 
24696
                return $roman;
 
24697
        }
 
24698
 
 
24699
        /**
 
24700
         * Output an HTML list bullet or ordered item symbol
 
24701
         * @param $listdepth (int) list nesting level
 
24702
         * @param $listtype (string) type of list
 
24703
         * @param $size (float) current font size
 
24704
         * @protected
 
24705
         * @since 4.4.004 (2008-12-10)
 
24706
         */
 
24707
        protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
 
24708
                $size /= $this->k;
 
24709
                $fill = '';
 
24710
                $bgcolor = $this->bgcolor;
 
24711
                $color = $this->fgcolor;
 
24712
                $strokecolor = $this->strokecolor;
 
24713
                $width = 0;
 
24714
                $textitem = '';
 
24715
                $tmpx = $this->x;
 
24716
                $lspace = $this->GetStringWidth('  ');
 
24717
                if ($listtype == '^') {
 
24718
                        // special symbol used for avoid justification of rect bullet
 
24719
                        $this->lispacer = '';
 
24720
                        return;
 
24721
                } elseif ($listtype == '!') {
 
24722
                        // set default list type for unordered list
 
24723
                        $deftypes = array('disc', 'circle', 'square');
 
24724
                        $listtype = $deftypes[($listdepth - 1) % 3];
 
24725
                } elseif ($listtype == '#') {
 
24726
                        // set default list type for ordered list
 
24727
                        $listtype = 'decimal';
 
24728
                } elseif (substr($listtype, 0, 4) == 'img|') {
 
24729
                        // custom image type ('img|type|width|height|image.ext')
 
24730
                        $img = explode('|', $listtype);
 
24731
                        $listtype = 'img';
 
24732
                }
 
24733
                switch ($listtype) {
 
24734
                        // unordered types
 
24735
                        case 'none': {
 
24736
                                break;
 
24737
                        }
 
24738
                        case 'disc': {
 
24739
                                $r = $size / 6;
 
24740
                                $lspace += (2 * $r);
 
24741
                                if ($this->rtl) {
 
24742
                                        $this->x += $lspace;
 
24743
                                } else {
 
24744
                                        $this->x -= $lspace;
 
24745
                                }
 
24746
                                $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8);
 
24747
                                break;
 
24748
                        }
 
24749
                        case 'circle': {
 
24750
                                $r = $size / 6;
 
24751
                                $lspace += (2 * $r);
 
24752
                                if ($this->rtl) {
 
24753
                                        $this->x += $lspace;
 
24754
                                } else {
 
24755
                                        $this->x -= $lspace;
 
24756
                                }
 
24757
                                $prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor;
 
24758
                                $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
 
24759
                                $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
 
24760
                                $this->_out($prev_line_style); // restore line settings
 
24761
                                break;
 
24762
                        }
 
24763
                        case 'square': {
 
24764
                                $l = $size / 3;
 
24765
                                $lspace += $l;
 
24766
                                if ($this->rtl) {;
 
24767
                                        $this->x += $lspace;
 
24768
                                } else {
 
24769
                                        $this->x -= $lspace;
 
24770
                                }
 
24771
                                $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color);
 
24772
                                break;
 
24773
                        }
 
24774
                        case 'img': {
 
24775
                                // 1=>type, 2=>width, 3=>height, 4=>image.ext
 
24776
                                $lspace += $img[2];
 
24777
                                if ($this->rtl) {;
 
24778
                                        $this->x += $lspace;
 
24779
                                } else {
 
24780
                                        $this->x -= $lspace;
 
24781
                                }
 
24782
                                $imgtype = strtolower($img[1]);
 
24783
                                $prev_y = $this->y;
 
24784
                                switch ($imgtype) {
 
24785
                                        case 'svg': {
 
24786
                                                $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
 
24787
                                                break;
 
24788
                                        }
 
24789
                                        case 'ai':
 
24790
                                        case 'eps': {
 
24791
                                                $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
 
24792
                                                break;
 
24793
                                        }
 
24794
                                        default: {
 
24795
                                                $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);
 
24796
                                                break;
 
24797
                                        }
 
24798
                                }
 
24799
                                $this->y = $prev_y;
 
24800
                                break;
 
24801
                        }
 
24802
                        // ordered types
 
24803
                        // $this->listcount[$this->listnum];
 
24804
                        // $textitem
 
24805
                        case '1':
 
24806
                        case 'decimal': {
 
24807
                                $textitem = $this->listcount[$this->listnum];
 
24808
                                break;
 
24809
                        }
 
24810
                        case 'decimal-leading-zero': {
 
24811
                                $textitem = sprintf('%02d', $this->listcount[$this->listnum]);
 
24812
                                break;
 
24813
                        }
 
24814
                        case 'i':
 
24815
                        case 'lower-roman': {
 
24816
                                $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum]));
 
24817
                                break;
 
24818
                        }
 
24819
                        case 'I':
 
24820
                        case 'upper-roman': {
 
24821
                                $textitem = $this->intToRoman($this->listcount[$this->listnum]);
 
24822
                                break;
 
24823
                        }
 
24824
                        case 'a':
 
24825
                        case 'lower-alpha':
 
24826
                        case 'lower-latin': {
 
24827
                                $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
 
24828
                                break;
 
24829
                        }
 
24830
                        case 'A':
 
24831
                        case 'upper-alpha':
 
24832
                        case 'upper-latin': {
 
24833
                                $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
 
24834
                                break;
 
24835
                        }
 
24836
                        case 'lower-greek': {
 
24837
                                $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1);
 
24838
                                break;
 
24839
                        }
 
24840
                        /*
 
24841
                        // Types to be implemented (special handling)
 
24842
                        case 'hebrew': {
 
24843
                                break;
 
24844
                        }
 
24845
                        case 'armenian': {
 
24846
                                break;
 
24847
                        }
 
24848
                        case 'georgian': {
 
24849
                                break;
 
24850
                        }
 
24851
                        case 'cjk-ideographic': {
 
24852
                                break;
 
24853
                        }
 
24854
                        case 'hiragana': {
 
24855
                                break;
 
24856
                        }
 
24857
                        case 'katakana': {
 
24858
                                break;
 
24859
                        }
 
24860
                        case 'hiragana-iroha': {
 
24861
                                break;
 
24862
                        }
 
24863
                        case 'katakana-iroha': {
 
24864
                                break;
 
24865
                        }
 
24866
                        */
 
24867
                        default: {
 
24868
                                $textitem = $this->listcount[$this->listnum];
 
24869
                        }
 
24870
                }
 
24871
                if (!$this->empty_string($textitem)) {
 
24872
                        // Check whether we need a new page or new column
 
24873
                        $prev_y = $this->y;
 
24874
                        $h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
 
24875
                        if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) {
 
24876
                                $tmpx = $this->x;
 
24877
                        }
 
24878
                        // print ordered item
 
24879
                        if ($this->rtl) {
 
24880
                                $textitem = '.'.$textitem;
 
24881
                        } else {
 
24882
                                $textitem = $textitem.'.';
 
24883
                        }
 
24884
                        $lspace += $this->GetStringWidth($textitem);
 
24885
                        if ($this->rtl) {
 
24886
                                $this->x += $lspace;
 
24887
                        } else {
 
24888
                                $this->x -= $lspace;
 
24889
                        }
 
24890
                        $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
 
24891
                }
 
24892
                $this->x = $tmpx;
 
24893
                $this->lispacer = '^';
 
24894
                // restore colors
 
24895
                $this->SetFillColorArray($bgcolor);
 
24896
                $this->SetDrawColorArray($strokecolor);
 
24897
                $this->SettextColorArray($color);
 
24898
        }
 
24899
 
 
24900
        /**
 
24901
         * Returns current graphic variables as array.
 
24902
         * @return array of graphic variables
 
24903
         * @protected
 
24904
         * @since 4.2.010 (2008-11-14)
 
24905
         */
 
24906
        protected function getGraphicVars() {
 
24907
                $grapvars = array(
 
24908
                        'FontFamily' => $this->FontFamily,
 
24909
                        'FontStyle' => $this->FontStyle,
 
24910
                        'FontSizePt' => $this->FontSizePt,
 
24911
                        'rMargin' => $this->rMargin,
 
24912
                        'lMargin' => $this->lMargin,
 
24913
                        'cell_padding' => $this->cell_padding,
 
24914
                        'cell_margin' => $this->cell_margin,
 
24915
                        'LineWidth' => $this->LineWidth,
 
24916
                        'linestyleWidth' => $this->linestyleWidth,
 
24917
                        'linestyleCap' => $this->linestyleCap,
 
24918
                        'linestyleJoin' => $this->linestyleJoin,
 
24919
                        'linestyleDash' => $this->linestyleDash,
 
24920
                        'textrendermode' => $this->textrendermode,
 
24921
                        'textstrokewidth' => $this->textstrokewidth,
 
24922
                        'DrawColor' => $this->DrawColor,
 
24923
                        'FillColor' => $this->FillColor,
 
24924
                        'TextColor' => $this->TextColor,
 
24925
                        'ColorFlag' => $this->ColorFlag,
 
24926
                        'bgcolor' => $this->bgcolor,
 
24927
                        'fgcolor' => $this->fgcolor,
 
24928
                        'htmlvspace' => $this->htmlvspace,
 
24929
                        'listindent' => $this->listindent,
 
24930
                        'listindentlevel' => $this->listindentlevel,
 
24931
                        'listnum' => $this->listnum,
 
24932
                        'listordered' => $this->listordered,
 
24933
                        'listcount' => $this->listcount,
 
24934
                        'lispacer' => $this->lispacer,
 
24935
                        'cell_height_ratio' => $this->cell_height_ratio,
 
24936
                        'font_stretching' => $this->font_stretching,
 
24937
                        'font_spacing' => $this->font_spacing,
 
24938
                        // extended
 
24939
                        'lasth' => $this->lasth,
 
24940
                        'tMargin' => $this->tMargin,
 
24941
                        'bMargin' => $this->bMargin,
 
24942
                        'AutoPageBreak' => $this->AutoPageBreak,
 
24943
                        'PageBreakTrigger' => $this->PageBreakTrigger,
 
24944
                        'x' => $this->x,
 
24945
                        'y' => $this->y,
 
24946
                        'w' => $this->w,
 
24947
                        'h' => $this->h,
 
24948
                        'wPt' => $this->wPt,
 
24949
                        'hPt' => $this->hPt,
 
24950
                        'fwPt' => $this->fwPt,
 
24951
                        'fhPt' => $this->fhPt,
 
24952
                        'page' => $this->page,
 
24953
                        'current_column' => $this->current_column,
 
24954
                        'num_columns' => $this->num_columns
 
24955
                        );
 
24956
                return $grapvars;
 
24957
        }
 
24958
 
 
24959
        /**
 
24960
         * Set graphic variables.
 
24961
         * @param $gvars (array) array of graphic variablesto restore
 
24962
         * @param $extended (boolean) if true restore extended graphic variables
 
24963
         * @protected
 
24964
         * @since 4.2.010 (2008-11-14)
 
24965
         */
 
24966
        protected function setGraphicVars($gvars, $extended=false) {
 
24967
                $this->FontFamily = $gvars['FontFamily'];
 
24968
                $this->FontStyle = $gvars['FontStyle'];
 
24969
                $this->FontSizePt = $gvars['FontSizePt'];
 
24970
                $this->rMargin = $gvars['rMargin'];
 
24971
                $this->lMargin = $gvars['lMargin'];
 
24972
                $this->cell_padding = $gvars['cell_padding'];
 
24973
                $this->cell_margin = $gvars['cell_margin'];
 
24974
                $this->LineWidth = $gvars['LineWidth'];
 
24975
                $this->linestyleWidth = $gvars['linestyleWidth'];
 
24976
                $this->linestyleCap = $gvars['linestyleCap'];
 
24977
                $this->linestyleJoin = $gvars['linestyleJoin'];
 
24978
                $this->linestyleDash = $gvars['linestyleDash'];
 
24979
                $this->textrendermode = $gvars['textrendermode'];
 
24980
                $this->textstrokewidth = $gvars['textstrokewidth'];
 
24981
                $this->DrawColor = $gvars['DrawColor'];
 
24982
                $this->FillColor = $gvars['FillColor'];
 
24983
                $this->TextColor = $gvars['TextColor'];
 
24984
                $this->ColorFlag = $gvars['ColorFlag'];
 
24985
                $this->bgcolor = $gvars['bgcolor'];
 
24986
                $this->fgcolor = $gvars['fgcolor'];
 
24987
                $this->htmlvspace = $gvars['htmlvspace'];
 
24988
                $this->listindent = $gvars['listindent'];
 
24989
                $this->listindentlevel = $gvars['listindentlevel'];
 
24990
                $this->listnum = $gvars['listnum'];
 
24991
                $this->listordered = $gvars['listordered'];
 
24992
                $this->listcount = $gvars['listcount'];
 
24993
                $this->lispacer = $gvars['lispacer'];
 
24994
                $this->cell_height_ratio = $gvars['cell_height_ratio'];
 
24995
                $this->font_stretching = $gvars['font_stretching'];
 
24996
                $this->font_spacing = $gvars['font_spacing'];
 
24997
                if ($extended) {
 
24998
                        // restore extended values
 
24999
                        $this->lasth = $gvars['lasth'];
 
25000
                        $this->tMargin = $gvars['tMargin'];
 
25001
                        $this->bMargin = $gvars['bMargin'];
 
25002
                        $this->AutoPageBreak = $gvars['AutoPageBreak'];
 
25003
                        $this->PageBreakTrigger = $gvars['PageBreakTrigger'];
 
25004
                        $this->x = $gvars['x'];
 
25005
                        $this->y = $gvars['y'];
 
25006
                        $this->w = $gvars['w'];
 
25007
                        $this->h = $gvars['h'];
 
25008
                        $this->wPt = $gvars['wPt'];
 
25009
                        $this->hPt = $gvars['hPt'];
 
25010
                        $this->fwPt = $gvars['fwPt'];
 
25011
                        $this->fhPt = $gvars['fhPt'];
 
25012
                        $this->page = $gvars['page'];
 
25013
                        $this->current_column = $gvars['current_column'];
 
25014
                        $this->num_columns = $gvars['num_columns'];
 
25015
                }
 
25016
                $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
 
25017
                if (!$this->empty_string($this->FontFamily)) {
 
25018
                        $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
 
25019
                }
 
25020
        }
 
25021
 
 
25022
        /**
 
25023
         * Returns a temporary filename for caching object on filesystem.
 
25024
         * @param $name (string) prefix to add to filename
 
25025
         * @return string filename.
 
25026
         * @since 4.5.000 (2008-12-31)
 
25027
         * @protected
 
25028
         */
 
25029
        protected function getObjFilename($name) {
 
25030
                return tempnam(K_PATH_CACHE, $name.'_');
 
25031
        }
 
25032
 
 
25033
        /**
 
25034
         * Writes data to a temporary file on filesystem.
 
25035
         * @param $filename (string) file name
 
25036
         * @param $data (mixed) data to write on file
 
25037
         * @param $append (boolean) if true append data, false replace.
 
25038
         * @since 4.5.000 (2008-12-31)
 
25039
         * @protected
 
25040
         */
 
25041
        protected function writeDiskCache($filename, $data, $append=false) {
 
25042
                if ($append) {
 
25043
                        $fmode = 'ab+';
 
25044
                } else {
 
25045
                        $fmode = 'wb+';
 
25046
                }
 
25047
                $f = @fopen($filename, $fmode);
 
25048
                if (!$f) {
 
25049
                        $this->Error('Unable to write cache file: '.$filename);
 
25050
                } else {
 
25051
                        fwrite($f, $data);
 
25052
                        fclose($f);
 
25053
                }
 
25054
                // update file length (needed for transactions)
 
25055
                if (!isset($this->cache_file_length['_'.$filename])) {
 
25056
                        $this->cache_file_length['_'.$filename] = strlen($data);
 
25057
                } else {
 
25058
                        $this->cache_file_length['_'.$filename] += strlen($data);
 
25059
                }
 
25060
        }
 
25061
 
 
25062
        /**
 
25063
         * Read data from a temporary file on filesystem.
 
25064
         * @param $filename (string) file name
 
25065
         * @return mixed retrieved data
 
25066
         * @since 4.5.000 (2008-12-31)
 
25067
         * @protected
 
25068
         */
 
25069
        protected function readDiskCache($filename) {
 
25070
                return file_get_contents($filename);
 
25071
        }
 
25072
 
 
25073
        /**
 
25074
         * Set buffer content (always append data).
 
25075
         * @param $data (string) data
 
25076
         * @protected
 
25077
         * @since 4.5.000 (2009-01-02)
 
25078
         */
 
25079
        protected function setBuffer($data) {
 
25080
                $this->bufferlen += strlen($data);
 
25081
                if ($this->diskcache) {
 
25082
                        if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
 
25083
                                $this->buffer = $this->getObjFilename('buffer');
 
25084
                        }
 
25085
                        $this->writeDiskCache($this->buffer, $data, true);
 
25086
                } else {
 
25087
                        $this->buffer .= $data;
 
25088
                }
 
25089
        }
 
25090
 
 
25091
        /**
 
25092
         * Replace the buffer content
 
25093
         * @param $data (string) data
 
25094
         * @protected
 
25095
         * @since 5.5.000 (2010-06-22)
 
25096
         */
 
25097
        protected function replaceBuffer($data) {
 
25098
                $this->bufferlen = strlen($data);
 
25099
                if ($this->diskcache) {
 
25100
                        if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
 
25101
                                $this->buffer = $this->getObjFilename('buffer');
 
25102
                        }
 
25103
                        $this->writeDiskCache($this->buffer, $data, false);
 
25104
                } else {
 
25105
                        $this->buffer = $data;
 
25106
                }
 
25107
        }
 
25108
 
 
25109
        /**
 
25110
         * Get buffer content.
 
25111
         * @return string buffer content
 
25112
         * @protected
 
25113
         * @since 4.5.000 (2009-01-02)
 
25114
         */
 
25115
        protected function getBuffer() {
 
25116
                if ($this->diskcache) {
 
25117
                        return $this->readDiskCache($this->buffer);
 
25118
                } else {
 
25119
                        return $this->buffer;
 
25120
                }
 
25121
        }
 
25122
 
 
25123
        /**
 
25124
         * Set page buffer content.
 
25125
         * @param $page (int) page number
 
25126
         * @param $data (string) page data
 
25127
         * @param $append (boolean) if true append data, false replace.
 
25128
         * @protected
 
25129
         * @since 4.5.000 (2008-12-31)
 
25130
         */
 
25131
        protected function setPageBuffer($page, $data, $append=false) {
 
25132
                if ($this->diskcache) {
 
25133
                        if (!isset($this->pages[$page])) {
 
25134
                                $this->pages[$page] = $this->getObjFilename('page'.$page);
 
25135
                        }
 
25136
                        $this->writeDiskCache($this->pages[$page], $data, $append);
 
25137
                } else {
 
25138
                        if ($append) {
 
25139
                                $this->pages[$page] .= $data;
 
25140
                        } else {
 
25141
                                $this->pages[$page] = $data;
 
25142
                        }
 
25143
                }
 
25144
                if ($append AND isset($this->pagelen[$page])) {
 
25145
                        $this->pagelen[$page] += strlen($data);
 
25146
                } else {
 
25147
                        $this->pagelen[$page] = strlen($data);
 
25148
                }
 
25149
        }
 
25150
 
 
25151
        /**
 
25152
         * Get page buffer content.
 
25153
         * @param $page (int) page number
 
25154
         * @return string page buffer content or false in case of error
 
25155
         * @protected
 
25156
         * @since 4.5.000 (2008-12-31)
 
25157
         */
 
25158
        protected function getPageBuffer($page) {
 
25159
                if ($this->diskcache) {
 
25160
                        return $this->readDiskCache($this->pages[$page]);
 
25161
                } elseif (isset($this->pages[$page])) {
 
25162
                        return $this->pages[$page];
 
25163
                }
 
25164
                return false;
 
25165
        }
 
25166
 
 
25167
        /**
 
25168
         * Set image buffer content.
 
25169
         * @param $image (string) image key
 
25170
         * @param $data (array) image data
 
25171
         * @protected
 
25172
         * @since 4.5.000 (2008-12-31)
 
25173
         */
 
25174
        protected function setImageBuffer($image, $data) {
 
25175
                if ($this->diskcache) {
 
25176
                        if (!isset($this->images[$image])) {
 
25177
                                $this->images[$image] = $this->getObjFilename('image'.$image);
 
25178
                        }
 
25179
                        $this->writeDiskCache($this->images[$image], serialize($data));
 
25180
                } else {
 
25181
                        $this->images[$image] = $data;
 
25182
                }
 
25183
                if (!in_array($image, $this->imagekeys)) {
 
25184
                        $this->imagekeys[] = $image;
 
25185
                        ++$this->numimages;
 
25186
                }
 
25187
        }
 
25188
 
 
25189
        /**
 
25190
         * Set image buffer content for a specified sub-key.
 
25191
         * @param $image (string) image key
 
25192
         * @param $key (string) image sub-key
 
25193
         * @param $data (array) image data
 
25194
         * @protected
 
25195
         * @since 4.5.000 (2008-12-31)
 
25196
         */
 
25197
        protected function setImageSubBuffer($image, $key, $data) {
 
25198
                if (!isset($this->images[$image])) {
 
25199
                        $this->setImageBuffer($image, array());
 
25200
                }
 
25201
                if ($this->diskcache) {
 
25202
                        $tmpimg = $this->getImageBuffer($image);
 
25203
                        $tmpimg[$key] = $data;
 
25204
                        $this->writeDiskCache($this->images[$image], serialize($tmpimg));
 
25205
                } else {
 
25206
                        $this->images[$image][$key] = $data;
 
25207
                }
 
25208
        }
 
25209
 
 
25210
        /**
 
25211
         * Get image buffer content.
 
25212
         * @param $image (string) image key
 
25213
         * @return string image buffer content or false in case of error
 
25214
         * @protected
 
25215
         * @since 4.5.000 (2008-12-31)
 
25216
         */
 
25217
        protected function getImageBuffer($image) {
 
25218
                if ($this->diskcache AND isset($this->images[$image])) {
 
25219
                        return unserialize($this->readDiskCache($this->images[$image]));
 
25220
                } elseif (isset($this->images[$image])) {
 
25221
                        return $this->images[$image];
 
25222
                }
 
25223
                return false;
 
25224
        }
 
25225
 
 
25226
        /**
 
25227
         * Set font buffer content.
 
25228
         * @param $font (string) font key
 
25229
         * @param $data (array) font data
 
25230
         * @protected
 
25231
         * @since 4.5.000 (2009-01-02)
 
25232
         */
 
25233
        protected function setFontBuffer($font, $data) {
 
25234
                if ($this->diskcache) {
 
25235
                        if (!isset($this->fonts[$font])) {
 
25236
                                $this->fonts[$font] = $this->getObjFilename('font');
 
25237
                        }
 
25238
                        $this->writeDiskCache($this->fonts[$font], serialize($data));
 
25239
                } else {
 
25240
                        $this->fonts[$font] = $data;
 
25241
                }
 
25242
                if (!in_array($font, $this->fontkeys)) {
 
25243
                        $this->fontkeys[] = $font;
 
25244
                        // store object ID for current font
 
25245
                        ++$this->n;
 
25246
                        $this->font_obj_ids[$font] = $this->n;
 
25247
                        $this->setFontSubBuffer($font, 'n', $this->n);
 
25248
                }
 
25249
        }
 
25250
 
 
25251
        /**
 
25252
         * Set font buffer content.
 
25253
         * @param $font (string) font key
 
25254
         * @param $key (string) font sub-key
 
25255
         * @param $data (array) font data
 
25256
         * @protected
 
25257
         * @since 4.5.000 (2009-01-02)
 
25258
         */
 
25259
        protected function setFontSubBuffer($font, $key, $data) {
 
25260
                if (!isset($this->fonts[$font])) {
 
25261
                        $this->setFontBuffer($font, array());
 
25262
                }
 
25263
                if ($this->diskcache) {
 
25264
                        $tmpfont = $this->getFontBuffer($font);
 
25265
                        $tmpfont[$key] = $data;
 
25266
                        $this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
 
25267
                } else {
 
25268
                        $this->fonts[$font][$key] = $data;
 
25269
                }
 
25270
        }
 
25271
 
 
25272
        /**
 
25273
         * Get font buffer content.
 
25274
         * @param $font (string) font key
 
25275
         * @return string font buffer content or false in case of error
 
25276
         * @protected
 
25277
         * @since 4.5.000 (2009-01-02)
 
25278
         */
 
25279
        protected function getFontBuffer($font) {
 
25280
                if ($this->diskcache AND isset($this->fonts[$font])) {
 
25281
                        return unserialize($this->readDiskCache($this->fonts[$font]));
 
25282
                } elseif (isset($this->fonts[$font])) {
 
25283
                        return $this->fonts[$font];
 
25284
                }
 
25285
                return false;
 
25286
        }
 
25287
 
 
25288
        /**
 
25289
         * Move a page to a previous position.
 
25290
         * @param $frompage (int) number of the source page
 
25291
         * @param $topage (int) number of the destination page (must be less than $frompage)
 
25292
         * @return true in case of success, false in case of error.
 
25293
         * @public
 
25294
         * @since 4.5.000 (2009-01-02)
 
25295
         */
 
25296
        public function movePage($frompage, $topage) {
 
25297
                if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
 
25298
                        return false;
 
25299
                }
 
25300
                if ($frompage == $this->page) {
 
25301
                        // close the page before moving it
 
25302
                        $this->endPage();
 
25303
                }
 
25304
                // move all page-related states
 
25305
                $tmppage = $this->getPageBuffer($frompage);
 
25306
                $tmppagedim = $this->pagedim[$frompage];
 
25307
                $tmppagelen = $this->pagelen[$frompage];
 
25308
                $tmpintmrk = $this->intmrk[$frompage];
 
25309
                $tmpbordermrk = $this->bordermrk[$frompage];
 
25310
                $tmpcntmrk = $this->cntmrk[$frompage];
 
25311
                if (isset($this->footerpos[$frompage])) {
 
25312
                        $tmpfooterpos = $this->footerpos[$frompage];
 
25313
                }
 
25314
                if (isset($this->footerlen[$frompage])) {
 
25315
                        $tmpfooterlen = $this->footerlen[$frompage];
 
25316
                }
 
25317
                if (isset($this->transfmrk[$frompage])) {
 
25318
                        $tmptransfmrk = $this->transfmrk[$frompage];
 
25319
                }
 
25320
                if (isset($this->PageAnnots[$frompage])) {
 
25321
                        $tmpannots = $this->PageAnnots[$frompage];
 
25322
                }
 
25323
                if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
 
25324
                        for ($i = $frompage; $i > $topage; --$i) {
 
25325
                                if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) {
 
25326
                                        --$this->pagegroups[$this->newpagegroup[$i]];
 
25327
                                        break;
 
25328
                                }
 
25329
                        }
 
25330
                        for ($i = $topage; $i > 0; --$i) {
 
25331
                                if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) {
 
25332
                                        ++$this->pagegroups[$this->newpagegroup[$i]];
 
25333
                                        break;
 
25334
                                }
 
25335
                        }
 
25336
                }
 
25337
                for ($i = $frompage; $i > $topage; --$i) {
 
25338
                        $j = $i - 1;
 
25339
                        // shift pages down
 
25340
                        $this->setPageBuffer($i, $this->getPageBuffer($j));
 
25341
                        $this->pagedim[$i] = $this->pagedim[$j];
 
25342
                        $this->pagelen[$i] = $this->pagelen[$j];
 
25343
                        $this->intmrk[$i] = $this->intmrk[$j];
 
25344
                        $this->bordermrk[$i] = $this->bordermrk[$j];
 
25345
                        $this->cntmrk[$i] = $this->cntmrk[$j];
 
25346
                        if (isset($this->footerpos[$j])) {
 
25347
                                $this->footerpos[$i] = $this->footerpos[$j];
 
25348
                        } elseif (isset($this->footerpos[$i])) {
 
25349
                                unset($this->footerpos[$i]);
 
25350
                        }
 
25351
                        if (isset($this->footerlen[$j])) {
 
25352
                                $this->footerlen[$i] = $this->footerlen[$j];
 
25353
                        } elseif (isset($this->footerlen[$i])) {
 
25354
                                unset($this->footerlen[$i]);
 
25355
                        }
 
25356
                        if (isset($this->transfmrk[$j])) {
 
25357
                                $this->transfmrk[$i] = $this->transfmrk[$j];
 
25358
                        } elseif (isset($this->transfmrk[$i])) {
 
25359
                                unset($this->transfmrk[$i]);
 
25360
                        }
 
25361
                        if (isset($this->PageAnnots[$j])) {
 
25362
                                $this->PageAnnots[$i] = $this->PageAnnots[$j];
 
25363
                        } elseif (isset($this->PageAnnots[$i])) {
 
25364
                                unset($this->PageAnnots[$i]);
 
25365
                        }
 
25366
                        if (isset($this->newpagegroup[$j])) {
 
25367
                                $this->newpagegroup[$i] = $this->newpagegroup[$j];
 
25368
                                unset($this->newpagegroup[$j]);
 
25369
                        }
 
25370
                        if ($this->currpagegroup == $j) {
 
25371
                                $this->currpagegroup = $i;
 
25372
                        }
 
25373
                }
 
25374
                $this->setPageBuffer($topage, $tmppage);
 
25375
                $this->pagedim[$topage] = $tmppagedim;
 
25376
                $this->pagelen[$topage] = $tmppagelen;
 
25377
                $this->intmrk[$topage] = $tmpintmrk;
 
25378
                $this->bordermrk[$topage] = $tmpbordermrk;
 
25379
                $this->cntmrk[$topage] = $tmpcntmrk;
 
25380
                if (isset($tmpfooterpos)) {
 
25381
                        $this->footerpos[$topage] = $tmpfooterpos;
 
25382
                } elseif (isset($this->footerpos[$topage])) {
 
25383
                        unset($this->footerpos[$topage]);
 
25384
                }
 
25385
                if (isset($tmpfooterlen)) {
 
25386
                        $this->footerlen[$topage] = $tmpfooterlen;
 
25387
                } elseif (isset($this->footerlen[$topage])) {
 
25388
                        unset($this->footerlen[$topage]);
 
25389
                }
 
25390
                if (isset($tmptransfmrk)) {
 
25391
                        $this->transfmrk[$topage] = $tmptransfmrk;
 
25392
                } elseif (isset($this->transfmrk[$topage])) {
 
25393
                        unset($this->transfmrk[$topage]);
 
25394
                }
 
25395
                if (isset($tmpannots)) {
 
25396
                        $this->PageAnnots[$topage] = $tmpannots;
 
25397
                } elseif (isset($this->PageAnnots[$topage])) {
 
25398
                        unset($this->PageAnnots[$topage]);
 
25399
                }
 
25400
                // adjust outlines
 
25401
                $tmpoutlines = $this->outlines;
 
25402
                foreach ($tmpoutlines as $key => $outline) {
 
25403
                        if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
 
25404
                                $this->outlines[$key]['p'] = ($outline['p'] + 1);
 
25405
                        } elseif ($outline['p'] == $frompage) {
 
25406
                                $this->outlines[$key]['p'] = $topage;
 
25407
                        }
 
25408
                }
 
25409
                // adjust dests
 
25410
                $tmpdests = $this->dests;
 
25411
                foreach ($tmpdests as $key => $dest) {
 
25412
                        if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) {
 
25413
                                $this->dests[$key]['p'] = ($dest['p'] + 1);
 
25414
                        } elseif ($dest['p'] == $frompage) {
 
25415
                                $this->dests[$key]['p'] = $topage;
 
25416
                        }
 
25417
                }
 
25418
                // adjust links
 
25419
                $tmplinks = $this->links;
 
25420
                foreach ($tmplinks as $key => $link) {
 
25421
                        if (($link[0] >= $topage) AND ($link[0] < $frompage)) {
 
25422
                                $this->links[$key][0] = ($link[0] + 1);
 
25423
                        } elseif ($link[0] == $frompage) {
 
25424
                                $this->links[$key][0] = $topage;
 
25425
                        }
 
25426
                }
 
25427
                // adjust javascript
 
25428
                $tmpjavascript = $this->javascript;
 
25429
                global $jfrompage, $jtopage;
 
25430
                $jfrompage = $frompage;
 
25431
                $jtopage = $topage;
 
25432
                $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
 
25433
                        create_function('$matches', 'global $jfrompage, $jtopage;
 
25434
                        $pagenum = intval($matches[3]) + 1;
 
25435
                        if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
 
25436
                                $newpage = ($pagenum + 1);
 
25437
                        } elseif ($pagenum == $jfrompage) {
 
25438
                                $newpage = $jtopage;
 
25439
                        } else {
 
25440
                                $newpage = $pagenum;
 
25441
                        }
 
25442
                        --$newpage;
 
25443
                        return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
 
25444
                // return to last page
 
25445
                $this->lastPage(true);
 
25446
                return true;
 
25447
        }
 
25448
 
 
25449
        /**
 
25450
         * Remove the specified page.
 
25451
         * @param $page (int) page to remove
 
25452
         * @return true in case of success, false in case of error.
 
25453
         * @public
 
25454
         * @since 4.6.004 (2009-04-23)
 
25455
         */
 
25456
        public function deletePage($page) {
 
25457
                if (($page < 1) OR ($page > $this->numpages)) {
 
25458
                        return false;
 
25459
                }
 
25460
                // delete current page
 
25461
                unset($this->pages[$page]);
 
25462
                unset($this->pagedim[$page]);
 
25463
                unset($this->pagelen[$page]);
 
25464
                unset($this->intmrk[$page]);
 
25465
                unset($this->bordermrk[$page]);
 
25466
                unset($this->cntmrk[$page]);
 
25467
                if (isset($this->footerpos[$page])) {
 
25468
                        unset($this->footerpos[$page]);
 
25469
                }
 
25470
                if (isset($this->footerlen[$page])) {
 
25471
                        unset($this->footerlen[$page]);
 
25472
                }
 
25473
                if (isset($this->transfmrk[$page])) {
 
25474
                        unset($this->transfmrk[$page]);
 
25475
                }
 
25476
                if (isset($this->PageAnnots[$page])) {
 
25477
                        unset($this->PageAnnots[$page]);
 
25478
                }
 
25479
                if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
 
25480
                        for ($i = $page; $i > 0; --$i) {
 
25481
                                if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) {
 
25482
                                        --$this->pagegroups[$this->newpagegroup[$i]];
 
25483
                                        break;
 
25484
                                }
 
25485
                        }
 
25486
                }
 
25487
                if (isset($this->pageopen[$page])) {
 
25488
                        unset($this->pageopen[$page]);
 
25489
                }
 
25490
                if ($page < $this->numpages) {
 
25491
                        // update remaining pages
 
25492
                        for ($i = $page; $i < $this->numpages; ++$i) {
 
25493
                                $j = $i + 1;
 
25494
                                // shift pages
 
25495
                                $this->setPageBuffer($i, $this->getPageBuffer($j));
 
25496
                                $this->pagedim[$i] = $this->pagedim[$j];
 
25497
                                $this->pagelen[$i] = $this->pagelen[$j];
 
25498
                                $this->intmrk[$i] = $this->intmrk[$j];
 
25499
                                $this->bordermrk[$i] = $this->bordermrk[$j];
 
25500
                                $this->cntmrk[$i] = $this->cntmrk[$j];
 
25501
                                if (isset($this->footerpos[$j])) {
 
25502
                                        $this->footerpos[$i] = $this->footerpos[$j];
 
25503
                                } elseif (isset($this->footerpos[$i])) {
 
25504
                                        unset($this->footerpos[$i]);
 
25505
                                }
 
25506
                                if (isset($this->footerlen[$j])) {
 
25507
                                        $this->footerlen[$i] = $this->footerlen[$j];
 
25508
                                } elseif (isset($this->footerlen[$i])) {
 
25509
                                        unset($this->footerlen[$i]);
 
25510
                                }
 
25511
                                if (isset($this->transfmrk[$j])) {
 
25512
                                        $this->transfmrk[$i] = $this->transfmrk[$j];
 
25513
                                } elseif (isset($this->transfmrk[$i])) {
 
25514
                                        unset($this->transfmrk[$i]);
 
25515
                                }
 
25516
                                if (isset($this->PageAnnots[$j])) {
 
25517
                                        $this->PageAnnots[$i] = $this->PageAnnots[$j];
 
25518
                                } elseif (isset($this->PageAnnots[$i])) {
 
25519
                                        unset($this->PageAnnots[$i]);
 
25520
                                }
 
25521
                                if (isset($this->newpagegroup[$j])) {
 
25522
                                        $this->newpagegroup[$i] = $this->newpagegroup[$j];
 
25523
                                        unset($this->newpagegroup[$j]);
 
25524
                                }
 
25525
                                if ($this->currpagegroup == $j) {
 
25526
                                        $this->currpagegroup = $i;
 
25527
                                }
 
25528
                                if (isset($this->pageopen[$j])) {
 
25529
                                        $this->pageopen[$i] = $this->pageopen[$j];
 
25530
                                } elseif (isset($this->pageopen[$i])) {
 
25531
                                        unset($this->pageopen[$i]);
 
25532
                                }
 
25533
                        }
 
25534
                        // remove last page
 
25535
                        unset($this->pages[$this->numpages]);
 
25536
                        unset($this->pagedim[$this->numpages]);
 
25537
                        unset($this->pagelen[$this->numpages]);
 
25538
                        unset($this->intmrk[$this->numpages]);
 
25539
                        unset($this->bordermrk[$this->numpages]);
 
25540
                        unset($this->cntmrk[$this->numpages]);
 
25541
                        if (isset($this->footerpos[$this->numpages])) {
 
25542
                                unset($this->footerpos[$this->numpages]);
 
25543
                        }
 
25544
                        if (isset($this->footerlen[$this->numpages])) {
 
25545
                                unset($this->footerlen[$this->numpages]);
 
25546
                        }
 
25547
                        if (isset($this->transfmrk[$this->numpages])) {
 
25548
                                unset($this->transfmrk[$this->numpages]);
 
25549
                        }
 
25550
                        if (isset($this->PageAnnots[$this->numpages])) {
 
25551
                                unset($this->PageAnnots[$this->numpages]);
 
25552
                        }
 
25553
                        if (isset($this->newpagegroup[$this->numpages])) {
 
25554
                                unset($this->newpagegroup[$this->numpages]);
 
25555
                        }
 
25556
                        if ($this->currpagegroup == $this->numpages) {
 
25557
                                $this->currpagegroup = ($this->numpages - 1);
 
25558
                        }
 
25559
                        if (isset($this->pagegroups[$this->numpages])) {
 
25560
                                unset($this->pagegroups[$this->numpages]);
 
25561
                        }
 
25562
                        if (isset($this->pageopen[$this->numpages])) {
 
25563
                                unset($this->pageopen[$this->numpages]);
 
25564
                        }
 
25565
                }
 
25566
                --$this->numpages;
 
25567
                $this->page = $this->numpages;
 
25568
                // adjust outlines
 
25569
                $tmpoutlines = $this->outlines;
 
25570
                foreach ($tmpoutlines as $key => $outline) {
 
25571
                        if ($outline['p'] > $page) {
 
25572
                                $this->outlines[$key]['p'] = $outline['p'] - 1;
 
25573
                        } elseif ($outline['p'] == $page) {
 
25574
                                unset($this->outlines[$key]);
 
25575
                        }
 
25576
                }
 
25577
                // adjust dests
 
25578
                $tmpdests = $this->dests;
 
25579
                foreach ($tmpdests as $key => $dest) {
 
25580
                        if ($dest['p'] > $page) {
 
25581
                                $this->dests[$key]['p'] = $dest['p'] - 1;
 
25582
                        } elseif ($dest['p'] == $page) {
 
25583
                                unset($this->dests[$key]);
 
25584
                        }
 
25585
                }
 
25586
                // adjust links
 
25587
                $tmplinks = $this->links;
 
25588
                foreach ($tmplinks as $key => $link) {
 
25589
                        if ($link[0] > $page) {
 
25590
                                $this->links[$key][0] = $link[0] - 1;
 
25591
                        } elseif ($link[0] == $page) {
 
25592
                                unset($this->links[$key]);
 
25593
                        }
 
25594
                }
 
25595
                // adjust javascript
 
25596
                $tmpjavascript = $this->javascript;
 
25597
                global $jpage;
 
25598
                $jpage = $page;
 
25599
                $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
 
25600
                        create_function('$matches', 'global $jpage;
 
25601
                        $pagenum = intval($matches[3]) + 1;
 
25602
                        if ($pagenum >= $jpage) {
 
25603
                                $newpage = ($pagenum - 1);
 
25604
                        } elseif ($pagenum == $jpage) {
 
25605
                                $newpage = 1;
 
25606
                        } else {
 
25607
                                $newpage = $pagenum;
 
25608
                        }
 
25609
                        --$newpage;
 
25610
                        return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
 
25611
                // return to last page
 
25612
                $this->lastPage(true);
 
25613
                return true;
 
25614
        }
 
25615
 
 
25616
        /**
 
25617
         * Clone the specified page to a new page.
 
25618
         * @param $page (int) number of page to copy (0 = current page)
 
25619
         * @return true in case of success, false in case of error.
 
25620
         * @public
 
25621
         * @since 4.9.015 (2010-04-20)
 
25622
         */
 
25623
        public function copyPage($page=0) {
 
25624
                if ($page == 0) {
 
25625
                        // default value
 
25626
                        $page = $this->page;
 
25627
                }
 
25628
                if (($page < 1) OR ($page > $this->numpages)) {
 
25629
                        return false;
 
25630
                }
 
25631
                // close the last page
 
25632
                $this->endPage();
 
25633
                // copy all page-related states
 
25634
                ++$this->numpages;
 
25635
                $this->page = $this->numpages;
 
25636
                $this->setPageBuffer($this->page, $this->getPageBuffer($page));
 
25637
                $this->pagedim[$this->page] = $this->pagedim[$page];
 
25638
                $this->pagelen[$this->page] = $this->pagelen[$page];
 
25639
                $this->intmrk[$this->page] = $this->intmrk[$page];
 
25640
                $this->bordermrk[$this->page] = $this->bordermrk[$page];
 
25641
                $this->cntmrk[$this->page] = $this->cntmrk[$page];
 
25642
                $this->pageopen[$this->page] = false;
 
25643
                if (isset($this->footerpos[$page])) {
 
25644
                        $this->footerpos[$this->page] = $this->footerpos[$page];
 
25645
                }
 
25646
                if (isset($this->footerlen[$page])) {
 
25647
                        $this->footerlen[$this->page] = $this->footerlen[$page];
 
25648
                }
 
25649
                if (isset($this->transfmrk[$page])) {
 
25650
                        $this->transfmrk[$this->page] = $this->transfmrk[$page];
 
25651
                }
 
25652
                if (isset($this->PageAnnots[$page])) {
 
25653
                        $this->PageAnnots[$this->page] = $this->PageAnnots[$page];
 
25654
                }
 
25655
                if (isset($this->newpagegroup[$page])) {
 
25656
                        // start a new group
 
25657
                        $this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1;
 
25658
                        $this->currpagegroup = $this->newpagegroup[$this->page];
 
25659
                        $this->pagegroups[$this->currpagegroup] = 1;
 
25660
                } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
 
25661
                        ++$this->pagegroups[$this->currpagegroup];
 
25662
                }
 
25663
                // copy outlines
 
25664
                $tmpoutlines = $this->outlines;
 
25665
                foreach ($tmpoutlines as $key => $outline) {
 
25666
                        if ($outline['p'] == $page) {
 
25667
                                $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'y' => $outline['y'], 'p' => $this->page, 's' => $outline['s'], 'c' => $outline['c']);
 
25668
                        }
 
25669
                }
 
25670
                // copy links
 
25671
                $tmplinks = $this->links;
 
25672
                foreach ($tmplinks as $key => $link) {
 
25673
                        if ($link[0] == $page) {
 
25674
                                $this->links[] = array($this->page, $link[1]);
 
25675
                        }
 
25676
                }
 
25677
                // return to last page
 
25678
                $this->lastPage(true);
 
25679
                return true;
 
25680
        }
 
25681
 
 
25682
        /**
 
25683
         * Output a Table of Content Index (TOC).
 
25684
         * This method must be called after all Bookmarks were set.
 
25685
         * Before calling this method you have to open the page using the addTOCPage() method.
 
25686
         * After calling this method you have to call endTOCPage() to close the TOC page.
 
25687
         * You can override this method to achieve different styles.
 
25688
         * @param $page (int) page number where this TOC should be inserted (leave empty for current page).
 
25689
         * @param $numbersfont (string) set the font for page numbers (please use monospaced font for better alignment).
 
25690
         * @param $filler (string) string used to fill the space between text and page number.
 
25691
         * @param $toc_name (string) name to use for TOC bookmark.
 
25692
         * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
 
25693
         * @param $color (array) RGB color array for bookmark title (values from 0 to 255).
 
25694
         * @public
 
25695
         * @author Nicola Asuni
 
25696
         * @since 4.5.000 (2009-01-02)
 
25697
         * @see addTOCPage(), endTOCPage(), addHTMLTOC()
 
25698
         */
 
25699
        public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
 
25700
                $fontsize = $this->FontSizePt;
 
25701
                $fontfamily = $this->FontFamily;
 
25702
                $fontstyle = $this->FontStyle;
 
25703
                $w = $this->w - $this->lMargin - $this->rMargin;
 
25704
                $spacer = $this->GetStringWidth(chr(32)) * 4;
 
25705
                $page_first = $this->getPage();
 
25706
                $lmargin = $this->lMargin;
 
25707
                $rmargin = $this->rMargin;
 
25708
                $x_start = $this->GetX();
 
25709
                $current_page = $this->page;
 
25710
                $current_column = $this->current_column;
 
25711
                if ($this->empty_string($numbersfont)) {
 
25712
                        $numbersfont = $this->default_monospaced_font;
 
25713
                }
 
25714
                if ($this->empty_string($filler)) {
 
25715
                        $filler = ' ';
 
25716
                }
 
25717
                if ($this->empty_string($page)) {
 
25718
                        $gap = ' ';
 
25719
                } else {
 
25720
                        $gap = '';
 
25721
                        if ($page < 1) {
 
25722
                                $page = 1;
 
25723
                        }
 
25724
                }
 
25725
                $this->SetFont($numbersfont, $fontstyle, $fontsize);
 
25726
                $numwidth = $this->GetStringWidth('00000');
 
25727
                $maxpage = 0; //used for pages on attached documents
 
25728
                foreach ($this->outlines as $key => $outline) {
 
25729
                        // check for extra pages (used for attachments)
 
25730
                        if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) {
 
25731
                                $outline['p'] += ($this->page - $page_first);
 
25732
                        }
 
25733
                        if ($this->rtl) {
 
25734
                                $aligntext = 'R';
 
25735
                                $alignnum = 'L';
 
25736
                        } else {
 
25737
                                $aligntext = 'L';
 
25738
                                $alignnum = 'R';
 
25739
                        }
 
25740
                        if ($outline['l'] == 0) {
 
25741
                                $this->SetFont($fontfamily, $fontstyle.'B', $fontsize);
 
25742
                        } else {
 
25743
                                $this->SetFont($fontfamily, $fontstyle, $fontsize - $outline['l']);
 
25744
                        }
 
25745
                        // check for page break
 
25746
                        $this->checkPageBreak((2 * $this->FontSize * $this->cell_height_ratio));
 
25747
                        // set margins and X position
 
25748
                        if (($this->page == $current_page) AND ($this->current_column == $current_column)) {
 
25749
                                $this->lMargin = $lmargin;
 
25750
                                $this->rMargin = $rmargin;
 
25751
                        } else {
 
25752
                                if ($this->current_column != $current_column) {
 
25753
                                        if ($this->rtl) {
 
25754
                                                $x_start = $this->w - $this->columns[$this->current_column]['x'];
 
25755
                                        } else {
 
25756
                                                $x_start = $this->columns[$this->current_column]['x'];
 
25757
                                        }
 
25758
                                }
 
25759
                                $lmargin = $this->lMargin;
 
25760
                                $rmargin = $this->rMargin;
 
25761
                                $current_page = $this->page;
 
25762
                                $current_column = $this->current_column;
 
25763
                        }
 
25764
                        $this->SetX($x_start);
 
25765
                        $indent = ($spacer * $outline['l']);
 
25766
                        if ($this->rtl) {
 
25767
                                $this->x -= $indent;
 
25768
                                $this->rMargin = $this->w - $this->x;
 
25769
                        } else {
 
25770
                                $this->x += $indent;
 
25771
                                $this->lMargin = $this->x;
 
25772
                        }
 
25773
                        $link = $this->AddLink();
 
25774
                        $this->SetLink($link, $outline['y'], $outline['p']);
 
25775
                        // write the text
 
25776
                        if ($this->rtl) {
 
25777
                                $txt = ' '.$outline['t'];
 
25778
                        } else {
 
25779
                                $txt = $outline['t'].' ';
 
25780
                        }
 
25781
                        $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, '');
 
25782
                        if ($this->rtl) {
 
25783
                                $tw = $this->x - $this->lMargin;
 
25784
                        } else {
 
25785
                                $tw = $this->w - $this->rMargin - $this->x;
 
25786
                        }
 
25787
                        $this->SetFont($numbersfont, $fontstyle, $fontsize);
 
25788
                        if ($this->empty_string($page)) {
 
25789
                                $pagenum = $outline['p'];
 
25790
                        } else {
 
25791
                                // placemark to be replaced with the correct number
 
25792
                                $pagenum = '{#'.($outline['p']).'}';
 
25793
                                if ($this->isUnicodeFont()) {
 
25794
                                        $pagenum = '{'.$pagenum.'}';
 
25795
                                }
 
25796
                                $maxpage = max($maxpage, $outline['p']);
 
25797
                        }
 
25798
                        $fw = ($tw - $this->GetStringWidth($pagenum.$filler));
 
25799
                        $numfills = floor($fw / $this->GetStringWidth($filler));
 
25800
                        if ($numfills > 0) {
 
25801
                                $rowfill = str_repeat($filler, $numfills);
 
25802
                        } else {
 
25803
                                $rowfill = '';
 
25804
                        }
 
25805
                        if ($this->rtl) {
 
25806
                                $pagenum = $pagenum.$gap.$rowfill;
 
25807
                        } else {
 
25808
                                $pagenum = $rowfill.$gap.$pagenum;
 
25809
                        }
 
25810
                        // write the number
 
25811
                        $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
 
25812
                }
 
25813
                $page_last = $this->getPage();
 
25814
                $maxpage = max($maxpage, $page_last);
 
25815
                $numpages = $page_last - $page_first + 1;
 
25816
                if (!$this->empty_string($page)) {
 
25817
                        for ($p = $page_first; $p <= $page_last; ++$p) {
 
25818
                                // get page data
 
25819
                                $temppage = $this->getPageBuffer($p);
 
25820
                                for ($n = 1; $n <= $maxpage; ++$n) {
 
25821
                                        // update page numbers
 
25822
                                        $a = '{#'.$n.'}';
 
25823
                                        // get page number aliases
 
25824
                                        $pnalias = $this->getInternalPageNumberAliases($a);
 
25825
                                        // calculate replacement number
 
25826
                                        if (($n >= $page) AND ($n <= $this->numpages)) {
 
25827
                                                $np = $n + $numpages;
 
25828
                                        } else {
 
25829
                                                $np = $n;
 
25830
                                        }
 
25831
                                        $na = $this->formatTOCPageNumber(($this->starting_page_number + $np - 1));
 
25832
                                        $nu = $this->UTF8ToUTF16BE($na, false);
 
25833
                                        // replace aliases with numbers
 
25834
                                        foreach ($pnalias['u'] as $u) {
 
25835
                                                $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' '))));
 
25836
                                                if ($this->rtl) {
 
25837
                                                        $nr = $nu.$this->UTF8ToUTF16BE(' '.$sfill);
 
25838
                                                } else {
 
25839
                                                        $nr = $this->UTF8ToUTF16BE($sfill.' ').$nu;
 
25840
                                                }
 
25841
                                                $temppage = str_replace($u, $nr, $temppage);
 
25842
                                        }
 
25843
                                        foreach ($pnalias['a'] as $a) {
 
25844
                                                $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' '))));
 
25845
                                                if ($this->rtl) {
 
25846
                                                        $nr = $na.' '.$sfill;
 
25847
                                                } else {
 
25848
                                                        $nr = $sfill.' '.$na;
 
25849
                                                }
 
25850
                                                $temppage = str_replace($a, $nr, $temppage);
 
25851
                                        }
 
25852
                                }
 
25853
                                // save changes
 
25854
                                $this->setPageBuffer($p, $temppage);
 
25855
                        }
 
25856
                        // move pages
 
25857
                        $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
 
25858
                        for ($i = 0; $i < $numpages; ++$i) {
 
25859
                                $this->movePage($page_last, $page);
 
25860
                        }
 
25861
                }
 
25862
        }
 
25863
 
 
25864
        /**
 
25865
         * Output a Table Of Content Index (TOC) using HTML templates.
 
25866
         * This method must be called after all Bookmarks were set.
 
25867
         * Before calling this method you have to open the page using the addTOCPage() method.
 
25868
         * After calling this method you have to call endTOCPage() to close the TOC page.
 
25869
         * @param $page (int) page number where this TOC should be inserted (leave empty for current page).
 
25870
         * @param $toc_name (string) name to use for TOC bookmark.
 
25871
         * @param $templates (array) array of html templates. Use: "#TOC_DESCRIPTION#" for bookmark title, "#TOC_PAGE_NUMBER#" for page number.
 
25872
         * @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)
 
25873
         * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
 
25874
         * @param $color (array) RGB color array for title (values from 0 to 255).
 
25875
         * @public
 
25876
         * @author Nicola Asuni
 
25877
         * @since 5.0.001 (2010-05-06)
 
25878
         * @see addTOCPage(), endTOCPage(), addTOC()
 
25879
         */
 
25880
        public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) {
 
25881
                $filler = ' ';
 
25882
                $prev_htmlLinkColorArray = $this->htmlLinkColorArray;
 
25883
                $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle;
 
25884
                // set new style for link
 
25885
                $this->htmlLinkColorArray = array();
 
25886
                $this->htmlLinkFontStyle = '';
 
25887
                $page_first = $this->getPage();
 
25888
                // get the font type used for numbers in each template
 
25889
                $current_font = $this->FontFamily;
 
25890
                foreach ($templates as $level => $html) {
 
25891
                        $dom = $this->getHtmlDomArray($html);
 
25892
                        foreach ($dom as $key => $value) {
 
25893
                                if ($value['value'] == '#TOC_PAGE_NUMBER#') {
 
25894
                                        $this->SetFont($dom[($key - 1)]['fontname']);
 
25895
                                        $templates['F'.$level] = $this->isUnicodeFont();
 
25896
                                }
 
25897
                        }
 
25898
                }
 
25899
                $this->SetFont($current_font);
 
25900
                $maxpage = 0; //used for pages on attached documents
 
25901
                foreach ($this->outlines as $key => $outline) {
 
25902
                        // get HTML template
 
25903
                        $row = $templates[$outline['l']];
 
25904
                        if ($this->empty_string($page)) {
 
25905
                                $pagenum = $outline['p'];
 
25906
                        } else {
 
25907
                                // placemark to be replaced with the correct number
 
25908
                                $pagenum = '{#'.($outline['p']).'}';
 
25909
                                if ($templates['F'.$outline['l']]) {
 
25910
                                        $pagenum = '{'.$pagenum.'}';
 
25911
                                }
 
25912
                                $maxpage = max($maxpage, $outline['p']);
 
25913
                        }
 
25914
                        // replace templates with current values
 
25915
                        $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
 
25916
                        $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
 
25917
                        // add link to page
 
25918
                        $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
 
25919
                        // write bookmark entry
 
25920
                        $this->writeHTML($row, false, false, true, false, '');
 
25921
                }
 
25922
                // restore link styles
 
25923
                $this->htmlLinkColorArray = $prev_htmlLinkColorArray;
 
25924
                $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle;
 
25925
                // move TOC page and replace numbers
 
25926
                $page_last = $this->getPage();
 
25927
                $maxpage = max($maxpage, $page_last);
 
25928
                $numpages = $page_last - $page_first + 1;
 
25929
                if (!$this->empty_string($page)) {
 
25930
                        for ($p = $page_first; $p <= $page_last; ++$p) {
 
25931
                                // get page data
 
25932
                                $temppage = $this->getPageBuffer($p);
 
25933
                                for ($n = 1; $n <= $maxpage; ++$n) {
 
25934
                                        // update page numbers
 
25935
                                        $a = '{#'.$n.'}';
 
25936
                                        // get page number aliases
 
25937
                                        $pnalias = $this->getInternalPageNumberAliases($a);
 
25938
                                        // calculate replacement number
 
25939
                                        if ($n >= $page) {
 
25940
                                                $np = $n + $numpages;
 
25941
                                        } else {
 
25942
                                                $np = $n;
 
25943
                                        }
 
25944
                                        $na = $this->formatTOCPageNumber(($this->starting_page_number + $np - 1));
 
25945
                                        $nu = $this->UTF8ToUTF16BE($na, false);
 
25946
                                        // replace aliases with numbers
 
25947
                                        foreach ($pnalias['u'] as $u) {
 
25948
                                                if ($correct_align) {
 
25949
                                                        $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' ')));
 
25950
                                                        if ($this->rtl) {
 
25951
                                                                $nr = $nu.$this->UTF8ToUTF16BE(' '.$sfill);
 
25952
                                                        } else {
 
25953
                                                                $nr = $this->UTF8ToUTF16BE($sfill.' ').$nu;
 
25954
                                                        }
 
25955
                                                } else {
 
25956
                                                        $nr = $nu;
 
25957
                                                }
 
25958
                                                $temppage = str_replace($u, $nr, $temppage);
 
25959
                                        }
 
25960
                                        foreach ($pnalias['a'] as $a) {
 
25961
                                                if ($correct_align) {
 
25962
                                                        $sfill = str_repeat($filler, (strlen($a) - strlen($na.' ')));
 
25963
                                                        if ($this->rtl) {
 
25964
                                                                $nr = $na.' '.$sfill;
 
25965
                                                        } else {
 
25966
                                                                $nr = $sfill.' '.$na;
 
25967
                                                        }
 
25968
                                                } else {
 
25969
                                                        $nr = $na;
 
25970
                                                }
 
25971
                                                $temppage = str_replace($a, $nr, $temppage);
 
25972
                                        }
 
25973
                                }
 
25974
                                // save changes
 
25975
                                $this->setPageBuffer($p, $temppage);
 
25976
                        }
 
25977
                        // move pages
 
25978
                        $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
 
25979
                        for ($i = 0; $i < $numpages; ++$i) {
 
25980
                                $this->movePage($page_last, $page);
 
25981
                        }
 
25982
                }
 
25983
        }
 
25984
 
 
25985
        /**
 
25986
         * Stores a copy of the current TCPDF object used for undo operation.
 
25987
         * @public
 
25988
         * @since 4.5.029 (2009-03-19)
 
25989
         */
 
25990
        public function startTransaction() {
 
25991
                if (isset($this->objcopy)) {
 
25992
                        // remove previous copy
 
25993
                        $this->commitTransaction();
 
25994
                }
 
25995
                // record current page number and Y position
 
25996
                $this->start_transaction_page = $this->page;
 
25997
                $this->start_transaction_y = $this->y;
 
25998
                // clone current object
 
25999
                $this->objcopy = $this->objclone($this);
 
26000
        }
 
26001
 
 
26002
        /**
 
26003
         * Delete the copy of the current TCPDF object used for undo operation.
 
26004
         * @public
 
26005
         * @since 4.5.029 (2009-03-19)
 
26006
         */
 
26007
        public function commitTransaction() {
 
26008
                if (isset($this->objcopy)) {
 
26009
                        $this->objcopy->_destroy(true, true);
 
26010
                        unset($this->objcopy);
 
26011
                }
 
26012
        }
 
26013
 
 
26014
        /**
 
26015
         * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction().
 
26016
         * @param $self (boolean) if true restores current class object to previous state without the need of reassignment via the returned value.
 
26017
         * @return TCPDF object.
 
26018
         * @public
 
26019
         * @since 4.5.029 (2009-03-19)
 
26020
         */
 
26021
        public function rollbackTransaction($self=false) {
 
26022
                if (isset($this->objcopy)) {
 
26023
                        if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
 
26024
                                // truncate files to previous values
 
26025
                                foreach ($this->objcopy->cache_file_length as $file => $length) {
 
26026
                                        $file = substr($file, 1);
 
26027
                                        $handle = fopen($file, 'r+');
 
26028
                                        ftruncate($handle, $length);
 
26029
                                }
 
26030
                        }
 
26031
                        $this->_destroy(true, true);
 
26032
                        if ($self) {
 
26033
                                $objvars = get_object_vars($this->objcopy);
 
26034
                                foreach ($objvars as $key => $value) {
 
26035
                                        $this->$key = $value;
 
26036
                                }
 
26037
                        }
 
26038
                        return $this->objcopy;
 
26039
                }
 
26040
                return $this;
 
26041
        }
 
26042
 
 
26043
        /**
 
26044
         * Creates a copy of a class object
 
26045
         * @param $object (object) class object to be cloned
 
26046
         * @return cloned object
 
26047
         * @public
 
26048
         * @since 4.5.029 (2009-03-19)
 
26049
         */
 
26050
        public function objclone($object) {
 
26051
                return @clone($object);
 
26052
        }
 
26053
 
 
26054
        /**
 
26055
         * Determine whether a string is empty.
 
26056
         * @param $str (string) string to be checked
 
26057
         * @return boolean true if string is empty
 
26058
         * @public
 
26059
         * @since 4.5.044 (2009-04-16)
 
26060
         */
 
26061
        public function empty_string($str) {
 
26062
                return (is_null($str) OR (is_string($str) AND (strlen($str) == 0)));
 
26063
        }
 
26064
 
 
26065
        /**
 
26066
         * Find position of last occurrence of a substring in a string
 
26067
         * @param $haystack (string) The string to search in.
 
26068
         * @param $needle (string) substring to search.
 
26069
         * @param $offset (int) May be specified to begin searching an arbitrary number of characters into the string.
 
26070
         * @return Returns the position where the needle exists. Returns FALSE if the needle was not found.
 
26071
         * @public
 
26072
         * @since 4.8.038 (2010-03-13)
 
26073
         */
 
26074
        public function revstrpos($haystack, $needle, $offset = 0) {
 
26075
                $length = strlen($haystack);
 
26076
                $offset = ($offset > 0)?($length - $offset):abs($offset);
 
26077
                $pos = strpos(strrev($haystack), strrev($needle), $offset);
 
26078
                return ($pos === false)?false:($length - $pos - strlen($needle));
 
26079
        }
 
26080
 
 
26081
        // --- MULTI COLUMNS METHODS -----------------------
 
26082
 
 
26083
        /**
 
26084
         * Set multiple columns of the same size
 
26085
         * @param $numcols (int) number of columns (set to zero to disable columns mode)
 
26086
         * @param $width (int) column width
 
26087
         * @param $y (int) column starting Y position (leave empty for current Y position)
 
26088
         * @public
 
26089
         * @since 4.9.001 (2010-03-28)
 
26090
         */
 
26091
        public function setEqualColumns($numcols=0, $width=0, $y='') {
 
26092
                $this->columns = array();
 
26093
                if ($numcols < 2) {
 
26094
                        $numcols = 0;
 
26095
                        $this->columns = array();
 
26096
                } else {
 
26097
                        // maximum column width
 
26098
                        $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols;
 
26099
                        if (($width == 0) OR ($width > $maxwidth)) {
 
26100
                                $width = $maxwidth;
 
26101
                        }
 
26102
                        if ($this->empty_string($y)) {
 
26103
                                $y = $this->y;
 
26104
                        }
 
26105
                        // space between columns
 
26106
                        $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1));
 
26107
                        // fill the columns array (with, space, starting Y position)
 
26108
                        for ($i = 0; $i < $numcols; ++$i) {
 
26109
                                $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y);
 
26110
                        }
 
26111
                }
 
26112
                $this->num_columns = $numcols;
 
26113
                $this->current_column = 0;
 
26114
                $this->column_start_page = $this->page;
 
26115
                $this->selectColumn(0);
 
26116
        }
 
26117
 
 
26118
        /**
 
26119
         * Remove columns and reset page margins.
 
26120
         * @public
 
26121
         * @since 5.9.072 (2011-04-26)
 
26122
         */
 
26123
        public function resetColumns() {
 
26124
                $this->lMargin = $this->original_lMargin;
 
26125
                $this->rMargin = $this->original_rMargin;
 
26126
                $this->setEqualColumns();
 
26127
        }
 
26128
 
 
26129
        /**
 
26130
         * Set columns array.
 
26131
         * Each column is represented by an array of arrays with the following keys: (w = width, s = space between columns, y = column top position).
 
26132
         * @param $columns (array)
 
26133
         * @public
 
26134
         * @since 4.9.001 (2010-03-28)
 
26135
         */
 
26136
        public function setColumnsArray($columns) {
 
26137
                $this->columns = $columns;
 
26138
                $this->num_columns = count($columns);
 
26139
                $this->current_column = 0;
 
26140
                $this->column_start_page = $this->page;
 
26141
                $this->selectColumn(0);
 
26142
        }
 
26143
 
 
26144
        /**
 
26145
         * Set position at a given column
 
26146
         * @param $col (int) column number (from 0 to getNumberOfColumns()-1); empty string = current column.
 
26147
         * @public
 
26148
         * @since 4.9.001 (2010-03-28)
 
26149
         */
 
26150
        public function selectColumn($col='') {
 
26151
                if (is_string($col)) {
 
26152
                        $col = $this->current_column;
 
26153
                } elseif ($col >= $this->num_columns) {
 
26154
                        $col = 0;
 
26155
                }
 
26156
                $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
 
26157
                $enable_thead = false;
 
26158
                if ($this->num_columns > 1) {
 
26159
                        if ($col != $this->current_column) {
 
26160
                                // move Y pointer at the top of the column
 
26161
                                if ($this->column_start_page == $this->page) {
 
26162
                                        $this->y = $this->columns[$col]['y'];
 
26163
                                } else {
 
26164
                                        $this->y = $this->tMargin;
 
26165
                                }
 
26166
                                // Avoid to write table headers more than once
 
26167
                                if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) {
 
26168
                                        $enable_thead = true;
 
26169
                                        $this->maxselcol['page'] = $this->page;
 
26170
                                        $this->maxselcol['column'] = $col;
 
26171
                                }
 
26172
                        }
 
26173
                        $xshift = $this->colxshift;
 
26174
                        // set X position of the current column by case
 
26175
                        $listindent = ($this->listindentlevel * $this->listindent);
 
26176
                        // calculate column X position
 
26177
                        $colpos = 0;
 
26178
                        for ($i = 0; $i < $col; ++$i) {
 
26179
                                $colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']);
 
26180
                        }
 
26181
                        if ($this->rtl) {
 
26182
                                $x = $this->w - $this->original_rMargin - $colpos;
 
26183
                                $this->rMargin = ($this->w - $x + $listindent);
 
26184
                                $this->lMargin = ($x - $this->columns[$col]['w']);
 
26185
                                $this->x = $x - $listindent;
 
26186
                        } else {
 
26187
                                $x = $this->original_lMargin + $colpos;
 
26188
                                $this->lMargin = ($x + $listindent);
 
26189
                                $this->rMargin = ($this->w - $x - $this->columns[$col]['w']);
 
26190
                                $this->x = $x + $listindent;
 
26191
                        }
 
26192
                        $this->columns[$col]['x'] = $x;
 
26193
                }
 
26194
                $this->current_column = $col;
 
26195
                // fix for HTML mode
 
26196
                $this->newline = true;
 
26197
                // print HTML table header (if any)
 
26198
                if ((!$this->empty_string($this->thead)) AND (!$this->inthead)) {
 
26199
                        if ($enable_thead) {
 
26200
                                // print table header
 
26201
                                $this->writeHTML($this->thead, false, false, false, false, '');
 
26202
                                $this->y += $xshift['s']['V'];
 
26203
                                // store end of header position
 
26204
                                if (!isset($this->columns[$col]['th'])) {
 
26205
                                        $this->columns[$col]['th'] = array();
 
26206
                                }
 
26207
                                $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y;
 
26208
                                $this->lasth = 0;
 
26209
                        } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) {
 
26210
                                $this->y = $this->columns[$col]['th']['\''.$this->page.'\''];
 
26211
                        }
 
26212
                }
 
26213
                // account for an html table cell over multiple columns
 
26214
                if ($this->rtl) {
 
26215
                        $this->rMargin += $xshift['x'];
 
26216
                        $this->x -= ($xshift['x'] + $xshift['p']['R']);
 
26217
                } else {
 
26218
                        $this->lMargin += $xshift['x'];
 
26219
                        $this->x += $xshift['x'] + $xshift['p']['L'];
 
26220
                }
 
26221
        }
 
26222
 
 
26223
        /**
 
26224
         * Return the current column number
 
26225
         * @return int current column number
 
26226
         * @public
 
26227
         * @since 5.5.011 (2010-07-08)
 
26228
         */
 
26229
        public function getColumn() {
 
26230
                return $this->current_column;
 
26231
        }
 
26232
 
 
26233
        /**
 
26234
         * Return the current number of columns.
 
26235
         * @return int number of columns
 
26236
         * @public
 
26237
         * @since 5.8.018 (2010-08-25)
 
26238
         */
 
26239
        public function getNumberOfColumns() {
 
26240
                return $this->num_columns;
 
26241
        }
 
26242
 
 
26243
        /**
 
26244
         * Serialize an array of parameters to be used with TCPDF tag in HTML code.
 
26245
         * @param $pararray (array) parameters array
 
26246
         * @return sting containing serialized data
 
26247
         * @public
 
26248
         * @since 4.9.006 (2010-04-02)
 
26249
         */
 
26250
        public function serializeTCPDFtagParameters($pararray) {
 
26251
                return urlencode(serialize($pararray));
 
26252
        }
 
26253
 
 
26254
        /**
 
26255
         * Set Text rendering mode.
 
26256
         * @param $stroke (int) outline size in user units (0 = disable).
 
26257
         * @param $fill (boolean) if true fills the text (default).
 
26258
         * @param $clip (boolean) if true activate clipping mode
 
26259
         * @public
 
26260
         * @since 4.9.008 (2009-04-02)
 
26261
         */
 
26262
        public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
 
26263
                // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
 
26264
                // convert text rendering parameters
 
26265
                if ($stroke < 0) {
 
26266
                        $stroke = 0;
 
26267
                }
 
26268
                if ($fill === true) {
 
26269
                        if ($stroke > 0) {
 
26270
                                if ($clip === true) {
 
26271
                                        // Fill, then stroke text and add to path for clipping
 
26272
                                        $textrendermode = 6;
 
26273
                                } else {
 
26274
                                        // Fill, then stroke text
 
26275
                                        $textrendermode = 2;
 
26276
                                }
 
26277
                                $textstrokewidth = $stroke;
 
26278
                        } else {
 
26279
                                if ($clip === true) {
 
26280
                                        // Fill text and add to path for clipping
 
26281
                                        $textrendermode = 4;
 
26282
                                } else {
 
26283
                                        // Fill text
 
26284
                                        $textrendermode = 0;
 
26285
                                }
 
26286
                        }
 
26287
                } else {
 
26288
                        if ($stroke > 0) {
 
26289
                                if ($clip === true) {
 
26290
                                        // Stroke text and add to path for clipping
 
26291
                                        $textrendermode = 5;
 
26292
                                } else {
 
26293
                                        // Stroke text
 
26294
                                        $textrendermode = 1;
 
26295
                                }
 
26296
                                $textstrokewidth = $stroke;
 
26297
                        } else {
 
26298
                                if ($clip === true) {
 
26299
                                        // Add text to path for clipping
 
26300
                                        $textrendermode = 7;
 
26301
                                } else {
 
26302
                                        // Neither fill nor stroke text (invisible)
 
26303
                                        $textrendermode = 3;
 
26304
                                }
 
26305
                        }
 
26306
                }
 
26307
                $this->textrendermode = $textrendermode;
 
26308
                $this->textstrokewidth = $stroke * $this->k;
 
26309
        }
 
26310
 
 
26311
        /**
 
26312
         * Returns an array of chars containing soft hyphens.
 
26313
         * @param $word (array) array of chars
 
26314
         * @param $patterns (array) Array of hypenation patterns.
 
26315
         * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm.
 
26316
         * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
 
26317
         * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
 
26318
         * @param $charmin (int) Minimum word length to apply the hyphenation algoritm.
 
26319
         * @param $charmax (int) Maximum length of broken piece of word.
 
26320
         * @return array text with soft hyphens
 
26321
         * @author Nicola Asuni
 
26322
         * @since 4.9.012 (2010-04-12)
 
26323
         * @protected
 
26324
         */
 
26325
        protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
 
26326
                $hyphenword = array(); // hyphens positions
 
26327
                $numchars = count($word);
 
26328
                if ($numchars <= $charmin) {
 
26329
                        return $word;
 
26330
                }
 
26331
                $word_string = $this->UTF8ArrSubString($word);
 
26332
                // some words will be returned as-is
 
26333
                $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})(\]?)$/';
 
26334
                if (preg_match($pattern, $word_string) > 0) {
 
26335
                        // email
 
26336
                        return $word;
 
26337
                }
 
26338
                $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})(\]?)$/';
 
26339
                if (preg_match($pattern, $word_string) > 0) {
 
26340
                        // URL
 
26341
                        return $word;
 
26342
                }
 
26343
                if (isset($dictionary[$word_string])) {
 
26344
                        return $this->UTF8StringToArray($dictionary[$word_string]);
 
26345
                }
 
26346
                // suround word with '_' characters
 
26347
                $tmpword = array_merge(array(95), $word, array(95));
 
26348
                $tmpnumchars = $numchars + 2;
 
26349
                $maxpos = $tmpnumchars - $charmin;
 
26350
                for ($pos = 0; $pos < $maxpos; ++$pos) {
 
26351
                        $imax = min(($tmpnumchars - $pos), $charmax);
 
26352
                        for ($i = $charmin; $i <= $imax; ++$i) {
 
26353
                                $subword = strtolower($this->UTF8ArrSubString($tmpword, $pos, $pos + $i));
 
26354
                                if (isset($patterns[$subword])) {
 
26355
                                        $pattern = $this->UTF8StringToArray($patterns[$subword]);
 
26356
                                        $pattern_length = count($pattern);
 
26357
                                        $digits = 1;
 
26358
                                        for ($j = 0; $j < $pattern_length; ++$j) {
 
26359
                                                // check if $pattern[$j] is a number
 
26360
                                                if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
 
26361
                                                        if ($j == 0) {
 
26362
                                                                $zero = $pos - 1;
 
26363
                                                        } else {
 
26364
                                                                $zero = $pos + $j - $digits;
 
26365
                                                        }
 
26366
                                                        if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] != $pattern[$j])) {
 
26367
                                                                $hyphenword[$zero] = $this->unichr($pattern[$j]);
 
26368
                                                        }
 
26369
                                                        ++$digits;
 
26370
                                                }
 
26371
                                        }
 
26372
                                }
 
26373
                        }
 
26374
                }
 
26375
                $inserted = 0;
 
26376
                $maxpos = $numchars - $rightmin;
 
26377
                for ($i = $leftmin; $i <= $maxpos; ++$i) {
 
26378
                        if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) {
 
26379
                                // 173 = soft hyphen character
 
26380
                                array_splice($word, $i + $inserted, 0, 173);
 
26381
                                ++$inserted;
 
26382
                        }
 
26383
                }
 
26384
                return $word;
 
26385
        }
 
26386
 
 
26387
        /**
 
26388
         * Returns an array of hyphenation patterns.
 
26389
         * @param $file (string) TEX file containing hypenation patterns. TEX pattrns can be downloaded from http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/
 
26390
         * @return array of hyphenation patterns
 
26391
         * @author Nicola Asuni
 
26392
         * @since 4.9.012 (2010-04-12)
 
26393
         * @public
 
26394
         */
 
26395
        public function getHyphenPatternsFromTEX($file) {
 
26396
                // TEX patterns are available at:
 
26397
                // http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/
 
26398
                $data = file_get_contents($file);
 
26399
                $patterns = array();
 
26400
                // remove comments
 
26401
                $data = preg_replace('/\%[^\n]*/', '', $data);
 
26402
                // extract the patterns part
 
26403
                preg_match('/\\\\patterns\{([^\}]*)\}/i', $data, $matches);
 
26404
                $data = trim(substr($matches[0], 10, -1));
 
26405
                // extract each pattern
 
26406
                $patterns_array = preg_split('/[\s]+/', $data);
 
26407
                // create new language array of patterns
 
26408
                $patterns = array();
 
26409
                foreach($patterns_array as $val) {
 
26410
                        if (!$this->empty_string($val)) {
 
26411
                                $val = trim($val);
 
26412
                                $val = str_replace('\'', '\\\'', $val);
 
26413
                                $key = preg_replace('/[0-9]+/', '', $val);
 
26414
                                $patterns[$key] = $val;
 
26415
                        }
 
26416
                }
 
26417
                return $patterns;
 
26418
        }
 
26419
 
 
26420
        /**
 
26421
         * Returns text with soft hyphens.
 
26422
         * @param $text (string) text to process
 
26423
         * @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/
 
26424
         * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm.
 
26425
         * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
 
26426
         * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
 
26427
         * @param $charmin (int) Minimum word length to apply the hyphenation algoritm.
 
26428
         * @param $charmax (int) Maximum length of broken piece of word.
 
26429
         * @return array text with soft hyphens
 
26430
         * @author Nicola Asuni
 
26431
         * @since 4.9.012 (2010-04-12)
 
26432
         * @public
 
26433
         */
 
26434
        public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
 
26435
                $text = $this->unhtmlentities($text);
 
26436
                $word = array(); // last word
 
26437
                $txtarr = array(); // text to be returned
 
26438
                $intag = false; // true if we are inside an HTML tag
 
26439
                if (!is_array($patterns)) {
 
26440
                        $patterns = $this->getHyphenPatternsFromTEX($patterns);
 
26441
                }
 
26442
                // get array of characters
 
26443
                $unichars = $this->UTF8StringToArray($text);
 
26444
                // for each char
 
26445
                foreach ($unichars as $char) {
 
26446
                        if ((!$intag) AND $this->unicode->uni_type[$char] == 'L') {
 
26447
                                // letter character
 
26448
                                $word[] = $char;
 
26449
                        } else {
 
26450
                                // other type of character
 
26451
                                if (!$this->empty_string($word)) {
 
26452
                                        // hypenate the word
 
26453
                                        $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
 
26454
                                        $word = array();
 
26455
                                }
 
26456
                                $txtarr[] = $char;
 
26457
                                if (chr($char) == '<') {
 
26458
                                        // we are inside an HTML tag
 
26459
                                        $intag = true;
 
26460
                                } elseif ($intag AND (chr($char) == '>')) {
 
26461
                                        // end of HTML tag
 
26462
                                        $intag = false;
 
26463
                                }
 
26464
                        }
 
26465
                }
 
26466
                if (!$this->empty_string($word)) {
 
26467
                        // hypenate the word
 
26468
                        $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
 
26469
                }
 
26470
                // convert char array to string and return
 
26471
                return $this->UTF8ArrSubString($txtarr);
 
26472
        }
 
26473
 
 
26474
        /**
 
26475
         * Enable/disable rasterization of vector images using ImageMagick library.
 
26476
         * @param $mode (boolean) if true enable rasterization, false otherwise.
 
26477
         * @public
 
26478
         * @since 5.0.000 (2010-04-27)
 
26479
         */
 
26480
        public function setRasterizeVectorImages($mode) {
 
26481
                $this->rasterize_vector_images = $mode;
 
26482
        }
 
26483
 
 
26484
        /**
 
26485
         * Get the Path-Painting Operators.
 
26486
         * @param $style (string) Style of rendering. Possible values are:
 
26487
         * <ul>
 
26488
         *   <li>S or D: Stroke the path.</li>
 
26489
         *   <li>s or d: Close and stroke the path.</li>
 
26490
         *   <li>f or F: Fill the path, using the nonzero winding number rule to determine the region to fill.</li>
 
26491
         *   <li>f* or F*: Fill the path, using the even-odd rule to determine the region to fill.</li>
 
26492
         *   <li>B or FD or DF: Fill and then stroke the path, using the nonzero winding number rule to determine the region to fill.</li>
 
26493
         *   <li>B* or F*D or DF*: Fill and then stroke the path, using the even-odd rule to determine the region to fill.</li>
 
26494
         *   <li>b or fd or df: Close, fill, and then stroke the path, using the nonzero winding number rule to determine the region to fill.</li>
 
26495
         *   <li>b or f*d or df*: Close, fill, and then stroke the path, using the even-odd rule to determine the region to fill.</li>
 
26496
         *   <li>CNZ: Clipping mode using the even-odd rule to determine which regions lie inside the clipping path.</li>
 
26497
         *   <li>CEO: Clipping mode using the nonzero winding number rule to determine which regions lie inside the clipping path</li>
 
26498
         *   <li>n: End the path object without filling or stroking it.</li>
 
26499
         * </ul>
 
26500
         * @param $default (string) default style
 
26501
         * @author Nicola Asuni
 
26502
         * @since 5.0.000 (2010-04-30)
 
26503
         * @protected
 
26504
         */
 
26505
        protected function getPathPaintOperator($style, $default='S') {
 
26506
                $op = '';
 
26507
                switch($style) {
 
26508
                        case 'S':
 
26509
                        case 'D': {
 
26510
                                $op = 'S';
 
26511
                                break;
 
26512
                        }
 
26513
                        case 's':
 
26514
                        case 'd': {
 
26515
                                $op = 's';
 
26516
                                break;
 
26517
                        }
 
26518
                        case 'f':
 
26519
                        case 'F': {
 
26520
                                $op = 'f';
 
26521
                                break;
 
26522
                        }
 
26523
                        case 'f*':
 
26524
                        case 'F*': {
 
26525
                                $op = 'f*';
 
26526
                                break;
 
26527
                        }
 
26528
                        case 'B':
 
26529
                        case 'FD':
 
26530
                        case 'DF': {
 
26531
                                $op = 'B';
 
26532
                                break;
 
26533
                        }
 
26534
                        case 'B*':
 
26535
                        case 'F*D':
 
26536
                        case 'DF*': {
 
26537
                                $op = 'B*';
 
26538
                                break;
 
26539
                        }
 
26540
                        case 'b':
 
26541
                        case 'fd':
 
26542
                        case 'df': {
 
26543
                                $op = 'b';
 
26544
                                break;
 
26545
                        }
 
26546
                        case 'b*':
 
26547
                        case 'f*d':
 
26548
                        case 'df*': {
 
26549
                                $op = 'b*';
 
26550
                                break;
 
26551
                        }
 
26552
                        case 'CNZ': {
 
26553
                                $op = 'W n';
 
26554
                                break;
 
26555
                        }
 
26556
                        case 'CEO': {
 
26557
                                $op = 'W* n';
 
26558
                                break;
 
26559
                        }
 
26560
                        case 'n': {
 
26561
                                $op = 'n';
 
26562
                                break;
 
26563
                        }
 
26564
                        default: {
 
26565
                                if (!empty($default)) {
 
26566
                                        $op = $this->getPathPaintOperator($default, '');
 
26567
                                } else {
 
26568
                                        $op = '';
 
26569
                                }
 
26570
                        }
 
26571
                }
 
26572
                return $op;
 
26573
        }
 
26574
 
 
26575
        /**
 
26576
         * Enable or disable default option for font subsetting.
 
26577
         * @param $enable (boolean) if true enable font subsetting by default.
 
26578
         * @author Nicola Asuni
 
26579
         * @public
 
26580
         * @since 5.3.002 (2010-06-07)
 
26581
         */
 
26582
        public function setFontSubsetting($enable=true) {
 
26583
                if ($this->pdfa_mode) {
 
26584
                        $this->font_subsetting = false;
 
26585
                } else {
 
26586
                        $this->font_subsetting = $enable ? true : false;
 
26587
                }
 
26588
        }
 
26589
 
 
26590
        /**
 
26591
         * Return the default option for font subsetting.
 
26592
         * @return boolean default font subsetting state.
 
26593
         * @author Nicola Asuni
 
26594
         * @public
 
26595
         * @since 5.3.002 (2010-06-07)
 
26596
         */
 
26597
        public function getFontSubsetting() {
 
26598
                return $this->font_subsetting;
 
26599
        }
 
26600
 
 
26601
        /**
 
26602
         * Left trim the input string
 
26603
         * @param $str (string) string to trim
 
26604
         * @param $replace (string) string that replace spaces.
 
26605
         * @return left trimmed string
 
26606
         * @author Nicola Asuni
 
26607
         * @public
 
26608
         * @since 5.8.000 (2010-08-11)
 
26609
         */
 
26610
        public function stringLeftTrim($str, $replace='') {
 
26611
                return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str);
 
26612
        }
 
26613
 
 
26614
        /**
 
26615
         * Right trim the input string
 
26616
         * @param $str (string) string to trim
 
26617
         * @param $replace (string) string that replace spaces.
 
26618
         * @return right trimmed string
 
26619
         * @author Nicola Asuni
 
26620
         * @public
 
26621
         * @since 5.8.000 (2010-08-11)
 
26622
         */
 
26623
        public function stringRightTrim($str, $replace='') {
 
26624
                return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str);
 
26625
        }
 
26626
 
 
26627
        /**
 
26628
         * Trim the input string
 
26629
         * @param $str (string) string to trim
 
26630
         * @param $replace (string) string that replace spaces.
 
26631
         * @return trimmed string
 
26632
         * @author Nicola Asuni
 
26633
         * @public
 
26634
         * @since 5.8.000 (2010-08-11)
 
26635
         */
 
26636
        public function stringTrim($str, $replace='') {
 
26637
                $str = $this->stringLeftTrim($str, $replace);
 
26638
                $str = $this->stringRightTrim($str, $replace);
 
26639
                return $str;
 
26640
        }
 
26641
 
 
26642
        /**
 
26643
         * Return true if the current font is unicode type.
 
26644
         * @return true for unicode font, false otherwise.
 
26645
         * @author Nicola Asuni
 
26646
         * @public
 
26647
         * @since 5.8.002 (2010-08-14)
 
26648
         */
 
26649
        public function isUnicodeFont() {
 
26650
                return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0'));
 
26651
        }
 
26652
 
 
26653
        /**
 
26654
         * Return normalized font name
 
26655
         * @param $fontfamily (string) property string containing font family names
 
26656
         * @return string normalized font name
 
26657
         * @author Nicola Asuni
 
26658
         * @public
 
26659
         * @since 5.8.004 (2010-08-17)
 
26660
         */
 
26661
        public function getFontFamilyName($fontfamily) {
 
26662
                // remove spaces and symbols
 
26663
                $fontfamily = preg_replace('/[^a-z0-9\,]/', '', strtolower($fontfamily));
 
26664
                // extract all font names
 
26665
                $fontslist = preg_split('/[,]/', $fontfamily);
 
26666
                // find first valid font name
 
26667
                foreach ($fontslist as $font) {
 
26668
                        // replace font variations
 
26669
                        $font = preg_replace('/italic$/', 'I', $font);
 
26670
                        $font = preg_replace('/oblique$/', 'I', $font);
 
26671
                        $font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
 
26672
                        // replace common family names and core fonts
 
26673
                        $pattern = array();
 
26674
                        $replacement = array();
 
26675
                        $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
 
26676
                        $replacement[] = 'times';
 
26677
                        $pattern[] = '/^sansserif/';
 
26678
                        $replacement[] = 'helvetica';
 
26679
                        $pattern[] = '/^monospace/';
 
26680
                        $replacement[] = 'courier';
 
26681
                        $font = preg_replace($pattern, $replacement, $font);
 
26682
                        if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) {
 
26683
                                return $font;
 
26684
                        }
 
26685
                }
 
26686
                // return current font as default
 
26687
                return $this->CurrentFont['fontkey'];
 
26688
        }
 
26689
 
 
26690
        /**
 
26691
         * Start a new XObject Template.
 
26692
         * 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).
 
26693
         * 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.
 
26694
         * Note: X,Y coordinates will be reset to 0,0.
 
26695
         * @param $w (int) Template width in user units (empty string or zero = page width less margins).
 
26696
         * @param $h (int) Template height in user units (empty string or zero = page height less margins).
 
26697
         * @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).
 
26698
         * @return int the XObject Template ID in case of success or false in case of error.
 
26699
         * @author Nicola Asuni
 
26700
         * @public
 
26701
         * @since 5.8.017 (2010-08-24)
 
26702
         * @see endTemplate(), printTemplate()
 
26703
         */
 
26704
        public function startTemplate($w=0, $h=0, $group=false) {
 
26705
                if ($this->inxobj) {
 
26706
                        // we are already inside an XObject template
 
26707
                        return false;
 
26708
                }
 
26709
                $this->inxobj = true;
 
26710
                ++$this->n;
 
26711
                // XObject ID
 
26712
                $this->xobjid = 'XT'.$this->n;
 
26713
                // object ID
 
26714
                $this->xobjects[$this->xobjid] = array('n' => $this->n);
 
26715
                // store current graphic state
 
26716
                $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars();
 
26717
                // initialize data
 
26718
                $this->xobjects[$this->xobjid]['intmrk'] = 0;
 
26719
                $this->xobjects[$this->xobjid]['transfmrk'] = array();
 
26720
                $this->xobjects[$this->xobjid]['outdata'] = '';
 
26721
                $this->xobjects[$this->xobjid]['xobjects'] = array();
 
26722
                $this->xobjects[$this->xobjid]['images'] = array();
 
26723
                $this->xobjects[$this->xobjid]['fonts'] = array();
 
26724
                $this->xobjects[$this->xobjid]['annotations'] = array();
 
26725
                $this->xobjects[$this->xobjid]['extgstates'] = array();
 
26726
                $this->xobjects[$this->xobjid]['gradients'] = array();
 
26727
                $this->xobjects[$this->xobjid]['spot_colors'] = array();
 
26728
                // set new environment
 
26729
                $this->num_columns = 1;
 
26730
                $this->current_column = 0;
 
26731
                $this->SetAutoPageBreak(false);
 
26732
                if (($w === '') OR ($w <= 0)) {
 
26733
                        $w = $this->w - $this->lMargin - $this->rMargin;
 
26734
                }
 
26735
                if (($h === '') OR ($h <= 0)) {
 
26736
                        $h = $this->h - $this->tMargin - $this->bMargin;
 
26737
                }
 
26738
                $this->xobjects[$this->xobjid]['x'] = 0;
 
26739
                $this->xobjects[$this->xobjid]['y'] = 0;
 
26740
                $this->xobjects[$this->xobjid]['w'] = $w;
 
26741
                $this->xobjects[$this->xobjid]['h'] = $h;
 
26742
                $this->w = $w;
 
26743
                $this->h = $h;
 
26744
                $this->wPt = $this->w * $this->k;
 
26745
                $this->hPt = $this->h * $this->k;
 
26746
                $this->fwPt = $this->wPt;
 
26747
                $this->fhPt = $this->hPt;
 
26748
                $this->x = 0;
 
26749
                $this->y = 0;
 
26750
                $this->lMargin = 0;
 
26751
                $this->rMargin = 0;
 
26752
                $this->tMargin = 0;
 
26753
                $this->bMargin = 0;
 
26754
                // set group mode
 
26755
                $this->xobjects[$this->xobjid]['group'] = $group;
 
26756
                return $this->xobjid;
 
26757
        }
 
26758
 
 
26759
        /**
 
26760
         * End the current XObject Template started with startTemplate() and restore the previous graphic state.
 
26761
         * 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).
 
26762
         * 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.
 
26763
         * @return int the XObject Template ID in case of success or false in case of error.
 
26764
         * @author Nicola Asuni
 
26765
         * @public
 
26766
         * @since 5.8.017 (2010-08-24)
 
26767
         * @see startTemplate(), printTemplate()
 
26768
         */
 
26769
        public function endTemplate() {
 
26770
                if (!$this->inxobj) {
 
26771
                        // we are not inside a template
 
26772
                        return false;
 
26773
                }
 
26774
                $this->inxobj = false;
 
26775
                // restore previous graphic state
 
26776
                $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true);
 
26777
                return $this->xobjid;
 
26778
        }
 
26779
 
 
26780
        /**
 
26781
         * Print an XObject Template.
 
26782
         * You can print an XObject Template inside the currently opened Template.
 
26783
         * 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).
 
26784
         * 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.
 
26785
         * @param $id (string) The ID of XObject Template to print.
 
26786
         * @param $x (int) X position in user units (empty string = current x position)
 
26787
         * @param $y (int) Y position in user units (empty string = current y position)
 
26788
         * @param $w (int) Width in user units (zero = remaining page width)
 
26789
         * @param $h (int) Height in user units (zero = remaining page height)
 
26790
         * @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>
 
26791
         * @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>
 
26792
         * @param $fitonpage (boolean) If true the template is resized to not exceed page dimensions.
 
26793
         * @author Nicola Asuni
 
26794
         * @public
 
26795
         * @since 5.8.017 (2010-08-24)
 
26796
         * @see startTemplate(), endTemplate()
 
26797
         */
 
26798
        public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
 
26799
                if (!isset($this->xobjects[$id])) {
 
26800
                        $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
 
26801
                }
 
26802
                if ($this->inxobj) {
 
26803
                        if ($id == $this->xobjid) {
 
26804
                                // close current template
 
26805
                                $this->endTemplate();
 
26806
                        } else {
 
26807
                                // use the template as resource for the template currently opened
 
26808
                                $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id];
 
26809
                        }
 
26810
                }
 
26811
                // set default values
 
26812
                if ($x === '') {
 
26813
                        $x = $this->x;
 
26814
                }
 
26815
                if ($y === '') {
 
26816
                        $y = $this->y;
 
26817
                }
 
26818
                // check page for no-write regions and adapt page margins if necessary
 
26819
                list($x, $y) = $this->checkPageRegions($h, $x, $y);
 
26820
                $ow = $this->xobjects[$id]['w'];
 
26821
                $oh = $this->xobjects[$id]['h'];
 
26822
                // calculate template width and height on document
 
26823
                if (($w <= 0) AND ($h <= 0)) {
 
26824
                        $w = $ow;
 
26825
                        $h = $oh;
 
26826
                } elseif ($w <= 0) {
 
26827
                        $w = $h * $ow / $oh;
 
26828
                } elseif ($h <= 0) {
 
26829
                        $h = $w * $oh / $ow;
 
26830
                }
 
26831
                // fit the template on available space
 
26832
                list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
 
26833
                // set page alignment
 
26834
                $rb_y = $y + $h;
 
26835
                // set alignment
 
26836
                if ($this->rtl) {
 
26837
                        if ($palign == 'L') {
 
26838
                                $xt = $this->lMargin;
 
26839
                        } elseif ($palign == 'C') {
 
26840
                                $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
 
26841
                        } elseif ($palign == 'R') {
 
26842
                                $xt = $this->w - $this->rMargin - $w;
 
26843
                        } else {
 
26844
                                $xt = $x - $w;
 
26845
                        }
 
26846
                        $rb_x = $xt;
 
26847
                } else {
 
26848
                        if ($palign == 'L') {
 
26849
                                $xt = $this->lMargin;
 
26850
                        } elseif ($palign == 'C') {
 
26851
                                $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
 
26852
                        } elseif ($palign == 'R') {
 
26853
                                $xt = $this->w - $this->rMargin - $w;
 
26854
                        } else {
 
26855
                                $xt = $x;
 
26856
                        }
 
26857
                        $rb_x = $xt + $w;
 
26858
                }
 
26859
                // print XObject Template + Transformation matrix
 
26860
                $this->StartTransform();
 
26861
                // translate and scale
 
26862
                $sx = ($w / $this->xobjects[$id]['w']);
 
26863
                $sy = ($h / $this->xobjects[$id]['h']);
 
26864
                $tm = array();
 
26865
                $tm[0] = $sx;
 
26866
                $tm[1] = 0;
 
26867
                $tm[2] = 0;
 
26868
                $tm[3] = $sy;
 
26869
                $tm[4] = $xt * $this->k;
 
26870
                $tm[5] = ($this->h - $h - $y) * $this->k;
 
26871
                $this->Transform($tm);
 
26872
                // set object
 
26873
                $this->_out('/'.$id.' Do');
 
26874
                $this->StopTransform();
 
26875
                // add annotations
 
26876
                if (!empty($this->xobjects[$id]['annotations'])) {
 
26877
                        foreach ($this->xobjects[$id]['annotations'] as $annot) {
 
26878
                                // transform original coordinates
 
26879
                                $coordlt = $this->getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k)));
 
26880
                                $ax = ($coordlt[4] / $this->k);
 
26881
                                $ay = ($this->h - $h - ($coordlt[5] / $this->k));
 
26882
                                $coordrb = $this->getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k)));
 
26883
                                $aw = ($coordrb[4] / $this->k) - $ax;
 
26884
                                $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay;
 
26885
                                $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
 
26886
                        }
 
26887
                }
 
26888
                // set pointer to align the next text/objects
 
26889
                switch($align) {
 
26890
                        case 'T': {
 
26891
                                $this->y = $y;
 
26892
                                $this->x = $rb_x;
 
26893
                                break;
 
26894
                        }
 
26895
                        case 'M': {
 
26896
                                $this->y = $y + round($h/2);
 
26897
                                $this->x = $rb_x;
 
26898
                                break;
 
26899
                        }
 
26900
                        case 'B': {
 
26901
                                $this->y = $rb_y;
 
26902
                                $this->x = $rb_x;
 
26903
                                break;
 
26904
                        }
 
26905
                        case 'N': {
 
26906
                                $this->SetY($rb_y);
 
26907
                                break;
 
26908
                        }
 
26909
                        default:{
 
26910
                                break;
 
26911
                        }
 
26912
                }
 
26913
        }
 
26914
 
 
26915
        /**
 
26916
         * Set the percentage of character stretching.
 
26917
         * @param $perc (int) percentage of stretching (100 = no stretching)
 
26918
         * @author Nicola Asuni
 
26919
         * @public
 
26920
         * @since 5.9.000 (2010-09-29)
 
26921
         */
 
26922
        public function setFontStretching($perc=100) {
 
26923
                $this->font_stretching = $perc;
 
26924
        }
 
26925
 
 
26926
        /**
 
26927
         * Get the percentage of character stretching.
 
26928
         * @return float stretching value
 
26929
         * @author Nicola Asuni
 
26930
         * @public
 
26931
         * @since 5.9.000 (2010-09-29)
 
26932
         */
 
26933
        public function getFontStretching() {
 
26934
                return $this->font_stretching;
 
26935
        }
 
26936
 
 
26937
        /**
 
26938
         * Set the amount to increase or decrease the space between characters in a text.
 
26939
         * @param $spacing (float) amount to increase or decrease the space between characters in a text (0 = default spacing)
 
26940
         * @author Nicola Asuni
 
26941
         * @public
 
26942
         * @since 5.9.000 (2010-09-29)
 
26943
         */
 
26944
        public function setFontSpacing($spacing=0) {
 
26945
                $this->font_spacing = $spacing;
 
26946
        }
 
26947
 
 
26948
        /**
 
26949
         * Get the amount to increase or decrease the space between characters in a text.
 
26950
         * @return int font spacing (tracking/kerning) value
 
26951
         * @author Nicola Asuni
 
26952
         * @public
 
26953
         * @since 5.9.000 (2010-09-29)
 
26954
         */
 
26955
        public function getFontSpacing() {
 
26956
                return $this->font_spacing;
 
26957
        }
 
26958
 
 
26959
        /**
 
26960
         * Return an array of no-write page regions
 
26961
         * @return array of no-write page regions
 
26962
         * @author Nicola Asuni
 
26963
         * @public
 
26964
         * @since 5.9.003 (2010-10-13)
 
26965
         * @see setPageRegions(), addPageRegion()
 
26966
         */
 
26967
        public function getPageRegions() {
 
26968
                return $this->page_regions;
 
26969
        }
 
26970
 
 
26971
        /**
 
26972
         * Set no-write regions on page.
 
26973
         * 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.
 
26974
         * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
 
26975
         * You can set multiple regions for the same page.
 
26976
         * @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.
 
26977
         * @author Nicola Asuni
 
26978
         * @public
 
26979
         * @since 5.9.003 (2010-10-13)
 
26980
         * @see addPageRegion(), getPageRegions()
 
26981
         */
 
26982
        public function setPageRegions($regions=array()) {
 
26983
                // empty current regions array
 
26984
                $this->page_regions = array();
 
26985
                // add regions
 
26986
                foreach ($regions as $data) {
 
26987
                        $this->addPageRegion($data);
 
26988
                }
 
26989
        }
 
26990
 
 
26991
        /**
 
26992
         * Add a single no-write region on selected page.
 
26993
         * 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.
 
26994
         * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
 
26995
         * You can set multiple regions for the same page.
 
26996
         * @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).
 
26997
         * @author Nicola Asuni
 
26998
         * @public
 
26999
         * @since 5.9.003 (2010-10-13)
 
27000
         * @see setPageRegions(), getPageRegions()
 
27001
         */
 
27002
        public function addPageRegion($region) {
 
27003
                if (!isset($region['page']) OR empty($region['page'])) {
 
27004
                        $region['page'] = $this->page;
 
27005
                }
 
27006
                if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
 
27007
                        AND isset($region['yt'])  AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
 
27008
                        AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
 
27009
                        $this->page_regions[] = $region;
 
27010
                }
 
27011
        }
 
27012
 
 
27013
        /**
 
27014
         * Remove a single no-write region.
 
27015
         * @param $key (int) region key
 
27016
         * @author Nicola Asuni
 
27017
         * @public
 
27018
         * @since 5.9.003 (2010-10-13)
 
27019
         * @see setPageRegions(), getPageRegions()
 
27020
         */
 
27021
        public function removePageRegion($key) {
 
27022
                if (isset($this->page_regions[$key])) {
 
27023
                        unset($this->page_regions[$key]);
 
27024
                }
 
27025
        }
 
27026
 
 
27027
        /**
 
27028
         * Check page for no-write regions and adapt current coordinates and page margins if necessary.
 
27029
         * 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.
 
27030
         * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
 
27031
         * @param $h (float) height of the text/image/object to print in user units
 
27032
         * @param $x (float) current X coordinate in user units
 
27033
         * @param $y (float) current Y coordinate in user units
 
27034
         * @return array($x, $y)
 
27035
         * @author Nicola Asuni
 
27036
         * @protected
 
27037
         * @since 5.9.003 (2010-10-13)
 
27038
         */
 
27039
        protected function checkPageRegions($h, $x, $y) {
 
27040
                // set default values
 
27041
                if ($x === '') {
 
27042
                        $x = $this->x;
 
27043
                }
 
27044
                if ($y === '') {
 
27045
                        $y = $this->y;
 
27046
                }
 
27047
                if (empty($this->page_regions)) {
 
27048
                        // no page regions defined
 
27049
                        return array($x, $y);
 
27050
                }
 
27051
                if (empty($h)) {
 
27052
                        $h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
 
27053
                }
 
27054
                // check for page break
 
27055
                if ($this->checkPageBreak($h, $y)) {
 
27056
                        // the content will be printed on a new page
 
27057
                        $x = $this->x;
 
27058
                        $y = $this->y;
 
27059
                }
 
27060
                if ($this->num_columns > 1) {
 
27061
                        if ($this->rtl) {
 
27062
                                $this->lMargin = $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w'];
 
27063
                        } else {
 
27064
                                $this->rMargin = $this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w'];
 
27065
                        }
 
27066
                } else {
 
27067
                        if ($this->rtl) {
 
27068
                                $this->lMargin = $this->original_lMargin;
 
27069
                        } else {
 
27070
                                $this->rMargin = $this->original_rMargin;
 
27071
                        }
 
27072
                }
 
27073
                // adjust coordinates and page margins
 
27074
                foreach ($this->page_regions as $regid => $regdata) {
 
27075
                        if ($regdata['page'] == $this->page) {
 
27076
                                // check region boundaries
 
27077
                                if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
 
27078
                                        // Y is inside the region
 
27079
                                        $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
 
27080
                                        $yt = max($y, $regdata['yt']);
 
27081
                                        $yb = min(($yt + $h), $regdata['yb']);
 
27082
                                        $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt'];
 
27083
                                        $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt'];
 
27084
                                        if ($regdata['side'] == 'L') { // left side
 
27085
                                                $new_margin = max($xt, $xb);
 
27086
                                                if ($this->lMargin < $new_margin) {
 
27087
                                                        if ($this->rtl) {
 
27088
                                                                // adjust left page margin
 
27089
                                                                $this->lMargin = $new_margin;
 
27090
                                                        }
 
27091
                                                        if ($x < $new_margin) {
 
27092
                                                                // adjust x position
 
27093
                                                                $x = $new_margin;
 
27094
                                                                if ($new_margin > ($this->w - $this->rMargin)) {
 
27095
                                                                        // adjust y position
 
27096
                                                                        $y = $regdata['yb'] - $h;
 
27097
                                                                }
 
27098
                                                        }
 
27099
                                                }
 
27100
                                        } elseif ($regdata['side'] == 'R') { // right side
 
27101
                                                $new_margin = min($xt, $xb);
 
27102
                                                if (($this->w - $this->rMargin) > $new_margin) {
 
27103
                                                        if (!$this->rtl) {
 
27104
                                                                // adjust right page margin
 
27105
                                                                $this->rMargin = ($this->w - $new_margin);
 
27106
                                                        }
 
27107
                                                        if ($x > $new_margin) {
 
27108
                                                                // adjust x position
 
27109
                                                                $x = $new_margin;
 
27110
                                                                if ($new_margin > $this->lMargin) {
 
27111
                                                                        // adjust y position
 
27112
                                                                        $y = $regdata['yb'] - $h;
 
27113
                                                                }
 
27114
                                                        }
 
27115
                                                }
 
27116
                                        }
 
27117
                                }
 
27118
                        }
 
27119
                }
 
27120
                return array($x, $y);
 
27121
        }
 
27122
 
 
27123
        // --- SVG METHODS ---------------------------------------------------------
 
27124
 
 
27125
        /**
 
27126
         * Embedd a Scalable Vector Graphics (SVG) image.
 
27127
         * NOTE: SVG standard is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
 
27128
         * @param $file (string) Name of the SVG file or a '@' character followed by the SVG data string.
 
27129
         * @param $x (float) Abscissa of the upper-left corner.
 
27130
         * @param $y (float) Ordinate of the upper-left corner.
 
27131
         * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
 
27132
         * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
 
27133
         * @param $link (mixed) URL or identifier returned by AddLink().
 
27134
         * @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.
 
27135
         * @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>
 
27136
         * @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)))
 
27137
         * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
 
27138
         * @author Nicola Asuni
 
27139
         * @since 5.0.000 (2010-05-02)
 
27140
         * @public
 
27141
         */
 
27142
        public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
 
27143
                if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
 
27144
                        // convert SVG to raster image using GD or ImageMagick libraries
 
27145
                        return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
 
27146
                }
 
27147
                if ($file{0} === '@') { // image from string
 
27148
                        $this->svgdir = '';
 
27149
                        $svgdata = substr($file, 1);
 
27150
                } else { // SVG file
 
27151
                        $this->svgdir = dirname($file);
 
27152
                        $svgdata = file_get_contents($file);
 
27153
                }
 
27154
                if ($svgdata === false) {
 
27155
                        $this->Error('SVG file not found: '.$file);
 
27156
                }
 
27157
                if ($x === '') {
 
27158
                        $x = $this->x;
 
27159
                }
 
27160
                if ($y === '') {
 
27161
                        $y = $this->y;
 
27162
                }
 
27163
                // check page for no-write regions and adapt page margins if necessary
 
27164
                list($x, $y) = $this->checkPageRegions($h, $x, $y);
 
27165
                $k = $this->k;
 
27166
                $ox = 0;
 
27167
                $oy = 0;
 
27168
                $ow = $w;
 
27169
                $oh = $h;
 
27170
                $aspect_ratio_align = 'xMidYMid';
 
27171
                $aspect_ratio_ms = 'meet';
 
27172
                $regs = array();
 
27173
                // get original image width and height
 
27174
                preg_match('/<svg([^\>]*)>/si', $svgdata, $regs);
 
27175
                if (isset($regs[1]) AND !empty($regs[1])) {
 
27176
                        $tmp = array();
 
27177
                        if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
 
27178
                                $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
 
27179
                        }
 
27180
                        $tmp = array();
 
27181
                        if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
 
27182
                                $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
 
27183
                        }
 
27184
                        $tmp = array();
 
27185
                        if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
 
27186
                                $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
 
27187
                        }
 
27188
                        $tmp = array();
 
27189
                        if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
 
27190
                                $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
 
27191
                        }
 
27192
                        $tmp = array();
 
27193
                        $view_box = array();
 
27194
                        if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
 
27195
                                if (count($tmp) == 5) {
 
27196
                                        array_shift($tmp);
 
27197
                                        foreach ($tmp as $key => $val) {
 
27198
                                                $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
 
27199
                                        }
 
27200
                                        $ox = $view_box[0];
 
27201
                                        $oy = $view_box[1];
 
27202
                                }
 
27203
                                // get aspect ratio
 
27204
                                $tmp = array();
 
27205
                                if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
 
27206
                                        $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
 
27207
                                        switch (count($aspect_ratio)) {
 
27208
                                                case 3: {
 
27209
                                                        $aspect_ratio_align = $aspect_ratio[1];
 
27210
                                                        $aspect_ratio_ms = $aspect_ratio[2];
 
27211
                                                        break;
 
27212
                                                }
 
27213
                                                case 2: {
 
27214
                                                        $aspect_ratio_align = $aspect_ratio[0];
 
27215
                                                        $aspect_ratio_ms = $aspect_ratio[1];
 
27216
                                                        break;
 
27217
                                                }
 
27218
                                                case 1: {
 
27219
                                                        $aspect_ratio_align = $aspect_ratio[0];
 
27220
                                                        $aspect_ratio_ms = 'meet';
 
27221
                                                        break;
 
27222
                                                }
 
27223
                                        }
 
27224
                                }
 
27225
                        }
 
27226
                }
 
27227
                // calculate image width and height on document
 
27228
                if (($w <= 0) AND ($h <= 0)) {
 
27229
                        // convert image size to document unit
 
27230
                        $w = $ow;
 
27231
                        $h = $oh;
 
27232
                } elseif ($w <= 0) {
 
27233
                        $w = $h * $ow / $oh;
 
27234
                } elseif ($h <= 0) {
 
27235
                        $h = $w * $oh / $ow;
 
27236
                }
 
27237
                // fit the image on available space
 
27238
                list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
 
27239
                if ($this->rasterize_vector_images) {
 
27240
                        // convert SVG to raster image using GD or ImageMagick libraries
 
27241
                        return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
 
27242
                }
 
27243
                // set alignment
 
27244
                $this->img_rb_y = $y + $h;
 
27245
                // set alignment
 
27246
                if ($this->rtl) {
 
27247
                        if ($palign == 'L') {
 
27248
                                $ximg = $this->lMargin;
 
27249
                        } elseif ($palign == 'C') {
 
27250
                                $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
 
27251
                        } elseif ($palign == 'R') {
 
27252
                                $ximg = $this->w - $this->rMargin - $w;
 
27253
                        } else {
 
27254
                                $ximg = $x - $w;
 
27255
                        }
 
27256
                        $this->img_rb_x = $ximg;
 
27257
                } else {
 
27258
                        if ($palign == 'L') {
 
27259
                                $ximg = $this->lMargin;
 
27260
                        } elseif ($palign == 'C') {
 
27261
                                $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
 
27262
                        } elseif ($palign == 'R') {
 
27263
                                $ximg = $this->w - $this->rMargin - $w;
 
27264
                        } else {
 
27265
                                $ximg = $x;
 
27266
                        }
 
27267
                        $this->img_rb_x = $ximg + $w;
 
27268
                }
 
27269
                // store current graphic vars
 
27270
                $gvars = $this->getGraphicVars();
 
27271
                // store SVG position and scale factors
 
27272
                $svgoffset_x = ($ximg - $ox) * $this->k;
 
27273
                $svgoffset_y = -($y - $oy) * $this->k;
 
27274
                if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
 
27275
                        $ow = $view_box[2];
 
27276
                        $oh = $view_box[3];
 
27277
                } else {
 
27278
                        if ($ow <= 0) {
 
27279
                                $ow = $w;
 
27280
                        }
 
27281
                        if ($oh <= 0) {
 
27282
                                $oh = $h;
 
27283
                        }
 
27284
                }
 
27285
                $svgscale_x = $w / $ow;
 
27286
                $svgscale_y = $h / $oh;
 
27287
                // scaling and alignment
 
27288
                if ($aspect_ratio_align != 'none') {
 
27289
                        // store current scaling values
 
27290
                        $svgscale_old_x = $svgscale_x;
 
27291
                        $svgscale_old_y = $svgscale_y;
 
27292
                        // force uniform scaling
 
27293
                        if ($aspect_ratio_ms == 'slice') {
 
27294
                                // the entire viewport is covered by the viewBox
 
27295
                                if ($svgscale_x > $svgscale_y) {
 
27296
                                        $svgscale_y = $svgscale_x;
 
27297
                                } elseif ($svgscale_x < $svgscale_y) {
 
27298
                                        $svgscale_x = $svgscale_y;
 
27299
                                }
 
27300
                        } else { // meet
 
27301
                                // the entire viewBox is visible within the viewport
 
27302
                                if ($svgscale_x < $svgscale_y) {
 
27303
                                        $svgscale_y = $svgscale_x;
 
27304
                                } elseif ($svgscale_x > $svgscale_y) {
 
27305
                                        $svgscale_x = $svgscale_y;
 
27306
                                }
 
27307
                        }
 
27308
                        // correct X alignment
 
27309
                        switch (substr($aspect_ratio_align, 1, 3)) {
 
27310
                                case 'Min': {
 
27311
                                        // do nothing
 
27312
                                        break;
 
27313
                                }
 
27314
                                case 'Max': {
 
27315
                                        $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x));
 
27316
                                        break;
 
27317
                                }
 
27318
                                default:
 
27319
                                case 'Mid': {
 
27320
                                        $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2);
 
27321
                                        break;
 
27322
                                }
 
27323
                        }
 
27324
                        // correct Y alignment
 
27325
                        switch (substr($aspect_ratio_align, 5)) {
 
27326
                                case 'Min': {
 
27327
                                        // do nothing
 
27328
                                        break;
 
27329
                                }
 
27330
                                case 'Max': {
 
27331
                                        $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y));
 
27332
                                        break;
 
27333
                                }
 
27334
                                default:
 
27335
                                case 'Mid': {
 
27336
                                        $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2);
 
27337
                                        break;
 
27338
                                }
 
27339
                        }
 
27340
                }
 
27341
                // store current page break mode
 
27342
                $page_break_mode = $this->AutoPageBreak;
 
27343
                $page_break_margin = $this->getBreakMargin();
 
27344
                $cell_padding = $this->cell_padding;
 
27345
                $this->SetCellPadding(0);
 
27346
                $this->SetAutoPageBreak(false);
 
27347
                // save the current graphic state
 
27348
                $this->_out('q'.$this->epsmarker);
 
27349
                // set initial clipping mask
 
27350
                $this->Rect($x, $y, $w, $h, 'CNZ', array(), array());
 
27351
                // scale and translate
 
27352
                $e = $ox * $this->k * (1 - $svgscale_x);
 
27353
                $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y);
 
27354
                $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $svgscale_x, 0, 0, $svgscale_y, $e + $svgoffset_x, $f + $svgoffset_y));
 
27355
                // creates a new XML parser to be used by the other XML functions
 
27356
                $this->parser = xml_parser_create('UTF-8');
 
27357
                // the following function allows to use parser inside object
 
27358
                xml_set_object($this->parser, $this);
 
27359
                // disable case-folding for this XML parser
 
27360
                xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
 
27361
                // sets the element handler functions for the XML parser
 
27362
                xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler');
 
27363
                // sets the character data handler function for the XML parser
 
27364
                xml_set_character_data_handler($this->parser, 'segSVGContentHandler');
 
27365
                // start parsing an XML document
 
27366
                if (!xml_parse($this->parser, $svgdata)) {
 
27367
                        $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));
 
27368
                        $this->Error($error_message);
 
27369
                }
 
27370
                // free this XML parser
 
27371
                xml_parser_free($this->parser);
 
27372
                // restore previous graphic state
 
27373
                $this->_out($this->epsmarker.'Q');
 
27374
                // restore graphic vars
 
27375
                $this->setGraphicVars($gvars);
 
27376
                $this->lasth = $gvars['lasth'];
 
27377
                if (!empty($border)) {
 
27378
                        $bx = $this->x;
 
27379
                        $by = $this->y;
 
27380
                        $this->x = $ximg;
 
27381
                        if ($this->rtl) {
 
27382
                                $this->x += $w;
 
27383
                        }
 
27384
                        $this->y = $y;
 
27385
                        $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
 
27386
                        $this->x = $bx;
 
27387
                        $this->y = $by;
 
27388
                }
 
27389
                if ($link) {
 
27390
                        $this->Link($ximg, $y, $w, $h, $link, 0);
 
27391
                }
 
27392
                // set pointer to align the next text/objects
 
27393
                switch($align) {
 
27394
                        case 'T':{
 
27395
                                $this->y = $y;
 
27396
                                $this->x = $this->img_rb_x;
 
27397
                                break;
 
27398
                        }
 
27399
                        case 'M':{
 
27400
                                $this->y = $y + round($h/2);
 
27401
                                $this->x = $this->img_rb_x;
 
27402
                                break;
 
27403
                        }
 
27404
                        case 'B':{
 
27405
                                $this->y = $this->img_rb_y;
 
27406
                                $this->x = $this->img_rb_x;
 
27407
                                break;
 
27408
                        }
 
27409
                        case 'N':{
 
27410
                                $this->SetY($this->img_rb_y);
 
27411
                                break;
 
27412
                        }
 
27413
                        default:{
 
27414
                                // restore pointer to starting position
 
27415
                                $this->x = $gvars['x'];
 
27416
                                $this->y = $gvars['y'];
 
27417
                                $this->page = $gvars['page'];
 
27418
                                $this->current_column = $gvars['current_column'];
 
27419
                                $this->tMargin = $gvars['tMargin'];
 
27420
                                $this->bMargin = $gvars['bMargin'];
 
27421
                                $this->w = $gvars['w'];
 
27422
                                $this->h = $gvars['h'];
 
27423
                                $this->wPt = $gvars['wPt'];
 
27424
                                $this->hPt = $gvars['hPt'];
 
27425
                                $this->fwPt = $gvars['fwPt'];
 
27426
                                $this->fhPt = $gvars['fhPt'];
 
27427
                                break;
 
27428
                        }
 
27429
                }
 
27430
                $this->endlinex = $this->img_rb_x;
 
27431
                // restore page break
 
27432
                $this->SetAutoPageBreak($page_break_mode, $page_break_margin);
 
27433
                $this->cell_padding = $cell_padding;
 
27434
        }
 
27435
 
 
27436
        /**
 
27437
         * Get the tranformation matrix from SVG transform attribute
 
27438
         * @param $attribute (string) transformation
 
27439
         * @return array of transformations
 
27440
         * @author Nicola Asuni
 
27441
         * @since 5.0.000 (2010-05-02)
 
27442
         * @protected
 
27443
         */
 
27444
        protected function getSVGTransformMatrix($attribute) {
 
27445
                // identity matrix
 
27446
                $tm = array(1, 0, 0, 1, 0, 0);
 
27447
                $transform = array();
 
27448
                if (preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)[\s]*\(([^\)]+)\)/si', $attribute, $transform, PREG_SET_ORDER) > 0) {
 
27449
                        foreach ($transform as $key => $data) {
 
27450
                                if (!empty($data[2])) {
 
27451
                                        $a = 1;
 
27452
                                        $b = 0;
 
27453
                                        $c = 0;
 
27454
                                        $d = 1;
 
27455
                                        $e = 0;
 
27456
                                        $f = 0;
 
27457
                                        $regs = array();
 
27458
                                        switch ($data[1]) {
 
27459
                                                case 'matrix': {
 
27460
                                                        if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
 
27461
                                                                $a = $regs[1];
 
27462
                                                                $b = $regs[2];
 
27463
                                                                $c = $regs[3];
 
27464
                                                                $d = $regs[4];
 
27465
                                                                $e = $regs[5];
 
27466
                                                                $f = $regs[6];
 
27467
                                                        }
 
27468
                                                        break;
 
27469
                                                }
 
27470
                                                case 'translate': {
 
27471
                                                        if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
 
27472
                                                                $e = $regs[1];
 
27473
                                                                $f = $regs[2];
 
27474
                                                        } elseif (preg_match('/([a-z0-9\-\.]+)/si', $data[2], $regs)) {
 
27475
                                                                $e = $regs[1];
 
27476
                                                        }
 
27477
                                                        break;
 
27478
                                                }
 
27479
                                                case 'scale': {
 
27480
                                                        if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
 
27481
                                                                $a = $regs[1];
 
27482
                                                                $d = $regs[2];
 
27483
                                                        } elseif (preg_match('/([a-z0-9\-\.]+)/si', $data[2], $regs)) {
 
27484
                                                                $a = $regs[1];
 
27485
                                                                $d = $a;
 
27486
                                                        }
 
27487
                                                        break;
 
27488
                                                }
 
27489
                                                case 'rotate': {
 
27490
                                                        if (preg_match('/([0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
 
27491
                                                                $ang = deg2rad($regs[1]);
 
27492
                                                                $x = $regs[2];
 
27493
                                                                $y = $regs[3];
 
27494
                                                                $a = cos($ang);
 
27495
                                                                $b = sin($ang);
 
27496
                                                                $c = -$b;
 
27497
                                                                $d = $a;
 
27498
                                                                $e = ($x * (1 - $a)) - ($y * $c);
 
27499
                                                                $f = ($y * (1 - $d)) - ($x * $b);
 
27500
                                                        } elseif (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) {
 
27501
                                                                $ang = deg2rad($regs[1]);
 
27502
                                                                $a = cos($ang);
 
27503
                                                                $b = sin($ang);
 
27504
                                                                $c = -$b;
 
27505
                                                                $d = $a;
 
27506
                                                                $e = 0;
 
27507
                                                                $f = 0;
 
27508
                                                        }
 
27509
                                                        break;
 
27510
                                                }
 
27511
                                                case 'skewX': {
 
27512
                                                        if (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) {
 
27513
                                                                $c = tan(deg2rad($regs[1]));
 
27514
                                                        }
 
27515
                                                        break;
 
27516
                                                }
 
27517
                                                case 'skewY': {
 
27518
                                                        if (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) {
 
27519
                                                                $b = tan(deg2rad($regs[1]));
 
27520
                                                        }
 
27521
                                                        break;
 
27522
                                                }
 
27523
                                        }
 
27524
                                        $tm = $this->getTransformationMatrixProduct($tm, array($a, $b, $c, $d, $e, $f));
 
27525
                                }
 
27526
                        }
 
27527
                }
 
27528
                return $tm;
 
27529
        }
 
27530
 
 
27531
        /**
 
27532
         * Get the product of two SVG tranformation matrices
 
27533
         * @param $ta (array) first SVG tranformation matrix
 
27534
         * @param $tb (array) second SVG tranformation matrix
 
27535
         * @return transformation array
 
27536
         * @author Nicola Asuni
 
27537
         * @since 5.0.000 (2010-05-02)
 
27538
         * @protected
 
27539
         */
 
27540
        protected function getTransformationMatrixProduct($ta, $tb) {
 
27541
                $tm = array();
 
27542
                $tm[0] = ($ta[0] * $tb[0]) + ($ta[2] * $tb[1]);
 
27543
                $tm[1] = ($ta[1] * $tb[0]) + ($ta[3] * $tb[1]);
 
27544
                $tm[2] = ($ta[0] * $tb[2]) + ($ta[2] * $tb[3]);
 
27545
                $tm[3] = ($ta[1] * $tb[2]) + ($ta[3] * $tb[3]);
 
27546
                $tm[4] = ($ta[0] * $tb[4]) + ($ta[2] * $tb[5]) + $ta[4];
 
27547
                $tm[5] = ($ta[1] * $tb[4]) + ($ta[3] * $tb[5]) + $ta[5];
 
27548
                return $tm;
 
27549
        }
 
27550
 
 
27551
        /**
 
27552
         * Convert SVG transformation matrix to PDF.
 
27553
         * @param $tm (array) original SVG transformation matrix
 
27554
         * @return array transformation matrix
 
27555
         * @protected
 
27556
         * @since 5.0.000 (2010-05-02)
 
27557
         */
 
27558
        protected function convertSVGtMatrix($tm) {
 
27559
                $a = $tm[0];
 
27560
                $b = -$tm[1];
 
27561
                $c = -$tm[2];
 
27562
                $d = $tm[3];
 
27563
                $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k;
 
27564
                $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k;
 
27565
                $x = 0;
 
27566
                $y = $this->h * $this->k;
 
27567
                $e = ($x * (1 - $a)) - ($y * $c) + $e;
 
27568
                $f = ($y * (1 - $d)) - ($x * $b) + $f;
 
27569
                return array($a, $b, $c, $d, $e, $f);
 
27570
        }
 
27571
 
 
27572
        /**
 
27573
         * Apply SVG graphic transformation matrix.
 
27574
         * @param $tm (array) original SVG transformation matrix
 
27575
         * @protected
 
27576
         * @since 5.0.000 (2010-05-02)
 
27577
         */
 
27578
        protected function SVGTransform($tm) {
 
27579
                $this->Transform($this->convertSVGtMatrix($tm));
 
27580
        }
 
27581
 
 
27582
        /**
 
27583
         * Apply the requested SVG styles (*** TO BE COMPLETED ***)
 
27584
         * @param $svgstyle (array) array of SVG styles to apply
 
27585
         * @param $prevsvgstyle (array) array of previous SVG style
 
27586
         * @param $x (int) X origin of the bounding box
 
27587
         * @param $y (int) Y origin of the bounding box
 
27588
         * @param $w (int) width of the bounding box
 
27589
         * @param $h (int) height of the bounding box
 
27590
         * @param $clip_function (string) clip function
 
27591
         * @param $clip_params (array) array of parameters for clipping function
 
27592
         * @return object style
 
27593
         * @author Nicola Asuni
 
27594
         * @since 5.0.000 (2010-05-02)
 
27595
         * @protected
 
27596
         */
 
27597
        protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
 
27598
                $objstyle = '';
 
27599
                $minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
 
27600
                if (!isset($svgstyle['opacity'])) {
 
27601
                        return $objstyle;
 
27602
                }
 
27603
                // clip-path
 
27604
                $regs = array();
 
27605
                if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) {
 
27606
                        $clip_path = $this->svgclippaths[$regs[1]];
 
27607
                        foreach ($clip_path as $cp) {
 
27608
                                $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
 
27609
                        }
 
27610
                }
 
27611
                // opacity
 
27612
                if ($svgstyle['opacity'] != 1) {
 
27613
                        $this->SetAlpha($svgstyle['opacity']);
 
27614
                }
 
27615
                // color
 
27616
                $fill_color = $this->convertHTMLColorToDec($svgstyle['color']);
 
27617
                $this->SetFillColorArray($fill_color);
 
27618
                // text color
 
27619
                $text_color = $this->convertHTMLColorToDec($svgstyle['text-color']);
 
27620
                $this->SetTextColorArray($text_color);
 
27621
                // clip
 
27622
                if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) {
 
27623
                        $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0);
 
27624
                        $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0);
 
27625
                        $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0);
 
27626
                        $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0);
 
27627
                        $cx = $x + $left;
 
27628
                        $cy = $y + $top;
 
27629
                        $cw = $w - $left - $right;
 
27630
                        $ch = $h - $top - $bottom;
 
27631
                        if ($svgstyle['clip-rule'] == 'evenodd') {
 
27632
                                $clip_rule = 'CNZ';
 
27633
                        } else {
 
27634
                                $clip_rule = 'CEO';
 
27635
                        }
 
27636
                        $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
 
27637
                }
 
27638
                // fill
 
27639
                $regs = array();
 
27640
                if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) {
 
27641
                        // gradient
 
27642
                        $gradient = $this->svggradients[$regs[1]];
 
27643
                        if (isset($gradient['xref'])) {
 
27644
                                // reference to another gradient definition
 
27645
                                $newgradient = $this->svggradients[$gradient['xref']];
 
27646
                                $newgradient['coords'] = $gradient['coords'];
 
27647
                                $newgradient['mode'] = $gradient['mode'];
 
27648
                                $newgradient['gradientUnits'] = $gradient['gradientUnits'];
 
27649
                                if (isset($gradient['gradientTransform'])) {
 
27650
                                        $newgradient['gradientTransform'] = $gradient['gradientTransform'];
 
27651
                                }
 
27652
                                $gradient = $newgradient;
 
27653
                        }
 
27654
                        //save current Graphic State
 
27655
                        $this->_out('q');
 
27656
                        //set clipping area
 
27657
                        if (!empty($clip_function) AND method_exists($this, $clip_function)) {
 
27658
                                $bbox = call_user_func_array(array($this, $clip_function), $clip_params);
 
27659
                                if (is_array($bbox) AND (count($bbox) == 4)) {
 
27660
                                        list($x, $y, $w, $h) = $bbox;
 
27661
                                }
 
27662
                        }
 
27663
                        if ($gradient['mode'] == 'measure') {
 
27664
                                if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
 
27665
                                        $gtm = $gradient['gradientTransform'];
 
27666
                                        // apply transformation matrix
 
27667
                                        $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4];
 
27668
                                        $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5];
 
27669
                                        $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4];
 
27670
                                        $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5];
 
27671
                                        if (isset($gradient['coords'][4])) {
 
27672
                                                $gradient['coords'][4] = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2));
 
27673
                                        }
 
27674
                                        $gradient['coords'][0] = $xa;
 
27675
                                        $gradient['coords'][1] = $ya;
 
27676
                                        $gradient['coords'][2] = $xb;
 
27677
                                        $gradient['coords'][3] = $yb;
 
27678
                                }
 
27679
                                // convert SVG coordinates to user units
 
27680
                                $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false);
 
27681
                                $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false);
 
27682
                                $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false);
 
27683
                                $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false);
 
27684
                                if (isset($gradient['coords'][4])) {
 
27685
                                        $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false);
 
27686
                                }
 
27687
                                // shift units
 
27688
                                if ($gradient['gradientUnits'] == 'objectBoundingBox') {
 
27689
                                        // convert to SVG coordinate system
 
27690
                                        $gradient['coords'][0] += $x;
 
27691
                                        $gradient['coords'][1] += $y;
 
27692
                                        $gradient['coords'][2] += $x;
 
27693
                                        $gradient['coords'][3] += $y;
 
27694
                                }
 
27695
                                if ($w <= $minlen) {
 
27696
                                        $w = $minlen;
 
27697
                                }
 
27698
                                if ($h <= $minlen) {
 
27699
                                        $h = $minlen;
 
27700
                                }
 
27701
                                // calculate percentages
 
27702
                                $gradient['coords'][0] = ($gradient['coords'][0] - $x) / $w;
 
27703
                                $gradient['coords'][1] = ($gradient['coords'][1] - $y) / $h;
 
27704
                                $gradient['coords'][2] = ($gradient['coords'][2] - $x) / $w;
 
27705
                                $gradient['coords'][3] = ($gradient['coords'][3] - $y) / $h;
 
27706
                                if (isset($gradient['coords'][4])) {
 
27707
                                        $gradient['coords'][4] /= $w;
 
27708
                                }
 
27709
                        } elseif ($gradient['mode'] == 'percentage') {
 
27710
                                foreach($gradient['coords'] as $key => $val) {
 
27711
                                        $gradient['coords'][$key] = (intval($val) / 100);
 
27712
                                }
 
27713
                        }
 
27714
                        // fix values
 
27715
                        foreach($gradient['coords'] as $key => $val) {
 
27716
                                if ($val < 0) {
 
27717
                                        $gradient['coords'][$key] = 0;
 
27718
                                } elseif ($val > 1) {
 
27719
                                        $gradient['coords'][$key] = 1;
 
27720
                                }
 
27721
                        }
 
27722
                        if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
 
27723
                                // single color (no shading)
 
27724
                                $gradient['coords'][0] = 1;
 
27725
                                $gradient['coords'][1] = 0;
 
27726
                                $gradient['coords'][2] = 0.999;
 
27727
                                $gradient['coords'][3] = 0;
 
27728
                        }
 
27729
                        // swap Y coordinates
 
27730
                        $tmp = $gradient['coords'][1];
 
27731
                        $gradient['coords'][1] = $gradient['coords'][3];
 
27732
                        $gradient['coords'][3] = $tmp;
 
27733
                        // set transformation map for gradient
 
27734
                        if ($gradient['type'] == 3) {
 
27735
                                // gradient is always circular
 
27736
                                $cy = $this->h - $y - ($gradient['coords'][1] * ($w + $h));
 
27737
                                $this->_out(sprintf('%.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $w*$this->k, $x*$this->k, $cy*$this->k));
 
27738
                        } else {
 
27739
                                $this->_out(sprintf('%.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k));
 
27740
                        }
 
27741
                        if (count($gradient['stops']) > 1) {
 
27742
                                $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
 
27743
                        }
 
27744
                } elseif ($svgstyle['fill'] != 'none') {
 
27745
                        $fill_color = $this->convertHTMLColorToDec($svgstyle['fill']);
 
27746
                        if ($svgstyle['fill-opacity'] != 1) {
 
27747
                                $this->SetAlpha($svgstyle['fill-opacity']);
 
27748
                        }
 
27749
                        $this->SetFillColorArray($fill_color);
 
27750
                        if ($svgstyle['fill-rule'] == 'evenodd') {
 
27751
                                $objstyle .= 'F*';
 
27752
                        } else {
 
27753
                                $objstyle .= 'F';
 
27754
                        }
 
27755
                }
 
27756
                // stroke
 
27757
                if ($svgstyle['stroke'] != 'none') {
 
27758
                        $stroke_style = array(
 
27759
                                'color' => $this->convertHTMLColorToDec($svgstyle['stroke']),
 
27760
                                'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false),
 
27761
                                'cap' => $svgstyle['stroke-linecap'],
 
27762
                                'join' => $svgstyle['stroke-linejoin']
 
27763
                                );
 
27764
                        if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
 
27765
                                $stroke_style['dash'] = $svgstyle['stroke-dasharray'];
 
27766
                        }
 
27767
                        $this->SetLineStyle($stroke_style);
 
27768
                        $objstyle .= 'D';
 
27769
                }
 
27770
                // font
 
27771
                $regs = array();
 
27772
                if (!empty($svgstyle['font'])) {
 
27773
                        if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
 
27774
                                $font_family = $this->getFontFamilyName($regs[1]);
 
27775
                        } else {
 
27776
                                $font_family = $svgstyle['font-family'];
 
27777
                        }
 
27778
                        if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
 
27779
                                $font_size = trim($regs[1]);
 
27780
                        } else {
 
27781
                                $font_size = $svgstyle['font-size'];
 
27782
                        }
 
27783
                        if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
 
27784
                                $font_style = trim($regs[1]);
 
27785
                        } else {
 
27786
                                $font_style = $svgstyle['font-style'];
 
27787
                        }
 
27788
                        if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
 
27789
                                $font_weight = trim($regs[1]);
 
27790
                        } else {
 
27791
                                $font_weight = $svgstyle['font-weight'];
 
27792
                        }
 
27793
                        if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
 
27794
                                $font_stretch = trim($regs[1]);
 
27795
                        } else {
 
27796
                                $font_stretch = $svgstyle['font-stretch'];
 
27797
                        }
 
27798
                        if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
 
27799
                                $font_spacing = trim($regs[1]);
 
27800
                        } else {
 
27801
                                $font_spacing = $svgstyle['letter-spacing'];
 
27802
                        }
 
27803
                } else {
 
27804
                        $font_family = $this->getFontFamilyName($svgstyle['font-family']);
 
27805
                        $font_size = $svgstyle['font-size'];
 
27806
                        $font_style = $svgstyle['font-style'];
 
27807
                        $font_weight = $svgstyle['font-weight'];
 
27808
                        $font_stretch = $svgstyle['font-stretch'];
 
27809
                        $font_spacing = $svgstyle['letter-spacing'];
 
27810
                }
 
27811
                $font_size = $this->getHTMLUnitToUnits($font_size, $prevsvgstyle['font-size'], $this->svgunit, false) * $this->k;
 
27812
                $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
 
27813
                $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
 
27814
                switch ($font_style) {
 
27815
                        case 'italic': {
 
27816
                                $font_style = 'I';
 
27817
                                break;
 
27818
                        }
 
27819
                        case 'oblique': {
 
27820
                                $font_style = 'I';
 
27821
                                break;
 
27822
                        }
 
27823
                        default:
 
27824
                        case 'normal': {
 
27825
                                $font_style = '';
 
27826
                                break;
 
27827
                        }
 
27828
                }
 
27829
                switch ($font_weight) {
 
27830
                        case 'bold':
 
27831
                        case 'bolder': {
 
27832
                                $font_style .= 'B';
 
27833
                                break;
 
27834
                        }
 
27835
                }
 
27836
                switch ($svgstyle['text-decoration']) {
 
27837
                        case 'underline': {
 
27838
                                $font_style .= 'U';
 
27839
                                break;
 
27840
                        }
 
27841
                        case 'overline': {
 
27842
                                $font_style .= 'O';
 
27843
                                break;
 
27844
                        }
 
27845
                        case 'line-through': {
 
27846
                                $font_style .= 'D';
 
27847
                                break;
 
27848
                        }
 
27849
                        default:
 
27850
                        case 'none': {
 
27851
                                break;
 
27852
                        }
 
27853
                }
 
27854
                $this->SetFont($font_family, $font_style, $font_size);
 
27855
                $this->setFontStretching($font_stretch);
 
27856
                $this->setFontSpacing($font_spacing);
 
27857
                return $objstyle;
 
27858
        }
 
27859
 
 
27860
        /**
 
27861
         * Draws an SVG path
 
27862
         * @param $d (string) attribute d of the path SVG element
 
27863
         * @param $style (string) Style of rendering. Possible values are:
 
27864
         * <ul>
 
27865
         *       <li>D or empty string: Draw (default).</li>
 
27866
         *       <li>F: Fill.</li>
 
27867
         *       <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
 
27868
         *       <li>DF or FD: Draw and fill.</li>
 
27869
         *       <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
 
27870
         *       <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
 
27871
         *       <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
 
27872
         * </ul>
 
27873
         * @return array of container box measures (x, y, w, h)
 
27874
         * @author Nicola Asuni
 
27875
         * @since 5.0.000 (2010-05-02)
 
27876
         * @protected
 
27877
         */
 
27878
        protected function SVGPath($d, $style='') {
 
27879
                // set fill/stroke style
 
27880
                $op = $this->getPathPaintOperator($style, '');
 
27881
                if (empty($op)) {
 
27882
                        return;
 
27883
                }
 
27884
                $paths = array();
 
27885
                $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
 
27886
                preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER);
 
27887
                $x = 0;
 
27888
                $y = 0;
 
27889
                $x1 = 0;
 
27890
                $y1 = 0;
 
27891
                $x2 = 0;
 
27892
                $y2 = 0;
 
27893
                $xmin = 2147483647;
 
27894
                $xmax = 0;
 
27895
                $ymin = 2147483647;
 
27896
                $ymax = 0;
 
27897
                $relcoord = false;
 
27898
                $minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
 
27899
                $firstcmd = true; // used to print first point
 
27900
                // draw curve pieces
 
27901
                foreach ($paths as $key => $val) {
 
27902
                        // get curve type
 
27903
                        $cmd = trim($val[1]);
 
27904
                        if (strtolower($cmd) == $cmd) {
 
27905
                                // use relative coordinated instead of absolute
 
27906
                                $relcoord = true;
 
27907
                                $xoffset = $x;
 
27908
                                $yoffset = $y;
 
27909
                        } else {
 
27910
                                $relcoord = false;
 
27911
                                $xoffset = 0;
 
27912
                                $yoffset = 0;
 
27913
                        }
 
27914
                        $params = array();
 
27915
                        if (isset($val[2])) {
 
27916
                                // get curve parameters
 
27917
                                $rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
 
27918
                                $params = array();
 
27919
                                foreach ($rawparams as $ck => $cp) {
 
27920
                                        $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false);
 
27921
                                        if (abs($params[$ck]) < $minlen) {
 
27922
                                                // aproximate little values to zero
 
27923
                                                $params[$ck] = 0;
 
27924
                                        }
 
27925
                                }
 
27926
                        }
 
27927
                        // store current origin point
 
27928
                        $x0 = $x;
 
27929
                        $y0 = $y;
 
27930
                        switch (strtoupper($cmd)) {
 
27931
                                case 'M': { // moveto
 
27932
                                        foreach ($params as $ck => $cp) {
 
27933
                                                if (($ck % 2) == 0) {
 
27934
                                                        $x = $cp + $xoffset;
 
27935
                                                } else {
 
27936
                                                        $y = $cp + $yoffset;
 
27937
                                                        if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
 
27938
                                                                if ($ck == 1) {
 
27939
                                                                        $this->_outPoint($x, $y);
 
27940
                                                                        $firstcmd = false;
 
27941
                                                                } else {
 
27942
                                                                        $this->_outLine($x, $y);
 
27943
                                                                }
 
27944
                                                        }
 
27945
                                                        $xmin = min($xmin, $x);
 
27946
                                                        $ymin = min($ymin, $y);
 
27947
                                                        $xmax = max($xmax, $x);
 
27948
                                                        $ymax = max($ymax, $y);
 
27949
                                                        if ($relcoord) {
 
27950
                                                                $xoffset = $x;
 
27951
                                                                $yoffset = $y;
 
27952
                                                        }
 
27953
                                                }
 
27954
                                        }
 
27955
                                        break;
 
27956
                                }
 
27957
                                case 'L': { // lineto
 
27958
                                        foreach ($params as $ck => $cp) {
 
27959
                                                if (($ck % 2) == 0) {
 
27960
                                                        $x = $cp + $xoffset;
 
27961
                                                } else {
 
27962
                                                        $y = $cp + $yoffset;
 
27963
                                                        if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
 
27964
                                                                $this->_outLine($x, $y);
 
27965
                                                        }
 
27966
                                                        $xmin = min($xmin, $x);
 
27967
                                                        $ymin = min($ymin, $y);
 
27968
                                                        $xmax = max($xmax, $x);
 
27969
                                                        $ymax = max($ymax, $y);
 
27970
                                                        if ($relcoord) {
 
27971
                                                                $xoffset = $x;
 
27972
                                                                $yoffset = $y;
 
27973
                                                        }
 
27974
                                                }
 
27975
                                        }
 
27976
                                        break;
 
27977
                                }
 
27978
                                case 'H': { // horizontal lineto
 
27979
                                        foreach ($params as $ck => $cp) {
 
27980
                                                $x = $cp + $xoffset;
 
27981
                                                if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
 
27982
                                                        $this->_outLine($x, $y);
 
27983
                                                }
 
27984
                                                $xmin = min($xmin, $x);
 
27985
                                                $xmax = max($xmax, $x);
 
27986
                                                if ($relcoord) {
 
27987
                                                        $xoffset = $x;
 
27988
                                                }
 
27989
                                        }
 
27990
                                        break;
 
27991
                                }
 
27992
                                case 'V': { // vertical lineto
 
27993
                                        foreach ($params as $ck => $cp) {
 
27994
                                                $y = $cp + $yoffset;
 
27995
                                                if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
 
27996
                                                        $this->_outLine($x, $y);
 
27997
                                                }
 
27998
                                                $ymin = min($ymin, $y);
 
27999
                                                $ymax = max($ymax, $y);
 
28000
                                                if ($relcoord) {
 
28001
                                                        $yoffset = $y;
 
28002
                                                }
 
28003
                                        }
 
28004
                                        break;
 
28005
                                }
 
28006
                                case 'C': { // curveto
 
28007
                                        foreach ($params as $ck => $cp) {
 
28008
                                                $params[$ck] = $cp;
 
28009
                                                if ((($ck + 1) % 6) == 0) {
 
28010
                                                        $x1 = $params[($ck - 5)] + $xoffset;
 
28011
                                                        $y1 = $params[($ck - 4)] + $yoffset;
 
28012
                                                        $x2 = $params[($ck - 3)] + $xoffset;
 
28013
                                                        $y2 = $params[($ck - 2)] + $yoffset;
 
28014
                                                        $x = $params[($ck - 1)] + $xoffset;
 
28015
                                                        $y = $params[($ck)] + $yoffset;
 
28016
                                                        $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
 
28017
                                                        $xmin = min($xmin, $x, $x1, $x2);
 
28018
                                                        $ymin = min($ymin, $y, $y1, $y2);
 
28019
                                                        $xmax = max($xmax, $x, $x1, $x2);
 
28020
                                                        $ymax = max($ymax, $y, $y1, $y2);
 
28021
                                                        if ($relcoord) {
 
28022
                                                                $xoffset = $x;
 
28023
                                                                $yoffset = $y;
 
28024
                                                        }
 
28025
                                                }
 
28026
                                        }
 
28027
                                        break;
 
28028
                                }
 
28029
                                case 'S': { // shorthand/smooth curveto
 
28030
                                        foreach ($params as $ck => $cp) {
 
28031
                                                $params[$ck] = $cp;
 
28032
                                                if ((($ck + 1) % 4) == 0) {
 
28033
                                                        if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
 
28034
                                                                $x1 = (2 * $x) - $x2;
 
28035
                                                                $y1 = (2 * $y) - $y2;
 
28036
                                                        } else {
 
28037
                                                                $x1 = $x;
 
28038
                                                                $y1 = $y;
 
28039
                                                        }
 
28040
                                                        $x2 = $params[($ck - 3)] + $xoffset;
 
28041
                                                        $y2 = $params[($ck - 2)] + $yoffset;
 
28042
                                                        $x = $params[($ck - 1)] + $xoffset;
 
28043
                                                        $y = $params[($ck)] + $yoffset;
 
28044
                                                        $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
 
28045
                                                        $xmin = min($xmin, $x, $x1, $x2);
 
28046
                                                        $ymin = min($ymin, $y, $y1, $y2);
 
28047
                                                        $xmax = max($xmax, $x, $x1, $x2);
 
28048
                                                        $ymax = max($ymax, $y, $y1, $y2);
 
28049
                                                        if ($relcoord) {
 
28050
                                                                $xoffset = $x;
 
28051
                                                                $yoffset = $y;
 
28052
                                                        }
 
28053
                                                }
 
28054
                                        }
 
28055
                                        break;
 
28056
                                }
 
28057
                                case 'Q': { // quadratic B�zier curveto
 
28058
                                        foreach ($params as $ck => $cp) {
 
28059
                                                $params[$ck] = $cp;
 
28060
                                                if ((($ck + 1) % 4) == 0) {
 
28061
                                                        // convert quadratic points to cubic points
 
28062
                                                        $x1 = $params[($ck - 3)] + $xoffset;
 
28063
                                                        $y1 = $params[($ck - 2)] + $yoffset;
 
28064
                                                        $xa = ($x + (2 * $x1)) / 3;
 
28065
                                                        $ya = ($y + (2 * $y1)) / 3;
 
28066
                                                        $x = $params[($ck - 1)] + $xoffset;
 
28067
                                                        $y = $params[($ck)] + $yoffset;
 
28068
                                                        $xb = ($x + (2 * $x1)) / 3;
 
28069
                                                        $yb = ($y + (2 * $y1)) / 3;
 
28070
                                                        $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
 
28071
                                                        $xmin = min($xmin, $x, $xa, $xb);
 
28072
                                                        $ymin = min($ymin, $y, $ya, $yb);
 
28073
                                                        $xmax = max($xmax, $x, $xa, $xb);
 
28074
                                                        $ymax = max($ymax, $y, $ya, $yb);
 
28075
                                                        if ($relcoord) {
 
28076
                                                                $xoffset = $x;
 
28077
                                                                $yoffset = $y;
 
28078
                                                        }
 
28079
                                                }
 
28080
                                        }
 
28081
                                        break;
 
28082
                                }
 
28083
                                case 'T': { // shorthand/smooth quadratic B�zier curveto
 
28084
                                        foreach ($params as $ck => $cp) {
 
28085
                                                $params[$ck] = $cp;
 
28086
                                                if (($ck % 2) != 0) {
 
28087
                                                        if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
 
28088
                                                                $x1 = (2 * $x) - $x1;
 
28089
                                                                $y1 = (2 * $y) - $y1;
 
28090
                                                        } else {
 
28091
                                                                $x1 = $x;
 
28092
                                                                $y1 = $y;
 
28093
                                                        }
 
28094
                                                        // convert quadratic points to cubic points
 
28095
                                                        $xa = ($x + (2 * $x1)) / 3;
 
28096
                                                        $ya = ($y + (2 * $y1)) / 3;
 
28097
                                                        $x = $params[($ck - 1)] + $xoffset;
 
28098
                                                        $y = $params[($ck)] + $yoffset;
 
28099
                                                        $xb = ($x + (2 * $x1)) / 3;
 
28100
                                                        $yb = ($y + (2 * $y1)) / 3;
 
28101
                                                        $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
 
28102
                                                        $xmin = min($xmin, $x, $xa, $xb);
 
28103
                                                        $ymin = min($ymin, $y, $ya, $yb);
 
28104
                                                        $xmax = max($xmax, $x, $xa, $xb);
 
28105
                                                        $ymax = max($ymax, $y, $ya, $yb);
 
28106
                                                        if ($relcoord) {
 
28107
                                                                $xoffset = $x;
 
28108
                                                                $yoffset = $y;
 
28109
                                                        }
 
28110
                                                }
 
28111
                                        }
 
28112
                                        break;
 
28113
                                }
 
28114
                                case 'A': { // elliptical arc
 
28115
                                        foreach ($params as $ck => $cp) {
 
28116
                                                $params[$ck] = $cp;
 
28117
                                                if ((($ck + 1) % 7) == 0) {
 
28118
                                                        $x0 = $x;
 
28119
                                                        $y0 = $y;
 
28120
                                                        $rx = abs($params[($ck - 6)]);
 
28121
                                                        $ry = abs($params[($ck - 5)]);
 
28122
                                                        $ang = -$rawparams[($ck - 4)];
 
28123
                                                        $angle = deg2rad($ang);
 
28124
                                                        $fa = $rawparams[($ck - 3)]; // large-arc-flag
 
28125
                                                        $fs = $rawparams[($ck - 2)]; // sweep-flag
 
28126
                                                        $x = $params[($ck - 1)] + $xoffset;
 
28127
                                                        $y = $params[$ck] + $yoffset;
 
28128
                                                        if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
 
28129
                                                                // endpoints are almost identical
 
28130
                                                                $xmin = min($xmin, $x);
 
28131
                                                                $ymin = min($ymin, $y);
 
28132
                                                                $xmax = max($xmax, $x);
 
28133
                                                                $ymax = max($ymax, $y);
 
28134
                                                        } else {
 
28135
                                                                $cos_ang = cos($angle);
 
28136
                                                                $sin_ang = sin($angle);
 
28137
                                                                $a = (($x0 - $x) / 2);
 
28138
                                                                $b = (($y0 - $y) / 2);
 
28139
                                                                $xa = ($a * $cos_ang) - ($b * $sin_ang);
 
28140
                                                                $ya = ($a * $sin_ang) + ($b * $cos_ang);
 
28141
                                                                $rx2 = $rx * $rx;
 
28142
                                                                $ry2 = $ry * $ry;
 
28143
                                                                $xa2 = $xa * $xa;
 
28144
                                                                $ya2 = $ya * $ya;
 
28145
                                                                $delta = ($xa2 / $rx2) + ($ya2 / $ry2);
 
28146
                                                                if ($delta > 1) {
 
28147
                                                                        $rx *= sqrt($delta);
 
28148
                                                                        $ry *= sqrt($delta);
 
28149
                                                                        $rx2 = $rx * $rx;
 
28150
                                                                        $ry2 = $ry * $ry;
 
28151
                                                                }
 
28152
                                                                $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
 
28153
                                                                if ($numerator < 0) {
 
28154
                                                                        $root = 0;
 
28155
                                                                } else {
 
28156
                                                                        $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2)));
 
28157
                                                                }
 
28158
                                                                if ($fa == $fs){
 
28159
                                                                        $root *= -1;
 
28160
                                                                }
 
28161
                                                                $cax = $root * (($rx * $ya) / $ry);
 
28162
                                                                $cay = -$root * (($ry * $xa) / $rx);
 
28163
                                                                // coordinates of ellipse center
 
28164
                                                                $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2);
 
28165
                                                                $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2);
 
28166
                                                                // get angles
 
28167
                                                                $angs = $this->getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
 
28168
                                                                $dang = $this->getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
 
28169
                                                                if (($fs == 0) AND ($dang > 0)) {
 
28170
                                                                        $dang -= (2 * M_PI);
 
28171
                                                                } elseif (($fs == 1) AND ($dang < 0)) {
 
28172
                                                                        $dang += (2 * M_PI);
 
28173
                                                                }
 
28174
                                                                $angf = $angs - $dang;
 
28175
                                                                if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
 
28176
                                                                        // reverse angles
 
28177
                                                                        $tmp = $angs;
 
28178
                                                                        $angs = $angf;
 
28179
                                                                        $angf = $tmp;
 
28180
                                                                }
 
28181
                                                                $angs = round(rad2deg($angs), 6);
 
28182
                                                                $angf = round(rad2deg($angf), 6);
 
28183
                                                                // covent angles to positive values
 
28184
                                                                if (($angs < 0) AND ($angf < 0)) {
 
28185
                                                                        $angs += 360;
 
28186
                                                                        $angf += 360;
 
28187
                                                                }
 
28188
                                                                $pie = false;
 
28189
                                                                if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) {
 
28190
                                                                        $pie = true;
 
28191
                                                                }
 
28192
                                                                list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
 
28193
                                                                $xmin = min($xmin, $x, $axmin);
 
28194
                                                                $ymin = min($ymin, $y, $aymin);
 
28195
                                                                $xmax = max($xmax, $x, $axmax);
 
28196
                                                                $ymax = max($ymax, $y, $aymax);
 
28197
                                                        }
 
28198
                                                        if ($relcoord) {
 
28199
                                                                $xoffset = $x;
 
28200
                                                                $yoffset = $y;
 
28201
                                                        }
 
28202
                                                }
 
28203
                                        }
 
28204
                                        break;
 
28205
                                }
 
28206
                                case 'Z': {
 
28207
                                        $this->_out('h');
 
28208
                                        break;
 
28209
                                }
 
28210
                        }
 
28211
                        $firstcmd = false;
 
28212
                } // end foreach
 
28213
                if (!empty($op)) {
 
28214
                        $this->_out($op);
 
28215
                }
 
28216
                return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
 
28217
        }
 
28218
 
 
28219
        /**
 
28220
         * Returns the angle in radiants between two vectors
 
28221
         * @param $x1 (int) X coordinate of first vector point
 
28222
         * @param $y1 (int) Y coordinate of first vector point
 
28223
         * @param $x2 (int) X coordinate of second vector point
 
28224
         * @param $y2 (int) Y coordinate of second vector point
 
28225
         * @author Nicola Asuni
 
28226
         * @since 5.0.000 (2010-05-04)
 
28227
         * @protected
 
28228
         */
 
28229
        protected function getVectorsAngle($x1, $y1, $x2, $y2) {
 
28230
                $dprod = ($x1 * $x2) + ($y1 * $y2);
 
28231
                $dist1 = sqrt(($x1 * $x1) + ($y1 * $y1));
 
28232
                $dist2 = sqrt(($x2 * $x2) + ($y2 * $y2));
 
28233
                $angle = acos($dprod / ($dist1 * $dist2));
 
28234
                if (is_nan($angle)) {
 
28235
                        $angle = M_PI;
 
28236
                }
 
28237
                if ((($x1 * $y2) - ($x2 * $y1)) < 0) {
 
28238
                        $angle *= -1;
 
28239
                }
 
28240
                return $angle;
 
28241
        }
 
28242
 
 
28243
        /**
 
28244
         * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***)
 
28245
         * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
 
28246
         * @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.
 
28247
         * @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.
 
28248
         * @param $ctm (array) tranformation matrix for clipping mode (starting transformation matrix).
 
28249
         * @author Nicola Asuni
 
28250
         * @since 5.0.000 (2010-05-02)
 
28251
         * @protected
 
28252
         */
 
28253
        protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
 
28254
                // check if we are in clip mode
 
28255
                if ($this->svgclipmode) {
 
28256
                        $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]);
 
28257
                        return;
 
28258
                }
 
28259
                if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
 
28260
                        if (!isset($attribs['id'])) {
 
28261
                                $attribs['id'] = 'DF_'.(count($this->svgdefs) + 1);
 
28262
                        }
 
28263
                        $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
 
28264
                        return;
 
28265
                }
 
28266
                $clipping = false;
 
28267
                if ($parser == 'clip-path') {
 
28268
                        // set clipping mode
 
28269
                        $clipping = true;
 
28270
                }
 
28271
                // get styling properties
 
28272
                $prev_svgstyle = $this->svgstyles[(count($this->svgstyles) - 1)]; // previous style
 
28273
                $svgstyle = $this->svgstyles[0]; // set default style
 
28274
                if (isset($attribs['style']) AND !$this->empty_string($attribs['style'])) {
 
28275
                        // fix style for regular expression
 
28276
                        $attribs['style'] = ';'.$attribs['style'];
 
28277
                }
 
28278
                foreach ($prev_svgstyle as $key => $val) {
 
28279
                        if (in_array($key, $this->svginheritprop)) {
 
28280
                                // inherit previous value
 
28281
                                $svgstyle[$key] = $val;
 
28282
                        }
 
28283
                        if (isset($attribs[$key]) AND !$this->empty_string($attribs[$key])) {
 
28284
                                // specific attribute settings
 
28285
                                if ($attribs[$key] == 'inherit') {
 
28286
                                        $svgstyle[$key] = $val;
 
28287
                                } else {
 
28288
                                        $svgstyle[$key] = $attribs[$key];
 
28289
                                }
 
28290
                        } elseif (isset($attribs['style']) AND !$this->empty_string($attribs['style'])) {
 
28291
                                // CSS style syntax
 
28292
                                $attrval = array();
 
28293
                                if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
 
28294
                                        if ($attrval[1] == 'inherit') {
 
28295
                                                $svgstyle[$key] = $val;
 
28296
                                        } else {
 
28297
                                                $svgstyle[$key] = $attrval[1];
 
28298
                                        }
 
28299
                                }
 
28300
                        }
 
28301
                }
 
28302
                // transformation matrix
 
28303
                if (!empty($ctm)) {
 
28304
                        $tm = $ctm;
 
28305
                } else {
 
28306
                        $tm = $this->svgstyles[(count($this->svgstyles) - 1)]['transfmatrix'];
 
28307
                }
 
28308
                if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
 
28309
                        $tm = $this->getTransformationMatrixProduct($tm, $this->getSVGTransformMatrix($attribs['transform']));
 
28310
                }
 
28311
                $svgstyle['transfmatrix'] = $tm;
 
28312
                $invisible = false;
 
28313
                if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
 
28314
                        // the current graphics element is invisible (nothing is painted)
 
28315
                        $invisible = true;
 
28316
                }
 
28317
                // process tag
 
28318
                switch($name) {
 
28319
                        case 'defs': {
 
28320
                                $this->svgdefsmode = true;
 
28321
                                break;
 
28322
                        }
 
28323
                        // clipPath
 
28324
                        case 'clipPath': {
 
28325
                                if ($invisible) {
 
28326
                                        break;
 
28327
                                }
 
28328
                                $this->svgclipmode = true;
 
28329
                                if (!isset($attribs['id'])) {
 
28330
                                        $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1);
 
28331
                                }
 
28332
                                $this->svgclipid = $attribs['id'];
 
28333
                                $this->svgclippaths[$this->svgclipid] = array();
 
28334
                                $this->svgcliptm[$this->svgclipid] = $tm;
 
28335
                                break;
 
28336
                        }
 
28337
                        case 'svg': {
 
28338
                                // start of SVG object
 
28339
                                break;
 
28340
                        }
 
28341
                        case 'g': {
 
28342
                                // group together related graphics elements
 
28343
                                array_push($this->svgstyles, $svgstyle);
 
28344
                                $this->StartTransform();
 
28345
                                $this->setSVGStyles($svgstyle, $prev_svgstyle);
 
28346
                                break;
 
28347
                        }
 
28348
                        case 'linearGradient': {
 
28349
                                if ($this->pdfa_mode) {
 
28350
                                        break;
 
28351
                                }
 
28352
                                if (!isset($attribs['id'])) {
 
28353
                                        $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
 
28354
                                }
 
28355
                                $this->svggradientid = $attribs['id'];
 
28356
                                $this->svggradients[$this->svggradientid] = array();
 
28357
                                $this->svggradients[$this->svggradientid]['type'] = 2;
 
28358
                                $this->svggradients[$this->svggradientid]['stops'] = array();
 
28359
                                if (isset($attribs['gradientUnits'])) {
 
28360
                                        $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
 
28361
                                } else {
 
28362
                                        $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
 
28363
                                }
 
28364
                                //$attribs['spreadMethod']
 
28365
                                $x1 = (isset($attribs['x1'])?$attribs['x1']:'0%');
 
28366
                                $y1 = (isset($attribs['y1'])?$attribs['y1']:'0%');
 
28367
                                $x2 = (isset($attribs['x2'])?$attribs['x2']:'100%');
 
28368
                                $y2 = (isset($attribs['y2'])?$attribs['y2']:'0%');
 
28369
                                if (substr($x1, -1) != '%') {
 
28370
                                        $this->svggradients[$this->svggradientid]['mode'] = 'measure';
 
28371
                                } else {
 
28372
                                        $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
 
28373
                                }
 
28374
                                if (isset($attribs['gradientTransform'])) {
 
28375
                                        $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']);
 
28376
                                }
 
28377
                                $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2);
 
28378
                                if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
 
28379
                                        // gradient is defined on another place
 
28380
                                        $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
 
28381
                                }
 
28382
                                break;
 
28383
                        }
 
28384
                        case 'radialGradient': {
 
28385
                                if ($this->pdfa_mode) {
 
28386
                                        break;
 
28387
                                }
 
28388
                                if (!isset($attribs['id'])) {
 
28389
                                        $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
 
28390
                                }
 
28391
                                $this->svggradientid = $attribs['id'];
 
28392
                                $this->svggradients[$this->svggradientid] = array();
 
28393
                                $this->svggradients[$this->svggradientid]['type'] = 3;
 
28394
                                $this->svggradients[$this->svggradientid]['stops'] = array();
 
28395
                                if (isset($attribs['gradientUnits'])) {
 
28396
                                        $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
 
28397
                                } else {
 
28398
                                        $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
 
28399
                                }
 
28400
                                //$attribs['spreadMethod']
 
28401
                                $cx = (isset($attribs['cx'])?$attribs['cx']:0.5);
 
28402
                                $cy = (isset($attribs['cy'])?$attribs['cy']:0.5);
 
28403
                                $fx = (isset($attribs['fx'])?$attribs['fx']:$cx);
 
28404
                                $fy = (isset($attribs['fy'])?$attribs['fy']:$cy);
 
28405
                                $r = (isset($attribs['r'])?$attribs['r']:0.5);
 
28406
                                if (isset($attribs['cx']) AND (substr($attribs['cx'], -1) != '%')) {
 
28407
                                        $this->svggradients[$this->svggradientid]['mode'] = 'measure';
 
28408
                                } else {
 
28409
                                        $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
 
28410
                                }
 
28411
                                if (isset($attribs['gradientTransform'])) {
 
28412
                                        $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']);
 
28413
                                }
 
28414
                                $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r);
 
28415
                                if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
 
28416
                                        // gradient is defined on another place
 
28417
                                        $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
 
28418
                                }
 
28419
                                break;
 
28420
                        }
 
28421
                        case 'stop': {
 
28422
                                // gradient stops
 
28423
                                if (substr($attribs['offset'], -1) == '%') {
 
28424
                                        $offset = floatval(substr($attribs['offset'], -1)) / 100;
 
28425
                                } else {
 
28426
                                        $offset = floatval($attribs['offset']);
 
28427
                                        if ($offset > 1) {
 
28428
                                                $offset /= 100;
 
28429
                                        }
 
28430
                                }
 
28431
                                $stop_color = isset($svgstyle['stop-color'])?$this->convertHTMLColorToDec($svgstyle['stop-color']):'black';
 
28432
                                $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
 
28433
                                $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
 
28434
                                break;
 
28435
                        }
 
28436
                        // paths
 
28437
                        case 'path': {
 
28438
                                if ($invisible) {
 
28439
                                        break;
 
28440
                                }
 
28441
                                if (isset($attribs['d'])) {
 
28442
                                        $d = trim($attribs['d']);
 
28443
                                        if (!empty($d)) {
 
28444
                                                if ($clipping) {
 
28445
                                                        $this->SVGTransform($tm);
 
28446
                                                        $this->SVGPath($d, 'CNZ');
 
28447
                                                } else {
 
28448
                                                        $this->StartTransform();
 
28449
                                                        $this->SVGTransform($tm);
 
28450
                                                        $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, 0, 0, 1, 1, 'SVGPath', array($d, 'CNZ'));
 
28451
                                                        if (!empty($obstyle)) {
 
28452
                                                                $this->SVGPath($d, $obstyle);
 
28453
                                                        }
 
28454
                                                        $this->StopTransform();
 
28455
                                                }
 
28456
                                        }
 
28457
                                }
 
28458
                                break;
 
28459
                        }
 
28460
                        // shapes
 
28461
                        case 'rect': {
 
28462
                                if ($invisible) {
 
28463
                                        break;
 
28464
                                }
 
28465
                                $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
 
28466
                                $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
 
28467
                                $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
 
28468
                                $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
 
28469
                                $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
 
28470
                                $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx);
 
28471
                                if ($clipping) {
 
28472
                                        $this->SVGTransform($tm);
 
28473
                                        $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
 
28474
                                } else {
 
28475
                                        $this->StartTransform();
 
28476
                                        $this->SVGTransform($tm);
 
28477
                                        $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
 
28478
                                        if (!empty($obstyle)) {
 
28479
                                                $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
 
28480
                                        }
 
28481
                                        $this->StopTransform();
 
28482
                                }
 
28483
                                break;
 
28484
                        }
 
28485
                        case 'circle': {
 
28486
                                if ($invisible) {
 
28487
                                        break;
 
28488
                                }
 
28489
                                $cx = (isset($attribs['cx'])?$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false):0);
 
28490
                                $cy = (isset($attribs['cy'])?$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false):0);
 
28491
                                $r = (isset($attribs['r'])?$this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false):0);
 
28492
                                $x = $cx - $r;
 
28493
                                $y = $cy - $r;
 
28494
                                $w = 2 * $r;
 
28495
                                $h = $w;
 
28496
                                if ($clipping) {
 
28497
                                        $this->SVGTransform($tm);
 
28498
                                        $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
 
28499
                                } else {
 
28500
                                        $this->StartTransform();
 
28501
                                        $this->SVGTransform($tm);
 
28502
                                        $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
 
28503
                                        if (!empty($obstyle)) {
 
28504
                                                $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
 
28505
                                        }
 
28506
                                        $this->StopTransform();
 
28507
                                }
 
28508
                                break;
 
28509
                        }
 
28510
                        case 'ellipse': {
 
28511
                                if ($invisible) {
 
28512
                                        break;
 
28513
                                }
 
28514
                                $cx = (isset($attribs['cx'])?$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false):0);
 
28515
                                $cy = (isset($attribs['cy'])?$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false):0);
 
28516
                                $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
 
28517
                                $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):0);
 
28518
                                $x = $cx - $rx;
 
28519
                                $y = $cy - $ry;
 
28520
                                $w = 2 * $rx;
 
28521
                                $h = 2 * $ry;
 
28522
                                if ($clipping) {
 
28523
                                        $this->SVGTransform($tm);
 
28524
                                        $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
 
28525
                                } else {
 
28526
                                        $this->StartTransform();
 
28527
                                        $this->SVGTransform($tm);
 
28528
                                        $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
 
28529
                                        if (!empty($obstyle)) {
 
28530
                                                $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
 
28531
                                        }
 
28532
                                        $this->StopTransform();
 
28533
                                }
 
28534
                                break;
 
28535
                        }
 
28536
                        case 'line': {
 
28537
                                if ($invisible) {
 
28538
                                        break;
 
28539
                                }
 
28540
                                $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0);
 
28541
                                $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0);
 
28542
                                $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0);
 
28543
                                $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0);
 
28544
                                $x = $x1;
 
28545
                                $y = $y1;
 
28546
                                $w = abs($x2 - $x1);
 
28547
                                $h = abs($y2 - $y1);
 
28548
                                if (!$clipping) {
 
28549
                                        $this->StartTransform();
 
28550
                                        $this->SVGTransform($tm);
 
28551
                                        $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
 
28552
                                        $this->Line($x1, $y1, $x2, $y2);
 
28553
                                        $this->StopTransform();
 
28554
                                }
 
28555
                                break;
 
28556
                        }
 
28557
                        case 'polyline':
 
28558
                        case 'polygon': {
 
28559
                                if ($invisible) {
 
28560
                                        break;
 
28561
                                }
 
28562
                                $points = (isset($attribs['points'])?$attribs['points']:'0 0');
 
28563
                                $points = trim($points);
 
28564
                                // note that point may use a complex syntax not covered here
 
28565
                                $points = preg_split('/[\,\s]+/si', $points);
 
28566
                                if (count($points) < 4) {
 
28567
                                        break;
 
28568
                                }
 
28569
                                $p = array();
 
28570
                                $xmin = 2147483647;
 
28571
                                $xmax = 0;
 
28572
                                $ymin = 2147483647;
 
28573
                                $ymax = 0;
 
28574
                                foreach ($points as $key => $val) {
 
28575
                                        $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
 
28576
                                        if (($key % 2) == 0) {
 
28577
                                                // X coordinate
 
28578
                                                $xmin = min($xmin, $p[$key]);
 
28579
                                                $xmax = max($xmax, $p[$key]);
 
28580
                                        } else {
 
28581
                                                // Y coordinate
 
28582
                                                $ymin = min($ymin, $p[$key]);
 
28583
                                                $ymax = max($ymax, $p[$key]);
 
28584
                                        }
 
28585
                                }
 
28586
                                $x = $xmin;
 
28587
                                $y = $ymin;
 
28588
                                $w = ($xmax - $xmin);
 
28589
                                $h = ($ymax - $ymin);
 
28590
                                if ($name == 'polyline') {
 
28591
                                        $this->StartTransform();
 
28592
                                        $this->SVGTransform($tm);
 
28593
                                        $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
 
28594
                                        $this->PolyLine($p, 'D', array(), array());
 
28595
                                        $this->StopTransform();
 
28596
                                } else { // polygon
 
28597
                                        if ($clipping) {
 
28598
                                                $this->SVGTransform($tm);
 
28599
                                                $this->Polygon($p, 'CNZ', array(), array(), true);
 
28600
                                        } else {
 
28601
                                                $this->StartTransform();
 
28602
                                                $this->SVGTransform($tm);
 
28603
                                                $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
 
28604
                                                if (!empty($obstyle)) {
 
28605
                                                        $this->Polygon($p, $obstyle, array(), array(), true);
 
28606
                                                }
 
28607
                                                $this->StopTransform();
 
28608
                                        }
 
28609
                                }
 
28610
                                break;
 
28611
                        }
 
28612
                        // image
 
28613
                        case 'image': {
 
28614
                                if ($invisible) {
 
28615
                                        break;
 
28616
                                }
 
28617
                                if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
 
28618
                                        break;
 
28619
                                }
 
28620
                                $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
 
28621
                                $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
 
28622
                                $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
 
28623
                                $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
 
28624
                                $img = $attribs['xlink:href'];
 
28625
                                if (!$clipping) {
 
28626
                                        $this->StartTransform();
 
28627
                                        $this->SVGTransform($tm);
 
28628
                                        $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
 
28629
                                        if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) {
 
28630
                                                // embedded image encoded as base64
 
28631
                                                $img = '@'.base64_decode(substr($img, strlen($m[0])));
 
28632
                                        } else {
 
28633
                                                // fix image path
 
28634
                                                if (!$this->empty_string($this->svgdir) AND (($img{0} == '.') OR (basename($img) == $img))) {
 
28635
                                                        // replace relative path with full server path
 
28636
                                                        $img = $this->svgdir.'/'.$img;
 
28637
                                                }
 
28638
                                                if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
 
28639
                                                        $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
 
28640
                                                        if (($findroot === false) OR ($findroot > 1)) {
 
28641
                                                                if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
 
28642
                                                                        $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
 
28643
                                                                } else {
 
28644
                                                                        $img = $_SERVER['DOCUMENT_ROOT'].$img;
 
28645
                                                                }
 
28646
                                                        }
 
28647
                                                }
 
28648
                                                $img = urldecode($img);
 
28649
                                                $testscrtype = @parse_url($img);
 
28650
                                                if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
 
28651
                                                        // convert URL to server path
 
28652
                                                        $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img);
 
28653
                                                }
 
28654
                                        }
 
28655
                                        $this->Image($img, $x, $y, $w, $h);
 
28656
                                        $this->StopTransform();
 
28657
                                }
 
28658
                                break;
 
28659
                        }
 
28660
                        // text
 
28661
                        case 'text':
 
28662
                        case 'tspan': {
 
28663
                                $this->svgtextmode['invisible'] = $invisible;
 
28664
                                if ($invisible) {
 
28665
                                        break;
 
28666
                                }
 
28667
                                array_push($this->svgstyles, $svgstyle);
 
28668
                                // only basic support - advanced features must be implemented
 
28669
                                $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):$this->x);
 
28670
                                $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):$this->y);
 
28671
                                $svgstyle['text-color'] = $svgstyle['fill'];
 
28672
                                $this->svgtext = '';
 
28673
                                if (isset($svgstyle['text-anchor'])) {
 
28674
                                        $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor'];
 
28675
                                } else {
 
28676
                                        $this->svgtextmode['text-anchor'] = 'start';
 
28677
                                }
 
28678
                                if (isset($svgstyle['direction'])) {
 
28679
                                        if ($svgstyle['direction'] == 'rtl') {
 
28680
                                                $this->svgtextmode['rtl'] = true;
 
28681
                                        } else {
 
28682
                                                $this->svgtextmode['rtl'] = false;
 
28683
                                        }
 
28684
                                } else {
 
28685
                                        $this->svgtextmode['rtl'] = false;
 
28686
                                }
 
28687
                                if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
 
28688
                                        $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false);
 
28689
                                } else {
 
28690
                                        $this->svgtextmode['stroke'] = false;
 
28691
                                }
 
28692
                                $this->StartTransform();
 
28693
                                $this->SVGTransform($tm);
 
28694
                                $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
 
28695
                                $this->x = $x;
 
28696
                                $this->y = $y;
 
28697
                                break;
 
28698
                        }
 
28699
                        // use
 
28700
                        case 'use': {
 
28701
                                if (isset($attribs['xlink:href'])) {
 
28702
                                        $use = $this->svgdefs[substr($attribs['xlink:href'], 1)];
 
28703
                                        if (isset($attribs['xlink:href'])) {
 
28704
                                                unset($attribs['xlink:href']);
 
28705
                                        }
 
28706
                                        if (isset($attribs['id'])) {
 
28707
                                                unset($attribs['id']);
 
28708
                                        }
 
28709
                                        $attribs = array_merge($use['attribs'], $attribs);
 
28710
                                        $this->startSVGElementHandler($parser, $use['name'], $use['attribs']);
 
28711
                                }
 
28712
                                break;
 
28713
                        }
 
28714
                        default: {
 
28715
                                break;
 
28716
                        }
 
28717
                } // end of switch
 
28718
        }
 
28719
 
 
28720
        /**
 
28721
         * Sets the closing SVG element handler function for the XML parser.
 
28722
         * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
 
28723
         * @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.
 
28724
         * @author Nicola Asuni
 
28725
         * @since 5.0.000 (2010-05-02)
 
28726
         * @protected
 
28727
         */
 
28728
        protected function endSVGElementHandler($parser, $name) {
 
28729
                switch($name) {
 
28730
                        case 'defs': {
 
28731
                                $this->svgdefsmode = false;
 
28732
                                break;
 
28733
                        }
 
28734
                        // clipPath
 
28735
                        case 'clipPath': {
 
28736
                                $this->svgclipmode = false;
 
28737
                                break;
 
28738
                        }
 
28739
                        case 'g': {
 
28740
                                // ungroup: remove last style from array
 
28741
                                array_pop($this->svgstyles);
 
28742
                                $this->StopTransform();
 
28743
                                break;
 
28744
                        }
 
28745
                        case 'text':
 
28746
                        case 'tspan': {
 
28747
                                if ($this->svgtextmode['invisible']) {
 
28748
                                        // This implementation must be fixed to following the rule:
 
28749
                                        // 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.
 
28750
                                        break;
 
28751
                                }
 
28752
                                // print text
 
28753
                                $text = $this->stringTrim($this->svgtext);
 
28754
                                if ($this->svgtextmode['text-anchor'] != 'start') {
 
28755
                                        $textlen = $this->GetStringWidth($text);
 
28756
                                        // check if string is RTL text
 
28757
                                        if ($this->svgtextmode['text-anchor'] == 'end') {
 
28758
                                                if ($this->svgtextmode['rtl']) {
 
28759
                                                        $this->x += $textlen;
 
28760
                                                } else {
 
28761
                                                        $this->x -= $textlen;
 
28762
                                                }
 
28763
                                        } elseif ($this->svgtextmode['text-anchor'] == 'middle') {
 
28764
                                                if ($this->svgtextmode['rtl']) {
 
28765
                                                        $this->x += ($textlen / 2);
 
28766
                                                } else {
 
28767
                                                        $this->x -= ($textlen / 2);
 
28768
                                                }
 
28769
                                        }
 
28770
                                }
 
28771
                                $textrendermode = $this->textrendermode;
 
28772
                                $textstrokewidth = $this->textstrokewidth;
 
28773
                                $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false);
 
28774
                                $this->Cell(0, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
 
28775
                                // restore previous rendering mode
 
28776
                                $this->textrendermode = $textrendermode;
 
28777
                                $this->textstrokewidth = $textstrokewidth;
 
28778
                                $this->svgtext = '';
 
28779
                                $this->StopTransform();
 
28780
                                array_pop($this->svgstyles);
 
28781
                                break;
 
28782
                        }
 
28783
                        default: {
 
28784
                                break;
 
28785
                        }
 
28786
                }
 
28787
        }
 
28788
 
 
28789
        /**
 
28790
         * Sets the character data handler function for the XML parser.
 
28791
         * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
 
28792
         * @param $data (string) The second parameter, data, contains the character data as a string.
 
28793
         * @author Nicola Asuni
 
28794
         * @since 5.0.000 (2010-05-02)
 
28795
         * @protected
 
28796
         */
 
28797
        protected function segSVGContentHandler($parser, $data) {
 
28798
                $this->svgtext .= $data;
 
28799
        }
 
28800
 
 
28801
        // --- END SVG METHODS -----------------------------------------------------
26131
28802
 
26132
28803
} // END OF TCPDF CLASS
26133
28804