Passed
Push — master ( 7c50e7...453b47 )
by Joe Nilson
02:27
created

FPDF::Cell()   F

Complexity

Conditions 28
Paths > 20000

Size

Total Lines 78
Code Lines 59

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 28
eloc 59
c 1
b 0
f 0
nc 395760
nop 8
dl 0
loc 78
rs 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
* FPDF                                                                         *
4
*                                                                              *
5
* Version: 1.83                                                                *
6
* Date:    2021-04-18                                                          *
7
* Author:  Olivier PLATHEY                                                     *
8
*******************************************************************************/
9
10
define('FPDF_VERSION','1.83');
11
12
class FPDF
13
{
14
protected $page;               // current page number
15
protected $n;                  // current object number
16
protected $offsets;            // array of object offsets
17
protected $buffer;             // buffer holding in-memory PDF
18
protected $pages;              // array containing pages
19
protected $state;              // current document state
20
protected $compress;           // compression flag
21
protected $k;                  // scale factor (number of points in user unit)
22
protected $DefOrientation;     // default orientation
23
protected $CurOrientation;     // current orientation
24
protected $StdPageSizes;       // standard page sizes
25
protected $DefPageSize;        // default page size
26
protected $CurPageSize;        // current page size
27
protected $CurRotation;        // current page rotation
28
protected $PageInfo;           // page-related data
29
protected $wPt, $hPt;          // dimensions of current page in points
30
protected $w, $h;              // dimensions of current page in user unit
31
protected $lMargin;            // left margin
32
protected $tMargin;            // top margin
33
protected $rMargin;            // right margin
34
protected $bMargin;            // page break margin
35
protected $cMargin;            // cell margin
36
protected $x, $y;              // current position in user unit
37
protected $lasth;              // height of last printed cell
38
protected $LineWidth;          // line width in user unit
39
protected $fontpath;           // path containing fonts
40
protected $CoreFonts;          // array of core font names
41
protected $fonts;              // array of used fonts
42
protected $FontFiles;          // array of font files
43
protected $encodings;          // array of encodings
44
protected $cmaps;              // array of ToUnicode CMaps
45
protected $FontFamily;         // current font family
46
protected $FontStyle;          // current font style
47
protected $underline;          // underlining flag
48
protected $CurrentFont;        // current font info
49
protected $FontSizePt;         // current font size in points
50
protected $FontSize;           // current font size in user unit
51
protected $DrawColor;          // commands for drawing color
52
protected $FillColor;          // commands for filling color
53
protected $TextColor;          // commands for text color
54
protected $ColorFlag;          // indicates whether fill and text colors are different
55
protected $WithAlpha;          // indicates whether alpha channel is used
56
protected $ws;                 // word spacing
57
protected $images;             // array of used images
58
protected $PageLinks;          // array of links in pages
59
protected $links;              // array of internal links
60
protected $AutoPageBreak;      // automatic page breaking
61
protected $PageBreakTrigger;   // threshold used to trigger page breaks
62
protected $InHeader;           // flag set when processing header
63
protected $InFooter;           // flag set when processing footer
64
protected $AliasNbPages;       // alias for total number of pages
65
protected $ZoomMode;           // zoom display mode
66
protected $LayoutMode;         // layout display mode
67
protected $metadata;           // document properties
68
protected $PDFVersion;         // PDF version number
69
70
/*******************************************************************************
71
*                               Public methods                                 *
72
*******************************************************************************/
73
74
function __construct($orientation='P', $unit='mm', $size='A4')
75
{
76
	// Some checks
77
	$this->_dochecks();
78
	// Initialization of properties
79
	$this->state = 0;
80
	$this->page = 0;
81
	$this->n = 2;
82
	$this->buffer = '';
83
	$this->pages = array();
84
	$this->PageInfo = array();
85
	$this->fonts = array();
86
	$this->FontFiles = array();
87
	$this->encodings = array();
88
	$this->cmaps = array();
89
	$this->images = array();
90
	$this->links = array();
91
	$this->InHeader = false;
92
	$this->InFooter = false;
93
	$this->lasth = 0;
94
	$this->FontFamily = '';
95
	$this->FontStyle = '';
96
	$this->FontSizePt = 12;
97
	$this->underline = false;
98
	$this->DrawColor = '0 G';
99
	$this->FillColor = '0 g';
100
	$this->TextColor = '0 g';
101
	$this->ColorFlag = false;
102
	$this->WithAlpha = false;
103
	$this->ws = 0;
104
	// Font path
105
	if(defined('FPDF_FONTPATH'))
106
	{
107
		$this->fontpath = FPDF_FONTPATH;
108
		if(substr($this->fontpath,-1)!='/' && substr($this->fontpath,-1)!='\\')
109
			$this->fontpath .= '/';
110
	}
111
	elseif(is_dir(dirname(__FILE__).'/font'))
112
		$this->fontpath = dirname(__FILE__).'/font/';
113
	else
114
		$this->fontpath = '';
115
	// Core fonts
116
	$this->CoreFonts = array('courier', 'helvetica', 'times', 'symbol', 'zapfdingbats','verdana','calligra','Arimo-Regular');
117
	// Scale factor
118
	if($unit=='pt')
119
		$this->k = 1;
120
	elseif($unit=='mm')
121
		$this->k = 72/25.4;
122
	elseif($unit=='cm')
123
		$this->k = 72/2.54;
124
	elseif($unit=='in')
125
		$this->k = 72;
126
	else
127
		$this->Error('Incorrect unit: '.$unit);
128
	// Page sizes
129
	$this->StdPageSizes = array('a3'=>array(841.89,1190.55), 'a4'=>array(595.28,841.89), 'a5'=>array(420.94,595.28),
130
		'letter'=>array(612,792), 'legal'=>array(612,1008));
131
	$size = $this->_getpagesize($size);
132
	$this->DefPageSize = $size;
133
	$this->CurPageSize = $size;
134
	// Page orientation
135
	$orientation = strtolower($orientation);
136
	if($orientation=='p' || $orientation=='portrait')
137
	{
138
		$this->DefOrientation = 'P';
139
		$this->w = $size[0];
140
		$this->h = $size[1];
141
	}
142
	elseif($orientation=='l' || $orientation=='landscape')
143
	{
144
		$this->DefOrientation = 'L';
145
		$this->w = $size[1];
146
		$this->h = $size[0];
147
	}
148
	else
149
		$this->Error('Incorrect orientation: '.$orientation);
150
	$this->CurOrientation = $this->DefOrientation;
151
	$this->wPt = $this->w*$this->k;
152
	$this->hPt = $this->h*$this->k;
153
	// Page rotation
154
	$this->CurRotation = 0;
155
	// Page margins (1 cm)
156
	$margin = 28.35/$this->k;
157
	$this->SetMargins($margin,$margin);
158
	// Interior cell margin (1 mm)
159
	$this->cMargin = $margin/10;
160
	// Line width (0.2 mm)
161
	$this->LineWidth = .567/$this->k;
162
	// Automatic page break
163
	$this->SetAutoPageBreak(true,2*$margin);
164
	// Default display mode
165
	$this->SetDisplayMode('default');
166
	// Enable compression
167
	$this->SetCompression(true);
168
	// Set default PDF version number
169
	$this->PDFVersion = '1.3';
170
}
171
172
function SetMargins($left, $top, $right=null)
173
{
174
	// Set left, top and right margins
175
	$this->lMargin = $left;
176
	$this->tMargin = $top;
177
	if($right===null)
178
		$right = $left;
179
	$this->rMargin = $right;
180
}
181
182
function SetLeftMargin($margin)
183
{
184
	// Set left margin
185
	$this->lMargin = $margin;
186
	if($this->page>0 && $this->x<$margin)
187
		$this->x = $margin;
188
}
189
190
function SetTopMargin($margin)
191
{
192
	// Set top margin
193
	$this->tMargin = $margin;
194
}
195
196
function SetRightMargin($margin)
197
{
198
	// Set right margin
199
	$this->rMargin = $margin;
200
}
201
202
function SetAutoPageBreak($auto, $margin=0)
203
{
204
	// Set auto page break mode and triggering margin
205
	$this->AutoPageBreak = $auto;
206
	$this->bMargin = $margin;
207
	$this->PageBreakTrigger = $this->h-$margin;
208
}
209
210
function SetDisplayMode($zoom, $layout='default')
211
{
212
	// Set display mode in viewer
213
	if($zoom=='fullpage' || $zoom=='fullwidth' || $zoom=='real' || $zoom=='default' || !is_string($zoom))
214
		$this->ZoomMode = $zoom;
215
	else
216
		$this->Error('Incorrect zoom display mode: '.$zoom);
217
	if($layout=='single' || $layout=='continuous' || $layout=='two' || $layout=='default')
218
		$this->LayoutMode = $layout;
219
	else
220
		$this->Error('Incorrect layout display mode: '.$layout);
221
}
222
223
function SetCompression($compress)
224
{
225
	// Set page compression
226
	if(function_exists('gzcompress'))
227
		$this->compress = $compress;
228
	else
229
		$this->compress = false;
230
}
231
232
function SetTitle($title, $isUTF8=false)
233
{
234
	// Title of document
235
	$this->metadata['Title'] = $isUTF8 ? $title : utf8_encode($title);
236
}
237
238
function SetAuthor($author, $isUTF8=false)
239
{
240
	// Author of document
241
	$this->metadata['Author'] = $isUTF8 ? $author : utf8_encode($author);
242
}
243
244
function SetSubject($subject, $isUTF8=false)
245
{
246
	// Subject of document
247
	$this->metadata['Subject'] = $isUTF8 ? $subject : utf8_encode($subject);
248
}
249
250
function SetKeywords($keywords, $isUTF8=false)
251
{
252
	// Keywords of document
253
	$this->metadata['Keywords'] = $isUTF8 ? $keywords : utf8_encode($keywords);
254
}
255
256
function SetCreator($creator, $isUTF8=false)
257
{
258
	// Creator of document
259
	$this->metadata['Creator'] = $isUTF8 ? $creator : utf8_encode($creator);
260
}
261
262
function AliasNbPages($alias='{nb}')
263
{
264
	// Define an alias for total number of pages
265
	$this->AliasNbPages = $alias;
266
}
267
268
function Error($msg)
269
{
270
	// Fatal error
271
	throw new Exception('FPDF error: '.$msg);
272
}
273
274
function Close()
275
{
276
	// Terminate document
277
	if($this->state==3)
278
		return;
279
	if($this->page==0)
280
		$this->AddPage();
281
	// Page footer
282
	$this->InFooter = true;
283
	$this->Footer();
284
	$this->InFooter = false;
285
	// Close page
286
	$this->_endpage();
287
	// Close document
288
	$this->_enddoc();
289
}
290
291
function AddPage($orientation='', $size='', $rotation=0)
292
{
293
	// Start a new page
294
	if($this->state==3)
295
		$this->Error('The document is closed');
296
	$family = $this->FontFamily;
297
	$style = $this->FontStyle.($this->underline ? 'U' : '');
298
	$fontsize = $this->FontSizePt;
299
	$lw = $this->LineWidth;
300
	$dc = $this->DrawColor;
301
	$fc = $this->FillColor;
302
	$tc = $this->TextColor;
303
	$cf = $this->ColorFlag;
304
	if($this->page>0)
305
	{
306
		// Page footer
307
		$this->InFooter = true;
308
		$this->Footer();
309
		$this->InFooter = false;
310
		// Close page
311
		$this->_endpage();
312
	}
313
	// Start new page
314
	$this->_beginpage($orientation,$size,$rotation);
315
	// Set line cap style to square
316
	$this->_out('2 J');
317
	// Set line width
318
	$this->LineWidth = $lw;
319
	$this->_out(sprintf('%.2F w',$lw*$this->k));
320
	// Set font
321
	if($family)
322
		$this->SetFont($family,$style,$fontsize);
323
	// Set colors
324
	$this->DrawColor = $dc;
325
	if($dc!='0 G')
326
		$this->_out($dc);
327
	$this->FillColor = $fc;
328
	if($fc!='0 g')
329
		$this->_out($fc);
330
	$this->TextColor = $tc;
331
	$this->ColorFlag = $cf;
332
	// Page header
333
	$this->InHeader = true;
334
	$this->Header();
335
	$this->InHeader = false;
336
	// Restore line width
337
	if($this->LineWidth!=$lw)
338
	{
339
		$this->LineWidth = $lw;
340
		$this->_out(sprintf('%.2F w',$lw*$this->k));
341
	}
342
	// Restore font
343
	if($family)
344
		$this->SetFont($family,$style,$fontsize);
345
	// Restore colors
346
	if($this->DrawColor!=$dc)
347
	{
348
		$this->DrawColor = $dc;
349
		$this->_out($dc);
350
	}
351
	if($this->FillColor!=$fc)
352
	{
353
		$this->FillColor = $fc;
354
		$this->_out($fc);
355
	}
356
	$this->TextColor = $tc;
357
	$this->ColorFlag = $cf;
358
}
359
360
function Header()
361
{
362
	// To be implemented in your own inherited class
363
}
364
365
function Footer()
366
{
367
	// To be implemented in your own inherited class
368
}
369
370
function PageNo()
371
{
372
	// Get current page number
373
	return $this->page;
374
}
375
376
function SetDrawColor($r, $g=null, $b=null)
377
{
378
	// Set color for all stroking operations
379
	if(($r==0 && $g==0 && $b==0) || $g===null)
380
		$this->DrawColor = sprintf('%.3F G',$r/255);
381
	else
382
		$this->DrawColor = sprintf('%.3F %.3F %.3F RG',$r/255,$g/255,$b/255);
383
	if($this->page>0)
384
		$this->_out($this->DrawColor);
385
}
386
387
function SetFillColor($r, $g=null, $b=null)
388
{
389
	// Set color for all filling operations
390
	if(($r==0 && $g==0 && $b==0) || $g===null)
391
		$this->FillColor = sprintf('%.3F g',$r/255);
392
	else
393
		$this->FillColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255);
394
	$this->ColorFlag = ($this->FillColor!=$this->TextColor);
395
	if($this->page>0)
396
		$this->_out($this->FillColor);
397
}
398
399
function SetTextColor($r, $g=null, $b=null)
400
{
401
	// Set color for text
402
	if(($r==0 && $g==0 && $b==0) || $g===null)
403
		$this->TextColor = sprintf('%.3F g',$r/255);
404
	else
405
		$this->TextColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255);
