Passed
Branch development (e0e718)
by Nils
04:45
created

tFPDF::Cell()   F

Complexity

Conditions 34
Paths > 20000

Size

Total Lines 122
Code Lines 81

Duplication

Lines 8
Ratio 6.56 %

Importance

Changes 0
Metric Value
cc 34
eloc 81
nc 591600
nop 8
dl 8
loc 122
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/*******************************************************************************
3
* tFPDF (based on FPDF 1.7)                                                    *
4
*                                                                              *
5
* Version:  1.24                                                               *
6
* Date:     2011-09-24                                                         *
7
* Author:   Ian Back <[email protected]>                                           *
8
* License:  LGPL                                                               *
9
*******************************************************************************/
10
11
define('tFPDF_VERSION', '1.24');
12
13
class tFPDF
14
{
15
16
var $unifontSubset;
17
var $page; // current page number
18
var $n; // current object number
19
var $offsets; // array of object offsets
20
var $buffer; // buffer holding in-memory PDF
21
var $pages; // array containing pages
22
var $state; // current document state
23
var $compress; // compression flag
24
var $k; // scale factor (number of points in user unit)
25
var $DefOrientation; // default orientation
26
var $CurOrientation; // current orientation
27
var $StdPageSizes; // standard page sizes
28
var $DefPageSize; // default page size
29
var $CurPageSize; // current page size
30
var $PageSizes; // used for pages with non default sizes or orientations
31
var $wPt, $hPt; // dimensions of current page in points
32
var $w, $h; // dimensions of current page in user unit
33
var $lMargin; // left margin
34
var $tMargin; // top margin
35
var $rMargin; // right margin
36
var $bMargin; // page break margin
37
var $cMargin; // cell margin
38
var $x, $y; // current position in user unit
39
var $lasth; // height of last printed cell
40
var $LineWidth; // line width in user unit
41
var $fontpath; // path containing fonts
42
var $CoreFonts; // array of core font names
43
var $fonts; // array of used fonts
44
var $FontFiles; // array of font files
45
var $diffs; // array of encoding differences
46
var $FontFamily; // current font family
47
var $FontStyle; // current font style
48
var $underline; // underlining flag
49
var $CurrentFont; // current font info
50
var $FontSizePt; // current font size in points
51
var $FontSize; // current font size in user unit
52
var $DrawColor; // commands for drawing color
53
var $FillColor; // commands for filling color
54
var $TextColor; // commands for text color
55
var $ColorFlag; // indicates whether fill and text colors are different
56
var $ws; // word spacing
57
var $images; // array of used images
58
var $PageLinks; // array of links in pages
59
var $links; // array of internal links
60
var $AutoPageBreak; // automatic page breaking
61
var $PageBreakTrigger; // threshold used to trigger page breaks
62
var $InHeader; // flag set when processing header
63
var $InFooter; // flag set when processing footer
64
var $ZoomMode; // zoom display mode
65
var $LayoutMode; // layout display mode
66
var $title; // title
67
var $subject; // subject
68
var $author; // author
69
var $keywords; // keywords
70
var $creator; // creator
71
var $AliasNbPages; // alias for total number of pages
72
var $PDFVersion; // PDF version number
73
74
/*******************************************************************************
75
*                                                                              *
76
*                               Public methods                                 *
77
*                                                                              *
78
*******************************************************************************/
79
function __construct($orientation = 'P', $unit = 'mm', $size = 'A4', $footer_text_1 = "Page")
80
{
81
    // Some checks
82
    $this->_dochecks();
83
    // Initialization of properties
84
    $this->page = 0;
85
    $this->n = 2;
86
    $this->buffer = '';
87
    $this->pages = array();
88
    $this->PageSizes = array();
89
    $this->state = 0;
90
    $this->fonts = array();
91
    $this->FontFiles = array();
92
    $this->diffs = array();
93
    $this->images = array();
94
    $this->links = array();
95
    $this->InHeader = false;
96
    $this->InFooter = false;
97
    $this->lasth = 0;
98
    $this->FontFamily = '';
99
    $this->FontStyle = '';
100
    $this->FontSizePt = 12;
101
    $this->underline = false;
102
    $this->DrawColor = '0 G';
103
    $this->FillColor = '0 g';
104
    $this->TextColor = '0 g';
105
    $this->ColorFlag = false;
106
    $this->ws = 0;
107
    // Font path
108
    if (defined('FPDF_FONTPATH'))
109
    {
110
        $this->fontpath = FPDF_FONTPATH;
111
        if (substr($this->fontpath, -1) != '/' && substr($this->fontpath, -1) != '\\') {
112
                    $this->fontpath .= '/';
113
        }
114
    } elseif (is_dir(dirname(__FILE__).'/font')) {
115
            $this->fontpath = dirname(__FILE__).'/font/';
116
    } else {
117
            $this->fontpath = '';
118
    }
119
    // Core fonts
120
    $this->CoreFonts = array('courier', 'helvetica', 'times', 'symbol', 'zapfdingbats');
121
    // Scale factor
122
    if ($unit == 'pt') {
123
            $this->k = 1;
124
    } elseif ($unit == 'mm') {
125
            $this->k = 72 / 25.4;
126
    } elseif ($unit == 'cm') {
127
            $this->k = 72 / 2.54;
128
    } elseif ($unit == 'in') {
129
            $this->k = 72;
130
    } else {
131
            $this->Error('Incorrect unit: '.$unit);
132
    }
133
    // Page sizes
134
    $this->StdPageSizes = array('a3'=>array(841.89, 1190.55), 'a4'=>array(595.28, 841.89), 'a5'=>array(420.94, 595.28),
135
        'letter'=>array(612, 792), 'legal'=>array(612, 1008));
136
    $size = $this->_getpagesize($size);
137
    $this->DefPageSize = $size;
138
    $this->CurPageSize = $size;
139
    // Page orientation
140
    $orientation = strtolower($orientation);
141
    if ($orientation == 'p' || $orientation == 'portrait')
142
    {
143
        $this->DefOrientation = 'P';
144
        $this->w = $size[0];
145
        $this->h = $size[1];
146
    } elseif ($orientation == 'l' || $orientation == 'landscape')
147
    {
148
        $this->DefOrientation = 'L';
149
        $this->w = $size[1];
150
        $this->h = $size[0];
151
    } else {
152
            $this->Error('Incorrect orientation: '.$orientation);
153
    }
154
    $this->CurOrientation = $this->DefOrientation;
155
    $this->wPt = $this->w * $this->k;
156
    $this->hPt = $this->h * $this->k;
157
    // Page margins (1 cm)
158
    $margin = 28.35 / $this->k;
159
    $this->SetMargins($margin, $margin);
160
    // Interior cell margin (1 mm)
161
    $this->cMargin = $margin / 10;
162
    // Line width (0.2 mm)
163
    $this->LineWidth = .567 / $this->k;
164
    // Automatic page break
165
    $this->SetAutoPageBreak(true, 2 * $margin);
166
    // Default display mode
167
    $this->SetDisplayMode('default');
168
    // Enable compression
169
    $this->SetCompression(true);
170
    // Set default PDF version number
171
    $this->PDFVersion = '1.3';
172
}
173
174
/**
175
 * @param double $left
176
 * @param double $top
177
 */
178
function SetMargins($left, $top, $right = null)
179
{
180
    // Set left, top and right margins
181
    $this->lMargin = $left;
182
    $this->tMargin = $top;
183
    if ($right === null) {
184
            $right = $left;
185
    }
186
    $this->rMargin = $right;
187
}
188
189
function SetLeftMargin($margin)
190
{
191
    // Set left margin
192
    $this->lMargin = $margin;
193
    if ($this->page > 0 && $this->x < $margin) {
194
            $this->x = $margin;
195
    }
196
    }
197
198
function SetTopMargin($margin)
199
{
200
    // Set top margin
201
    $this->tMargin = $margin;
202
}
203
204
function SetRightMargin($margin)
205
{
206
    // Set right margin
207
    $this->rMargin = $margin;
208
}
209
210
/**
211
 * @param boolean $auto
212
 */
213
function SetAutoPageBreak($auto, $margin = 0)
214
{
215
    // Set auto page break mode and triggering margin
216
    $this->AutoPageBreak = $auto;
217
    $this->bMargin = $margin;
218
    $this->PageBreakTrigger = $this->h - $margin;
219
}
220
221
/**
222
 * @param string $zoom
223
 */
224
function SetDisplayMode($zoom, $layout = 'default')
225
{
226
    // Set display mode in viewer
227
    if ($zoom == 'fullpage' || $zoom == 'fullwidth' || $zoom == 'real' || $zoom == 'default' || !is_string($zoom)) {
228
            $this->ZoomMode = $zoom;
229
    } else {
230
            $this->Error('Incorrect zoom display mode: '.$zoom);
231
    }
232
    if ($layout == 'single' || $layout == 'continuous' || $layout == 'two' || $layout == 'default') {
233
            $this->LayoutMode = $layout;
234
    } else {
235
            $this->Error('Incorrect layout display mode: '.$layout);
236
    }
237
    }
238
239
/**
240
 * @param boolean $compress
241
 */
242
function SetCompression($compress)
243
{
244
    // Set page compression
245
    if (function_exists('gzcompress')) {
246
            $this->compress = $compress;
247
    } else {
248
            $this->compress = false;
249
    }
250
    }
251
252
function SetTitle($title, $isUTF8 = false)
253
{
254
    // Title of document
255
    if ($isUTF8) {
256
            $title = $this->_UTF8toUTF16($title);
257
    }
258
    $this->title = $title;
259
}
260
261
function SetSubject($subject, $isUTF8 = false)
262
{
263
    // Subject of document
264
    if ($isUTF8) {
265
            $subject = $this->_UTF8toUTF16($subject);
266
    }
267
    $this->subject = $subject;
268
}
269
270
function SetAuthor($author, $isUTF8 = false)
271
{
272
    // Author of document
273
    if ($isUTF8) {
274
            $author = $this->_UTF8toUTF16($author);
275
    }
276
    $this->author = $author;
277
}
278
279
function SetKeywords($keywords, $isUTF8 = false)
280
{
281
    // Keywords of document
282
    if ($isUTF8) {
283
            $keywords = $this->_UTF8toUTF16($keywords);
284
    }
285
    $this->keywords = $keywords;
286
}
287
288
function SetCreator($creator, $isUTF8 = false)
289
{
290
    // Creator of document
291
    if ($isUTF8) {
292
            $creator = $this->_UTF8toUTF16($creator);
293
    }
294
    $this->creator = $creator;
295
}
296
297
function AliasNbPages($alias = '{nb}')
298
{
299
    // Define an alias for total number of pages
300
    $this->AliasNbPages = $alias;
301
}
302
303
function Error($msg)
304
{
305
    // Fatal error
306
    die('<b>FPDF error:</b> '.$msg);
307
}
308
309
function Open()
310
{
311
    // Begin document
312
    $this->state = 1;
313
}
314
315
function Close()
316
{
317
    // Terminate document
318
    if ($this->state == 3) {
319
            return;
320
    }
321
    if ($this->page == 0) {
322
            $this->AddPage();
323
    }
324
    // Page footer
325
    $this->InFooter = true;
326
    $this->Footer();
327
    $this->InFooter = false;
328
    // Close page
329
    $this->_endpage();
330
    // Close document
331
    $this->_enddoc();
332
}
333
334
function AddPage($orientation = '', $size = '')
335
{
336
    // Start a new page
337
    if ($this->state == 0) {
338
            $this->Open();
339
    }
340
    $family = $this->FontFamily;
341
    $style = $this->FontStyle.($this->underline ? 'U' : '');
342
    $fontsize = $this->FontSizePt;
343
    $lw = $this->LineWidth;
344
    $dc = $this->DrawColor;
345
    $fc = $this->FillColor;
346
    $tc = $this->TextColor;
347
    $cf = $this->ColorFlag;
348
    if ($this->page > 0)
349
    {
350
        // Page footer
351
        $this->InFooter = true;
352
        $this->Footer();
353
        $this->InFooter = false;
354
        // Close page
355
        $this->_endpage();
356
    }
357
    // Start new page
358
    $this->_beginpage($orientation, $size);
359
    // Set line cap style to square
360
    $this->_out('2 J');
361
    // Set line width
362
    $this->LineWidth = $lw;
363
    $this->_out(sprintf('%.2F w', $lw * $this->k));
364
    // Set font
365
    if ($family) {
366
            $this->SetFont($family, $style, $fontsize);
367
    }
368
    // Set colors
369
    $this->DrawColor = $dc;
370
    if ($dc != '0 G') {
371
            $this->_out($dc);
372
    }
373
    $this->FillColor = $fc;
374
    if ($fc != '0 g') {
375
            $this->_out($fc);
376
    }
377
    $this->TextColor = $tc;
378
    $this->ColorFlag = $cf;
379
    // Page header
380
    $this->InHeader = true;
381
    $this->Header();
382
    $this->InHeader = false;
383
    // Restore line width
384
    if ($this->LineWidth != $lw)
385
    {
386
        $this->LineWidth = $lw;
387
        $this->_out(sprintf('%.2F w', $lw * $this->k));
388
    }
389
    // Restore font
390
    if ($family) {
391
            $this->SetFont($family, $style, $fontsize);
392
    }
393
    // Restore colors
394
    if ($this->DrawColor != $dc)
395
    {
396
        $this->DrawColor = $dc;
397
        $this->_out($dc);
398
    }
399
    if ($this->FillColor != $fc)
400
    {
401
        $this->FillColor = $fc;
402
        $this->_out($fc);
403
    }
404
    $this->TextColor = $tc;
405
    $this->ColorFlag = $cf;
406
}
407
408
function Header()
409
{
410
    // To be implemented in your own inherited class
411
}
412
413
function Footer()
414
{
415
    // To be implemented in your own inherited class
416
}
417
418
function PageNo()
419
{
420
    // Get current page number
421
    return $this->page;
422
}
423
424
function SetDrawColor($r, $g = null, $b = null)
425
{
426
    // Set color for all stroking operations
427
    if (($r == 0 && $g == 0 && $b == 0) || $g === null) {
428
            $this->DrawColor = sprintf('%.3F G', $r / 255);
429
    } else {
430
            $this->DrawColor = sprintf('%.3F %.3F %.3F RG', $r / 255, $g / 255, $b / 255);
431
    }
432
    if ($this->page > 0) {
433
            $this->_out($this->DrawColor);
434
    }
435
    }
436
437
function SetFillColor($r, $g = null, $b = null)
438
{
439
    // Set color for all filling operations
440
    if (($r == 0 && $g == 0 && $b == 0) || $g === null) {
441
            $this->FillColor = sprintf('%.3F g', $r / 255);
442
    } else {
443
            $this->FillColor = sprintf('%.3F %.3F %.3F rg', $r / 255, $g / 255, $b / 255);
444
    }
445
    $this->ColorFlag = ($this->FillColor != $this->TextColor);
446
    if ($this->page > 0) {
447
            $this->_out($this->FillColor);
448
    }
449
    }
450
451
function SetTextColor($r, $g = null, $b = null)
452
{
453
    // Set color for text
454
    if (($r == 0 && $g == 0 && $b == 0) || $g === null) {
455
            $this->TextColor = sprintf('%.3F g', $r / 255);
456
    } else {
457
            $this->TextColor = sprintf('%.3F %.3F %.3F rg', $r / 255, $g / 255, $b / 255);
458
    }
459
    $this->ColorFlag = ($this->FillColor != $this->TextColor);
460
}
461
462
function GetStringWidth($s)
463
{
464
    // Get width of a string in the current font
465
    $s = (string) $s;
466
    $cw = &$this->CurrentFont['cw'];
467
    $w = 0;
468
    if ($this->unifontSubset) {
469
        $unicode = $this->UTF8StringToArray($s);
470
        foreach ($unicode as $char) {
471
            if (isset($cw[$char])) { $w += (ord($cw[2 * $char]) << 8) + ord($cw[2 * $char + 1]); } else if ($char > 0 && $char < 128 && isset($cw[chr($char)])) { $w += $cw[chr($char)]; } else if (isset($this->CurrentFont['desc']['MissingWidth'])) { $w += $this->CurrentFont['desc']['MissingWidth']; } else if (isset($this->CurrentFont['MissingWidth'])) { $w += $this->CurrentFont['MissingWidth']; } else { $w += 500; }
472
        }
473
    } else {
474
        $l = strlen($s);
475
        for ($i = 0; $i < $l; $i++) {
476
                    $w += $cw[$s[$i]];
477
        }
478
    }
479
    return $w * $this->FontSize / 1000;
480
}
481
482
function SetLineWidth($width)
483
{
484
    // Set line width
485
    $this->LineWidth = $width;
486
    if ($this->page > 0) {
487
            $this->_out(sprintf('%.2F w', $width * $this->k));
488
    }
489
    }
490
491
function Line($x1, $y1, $x2, $y2)
492
{
493
    // Draw a line
494
    $this->_out(sprintf('%.2F %.2F m %.2F %.2F l S', $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k));
495
}
496
497
function Rect($x, $y, $w, $h, $style = '')
498
{
499
    // Draw a rectangle
500
    if ($style == 'F') {
501
            $op = 'f';
502
    } elseif ($style == 'FD' || $style == 'DF') {
503
            $op = 'B';
504
    } else {
505
            $op = 'S';
506
    }
507
    $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x * $this->k, ($this->h - $y) * $this->k, $w * $this->k, -$h * $this->k, $op));
