File_PDF
[ class tree: File_PDF ] [ index: File_PDF ] [ all elements ]

Source for file PDF.php

Documentation is available at PDF.php

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

Documentation generated on Mon, 11 Mar 2019 14:54:39 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.