406
	$this->ColorFlag = ($this->FillColor!=$this->TextColor);
407
}
408
409
function GetStringWidth($s)
410
{
411
	// Get width of a string in the current font
412
	$s = (string)$s;
413
	$cw = &$this->CurrentFont['cw'];
414
	$w = 0;
415
	$l = strlen($s);
416
	for($i=0;$i<$l;$i++)
417
		$w += $cw[$s[$i]];
418
	return $w*$this->FontSize/1000;
419
}
420
421
function SetLineWidth($width)
422
{
423
	// Set line width
424
	$this->LineWidth = $width;
425
	if($this->page>0)
426
		$this->_out(sprintf('%.2F w',$width*$this->k));
427
}
428
429
function Line($x1, $y1, $x2, $y2)
430
{
431
	// Draw a line
432
	$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));
433
}
434
435
function Rect($x, $y, $w, $h, $style='')
436
{
437
	// Draw a rectangle
438
	if($style=='F')
439
		$op = 'f';
440
	elseif($style=='FD' || $style=='DF')
441
		$op = 'B';
442
	else
443
		$op = 'S';
444
	$this->_out(sprintf('%.2F %.2F %.2F %.2F re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op));
445
}
446
447
function AddFont($family, $style='', $file='')
448
{
449
	// Add a TrueType, OpenType or Type1 font
450
	$family = strtolower($family);
451
	if($file=='')
452
		$file = str_replace(' ','',$family).strtolower($style).'.php';
453
	$style = strtoupper($style);
454
	if($style=='IB')
455
		$style = 'BI';
456
	$fontkey = $family.$style;
457
	if(isset($this->fonts[$fontkey]))
458
		return;
459
	$info = $this->_loadfont($file);
460
	$info['i'] = count($this->fonts)+1;
461
	if(!empty($info['file']))
462
	{
463
		// Embedded font
464
		if($info['type']=='TrueType')
465
			$this->FontFiles[$info['file']] = array('length1'=>$info['originalsize']);
466
		else
467
			$this->FontFiles[$info['file']] = array('length1'=>$info['size1'], 'length2'=>$info['size2']);
468
	}
469
	$this->fonts[$fontkey] = $info;
470
}
471
472
function SetFont($family, $style='', $size=0)
473
{
474
	// Select a font; size given in points
475
	if($family=='')
476
		$family = $this->FontFamily;
477
	else
478
		$family = strtolower($family);
479
	$style = strtoupper($style);
480
	if(strpos($style,'U')!==false)
481
	{
482
		$this->underline = true;
483
		$style = str_replace('U','',$style);
484
	}
485
	else
486
		$this->underline = false;
487
	if($style=='IB')
488
		$style = 'BI';
489
	if($size==0)
490
		$size = $this->FontSizePt;
491
	// Test if font is already selected
492
	if($this->FontFamily==$family && $this->FontStyle==$style && $this->FontSizePt==$size)
493
		return;
494
	// Test if font is already loaded
495
	$fontkey = $family.$style;
496
	if(!isset($this->fonts[$fontkey]))
497
	{
498
		// Test if one of the core fonts
499
		if($family=='arial')
500
			$family = 'helvetica';
501
		if(in_array($family,$this->CoreFonts))
502
		{
503
			if($family=='symbol' || $family=='zapfdingbats')
504
				$style = '';
505
			$fontkey = $family.$style;
506
			if(!isset($this->fonts[$fontkey]))
507
				$this->AddFont($family,$style);
508
		}
509
		else
510
			$this->Error('Undefined font: '.$family.' '.$style);
511
	}
512
	// Select it
513
	$this->FontFamily = $family;
514
	$this->FontStyle = $style;
515
	$this->FontSizePt = $size;
516
	$this->FontSize = $size/$this->k;
517
	$this->CurrentFont = &$this->fonts[$fontkey];
518
	if($this->page>0)
519
		$this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
520
}
521
522
function SetFontSize($size)
523
{
524
	// Set font size in points
525
	if($this->FontSizePt==$size)
526
		return;
527
	$this->FontSizePt = $size;
528
	$this->FontSize = $size/$this->k;
529
	if($this->page>0)
530
		$this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
531
}
532
533
function AddLink()
534
{
535
	// Create a new internal link
536
	$n = count($this->links)+1;
537
	$this->links[$n] = array(0, 0);
538
	return $n;
539
}
540
541
function SetLink($link, $y=0, $page=-1)
542
{
543
	// Set destination of internal link
544
	if($y==-1)
545
		$y = $this->y;
546
	if($page==-1)
547
		$page = $this->page;
548
	$this->links[$link] = array($page, $y);
549
}
550
551
function Link($x, $y, $w, $h, $link)
552
{
553
	// Put a link on the page
554
	$this->PageLinks[$this->page][] = array($x*$this->k, $this->hPt-$y*$this->k, $w*$this->k, $h*$this->k, $link);
555
}
556
557
function Text($x, $y, $txt)
558
{
559
	// Output a string
560
	if(!isset($this->CurrentFont))
561
		$this->Error('No font has been set');
562
	$s = sprintf('BT %.2F %.2F Td (%s) Tj ET',$x*$this->k,($this->h-$y)*$this->k,$this->_escape($txt));
563
	if($this->underline && $txt!='')
564
		$s .= ' '.$this->_dounderline($x,$y,$txt);
565
	if($this->ColorFlag)
566
		$s = 'q '.$this->TextColor.' '.$s.' Q';
567
	$this->_out($s);
568
}
569
570
function AcceptPageBreak()
571
{
572
	// Accept automatic page break or not
573
	return $this->AutoPageBreak;
574
}
575
576
function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='')
577
{
578
	// Output a cell
579
	$k = $this->k;
580
	if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak())
581
	{
582
		// Automatic page break
583
		$x = $this->x;
584
		$ws = $this->ws;
585
		if($ws>0)
586
		{
587
			$this->ws = 0;
588
			$this->_out('0 Tw');
589
		}
590
		$this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation);
591
		$this->x = $x;
592
		if($ws>0)
593
		{
594
			$this->ws = $ws;
595
			$this->_out(sprintf('%.3F Tw',$ws*$k));
596
		}
597
	}