508
}
509
510
/**
511
 * @param string $family
512
 */
513
function AddFont($family, $style = '', $file = '', $uni = false)
514
{
515
    // Add a TrueType, OpenType or Type1 font
516
    $family = strtolower($family);
517
    $style = strtoupper($style);
518
    if ($style == 'IB') {
519
            $style = 'BI';
520
    }
521
    if ($file == '') {
522
        if ($uni) {
523
        $file = str_replace(' ', '', $family).strtolower($style).'.ttf';
524
        } else {
525
        $file = str_replace(' ', '', $family).strtolower($style).'.php';
526
        }
527
    }
528
    $fontkey = $family.$style;
529
    if (isset($this->fonts[$fontkey])) {
530
            return;
531
    }
532
533
    if ($uni) {
534
        if (defined("_SYSTEM_TTFONTS") && file_exists(_SYSTEM_TTFONTS.$file)) { $ttffilename = _SYSTEM_TTFONTS.$file; } else { $ttffilename = $this->_getfontpath().'unifont/'.$file; }
535
        $unifilename = $this->_getfontpath().'unifont/'.strtolower(substr($file, 0, (strpos($file, '.'))));
536
        $name = '';
537
        $originalsize = 0;
538
        $ttfstat = stat($ttffilename);
539
        if (file_exists($unifilename.'.mtx.php')) {
540
            include($unifilename.'.mtx.php');
541
        }
542
        if (!isset($type) || !isset($name) || $originalsize != $ttfstat['size']) {
543
            $ttffile = $ttffilename;
544
            require_once($this->_getfontpath().'unifont/ttfonts.php');
545
            $ttf = new TTFontFile();
546
            $ttf->getMetrics($ttffile);
547
            $cw = $ttf->charWidths;
548
            $name = preg_replace('/[ ()]/', '', $ttf->fullName);
549
550
            $desc = array('Ascent'=>round($ttf->ascent),
551
            'Descent'=>round($ttf->descent),
552
            'CapHeight'=>round($ttf->capHeight),
553
            'Flags'=>$ttf->flags,
554
            'FontBBox'=>'['.round($ttf->bbox[0])." ".round($ttf->bbox[1])." ".round($ttf->bbox[2])." ".round($ttf->bbox[3]).']',
555
            'ItalicAngle'=>$ttf->italicAngle,
556
            'StemV'=>round($ttf->stemV),
557
            'MissingWidth'=>round($ttf->defaultWidth));
558
            $up = round($ttf->underlinePosition);
559
            $ut = round($ttf->underlineThickness);
560
            $originalsize = $ttfstat['size'] + 0;
561
            $type = 'TTF';
562
            // Generate metrics .php file
563
            $s = '<?php'."\n";
564
            $s .= '$name=\''.$name."';\n";
565
            $s .= '$type=\''.$type."';\n";
566
            $s .= '$desc='.var_export($desc, true).";\n";
567
            $s .= '$up='.$up.";\n";
568
            $s .= '$ut='.$ut.";\n";
569
            $s .= '$ttffile=\''.$ttffile."';\n";
570
            $s .= '$originalsize='.$originalsize.";\n";
571
            $s .= '$fontkey=\''.$fontkey."';\n";
572
            $s .= "?>";
573
            if (is_writable(dirname($this->_getfontpath().'unifont/'.'x'))) {
574
                $fh = fopen($unifilename.'.mtx.php', "w");
575
                fwrite($fh, $s, strlen($s));
576
                fclose($fh);
577
                $fh = fopen($unifilename.'.cw.dat', "wb");
578
                fwrite($fh, $cw, strlen($cw));
579
                fclose($fh);
580
                @unlink($unifilename.'.cw127.php');
581
            }
582
            unset($ttf);
583
        } else {
584
            $cw = @file_get_contents($unifilename.'.cw.dat');
585
        }
586
        $i = count($this->fonts) + 1;
587
        if (!empty($this->AliasNbPages)) {
588
                    $sbarr = range(0, 57);
589
        } else {
590
                    $sbarr = range(0, 32);
591
        }
592
        $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'ttffile'=>$ttffile, 'fontkey'=>$fontkey, 'subset'=>$sbarr, 'unifilename'=>$unifilename);
593
594
        $this->FontFiles[$fontkey] = array('length1'=>$originalsize, 'type'=>"TTF", 'ttffile'=>$ttffile);
595
        $this->FontFiles[$file] = array('type'=>"TTF");
596
        unset($cw);
597
    } else {
598
        $info = $this->_loadfont($file);
599
        $info['i'] = count($this->fonts) + 1;
600
        if (!empty($info['diff']))
601
        {
602
            // Search existing encodings
603
            $n = array_search($info['diff'], $this->diffs);
604
            if (!$n)
605
            {
606
                $n = count($this->diffs) + 1;
607
                $this->diffs[$n] = $info['diff'];
608
            }
609
            $info['diffn'] = $n;
610
        }
611
        if (!empty($info['file']))
612
        {
613
            // Embedded font
614
            if ($info['type'] == 'TrueType') {
615
                            $this->FontFiles[$info['file']] = array('length1'=>$info['originalsize']);
616
            } else {
617
                            $this->FontFiles[$info['file']] = array('length1'=>$info['size1'], 'length2'=>$info['size2']);
618
            }
619
        }
620
        $this->fonts[$fontkey] = $info;
621
    }
