Changeset 112228 in spip-zone


Ignore:
Timestamp:
Oct 31, 2018, 8:57:14 AM (9 months ago)
Author:
pierrekuhn82@…
Message:

Mise à jour de la librairie FPDI en 1.6.2 pour le moment car l'upgrade vers la 2.0.0 provoque pas mal de changement. A suivre ...

Location:
_plugins_/fpdf/trunk
Files:
4 added
3 deleted
7 edited

Legend:

Unmodified
Added
Removed
  • _plugins_/fpdf/trunk/filters/FilterASCII85.php

    r99957 r112228  
    11<?php
    2 //
    3 //  FPDI - Version 1.4.4
    4 //
    5 //    Copyright 2004-2013 Setasign - Jan Slabon
    6 //
    7 //  Licensed under the Apache License, Version 2.0 (the "License");
    8 //  you may not use this file except in compliance with the License.
    9 //  You may obtain a copy of the License at
    10 //
    11 //      http://www.apache.org/licenses/LICENSE-2.0
    12 //
    13 //  Unless required by applicable law or agreed to in writing, software
    14 //  distributed under the License is distributed on an "AS IS" BASIS,
    15 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16 //  See the License for the specific language governing permissions and
    17 //  limitations under the License.
    18 //
     2/**
     3 * This file is part of FPDI
     4 *
     5 * @package   FPDI
     6 * @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com)
     7 * @license   http://opensource.org/licenses/mit-license The MIT License
     8 * @version   1.6.1
     9 */
    1910
    20 if (!defined('ORD_z'))
    21         define('ORD_z',ord('z'));
    22 if (!defined('ORD_exclmark'))
    23         define('ORD_exclmark', ord('!'));
    24 if (!defined('ORD_u')) 
    25         define('ORD_u', ord('u'));
    26 if (!defined('ORD_tilde'))
    27         define('ORD_tilde', ord('~'));
     11/**
     12 * Class FilterASCII85
     13 */
     14class FilterASCII85
     15{
     16    /**
     17     * Decode ASCII85 encoded string
     18     *
     19     * @param string $in
     20     * @return string
     21     * @throws Exception
     22     */
     23    public function decode($in)
     24    {
     25        $ord = array(
     26            '~' => ord('~'),
     27            'z' => ord('z'),
     28            'u' => ord('u'),
     29            '!' => ord('!')
     30        );
    2831
    29 if (!class_exists('FilterASCII85', false)) {
     32        $out = '';
     33        $state = 0;
     34        $chn = null;
    3035
    31     class FilterASCII85 {
    32        
    33         function error($msg) {
    34             die($msg);
    35         }
    36        
    37         function decode($in) {
    38             $out = '';
    39             $state = 0;
    40             $chn = null;
    41            
    42             $l = strlen($in);
    43            
    44             for ($k = 0; $k < $l; ++$k) {
    45                 $ch = ord($in[$k]) & 0xff;
    46                
    47                 if ($ch == ORD_tilde) {
    48                     break;
     36        $l = strlen($in);
     37
     38        for ($k = 0; $k < $l; ++$k) {
     39            $ch = ord($in[$k]) & 0xff;
     40
     41            if ($ch == $ord['~']) {
     42                break;
     43            }
     44            if (preg_match('/^\s$/',chr($ch))) {
     45                continue;
     46            }
     47            if ($ch == $ord['z'] && $state == 0) {
     48                $out .= chr(0) . chr(0) . chr(0) . chr(0);
     49                continue;
     50            }
     51            if ($ch < $ord['!'] || $ch > $ord['u']) {
     52                throw new Exception('Illegal character in ASCII85Decode.');
     53            }
     54
     55            $chn[$state++] = $ch - $ord['!'];
     56
     57            if ($state == 5) {
     58                $state = 0;
     59                $r = 0;
     60                for ($j = 0; $j < 5; ++$j) {
     61                    $r = (int)($r * 85 + $chn[$j]);
    4962                }
    50                 if (preg_match('/^\s$/',chr($ch))) {
    51                     continue;
    52                 }
    53                 if ($ch == ORD_z && $state == 0) {
    54                     $out .= chr(0) . chr(0) . chr(0) . chr(0);
    55                     continue;
    56                 }
    57                 if ($ch < ORD_exclmark || $ch > ORD_u) {
    58                     return $this->error('Illegal character in ASCII85Decode.');
    59                 }
    60                
    61                 $chn[$state++] = $ch - ORD_exclmark;
    62                
    63                 if ($state == 5) {
    64                     $state = 0;
    65                     $r = 0;
    66                     for ($j = 0; $j < 5; ++$j)
    67                         $r = $r * 85 + $chn[$j];
    68                     $out .= chr($r >> 24);
    69                     $out .= chr($r >> 16);
    70                     $out .= chr($r >> 8);
    71                     $out .= chr($r);
    72                 }
    73             }
    74             $r = 0;
    75            
    76             if ($state == 1)
    77                 return $this->error('Illegal length in ASCII85Decode.');
    78             if ($state == 2) {
    79                 $r = $chn[0] * 85 * 85 * 85 * 85 + ($chn[1]+1) * 85 * 85 * 85;
    80                 $out .= chr($r >> 24);
    81             }
    82             else if ($state == 3) {
    83                 $r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85  + ($chn[2]+1) * 85 * 85;
    84                 $out .= chr($r >> 24);
    85                 $out .= chr($r >> 16);
    86             }
    87             else if ($state == 4) {
    88                 $r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85  + $chn[2] * 85 * 85  + ($chn[3]+1) * 85 ;
     63
    8964                $out .= chr($r >> 24);
    9065                $out .= chr($r >> 16);
    9166                $out .= chr($r >> 8);
     67                $out .= chr($r);
    9268            }
    93    
    94             return $out;
    9569        }
    96        
    97         function encode($in) {
    98             return $this->error("ASCII85 encoding not implemented.");
     70        $r = 0;
     71
     72        if ($state == 1) {
     73            throw new Exception('Illegal length in ASCII85Decode.');
    9974        }
     75
     76        if ($state == 2) {
     77            $r = $chn[0] * 85 * 85 * 85 * 85 + ($chn[1]+1) * 85 * 85 * 85;
     78            $out .= chr($r >> 24);
     79
     80        } else if ($state == 3) {
     81            $r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85  + ($chn[2]+1) * 85 * 85;
     82            $out .= chr($r >> 24);
     83            $out .= chr($r >> 16);
     84
     85        } else if ($state == 4) {
     86            $r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85  + $chn[2] * 85 * 85  + ($chn[3]+1) * 85 ;
     87            $out .= chr($r >> 24);
     88            $out .= chr($r >> 16);
     89            $out .= chr($r >> 8);
     90        }
     91
     92        return $out;
     93    }
     94
     95    /**
     96     * NOT IMPLEMENTED
     97     *
     98     * @param string $in
     99     * @return string
     100     * @throws LogicException
     101     */
     102    public function encode($in)
     103    {
     104        throw new LogicException("ASCII85 encoding not implemented.");
    100105    }
    101106}
  • _plugins_/fpdf/trunk/filters/FilterLZW.php

    r99957 r112228  
    11<?php
    2 //
    3 //  FPDI - Version 1.4.4
    4 //
    5 //    Copyright 2004-2013 Setasign - Jan Slabon
    6 //
    7 //  Licensed under the Apache License, Version 2.0 (the "License");
    8 //  you may not use this file except in compliance with the License.
    9 //  You may obtain a copy of the License at
    10 //
    11 //      http://www.apache.org/licenses/LICENSE-2.0
    12 //
    13 //  Unless required by applicable law or agreed to in writing, software
    14 //  distributed under the License is distributed on an "AS IS" BASIS,
    15 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16 //  See the License for the specific language governing permissions and
    17 //  limitations under the License.
    18 //
     2/**
     3 * This file is part of FPDI
     4 *
     5 * @package   FPDI
     6 * @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com)
     7 * @license   http://opensource.org/licenses/mit-license The MIT License
     8 * @version   1.6.1
     9 */
    1910
    20 if (!class_exists('FilterLZW', false)) {
     11/**
     12 * Class FilterLZW
     13 */
     14class FilterLZW
     15{
     16    protected $_sTable = array();
     17    protected $_data = null;
     18    protected $_dataLength = 0;
     19    protected $_tIdx;
     20    protected $_bitsToGet = 9;
     21    protected $_bytePointer;
     22    protected $_bitPointer;
     23    protected $_nextData = 0;
     24    protected $_nextBits = 0;
     25    protected $_andTable = array(511, 1023, 2047, 4095);
    2126
    22     class FilterLZW {
    23        
    24         var $sTable = array();
    25         var $data = null;
    26         var $dataLength = 0;
    27         var $tIdx;
    28         var $bitsToGet = 9;
    29         var $bytePointer;
    30         var $bitPointer;
    31         var $nextData = 0;
    32         var $nextBits = 0;
    33         var $andTable = array(511, 1023, 2047, 4095);
    34    
    35         function error($msg) {
    36             die($msg);
     27    /**
     28     * Decodes LZW compressed data.
     29     *
     30     * @param string $data The compressed data.
     31     * @throws Exception
     32     * @return string
     33     */
     34    public function decode($data)
     35    {
     36        if ($data[0] == 0x00 && $data[1] == 0x01) {
     37            throw new Exception('LZW flavour not supported.');
    3738        }
    38        
    39         /**
    40          * Method to decode LZW compressed data.
    41          *
    42          * @param string data    The compressed data.
    43          */
    44         function decode($data) {
    45    
    46             if($data[0] == 0x00 && $data[1] == 0x01) {
    47                 $this->error('LZW flavour not supported.');
    48             }
    49    
    50             $this->initsTable();
    51    
    52             $this->data = $data;
    53             $this->dataLength = strlen($data);
    54    
    55             // Initialize pointers
    56             $this->bytePointer = 0;
    57             $this->bitPointer = 0;
    58    
    59             $this->nextData = 0;
    60             $this->nextBits = 0;
    61    
    62             $oldCode = 0;
    63    
    64             $string = '';
    65             $uncompData = '';
    66    
    67             while (($code = $this->getNextCode()) != 257) {
    68                 if ($code == 256) {
    69                     $this->initsTable();
    70                     $code = $this->getNextCode();
    71    
    72                     if ($code == 257) {
    73                         break;
    74                     }
    75    
    76                     $uncompData .= $this->sTable[$code];
     39
     40        $this->_initsTable();
     41
     42        $this->_data = $data;
     43        $this->_dataLength = strlen($data);
     44
     45        // Initialize pointers
     46        $this->_bytePointer = 0;
     47        $this->_bitPointer = 0;
     48
     49        $this->_nextData = 0;
     50        $this->_nextBits = 0;
     51
     52        $oldCode = 0;
     53
     54        $unCompData = '';
     55
     56        while (($code = $this->_getNextCode()) != 257) {
     57            if ($code == 256) {
     58                $this->_initsTable();
     59                $code = $this->_getNextCode();
     60
     61                if ($code == 257) {
     62                    break;
     63                }
     64
     65                if (!isset($this->_sTable[$code])) {
     66                    throw new Exception('Error while decompression LZW compressed data.');
     67                }
     68
     69                $unCompData .= $this->_sTable[$code];
     70                $oldCode = $code;
     71
     72            } else {
     73
     74                if ($code < $this->_tIdx) {
     75                    $string = $this->_sTable[$code];
     76                    $unCompData .= $string;
     77
     78                    $this->_addStringToTable($this->_sTable[$oldCode], $string[0]);
    7779                    $oldCode = $code;
    78    
    7980                } else {
    80    
    81                     if ($code < $this->tIdx) {
    82                         $string = $this->sTable[$code];
    83                         $uncompData .= $string;
    84    
    85                         $this->addStringToTable($this->sTable[$oldCode], $string[0]);
    86                         $oldCode = $code;
    87                     } else {
    88                         $string = $this->sTable[$oldCode];
    89                         $string = $string . $string[0];
    90                         $uncompData .= $string;
    91    
    92                         $this->addStringToTable($string);
    93                         $oldCode = $code;
    94                     }
     81                    $string = $this->_sTable[$oldCode];
     82                    $string = $string . $string[0];
     83                    $unCompData .= $string;
     84
     85                    $this->_addStringToTable($string);
     86                    $oldCode = $code;
    9587                }
    9688            }
    97            
    98             return $uncompData;
    9989        }
    100    
    101    
    102         /**
    103          * Initialize the string table.
    104          */
    105         function initsTable() {
    106             $this->sTable = array();
    107    
    108             for ($i = 0; $i < 256; $i++)
    109                 $this->sTable[$i] = chr($i);
    110    
    111             $this->tIdx = 258;
    112             $this->bitsToGet = 9;
    113         }
    114    
    115         /**
    116          * Add a new string to the string table.
    117          */
    118         function addStringToTable ($oldString, $newString='') {
    119             $string = $oldString.$newString;
    120    
    121             // Add this new String to the table
    122             $this->sTable[$this->tIdx++] = $string;
    123    
    124             if ($this->tIdx == 511) {
    125                 $this->bitsToGet = 10;
    126             } else if ($this->tIdx == 1023) {
    127                 $this->bitsToGet = 11;
    128             } else if ($this->tIdx == 2047) {
    129                 $this->bitsToGet = 12;
    130             }
    131         }
    132    
    133         // Returns the next 9, 10, 11 or 12 bits
    134         function getNextCode() {
    135             if ($this->bytePointer == $this->dataLength) {
    136                 return 257;
    137             }
    138    
    139             $this->nextData = ($this->nextData << 8) | (ord($this->data[$this->bytePointer++]) & 0xff);
    140             $this->nextBits += 8;
    141    
    142             if ($this->nextBits < $this->bitsToGet) {
    143                 $this->nextData = ($this->nextData << 8) | (ord($this->data[$this->bytePointer++]) & 0xff);
    144                 $this->nextBits += 8;
    145             }
    146    
    147             $code = ($this->nextData >> ($this->nextBits - $this->bitsToGet)) & $this->andTable[$this->bitsToGet-9];
    148             $this->nextBits -= $this->bitsToGet;
    149    
    150             return $code;
    151         }
    152        
    153         function encode($in) {
    154             $this->error("LZW encoding not implemented.");
     90
     91        return $unCompData;
     92    }
     93
     94
     95    /**
     96     * Initialize the string table.
     97     */
     98    protected function _initsTable()
     99    {
     100        $this->_sTable = array();
     101
     102        for ($i = 0; $i < 256; $i++)
     103            $this->_sTable[$i] = chr($i);
     104
     105        $this->_tIdx = 258;
     106        $this->_bitsToGet = 9;
     107    }
     108
     109    /**
     110     * Add a new string to the string table.
     111     */
     112    protected function _addStringToTable($oldString, $newString = '')
     113    {
     114        $string = $oldString . $newString;
     115
     116        // Add this new String to the table
     117        $this->_sTable[$this->_tIdx++] = $string;
     118
     119        if ($this->_tIdx == 511) {
     120            $this->_bitsToGet = 10;
     121        } else if ($this->_tIdx == 1023) {
     122            $this->_bitsToGet = 11;
     123        } else if ($this->_tIdx == 2047) {
     124            $this->_bitsToGet = 12;
    155125        }
    156126    }
     127
     128    /**
     129     * Returns the next 9, 10, 11 or 12 bits
     130     *
     131     * @return int
     132     */
     133    protected function _getNextCode()
     134    {
     135        if ($this->_bytePointer == $this->_dataLength) {
     136            return 257;
     137        }
     138
     139        $this->_nextData = ($this->_nextData << 8) | (ord($this->_data[$this->_bytePointer++]) & 0xff);
     140        $this->_nextBits += 8;
     141
     142        if ($this->_nextBits < $this->_bitsToGet) {
     143            $this->_nextData = ($this->_nextData << 8) | (ord($this->_data[$this->_bytePointer++]) & 0xff);
     144            $this->_nextBits += 8;
     145        }
     146
     147        $code = ($this->_nextData >> ($this->_nextBits - $this->_bitsToGet)) & $this->_andTable[$this->_bitsToGet-9];
     148        $this->_nextBits -= $this->_bitsToGet;
     149
     150        return $code;
     151    }
     152
     153    /**
     154     * NOT IMPLEMENTED
     155     *
     156     * @param string $in
     157     * @return string
     158     * @throws LogicException
     159     */
     160    public function encode($in)
     161    {
     162        throw new LogicException("LZW encoding not implemented.");
     163    }
    157164}
  • _plugins_/fpdf/trunk/fpdf_tpl.php

    r99957 r112228  
    11<?php
    2 //
    3 //  FPDF_TPL - Version 1.2.3
    4 //
    5 //    Copyright 2004-2013 Setasign - Jan Slabon
    6 //
    7 //  Licensed under the Apache License, Version 2.0 (the "License");
    8 //  you may not use this file except in compliance with the License.
    9 //  You may obtain a copy of the License at
    10 //
    11 //      http://www.apache.org/licenses/LICENSE-2.0
    12 //
    13 //  Unless required by applicable law or agreed to in writing, software
    14 //  distributed under the License is distributed on an "AS IS" BASIS,
    15 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16 //  See the License for the specific language governing permissions and
    17 //  limitations under the License.
    18 //
    19 
    20 class FPDF_TPL extends FPDF {
    21     /**
    22      * Array of Tpl-Data
     2/**
     3 * This file is part of FPDI
     4 *
     5 * @package   FPDI
     6 * @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com)
     7 * @license   http://opensource.org/licenses/mit-license The MIT License
     8 * @version   1.6.1
     9 */
     10
     11if (!class_exists('fpdi_bridge')) {
     12    require_once('fpdi_bridge.php');
     13}
     14
     15/**
     16 * Class FPDF_TPL
     17 */
     18class FPDF_TPL extends fpdi_bridge
     19{
     20    /**
     21     * Array of template data
     22     *
    2323     * @var array
    2424     */
    25     var $tpls = array();
    26 
    27     /**
    28      * Current Template-ID
     25    protected $_tpls = array();
     26
     27    /**
     28     * Current Template-Id
     29     *
    2930     * @var int
    3031     */
    31     var $tpl = 0;
    32    
     32    public $tpl = 0;
     33
    3334    /**
    3435     * "In Template"-Flag
     36     *
    3537     * @var boolean
    3638     */
    37     var $_intpl = false;
    38    
    39     /**
    40      * Nameprefix of Templates used in Resources-Dictonary
    41      * @var string A String defining the Prefix used as Template-Object-Names. Have to beginn with an /
    42      */
    43     var $tplprefix = "/TPL";
    44 
    45     /**
    46      * Resources used By Templates and Pages
     39    protected $_inTpl = false;
     40
     41    /**
     42     * Name prefix of templates used in Resources dictionary
     43     *
     44     * @var string A String defining the Prefix used as Template-Object-Names. Have to begin with an /
     45     */
     46    public $tplPrefix = "/TPL";
     47
     48    /**
     49     * Resources used by templates and pages
     50     *
    4751     * @var array
    4852     */
    49     var $_res = array();
    50    
    51     /**
    52      * Last used Template data
     53    protected $_res = array();
     54
     55    /**
     56     * Last used template data
    5357     *
    5458     * @var array
    5559     */
    56     var $lastUsedTemplateData = array();
    57    
    58     /**
    59      * Start a Template
     60    public $lastUsedTemplateData = array();
     61
     62    /**
     63     * Start a template.
    6064     *
    6165     * This method starts a template. You can give own coordinates to build an own sized
    62      * Template. Pay attention, that the margins are adapted to the new templatesize.
    63      * If you want to write outside the template, for example to build a clipped Template,
    64      * you have to set the Margins and "Cursor"-Position manual after beginTemplate-Call.
     66     * template. Pay attention, that the margins are adapted to the new template size.
     67     * If you want to write outside the template, for example to build a clipped template,
     68     * you have to set the margins and "cursor"-position manual after beginTemplate()-call.
    6569     *
    6670     * If no parameter is given, the template uses the current page-size.
    67      * The Method returns an ID of the current Template. This ID is used later for using this template.
    68      * Warning: A created Template is used in PDF at all events. Still if you don't use it after creation!
     71     * The method returns an id of the current template. This id is used later for using this template.
     72     * Warning: A created template is saved in the resulting PDF at all events. Also if you don't use it after creation!
    6973     *
    7074     * @param int $x The x-coordinate given in user-unit
     
    7276     * @param int $w The width given in user-unit
    7377     * @param int $h The height given in user-unit
    74      * @return int The ID of new created Template
    75      */
    76     function beginTemplate($x = null, $y = null, $w = null, $h = null) {
    77         if (is_subclass_of($this, 'TCPDF')) {
    78                 $this->Error('This method is only usable with FPDF. Use TCPDF methods startTemplate() instead.');
    79                 return;
    80         }
    81        
    82         if ($this->page <= 0)
    83             $this->error("You have to add a page to fpdf first!");
     78     * @return int The id of new created template
     79     * @throws LogicException
     80     */
     81    public function beginTemplate($x = null, $y = null, $w = null, $h = null)
     82    {
     83        if (is_subclass_of($this, 'TCPDF')) {
     84            throw new LogicException('This method is only usable with FPDF. Use TCPDF methods startTemplate() instead.');
     85        }
     86
     87        if ($this->page <= 0) {
     88            throw new LogicException("You have to add at least a page first!");
     89        }
    8490
    8591        if ($x == null)
     
    94100        // Save settings
    95101        $this->tpl++;
    96         $tpl =& $this->tpls[$this->tpl];
     102        $tpl =& $this->_tpls[$this->tpl];
    97103        $tpl = array(
    98104            'o_x' => $this->x,
     
    117123
    118124        $this->SetAutoPageBreak(false);
    119        
    120         // Define own high and width to calculate possitions correct
     125
     126        // Define own high and width to calculate correct positions
    121127        $this->h = $h;
    122128        $this->w = $w;
    123129
    124         $this->_intpl = true;
     130        $this->_inTpl = true;
    125131        $this->SetXY($x + $this->lMargin, $y + $this->tMargin);
    126132        $this->SetRightMargin($this->w - $w + $this->rMargin);
    127133
    128134        if ($this->CurrentFont) {
    129             $fontkey = $this->FontFamily . $this->FontStyle;
    130                     $this->_res['tpl'][$this->tpl]['fonts'][$fontkey] =& $this->fonts[$fontkey];
    131            
    132                 $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
    133         }
    134        
     135            $fontKey = $this->FontFamily . $this->FontStyle;
     136            if ($fontKey) {
     137                $this->_res['tpl'][$this->tpl]['fonts'][$fontKey] =& $this->fonts[$fontKey];
     138                $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
     139            }
     140        }
     141
    135142        return $this->tpl;
    136143    }
    137    
    138     /**
    139      * End Template
    140      *
    141      * This method ends a template and reset initiated variables on beginTemplate.
    142      *
    143      * @return mixed If a template is opened, the ID is returned. If not a false is returned.
    144      */
    145     function endTemplate() {
    146         if (is_subclass_of($this, 'TCPDF')) {
    147                 $args = func_get_args();
    148                 return call_user_func_array(array($this, 'TCPDF::endTemplate'), $args);
    149         }
    150        
    151         if ($this->_intpl) {
    152             $this->_intpl = false;
    153             $tpl =& $this->tpls[$this->tpl];
     144
     145    /**
     146     * End template.
     147     *
     148     * This method ends a template and reset initiated variables collected in {@link beginTemplate()}.
     149     *
     150     * @return int|boolean If a template is opened, the id is returned. If not a false is returned.
     151     */
     152    public function endTemplate()
     153    {
     154        if (is_subclass_of($this, 'TCPDF')) {
     155            $args = func_get_args();
     156            return call_user_func_array(array($this, 'TCPDF::endTemplate'), $args);
     157        }
     158
     159        if ($this->_inTpl) {
     160            $this->_inTpl = false;
     161            $tpl = $this->_tpls[$this->tpl];
    154162            $this->SetXY($tpl['o_x'], $tpl['o_y']);
    155163            $this->tMargin = $tpl['o_tMargin'];
     
    159167            $this->w = $tpl['o_w'];
    160168            $this->SetAutoPageBreak($tpl['o_AutoPageBreak'], $tpl['o_bMargin']);
    161            
     169
    162170            $this->FontFamily = $tpl['o_FontFamily'];
    163                         $this->FontStyle = $tpl['o_FontStyle'];
    164                         $this->FontSizePt = $tpl['o_FontSizePt'];
    165                         $this->FontSize = $tpl['o_FontSize'];
    166                
    167                         $fontkey = $this->FontFamily . $this->FontStyle;
    168                         if ($fontkey)
    169                 $this->CurrentFont =& $this->fonts[$fontkey];
    170            
     171            $this->FontStyle = $tpl['o_FontStyle'];
     172            $this->FontSizePt = $tpl['o_FontSizePt'];
     173            $this->FontSize = $tpl['o_FontSize'];
     174
     175            $fontKey = $this->FontFamily . $this->FontStyle;
     176            if ($fontKey)
     177                $this->CurrentFont =& $this->fonts[$fontKey];
     178
    171179            return $this->tpl;
    172180        } else {
     
    174182        }
    175183    }
    176    
    177     /**
    178      * Use a Template in current Page or other Template
     184
     185    /**
     186     * Use a template in current page or other template.
    179187     *
    180188     * You can use a template in a page or in another template.
    181      * You can give the used template a new size like you use the Image()-method.
    182      * All parameters are optional. The width or height is calculated automaticaly
     189     * You can give the used template a new size.
     190     * All parameters are optional. The width or height is calculated automatically
    183191     * if one is given. If no parameter is given the origin size as defined in
    184      * beginTemplate() is used.
     192     * {@link beginTemplate()} method is used.
     193     *
    185194     * The calculated or used width and height are returned as an array.
    186195     *
    187      * @param int $tplidx A valid template-Id
    188      * @param int $_x The x-position
    189      * @param int $_y The y-position
    190      * @param int $_w The new width of the template
    191      * @param int $_h The new height of the template
    192      * @retrun array The height and width of the template
    193      */
    194     function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0) {
    195         if ($this->page <= 0)
    196                 $this->error('You have to add a page first!');
    197        
    198         if (!isset($this->tpls[$tplidx]))
    199             $this->error('Template does not exist!');
    200            
    201         if ($this->_intpl) {
    202             $this->_res['tpl'][$this->tpl]['tpls'][$tplidx] =& $this->tpls[$tplidx];
    203         }
    204        
    205         $tpl =& $this->tpls[$tplidx];
    206         $w = $tpl['w'];
    207         $h = $tpl['h'];
    208        
    209         if ($_x == null)
    210             $_x = 0;
    211         if ($_y == null)
    212             $_y = 0;
    213            
    214         $_x += $tpl['x'];
    215         $_y += $tpl['y'];
    216        
    217         $wh = $this->getTemplateSize($tplidx, $_w, $_h);
    218         $_w = $wh['w'];
    219         $_h = $wh['h'];
    220    
    221         $tData = array(
     196     * @param int $tplIdx A valid template-id
     197     * @param int $x The x-position
     198     * @param int $y The y-position
     199     * @param int $w The new width of the template
     200     * @param int $h The new height of the template
     201     * @return array The height and width of the template (array('w' => ..., 'h' => ...))
     202     * @throws LogicException|InvalidArgumentException
     203     */
     204    public function useTemplate($tplIdx, $x = null, $y = null, $w = 0, $h = 0)
     205    {
     206        if ($this->page <= 0) {
     207            throw new LogicException('You have to add at least a page first!');
     208        }
     209
     210        if (!isset($this->_tpls[$tplIdx])) {
     211            throw new InvalidArgumentException('Template does not exist!');
     212        }
     213
     214        if ($this->_inTpl) {
     215            $this->_res['tpl'][$this->tpl]['tpls'][$tplIdx] =& $this->_tpls[$tplIdx];
     216        }
     217
     218        $tpl = $this->_tpls[$tplIdx];
     219        $_w = $tpl['w'];
     220        $_h = $tpl['h'];
     221
     222        if ($x == null) {
     223            $x = 0;
     224        }
     225
     226        if ($y == null) {
     227            $y = 0;
     228        }
     229
     230        $x += $tpl['x'];
     231        $y += $tpl['y'];
     232
     233        $wh = $this->getTemplateSize($tplIdx, $w, $h);
     234        $w = $wh['w'];
     235        $h = $wh['h'];
     236
     237        $tplData = array(
    222238            'x' => $this->x,
    223239            'y' => $this->y,
    224             'w' => $_w,
    225             'h' => $_h,
    226             'scaleX' => ($_w / $w),
    227             'scaleY' => ($_h / $h),
    228             'tx' => $_x,
    229             'ty' =>  ($this->h - $_y - $_h),
    230             'lty' => ($this->h - $_y - $_h) - ($this->h - $h) * ($_h / $h)
     240            'w' => $w,
     241            'h' => $h,
     242            'scaleX' => ($w / $_w),
     243            'scaleY' => ($h / $_h),
     244            'tx' => $x,
     245            'ty' =>  ($this->h - $y - $h),
     246            'lty' => ($this->h - $y - $h) - ($this->h - $_h) * ($h / $_h)
    231247        );
    232        
    233         $this->_out(sprintf('q %.4F 0 0 %.4F %.4F %.4F cm', $tData['scaleX'], $tData['scaleY'], $tData['tx'] * $this->k, $tData['ty'] * $this->k)); // Translate
    234         $this->_out(sprintf('%s%d Do Q', $this->tplprefix, $tplidx));
    235 
    236         $this->lastUsedTemplateData = $tData;
    237        
    238         return array('w' => $_w, 'h' => $_h);
    239     }
    240    
    241     /**
    242      * Get The calculated Size of a Template
     248
     249        $this->_out(sprintf('q %.4F 0 0 %.4F %.4F %.4F cm',
     250            $tplData['scaleX'], $tplData['scaleY'], $tplData['tx'] * $this->k, $tplData['ty'] * $this->k)
     251        ); // Translate
     252        $this->_out(sprintf('%s%d Do Q', $this->tplPrefix, $tplIdx));
     253
     254        $this->lastUsedTemplateData = $tplData;
     255
     256        return array('w' => $w, 'h' => $h);
     257    }
     258
     259    /**
     260     * Get the calculated size of a template.
    243261     *
    244262     * If one size is given, this method calculates the other one.
    245263     *
    246      * @param int $tplidx A valid template-Id
    247      * @param int $_w The width of the template
    248      * @param int $_h The height of the template
    249      * @return array The height and width of the template
    250      */
    251     function getTemplateSize($tplidx, $_w = 0, $_h = 0) {
    252         if (!isset($this->tpls[$tplidx]))
     264     * @param int $tplIdx A valid template-id
     265     * @param int $w The width of the template
     266     * @param int $h The height of the template
     267     * @return array The height and width of the template (array('w' => ..., 'h' => ...))
     268     */
     269    public function getTemplateSize($tplIdx, $w = 0, $h = 0)
     270    {
     271        if (!isset($this->_tpls[$tplIdx]))
    253272            return false;
    254273
    255         $tpl =& $this->tpls[$tplidx];
    256         $w = $tpl['w'];
    257         $h = $tpl['h'];
    258        
    259         if ($_w == 0 and $_h == 0) {
    260             $_w = $w;
    261             $_h = $h;
    262         }
    263 
    264         if($_w == 0)
    265                 $_w = $_h * $w / $h;
    266         if($_h == 0)
    267                 $_h = $_w * $h / $w;
    268                
    269         return array("w" => $_w, "h" => $_h);
    270     }
    271    
    272     /**
    273      * See FPDF/TCPDF-Documentation ;-)
    274      */
    275     public function SetFont($family, $style = '', $size = 0) {
    276         if (is_subclass_of($this, 'TCPDF')) {
    277                 $args = func_get_args();
    278                 return call_user_func_array(array($this, 'TCPDF::SetFont'), $args);
    279         }
    280        
     274        $tpl = $this->_tpls[$tplIdx];
     275        $_w = $tpl['w'];
     276        $_h = $tpl['h'];
     277
     278        if ($w == 0 && $h == 0) {
     279            $w = $_w;
     280            $h = $_h;
     281        }
     282
     283        if ($w == 0)
     284            $w = $h * $_w / $_h;
     285        if($h == 0)
     286            $h = $w * $_h / $_w;
     287
     288        return array("w" => $w, "h" => $h);
     289    }
     290
     291    /**
     292     * Sets the font used to print character strings.
     293     *
     294     * See FPDF/TCPDF documentation.
     295     *
     296     * @see http://fpdf.org/en/doc/setfont.htm
     297     * @see http://www.tcpdf.org/doc/code/classTCPDF.html#afd56e360c43553830d543323e81bc045
     298     */
     299    public function SetFont($family, $style = '', $size = null, $fontfile = '', $subset = 'default', $out = true)
     300    {
     301        if (is_subclass_of($this, 'TCPDF')) {
     302            $args = func_get_args();
     303            return call_user_func_array(array($this, 'TCPDF::SetFont'), $args);
     304        }
     305
    281306        parent::SetFont($family, $style, $size);
    282        
     307
    283308        $fontkey = $this->FontFamily . $this->FontStyle;
    284        
    285         if ($this->_intpl) {
     309
     310        if ($this->_inTpl) {
    286311            $this->_res['tpl'][$this->tpl]['fonts'][$fontkey] =& $this->fonts[$fontkey];
    287312        } else {
     
    289314        }
    290315    }
    291    
    292     /**
    293      * See FPDF/TCPDF-Documentation ;-)
    294      */
    295     function Image(
    296                 $file, $x = '', $y = '', $w = 0, $h = 0, $type = '', $link = '', $align = '', $resize = false,
    297                 $dpi = 300, $palign = '', $ismask = false, $imgmask = false, $border = 0, $fitbox = false,
    298                 $hidden = false, $fitonpage = false, $alt = false, $altimgs = array()
    299     ) {
    300         if (is_subclass_of($this, 'TCPDF')) {
    301                 $args = func_get_args();
    302                         return call_user_func_array(array($this, 'TCPDF::Image'), $args);
    303         }
    304        
     316
     317    /**
     318     * Puts an image.
     319     *
     320     * See FPDF/TCPDF documentation.
     321     *
     322     * @see http://fpdf.org/en/doc/image.htm
     323     * @see http://www.tcpdf.org/doc/code/classTCPDF.html#a714c2bee7d6b39d4d6d304540c761352
     324     */
     325    public function Image(
     326        $file, $x = '', $y = '', $w = 0, $h = 0, $type = '', $link = '', $align = '', $resize = false,
     327        $dpi = 300, $palign = '', $ismask = false, $imgmask = false, $border = 0, $fitbox = false,
     328        $hidden = false, $fitonpage = false, $alt = false, $altimgs = array()
     329    )
     330    {
     331        if (is_subclass_of($this, 'TCPDF')) {
     332            $args = func_get_args();
     333            return call_user_func_array(array($this, 'TCPDF::Image'), $args);
     334        }
     335
    305336        $ret = parent::Image($file, $x, $y, $w, $h, $type, $link);
    306         if ($this->_intpl) {
     337        if ($this->_inTpl) {
    307338            $this->_res['tpl'][$this->tpl]['images'][$file] =& $this->images[$file];
    308339        } else {
    309340            $this->_res['page'][$this->page]['images'][$file] =& $this->images[$file];
    310341        }
    311        
     342
    312343        return $ret;
    313344    }
    314    
    315     /**
    316      * See FPDF-Documentation ;-)
    317      *
    318      * AddPage is not available when you're "in" a template.
    319      */
    320     function AddPage($orientation = '', $format = '', $keepmargins = false, $tocpage = false) {
    321         if (is_subclass_of($this, 'TCPDF')) {
    322                 $args = func_get_args();
    323                 return call_user_func_array(array($this, 'TCPDF::AddPage'), $args);
    324         }
    325        
    326         if ($this->_intpl)
    327             $this->Error('Adding pages in templates isn\'t possible!');
    328            
    329         parent::AddPage($orientation, $format);
    330     }
    331 
    332     /**
    333      * Preserve adding Links in Templates ...won't work
    334      */
    335     function Link($x, $y, $w, $h, $link, $spaces = 0) {
    336         if (is_subclass_of($this, 'TCPDF')) {
    337                 $args = func_get_args();
    338                         return call_user_func_array(array($this, 'TCPDF::Link'), $args);
    339         }
    340        
    341         if ($this->_intpl)
    342             $this->Error('Using links in templates aren\'t possible!');
    343            
     345
     346    /**
     347     * Adds a new page to the document.
     348     *
     349     * See FPDF/TCPDF documentation.
     350     *
     351     * This method cannot be used if you'd started a template.
     352     *
     353     * @see http://fpdf.org/en/doc/addpage.htm
     354     * @see http://www.tcpdf.org/doc/code/classTCPDF.html#a5171e20b366b74523709d84c349c1ced
     355     */
     356    public function AddPage($orientation = '', $format = '', $rotationOrKeepmargins = false, $tocpage = false)
     357    {
     358        if (is_subclass_of($this, 'TCPDF')) {
     359            $args = func_get_args();
     360            return call_user_func_array(array($this, 'TCPDF::AddPage'), $args);
     361        }
     362
     363        if ($this->_inTpl) {
     364            throw new LogicException('Adding pages in templates is not possible!');
     365        }
     366
     367        parent::AddPage($orientation, $format, $rotationOrKeepmargins);
     368    }
     369
     370    /**
     371     * Puts a link on a rectangular area of the page.
     372     *
     373     * Overwritten because adding links in a template will not work.
     374     *
     375     * @see http://fpdf.org/en/doc/link.htm
     376     * @see http://www.tcpdf.org/doc/code/classTCPDF.html#ab87bf1826384fbfe30eb499d42f1d994
     377     */
     378    public function Link($x, $y, $w, $h, $link, $spaces = 0)
     379    {
     380        if (is_subclass_of($this, 'TCPDF')) {
     381            $args = func_get_args();
     382            return call_user_func_array(array($this, 'TCPDF::Link'), $args);
     383        }
     384
     385        if ($this->_inTpl) {
     386            throw new LogicException('Using links in templates is not posible!');
     387        }
     388
    344389        parent::Link($x, $y, $w, $h, $link);
    345390    }
    346    
    347     function AddLink() {
    348         if (is_subclass_of($this, 'TCPDF')) {
    349                 $args = func_get_args();
    350                         return call_user_func_array(array($this, 'TCPDF::AddLink'), $args);
    351         }
    352        
    353         if ($this->_intpl)
    354             $this->Error('Adding links in templates aren\'t possible!');
     391
     392    /**
     393     * Creates a new internal link and returns its identifier.
     394     *
     395     * Overwritten because adding links in a template will not work.
     396     *
     397     * @see http://fpdf.org/en/doc/addlink.htm
     398     * @see http://www.tcpdf.org/doc/code/classTCPDF.html#a749522038ed7786c3e1701435dcb891e
     399     */
     400    public function AddLink()
     401    {
     402        if (is_subclass_of($this, 'TCPDF')) {
     403            $args = func_get_args();
     404            return call_user_func_array(array($this, 'TCPDF::AddLink'), $args);
     405        }
     406
     407        if ($this->_inTpl) {
     408            throw new LogicException('Adding links in templates is not possible!');
     409        }
     410
    355411        return parent::AddLink();
    356412    }
    357    
    358     function SetLink($link, $y = 0, $page = -1) {
    359         if (is_subclass_of($this, 'TCPDF')) {
    360                 $args = func_get_args();
    361                         return call_user_func_array(array($this, 'TCPDF::SetLink'), $args);
    362         }
    363        
    364         if ($this->_intpl)
    365             $this->Error('Setting links in templates aren\'t possible!');
     413
     414    /**
     415     * Defines the page and position a link points to.
     416     *
     417     * Overwritten because adding links in a template will not work.
     418     *
     419     * @see http://fpdf.org/en/doc/setlink.htm
     420     * @see http://www.tcpdf.org/doc/code/classTCPDF.html#ace5be60e7857953ea5e2b89cb90df0ae
     421     */
     422    public function SetLink($link, $y = 0, $page = -1)
     423    {
     424        if (is_subclass_of($this, 'TCPDF')) {
     425            $args = func_get_args();
     426            return call_user_func_array(array($this, 'TCPDF::SetLink'), $args);
     427        }
     428
     429        if ($this->_inTpl) {
     430            throw new LogicException('Setting links in templates is not possible!');
     431        }
     432
    366433        parent::SetLink($link, $y, $page);
    367434    }
    368    
    369     /**
    370      * Private Method that writes the form xobjects
    371      */
    372     function _putformxobjects() {
     435
     436    /**
     437     * Writes the form XObjects to the PDF document.
     438     */
     439    protected function _putformxobjects()
     440    {
    373441        $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
    374             reset($this->tpls);
    375         foreach($this->tpls AS $tplidx => $tpl) {
    376 
    377             $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
    378                 $this->_newobj();
    379                 $this->tpls[$tplidx]['n'] = $this->n;
    380                 $this->_out('<<'.$filter.'/Type /XObject');
     442        reset($this->_tpls);
     443
     444        foreach($this->_tpls AS $tplIdx => $tpl) {
     445            $this->_newobj();
     446            $this->_tpls[$tplIdx]['n'] = $this->n;
     447            $this->_out('<<'.$filter.'/Type /XObject');
    381448            $this->_out('/Subtype /Form');
    382449            $this->_out('/FormType 1');
     
    391458                ($tpl['h'] - $tpl['y']) * $this->k
    392459            ));
    393            
     460
    394461            if ($tpl['x'] != 0 || $tpl['y'] != 0) {
    395462                $this->_out(sprintf('/Matrix [1 0 0 1 %.5F %.5F]',
    396                      -$tpl['x'] * $this->k * 2, $tpl['y'] * $this->k * 2
     463                    -$tpl['x'] * $this->k * 2, $tpl['y'] * $this->k * 2
    397464                ));
    398465            }
    399            
     466
    400467            $this->_out('/Resources ');
    401 
    402468            $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
    403                 if (isset($this->_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) {
    404                 $this->_out('/Font <<');
    405                 foreach($this->_res['tpl'][$tplidx]['fonts'] as $font)
    406                         $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
    407                 $this->_out('>>');
     469
     470            if (isset($this->_res['tpl'][$tplIdx])) {
     471                $res = $this->_res['tpl'][$tplIdx];
     472                if (isset($res['fonts']) && count($res['fonts'])) {
     473                    $this->_out('/Font <<');
     474
     475                    foreach($res['fonts'] as $font) {
     476                        $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
     477                    }
     478
     479                    $this->_out('>>');
     480                }
     481
     482                if(isset($res['images']) || isset($res['tpls'])) {
     483                    $this->_out('/XObject <<');
     484
     485                    if (isset($res['images'])) {
     486                        foreach($res['images'] as $image)
     487                            $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
     488                    }
     489
     490                    if (isset($res['tpls'])) {
     491                        foreach($res['tpls'] as $i => $_tpl)
     492                            $this->_out($this->tplPrefix . $i . ' ' . $_tpl['n'] . ' 0 R');
     493                    }
     494
     495                    $this->_out('>>');
     496                }
    408497            }
    409                 if(isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) ||
    410                    isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls']))
    411                 {
    412                 $this->_out('/XObject <<');
    413                 if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) {
    414                     foreach($this->_res['tpl'][$tplidx]['images'] as $image)
    415                                 $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
    416                 }
    417                 if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
    418                     foreach($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl)
    419                         $this->_out($this->tplprefix . $i . ' ' . $tpl['n'] . ' 0 R');
    420                 }
    421                 $this->_out('>>');
    422                 }
    423                 $this->_out('>>');
    424                
    425                 $this->_out('/Length ' . strlen($p) . ' >>');
    426                 $this->_putstream($p);
    427                 $this->_out('endobj');
    428         }
    429     }
    430    
    431     /**
    432      * Overwritten to add _putformxobjects() after _putimages()
    433      *
    434      */
    435     function _putimages() {
     498
     499            $this->_out('>>');
     500
     501            $buffer = ($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
     502            $this->_out('/Length ' . strlen($buffer) . ' >>');
     503            $this->_putstream($buffer);
     504            $this->_out('endobj');
     505        }
     506    }
     507
     508    /**
     509     * Output images.
     510     *
     511     * Overwritten to add {@link _putformxobjects()} after _putimages().
     512     */
     513    public function _putimages()
     514    {
    436515        parent::_putimages();
    437516        $this->_putformxobjects();
    438517    }
    439    
    440     function _putxobjectdict() {
     518
     519    /**
     520     * Writes the references of XObject resources to the document.
     521     *
     522     * Overwritten to add the the templates to the XObject resource dictionary.
     523     */
     524    public function _putxobjectdict()
     525    {
    441526        parent::_putxobjectdict();
    442        
    443         if (count($this->tpls)) {
    444             foreach($this->tpls as $tplidx => $tpl) {
    445                 $this->_out(sprintf('%s%d %d 0 R', $this->tplprefix, $tplidx, $tpl['n']));
    446             }
    447         }
    448     }
    449 
    450     /**
    451      * Private Method
    452      */
    453     function _out($s) {
    454         if ($this->state == 2 && $this->_intpl) {
    455             $this->tpls[$this->tpl]['buffer'] .= $s . "\n";
     527
     528        foreach($this->_tpls as $tplIdx => $tpl) {
     529            $this->_out(sprintf('%s%d %d 0 R', $this->tplPrefix, $tplIdx, $tpl['n']));
     530        }
     531    }
     532
     533    /**
     534     * Writes bytes to the resulting document.
     535     *
     536     * Overwritten to delegate the data to the template buffer.
     537     *
     538     * @param string $s
     539     */
     540    public function _out($s)
     541    {
     542        if ($this->state == 2 && $this->_inTpl) {
     543            $this->_tpls[$this->tpl]['buffer'] .= $s . "\n";
    456544        } else {
    457545            parent::_out($s);
  • _plugins_/fpdf/trunk/fpdi.php

    r99957 r112228  
    11<?php
    2 //
    3 //  FPDI - Version 1.4.4
    4 //
    5 //    Copyright 2004-2013 Setasign - Jan Slabon
    6 //
    7 //  Licensed under the Apache License, Version 2.0 (the "License");
    8 //  you may not use this file except in compliance with the License.
    9 //  You may obtain a copy of the License at
    10 //
    11 //      http://www.apache.org/licenses/LICENSE-2.0
    12 //
    13 //  Unless required by applicable law or agreed to in writing, software
    14 //  distributed under the License is distributed on an "AS IS" BASIS,
    15 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16 //  See the License for the specific language governing permissions and
    17 //  limitations under the License.
    18 //
    19 
    20 define('FPDI_VERSION', '1.4.4');
    21 
    22 // Check for TCPDF and remap TCPDF to FPDF
    23 if (class_exists('TCPDF', false)) {
    24     require_once('fpdi2tcpdf_bridge.php');
     2/**
     3 * This file is part of FPDI
     4 *
     5 * @package   FPDI
     6 * @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com)
     7 * @license   http://opensource.org/licenses/mit-license The MIT License
     8 * @version   1.6.1
     9 */
     10
     11if (!class_exists('FPDF_TPL')) {
     12    require_once('fpdf_tpl.php');
    2513}
    2614
    27 require_once('fpdf_tpl.php');
    28 require_once('fpdi_pdf_parser.php');
    29 
    30 
    31 class FPDI extends FPDF_TPL {
     15/**
     16 * Class FPDI
     17 */
     18class FPDI extends FPDF_TPL
     19{
     20    /**
     21     * FPDI version
     22     *
     23     * @string
     24     */
     25    const VERSION = '1.6.1';
     26
    3227    /**
    3328     * Actual filename
     29     *
    3430     * @var string
    3531     */
    36     var $current_filename;
     32    public $currentFilename;
    3733
    3834    /**
    3935     * Parser-Objects
     36     *
     37     * @var fpdi_pdf_parser[]
     38     */
     39    public $parsers = array();
     40   
     41    /**
     42     * Current parser
     43     *
     44     * @var fpdi_pdf_parser
     45     */
     46    public $currentParser;
     47
     48    /**
     49     * The name of the last imported page box
     50     *
     51     * @var string
     52     */
     53    public $lastUsedPageBox;
     54
     55    /**
     56     * Object stack
     57     *
    4058     * @var array
    4159     */
    42     var $parsers;
    43    
    44     /**
    45      * Current parser
    46      * @var object
    47      */
    48     var $current_parser;
    49    
    50     /**
    51      * object stack
     60    protected $_objStack;
     61   
     62    /**
     63     * Done object stack
     64     *
    5265     * @var array
    5366     */
    54     var $_obj_stack;
    55    
    56     /**
    57      * done object stack
     67    protected $_doneObjStack;
     68
     69    /**
     70     * Current Object Id.
     71     *
     72     * @var integer
     73     */
     74    protected $_currentObjId;
     75   
     76    /**
     77     * Cache for imported pages/template ids
     78     *
    5879     * @var array
    5980     */
    60     var $_don_obj_stack;
    61 
    62     /**
    63      * Current Object Id.
    64      * @var integer
    65      */
    66     var $_current_obj_id;
    67    
    68     /**
    69      * The name of the last imported page box
    70      * @var string
    71      */
    72     var $lastUsedPageBox;
    73    
    74     /**
    75      * Cache for imported pages/template ids
    76      * @var array
    77      */
    78     var $_importedPages = array();
    79    
    80     /**
    81      * Set a source-file
    82      *
    83      * @param string $filename a valid filename
    84      * @return int number of available pages
    85      */
    86     function setSourceFile($filename) {
    87         $this->current_filename = $filename;
    88        
    89         if (!isset($this->parsers[$filename]))
     81    protected $_importedPages = array();
     82   
     83    /**
     84     * Set a source-file.
     85     *
     86     * Depending on the PDF version of the used document the PDF version of the resulting document will
     87     * be adjusted to the higher version.
     88     *
     89     * @param string $filename A valid path to the PDF document from which pages should be imported from
     90     * @return int The number of pages in the document
     91     */
     92    public function setSourceFile($filename)
     93    {
     94        $_filename = realpath($filename);
     95        if (false !== $_filename)
     96            $filename = $_filename;
     97
     98        $this->currentFilename = $filename;
     99       
     100        if (!isset($this->parsers[$filename])) {
    90101            $this->parsers[$filename] = $this->_getPdfParser($filename);
    91         $this->current_parser =& $this->parsers[$filename];
     102            $this->setPdfVersion(
     103                max($this->getPdfVersion(), $this->parsers[$filename]->getPdfVersion())
     104            );
     105        }
     106
     107        $this->currentParser = $this->parsers[$filename];
    92108       
    93109        return $this->parsers[$filename]->getPageCount();
     
    100116     * @return fpdi_pdf_parser
    101117     */
    102     function _getPdfParser($filename) {
    103         return new fpdi_pdf_parser($filename, $this);
    104     }
    105    
    106     /**
    107      * Get the current PDF version
     118    protected function _getPdfParser($filename)
     119    {
     120        if (!class_exists('fpdi_pdf_parser')) {
     121            require_once('fpdi_pdf_parser.php');
     122        }
     123        return new fpdi_pdf_parser($filename);
     124    }
     125   
     126    /**
     127     * Get the current PDF version.
    108128     *
    109129     * @return string
    110130     */
    111     function getPDFVersion() {
    112                 return $this->PDFVersion;
    113         }
    114    
    115         /**
    116      * Set the PDF version
    117      *
    118      * @return string
    119      */
    120         function setPDFVersion($version = '1.3') {
    121                 $this->PDFVersion = $version;
    122         }
    123        
    124     /**
    125      * Import a page
    126      *
    127      * @param int $pageno pagenumber
    128      * @return int Index of imported page - to use with fpdf_tpl::useTemplate()
    129      */
    130     function importPage($pageno, $boxName = '/CropBox') {
    131         if ($this->_intpl) {
    132             return $this->error('Please import the desired pages before creating a new template.');
    133         }
    134        
    135         $fn = $this->current_filename;
    136        
     131    public function getPdfVersion()
     132    {
     133        return $this->PDFVersion;
     134    }
     135   
     136    /**
     137     * Set the PDF version.
     138     *
     139     * @param string $version
     140     */
     141    public function setPdfVersion($version = '1.3')
     142    {
     143        $this->PDFVersion = sprintf('%.1F', $version);
     144    }
     145   
     146    /**
     147     * Import a page.
     148     *
     149     * The second parameter defines the bounding box that should be used to transform the page into a
     150     * form XObject.
     151     *
     152     * Following values are available: MediaBox, CropBox, BleedBox, TrimBox, ArtBox.
     153     * If a box is not especially defined its default box will be used:
     154     *
     155     * <ul>
     156     *   <li>CropBox: Default -> MediaBox</li>
     157     *   <li>BleedBox: Default -> CropBox</li>
     158     *   <li>TrimBox: Default -> CropBox</li>
     159     *   <li>ArtBox: Default -> CropBox</li>
     160     * </ul>
     161     *
     162     * It is possible to get the used page box by the {@link getLastUsedPageBox()} method.
     163     *
     164     * @param int $pageNo The page number
     165     * @param string $boxName The boundary box to use when transforming the page into a form XObject
     166     * @param boolean $groupXObject Define the form XObject as a group XObject to support transparency (if used)
     167     * @return int An id of the imported page/template to use with e.g. fpdf_tpl::useTemplate()
     168     * @throws LogicException|InvalidArgumentException
     169     * @see getLastUsedPageBox()
     170     */
     171    public function importPage($pageNo, $boxName = 'CropBox', $groupXObject = true)
     172    {
     173        if ($this->_inTpl) {
     174            throw new LogicException('Please import the desired pages before creating a new template.');
     175        }
     176       
     177        $fn = $this->currentFilename;
     178        $boxName = '/' . ltrim($boxName, '/');
     179
    137180        // check if page already imported
    138         $pageKey = $fn . '-' . ((int)$pageno) . $boxName;
    139         if (isset($this->_importedPages[$pageKey]))
     181        $pageKey = $fn . '-' . ((int)$pageNo) . $boxName;
     182        if (isset($this->_importedPages[$pageKey])) {
    140183            return $this->_importedPages[$pageKey];
    141        
    142         $parser =& $this->parsers[$fn];
    143         $parser->setPageno($pageno);
    144 
    145         if (!in_array($boxName, $parser->availableBoxes))
    146             return $this->Error(sprintf('Unknown box: %s', $boxName));
    147            
    148         $pageboxes = $parser->getPageBoxes($pageno, $this->k);
     184        }
     185       
     186        $parser = $this->parsers[$fn];
     187        $parser->setPageNo($pageNo);
     188
     189        if (!in_array($boxName, $parser->availableBoxes)) {
     190            throw new InvalidArgumentException(sprintf('Unknown box: %s', $boxName));
     191        }
     192           
     193        $pageBoxes = $parser->getPageBoxes($pageNo, $this->k);
    149194       
    150195        /**
     
    155200         * ArtBox: Default -> CropBox
    156201         */
    157         if (!isset($pageboxes[$boxName]) && ($boxName == '/BleedBox' || $boxName == '/TrimBox' || $boxName == '/ArtBox'))
     202        if (!isset($pageBoxes[$boxName]) && ($boxName == '/BleedBox' || $boxName == '/TrimBox' || $boxName == '/ArtBox'))
    158203            $boxName = '/CropBox';
    159         if (!isset($pageboxes[$boxName]) && $boxName == '/CropBox')
     204        if (!isset($pageBoxes[$boxName]) && $boxName == '/CropBox')
    160205            $boxName = '/MediaBox';
    161206       
    162         if (!isset($pageboxes[$boxName]))
     207        if (!isset($pageBoxes[$boxName]))
    163208            return false;
    164209           
    165210        $this->lastUsedPageBox = $boxName;
    166211       
    167         $box = $pageboxes[$boxName];
     212        $box = $pageBoxes[$boxName];
    168213       
    169214        $this->tpl++;
    170         $this->tpls[$this->tpl] = array();
    171         $tpl =& $this->tpls[$this->tpl];
    172         $tpl['parser'] =& $parser;
     215        $this->_tpls[$this->tpl] = array();
     216        $tpl =& $this->_tpls[$this->tpl];
     217        $tpl['parser'] = $parser;
    173218        $tpl['resources'] = $parser->getPageResources();
    174219        $tpl['buffer'] = $parser->getContent();
    175220        $tpl['box'] = $box;
    176        
     221        $tpl['groupXObject'] = $groupXObject;
     222        if ($groupXObject) {
     223            $this->setPdfVersion(max($this->getPdfVersion(), 1.4));
     224        }
     225
    177226        // To build an array that can be used by PDF_TPL::useTemplate()
    178         $this->tpls[$this->tpl] = array_merge($this->tpls[$this->tpl], $box);
    179        
    180         // An imported page will start at 0,0 everytime. Translation will be set in _putformxobjects()
     227        $this->_tpls[$this->tpl] = array_merge($this->_tpls[$this->tpl], $box);
     228       
     229        // An imported page will start at 0,0 all the time. Translation will be set in _putformxobjects()
    181230        $tpl['x'] = 0;
    182231        $tpl['y'] = 0;
    183232       
    184233        // handle rotated pages
    185         $rotation = $parser->getPageRotation($pageno);
     234        $rotation = $parser->getPageRotation($pageNo);
    186235        $tpl['_rotationAngle'] = 0;
    187236        if (isset($rotation[1]) && ($angle = $rotation[1] % 360) != 0) {
    188                 $steps = $angle / 90;
     237            $steps = $angle / 90;
    189238               
    190239            $_w = $tpl['w'];
     
    194243           
    195244            if ($angle < 0)
    196                 $angle += 360;
    197            
    198                 $tpl['_rotationAngle'] = $angle * -1;
     245                $angle += 360;
     246           
     247            $tpl['_rotationAngle'] = $angle * -1;
    199248        }
    200249       
     
    205254   
    206255    /**
    207      * Returns the last used page box
    208      *
    209      * @return string
    210      */
    211     function getLastUsedPageBox() {
     256     * Returns the last used page boundary box.
     257     *
     258     * @return string The used boundary box: MediaBox, CropBox, BleedBox, TrimBox or ArtBox
     259     */
     260    public function getLastUsedPageBox()
     261    {
    212262        return $this->lastUsedPageBox;
    213263    }
    214    
    215    
    216     function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0, $adjustPageSize = false) {
    217         if ($adjustPageSize == true && is_null($_x) && is_null($_y)) {
    218             $size = $this->getTemplateSize($tplidx, $_w, $_h);
     264
     265    /**
     266     * Use a template or imported page in current page or other template.
     267     *
     268     * You can use a template in a page or in another template.
     269     * You can give the used template a new size. All parameters are optional.
     270     * The width or height is calculated automatically if one is given. If no
     271     * parameter is given the origin size as defined in beginTemplate() or of
     272     * the imported page is used.
     273     *
     274     * The calculated or used width and height are returned as an array.
     275     *
     276     * @param int $tplIdx A valid template-id
     277     * @param int $x The x-position
     278     * @param int $y The y-position
     279     * @param int $w The new width of the template
     280     * @param int $h The new height of the template
     281     * @param boolean $adjustPageSize If set to true the current page will be resized to fit the dimensions
     282     *                                of the template
     283     *
     284     * @return array The height and width of the template (array('w' => ..., 'h' => ...))
     285     * @throws LogicException|InvalidArgumentException
     286     */
     287    public function useTemplate($tplIdx, $x = null, $y = null, $w = 0, $h = 0, $adjustPageSize = false)
     288    {
     289        if ($adjustPageSize == true && is_null($x) && is_null($y)) {
     290            $size = $this->getTemplateSize($tplIdx, $w, $h);
    219291            $orientation = $size['w'] > $size['h'] ? 'L' : 'P';
    220292            $size = array($size['w'], $size['h']);
    221293           
    222294            if (is_subclass_of($this, 'TCPDF')) {
    223                 $this->setPageFormat($size, $orientation);
     295                $this->setPageFormat($size, $orientation);
    224296            } else {
    225                 $size = $this->_getpagesize($size);
    226                
    227                 if($orientation!=$this->CurOrientation || $size[0]!=$this->CurPageSize[0] || $size[1]!=$this->CurPageSize[1])
    228                                 {
    229                                         // New size or orientation
    230                                         if($orientation=='P')
    231                                         {
    232                                                 $this->w = $size[0];
    233                                                 $this->h = $size[1];
    234                                         }
    235                                         else
    236                                         {
    237                                                 $this->w = $size[1];
    238                                                 $this->h = $size[0];
    239                                         }
    240                                         $this->wPt = $this->w*$this->k;
    241                                         $this->hPt = $this->h*$this->k;
    242                                         $this->PageBreakTrigger = $this->h-$this->bMargin;
    243                                         $this->CurOrientation = $orientation;
    244                                         $this->CurPageSize = $size;
    245                                         $this->PageSizes[$this->page] = array($this->wPt, $this->hPt);
    246                                 }
     297                $size = $this->_getpagesize($size);
     298               
     299                if($orientation != $this->CurOrientation ||
     300                    $size[0] != $this->CurPageSize[0] ||
     301                    $size[1] != $this->CurPageSize[1]
     302                ) {
     303                    // New size or orientation
     304                    if ($orientation=='P') {
     305                        $this->w = $size[0];
     306                        $this->h = $size[1];
     307                    } else {
     308                        $this->w = $size[1];
     309                        $this->h = $size[0];
     310                    }
     311                    $this->wPt = $this->w * $this->k;
     312                    $this->hPt = $this->h * $this->k;
     313                    $this->PageBreakTrigger = $this->h - $this->bMargin;
     314                    $this->CurOrientation = $orientation;
     315                    $this->CurPageSize = $size;
     316                    if (FPDF_VERSION >= 1.8) {
     317                        $this->PageInfo[$this->page]['size'] = array($this->wPt, $this->hPt);
     318                    } else {
     319                        $this->PageSizes[$this->page] = array($this->wPt, $this->hPt);
     320                    }
     321                }
    247322            }
    248323        }
    249324       
    250325        $this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values
    251         $s = parent::useTemplate($tplidx, $_x, $_y, $_w, $_h);
     326        $size = parent::useTemplate($tplIdx, $x, $y, $w, $h);
    252327        $this->_out('Q');
    253328       
    254         return $s;
    255     }
    256    
    257     /**
    258      * Private method, that rebuilds all needed objects of source files
    259      */
    260     function _putimportedobjects() {
    261         if (is_array($this->parsers) && count($this->parsers) > 0) {
    262             foreach($this->parsers AS $filename => $p) {
    263                 $this->current_parser =& $this->parsers[$filename];
    264                 if (isset($this->_obj_stack[$filename]) && is_array($this->_obj_stack[$filename])) {
    265                     while(($n = key($this->_obj_stack[$filename])) !== null) {
    266                         $nObj = $this->current_parser->pdf_resolve_object($this->current_parser->c, $this->_obj_stack[$filename][$n][1]);
    267                                                
    268                         $this->_newobj($this->_obj_stack[$filename][$n][0]);
    269                        
    270                         if ($nObj[0] == PDF_TYPE_STREAM) {
    271                                                         $this->pdf_write_value($nObj);
    272                         } else {
    273                             $this->pdf_write_value($nObj[1]);
    274                         }
    275                        
    276                         $this->_out('endobj');
    277                         $this->_obj_stack[$filename][$n] = null; // free memory
    278                         unset($this->_obj_stack[$filename][$n]);
    279                         reset($this->_obj_stack[$filename]);
    280                     }
    281                 }
    282             }
    283         }
    284     }
    285    
    286    
    287     /**
    288      * Private Method that writes the form xobjects
    289      */
    290     function _putformxobjects() {
    291         $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
    292             reset($this->tpls);
    293         foreach($this->tpls AS $tplidx => $tpl) {
    294             $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
    295                 $this->_newobj();
    296                 $cN = $this->n; // TCPDF/Protection: rem current "n"
    297                
    298                 $this->tpls[$tplidx]['n'] = $this->n;
    299                 $this->_out('<<' . $filter . '/Type /XObject');
     329        return $size;
     330    }
     331   
     332    /**
     333     * Copy all imported objects to the resulting document.
     334     */
     335    protected function _putimportedobjects()
     336    {
     337        foreach($this->parsers AS $filename => $p) {
     338            $this->currentParser = $p;
     339            if (!isset($this->_objStack[$filename]) || !is_array($this->_objStack[$filename])) {
     340                continue;
     341            }
     342            while(($n = key($this->_objStack[$filename])) !== null) {
     343                try {
     344                    $nObj = $this->currentParser->resolveObject($this->_objStack[$filename][$n][1]);
     345                } catch (Exception $e) {
     346                    $nObj = array(pdf_parser::TYPE_OBJECT, pdf_parser::TYPE_NULL);
     347                }
     348
     349                $this->_newobj($this->_objStack[$filename][$n][0]);
     350
     351                if ($nObj[0] == pdf_parser::TYPE_STREAM) {
     352                    $this->_writeValue($nObj);
     353                } else {
     354                    $this->_writeValue($nObj[1]);
     355                }
     356
     357                $this->_out("\nendobj");
     358                $this->_objStack[$filename][$n] = null; // free memory
     359                unset($this->_objStack[$filename][$n]);
     360                reset($this->_objStack[$filename]);
     361            }
     362        }
     363    }
     364
     365    /**
     366     * Writes the form XObjects to the PDF document.
     367     */
     368    protected function _putformxobjects()
     369    {
     370        $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
     371        reset($this->_tpls);
     372        foreach($this->_tpls AS $tplIdx => $tpl) {
     373            $this->_newobj();
     374            $currentN = $this->n; // TCPDF/Protection: rem current "n"
     375           
     376            $this->_tpls[$tplIdx]['n'] = $this->n;
     377            $this->_out('<<' . $filter . '/Type /XObject');
    300378            $this->_out('/Subtype /Form');
    301379            $this->_out('/FormType 1');
     
    319397                if ($tpl['_rotationAngle'] <> 0) {
    320398                    $angle = $tpl['_rotationAngle'] * M_PI/180;
    321                     $c=cos($angle);
    322                     $s=sin($angle);
     399                    $c = cos($angle);
     400                    $s = sin($angle);
    323401                   
    324402                    switch($tpl['_rotationAngle']) {
    325403                        case -90:
    326                            $tx = -$tpl['box']['lly'];
    327                            $ty = $tpl['box']['urx'];
    328                            break;
     404                            $tx = -$tpl['box']['lly'];
     405                            $ty = $tpl['box']['urx'];
     406                            break;
    329407                        case -180:
    330408                            $tx = $tpl['box']['urx'];
     
    332410                            break;
    333411                        case -270:
    334                                 $tx = $tpl['box']['ury'];
     412                            $tx = $tpl['box']['ury'];
    335413                            $ty = -$tpl['box']['llx'];
    336414                            break;
    337415                    }
    338416                }
    339             } elseif ($tpl['x'] != 0 || $tpl['y'] != 0) {
     417            } else if ($tpl['x'] != 0 || $tpl['y'] != 0) {
    340418                $tx = -$tpl['x'] * 2;
    341419                $ty = $tpl['y'] * 2;
     
    354432
    355433            if (isset($tpl['resources'])) {
    356                 $this->current_parser =& $tpl['parser'];
    357                 $this->pdf_write_value($tpl['resources']); // "n" will be changed
     434                $this->currentParser = $tpl['parser'];
     435                $this->_writeValue($tpl['resources']); // "n" will be changed
    358436            } else {
     437
    359438                $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
    360                 if (isset($this->_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) {
    361                         $this->_out('/Font <<');
    362                     foreach($this->_res['tpl'][$tplidx]['fonts'] as $font)
    363                                 $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
    364                         $this->_out('>>');
    365                 }
    366                 if(isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) ||
    367                    isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls']))
    368                 {
    369                     $this->_out('/XObject <<');
    370                     if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) {
    371                         foreach($this->_res['tpl'][$tplidx]['images'] as $image)
    372                                         $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
     439                if (isset($this->_res['tpl'][$tplIdx])) {
     440                    $res = $this->_res['tpl'][$tplIdx];
     441
     442                    if (isset($res['fonts']) && count($res['fonts'])) {
     443                        $this->_out('/Font <<');
     444                        foreach ($res['fonts'] as $font)
     445                            $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
     446                        $this->_out('>>');
    373447                    }
    374                     if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
    375                         foreach($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl)
    376                             $this->_out($this->tplprefix . $i . ' ' . $tpl['n'] . ' 0 R');
     448                    if (isset($res['images']) && count($res['images']) ||
     449                       isset($res['tpls']) && count($res['tpls']))
     450                    {
     451                        $this->_out('/XObject <<');
     452                        if (isset($res['images'])) {
     453                            foreach ($res['images'] as $image)
     454                                $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
     455                        }
     456                        if (isset($res['tpls'])) {
     457                            foreach ($res['tpls'] as $i => $_tpl)
     458                                $this->_out($this->tplPrefix . $i . ' ' . $_tpl['n'] . ' 0 R');
     459                        }
     460                        $this->_out('>>');
    377461                    }
    378462                    $this->_out('>>');
    379                 }
    380                 $this->_out('>>');
    381             }
    382            
    383             $this->_out('/Group <</Type/Group/S/Transparency>>');
    384            
    385             $nN = $this->n; // TCPDF: rem new "n"
    386             $this->n = $cN; // TCPDF: reset to current "n"
     463                }
     464            }
     465
     466            if (isset($tpl['groupXObject']) && $tpl['groupXObject']) {
     467                $this->_out('/Group <</Type/Group/S/Transparency>>');
     468            }
     469
     470            $newN = $this->n; // TCPDF: rem new "n"
     471            $this->n = $currentN; // TCPDF: reset to current "n"
     472
     473            $buffer = ($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
     474
    387475            if (is_subclass_of($this, 'TCPDF')) {
    388                 $p = $this->_getrawstream($p);
    389                 $this->_out('/Length ' . strlen($p) . ' >>');
    390                 $this->_out("stream\n" . $p . "\nendstream");
     476                $buffer = $this->_getrawstream($buffer);
     477                $this->_out('/Length ' . strlen($buffer) . ' >>');
     478                $this->_out("stream\n" . $buffer . "\nendstream");
    391479            } else {
    392                     $this->_out('/Length ' . strlen($p) . ' >>');
    393                         $this->_putstream($p);
    394             }
    395                 $this->_out('endobj');
    396                 $this->n = $nN; // TCPDF: reset to new "n"
     480                $this->_out('/Length ' . strlen($buffer) . ' >>');
     481                $this->_putstream($buffer);
     482            }
     483            $this->_out('endobj');
     484            $this->n = $newN; // TCPDF: reset to new "n"
    397485        }
    398486       
     
    401489
    402490    /**
     491     * Creates and optionally write the object definition to the document.
     492     *
    403493     * Rewritten to handle existing own defined objects
    404      */
    405     function _newobj($obj_id = false, $onlynewobj = false) {
    406         if (!$obj_id) {
    407             $obj_id = ++$this->n;
    408         }
    409 
    410         //Begin a new object
    411         if (!$onlynewobj) {
    412             $this->offsets[$obj_id] = is_subclass_of($this, 'TCPDF') ? $this->bufferlen : strlen($this->buffer);
    413             $this->_out($obj_id . ' 0 obj');
    414             $this->_current_obj_id = $obj_id; // for later use with encryption
    415         }
    416        
    417         return $obj_id;
    418     }
    419 
    420     /**
    421      * Writes a value
     494     *
     495     * @param bool $objId
     496     * @param bool $onlyNewObj
     497     * @return bool|int
     498     */
     499    public function _newobj($objId = false, $onlyNewObj = false)
     500    {
     501        if (!$objId) {
     502            $objId = ++$this->n;
     503        }
     504
     505        // Begin a new object
     506        if (!$onlyNewObj) {
     507            $this->offsets[$objId] = is_subclass_of($this, 'TCPDF') ? $this->bufferlen : strlen($this->buffer);
     508            $this->_out($objId . ' 0 obj');
     509            $this->_currentObjId = $objId; // for later use with encryption
     510        }
     511       
     512        return $objId;
     513    }
     514
     515    /**
     516     * Writes a PDF value to the resulting document.
     517     *
    422518     * Needed to rebuild the source document
    423519     *
    424520     * @param mixed $value A PDF-Value. Structure of values see cases in this method
    425521     */
    426     function pdf_write_value(&$value)
     522    protected function _writeValue(&$value)
    427523    {
    428524        if (is_subclass_of($this, 'TCPDF')) {
    429             parent::pdf_write_value($value);
     525            parent::_prepareValue($value);
    430526        }
    431527       
    432528        switch ($value[0]) {
    433529
    434                 case PDF_TYPE_TOKEN:
     530            case pdf_parser::TYPE_TOKEN:
    435531                $this->_straightOut($value[1] . ' ');
    436                         break;
    437                     case PDF_TYPE_NUMERIC:
    438                 case PDF_TYPE_REAL:
     532                break;
     533            case pdf_parser::TYPE_NUMERIC:
     534            case pdf_parser::TYPE_REAL:
    439535                if (is_float($value[1]) && $value[1] != 0) {
    440                             $this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' ');
    441                         } else {
    442                                 $this->_straightOut($value[1] . ' ');
    443                         }
    444                         break;
    445                        
    446                 case PDF_TYPE_ARRAY:
    447 
    448                         // An array. Output the proper
    449                         // structure and move on.
    450 
    451                         $this->_straightOut('[');
     536                    $this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' ');
     537                } else {
     538                    $this->_straightOut($value[1] . ' ');
     539                }
     540                break;
     541               
     542            case pdf_parser::TYPE_ARRAY:
     543
     544                // An array. Output the proper
     545                // structure and move on.
     546
     547                $this->_straightOut('[');
    452548                for ($i = 0; $i < count($value[1]); $i++) {
    453                                 $this->pdf_write_value($value[1][$i]);
    454                         }
    455 
    456                         $this->_out(']');
    457                         break;
    458 
    459                 case PDF_TYPE_DICTIONARY:
    460 
    461                         // A dictionary.
    462                         $this->_straightOut('<<');
    463 
    464                         reset ($value[1]);
    465 
    466                         while (list($k, $v) = each($value[1])) {
    467                                 $this->_straightOut($k . ' ');
    468                                 $this->pdf_write_value($v);
    469                         }
    470 
    471                         $this->_straightOut('>>');
    472                         break;
    473 
    474                 case PDF_TYPE_OBJREF:
    475 
    476                         // An indirect object reference
    477                         // Fill the object stack if needed
    478                         $cpfn =& $this->current_parser->filename;
    479                        
    480                         if (!isset($this->_don_obj_stack[$cpfn][$value[1]])) {
    481                             $this->_newobj(false, true);
    482                             $this->_obj_stack[$cpfn][$value[1]] = array($this->n, $value);
    483                     $this->_don_obj_stack[$cpfn][$value[1]] = array($this->n, $value); // Value is maybee obsolete!!!
    484                 }
    485                 $objid = $this->_don_obj_stack[$cpfn][$value[1]][0];
    486 
    487                         $this->_out($objid . ' 0 R');
    488                         break;
    489 
    490                 case PDF_TYPE_STRING:
    491 
    492                         // A string.
     549                    $this->_writeValue($value[1][$i]);
     550                }
     551
     552                $this->_out(']');
     553                break;
     554
     555            case pdf_parser::TYPE_DICTIONARY:
     556
     557                // A dictionary.
     558                $this->_straightOut('<<');
     559
     560                reset ($value[1]);
     561
     562                while (list($k, $v) = each($value[1])) {
     563                    $this->_straightOut($k . ' ');
     564                    $this->_writeValue($v);
     565                }
     566
     567                $this->_straightOut('>>');
     568                break;
     569
     570            case pdf_parser::TYPE_OBJREF:
     571
     572                // An indirect object reference
     573                // Fill the object stack if needed
     574                $cpfn =& $this->currentParser->filename;
     575                if (!isset($this->_doneObjStack[$cpfn][$value[1]])) {
     576                    $this->_newobj(false, true);
     577                    $this->_objStack[$cpfn][$value[1]] = array($this->n, $value);
     578                    $this->_doneObjStack[$cpfn][$value[1]] = array($this->n, $value);
     579                }
     580                $objId = $this->_doneObjStack[$cpfn][$value[1]][0];
     581
     582                $this->_out($objId . ' 0 R');
     583                break;
     584
     585            case pdf_parser::TYPE_STRING:
     586
     587                // A string.
    493588                $this->_straightOut('(' . $value[1] . ')');
    494589
    495                         break;
    496 
    497                 case PDF_TYPE_STREAM:
    498 
    499                         // A stream. First, output the
    500                         // stream dictionary, then the
    501                         // stream data itself.
    502                 $this->pdf_write_value($value[1]);
    503                         $this->_out('stream');
    504                         $this->_out($value[2][1]);
    505                         $this->_out('endstream');
    506                         break;
    507                        
    508             case PDF_TYPE_HEX:
     590                break;
     591
     592            case pdf_parser::TYPE_STREAM:
     593
     594                // A stream. First, output the
     595                // stream dictionary, then the
     596                // stream data itself.
     597                $this->_writeValue($value[1]);
     598                $this->_out('stream');
     599                $this->_out($value[2][1]);
     600                $this->_straightOut("endstream");
     601                break;
     602               
     603            case pdf_parser::TYPE_HEX:
    509604                $this->_straightOut('<' . $value[1] . '>');
    510605                break;
    511606
    512             case PDF_TYPE_BOOLEAN:
    513                     $this->_straightOut($value[1] ? 'true ' : 'false ');
    514                     break;
    515            
    516                 case PDF_TYPE_NULL:
     607            case pdf_parser::TYPE_BOOLEAN:
     608                $this->_straightOut($value[1] ? 'true ' : 'false ');
     609                break;
     610           
     611            case pdf_parser::TYPE_NULL:
    517612                // The null object.
    518 
    519                         $this->_straightOut('null ');
    520                         break;
    521         }
    522     }
    523    
    524    
    525     /**
    526      * Modified so not each call will add a newline to the output.
    527      */
    528     function _straightOut($s) {
     613                $this->_straightOut('null ');
     614                break;
     615        }
     616    }
     617   
     618   
     619    /**
     620     * Modified _out() method so not each call will add a newline to the output.
     621     */
     622    protected function _straightOut($s)
     623    {
    529624        if (!is_subclass_of($this, 'TCPDF')) {
    530             if($this->state==2)
    531                         $this->pages[$this->page] .= $s;
    532                 else
    533                         $this->buffer .= $s;
     625            if ($this->state == 2) {
     626                $this->pages[$this->page] .= $s;
     627            } else {
     628                $this->buffer .= $s;
     629            }
     630
    534631        } else {
    535632            if ($this->state == 2) {
    536                                 if ($this->inxobj) {
    537                                         // we are inside an XObject template
    538                                         $this->xobjects[$this->xobjid]['outdata'] .= $s;
    539                                 } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
    540                                         // puts data before page footer
    541                                         $pagebuff = $this->getPageBuffer($this->page);
    542                                         $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
    543                                         $footer = substr($pagebuff, -$this->footerlen[$this->page]);
    544                                         $this->setPageBuffer($this->page, $page.$s.$footer);
    545                                         // update footer position
    546                                         $this->footerpos[$this->page] += strlen($s);
    547                                 } else {
    548                                         // set page data
    549                                         $this->setPageBuffer($this->page, $s, true);
    550                                 }
    551                         } elseif ($this->state > 0) {
    552                                 // set general data
    553                                 $this->setBuffer($s);
    554                         }
    555         }
    556     }
    557 
    558     /**
    559      * rewritten to close opened parsers
    560      *
    561      */
    562     function _enddoc() {
     633                if ($this->inxobj) {
     634                    // we are inside an XObject template
     635                    $this->xobjects[$this->xobjid]['outdata'] .= $s;
     636                } else if ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
     637                    // puts data before page footer
     638                    $pagebuff = $this->getPageBuffer($this->page);
     639                    $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
     640                    $footer = substr($pagebuff, -$this->footerlen[$this->page]);
     641                    $this->setPageBuffer($this->page, $page . $s . $footer);
     642                    // update footer position
     643                    $this->footerpos[$this->page] += strlen($s);
     644                } else {
     645                    // set page data
     646                    $this->setPageBuffer($this->page, $s, true);
     647                }
     648            } else if ($this->state > 0) {
     649                // set general data
     650                $this->setBuffer($s);
     651            }
     652        }
     653    }
     654
     655    /**
     656     * Ends the document
     657     *
     658     * Overwritten to close opened parsers
     659     */
     660    public function _enddoc()
     661    {
    563662        parent::_enddoc();
    564663        $this->_closeParsers();
     
    566665   
    567666    /**
    568      * close all files opened by parsers
    569      */
    570     function _closeParsers() {
    571         if ($this->state > 2 && count($this->parsers) > 0) {
    572                 $this->cleanUp();
     667     * Close all files opened by parsers.
     668     *
     669     * @return boolean
     670     */
     671    protected function _closeParsers()
     672    {
     673        if ($this->state > 2) {
     674            $this->cleanUp();
    573675            return true;
    574676        }
     677
    575678        return false;
    576679    }
    577680   
    578681    /**
    579      * Removes cylced references and closes the file handles of the parser objects
    580      */
    581     function cleanUp() {
    582         foreach ($this->parsers as $k => $_){
    583                 $this->parsers[$k]->cleanUp();
    584                 $this->parsers[$k] = null;
    585                 unset($this->parsers[$k]);
     682     * Removes cycled references and closes the file handles of the parser objects.
     683     */
     684    public function cleanUp()
     685    {
     686        while (($parser = array_pop($this->parsers)) !== null) {
     687            /**
     688             * @var fpdi_pdf_parser $parser
     689             */
     690            $parser->closeFile();
    586691        }
    587692    }
  • _plugins_/fpdf/trunk/fpdi_pdf_parser.php

    r99957 r112228  
    11<?php
    2 //
    3 //  FPDI - Version 1.4.4
    4 //
    5 //    Copyright 2004-2013 Setasign - Jan Slabon
    6 //
    7 //  Licensed under the Apache License, Version 2.0 (the "License");
    8 //  you may not use this file except in compliance with the License.
    9 //  You may obtain a copy of the License at
    10 //
    11 //      http://www.apache.org/licenses/LICENSE-2.0
    12 //
    13 //  Unless required by applicable law or agreed to in writing, software
    14 //  distributed under the License is distributed on an "AS IS" BASIS,
    15 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16 //  See the License for the specific language governing permissions and
    17 //  limitations under the License.
    18 //
    19 
    20 require_once('pdf_parser.php');
    21 
    22 class fpdi_pdf_parser extends pdf_parser {
    23 
     2/**
     3 * This file is part of FPDI
     4 *
     5 * @package   FPDI
     6 * @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com)
     7 * @license   http://opensource.org/licenses/mit-license The MIT License
     8 * @version   1.6.1
     9 */
     10
     11if (!class_exists('pdf_parser')) {
     12    require_once('pdf_parser.php');
     13}
     14
     15/**
     16 * Class fpdi_pdf_parser
     17 */
     18class fpdi_pdf_parser extends pdf_parser
     19{
    2420    /**
    2521     * Pages
    26      * Index beginns at 0
     22     *
     23     * Index begins at 0
    2724     *
    2825     * @var array
    2926     */
    30     var $pages;
     27    protected $_pages;
    3128   
    3229    /**
    3330     * Page count
     31     *
    3432     * @var integer
    3533     */
    36     var $page_count;
    37    
    38     /**
    39      * actual page number
     34    protected $_pageCount;
     35   
     36    /**
     37     * Current page number
     38     *
    4039     * @var integer
    4140     */
    42     var $pageno;
    43    
    44     /**
    45      * PDF Version of imported Document
     41    public $pageNo;
     42   
     43    /**
     44     * PDF version of imported document
     45     *
    4646     * @var string
    4747     */
    48     var $pdfVersion;
    49    
    50     /**
    51      * FPDI Reference
    52      * @var object
    53      */
    54     var $fpdi;
     48    public $_pdfVersion;
    5549   
    5650    /**
     
    5953     * @var array
    6054     */
    61     var $availableBoxes = array('/MediaBox', '/CropBox', '/BleedBox', '/TrimBox', '/ArtBox');
    62        
    63     /**
    64      * Constructor
    65      *
    66      * @param string $filename  Source-Filename
    67      * @param object $fpdi      Object of type fpdi
    68      */
    69     function fpdi_pdf_parser($filename, &$fpdi) {
    70         $this->fpdi =& $fpdi;
    71                
    72         parent::pdf_parser($filename);
     55    public $availableBoxes = array('/MediaBox', '/CropBox', '/BleedBox', '/TrimBox', '/ArtBox');
     56       
     57    /**
     58     * The constructor.
     59     *
     60     * @param string $filename The source filename
     61     */
     62    public function __construct($filename)
     63    {
     64        parent::__construct($filename);
    7365
    7466        // resolve Pages-Dictonary
    75         $pages = $this->pdf_resolve_object($this->c, $this->root[1][1]['/Pages']);
     67        $pages = $this->resolveObject($this->_root[1][1]['/Pages']);
    7668
    7769        // Read pages
    78         $this->read_pages($this->c, $pages, $this->pages);
     70        $this->_readPages($pages, $this->_pages);
    7971       
    8072        // count pages;
    81         $this->page_count = count($this->pages);
    82     }
    83    
    84     /**
    85      * Removes reference to fpdi object and closes the file handle
    86      */
    87     function cleanUp() {
    88         $this->fpdi = null;
    89         $this->closeFile();
    90     }
    91    
    92     /**
    93      * Overwrite parent::error()
    94      *
    95      * @param string $msg  Error-Message
    96      */
    97     function error($msg) {
    98         $this->fpdi->error($msg);       
    99     }
    100    
    101     /**
    102      * Get pagecount from sourcefile
     73        $this->_pageCount = count($this->_pages);
     74    }
     75   
     76    /**
     77     * Get page count from source file.
    10378     *
    10479     * @return int
    10580     */
    106     function getPageCount() {
    107         return $this->page_count;
    108     }
    109 
    110 
    111     /**
    112      * Set pageno
    113      *
    114      * @param int $pageno Pagenumber to use
    115      */
    116     function setPageno($pageno) {
    117         $pageno = ((int) $pageno) - 1;
    118 
    119         if ($pageno < 0 || $pageno >= $this->getPageCount()) {
    120             $this->fpdi->error('Pagenumber is wrong!');
    121         }
    122 
    123         $this->pageno = $pageno;
     81    public function getPageCount()
     82    {
     83        return $this->_pageCount;
     84    }
     85
     86    /**
     87     * Set the page number.
     88     *
     89     * @param int $pageNo Page number to use
     90     * @throws InvalidArgumentException
     91     */
     92    public function setPageNo($pageNo)
     93    {
     94        $pageNo = ((int) $pageNo) - 1;
     95
     96        if ($pageNo < 0 || $pageNo >= $this->getPageCount()) {
     97            throw new InvalidArgumentException('Invalid page number!');
     98        }
     99
     100        $this->pageNo = $pageNo;
    124101    }
    125102   
     
    127104     * Get page-resources from current page
    128105     *
    129      * @return array
    130      */
    131     function getPageResources() {
    132         return $this->_getPageResources($this->pages[$this->pageno]);
    133     }
    134    
    135     /**
    136      * Get page-resources from /Page
     106     * @return array|boolean
     107     */
     108    public function getPageResources()
     109    {
     110        return $this->_getPageResources($this->_pages[$this->pageNo]);
     111    }
     112   
     113    /**
     114     * Get page-resources from a /Page dictionary.
    137115     *
    138116     * @param array $obj Array of pdf-data
    139      */
    140     function _getPageResources ($obj) { // $obj = /Page
    141         $obj = $this->pdf_resolve_object($this->c, $obj);
     117     * @return array|boolean
     118     */
     119    protected function _getPageResources($obj)
     120    {
     121        $obj = $this->resolveObject($obj);
    142122
    143123        // If the current object has a resources
    144         // dictionary associated with it, we use
    145         // it. Otherwise, we move back to its
    146         // parent object.
    147         if (isset ($obj[1][1]['/Resources'])) {
    148                 $res = $this->pdf_resolve_object($this->c, $obj[1][1]['/Resources']);
    149                 if ($res[0] == PDF_TYPE_OBJECT)
     124        // dictionary associated with it, we use
     125        // it. Otherwise, we move back to its
     126        // parent object.
     127        if (isset($obj[1][1]['/Resources'])) {
     128            $res = $this->resolveObject($obj[1][1]['/Resources']);
     129            if ($res[0] == pdf_parser::TYPE_OBJECT)
    150130                return $res[1];
    151131            return $res;
    152         } else {
    153                 if (!isset ($obj[1][1]['/Parent'])) {
    154                         return false;
    155                 } else {
    156                 $res = $this->_getPageResources($obj[1][1]['/Parent']);
    157                 if ($res[0] == PDF_TYPE_OBJECT)
    158                     return $res[1];
    159                 return $res;
    160                 }
    161         }
    162     }
    163 
    164 
    165     /**
    166      * Get content of current page
    167      *
    168      * If more /Contents is an array, the streams are concated
     132        }
     133
     134        if (!isset($obj[1][1]['/Parent'])) {
     135            return false;
     136        }
     137
     138        $res = $this->_getPageResources($obj[1][1]['/Parent']);
     139        if ($res[0] == pdf_parser::TYPE_OBJECT)
     140            return $res[1];
     141        return $res;
     142    }
     143
     144    /**
     145     * Get content of current page.
     146     *
     147     * If /Contents is an array, the streams are concatenated
    169148     *
    170149     * @return string
    171150     */
    172     function getContent() {
     151    public function getContent()
     152    {
    173153        $buffer = '';
    174154       
    175         if (isset($this->pages[$this->pageno][1][1]['/Contents'])) {
    176             $contents = $this->_getPageContent($this->pages[$this->pageno][1][1]['/Contents']);
    177             foreach($contents AS $tmp_content) {
    178                 $buffer .= $this->_rebuildContentStream($tmp_content) . ' ';
     155        if (isset($this->_pages[$this->pageNo][1][1]['/Contents'])) {
     156            $contents = $this->_getPageContent($this->_pages[$this->pageNo][1][1]['/Contents']);
     157            foreach ($contents AS $tmpContent) {
     158                $buffer .= $this->_unFilterStream($tmpContent) . ' ';
    179159            }
    180160        }
     
    182162        return $buffer;
    183163    }
    184    
    185    
    186     /**
    187      * Resolve all content-objects
    188      *
    189      * @param array $content_ref
     164
     165    /**
     166     * Resolve all content objects.
     167     *
     168     * @param array $contentRef
    190169     * @return array
    191170     */
    192     function _getPageContent($content_ref) {
     171    protected function _getPageContent($contentRef)
     172    {
    193173        $contents = array();
    194174       
    195         if ($content_ref[0] == PDF_TYPE_OBJREF) {
    196             $content = $this->pdf_resolve_object($this->c, $content_ref);
    197             if ($content[1][0] == PDF_TYPE_ARRAY) {
     175        if ($contentRef[0] == pdf_parser::TYPE_OBJREF) {
     176            $content = $this->resolveObject($contentRef);
     177            if ($content[1][0] == pdf_parser::TYPE_ARRAY) {
    198178                $contents = $this->_getPageContent($content[1]);
    199179            } else {
    200180                $contents[] = $content;
    201181            }
    202         } elseif ($content_ref[0] == PDF_TYPE_ARRAY) {
    203             foreach ($content_ref[1] AS $tmp_content_ref) {
    204                 $contents = array_merge($contents,$this->_getPageContent($tmp_content_ref));
     182        } else if ($contentRef[0] == pdf_parser::TYPE_ARRAY) {
     183            foreach ($contentRef[1] AS $tmp_content_ref) {
     184                $contents = array_merge($contents, $this->_getPageContent($tmp_content_ref));
    205185            }
    206186        }
     
    209189    }
    210190
    211 
    212     /**
    213      * Rebuild content-streams
    214      *
    215      * @param array $obj
    216      * @return string
    217      */
    218     function _rebuildContentStream($obj) {
    219         $filters = array();
    220        
    221         if (isset($obj[1][1]['/Filter'])) {
    222             $_filter = $obj[1][1]['/Filter'];
    223 
    224             if ($_filter[0] == PDF_TYPE_OBJREF) {
    225                 $tmpFilter = $this->pdf_resolve_object($this->c, $_filter);
    226                 $_filter = $tmpFilter[1];
    227             }
     191    /**
     192     * Get a boundary box from a page
     193     *
     194     * Array format is same as used by FPDF_TPL.
     195     *
     196     * @param array $page a /Page dictionary
     197     * @param string $boxIndex Type of box {see {@link $availableBoxes})
     198     * @param float Scale factor from user space units to points
     199     *
     200     * @return array|boolean
     201     */
     202    protected function _getPageBox($page, $boxIndex, $k)
     203    {
     204        $page = $this->resolveObject($page);
     205        $box = null;
     206        if (isset($page[1][1][$boxIndex])) {
     207            $box = $page[1][1][$boxIndex];
     208        }
     209       
     210        if (!is_null($box) && $box[0] == pdf_parser::TYPE_OBJREF) {
     211            $tmp_box = $this->resolveObject($box);
     212            $box = $tmp_box[1];
     213        }
    228214           
    229             if ($_filter[0] == PDF_TYPE_TOKEN) {
    230                 $filters[] = $_filter;
    231             } elseif ($_filter[0] == PDF_TYPE_ARRAY) {
    232                 $filters = $_filter[1];
    233             }
    234         }
    235 
    236         $stream = $obj[2][1];
    237 
    238         foreach ($filters AS $_filter) {
    239             switch ($_filter[1]) {
    240                 case '/FlateDecode':
    241                 case '/Fl':
    242                         // $stream .= "\x0F\x0D"; // in an errorious stream this suffix could work
    243                         // $stream .= "\x0A";
    244                         // $stream .= "\x0D";
    245                         if (function_exists('gzuncompress')) {
    246                                 $oStream = $stream;
    247                         $stream = (strlen($stream) > 0) ? @gzuncompress($stream) : '';
    248                     } else {
    249                         $this->error(sprintf('To handle %s filter, please compile php with zlib support.',$_filter[1]));
    250                     }
    251                    
    252                     if ($stream === false) {
    253                         $oStream = substr($oStream, 2);
    254                         $stream = @gzinflate($oStream);
    255                         if ($stream == false) {
    256                                 $this->error('Error while decompressing stream.');
    257                         }
    258                     }
    259                 break;
    260                 case '/LZWDecode':
    261                     include_once('filters/FilterLZW_FPDI.php');
    262                     $decoder = new FilterLZW_FPDI($this->fpdi);
    263                     $stream = $decoder->decode($stream);
    264                     break;
    265                 case '/ASCII85Decode':
    266                     include_once('filters/FilterASCII85_FPDI.php');
    267                     $decoder = new FilterASCII85_FPDI($this->fpdi);
    268                     $stream = $decoder->decode($stream);
    269                     break;
    270                 case null:
    271                     $stream = $stream;
    272                 break;
    273                 default:
    274                     $this->error(sprintf('Unsupported Filter: %s',$_filter[1]));
    275             }
    276         }
    277        
    278         return $stream;
    279     }
    280    
    281    
    282     /**
    283      * Get a Box from a page
    284      * Arrayformat is same as used by fpdf_tpl
    285      *
    286      * @param array $page a /Page
    287      * @param string $box_index Type of Box @see $availableBoxes
    288      * @param float Scale factor from user space units to points
    289      * @return array
    290      */
    291     function getPageBox($page, $box_index, $k) {
    292         $page = $this->pdf_resolve_object($this->c, $page);
    293         $box = null;
    294         if (isset($page[1][1][$box_index]))
    295             $box =& $page[1][1][$box_index];
    296        
    297         if (!is_null($box) && $box[0] == PDF_TYPE_OBJREF) {
    298             $tmp_box = $this->pdf_resolve_object($this->c, $box);
    299             $box = $tmp_box[1];
    300         }
    301            
    302         if (!is_null($box) && $box[0] == PDF_TYPE_ARRAY) {
    303             $b =& $box[1];
    304             return array('x' => $b[0][1] / $k,
    305                          'y' => $b[1][1] / $k,
    306                          'w' => abs($b[0][1] - $b[2][1]) / $k,
    307                          'h' => abs($b[1][1] - $b[3][1]) / $k,
    308                          'llx' => min($b[0][1], $b[2][1]) / $k,
    309                          'lly' => min($b[1][1], $b[3][1]) / $k,
    310                          'urx' => max($b[0][1], $b[2][1]) / $k,
    311                          'ury' => max($b[1][1], $b[3][1]) / $k,
    312                          );
    313         } elseif (!isset ($page[1][1]['/Parent'])) {
     215        if (!is_null($box) && $box[0] == pdf_parser::TYPE_ARRAY) {
     216            $b = $box[1];
     217            return array(
     218                'x' => $b[0][1] / $k,
     219                'y' => $b[1][1] / $k,
     220                'w' => abs($b[0][1] - $b[2][1]) / $k,
     221                'h' => abs($b[1][1] - $b[3][1]) / $k,
     222                'llx' => min($b[0][1], $b[2][1]) / $k,
     223                'lly' => min($b[1][1], $b[3][1]) / $k,
     224                'urx' => max($b[0][1], $b[2][1]) / $k,
     225                'ury' => max($b[1][1], $b[3][1]) / $k,
     226            );
     227        } else if (!isset($page[1][1]['/Parent'])) {
    314228            return false;
    315229        } else {
    316             return $this->getPageBox($this->pdf_resolve_object($this->c, $page[1][1]['/Parent']), $box_index, $k);
    317         }
    318     }
    319 
    320     /**
    321      * Get all page boxes by page no
     230            return $this->_getPageBox($this->resolveObject($page[1][1]['/Parent']), $boxIndex, $k);
     231        }
     232    }
     233
     234    /**
     235     * Get all page boundary boxes by page number
    322236     *
    323      * @param int The page number
    324      * @param float Scale factor from user space units to points
     237     * @param int $pageNo The page number
     238     * @param float $k Scale factor from user space units to points
    325239     * @return array
    326      */
    327      function getPageBoxes($pageno, $k) {
    328         return $this->_getPageBoxes($this->pages[$pageno - 1], $k);
    329     }
    330    
    331     /**
    332      * Get all boxes from /Page
    333      *
    334      * @param array a /Page
     240     * @throws InvalidArgumentException
     241     */
     242    public function getPageBoxes($pageNo, $k)
     243    {
     244        if (!isset($this->_pages[$pageNo - 1])) {
     245            throw new InvalidArgumentException('Page ' . $pageNo . ' does not exists.');
     246        }
     247
     248        return $this->_getPageBoxes($this->_pages[$pageNo - 1], $k);
     249    }
     250   
     251    /**
     252     * Get all boxes from /Page dictionary
     253     *
     254     * @param array $page A /Page dictionary
     255     * @param float $k Scale factor from user space units to points
    335256     * @return array
    336257     */
    337     function _getPageBoxes($page, $k) {
     258    protected function _getPageBoxes($page, $k)
     259    {
    338260        $boxes = array();
    339261
    340262        foreach($this->availableBoxes AS $box) {
    341             if ($_box = $this->getPageBox($page, $box, $k)) {
     263            if ($_box = $this->_getPageBox($page, $box, $k)) {
    342264                $boxes[$box] = $_box;
    343265            }
     
    348270
    349271    /**
    350      * Get the page rotation by pageno
    351      *
    352      * @param integer $pageno
     272     * Get the page rotation by page number
     273     *
     274     * @param integer $pageNo
     275     * @throws InvalidArgumentException
    353276     * @return array
    354277     */
    355     function getPageRotation($pageno) {
    356         return $this->_getPageRotation($this->pages[$pageno - 1]);
    357     }
    358    
    359     function _getPageRotation($obj) { // $obj = /Page
    360         $obj = $this->pdf_resolve_object($this->c, $obj);
    361         if (isset ($obj[1][1]['/Rotate'])) {
    362                 $res = $this->pdf_resolve_object($this->c, $obj[1][1]['/Rotate']);
    363                 if ($res[0] == PDF_TYPE_OBJECT)
     278    public function getPageRotation($pageNo)
     279    {
     280        if (!isset($this->_pages[$pageNo - 1])) {
     281            throw new InvalidArgumentException('Page ' . $pageNo . ' does not exists.');
     282        }
     283
     284        return $this->_getPageRotation($this->_pages[$pageNo - 1]);
     285    }
     286
     287    /**
     288     * Get the rotation value of a page
     289     *
     290     * @param array $obj A /Page dictionary
     291     * @return array|bool
     292     */
     293    protected function _getPageRotation($obj)
     294    {
     295        $obj = $this->resolveObject($obj);
     296        if (isset($obj[1][1]['/Rotate'])) {
     297            $res = $this->resolveObject($obj[1][1]['/Rotate']);
     298            if ($res[0] == pdf_parser::TYPE_OBJECT)
    364299                return $res[1];
    365300            return $res;
    366         } else {
    367                 if (!isset ($obj[1][1]['/Parent'])) {
    368                         return false;
    369                 } else {
    370                 $res = $this->_getPageRotation($obj[1][1]['/Parent']);
    371                 if ($res[0] == PDF_TYPE_OBJECT)
    372                     return $res[1];
    373                 return $res;
    374                 }
    375         }
    376     }
    377    
    378     /**
    379      * Read all /Page(es)
    380      *
    381      * @param object pdf_context
    382      * @param array /Pages
    383      * @param array the result-array
    384      */
    385     function read_pages(&$c, &$pages, &$result) {
     301        }
     302
     303        if (!isset($obj[1][1]['/Parent'])) {
     304            return false;
     305        }
     306
     307        $res = $this->_getPageRotation($obj[1][1]['/Parent']);
     308        if ($res[0] == pdf_parser::TYPE_OBJECT)
     309            return $res[1];
     310
     311        return $res;
     312    }
     313
     314    /**
     315     * Read all pages
     316     *
     317     * @param array $pages /Pages dictionary
     318     * @param array $result The result array
     319     * @throws Exception
     320     */
     321    protected function _readPages(&$pages, &$result)
     322    {
    386323        // Get the kids dictionary
    387         $_kids = $this->pdf_resolve_object ($c, $pages[1][1]['/Kids']);
    388        
    389         if (!is_array($_kids))
    390             $this->error('Cannot find /Kids in current /Page-Dictionary');
    391            
    392         if ($_kids[1][0] == PDF_TYPE_ARRAY) {
    393             $kids = $_kids[1][1];
    394         } else {
    395             $kids = $_kids[1];
    396         }
    397        
     324        $_kids = $this->resolveObject($pages[1][1]['/Kids']);
     325
     326        if (!is_array($_kids)) {
     327            throw new Exception('Cannot find /Kids in current /Page-Dictionary');
     328        }
     329
     330        if ($_kids[0] === self::TYPE_OBJECT) {
     331            $_kids =  $_kids[1];
     332        }
     333
     334        $kids = $_kids[1];
     335
    398336        foreach ($kids as $v) {
    399                 $pg = $this->pdf_resolve_object ($c, $v);
     337            $pg = $this->resolveObject($v);
    400338            if ($pg[1][1]['/Type'][1] === '/Pages') {
    401339                // If one of the kids is an embedded
    402                         // /Pages array, resolve it as well.
    403                 $this->read_pages($c, $pg, $result);
    404                 } else {
    405                         $result[] = $pg;
    406                 }
    407         }
    408     }
    409 
    410    
    411    
    412     /**
    413      * Get PDF-Version
    414      *
    415      * And reset the PDF Version used in FPDI if needed
    416      */
    417     function getPDFVersion() {
    418         parent::getPDFVersion();
    419         $this->fpdi->setPDFVersion(max($this->fpdi->getPDFVersion(), $this->pdfVersion));
     340                // /Pages array, resolve it as well.
     341                $this->_readPages($pg, $result);
     342            } else {
     343                $result[] = $pg;
     344            }
     345        }
    420346    }
    421347}
  • _plugins_/fpdf/trunk/pdf_context.php

    r99957 r112228  
    11<?php
    2 //
    3 //  FPDI - Version 1.4.4
    4 //
    5 //    Copyright 2004-2013 Setasign - Jan Slabon
    6 //
    7 //  Licensed under the Apache License, Version 2.0 (the "License");
    8 //  you may not use this file except in compliance with the License.
    9 //  You may obtain a copy of the License at
    10 //
    11 //      http://www.apache.org/licenses/LICENSE-2.0
    12 //
    13 //  Unless required by applicable law or agreed to in writing, software
    14 //  distributed under the License is distributed on an "AS IS" BASIS,
    15 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16 //  See the License for the specific language governing permissions and
    17 //  limitations under the License.
    18 //
     2/**
     3 * This file is part of FPDI
     4 *
     5 * @package   FPDI
     6 * @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com)
     7 * @license   http://opensource.org/licenses/mit-license The MIT License
     8 * @version   1.6.1
     9 */
    1910
    20 if (!class_exists('pdf_context', false)) {
    21    
    22     class pdf_context {
    23    
    24         /**
    25          * Modi
    26          *
    27          * @var integer 0 = file | 1 = string
    28          */
    29         var $_mode = 0;
    30        
    31         var $file;
    32         var $buffer;
    33         var $offset;
    34         var $length;
    35    
    36         var $stack;
    37    
    38         // Constructor
    39    
    40         function pdf_context(&$f) {
    41                 $this->file =& $f;
    42                 if (is_string($this->file))
    43                     $this->_mode = 1;
    44                 $this->reset();
    45         }
    46    
    47         // Optionally move the file
    48         // pointer to a new location
    49         // and reset the buffered data
    50    
    51         function reset($pos = null, $l = 100) {
    52             if ($this->_mode == 0) {
    53                 if (!is_null ($pos)) {
    54                                 fseek ($this->file, $pos);
    55                         }
    56        
    57                         $this->buffer = $l > 0 ? fread($this->file, $l) : '';
    58                         $this->length = strlen($this->buffer);
    59                         if ($this->length < $l)
    60                     $this->increase_length($l - $this->length);
    61             } else {
    62                 $this->buffer = $this->file;
    63                 $this->length = strlen($this->buffer);
    64             }
    65                 $this->offset = 0;
    66                 $this->stack = array();
    67         }
    68    
    69         // Make sure that there is at least one
    70         // character beyond the current offset in
    71         // the buffer to prevent the tokenizer
    72         // from attempting to access data that does
    73         // not exist
    74    
    75         function ensure_content() {
    76                 if ($this->offset >= $this->length - 1) {
    77                         return $this->increase_length();
    78                 } else {
    79                         return true;
    80                 }
    81         }
    82    
    83         // Forcefully read more data into the buffer
    84    
    85         function increase_length($l = 100) {
    86                         if ($this->_mode == 0 && feof($this->file)) {
    87                                 return false;
    88                         } elseif ($this->_mode == 0) {
    89                             $totalLength = $this->length + $l;
    90                             do {
    91                                 $toRead = $totalLength - $this->length;
    92                                 if ($toRead < 1)
    93                                         break;
    94                            
    95                                 $this->buffer .= fread($this->file, $toRead);
    96                     } while ((($this->length = strlen($this->buffer)) != $totalLength) && !feof($this->file));
    97                                
    98                                 return true;
    99                         } else {
    100                         return false;
    101                         }
    102                 }
     11/**
     12 * Class pdf_context
     13 */
     14class pdf_context
     15{
     16    /**
     17     * Mode
     18     *
     19     * @var integer 0 = file | 1 = string
     20     */
     21    protected $_mode = 0;
     22
     23    /**
     24     * @var resource|string
     25     */
     26    public $file;
     27
     28    /**
     29     * @var string
     30     */
     31    public $buffer;
     32
     33    /**
     34     * @var integer
     35     */
     36    public $offset;
     37
     38    /**
     39     * @var integer
     40     */
     41    public $length;
     42
     43    /**
     44     * @var array
     45     */
     46    public $stack;
     47
     48    /**
     49     * The constructor
     50     *
     51     * @param resource $f
     52     */
     53    public function __construct(&$f)
     54    {
     55        $this->file =& $f;
     56        if (is_string($this->file))
     57            $this->_mode = 1;
     58
     59        $this->reset();
     60    }
     61
     62    /**
     63     * Get the position in the file stream
     64     *
     65     * @return int
     66     */
     67    public function getPos()
     68    {
     69        if ($this->_mode == 0) {
     70            return ftell($this->file);
     71        } else {
     72            return 0;
     73        }
     74    }
     75
     76    /**
     77     * Reset the position in the file stream.
     78     *
     79     * Optionally move the file pointer to a new location and reset the buffered data.
     80     *
     81     * @param null $pos
     82     * @param int $l
     83     */
     84    public function reset($pos = null, $l = 100)
     85    {
     86        if ($this->_mode == 0) {
     87            if (!is_null($pos)) {
     88                fseek ($this->file, $pos);
     89            }
     90
     91            $this->buffer = $l > 0 ? fread($this->file, $l) : '';
     92            $this->length = strlen($this->buffer);
     93            if ($this->length < $l)
     94                $this->increaseLength($l - $this->length);
     95        } else {
     96            $this->buffer = $this->file;
     97            $this->length = strlen($this->buffer);
     98        }
     99        $this->offset = 0;
     100        $this->stack = array();
     101    }
     102
     103    /**
     104     * Make sure that there is at least one character beyond the current offset in the buffer.
     105     *
     106     * To prevent the tokenizer from attempting to access data that does not exist.
     107     *
     108     * @return bool
     109     */
     110    public function ensureContent()
     111    {
     112        if ($this->offset >= $this->length - 1) {
     113            return $this->increaseLength();
     114        } else {
     115            return true;
     116        }
     117    }
     118
     119    /**
     120     * Forcefully read more data into the buffer
     121     *
     122     * @param int $l
     123     * @return bool
     124     */
     125    public function increaseLength($l = 100)
     126    {
     127        if ($this->_mode == 0 && feof($this->file)) {
     128            return false;
     129        } else if ($this->_mode == 0) {
     130            $totalLength = $this->length + $l;
     131            do {
     132                $toRead = $totalLength - $this->length;
     133                if ($toRead < 1)
     134                    break;
     135
     136                $this->buffer .= fread($this->file, $toRead);
     137            } while ((($this->length = strlen($this->buffer)) != $totalLength) && !feof($this->file));
     138
     139            return true;
     140        } else {
     141            return false;
     142        }
    103143    }
    104144}
  • _plugins_/fpdf/trunk/pdf_parser.php

    r99957 r112228  
    11<?php
    2 //
    3 //  FPDI - Version 1.4.4
    4 //
    5 //    Copyright 2004-2013 Setasign - Jan Slabon
    6 //
    7 //  Licensed under the Apache License, Version 2.0 (the "License");
    8 //  you may not use this file except in compliance with the License.
    9 //  You may obtain a copy of the License at
    10 //
    11 //      http://www.apache.org/licenses/LICENSE-2.0
    12 //
    13 //  Unless required by applicable law or agreed to in writing, software
    14 //  distributed under the License is distributed on an "AS IS" BASIS,
    15 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16 //  See the License for the specific language governing permissions and
    17 //  limitations under the License.
    18 //
    19 
    20 if (!defined ('PDF_TYPE_NULL'))
    21     define ('PDF_TYPE_NULL', 0);
    22 if (!defined ('PDF_TYPE_NUMERIC'))
    23     define ('PDF_TYPE_NUMERIC', 1);
    24 if (!defined ('PDF_TYPE_TOKEN'))
    25     define ('PDF_TYPE_TOKEN', 2);
    26 if (!defined ('PDF_TYPE_HEX'))
    27     define ('PDF_TYPE_HEX', 3);
    28 if (!defined ('PDF_TYPE_STRING'))
    29     define ('PDF_TYPE_STRING', 4);
    30 if (!defined ('PDF_TYPE_DICTIONARY'))
    31     define ('PDF_TYPE_DICTIONARY', 5);
    32 if (!defined ('PDF_TYPE_ARRAY'))
    33     define ('PDF_TYPE_ARRAY', 6);
    34 if (!defined ('PDF_TYPE_OBJDEC'))
    35     define ('PDF_TYPE_OBJDEC', 7);
    36 if (!defined ('PDF_TYPE_OBJREF'))
    37     define ('PDF_TYPE_OBJREF', 8);
    38 if (!defined ('PDF_TYPE_OBJECT'))
    39     define ('PDF_TYPE_OBJECT', 9);
    40 if (!defined ('PDF_TYPE_STREAM'))
    41     define ('PDF_TYPE_STREAM', 10);
    42 if (!defined ('PDF_TYPE_BOOLEAN'))
    43     define ('PDF_TYPE_BOOLEAN', 11);
    44 if (!defined ('PDF_TYPE_REAL'))
    45     define ('PDF_TYPE_REAL', 12);
    46    
    47 require_once('pdf_context.php');
    48 
    49 if (!class_exists('pdf_parser', false)) {
    50    
    51     class pdf_parser {
    52        
    53         /**
    54          * Filename
    55          * @var string
    56          */
    57         var $filename;
    58        
    59         /**
    60          * File resource
    61          * @var resource
    62          */
    63         var $f;
    64        
    65         /**
    66          * PDF Context
    67          * @var object pdf_context-Instance
    68          */
    69         var $c;
    70        
    71         /**
    72          * xref-Data
    73          * @var array
    74          */
    75         var $xref;
    76    
    77         /**
    78          * root-Object
    79          * @var array
    80          */
    81         var $root;
    82        
    83         /**
    84          * PDF version of the loaded document
    85          * @var string
    86          */
    87         var $pdfVersion;
    88        
    89         /**
    90              * For reading encrypted documents and xref/objectstreams are in use
    91              *
    92              * @var boolean
    93              */
    94             var $readPlain = true;
    95            
    96         /**
    97          * Constructor
    98          *
    99          * @param string $filename  Source-Filename
    100          */
    101         function pdf_parser($filename) {
    102             $this->filename = $filename;
    103            
    104             $this->f = @fopen($this->filename, 'rb');
    105    
    106             if (!$this->f)
    107                 $this->error(sprintf('Cannot open %s !', $filename));
    108    
    109             $this->getPDFVersion();
    110    
    111             $this->c = new pdf_context($this->f);
    112            
    113             // Read xref-Data
    114             $this->xref = array();
    115             $this->pdf_read_xref($this->xref, $this->pdf_find_xref());
    116            
    117             // Check for Encryption
    118             $this->getEncryption();
    119    
    120             // Read root
    121             $this->pdf_read_root();
    122         }
    123        
    124         /**
    125          * Close the opened file
    126          */
    127         function closeFile() {
    128                 if (isset($this->f) && is_resource($this->f)) {
    129                     fclose($this->f);   
    130                         unset($this->f);
    131                 }       
    132         }
    133        
    134         /**
    135          * Print Error and die
    136          *
    137          * @param string $msg  Error-Message
    138          */
    139         function error($msg) {
    140                 die('<b>PDF-Parser Error:</b> ' . $msg);       
    141         }
    142        
    143         /**
    144          * Check Trailer for Encryption
    145          */
    146         function getEncryption() {
    147             if (isset($this->xref['trailer'][1]['/Encrypt'])) {
    148                 $this->error('File is encrypted!');
     2/**
     3 * This file is part of FPDI
     4 *
     5 * @package   FPDI
     6 * @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com)
     7 * @license   http://opensource.org/licenses/mit-license The MIT License
     8 * @version   1.6.1
     9 */
     10
     11/**
     12 * Class pdf_parser
     13 */
     14class pdf_parser
     15{
     16    /**
     17     * Type constant
     18     *
     19     * @var integer
     20     */
     21    const TYPE_NULL = 0;
     22
     23    /**
     24     * Type constant
     25     *
     26     * @var integer
     27     */
     28    const TYPE_NUMERIC = 1;
     29
     30    /**
     31     * Type constant
     32     *
     33     * @var integer
     34     */
     35    const TYPE_TOKEN = 2;
     36
     37    /**
     38     * Type constant
     39     *
     40     * @var integer
     41     */
     42    const TYPE_HEX = 3;
     43
     44    /**
     45     * Type constant
     46     *
     47     * @var integer
     48     */
     49    const TYPE_STRING = 4;
     50
     51    /**
     52     * Type constant
     53     *
     54     * @var integer
     55     */
     56    const TYPE_DICTIONARY = 5;
     57
     58    /**
     59     * Type constant
     60     *
     61     * @var integer
     62     */
     63    const TYPE_ARRAY = 6;
     64
     65    /**
     66     * Type constant
     67     *
     68     * @var integer
     69     */
     70    const TYPE_OBJDEC = 7;
     71
     72    /**
     73     * Type constant
     74     *
     75     * @var integer
     76     */
     77    const TYPE_OBJREF = 8;
     78
     79    /**
     80     * Type constant
     81     *
     82     * @var integer
     83     */
     84    const TYPE_OBJECT = 9;
     85
     86    /**
     87     * Type constant
     88     *
     89     * @var integer
     90     */
     91    const TYPE_STREAM = 10;
     92
     93    /**
     94     * Type constant
     95     *
     96     * @var integer
     97     */
     98    const TYPE_BOOLEAN = 11;
     99
     100    /**
     101     * Type constant
     102     *
     103     * @var integer
     104     */
     105    const TYPE_REAL = 12;
     106
     107    /**
     108     * Define the amount of byte in which the initial keyword of a PDF document should be searched.
     109     *
     110     * @var int
     111     */
     112    static public $searchForStartxrefLength = 5500;
     113
     114    /**
     115     * Filename
     116     *
     117     * @var string
     118     */
     119    public $filename;
     120
     121    /**
     122     * File resource
     123     *
     124     * @var resource
     125     */
     126    protected $_f;
     127
     128    /**
     129     * PDF Context
     130     *
     131     * @var pdf_context
     132     */
     133    protected $_c;
     134
     135    /**
     136     * xref-Data
     137     *
     138     * @var array
     139     */
     140    protected $_xref;
     141
     142    /**
     143     * Data of the Root object
     144     *
     145     * @var array
     146     */
     147    protected $_root;
     148
     149    /**
     150     * PDF version of the loaded document
     151     *
     152     * @var string
     153     */
     154    protected $_pdfVersion;
     155
     156    /**
     157     * For reading encrypted documents and xref/object streams are in use
     158     *
     159     * @var boolean
     160     */
     161    protected $_readPlain = true;
     162
     163    /**
     164     * The current read object
     165     *
     166     * @var array
     167     */
     168    protected $_currentObj;
     169
     170    /**
     171     * Constructor
     172     *
     173     * @param string $filename Source filename
     174     * @throws InvalidArgumentException
     175     */
     176    public function __construct($filename)
     177    {
     178        $this->filename = $filename;
     179
     180        $this->_f = @fopen($this->filename, 'rb');
     181
     182        if (!$this->_f) {
     183            throw new InvalidArgumentException(sprintf('Cannot open %s !', $filename));
     184        }
     185
     186        $this->getPdfVersion();
     187
     188        if (!class_exists('pdf_context')) {
     189            require_once('pdf_context.php');
     190        }
     191        $this->_c = new pdf_context($this->_f);
     192
     193        // Read xref-Data
     194        $this->_xref = array();
     195        $this->_readXref($this->_xref, $this->_findXref());
     196
     197        // Check for Encryption
     198        $this->getEncryption();
     199
     200        // Read root
     201        $this->_readRoot();
     202    }
     203
     204    /**
     205     * Destructor
     206     */
     207    public function __destruct()
     208    {
     209        $this->closeFile();
     210    }
     211
     212    /**
     213     * Close the opened file
     214     */
     215    public function closeFile()
     216    {
     217        if (isset($this->_f) && is_resource($this->_f)) {
     218            fclose($this->_f);
     219            unset($this->_f);
     220        }
     221    }
     222
     223    /**
     224     * Check Trailer for Encryption
     225     *
     226     * @throws Exception
     227     */
     228    public function getEncryption()
     229    {
     230        if (isset($this->_xref['trailer'][1]['/Encrypt'])) {
     231            throw new Exception('File is encrypted!');
     232        }
     233    }
     234
     235    /**
     236     * Get PDF-Version
     237     *
     238     * @return string
     239     */
     240    public function getPdfVersion()
     241    {
     242        if ($this->_pdfVersion === null) {
     243            fseek($this->_f, 0);
     244            preg_match('/\d\.\d/', fread($this->_f, 16), $m);
     245            if (isset($m[0]))
     246                $this->_pdfVersion = $m[0];
     247        }
     248
     249        return $this->_pdfVersion;
     250    }
     251
     252    /**
     253     * Read the /Root dictionary
     254     */
     255    protected function _readRoot()
     256    {
     257        if ($this->_xref['trailer'][1]['/Root'][0] != self::TYPE_OBJREF) {
     258            throw new Exception('Wrong Type of Root-Element! Must be an indirect reference');
     259        }
     260
     261        $this->_root = $this->resolveObject($this->_xref['trailer'][1]['/Root']);
     262    }
     263
     264    /**
     265     * Find the xref table
     266     *
     267     * @return integer
     268     * @throws Exception
     269     */
     270    protected function _findXref()
     271    {
     272        $toRead = self::$searchForStartxrefLength;
     273
     274        $stat = fseek($this->_f, -$toRead, SEEK_END);
     275        if ($stat === -1) {
     276            fseek($this->_f, 0);
     277        }
     278
     279        $data = fread($this->_f, $toRead);
     280
     281        $keywordPos = strpos(strrev($data), strrev('startxref'));
     282        if (false === $keywordPos) {
     283            $keywordPos = strpos(strrev($data), strrev('startref'));
     284        }
     285
     286        if (false === $keywordPos) {
     287            throw new Exception('Unable to find "startxref" keyword.');
     288        }
     289
     290        $pos = strlen($data) - $keywordPos;
     291        $data = substr($data, $pos);
     292
     293        if (!preg_match('/\s*(\d+).*$/s', $data, $matches)) {
     294            throw new Exception('Unable to find pointer to xref table.');
     295        }
     296
     297        return (int) $matches[1];
     298    }
     299
     300    /**
     301     * Read the xref table
     302     *
     303     * @param array $result Array of xref table entries
     304     * @param integer $offset of xref table
     305     * @return boolean
     306     * @throws Exception
     307     */
     308    protected function _readXref(&$result, $offset)
     309    {
     310        $tempPos = $offset - min(20, $offset);
     311        fseek($this->_f, $tempPos); // set some bytes backwards to fetch corrupted docs
     312
     313        $data = fread($this->_f, 100);
     314
     315        $xrefPos = strrpos($data, 'xref');
     316
     317        if ($xrefPos === false) {
     318            $this->_c->reset($offset);
     319            $xrefStreamObjDec = $this->_readValue($this->_c);
     320
     321            if (is_array($xrefStreamObjDec) && isset($xrefStreamObjDec[0]) && $xrefStreamObjDec[0] == self::TYPE_OBJDEC) {
     322                throw new Exception(
     323                    sprintf(
     324                        'This document (%s) probably uses a compression technique which is not supported by the ' .
     325                        'free parser shipped with FPDI. (See https://www.setasign.com/fpdi-pdf-parser for more details)',
     326                        $this->filename
     327                    )
     328                );
     329            } else {
     330                throw new Exception('Unable to find xref table.');
    149331            }
    150332        }
    151        
    152         /**
    153          * Find/Return /Root
    154          *
    155          * @return array
    156          */
    157         function pdf_find_root() {
    158             if ($this->xref['trailer'][1]['/Root'][0] != PDF_TYPE_OBJREF) {
    159                 $this->error('Wrong Type of Root-Element! Must be an indirect reference');
     333
     334        if (!isset($result['xrefLocation'])) {
     335            $result['xrefLocation'] = $tempPos + $xrefPos;
     336            $result['maxObject'] = 0;
     337        }
     338
     339        $cycles = -1;
     340        $bytesPerCycle = 100;
     341
     342        fseek($this->_f, $tempPos = $tempPos + $xrefPos + 4); // set the handle directly after the "xref"-keyword
     343        $data = fread($this->_f, $bytesPerCycle);
     344
     345        while (($trailerPos = strpos($data, 'trailer', max($bytesPerCycle * $cycles++, 0))) === false && !feof($this->_f)) {
     346            $data .= fread($this->_f, $bytesPerCycle);
     347        }
     348
     349        if ($trailerPos === false) {
     350            throw new Exception('Trailer keyword not found after xref table');
     351        }
     352
     353        $data = ltrim(substr($data, 0, $trailerPos));
     354
     355        // get Line-Ending
     356        $found = preg_match_all("/(\r\n|\n|\r)/", substr($data, 0, 100), $m); // check the first 100 bytes for line breaks
     357        if ($found === 0) {
     358            throw new Exception('Xref table seems to be corrupted.');
     359        }
     360        $differentLineEndings = count(array_unique($m[0]));
     361        if ($differentLineEndings > 1) {
     362            $lines = preg_split("/(\r\n|\n|\r)/", $data, -1, PREG_SPLIT_NO_EMPTY);
     363        } else {
     364            $lines = explode($m[0][0], $data);
     365        }
     366
     367        $data = $differentLineEndings = $m = null;
     368        unset($data, $differentLineEndings, $m);
     369
     370        $linesCount = count($lines);
     371
     372        $start = 1;
     373
     374        for ($i = 0; $i < $linesCount; $i++) {
     375            $line = trim($lines[$i]);
     376            if ($line) {
     377                $pieces = explode(' ', $line);
     378                $c = count($pieces);
     379                switch($c) {
     380                    case 2:
     381                        $start = (int)$pieces[0];
     382                        $end   = $start + (int)$pieces[1];
     383                        if ($end > $result['maxObject'])
     384                            $result['maxObject'] = $end;
     385                        break;
     386                    case 3:
     387                        if (!isset($result['xref'][$start]))
     388                            $result['xref'][$start] = array();
     389
     390                        if (!array_key_exists($gen = (int) $pieces[1], $result['xref'][$start])) {
     391                            $result['xref'][$start][$gen] = $pieces[2] == 'n' ? (int) $pieces[0] : null;
     392                        }
     393                        $start++;
     394                        break;
     395                    default:
     396                        throw new Exception('Unexpected data in xref table');
     397                }
    160398            }
    161            
    162             return $this->xref['trailer'][1]['/Root'];
    163         }
    164    
    165         /**
    166          * Read the /Root
    167          */
    168         function pdf_read_root() {
    169             // read root
    170             $this->root = $this->pdf_resolve_object($this->c, $this->pdf_find_root());
    171         }
    172        
    173         /**
    174          * Get PDF-Version
    175          *
    176          * And reset the PDF Version used in FPDI if needed
    177          */
    178         function getPDFVersion() {
    179             fseek($this->f, 0);
    180             preg_match('/\d\.\d/',fread($this->f, 16), $m);
    181             if (isset($m[0]))
    182                 $this->pdfVersion = $m[0];
    183             return $this->pdfVersion;
    184         }
    185        
    186         /**
    187          * Find the xref-Table
    188          */
    189         function pdf_find_xref() {
    190                 $toRead = 1500;
    191                    
    192             $stat = fseek ($this->f, -$toRead, SEEK_END);
    193             if ($stat === -1) {
    194                 fseek ($this->f, 0);
    195             }
    196                 $data = fread($this->f, $toRead);
    197            
    198             $pos = strlen($data) - strpos(strrev($data), strrev('startxref'));
    199             $data = substr($data, $pos);
    200            
    201             if (!preg_match('/\s*(\d+).*$/s', $data, $matches)) {
    202                 $this->error('Unable to find pointer to xref table');
    203                 }
    204    
    205                 return (int) $matches[1];
    206         }
    207    
    208         /**
    209          * Read xref-table
    210          *
    211          * @param array $result Array of xref-table
    212          * @param integer $offset of xref-table
    213          */
    214         function pdf_read_xref(&$result, $offset) {
    215             $o_pos = $offset-min(20, $offset);
    216                 fseek($this->f, $o_pos); // set some bytes backwards to fetch errorious docs
    217                
    218             $data = fread($this->f, 100);
    219            
    220             $xrefPos = strrpos($data, 'xref');
    221    
    222             if ($xrefPos === false) {
    223                 fseek($this->f, $offset);
    224                 $c = new pdf_context($this->f);
    225                 $xrefStreamObjDec = $this->pdf_read_value($c);
    226                
    227                 if (is_array($xrefStreamObjDec) && isset($xrefStreamObjDec[0]) && $xrefStreamObjDec[0] == PDF_TYPE_OBJDEC) {
    228                     $this->error(sprintf('This document (%s) probably uses a compression technique which is not supported by the free parser shipped with FPDI.', $this->filename));
    229                 } else {           
    230                         $this->error('Unable to find xref table.');
    231                 }
    232             }
    233            
    234             if (!isset($result['xref_location'])) {
    235                 $result['xref_location'] = $o_pos + $xrefPos;
    236                 $result['max_object'] = 0;
    237                 }
    238    
    239                 $cylces = -1;
    240             $bytesPerCycle = 100;
    241            
    242                 fseek($this->f, $o_pos = $o_pos + $xrefPos + 4); // set the handle directly after the "xref"-keyword
    243             $data = fread($this->f, $bytesPerCycle);
    244            
    245             while (($trailerPos = strpos($data, 'trailer', max($bytesPerCycle * $cylces++, 0))) === false && !feof($this->f)) {
    246                 $data .= fread($this->f, $bytesPerCycle);
    247             }
    248            
    249             if ($trailerPos === false) {
    250                 $this->error('Trailer keyword not found after xref table');
    251             }
    252            
    253             $data = substr($data, 0, $trailerPos);
    254            
    255             // get Line-Ending
    256             preg_match_all("/(\r\n|\n|\r)/", substr($data, 0, 100), $m); // check the first 100 bytes for linebreaks
    257    
    258             $differentLineEndings = count(array_unique($m[0]));
    259             if ($differentLineEndings > 1) {
    260                 $lines = preg_split("/(\r\n|\n|\r)/", $data, -1, PREG_SPLIT_NO_EMPTY);
    261             } else {
    262                 $lines = explode($m[0][1], $data);
    263             }
    264            
    265             $data = $differentLineEndings = $m = null;
    266             unset($data, $differentLineEndings, $m);
    267            
    268             $linesCount = count($lines);
    269            
    270             $start = 1;
    271            
    272             for ($i = 0; $i < $linesCount; $i++) {
    273                 $line = trim($lines[$i]);
    274                 if ($line) {
    275                     $pieces = explode(' ', $line);
    276                     $c = count($pieces);
    277                     switch($c) {
    278                         case 2:
    279                             $start = (int)$pieces[0];
    280                             $end   = $start + (int)$pieces[1];
    281                             if ($end > $result['max_object'])
    282                                 $result['max_object'] = $end;
    283                             break;
    284                         case 3:
    285                             if (!isset($result['xref'][$start]))
    286                                 $result['xref'][$start] = array();
    287                            
    288                             if (!array_key_exists($gen = (int) $pieces[1], $result['xref'][$start])) {
    289                                 $result['xref'][$start][$gen] = $pieces[2] == 'n' ? (int) $pieces[0] : null;
    290                             }
    291                             $start++;
    292                             break;
    293                         default:
    294                             $this->error('Unexpected data in xref table');
    295                     }
    296                 }
    297             }
    298            
    299             $lines = $pieces = $line = $start = $end = $gen = null;
    300             unset($lines, $pieces, $line, $start, $end, $gen);
    301            
    302             fseek($this->f, $o_pos + $trailerPos + 7);
    303            
    304             $c = new pdf_context($this->f);
    305             $trailer = $this->pdf_read_value($c);
    306            
    307             $c = null;
    308             unset($c);
    309            
    310             if (!isset($result['trailer'])) {
    311                 $result['trailer'] = $trailer;         
    312             }
    313            
    314             if (isset($trailer[1]['/Prev'])) {
    315                 $this->pdf_read_xref($result, $trailer[1]['/Prev'][1]);
    316             }
    317            
    318             $trailer = null;
    319             unset($trailer);
    320            
    321             return true;
    322         }
    323        
    324         /**
    325          * Reads an Value
    326          *
    327          * @param object $c pdf_context
    328          * @param string $token a Token
    329          * @return mixed
    330          */
    331         function pdf_read_value(&$c, $token = null) {
    332                 if (is_null($token)) {
    333                     $token = $this->pdf_read_token($c);
    334                 }
    335                
    336             if ($token === false) {
    337                     return false;
    338                 }
    339    
    340                 switch ($token) {
    341                 case    '<':
    342                                 // This is a hex string.
    343                                 // Read the value, then the terminator
    344    
    345                     $pos = $c->offset;
    346    
    347                                 while(1) {
    348    
    349                         $match = strpos ($c->buffer, '>', $pos);
    350                                
    351                                         // If you can't find it, try
    352                                         // reading more data from the stream
    353    
    354                                         if ($match === false) {
    355                                                 if (!$c->increase_length()) {
    356                                                         return false;
    357                                                 } else {
    358                                 continue;
    359                                 }
    360                                         }
    361    
    362                                         $result = substr ($c->buffer, $c->offset, $match - $c->offset);
    363                                         $c->offset = $match + 1;
    364                                        
    365                                         return array (PDF_TYPE_HEX, $result);
    366                     }
    367                    
    368                     break;
    369                         case    '<<':
    370                                 // This is a dictionary.
    371    
    372                                 $result = array();
    373    
    374                                 // Recurse into this function until we reach
    375                                 // the end of the dictionary.
    376                                 while (($key = $this->pdf_read_token($c)) !== '>>') {
    377                                         if ($key === false) {
    378                                                 return false;
    379                                         }
    380                                        
    381                                         if (($value =   $this->pdf_read_value($c)) === false) {
    382                                                 return false;
    383                                         }
    384                                        
    385                                         // Catch missing value
    386                                         if ($value[0] == PDF_TYPE_TOKEN && $value[1] == '>>') {
    387                                             $result[$key] = array(PDF_TYPE_NULL);
    388                                             break;
    389                                         }
    390                                        
    391                                         $result[$key] = $value;
    392                                 }
    393                                
    394                                 return array (PDF_TYPE_DICTIONARY, $result);
    395    
    396                         case    '[':
    397                                 // This is an array.
    398    
    399                                 $result = array();
    400    
    401                                 // Recurse into this function until we reach
    402                                 // the end of the array.
    403                                 while (($token = $this->pdf_read_token($c)) !== ']') {
    404                         if ($token === false) {
    405                                                 return false;
    406                                         }
    407                                        
    408                                         if (($value = $this->pdf_read_value($c, $token)) === false) {
     399        }
     400
     401        $lines = $pieces = $line = $start = $end = $gen = null;
     402        unset($lines, $pieces, $line, $start, $end, $gen);
     403
     404        $this->_c->reset($tempPos + $trailerPos + 7);
     405        $trailer = $this->_readValue($this->_c);
     406
     407        if (!isset($result['trailer'])) {
     408            $result['trailer'] = $trailer;
     409        }
     410
     411        if (isset($trailer[1]['/Prev'])) {
     412            $this->_readXref($result, $trailer[1]['/Prev'][1]);
     413        }
     414
     415        $trailer = null;
     416        unset($trailer);
     417
     418        return true;
     419    }
     420
     421    /**
     422     * Reads a PDF value
     423     *
     424     * @param pdf_context $c
     425     * @param string $token A token
     426     * @return mixed
     427     * @throws Exception
     428     */
     429    protected function _readValue(&$c, $token = null)
     430    {
     431        if (is_null($token)) {
     432            $token = $this->_readToken($c);
     433        }
     434
     435        if ($token === false) {
     436            return false;
     437        }
     438
     439        switch ($token) {
     440            case '<':
     441                // This is a hex string.
     442                // Read the value, then the terminator
     443
     444                $pos = $c->offset;
     445
     446                while(1) {
     447
     448                    $match = strpos($c->buffer, '>', $pos);
     449
     450                    // If you can't find it, try
     451                    // reading more data from the stream
     452
     453                    if ($match === false) {
     454                        if (!$c->increaseLength()) {
    409455                            return false;
    410                                         }
    411                                        
    412                                         $result[] = $value;
    413                                 }
    414                                
    415                     return array (PDF_TYPE_ARRAY, $result);
    416    
    417                         case    '('             :
    418                     // This is a string
    419                     $pos = $c->offset;
    420                    
    421                     $openBrackets = 1;
    422                                 do {
    423                         for (; $openBrackets != 0 && $pos < $c->length; $pos++) {
    424                             switch (ord($c->buffer[$pos])) {
    425                                 case 0x28: // '('
    426                                     $openBrackets++;
    427                                     break;
    428                                 case 0x29: // ')'
    429                                     $openBrackets--;
    430                                     break;
    431                                 case 0x5C: // backslash
    432                                     $pos++;
     456                        } else {
     457                            continue;
     458                        }
     459                    }
     460
     461                    $result = substr($c->buffer, $c->offset, $match - $c->offset);
     462                    $c->offset = $match + 1;
     463
     464                    return array (self::TYPE_HEX, $result);
     465                }
     466                break;
     467
     468            case '<<':
     469                // This is a dictionary.
     470
     471                $result = array();
     472
     473                // Recurse into this function until we reach
     474                // the end of the dictionary.
     475                while (($key = $this->_readToken($c)) !== '>>') {
     476                    if ($key === false) {
     477                        return false;
     478                    }
     479
     480                    if (($value =   $this->_readValue($c)) === false) {
     481                        return false;
     482                    }
     483
     484                    // Catch missing value
     485                    if ($value[0] == self::TYPE_TOKEN && $value[1] == '>>') {
     486                        $result[$key] = array(self::TYPE_NULL);
     487                        break;
     488                    }
     489
     490                    $result[$key] = $value;
     491                }
     492
     493                return array (self::TYPE_DICTIONARY, $result);
     494
     495            case '[':
     496                // This is an array.
     497
     498                $result = array();
     499
     500                // Recurse into this function until we reach
     501                // the end of the array.
     502                while (($token = $this->_readToken($c)) !== ']') {
     503                    if ($token === false) {
     504                        return false;
     505                    }
     506
     507                    if (($value = $this->_readValue($c, $token)) === false) {
     508                        return false;
     509                    }
     510
     511                    $result[] = $value;
     512                }
     513
     514                return array (self::TYPE_ARRAY, $result);
     515
     516            case '(':
     517                // This is a string
     518                $pos = $c->offset;
     519
     520                $openBrackets = 1;
     521                do {
     522                    for (; $openBrackets != 0 && $pos < $c->length; $pos++) {
     523                        switch (ord($c->buffer[$pos])) {
     524                            case 0x28: // '('
     525                                $openBrackets++;
     526                                break;
     527                            case 0x29: // ')'
     528                                $openBrackets--;
     529                                break;
     530                            case 0x5C: // backslash
     531                                $pos++;
     532                        }
     533                    }
     534                } while($openBrackets != 0 && $c->increaseLength());
     535
     536                $result = substr($c->buffer, $c->offset, $pos - $c->offset - 1);
     537                $c->offset = $pos;
     538
     539                return array (self::TYPE_STRING, $result);
     540
     541            case 'stream':
     542                $tempPos = $c->getPos() - strlen($c->buffer);
     543                $tempOffset = $c->offset;
     544
     545                $c->reset($startPos = $tempPos + $tempOffset);
     546
     547                // Find the first "newline"
     548                while ($c->buffer[0] !== chr(10) && $c->buffer[0] !== chr(13)) {
     549                    $c->reset(++$startPos);
     550                    if ($c->ensureContent() === false) {
     551                        throw new Exception(
     552                            'Unable to parse stream data. No newline followed the stream keyword.'
     553                        );
     554                    }
     555                }
     556
     557                $e = 0; // ensure line breaks in front of the stream
     558                if ($c->buffer[0] == chr(10) || $c->buffer[0] == chr(13))
     559                    $e++;
     560                if ($c->buffer[1] == chr(10) && $c->buffer[0] != chr(10))
     561                    $e++;
     562
     563                if ($this->_currentObj[1][1]['/Length'][0] == self::TYPE_OBJREF) {
     564                    $tmpLength = $this->resolveObject($this->_currentObj[1][1]['/Length']);
     565                    $length = $tmpLength[1][1];
     566                } else {
     567                    $length = $this->_currentObj[1][1]['/Length'][1];
     568                }
     569
     570                if ($length > 0) {
     571                    $c->reset($startPos + $e, $length);
     572                    $v = $c->buffer;
     573                } else {
     574                    $v = '';
     575                }
     576
     577                $c->reset($startPos + $e + $length);
     578                $endstream = $this->_readToken($c);
     579
     580                if ($endstream != 'endstream') {
     581                    $c->reset($startPos + $e + $length + 9); // 9 = strlen("endstream")
     582                    // We don't throw an error here because the next
     583                    // round trip will start at a new offset
     584                }
     585
     586                return array(self::TYPE_STREAM, $v);
     587
     588            default:
     589                if (is_numeric($token)) {
     590                    // A numeric token. Make sure that
     591                    // it is not part of something else.
     592                    if (($tok2 = $this->_readToken($c)) !== false) {
     593                        if (is_numeric($tok2)) {
     594
     595                            // Two numeric tokens in a row.
     596                            // In this case, we're probably in
     597                            // front of either an object reference
     598                            // or an object specification.
     599                            // Determine the case and return the data
     600                            if (($tok3 = $this->_readToken($c)) !== false) {
     601                                switch ($tok3) {
     602                                    case 'obj':
     603                                        return array(self::TYPE_OBJDEC, (int)$token, (int)$tok2);
     604                                    case 'R':
     605                                        return array(self::TYPE_OBJREF, (int)$token, (int)$tok2);
     606                                }
     607                                // If we get to this point, that numeric value up
     608                                // there was just a numeric value. Push the extra
     609                                // tokens back into the stack and return the value.
     610                                array_push($c->stack, $tok3);
    433611                            }
    434612                        }
    435                                 } while($openBrackets != 0 && $c->increase_length());
    436                                
    437                                 $result = substr($c->buffer, $c->offset, $pos - $c->offset - 1);
    438                                 $c->offset = $pos;
    439                                
    440                                 return array (PDF_TYPE_STRING, $result);
    441    
    442                 case 'stream':
    443                         $o_pos = ftell($c->file)-strlen($c->buffer);
    444                         $o_offset = $c->offset;
    445                        
    446                         $c->reset($startpos = $o_pos + $o_offset);
    447                        
    448                         $e = 0; // ensure line breaks in front of the stream
    449                         if ($c->buffer[0] == chr(10) || $c->buffer[0] == chr(13))
    450                                 $e++;
    451                         if ($c->buffer[1] == chr(10) && $c->buffer[0] != chr(10))
    452                                 $e++;
    453                        
    454                         if ($this->actual_obj[1][1]['/Length'][0] == PDF_TYPE_OBJREF) {
    455                                 $tmp_c = new pdf_context($this->f);
    456                                 $tmp_length = $this->pdf_resolve_object($tmp_c, $this->actual_obj[1][1]['/Length']);
    457                                 $length = $tmp_length[1][1];
    458                         } else {
    459                                 $length = $this->actual_obj[1][1]['/Length'][1];       
    460                         }
    461                                
    462                         if ($length > 0) {
    463                                 $c->reset($startpos + $e,$length);
    464                                 $v = $c->buffer;
    465                         } else {
    466                             $v = '';   
    467                         }
    468                         $c->reset($startpos + $e + $length + 9); // 9 = strlen("endstream")
    469                        
    470                         return array(PDF_TYPE_STREAM, $v);
    471                        
    472                 default :
    473                         if (is_numeric ($token)) {
    474                         // A numeric token. Make sure that
    475                                         // it is not part of something else.
    476                                         if (($tok2 = $this->pdf_read_token ($c)) !== false) {
    477                             if (is_numeric ($tok2)) {
    478    
    479                                                         // Two numeric tokens in a row.
    480                                                         // In this case, we're probably in
    481                                                         // front of either an object reference
    482                                                         // or an object specification.
    483                                                         // Determine the case and return the data
    484                                                         if (($tok3 = $this->pdf_read_token ($c)) !== false) {
    485                                     switch ($tok3) {
    486                                                                         case 'obj':
    487                                             return array (PDF_TYPE_OBJDEC, (int) $token, (int) $tok2);
    488                                                                         case 'R':
    489                                                                                 return array (PDF_TYPE_OBJREF, (int) $token, (int) $tok2);
    490                                                                 }
    491                                                                 // If we get to this point, that numeric value up
    492                                                                 // there was just a numeric value. Push the extra
    493                                                                 // tokens back into the stack and return the value.
    494                                                                 array_push ($c->stack, $tok3);
    495                                                         }
    496                                                 }
    497    
    498                                                 array_push ($c->stack, $tok2);
    499                                         }
    500    
    501                                         if ($token === (string)((int)$token))
    502                                         return array (PDF_TYPE_NUMERIC, (int)$token);
    503                                         else
    504                                                 return array (PDF_TYPE_REAL, (float)$token);
    505                                 } elseif ($token == 'true' || $token == 'false') {
    506                         return array (PDF_TYPE_BOOLEAN, $token == 'true');
    507                                 } elseif ($token == 'null') {
    508                                    return array (PDF_TYPE_NULL);
    509                                 } else {
    510                         // Just a token. Return it.
    511                                         return array (PDF_TYPE_TOKEN, $token);
    512                                 }
    513              }
    514         }
    515        
    516         /**
    517          * Resolve an object
    518          *
    519          * @param object $c pdf_context
    520          * @param array $obj_spec The object-data
    521          * @param boolean $encapsulate Must set to true, cause the parsing and fpdi use this method only without this para
    522          */
    523         function pdf_resolve_object(&$c, $obj_spec, $encapsulate = true) {
    524             // Exit if we get invalid data
    525                 if (!is_array($obj_spec)) {
    526                 $ret = false;
    527                     return $ret;
    528                 }
    529    
    530                 if ($obj_spec[0] == PDF_TYPE_OBJREF) {
    531    
    532                         // This is a reference, resolve it
    533                         if (isset($this->xref['xref'][$obj_spec[1]][$obj_spec[2]])) {
    534    
    535                                 // Save current file position
    536                                 // This is needed if you want to resolve
    537                                 // references while you're reading another object
    538                                 // (e.g.: if you need to determine the length
    539                                 // of a stream)
    540    
    541                                 $old_pos = ftell($c->file);
    542    
    543                                 // Reposition the file pointer and
    544                                 // load the object header.
    545                                
    546                                 $c->reset($this->xref['xref'][$obj_spec[1]][$obj_spec[2]]);
    547    
    548                                 $header = $this->pdf_read_value($c);
    549    
    550                                 if ($header[0] != PDF_TYPE_OBJDEC || $header[1] != $obj_spec[1] || $header[2] != $obj_spec[2]) {
    551                                         $toSearchFor = $obj_spec[1] . ' ' . $obj_spec[2] . ' obj';
    552                                         if (preg_match('/' . $toSearchFor . '/', $c->buffer)) {
    553                                                 $c->offset = strpos($c->buffer, $toSearchFor) + strlen($toSearchFor);
    554                                                 // reset stack
    555                                                 $c->stack = array();
    556                                         } else {
    557                                                 $this->error("Unable to find object ({$obj_spec[1]}, {$obj_spec[2]}) at expected location");
    558                                         }
    559                                 }
    560    
    561                                 // If we're being asked to store all the information
    562                                 // about the object, we add the object ID and generation
    563                                 // number for later use
    564                                 $result = array();
    565                                 $this->actual_obj =& $result;
    566                                 if ($encapsulate) {
    567                                         $result = array (
    568                                                 PDF_TYPE_OBJECT,
    569                                                 'obj' => $obj_spec[1],
    570                                                 'gen' => $obj_spec[2]
    571                                         );
    572                                 }
    573    
    574                                 // Now simply read the object data until
    575                                 // we encounter an end-of-object marker
    576                                 while(1) {
    577                         $value = $this->pdf_read_value($c);
    578                                         if ($value === false || count($result) > 4) {
    579                                                 // in this case the parser coudn't find an endobj so we break here
    580                                                 break;
    581                                         }
    582    
    583                                         if ($value[0] == PDF_TYPE_TOKEN && $value[1] === 'endobj') {
    584                                                 break;
    585                                         }
    586    
    587                         $result[] = $value;
    588                                 }
    589    
    590                                 $c->reset($old_pos);
    591    
    592                     if (isset($result[2][0]) && $result[2][0] == PDF_TYPE_STREAM) {
    593                         $result[0] = PDF_TYPE_STREAM;
    594                     }
    595    
    596                                 return $result;
    597                         }
    598                 } else {
    599                         return $obj_spec;
    600                 }
    601         }
    602    
    603        
    604        
    605         /**
    606          * Reads a token from the file
    607          *
    608          * @param object $c pdf_context
    609          * @return mixed
    610          */
    611         function pdf_read_token(&$c)
    612         {
    613                 // If there is a token available
    614                 // on the stack, pop it out and
    615                 // return it.
    616    
    617                 if (count($c->stack)) {
    618                         return array_pop($c->stack);
    619                 }
    620    
    621                 // Strip away any whitespace
    622    
    623                 do {
    624                         if (!$c->ensure_content()) {
    625                                 return false;
    626                         }
    627                         $c->offset += strspn($c->buffer, "\x20\x0A\x0C\x0D\x09\x00", $c->offset);
    628                 } while ($c->offset >= $c->length - 1);
    629    
    630                 // Get the first character in the stream
    631    
    632                 $char = $c->buffer[$c->offset++];
    633    
    634                 switch ($char) {
    635    
    636                         case '[':
    637                         case ']':
    638                         case '(':
    639                         case ')':
    640                        
    641                                 // This is either an array or literal string
    642                                 // delimiter, Return it
    643    
    644                                 return $char;
    645    
    646                         case '<':
    647                         case '>':
    648    
    649                                 // This could either be a hex string or
    650                                 // dictionary delimiter. Determine the
    651                                 // appropriate case and return the token
    652    
    653                                 if ($c->buffer[$c->offset] == $char) {
    654                                         if (!$c->ensure_content()) {
    655                                             return false;
    656                                         }
    657                                         $c->offset++;
    658                                         return $char . $char;
    659                                 } else {
    660                                         return $char;
    661                                 }
    662    
    663                         case '%':
    664                            
    665                             // This is a comment - jump over it!
    666                            
    667                     $pos = $c->offset;
    668                                 while(1) {
    669                                     $match = preg_match("/(\r\n|\r|\n)/", $c->buffer, $m, PREG_OFFSET_CAPTURE, $pos);
    670                         if ($match === 0) {
    671                                                 if (!$c->increase_length()) {
    672                                                         return false;
    673                                                 } else {
    674                                 continue;
    675                                 }
    676                                         }
    677    
    678                                         $c->offset = $m[0][1]+strlen($m[0][0]);
    679                                        
    680                                         return $this->pdf_read_token($c);
    681                     }
    682                    
    683                         default:
    684    
    685                                 // This is "another" type of token (probably
    686                                 // a dictionary entry or a numeric value)
    687                                 // Find the end and return it.
    688    
    689                                 if (!$c->ensure_content()) {
    690                                         return false;
    691                                 }
    692    
    693                                 while(1) {
    694    
    695                                         // Determine the length of the token
    696    
    697                                         $pos = strcspn($c->buffer, "\x20%[]<>()/\x0A\x0C\x0D\x09\x00", $c->offset);
    698                                        
    699                                         if ($c->offset + $pos <= $c->length - 1) {
    700                                                 break;
    701                                         } else {
    702                                                 // If the script reaches this point,
    703                                                 // the token may span beyond the end
    704                                                 // of the current buffer. Therefore,
    705                                                 // we increase the size of the buffer
    706                                                 // and try again--just to be safe.
    707    
    708                                                 $c->increase_length();
    709                                         }
    710                                 }
    711    
    712                                 $result = substr($c->buffer, $c->offset - 1, $pos + 1);
    713    
    714                                 $c->offset += $pos;
    715                                 return $result;
    716                 }
    717         }
     613
     614                        array_push($c->stack, $tok2);
     615                    }
     616
     617                    if ($token === (string)((int)$token))
     618                        return array(self::TYPE_NUMERIC, (int)$token);
     619                    else
     620                        return array(self::TYPE_REAL, (float)$token);
     621                } else if ($token == 'true' || $token == 'false') {
     622                    return array(self::TYPE_BOOLEAN, $token == 'true');
     623                } else if ($token == 'null') {
     624                   return array(self::TYPE_NULL);
     625                } else {
     626                    // Just a token. Return it.
     627                    return array(self::TYPE_TOKEN, $token);
     628                }
     629         }
     630    }
     631
     632    /**
     633     * Resolve an object
     634     *
     635     * @param array $objSpec The object-data
     636     * @return array|boolean
     637     * @throws Exception
     638     */
     639    public function resolveObject($objSpec)
     640    {
     641        $c = $this->_c;
     642
     643        // Exit if we get invalid data
     644        if (!is_array($objSpec)) {
     645            return false;
     646        }
     647
     648        if ($objSpec[0] == self::TYPE_OBJREF) {
     649
     650            // This is a reference, resolve it
     651            if (isset($this->_xref['xref'][$objSpec[1]][$objSpec[2]])) {
     652
     653                // Save current file position
     654                // This is needed if you want to resolve
     655                // references while you're reading another object
     656                // (e.g.: if you need to determine the length
     657                // of a stream)
     658
     659                $oldPos = $c->getPos();
     660
     661                // Reposition the file pointer and
     662                // load the object header.
     663
     664                $c->reset($this->_xref['xref'][$objSpec[1]][$objSpec[2]]);
     665
     666                $header = $this->_readValue($c);
     667
     668                if ($header[0] != self::TYPE_OBJDEC || $header[1] != $objSpec[1] || $header[2] != $objSpec[2]) {
     669                    $toSearchFor = $objSpec[1] . ' ' . $objSpec[2] . ' obj';
     670                    if (preg_match('/' . $toSearchFor . '/', $c->buffer)) {
     671                        $c->offset = strpos($c->buffer, $toSearchFor) + strlen($toSearchFor);
     672                        // reset stack
     673                        $c->stack = array();
     674                    } else {
     675                        throw new Exception(
     676                            sprintf("Unable to find object (%s, %s) at expected location.", $objSpec[1], $objSpec[2])
     677                        );
     678                    }
     679                }
     680
     681                // If we're being asked to store all the information
     682                // about the object, we add the object ID and generation
     683                // number for later use
     684                $result = array (
     685                    self::TYPE_OBJECT,
     686                    'obj' => $objSpec[1],
     687                    'gen' => $objSpec[2]
     688                );
     689
     690                $this->_currentObj =& $result;
     691
     692                // Now simply read the object data until
     693                // we encounter an end-of-object marker
     694                while (true) {
     695                    $value = $this->_readValue($c);
     696                    if ($value === false || count($result) > 4) {
     697                        // in this case the parser couldn't find an "endobj" so we break here
     698                        break;
     699                    }
     700
     701                    if ($value[0] == self::TYPE_TOKEN && $value[1] === 'endobj') {
     702                        break;
     703                    }
     704
     705                    $result[] = $value;
     706                }
     707
     708                $c->reset($oldPos);
     709
     710                if (isset($result[2][0]) && $result[2][0] == self::TYPE_STREAM) {
     711                    $result[0] = self::TYPE_STREAM;
     712                }
     713
     714            } else {
     715                throw new Exception(
     716                    sprintf("Unable to find object (%s, %s) at expected location.", $objSpec[1], $objSpec[2])
     717                );
     718            }
     719
     720            return $result;
     721        } else {
     722            return $objSpec;
     723        }
     724    }
     725
     726    /**
     727     * Reads a token from the context
     728     *
     729     * @param pdf_context $c
     730     * @return mixed
     731     */
     732    protected function _readToken($c)
     733    {
     734        // If there is a token available
     735        // on the stack, pop it out and
     736        // return it.
     737
     738        if (count($c->stack)) {
     739            return array_pop($c->stack);
     740        }
     741
     742        // Strip away any whitespace
     743
     744        do {
     745            if (!$c->ensureContent()) {
     746                return false;
     747            }
     748            $c->offset += strspn($c->buffer, "\x20\x0A\x0C\x0D\x09\x00", $c->offset);
     749        } while ($c->offset >= $c->length - 1);
     750
     751        // Get the first character in the stream
     752
     753        $char = $c->buffer[$c->offset++];
     754
     755        switch ($char) {
     756
     757            case '[':
     758            case ']':
     759            case '(':
     760            case ')':
     761
     762                // This is either an array or literal string
     763                // delimiter, Return it
     764
     765                return $char;
     766
     767            case '<':
     768            case '>':
     769
     770                // This could either be a hex string or
     771                // dictionary delimiter. Determine the
     772                // appropriate case and return the token
     773
     774                if ($c->buffer[$c->offset] == $char) {
     775                    if (!$c->ensureContent()) {
     776                        return false;
     777                    }
     778                    $c->offset++;
     779                    return $char . $char;
     780                } else {
     781                    return $char;
     782                }
     783
     784            case '%':
     785
     786                // This is a comment - jump over it!
     787
     788                $pos = $c->offset;
     789                while(1) {
     790                    $match = preg_match("/(\r\n|\r|\n)/", $c->buffer, $m, PREG_OFFSET_CAPTURE, $pos);
     791                    if ($match === 0) {
     792                        if (!$c->increaseLength()) {
     793                            return false;
     794                        } else {
     795                            continue;
     796                        }
     797                    }
     798
     799                    $c->offset = $m[0][1] + strlen($m[0][0]);
     800
     801                    return $this->_readToken($c);
     802                }
     803
     804            default:
     805
     806                // This is "another" type of token (probably
     807                // a dictionary entry or a numeric value)
     808                // Find the end and return it.
     809
     810                if (!$c->ensureContent()) {
     811                    return false;
     812                }
     813
     814                while(1) {
     815
     816                    // Determine the length of the token
     817
     818                    $pos = strcspn($c->buffer, "\x20%[]<>()/\x0A\x0C\x0D\x09\x00", $c->offset);
     819
     820                    if ($c->offset + $pos <= $c->length - 1) {
     821                        break;
     822                    } else {
     823                        // If the script reaches this point,
     824                        // the token may span beyond the end
     825                        // of the current buffer. Therefore,
     826                        // we increase the size of the buffer
     827                        // and try again--just to be safe.
     828
     829                        $c->increaseLength();
     830                    }
     831                }
     832
     833                $result = substr($c->buffer, $c->offset - 1, $pos + 1);
     834
     835                $c->offset += $pos;
     836
     837                return $result;
     838        }
     839    }
     840
     841    /**
     842     * Un-filter a stream object
     843     *
     844     * @param array $obj
     845     * @return string
     846     * @throws Exception
     847     */
     848    protected function _unFilterStream($obj)
     849    {
     850        $filters = array();
     851
     852        if (isset($obj[1][1]['/Filter'])) {
     853            $filter = $obj[1][1]['/Filter'];
     854
     855            if ($filter[0] == pdf_parser::TYPE_OBJREF) {
     856                $tmpFilter = $this->resolveObject($filter);
     857                $filter = $tmpFilter[1];
     858            }
     859
     860            if ($filter[0] == pdf_parser::TYPE_TOKEN) {
     861                $filters[] = $filter;
     862            } else if ($filter[0] == pdf_parser::TYPE_ARRAY) {
     863                $filters = $filter[1];
     864            }
     865        }
     866
     867        $stream = $obj[2][1];
     868
     869        foreach ($filters AS $filter) {
     870            switch ($filter[1]) {
     871                case '/FlateDecode':
     872                case '/Fl':
     873                    if (function_exists('gzuncompress')) {
     874                        $oStream = $stream;
     875                        $stream = (strlen($stream) > 0) ? @gzuncompress($stream) : '';
     876                    } else {
     877                        throw new Exception(
     878                            sprintf('To handle %s filter, please compile php with zlib support.', $filter[1])
     879                        );
     880                    }
     881
     882                    if ($stream === false) {
     883                        $tries = 0;
     884                        while ($tries < 8 && ($stream === false || strlen($stream) < strlen($oStream))) {
     885                            $oStream = substr($oStream, 1);
     886                            $stream = @gzinflate($oStream);
     887                            $tries++;
     888                        }
     889
     890                        if ($stream === false) {
     891                            throw new Exception('Error while decompressing stream.');
     892                        }
     893                    }
     894                    break;
     895                case '/LZWDecode':
     896                    if (!class_exists('FilterLZW')) {
     897                        require_once('filters/FilterLZW.php');
     898                    }
     899                    $decoder = new FilterLZW();
     900                    $stream = $decoder->decode($stream);
     901                    break;
     902                case '/ASCII85Decode':
     903                    if (!class_exists('FilterASCII85')) {
     904                        require_once('filters/FilterASCII85.php');
     905                    }
     906                    $decoder = new FilterASCII85();
     907                    $stream = $decoder->decode($stream);
     908                    break;
     909                case '/ASCIIHexDecode':
     910                    if (!class_exists('FilterASCIIHexDecode')) {
     911                        require_once('filters/FilterASCIIHexDecode.php');
     912                    }
     913                    $decoder = new FilterASCIIHexDecode();
     914                    $stream = $decoder->decode($stream);
     915                    break;
     916                case null:
     917                    break;
     918                default:
     919                    throw new Exception(sprintf('Unsupported Filter: %s', $filter[1]));
     920            }
     921        }
     922
     923        return $stream;
    718924    }
    719925}
Note: See TracChangeset for help on using the changeset viewer.