598
	if($w==0)
599
		$w = $this->w-$this->rMargin-$this->x;
600
	$s = '';
601
	if($fill || $border==1)
602
	{
603
		if($fill)
604
			$op = ($border==1) ? 'B' : 'f';
605
		else
606
			$op = 'S';
607
		$s = sprintf('%.2F %.2F %.2F %.2F re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op);
608
	}
609
	if(is_string($border))
610
	{
611
		$x = $this->x;
612
		$y = $this->y;
613
		if(strpos($border,'L')!==false)
614
			$s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k);
615
		if(strpos($border,'T')!==false)
616
			$s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k);
617
		if(strpos($border,'R')!==false)
618
			$s .= sprintf('%.2F %.2F m %.2F %.2F l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
619
		if(strpos($border,'B')!==false)
620
			$s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
621
	}
622
	if($txt!=='')
623
	{
624
		if(!isset($this->CurrentFont))
625
			$this->Error('No font has been set');
626
		if($align=='R')
627
			$dx = $w-$this->cMargin-$this->GetStringWidth($txt);
628
		elseif($align=='C')
629
			$dx = ($w-$this->GetStringWidth($txt))/2;
630
		else
631
			$dx = $this->cMargin;
632
		if($this->ColorFlag)
633
			$s .= 'q '.$this->TextColor.' ';
634
		$s .= sprintf('BT %.2F %.2F Td (%s) Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$this->_escape($txt));
635
		if($this->underline)
636
			$s .= ' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$txt);
637
		if($this->ColorFlag)
638
			$s .= ' Q';
639
		if($link)
640
			$this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$this->GetStringWidth($txt),$this->FontSize,$link);
641
	}
642
	if($s)
643
		$this->_out($s);
644
	$this->lasth = $h;
645
	if($ln>0)
646
	{
647
		// Go to next line
648
		$this->y += $h;
649
		if($ln==1)
650
			$this->x = $this->lMargin;
651
	}
652
	else
653
		$this->x += $w;
