home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / File / PDF.php < prev    next >
Encoding:
PHP Script  |  2008-07-02  |  104.1 KB  |  3,179 lines

  1. <?php
  2. /**
  3.  * The File_PDF class provides a PHP-only implementation of a PDF library. No
  4.  * external libs or PHP extensions are required.
  5.  *
  6.  * Based on the FPDF class by Olivier Plathey (http://www.fpdf.org/).
  7.  *
  8.  * $Horde: framework/File_PDF/PDF.php,v 1.64 2008/02/26 23:18:55 jan Exp $
  9.  *
  10.  * Copyright 2001-2003 Olivier Plathey <olivier@fpdf.org>
  11.  * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
  12.  *
  13.  * See the enclosed file COPYING for license information (LGPL). If you
  14.  * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
  15.  *
  16.  * @author   Olivier Plathey <olivier@fpdf.org>
  17.  * @author   Marko Djukic <marko@oblo.com>
  18.  * @author   Jan Schneider <jan@horde.org>
  19.  * @package  File_PDF
  20.  * @category Fileformats
  21.  */
  22.  
  23. /**
  24.  * This hack works around Horde bug #4094
  25.  * (http://bugs.horde.org/ticket/?id=4094)
  26.  *
  27.  * Once this package does not need to support PHP < 4.3.10 anymore the
  28.  * following definiton can be removed and the ugly code can be removed
  29.  * using
  30.  *
  31.  * sed -i -e 's/\' \. FILE_PDF_FLOAT \. \'/F/g' PDF.php
  32.  */
  33. if (version_compare(PHP_VERSION, '4.3.10', '>=')) {
  34.     define('FILE_PDF_FLOAT', 'F');
  35. } else {
  36.     define('FILE_PDF_FLOAT', 'f');
  37. }
  38.  
  39. class File_PDF {
  40.  
  41.     /**
  42.      * Current page number.
  43.      *
  44.      * @var integer
  45.      */
  46.     var $_page = 0;
  47.  
  48.     /**
  49.      * Current object number.
  50.      *
  51.      * @var integer
  52.      */
  53.     var $_n = 2;
  54.  
  55.     /**
  56.      * Array of object offsets.
  57.      *
  58.      * @var array
  59.      */
  60.     var $_offsets = array();
  61.  
  62.     /**
  63.      * Buffer holding in-memory PDF.
  64.      *
  65.      * @var string
  66.      */
  67.     var $_buffer = '';
  68.  
  69.     /**
  70.      * Buffer length, including already flushed content.
  71.      *
  72.      * @var integer
  73.      */
  74.     var $_buflen = 0;
  75.  
  76.     /**
  77.      * Whether the buffer has been flushed already.
  78.      *
  79.      * @var boolean
  80.      */
  81.     var $_flushed = false;
  82.  
  83.     /**
  84.      * Array containing the pages.
  85.      *
  86.      * @var array
  87.      */
  88.     var $_pages = array();
  89.  
  90.     /**
  91.      * Current document state.<pre>
  92.      *   0 - initial state
  93.      *   1 - document opened
  94.      *   2 - page opened
  95.      *   3 - document closed
  96.      * </pre>
  97.      *
  98.      * @var integer
  99.      */
  100.     var $_state = 0;
  101.  
  102.     /**
  103.      * Flag indicating if PDF file is to be compressed or not.
  104.      *
  105.      * @var boolean
  106.      */
  107.     var $_compress;
  108.  
  109.     /**
  110.      * The default page orientation.
  111.      *
  112.      * @var string
  113.      */
  114.     var $_default_orientation;
  115.  
  116.     /**
  117.      * The current page orientation.
  118.      *
  119.      * @var string
  120.      */
  121.     var $_current_orientation;
  122.  
  123.     /**
  124.      * Array indicating orientation changes.
  125.      *
  126.      * @var array
  127.      */
  128.     var $_orientation_changes = array();
  129.  
  130.     /**
  131.      * Current width of page format in points.
  132.      *
  133.      * @var float
  134.      */
  135.     var $fwPt;
  136.  
  137.     /**
  138.      * Current height of page format in points.
  139.      *
  140.      * @var float
  141.      */
  142.     var $fhPt;
  143.  
  144.     /**
  145.      * Current width of page format in user units.
  146.      *
  147.      * @var float
  148.      */
  149.     var $fw;
  150.  
  151.     /**
  152.      * Current height of page format in user units.
  153.      *
  154.      * @var float
  155.      */
  156.     var $fh;
  157.  
  158.     /**
  159.      * Current width of page in points.
  160.      *
  161.      * @var float
  162.      */
  163.     var $wPt;
  164.  
  165.     /**
  166.      * Current height of page in points.
  167.      *
  168.      * @var float
  169.      */
  170.     var $hPt;
  171.  
  172.     /**
  173.      * Current width of page in user units
  174.      *
  175.      * @var float
  176.      */
  177.     var $w;
  178.  
  179.     /**
  180.      * Current height of page in user units
  181.      *
  182.      * @var float
  183.      */
  184.     var $h;
  185.  
  186.     /**
  187.      * Scale factor (number of points in user units).
  188.      *
  189.      * @var float
  190.      */
  191.     var $_scale;
  192.  
  193.     /**
  194.      * Left page margin size.
  195.      *
  196.      * @var float
  197.      */
  198.     var $_left_margin;
  199.  
  200.     /**
  201.      * Top page margin size.
  202.      *
  203.      * @var float
  204.      */
  205.     var $_top_margin;
  206.  
  207.     /**
  208.      * Right page margin size.
  209.      *
  210.      * @var float
  211.      */
  212.     var $_right_margin;
  213.  
  214.     /**
  215.      * Break page margin size, the bottom margin which triggers a page break.
  216.      *
  217.      * @var float
  218.      */
  219.     var $_break_margin;
  220.  
  221.     /**
  222.      * Cell margin size.
  223.      *
  224.      * @var float
  225.      */
  226.     var $_cell_margin;
  227.  
  228.     /**
  229.      * The current horizontal position for cell positioning.
  230.      * Value is set in user units and is calculated from the top left corner
  231.      * as origin.
  232.      *
  233.      * @var float
  234.      */
  235.     var $x;
  236.  
  237.     /**
  238.      * The current vertical position for cell positioning.
  239.      * Value is set in user units and is calculated from the top left corner
  240.      * as origin.
  241.      *
  242.      * @var float
  243.      */
  244.     var $y;
  245.  
  246.     /**
  247.      * The height of the last cell printed.
  248.      *
  249.      * @var float
  250.      */
  251.     var $_last_height;
  252.  
  253.     /**
  254.      * Line width in user units.
  255.      *
  256.      * @var float
  257.      */
  258.     var $_line_width;
  259.  
  260.     /**
  261.      * An array of standard font names.
  262.      *
  263.      * @var array
  264.      */
  265.     var $_core_fonts = array('courier'      => 'Courier',
  266.                              'courierB'     => 'Courier-Bold',
  267.                              'courierI'     => 'Courier-Oblique',
  268.                              'courierBI'    => 'Courier-BoldOblique',
  269.                              'helvetica'    => 'Helvetica',
  270.                              'helveticaB'   => 'Helvetica-Bold',
  271.                              'helveticaI'   => 'Helvetica-Oblique',
  272.                              'helveticaBI'  => 'Helvetica-BoldOblique',
  273.                              'times'        => 'Times-Roman',
  274.                              'timesB'       => 'Times-Bold',
  275.                              'timesI'       => 'Times-Italic',
  276.                              'timesBI'      => 'Times-BoldItalic',
  277.                              'symbol'       => 'Symbol',
  278.                              'zapfdingbats' => 'ZapfDingbats');
  279.  
  280.     /**
  281.      * An array of used fonts.
  282.      *
  283.      * @var array
  284.      */
  285.     var $_fonts = array();
  286.  
  287.     /**
  288.      * An array of font files.
  289.      *
  290.      * @var array
  291.      */
  292.     var $_font_files = array();
  293.  
  294.     /**
  295.      * An array of encoding differences.
  296.      *
  297.      * @var array
  298.      */
  299.     var $_diffs = array();
  300.  
  301.     /**
  302.      * An array of used images.
  303.      *
  304.      * @var array
  305.      */
  306.     var $_images = array();
  307.  
  308.     /**
  309.      * An array of links in pages.
  310.      *
  311.      * @var array
  312.      */
  313.     var $_page_links;
  314.  
  315.     /**
  316.      * An array of internal links.
  317.      *
  318.      * @var array
  319.      */
  320.     var $_links = array();
  321.  
  322.     /**
  323.      * Current font family.
  324.      *
  325.      * @var string
  326.      */
  327.     var $_font_family = '';
  328.  
  329.     /**
  330.      * Current font style.
  331.      *
  332.      * @var string
  333.      */
  334.     var $_font_style = '';
  335.  
  336.     /**
  337.      * Underlining flag.
  338.      *
  339.      * @var boolean
  340.      */
  341.     var $_underline = false;
  342.  
  343.     /**
  344.      * An array containing current font info.
  345.      *
  346.      * @var array
  347.      */
  348.     var $_current_font;
  349.  
  350.     /**
  351.      * Current font size in points.
  352.      *
  353.      * @var float
  354.      */
  355.     var $_font_size_pt = 12;
  356.  
  357.     /**
  358.      * Current font size in user units.
  359.      *
  360.      * @var float
  361.      */
  362.     var $_font_size = 12;
  363.  
  364.     /**
  365.      * Commands for filling color.
  366.      *
  367.      * @var string
  368.      */
  369.     var $_fill_color = '0 g';
  370.  
  371.     /**
  372.      * Commands for text color.
  373.      *
  374.      * @var string
  375.      */
  376.     var $_text_color = '0 g';
  377.  
  378.     /**
  379.      * Whether text color is different from fill color.
  380.      *
  381.      * @var boolean
  382.      */
  383.     var $_color_flag = false;
  384.  
  385.     /**
  386.      * Commands for drawing color.
  387.      *
  388.      * @var string
  389.      */
  390.     var $_draw_color = '0 G';
  391.  
  392.     /**
  393.      * Word spacing.
  394.      *
  395.      * @var integer
  396.      */
  397.     var $_word_spacing = 0;
  398.  
  399.     /**
  400.      * Automatic page breaking.
  401.      *
  402.      * @var boolean
  403.      */
  404.     var $_auto_page_break;
  405.  
  406.     /**
  407.      * Threshold used to trigger page breaks.
  408.      *
  409.      * @var float
  410.      */
  411.     var $_page_break_trigger;
  412.  
  413.     /**
  414.      * Flag set when processing footer.
  415.      *
  416.      * @var boolean
  417.      */
  418.     var $_in_footer = false;
  419.  
  420.     /**
  421.      * Zoom display mode.
  422.      *
  423.      * @var string
  424.      */
  425.     var $_zoom_mode;
  426.  
  427.     /**
  428.      * Layout display mode.
  429.      *
  430.      * @var string
  431.      */
  432.     var $_layout_mode;
  433.  
  434.     /**
  435.      * An array containing the document info, consisting of:
  436.      *   - title
  437.      *   - subject
  438.      *   - author
  439.      *   - keywords
  440.      *   - creator
  441.      *
  442.      * @var array
  443.      */
  444.     var $_info = array();
  445.  
  446.     /**
  447.      * Alias for total number of pages.
  448.      *
  449.      * @var string
  450.      */
  451.     var $_alias_nb_pages = '{nb}';
  452.  
  453.     /**
  454.      * Attempts to return a conrete PDF instance.
  455.      *
  456.      * It allows to set up the page format, the orientation and the units of
  457.      * measurement used in all the methods (except for the font sizes).
  458.      *
  459.      * Example:
  460.      * <code>
  461.      * $pdf = File_PDF::factory(array('orientation' => 'P',
  462.      *                                'unit' => 'mm',
  463.      *                                'format' => 'A4'));
  464.      * </code>
  465.      *
  466.      * @param array $params  A hash with parameters for the created PDF object.
  467.      *                       Possible parameters are:
  468.      *                       - orientation - Default page orientation. Possible
  469.      *                         values are (case insensitive):
  470.      *                         - P or Portrait (default)
  471.      *                         - L or Landscape
  472.      *                       - unit - User measure units. Possible values
  473.      *                         values are:
  474.      *                         - pt: point
  475.      *                         - mm: millimeter (default)
  476.      *                         - cm: centimeter
  477.      *                         - in: inch
  478.      *                         A point equals 1/72 of inch, that is to say
  479.      *                         about 0.35 mm (an inch being 2.54 cm). This is a
  480.      *                         very common unit in typography; font sizes are
  481.      *                         expressed in that unit.
  482.      *                       - format - The format used for pages. It can be
  483.      *                         either one of the following values (case
  484.      *                         insensitive):
  485.      *                         - A3
  486.      *                         - A4 (default)
  487.      *                         - A5
  488.      *                         - Letter
  489.      *                         - Legal
  490.      *                         or a custom format in the form of a two-element
  491.      *                         array containing the width and the height
  492.      *                         (expressed in the unit given by the unit
  493.      *                         parameter).
  494.      * @param string $class  The concrete class name to return an instance of.
  495.      *                       Defaults to File_PDF.
  496.      */
  497.     function &factory($params = array(), $class = 'File_PDF')
  498.     {
  499.         /* Default parameters. */
  500.         $defaults = array('orientation' => 'P',
  501.                           'unit' => 'mm',
  502.                           'format' => 'A4');
  503.  
  504.         /* Backward compatibility with old method signature. */
  505.         /* Should be removed a few versions later. */
  506.         if (!is_array($params)) {
  507.             $class = 'File_PDF';
  508.             $params = $defaults;
  509.             $names = array_keys($defaults);
  510.             for ($i = 0; $i < func_num_args(); $i++) {
  511.                 $params[$names[$i]] = func_get_arg($i);
  512.             }
  513.         } else {
  514.             $params = array_merge($defaults, $params);
  515.         }
  516.  
  517.         /* Create the PDF object. */
  518.         $pdf = new $class($params);
  519.  
  520.         /* Scale factor. */
  521.         if ($params['unit'] == 'pt') {
  522.             $pdf->_scale = 1;
  523.         } elseif ($params['unit'] == 'mm') {
  524.             $pdf->_scale = 72 / 25.4;
  525.         } elseif ($params['unit'] == 'cm') {
  526.             $pdf->_scale = 72 / 2.54;
  527.         } elseif ($params['unit'] == 'in') {
  528.             $pdf->_scale = 72;
  529.         } else {
  530.             $error = File_PDF::raiseError(sprintf('Incorrect units: %s', $params['unit']));
  531.             return $error;
  532.         }
  533.         /* Page format. */
  534.         if (is_string($params['format'])) {
  535.             $params['format'] = strtolower($params['format']);
  536.             if ($params['format'] == 'a3') {
  537.                 $params['format'] = array(841.89, 1190.55);
  538.             } elseif ($params['format'] == 'a4') {
  539.                 $params['format'] = array(595.28, 841.89);
  540.             } elseif ($params['format'] == 'a5') {
  541.                 $params['format'] = array(420.94, 595.28);
  542.             } elseif ($params['format'] == 'letter') {
  543.                 $params['format'] = array(612, 792);
  544.             } elseif ($params['format'] == 'legal') {
  545.                 $params['format'] = array(612, 1008);
  546.             } else {
  547.                 $error = File_PDF::raiseError(sprintf('Unknown page format: %s', $params['format']));
  548.                 return $error;
  549.             }
  550.             $pdf->fwPt = $params['format'][0];
  551.             $pdf->fhPt = $params['format'][1];
  552.         } else {
  553.             $pdf->fwPt = $params['format'][0] * $pdf->_scale;
  554.             $pdf->fhPt = $params['format'][1] * $pdf->_scale;
  555.         }
  556.         $pdf->fw = $pdf->fwPt / $pdf->_scale;
  557.         $pdf->fh = $pdf->fhPt / $pdf->_scale;
  558.  
  559.         /* Page orientation. */
  560.         $params['orientation'] = strtolower($params['orientation']);
  561.         if ($params['orientation'] == 'p' || $params['orientation'] == 'portrait') {
  562.             $pdf->_default_orientation = 'P';
  563.             $pdf->wPt = $pdf->fwPt;
  564.             $pdf->hPt = $pdf->fhPt;
  565.         } elseif ($params['orientation'] == 'l' || $params['orientation'] == 'landscape') {
  566.             $pdf->_default_orientation = 'L';
  567.             $pdf->wPt = $pdf->fhPt;
  568.             $pdf->hPt = $pdf->fwPt;
  569.         } else {
  570.             $error = File_PDF::raiseError(sprintf('Incorrect orientation: %s', $params['orientation']));
  571.             return $error;
  572.         }
  573.         $pdf->_current_orientation = $pdf->_default_orientation;
  574.         $pdf->w = $pdf->wPt / $pdf->_scale;
  575.         $pdf->h = $pdf->hPt / $pdf->_scale;
  576.  
  577.         /* Page margins (1 cm) */
  578.         $margin = 28.35 / $pdf->_scale;
  579.         $pdf->setMargins($margin, $margin);
  580.  
  581.         /* Interior cell margin (1 mm) */
  582.         $pdf->_cell_margin = $margin / 10;
  583.  
  584.         /* Line width (0.2 mm) */
  585.         $pdf->_line_width = .567 / $pdf->_scale;
  586.  
  587.         /* Automatic page break */
  588.         $pdf->setAutoPageBreak(true, 2 * $margin);
  589.  
  590.         /* Full width display mode */
  591.         $pdf->setDisplayMode('fullwidth');
  592.  
  593.         /* Compression */
  594.         $pdf->setCompression(true);
  595.  
  596.         return $pdf;
  597.     }
  598.  
  599.     /**
  600.      * Returns a PEAR_Error object.
  601.      *
  602.      * Wraps around PEAR::raiseError() to avoid having to include PEAR.php
  603.      * unless an error occurs.
  604.      *
  605.      * @param mixed $error  The error message.
  606.      *
  607.      * @return object PEAR_Error
  608.      */
  609.     function raiseError($error)
  610.     {
  611.         require_once 'PEAR.php';
  612.         return PEAR::raiseError($error);
  613.     }
  614.  
  615.     /**
  616.      * Defines the left, top and right margins.
  617.      *
  618.      * By default, they equal 1 cm. Call this method to change them.
  619.      *
  620.      * @param float $left   Left margin.
  621.      * @param float $top    Top margin.
  622.      * @param float $right  Right margin. If not specified default to the value
  623.      *                      of the left one.
  624.      *
  625.      * @see setAutoPageBreak()
  626.      * @see setLeftMargin()
  627.      * @see setRightMargin()
  628.      * @see setTopMargin()
  629.      */
  630.     function setMargins($left, $top, $right = null)
  631.     {
  632.         /* Set left and top margins. */
  633.         $this->_left_margin  = $left;
  634.         $this->_top_margin   = $top;
  635.         /* If no right margin set default to same as left. */
  636.         $this->_right_margin = (is_null($right) ? $left : $right);
  637.     }
  638.  
  639.     /**
  640.      * Defines the left margin.
  641.      *
  642.      * The method can be called before creating the first page.  If the
  643.      * current abscissa gets out of page, it is brought back to the margin.
  644.      *
  645.      * @param float $margin  The margin.
  646.      *
  647.      * @see setAutoPageBreak()
  648.      * @see setMargins()
  649.      * @see setRightMargin()
  650.      * @see setTopMargin()
  651.      */
  652.     function setLeftMargin($margin)
  653.     {
  654.         $this->_left_margin = $margin;
  655.         /* If there is a current page and the current X position is less than
  656.          * margin set the X position to the margin value. */
  657.         if ($this->_page > 0 && $this->x < $margin) {
  658.             $this->x = $margin;
  659.         }
  660.     }
  661.  
  662.     /**
  663.      * Defines the top margin.
  664.      *
  665.      * The method can be called before creating the first page.
  666.      *
  667.      * @param float $margin  The margin.
  668.      */
  669.     function setTopMargin($margin)
  670.     {
  671.         $this->_top_margin = $margin;
  672.     }
  673.  
  674.     /**
  675.      * Defines the right margin.
  676.      *
  677.      * The method can be called before creating the first page.
  678.      *
  679.      * @param float $margin  The margin.
  680.      */
  681.     function setRightMargin($margin)
  682.     {
  683.         $this->_right_margin = $margin;
  684.     }
  685.  
  686.     /**
  687.      * Returns the actual page width.
  688.      *
  689.      * @since File_PDF 0.2.0
  690.      * @since Horde 3.2
  691.      *
  692.      * @return float  The page width.
  693.      */
  694.     function getPageWidth()
  695.     {
  696.         return ($this->w - $this->_right_margin - $this->_left_margin);
  697.     }
  698.  
  699.     /**
  700.      * Returns the actual page height.
  701.      *
  702.      * @since File_PDF 0.2.0
  703.      * @since Horde 3.2
  704.      *
  705.      * @return float  The page height.
  706.      */
  707.     function getPageHeight()
  708.     {
  709.         return ($this->h - $this->_top_margin - $this->_break_margin);
  710.     }
  711.  
  712.     /**
  713.      * Enables or disables the automatic page breaking mode.
  714.      *
  715.      * When enabling, the second parameter is the distance from the bottom of
  716.      * the page that defines the triggering limit. By default, the mode is on
  717.      * and the margin is 2 cm.
  718.      *
  719.      * @param boolean $auto  Boolean indicating if mode should be on or off.
  720.      * @param float $margin  Distance from the bottom of the page.
  721.      */
  722.     function setAutoPageBreak($auto, $margin = 0)
  723.     {
  724.         $this->_auto_page_break    = $auto;
  725.         $this->_break_margin       = $margin;
  726.         $this->_page_break_trigger = $this->h - $margin;
  727.     }
  728.  
  729.     /**
  730.      * Defines the way the document is to be displayed by the viewer.
  731.      *
  732.      * The zoom level can be set: pages can be displayed entirely on screen,
  733.      * occupy the full width of the window, use real size, be scaled by a
  734.      * specific zooming factor or use viewer default (configured in the
  735.      * Preferences menu of Acrobat). The page layout can be specified too:
  736.      * single at once, continuous display, two columns or viewer default.  By
  737.      * default, documents use the full width mode with continuous display.
  738.      *
  739.      * @param mixed $zoom    The zoom to use. It can be one of the following
  740.      *                       string values:
  741.      *                         - fullpage: entire page on screen
  742.      *                         - fullwidth: maximum width of window
  743.      *                         - real: uses real size (100% zoom)
  744.      *                         - default: uses viewer default mode
  745.      *                       or a number indicating the zooming factor.
  746.      * @param string layout  The page layout. Possible values are:
  747.      *                         - single: one page at once
  748.      *                         - continuous: pages in continuously
  749.      *                         - two: two pages on two columns
  750.      *                         - default: uses viewer default mode
  751.      *                       Default value is continuous.
  752.      */
  753.     function setDisplayMode($zoom, $layout = 'continuous')
  754.     {
  755.         $zoom = strtolower($zoom);
  756.         if ($zoom == 'fullpage' || $zoom == 'fullwidth' || $zoom == 'real'
  757.             || $zoom == 'default' || !is_string($zoom)) {
  758.             $this->_zoom_mode = $zoom;
  759.         } elseif ($zoom == 'zoom') {
  760.             $this->_zoom_mode = $layout;
  761.         } else {
  762.             return $this->raiseError(sprintf('Incorrect zoom display mode: %s', $zoom));
  763.         }
  764.  
  765.         $layout = strtolower($layout);
  766.         if ($layout == 'single' || $layout == 'continuous' || $layout == 'two'
  767.             || $layout == 'default') {
  768.             $this->_layout_mode = $layout;
  769.         } elseif ($zoom != 'zoom') {
  770.             return $this->raiseError(sprintf('Incorrect layout display mode: %s', $layout));
  771.         }
  772.     }
  773.  
  774.     /**
  775.      * Activates or deactivates page compression.
  776.      *
  777.      * When activated, the internal representation of each page is compressed,
  778.      * which leads to a compression ratio of about 2 for the resulting
  779.      * document. Compression is on by default.
  780.      *
  781.      * Note: the {@link http://www.php.net/zlib/ zlib extension} is required
  782.      * for this feature. If not present, compression will be turned off.
  783.      *
  784.      * @param boolean $compress  Boolean indicating if compression must be
  785.      *                           enabled or not.
  786.      */
  787.     function setCompression($compress)
  788.     {
  789.         /* If no gzcompress function is available then default to false. */
  790.         $this->_compress = (function_exists('gzcompress') ? $compress : false);
  791.     }
  792.  
  793.     /**
  794.      * Set the info to a document.
  795.      *
  796.      * Possible info settings are:
  797.      *   - title
  798.      *   - subject
  799.      *   - author
  800.      *   - keywords
  801.      *   - creator
  802.      *
  803.      * @param array|string $info  If passed as an array then the complete hash
  804.      *                            containing the info to be inserted into the
  805.      *                            document. Otherwise the name of setting to be
  806.      *                            set.
  807.      * @param string $value       The value of the setting.
  808.      */
  809.     function setInfo($info, $value = '')
  810.     {
  811.         if (is_array($info)) {
  812.             $this->_info = $info;
  813.         } else {
  814.             $this->_info[$info] = $value;
  815.         }
  816.     }
  817.  
  818.     /**
  819.      * Defines an alias for the total number of pages.
  820.      *
  821.      * It will be substituted as the document is closed.
  822.      *
  823.      * Example:
  824.      * <code>
  825.      * class My_File_PDF extends File_PDF {
  826.      *     function footer()
  827.      *     {
  828.      *         // Go to 1.5 cm from bottom
  829.      *         $this->setY(-15);
  830.      *         // Select Arial italic 8
  831.      *         $this->setFont('Arial', 'I', 8);
  832.      *         // Print current and total page numbers
  833.      *         $this->cell(0, 10, 'Page ' . $this->getPageNo() . '/{nb}', 0,
  834.      *                     0, 'C');
  835.      *     }
  836.      * }
  837.      * $pdf = My_File_PDF::factory();
  838.      * $pdf->aliasNbPages();
  839.      * </code>
  840.      *
  841.      * @param string $alias  The alias.
  842.      *
  843.      * @see getPageNo()
  844.      * @see footer()
  845.      */
  846.     function aliasNbPages($alias = '{nb}')
  847.     {
  848.         $this->_alias_nb_pages = $alias;
  849.     }
  850.  
  851.     /**
  852.      * This method begins the generation of the PDF document; it must be
  853.      * called before any output commands.
  854.      *
  855.      * No page is created by this method, therefore it is necessary to call
  856.      * {@link addPage()}.
  857.      *
  858.      * @see addPage()
  859.      * @see close()
  860.      */
  861.     function open()
  862.     {
  863.         $this->_beginDoc();
  864.     }
  865.  
  866.     /**
  867.      * Terminates the PDF document. It is not necessary to call this method
  868.      * explicitly because {@link output()} does it automatically.
  869.      *
  870.      * If the document contains no page, {@link addPage()} is called to
  871.      * prevent from getting an invalid document.
  872.      *
  873.      * @see open()
  874.      * @see output()
  875.      */
  876.     function close()
  877.     {
  878.         /* Terminate document */
  879.         if ($this->_page == 0) {
  880.             $result = $this->addPage();
  881.             if (is_a($result, 'PEAR_Error')) {
  882.                 return $result;
  883.             }
  884.         }
  885.         /* Page footer */
  886.         $this->_in_footer = true;
  887.         $this->x = $this->_left_margin;
  888.         $this->footer();
  889.         $this->_in_footer = false;
  890.         /* Close page */
  891.         $this->_endPage();
  892.         /* Close document */
  893.         $this->_endDoc();
  894.     }
  895.  
  896.     /**
  897.      * Adds a new page to the document.
  898.      *
  899.      * If a page is already present, the {@link footer()} method is called
  900.      * first to output the footer. Then the page is added, the current
  901.      * position set to the top-left corner according to the left and top
  902.      * margins, and {@link header()} is called to display the header.
  903.      *
  904.      * The font which was set before calling is automatically restored. There
  905.      * is no need to call {@link setFont()} again if you want to continue with
  906.      * the same font. The same is true for colors and line width.  The origin
  907.      * of the coordinate system is at the top-left corner and increasing
  908.      * ordinates go downwards.
  909.      *
  910.      * @param string $orientation  Page orientation. Possible values
  911.      *                             are (case insensitive):
  912.      *                               - P or Portrait
  913.      *                               - L or Landscape
  914.      *                             The default value is the one passed to the
  915.      *                             constructor.
  916.      *
  917.      * @see header()
  918.      * @see footer()
  919.      * @see setMargins()
  920.      */
  921.     function addPage($orientation = '')
  922.     {
  923.         /* For good measure make sure this is called. */
  924.         $this->_beginDoc();
  925.  
  926.         /* Save style settings so that they are not overridden by footer() or
  927.          * header(). */
  928.         $lw = $this->_line_width;
  929.         $dc = $this->_draw_color;
  930.         $fc = $this->_fill_color;
  931.         $tc = $this->_text_color;
  932.         $cf = $this->_color_flag;
  933.         $font_family = $this->_font_family;
  934.         $font_style = $this->_font_style . ($this->_underline ? 'U' : '');
  935.         $font_size  = $this->_font_size_pt;
  936.  
  937.         /* Close old page. */
  938.         if ($this->_page > 0) {
  939.             /* Page footer. */
  940.             $this->_in_footer = true;
  941.             $this->x = $this->_left_margin;
  942.             $this->footer();
  943.             $this->_in_footer = false;
  944.  
  945.             /* Close page. */
  946.             $this->_endPage();
  947.         }
  948.  
  949.         /* Start new page. */
  950.         $this->_beginPage($orientation);
  951.         /* Set line cap style to square. */
  952.         $this->_out('2 J');
  953.         /* Set line width. */
  954.         $this->_line_width = $lw;
  955.         $this->_out(sprintf('%.2' . FILE_PDF_FLOAT . ' w', $lw * $this->_scale));
  956.  
  957.         /* Force the setting of the font. Each new page requires a new
  958.          * call. */
  959.         if ($font_family) {
  960.             $result = $this->setFont($font_family, $font_style, $font_size, true);
  961.             if (is_a($result, 'PEAR_Error')) {
  962.                 return $result;
  963.             }
  964.         }
  965.  
  966.         /* Restore styles. */
  967.         if ($this->_fill_color != $fc) {
  968.             $this->_fill_color = $fc;
  969.             $this->_out($this->_fill_color);
  970.         }
  971.         if ($this->_draw_color != $dc) {
  972.             $this->_draw_color = $dc;
  973.             $this->_out($this->_draw_color);
  974.         }
  975.         $this->_text_color = $tc;
  976.         $this->_color_flag = $cf;
  977.  
  978.         /* Page header. */
  979.         $this->header();
  980.  
  981.         /* Restore styles. */
  982.         if ($this->_line_width != $lw) {
  983.             $this->_line_width = $lw;
  984.             $this->_out(sprintf('%.2' . FILE_PDF_FLOAT . ' w', $lw * $this->_scale));
  985.         }
  986.         $result = $this->setFont($font_family, $font_style, $font_size);
  987.         if (is_a($result, 'PEAR_Error')) {
  988.             return $result;
  989.         }
  990.         if ($this->_fill_color != $fc) {
  991.             $this->_fill_color = $fc;
  992.             $this->_out($this->_fill_color);
  993.         }
  994.         if ($this->_draw_color != $dc) {
  995.             $this->_draw_color = $dc;
  996.             $this->_out($this->_draw_color);
  997.         }
  998.         $this->_text_color = $tc;
  999.         $this->_color_flag = $cf;
  1000.     }
  1001.  
  1002.     /**
  1003.      * This method is used to render the page header.
  1004.      *
  1005.      * It is automatically called by {@link addPage()} and should not be
  1006.      * called directly by the application. The implementation in File_PDF:: is
  1007.      * empty, so you have to subclass it and override the method if you want a
  1008.      * specific processing.
  1009.      *
  1010.      * Example:
  1011.      * <code>
  1012.      * class My_File_PDF extends File_PDF {
  1013.      *     function header()
  1014.      *     {
  1015.      *         // Select Arial bold 15
  1016.      *         $this->setFont('Arial', 'B', 15);
  1017.      *         // Move to the right
  1018.      *         $this->cell(80);
  1019.      *         // Framed title
  1020.      *         $this->cell(30, 10, 'Title', 1, 0, 'C');
  1021.      *         // Line break
  1022.      *         $this->newLine(20);
  1023.      *     }
  1024.      * }
  1025.      * </code>
  1026.      *
  1027.      * @see footer()
  1028.      */
  1029.     function header()
  1030.     {
  1031.         /* To be implemented in your own inherited class. */
  1032.     }
  1033.  
  1034.     /**
  1035.      * This method is used to render the page footer.
  1036.      *
  1037.      * It is automatically called by {@link addPage()} and {@link close()} and
  1038.      * should not be called directly by the application. The implementation in
  1039.      * File_PDF:: is empty, so you have to subclass it and override the method
  1040.      * if you want a specific processing.
  1041.      *
  1042.      * Example:
  1043.      * <code>
  1044.      * class My_File_PDF extends File_PDF {
  1045.      *    function footer()
  1046.      *    {
  1047.      *        // Go to 1.5 cm from bottom
  1048.      *        $this->setY(-15);
  1049.      *        // Select Arial italic 8
  1050.      *        $this->setFont('Arial', 'I', 8);
  1051.      *        // Print centered page number
  1052.      *        $this->cell(0, 10, 'Page ' . $this->getPageNo(), 0, 0, 'C');
  1053.      *    }
  1054.      * }
  1055.      * </code>
  1056.      *
  1057.      * @see header()
  1058.      */
  1059.     function footer()
  1060.     {
  1061.         /* To be implemented in your own inherited class. */
  1062.     }
  1063.  
  1064.     /**
  1065.      * Returns the current page number.
  1066.      *
  1067.      * @return integer
  1068.      *
  1069.      * @see aliasNbPages()
  1070.      */
  1071.     function getPageNo()
  1072.     {
  1073.         return $this->_page;
  1074.     }
  1075.  
  1076.     /**
  1077.      * Sets the fill color.
  1078.      *
  1079.      * Depending on the colorspace called, the number of color component
  1080.      * parameters required can be either 1, 3 or 4. The method can be called
  1081.      * before the first page is created and the color is retained from page to
  1082.      * page.
  1083.      *
  1084.      * @param string $cs  Indicates the colorspace which can be either 'rgb',
  1085.      *                    'cmyk' or 'gray'. Defaults to 'rgb'.
  1086.      * @param float $c1   First color component, floating point value between 0
  1087.      *                    and 1. Required for gray, rgb and cmyk.
  1088.      * @param float $c2   Second color component, floating point value
  1089.      *                    between 0 and 1. Required for rgb and cmyk.
  1090.      * @param float $c3   Third color component, floating point value between 0
  1091.      *                    and 1. Required for rgb and cmyk.
  1092.      * @param float $c4   Fourth color component, floating point value
  1093.      *                    between 0 and 1. Required for cmyk.
  1094.      *
  1095.      * @see setTextColor()
  1096.      * @see setDrawColor()
  1097.      * @see rect()
  1098.      * @see cell()
  1099.      * @see multiCell()
  1100.      */
  1101.     function setFillColor($cs = 'rgb', $c1, $c2 = 0, $c3 = 0, $c4 = 0)
  1102.     {
  1103.         $cs = strtolower($cs);
  1104.         if ($cs == 'rgb') {
  1105.             $this->_fill_color = sprintf('%.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' rg', $c1, $c2, $c3);
  1106.         } elseif ($cs == 'cmyk') {
  1107.             $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);
  1108.         } else {
  1109.             $this->_fill_color = sprintf('%.3' . FILE_PDF_FLOAT . ' g', $c1);
  1110.         }
  1111.         if ($this->_page > 0) {
  1112.             $this->_out($this->_fill_color);
  1113.         }
  1114.         $this->_color_flag = $this->_fill_color != $this->_text_color;
  1115.     }
  1116.  
  1117.     /**
  1118.      * Sets the text color.
  1119.      *
  1120.      * Depending on the colorspace called, the number of color component
  1121.      * parameters required can be either 1, 3 or 4. The method can be called
  1122.      * before the first page is created and the color is retained from page to
  1123.      * page.
  1124.      *
  1125.      * @param string $cs  Indicates the colorspace which can be either 'rgb',
  1126.      *                    'cmyk' or 'gray'. Defaults to 'rgb'.
  1127.      * @param float $c1   First color component, floating point value between 0
  1128.      *                    and 1. Required for gray, rgb and cmyk.
  1129.      * @param float $c2   Second color component, floating point value
  1130.      *                    between 0 and 1. Required for rgb and cmyk.
  1131.      * @param float $c3   Third color component, floating point value between 0
  1132.      *                    and 1. Required for rgb and cmyk.
  1133.      * @param float $c4   Fourth color component, floating point value
  1134.      *                    between 0 and 1. Required for cmyk.
  1135.      *
  1136.      * @since File_PDF 0.2.0
  1137.      * @since Horde 3.2
  1138.      * @see setFillColor()
  1139.      * @see setDrawColor()
  1140.      * @see rect()
  1141.      * @see cell()
  1142.      * @see multiCell()
  1143.      */
  1144.     function setTextColor($cs, $c1, $c2 = 0, $c3 = 0, $c4 = 0)
  1145.     {
  1146.         $cs = strtolower($cs);
  1147.         if ($cs == 'rgb') {
  1148.             $this->_text_color = sprintf('%.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' rg', $c1, $c2, $c3);
  1149.         } elseif ($cs == 'cmyk') {
  1150.             $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);
  1151.         } else {
  1152.             $this->_text_color = sprintf('%.3' . FILE_PDF_FLOAT . ' g', $c1);
  1153.         }
  1154.         $this->_color_flag = $this->_fill_color != $this->_text_color;
  1155.     }
  1156.  
  1157.     /**
  1158.      * Sets the draw color, used when drawing lines.
  1159.      *
  1160.      * Depending on the colorspace called, the number of color component
  1161.      * parameters required can be either 1, 3 or 4. The method can be called
  1162.      * before the first page is created and the color is retained from page to
  1163.      * page.
  1164.      *
  1165.      * @param string $cs  Indicates the colorspace which can be either 'rgb',
  1166.      *                    'cmyk' or 'gray'. Defaults to 'rgb'.
  1167.      * @param float $c1   First color component, floating point value between 0
  1168.      *                    and 1. Required for gray, rgb and cmyk.
  1169.      * @param float $c2   Second color component, floating point value
  1170.      *                    between 0 and 1. Required for rgb and cmyk.
  1171.      * @param float $c3   Third color component, floating point value between 0
  1172.      *                    and 1. Required for rgb and cmyk.
  1173.      * @param float $c4   Fourth color component, floating point value
  1174.      *                    between 0 and 1. Required for cmyk.
  1175.      *
  1176.      * @see setFillColor()
  1177.      * @see line()
  1178.      * @see rect()
  1179.      * @see cell()
  1180.      * @see multiCell()
  1181.      */
  1182.     function setDrawColor($cs = 'rgb', $c1, $c2 = 0, $c3 = 0, $c4 = 0)
  1183.     {
  1184.         $cs = strtolower($cs);
  1185.         if ($cs == 'rgb') {
  1186.             $this->_draw_color = sprintf('%.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' RG', $c1, $c2, $c3);
  1187.         } elseif ($cs == 'cmyk') {
  1188.             $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);
  1189.         } else {
  1190.             $this->_draw_color = sprintf('%.3' . FILE_PDF_FLOAT . ' G', $c1);
  1191.         }
  1192.         if ($this->_page > 0) {
  1193.             $this->_out($this->_draw_color);
  1194.         }
  1195.     }
  1196.  
  1197.     /**
  1198.      * Returns the length of a text string. A font must be selected.
  1199.      *
  1200.      * @param string $text  The text whose length is to be computed.
  1201.      * @param boolean $pt   Whether the width should be returned in points or
  1202.      *                      user units.
  1203.      *
  1204.      * @return float
  1205.      */
  1206.     function getStringWidth($text, $pt = false)
  1207.     {
  1208.         $text = (string)$text;
  1209.         $width = 0;
  1210.         $length = strlen($text);
  1211.         for ($i = 0; $i < $length; $i++) {
  1212.             $width += $this->_current_font['cw'][$text{$i}];
  1213.         }
  1214.  
  1215.         /* Adjust for word spacing. */
  1216.         $width += $this->_word_spacing * substr_count($text, ' ') * $this->_current_font['cw'][' '];
  1217.  
  1218.         if ($pt) {
  1219.             return $width * $this->_font_size_pt / 1000;
  1220.         } else {
  1221.             return $width * $this->_font_size / 1000;
  1222.         }
  1223.     }
  1224.  
  1225.     /**
  1226.      * Defines the line width.
  1227.      *
  1228.      * By default, the value equals 0.2 mm. The method can be called before
  1229.      * the first page is created and the value is retained from page to page.
  1230.      *
  1231.      * @param float $width  The width.
  1232.      *
  1233.      * @see line()
  1234.      * @see rect()
  1235.      * @see cell()
  1236.      * @see multiCell()
  1237.      */
  1238.     function setLineWidth($width)
  1239.     {
  1240.         $this->_line_width = $width;
  1241.         if ($this->_page > 0) {
  1242.             $this->_out(sprintf('%.2' . FILE_PDF_FLOAT . ' w', $width * $this->_scale));
  1243.         }
  1244.     }
  1245.  
  1246.     /**
  1247.      * Draws a line between two points.
  1248.      *
  1249.      * All coordinates can be negative to provide values from the right or
  1250.      * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
  1251.      *
  1252.      * @param float $x1  Abscissa of first point.
  1253.      * @param float $y1  Ordinate of first point.
  1254.      * @param float $x2  Abscissa of second point.
  1255.      * @param float $y2  Ordinate of second point.
  1256.      *
  1257.      * @see setLineWidth()
  1258.      * @see setDrawColor()
  1259.      */
  1260.     function line($x1, $y1, $x2, $y2)
  1261.     {
  1262.         if ($x1 < 0) {
  1263.             $x1 += $this->w;
  1264.         }
  1265.         if ($y1 < 0) {
  1266.             $y1 += $this->h;
  1267.         }
  1268.         if ($x2 < 0) {
  1269.             $x2 += $this->w;
  1270.         }
  1271.         if ($y2 < 0) {
  1272.             $y2 += $this->h;
  1273.         }
  1274.  
  1275.         $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));
  1276.     }
  1277.  
  1278.     /**
  1279.      * Outputs a rectangle.
  1280.      *
  1281.      * It can be drawn (border only), filled (with no border) or both.
  1282.      *
  1283.      * All coordinates can be negative to provide values from the right or
  1284.      * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
  1285.      *
  1286.      * @param float $x       Abscissa of upper-left corner.
  1287.      * @param float $y       Ordinate of upper-left corner.
  1288.      * @param float $width   Width.
  1289.      * @param float $height  Height.
  1290.      * @param float $style   Style of rendering. Possible values are:
  1291.      *                         - D or empty string: draw (default)
  1292.      *                         - F: fill
  1293.      *                         - DF or FD: draw and fill
  1294.      *
  1295.      * @see setLineWidth()
  1296.      * @see setDrawColor()
  1297.      * @see setFillColor()
  1298.      */
  1299.     function rect($x, $y, $width, $height, $style = '')
  1300.     {
  1301.         if ($x < 0) {
  1302.             $x += $this->w;
  1303.         }
  1304.         if ($y < 0) {
  1305.             $y += $this->h;
  1306.         }
  1307.  
  1308.         $style = strtoupper($style);
  1309.         if ($style == 'F') {
  1310.             $op = 'f';
  1311.         } elseif ($style == 'FD' || $style == 'DF') {
  1312.             $op = 'B';
  1313.         } else {
  1314.             $op = 'S';
  1315.         }
  1316.  
  1317.         $x      = $this->_toPt($x);
  1318.         $y      = $this->_toPt($y);
  1319.         $width  = $this->_toPt($width);
  1320.         $height = $this->_toPt($height);
  1321.  
  1322.         $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));
  1323.     }
  1324.  
  1325.     /**
  1326.      * Outputs a circle. It can be drawn (border only), filled (with no
  1327.      * border) or both.
  1328.      *
  1329.      * All coordinates can be negative to provide values from the right or
  1330.      * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
  1331.      *
  1332.      * @param float $x       Abscissa of the center of the circle.
  1333.      * @param float $y       Ordinate of the center of the circle.
  1334.      * @param float $r       Circle radius.
  1335.      * @param string $style  Style of rendering. Possible values are:
  1336.      *                         - D or empty string: draw (default)
  1337.      *                         - F: fill
  1338.      *                         - DF or FD: draw and fill
  1339.      */
  1340.     function circle($x, $y, $r, $style = '')
  1341.     {
  1342.         if ($x < 0) {
  1343.             $x += $this->w;
  1344.         }
  1345.         if ($y < 0) {
  1346.             $y += $this->h;
  1347.         }
  1348.  
  1349.         $style = strtolower($style);
  1350.         if ($style == 'f') {
  1351.             $op = 'f';      // Style is fill only.
  1352.         } elseif ($style == 'fd' || $style == 'df') {
  1353.             $op = 'B';      // Style is fill and stroke.
  1354.         } else {
  1355.             $op = 'S';      // Style is stroke only.
  1356.         }
  1357.  
  1358.         $x = $this->_toPt($x);
  1359.         $y = $this->_toPt($y);
  1360.         $r = $this->_toPt($r);
  1361.  
  1362.         /* Invert the y scale. */
  1363.         $y = $this->hPt - $y;
  1364.         /* Length of the Bezier control. */
  1365.         $b = $r * 0.552;
  1366.  
  1367.         /* Move from the given origin and set the current point
  1368.          * to the start of the first Bezier curve. */
  1369.         $c = sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' m', $x - $r, $y);
  1370.         $x = $x - $r;
  1371.         /* First circle quarter. */
  1372.         $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',
  1373.                       $x, $y + $b,           // First control point.
  1374.                       $x + $r - $b, $y + $r, // Second control point.
  1375.                       $x + $r, $y + $r);     // Final point.
  1376.         /* Set x/y to the final point. */
  1377.         $x = $x + $r;
  1378.         $y = $y + $r;
  1379.         /* Second circle quarter. */
  1380.         $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',
  1381.                       $x + $b, $y,
  1382.                       $x + $r, $y - $r + $b,
  1383.                       $x + $r, $y - $r);
  1384.         /* Set x/y to the final point. */
  1385.         $x = $x + $r;
  1386.         $y = $y - $r;
  1387.         /* Third circle quarter. */
  1388.         $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',
  1389.                       $x, $y - $b,
  1390.                       $x - $r + $b, $y - $r,
  1391.                       $x - $r, $y - $r);
  1392.         /* Set x/y to the final point. */
  1393.         $x = $x - $r;
  1394.         $y = $y - $r;
  1395.         /* Fourth circle quarter. */
  1396.         $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',
  1397.                       $x - $b, $y,
  1398.                       $x - $r, $y + $r - $b,
  1399.                       $x - $r, $y + $r,
  1400.                       $op);
  1401.         /* Output the whole string. */
  1402.         $this->_out($c);
  1403.     }
  1404.  
  1405.     /**
  1406.      * Imports a TrueType or Type1 font and makes it available. It is
  1407.      * necessary to generate a font definition file first with the
  1408.      * makefont.php utility.
  1409.      * The location of the definition file (and the font file itself when
  1410.      * embedding) must be found at the full path name included.
  1411.      *
  1412.      * Example:
  1413.      * <code>
  1414.      * $pdf->addFont('Comic', 'I');
  1415.      * is equivalent to:
  1416.      * $pdf->addFont('Comic', 'I', 'comici.php');
  1417.      * </code>
  1418.      *
  1419.      * @param string $family  Font family. The name can be chosen arbitrarily.
  1420.      *                        If it is a standard family name, it will
  1421.      *                        override the corresponding font.
  1422.      * @param string $style   Font style. Possible values are (case
  1423.      *                        insensitive):
  1424.      *                          - empty string: regular (default)
  1425.      *                          - B: bold
  1426.      *                          - I: italic
  1427.      *                          - BI or IB: bold italic
  1428.      * @param string $file    The font definition file. By default, the name is
  1429.      *                        built from the family and style, in lower case
  1430.      *                        with no space.
  1431.      *
  1432.      * @see setFont()
  1433.      */
  1434.     function addFont($family, $style = '', $file = '')
  1435.     {
  1436.         $family = strtolower($family);
  1437.         if ($family == 'arial') {
  1438.             $family = 'helvetica';
  1439.         }
  1440.  
  1441.         $style = strtoupper($style);
  1442.         if ($style == 'IB') {
  1443.             $style = 'BI';
  1444.         }
  1445.         if (isset($this->_fonts[$family . $style])) {
  1446.             return $this->raiseError(sprintf('Font already added: %s %s', $family, $style));
  1447.         }
  1448.         if ($file == '') {
  1449.             $file = str_replace(' ', '', $family) . strtolower($style) . '.php';
  1450.         }
  1451.         include($file);
  1452.         if (!isset($name)) {
  1453.             return $this->raiseError('Could not include font definition file');
  1454.         }
  1455.         $i = count($this->_fonts) + 1;
  1456.         $this->_fonts[$family . $style] = array('i' => $i, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'enc' => $enc, 'file' => $file);
  1457.         if ($diff) {
  1458.             /* Search existing encodings. */
  1459.             $d = 0;
  1460.             $nb = count($this->_diffs);
  1461.             for ($i = 1; $i <= $nb; $i++) {
  1462.                 if ($this->_diffs[$i] == $diff) {
  1463.                     $d = $i;
  1464.                     break;
  1465.                 }
  1466.             }
  1467.             if ($d == 0) {
  1468.                 $d = $nb + 1;
  1469.                 $this->_diffs[$d] = $diff;
  1470.             }
  1471.             $this->_fonts[$family . $style]['diff'] = $d;
  1472.         }
  1473.         if ($file) {
  1474.             if ($type == 'TrueType') {
  1475.                 $this->_font_files[$file] = array('length1' => $originalsize);
  1476.             } else {
  1477.                 $this->_font_files[$file] = array('length1' => $size1, 'length2' => $size2);
  1478.             }
  1479.         }
  1480.     }
  1481.  
  1482.     /**
  1483.      * Sets the font used to print character strings.
  1484.      *
  1485.      * It is mandatory to call this method at least once before printing text
  1486.      * or the resulting document would not be valid. The font can be either a
  1487.      * standard one or a font added via the {@link addFont()} method. Standard
  1488.      * fonts use Windows encoding cp1252 (Western Europe).
  1489.      *
  1490.      * The method can be called before the first page is created and the font
  1491.      * is retained from page to page.
  1492.      *
  1493.      * If you just wish to change the current font size, it is simpler to call
  1494.      * {@link setFontSize()}.
  1495.      *
  1496.      * @param string $family  Family font. It can be either a name defined by
  1497.      *                        {@link addFont()} or one of the standard families
  1498.      *                        (case insensitive):
  1499.      *                          - Courier (fixed-width)
  1500.      *                          - Helvetica or Arial (sans serif)
  1501.      *                          - Times (serif)
  1502.      *                          - Symbol (symbolic)
  1503.      *                          - ZapfDingbats (symbolic)
  1504.      *                        It is also possible to pass an empty string. In
  1505.      *                        that case, the current family is retained.
  1506.      * @param string $style   Font style. Possible values are (case
  1507.      *                        insensitive):
  1508.      *                          - empty string: regular
  1509.      *                          - B: bold
  1510.      *                          - I: italic
  1511.      *                          - U: underline
  1512.      *                        or any combination. Bold and italic styles do not
  1513.      *                        apply to Symbol and ZapfDingbats.
  1514.      * @param integer $size   Font size in points. The default value is the
  1515.      *                        current size. If no size has been specified since
  1516.      *                        the beginning of the document, the value taken
  1517.      *                        is 12.
  1518.      * @param boolean $force  Force the setting of the font. Each new page will
  1519.      *                        require a new call to {@link setFont()} and
  1520.      *                        settings this to true will make sure that the
  1521.      *                        checks for same font calls will be skipped.
  1522.      *
  1523.      * @see addFont()
  1524.      * @see setFontSize()
  1525.      * @see cell()
  1526.      * @see multiCell()
  1527.      * @see write()
  1528.      */
  1529.     function setFont($family, $style = '', $size = null, $force = false)
  1530.     {
  1531.         $family = strtolower($family);
  1532.         if (empty($family)) {
  1533.             $family = $this->_font_family;
  1534.         }
  1535.         if ($family == 'arial') {
  1536.             /* Use helvetica instead of arial. */
  1537.             $family = 'helvetica';
  1538.         } elseif ($family == 'symbol' || $family == 'zapfdingbats') {
  1539.             /* These two fonts do not have styles available. */
  1540.             $style = '';
  1541.         }
  1542.  
  1543.         $style = strtoupper($style);
  1544.  
  1545.         /* Underline is handled separately, if specified in the style var
  1546.          * remove it from the style and set the underline flag. */
  1547.         if (strpos($style, 'U') !== false) {
  1548.             $this->_underline = true;
  1549.             $style = str_replace('U', '', $style);
  1550.         } else {
  1551.             $this->_underline = false;
  1552.         }
  1553.  
  1554.         if ($style == 'IB') {
  1555.             $style = 'BI';
  1556.         }
  1557.  
  1558.         /* If no size specified, use current size. */
  1559.         if (is_null($size)) {
  1560.             $size = $this->_font_size_pt;
  1561.         }
  1562.  
  1563.         /* If font requested is already the current font and no force setting
  1564.          * of the font is requested (eg. when adding a new page) don't bother
  1565.          * with the rest of the function and simply return. */
  1566.         if ($this->_font_family == $family && $this->_font_style == $style &&
  1567.             $this->_font_size_pt == $size && !$force) {
  1568.             return;
  1569.         }
  1570.  
  1571.         /* Set the font key. */
  1572.         $fontkey = $family . $style;
  1573.  
  1574.         /* Test if already cached. */
  1575.         if (!isset($this->_fonts[$fontkey])) {
  1576.             /* Get the character width definition file. */
  1577.             $font_widths = File_PDF::_getFontFile($fontkey);
  1578.             if (is_a($font_widths, 'PEAR_Error')) {
  1579.                 return $font_widths;
  1580.             }
  1581.  
  1582.             $i = count($this->_fonts) + 1;
  1583.             $this->_fonts[$fontkey] = array(
  1584.                 'i'    => $i,
  1585.                 'type' => 'core',
  1586.                 'name' => $this->_core_fonts[$fontkey],
  1587.                 'up'   => -100,
  1588.                 'ut'   => 50,
  1589.                 'cw'   => $font_widths[$fontkey]);
  1590.         }
  1591.  
  1592.         /* Store font information as current font. */
  1593.         $this->_font_family  = $family;
  1594.         $this->_font_style   = $style;
  1595.         $this->_font_size_pt = $size;
  1596.         $this->_font_size    = $size / $this->_scale;
  1597.         $this->_current_font = &$this->_fonts[$fontkey];
  1598.  
  1599.         /* Output font information if at least one page has been defined. */
  1600.         if ($this->_page > 0) {
  1601.             $this->_out(sprintf('BT /F%d %.2' . FILE_PDF_FLOAT . ' Tf ET', $this->_current_font['i'], $this->_font_size_pt));
  1602.         }
  1603.     }
  1604.  
  1605.     /**
  1606.      * Defines the size of the current font.
  1607.      *
  1608.      * @param float $size  The size (in points).
  1609.      *
  1610.      * @see setFont()
  1611.      */
  1612.     function setFontSize($size)
  1613.     {
  1614.         /* If the font size is already the current font size, just return. */
  1615.         if ($this->_font_size_pt == $size) {
  1616.             return;
  1617.         }
  1618.         /* Set the current font size, both in points and scaled to user
  1619.          * units. */
  1620.         $this->_font_size_pt = $size;
  1621.         $this->_font_size = $size / $this->_scale;
  1622.  
  1623.         /* Output font information if at least one page has been defined. */
  1624.         if ($this->_page > 0) {
  1625.             $this->_out(sprintf('BT /F%d %.2' . FILE_PDF_FLOAT . ' Tf ET', $this->_current_font['i'], $this->_font_size_pt));
  1626.         }
  1627.     }
  1628.  
  1629.     /**
  1630.      * Defines the style of the current font.
  1631.      *
  1632.      * @param string $style  The font style.
  1633.      *
  1634.      * @since File_PDF 0.2.0
  1635.      * @since Horde 3.2
  1636.      * @see setFont()
  1637.      */
  1638.     function setFontStyle($style)
  1639.     {
  1640.         return $this->setFont($this->_font_family, $style);
  1641.     }
  1642.  
  1643.     /**
  1644.      * Creates a new internal link and returns its identifier.
  1645.      *
  1646.      * An internal link is a clickable area which directs to another place
  1647.      * within the document.
  1648.      *
  1649.      * The identifier can then be passed to {@link cell()}, {@link()} write,
  1650.      * {@link image()} or {@link link()}. The destination is defined with
  1651.      * {@link setLink()}.
  1652.      *
  1653.      * @see cell()
  1654.      * @see write()
  1655.      * @see image()
  1656.      * @see link()
  1657.      * @see setLink()
  1658.      */
  1659.     function addLink()
  1660.     {
  1661.         $n = count($this->_links) + 1;
  1662.         $this->_links[$n] = array(0, 0);
  1663.         return $n;
  1664.     }
  1665.  
  1666.     /**
  1667.      * Defines the page and position a link points to.
  1668.      *
  1669.      * @param integer $link  The link identifier returned by {@link addLink()}.
  1670.      * @param float $y       Ordinate of target position; -1 indicates the
  1671.      *                       current position. The default value is 0 (top of
  1672.      *                       page).
  1673.      * @param integer $page  Number of target page; -1 indicates the current
  1674.      *                       page.
  1675.      *
  1676.      * @see addLink()
  1677.      */
  1678.     function setLink($link, $y = 0, $page = -1)
  1679.     {
  1680.         if ($y == -1) {
  1681.             $y = $this->y;
  1682.         }
  1683.         if ($page == -1) {
  1684.             $page = $this->_page;
  1685.         }
  1686.         $this->_links[$link] = array($page, $y);
  1687.     }
  1688.  
  1689.     /**
  1690.      * Puts a link on a rectangular area of the page.
  1691.      *
  1692.      * Text or image links are generally put via {@link cell()}, {@link
  1693.      * write()} or {@link image()}, but this method can be useful for instance
  1694.      * to define a clickable area inside an image.
  1695.      *
  1696.      * All coordinates can be negative to provide values from the right or
  1697.      * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
  1698.      *
  1699.      * @param float $x       Abscissa of the upper-left corner of the
  1700.      *                       rectangle.
  1701.      * @param float $y       Ordinate of the upper-left corner of the
  1702.      *                       rectangle.
  1703.      * @param float $width   Width of the rectangle.
  1704.      * @param float $height  Height of the rectangle.
  1705.      * @param mixed $link    URL or identifier returned by {@link addLink()}.
  1706.      *
  1707.      * @see addLink()
  1708.      * @see cell()
  1709.      * @see write()
  1710.      * @see image()
  1711.      */
  1712.     function link($x, $y, $width, $height, $link)
  1713.     {
  1714.         if ($x < 0) {
  1715.             $x += $this->w;
  1716.         }
  1717.         if ($y < 0) {
  1718.             $y += $this->h;
  1719.         }
  1720.  
  1721.         /* Set up the coordinates with correct scaling in pt. */
  1722.         $x      = $this->_toPt($x);
  1723.         $y      = $this->hPt - $this->_toPt($y);
  1724.         $width  = $this->_toPt($width);
  1725.         $height = $this->_toPt($height);
  1726.  
  1727.         /* Save link to page links array. */
  1728.         $this->_link($x, $y, $width, $height, $link);
  1729.     }
  1730.  
  1731.     /**
  1732.      * Prints a character string.
  1733.      *
  1734.      * The origin is on the left of the first character, on the baseline. This
  1735.      * method allows to place a string precisely on the page, but it is
  1736.      * usually easier to use {@link cell()}, {@link multiCell()} or {@link
  1737.      * write()} which are the standard methods to print text.
  1738.      *
  1739.      * All coordinates can be negative to provide values from the right or
  1740.      * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
  1741.      *
  1742.      * @param float $x      Abscissa of the origin.
  1743.      * @param float $y      Ordinate of the origin.
  1744.      * @param string $text  String to print.
  1745.      *
  1746.      * @see setFont()
  1747.      * @see cell()
  1748.      * @see multiCell()
  1749.      * @see write()
  1750.      */
  1751.     function text($x, $y, $text)
  1752.     {
  1753.         if ($x < 0) {
  1754.             $x += $this->w;
  1755.         }
  1756.         if ($y < 0) {
  1757.             $y += $this->h;
  1758.         }
  1759.  
  1760.         /* Scale coordinates into points and set correct Y position. */
  1761.         $x = $this->_toPt($x);
  1762.         $y = $this->hPt - $this->_toPt($y);
  1763.  
  1764.         /* Escape any potentially harmful characters. */
  1765.         $text = $this->_escape($text);
  1766.  
  1767.         $out = sprintf('BT %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' Td (%s) Tj ET', $x, $y, $text);
  1768.         if ($this->_underline && $text != '') {
  1769.             $out .= ' ' . $this->_doUnderline($x, $y, $text);
  1770.         }
  1771.         if ($this->_color_flag) {
  1772.             $out = sprintf('q %s %s Q', $this->_text_color, $out);
  1773.         }
  1774.         $this->_out($out);
  1775.     }
  1776.  
  1777.     /**
  1778.      * Whenever a page break condition is met, the method is called, and the
  1779.      * break is issued or not depending on the returned value. The default
  1780.      * implementation returns a value according to the mode selected by
  1781.      * {@link setAutoPageBreak()}.
  1782.      * This method is called automatically and should not be called directly
  1783.      * by the application.
  1784.      *
  1785.      * @return boolean
  1786.      *
  1787.      * @see setAutoPageBreak()
  1788.      */
  1789.     function acceptPageBreak()
  1790.     {
  1791.         return $this->_auto_page_break;
  1792.     }
  1793.  
  1794.     /**
  1795.      * Prints a cell (rectangular area) with optional borders, background
  1796.      * color and character string.
  1797.      *
  1798.      * The upper-left corner of the cell corresponds to the current
  1799.      * position. The text can be aligned or centered. After the call, the
  1800.      * current position moves to the right or to the next line. It is possible
  1801.      * to put a link on the text.  If automatic page breaking is enabled and
  1802.      * the cell goes beyond the limit, a page break is done before outputting.
  1803.      *
  1804.      * @param float $width   Cell width. If 0, the cell extends up to the right
  1805.      *                       margin.
  1806.      * @param float $height  Cell height.
  1807.      * @param string $text   String to print.
  1808.      * @param mixed $border  Indicates if borders must be drawn around the
  1809.      *                       cell. The value can be either a number:
  1810.      *                         - 0: no border (default)
  1811.      *                         - 1: frame
  1812.      *                       or a string containing some or all of the
  1813.      *                       following characters (in any order):
  1814.      *                         - L: left
  1815.      *                         - T: top
  1816.      *                         - R: right
  1817.      *                         - B: bottom
  1818.      * @param integer $ln    Indicates where the current position should go
  1819.      *                       after the call. Possible values are:
  1820.      *                         - 0: to the right (default)
  1821.      *                         - 1: to the beginning of the next line
  1822.      *                         - 2: below
  1823.      *                       Putting 1 is equivalent to putting 0 and calling
  1824.      *                       {@link newLine()} just after.
  1825.      * @param string $align  Allows to center or align the text. Possible
  1826.      *                       values are:
  1827.      *                         - L or empty string: left (default)
  1828.      *                         - C: center
  1829.      *                         - R: right
  1830.      * @param integer $fill  Indicates if the cell fill type. Possible values
  1831.      *                       are:
  1832.      *                         - 0: transparent (default)
  1833.      *                         - 1: painted
  1834.      * @param string $link   URL or identifier returned by {@link addLink()}.
  1835.      *
  1836.      * @see setFont()
  1837.      * @see setDrawColor()
  1838.      * @see setFillColor()
  1839.      * @see setLineWidth()
  1840.      * @see addLink()
  1841.      * @see newLine()
  1842.      * @see multiCell()
  1843.      * @see write()
  1844.      * @see setAutoPageBreak()
  1845.      */
  1846.     function cell($width, $height = 0, $text = '', $border = 0, $ln = 0,
  1847.                   $align = '', $fill = 0, $link = '')
  1848.     {
  1849.         $k = $this->_scale;
  1850.         if ($this->y + $height > $this->_page_break_trigger &&
  1851.             !$this->_in_footer && $this->acceptPageBreak()) {
  1852.             $x = $this->x;
  1853.             $ws = $this->_word_spacing;
  1854.             if ($ws > 0) {
  1855.                 $this->_word_spacing = 0;
  1856.                 $this->_out('0 Tw');
  1857.             }
  1858.             $result = $this->addPage($this->_current_orientation);
  1859.             if (is_a($result, 'PEAR_Error')) {
  1860.                 return $result;
  1861.             }
  1862.             $this->x = $x;
  1863.             if ($ws > 0) {
  1864.                 $this->_word_spacing = $ws;
  1865.                 $this->_out(sprintf('%.3' . FILE_PDF_FLOAT . ' Tw', $ws * $k));
  1866.             }
  1867.         }
  1868.         if ($width == 0) {
  1869.             $width = $this->w - $this->_right_margin - $this->x;
  1870.         }
  1871.         $s = '';
  1872.         if ($fill == 1 || $border == 1) {
  1873.             if ($fill == 1) {
  1874.                 $op = ($border == 1) ? 'B' : 'f';
  1875.             } else {
  1876.                 $op = 'S';
  1877.             }
  1878.             $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);
  1879.         }
  1880.         if (is_string($border)) {
  1881.             if (strpos($border, 'L') !== false) {
  1882.                 $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);
  1883.             }
  1884.             if (strpos($border, 'T') !== false) {
  1885.                 $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);
  1886.             }
  1887.             if (strpos($border, 'R') !== false) {
  1888.                 $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);
  1889.             }
  1890.             if (strpos($border, 'B') !== false) {
  1891.                 $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);
  1892.             }
  1893.         }
  1894.         if ($text != '') {
  1895.             if ($align == 'R') {
  1896.                 $dx = $width - $this->_cell_margin - $this->getStringWidth($text);
  1897.             } elseif ($align == 'C') {
  1898.                 $dx = ($width - $this->getStringWidth($text)) / 2;
  1899.             } else {
  1900.                 $dx = $this->_cell_margin;
  1901.             }
  1902.             if ($this->_color_flag) {
  1903.                 $s .= 'q ' . $this->_text_color . ' ';
  1904.             }
  1905.             $text = str_replace(')', '\\)', str_replace('(', '\\(', str_replace('\\', '\\\\', $text)));
  1906.             $test2 = ((.5 * $height) + (.3 * $this->_font_size));
  1907.             $test1 = $this->fhPt - (($this->y + $test2) * $k);
  1908.             $x = ($this->x + $dx) * $k;
  1909.             $y = ($this->h - ($this->y + .5 * $height + .3 * $this->_font_size)) * $k;
  1910.             $s .= sprintf('BT %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' Td (%s) Tj ET', $x, $y, $text);
  1911.             if ($this->_underline) {
  1912.                 $s .= ' ' . $this->_doUnderline($x, $y, $text);
  1913.             }
  1914.             if ($this->_color_flag) {
  1915.                 $s .= ' Q';
  1916.             }
  1917.             if ($link) {
  1918.                 $this->link($this->x + $dx, $this->y + .5 * $height- .5 * $this->_font_size, $this->getStringWidth($text), $this->_font_size, $link);
  1919.             }
  1920.         }
  1921.         if ($s) {
  1922.             $this->_out($s);
  1923.         }
  1924.         $this->_last_height = $height;
  1925.         if ($ln > 0) {
  1926.             /* Go to next line. */
  1927.             $this->y += $height;
  1928.             if ($ln == 1) {
  1929.                 $this->x = $this->_left_margin;
  1930.             }
  1931.         } else {
  1932.             $this->x += $width;
  1933.         }
  1934.     }
  1935.  
  1936.     /**
  1937.      * This method allows printing text with line breaks.
  1938.      *
  1939.      * They can be automatic (as soon as the text reaches the right border of
  1940.      * the cell) or explicit (via the \n character). As many cells as
  1941.      * necessary are output, one below the other. Text can be aligned,
  1942.      * centered or justified. The cell block can be framed and the background
  1943.      * painted.
  1944.      *
  1945.      * @param float $width   Width of cells. If 0, they extend up to the right
  1946.      *                       margin of the page.
  1947.      * @param float $height  Height of cells.
  1948.      * @param string $text   String to print.
  1949.      * @param mixed $border  Indicates if borders must be drawn around the cell
  1950.      *                       block. The value can be either a number:
  1951.      *                         - 0: no border (default)
  1952.      *                         - 1: frame
  1953.      *                       or a string containing some or all of the
  1954.      *                       following characters (in any order):
  1955.      *                         - L: left
  1956.      *                         - T: top
  1957.      *                         - R: right
  1958.      *                         - B: bottom
  1959.      * @param string $align  Sets the text alignment. Possible values are:
  1960.      *                         - L: left alignment
  1961.      *                         - C: center
  1962.      *                         - R: right alignment
  1963.      *                         - J: justification (default value)
  1964.      * @param integer $fill  Indicates if the cell background must:
  1965.      *                         - 0: transparent (default)
  1966.      *                         - 1: painted
  1967.      *
  1968.      * @see setFont()
  1969.      * @see setDrawColor()
  1970.      * @see setFillColor()
  1971.      * @see setLineWidth()
  1972.      * @see cell()
  1973.      * @see write()
  1974.      * @see setAutoPageBreak()
  1975.      */
  1976.     function multiCell($width, $height, $text, $border = 0, $align = 'J',
  1977.                        $fill = 0)
  1978.     {
  1979.         $cw = &$this->_current_font['cw'];
  1980.         if ($width == 0) {
  1981.             $width = $this->w - $this->_right_margin - $this->x;
  1982.         }
  1983.         $wmax = ($width-2 * $this->_cell_margin) * 1000 / $this->_font_size;
  1984.         $s = str_replace("\r", '', $text);
  1985.         $nb = strlen($s);
  1986.         if ($nb > 0 && $s[$nb-1] == "\n") {
  1987.             $nb--;
  1988.         }
  1989.         $b = 0;
  1990.         if ($border) {
  1991.             if ($border == 1) {
  1992.                 $border = 'LTRB';
  1993.                 $b = 'LRT';
  1994.                 $b2 = 'LR';
  1995.             } else {
  1996.                 $b2 = '';
  1997.                 if (strpos($border, 'L') !== false) {
  1998.                     $b2 .= 'L';
  1999.                 }
  2000.                 if (strpos($border, 'R') !== false) {
  2001.                     $b2 .= 'R';
  2002.                 }
  2003.                 $b = (strpos($border, 'T') !== false) ? $b2 . 'T' : $b2;
  2004.             }
  2005.         }
  2006.         $sep = -1;
  2007.         $i   = 0;
  2008.         $j   = 0;
  2009.         $l   = 0;
  2010.         $ns  = 0;
  2011.         $nl  = 1;
  2012.         while ($i < $nb) {
  2013.             /* Get next character. */
  2014.             $c = $s[$i];
  2015.             if ($c == "\n") {
  2016.                 /* Explicit line break. */
  2017.                 if ($this->_word_spacing > 0) {
  2018.                     $this->_word_spacing = 0;
  2019.                     $this->_out('0 Tw');
  2020.                 }
  2021.                 $result = $this->cell($width, $height, substr($s, $j, $i-$j),
  2022.                                       $b, 2, $align, $fill);
  2023.                 if (is_a($result, 'PEAR_Error')) {
  2024.                     return $result;
  2025.                 }
  2026.                 $i++;
  2027.                 $sep = -1;
  2028.                 $j = $i;
  2029.                 $l = 0;
  2030.                 $ns = 0;
  2031.                 $nl++;
  2032.                 if ($border && $nl == 2) {
  2033.                     $b = $b2;
  2034.                 }
  2035.                 continue;
  2036.             }
  2037.             if ($c == ' ') {
  2038.                 $sep = $i;
  2039.                 $ls = $l;
  2040.                 $ns++;
  2041.             }
  2042.             $l += $cw[$c];
  2043.             if ($l > $wmax) {
  2044.                 /* Automatic line break. */
  2045.                 if ($sep == -1) {
  2046.                     if ($i == $j) {
  2047.                         $i++;
  2048.                     }
  2049.                     if ($this->_word_spacing > 0) {
  2050.                         $this->_word_spacing = 0;
  2051.                         $this->_out('0 Tw');
  2052.                     }
  2053.                     $result = $this->cell($width, $height,
  2054.                                           substr($s, $j, $i - $j), $b, 2,
  2055.                                           $align, $fill);
  2056.                     if (is_a($result, 'PEAR_Error')) {
  2057.                         return $result;
  2058.                     }
  2059.                 } else {
  2060.                     if ($align == 'J') {
  2061.                         $this->_word_spacing = ($ns>1)
  2062.                             ? ($wmax - $ls) / 1000 * $this->_font_size / ($ns - 1)
  2063.                             : 0;
  2064.                         $this->_out(sprintf('%.3' . FILE_PDF_FLOAT . ' Tw',
  2065.                                             $this->_word_spacing * $this->_scale));
  2066.                     }
  2067.                     $result = $this->cell($width, $height,
  2068.                                           substr($s, $j, $sep - $j),
  2069.                                           $b, 2, $align, $fill);
  2070.                     if (is_a($result, 'PEAR_Error')) {
  2071.                         return $result;
  2072.                     }
  2073.                     $i = $sep + 1;
  2074.                 }
  2075.                 $sep = -1;
  2076.                 $j = $i;
  2077.                 $l = 0;
  2078.                 $ns = 0;
  2079.                 $nl++;
  2080.                 if ($border && $nl == 2) {
  2081.                     $b = $b2;
  2082.                 }
  2083.             } else {
  2084.                 $i++;
  2085.             }
  2086.         }
  2087.         /* Last chunk. */
  2088.         if ($this->_word_spacing > 0) {
  2089.             $this->_word_spacing = 0;
  2090.             $this->_out('0 Tw');
  2091.         }
  2092.         if ($border && strpos($border, 'B') !== false) {
  2093.             $b .= 'B';
  2094.         }
  2095.         $result = $this->cell($width, $height, substr($s, $j, $i), $b, 2,
  2096.                               $align, $fill);
  2097.         if (is_a($result, 'PEAR_Error')) {
  2098.             return $result;
  2099.         }
  2100.         $this->x = $this->_left_margin;
  2101.     }
  2102.  
  2103.     /**
  2104.      * This method prints text from the current position.
  2105.      *
  2106.      * When the right margin is reached (or the \n character is met) a line
  2107.      * break occurs and text continues from the left margin. Upon method exit,
  2108.      * the current position is left just at the end of the text.
  2109.      *
  2110.      * It is possible to put a link on the text.
  2111.      *
  2112.      * Example:
  2113.      * <code>
  2114.      * // Begin with regular font
  2115.      * $pdf->setFont('Arial', '', 14);
  2116.      * $pdf->write(5, 'Visit ');
  2117.      * // Then put a blue underlined link
  2118.      * $pdf->setTextColor(0, 0, 255);
  2119.      * $pdf->setFont('', 'U');
  2120.      * $pdf->write(5, 'www.fpdf.org', 'http://www.fpdf.org');
  2121.      * </code>
  2122.      *
  2123.      * @param float $height  Line height.
  2124.      * @param string $text   String to print.
  2125.      * @param mixed $link    URL or identifier returned by {@link addLink()}.
  2126.      *
  2127.      * @see setFont()
  2128.      * @see addLink()
  2129.      * @see multiCell()
  2130.      * @see setAutoPageBreak()
  2131.      */
  2132.     function write($height, $text, $link = '')
  2133.     {
  2134.         $cw = &$this->_current_font['cw'];
  2135.         $width = $this->w - $this->_right_margin - $this->x;
  2136.         $wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
  2137.         $s = str_replace("\r", '', $text);
  2138.         $nb = strlen($s);
  2139.         $sep = -1;
  2140.         $i = 0;
  2141.         $j = 0;
  2142.         $l = 0;
  2143.         $nl = 1;
  2144.         while ($i < $nb) {
  2145.             /* Get next character. */
  2146.             $c = $s{$i};
  2147.             if ($c == "\n") {
  2148.                 /* Explicit line break. */
  2149.                 $result = $this->cell($width, $height, substr($s, $j, $i - $j),
  2150.                                       0, 2, '', 0, $link);
  2151.                 if (is_a($result, 'PEAR_Error')) {
  2152.                     return $result;
  2153.                 }
  2154.                 $i++;
  2155.                 $sep = -1;
  2156.                 $j = $i;
  2157.                 $l = 0;
  2158.                 if ($nl == 1) {
  2159.                     $this->x = $this->_left_margin;
  2160.                     $width = $this->w - $this->_right_margin - $this->x;
  2161.                     $wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
  2162.                 }
  2163.                 $nl++;
  2164.                 continue;
  2165.             }
  2166.             if ($c == ' ') {
  2167.                 $sep = $i;
  2168.                 $ls = $l;
  2169.             }
  2170.             $l += (isset($cw[$c]) ? $cw[$c] : 0);
  2171.             if ($l > $wmax) {
  2172.                 /* Automatic line break. */
  2173.                 if ($sep == -1) {
  2174.                     if ($this->x > $this->_left_margin) {
  2175.                         /* Move to next line. */
  2176.                         $this->x = $this->_left_margin;
  2177.                         $this->y += $height;
  2178.                         $width = $this->w - $this->_right_margin - $this->x;
  2179.                         $wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
  2180.                         $i++;
  2181.                         $nl++;
  2182.                         continue;
  2183.                     }
  2184.                     if ($i == $j) {
  2185.                         $i++;
  2186.                     }
  2187.                     $result = $this->cell($width, $height,
  2188.                                           substr($s, $j, $i - $j),
  2189.                                           0, 2, '', 0, $link);
  2190.                     if (is_a($result, 'PEAR_Error')) {
  2191.                         return $result;
  2192.                     }
  2193.                 } else {
  2194.                     $result = $this->cell($width, $height,
  2195.                                           substr($s, $j, $sep - $j),
  2196.                                           0, 2, '', 0, $link);
  2197.                     if (is_a($result, 'PEAR_Error')) {
  2198.                         return $result;
  2199.                     }
  2200.                     $i = $sep + 1;
  2201.                 }
  2202.                 $sep = -1;
  2203.                 $j = $i;
  2204.                 $l = 0;
  2205.                 if ($nl == 1) {
  2206.                     $this->x = $this->_left_margin;
  2207.                     $width = $this->w - $this->_right_margin - $this->x;
  2208.                     $wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
  2209.                 }
  2210.                 $nl++;
  2211.             } else {
  2212.                 $i++;
  2213.             }
  2214.         }
  2215.         /* Last chunk. */
  2216.         if ($i != $j) {
  2217.             $result = $this->cell($l / 1000 * $this->_font_size, $height,
  2218.                                   substr($s, $j, $i), 0, 0, '', 0, $link);
  2219.             if (is_a($result, 'PEAR_Error')) {
  2220.                 return $result;
  2221.             }
  2222.         }
  2223.     }
  2224.  
  2225.     /**
  2226.      * Writes text at an angle.
  2227.      *
  2228.      * All coordinates can be negative to provide values from the right or
  2229.      * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
  2230.      *
  2231.      * @param integer $x         X coordinate.
  2232.      * @param integer $y         Y coordinate.
  2233.      * @param string $text       Text to write.
  2234.      * @param float $text_angle  Angle to rotate (Eg. 90 = bottom to top).
  2235.      * @param float $font_angle  Rotate characters as well as text.
  2236.      *
  2237.      * @see setFont()
  2238.      */
  2239.     function writeRotated($x, $y, $text, $text_angle, $font_angle = 0)
  2240.     {
  2241.         if ($x < 0) {
  2242.             $x += $this->w;
  2243.         }
  2244.         if ($y < 0) {
  2245.             $y += $this->h;
  2246.         }
  2247.  
  2248.         /* Escape text. */
  2249.         $text = $this->_escape($text);
  2250.  
  2251.         $font_angle += 90 + $text_angle;
  2252.         $text_angle *= M_PI / 180;
  2253.         $font_angle *= M_PI / 180;
  2254.  
  2255.         $text_dx = cos($text_angle);
  2256.         $text_dy = sin($text_angle);
  2257.         $font_dx = cos($font_angle);
  2258.         $font_dy = sin($font_angle);
  2259.  
  2260.         $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',
  2261.                     $text_dx, $text_dy, $font_dx, $font_dy,
  2262.                     $x * $this->_scale, ($this->h-$y) * $this->_scale, $text);
  2263.  
  2264.         if ($this->_draw_color) {
  2265.             $s = 'q ' . $this->_draw_color . ' ' . $s . ' Q';
  2266.         }
  2267.         $this->_out($s);
  2268.     }
  2269.  
  2270.     /**
  2271.      * Prints an image in the page.
  2272.      *
  2273.      * The upper-left corner and at least one of the dimensions must be
  2274.      * specified; the height or the width can be calculated automatically in
  2275.      * order to keep the image proportions. Supported formats are JPEG and
  2276.      * PNG.
  2277.      *
  2278.      * All coordinates can be negative to provide values from the right or
  2279.      * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
  2280.      *
  2281.      * For JPEG, all flavors are allowed:
  2282.      *   - gray scales
  2283.      *   - true colors (24 bits)
  2284.      *   - CMYK (32 bits)
  2285.      *
  2286.      * For PNG, are allowed:
  2287.      *   - gray scales on at most 8 bits (256 levels)
  2288.      *   - indexed colors
  2289.      *   - true colors (24 bits)
  2290.      * but are not supported:
  2291.      *   - Interlacing
  2292.      *   - Alpha channel
  2293.      *
  2294.      * If a transparent color is defined, it will be taken into account (but
  2295.      * will be only interpreted by Acrobat 4 and above).
  2296.      * The format can be specified explicitly or inferred from the file
  2297.      * extension.
  2298.      * It is possible to put a link on the image.
  2299.      *
  2300.      * Remark: if an image is used several times, only one copy will be
  2301.      * embedded in the file.
  2302.      *
  2303.      * @param string $file   Name of the file containing the image.
  2304.      * @param float $x       Abscissa of the upper-left corner.
  2305.      * @param float $y       Ordinate of the upper-left corner.
  2306.      * @param float $width   Width of the image in the page. If equal to zero,
  2307.      *                       it is automatically calculated to keep the
  2308.      *                       original proportions.
  2309.      * @param float $height  Height of the image in the page. If not specified
  2310.      *                       or equal to zero, it is automatically calculated
  2311.      *                       to keep the original proportions.
  2312.      * @param string $type   Image format. Possible values are (case
  2313.      *                       insensitive): JPG, JPEG, PNG. If not specified,
  2314.      *                       the type is inferred from the file extension.
  2315.      * @param mixed $link    URL or identifier returned by {@link addLink()}.
  2316.      *
  2317.      * @see addLink()
  2318.      */
  2319.     function image($file, $x, $y, $width = 0, $height = 0, $type = '',
  2320.                    $link = '')
  2321.     {
  2322.         if ($x < 0) {
  2323.             $x += $this->w;
  2324.         }
  2325.         if ($y < 0) {
  2326.             $y += $this->h;
  2327.         }
  2328.  
  2329.         if (!isset($this->_images[$file])) {
  2330.             /* First use of image, get some file info. */
  2331.             if ($type == '') {
  2332.                 $pos = strrpos($file, '.');
  2333.                 if ($pos === false) {
  2334.                     return $this->raiseError(sprintf('Image file has no extension and no type was specified: %s', $file));
  2335.                 }
  2336.                 $type = substr($file, $pos + 1);
  2337.             }
  2338.             $type = strtolower($type);
  2339.             $mqr = get_magic_quotes_runtime();
  2340.             set_magic_quotes_runtime(0);
  2341.             if ($type == 'jpg' || $type == 'jpeg') {
  2342.                 $info = $this->_parseJPG($file);
  2343.             } elseif ($type == 'png') {
  2344.                 $info = $this->_parsePNG($file);
  2345.             } else {
  2346.                 return $this->raiseError(sprintf('Unsupported image file type: %s', $type));
  2347.             }
  2348.             if (is_a($info, 'PEAR_Error')) {
  2349.                 return $info;
  2350.             }
  2351.             set_magic_quotes_runtime($mqr);
  2352.             $info['i'] = count($this->_images) + 1;
  2353.             $this->_images[$file] = $info;
  2354.         } else {
  2355.             $info = $this->_images[$file];
  2356.         }
  2357.  
  2358.         /* Make sure all vars are converted to pt scale. */
  2359.         $x      = $this->_toPt($x);
  2360.         $y      = $this->hPt - $this->_toPt($y);
  2361.         $width  = $this->_toPt($width);
  2362.         $height = $this->_toPt($height);
  2363.  
  2364.         /* If not specified do automatic width and height calculations. */
  2365.         if (empty($width) && empty($height)) {
  2366.             $width = $info['w'];
  2367.             $height = $info['h'];
  2368.         } elseif (empty($width)) {
  2369.             $width = $height * $info['w'] / $info['h'];
  2370.         } elseif (empty($height)) {
  2371.             $height = $width * $info['h'] / $info['w'];
  2372.         }
  2373.  
  2374.         $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']));
  2375.  
  2376.         /* Set any link if requested. */
  2377.         if ($link) {
  2378.             $this->_link($x, $y, $width, $height, $link);
  2379.         }
  2380.     }
  2381.  
  2382.     /**
  2383.      * Performs a line break.
  2384.      *
  2385.      * The current abscissa goes back to the left margin and the ordinate
  2386.      * increases by the amount passed in parameter.
  2387.      *
  2388.      * @param float $height  The height of the break. By default, the value
  2389.      *                       equals the height of the last printed cell.
  2390.      *
  2391.      * @see cell()
  2392.      */
  2393.     function newLine($height = '')
  2394.     {
  2395.         $this->x = $this->_left_margin;
  2396.         if (is_string($height)) {
  2397.             $this->y += $this->_last_height;
  2398.         } else {
  2399.             $this->y += $height;
  2400.         }
  2401.     }
  2402.  
  2403.     /**
  2404.      * Returns the abscissa of the current position in user units.
  2405.      *
  2406.      * @return float
  2407.      *
  2408.      * @see setX()
  2409.      * @see getY()
  2410.      * @see setY()
  2411.      */
  2412.     function getX()
  2413.     {
  2414.         return $this->x;
  2415.     }
  2416.  
  2417.     /**
  2418.      * Defines the abscissa of the current position.
  2419.      *
  2420.      * If the passed value is negative, it is relative to the right of the
  2421.      * page.
  2422.      *
  2423.      * @param float $x  The value of the abscissa.
  2424.      *
  2425.      * @see getX()
  2426.      * @see getY()
  2427.      * @see setY()
  2428.      * @see setXY()
  2429.      */
  2430.     function setX($x)
  2431.     {
  2432.         if ($x >= 0) {
  2433.             /* Absolute value. */
  2434.             $this->x = $x;
  2435.         } else {
  2436.             /* Negative, so relative to right edge of the page. */
  2437.             $this->x = $this->w + $x;
  2438.         }
  2439.     }
  2440.  
  2441.     /**
  2442.      * Returns the ordinate of the current position in user units.
  2443.      *
  2444.      * @return float
  2445.      *
  2446.      * @see setY()
  2447.      * @see getX()
  2448.      * @see setX()
  2449.      */
  2450.     function getY()
  2451.     {
  2452.         return $this->y;
  2453.     }
  2454.  
  2455.     /**
  2456.      * Defines the ordinate of the current position.
  2457.      *
  2458.      * If the passed value is negative, it is relative to the bottom of the
  2459.      * page.
  2460.      *
  2461.      * @param float $y  The value of the ordinate.
  2462.      *
  2463.      * @see getX()
  2464.      * @see getY()
  2465.      * @see setY()
  2466.      * @see setXY()
  2467.      */
  2468.     function setY($y)
  2469.     {
  2470.         if ($y >= 0) {
  2471.             /* Absolute value. */
  2472.             $this->y = $y;
  2473.         } else {
  2474.             /* Negative, so relative to bottom edge of the page. */
  2475.             $this->y = $this->h + $y;
  2476.         }
  2477.     }
  2478.  
  2479.     /**
  2480.      * Defines the abscissa and ordinate of the current position.
  2481.      *
  2482.      * If the passed values are negative, they are relative respectively to
  2483.      * the right and bottom of the page.
  2484.      *
  2485.      * @param float $x  The value of the abscissa.
  2486.      * @param float $y  The value of the ordinate.
  2487.      *
  2488.      * @see setX()
  2489.      * @see setY()
  2490.      */
  2491.     function setXY($x, $y)
  2492.     {
  2493.         $this->setY($y);
  2494.         $this->setX($x);
  2495.     }
  2496.  
  2497.     /**
  2498.      * Returns the current buffer content and resets the buffer.
  2499.      *
  2500.      * Use this method when creating large files to avoid memory problems.
  2501.      * This method doesn't work in combination with the output() or save()
  2502.      * methods, use getOutput() at the end. Calling this method doubles the
  2503.      * memory usage during the call.
  2504.      *
  2505.      * @since File_PDF 0.2.0
  2506.      * @since Horde 3.2
  2507.      * @see getOutput()
  2508.      */
  2509.     function flush()
  2510.     {
  2511.         // Make sure we have the file header.
  2512.         $this->_beginDoc();
  2513.  
  2514.         $buffer = $this->_buffer;
  2515.         $this->_buffer = '';
  2516.         $this->_flushed = true;
  2517.         $this->_buflen += strlen($buffer);
  2518.  
  2519.         return $buffer;
  2520.     }
  2521.  
  2522.     /**
  2523.      * Returns the raw PDF file.
  2524.      *
  2525.      * @see output()
  2526.      * @see flush()
  2527.      */
  2528.     function getOutput()
  2529.     {
  2530.         /* Check whether file has been closed. */
  2531.         if ($this->_state < 3) {
  2532.             $result = $this->close();
  2533.             if (is_a($result, 'PEAR_Error')) {
  2534.                 return $result;
  2535.             }
  2536.         }
  2537.  
  2538.         return $this->_buffer;
  2539.     }
  2540.  
  2541.     /**
  2542.      * Sends the buffered data to the browser.
  2543.      *
  2544.      * @param string $filename  The filename for the output file.
  2545.      * @param boolean $inline   True if inline, false if attachment.
  2546.      */
  2547.     function output($filename = 'unknown.pdf', $inline = false)
  2548.     {
  2549.         /* Check whether the buffer has been flushed already. */
  2550.         if ($this->_flushed) {
  2551.             return $this->raiseError('The buffer has been flushed already, don\'t use output() in combination with flush().');
  2552.         }
  2553.  
  2554.         /* Check whether file has been closed. */
  2555.         if ($this->_state < 3) {
  2556.             $result = $this->close();
  2557.             if (is_a($result, 'PEAR_Error')) {
  2558.                 return $result;
  2559.             }
  2560.         }
  2561.  
  2562.         /* Check if headers have been sent. */
  2563.         if (headers_sent()) {
  2564.             return $this->raiseError('Unable to send PDF file, some data has already been output to browser');
  2565.         }
  2566.  
  2567.         /* If HTTP_Download is not available return a PEAR_Error. */
  2568.         if (!include_once 'HTTP/Download.php') {
  2569.             return $this->raiseError('Missing PEAR package HTTP_Download');
  2570.         }
  2571.  
  2572.         /* Params for the output. */
  2573.         $disposition = $inline ? HTTP_DOWNLOAD_INLINE : HTTP_DOWNLOAD_ATTACHMENT;
  2574.         $params = array('data'               => $this->_buffer,
  2575.                         'contenttype'        => 'application/pdf',
  2576.                         'contentdisposition' => array($disposition, $filename));
  2577.         /* Output the file. */
  2578.         return HTTP_Download::staticSend($params);
  2579.     }
  2580.  
  2581.     /**
  2582.      * Saves the PDF file on the filesystem.
  2583.      *
  2584.      * @param string $filename  The filename for the output file.
  2585.      */
  2586.     function save($filename = 'unknown.pdf')
  2587.     {
  2588.         /* Check whether the buffer has been flushed already. */
  2589.         if ($this->_flushed) {
  2590.             return $this->raiseError('The buffer has been flushed already, don\'t use save() in combination with flush().');
  2591.         }
  2592.  
  2593.         /* Check whether file has been closed. */
  2594.         if ($this->_state < 3) {
  2595.             $result = $this->close();
  2596.             if (is_a($result, 'PEAR_Error')) {
  2597.                 return $result;
  2598.             }
  2599.         }
  2600.  
  2601.         $f = fopen($filename, 'wb');
  2602.         if (!$f) {
  2603.             return $this->raiseError(sprintf('Unable to save PDF file: %s', $filename));
  2604.         }
  2605.         fwrite($f, $this->_buffer, strlen($this->_buffer));
  2606.         fclose($f);
  2607.     }
  2608.  
  2609.     function _toPt($val)
  2610.     {
  2611.         return $val * $this->_scale;
  2612.     }
  2613.  
  2614.     function _getFontFile($fontkey, $path = '')
  2615.     {
  2616.         static $font_widths = array();
  2617.  
  2618.         if (!isset($font_widths[$fontkey])) {
  2619.             if (!empty($path)) {
  2620.                 $file = $path . strtolower($fontkey) . '.php';
  2621.             } else {
  2622.                 $file = 'File/PDF/fonts/' . strtolower($fontkey) . '.php';
  2623.             }
  2624.             include $file;
  2625.             if (!isset($font_widths[$fontkey])) {
  2626.                 return $this->raiseError(sprintf('Could not include font metric file: %s', $file));
  2627.             }
  2628.         }
  2629.  
  2630.         return $font_widths;
  2631.     }
  2632.  
  2633.     function _link($x, $y, $width, $height, $link)
  2634.     {
  2635.         /* Save link to page links array. */
  2636.         $this->_page_links[$this->_page][] = array($x, $y, $width, $height, $link);
  2637.     }
  2638.  
  2639.     function _beginDoc()
  2640.     {
  2641.         /* Start document, but only if not yet started. */
  2642.         if ($this->_state < 1) {
  2643.             $this->_state = 1;
  2644.             $this->_out('%PDF-1.3');
  2645.         }
  2646.     }
  2647.  
  2648.     function _putPages()
  2649.     {
  2650.         $nb = $this->_page;
  2651.         if (!empty($this->_alias_nb_pages)) {
  2652.             /* Replace number of pages. */
  2653.             for ($n = 1; $n <= $nb; $n++) {
  2654.                 $this->_pages[$n] = str_replace($this->_alias_nb_pages, $nb, $this->_pages[$n]);
  2655.             }
  2656.         }
  2657.         if ($this->_default_orientation == 'P') {
  2658.             $wPt = $this->fwPt;
  2659.             $hPt = $this->fhPt;
  2660.         } else {
  2661.             $wPt = $this->fhPt;
  2662.             $hPt = $this->fwPt;
  2663.         }
  2664.         $filter = ($this->_compress) ? '/Filter /FlateDecode ' : '';
  2665.         for ($n = 1; $n <= $nb; $n++) {
  2666.             /* Page */
  2667.             $this->_newobj();
  2668.             $this->_out('<</Type /Page');
  2669.             $this->_out('/Parent 1 0 R');
  2670.             if (isset($this->_orientation_changes[$n])) {
  2671.                 $this->_out(sprintf('/MediaBox [0 0 %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ']', $hPt, $wPt));
  2672.             }
  2673.             $this->_out('/Resources 2 0 R');
  2674.             if (isset($this->_page_links[$n])) {
  2675.                 /* Links */
  2676.                 $annots = '/Annots [';
  2677.                 foreach ($this->_page_links[$n] as $pl) {
  2678.                     $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]);
  2679.                     $annots .= '<</Type /Annot /Subtype /Link /Rect [' . $rect . '] /Border [0 0 0] ';
  2680.                     if (is_string($pl[4])) {
  2681.                         $annots .= '/A <</S /URI /URI ' . $this->_textString($pl[4]) . '>>>>';
  2682.                     } else {
  2683.                         $l = $this->_links[$pl[4]];
  2684.                         $height = isset($this->_orientation_changes[$l[0]]) ? $wPt : $hPt;
  2685.                         $annots .= sprintf('/Dest [%d 0 R /XYZ 0 %.2' . FILE_PDF_FLOAT . ' null]>>', 1 + 2 * $l[0], $height - $l[1] * $this->_scale);
  2686.                     }
  2687.                 }
  2688.                 $this->_out($annots . ']');
  2689.             }
  2690.             $this->_out('/Contents ' . ($this->_n + 1) . ' 0 R>>');
  2691.             $this->_out('endobj');
  2692.             /* Page content */
  2693.             $p = ($this->_compress) ? gzcompress($this->_pages[$n]) : $this->_pages[$n];
  2694.             $this->_newobj();
  2695.             $this->_out('<<' . $filter . '/Length ' . strlen($p) . '>>');
  2696.             $this->_putStream($p);
  2697.             $this->_out('endobj');
  2698.         }
  2699.         /* Pages root */
  2700.         $this->_offsets[1] = $this->_buflen + strlen($this->_buffer);
  2701.         $this->_out('1 0 obj');
  2702.         $this->_out('<</Type /Pages');
  2703.         $kids = '/Kids [';
  2704.         for ($i = 0; $i < $nb; $i++) {
  2705.             $kids .= (3 + 2 * $i) . ' 0 R ';
  2706.         }
  2707.         $this->_out($kids . ']');
  2708.         $this->_out('/Count ' . $nb);
  2709.         $this->_out(sprintf('/MediaBox [0 0 %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ']', $wPt, $hPt));
  2710.         $this->_out('>>');
  2711.         $this->_out('endobj');
  2712.     }
  2713.  
  2714.     function _putFonts()
  2715.     {
  2716.         $nf = $this->_n;
  2717.         foreach ($this->_diffs as $diff) {
  2718.             /* Encodings */
  2719.             $this->_newobj();
  2720.             $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [' . $diff . ']>>');
  2721.             $this->_out('endobj');
  2722.         }
  2723.         $mqr = get_magic_quotes_runtime();
  2724.         set_magic_quotes_runtime(0);
  2725.         foreach ($this->_font_files as $file => $info) {
  2726.             /* Font file embedding. */
  2727.             $this->_newobj();
  2728.             $this->_font_files[$file]['n'] = $this->_n;
  2729.             $size = filesize($file);
  2730.             if (!$size) {
  2731.                 return $this->raiseError('Font file not found');
  2732.             }
  2733.             $this->_out('<</Length ' . $size);
  2734.             if (substr($file, -2) == '.z') {
  2735.                 $this->_out('/Filter /FlateDecode');
  2736.             }
  2737.             $this->_out('/Length1 ' . $info['length1']);
  2738.             if (isset($info['length2'])) {
  2739.                 $this->_out('/Length2 ' . $info['length2'] . ' /Length3 0');
  2740.             }
  2741.             $this->_out('>>');
  2742.             $f = fopen($file, 'rb');
  2743.             $this->_putStream(fread($f, $size));
  2744.             fclose($f);
  2745.             $this->_out('endobj');
  2746.         }
  2747.         set_magic_quotes_runtime($mqr);
  2748.         foreach ($this->_fonts as $k => $font) {
  2749.             /* Font objects */
  2750.             $this->_newobj();
  2751.             $this->_fonts[$k]['n'] = $this->_n;
  2752.             $name = $font['name'];
  2753.             $this->_out('<</Type /Font');
  2754.             $this->_out('/BaseFont /' . $name);
  2755.             if ($font['type'] == 'core') {
  2756.                 /* Standard font. */
  2757.                 $this->_out('/Subtype /Type1');
  2758.                 if ($name != 'Symbol' && $name != 'ZapfDingbats') {
  2759.                     $this->_out('/Encoding /WinAnsiEncoding');
  2760.                 }
  2761.             } else {
  2762.                 /* Additional font. */
  2763.                 $this->_out('/Subtype /' . $font['type']);
  2764.                 $this->_out('/FirstChar 32');
  2765.                 $this->_out('/LastChar 255');
  2766.                 $this->_out('/Widths ' . ($this->_n + 1) . ' 0 R');
  2767.                 $this->_out('/FontDescriptor ' . ($this->_n + 2) . ' 0 R');
  2768.                 if ($font['enc']) {
  2769.                     if (isset($font['diff'])) {
  2770.                         $this->_out('/Encoding ' . ($nf + $font['diff']) . ' 0 R');
  2771.                     } else {
  2772.                         $this->_out('/Encoding /WinAnsiEncoding');
  2773.                     }
  2774.                 }
  2775.             }
  2776.             $this->_out('>>');
  2777.             $this->_out('endobj');
  2778.             if ($font['type'] != 'core') {
  2779.                 /* Widths. */
  2780.                 $this->_newobj();
  2781.                 $cw = &$font['cw'];
  2782.                 $s = '[';
  2783.                 for ($i = 32; $i <= 255; $i++) {
  2784.                     $s .= $cw[chr($i)] . ' ';
  2785.                 }
  2786.                 $this->_out($s . ']');
  2787.                 $this->_out('endobj');
  2788.                 /* Descriptor. */
  2789.                 $this->_newobj();
  2790.                 $s = '<</Type /FontDescriptor /FontName /' . $name;
  2791.                 foreach ($font['desc'] as $k => $v) {
  2792.                     $s .= ' /' . $k . ' ' . $v;
  2793.                 }
  2794.                 $file = $font['file'];
  2795.                 if ($file) {
  2796.                     $s .= ' /FontFile' . ($font['type'] == 'Type1' ? '' : '2') . ' ' . $this->_font_files[$file]['n'] . ' 0 R';
  2797.                 }
  2798.                 $this->_out($s . '>>');
  2799.                 $this->_out('endobj');
  2800.             }
  2801.         }
  2802.     }
  2803.  
  2804.     function _putImages()
  2805.     {
  2806.         $filter = ($this->_compress) ? '/Filter /FlateDecode ' : '';
  2807.         foreach ($this->_images as $file => $info) {
  2808.             $this->_newobj();
  2809.             $this->_images[$file]['n'] = $this->_n;
  2810.             $this->_out('<</Type /XObject');
  2811.             $this->_out('/Subtype /Image');
  2812.             $this->_out('/Width ' . $info['w']);
  2813.             $this->_out('/Height ' . $info['h']);
  2814.             if ($info['cs'] == 'Indexed') {
  2815.                 $this->_out('/ColorSpace [/Indexed /DeviceRGB ' . (strlen($info['pal'])/3 - 1) . ' ' . ($this->_n + 1) . ' 0 R]');
  2816.             } else {
  2817.                 $this->_out('/ColorSpace /' . $info['cs']);
  2818.                 if ($info['cs'] == 'DeviceCMYK') {
  2819.                     $this->_out('/Decode [1 0 1 0 1 0 1 0]');
  2820.                 }
  2821.             }
  2822.             $this->_out('/BitsPerComponent ' . $info['bpc']);
  2823.             $this->_out('/Filter /' . $info['f']);
  2824.             if (isset($info['parms'])) {
  2825.                 $this->_out($info['parms']);
  2826.             }
  2827.             if (isset($info['trns']) && is_array($info['trns'])) {
  2828.                 $trns = '';
  2829.                 $i_max = count($info['trns']);
  2830.                 for ($i = 0; $i < $i_max; $i++) {
  2831.                     $trns .= $info['trns'][$i] . ' ' . $info['trns'][$i] . ' ';
  2832.                 }
  2833.                 $this->_out('/Mask [' . $trns . ']');
  2834.             }
  2835.             $this->_out('/Length ' . strlen($info['data']) . '>>');
  2836.             $this->_putStream($info['data']);
  2837.             $this->_out('endobj');
  2838.  
  2839.             /* Palette. */
  2840.             if ($info['cs'] == 'Indexed') {
  2841.                 $this->_newobj();
  2842.                 $pal = ($this->_compress) ? gzcompress($info['pal']) : $info['pal'];
  2843.                 $this->_out('<<' . $filter . '/Length ' . strlen($pal) . '>>');
  2844.                 $this->_putStream($pal);
  2845.                 $this->_out('endobj');
  2846.             }
  2847.         }
  2848.     }
  2849.  
  2850.     function _putResources()
  2851.     {
  2852.         $this->_putFonts();
  2853.         $this->_putImages();
  2854.         /* Resource dictionary */
  2855.         $this->_offsets[2] = $this->_buflen + strlen($this->_buffer);
  2856.         $this->_out('2 0 obj');
  2857.         $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
  2858.         $this->_out('/Font <<');
  2859.         foreach ($this->_fonts as $font) {
  2860.             $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
  2861.         }
  2862.         $this->_out('>>');
  2863.         if (count($this->_images)) {
  2864.             $this->_out('/XObject <<');
  2865.             foreach ($this->_images as $image) {
  2866.                 $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
  2867.             }
  2868.             $this->_out('>>');
  2869.         }
  2870.         $this->_out('>>');
  2871.         $this->_out('endobj');
  2872.     }
  2873.  
  2874.     function _putInfo()
  2875.     {
  2876.         $this->_out('/Producer ' . $this->_textString('Horde PDF'));
  2877.         if (!empty($this->_info['title'])) {
  2878.             $this->_out('/Title ' . $this->_textString($this->_info['title']));
  2879.         }
  2880.         if (!empty($this->_info['subject'])) {
  2881.             $this->_out('/Subject ' . $this->_textString($this->_info['subject']));
  2882.         }
  2883.         if (!empty($this->_info['author'])) {
  2884.             $this->_out('/Author ' . $this->_textString($this->_info['author']));
  2885.         }
  2886.         if (!empty($this->keywords)) {
  2887.             $this->_out('/Keywords ' . $this->_textString($this->keywords));
  2888.         }
  2889.         if (!empty($this->creator)) {
  2890.             $this->_out('/Creator ' . $this->_textString($this->creator));
  2891.         }
  2892.         $this->_out('/CreationDate ' . $this->_textString('D:' . date('YmdHis')));
  2893.     }
  2894.  
  2895.     function _putCatalog()
  2896.     {
  2897.         $this->_out('/Type /Catalog');
  2898.         $this->_out('/Pages 1 0 R');
  2899.         if ($this->_zoom_mode == 'fullpage') {
  2900.             $this->_out('/OpenAction [3 0 R /Fit]');
  2901.         } elseif ($this->_zoom_mode == 'fullwidth') {
  2902.             $this->_out('/OpenAction [3 0 R /FitH null]');
  2903.         } elseif ($this->_zoom_mode == 'real') {
  2904.             $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
  2905.         } elseif (!is_string($this->_zoom_mode)) {
  2906.             $this->_out('/OpenAction [3 0 R /XYZ null null ' . ($this->_zoom_mode / 100) . ']');
  2907.         }
  2908.         if ($this->_layout_mode == 'single') {
  2909.             $this->_out('/PageLayout /SinglePage');
  2910.         } elseif ($this->_layout_mode == 'continuous') {
  2911.             $this->_out('/PageLayout /OneColumn');
  2912.         } elseif ($this->_layout_mode == 'two') {
  2913.             $this->_out('/PageLayout /TwoColumnLeft');
  2914.         }
  2915.     }
  2916.  
  2917.     function _putTrailer()
  2918.     {
  2919.         $this->_out('/Size ' . ($this->_n + 1));
  2920.         $this->_out('/Root ' . $this->_n . ' 0 R');
  2921.         $this->_out('/Info ' . ($this->_n - 1) . ' 0 R');
  2922.     }
  2923.  
  2924.     function _endDoc()
  2925.     {
  2926.         $this->_putPages();
  2927.         $this->_putResources();
  2928.         /* Info */
  2929.         $this->_newobj();
  2930.         $this->_out('<<');
  2931.         $this->_putInfo();
  2932.         $this->_out('>>');
  2933.         $this->_out('endobj');
  2934.         /* Catalog */
  2935.         $this->_newobj();
  2936.         $this->_out('<<');
  2937.         $this->_putCatalog();
  2938.         $this->_out('>>');
  2939.         $this->_out('endobj');
  2940.         /* Cross-ref */
  2941.         $o = $this->_buflen + strlen($this->_buffer);
  2942.         $this->_out('xref');
  2943.         $this->_out('0 ' . ($this->_n + 1));
  2944.         $this->_out('0000000000 65535 f ');
  2945.         for ($i = 1; $i <= $this->_n; $i++) {
  2946.             $this->_out(sprintf('%010d 00000 n ', $this->_offsets[$i]));
  2947.         }
  2948.         /* Trailer */
  2949.         $this->_out('trailer');
  2950.         $this->_out('<<');
  2951.         $this->_putTrailer();
  2952.         $this->_out('>>');
  2953.         $this->_out('startxref');
  2954.         $this->_out($o);
  2955.         $this->_out('%%EOF');
  2956.         $this->_state = 3;
  2957.     }
  2958.  
  2959.     function _beginPage($orientation)
  2960.     {
  2961.         $this->_page++;
  2962.         $this->_pages[$this->_page] = '';
  2963.         $this->_state = 2;
  2964.         $this->x = $this->_left_margin;
  2965.         $this->y = $this->_top_margin;
  2966.         $this->_last_height = 0;
  2967.         /* Page orientation */
  2968.         if (!$orientation) {
  2969.             $orientation = $this->_default_orientation;
  2970.         } else {
  2971.             $orientation = strtoupper($orientation[0]);
  2972.             if ($orientation != $this->_default_orientation) {
  2973.                 $this->_orientation_changes[$this->_page] = true;
  2974.             }
  2975.         }
  2976.         if ($orientation != $this->_current_orientation) {
  2977.             /* Change orientation */
  2978.             if ($orientation == 'P') {
  2979.                 $this->wPt = $this->fwPt;
  2980.                 $this->hPt = $this->fhPt;
  2981.                 $this->w   = $this->fw;
  2982.                 $this->h   = $this->fh;
  2983.             } else {
  2984.                 $this->wPt = $this->fhPt;
  2985.                 $this->hPt = $this->fwPt;
  2986.                 $this->w   = $this->fh;
  2987.                 $this->h   = $this->fw;
  2988.             }
  2989.             $this->_page_break_trigger = $this->h - $this->_break_margin;
  2990.             $this->_current_orientation = $orientation;
  2991.         }
  2992.     }
  2993.  
  2994.     function _endPage()
  2995.     {
  2996.         /* End of page contents */
  2997.         $this->_state = 1;
  2998.     }
  2999.  
  3000.     function _newobj()
  3001.     {
  3002.         /* Begin a new object */
  3003.         $this->_n++;
  3004.         $this->_offsets[$this->_n] = $this->_buflen + strlen($this->_buffer);
  3005.         $this->_out($this->_n . ' 0 obj');
  3006.     }
  3007.  
  3008.     function _doUnderline($x, $y, $text)
  3009.     {
  3010.         /* Set the rectangle width according to text width. */
  3011.         $width  = $this->getStringWidth($text, true);
  3012.  
  3013.         /* Set rectangle position and height, using underline position and
  3014.          * thickness settings scaled by the font size. */
  3015.         $y = $y + ($this->_current_font['up'] * $this->_font_size_pt / 1000);
  3016.         $height = -$this->_current_font['ut'] * $this->_font_size_pt / 1000;
  3017.  
  3018.         return sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' re f', $x, $y, $width, $height);
  3019.     }
  3020.  
  3021.     function _parseJPG($file)
  3022.     {
  3023.         /* Extract info from a JPEG file. */
  3024.         $img = @getimagesize($file);
  3025.         if (!$img) {
  3026.             return $this->raiseError(sprintf('Missing or incorrect image file: %s', $file));
  3027.         }
  3028.         if ($img[2] != 2) {
  3029.             return $this->raiseError(sprintf('Not a JPEG file: %s', $file));
  3030.         }
  3031.         if (!isset($img['channels']) || $img['channels'] == 3) {
  3032.             $colspace = 'DeviceRGB';
  3033.         } elseif ($img['channels'] == 4) {
  3034.             $colspace = 'DeviceCMYK';
  3035.         } else {
  3036.             $colspace = 'DeviceGray';
  3037.         }
  3038.         $bpc = isset($img['bits']) ? $img['bits'] : 8;
  3039.  
  3040.         /* Read whole file. */
  3041.         $f = fopen($file, 'rb');
  3042.         $data = fread($f, filesize($file));
  3043.         fclose($f);
  3044.  
  3045.         return array('w' => $img[0], 'h' => $img[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
  3046.     }
  3047.  
  3048.     function _parsePNG($file)
  3049.     {
  3050.         /* Extract info from a PNG file. */
  3051.         $f = fopen($file, 'rb');
  3052.         if (!$f) {
  3053.             return $this->raiseError(sprintf('Unable to open image file: %s', $file));
  3054.         }
  3055.  
  3056.         /* Check signature. */
  3057.         if (fread($f, 8) != chr(137) . 'PNG' . chr(13) . chr(10) . chr(26) . chr(10)) {
  3058.             return $this->raiseError(sprintf('Not a PNG file: %s', $file));
  3059.         }
  3060.  
  3061.         /* Read header chunk. */
  3062.         fread($f, 4);
  3063.         if (fread($f, 4) != 'IHDR') {
  3064.             return $this->raiseError(sprintf('Incorrect PNG file: %s', $file));
  3065.         }
  3066.         $width = $this->_freadInt($f);
  3067.         $height = $this->_freadInt($f);
  3068.         $bpc = ord(fread($f, 1));
  3069.         if ($bpc > 8) {
  3070.             return $this->raiseError(sprintf('16-bit depth not supported: %s', $file));
  3071.         }
  3072.         $ct = ord(fread($f, 1));
  3073.         if ($ct == 0) {
  3074.             $colspace = 'DeviceGray';
  3075.         } elseif ($ct == 2) {
  3076.             $colspace = 'DeviceRGB';
  3077.         } elseif ($ct == 3) {
  3078.             $colspace = 'Indexed';
  3079.         } else {
  3080.             return $this->raiseError(sprintf('Alpha channel not supported: %s', $file));
  3081.         }
  3082.         if (ord(fread($f, 1)) != 0) {
  3083.             return $this->raiseError(sprintf('Unknown compression method: %s', $file));
  3084.         }
  3085.         if (ord(fread($f, 1)) != 0) {
  3086.             return $this->raiseError(sprintf('Unknown filter method: %s', $file));
  3087.         }
  3088.         if (ord(fread($f, 1)) != 0) {
  3089.             return $this->raiseError(sprintf('Interlacing not supported: %s', $file));
  3090.         }
  3091.         fread($f, 4);
  3092.         $parms = '/DecodeParms <</Predictor 15 /Colors ' . ($ct == 2 ? 3 : 1) . ' /BitsPerComponent ' . $bpc . ' /Columns ' . $width . '>>';
  3093.         /* Scan chunks looking for palette, transparency and image data. */
  3094.         $pal = '';
  3095.         $trns = '';
  3096.         $data = '';
  3097.         do {
  3098.             $n = $this->_freadInt($f);
  3099.             $type = fread($f, 4);
  3100.             if ($type == 'PLTE') {
  3101.                 /* Read palette */
  3102.                 $pal = fread($f, $n);
  3103.                 fread($f, 4);
  3104.             } elseif ($type == 'tRNS') {
  3105.                 /* Read transparency info */
  3106.                 $t = fread($f, $n);
  3107.                 if ($ct == 0) {
  3108.                     $trns = array(ord(substr($t, 1, 1)));
  3109.                 } elseif ($ct == 2) {
  3110.                     $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
  3111.                 } else {
  3112.                     $pos = strpos($t, chr(0));
  3113.                     if (is_int($pos)) {
  3114.                         $trns = array($pos);
  3115.                     }
  3116.                 }
  3117.                 fread($f, 4);
  3118.             } elseif ($type == 'IDAT') {
  3119.                 /* Read image data block */
  3120.                 $data .= fread($f, $n);
  3121.                 fread($f, 4);
  3122.             } elseif ($type == 'IEND') {
  3123.                 break;
  3124.             } else {
  3125.                 fread($f, $n + 4);
  3126.             }
  3127.         } while ($n);
  3128.  
  3129.         if ($colspace == 'Indexed' && empty($pal)) {
  3130.             return $this->raiseError(sprintf('Missing palette in: %s', $file));
  3131.         }
  3132.         fclose($f);
  3133.  
  3134.         return array('w' => $width, 'h' => $height, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
  3135.     }
  3136.  
  3137.     function _freadInt($f)
  3138.     {
  3139.         /* Read a 4-byte integer from file. */
  3140.         $i  = ord(fread($f, 1)) << 24;
  3141.         $i += ord(fread($f, 1)) << 16;
  3142.         $i += ord(fread($f, 1)) << 8;
  3143.         $i += ord(fread($f, 1));
  3144.         return $i;
  3145.     }
  3146.  
  3147.     function _textString($s)
  3148.     {
  3149.         /* Format a text string */
  3150.         return '(' . $this->_escape($s) . ')';
  3151.     }
  3152.  
  3153.     function _escape($s)
  3154.     {
  3155.         /* Add \ before \, ( and ) */
  3156.         return str_replace(array('\\', ')', '('),
  3157.                            array('\\\\', '\\)', '\\('),
  3158.                            $s);
  3159.     }
  3160.  
  3161.     function _putStream($s)
  3162.     {
  3163.         $this->_out('stream');
  3164.         $this->_out($s);
  3165.         $this->_out('endstream');
  3166.     }
  3167.  
  3168.     function _out($s)
  3169.     {
  3170.         /* Add a line to the document. */
  3171.         if ($this->_state == 2) {
  3172.             $this->_pages[$this->_page] .= $s . "\n";
  3173.         } else {
  3174.             $this->_buffer .= $s . "\n";
  3175.         }
  3176.     }
  3177.  
  3178. }
  3179.