622
}
623
624
/**
625
 * @param string $family
626
 */
627
function SetFont($family, $style = '', $size = 0)
628
{
629
    // Select a font; size given in points
630
    if ($family == '') {
631
            $family = $this->FontFamily;
632
    } else {
633
            $family = strtolower($family);
634
    }
635
    $style = strtoupper($style);
636
    if (strpos($style, 'U') !== false)
637
    {
638
        $this->underline = true;
639
        $style = str_replace('U', '', $style);
640
    } else {
641
            $this->underline = false;
642
    }
643
    if ($style == 'IB') {
644
            $style = 'BI';
645
    }
646
    if ($size == 0) {
647
            $size = $this->FontSizePt;
648
    }
649
    // Test if font is already selected
650
    if ($this->FontFamily == $family && $this->FontStyle == $style && $this->FontSizePt == $size) {
651
            return;
652
    }
653
    // Test if font is already loaded
654
    $fontkey = $family.$style;
655
    if (!isset($this->fonts[$fontkey]))
656
    {
657
        // Test if one of the core fonts
658
        if ($family == 'arial') {
659
                    $family = 'helvetica';
660
        }
661
        if (in_array($family, $this->CoreFonts))
662
        {
663
            if ($family == 'symbol' || $family == 'zapfdingbats') {
664
                            $style = '';
665
            }
666
            $fontkey = $family.$style;
667
            if (!isset($this->fonts[$fontkey])) {
668
                            $this->AddFont($family, $style);
669
            }
670
        } else {
671
                    $this->Error('Undefined font: '.$family.' '.$style);
672
        }
673
    }
674
    // Select it
675
    $this->FontFamily = $family;
676
    $this->FontStyle = $style;
677
    $this->FontSizePt = $size;
678
    $this->FontSize = $size / $this->k;
679
    $this->CurrentFont = &$this->fonts[$fontkey];
680
    if ($this->fonts[$fontkey]['type'] == 'TTF') { $this->unifontSubset = true; } else { $this->unifontSubset = false; }
681
    if ($this->page > 0) {
682
            $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
683
    }
684
    }
685
686
function SetFontSize($size)
687
{
688
    // Set font size in points
689
    if ($this->FontSizePt == $size) {
690
            return;
691
    }
692
    $this->FontSizePt = $size;
693
    $this->FontSize = $size / $this->k;
694
    if ($this->page > 0) {
695
            $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
696
    }
697
    }
698
699
function AddLink()
700
{
701
    // Create a new internal link
702
    $n = count($this->links) + 1;
703
    $this->links[$n] = array(0, 0);
704
    return $n;
705
}
706
707
function SetLink($link, $y = 0, $page = -1)
708
{
709
    // Set destination of internal link
710
    if ($y == -1) {
711
            $y = $this->y;
712
    }
713
    if ($page == -1) {
714
            $page = $this->page;
715
    }
716
    $this->links[$link] = array($page, $y);
717
}
718
719
/**
720
 * @param double $y
721
 * @param string $link
722
 */
723
function Link($x, $y, $w, $h, $link)
724
{
725
    // Put a link on the page
726
    $this->PageLinks[$this->page][] = array($x * $this->k, $this->hPt - $y * $this->k, $w * $this->k, $h * $this->k, $link);
727
}
728
729
function Text($x, $y, $txt)
730
{
731
    // Output a string
732
    if ($this->unifontSubset)
733
    {
734
        $txt2 = '('.$this->_escape($this->UTF8ToUTF16BE($txt, false)).')';
735
        foreach ($this->UTF8StringToArray($txt) as $uni) {
736
                    $this->CurrentFont['subset'][$uni] = $uni;
737
        }
738
    } else {
739
            $txt2 = '('.$this->_escape($txt).')';
740
    }
741
    $s = sprintf('BT %.2F %.2F Td %s Tj ET', $x * $this->k, ($this->h - $y) * $this->k, $txt2);
742
    if ($this->underline && $txt != '') {
743
            $s .= ' '.$this->_dounderline($x, $y, $txt);
744
    }
745
    if ($this->ColorFlag) {
746
            $s = 'q '.$this->TextColor.' '.$s.' Q';
747
    }
748
    $this->_out($s);
749
}
750
751
function AcceptPageBreak()
752
{
753
    // Accept automatic page break or not
754
    return $this->AutoPageBreak;
755
}
756
757
function Cell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = false, $link = '')
758
{
759
    // Output a cell
760
    $k = $this->k;
761
    if ($this->y + $h > $this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak())
762
    {
763
        // Automatic page break
764
        $x = $this->x;
765
        $ws = $this->ws;
766
        if ($ws > 0)
767
        {
768
            $this->ws = 0;
769
            $this->_out('0 Tw');
770
        }
771
        $this->AddPage($this->CurOrientation, $this->CurPageSize);
772
        $this->x = $x;
773
        if ($ws > 0)
774
        {
775
            $this->ws = $ws;
776
            $this->_out(sprintf('%.3F Tw', $ws * $k));
777
        }
778
    }
779
    if ($w == 0) {
780
            $w = $this->w - $this->rMargin - $this->x;
781
    }
782
    $s = '';
783
    if ($fill || $border == 1)
784
    {
785
        if ($fill) {
786
                    $op = ($border == 1) ? 'B' : 'f';
787
        } else {
788
                    $op = 'S';
789
        }
790
        $s = sprintf('%.2F %.2F %.2F %.2F re %s ', $this->x * $k, ($this->h - $this->y) * $k, $w * $k, -$h * $k, $op);
791
    }
792
    if (is_string($border))
793
    {
794
        $x = $this->x;
795
        $y = $this->y;
796
        if (strpos($border, 'L') !== false) {
797
                    $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $x * $k, ($this->h - $y) * $k, $x * $k, ($this->h - ($y + $h)) * $k);
798
        }
799
        if (strpos($border, 'T') !== false) {
800
                    $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $x * $k, ($this->h - $y) * $k, ($x + $w) * $k, ($this->h - $y) * $k);
801
        }
802
        if (strpos($border, 'R') !== false) {
803
                    $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', ($x + $w) * $k, ($this->h - $y) * $k, ($x + $w) * $k, ($this->h - ($y + $h)) * $k);
804
        }
805
        if (strpos($border, 'B') !== false) {
806
                    $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $x * $k, ($this->h - ($y + $h)) * $k, ($x + $w) * $k, ($this->h - ($y + $h)) * $k);
807
        }
808
    }
809
    if ($txt !== '')
810
    {
811
        if ($align == 'R') {
812
                    $dx = $w - $this->cMargin - $this->GetStringWidth($txt);
813
        } elseif ($align == 'C') {
814
                    $dx = ($w - $this->GetStringWidth($txt)) / 2;
815
        } else {
816
                    $dx = $this->cMargin;
817
        }
818
        if ($this->ColorFlag) {
819
                    $s .= 'q '.$this->TextColor.' ';
820
        }
821
822
        // If multibyte, Tw has no effect - do word spacing using an adjustment before each space
823
        if ($this->ws && $this->unifontSubset) {
824
            foreach ($this->UTF8StringToArray($txt) as $uni) {
825
                            $this->CurrentFont['subset'][$uni] = $uni;
826
            }
827
            $space = $this->_escape($this->UTF8ToUTF16BE(' ', false));
828
            $s .= sprintf('BT 0 Tw %.2F %.2F Td [', ($this->x + $dx) * $k, ($this->h - ($this->y + .5 * $h + .3 * $this->FontSize)) * $k);
829
            $t = explode(' ', $txt);
830
            $numt = count($t);
831
            for ($i = 0; $i < $numt; $i++) {
832
                $tx = $t[$i];
833
                $tx = '('.$this->_escape($this->UTF8ToUTF16BE($tx, false)).')';
834
                $s .= sprintf('%s ', $tx);
835
                if (($i + 1) < $numt) {
836
                    $adj = -($this->ws * $this->k) * 1000 / $this->FontSizePt;
837
                    $s .= sprintf('%d(%s) ', $adj, $space);
838
                }
839
            }
840
            $s .= '] TJ';
841
            $s .= ' ET';
842
        } else {
843
            if ($this->unifontSubset)
844
            {
845
                $txt2 = '('.$this->_escape($this->UTF8ToUTF16BE($txt, false)).')';
846
                foreach ($this->UTF8StringToArray($txt) as $uni) {
847
                                    $this->CurrentFont['subset'][$uni] = $uni;
848
                }
849
            } else {
850
                            $txt2 = '('.str_replace(')', '\\)', str_replace('(', '\\(', str_replace('\\', '\\\\', $txt))).')';
851
            }
852
            $s .= sprintf('BT %.2F %.2F Td %s Tj ET', ($this->x + $dx) * $k, ($this->h - ($this->y + .5 * $h + .3 * $this->FontSize)) * $k, $txt2);
853
        }
854
        if ($this->underline) {
855
                    $s .= ' '.$this->_dounderline($this->x + $dx, $this->y + .5 * $h + .3 * $this->FontSize, $txt);
856
        }
857
        if ($this->ColorFlag) {
858
                    $s .= ' Q';
859
        }
860
        if ($link) {
861
                    $this->Link($this->x + $dx, $this->y + .5 * $h - .5 * $this->FontSize, $this->GetStringWidth($txt), $this->FontSize, $link);
862
        }
863
    }
864
    if ($s) {
865
            $this->_out($s);
866
    }
867
    $this->lasth = $h;
868
    if ($ln > 0)
869
    {
870
        // Go to next line
871
        $this->y += $h;
872
        if ($ln == 1) {
873
                    $this->x = $this->lMargin;
874
        }
875
    } else {
876
            $this->x += $w;
877
    }
878
    }