654
}
655
656
function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false)
657
{
658
	// Output text with automatic or explicit line breaks
659
	if(!isset($this->CurrentFont))
660
		$this->Error('No font has been set');
661
	$cw = &$this->CurrentFont['cw'];
662
	if($w==0)
663
		$w = $this->w-$this->rMargin-$this->x;
664
	$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
665
	$s = str_replace("\r",'',$txt);
666
	$nb = strlen($s);
667
	if($nb>0 && $s[$nb-1]=="\n")
668
		$nb--;
669
	$b = 0;
670
	if($border)
671
	{
672
		if($border==1)
673
		{
674
			$border = 'LTRB';
675
			$b = 'LRT';
676
			$b2 = 'LR';
677
		}
678
		else
679
		{
680
			$b2 = '';
681
			if(strpos($border,'L')!==false)
682
				$b2 .= 'L';
683
			if(strpos($border,'R')!==false)
684
				$b2 .= 'R';
685
			$b = (strpos($border,'T')!==false) ? $b2.'T' : $b2;
686
		}
687
	}
688
	$sep = -1;
689
	$i = 0;
690
	$j = 0;
691
	$l = 0;
692
	$ns = 0;
693
	$nl = 1;
694
	while($i<$nb)
695
	{
696
		// Get next character
697
		$c = $s[$i];
698
		if($c=="\n")
699
		{
700
			// Explicit line break
701
			if($this->ws>0)
702
			{
703
				$this->ws = 0;
704
				$this->_out('0 Tw');
705
			}
706
			$this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
707
			$i++;
708
			$sep = -1;
709
			$j = $i;
710
			$l = 0;
711
			$ns = 0;
712
			$nl++;
713
			if($border && $nl==2)
714
				$b = $b2;
715
			continue;
716
		}
717
		if($c==' ')
718
		{
719
			$sep = $i;
720
			$ls = $l;
721
			$ns++;
722
		}
723
		$l += $cw[$c];
724
		if($l>$wmax)
725
		{
726
			// Automatic line break
727
			if($sep==-1)
728
			{
729
				if($i==$j)
730
					$i++;
731
				if($this->ws>0)
732
				{
733
					$this->ws = 0;
734
					$this->_out('0 Tw');
735
				}
736
				$this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
737
			}
738
			else
739
			{
740
				if($align=='J')
741
				{
742
					$this->ws = ($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0;
743
					$this->_out(sprintf('%.3F Tw',$this->ws*$this->k));
744
				}
745
				$this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill);
746
				$i = $sep+1;
747
			}
748
			$sep = -1;
749
			$j = $i;
750
			$l = 0;
751
			$ns = 0;
752
			$nl++;
753
			if($border && $nl==2)
754
				$b = $b2;
755
		}
756
		else
757
			$i++;
758
	}
759
	// Last chunk
760
	if($this->ws>0)
761
	{
762
		$this->ws = 0;
763
		$this->_out('0 Tw');
764
	}
765
	if($border && strpos($border,'B')!==false)
766
		$b .= 'B';
767
	$this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
768
	$this->x = $this->lMargin;
769
}
770
771
function Write($h, $txt, $link='')
772
{
773
	// Output text in flowing mode
774
	if(!isset($this->CurrentFont))
775
		$this->Error('No font has been set');
776
	$cw = &$this->CurrentFont['cw'];
777
	$w = $this->w-$this->rMargin-$this->x;
778
	$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
779
	$s = str_replace("\r",'',$txt);
780
	$nb = strlen($s);
781
	$sep = -1;
782
	$i = 0;
783
	$j = 0;
784
	$l = 0;
785
	$nl = 1;
786
	while($i<$nb)
787
	{
788
		// Get next character
789
		$c = $s[$i];
790
		if($c=="\n")
791
		{
792
			// Explicit line break
793
			$this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',false,$link);
794
			$i++;
795
			$sep = -1;
796
			$j = $i;
797
			$l = 0;
798
			if($nl==1)
799
			{
800
				$this->x = $this->lMargin;
801
				$w = $this->w-$this->rMargin-$this->x;
802
				$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
803
			}
804
			$nl++;
805
			continue;
806
		}
807
		if($c==' ')
808
			$sep = $i;
809
		$l += $cw[$c];
810
		if($l>$wmax)
811
		{
812
			// Automatic line break
813
			if($sep==-1)
814
			{
815
				if($this->x>$this->lMargin)
816
				{
817
					// Move to next line
818
					$this->x = $this->lMargin;
819
					$this->y += $h;
820
					$w = $this->w-$this->rMargin-$this->x;
821
					$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
822
					$i++;
823
					$nl++;
824
					continue;
825
				}
826
				if($i==$j)
827
					$i++;
828
				$this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',false,$link);
829
			}
830
			else
831
			{
832
				$this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',false,$link);
833
				$i = $sep+1;
834
			}
835
			$sep = -1;
836
			$j = $i;
837
			$l = 0;
838
			if($nl==1)
839
			{
840
				$this->x = $this->lMargin;
841
				$w = $this->w-$this->rMargin-$this->x;
842
				$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
843
			}
844
			$nl++;
845
		}
846
		else
847
			$i++;
848
	}
849
	// Last chunk
850
	if($i!=$j)
851
		$this->Cell($l/1000*$this->FontSize,$h,substr($s,$j),0,0,'',false,$link);
852
}
853
854
function Ln($h=null)
855
{
856
	// Line feed; default value is the last cell height
857
	$this->x = $this->lMargin;
858
	if($h===null)
859
		$this->y += $this->lasth;
860
	else
861
		$this->y += $h;
862
}
863
864
function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='')
865
{
866
	// Put an image on the page
867
	if($file=='')
868
		$this->Error('Image file name is empty');
869
	if(!isset($this->images[$file]))
870
	{
871
		// First use of this image, get info
872
		if($type=='')
873
		{
874
			$pos = strrpos($file,'.');
875
			if(!$pos)
876
				$this->Error('Image file has no extension and no type was specified: '.$file);
877
			$type = substr($file,$pos+1);
878
		}
879
		$type = strtolower($type);
880
		if($type=='jpeg')
881
			$type = 'jpg';
882
		$mtd = '_parse'.$type;
883
		if(!method_exists($this,$mtd))
884
			$this->Error('Unsupported image type: '.$type);
885
		$info = $this->$mtd($file);
886
		$info['i'] = count($this->images)+1;
887
		$this->images[$file] = $info;
888
	}
889
	else
890
		$info = $this->images[$file];
891
892
	// Automatic width and height calculation if needed
893
	if($w==0 && $h==0)
894
	{
895
		// Put image at 96 dpi
896
		$w = -96;
897
		$h = -96;
898
	}
899
	if($w<0)
900
		$w = -$info['w']*72/$w/$this->k;
901
	if($h<0)
902
		$h = -$info['h']*72/$h/$this->k;
903
	if($w==0)
904
		$w = $h*$info['w']/$info['h'];
905
	if($h==0)
906
		$h = $w*$info['h']/$info['w'];
907
908
	// Flowing mode
909
	if($y===null)
910
	{
911
		if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak())
912
		{
913
			// Automatic page break
914
			$x2 = $this->x;
915
			$this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation);
916
			$this->x = $x2;
917
		}
918
		$y = $this->y;
919
		$this->y += $h;
920
	}
921
922
	if($x===null)
923
		$x = $this->x;
924
	$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']));
925
	if($link)
926
		$this->Link($x,$y,$w,$h,$link);
