2 /*******************************************************************************
6 * Author: Olivier PLATHEY *
9 * You may use and modify this software as you wish. *
10 *******************************************************************************/
12 if (!class_exists('FPDF')) {
14 define('FPDF_FONTPATH', dirname(__FILE__) . "/fpdf/");
15 define('FPDF_VERSION', '1.52');
20 var $page; //current page number
21 var $n; //current object number
22 var $offsets; //array of object offsets
23 var $buffer; //buffer holding in-memory PDF
24 var $pages; //array containing pages
25 var $state; //current document state
26 var $compress; //compression flag
27 var $DefOrientation; //default orientation
28 var $CurOrientation; //current orientation
29 var $OrientationChanges; //array indicating orientation changes
30 var $k; //scale factor (number of points in user unit)
31 var $fwPt, $fhPt; //dimensions of page format in points
32 var $fw, $fh; //dimensions of page format in user unit
33 var $wPt, $hPt; //current dimensions of page in points
34 var $w, $h; //current dimensions of page in user unit
35 var $lMargin; //left margin
36 var $tMargin; //top margin
37 var $rMargin; //right margin
38 var $bMargin; //page break margin
39 var $cMargin; //cell margin
40 var $x, $y; //current position in user unit for cell positioning
41 var $lasth; //height of last cell printed
42 var $LineWidth; //line width in user unit
43 var $CoreFonts; //array of standard font names
44 var $fonts; //array of used fonts
45 var $FontFiles; //array of font files
46 var $diffs; //array of encoding differences
47 var $images; //array of used images
48 var $PageLinks; //array of links in pages
49 var $links; //array of internal links
50 var $FontFamily; //current font family
51 var $FontStyle; //current font style
52 var $underline; //underlining flag
53 var $CurrentFont; //current font info
54 var $FontSizePt; //current font size in points
55 var $FontSize; //current font size in user unit
56 var $DrawColor; //commands for drawing color
57 var $FillColor; //commands for filling color
58 var $TextColor; //commands for text color
59 var $ColorFlag; //indicates whether fill and text colors are different
60 var $ws; //word spacing
61 var $AutoPageBreak; //automatic page breaking
62 var $PageBreakTrigger; //threshold used to trigger page breaks
63 var $InFooter; //flag set when processing footer
64 var $ZoomMode; //zoom display mode
65 var $LayoutMode; //layout display mode
67 var $subject; //subject
69 var $keywords; //keywords
70 var $creator; //creator
71 var $producer; //Producer
72 var $AliasNbPages; //alias for total number of pages
74 /*******************************************************************************
78 *******************************************************************************/
79 function FPDF($orientation = 'P', $unit = 'mm', $format = 'A4')
83 //Initialization of properties
87 $this->pages = array();
88 $this->OrientationChanges = array();
90 $this->fonts = array();
91 $this->FontFiles = array();
92 $this->diffs = array();
93 $this->images = array();
94 $this->links = array();
95 $this->InFooter = false;
97 $this->FontFamily = '';
98 $this->FontStyle = '';
99 $this->FontSizePt = 12;
100 $this->underline = false;
101 $this->DrawColor = '0 G';
102 $this->FillColor = '0 g';
103 $this->TextColor = '0 g';
104 $this->ColorFlag = false;
107 $this->CoreFonts = array('japanees' => 'Japanees',
108 'courier' => 'Courier', 'courierB' => 'Courier-Bold', 'courierI' => 'Courier-Oblique', 'courierBI' => 'Courier-BoldOblique',
109 'helvetica' => 'Helvetica', 'helveticaB' => 'Helvetica-Bold', 'helveticaI' => 'Helvetica-Oblique', 'helveticaBI' => 'Helvetica-BoldOblique',
110 'times' => 'Times-Roman', 'timesB' => 'Times-Bold', 'timesI' => 'Times-Italic', 'timesBI' => 'Times-BoldItalic',
111 'symbol' => 'Symbol', 'zapfdingbats' => 'ZapfDingbats');
115 elseif ($unit == 'mm')
116 $this->k = 72 / 25.4; elseif ($unit == 'cm')
117 $this->k = 72 / 2.54; elseif ($unit == 'in')
119 $this->Error('Incorrect unit: ' . $unit);
121 if (is_string($format)) {
122 $format = strtolower($format);
124 $format = array(841.89, 1190.55);
125 elseif ($format == 'a4')
126 $format = array(595.28, 841.89); elseif ($format == 'a5')
127 $format = array(420.94, 595.28); elseif ($format == 'letter')
128 $format = array(612, 792); elseif ($format == 'legal')
129 $format = array(612, 1008); else
130 $this->Error('Unknown page format: ' . $format);
131 $this->fwPt = $format[0];
132 $this->fhPt = $format[1];
134 $this->fwPt = $format[0] * $this->k;
135 $this->fhPt = $format[1] * $this->k;
137 $this->fw = $this->fwPt / $this->k;
138 $this->fh = $this->fhPt / $this->k;
140 $orientation = strtolower($orientation);
141 if ($orientation == 'p' or $orientation == 'portrait') {
142 $this->DefOrientation = 'P';
143 $this->wPt = $this->fwPt;
144 $this->hPt = $this->fhPt;
145 } elseif ($orientation == 'l' or $orientation == 'landscape') {
146 $this->DefOrientation = 'L';
147 $this->wPt = $this->fhPt;
148 $this->hPt = $this->fwPt;
150 $this->Error('Incorrect orientation: ' . $orientation);
151 $this->CurOrientation = $this->DefOrientation;
152 $this->w = $this->wPt / $this->k;
153 $this->h = $this->hPt / $this->k;
154 //Page margins (1 cm)
155 $margin = 28.35 / $this->k;
156 $this->SetMargins($margin, $margin);
157 //Interior cell margin (1 mm)
158 $this->cMargin = $margin / 10;
159 //Line width (0.2 mm)
160 $this->LineWidth = .567 / $this->k;
161 //Automatic page break
162 $this->SetAutoPageBreak(true, 2 * $margin);
163 //Full width display mode
164 $this->SetDisplayMode('fullwidth');
166 $this->SetCompression(true);
169 function SetMargins($left, $top, $right = -1)
171 //Set left, top and right margins
172 $this->lMargin = $left;
173 $this->tMargin = $top;
176 $this->rMargin = $right;
179 function SetLeftMargin($margin)
182 $this->lMargin = $margin;
183 if ($this->page > 0 and $this->x < $margin)
187 function SetTopMargin($margin)
190 $this->tMargin = $margin;
193 function SetRightMargin($margin)
196 $this->rMargin = $margin;
199 function SetAutoPageBreak($auto, $margin = 0)
201 //Set auto page break mode and triggering margin
202 $this->AutoPageBreak = $auto;
203 $this->bMargin = $margin;
204 $this->PageBreakTrigger = $this->h - $margin;
207 function SetDisplayMode($zoom, $layout = 'continuous')
209 //Set display mode in viewer
210 if ($zoom == 'fullpage' or $zoom == 'fullwidth' or $zoom == 'real' or $zoom == 'default' or !is_string($zoom))
211 $this->ZoomMode = $zoom;
213 $this->Error('Incorrect zoom display mode: ' . $zoom);
214 if ($layout == 'single' or $layout == 'continuous' or $layout == 'two' or $layout == 'default')
215 $this->LayoutMode = $layout;
217 $this->Error('Incorrect layout display mode: ' . $layout);
220 function SetCompression($compress)
222 //Set page compression
223 if (function_exists('gzcompress'))
224 $this->compress = $compress;
226 $this->compress = false;
229 function SetTitle($title)
232 $this->title = $title;
235 function SetSubject($subject)
237 //Subject of document
238 $this->subject = $subject;
241 function SetAuthor($author)
244 $this->author = $author;
247 function SetKeywords($keywords)
249 //Keywords of document
250 $this->keywords = $keywords;
253 function SetCreator($creator)
255 //Creator of document
256 $this->creator = $creator;
259 function SetProducer($producer)
261 //Producer of document
262 $this->producer = $producer;
265 function AliasNbPages($alias = '{nb}')
267 //Define an alias for total number of pages
268 $this->AliasNbPages = $alias;
274 die('<B>FPDF error: </B>' . $msg);
280 if ($this->state == 0)
287 if ($this->state == 3)
289 if ($this->page == 0)
292 $this->InFooter = true;
294 $this->InFooter = false;
301 function AddPage($orientation = '')
304 if ($this->state == 0)
306 $family = $this->FontFamily;
307 $style = $this->FontStyle . ($this->underline ? 'U' : '');
308 $size = $this->FontSizePt;
309 $lw = $this->LineWidth;
310 $dc = $this->DrawColor;
311 $fc = $this->FillColor;
312 $tc = $this->TextColor;
313 $cf = $this->ColorFlag;
314 if ($this->page > 0) {
316 $this->InFooter = true;
318 $this->InFooter = false;
323 $this->_beginpage($orientation);
324 //Set line cap style to square
327 $this->LineWidth = $lw;
328 $this->_out(sprintf('%.2f w', $lw * $this->k));
331 $this->SetFont($family, $style, $size);
333 $this->DrawColor = $dc;
336 $this->FillColor = $fc;
339 $this->TextColor = $tc;
340 $this->ColorFlag = $cf;
344 if ($this->LineWidth != $lw) {
345 $this->LineWidth = $lw;
346 $this->_out(sprintf('%.2f w', $lw * $this->k));
350 $this->SetFont($family, $style, $size);
352 if ($this->DrawColor != $dc) {
353 $this->DrawColor = $dc;
356 if ($this->FillColor != $fc) {
357 $this->FillColor = $fc;
360 $this->TextColor = $tc;
361 $this->ColorFlag = $cf;
366 //To be implemented in your own inherited class
371 //To be implemented in your own inherited class
376 //Get current page number
380 function SetDrawColor($r, $g = -1, $b = -1)
382 //Set color for all stroking operations
383 if (($r == 0 and $g == 0 and $b == 0) or $g == -1)
384 $this->DrawColor = sprintf('%.3f G', $r / 255);
386 $this->DrawColor = sprintf('%.3f %.3f %.3f RG', $r / 255, $g / 255, $b / 255);
388 $this->_out($this->DrawColor);
391 function SetFillColor($r, $g = -1, $b = -1)
393 //Set color for all filling operations
394 if (($r == 0 and $g == 0 and $b == 0) or $g == -1)
395 $this->FillColor = sprintf('%.3f g', $r / 255);
397 $this->FillColor = sprintf('%.3f %.3f %.3f rg', $r / 255, $g / 255, $b / 255);
398 $this->ColorFlag = ($this->FillColor != $this->TextColor);
400 $this->_out($this->FillColor);
403 function SetTextColor($r, $g = -1, $b = -1)
406 if (($r == 0 and $g == 0 and $b == 0) or $g == -1)
407 $this->TextColor = sprintf('%.3f g', $r / 255);
409 $this->TextColor = sprintf('%.3f %.3f %.3f rg', $r / 255, $g / 255, $b / 255);
410 $this->ColorFlag = ($this->FillColor != $this->TextColor);
413 function GetStringWidth($s)
415 //Get width of a string in the current font
417 $cw =& $this->CurrentFont['cw'];
420 for ($i = 0; $i < $l; $i++)
422 return $w * $this->FontSize / 1000;
425 function SetLineWidth($width)
428 $this->LineWidth = $width;
430 $this->_out(sprintf('%.2f w', $width * $this->k));
433 function Line($x1, $y1, $x2, $y2)
436 $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));
439 function Rect($x, $y, $w, $h, $style = '')
444 elseif ($style == 'FD' or $style == 'DF')
447 $this->_out(sprintf('%.2f %.2f %.2f %.2f re %s', $x * $this->k, ($this->h - $y) * $this->k, $w * $this->k, -$h * $this->k, $op));
450 function AddFont($family, $style = '', $file = '')
452 //Add a TrueType or Type1 font
453 $family = strtolower($family);
454 if ($family == 'arial')
455 $family = 'helvetica';
456 $style = strtoupper($style);
459 if (isset($this->fonts[$family . $style]))
460 $this->Error('Font already added: ' . $family . ' ' . $style);
462 $file = str_replace(' ', '', $family) . strtolower($style) . '.php';
463 if (defined('FPDF_FONTPATH'))
464 $file = FPDF_FONTPATH . $file;
467 $this->Error('Could not include font definition file');
468 $i = count($this->fonts) + 1;
469 $this->fonts[$family . $style] = array('i' => $i, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'enc' => $enc, 'file' => $file);
471 //Search existing encodings
473 $nb = count($this->diffs);
474 for ($i = 1; $i <= $nb; $i++)
475 if ($this->diffs[$i] == $diff) {
481 $this->diffs[$d] = $diff;
483 $this->fonts[$family . $style]['diff'] = $d;
486 if ($type == 'TrueType')
487 $this->FontFiles[$file] = array('length1' => $originalsize);
489 $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2);
493 function SetFont($family, $style = '', $size = 0)
495 //Select a font; size given in points
496 global $fpdf_charwidths;
498 $family = strtolower($family);
500 $family = $this->FontFamily;
501 if ($family == 'arial')
502 $family = 'helvetica';
503 elseif ($family == 'symbol' or $family == 'zapfdingbats')
505 $style = strtoupper($style);
506 if (is_int(strpos($style, 'U'))) {
507 $this->underline = true;
508 $style = str_replace('U', '', $style);
510 $this->underline = false;
514 $size = $this->FontSizePt;
515 //Test if font is already selected
516 if ($this->FontFamily == $family and $this->FontStyle == $style and $this->FontSizePt == $size)
518 //Test if used for the first time
519 $fontkey = $family . $style;
520 if (!isset($this->fonts[$fontkey])) {
521 //Check if one of the standard fonts
522 if (isset($this->CoreFonts[$fontkey])) {
523 if (!isset($fpdf_charwidths[$fontkey])) {
526 if ($family == 'times' or $family == 'helvetica')
527 $file .= strtolower($style);
529 if (defined('FPDF_FONTPATH'))
530 $file = FPDF_FONTPATH . $file;
532 if (!isset($fpdf_charwidths[$fontkey]))
533 $this->Error('Could not include font metric file');
535 $i = count($this->fonts) + 1;
536 $this->fonts[$fontkey] = array('i' => $i, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'up' => -100, 'ut' => 50, 'cw' => $fpdf_charwidths[$fontkey]);
538 $this->Error('Undefined font: ' . $family . ' ' . $style);
541 $this->FontFamily = $family;
542 $this->FontStyle = $style;
543 $this->FontSizePt = $size;
544 $this->FontSize = $size / $this->k;
545 $this->CurrentFont =& $this->fonts[$fontkey];
547 $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
550 function SetFontSize($size)
552 //Set font size in points
553 if ($this->FontSizePt == $size)
555 $this->FontSizePt = $size;
556 $this->FontSize = $size / $this->k;
558 $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
563 //Create a new internal link
564 $n = count($this->links) + 1;
565 $this->links[$n] = array(0, 0);
569 function SetLink($link, $y = 0, $page = -1)
571 //Set destination of internal link
576 $this->links[$link] = array($page, $y);
579 function Link($x, $y, $w, $h, $link)
581 //Put a link on the page
582 $this->PageLinks[$this->page][] = array($x * $this->k, $this->hPt - $y * $this->k, $w * $this->k, $h * $this->k, $link);
585 function Text($x, $y, $txt)
588 $s = sprintf('BT %.2f %.2f Td (%s) Tj ET', $x * $this->k, ($this->h - $y) * $this->k, $this->_escape($txt));
589 if ($this->underline and $txt != '')
590 $s .= ' ' . $this->_dounderline($x, $y, $txt);
591 if ($this->ColorFlag)
592 $s = 'q ' . $this->TextColor . ' ' . $s . ' Q';
596 function AcceptPageBreak()
598 //Accept automatic page break or not
599 return $this->AutoPageBreak;
602 function Cell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '')
606 if ($this->y + $h > $this->PageBreakTrigger and !$this->InFooter and $this->AcceptPageBreak()) {
607 //Automatic page break
614 $this->AddPage($this->CurOrientation);
618 $this->_out(sprintf('%.3f Tw', $ws * $k));
622 $w = $this->w - $this->rMargin - $this->x;
624 if ($fill == 1 or $border == 1) {
626 $op = ($border == 1) ? 'B' : 'f';
629 $s = sprintf('%.2f %.2f %.2f %.2f re %s ', $this->x * $k, ($this->h - $this->y) * $k, $w * $k, -$h * $k, $op);
631 if (is_string($border)) {
634 if (is_int(strpos($border, 'L')))
635 $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $x * $k, ($this->h - $y) * $k, $x * $k, ($this->h - ($y + $h)) * $k);
636 if (is_int(strpos($border, 'T')))
637 $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $x * $k, ($this->h - $y) * $k, ($x + $w) * $k, ($this->h - $y) * $k);
638 if (is_int(strpos($border, 'R')))
639 $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', ($x + $w) * $k, ($this->h - $y) * $k, ($x + $w) * $k, ($this->h - ($y + $h)) * $k);
640 if (is_int(strpos($border, 'B')))
641 $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $x * $k, ($this->h - ($y + $h)) * $k, ($x + $w) * $k, ($this->h - ($y + $h)) * $k);
645 $dx = $w - $this->cMargin - $this->GetStringWidth($txt);
646 elseif ($align == 'C')
647 $dx = ($w - $this->GetStringWidth($txt)) / 2; else
648 $dx = $this->cMargin;
649 if ($this->ColorFlag)
650 $s .= 'q ' . $this->TextColor . ' ';
651 $txt2 = str_replace(')', '\\)', str_replace('(', '\\(', str_replace('\\', '\\\\', $txt)));
652 $s .= sprintf('BT %.2f %.2f Td (%s) Tj ET', ($this->x + $dx) * $k, ($this->h - ($this->y + .5 * $h + .3 * $this->FontSize)) * $k, $txt2);
653 if ($this->underline)
654 $s .= ' ' . $this->_dounderline($this->x + $dx, $this->y + .5 * $h + .3 * $this->FontSize, $txt);
655 if ($this->ColorFlag)
658 $this->Link($this->x + $dx, $this->y + .5 * $h - .5 * $this->FontSize, $this->GetStringWidth($txt), $this->FontSize, $link);
667 $this->x = $this->lMargin;
672 function MultiCell($w, $h, $txt, $border = 0, $align = 'J', $fill = 0)
674 //Output text with automatic or explicit line breaks
675 $cw =& $this->CurrentFont['cw'];
677 $w = $this->w - $this->rMargin - $this->x;
678 $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
679 $s = str_replace("\r", '', $txt);
681 if ($nb > 0 and $s[$nb - 1] == "\n")
691 if (is_int(strpos($border, 'L')))
693 if (is_int(strpos($border, 'R')))
695 $b = is_int(strpos($border, 'T')) ? $b2 . 'T' : $b2;
708 //Explicit line break
713 $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill);
720 if ($border and $nl == 2)
731 //Automatic line break
739 $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill);
742 $this->ws = ($ns > 1) ? ($wmax - $ls) / 1000 * $this->FontSize / ($ns - 1) : 0;
743 $this->_out(sprintf('%.3f Tw', $this->ws * $this->k));
745 $this->Cell($w, $h, substr($s, $j, $sep - $j), $b, 2, $align, $fill);
753 if ($border and $nl == 2)
763 if ($border and is_int(strpos($border, 'B')))
765 $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill);
766 $this->x = $this->lMargin;
769 function Write($h, $txt, $link = '')
771 //Output text in flowing mode
772 $cw =& $this->CurrentFont['cw'];
773 $w = $this->w - $this->rMargin - $this->x;
774 $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
775 $s = str_replace("\r", '', $txt);
786 //Explicit line break
787 $this->Cell($w, $h, substr($s, $j, $i - $j), 0, 2, '', 0, $link);
793 $this->x = $this->lMargin;
794 $w = $this->w - $this->rMargin - $this->x;
795 $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
804 //Automatic line break
806 if ($this->x > $this->lMargin) {
808 $this->x = $this->lMargin;
810 $w = $this->w - $this->rMargin - $this->x;
811 $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
818 $this->Cell($w, $h, substr($s, $j, $i - $j), 0, 2, '', 0, $link);
820 $this->Cell($w, $h, substr($s, $j, $sep - $j), 0, 2, '', 0, $link);
827 $this->x = $this->lMargin;
828 $w = $this->w - $this->rMargin - $this->x;
829 $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
837 $this->Cell($l / 1000 * $this->FontSize, $h, substr($s, $j), 0, 0, '', 0, $link);
840 function Image($file, $x, $y, $w = 0, $h = 0, $type = '', $link = '')
842 //Put an image on the page
843 if (!isset($this->images[$file])) {
844 //First use of image, get info
846 $pos = strrpos($file, '.');
848 $this->Error('Image file has no extension and no type was specified: ' . $file);
849 $type = substr($file, $pos + 1);
851 $type = strtolower($type);
852 $mqr = get_magic_quotes_runtime();
853 set_magic_quotes_runtime(0);
854 if ($type == 'jpg' or $type == 'jpeg')
855 $info = $this->_parsejpg($file);
856 elseif ($type == 'png')
857 $info = $this->_parsepng($file); elseif ($type == 'gif')
858 $info = $this->_parsegif($file); else {
859 //Allow for additional formats
860 $mtd = '_parse' . $type;
861 if (!method_exists($this, $mtd))
862 $this->Error('Unsupported image type: ' . $type);
863 $info = $this->$mtd($file);
865 set_magic_quotes_runtime($mqr);
866 $info['i'] = count($this->images) + 1;
867 $this->images[$file] = $info;
869 $info = $this->images[$file];
870 //Automatic width and height calculation if needed
871 if ($w == 0 and $h == 0) {
872 //Put image at 72 dpi
873 $w = $info['w'] / $this->k;
874 $h = $info['h'] / $this->k;
877 $w = $h * $info['w'] / $info['h'];
879 $h = $w * $info['h'] / $info['w'];
880 $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']));
882 $this->Link($x, $y, $w, $h, $link);
887 //Line feed; default value is last cell height
888 $this->x = $this->lMargin;
890 $this->y += $this->lasth;
907 $this->x = $this->w + $x;
918 //Set y position and reset x
919 $this->x = $this->lMargin;
923 $this->y = $this->h + $y;
926 function SetXY($x, $y)
928 //Set x and y positions
933 function Output($name = '', $dest = '')
935 //Output PDF to some destination
936 global $HTTP_SERVER_VARS;
938 //Finish document if necessary
939 if ($this->state < 3)
941 //Normalize parameters
943 $dest = $dest ? 'D' : 'F';
944 $dest = strtoupper($dest);
954 //Send to standard output
955 if (isset($HTTP_SERVER_VARS['SERVER_NAME'])) {
956 //We send to a browser
957 Header('Content-Type: application/pdf');
959 $this->Error('Some data has already been output to browser, can\'t send PDF file');
960 Header('Content-Length: ' . strlen($this->buffer));
961 Header('Content-disposition: inline; filename=' . $name);
967 if (isset($HTTP_SERVER_VARS['HTTP_USER_AGENT']) and strpos($HTTP_SERVER_VARS['HTTP_USER_AGENT'], 'MSIE'))
968 Header('Content-Type: application/force-download');
970 Header('Content-Type: application/octet-stream');
972 $this->Error('Some data has already been output to browser, can\'t send PDF file');
973 Header('Content-Length: ' . strlen($this->buffer));
974 Header('Content-disposition: attachment; filename=' . $name);
979 $f = fopen($name, 'wb');
981 $this->Error('Unable to create output file: ' . $name);
982 fwrite($f, $this->buffer, strlen($this->buffer));
987 return $this->buffer;
989 $this->Error('Incorrect output destination: ' . $dest);
994 /*******************************************************************************
996 * Protected methods *
998 *******************************************************************************/
1001 //Check for locale-related bug. we must have "." as comma seperator
1002 $attempts = array("C", "en", "en_us", "English");
1004 while (1.1 == 1 and $i++ < count($attempts)) {
1005 setlocale(LC_NUMERIC, $attempts[$i]);
1008 $this->Error('1.1 == 1: Wrong locale with comma as dot. German? Don\'t alter the locale before including class file');
1010 //Check for decimal separator
1011 if (sprintf('%.1f', 1.0) != '1.0')
1012 setlocale(LC_NUMERIC, 'C');
1015 function _begindoc()
1019 $this->_out('%PDF-1.3');
1022 function _putpages()
1025 if (!empty($this->AliasNbPages)) {
1026 //Replace number of pages
1027 for ($n = 1; $n <= $nb; $n++)
1028 $this->pages[$n] = str_replace($this->AliasNbPages, $nb, $this->pages[$n]);
1030 if ($this->DefOrientation == 'P') {
1037 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
1038 for ($n = 1; $n <= $nb; $n++) {
1041 $this->_out('<</Type /Page');
1042 $this->_out('/Parent 1 0 R');
1043 if (isset($this->OrientationChanges[$n]))
1044 $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]', $hPt, $wPt));
1045 $this->_out('/Resources 2 0 R');
1046 if (isset($this->PageLinks[$n])) {
1048 $annots = '/Annots [';
1049 foreach ($this->PageLinks[$n] as $pl) {
1050 $rect = sprintf('%.2f %.2f %.2f %.2f', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3]);
1051 $annots .= '<</Type /Annot /Subtype /Link /Rect [' . $rect . '] /Border [0 0 0] ';
1052 if (is_string($pl[4]))
1053 $annots .= '/A <</S /URI /URI ' . $this->_textstring($pl[4]) . '>>>>';
1055 $l = $this->links[$pl[4]];
1056 $h = isset($this->OrientationChanges[$l[0]]) ? $wPt : $hPt;
1057 $annots .= sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]>>', 1 + 2 * $l[0], $h - $l[1] * $this->k);
1060 $this->_out($annots . ']');
1062 $this->_out('/Contents ' . ($this->n + 1) . ' 0 R>>');
1063 $this->_out('endobj');
1065 $p = ($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n];
1067 $this->_out('<<' . $filter . '/Length ' . strlen($p) . '>>');
1068 $this->_putstream($p);
1069 $this->_out('endobj');
1072 $this->offsets[1] = strlen($this->buffer);
1073 $this->_out('1 0 obj');
1074 $this->_out('<</Type /Pages');
1076 for ($i = 0; $i < $nb; $i++)
1077 $kids .= (3 + 2 * $i) . ' 0 R ';
1078 $this->_out($kids . ']');
1079 $this->_out('/Count ' . $nb);
1080 $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]', $wPt, $hPt));
1082 $this->_out('endobj');
1085 function _putfonts()
1088 foreach ($this->diffs as $diff) {
1091 $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [' . $diff . ']>>');
1092 $this->_out('endobj');
1094 if (!check_php_version(5, 3)) {
1095 $mqr = get_magic_quotes_runtime();
1096 set_magic_quotes_runtime(0);
1098 foreach ($this->FontFiles as $file => $info) {
1099 //Font file embedding
1101 $this->FontFiles[$file]['n'] = $this->n;
1102 if (defined('FPDF_FONTPATH'))
1103 $file = FPDF_FONTPATH . $file;
1104 $size = filesize($file);
1106 $this->Error('Font file not found');
1107 $this->_out('<</Length ' . $size);
1108 if (substr($file, -2) == '.z')
1109 $this->_out('/Filter /FlateDecode');
1110 $this->_out('/Length1 ' . $info['length1']);
1111 if (isset($info['length2']))
1112 $this->_out('/Length2 ' . $info['length2'] . ' /Length3 0');
1114 $f = fopen($file, 'rb');
1115 $this->_putstream(fread($f, $size));
1117 $this->_out('endobj');
1119 if (!check_php_version(5, 3)) {
1120 set_magic_quotes_runtime($mqr);
1122 foreach ($this->fonts as $k => $font) {
1124 $this->fonts[$k]['n'] = $this->n + 1;
1125 $type = $font['type'];
1126 $name = $font['name'];
1127 if ($type == 'core') {
1130 $this->_out('<</Type /Font');
1131 $this->_out('/BaseFont /' . $name);
1132 $this->_out('/Subtype /Type1');
1133 if ($name != 'Symbol' and $name != 'ZapfDingbats')
1134 $this->_out('/Encoding /WinAnsiEncoding');
1136 $this->_out('endobj');
1137 } elseif ($type == 'Type1' or $type == 'TrueType') {
1138 //Additional Type1 or TrueType font
1140 $this->_out('<</Type /Font');
1141 $this->_out('/BaseFont /' . $name);
1142 $this->_out('/Subtype /' . $type);
1143 $this->_out('/FirstChar 32 /LastChar 255');
1144 $this->_out('/Widths ' . ($this->n + 1) . ' 0 R');
1145 $this->_out('/FontDescriptor ' . ($this->n + 2) . ' 0 R');
1147 if (isset($font['diff']))
1148 $this->_out('/Encoding ' . ($nf + $font['diff']) . ' 0 R');
1150 $this->_out('/Encoding /WinAnsiEncoding');
1153 $this->_out('endobj');
1158 for ($i = 32; $i <= 255; $i++)
1159 $s .= $cw[chr($i)] . ' ';
1160 $this->_out($s . ']');
1161 $this->_out('endobj');
1164 $s = '<</Type /FontDescriptor /FontName /' . $name;
1165 foreach ($font['desc'] as $k => $v)
1166 $s .= ' /' . $k . ' ' . $v;
1167 $file = $font['file'];
1169 $s .= ' /FontFile' . ($type == 'Type1' ? '' : '2') . ' ' . $this->FontFiles[$file]['n'] . ' 0 R';
1170 $this->_out($s . '>>');
1171 $this->_out('endobj');
1173 //Allow for additional types
1174 $mtd = '_put' . strtolower($type);
1175 if (!method_exists($this, $mtd))
1176 $this->Error('Unsupported font type: ' . $type);
1182 function _putimages()
1184 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
1185 reset($this->images);
1186 while (list($file, $info) = each($this->images)) {
1188 $this->images[$file]['n'] = $this->n;
1189 $this->_out('<</Type /XObject');
1190 $this->_out('/Subtype /Image');
1191 $this->_out('/Width ' . $info['w']);
1192 $this->_out('/Height ' . $info['h']);
1193 if ($info['cs'] == 'Indexed')
1194 $this->_out('/ColorSpace [/Indexed /DeviceRGB ' . (strlen($info['pal']) / 3 - 1) . ' ' . ($this->n + 1) . ' 0 R]');
1196 $this->_out('/ColorSpace /' . $info['cs']);
1197 if ($info['cs'] == 'DeviceCMYK')
1198 $this->_out('/Decode [1 0 1 0 1 0 1 0]');
1200 $this->_out('/BitsPerComponent ' . $info['bpc']);
1201 $this->_out('/Filter /' . $info['f']);
1202 if (isset($info['parms']))
1203 $this->_out($info['parms']);
1204 if (isset($info['trns']) and is_array($info['trns'])) {
1206 for ($i = 0; $i < count($info['trns']); $i++)
1207 $trns .= $info['trns'][$i] . ' ' . $info['trns'][$i] . ' ';
1208 $this->_out('/Mask [' . $trns . ']');
1210 $this->_out('/Length ' . strlen($info['data']) . '>>');
1211 $this->_putstream($info['data']);
1212 unset($this->images[$file]['data']);
1213 $this->_out('endobj');
1215 if ($info['cs'] == 'Indexed') {
1217 $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
1218 $this->_out('<<' . $filter . '/Length ' . strlen($pal) . '>>');
1219 $this->_putstream($pal);
1220 $this->_out('endobj');
1225 function _putresources()
1228 $this->_putimages();
1229 //Resource dictionary
1230 $this->offsets[2] = strlen($this->buffer);
1231 $this->_out('2 0 obj');
1232 $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
1233 $this->_out('/Font <<');
1234 foreach ($this->fonts as $font)
1235 $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
1237 if (count($this->images)) {
1238 $this->_out('/XObject <<');
1239 foreach ($this->images as $image)
1240 $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
1244 $this->_out('endobj');
1249 if (!empty($this->producer))
1250 $this->_out('/Producer ' . $this->_textstring($this->producer));
1251 if (!empty($this->title))
1252 $this->_out('/Title ' . $this->_textstring($this->title));
1253 if (!empty($this->subject))
1254 $this->_out('/Subject ' . $this->_textstring($this->subject));
1255 if (!empty($this->author))
1256 $this->_out('/Author ' . $this->_textstring($this->author));
1257 if (!empty($this->keywords))
1258 $this->_out('/Keywords ' . $this->_textstring($this->keywords));
1259 if (!empty($this->creator))
1260 $this->_out('/Creator ' . $this->_textstring($this->creator));
1261 $this->_out('/CreationDate ' . $this->_textstring('D:' . date('YmdHis')));
1264 function _putcatalog()
1266 $this->_out('/Type /Catalog');
1267 $this->_out('/Pages 1 0 R');
1268 if ($this->ZoomMode == 'fullpage')
1269 $this->_out('/OpenAction [3 0 R /Fit]');
1270 elseif ($this->ZoomMode == 'fullwidth')
1271 $this->_out('/OpenAction [3 0 R /FitH null]'); elseif ($this->ZoomMode == 'real')
1272 $this->_out('/OpenAction [3 0 R /XYZ null null 1]'); elseif (!is_string($this->ZoomMode))
1273 $this->_out('/OpenAction [3 0 R /XYZ null null ' . ($this->ZoomMode / 100) . ']');
1274 if ($this->LayoutMode == 'single')
1275 $this->_out('/PageLayout /SinglePage');
1276 elseif ($this->LayoutMode == 'continuous')
1277 $this->_out('/PageLayout /OneColumn'); elseif ($this->LayoutMode == 'two')
1278 $this->_out('/PageLayout /TwoColumnLeft');
1281 function _puttrailer()
1283 $this->_out('/Size ' . ($this->n + 1));
1284 $this->_out('/Root ' . $this->n . ' 0 R');
1285 $this->_out('/Info ' . ($this->n - 1) . ' 0 R');
1291 $this->_putresources();
1297 $this->_out('endobj');
1301 $this->_putcatalog();
1303 $this->_out('endobj');
1305 $o = strlen($this->buffer);
1306 $this->_out('xref');
1307 $this->_out('0 ' . ($this->n + 1));
1308 $this->_out('0000000000 65535 f ');
1309 for ($i = 1; $i <= $this->n; $i++)
1310 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
1312 $this->_out('trailer');
1314 $this->_puttrailer();
1316 $this->_out('startxref');
1318 $this->_out('%%EOF');
1322 function _beginpage($orientation)
1325 $this->pages[$this->page] = '';
1327 $this->x = $this->lMargin;
1328 $this->y = $this->tMargin;
1329 $this->FontFamily = '';
1332 $orientation = $this->DefOrientation;
1334 $orientation = strtoupper($orientation{0});
1335 if ($orientation != $this->DefOrientation)
1336 $this->OrientationChanges[$this->page] = true;
1338 if ($orientation != $this->CurOrientation) {
1339 //Change orientation
1340 if ($orientation == 'P') {
1341 $this->wPt = $this->fwPt;
1342 $this->hPt = $this->fhPt;
1343 $this->w = $this->fw;
1344 $this->h = $this->fh;
1346 $this->wPt = $this->fhPt;
1347 $this->hPt = $this->fwPt;
1348 $this->w = $this->fh;
1349 $this->h = $this->fw;
1351 $this->PageBreakTrigger = $this->h - $this->bMargin;
1352 $this->CurOrientation = $orientation;
1358 //End of page contents
1364 //Begin a new object
1366 $this->offsets[$this->n] = strlen($this->buffer);
1367 $this->_out($this->n . ' 0 obj');
1370 function _dounderline($x, $y, $txt)
1373 $up = $this->CurrentFont['up'];
1374 $ut = $this->CurrentFont['ut'];
1375 $w = $this->GetStringWidth($txt) + $this->ws * substr_count($txt, ' ');
1376 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);
1379 function _parsegif($file)
1381 //Function by Jérôme Fenal
1382 //GIF class in pure PHP from Yamasoft (http://www.yamasoft.com/php-gif.zip)
1383 require_once 'lib/gif.php';
1389 if (!$gif->loadFile($file, 0))
1390 $this->Error("GIF parser: unable to open file $file");
1392 if ($gif->m_img->m_gih->m_bLocalClr) {
1393 $nColors = $gif->m_img->m_gih->m_nTableSize;
1394 $pal = $gif->m_img->m_gih->m_colorTable->toString();
1395 if ($bgColor != -1) {
1396 $bgColor = $this->m_img->m_gih->m_colorTable->colorIndex($bgColor);
1398 $colspace = 'Indexed';
1399 } elseif ($gif->m_gfh->m_bGlobalClr) {
1400 $nColors = $gif->m_gfh->m_nTableSize;
1401 $pal = $gif->m_gfh->m_colorTable->toString();
1402 if ($bgColor != -1) {
1403 $bgColor = $gif->m_gfh->m_colorTable->colorIndex($bgColor);
1405 $colspace = 'Indexed';
1409 $colspace = 'DeviceGray';
1414 if ($gif->m_img->m_bTrans && ($nColors > 0)) {
1415 $trns = array($gif->m_img->m_nTrans);
1418 $data = $gif->m_img->m_data;
1419 $w = $gif->m_gfh->m_nWidth;
1420 $h = $gif->m_gfh->m_nHeight;
1422 if ($colspace == 'Indexed' and empty($pal))
1423 $this->Error('Missing palette in ' . $file);
1425 if ($this->compress) {
1426 $data = gzcompress($data);
1427 return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => 8, 'f' => 'FlateDecode', 'pal' => $pal, 'trns' => $trns, 'data' => $data);
1429 return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => 8, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
1433 function _parsejpg($file)
1435 //Extract info from a JPEG file
1436 $a = GetImageSize($file);
1438 $this->Error('Missing or incorrect image file: ' . $file);
1440 $this->Error('Not a JPEG file: ' . $file);
1441 if (!isset($a['channels']) or $a['channels'] == 3)
1442 $colspace = 'DeviceRGB';
1443 elseif ($a['channels'] == 4)
1444 $colspace = 'DeviceCMYK'; else
1445 $colspace = 'DeviceGray';
1446 $bpc = isset($a['bits']) ? $a['bits'] : 8;
1448 $f = fopen($file, 'rb');
1451 $data .= fread($f, 4096);
1453 return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
1456 function _parsepng($file)
1458 //Extract info from a PNG file
1459 $f = fopen($file, 'rb');
1461 $this->Error('Can\'t open image file: ' . $file);
1463 if (fread($f, 8) != chr(137) . 'PNG' . chr(13) . chr(10) . chr(26) . chr(10))
1464 $this->Error('Not a PNG file: ' . $file);
1467 if (fread($f, 4) != 'IHDR')
1468 $this->Error('Incorrect PNG file: ' . $file);
1469 $w = $this->_freadint($f);
1470 $h = $this->_freadint($f);
1471 $bpc = ord(fread($f, 1));
1473 $this->Error('16-bit depth not supported: ' . $file);
1474 $ct = ord(fread($f, 1));
1476 $colspace = 'DeviceGray';
1478 $colspace = 'DeviceRGB'; elseif ($ct == 3)
1479 $colspace = 'Indexed'; else
1480 $this->Error('Alpha channel not supported: ' . $file);
1481 if (ord(fread($f, 1)) != 0)
1482 $this->Error('Unknown compression method: ' . $file);
1483 if (ord(fread($f, 1)) != 0)
1484 $this->Error('Unknown filter method: ' . $file);
1485 if (ord(fread($f, 1)) != 0)
1486 $this->Error('Interlacing not supported: ' . $file);
1488 $parms = '/DecodeParms <</Predictor 15 /Colors ' . ($ct == 2 ? 3 : 1) . ' /BitsPerComponent ' . $bpc . ' /Columns ' . $w . '>>';
1489 //Scan chunks looking for palette, transparency and image data
1494 $n = $this->_freadint($f);
1495 $type = fread($f, 4);
1496 if ($type == 'PLTE') {
1498 $pal = fread($f, $n);
1500 } elseif ($type == 'tRNS') {
1501 //Read transparency info
1504 $trns = array(ord(substr($t, 1, 1)));
1506 $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1))); else {
1507 $pos = strpos($t, chr(0));
1509 $trns = array($pos);
1512 } elseif ($type == 'IDAT') {
1513 //Read image data block
1514 $data .= fread($f, $n);
1516 } elseif ($type == 'IEND')
1520 if ($colspace == 'Indexed' and empty($pal))
1521 $this->Error('Missing palette in ' . $file);
1523 return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
1526 function _freadint($f)
1528 //Read a 4-byte integer from file
1529 $i = ord(fread($f, 1)) << 24;
1530 $i += ord(fread($f, 1)) << 16;
1531 $i += ord(fread($f, 1)) << 8;
1532 $i += ord(fread($f, 1));
1536 function _textstring($s)
1538 //Format a text string
1539 return '(' . $this->_escape($s) . ')';
1542 function _escape($s)
1544 //Add \ before \, ( and )
1545 return str_replace(')', '\\)', str_replace('(', '\\(', str_replace('\\', '\\\\', $s)));
1548 function _putstream($s)
1550 $this->_out('stream');
1552 $this->_out('endstream');
1557 //Add a line to the document
1558 if ($this->state == 2)
1559 $this->pages[$this->page] .= $s . "\n";
1561 $this->buffer .= $s . "\n";
1567 //Handle special IE contype request
1568 if (isset($HTTP_SERVER_VARS['HTTP_USER_AGENT']) and
1569 $HTTP_SERVER_VARS['HTTP_USER_AGENT'] == 'contype'
1571 Header('Content-Type: application/pdf');
1578 // c-basic-offset: 4
1579 // c-hanging-comment-ender-p: nil
1580 // indent-tabs-mode: nil