879
880
function MultiCell($w, $h, $txt, $border = 0, $align = 'J', $fill = false)
881
{
882
    // Output text with automatic or explicit line breaks
883
    $cw = &$this->CurrentFont['cw'];
884
    if ($w == 0) {
885
            $w = $this->w - $this->rMargin - $this->x;
886
    }
887
    $wmax = ($w - 2 * $this->cMargin);
888
    $s = str_replace("\r", '', $txt);
889
    if ($this->unifontSubset) {
890
        $nb = mb_strlen($s, 'utf-8');
891
        while ($nb > 0 && mb_substr($s, $nb - 1, 1, 'utf-8') == "\n") {
892
            $nb--;
893
        }
894
    } else {
895
        $nb = strlen($s);
896
        if ($nb > 0 && $s[$nb - 1] == "\n") {
897
                    $nb--;
898
        }
899
    }
900
    $b = 0;
901
    if ($border)
902
    {
903
        if ($border == 1)
904
        {
905
            $border = 'LTRB';
906
            $b = 'LRT';
907
            $b2 = 'LR';
908
        } else
909
        {
910
            $b2 = '';
911
            if (strpos($border, 'L') !== false) {
912
                            $b2 .= 'L';
913
            }
914
            if (strpos($border, 'R') !== false) {
915
                            $b2 .= 'R';
916
            }
917
            $b = (strpos($border, 'T') !== false) ? $b2.'T' : $b2;
918
        }
919
    }
920
    $sep = -1;
921
    $i = 0;
922
    $j = 0;
923
    $l = 0;
924
    $ns = 0;
925
    $nl = 1;
926
    while ($i < $nb)
927
    {
928
        // Get next character
929
        if ($this->unifontSubset) {
930
            $c = mb_substr($s, $i, 1, 'UTF-8');
931
        } else {
932
            $c = $s[$i];
933
        }
934
        if ($c == "\n")
935
        {
936
            // Explicit line break
937
            if ($this->ws > 0)
938
            {
939
                $this->ws = 0;
940
                $this->_out('0 Tw');
941
            }
942
            if ($this->unifontSubset) {
943
                $this->Cell($w, $h, mb_substr($s, $j, $i - $j, 'UTF-8'), $b, 2, $align, $fill);
944
            } else {
945
                $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill);
946
            }
947
            $i++;
948
            $sep = -1;
949
            $j = $i;
950
            $l = 0;
951
            $ns = 0;
952
            $nl++;
953
            if ($border && $nl == 2) {
954
                            $b = $b2;
955
            }
956
            continue;
957
        }
958
        if ($c == ' ')
959
        {
960
            $sep = $i;
961
            $ls = $l;
962
            $ns++;
963
        }
964
965
        if ($this->unifontSubset) { $l += $this->GetStringWidth($c); } else { $l += $cw[$c] * $this->FontSize / 1000; }
966
967
        if ($l > $wmax)
968
        {
969
            // Automatic line break
970
            if ($sep == -1)
971
            {
972
                if ($i == $j) {
973
                                    $i++;
974
                }
975
                if ($this->ws > 0)
976
                {
977
                    $this->ws = 0;
978
                    $this->_out('0 Tw');
979
                }
980
                if ($this->unifontSubset) {
981
                    $this->Cell($w, $h, mb_substr($s, $j, $i - $j, 'UTF-8'), $b, 2, $align, $fill);
982
                } else {
983
                    $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill);
984
                }
985
            } else
986
            {
987
                if ($align == 'J')
988
                {
989
                    $this->ws = ($ns > 1) ? ($wmax - $ls) / ($ns - 1) : 0;
990
                    $this->_out(sprintf('%.3F Tw', $this->ws * $this->k));
991
                }
992
                if ($this->unifontSubset) {
993
                    $this->Cell($w, $h, mb_substr($s, $j, $sep - $j, 'UTF-8'), $b, 2, $align, $fill);
994
                } else {
995
                    $this->Cell($w, $h, substr($s, $j, $sep - $j), $b, 2, $align, $fill);
996
                }
997
                $i = $sep + 1;
998
            }
999
            $sep = -1;
1000
            $j = $i;
1001
            $l = 0;
1002
            $ns = 0;
1003
            $nl++;
1004
            if ($border && $nl == 2) {
1005
                            $b = $b2;
1006
            }
1007
        } else {
1008
                    $i++;
1009
        }
1010
    }
1011
    // Last chunk
1012
    if ($this->ws > 0)
1013
    {
1014
        $this->ws = 0;
1015
        $this->_out('0 Tw');
1016
    }
1017
    if ($border && strpos($border, 'B') !== false) {
1018
            $b .= 'B';
1019
    }
1020
    if ($this->unifontSubset) {
1021
        $this->Cell($w, $h, mb_substr($s, $j, $i - $j, 'UTF-8'), $b, 2, $align, $fill);
1022
    } else {
1023
        $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill);
1024
    }
1025
    $this->x = $this->lMargin;
1026
}
1027
1028
function Write($h, $txt, $link = '')
1029
{
1030
    // Output text in flowing mode
1031
    $cw = &$this->CurrentFont['cw'];
1032
    $w = $this->w - $this->rMargin - $this->x;
1033
1034
    $wmax = ($w - 2 * $this->cMargin);
1035
    $s = str_replace("\r", '', $txt);
1036
    if ($this->unifontSubset) {
1037
        $nb = mb_strlen($s, 'UTF-8');
1038
        if ($nb == 1 && $s == " ") {
1039
            $this->x += $this->GetStringWidth($s);
1040
            return;
1041
        }
1042
    } else {
1043
        $nb = strlen($s);
1044
    }
1045
    $sep = -1;
1046
    $i = 0;
1047
    $j = 0;
1048
    $l = 0;
1049
    $nl = 1;
1050
    while ($i < $nb)
1051
    {
1052
        // Get next character
1053
        if ($this->unifontSubset) {
1054
            $c = mb_substr($s, $i, 1, 'UTF-8');
1055
        } else {
1056
            $c = $s[$i];
1057
        }
1058
        if ($c == "\n")
1059
        {
1060
            // Explicit line break
1061
            if ($this->unifontSubset) {
1062
                $this->Cell($w, $h, mb_substr($s, $j, $i - $j, 'UTF-8'), 0, 2, '', 0, $link);
1063
            } else {
1064
                $this->Cell($w, $h, substr($s, $j, $i - $j), 0, 2, '', 0, $link);
1065
            }
1066
            $i++;
1067
            $sep = -1;
1068
            $j = $i;
1069
            $l = 0;
1070
            if ($nl == 1)
1071
            {
1072
                $this->x = $this->lMargin;
1073
                $w = $this->w - $this->rMargin - $this->x;
1074
                $wmax = ($w - 2 * $this->cMargin);
1075
            }
1076
            $nl++;
1077
            continue;
1078
        }
1079
        if ($c == ' ') {
1080
                    $sep = $i;
1081
        }
1082
1083
        if ($this->unifontSubset) { $l += $this->GetStringWidth($c); } else { $l += $cw[$c] * $this->FontSize / 1000; }
1084
1085
        if ($l > $wmax)
1086
        {
1087
            // Automatic line break
1088
            if ($sep == -1)
1089
            {
1090
                if ($this->x > $this->lMargin)
1091
                {
1092
                    // Move to next line
1093
                    $this->x = $this->lMargin;
1094
                    $this->y += $h;
1095
                    $w = $this->w - $this->rMargin - $this->x;
1096
                    $wmax = ($w - 2 * $this->cMargin);
1097
                    $i++;
1098
                    $nl++;
1099
                    continue;
1100
                }
1101
                if ($i == $j) {
1102
                                    $i++;
1103
                }
1104
                if ($this->unifontSubset) {
1105
                    $this->Cell($w, $h, mb_substr($s, $j, $i - $j, 'UTF-8'), 0, 2, '', 0, $link);
1106
                } else {
1107
                    $this->Cell($w, $h, substr($s, $j, $i - $j), 0, 2, '', 0, $link);
1108
                }
1109
            } else
1110
            {
1111
                if ($this->unifontSubset) {
1112
                    $this->Cell($w, $h, mb_substr($s, $j, $sep - $j, 'UTF-8'), 0, 2, '', 0, $link);
1113
                } else {
1114
                    $this->Cell($w, $h, substr($s, $j, $sep - $j), 0, 2, '', 0, $link);
1115
                }
1116
                $i = $sep + 1;
1117
            }
1118
            $sep = -1;
1119
            $j = $i;
1120
            $l = 0;
1121
            if ($nl == 1)
1122
            {
1123
                $this->x = $this->lMargin;
1124
                $w = $this->w - $this->rMargin - $this->x;
1125
                $wmax = ($w - 2 * $this->cMargin);
1126
            }
1127
            $nl++;
1128
        } else {
1129
                    $i++;
1130
        }
1131
    }
1132
    // Last chunk
1133
    if ($i != $j) {
1134
        if ($this->unifontSubset) {
1135
            $this->Cell($l, $h, mb_substr($s, $j, $i - $j, 'UTF-8'), 0, 0, '', 0, $link);
1136
        } else {
1137
            $this->Cell($l, $h, substr($s, $j), 0, 0, '', 0, $link);
1138
        }
1139
    }
1140
}
1141
1142
/**
1143
 * @param integer $h
1144
 */
1145
function Ln($h = null)
1146
{
1147
    // Line feed; default value is last cell height
1148
    $this->x = $this->lMargin;
1149
    if ($h === null) {
1150
            $this->y += $this->lasth;
1151
    } else {
1152
            $this->y += $h;
1153
    }
1154
    }
1155
1156
function Image($file, $x = null, $y = null, $w = 0, $h = 0, $type = '', $link = '')
1157
{
1158
    // Put an image on the page
1159
    if (!isset($this->images[$file]))
1160
    {
1161
        // First use of this image, get info
1162
        if ($type == '')
1163
        {
1164
            $pos = strrpos($file, '.');
1165
            if (!$pos) {
1166
                            $this->Error('Image file has no extension and no type was specified: '.$file);
1167
            }
1168
            $type = substr($file, $pos + 1);
1169
        }
1170
        $type = strtolower($type);
1171
        if ($type == 'jpeg') {
1172
                    $type = 'jpg';
1173
        }
1174
        $mtd = '_parse'.$type;
1175
        if (!method_exists($this, $mtd)) {
1176
                    $this->Error('Unsupported image type: '.$type);
1177
        }
1178
        $info = $this->$mtd($file);
1179
        $info['i'] = count($this->images) + 1;
1180
        $this->images[$file] = $info;
1181
    } else {
1182
            $info = $this->images[$file];
1183
    }
1184
1185
    // Automatic width and height calculation if needed
1186
    if ($w == 0 && $h == 0)
1187
    {
1188
        // Put image at 96 dpi
1189
        $w = -96;
1190
        $h = -96;
1191
    }
1192
    if ($w < 0) {
1193
            $w = -$info['w'] * 72 / $w / $this->k;
1194
    }
1195
    if ($h < 0) {
1196
            $h = -$info['h'] * 72 / $h / $this->k;
1197
    }
1198
    if ($w == 0) {
1199
            $w = $h * $info['w'] / $info['h'];
1200
    }
1201
    if ($h == 0) {
1202
            $h = $w * $info['h'] / $info['w'];
1203
    }
1204
1205
    // Flowing mode
1206
    if ($y === null)
1207
    {
1208
        if ($this->y + $h > $this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak())
1209
        {
1210
            // Automatic page break
1211
            $x2 = $this->x;
1212
            $this->AddPage($this->CurOrientation, $this->CurPageSize);
1213
            $this->x = $x2;
1214
        }
1215
        $y = $this->y;
1216
        $this->y += $h;
1217
    }
1218
1219
    if ($x === null) {
1220
            $x = $this->x;
1221
    }
1222
    $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q', $w * $this->k, $h * $this->k, $x * $this->k, ($this->h - ($y + $h)) * $this->k, $info['i']));
1223
    if ($link) {
1224
            $this->Link($x, $y, $w, $h, $link);
1225
    }
1226
    }
1227
1228
function GetX()
1229
{
1230
    // Get x position
1231
    return $this->x;
1232
}
1233
1234
function SetX($x)
1235
{
1236
    // Set x position
1237
    if ($x >= 0) {
1238
            $this->x = $x;
1239
    } else {
1240
            $this->x = $this->w + $x;
1241
    }
1242
    }
1243
1244
function GetY()
1245
{
1246
    // Get y position
1247
    return $this->y;
1248
}
1249
1250
function SetY($y)
1251
{
1252
    // Set y position and reset x
1253
    $this->x = $this->lMargin;
1254
    if ($y >= 0) {
1255
            $this->y = $y;
1256
    } else {
1257
            $this->y = $this->h + $y;
1258
    }
1259
    }