927
}
928
929
function GetPageWidth()
930
{
931
	// Get current page width
932
	return $this->w;
933
}
934
935
function GetPageHeight()
936
{
937
	// Get current page height
938
	return $this->h;
939
}
940
941
function GetX()
942
{
943
	// Get x position
944
	return $this->x;
945
}
946
947
function SetX($x)
948
{
949
	// Set x position
950
	if($x>=0)
951
		$this->x = $x;
952
	else
953
		$this->x = $this->w+$x;
954
}
955
956
function GetY()
957
{
958
	// Get y position
959
	return $this->y;
960
}
961
962
function SetY($y, $resetX=true)
963
{
964
	// Set y position and optionally reset x
965
	if($y>=0)
966
		$this->y = $y;
967
	else
968
		$this->y = $this->h+$y;
969
	if($resetX)
970
		$this->x = $this->lMargin;
971
}
972
973
function SetXY($x, $y)
974
{
975
	// Set x and y positions
976
	$this->SetX($x);
977
	$this->SetY($y,false);
978
}
979
980
function Output($dest='', $name='', $isUTF8=false)
981
{
982
	// Output PDF to some destination
983
	$this->Close();
984
	if(strlen($name)==1 && strlen($dest)!=1)
985
	{
986
		// Fix parameter order
987
		$tmp = $dest;
988
		$dest = $name;
989
		$name = $tmp;
990
	}
991
	if($dest=='')
992
		$dest = 'I';
993
	if($name=='')
994
		$name = 'doc.pdf';
995
	switch(strtoupper($dest))
996
	{
997
		case 'I':
998
			// Send to standard output
999
			$this->_checkoutput();
1000
			if(PHP_SAPI!='cli')
1001
			{
1002
				// We send to a browser
1003
				header('Content-Type: application/pdf');
1004
				header('Content-Disposition: inline; '.$this->_httpencode('filename',$name,$isUTF8));
1005
				header('Cache-Control: private, max-age=0, must-revalidate');
1006
				header('Pragma: public');
1007
			}
1008
			echo $this->buffer;
1009
			break;
1010
		case 'D':
1011
			// Download file
1012
			$this->_checkoutput();
1013
			header('Content-Type: application/x-download');
1014
			header('Content-Disposition: attachment; '.$this->_httpencode('filename',$name,$isUTF8));
1015
			header('Cache-Control: private, max-age=0, must-revalidate');
1016
			header('Pragma: public');
1017
			echo $this->buffer;
1018
			break;
1019
		case 'F':
1020
			// Save to local file
1021
			if(!file_put_contents($name,$this->buffer))
1022
				$this->Error('Unable to create output file: '.$name);
1023
			break;
1024
		case 'S':
1025
			// Return as a string
1026
			return $this->buffer;
1027
		default:
1028
			$this->Error('Incorrect output destination: '.$dest);
1029
	}
1030
	return '';
1031
}
1032
1033
/*******************************************************************************
1034
*                              Protected methods                               *
1035
*******************************************************************************/
1036
1037
protected function _dochecks()
1038
{
1039
	// Check mbstring overloading
1040
	if(ini_get('mbstring.func_overload') & 2)
1041
		$this->Error('mbstring overloading must be disabled');
1042
}
1043
1044
protected function _checkoutput()
1045
{
1046
	if(PHP_SAPI!='cli')
1047
	{
1048
		if(headers_sent($file,$line))
1049
			$this->Error("Some data has already been output, can't send PDF file (output started at $file:$line)");
1050
	}
1051
	if(ob_get_length())
1052
	{
1053
		// The output buffer is not empty
1054
		if(preg_match('/^(\xEF\xBB\xBF)?\s*$/',ob_get_contents()))
1055
		{
1056
			// It contains only a UTF-8 BOM and/or whitespace, let's clean it
1057
			ob_clean();
1058
		}
1059
		else
1060
			$this->Error("Some data has already been output, can't send PDF file");
1061
	}
1062
}
1063
1064
protected function _getpagesize($size)
1065
{
1066
	if(is_string($size))
1067
	{
1068
		$size = strtolower($size);
1069
		if(!isset($this->StdPageSizes[$size]))
1070
			$this->Error('Unknown page size: '.$size);
1071
		$a = $this->StdPageSizes[$size];
1072
		return array($a[0]/$this->k, $a[1]/$this->k);
1073
	}
1074
	else
1075
	{
1076
		if($size[0]>$size[1])
1077
			return array($size[1], $size[0]);
1078
		else
1079
			return $size;
1080
	}
1081
}
1082
1083
protected function _beginpage($orientation, $size, $rotation)
1084
{
1085
	$this->page++;
1086
	$this->pages[$this->page] = '';
1087
	$this->PageLinks[$this->page] = array();
1088
	$this->state = 2;
1089
	$this->x = $this->lMargin;
1090
	$this->y = $this->tMargin;
1091
	$this->FontFamily = '';
1092
	// Check page size and orientation
1093
	if($orientation=='')
1094
		$orientation = $this->DefOrientation;
1095
	else
1096
		$orientation = strtoupper($orientation[0]);
1097
	if($size=='')
1098
		$size = $this->DefPageSize;
1099
	else
1100
		$size = $this->_getpagesize($size);
1101
	if($orientation!=$this->CurOrientation || $size[0]!=$this->CurPageSize[0] || $size[1]!=$this->CurPageSize[1])
1102
	{
1103
		// New size or orientation
1104
		if($orientation=='P')
1105
		{
1106
			$this->w = $size[0];
1107
			$this->h = $size[1];
1108
		}
1109
		else
1110
		{
1111
			$this->w = $size[1];
1112
			$this->h = $size[0];
1113
		}
1114
		$this->wPt = $this->w*$this->k;
1115
		$this->hPt = $this->h*$this->k;
1116
		$this->PageBreakTrigger = $this->h-$this->bMargin;
1117
		$this->CurOrientation = $orientation;
1118
		$this->CurPageSize = $size;
1119
	}
1120
	if($orientation!=$this->DefOrientation || $size[0]!=$this->DefPageSize[0] || $size[1]!=$this->DefPageSize[1])
1121
		$this->PageInfo[$this->page]['size'] = array($this->wPt, $this->hPt);
1122
	if($rotation!=0)
1123
	{
1124
		if($rotation%90!=0)
1125
			$this->Error('Incorrect rotation value: '.$rotation);
1126
		$this->CurRotation = $rotation;
1127
		$this->PageInfo[$this->page]['rotation'] = $rotation;
1128
	}
1129
}
1130
1131
protected function _endpage()
1132
{
1133
	$this->state = 1;
1134
}
1135
1136
protected function _loadfont($font)
1137
{
1138
	// Load a font definition file from the font directory
1139
	if(strpos($font,'/')!==false || strpos($font,"\\")!==false)
1140
		$this->Error('Incorrect font definition file name: '.$font);
1141
	include($this->fontpath.$font);
1142
	if(!isset($name))
1143
		$this->Error('Could not include font definition file');
1144
	if(isset($enc))
1145
		$enc = strtolower($enc);
1146
	if(!isset($subsetted))
1147
		$subsetted = false;
1148
	return get_defined_vars();
1149
}
1150
1151
protected function _isascii($s)
1152
{
1153
	// Test if string is ASCII
1154
	$nb = strlen($s);
1155
	for($i=0;$i<$nb;$i++)
1156
	{
1157
		if(ord($s[$i])>127)
1158
			return false;
1159
	}
1160
	return true;
1161
}
1162
1163
protected function _httpencode($param, $value, $isUTF8)
1164
{
1165
	// Encode HTTP header field parameter
1166
	if($this->_isascii($value))
1167
		return $param.'="'.$value.'"';
1168
	if(!$isUTF8)
1169
		$value = utf8_encode($value);
1170
	if(strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')!==false)
1171
		return $param.'="'.rawurlencode($value).'"';
1172
	else
1173
		return $param."*=UTF-8''".rawurlencode($value);
1174
}
1175
1176
protected function _UTF8toUTF16($s)
1177
{
1178
	// Convert UTF-8 to UTF-16BE with BOM
1179
	$res = "\xFE\xFF";
1180
	$nb = strlen($s);
1181
	$i = 0;
1182
	while($i<$nb)
1183
	{
1184
		$c1 = ord($s[$i++]);
1185
		if($c1>=224)
1186
		{
1187
			// 3-byte character
1188
			$c2 = ord($s[$i++]);
1189
			$c3 = ord($s[$i++]);
1190
			$res .= chr((($c1 & 0x0F)<<4) + (($c2 & 0x3C)>>2));
1191
			$res .= chr((($c2 & 0x03)<<6) + ($c3 & 0x3F));
1192
		}
1193
		elseif($c1>=192)
1194
		{
1195
			// 2-byte character
1196
			$c2 = ord($s[$i++]);
1197
			$res .= chr(($c1 & 0x1C)>>2);
1198
			$res .= chr((($c1 & 0x03)<<6) + ($c2 & 0x3F));
1199
		}
1200
		else
1201
		{
1202
			// Single-byte character
1203
			$res .= "\0".chr($c1);
1204
		}
1205
	}
1206
	return $res;
1207
}
1208
1209
protected function _escape($s)
1210
{
1211
	// Escape special characters
1212
	if(strpos($s,'(')!==false || strpos($s,')')!==false || strpos($s,'\\')!==false || strpos($s,"\r")!==false)
1213
		return str_replace(array('\\','(',')',"\r"), array('\\\\','\\(','\\)','\\r'), $s);
1214
	else
1215
		return $s;
1216
}
1217
1218
protected function _textstring($s)
1219
{
1220
	// Format a text string
1221
	if(!$this->_isascii($s))
1222
		$s = $this->_UTF8toUTF16($s);
1223
	return '('.$this->_escape($s).')';
1224
}
1225
1226
protected function _dounderline($x, $y, $txt)
1227
{
1228
	// Underline text
1229
	$up = $this->CurrentFont['up'];
1230
	$ut = $this->CurrentFont['ut'];
1231
	$w = $this->GetStringWidth($txt)+$this->ws*substr_count($txt,' ');
1232
	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);
1233
}
1234
1235
protected function _parsejpg($file)
1236
{
1237
	// Extract info from a JPEG file
1238
	$a = getimagesize($file);
1239
	if(!$a)
1240
		$this->Error('Missing or incorrect image file: '.$file);
1241
	if($a[2]!=2)
1242
		$this->Error('Not a JPEG file: '.$file);
1243
	if(!isset($a['channels']) || $a['channels']==3)
1244
		$colspace = 'DeviceRGB';
1245
	elseif($a['channels']==4)
1246
		$colspace = 'DeviceCMYK';
1247
	else
1248
		$colspace = 'DeviceGray';
1249
	$bpc = isset($a['bits']) ? $a['bits'] : 8;
1250
	$data = file_get_contents($file);
1251
	return array('w'=>$a[0], 'h'=>$a[1], 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'DCTDecode', 'data'=>$data);
1252
}
1253
1254
protected function _parsepng($file)
1255
{
1256
	// Extract info from a PNG file
1257
	$f = fopen($file,'rb');
1258
	if(!$f)
1259
		$this->Error('Can\'t open image file: '.$file);
1260
	$info = $this->_parsepngstream($f,$file);
1261
	fclose($f);
1262
	return $info;
1263
}
1264
1265
protected function _parsepngstream($f, $file)
1266
{
1267
	// Check signature
1268
	if($this->_readstream($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10))
1269
		$this->Error('Not a PNG file: '.$file);
1270
1271
	// Read header chunk
1272
	$this->_readstream($f,4);
1273
	if($this->_readstream($f,4)!='IHDR')
1274
		$this->Error('Incorrect PNG file: '.$file);
1275
	$w = $this->_readint($f);
1276
	$h = $this->_readint($f);
1277
	$bpc = ord($this->_readstream($f,1));
1278
	if($bpc>8)
1279
		$this->Error('16-bit depth not supported: '.$file);
1280
	$ct = ord($this->_readstream($f,1));
1281
	if($ct==0 || $ct==4)
1282
		$colspace = 'DeviceGray';
1283
	elseif($ct==2 || $ct==6)
1284
		$colspace = 'DeviceRGB';
1285
	elseif($ct==3)
1286
		$colspace = 'Indexed';
1287
	else
1288
		$this->Error('Unknown color type: '.$file);
1289
	if(ord($this->_readstream($f,1))!=0)
1290
		$this->Error('Unknown compression method: '.$file);
1291
	if(ord($this->_readstream($f,1))!=0)
1292
		$this->Error('Unknown filter method: '.$file);
1293
	if(ord($this->_readstream($f,1))!=0)
1294
		$this->Error('Interlacing not supported: '.$file);
1295
	$this->_readstream($f,4);
1296
	$dp = '/Predictor 15 /Colors '.($colspace=='DeviceRGB' ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w;
1297
1298
	// Scan chunks looking for palette, transparency and image data
1299
	$pal = '';
1300
	$trns = '';
1301
	$data = '';
1302
	do
1303
	{
1304
		$n = $this->_readint($f);
1305
		$type = $this->_readstream($f,4);
1306
		if($type=='PLTE')
1307
		{
1308
			// Read palette
1309
			$pal = $this->_readstream($f,$n);
1310
			$this->_readstream($f,4);
1311
		}
1312
		elseif($type=='tRNS')
1313
		{
1314
			// Read transparency info
1315
			$t = $this->_readstream($f,$n);
1316
			if($ct==0)
1317
				$trns = array(ord(substr($t,1,1)));
1318
			elseif($ct==2)
1319
				$trns = array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1)));
1320
			else
1321
			{
1322
				$pos = strpos($t,chr(0));
1323
				if($pos!==false)
1324
					$trns = array($pos);
1325
			}
1326
			$this->_readstream($f,4);
1327
		}
