home *** CD-ROM | disk | FTP | other *** search
- <?php
- /**
- * The File_PDF class provides a PHP-only implementation of a PDF library. No
- * external libs or PHP extensions are required.
- *
- * Based on the FPDF class by Olivier Plathey (http://www.fpdf.org/).
- *
- * $Horde: framework/File_PDF/PDF.php,v 1.64 2008/02/26 23:18:55 jan Exp $
- *
- * Copyright 2001-2003 Olivier Plathey <olivier@fpdf.org>
- * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author Olivier Plathey <olivier@fpdf.org>
- * @author Marko Djukic <marko@oblo.com>
- * @author Jan Schneider <jan@horde.org>
- * @package File_PDF
- * @category Fileformats
- */
-
- /**
- * This hack works around Horde bug #4094
- * (http://bugs.horde.org/ticket/?id=4094)
- *
- * Once this package does not need to support PHP < 4.3.10 anymore the
- * following definiton can be removed and the ugly code can be removed
- * using
- *
- * sed -i -e 's/\' \. FILE_PDF_FLOAT \. \'/F/g' PDF.php
- */
- if (version_compare(PHP_VERSION, '4.3.10', '>=')) {
- define('FILE_PDF_FLOAT', 'F');
- } else {
- define('FILE_PDF_FLOAT', 'f');
- }
-
- class File_PDF {
-
- /**
- * Current page number.
- *
- * @var integer
- */
- var $_page = 0;
-
- /**
- * Current object number.
- *
- * @var integer
- */
- var $_n = 2;
-
- /**
- * Array of object offsets.
- *
- * @var array
- */
- var $_offsets = array();
-
- /**
- * Buffer holding in-memory PDF.
- *
- * @var string
- */
- var $_buffer = '';
-
- /**
- * Buffer length, including already flushed content.
- *
- * @var integer
- */
- var $_buflen = 0;
-
- /**
- * Whether the buffer has been flushed already.
- *
- * @var boolean
- */
- var $_flushed = false;
-
- /**
- * Array containing the pages.
- *
- * @var array
- */
- var $_pages = array();
-
- /**
- * Current document state.<pre>
- * 0 - initial state
- * 1 - document opened
- * 2 - page opened
- * 3 - document closed
- * </pre>
- *
- * @var integer
- */
- var $_state = 0;
-
- /**
- * Flag indicating if PDF file is to be compressed or not.
- *
- * @var boolean
- */
- var $_compress;
-
- /**
- * The default page orientation.
- *
- * @var string
- */
- var $_default_orientation;
-
- /**
- * The current page orientation.
- *
- * @var string
- */
- var $_current_orientation;
-
- /**
- * Array indicating orientation changes.
- *
- * @var array
- */
- var $_orientation_changes = array();
-
- /**
- * Current width of page format in points.
- *
- * @var float
- */
- var $fwPt;
-
- /**
- * Current height of page format in points.
- *
- * @var float
- */
- var $fhPt;
-
- /**
- * Current width of page format in user units.
- *
- * @var float
- */
- var $fw;
-
- /**
- * Current height of page format in user units.
- *
- * @var float
- */
- var $fh;
-
- /**
- * Current width of page in points.
- *
- * @var float
- */
- var $wPt;
-
- /**
- * Current height of page in points.
- *
- * @var float
- */
- var $hPt;
-
- /**
- * Current width of page in user units
- *
- * @var float
- */
- var $w;
-
- /**
- * Current height of page in user units
- *
- * @var float
- */
- var $h;
-
- /**
- * Scale factor (number of points in user units).
- *
- * @var float
- */
- var $_scale;
-
- /**
- * Left page margin size.
- *
- * @var float
- */
- var $_left_margin;
-
- /**
- * Top page margin size.
- *
- * @var float
- */
- var $_top_margin;
-
- /**
- * Right page margin size.
- *
- * @var float
- */
- var $_right_margin;
-
- /**
- * Break page margin size, the bottom margin which triggers a page break.
- *
- * @var float
- */
- var $_break_margin;
-
- /**
- * Cell margin size.
- *
- * @var float
- */
- var $_cell_margin;
-
- /**
- * The current horizontal position for cell positioning.
- * Value is set in user units and is calculated from the top left corner
- * as origin.
- *
- * @var float
- */
- var $x;
-
- /**
- * The current vertical position for cell positioning.
- * Value is set in user units and is calculated from the top left corner
- * as origin.
- *
- * @var float
- */
- var $y;
-
- /**
- * The height of the last cell printed.
- *
- * @var float
- */
- var $_last_height;
-
- /**
- * Line width in user units.
- *
- * @var float
- */
- var $_line_width;
-
- /**
- * An array of standard font names.
- *
- * @var array
- */
- var $_core_fonts = array('courier' => 'Courier',
- 'courierB' => 'Courier-Bold',
- 'courierI' => 'Courier-Oblique',
- 'courierBI' => 'Courier-BoldOblique',
- 'helvetica' => 'Helvetica',
- 'helveticaB' => 'Helvetica-Bold',
- 'helveticaI' => 'Helvetica-Oblique',
- 'helveticaBI' => 'Helvetica-BoldOblique',
- 'times' => 'Times-Roman',
- 'timesB' => 'Times-Bold',
- 'timesI' => 'Times-Italic',
- 'timesBI' => 'Times-BoldItalic',
- 'symbol' => 'Symbol',
- 'zapfdingbats' => 'ZapfDingbats');
-
- /**
- * An array of used fonts.
- *
- * @var array
- */
- var $_fonts = array();
-
- /**
- * An array of font files.
- *
- * @var array
- */
- var $_font_files = array();
-
- /**
- * An array of encoding differences.
- *
- * @var array
- */
- var $_diffs = array();
-
- /**
- * An array of used images.
- *
- * @var array
- */
- var $_images = array();
-
- /**
- * An array of links in pages.
- *
- * @var array
- */
- var $_page_links;
-
- /**
- * An array of internal links.
- *
- * @var array
- */
- var $_links = array();
-
- /**
- * Current font family.
- *
- * @var string
- */
- var $_font_family = '';
-
- /**
- * Current font style.
- *
- * @var string
- */
- var $_font_style = '';
-
- /**
- * Underlining flag.
- *
- * @var boolean
- */
- var $_underline = false;
-
- /**
- * An array containing current font info.
- *
- * @var array
- */
- var $_current_font;
-
- /**
- * Current font size in points.
- *
- * @var float
- */
- var $_font_size_pt = 12;
-
- /**
- * Current font size in user units.
- *
- * @var float
- */
- var $_font_size = 12;
-
- /**
- * Commands for filling color.
- *
- * @var string
- */
- var $_fill_color = '0 g';
-
- /**
- * Commands for text color.
- *
- * @var string
- */
- var $_text_color = '0 g';
-
- /**
- * Whether text color is different from fill color.
- *
- * @var boolean
- */
- var $_color_flag = false;
-
- /**
- * Commands for drawing color.
- *
- * @var string
- */
- var $_draw_color = '0 G';
-
- /**
- * Word spacing.
- *
- * @var integer
- */
- var $_word_spacing = 0;
-
- /**
- * Automatic page breaking.
- *
- * @var boolean
- */
- var $_auto_page_break;
-
- /**
- * Threshold used to trigger page breaks.
- *
- * @var float
- */
- var $_page_break_trigger;
-
- /**
- * Flag set when processing footer.
- *
- * @var boolean
- */
- var $_in_footer = false;
-
- /**
- * Zoom display mode.
- *
- * @var string
- */
- var $_zoom_mode;
-
- /**
- * Layout display mode.
- *
- * @var string
- */
- var $_layout_mode;
-
- /**
- * An array containing the document info, consisting of:
- * - title
- * - subject
- * - author
- * - keywords
- * - creator
- *
- * @var array
- */
- var $_info = array();
-
- /**
- * Alias for total number of pages.
- *
- * @var string
- */
- var $_alias_nb_pages = '{nb}';
-
- /**
- * Attempts to return a conrete PDF instance.
- *
- * It allows to set up the page format, the orientation and the units of
- * measurement used in all the methods (except for the font sizes).
- *
- * Example:
- * <code>
- * $pdf = File_PDF::factory(array('orientation' => 'P',
- * 'unit' => 'mm',
- * 'format' => 'A4'));
- * </code>
- *
- * @param array $params A hash with parameters for the created PDF object.
- * Possible parameters are:
- * - orientation - Default page orientation. Possible
- * values are (case insensitive):
- * - P or Portrait (default)
- * - L or Landscape
- * - unit - User measure units. Possible values
- * values are:
- * - pt: point
- * - mm: millimeter (default)
- * - cm: centimeter
- * - in: inch
- * A point equals 1/72 of inch, that is to say
- * about 0.35 mm (an inch being 2.54 cm). This is a
- * very common unit in typography; font sizes are
- * expressed in that unit.
- * - format - The format used for pages. It can be
- * either one of the following values (case
- * insensitive):
- * - A3
- * - A4 (default)
- * - A5
- * - Letter
- * - Legal
- * or a custom format in the form of a two-element
- * array containing the width and the height
- * (expressed in the unit given by the unit
- * parameter).
- * @param string $class The concrete class name to return an instance of.
- * Defaults to File_PDF.
- */
- function &factory($params = array(), $class = 'File_PDF')
- {
- /* Default parameters. */
- $defaults = array('orientation' => 'P',
- 'unit' => 'mm',
- 'format' => 'A4');
-
- /* Backward compatibility with old method signature. */
- /* Should be removed a few versions later. */
- if (!is_array($params)) {
- $class = 'File_PDF';
- $params = $defaults;
- $names = array_keys($defaults);
- for ($i = 0; $i < func_num_args(); $i++) {
- $params[$names[$i]] = func_get_arg($i);
- }
- } else {
- $params = array_merge($defaults, $params);
- }
-
- /* Create the PDF object. */
- $pdf = new $class($params);
-
- /* Scale factor. */
- if ($params['unit'] == 'pt') {
- $pdf->_scale = 1;
- } elseif ($params['unit'] == 'mm') {
- $pdf->_scale = 72 / 25.4;
- } elseif ($params['unit'] == 'cm') {
- $pdf->_scale = 72 / 2.54;
- } elseif ($params['unit'] == 'in') {
- $pdf->_scale = 72;
- } else {
- $error = File_PDF::raiseError(sprintf('Incorrect units: %s', $params['unit']));
- return $error;
- }
- /* Page format. */
- if (is_string($params['format'])) {
- $params['format'] = strtolower($params['format']);
- if ($params['format'] == 'a3') {
- $params['format'] = array(841.89, 1190.55);
- } elseif ($params['format'] == 'a4') {
- $params['format'] = array(595.28, 841.89);
- } elseif ($params['format'] == 'a5') {
- $params['format'] = array(420.94, 595.28);
- } elseif ($params['format'] == 'letter') {
- $params['format'] = array(612, 792);
- } elseif ($params['format'] == 'legal') {
- $params['format'] = array(612, 1008);
- } else {
- $error = File_PDF::raiseError(sprintf('Unknown page format: %s', $params['format']));
- return $error;
- }
- $pdf->fwPt = $params['format'][0];
- $pdf->fhPt = $params['format'][1];
- } else {
- $pdf->fwPt = $params['format'][0] * $pdf->_scale;
- $pdf->fhPt = $params['format'][1] * $pdf->_scale;
- }
- $pdf->fw = $pdf->fwPt / $pdf->_scale;
- $pdf->fh = $pdf->fhPt / $pdf->_scale;
-
- /* Page orientation. */
- $params['orientation'] = strtolower($params['orientation']);
- if ($params['orientation'] == 'p' || $params['orientation'] == 'portrait') {
- $pdf->_default_orientation = 'P';
- $pdf->wPt = $pdf->fwPt;
- $pdf->hPt = $pdf->fhPt;
- } elseif ($params['orientation'] == 'l' || $params['orientation'] == 'landscape') {
- $pdf->_default_orientation = 'L';
- $pdf->wPt = $pdf->fhPt;
- $pdf->hPt = $pdf->fwPt;
- } else {
- $error = File_PDF::raiseError(sprintf('Incorrect orientation: %s', $params['orientation']));
- return $error;
- }
- $pdf->_current_orientation = $pdf->_default_orientation;
- $pdf->w = $pdf->wPt / $pdf->_scale;
- $pdf->h = $pdf->hPt / $pdf->_scale;
-
- /* Page margins (1 cm) */
- $margin = 28.35 / $pdf->_scale;
- $pdf->setMargins($margin, $margin);
-
- /* Interior cell margin (1 mm) */
- $pdf->_cell_margin = $margin / 10;
-
- /* Line width (0.2 mm) */
- $pdf->_line_width = .567 / $pdf->_scale;
-
- /* Automatic page break */
- $pdf->setAutoPageBreak(true, 2 * $margin);
-
- /* Full width display mode */
- $pdf->setDisplayMode('fullwidth');
-
- /* Compression */
- $pdf->setCompression(true);
-
- return $pdf;
- }
-
- /**
- * Returns a PEAR_Error object.
- *
- * Wraps around PEAR::raiseError() to avoid having to include PEAR.php
- * unless an error occurs.
- *
- * @param mixed $error The error message.
- *
- * @return object PEAR_Error
- */
- function raiseError($error)
- {
- require_once 'PEAR.php';
- return PEAR::raiseError($error);
- }
-
- /**
- * Defines the left, top and right margins.
- *
- * By default, they equal 1 cm. Call this method to change them.
- *
- * @param float $left Left margin.
- * @param float $top Top margin.
- * @param float $right Right margin. If not specified default to the value
- * of the left one.
- *
- * @see setAutoPageBreak()
- * @see setLeftMargin()
- * @see setRightMargin()
- * @see setTopMargin()
- */
- function setMargins($left, $top, $right = null)
- {
- /* Set left and top margins. */
- $this->_left_margin = $left;
- $this->_top_margin = $top;
- /* If no right margin set default to same as left. */
- $this->_right_margin = (is_null($right) ? $left : $right);
- }
-
- /**
- * Defines the left margin.
- *
- * The method can be called before creating the first page. If the
- * current abscissa gets out of page, it is brought back to the margin.
- *
- * @param float $margin The margin.
- *
- * @see setAutoPageBreak()
- * @see setMargins()
- * @see setRightMargin()
- * @see setTopMargin()
- */
- function setLeftMargin($margin)
- {
- $this->_left_margin = $margin;
- /* If there is a current page and the current X position is less than
- * margin set the X position to the margin value. */
- if ($this->_page > 0 && $this->x < $margin) {
- $this->x = $margin;
- }
- }
-
- /**
- * Defines the top margin.
- *
- * The method can be called before creating the first page.
- *
- * @param float $margin The margin.
- */
- function setTopMargin($margin)
- {
- $this->_top_margin = $margin;
- }
-
- /**
- * Defines the right margin.
- *
- * The method can be called before creating the first page.
- *
- * @param float $margin The margin.
- */
- function setRightMargin($margin)
- {
- $this->_right_margin = $margin;
- }
-
- /**
- * Returns the actual page width.
- *
- * @since File_PDF 0.2.0
- * @since Horde 3.2
- *
- * @return float The page width.
- */
- function getPageWidth()
- {
- return ($this->w - $this->_right_margin - $this->_left_margin);
- }
-
- /**
- * Returns the actual page height.
- *
- * @since File_PDF 0.2.0
- * @since Horde 3.2
- *
- * @return float The page height.
- */
- function getPageHeight()
- {
- return ($this->h - $this->_top_margin - $this->_break_margin);
- }
-
- /**
- * Enables or disables the automatic page breaking mode.
- *
- * When enabling, the second parameter is the distance from the bottom of
- * the page that defines the triggering limit. By default, the mode is on
- * and the margin is 2 cm.
- *
- * @param boolean $auto Boolean indicating if mode should be on or off.
- * @param float $margin Distance from the bottom of the page.
- */
- function setAutoPageBreak($auto, $margin = 0)
- {
- $this->_auto_page_break = $auto;
- $this->_break_margin = $margin;
- $this->_page_break_trigger = $this->h - $margin;
- }
-
- /**
- * Defines the way the document is to be displayed by the viewer.
- *
- * The zoom level can be set: pages can be displayed entirely on screen,
- * occupy the full width of the window, use real size, be scaled by a
- * specific zooming factor or use viewer default (configured in the
- * Preferences menu of Acrobat). The page layout can be specified too:
- * single at once, continuous display, two columns or viewer default. By
- * default, documents use the full width mode with continuous display.
- *
- * @param mixed $zoom The zoom to use. It can be one of the following
- * string values:
- * - fullpage: entire page on screen
- * - fullwidth: maximum width of window
- * - real: uses real size (100% zoom)
- * - default: uses viewer default mode
- * or a number indicating the zooming factor.
- * @param string layout The page layout. Possible values are:
- * - single: one page at once
- * - continuous: pages in continuously
- * - two: two pages on two columns
- * - default: uses viewer default mode
- * Default value is continuous.
- */
- function setDisplayMode($zoom, $layout = 'continuous')
- {
- $zoom = strtolower($zoom);
- if ($zoom == 'fullpage' || $zoom == 'fullwidth' || $zoom == 'real'
- || $zoom == 'default' || !is_string($zoom)) {
- $this->_zoom_mode = $zoom;
- } elseif ($zoom == 'zoom') {
- $this->_zoom_mode = $layout;
- } else {
- return $this->raiseError(sprintf('Incorrect zoom display mode: %s', $zoom));
- }
-
- $layout = strtolower($layout);
- if ($layout == 'single' || $layout == 'continuous' || $layout == 'two'
- || $layout == 'default') {
- $this->_layout_mode = $layout;
- } elseif ($zoom != 'zoom') {
- return $this->raiseError(sprintf('Incorrect layout display mode: %s', $layout));
- }
- }
-
- /**
- * Activates or deactivates page compression.
- *
- * When activated, the internal representation of each page is compressed,
- * which leads to a compression ratio of about 2 for the resulting
- * document. Compression is on by default.
- *
- * Note: the {@link http://www.php.net/zlib/ zlib extension} is required
- * for this feature. If not present, compression will be turned off.
- *
- * @param boolean $compress Boolean indicating if compression must be
- * enabled or not.
- */
- function setCompression($compress)
- {
- /* If no gzcompress function is available then default to false. */
- $this->_compress = (function_exists('gzcompress') ? $compress : false);
- }
-
- /**
- * Set the info to a document.
- *
- * Possible info settings are:
- * - title
- * - subject
- * - author
- * - keywords
- * - creator
- *
- * @param array|string $info If passed as an array then the complete hash
- * containing the info to be inserted into the
- * document. Otherwise the name of setting to be
- * set.
- * @param string $value The value of the setting.
- */
- function setInfo($info, $value = '')
- {
- if (is_array($info)) {
- $this->_info = $info;
- } else {
- $this->_info[$info] = $value;
- }
- }
-
- /**
- * Defines an alias for the total number of pages.
- *
- * It will be substituted as the document is closed.
- *
- * Example:
- * <code>
- * class My_File_PDF extends File_PDF {
- * function footer()
- * {
- * // Go to 1.5 cm from bottom
- * $this->setY(-15);
- * // Select Arial italic 8
- * $this->setFont('Arial', 'I', 8);
- * // Print current and total page numbers
- * $this->cell(0, 10, 'Page ' . $this->getPageNo() . '/{nb}', 0,
- * 0, 'C');
- * }
- * }
- * $pdf = My_File_PDF::factory();
- * $pdf->aliasNbPages();
- * </code>
- *
- * @param string $alias The alias.
- *
- * @see getPageNo()
- * @see footer()
- */
- function aliasNbPages($alias = '{nb}')
- {
- $this->_alias_nb_pages = $alias;
- }
-
- /**
- * This method begins the generation of the PDF document; it must be
- * called before any output commands.
- *
- * No page is created by this method, therefore it is necessary to call
- * {@link addPage()}.
- *
- * @see addPage()
- * @see close()
- */
- function open()
- {
- $this->_beginDoc();
- }
-
- /**
- * Terminates the PDF document. It is not necessary to call this method
- * explicitly because {@link output()} does it automatically.
- *
- * If the document contains no page, {@link addPage()} is called to
- * prevent from getting an invalid document.
- *
- * @see open()
- * @see output()
- */
- function close()
- {
- /* Terminate document */
- if ($this->_page == 0) {
- $result = $this->addPage();
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- }
- /* Page footer */
- $this->_in_footer = true;
- $this->x = $this->_left_margin;
- $this->footer();
- $this->_in_footer = false;
- /* Close page */
- $this->_endPage();
- /* Close document */
- $this->_endDoc();
- }
-
- /**
- * Adds a new page to the document.
- *
- * If a page is already present, the {@link footer()} method is called
- * first to output the footer. Then the page is added, the current
- * position set to the top-left corner according to the left and top
- * margins, and {@link header()} is called to display the header.
- *
- * The font which was set before calling is automatically restored. There
- * is no need to call {@link setFont()} again if you want to continue with
- * the same font. The same is true for colors and line width. The origin
- * of the coordinate system is at the top-left corner and increasing
- * ordinates go downwards.
- *
- * @param string $orientation Page orientation. Possible values
- * are (case insensitive):
- * - P or Portrait
- * - L or Landscape
- * The default value is the one passed to the
- * constructor.
- *
- * @see header()
- * @see footer()
- * @see setMargins()
- */
- function addPage($orientation = '')
- {
- /* For good measure make sure this is called. */
- $this->_beginDoc();
-
- /* Save style settings so that they are not overridden by footer() or
- * header(). */
- $lw = $this->_line_width;
- $dc = $this->_draw_color;
- $fc = $this->_fill_color;
- $tc = $this->_text_color;
- $cf = $this->_color_flag;
- $font_family = $this->_font_family;
- $font_style = $this->_font_style . ($this->_underline ? 'U' : '');
- $font_size = $this->_font_size_pt;
-
- /* Close old page. */
- if ($this->_page > 0) {
- /* Page footer. */
- $this->_in_footer = true;
- $this->x = $this->_left_margin;
- $this->footer();
- $this->_in_footer = false;
-
- /* Close page. */
- $this->_endPage();
- }
-
- /* Start new page. */
- $this->_beginPage($orientation);
- /* Set line cap style to square. */
- $this->_out('2 J');
- /* Set line width. */
- $this->_line_width = $lw;
- $this->_out(sprintf('%.2' . FILE_PDF_FLOAT . ' w', $lw * $this->_scale));
-
- /* Force the setting of the font. Each new page requires a new
- * call. */
- if ($font_family) {
- $result = $this->setFont($font_family, $font_style, $font_size, true);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- }
-
- /* Restore styles. */
- if ($this->_fill_color != $fc) {
- $this->_fill_color = $fc;
- $this->_out($this->_fill_color);
- }
- if ($this->_draw_color != $dc) {
- $this->_draw_color = $dc;
- $this->_out($this->_draw_color);
- }
- $this->_text_color = $tc;
- $this->_color_flag = $cf;
-
- /* Page header. */
- $this->header();
-
- /* Restore styles. */
- if ($this->_line_width != $lw) {
- $this->_line_width = $lw;
- $this->_out(sprintf('%.2' . FILE_PDF_FLOAT . ' w', $lw * $this->_scale));
- }
- $result = $this->setFont($font_family, $font_style, $font_size);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- if ($this->_fill_color != $fc) {
- $this->_fill_color = $fc;
- $this->_out($this->_fill_color);
- }
- if ($this->_draw_color != $dc) {
- $this->_draw_color = $dc;
- $this->_out($this->_draw_color);
- }
- $this->_text_color = $tc;
- $this->_color_flag = $cf;
- }
-
- /**
- * This method is used to render the page header.
- *
- * It is automatically called by {@link addPage()} and should not be
- * called directly by the application. The implementation in File_PDF:: is
- * empty, so you have to subclass it and override the method if you want a
- * specific processing.
- *
- * Example:
- * <code>
- * class My_File_PDF extends File_PDF {
- * function header()
- * {
- * // Select Arial bold 15
- * $this->setFont('Arial', 'B', 15);
- * // Move to the right
- * $this->cell(80);
- * // Framed title
- * $this->cell(30, 10, 'Title', 1, 0, 'C');
- * // Line break
- * $this->newLine(20);
- * }
- * }
- * </code>
- *
- * @see footer()
- */
- function header()
- {
- /* To be implemented in your own inherited class. */
- }
-
- /**
- * This method is used to render the page footer.
- *
- * It is automatically called by {@link addPage()} and {@link close()} and
- * should not be called directly by the application. The implementation in
- * File_PDF:: is empty, so you have to subclass it and override the method
- * if you want a specific processing.
- *
- * Example:
- * <code>
- * class My_File_PDF extends File_PDF {
- * function footer()
- * {
- * // Go to 1.5 cm from bottom
- * $this->setY(-15);
- * // Select Arial italic 8
- * $this->setFont('Arial', 'I', 8);
- * // Print centered page number
- * $this->cell(0, 10, 'Page ' . $this->getPageNo(), 0, 0, 'C');
- * }
- * }
- * </code>
- *
- * @see header()
- */
- function footer()
- {
- /* To be implemented in your own inherited class. */
- }
-
- /**
- * Returns the current page number.
- *
- * @return integer
- *
- * @see aliasNbPages()
- */
- function getPageNo()
- {
- return $this->_page;
- }
-
- /**
- * Sets the fill color.
- *
- * Depending on the colorspace called, the number of color component
- * parameters required can be either 1, 3 or 4. The method can be called
- * before the first page is created and the color is retained from page to
- * page.
- *
- * @param string $cs Indicates the colorspace which can be either 'rgb',
- * 'cmyk' or 'gray'. Defaults to 'rgb'.
- * @param float $c1 First color component, floating point value between 0
- * and 1. Required for gray, rgb and cmyk.
- * @param float $c2 Second color component, floating point value
- * between 0 and 1. Required for rgb and cmyk.
- * @param float $c3 Third color component, floating point value between 0
- * and 1. Required for rgb and cmyk.
- * @param float $c4 Fourth color component, floating point value
- * between 0 and 1. Required for cmyk.
- *
- * @see setTextColor()
- * @see setDrawColor()
- * @see rect()
- * @see cell()
- * @see multiCell()
- */
- function setFillColor($cs = 'rgb', $c1, $c2 = 0, $c3 = 0, $c4 = 0)
- {
- $cs = strtolower($cs);
- if ($cs == 'rgb') {
- $this->_fill_color = sprintf('%.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' rg', $c1, $c2, $c3);
- } elseif ($cs == 'cmyk') {
- $this->_fill_color = sprintf('%.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' k', $c1, $c2, $c3, $c4);
- } else {
- $this->_fill_color = sprintf('%.3' . FILE_PDF_FLOAT . ' g', $c1);
- }
- if ($this->_page > 0) {
- $this->_out($this->_fill_color);
- }
- $this->_color_flag = $this->_fill_color != $this->_text_color;
- }
-
- /**
- * Sets the text color.
- *
- * Depending on the colorspace called, the number of color component
- * parameters required can be either 1, 3 or 4. The method can be called
- * before the first page is created and the color is retained from page to
- * page.
- *
- * @param string $cs Indicates the colorspace which can be either 'rgb',
- * 'cmyk' or 'gray'. Defaults to 'rgb'.
- * @param float $c1 First color component, floating point value between 0
- * and 1. Required for gray, rgb and cmyk.
- * @param float $c2 Second color component, floating point value
- * between 0 and 1. Required for rgb and cmyk.
- * @param float $c3 Third color component, floating point value between 0
- * and 1. Required for rgb and cmyk.
- * @param float $c4 Fourth color component, floating point value
- * between 0 and 1. Required for cmyk.
- *
- * @since File_PDF 0.2.0
- * @since Horde 3.2
- * @see setFillColor()
- * @see setDrawColor()
- * @see rect()
- * @see cell()
- * @see multiCell()
- */
- function setTextColor($cs, $c1, $c2 = 0, $c3 = 0, $c4 = 0)
- {
- $cs = strtolower($cs);
- if ($cs == 'rgb') {
- $this->_text_color = sprintf('%.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' rg', $c1, $c2, $c3);
- } elseif ($cs == 'cmyk') {
- $this->_text_color = sprintf('%.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' k', $c1, $c2, $c3, $c4);
- } else {
- $this->_text_color = sprintf('%.3' . FILE_PDF_FLOAT . ' g', $c1);
- }
- $this->_color_flag = $this->_fill_color != $this->_text_color;
- }
-
- /**
- * Sets the draw color, used when drawing lines.
- *
- * Depending on the colorspace called, the number of color component
- * parameters required can be either 1, 3 or 4. The method can be called
- * before the first page is created and the color is retained from page to
- * page.
- *
- * @param string $cs Indicates the colorspace which can be either 'rgb',
- * 'cmyk' or 'gray'. Defaults to 'rgb'.
- * @param float $c1 First color component, floating point value between 0
- * and 1. Required for gray, rgb and cmyk.
- * @param float $c2 Second color component, floating point value
- * between 0 and 1. Required for rgb and cmyk.
- * @param float $c3 Third color component, floating point value between 0
- * and 1. Required for rgb and cmyk.
- * @param float $c4 Fourth color component, floating point value
- * between 0 and 1. Required for cmyk.
- *
- * @see setFillColor()
- * @see line()
- * @see rect()
- * @see cell()
- * @see multiCell()
- */
- function setDrawColor($cs = 'rgb', $c1, $c2 = 0, $c3 = 0, $c4 = 0)
- {
- $cs = strtolower($cs);
- if ($cs == 'rgb') {
- $this->_draw_color = sprintf('%.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' RG', $c1, $c2, $c3);
- } elseif ($cs == 'cmyk') {
- $this->_draw_color = sprintf('%.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' K', $c1, $c2, $c3, $c4);
- } else {
- $this->_draw_color = sprintf('%.3' . FILE_PDF_FLOAT . ' G', $c1);
- }
- if ($this->_page > 0) {
- $this->_out($this->_draw_color);
- }
- }
-
- /**
- * Returns the length of a text string. A font must be selected.
- *
- * @param string $text The text whose length is to be computed.
- * @param boolean $pt Whether the width should be returned in points or
- * user units.
- *
- * @return float
- */
- function getStringWidth($text, $pt = false)
- {
- $text = (string)$text;
- $width = 0;
- $length = strlen($text);
- for ($i = 0; $i < $length; $i++) {
- $width += $this->_current_font['cw'][$text{$i}];
- }
-
- /* Adjust for word spacing. */
- $width += $this->_word_spacing * substr_count($text, ' ') * $this->_current_font['cw'][' '];
-
- if ($pt) {
- return $width * $this->_font_size_pt / 1000;
- } else {
- return $width * $this->_font_size / 1000;
- }
- }
-
- /**
- * Defines the line width.
- *
- * By default, the value equals 0.2 mm. The method can be called before
- * the first page is created and the value is retained from page to page.
- *
- * @param float $width The width.
- *
- * @see line()
- * @see rect()
- * @see cell()
- * @see multiCell()
- */
- function setLineWidth($width)
- {
- $this->_line_width = $width;
- if ($this->_page > 0) {
- $this->_out(sprintf('%.2' . FILE_PDF_FLOAT . ' w', $width * $this->_scale));
- }
- }
-
- /**
- * Draws a line between two points.
- *
- * All coordinates can be negative to provide values from the right or
- * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
- *
- * @param float $x1 Abscissa of first point.
- * @param float $y1 Ordinate of first point.
- * @param float $x2 Abscissa of second point.
- * @param float $y2 Ordinate of second point.
- *
- * @see setLineWidth()
- * @see setDrawColor()
- */
- function line($x1, $y1, $x2, $y2)
- {
- if ($x1 < 0) {
- $x1 += $this->w;
- }
- if ($y1 < 0) {
- $y1 += $this->h;
- }
- if ($x2 < 0) {
- $x2 += $this->w;
- }
- if ($y2 < 0) {
- $y2 += $this->h;
- }
-
- $this->_out(sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' m %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' l S', $x1 * $this->_scale, ($this->h - $y1) * $this->_scale, $x2 * $this->_scale, ($this->h - $y2) * $this->_scale));
- }
-
- /**
- * Outputs a rectangle.
- *
- * It can be drawn (border only), filled (with no border) or both.
- *
- * All coordinates can be negative to provide values from the right or
- * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
- *
- * @param float $x Abscissa of upper-left corner.
- * @param float $y Ordinate of upper-left corner.
- * @param float $width Width.
- * @param float $height Height.
- * @param float $style Style of rendering. Possible values are:
- * - D or empty string: draw (default)
- * - F: fill
- * - DF or FD: draw and fill
- *
- * @see setLineWidth()
- * @see setDrawColor()
- * @see setFillColor()
- */
- function rect($x, $y, $width, $height, $style = '')
- {
- if ($x < 0) {
- $x += $this->w;
- }
- if ($y < 0) {
- $y += $this->h;
- }
-
- $style = strtoupper($style);
- if ($style == 'F') {
- $op = 'f';
- } elseif ($style == 'FD' || $style == 'DF') {
- $op = 'B';
- } else {
- $op = 'S';
- }
-
- $x = $this->_toPt($x);
- $y = $this->_toPt($y);
- $width = $this->_toPt($width);
- $height = $this->_toPt($height);
-
- $this->_out(sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' re %s', $x, $this->hPt - $y, $width, -$height, $op));
- }
-
- /**
- * Outputs a circle. It can be drawn (border only), filled (with no
- * border) or both.
- *
- * All coordinates can be negative to provide values from the right or
- * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
- *
- * @param float $x Abscissa of the center of the circle.
- * @param float $y Ordinate of the center of the circle.
- * @param float $r Circle radius.
- * @param string $style Style of rendering. Possible values are:
- * - D or empty string: draw (default)
- * - F: fill
- * - DF or FD: draw and fill
- */
- function circle($x, $y, $r, $style = '')
- {
- if ($x < 0) {
- $x += $this->w;
- }
- if ($y < 0) {
- $y += $this->h;
- }
-
- $style = strtolower($style);
- if ($style == 'f') {
- $op = 'f'; // Style is fill only.
- } elseif ($style == 'fd' || $style == 'df') {
- $op = 'B'; // Style is fill and stroke.
- } else {
- $op = 'S'; // Style is stroke only.
- }
-
- $x = $this->_toPt($x);
- $y = $this->_toPt($y);
- $r = $this->_toPt($r);
-
- /* Invert the y scale. */
- $y = $this->hPt - $y;
- /* Length of the Bezier control. */
- $b = $r * 0.552;
-
- /* Move from the given origin and set the current point
- * to the start of the first Bezier curve. */
- $c = sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' m', $x - $r, $y);
- $x = $x - $r;
- /* First circle quarter. */
- $c .= sprintf(' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' c',
- $x, $y + $b, // First control point.
- $x + $r - $b, $y + $r, // Second control point.
- $x + $r, $y + $r); // Final point.
- /* Set x/y to the final point. */
- $x = $x + $r;
- $y = $y + $r;
- /* Second circle quarter. */
- $c .= sprintf(' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' c',
- $x + $b, $y,
- $x + $r, $y - $r + $b,
- $x + $r, $y - $r);
- /* Set x/y to the final point. */
- $x = $x + $r;
- $y = $y - $r;
- /* Third circle quarter. */
- $c .= sprintf(' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' c',
- $x, $y - $b,
- $x - $r + $b, $y - $r,
- $x - $r, $y - $r);
- /* Set x/y to the final point. */
- $x = $x - $r;
- $y = $y - $r;
- /* Fourth circle quarter. */
- $c .= sprintf(' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' c %s',
- $x - $b, $y,
- $x - $r, $y + $r - $b,
- $x - $r, $y + $r,
- $op);
- /* Output the whole string. */
- $this->_out($c);
- }
-
- /**
- * Imports a TrueType or Type1 font and makes it available. It is
- * necessary to generate a font definition file first with the
- * makefont.php utility.
- * The location of the definition file (and the font file itself when
- * embedding) must be found at the full path name included.
- *
- * Example:
- * <code>
- * $pdf->addFont('Comic', 'I');
- * is equivalent to:
- * $pdf->addFont('Comic', 'I', 'comici.php');
- * </code>
- *
- * @param string $family Font family. The name can be chosen arbitrarily.
- * If it is a standard family name, it will
- * override the corresponding font.
- * @param string $style Font style. Possible values are (case
- * insensitive):
- * - empty string: regular (default)
- * - B: bold
- * - I: italic
- * - BI or IB: bold italic
- * @param string $file The font definition file. By default, the name is
- * built from the family and style, in lower case
- * with no space.
- *
- * @see setFont()
- */
- function addFont($family, $style = '', $file = '')
- {
- $family = strtolower($family);
- if ($family == 'arial') {
- $family = 'helvetica';
- }
-
- $style = strtoupper($style);
- if ($style == 'IB') {
- $style = 'BI';
- }
- if (isset($this->_fonts[$family . $style])) {
- return $this->raiseError(sprintf('Font already added: %s %s', $family, $style));
- }
- if ($file == '') {
- $file = str_replace(' ', '', $family) . strtolower($style) . '.php';
- }
- include($file);
- if (!isset($name)) {
- return $this->raiseError('Could not include font definition file');
- }
- $i = count($this->_fonts) + 1;
- $this->_fonts[$family . $style] = array('i' => $i, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'enc' => $enc, 'file' => $file);
- if ($diff) {
- /* Search existing encodings. */
- $d = 0;
- $nb = count($this->_diffs);
- for ($i = 1; $i <= $nb; $i++) {
- if ($this->_diffs[$i] == $diff) {
- $d = $i;
- break;
- }
- }
- if ($d == 0) {
- $d = $nb + 1;
- $this->_diffs[$d] = $diff;
- }
- $this->_fonts[$family . $style]['diff'] = $d;
- }
- if ($file) {
- if ($type == 'TrueType') {
- $this->_font_files[$file] = array('length1' => $originalsize);
- } else {
- $this->_font_files[$file] = array('length1' => $size1, 'length2' => $size2);
- }
- }
- }
-
- /**
- * Sets the font used to print character strings.
- *
- * It is mandatory to call this method at least once before printing text
- * or the resulting document would not be valid. The font can be either a
- * standard one or a font added via the {@link addFont()} method. Standard
- * fonts use Windows encoding cp1252 (Western Europe).
- *
- * The method can be called before the first page is created and the font
- * is retained from page to page.
- *
- * If you just wish to change the current font size, it is simpler to call
- * {@link setFontSize()}.
- *
- * @param string $family Family font. It can be either a name defined by
- * {@link addFont()} or one of the standard families
- * (case insensitive):
- * - Courier (fixed-width)
- * - Helvetica or Arial (sans serif)
- * - Times (serif)
- * - Symbol (symbolic)
- * - ZapfDingbats (symbolic)
- * It is also possible to pass an empty string. In
- * that case, the current family is retained.
- * @param string $style Font style. Possible values are (case
- * insensitive):
- * - empty string: regular
- * - B: bold
- * - I: italic
- * - U: underline
- * or any combination. Bold and italic styles do not
- * apply to Symbol and ZapfDingbats.
- * @param integer $size Font size in points. The default value is the
- * current size. If no size has been specified since
- * the beginning of the document, the value taken
- * is 12.
- * @param boolean $force Force the setting of the font. Each new page will
- * require a new call to {@link setFont()} and
- * settings this to true will make sure that the
- * checks for same font calls will be skipped.
- *
- * @see addFont()
- * @see setFontSize()
- * @see cell()
- * @see multiCell()
- * @see write()
- */
- function setFont($family, $style = '', $size = null, $force = false)
- {
- $family = strtolower($family);
- if (empty($family)) {
- $family = $this->_font_family;
- }
- if ($family == 'arial') {
- /* Use helvetica instead of arial. */
- $family = 'helvetica';
- } elseif ($family == 'symbol' || $family == 'zapfdingbats') {
- /* These two fonts do not have styles available. */
- $style = '';
- }
-
- $style = strtoupper($style);
-
- /* Underline is handled separately, if specified in the style var
- * remove it from the style and set the underline flag. */
- if (strpos($style, 'U') !== false) {
- $this->_underline = true;
- $style = str_replace('U', '', $style);
- } else {
- $this->_underline = false;
- }
-
- if ($style == 'IB') {
- $style = 'BI';
- }
-
- /* If no size specified, use current size. */
- if (is_null($size)) {
- $size = $this->_font_size_pt;
- }
-
- /* If font requested is already the current font and no force setting
- * of the font is requested (eg. when adding a new page) don't bother
- * with the rest of the function and simply return. */
- if ($this->_font_family == $family && $this->_font_style == $style &&
- $this->_font_size_pt == $size && !$force) {
- return;
- }
-
- /* Set the font key. */
- $fontkey = $family . $style;
-
- /* Test if already cached. */
- if (!isset($this->_fonts[$fontkey])) {
- /* Get the character width definition file. */
- $font_widths = File_PDF::_getFontFile($fontkey);
- if (is_a($font_widths, 'PEAR_Error')) {
- return $font_widths;
- }
-
- $i = count($this->_fonts) + 1;
- $this->_fonts[$fontkey] = array(
- 'i' => $i,
- 'type' => 'core',
- 'name' => $this->_core_fonts[$fontkey],
- 'up' => -100,
- 'ut' => 50,
- 'cw' => $font_widths[$fontkey]);
- }
-
- /* Store font information as current font. */
- $this->_font_family = $family;
- $this->_font_style = $style;
- $this->_font_size_pt = $size;
- $this->_font_size = $size / $this->_scale;
- $this->_current_font = &$this->_fonts[$fontkey];
-
- /* Output font information if at least one page has been defined. */
- if ($this->_page > 0) {
- $this->_out(sprintf('BT /F%d %.2' . FILE_PDF_FLOAT . ' Tf ET', $this->_current_font['i'], $this->_font_size_pt));
- }
- }
-
- /**
- * Defines the size of the current font.
- *
- * @param float $size The size (in points).
- *
- * @see setFont()
- */
- function setFontSize($size)
- {
- /* If the font size is already the current font size, just return. */
- if ($this->_font_size_pt == $size) {
- return;
- }
- /* Set the current font size, both in points and scaled to user
- * units. */
- $this->_font_size_pt = $size;
- $this->_font_size = $size / $this->_scale;
-
- /* Output font information if at least one page has been defined. */
- if ($this->_page > 0) {
- $this->_out(sprintf('BT /F%d %.2' . FILE_PDF_FLOAT . ' Tf ET', $this->_current_font['i'], $this->_font_size_pt));
- }
- }
-
- /**
- * Defines the style of the current font.
- *
- * @param string $style The font style.
- *
- * @since File_PDF 0.2.0
- * @since Horde 3.2
- * @see setFont()
- */
- function setFontStyle($style)
- {
- return $this->setFont($this->_font_family, $style);
- }
-
- /**
- * Creates a new internal link and returns its identifier.
- *
- * An internal link is a clickable area which directs to another place
- * within the document.
- *
- * The identifier can then be passed to {@link cell()}, {@link()} write,
- * {@link image()} or {@link link()}. The destination is defined with
- * {@link setLink()}.
- *
- * @see cell()
- * @see write()
- * @see image()
- * @see link()
- * @see setLink()
- */
- function addLink()
- {
- $n = count($this->_links) + 1;
- $this->_links[$n] = array(0, 0);
- return $n;
- }
-
- /**
- * Defines the page and position a link points to.
- *
- * @param integer $link The link identifier returned by {@link addLink()}.
- * @param float $y Ordinate of target position; -1 indicates the
- * current position. The default value is 0 (top of
- * page).
- * @param integer $page Number of target page; -1 indicates the current
- * page.
- *
- * @see addLink()
- */
- function setLink($link, $y = 0, $page = -1)
- {
- if ($y == -1) {
- $y = $this->y;
- }
- if ($page == -1) {
- $page = $this->_page;
- }
- $this->_links[$link] = array($page, $y);
- }
-
- /**
- * Puts a link on a rectangular area of the page.
- *
- * Text or image links are generally put via {@link cell()}, {@link
- * write()} or {@link image()}, but this method can be useful for instance
- * to define a clickable area inside an image.
- *
- * All coordinates can be negative to provide values from the right or
- * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
- *
- * @param float $x Abscissa of the upper-left corner of the
- * rectangle.
- * @param float $y Ordinate of the upper-left corner of the
- * rectangle.
- * @param float $width Width of the rectangle.
- * @param float $height Height of the rectangle.
- * @param mixed $link URL or identifier returned by {@link addLink()}.
- *
- * @see addLink()
- * @see cell()
- * @see write()
- * @see image()
- */
- function link($x, $y, $width, $height, $link)
- {
- if ($x < 0) {
- $x += $this->w;
- }
- if ($y < 0) {
- $y += $this->h;
- }
-
- /* Set up the coordinates with correct scaling in pt. */
- $x = $this->_toPt($x);
- $y = $this->hPt - $this->_toPt($y);
- $width = $this->_toPt($width);
- $height = $this->_toPt($height);
-
- /* Save link to page links array. */
- $this->_link($x, $y, $width, $height, $link);
- }
-
- /**
- * Prints a character string.
- *
- * The origin is on the left of the first character, on the baseline. This
- * method allows to place a string precisely on the page, but it is
- * usually easier to use {@link cell()}, {@link multiCell()} or {@link
- * write()} which are the standard methods to print text.
- *
- * All coordinates can be negative to provide values from the right or
- * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
- *
- * @param float $x Abscissa of the origin.
- * @param float $y Ordinate of the origin.
- * @param string $text String to print.
- *
- * @see setFont()
- * @see cell()
- * @see multiCell()
- * @see write()
- */
- function text($x, $y, $text)
- {
- if ($x < 0) {
- $x += $this->w;
- }
- if ($y < 0) {
- $y += $this->h;
- }
-
- /* Scale coordinates into points and set correct Y position. */
- $x = $this->_toPt($x);
- $y = $this->hPt - $this->_toPt($y);
-
- /* Escape any potentially harmful characters. */
- $text = $this->_escape($text);
-
- $out = sprintf('BT %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' Td (%s) Tj ET', $x, $y, $text);
- if ($this->_underline && $text != '') {
- $out .= ' ' . $this->_doUnderline($x, $y, $text);
- }
- if ($this->_color_flag) {
- $out = sprintf('q %s %s Q', $this->_text_color, $out);
- }
- $this->_out($out);
- }
-
- /**
- * Whenever a page break condition is met, the method is called, and the
- * break is issued or not depending on the returned value. The default
- * implementation returns a value according to the mode selected by
- * {@link setAutoPageBreak()}.
- * This method is called automatically and should not be called directly
- * by the application.
- *
- * @return boolean
- *
- * @see setAutoPageBreak()
- */
- function acceptPageBreak()
- {
- return $this->_auto_page_break;
- }
-
- /**
- * Prints a cell (rectangular area) with optional borders, background
- * color and character string.
- *
- * The upper-left corner of the cell corresponds to the current
- * position. The text can be aligned or centered. After the call, the
- * current position moves to the right or to the next line. It is possible
- * to put a link on the text. If automatic page breaking is enabled and
- * the cell goes beyond the limit, a page break is done before outputting.
- *
- * @param float $width Cell width. If 0, the cell extends up to the right
- * margin.
- * @param float $height Cell height.
- * @param string $text String to print.
- * @param mixed $border Indicates if borders must be drawn around the
- * cell. The value can be either a number:
- * - 0: no border (default)
- * - 1: frame
- * or a string containing some or all of the
- * following characters (in any order):
- * - L: left
- * - T: top
- * - R: right
- * - B: bottom
- * @param integer $ln Indicates where the current position should go
- * after the call. Possible values are:
- * - 0: to the right (default)
- * - 1: to the beginning of the next line
- * - 2: below
- * Putting 1 is equivalent to putting 0 and calling
- * {@link newLine()} just after.
- * @param string $align Allows to center or align the text. Possible
- * values are:
- * - L or empty string: left (default)
- * - C: center
- * - R: right
- * @param integer $fill Indicates if the cell fill type. Possible values
- * are:
- * - 0: transparent (default)
- * - 1: painted
- * @param string $link URL or identifier returned by {@link addLink()}.
- *
- * @see setFont()
- * @see setDrawColor()
- * @see setFillColor()
- * @see setLineWidth()
- * @see addLink()
- * @see newLine()
- * @see multiCell()
- * @see write()
- * @see setAutoPageBreak()
- */
- function cell($width, $height = 0, $text = '', $border = 0, $ln = 0,
- $align = '', $fill = 0, $link = '')
- {
- $k = $this->_scale;
- if ($this->y + $height > $this->_page_break_trigger &&
- !$this->_in_footer && $this->acceptPageBreak()) {
- $x = $this->x;
- $ws = $this->_word_spacing;
- if ($ws > 0) {
- $this->_word_spacing = 0;
- $this->_out('0 Tw');
- }
- $result = $this->addPage($this->_current_orientation);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- $this->x = $x;
- if ($ws > 0) {
- $this->_word_spacing = $ws;
- $this->_out(sprintf('%.3' . FILE_PDF_FLOAT . ' Tw', $ws * $k));
- }
- }
- if ($width == 0) {
- $width = $this->w - $this->_right_margin - $this->x;
- }
- $s = '';
- if ($fill == 1 || $border == 1) {
- if ($fill == 1) {
- $op = ($border == 1) ? 'B' : 'f';
- } else {
- $op = 'S';
- }
- $s = sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' re %s ', $this->x * $k, ($this->h - $this->y) * $k, $width * $k, -$height * $k, $op);
- }
- if (is_string($border)) {
- if (strpos($border, 'L') !== false) {
- $s .= sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' m %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' l S ', $this->x * $k, ($this->h - $this->y) * $k, $this->x * $k, ($this->h - ($this->y + $height)) * $k);
- }
- if (strpos($border, 'T') !== false) {
- $s .= sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' m %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' l S ', $this->x * $k, ($this->h - $this->y) * $k, ($this->x + $width) * $k, ($this->h - $this->y) * $k);
- }
- if (strpos($border, 'R') !== false) {
- $s .= sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' m %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' l S ', ($this->x + $width) * $k, ($this->h - $this->y) * $k, ($this->x + $width) * $k, ($this->h - ($this->y + $height)) * $k);
- }
- if (strpos($border, 'B') !== false) {
- $s .= sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' m %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' l S ', $this->x * $k, ($this->h - ($this->y + $height)) * $k, ($this->x + $width) * $k, ($this->h - ($this->y + $height)) * $k);
- }
- }
- if ($text != '') {
- if ($align == 'R') {
- $dx = $width - $this->_cell_margin - $this->getStringWidth($text);
- } elseif ($align == 'C') {
- $dx = ($width - $this->getStringWidth($text)) / 2;
- } else {
- $dx = $this->_cell_margin;
- }
- if ($this->_color_flag) {
- $s .= 'q ' . $this->_text_color . ' ';
- }
- $text = str_replace(')', '\\)', str_replace('(', '\\(', str_replace('\\', '\\\\', $text)));
- $test2 = ((.5 * $height) + (.3 * $this->_font_size));
- $test1 = $this->fhPt - (($this->y + $test2) * $k);
- $x = ($this->x + $dx) * $k;
- $y = ($this->h - ($this->y + .5 * $height + .3 * $this->_font_size)) * $k;
- $s .= sprintf('BT %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' Td (%s) Tj ET', $x, $y, $text);
- if ($this->_underline) {
- $s .= ' ' . $this->_doUnderline($x, $y, $text);
- }
- if ($this->_color_flag) {
- $s .= ' Q';
- }
- if ($link) {
- $this->link($this->x + $dx, $this->y + .5 * $height- .5 * $this->_font_size, $this->getStringWidth($text), $this->_font_size, $link);
- }
- }
- if ($s) {
- $this->_out($s);
- }
- $this->_last_height = $height;
- if ($ln > 0) {
- /* Go to next line. */
- $this->y += $height;
- if ($ln == 1) {
- $this->x = $this->_left_margin;
- }
- } else {
- $this->x += $width;
- }
- }
-
- /**
- * This method allows printing text with line breaks.
- *
- * They can be automatic (as soon as the text reaches the right border of
- * the cell) or explicit (via the \n character). As many cells as
- * necessary are output, one below the other. Text can be aligned,
- * centered or justified. The cell block can be framed and the background
- * painted.
- *
- * @param float $width Width of cells. If 0, they extend up to the right
- * margin of the page.
- * @param float $height Height of cells.
- * @param string $text String to print.
- * @param mixed $border Indicates if borders must be drawn around the cell
- * block. The value can be either a number:
- * - 0: no border (default)
- * - 1: frame
- * or a string containing some or all of the
- * following characters (in any order):
- * - L: left
- * - T: top
- * - R: right
- * - B: bottom
- * @param string $align Sets the text alignment. Possible values are:
- * - L: left alignment
- * - C: center
- * - R: right alignment
- * - J: justification (default value)
- * @param integer $fill Indicates if the cell background must:
- * - 0: transparent (default)
- * - 1: painted
- *
- * @see setFont()
- * @see setDrawColor()
- * @see setFillColor()
- * @see setLineWidth()
- * @see cell()
- * @see write()
- * @see setAutoPageBreak()
- */
- function multiCell($width, $height, $text, $border = 0, $align = 'J',
- $fill = 0)
- {
- $cw = &$this->_current_font['cw'];
- if ($width == 0) {
- $width = $this->w - $this->_right_margin - $this->x;
- }
- $wmax = ($width-2 * $this->_cell_margin) * 1000 / $this->_font_size;
- $s = str_replace("\r", '', $text);
- $nb = strlen($s);
- if ($nb > 0 && $s[$nb-1] == "\n") {
- $nb--;
- }
- $b = 0;
- if ($border) {
- if ($border == 1) {
- $border = 'LTRB';
- $b = 'LRT';
- $b2 = 'LR';
- } else {
- $b2 = '';
- if (strpos($border, 'L') !== false) {
- $b2 .= 'L';
- }
- if (strpos($border, 'R') !== false) {
- $b2 .= 'R';
- }
- $b = (strpos($border, 'T') !== false) ? $b2 . 'T' : $b2;
- }
- }
- $sep = -1;
- $i = 0;
- $j = 0;
- $l = 0;
- $ns = 0;
- $nl = 1;
- while ($i < $nb) {
- /* Get next character. */
- $c = $s[$i];
- if ($c == "\n") {
- /* Explicit line break. */
- if ($this->_word_spacing > 0) {
- $this->_word_spacing = 0;
- $this->_out('0 Tw');
- }
- $result = $this->cell($width, $height, substr($s, $j, $i-$j),
- $b, 2, $align, $fill);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- $i++;
- $sep = -1;
- $j = $i;
- $l = 0;
- $ns = 0;
- $nl++;
- if ($border && $nl == 2) {
- $b = $b2;
- }
- continue;
- }
- if ($c == ' ') {
- $sep = $i;
- $ls = $l;
- $ns++;
- }
- $l += $cw[$c];
- if ($l > $wmax) {
- /* Automatic line break. */
- if ($sep == -1) {
- if ($i == $j) {
- $i++;
- }
- if ($this->_word_spacing > 0) {
- $this->_word_spacing = 0;
- $this->_out('0 Tw');
- }
- $result = $this->cell($width, $height,
- substr($s, $j, $i - $j), $b, 2,
- $align, $fill);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- } else {
- if ($align == 'J') {
- $this->_word_spacing = ($ns>1)
- ? ($wmax - $ls) / 1000 * $this->_font_size / ($ns - 1)
- : 0;
- $this->_out(sprintf('%.3' . FILE_PDF_FLOAT . ' Tw',
- $this->_word_spacing * $this->_scale));
- }
- $result = $this->cell($width, $height,
- substr($s, $j, $sep - $j),
- $b, 2, $align, $fill);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- $i = $sep + 1;
- }
- $sep = -1;
- $j = $i;
- $l = 0;
- $ns = 0;
- $nl++;
- if ($border && $nl == 2) {
- $b = $b2;
- }
- } else {
- $i++;
- }
- }
- /* Last chunk. */
- if ($this->_word_spacing > 0) {
- $this->_word_spacing = 0;
- $this->_out('0 Tw');
- }
- if ($border && strpos($border, 'B') !== false) {
- $b .= 'B';
- }
- $result = $this->cell($width, $height, substr($s, $j, $i), $b, 2,
- $align, $fill);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- $this->x = $this->_left_margin;
- }
-
- /**
- * This method prints text from the current position.
- *
- * When the right margin is reached (or the \n character is met) a line
- * break occurs and text continues from the left margin. Upon method exit,
- * the current position is left just at the end of the text.
- *
- * It is possible to put a link on the text.
- *
- * Example:
- * <code>
- * // Begin with regular font
- * $pdf->setFont('Arial', '', 14);
- * $pdf->write(5, 'Visit ');
- * // Then put a blue underlined link
- * $pdf->setTextColor(0, 0, 255);
- * $pdf->setFont('', 'U');
- * $pdf->write(5, 'www.fpdf.org', 'http://www.fpdf.org');
- * </code>
- *
- * @param float $height Line height.
- * @param string $text String to print.
- * @param mixed $link URL or identifier returned by {@link addLink()}.
- *
- * @see setFont()
- * @see addLink()
- * @see multiCell()
- * @see setAutoPageBreak()
- */
- function write($height, $text, $link = '')
- {
- $cw = &$this->_current_font['cw'];
- $width = $this->w - $this->_right_margin - $this->x;
- $wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
- $s = str_replace("\r", '', $text);
- $nb = strlen($s);
- $sep = -1;
- $i = 0;
- $j = 0;
- $l = 0;
- $nl = 1;
- while ($i < $nb) {
- /* Get next character. */
- $c = $s{$i};
- if ($c == "\n") {
- /* Explicit line break. */
- $result = $this->cell($width, $height, substr($s, $j, $i - $j),
- 0, 2, '', 0, $link);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- $i++;
- $sep = -1;
- $j = $i;
- $l = 0;
- if ($nl == 1) {
- $this->x = $this->_left_margin;
- $width = $this->w - $this->_right_margin - $this->x;
- $wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
- }
- $nl++;
- continue;
- }
- if ($c == ' ') {
- $sep = $i;
- $ls = $l;
- }
- $l += (isset($cw[$c]) ? $cw[$c] : 0);
- if ($l > $wmax) {
- /* Automatic line break. */
- if ($sep == -1) {
- if ($this->x > $this->_left_margin) {
- /* Move to next line. */
- $this->x = $this->_left_margin;
- $this->y += $height;
- $width = $this->w - $this->_right_margin - $this->x;
- $wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
- $i++;
- $nl++;
- continue;
- }
- if ($i == $j) {
- $i++;
- }
- $result = $this->cell($width, $height,
- substr($s, $j, $i - $j),
- 0, 2, '', 0, $link);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- } else {
- $result = $this->cell($width, $height,
- substr($s, $j, $sep - $j),
- 0, 2, '', 0, $link);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- $i = $sep + 1;
- }
- $sep = -1;
- $j = $i;
- $l = 0;
- if ($nl == 1) {
- $this->x = $this->_left_margin;
- $width = $this->w - $this->_right_margin - $this->x;
- $wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
- }
- $nl++;
- } else {
- $i++;
- }
- }
- /* Last chunk. */
- if ($i != $j) {
- $result = $this->cell($l / 1000 * $this->_font_size, $height,
- substr($s, $j, $i), 0, 0, '', 0, $link);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- }
- }
-
- /**
- * Writes text at an angle.
- *
- * All coordinates can be negative to provide values from the right or
- * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
- *
- * @param integer $x X coordinate.
- * @param integer $y Y coordinate.
- * @param string $text Text to write.
- * @param float $text_angle Angle to rotate (Eg. 90 = bottom to top).
- * @param float $font_angle Rotate characters as well as text.
- *
- * @see setFont()
- */
- function writeRotated($x, $y, $text, $text_angle, $font_angle = 0)
- {
- if ($x < 0) {
- $x += $this->w;
- }
- if ($y < 0) {
- $y += $this->h;
- }
-
- /* Escape text. */
- $text = $this->_escape($text);
-
- $font_angle += 90 + $text_angle;
- $text_angle *= M_PI / 180;
- $font_angle *= M_PI / 180;
-
- $text_dx = cos($text_angle);
- $text_dy = sin($text_angle);
- $font_dx = cos($font_angle);
- $font_dy = sin($font_angle);
-
- $s= sprintf('BT %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' Tm (%s) Tj ET',
- $text_dx, $text_dy, $font_dx, $font_dy,
- $x * $this->_scale, ($this->h-$y) * $this->_scale, $text);
-
- if ($this->_draw_color) {
- $s = 'q ' . $this->_draw_color . ' ' . $s . ' Q';
- }
- $this->_out($s);
- }
-
- /**
- * Prints an image in the page.
- *
- * The upper-left corner and at least one of the dimensions must be
- * specified; the height or the width can be calculated automatically in
- * order to keep the image proportions. Supported formats are JPEG and
- * PNG.
- *
- * All coordinates can be negative to provide values from the right or
- * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
- *
- * For JPEG, all flavors are allowed:
- * - gray scales
- * - true colors (24 bits)
- * - CMYK (32 bits)
- *
- * For PNG, are allowed:
- * - gray scales on at most 8 bits (256 levels)
- * - indexed colors
- * - true colors (24 bits)
- * but are not supported:
- * - Interlacing
- * - Alpha channel
- *
- * If a transparent color is defined, it will be taken into account (but
- * will be only interpreted by Acrobat 4 and above).
- * The format can be specified explicitly or inferred from the file
- * extension.
- * It is possible to put a link on the image.
- *
- * Remark: if an image is used several times, only one copy will be
- * embedded in the file.
- *
- * @param string $file Name of the file containing the image.
- * @param float $x Abscissa of the upper-left corner.
- * @param float $y Ordinate of the upper-left corner.
- * @param float $width Width of the image in the page. If equal to zero,
- * it is automatically calculated to keep the
- * original proportions.
- * @param float $height Height of the image in the page. If not specified
- * or equal to zero, it is automatically calculated
- * to keep the original proportions.
- * @param string $type Image format. Possible values are (case
- * insensitive): JPG, JPEG, PNG. If not specified,
- * the type is inferred from the file extension.
- * @param mixed $link URL or identifier returned by {@link addLink()}.
- *
- * @see addLink()
- */
- function image($file, $x, $y, $width = 0, $height = 0, $type = '',
- $link = '')
- {
- if ($x < 0) {
- $x += $this->w;
- }
- if ($y < 0) {
- $y += $this->h;
- }
-
- if (!isset($this->_images[$file])) {
- /* First use of image, get some file info. */
- if ($type == '') {
- $pos = strrpos($file, '.');
- if ($pos === false) {
- return $this->raiseError(sprintf('Image file has no extension and no type was specified: %s', $file));
- }
- $type = substr($file, $pos + 1);
- }
- $type = strtolower($type);
- $mqr = get_magic_quotes_runtime();
- set_magic_quotes_runtime(0);
- if ($type == 'jpg' || $type == 'jpeg') {
- $info = $this->_parseJPG($file);
- } elseif ($type == 'png') {
- $info = $this->_parsePNG($file);
- } else {
- return $this->raiseError(sprintf('Unsupported image file type: %s', $type));
- }
- if (is_a($info, 'PEAR_Error')) {
- return $info;
- }
- set_magic_quotes_runtime($mqr);
- $info['i'] = count($this->_images) + 1;
- $this->_images[$file] = $info;
- } else {
- $info = $this->_images[$file];
- }
-
- /* Make sure all vars are converted to pt scale. */
- $x = $this->_toPt($x);
- $y = $this->hPt - $this->_toPt($y);
- $width = $this->_toPt($width);
- $height = $this->_toPt($height);
-
- /* If not specified do automatic width and height calculations. */
- if (empty($width) && empty($height)) {
- $width = $info['w'];
- $height = $info['h'];
- } elseif (empty($width)) {
- $width = $height * $info['w'] / $info['h'];
- } elseif (empty($height)) {
- $height = $width * $info['h'] / $info['w'];
- }
-
- $this->_out(sprintf('q %.2' . FILE_PDF_FLOAT . ' 0 0 %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' cm /I%d Do Q', $width, $height, $x, $y - $height, $info['i']));
-
- /* Set any link if requested. */
- if ($link) {
- $this->_link($x, $y, $width, $height, $link);
- }
- }
-
- /**
- * Performs a line break.
- *
- * The current abscissa goes back to the left margin and the ordinate
- * increases by the amount passed in parameter.
- *
- * @param float $height The height of the break. By default, the value
- * equals the height of the last printed cell.
- *
- * @see cell()
- */
- function newLine($height = '')
- {
- $this->x = $this->_left_margin;
- if (is_string($height)) {
- $this->y += $this->_last_height;
- } else {
- $this->y += $height;
- }
- }
-
- /**
- * Returns the abscissa of the current position in user units.
- *
- * @return float
- *
- * @see setX()
- * @see getY()
- * @see setY()
- */
- function getX()
- {
- return $this->x;
- }
-
- /**
- * Defines the abscissa of the current position.
- *
- * If the passed value is negative, it is relative to the right of the
- * page.
- *
- * @param float $x The value of the abscissa.
- *
- * @see getX()
- * @see getY()
- * @see setY()
- * @see setXY()
- */
- function setX($x)
- {
- if ($x >= 0) {
- /* Absolute value. */
- $this->x = $x;
- } else {
- /* Negative, so relative to right edge of the page. */
- $this->x = $this->w + $x;
- }
- }
-
- /**
- * Returns the ordinate of the current position in user units.
- *
- * @return float
- *
- * @see setY()
- * @see getX()
- * @see setX()
- */
- function getY()
- {
- return $this->y;
- }
-
- /**
- * Defines the ordinate of the current position.
- *
- * If the passed value is negative, it is relative to the bottom of the
- * page.
- *
- * @param float $y The value of the ordinate.
- *
- * @see getX()
- * @see getY()
- * @see setY()
- * @see setXY()
- */
- function setY($y)
- {
- if ($y >= 0) {
- /* Absolute value. */
- $this->y = $y;
- } else {
- /* Negative, so relative to bottom edge of the page. */
- $this->y = $this->h + $y;
- }
- }
-
- /**
- * Defines the abscissa and ordinate of the current position.
- *
- * If the passed values are negative, they are relative respectively to
- * the right and bottom of the page.
- *
- * @param float $x The value of the abscissa.
- * @param float $y The value of the ordinate.
- *
- * @see setX()
- * @see setY()
- */
- function setXY($x, $y)
- {
- $this->setY($y);
- $this->setX($x);
- }
-
- /**
- * Returns the current buffer content and resets the buffer.
- *
- * Use this method when creating large files to avoid memory problems.
- * This method doesn't work in combination with the output() or save()
- * methods, use getOutput() at the end. Calling this method doubles the
- * memory usage during the call.
- *
- * @since File_PDF 0.2.0
- * @since Horde 3.2
- * @see getOutput()
- */
- function flush()
- {
- // Make sure we have the file header.
- $this->_beginDoc();
-
- $buffer = $this->_buffer;
- $this->_buffer = '';
- $this->_flushed = true;
- $this->_buflen += strlen($buffer);
-
- return $buffer;
- }
-
- /**
- * Returns the raw PDF file.
- *
- * @see output()
- * @see flush()
- */
- function getOutput()
- {
- /* Check whether file has been closed. */
- if ($this->_state < 3) {
- $result = $this->close();
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- }
-
- return $this->_buffer;
- }
-
- /**
- * Sends the buffered data to the browser.
- *
- * @param string $filename The filename for the output file.
- * @param boolean $inline True if inline, false if attachment.
- */
- function output($filename = 'unknown.pdf', $inline = false)
- {
- /* Check whether the buffer has been flushed already. */
- if ($this->_flushed) {
- return $this->raiseError('The buffer has been flushed already, don\'t use output() in combination with flush().');
- }
-
- /* Check whether file has been closed. */
- if ($this->_state < 3) {
- $result = $this->close();
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- }
-
- /* Check if headers have been sent. */
- if (headers_sent()) {
- return $this->raiseError('Unable to send PDF file, some data has already been output to browser');
- }
-
- /* If HTTP_Download is not available return a PEAR_Error. */
- if (!include_once 'HTTP/Download.php') {
- return $this->raiseError('Missing PEAR package HTTP_Download');
- }
-
- /* Params for the output. */
- $disposition = $inline ? HTTP_DOWNLOAD_INLINE : HTTP_DOWNLOAD_ATTACHMENT;
- $params = array('data' => $this->_buffer,
- 'contenttype' => 'application/pdf',
- 'contentdisposition' => array($disposition, $filename));
- /* Output the file. */
- return HTTP_Download::staticSend($params);
- }
-
- /**
- * Saves the PDF file on the filesystem.
- *
- * @param string $filename The filename for the output file.
- */
- function save($filename = 'unknown.pdf')
- {
- /* Check whether the buffer has been flushed already. */
- if ($this->_flushed) {
- return $this->raiseError('The buffer has been flushed already, don\'t use save() in combination with flush().');
- }
-
- /* Check whether file has been closed. */
- if ($this->_state < 3) {
- $result = $this->close();
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- }
-
- $f = fopen($filename, 'wb');
- if (!$f) {
- return $this->raiseError(sprintf('Unable to save PDF file: %s', $filename));
- }
- fwrite($f, $this->_buffer, strlen($this->_buffer));
- fclose($f);
- }
-
- function _toPt($val)
- {
- return $val * $this->_scale;
- }
-
- function _getFontFile($fontkey, $path = '')
- {
- static $font_widths = array();
-
- if (!isset($font_widths[$fontkey])) {
- if (!empty($path)) {
- $file = $path . strtolower($fontkey) . '.php';
- } else {
- $file = 'File/PDF/fonts/' . strtolower($fontkey) . '.php';
- }
- include $file;
- if (!isset($font_widths[$fontkey])) {
- return $this->raiseError(sprintf('Could not include font metric file: %s', $file));
- }
- }
-
- return $font_widths;
- }
-
- function _link($x, $y, $width, $height, $link)
- {
- /* Save link to page links array. */
- $this->_page_links[$this->_page][] = array($x, $y, $width, $height, $link);
- }
-
- function _beginDoc()
- {
- /* Start document, but only if not yet started. */
- if ($this->_state < 1) {
- $this->_state = 1;
- $this->_out('%PDF-1.3');
- }
- }
-
- function _putPages()
- {
- $nb = $this->_page;
- if (!empty($this->_alias_nb_pages)) {
- /* Replace number of pages. */
- for ($n = 1; $n <= $nb; $n++) {
- $this->_pages[$n] = str_replace($this->_alias_nb_pages, $nb, $this->_pages[$n]);
- }
- }
- if ($this->_default_orientation == 'P') {
- $wPt = $this->fwPt;
- $hPt = $this->fhPt;
- } else {
- $wPt = $this->fhPt;
- $hPt = $this->fwPt;
- }
- $filter = ($this->_compress) ? '/Filter /FlateDecode ' : '';
- for ($n = 1; $n <= $nb; $n++) {
- /* Page */
- $this->_newobj();
- $this->_out('<</Type /Page');
- $this->_out('/Parent 1 0 R');
- if (isset($this->_orientation_changes[$n])) {
- $this->_out(sprintf('/MediaBox [0 0 %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ']', $hPt, $wPt));
- }
- $this->_out('/Resources 2 0 R');
- if (isset($this->_page_links[$n])) {
- /* Links */
- $annots = '/Annots [';
- foreach ($this->_page_links[$n] as $pl) {
- $rect = sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . '', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3]);
- $annots .= '<</Type /Annot /Subtype /Link /Rect [' . $rect . '] /Border [0 0 0] ';
- if (is_string($pl[4])) {
- $annots .= '/A <</S /URI /URI ' . $this->_textString($pl[4]) . '>>>>';
- } else {
- $l = $this->_links[$pl[4]];
- $height = isset($this->_orientation_changes[$l[0]]) ? $wPt : $hPt;
- $annots .= sprintf('/Dest [%d 0 R /XYZ 0 %.2' . FILE_PDF_FLOAT . ' null]>>', 1 + 2 * $l[0], $height - $l[1] * $this->_scale);
- }
- }
- $this->_out($annots . ']');
- }
- $this->_out('/Contents ' . ($this->_n + 1) . ' 0 R>>');
- $this->_out('endobj');
- /* Page content */
- $p = ($this->_compress) ? gzcompress($this->_pages[$n]) : $this->_pages[$n];
- $this->_newobj();
- $this->_out('<<' . $filter . '/Length ' . strlen($p) . '>>');
- $this->_putStream($p);
- $this->_out('endobj');
- }
- /* Pages root */
- $this->_offsets[1] = $this->_buflen + strlen($this->_buffer);
- $this->_out('1 0 obj');
- $this->_out('<</Type /Pages');
- $kids = '/Kids [';
- for ($i = 0; $i < $nb; $i++) {
- $kids .= (3 + 2 * $i) . ' 0 R ';
- }
- $this->_out($kids . ']');
- $this->_out('/Count ' . $nb);
- $this->_out(sprintf('/MediaBox [0 0 %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ']', $wPt, $hPt));
- $this->_out('>>');
- $this->_out('endobj');
- }
-
- function _putFonts()
- {
- $nf = $this->_n;
- foreach ($this->_diffs as $diff) {
- /* Encodings */
- $this->_newobj();
- $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [' . $diff . ']>>');
- $this->_out('endobj');
- }
- $mqr = get_magic_quotes_runtime();
- set_magic_quotes_runtime(0);
- foreach ($this->_font_files as $file => $info) {
- /* Font file embedding. */
- $this->_newobj();
- $this->_font_files[$file]['n'] = $this->_n;
- $size = filesize($file);
- if (!$size) {
- return $this->raiseError('Font file not found');
- }
- $this->_out('<</Length ' . $size);
- if (substr($file, -2) == '.z') {
- $this->_out('/Filter /FlateDecode');
- }
- $this->_out('/Length1 ' . $info['length1']);
- if (isset($info['length2'])) {
- $this->_out('/Length2 ' . $info['length2'] . ' /Length3 0');
- }
- $this->_out('>>');
- $f = fopen($file, 'rb');
- $this->_putStream(fread($f, $size));
- fclose($f);
- $this->_out('endobj');
- }
- set_magic_quotes_runtime($mqr);
- foreach ($this->_fonts as $k => $font) {
- /* Font objects */
- $this->_newobj();
- $this->_fonts[$k]['n'] = $this->_n;
- $name = $font['name'];
- $this->_out('<</Type /Font');
- $this->_out('/BaseFont /' . $name);
- if ($font['type'] == 'core') {
- /* Standard font. */
- $this->_out('/Subtype /Type1');
- if ($name != 'Symbol' && $name != 'ZapfDingbats') {
- $this->_out('/Encoding /WinAnsiEncoding');
- }
- } else {
- /* Additional font. */
- $this->_out('/Subtype /' . $font['type']);
- $this->_out('/FirstChar 32');
- $this->_out('/LastChar 255');
- $this->_out('/Widths ' . ($this->_n + 1) . ' 0 R');
- $this->_out('/FontDescriptor ' . ($this->_n + 2) . ' 0 R');
- if ($font['enc']) {
- if (isset($font['diff'])) {
- $this->_out('/Encoding ' . ($nf + $font['diff']) . ' 0 R');
- } else {
- $this->_out('/Encoding /WinAnsiEncoding');
- }
- }
- }
- $this->_out('>>');
- $this->_out('endobj');
- if ($font['type'] != 'core') {
- /* Widths. */
- $this->_newobj();
- $cw = &$font['cw'];
- $s = '[';
- for ($i = 32; $i <= 255; $i++) {
- $s .= $cw[chr($i)] . ' ';
- }
- $this->_out($s . ']');
- $this->_out('endobj');
- /* Descriptor. */
- $this->_newobj();
- $s = '<</Type /FontDescriptor /FontName /' . $name;
- foreach ($font['desc'] as $k => $v) {
- $s .= ' /' . $k . ' ' . $v;
- }
- $file = $font['file'];
- if ($file) {
- $s .= ' /FontFile' . ($font['type'] == 'Type1' ? '' : '2') . ' ' . $this->_font_files[$file]['n'] . ' 0 R';
- }
- $this->_out($s . '>>');
- $this->_out('endobj');
- }
- }
- }
-
- function _putImages()
- {
- $filter = ($this->_compress) ? '/Filter /FlateDecode ' : '';
- foreach ($this->_images as $file => $info) {
- $this->_newobj();
- $this->_images[$file]['n'] = $this->_n;
- $this->_out('<</Type /XObject');
- $this->_out('/Subtype /Image');
- $this->_out('/Width ' . $info['w']);
- $this->_out('/Height ' . $info['h']);
- if ($info['cs'] == 'Indexed') {
- $this->_out('/ColorSpace [/Indexed /DeviceRGB ' . (strlen($info['pal'])/3 - 1) . ' ' . ($this->_n + 1) . ' 0 R]');
- } else {
- $this->_out('/ColorSpace /' . $info['cs']);
- if ($info['cs'] == 'DeviceCMYK') {
- $this->_out('/Decode [1 0 1 0 1 0 1 0]');
- }
- }
- $this->_out('/BitsPerComponent ' . $info['bpc']);
- $this->_out('/Filter /' . $info['f']);
- if (isset($info['parms'])) {
- $this->_out($info['parms']);
- }
- if (isset($info['trns']) && is_array($info['trns'])) {
- $trns = '';
- $i_max = count($info['trns']);
- for ($i = 0; $i < $i_max; $i++) {
- $trns .= $info['trns'][$i] . ' ' . $info['trns'][$i] . ' ';
- }
- $this->_out('/Mask [' . $trns . ']');
- }
- $this->_out('/Length ' . strlen($info['data']) . '>>');
- $this->_putStream($info['data']);
- $this->_out('endobj');
-
- /* Palette. */
- if ($info['cs'] == 'Indexed') {
- $this->_newobj();
- $pal = ($this->_compress) ? gzcompress($info['pal']) : $info['pal'];
- $this->_out('<<' . $filter . '/Length ' . strlen($pal) . '>>');
- $this->_putStream($pal);
- $this->_out('endobj');
- }
- }
- }
-
- function _putResources()
- {
- $this->_putFonts();
- $this->_putImages();
- /* Resource dictionary */
- $this->_offsets[2] = $this->_buflen + strlen($this->_buffer);
- $this->_out('2 0 obj');
- $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
- $this->_out('/Font <<');
- foreach ($this->_fonts as $font) {
- $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
- }
- $this->_out('>>');
- if (count($this->_images)) {
- $this->_out('/XObject <<');
- foreach ($this->_images as $image) {
- $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
- }
- $this->_out('>>');
- }
- $this->_out('>>');
- $this->_out('endobj');
- }
-
- function _putInfo()
- {
- $this->_out('/Producer ' . $this->_textString('Horde PDF'));
- if (!empty($this->_info['title'])) {
- $this->_out('/Title ' . $this->_textString($this->_info['title']));
- }
- if (!empty($this->_info['subject'])) {
- $this->_out('/Subject ' . $this->_textString($this->_info['subject']));
- }
- if (!empty($this->_info['author'])) {
- $this->_out('/Author ' . $this->_textString($this->_info['author']));
- }
- if (!empty($this->keywords)) {
- $this->_out('/Keywords ' . $this->_textString($this->keywords));
- }
- if (!empty($this->creator)) {
- $this->_out('/Creator ' . $this->_textString($this->creator));
- }
- $this->_out('/CreationDate ' . $this->_textString('D:' . date('YmdHis')));
- }
-
- function _putCatalog()
- {
- $this->_out('/Type /Catalog');
- $this->_out('/Pages 1 0 R');
- if ($this->_zoom_mode == 'fullpage') {
- $this->_out('/OpenAction [3 0 R /Fit]');
- } elseif ($this->_zoom_mode == 'fullwidth') {
- $this->_out('/OpenAction [3 0 R /FitH null]');
- } elseif ($this->_zoom_mode == 'real') {
- $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
- } elseif (!is_string($this->_zoom_mode)) {
- $this->_out('/OpenAction [3 0 R /XYZ null null ' . ($this->_zoom_mode / 100) . ']');
- }
- if ($this->_layout_mode == 'single') {
- $this->_out('/PageLayout /SinglePage');
- } elseif ($this->_layout_mode == 'continuous') {
- $this->_out('/PageLayout /OneColumn');
- } elseif ($this->_layout_mode == 'two') {
- $this->_out('/PageLayout /TwoColumnLeft');
- }
- }
-
- function _putTrailer()
- {
- $this->_out('/Size ' . ($this->_n + 1));
- $this->_out('/Root ' . $this->_n . ' 0 R');
- $this->_out('/Info ' . ($this->_n - 1) . ' 0 R');
- }
-
- function _endDoc()
- {
- $this->_putPages();
- $this->_putResources();
- /* Info */
- $this->_newobj();
- $this->_out('<<');
- $this->_putInfo();
- $this->_out('>>');
- $this->_out('endobj');
- /* Catalog */
- $this->_newobj();
- $this->_out('<<');
- $this->_putCatalog();
- $this->_out('>>');
- $this->_out('endobj');
- /* Cross-ref */
- $o = $this->_buflen + strlen($this->_buffer);
- $this->_out('xref');
- $this->_out('0 ' . ($this->_n + 1));
- $this->_out('0000000000 65535 f ');
- for ($i = 1; $i <= $this->_n; $i++) {
- $this->_out(sprintf('%010d 00000 n ', $this->_offsets[$i]));
- }
- /* Trailer */
- $this->_out('trailer');
- $this->_out('<<');
- $this->_putTrailer();
- $this->_out('>>');
- $this->_out('startxref');
- $this->_out($o);
- $this->_out('%%EOF');
- $this->_state = 3;
- }
-
- function _beginPage($orientation)
- {
- $this->_page++;
- $this->_pages[$this->_page] = '';
- $this->_state = 2;
- $this->x = $this->_left_margin;
- $this->y = $this->_top_margin;
- $this->_last_height = 0;
- /* Page orientation */
- if (!$orientation) {
- $orientation = $this->_default_orientation;
- } else {
- $orientation = strtoupper($orientation[0]);
- if ($orientation != $this->_default_orientation) {
- $this->_orientation_changes[$this->_page] = true;
- }
- }
- if ($orientation != $this->_current_orientation) {
- /* Change orientation */
- if ($orientation == 'P') {
- $this->wPt = $this->fwPt;
- $this->hPt = $this->fhPt;
- $this->w = $this->fw;
- $this->h = $this->fh;
- } else {
- $this->wPt = $this->fhPt;
- $this->hPt = $this->fwPt;
- $this->w = $this->fh;
- $this->h = $this->fw;
- }
- $this->_page_break_trigger = $this->h - $this->_break_margin;
- $this->_current_orientation = $orientation;
- }
- }
-
- function _endPage()
- {
- /* End of page contents */
- $this->_state = 1;
- }
-
- function _newobj()
- {
- /* Begin a new object */
- $this->_n++;
- $this->_offsets[$this->_n] = $this->_buflen + strlen($this->_buffer);
- $this->_out($this->_n . ' 0 obj');
- }
-
- function _doUnderline($x, $y, $text)
- {
- /* Set the rectangle width according to text width. */
- $width = $this->getStringWidth($text, true);
-
- /* Set rectangle position and height, using underline position and
- * thickness settings scaled by the font size. */
- $y = $y + ($this->_current_font['up'] * $this->_font_size_pt / 1000);
- $height = -$this->_current_font['ut'] * $this->_font_size_pt / 1000;
-
- return sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' re f', $x, $y, $width, $height);
- }
-
- function _parseJPG($file)
- {
- /* Extract info from a JPEG file. */
- $img = @getimagesize($file);
- if (!$img) {
- return $this->raiseError(sprintf('Missing or incorrect image file: %s', $file));
- }
- if ($img[2] != 2) {
- return $this->raiseError(sprintf('Not a JPEG file: %s', $file));
- }
- if (!isset($img['channels']) || $img['channels'] == 3) {
- $colspace = 'DeviceRGB';
- } elseif ($img['channels'] == 4) {
- $colspace = 'DeviceCMYK';
- } else {
- $colspace = 'DeviceGray';
- }
- $bpc = isset($img['bits']) ? $img['bits'] : 8;
-
- /* Read whole file. */
- $f = fopen($file, 'rb');
- $data = fread($f, filesize($file));
- fclose($f);
-
- return array('w' => $img[0], 'h' => $img[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
- }
-
- function _parsePNG($file)
- {
- /* Extract info from a PNG file. */
- $f = fopen($file, 'rb');
- if (!$f) {
- return $this->raiseError(sprintf('Unable to open image file: %s', $file));
- }
-
- /* Check signature. */
- if (fread($f, 8) != chr(137) . 'PNG' . chr(13) . chr(10) . chr(26) . chr(10)) {
- return $this->raiseError(sprintf('Not a PNG file: %s', $file));
- }
-
- /* Read header chunk. */
- fread($f, 4);
- if (fread($f, 4) != 'IHDR') {
- return $this->raiseError(sprintf('Incorrect PNG file: %s', $file));
- }
- $width = $this->_freadInt($f);
- $height = $this->_freadInt($f);
- $bpc = ord(fread($f, 1));
- if ($bpc > 8) {
- return $this->raiseError(sprintf('16-bit depth not supported: %s', $file));
- }
- $ct = ord(fread($f, 1));
- if ($ct == 0) {
- $colspace = 'DeviceGray';
- } elseif ($ct == 2) {
- $colspace = 'DeviceRGB';
- } elseif ($ct == 3) {
- $colspace = 'Indexed';
- } else {
- return $this->raiseError(sprintf('Alpha channel not supported: %s', $file));
- }
- if (ord(fread($f, 1)) != 0) {
- return $this->raiseError(sprintf('Unknown compression method: %s', $file));
- }
- if (ord(fread($f, 1)) != 0) {
- return $this->raiseError(sprintf('Unknown filter method: %s', $file));
- }
- if (ord(fread($f, 1)) != 0) {
- return $this->raiseError(sprintf('Interlacing not supported: %s', $file));
- }
- fread($f, 4);
- $parms = '/DecodeParms <</Predictor 15 /Colors ' . ($ct == 2 ? 3 : 1) . ' /BitsPerComponent ' . $bpc . ' /Columns ' . $width . '>>';
- /* Scan chunks looking for palette, transparency and image data. */
- $pal = '';
- $trns = '';
- $data = '';
- do {
- $n = $this->_freadInt($f);
- $type = fread($f, 4);
- if ($type == 'PLTE') {
- /* Read palette */
- $pal = fread($f, $n);
- fread($f, 4);
- } elseif ($type == 'tRNS') {
- /* Read transparency info */
- $t = fread($f, $n);
- if ($ct == 0) {
- $trns = array(ord(substr($t, 1, 1)));
- } elseif ($ct == 2) {
- $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
- } else {
- $pos = strpos($t, chr(0));
- if (is_int($pos)) {
- $trns = array($pos);
- }
- }
- fread($f, 4);
- } elseif ($type == 'IDAT') {
- /* Read image data block */
- $data .= fread($f, $n);
- fread($f, 4);
- } elseif ($type == 'IEND') {
- break;
- } else {
- fread($f, $n + 4);
- }
- } while ($n);
-
- if ($colspace == 'Indexed' && empty($pal)) {
- return $this->raiseError(sprintf('Missing palette in: %s', $file));
- }
- fclose($f);
-
- return array('w' => $width, 'h' => $height, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
- }
-
- function _freadInt($f)
- {
- /* Read a 4-byte integer from file. */
- $i = ord(fread($f, 1)) << 24;
- $i += ord(fread($f, 1)) << 16;
- $i += ord(fread($f, 1)) << 8;
- $i += ord(fread($f, 1));
- return $i;
- }
-
- function _textString($s)
- {
- /* Format a text string */
- return '(' . $this->_escape($s) . ')';
- }
-
- function _escape($s)
- {
- /* Add \ before \, ( and ) */
- return str_replace(array('\\', ')', '('),
- array('\\\\', '\\)', '\\('),
- $s);
- }
-
- function _putStream($s)
- {
- $this->_out('stream');
- $this->_out($s);
- $this->_out('endstream');
- }
-
- function _out($s)
- {
- /* Add a line to the document. */
- if ($this->_state == 2) {
- $this->_pages[$this->_page] .= $s . "\n";
- } else {
- $this->_buffer .= $s . "\n";
- }
- }
-
- }
-