1260
1261
function SetXY($x, $y)
1262
{
1263
    // Set x and y positions
1264
    $this->SetY($y);
1265
    $this->SetX($x);
1266
}
1267
1268
function Output($name = '', $dest = '')
1269
{
1270
    // Output PDF to some destination
1271
    if ($this->state < 3) {
1272
            $this->Close();
1273
    }
1274
    $dest = strtoupper($dest);
1275
    if ($dest == '')
1276
    {
1277
        if ($name == '')
1278
        {
1279
            $name = 'doc.pdf';
1280
            $dest = 'I';
1281
        } else {
1282
                    $dest = 'F';
1283
        }
1284
    }
1285
    switch ($dest)
1286
    {
1287
        case 'I':
1288
            // Send to standard output
1289
            $this->_checkoutput();
1290
            if (PHP_SAPI != 'cli')
1291
            {
1292
                // We send to a browser
1293
                header('Content-Type: application/pdf');
1294
                header('Content-Disposition: inline; filename="'.$name.'"');
1295
                header('Cache-Control: private, max-age=0, must-revalidate');
1296
                header('Pragma: public');
1297
            }
1298
            echo $this->buffer;
1299
            break;
1300
        case 'D':
1301
            // Download file
1302
            $this->_checkoutput();
1303
            header('Content-Type: application/x-download');
1304
            header('Content-Disposition: attachment; filename="'.$name.'"');
1305
            header('Cache-Control: private, max-age=0, must-revalidate');
1306
            header('Pragma: public');
1307
            echo $this->buffer;
1308
            break;
1309
        case 'F':
1310
            // Save to local file
1311
            $f = fopen($name, 'wb');
1312
            if (!$f) {
1313
                            $this->Error('Unable to create output file: '.$name);
1314
            }
1315
            fwrite($f, $this->buffer, strlen($this->buffer));
1316
            fclose($f);
1317
            break;
1318
        case 'S':
1319
            // Return as a string
1320
            return $this->buffer;
1321
        default:
1322
            $this->Error('Incorrect output destination: '.$dest);
1323
    }
1324
    return '';
1325
}
1326
1327
/*******************************************************************************
1328
*                                                                              *
1329
*                              Protected methods                               *
1330
*                                                                              *
1331
*******************************************************************************/
1332
function _dochecks()
1333
{
1334
    // Check availability of %F
1335
    if (sprintf('%.1F', 1.0) != '1.0') {
1336
            $this->Error('This version of PHP is not supported');
1337
    }
1338
    // Check availability of mbstring
1339
    if (!function_exists('mb_strlen')) {
1340
            $this->Error('mbstring extension is not available');
1341
    }
1342
    // Check mbstring overloading
1343
    if (ini_get('mbstring.func_overload') & 2) {
1344
            $this->Error('mbstring overloading must be disabled');
1345
    }
1346
    // Ensure runtime magic quotes are disabled
1347
    if (get_magic_quotes_runtime()) {
1348
            @set_magic_quotes_runtime(0);
1349
    }
1350
    }
1351
1352
function _getfontpath()
1353
{
1354
    return $this->fontpath;
1355
}
1356
1357
function _checkoutput()
1358
{
1359
    if (PHP_SAPI != 'cli')
1360
    {
1361
        if (headers_sent($file, $line)) {
1362
                    $this->Error("Some data has already been output, can't send PDF file (output started at $file:$line)");
1363
        }
1364
    }
1365
    if (ob_get_length())
1366
    {
1367
        // The output buffer is not empty
1368
        if (preg_match('/^(\xEF\xBB\xBF)?\s*$/', ob_get_contents()))
1369
        {
1370
            // It contains only a UTF-8 BOM and/or whitespace, let's clean it
1371
            ob_clean();
1372
        } else {
1373
                    $this->Error("Some data has already been output, can't send PDF file");
1374
        }
1375
    }
1376
}
1377
1378
/**
1379
 * @param string $size
1380
 */
1381
function _getpagesize($size)
1382
{
1383
    if (is_string($size))
1384
    {
1385
        $size = strtolower($size);
1386
        if (!isset($this->StdPageSizes[$size])) {
1387
                    $this->Error('Unknown page size: '.$size);
1388
        }
1389
        $a = $this->StdPageSizes[$size];
1390
        return array($a[0] / $this->k, $a[1] / $this->k);
1391
    } else
1392
    {
1393
        if ($size[0] > $size[1]) {
1394
                    return array($size[1], $size[0]);
1395
        } else {
1396
                    return $size;
1397
        }
1398
    }
1399
}
1400
1401
/**
1402
 * @param string $orientation
1403
 * @param string $size
1404
 */
1405
function _beginpage($orientation, $size)
1406
{
1407
    $this->page++;
1408
    $this->pages[$this->page] = '';
1409
    $this->state = 2;
1410
    $this->x = $this->lMargin;
1411
    $this->y = $this->tMargin;
1412
    $this->FontFamily = '';
1413
    // Check page size and orientation
1414
    if ($orientation == '') {
1415
            $orientation = $this->DefOrientation;
1416
    } else {
1417
            $orientation = strtoupper($orientation[0]);
1418
    }
1419
    if ($size == '') {
1420
            $size = $this->DefPageSize;
1421
    } else {
1422
            $size = $this->_getpagesize($size);
1423
    }
1424
    if ($orientation != $this->CurOrientation || $size[0] != $this->CurPageSize[0] || $size[1] != $this->CurPageSize[1])
1425
    {
1426
        // New size or orientation
1427
        if ($orientation == 'P')
1428
        {
1429
            $this->w = $size[0];
1430
            $this->h = $size[1];
1431
        } else
1432
        {
1433
            $this->w = $size[1];
1434
            $this->h = $size[0];
1435
        }
1436
        $this->wPt = $this->w * $this->k;
1437
        $this->hPt = $this->h * $this->k;
1438
        $this->PageBreakTrigger = $this->h - $this->bMargin;
1439
        $this->CurOrientation = $orientation;
1440
        $this->CurPageSize = $size;
1441
    }
1442
    if ($orientation != $this->DefOrientation || $size[0] != $this->DefPageSize[0] || $size[1] != $this->DefPageSize[1]) {
1443
            $this->PageSizes[$this->page] = array($this->wPt, $this->hPt);
1444
    }
1445
    }
1446
1447
function _endpage()
1448
{
1449
    $this->state = 1;
1450
}
1451
1452
/**
1453
 * @param string $font
1454
 */
1455
function _loadfont($font)
1456
{
1457
    // Load a font definition file from the font directory
1458
    include($this->fontpath.$font);
1459
    $a = get_defined_vars();
1460
    if (!isset($a['name'])) {
1461
            $this->Error('Could not include font definition file');
1462
    }
1463
    return $a;
1464
}
1465
1466
function _escape($s)
1467
{
1468
    // Escape special characters in strings
1469
    $s = str_replace('\\', '\\\\', $s);
1470
    $s = str_replace('(', '\\(', $s);
1471
    $s = str_replace(')', '\\)', $s);
1472
    $s = str_replace("\r", '\\r', $s);
1473
    return $s;
1474
}
1475
1476
function _textstring($s)
1477
{
1478
    // Format a text string
1479
    return '('.$this->_escape($s).')';
1480
}
1481
1482
function _UTF8toUTF16($s)
1483
{
1484
    // Convert UTF-8 to UTF-16BE with BOM
1485
    $res = "\xFE\xFF";
1486
    $nb = strlen($s);
1487
    $i = 0;
1488
    while ($i < $nb)
1489
    {
1490
        $c1 = ord($s[$i++]);
1491
        if ($c1 >= 224)
1492
        {
1493
            // 3-byte character
1494
            $c2 = ord($s[$i++]);
1495
            $c3 = ord($s[$i++]);
1496
            $res .= chr((($c1 & 0x0F) << 4) + (($c2 & 0x3C) >> 2));
1497
            $res .= chr((($c2 & 0x03) << 6) + ($c3 & 0x3F));
1498
        } elseif ($c1 >= 192)
1499
        {
1500
            // 2-byte character
1501
            $c2 = ord($s[$i++]);
1502
            $res .= chr(($c1 & 0x1C) >> 2);
1503
            $res .= chr((($c1 & 0x03) << 6) + ($c2 & 0x3F));
1504
        } else
1505
        {
1506
            // Single-byte character
1507
            $res .= "\0".chr($c1);
1508
        }
1509
    }
1510
    return $res;
1511
}
1512
1513
function _dounderline($x, $y, $txt)
1514
{
1515
    // Underline text
1516
    $up = $this->CurrentFont['up'];
1517
    $ut = $this->CurrentFont['ut'];
1518
    $w = $this->GetStringWidth($txt) + $this->ws * substr_count($txt, ' ');
1519
    return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - ($y - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt);
1520
}
1521
1522
function _parsejpg($file)
1523
{
1524
    // Extract info from a JPEG file
1525
    $a = getimagesize($file);
1526
    if (!$a) {
1527
            $this->Error('Missing or incorrect image file: '.$file);
1528
    }
1529
    if ($a[2] != 2) {
1530
            $this->Error('Not a JPEG file: '.$file);
1531
    }
1532
    if (!isset($a['channels']) || $a['channels'] == 3) {
1533
            $colspace = 'DeviceRGB';
1534
    } elseif ($a['channels'] == 4) {
1535
            $colspace = 'DeviceCMYK';
1536
    } else {
1537
            $colspace = 'DeviceGray';
1538
    }
1539
    $bpc = isset($a['bits']) ? $a['bits'] : 8;
1540
    $data = file_get_contents($file);
1541
    return array('w'=>$a[0], 'h'=>$a[1], 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'DCTDecode', 'data'=>$data);
1542
}
1543
1544
/**
1545
 * @param string $file
1546
 */