1328
		elseif($type=='IDAT')
1329
		{
1330
			// Read image data block
1331
			$data .= $this->_readstream($f,$n);
1332
			$this->_readstream($f,4);
1333
		}
1334
		elseif($type=='IEND')
1335
			break;
1336
		else
1337
			$this->_readstream($f,$n+4);
1338
	}
1339
	while($n);
1340
1341
	if($colspace=='Indexed' && empty($pal))
1342
		$this->Error('Missing palette in '.$file);
1343
	$info = array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'dp'=>$dp, 'pal'=>$pal, 'trns'=>$trns);
1344
	if($ct>=4)
1345
	{
1346
		// Extract alpha channel
1347
		if(!function_exists('gzuncompress'))
1348
			$this->Error('Zlib not available, can\'t handle alpha channel: '.$file);
1349
		$data = gzuncompress($data);
1350
		$color = '';
1351
		$alpha = '';
1352
		if($ct==4)
1353
		{
1354
			// Gray image
1355
			$len = 2*$w;
1356
			for($i=0;$i<$h;$i++)
1357
			{
1358
				$pos = (1+$len)*$i;
1359
				$color .= $data[$pos];
1360
				$alpha .= $data[$pos];
1361
				$line = substr($data,$pos+1,$len);
1362
				$color .= preg_replace('/(.)./s','$1',$line);
1363
				$alpha .= preg_replace('/.(.)/s','$1',$line);
1364
			}
1365
		}
1366
		else
1367
		{
1368
			// RGB image
1369
			$len = 4*$w;
1370
			for($i=0;$i<$h;$i++)
1371
			{
1372
				$pos = (1+$len)*$i;
1373
				$color .= $data[$pos];
1374
				$alpha .= $data[$pos];
1375
				$line = substr($data,$pos+1,$len);
1376
				$color .= preg_replace('/(.{3})./s','$1',$line);
1377
				$alpha .= preg_replace('/.{3}(.)/s','$1',$line);
1378
			}
1379
		}
1380
		unset($data);
1381
		$data = gzcompress($color);
1382
		$info['smask'] = gzcompress($alpha);
1383
		$this->WithAlpha = true;
1384
		if($this->PDFVersion<'1.4')
1385
			$this->PDFVersion = '1.4';
1386
	}
1387
	$info['data'] = $data;
1388
	return $info;
1389
}
1390
1391
protected function _readstream($f, $n)
1392
{
1393
	// Read n bytes from stream
1394
	$res = '';
1395
	while($n>0 && !feof($f))
1396
	{
1397
		$s = fread($f,$n);
1398
		if($s===false)
1399
			$this->Error('Error while reading stream');
1400
		$n -= strlen($s);
1401
		$res .= $s;
1402
	}
1403
	if($n>0)
1404
		$this->Error('Unexpected end of stream');
1405
	return $res;
1406
}
1407
1408
protected function _readint($f)
1409
{
1410
	// Read a 4-byte integer from stream
1411
	$a = unpack('Ni',$this->_readstream($f,4));
1412
	return $a['i'];
1413
}
1414
1415
protected function _parsegif($file)
1416
{
1417
	// Extract info from a GIF file (via PNG conversion)
1418
	if(!function_exists('imagepng'))
1419
		$this->Error('GD extension is required for GIF support');
1420
	if(!function_exists('imagecreatefromgif'))
1421
		$this->Error('GD has no GIF read support');
1422
	$im = imagecreatefromgif($file);
1423
	if(!$im)
1424
		$this->Error('Missing or incorrect image file: '.$file);
1425
	imageinterlace($im,0);
1426
	ob_start();
1427
	imagepng($im);
1428
	$data = ob_get_clean();
1429
	imagedestroy($im);
1430
	$f = fopen('php://temp','rb+');
1431
	if(!$f)
1432
		$this->Error('Unable to create memory stream');
1433
	fwrite($f,$data);
1434
	rewind($f);
1435
	$info = $this->_parsepngstream($f,$file);
1436
	fclose($f);
1437
	return $info;
1438
}
1439
1440
protected function _out($s)
1441
{
1442
	// Add a line to the document
1443
	if($this->state==2)
1444
		$this->pages[$this->page] .= $s."\n";
1445
	elseif($this->state==1)
1446
		$this->_put($s);
1447
	elseif($this->state==0)
1448
		$this->Error('No page has been added yet');
1449
	elseif($this->state==3)
1450
		$this->Error('The document is closed');
1451
}
1452
1453
protected function _put($s)
1454
{
1455
	$this->buffer .= $s."\n";
1456
}
1457
1458
protected function _getoffset()
1459
{
1460
	return strlen($this->buffer);
1461
}
1462
1463
protected function _newobj($n=null)
1464
{
1465
	// Begin a new object
1466
	if($n===null)
1467
		$n = ++$this->n;
1468
	$this->offsets[$n] = $this->_getoffset();
1469
	$this->_put($n.' 0 obj');
1470
}
1471
1472
protected function _putstream($data)
1473
{
1474
	$this->_put('stream');
1475
	$this->_put($data);
1476
	$this->_put('endstream');
1477
}
1478
1479
protected function _putstreamobject($data)
1480
{
1481
	if($this->compress)
1482
	{
1483
		$entries = '/Filter /FlateDecode ';
1484
		$data = gzcompress($data);
1485
	}
1486
	else
1487
		$entries = '';
1488
	$entries .= '/Length '.strlen($data);
1489
	$this->_newobj();
1490
	$this->_put('<<'.$entries.'>>');
1491
	$this->_putstream($data);
1492
	$this->_put('endobj');
1493
}
1494
1495
protected function _putpage($n)
1496
{
1497
	$this->_newobj();
1498
	$this->_put('<</Type /Page');
1499
	$this->_put('/Parent 1 0 R');
1500
	if(isset($this->PageInfo[$n]['size']))
1501
		$this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->PageInfo[$n]['size'][0],$this->PageInfo[$n]['size'][1]));
1502
	if(isset($this->PageInfo[$n]['rotation']))
1503
		$this->_put('/Rotate '.$this->PageInfo[$n]['rotation']);
1504
	$this->_put('/Resources 2 0 R');
