first commit

This commit is contained in:
Dave Umrysh 2022-12-30 13:48:08 -07:00
commit 0a47a984e0
12 changed files with 3341 additions and 0 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 Setasign - Jan Slabon, https://www.setasign.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

87
README.md Normal file
View File

@ -0,0 +1,87 @@
FPDI - Free PDF Document Importer
=================================
[![Latest Stable Version](https://poser.pugx.org/setasign/fpdi/v/stable.svg)](https://packagist.org/packages/setasign/fpdi) [![Total Downloads](https://poser.pugx.org/setasign/fpdi/downloads.svg)](https://packagist.org/packages/setasign/fpdi) [![Latest Unstable Version](https://poser.pugx.org/setasign/fpdi/v/unstable.svg)](https://packagist.org/packages/setasign/fpdi) [![License](https://poser.pugx.org/setasign/fpdi/license.svg)](https://packagist.org/packages/setasign/fpdi)
A clone of [FPDI](https://www.setasign.com/fpdi) for GitHub/[Composer](https://packagist.org/packages/setasign/fpdi).
FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF, which was developed by Olivier Plathey. Apart from a copy of FPDF, FPDI does not require any special PHP extensions.
## Installation with [Composer](https://packagist.org/packages/setasign/fpdi)
FPDI is an add-on for [FPDF](http://fpdf.org/). Additionally FPDI can be used with [TCPDF](http://www.tcpdf.org/).
For completion we added a [FPDF repository](https://github.com/Setasign/FPDF) which simply clones the offical releases.
This package comes without any dependency configuration in the composer.json file. It's up to you to load the desired package as described below.
A basic installation via Composer could be done this way:
```bash
$ composer require setasign/fpdi:1.6.2
```
or you can include the following in your composer.json file:
```json
{
"require": {
"setasign/fpdi": "1.6.2"
}
}
```
### Evaluate Dependencies Automatically
To load dependencies automatically we prepared kind of metadata packages. To use FPDI with FPDF use [this](https://github.com/Setasign/FPDI-FPDF) package:
```json
{
"require": {
"setasign/fpdi-fpdf": "1.6.2"
}
}
```
For TCPDF use [this](https://github.com/Setasign/FPDI-TCPDF):
```json
{
"require": {
"setasign/fpdi-tcpdf": "1.6.2"
}
}
```
### Manual Dependencies
To support both FPDF and TCPDF its up to you to load the preferred package before the classes of FPDI are loaded. By default FPDI will extend FPDF. If the TCPDF class exists, a new FPDF class will be created which will extend TCPDF while FPDI will extend this.
To use FPDI with FPDF include following in your composer.json file:
```json
{
"require": {
"setasign/fpdf": "1.8",
"setasign/fpdi": "1.6.2"
}
}
```
If you are using TCPDF, your have to update your composer.json respectively to:
```json
{
"require": {
"tecnickcom/tcpdf": "6.2.13",
"setasign/fpdi": "1.6.2"
}
}
```
Additionally you have to trigger composers autoloader for the TCPDF class before you are initiating FPDI:
```php
class_exists('TCPDF', true); // trigger Composers autoloader to load the TCPDF class
$pdf = new FPDI();
```

30
composer.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "setasign/fpdi",
"version": "1.6.2",
"homepage": "https://www.setasign.com/fpdi",
"description": "FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF. Because it is also possible to use FPDI with TCPDF, there are no fixed dependencies defined. Please see suggestions for packages which evaluates the dependencies automatically.",
"type": "library",
"keywords": ["pdf", "fpdi", "fpdf"],
"license": "MIT",
"authors": [
{
"name": "Jan Slabon",
"email": "jan.slabon@setasign.com",
"homepage": "https://www.setasign.com"
}
],
"autoload": {
"classmap": [
"filters/",
"fpdi.php",
"fpdf_tpl.php",
"fpdi_pdf_parser.php",
"pdf_context.php"
]
},
"suggest": {
"setasign/fpdf": "FPDI will extend this class but as it is also possible to use \"tecnickcom/tcpdf\" as an alternative there's no fixed dependency configured.",
"setasign/fpdi-fpdf": "Use this package to automatically evaluate dependencies to FPDF.",
"setasign/fpdi-tcpdf": "Use this package to automatically evaluate dependencies to TCPDF."
}
}

106
filters/FilterASCII85.php Normal file
View File

@ -0,0 +1,106 @@
<?php
/**
* This file is part of FPDI
*
* @package FPDI
* @copyright Copyright (c) 2017 Setasign - Jan Slabon (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
* @version 1.6.2
*/
/**
* Class FilterASCII85
*/
class FilterASCII85
{
/**
* Decode ASCII85 encoded string
*
* @param string $in
* @return string
* @throws Exception
*/
public function decode($in)
{
$ord = array(
'~' => ord('~'),
'z' => ord('z'),
'u' => ord('u'),
'!' => ord('!')
);
$out = '';
$state = 0;
$chn = null;
$l = strlen($in);
for ($k = 0; $k < $l; ++$k) {
$ch = ord($in[$k]) & 0xff;
if ($ch == $ord['~']) {
break;
}
if (preg_match('/^\s$/',chr($ch))) {
continue;
}
if ($ch == $ord['z'] && $state == 0) {
$out .= chr(0) . chr(0) . chr(0) . chr(0);
continue;
}
if ($ch < $ord['!'] || $ch > $ord['u']) {
throw new Exception('Illegal character in ASCII85Decode.');
}
$chn[$state++] = $ch - $ord['!'];
if ($state == 5) {
$state = 0;
$r = 0;
for ($j = 0; $j < 5; ++$j) {
$r = (int)($r * 85 + $chn[$j]);
}
$out .= chr($r >> 24);
$out .= chr($r >> 16);
$out .= chr($r >> 8);
$out .= chr($r);
}
}
$r = 0;
if ($state == 1) {
throw new Exception('Illegal length in ASCII85Decode.');
}
if ($state == 2) {
$r = $chn[0] * 85 * 85 * 85 * 85 + ($chn[1]+1) * 85 * 85 * 85;
$out .= chr($r >> 24);
} else if ($state == 3) {
$r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85 + ($chn[2]+1) * 85 * 85;
$out .= chr($r >> 24);
$out .= chr($r >> 16);
} else if ($state == 4) {
$r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85 + $chn[2] * 85 * 85 + ($chn[3]+1) * 85 ;
$out .= chr($r >> 24);
$out .= chr($r >> 16);
$out .= chr($r >> 8);
}
return $out;
}
/**
* NOT IMPLEMENTED
*
* @param string $in
* @return string
* @throws LogicException
*/
public function encode($in)
{
throw new LogicException("ASCII85 encoding not implemented.");
}
}

View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of FPDI
*
* @package FPDI
* @copyright Copyright (c) 2017 Setasign - Jan Slabon (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
* @version 1.6.2
*/
/**
* Class FilterASCIIHexDecode
*/
class FilterASCIIHexDecode
{
/**
* Converts an ASCII hexadecimal encoded string into it's binary representation.
*
* @param string $data The input string
* @return string
*/
public function decode($data)
{
$data = preg_replace('/[^0-9A-Fa-f]/', '', rtrim($data, '>'));
if ((strlen($data) % 2) == 1) {
$data .= '0';
}
return pack('H*', $data);
}
/**
* Converts a string into ASCII hexadecimal representation.
*
* @param string $data The input string
* @param boolean $leaveEOD
* @return string
*/
public function encode($data, $leaveEOD = false)
{
return current(unpack('H*', $data)) . ($leaveEOD ? '' : '>');
}
}

164
filters/FilterLZW.php Normal file
View File

@ -0,0 +1,164 @@
<?php
/**
* This file is part of FPDI
*
* @package FPDI
* @copyright Copyright (c) 2017 Setasign - Jan Slabon (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
* @version 1.6.2
*/
/**
* Class FilterLZW
*/
class FilterLZW
{
protected $_sTable = array();
protected $_data = null;
protected $_dataLength = 0;
protected $_tIdx;
protected $_bitsToGet = 9;
protected $_bytePointer;
protected $_bitPointer;
protected $_nextData = 0;
protected $_nextBits = 0;
protected $_andTable = array(511, 1023, 2047, 4095);
/**
* Decodes LZW compressed data.
*
* @param string $data The compressed data.
* @throws Exception
* @return string
*/
public function decode($data)
{
if ($data[0] == 0x00 && $data[1] == 0x01) {
throw new Exception('LZW flavour not supported.');
}
$this->_initsTable();
$this->_data = $data;
$this->_dataLength = strlen($data);
// Initialize pointers
$this->_bytePointer = 0;
$this->_bitPointer = 0;
$this->_nextData = 0;
$this->_nextBits = 0;
$oldCode = 0;
$unCompData = '';
while (($code = $this->_getNextCode()) != 257) {
if ($code == 256) {
$this->_initsTable();
$code = $this->_getNextCode();
if ($code == 257) {
break;
}
if (!isset($this->_sTable[$code])) {
throw new Exception('Error while decompression LZW compressed data.');
}
$unCompData .= $this->_sTable[$code];
$oldCode = $code;
} else {
if ($code < $this->_tIdx) {
$string = $this->_sTable[$code];
$unCompData .= $string;
$this->_addStringToTable($this->_sTable[$oldCode], $string[0]);
$oldCode = $code;
} else {
$string = $this->_sTable[$oldCode];
$string = $string . $string[0];
$unCompData .= $string;
$this->_addStringToTable($string);
$oldCode = $code;
}
}
}
return $unCompData;
}
/**
* Initialize the string table.
*/
protected function _initsTable()
{
$this->_sTable = array();
for ($i = 0; $i < 256; $i++)
$this->_sTable[$i] = chr($i);
$this->_tIdx = 258;
$this->_bitsToGet = 9;
}
/**
* Add a new string to the string table.
*/
protected function _addStringToTable($oldString, $newString = '')
{
$string = $oldString . $newString;
// Add this new String to the table
$this->_sTable[$this->_tIdx++] = $string;
if ($this->_tIdx == 511) {
$this->_bitsToGet = 10;
} else if ($this->_tIdx == 1023) {
$this->_bitsToGet = 11;
} else if ($this->_tIdx == 2047) {
$this->_bitsToGet = 12;
}
}
/**
* Returns the next 9, 10, 11 or 12 bits
*
* @return int
*/
protected function _getNextCode()
{
if ($this->_bytePointer == $this->_dataLength) {
return 257;
}
$this->_nextData = ($this->_nextData << 8) | (ord($this->_data[$this->_bytePointer++]) & 0xff);
$this->_nextBits += 8;
if ($this->_nextBits < $this->_bitsToGet) {
$this->_nextData = ($this->_nextData << 8) | (ord($this->_data[$this->_bytePointer++]) & 0xff);
$this->_nextBits += 8;
}
$code = ($this->_nextData >> ($this->_nextBits - $this->_bitsToGet)) & $this->_andTable[$this->_bitsToGet-9];
$this->_nextBits -= $this->_bitsToGet;
return $code;
}
/**
* NOT IMPLEMENTED
*
* @param string $in
* @return string
* @throws LogicException
*/
public function encode($in)
{
throw new LogicException("LZW encoding not implemented.");
}
}

548
fpdf_tpl.php Normal file

File diff suppressed because it is too large Load Diff

705
fpdi.php Normal file

File diff suppressed because it is too large Load Diff

206
fpdi_bridge.php Normal file
View File

@ -0,0 +1,206 @@
<?php
/**
* This file is part of FPDI
*
* @package FPDI
* @copyright Copyright (c) 2017 Setasign - Jan Slabon (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
* @version 1.6.2
*/
/**
* This file is used as a bridge between TCPDF or FPDF
* It will dynamically create the class extending the available
* class FPDF or TCPDF.
*
* This way it is possible to use FPDI for both FPDF and TCPDF with one FPDI version.
*/
if (!class_exists('TCPDF', false)) {
/**
* Class fpdi_bridge
*/
class fpdi_bridge extends FPDF
{
// empty body
}
} else {
/**
* Class fpdi_bridge
*/
class fpdi_bridge extends TCPDF
{
/**
* Array of Tpl-Data
*
* @var array
*/
protected $_tpls = array();
/**
* Name-prefix of Templates used in Resources-Dictionary
*
* @var string A String defining the Prefix used as Template-Object-Names. Have to begin with an /
*/
public $tplPrefix = "/TPL";
/**
* Current Object Id.
*
* @var integer
*/
protected $_currentObjId;
/**
* Return XObjects Dictionary.
*
* Overwritten to add additional XObjects to the resources dictionary of TCPDF
*
* @return string
*/
protected function _getxobjectdict()
{
$out = parent::_getxobjectdict();
foreach ($this->_tpls as $tplIdx => $tpl) {
$out .= sprintf('%s%d %d 0 R', $this->tplPrefix, $tplIdx, $tpl['n']);
}
return $out;
}
/**
* Writes a PDF value to the resulting document.
*
* Prepares the value for encryption of imported data by FPDI
*
* @param array $value
*/
protected function _prepareValue(&$value)
{
switch ($value[0]) {
case pdf_parser::TYPE_STRING:
if ($this->encrypted) {
$value[1] = $this->_unescape($value[1]);
$value[1] = $this->_encrypt_data($this->_currentObjId, $value[1]);
$value[1] = TCPDF_STATIC::_escape($value[1]);
}
break;
case pdf_parser::TYPE_STREAM:
if ($this->encrypted) {
$value[2][1] = $this->_encrypt_data($this->_currentObjId, $value[2][1]);
$value[1][1]['/Length'] = array(
pdf_parser::TYPE_NUMERIC,
strlen($value[2][1])
);
}
break;
case pdf_parser::TYPE_HEX:
if ($this->encrypted) {
$value[1] = $this->hex2str($value[1]);
$value[1] = $this->_encrypt_data($this->_currentObjId, $value[1]);
// remake hexstring of encrypted string
$value[1] = $this->str2hex($value[1]);
}
break;
}
}
/**
* Un-escapes a PDF string
*
* @param string $s
* @return string
*/
protected function _unescape($s)
{
$out = '';
for ($count = 0, $n = strlen($s); $count < $n; $count++) {
if ($s[$count] != '\\' || $count == $n-1) {
$out .= $s[$count];
} else {
switch ($s[++$count]) {
case ')':
case '(':
case '\\':
$out .= $s[$count];
break;
case 'f':
$out .= chr(0x0C);
break;
case 'b':
$out .= chr(0x08);
break;
case 't':
$out .= chr(0x09);
break;
case 'r':
$out .= chr(0x0D);
break;
case 'n':
$out .= chr(0x0A);
break;
case "\r":
if ($count != $n-1 && $s[$count+1] == "\n")
$count++;
break;
case "\n":
break;
default:
// Octal-Values
if (ord($s[$count]) >= ord('0') &&
ord($s[$count]) <= ord('9')) {
$oct = ''. $s[$count];
if (ord($s[$count+1]) >= ord('0') &&
ord($s[$count+1]) <= ord('9')) {
$oct .= $s[++$count];
if (ord($s[$count+1]) >= ord('0') &&
ord($s[$count+1]) <= ord('9')) {
$oct .= $s[++$count];
}
}
$out .= chr(octdec($oct));
} else {
$out .= $s[$count];
}
}
}
}
return $out;
}
/**
* Hexadecimal to string
*
* @param string $data
* @return string
*/
public function hex2str($data)
{
$data = preg_replace('/[^0-9A-Fa-f]/', '', rtrim($data, '>'));
if ((strlen($data) % 2) == 1) {
$data .= '0';
}
return pack('H*', $data);
}
/**
* String to hexadecimal
*
* @param string $str
* @return string
*/
public function str2hex($str)
{
return current(unpack('H*', $str));
}
}
}

355
fpdi_pdf_parser.php Normal file
View File

@ -0,0 +1,355 @@
<?php
/**
* This file is part of FPDI
*
* @package FPDI
* @copyright Copyright (c) 2017 Setasign - Jan Slabon (http://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
* @version 1.6.2
*/
if (!class_exists('pdf_parser')) {
require_once('pdf_parser.php');
}
/**
* Class fpdi_pdf_parser
*/
class fpdi_pdf_parser extends pdf_parser
{
/**
* Pages
*
* Index begins at 0
*
* @var array
*/
protected $_pages;
/**
* Page count
*
* @var integer
*/
protected $_pageCount;
/**
* Current page number
*
* @var integer
*/
public $pageNo;
/**
* PDF version of imported document
*
* @var string
*/
public $_pdfVersion;
/**
* Available BoxTypes
*
* @var array
*/
public $availableBoxes = array('/MediaBox', '/CropBox', '/BleedBox', '/TrimBox', '/ArtBox');
/**
* The constructor.
*
* @param string $filename The source filename
*/
public function __construct($filename)
{
parent::__construct($filename);
// resolve Pages-Dictonary
$pages = $this->resolveObject($this->_root[1][1]['/Pages']);
// Read pages
$this->_readPages($pages, $this->_pages);
// count pages;
$this->_pageCount = count($this->_pages);
}
/**
* Get page count from source file.
*
* @return int
*/
public function getPageCount()
{
return $this->_pageCount;
}
/**
* Set the page number.
*
* @param int $pageNo Page number to use
* @throws InvalidArgumentException
*/
public function setPageNo($pageNo)
{
$pageNo = ((int) $pageNo) - 1;
if ($pageNo < 0 || $pageNo >= $this->getPageCount()) {
throw new InvalidArgumentException('Invalid page number!');
}
$this->pageNo = $pageNo;
}
/**
* Get page-resources from current page
*
* @return array|boolean
*/
public function getPageResources()
{
return $this->_getPageResources($this->_pages[$this->pageNo]);
}
/**
* Get page-resources from a /Page dictionary.
*
* @param array $obj Array of pdf-data
* @return array|boolean
*/
protected function _getPageResources($obj)
{
$obj = $this->resolveObject($obj);
// If the current object has a resources
// dictionary associated with it, we use
// it. Otherwise, we move back to its
// parent object.
if (isset($obj[1][1]['/Resources'])) {
$res = $this->resolveObject($obj[1][1]['/Resources']);
if ($res[0] == pdf_parser::TYPE_OBJECT)
return $res[1];
return $res;
}
if (!isset($obj[1][1]['/Parent'])) {
return false;
}
$res = $this->_getPageResources($obj[1][1]['/Parent']);
if ($res[0] == pdf_parser::TYPE_OBJECT)
return $res[1];
return $res;
}
/**
* Get content of current page.
*
* If /Contents is an array, the streams are concatenated
*
* @return string
*/
public function getContent()
{
$buffer = '';
if (isset($this->_pages[$this->pageNo][1][1]['/Contents'])) {
$contents = $this->_getPageContent($this->_pages[$this->pageNo][1][1]['/Contents']);
foreach ($contents AS $tmpContent) {
if ($tmpContent[0] !== pdf_parser::TYPE_STREAM) {
continue;
}
$buffer .= $this->_unFilterStream($tmpContent) . ' ';
}
}
return $buffer;
}
/**
* Resolve all content objects.
*
* @param array $contentRef
* @return array
*/
protected function _getPageContent($contentRef)
{
$contents = array();
if ($contentRef[0] == pdf_parser::TYPE_OBJREF) {
$content = $this->resolveObject($contentRef);
if ($content[1][0] == pdf_parser::TYPE_ARRAY) {
$contents = $this->_getPageContent($content[1]);
} else {
$contents[] = $content;
}
} else if ($contentRef[0] == pdf_parser::TYPE_ARRAY) {
foreach ($contentRef[1] AS $tmp_content_ref) {
$contents = array_merge($contents, $this->_getPageContent($tmp_content_ref));
}
}
return $contents;
}
/**
* Get a boundary box from a page
*
* Array format is same as used by FPDF_TPL.
*
* @param array $page a /Page dictionary
* @param string $boxIndex Type of box {see {@link $availableBoxes})
* @param float Scale factor from user space units to points
*
* @return array|boolean
*/
protected function _getPageBox($page, $boxIndex, $k)
{
$page = $this->resolveObject($page);
$box = null;
if (isset($page[1][1][$boxIndex])) {
$box = $page[1][1][$boxIndex];
}
if (!is_null($box) && $box[0] == pdf_parser::TYPE_OBJREF) {
$tmp_box = $this->resolveObject($box);
$box = $tmp_box[1];
}
if (!is_null($box) && $box[0] == pdf_parser::TYPE_ARRAY) {
$b = $box[1];
return array(
'x' => $b[0][1] / $k,
'y' => $b[1][1] / $k,
'w' => abs($b[0][1] - $b[2][1]) / $k,
'h' => abs($b[1][1] - $b[3][1]) / $k,
'llx' => min($b[0][1], $b[2][1]) / $k,
'lly' => min($b[1][1], $b[3][1]) / $k,
'urx' => max($b[0][1], $b[2][1]) / $k,
'ury' => max($b[1][1], $b[3][1]) / $k,
);
} else if (!isset($page[1][1]['/Parent'])) {
return false;
} else {
return $this->_getPageBox($this->resolveObject($page[1][1]['/Parent']), $boxIndex, $k);
}
}
/**
* Get all page boundary boxes by page number
*
* @param int $pageNo The page number
* @param float $k Scale factor from user space units to points
* @return array
* @throws InvalidArgumentException
*/
public function getPageBoxes($pageNo, $k)
{
if (!isset($this->_pages[$pageNo - 1])) {
throw new InvalidArgumentException('Page ' . $pageNo . ' does not exists.');
}
return $this->_getPageBoxes($this->_pages[$pageNo - 1], $k);
}
/**
* Get all boxes from /Page dictionary
*
* @param array $page A /Page dictionary
* @param float $k Scale factor from user space units to points
* @return array
*/
protected function _getPageBoxes($page, $k)
{
$boxes = array();
foreach($this->availableBoxes AS $box) {
if ($_box = $this->_getPageBox($page, $box, $k)) {
$boxes[$box] = $_box;
}
}
return $boxes;
}
/**
* Get the page rotation by page number
*
* @param integer $pageNo
* @throws InvalidArgumentException
* @return array
*/
public function getPageRotation($pageNo)
{
if (!isset($this->_pages[$pageNo - 1])) {
throw new InvalidArgumentException('Page ' . $pageNo . ' does not exists.');
}
return $this->_getPageRotation($this->_pages[$pageNo - 1]);
}
/**
* Get the rotation value of a page
*
* @param array $obj A /Page dictionary
* @return array|bool
*/
protected function _getPageRotation($obj)
{
$obj = $this->resolveObject($obj);
if (isset($obj[1][1]['/Rotate'])) {
$res = $this->resolveObject($obj[1][1]['/Rotate']);
if ($res[0] == pdf_parser::TYPE_OBJECT)
return $res[1];
return $res;
}
if (!isset($obj[1][1]['/Parent'])) {
return false;
}
$res = $this->_getPageRotation($obj[1][1]['/Parent']);
if ($res[0] == pdf_parser::TYPE_OBJECT)
return $res[1];
return $res;
}
/**
* Read all pages
*
* @param array $pages /Pages dictionary
* @param array $result The result array
* @throws Exception
*/
protected function _readPages(&$pages, &$result)
{
// Get the kids dictionary
$_kids = $this->resolveObject($pages[1][1]['/Kids']);
if (!is_array($_kids)) {
throw new Exception('Cannot find /Kids in current /Page-Dictionary');
}
if ($_kids[0] === self::TYPE_OBJECT) {
$_kids = $_kids[1];
}
$kids = $_kids[1];
foreach ($kids as $v) {
$pg = $this->resolveObject($v);
if ($pg[0] !== pdf_parser::TYPE_OBJECT) {
throw new Exception('Invalid data type in page tree.');
}
if ($pg[1][1]['/Type'][1] === '/Pages') {
// If one of the kids is an embedded
// /Pages array, resolve it as well.
$this->_readPages($pg, $result);
} else {
$result[] = $pg;
}
}
}
}

151
pdf_context.php Normal file
View File

@ -0,0 +1,151 @@
<?php
/**
* This file is part of FPDI
*
* @package FPDI
* @copyright Copyright (c) 2017 Setasign - Jan Slabon (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
* @version 1.6.2
*/
/**
* Class pdf_context
*/
class pdf_context
{
/**
* Mode
*
* @var integer 0 = file | 1 = string
*/
protected $_mode = 0;
/**
* @var resource|string
*/
public $file;
/**
* @var string
*/
public $buffer;
/**
* @var integer
*/
public $offset;
/**
* @var integer
*/
public $length;
/**
* @var array
*/
public $stack;
/**
* The constructor
*
* @param resource $f
*/
public function __construct(&$f)
{
$this->file =& $f;
if (is_string($this->file))
$this->_mode = 1;
$this->reset();
}
/**
* Get the position in the file stream
*
* @return int
*/
public function getPos()
{
if ($this->_mode == 0) {
if (feof($this->file)) {
$stat = fstat($this->file);
fseek($this->file, $stat['size']);
}
$pos = ftell($this->file);
return $pos;
} else {
return 0;
}
}
/**
* Reset the position in the file stream.
*
* Optionally move the file pointer to a new location and reset the buffered data.
*
* @param null $pos
* @param int $l
*/
public function reset($pos = null, $l = 100)
{
if ($this->_mode == 0) {
if (!is_null($pos)) {
fseek($this->file, $pos);
}
$this->buffer = $l > 0 ? fread($this->file, $l) : '';
$this->length = strlen($this->buffer);
if ($this->length < $l)
$this->increaseLength($l - $this->length);
} else {
$this->buffer = $this->file;
$this->length = strlen($this->buffer);
}
$this->offset = 0;
$this->stack = array();
}
/**
* Make sure that there is at least one character beyond the current offset in the buffer.
*
* To prevent the tokenizer from attempting to access data that does not exist.
*
* @return bool
*/
public function ensureContent()
{
if ($this->offset >= $this->length - 1) {
return $this->increaseLength();
} else {
return true;
}
}
/**
* Forcefully read more data into the buffer
*
* @param int $l
* @return bool
*/
public function increaseLength($l = 100)
{
if ($this->_mode == 0 && feof($this->file)) {
return false;
} else if ($this->_mode == 0) {
$totalLength = $this->length + $l;
do {
$toRead = $totalLength - $this->length;
if ($toRead < 1)
break;
$this->buffer .= fread($this->file, $toRead);
} while ((($this->length = strlen($this->buffer)) != $totalLength) && !feof($this->file));
return true;
} else {
return false;
}
}
}

925
pdf_parser.php Normal file

File diff suppressed because it is too large Load Diff