1547
function _parsepng($file)
1548
{
1549
    // Extract info from a PNG file
1550
    $f = fopen($file, 'rb');
1551
    if (!$f) {
1552
            $this->Error('Can\'t open image file: '.$file);
1553
    }
1554
    $info = $this->_parsepngstream($f, $file);
1555
    fclose($f);
1556
    return $info;
1557
}
1558
1559
function _parsepngstream($f, $file)
1560
{
1561
    // Check signature
1562
    if ($this->_readstream($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
1563
            $this->Error('Not a PNG file: '.$file);
1564
    }
1565
1566
    // Read header chunk
1567
    $this->_readstream($f, 4);
1568
    if ($this->_readstream($f, 4) != 'IHDR') {
1569
            $this->Error('Incorrect PNG file: '.$file);
1570
    }
1571
    $w = $this->_readint($f);
1572
    $h = $this->_readint($f);
1573
    $bpc = ord($this->_readstream($f, 1));
1574
    if ($bpc > 8) {
1575
            $this->Error('16-bit depth not supported: '.$file);
1576
    }
1577
    $ct = ord($this->_readstream($f, 1));
1578
    if ($ct == 0 || $ct == 4) {
1579
            $colspace = 'DeviceGray';
1580
    } elseif ($ct == 2 || $ct == 6) {
1581
            $colspace = 'DeviceRGB';
1582
    } elseif ($ct == 3) {
1583
            $colspace = 'Indexed';
1584
    } else {
1585
            $this->Error('Unknown color type: '.$file);
1586
    }
1587
    if (ord($this->_readstream($f, 1)) != 0) {
1588
            $this->Error('Unknown compression method: '.$file);
1589
    }
1590
    if (ord($this->_readstream($f, 1)) != 0) {
1591
            $this->Error('Unknown filter method: '.$file);
1592
    }
1593
    if (ord($this->_readstream($f, 1)) != 0) {
1594
            $this->Error('Interlacing not supported: '.$file);
1595
    }
1596
    $this->_readstream($f, 4);
1597
    $dp = '/Predictor 15 /Colors '.($colspace == 'DeviceRGB' ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w;
1598
1599
    // Scan chunks looking for palette, transparency and image data
1600
    $pal = '';
1601
    $trns = '';
1602
    $data = '';
1603
    do
1604
    {
1605
        $n = $this->_readint($f);
1606
        $type = $this->_readstream($f, 4);
1607
        if ($type == 'PLTE')
1608
        {
1609
            // Read palette
1610
            $pal = $this->_readstream($f, $n);
1611
            $this->_readstream($f, 4);
1612
        } elseif ($type == 'tRNS')
1613
        {
1614
            // Read transparency info
1615
            $t = $this->_readstream($f, $n);
1616
            if ($ct == 0) {
1617
                            $trns = array(ord(substr($t, 1, 1)));
1618
            } elseif ($ct == 2) {
1619
                            $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
1620
            } else
1621
            {
1622
                $pos = strpos($t, chr(0));
1623
                if ($pos !== false) {
1624
                                    $trns = array($pos);
1625
                }
1626
            }
1627
            $this->_readstream($f, 4);
1628
        } elseif ($type == 'IDAT')
1629
        {
1630
            // Read image data block
1631
            $data .= $this->_readstream($f, $n);
1632
            $this->_readstream($f, 4);
1633
        } elseif ($type == 'IEND') {
1634
                    break;
1635
        } else {
1636
                    $this->_readstream($f, $n + 4);
1637
        }
1638
    }
1639
    while ($n);
1640
1641
    if ($colspace == 'Indexed' && empty($pal)) {
1642
            $this->Error('Missing palette in '.$file);
1643
    }
1644
    $info = array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'dp'=>$dp, 'pal'=>$pal, 'trns'=>$trns);
1645
    if ($ct >= 4)
1646
    {
1647
        // Extract alpha channel
1648
        if (!function_exists('gzuncompress')) {
1649
                    $this->Error('Zlib not available, can\'t handle alpha channel: '.$file);
1650
        }
1651
        $data = gzuncompress($data);
1652
        $color = '';
1653
        $alpha = '';
1654
        if ($ct == 4)
1655
        {
1656
            // Gray image
1657
            $len = 2 * $w;
1658
            for ($i = 0; $i < $h; $i++)
1659
            {
1660
                $pos = (1 + $len) * $i;
1661
                $color .= $data[$pos];
1662
                $alpha .= $data[$pos];
1663
                $line = substr($data, $pos + 1, $len);
1664
                $color .= preg_replace('/(.)./s', '$1', $line);
1665
                $alpha .= preg_replace('/.(.)/s', '$1', $line);
1666
            }
1667
        } else
1668
        {
1669
            // RGB image
1670
            $len = 4 * $w;
1671
            for ($i = 0; $i < $h; $i++)
1672
            {
1673
                $pos = (1 + $len) * $i;
1674
                $color .= $data[$pos];
1675
                $alpha .= $data[$pos];
1676
                $line = substr($data, $pos + 1, $len);
1677
                $color .= preg_replace('/(.{3})./s', '$1', $line);
1678
                $alpha .= preg_replace('/.{3}(.)/s', '$1', $line);
1679
            }
1680
        }
1681
        unset($data);
1682
        $data = gzcompress($color);
1683
        $info['smask'] = gzcompress($alpha);
1684
        if ($this->PDFVersion < '1.4') {
1685
                    $this->PDFVersion = '1.4';
1686
        }
1687
    }
1688
    $info['data'] = $data;
1689
    return $info;
1690
}
1691
1692
function _readstream($f, $n)
1693
{
1694
    // Read n bytes from stream
1695
    $res = '';
1696
    while ($n > 0 && !feof($f))
1697
    {
1698
        $s = fread($f, $n);
1699
        if ($s === false) {
1700
                    $this->Error('Error while reading stream');
1701
        }
1702
        $n -= strlen($s);
1703
        $res .= $s;
1704
    }
1705
    if ($n > 0) {
1706
            $this->Error('Unexpected end of stream');
1707
    }
1708
    return $res;
1709
}
1710
1711
function _readint($f)
1712
{
1713
    // Read a 4-byte integer from stream
1714
    $a = unpack('Ni', $this->_readstream($f, 4));
1715
    return $a['i'];
1716
}
1717
1718
function _parsegif($file)
1719
{
1720
    // Extract info from a GIF file (via PNG conversion)
1721
    if (!function_exists('imagepng')) {
1722
            $this->Error('GD extension is required for GIF support');
1723
    }
1724
    if (!function_exists('imagecreatefromgif')) {
1725
            $this->Error('GD has no GIF read support');
1726
    }
1727
    $im = imagecreatefromgif($file);
1728
    if (!$im) {
1729
            $this->Error('Missing or incorrect image file: '.$file);
1730
    }
1731
    imageinterlace($im, 0);
1732
    $f = @fopen('php://temp', 'rb+');
1733
    if ($f)
1734
    {
1735
        // Perform conversion in memory
1736
        ob_start();
1737
        imagepng($im);
1738
        $data = ob_get_clean();
1739
        imagedestroy($im);
1740
        fwrite($f, $data);
1741
        rewind($f);
1742
        $info = $this->_parsepngstream($f, $file);
1743
        fclose($f);
1744
    } else
1745
    {
1746
        // Use temporary file
1747
        $tmp = tempnam('.', 'gif');
1748
        if (!$tmp) {
1749
                    $this->Error('Unable to create a temporary file');
1750
        }
1751
        if (!imagepng($im, $tmp)) {
1752
                    $this->Error('Error while saving to temporary file');
1753
        }
1754
        imagedestroy($im);
1755
        $info = $this->_parsepng($tmp);
1756
        unlink($tmp);
1757
    }
1758
    return $info;
1759
}
1760
1761
function _newobj()
1762
{
1763
    // Begin a new object
1764
    $this->n++;
1765
    $this->offsets[$this->n] = strlen($this->buffer);
1766
    $this->_out($this->n.' 0 obj');
1767
}
1768
1769
function _putstream($s)
1770
{
1771
    $this->_out('stream');
1772
    $this->_out($s);
1773
    $this->_out('endstream');
1774
}
1775
1776
function _out($s)
1777
{
1778
    // Add a line to the document
1779
    if ($this->state == 2) {
1780
            $this->pages[$this->page] .= $s."\n";
1781
    } else {
1782
            $this->buffer .= $s."\n";
1783
    }
1784
    }
1785
1786
function _putpages()
1787
{
1788
    $nb = $this->page;
1789
    if (!empty($this->AliasNbPages))
1790
    {
1791
        // Replace number of pages in fonts using subsets
1792
        $alias = $this->UTF8ToUTF16BE($this->AliasNbPages, false);
1793
        $r = $this->UTF8ToUTF16BE("$nb", false);
1794
        for ($n = 1; $n <= $nb; $n++) {
1795
                    $this->pages[$n] = str_replace($alias, $r, $this->pages[$n]);
1796
        }
1797
        // Now repeat for no pages in non-subset fonts
1798
        for ($n = 1; $n <= $nb; $n++) {
1799
                    $this->pages[$n] = str_replace($this->AliasNbPages, $nb, $this->pages[$n]);
1800
        }
1801
    }
1802
    if ($this->DefOrientation == 'P')
1803
    {
1804
        $wPt = $this->DefPageSize[0] * $this->k;
1805
        $hPt = $this->DefPageSize[1] * $this->k;
1806
    } else
1807
    {
1808
        $wPt = $this->DefPageSize[1] * $this->k;
1809
        $hPt = $this->DefPageSize[0] * $this->k;
1810
    }
1811
    $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
1812
    for ($n = 1; $n <= $nb; $n++)
1813
    {
1814
        // Page
1815
        $this->_newobj();
1816
        $this->_out('<</Type /Page');
1817
        $this->_out('/Parent 1 0 R');
1818
        if (isset($this->PageSizes[$n])) {
1819
                    $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]', $this->PageSizes[$n][0], $this->PageSizes[$n][1]));
1820
        }
1821
        $this->_out('/Resources 2 0 R');
1822
        if (isset($this->PageLinks[$n]))
1823
        {
1824
            // Links
1825
            $annots = '/Annots [';
1826
            foreach ($this->PageLinks[$n] as $pl)
1827
            {
1828
                $rect = sprintf('%.2F %.2F %.2F %.2F', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3]);
1829
                $annots .= '<</Type /Annot /Subtype /Link /Rect ['.$rect.'] /Border [0 0 0] ';
1830
                if (is_string($pl[4])) {
1831
                                    $annots .= '/A <</S /URI /URI '.$this->_textstring($pl[4]).'>>>>';
1832
                } else
1833
                {
1834
                    $l = $this->links[$pl[4]];
1835
                    $h = isset($this->PageSizes[$l[0]]) ? $this->PageSizes[$l[0]][1] : $hPt;
1836
                    $annots .= sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]>>', 1 + 2 * $l[0], $h - $l[1] * $this->k);
1837
                }
1838
            }
1839
            $this->_out($annots.']');
1840
        }
1841
        if ($this->PDFVersion > '1.3') {
1842
                    $this->_out('/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>');
1843
        }
1844
        $this->_out('/Contents '.($this->n + 1).' 0 R>>');
1845
        $this->_out('endobj');
1846
        // Page content
1847
        $p = ($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n];
1848
        $this->_newobj();
1849
        $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
1850
        $this->_putstream($p);
1851
        $this->_out('endobj');
1852
    }
1853
    // Pages root
1854
    $this->offsets[1] = strlen($this->buffer);
1855
    $this->_out('1 0 obj');
1856
    $this->_out('<</Type /Pages');
1857
    $kids = '/Kids [';
1858
    for ($i = 0; $i < $nb; $i++) {
1859
            $kids .= (3 + 2 * $i).' 0 R ';
1860
    }
1861
    $this->_out($kids.']');
1862
    $this->_out('/Count '.$nb);
1863
    $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]', $wPt, $hPt));
1864
    $this->_out('>>');
1865
    $this->_out('endobj');