1505
	if(!empty($this->PageLinks[$n]))
1506
	{
1507
		$s = '/Annots [';
1508
		foreach($this->PageLinks[$n] as $pl)
1509
			$s .= $pl[5].' 0 R ';
1510
		$s .= ']';
1511
		$this->_put($s);
1512
	}
1513
	if($this->WithAlpha)
1514
		$this->_put('/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>');
1515
	$this->_put('/Contents '.($this->n+1).' 0 R>>');
1516
	$this->_put('endobj');
1517
	// Page content
1518
	if(!empty($this->AliasNbPages))
1519
		$this->pages[$n] = str_replace($this->AliasNbPages,$this->page,$this->pages[$n]);
1520
	$this->_putstreamobject($this->pages[$n]);
1521
	// Annotations
1522
	foreach($this->PageLinks[$n] as $pl)
1523
	{
1524
		$rect = sprintf('%.2F %.2F %.2F %.2F',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]);
1525
		$s = '<</Type /Annot /Subtype /Link /Rect ['.$rect.'] /Border [0 0 0] ';
1526
		if(is_string($pl[4]))
1527
			$s .= '/A <</S /URI /URI '.$this->_textstring($pl[4]).'>>>>';
1528
		else
1529
		{
1530
			$l = $this->links[$pl[4]];
1531
			if(isset($this->PageInfo[$l[0]]['size']))
1532
				$h = $this->PageInfo[$l[0]]['size'][1];
1533
			else
1534
				$h = ($this->DefOrientation=='P') ? $this->DefPageSize[1]*$this->k : $this->DefPageSize[0]*$this->k;
1535
			$s .= sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]>>',$this->PageInfo[$l[0]]['n'],$h-$l[1]*$this->k);
1536
		}
1537
		$this->_newobj();
1538
		$this->_put($s);
1539
		$this->_put('endobj');
1540
	}
1541
}
1542
1543
protected function _putpages()
1544
{
1545
	$nb = $this->page;
1546
	$n = $this->n;
1547
	for($i=1;$i<=$nb;$i++)
1548
	{
1549
		$this->PageInfo[$i]['n'] = ++$n;
1550
		$n++;
1551
		foreach($this->PageLinks[$i] as &$pl)
1552
			$pl[5] = ++$n;
1553
		unset($pl);
1554
	}
1555
	for($i=1;$i<=$nb;$i++)
1556
		$this->_putpage($i);
1557
	// Pages root
1558
	$this->_newobj(1);
1559
	$this->_put('<</Type /Pages');
1560
	$kids = '/Kids [';
1561
	for($i=1;$i<=$nb;$i++)
1562
		$kids .= $this->PageInfo[$i]['n'].' 0 R ';
1563
	$kids .= ']';
1564
	$this->_put($kids);
1565
	$this->_put('/Count '.$nb);
1566
	if($this->DefOrientation=='P')
1567
	{
1568
		$w = $this->DefPageSize[0];
1569
		$h = $this->DefPageSize[1];
1570
	}
1571
	else
1572
	{
1573
		$w = $this->DefPageSize[1];
1574
		$h = $this->DefPageSize[0];
1575
	}
1576
	$this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]',$w*$this->k,$h*$this->k));
1577
	$this->_put('>>');
1578
	$this->_put('endobj');
