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

tFPDF::_putfonts()   F

Complexity

Conditions 32
Paths 8692

Size

Total Lines 242
Code Lines 176

Duplication

Lines 2
Ratio 0.83 %

Importance

Changes 0
Metric Value
cc 32
eloc 176
nc 8692
nop 0
dl 2
loc 242
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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:

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
}