1866
}
1867
1868
function _putfonts()
1869
{
1870
    $nf = $this->n;
1871
    foreach ($this->diffs as $diff)
1872
    {
1873
        // Encodings
1874
        $this->_newobj();
1875
        $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
1876
        $this->_out('endobj');
1877
    }
1878
    foreach ($this->FontFiles as $file=>$info)
1879
    {
1880
        if (!isset($info['type']) || $info['type'] != 'TTF') {
1881
        // Font file embedding
1882
        $this->_newobj();
1883
        $this->FontFiles[$file]['n'] = $this->n;
1884
        $font = '';
1885
        $f = fopen($this->_getfontpath().$file, 'rb', 1);
1886
        if (!$f) {
1887
                    $this->Error('Font file not found');
1888
        }
1889
        while (!feof($f)) {
1890
                    $font .= fread($f, 8192);
1891
        }
1892
        fclose($f);
1893
        $compressed = (substr($file, -2) == '.z');
1894
        if (!$compressed && isset($info['length2']))
1895
        {
1896
            $header = (ord($font[0]) == 128);
1897
            if ($header)
1898
            {
1899
                // Strip first binary header
1900
                $font = substr($font, 6);
1901
            }
1902
            if ($header && ord($font[$info['length1']]) == 128)
1903
            {
1904
                // Strip second binary header
1905
                $font = substr($font, 0, $info['length1']).substr($font, $info['length1'] + 6);
1906
            }
1907
        }
1908
        $this->_out('<</Length '.strlen($font));
1909
        if ($compressed) {
1910
                    $this->_out('/Filter /FlateDecode');
1911
        }
1912
        $this->_out('/Length1 '.$info['length1']);
1913
        if (isset($info['length2'])) {
1914
                    $this->_out('/Length2 '.$info['length2'].' /Length3 0');
1915
        }
1916
        $this->_out('>>');
1917
        $this->_putstream($font);
1918
        $this->_out('endobj');
1919
        }
1920
    }
1921
    foreach ($this->fonts as $k=>$font)
1922
    {
1923
        // Font objects
1924
        //$this->fonts[$k]['n']=$this->n+1;
1925
        $type = $font['type'];
1926
        $name = $font['name'];
1927
        if ($type == 'Core')
1928
        {
1929
            // Standard font
1930
            $this->fonts[$k]['n'] = $this->n + 1;
1931
            $this->_newobj();
1932
            $this->_out('<</Type /Font');
1933
            $this->_out('/BaseFont /'.$name);
1934
            $this->_out('/Subtype /Type1');
1935
            if ($name != 'Symbol' && $name != 'ZapfDingbats') {
1936
                            $this->_out('/Encoding /WinAnsiEncoding');
1937
            }
1938
            $this->_out('>>');
1939
            $this->_out('endobj');
1940
        } elseif ($type == 'Type1' || $type == 'TrueType')
1941
        {
1942
            // Additional Type1 or TrueType font
1943
            $this->fonts[$k]['n'] = $this->n + 1;
1944
            $this->_newobj();
1945
            $this->_out('<</Type /Font');
1946
            $this->_out('/BaseFont /'.$name);
1947
            $this->_out('/Subtype /'.$type);
1948
            $this->_out('/FirstChar 32 /LastChar 255');
1949
            $this->_out('/Widths '.($this->n + 1).' 0 R');
1950
            $this->_out('/FontDescriptor '.($this->n + 2).' 0 R');
1951
            if ($font['enc'])
1952
            {
1953
                if (isset($font['diff'])) {
1954
                                    $this->_out('/Encoding '.($nf + $font['diff']).' 0 R');
1955
                } else {
1956
                                    $this->_out('/Encoding /WinAnsiEncoding');
1957
                }
1958
            }
1959
            $this->_out('>>');
1960
            $this->_out('endobj');
1961
            // Widths
1962
            $this->_newobj();
1963
            $cw = &$font['cw'];
1964
            $s = '[';
1965
            for ($i = 32; $i <= 255; $i++) {
1966
                            $s .= $cw[chr($i)].' ';
1967
            }
1968
            $this->_out($s.']');
1969
            $this->_out('endobj');
1970
            // Descriptor
1971
            $this->_newobj();
1972
            $s = '<</Type /FontDescriptor /FontName /'.$name;
1973
            foreach ($font['desc'] as $k=>$v) {
1974
                            $s .= ' /'.$k.' '.$v;
1975
            }
1976
            $file = $font['file'];
1977
            if ($file) {
1978
                            $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R';
1979
            }
1980
            $this->_out($s.'>>');
1981
            $this->_out('endobj');
1982
        }
1983
        // TrueType embedded SUBSETS or FULL
1984
        else if ($type == 'TTF') {
1985
            $this->fonts[$k]['n'] = $this->n + 1;
1986
            require_once($this->_getfontpath().'unifont/ttfonts.php');
1987
            $ttf = new TTFontFile();
1988
            $fontname = 'MPDFAA'.'+'.$font['name'];
1989
            $subset = $font['subset'];
1990
            unset($subset[0]);
1991
            $ttfontstream = $ttf->makeSubset($font['ttffile'], $subset);
1992
            $ttfontsize = strlen($ttfontstream);
1993
            $fontstream = gzcompress($ttfontstream);
1994
            $codeToGlyph = $ttf->codeToGlyph;
1995
            unset($codeToGlyph[0]);
1996
1997
            // Type0 Font
1998
            // A composite font - a font composed of other fonts, organized hierarchically
1999
            $this->_newobj();
2000
            $this->_out('<</Type /Font');
2001
            $this->_out('/Subtype /Type0');
2002
            $this->_out('/BaseFont /'.$fontname.'');
2003
            $this->_out('/Encoding /Identity-H');
2004
            $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
2005
            $this->_out('/ToUnicode '.($this->n + 2).' 0 R');
2006
            $this->_out('>>');
2007
            $this->_out('endobj');
2008
2009
            // CIDFontType2
2010
            // A CIDFont whose glyph descriptions are based on TrueType font technology
2011
            $this->_newobj();
2012
            $this->_out('<</Type /Font');
2013
            $this->_out('/Subtype /CIDFontType2');
2014
            $this->_out('/BaseFont /'.$fontname.'');
2015
            $this->_out('/CIDSystemInfo '.($this->n + 2).' 0 R');
2016
            $this->_out('/FontDescriptor '.($this->n + 3).' 0 R');
2017
            if (isset($font['desc']['MissingWidth'])) {
2018
                $this->_out('/DW '.$font['desc']['MissingWidth'].'');
2019
            }
2020
2021
            $this->_putTTfontwidths($font, $ttf->maxUni);
2022
2023
            $this->_out('/CIDToGIDMap '.($this->n + 4).' 0 R');
2024
            $this->_out('>>');
2025
            $this->_out('endobj');
2026
2027
            // ToUnicode
2028
            $this->_newobj();
2029
            $toUni = "/CIDInit /ProcSet findresource begin\n";
2030
            $toUni .= "12 dict begin\n";
2031
            $toUni .= "begincmap\n";
2032
            $toUni .= "/CIDSystemInfo\n";
2033
            $toUni .= "<</Registry (Adobe)\n";
2034
            $toUni .= "/Ordering (UCS)\n";
2035
            $toUni .= "/Supplement 0\n";
2036
            $toUni .= ">> def\n";
2037
            $toUni .= "/CMapName /Adobe-Identity-UCS def\n";
2038
            $toUni .= "/CMapType 2 def\n";
2039
            $toUni .= "1 begincodespacerange\n";
2040
            $toUni .= "<0000> <FFFF>\n";
2041
            $toUni .= "endcodespacerange\n";
2042
            $toUni .= "1 beginbfrange\n";
2043
            $toUni .= "<0000> <FFFF> <0000>\n";
2044
            $toUni .= "endbfrange\n";
2045
            $toUni .= "endcmap\n";
2046
            $toUni .= "CMapName currentdict /CMap defineresource pop\n";
2047
            $toUni .= "end\n";
2048
            $toUni .= "end";
2049
            $this->_out('<</Length '.(strlen($toUni)).'>>');
2050
            $this->_putstream($toUni);
2051
            $this->_out('endobj');
2052
2053
            // CIDSystemInfo dictionary
2054
            $this->_newobj();
2055
            $this->_out('<</Registry (Adobe)');
2056
            $this->_out('/Ordering (UCS)');
2057
            $this->_out('/Supplement 0');
2058
            $this->_out('>>');
2059
            $this->_out('endobj');
2060
2061
            // Font descriptor
2062
            $this->_newobj();
2063
            $this->_out('<</Type /FontDescriptor');
2064
            $this->_out('/FontName /'.$fontname);
2065
            foreach ($font['desc'] as $kd=>$v) {
2066
                if ($kd == 'Flags') { $v = $v | 4; $v = $v & ~32; } // SYMBOLIC font flag
2067
                $this->_out(' /'.$kd.' '.$v);
2068
            }
2069
            $this->_out('/FontFile2 '.($this->n + 2).' 0 R');
2070
            $this->_out('>>');
2071
            $this->_out('endobj');
2072
2073
            // Embed CIDToGIDMap
2074
            // A specification of the mapping from CIDs to glyph indices
2075
            $cidtogidmap = '';
2076
            $cidtogidmap = str_pad('', 256 * 256 * 2, "\x00");
2077
            foreach ($codeToGlyph as $cc=>$glyph) {
2078
                $cidtogidmap[$cc * 2] = chr($glyph >> 8);
2079
                $cidtogidmap[$cc * 2 + 1] = chr($glyph & 0xFF);
2080
            }
2081
            $cidtogidmap = gzcompress($cidtogidmap);
2082
            $this->_newobj();
2083
            $this->_out('<</Length '.strlen($cidtogidmap).'');
2084
            $this->_out('/Filter /FlateDecode');
2085
            $this->_out('>>');
2086
            $this->_putstream($cidtogidmap);
2087
            $this->_out('endobj');
2088
2089
            //Font file
2090
            $this->_newobj();
2091
            $this->_out('<</Length '.strlen($fontstream));
2092
            $this->_out('/Filter /FlateDecode');
2093
            $this->_out('/Length1 '.$ttfontsize);
2094
            $this->_out('>>');
2095
            $this->_putstream($fontstream);
2096
            $this->_out('endobj');
2097
            unset($ttf);
2098
        } else
2099
        {
2100
            // Allow for additional types
2101
            $this->fonts[$k]['n'] = $this->n + 1;
2102
            $mtd = '_put'.strtolower($type);
2103
            if (!method_exists($this, $mtd)) {
2104
                            $this->Error('Unsupported font type: '.$type);
2105
            }
2106
            $this->$mtd($font);
2107
        }
2108
    }
2109
}
2110
2111
/**
2112
 * @param integer $maxUni
2113
 */