1579
}
1580
1581
protected function _putfonts()
1582
{
1583
	foreach($this->FontFiles as $file=>$info)
1584
	{
1585
		// Font file embedding
1586
		$this->_newobj();
1587
		$this->FontFiles[$file]['n'] = $this->n;
1588
		$font = file_get_contents($this->fontpath.$file,true);
1589
		if(!$font)
1590
			$this->Error('Font file not found: '.$file);
1591
		$compressed = (substr($file,-2)=='.z');
1592
		if(!$compressed && isset($info['length2']))
1593
			$font = substr($font,6,$info['length1']).substr($font,6+$info['length1']+6,$info['length2']);
1594
		$this->_put('<</Length '.strlen($font));
1595
		if($compressed)
1596
			$this->_put('/Filter /FlateDecode');
1597
		$this->_put('/Length1 '.$info['length1']);
1598
		if(isset($info['length2']))
1599
			$this->_put('/Length2 '.$info['length2'].' /Length3 0');
1600
		$this->_put('>>');
1601
		$this->_putstream($font);
1602
		$this->_put('endobj');
1603
	}
1604
	foreach($this->fonts as $k=>$font)
1605
	{
1606
		// Encoding
1607
		if(isset($font['diff']))
1608
		{
1609
			if(!isset($this->encodings[$font['enc']]))
1610
			{
1611
				$this->_newobj();
1612
				$this->_put('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$font['diff'].']>>');
1613
				$this->_put('endobj');
1614
				$this->encodings[$font['enc']] = $this->n;
1615
			}
1616
		}
1617
		// ToUnicode CMap
1618
		if(isset($font['uv']))
1619
		{
1620
			if(isset($font['enc']))
1621
				$cmapkey = $font['enc'];
1622
			else
1623
				$cmapkey = $font['name'];
1624
			if(!isset($this->cmaps[$cmapkey]))
1625
			{
1626
				$cmap = $this->_tounicodecmap($font['uv']);
1627
				$this->_putstreamobject($cmap);
1628
				$this->cmaps[$cmapkey] = $this->n;
1629
			}
1630
		}
1631
		// Font object
1632
		$this->fonts[$k]['n'] = $this->n+1;
1633
		$type = $font['type'];
1634
		$name = $font['name'];
1635
		if($font['subsetted'])
1636
			$name = 'AAAAAA+'.$name;
1637
		if($type=='Core')
1638
		{
1639
			// Core font
1640
			$this->_newobj();
1641
			$this->_put('<</Type /Font');
1642
			$this->_put('/BaseFont /'.$name);
1643
			$this->_put('/Subtype /Type1');
1644
			if($name!='Symbol' && $name!='ZapfDingbats')
1645
				$this->_put('/Encoding /WinAnsiEncoding');
1646
			if(isset($font['uv']))
1647
				$this->_put('/ToUnicode '.$this->cmaps[$cmapkey].' 0 R');
1648
			$this->_put('>>');
1649
			$this->_put('endobj');
1650
		}
1651
		elseif($type=='Type1' || $type=='TrueType')
1652
		{
1653
			// Additional Type1 or TrueType/OpenType font
1654
			$this->_newobj();
1655
			$this->_put('<</Type /Font');
1656
			$this->_put('/BaseFont /'.$name);
1657
			$this->_put('/Subtype /'.$type);
1658
			$this->_put('/FirstChar 32 /LastChar 255');
1659
			$this->_put('/Widths '.($this->n+1).' 0 R');
1660
			$this->_put('/FontDescriptor '.($this->n+2).' 0 R');
1661
			if(isset($font['diff']))
1662
				$this->_put('/Encoding '.$this->encodings[$font['enc']].' 0 R');
1663
			else
1664
				$this->_put('/Encoding /WinAnsiEncoding');
1665
			if(isset($font['uv']))
1666
				$this->_put('/ToUnicode '.$this->cmaps[$cmapkey].' 0 R');
1667
			$this->_put('>>');
1668
			$this->_put('endobj');
1669
			// Widths
1670
			$this->_newobj();
1671
			$cw = &$font['cw'];
1672
			$s = '[';
1673
			for($i=32;$i<=255;$i++)
1674
				$s .= $cw[chr($i)].' ';
1675
			$this->_put($s.']');
1676
			$this->_put('endobj');
1677
			// Descriptor
1678
			$this->_newobj();
1679
			$s = '<</Type /FontDescriptor /FontName /'.$name;
1680
			foreach($font['desc'] as $k=>$v)
1681
				$s .= ' /'.$k.' '.$v;
1682
			if(!empty($font['file']))
1683
				$s .= ' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
1684
			$this->_put($s.'>>');
1685
			$this->_put('endobj');
1686
		}
1687
		else
1688
		{
1689
			// Allow for additional types
1690
			$mtd = '_put'.strtolower($type);
1691
			if(!method_exists($this,$mtd))
1692
				$this->Error('Unsupported font type: '.$type);
1693
			$this->$mtd($font);
1694
		}
1695
	}
1696
}
1697
1698
protected function _tounicodecmap($uv)
1699
{
1700
	$ranges = '';
1701
	$nbr = 0;
1702
	$chars = '';
1703
	$nbc = 0;
1704
	foreach($uv as $c=>$v)
1705
	{
1706
		if(is_array($v))
1707
		{
1708
			$ranges .= sprintf("<%02X> <%02X> <%04X>\n",$c,$c+$v[1]-1,$v[0]);
1709
			$nbr++;
1710
		}
1711
		else
1712
		{
1713
			$chars .= sprintf("<%02X> <%04X>\n",$c,$v);
1714
			$nbc++;
1715
		}
1716
	}
1717
	$s = "/CIDInit /ProcSet findresource begin\n";
1718
	$s .= "12 dict begin\n";
1719
	$s .= "begincmap\n";
1720
	$s .= "/CIDSystemInfo\n";
1721
	$s .= "<</Registry (Adobe)\n";
1722
	$s .= "/Ordering (UCS)\n";
1723
	$s .= "/Supplement 0\n";
1724
	$s .= ">> def\n";
1725
	$s .= "/CMapName /Adobe-Identity-UCS def\n";
1726
	$s .= "/CMapType 2 def\n";
1727
	$s .= "1 begincodespacerange\n";
1728
	$s .= "<00> <FF>\n";
1729
	$s .= "endcodespacerange\n";
1730
	if($nbr>0)
1731
	{
1732
		$s .= "$nbr beginbfrange\n";
1733
		$s .= $ranges;
1734
		$s .= "endbfrange\n";
1735
	}
1736
	if($nbc>0)
1737
	{
1738
		$s .= "$nbc beginbfchar\n";
1739
		$s .= $chars;
1740
		$s .= "endbfchar\n";
1741
	}
1742
	$s .= "endcmap\n";
1743
	$s .= "CMapName currentdict /CMap defineresource pop\n";
1744
	$s .= "end\n";
1745
	$s .= "end";
1746
	return $s;
1747
}
1748
1749
protected function _putimages()
1750
{
1751
	foreach(array_keys($this->images) as $file)
1752
	{
1753
		$this->_putimage($this->images[$file]);
1754
		unset($this->images[$file]['data']);
1755
		unset($this->images[$file]['smask']);
1756
	}
1757
}
1758
1759
protected function _putimage(&$info)
1760
{
1761
	$this->_newobj();
1762
	$info['n'] = $this->n;
1763
	$this->_put('<</Type /XObject');
1764
	$this->_put('/Subtype /Image');
1765
	$this->_put('/Width '.$info['w']);
1766
	$this->_put('/Height '.$info['h']);
1767
	if($info['cs']=='Indexed')
1768
		$this->_put('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]');
1769
	else
1770
	{
1771
		$this->_put('/ColorSpace /'.$info['cs']);
1772
		if($info['cs']=='DeviceCMYK')
1773
			$this->_put('/Decode [1 0 1 0 1 0 1 0]');
1774
	}
1775
	$this->_put('/BitsPerComponent '.$info['bpc']);
1776
	if(isset($info['f']))
1777
		$this->_put('/Filter /'.$info['f']);
1778
	if(isset($info['dp']))
1779
		$this->_put('/DecodeParms <<'.$info['dp'].'>>');
1780
	if(isset($info['trns']) && is_array($info['trns']))
1781
	{
1782
		$trns = '';
1783
		for($i=0;$i<count($info['trns']);$i++)
1784
			$trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
1785
		$this->_put('/Mask ['.$trns.']');
1786
	}
1787
	if(isset($info['smask']))
1788
		$this->_put('/SMask '.($this->n+1).' 0 R');
1789
	$this->_put('/Length '.strlen($info['data']).'>>');
1790
	$this->_putstream($info['data']);
1791
	$this->_put('endobj');
1792
	// Soft mask
1793
	if(isset($info['smask']))
1794
	{
1795
		$dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns '.$info['w'];
1796
		$smask = array('w'=>$info['w'], 'h'=>$info['h'], 'cs'=>'DeviceGray', 'bpc'=>8, 'f'=>$info['f'], 'dp'=>$dp, 'data'=>$info['smask']);
1797
		$this->_putimage($smask);
1798
	}
1799
	// Palette
1800
	if($info['cs']=='Indexed')
1801
		$this->_putstreamobject($info['pal']);
1802
}
1803
1804
protected function _putxobjectdict()
1805
{
1806
	foreach($this->images as $image)
1807
		$this->_put('/I'.$image['i'].' '.$image['n'].' 0 R');
1808
}
1809
1810
protected function _putresourcedict()
1811
{
1812
	$this->_put('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
1813
	$this->_put('/Font <<');
1814
	foreach($this->fonts as $font)
1815
		$this->_put('/F'.$font['i'].' '.$font['n'].' 0 R');
1816
	$this->_put('>>');
1817
	$this->_put('/XObject <<');
1818
	$this->_putxobjectdict();
1819
	$this->_put('>>');
1820
}
1821
1822
protected function _putresources()
1823
{
1824
	$this->_putfonts();
1825
	$this->_putimages();
1826
	// Resource dictionary
1827
	$this->_newobj(2);
1828
	$this->_put('<<');
1829
	$this->_putresourcedict();
1830
	$this->_put('>>');
1831
	$this->_put('endobj');
1832
}
1833
1834
protected function _putinfo()
1835
{
1836
	$this->metadata['Producer'] = 'FPDF '.FPDF_VERSION;
1837
	$this->metadata['CreationDate'] = 'D:'.@date('YmdHis');
1838
	foreach($this->metadata as $key=>$value)
1839
		$this->_put('/'.$key.' '.$this->_textstring($value));
1840
}
1841
1842
protected function _putcatalog()
1843
{
1844
	$n = $this->PageInfo[1]['n'];
1845
	$this->_put('/Type /Catalog');
1846
	$this->_put('/Pages 1 0 R');
1847
	if($this->ZoomMode=='fullpage')
1848
		$this->_put('/OpenAction ['.$n.' 0 R /Fit]');
1849
	elseif($this->ZoomMode=='fullwidth')
1850
		$this->_put('/OpenAction ['.$n.' 0 R /FitH null]');
1851
	elseif($this->ZoomMode=='real')
1852
		$this->_put('/OpenAction ['.$n.' 0 R /XYZ null null 1]');
1853
	elseif(!is_string($this->ZoomMode))
1854
		$this->_put('/OpenAction ['.$n.' 0 R /XYZ null null '.sprintf('%.2F',$this->ZoomMode/100).']');
1855
	if($this->LayoutMode=='single')
1856
		$this->_put('/PageLayout /SinglePage');
1857
	elseif($this->LayoutMode=='continuous')
1858
		$this->_put('/PageLayout /OneColumn');
1859
	elseif($this->LayoutMode=='two')
1860
		$this->_put('/PageLayout /TwoColumnLeft');
1861
}
1862
1863
protected function _putheader()
1864
{
1865
	$this->_put('%PDF-'.$this->PDFVersion);
1866
}
1867
1868
protected function _puttrailer()
1869
{
1870
	$this->_put('/Size '.($this->n+1));
1871
	$this->_put('/Root '.$this->n.' 0 R');
1872
	$this->_put('/Info '.($this->n-1).' 0 R');
1873
}
1874
1875
protected function _enddoc()
1876
{
1877
	$this->_putheader();
1878
	$this->_putpages();
1879
	$this->_putresources();
1880
	// Info
1881
	$this->_newobj();
1882
	$this->_put('<<');
1883
	$this->_putinfo();
1884
	$this->_put('>>');
1885
	$this->_put('endobj');
1886
	// Catalog
1887
	$this->_newobj();
1888
	$this->_put('<<');
1889
	$this->_putcatalog();
1890
	$this->_put('>>');
1891
	$this->_put('endobj');
1892
	// Cross-ref
1893
	$offset = $this->_getoffset();
1894
	$this->_put('xref');
1895
	$this->_put('0 '.($this->n+1));
1896
	$this->_put('0000000000 65535 f ');
1897
	for($i=1;$i<=$this->n;$i++)
1898
		$this->_put(sprintf('%010d 00000 n ',$this->offsets[$i]));
1899
	// Trailer
1900
	$this->_put('trailer');
1901
	$this->_put('<<');
1902
	$this->_puttrailer();
1903
	$this->_put('>>');
1904
	$this->_put('startxref');
1905
	$this->_put($offset);
1906
	$this->_put('%%EOF');
1907
	$this->state = 3;
1908
}
1909
}
1910
?>
1911