2114
function _putTTfontwidths(&$font, $maxUni) {
2115
    if (file_exists($font['unifilename'].'.cw127.php')) {
2116
        include($font['unifilename'].'.cw127.php');
2117
        $startcid = 128;
2118
    } else {
2119
        $rangeid = 0;
2120
        $range = array();
2121
        $prevcid = -2;
2122
        $prevwidth = -1;
2123
        $interval = false;
2124
        $startcid = 1;
2125
    }
2126
    $cwlen = $maxUni + 1;
2127
2128
    // for each character
2129
    for ($cid = $startcid; $cid < $cwlen; $cid++) {
2130
        if ($cid == 128 && (!file_exists($font['unifilename'].'.cw127.php'))) {
2131
            if (is_writable(dirname($this->_getfontpath().'unifont/x'))) {
2132
                $fh = fopen($font['unifilename'].'.cw127.php', "wb");
2133
                $cw127 = '<?php'."\n";
2134
                $cw127 .= '$rangeid='.$rangeid.";\n";
2135
                $cw127 .= '$prevcid='.$prevcid.";\n";
2136
                $cw127 .= '$prevwidth='.$prevwidth.";\n";
2137
                if ($interval) { $cw127 .= '$interval=true'.";\n"; } else { $cw127 .= '$interval=false'.";\n"; }
2138
                $cw127 .= '$range='.var_export($range, true).";\n";
2139
                $cw127 .= "?>";
2140
                fwrite($fh, $cw127, strlen($cw127));
2141
                fclose($fh);
2142
            }
2143
        }
2144
        if ($font['cw'][$cid * 2] == "\00" && $font['cw'][$cid * 2 + 1] == "\00") { continue; }
2145
        $width = (ord($font['cw'][$cid * 2]) << 8) + ord($font['cw'][$cid * 2 + 1]);
2146
        if ($width == 65535) { $width = 0; }
2147
        if ($cid > 255 && (!isset($font['subset'][$cid]) || !$font['subset'][$cid])) { continue; }
2148
        if (!isset($font['dw']) || (isset($font['dw']) && $width != $font['dw'])) {
2149
            if ($cid == ($prevcid + 1)) {
2150
                if ($width == $prevwidth) {
2151
                    if ($width == $range[$rangeid][0]) {
2152
                        $range[$rangeid][] = $width;
2153
                    } else {
2154
                        array_pop($range[$rangeid]);
2155
                        // new range
2156
                        $rangeid = $prevcid;
2157
                        $range[$rangeid] = array();
2158
                        $range[$rangeid][] = $prevwidth;
2159
                        $range[$rangeid][] = $width;
2160
                    }
2161
                    $interval = true;
2162
                    $range[$rangeid]['interval'] = true;
2163
                } else {
2164
                    if ($interval) {
2165
                        // new range
2166
                        $rangeid = $cid;
2167
                        $range[$rangeid] = array();
2168
                        $range[$rangeid][] = $width;
2169
                    } else { $range[$rangeid][] = $width; }
2170
                    $interval = false;
2171
                }
2172
            } else {
2173
                $rangeid = $cid;
2174
                $range[$rangeid] = array();
2175
                $range[$rangeid][] = $width;
2176
                $interval = false;
2177
            }
2178
            $prevcid = $cid;
2179
            $prevwidth = $width;
2180
        }
2181
    }
2182
    $prevk = -1;
2183
    $nextk = -1;
2184
    $prevint = false;
2185
    foreach ($range as $k => $ws) {
2186
        $cws = count($ws);
2187
        if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
2188
            if (isset($range[$k]['interval'])) { unset($range[$k]['interval']); }
2189
            $range[$prevk] = array_merge($range[$prevk], $range[$k]);
2190
            unset($range[$k]);
2191
        } else { $prevk = $k; }
2192
        $nextk = $k + $cws;
2193
        if (isset($ws['interval'])) {
2194
            if ($cws > 3) { $prevint = true; } else { $prevint = false; }
2195
            unset($range[$k]['interval']);
2196
            --$nextk;
2197
        } else { $prevint = false; }
2198
    }
2199
    $w = '';
2200
    foreach ($range as $k => $ws) {
2201
        if (count(array_count_values($ws)) == 1) { $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0]; } else { $w .= ' '.$k.' [ '.implode(' ', $ws).' ]'."\n"; }
2202
    }
2203
    $this->_out('/W ['.$w.' ]');
2204
}
2205
2206
function _putimages()
2207
{
2208
    foreach (array_keys($this->images) as $file)
2209
    {
2210
        $this->_putimage($this->images[$file]);
2211
        unset($this->images[$file]['data']);
2212
        unset($this->images[$file]['smask']);
2213
    }
2214
}
2215
2216
function _putimage(&$info)
2217
{
2218
    $this->_newobj();
2219
    $info['n'] = $this->n;
2220
    $this->_out('<</Type /XObject');
2221
    $this->_out('/Subtype /Image');
2222
    $this->_out('/Width '.$info['w']);
2223
    $this->_out('/Height '.$info['h']);
2224
    if ($info['cs'] == 'Indexed') {
2225
            $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal']) / 3 - 1).' '.($this->n + 1).' 0 R]');
2226
    } else
2227
    {
2228
        $this->_out('/ColorSpace /'.$info['cs']);
2229
        if ($info['cs'] == 'DeviceCMYK') {
2230
                    $this->_out('/Decode [1 0 1 0 1 0 1 0]');
2231
        }
2232
    }
2233
    $this->_out('/BitsPerComponent '.$info['bpc']);
2234
    if (isset($info['f'])) {
2235
            $this->_out('/Filter /'.$info['f']);
2236
    }
2237
    if (isset($info['dp'])) {
2238
            $this->_out('/DecodeParms <<'.$info['dp'].'>>');
2239
    }
2240
    if (isset($info['trns']) && is_array($info['trns']))
2241
    {
2242
        $trns = '';
2243
        for ($i = 0; $i < count($info['trns']); $i++) {
2244
                    $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
2245
        }
2246
        $this->_out('/Mask ['.$trns.']');
2247
    }
2248
    if (isset($info['smask'])) {
2249
            $this->_out('/SMask '.($this->n + 1).' 0 R');
2250
    }
2251
    $this->_out('/Length '.strlen($info['data']).'>>');
2252
    $this->_putstream($info['data']);
2253
    $this->_out('endobj');
2254
    // Soft mask
2255
    if (isset($info['smask']))
2256
    {
2257
        $dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns '.$info['w'];
2258
        $smask = array('w'=>$info['w'], 'h'=>$info['h'], 'cs'=>'DeviceGray', 'bpc'=>8, 'f'=>$info['f'], 'dp'=>$dp, 'data'=>$info['smask']);
2259
        $this->_putimage($smask);
2260
    }
2261
    // Palette
2262
    if ($info['cs'] == 'Indexed')
2263
    {
2264
        $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
2265
        $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
2266
        $this->_newobj();
2267
        $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
2268
        $this->_putstream($pal);
2269
        $this->_out('endobj');
2270
    }
2271
}
2272
2273
function _putxobjectdict()
2274
{
2275
    foreach ($this->images as $image) {
2276
            $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
2277
    }
2278
    }
2279
2280
function _putresourcedict()
2281
{
2282
    $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
2283
    $this->_out('/Font <<');
2284
    foreach ($this->fonts as $font) {
2285
        $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
2286
    }
2287
    $this->_out('>>');
2288
    $this->_out('/XObject <<');
2289
    $this->_putxobjectdict();
2290
    $this->_out('>>');
2291
}
2292
2293
function _putresources()
2294
{
2295
    $this->_putfonts();
2296
    $this->_putimages();
2297
    // Resource dictionary
2298
    $this->offsets[2] = strlen($this->buffer);
2299
    $this->_out('2 0 obj');
2300
    $this->_out('<<');
2301
    $this->_putresourcedict();
2302
    $this->_out('>>');
2303
    $this->_out('endobj');
2304
}
2305
2306
function _putinfo()
2307
{
2308
    $this->_out('/Producer '.$this->_textstring('tFPDF '.tFPDF_VERSION));
2309
    if (!empty($this->title)) {
2310
            $this->_out('/Title '.$this->_textstring($this->title));
2311
    }
2312
    if (!empty($this->subject)) {
2313
            $this->_out('/Subject '.$this->_textstring($this->subject));
2314
    }
2315
    if (!empty($this->author)) {
2316
            $this->_out('/Author '.$this->_textstring($this->author));
2317
    }
2318
    if (!empty($this->keywords)) {
2319
            $this->_out('/Keywords '.$this->_textstring($this->keywords));
2320
    }
2321
    if (!empty($this->creator)) {
2322
            $this->_out('/Creator '.$this->_textstring($this->creator));
2323
    }
2324
    $this->_out('/CreationDate '.$this->_textstring('D:'.@date('YmdHis')));
2325
}
2326
2327
function _putcatalog()
2328
{
2329
    $this->_out('/Type /Catalog');
2330
    $this->_out('/Pages 1 0 R');
2331
    if ($this->ZoomMode == 'fullpage') {
2332
            $this->_out('/OpenAction [3 0 R /Fit]');
2333
    } elseif ($this->ZoomMode == 'fullwidth') {
2334
            $this->_out('/OpenAction [3 0 R /FitH null]');
2335
    } elseif ($this->ZoomMode == 'real') {
2336
            $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
2337
    } elseif (!is_string($this->ZoomMode)) {
2338
            $this->_out('/OpenAction [3 0 R /XYZ null null '.sprintf('%.2F', $this->ZoomMode / 100).']');
2339
    }
2340
    if ($this->LayoutMode == 'single') {
2341
            $this->_out('/PageLayout /SinglePage');
2342
    } elseif ($this->LayoutMode == 'continuous') {
2343
            $this->_out('/PageLayout /OneColumn');
2344
    } elseif ($this->LayoutMode == 'two') {
2345
            $this->_out('/PageLayout /TwoColumnLeft');
2346
    }
2347
    }
2348
2349
function _putheader()
2350
{
2351
    $this->_out('%PDF-'.$this->PDFVersion);
2352
}
2353
2354
function _puttrailer()
2355
{
2356
    $this->_out('/Size '.($this->n + 1));
2357
    $this->_out('/Root '.$this->n.' 0 R');
2358
    $this->_out('/Info '.($this->n - 1).' 0 R');
2359
}
2360
2361
function _enddoc()
2362
{
2363
    $this->_putheader();
2364
    $this->_putpages();
2365
    $this->_putresources();
2366
    // Info
2367
    $this->_newobj();
2368
    $this->_out('<<');
2369
    $this->_putinfo();
2370
    $this->_out('>>');
2371
    $this->_out('endobj');
2372
    // Catalog
2373
    $this->_newobj();
2374
    $this->_out('<<');
2375
    $this->_putcatalog();
2376
    $this->_out('>>');
2377
    $this->_out('endobj');
2378
    // Cross-ref
2379
    $o = strlen($this->buffer);
2380
    $this->_out('xref');
2381
    $this->_out('0 '.($this->n + 1));
2382
    $this->_out('0000000000 65535 f ');
2383
    for ($i = 1; $i <= $this->n; $i++) {
2384
            $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
2385
    }
2386
    // Trailer
2387
    $this->_out('trailer');
2388
    $this->_out('<<');
2389
    $this->_puttrailer();
2390
    $this->_out('>>');
2391
    $this->_out('startxref');
2392
    $this->_out($o);
2393
    $this->_out('%%EOF');
2394
    $this->state = 3;
2395
}
2396
2397
// ********* NEW FUNCTIONS *********
2398
// Converts UTF-8 strings to UTF16-BE.
2399
function UTF8ToUTF16BE($str, $setbom = true) {
2400
    $outstr = "";
2401
    if ($setbom) {
2402
        $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
2403
    }
2404
    $outstr .= mb_convert_encoding($str, 'UTF-16BE', 'UTF-8');
2405
    return $outstr;
2406
}
2407
2408
// Converts UTF-8 strings to codepoints array
2409
function UTF8StringToArray($str) {
2410
    $out = array();
2411
    $len = strlen($str);
2412
    for ($i = 0; $i < $len; $i++) {
2413
    $uni = -1;
2414
        $h = ord($str[$i]);
2415
        if ($h <= 0x7F) {
2416
                    $uni = $h;
2417
        } elseif ($h >= 0xC2) {
2418
            if (($h <= 0xDF) && ($i < $len - 1)) {
2419
                        $uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F);
2420
            } elseif (($h <= 0xEF) && ($i < $len - 2)) {
2421
                        $uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6
2422
                                        | (ord($str[++$i]) & 0x3F);
2423
            } elseif (($h <= 0xF4) && ($i < $len - 3)) {
2424
                        $uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12
2425
                                        | (ord($str[++$i]) & 0x3F) << 6
2426
                                        | (ord($str[++$i]) & 0x3F);
2427
            }
2428
        }
2429
    if ($uni >= 0) {
2430
        $out[] = $uni;
2431
    }
2432
    }
2433
    return $out;
2434
}
2435
2436
2437
// End of class
2438
}
2439
2440
// Handle special IE contype request
2441
if (isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] == 'contype')
2442
{
2443
    header('Content-Type: application/pdf');
2444
    exit;
2445
}