diff --git a/include/fpdi/filters/FilterASCII85.php b/include/fpdi/filters/FilterASCII85.php
new file mode 100644
index 00000000..01402ba1
--- /dev/null
+++ b/include/fpdi/filters/FilterASCII85.php
@@ -0,0 +1,101 @@
+ ORD_u) {
+ return $this->error('Illegal character in ASCII85Decode.');
+ }
+
+ $chn[$state++] = $ch - ORD_exclmark;
+
+ if ($state == 5) {
+ $state = 0;
+ $r = 0;
+ for ($j = 0; $j < 5; ++$j)
+ $r = $r * 85 + $chn[$j];
+ $out .= chr($r >> 24);
+ $out .= chr($r >> 16);
+ $out .= chr($r >> 8);
+ $out .= chr($r);
+ }
+ }
+ $r = 0;
+
+ if ($state == 1)
+ return $this->error('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;
+ }
+
+ function encode($in) {
+ return $this->error("ASCII85 encoding not implemented.");
+ }
+ }
+}
diff --git a/include/fpdi/filters/FilterASCII85_FPDI.php b/include/fpdi/filters/FilterASCII85_FPDI.php
new file mode 100644
index 00000000..fb560011
--- /dev/null
+++ b/include/fpdi/filters/FilterASCII85_FPDI.php
@@ -0,0 +1,33 @@
+fpdi =& $fpdi;
+ }
+
+ function error($msg) {
+ $this->fpdi->error($msg);
+ }
+}
\ No newline at end of file
diff --git a/include/fpdi/filters/FilterLZW.php b/include/fpdi/filters/FilterLZW.php
new file mode 100644
index 00000000..292461d2
--- /dev/null
+++ b/include/fpdi/filters/FilterLZW.php
@@ -0,0 +1,157 @@
+error('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;
+
+ $string = '';
+ $uncompData = '';
+
+ while (($code = $this->getNextCode()) != 257) {
+ if ($code == 256) {
+ $this->initsTable();
+ $code = $this->getNextCode();
+
+ if ($code == 257) {
+ break;
+ }
+
+ $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.
+ */
+ 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.
+ */
+ 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
+ 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;
+ }
+
+ function encode($in) {
+ $this->error("LZW encoding not implemented.");
+ }
+ }
+}
diff --git a/include/fpdi/filters/FilterLZW_FPDI.php b/include/fpdi/filters/FilterLZW_FPDI.php
new file mode 100644
index 00000000..e7a88119
--- /dev/null
+++ b/include/fpdi/filters/FilterLZW_FPDI.php
@@ -0,0 +1,33 @@
+fpdi =& $fpdi;
+ }
+
+ function error($msg) {
+ $this->fpdi->error($msg);
+ }
+}
\ No newline at end of file
diff --git a/include/fpdi/fpdi.php b/include/fpdi/fpdi.php
index c1ebb89c..0ae2c637 100644
--- a/include/fpdi/fpdi.php
+++ b/include/fpdi/fpdi.php
@@ -1,8 +1,8 @@
current_filename = $filename;
- $fn =& $this->current_filename;
-
- if (!isset($this->parsers[$fn]))
- $this->parsers[$fn] = new fpdi_pdf_parser($fn, $this);
- $this->current_parser =& $this->parsers[$fn];
- return $this->parsers[$fn]->getPageCount();
+ if (!isset($this->parsers[$filename]))
+ $this->parsers[$filename] = $this->_getPdfParser($filename);
+ $this->current_parser =& $this->parsers[$filename];
+
+ return $this->parsers[$filename]->getPageCount();
}
+ /**
+ * Returns a PDF parser object
+ *
+ * @param string $filename
+ * @return fpdi_pdf_parser
+ */
+ function _getPdfParser($filename) {
+ return new fpdi_pdf_parser($filename, $this);
+ }
+
+ /**
+ * Get the current PDF version
+ *
+ * @return string
+ */
+ function getPDFVersion() {
+ return $this->PDFVersion;
+ }
+
+ /**
+ * Set the PDF version
+ *
+ * @return string
+ */
+ function setPDFVersion($version = '1.3') {
+ $this->PDFVersion = $version;
+ }
+
/**
* Import a page
*
* @param int $pageno pagenumber
* @return int Index of imported page - to use with fpdf_tpl::useTemplate()
*/
- function importPage($pageno, $boxName='/CropBox') {
+ function importPage($pageno, $boxName = '/CropBox') {
if ($this->_intpl) {
return $this->error('Please import the desired pages before creating a new template.');
}
- $fn =& $this->current_filename;
+ $fn = $this->current_filename;
// check if page already imported
- $pageKey = $fn.((int)$pageno).$boxName;
+ $pageKey = $fn . '-' . ((int)$pageno) . $boxName;
if (isset($this->_importedPages[$pageKey]))
return $this->_importedPages[$pageKey];
$parser =& $this->parsers[$fn];
$parser->setPageno($pageno);
- $this->tpl++;
- $this->tpls[$this->tpl] = array();
- $tpl =& $this->tpls[$this->tpl];
- $tpl['parser'] =& $parser;
- $tpl['resources'] = $parser->getPageResources();
- $tpl['buffer'] = $parser->getContent();
-
if (!in_array($boxName, $parser->availableBoxes))
return $this->Error(sprintf('Unknown box: %s', $boxName));
- $pageboxes = $parser->getPageBoxes($pageno);
+
+ $pageboxes = $parser->getPageBoxes($pageno, $this->k);
/**
* MediaBox
@@ -137,32 +161,41 @@ class FPDI extends FPDF_TPL {
if (!isset($pageboxes[$boxName]))
return false;
+
$this->lastUsedPageBox = $boxName;
$box = $pageboxes[$boxName];
+
+ $this->tpl++;
+ $this->tpls[$this->tpl] = array();
+ $tpl =& $this->tpls[$this->tpl];
+ $tpl['parser'] =& $parser;
+ $tpl['resources'] = $parser->getPageResources();
+ $tpl['buffer'] = $parser->getContent();
$tpl['box'] = $box;
// To build an array that can be used by PDF_TPL::useTemplate()
- $this->tpls[$this->tpl] = array_merge($this->tpls[$this->tpl],$box);
+ $this->tpls[$this->tpl] = array_merge($this->tpls[$this->tpl], $box);
// An imported page will start at 0,0 everytime. Translation will be set in _putformxobjects()
$tpl['x'] = 0;
$tpl['y'] = 0;
- $page =& $parser->pages[$parser->pageno];
-
// handle rotated pages
$rotation = $parser->getPageRotation($pageno);
$tpl['_rotationAngle'] = 0;
if (isset($rotation[1]) && ($angle = $rotation[1] % 360) != 0) {
- $steps = $angle / 90;
+ $steps = $angle / 90;
$_w = $tpl['w'];
$_h = $tpl['h'];
$tpl['w'] = $steps % 2 == 0 ? $_w : $_h;
$tpl['h'] = $steps % 2 == 0 ? $_h : $_w;
- $tpl['_rotationAngle'] = $angle*-1;
+ if ($angle < 0)
+ $angle += 360;
+
+ $tpl['_rotationAngle'] = $angle * -1;
}
$this->_importedPages[$pageKey] = $this->tpl;
@@ -170,28 +203,54 @@ class FPDI extends FPDF_TPL {
return $this->tpl;
}
+ /**
+ * Returns the last used page box
+ *
+ * @return string
+ */
function getLastUsedPageBox() {
return $this->lastUsedPageBox;
}
- function useTemplate($tplidx, $_x=null, $_y=null, $_w=0, $_h=0, $adjustPageSize=false) {
+
+ function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0, $adjustPageSize = false) {
if ($adjustPageSize == true && is_null($_x) && is_null($_y)) {
$size = $this->getTemplateSize($tplidx, $_w, $_h);
- $format = array($size['w'], $size['h']);
- if ($format[0]!=$this->CurPageFormat[0] || $format[1]!=$this->CurPageFormat[1]) {
- $this->w=$format[0];
- $this->h=$format[1];
- $this->wPt=$this->w*$this->k;
- $this->hPt=$this->h*$this->k;
- $this->PageBreakTrigger=$this->h-$this->bMargin;
- $this->CurPageFormat=$format;
- $this->PageSizes[$this->page]=array($this->wPt, $this->hPt);
- }
+ $orientation = $size['w'] > $size['h'] ? 'L' : 'P';
+ $size = array($size['w'], $size['h']);
+
+ if (is_subclass_of($this, 'TCPDF')) {
+ $this->setPageFormat($size, $orientation);
+ } else {
+ $size = $this->_getpagesize($size);
+
+ if($orientation!=$this->CurOrientation || $size[0]!=$this->CurPageSize[0] || $size[1]!=$this->CurPageSize[1])
+ {
+ // New size or orientation
+ if($orientation=='P')
+ {
+ $this->w = $size[0];
+ $this->h = $size[1];
+ }
+ else
+ {
+ $this->w = $size[1];
+ $this->h = $size[0];
+ }
+ $this->wPt = $this->w*$this->k;
+ $this->hPt = $this->h*$this->k;
+ $this->PageBreakTrigger = $this->h-$this->bMargin;
+ $this->CurOrientation = $orientation;
+ $this->CurPageSize = $size;
+ $this->PageSizes[$this->page] = array($this->wPt, $this->hPt);
+ }
+ }
}
$this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values
$s = parent::useTemplate($tplidx, $_x, $_y, $_w, $_h);
$this->_out('Q');
+
return $s;
}
@@ -204,14 +263,14 @@ class FPDI extends FPDF_TPL {
$this->current_parser =& $this->parsers[$filename];
if (isset($this->_obj_stack[$filename]) && is_array($this->_obj_stack[$filename])) {
while(($n = key($this->_obj_stack[$filename])) !== null) {
- $nObj = $this->current_parser->pdf_resolve_object($this->current_parser->c,$this->_obj_stack[$filename][$n][1]);
+ $nObj = $this->current_parser->pdf_resolve_object($this->current_parser->c, $this->_obj_stack[$filename][$n][1]);
$this->_newobj($this->_obj_stack[$filename][$n][0]);
if ($nObj[0] == PDF_TYPE_STREAM) {
- $this->pdf_write_value ($nObj);
+ $this->pdf_write_value($nObj);
} else {
- $this->pdf_write_value ($nObj[1]);
+ $this->pdf_write_value($nObj[1]);
}
$this->_out('endobj');
@@ -237,15 +296,15 @@ class FPDI extends FPDF_TPL {
$cN = $this->n; // TCPDF/Protection: rem current "n"
$this->tpls[$tplidx]['n'] = $this->n;
- $this->_out('<<'.$filter.'/Type /XObject');
+ $this->_out('<<' . $filter . '/Type /XObject');
$this->_out('/Subtype /Form');
$this->_out('/FormType 1');
$this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]',
- (isset($tpl['box']['llx']) ? $tpl['box']['llx'] : $tpl['x'])*$this->k,
- (isset($tpl['box']['lly']) ? $tpl['box']['lly'] : -$tpl['y'])*$this->k,
- (isset($tpl['box']['urx']) ? $tpl['box']['urx'] : $tpl['w'] + $tpl['x'])*$this->k,
- (isset($tpl['box']['ury']) ? $tpl['box']['ury'] : $tpl['h']-$tpl['y'])*$this->k
+ (isset($tpl['box']['llx']) ? $tpl['box']['llx'] : $tpl['x']) * $this->k,
+ (isset($tpl['box']['lly']) ? $tpl['box']['lly'] : -$tpl['y']) * $this->k,
+ (isset($tpl['box']['urx']) ? $tpl['box']['urx'] : $tpl['w'] + $tpl['x']) * $this->k,
+ (isset($tpl['box']['ury']) ? $tpl['box']['ury'] : $tpl['h'] - $tpl['y']) * $this->k
));
$c = 1;
@@ -255,7 +314,7 @@ class FPDI extends FPDF_TPL {
if (isset($tpl['box'])) {
$tx = -$tpl['box']['llx'];
- $ty = -$tpl['box']['lly'];
+ $ty = -$tpl['box']['lly'];
if ($tpl['_rotationAngle'] <> 0) {
$angle = $tpl['_rotationAngle'] * M_PI/180;
@@ -272,14 +331,14 @@ class FPDI extends FPDF_TPL {
$ty = $tpl['box']['ury'];
break;
case -270:
- $tx = $tpl['box']['ury'];
- $ty = 0;
+ $tx = $tpl['box']['ury'];
+ $ty = -$tpl['box']['llx'];
break;
}
}
} else if ($tpl['x'] != 0 || $tpl['y'] != 0) {
- $tx = -$tpl['x']*2;
- $ty = $tpl['y']*2;
+ $tx = -$tpl['x'] * 2;
+ $ty = $tpl['y'] * 2;
}
$tx *= $this->k;
@@ -301,7 +360,7 @@ class FPDI extends FPDF_TPL {
if (isset($this->_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) {
$this->_out('/Font <<');
foreach($this->_res['tpl'][$tplidx]['fonts'] as $font)
- $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
+ $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
$this->_out('>>');
}
if(isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) ||
@@ -310,11 +369,11 @@ class FPDI extends FPDF_TPL {
$this->_out('/XObject <<');
if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) {
foreach($this->_res['tpl'][$tplidx]['images'] as $image)
- $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
+ $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
}
if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
foreach($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl)
- $this->_out($this->tplprefix.$i.' '.$tpl['n'].' 0 R');
+ $this->_out($this->tplprefix . $i . ' ' . $tpl['n'] . ' 0 R');
}
$this->_out('>>');
}
@@ -323,8 +382,14 @@ class FPDI extends FPDF_TPL {
$nN = $this->n; // TCPDF: rem new "n"
$this->n = $cN; // TCPDF: reset to current "n"
- $this->_out('/Length '.strlen($p).' >>');
- $this->_putstream($p);
+ if (is_subclass_of($this, 'TCPDF')) {
+ $p = $this->_getrawstream($p);
+ $this->_out('/Length ' . strlen($p) . ' >>');
+ $this->_out("stream\n" . $p . "\nendstream");
+ } else {
+ $this->_out('/Length ' . strlen($p) . ' >>');
+ $this->_putstream($p);
+ }
$this->_out('endobj');
$this->n = $nN; // TCPDF: reset to new "n"
}
@@ -335,7 +400,7 @@ class FPDI extends FPDF_TPL {
/**
* Rewritten to handle existing own defined objects
*/
- function _newobj($obj_id=false,$onlynewobj=false) {
+ function _newobj($obj_id = false, $onlynewobj = false) {
if (!$obj_id) {
$obj_id = ++$this->n;
}
@@ -343,9 +408,11 @@ class FPDI extends FPDF_TPL {
//Begin a new object
if (!$onlynewobj) {
$this->offsets[$obj_id] = is_subclass_of($this, 'TCPDF') ? $this->bufferlen : strlen($this->buffer);
- $this->_out($obj_id.' 0 obj');
+ $this->_out($obj_id . ' 0 obj');
$this->_current_obj_id = $obj_id; // for later use with encryption
}
+
+ return $obj_id;
}
/**
@@ -362,19 +429,19 @@ class FPDI extends FPDF_TPL {
switch ($value[0]) {
- case PDF_TYPE_TOKEN :
+ case PDF_TYPE_TOKEN:
$this->_straightOut($value[1] . ' ');
break;
- case PDF_TYPE_NUMERIC :
- case PDF_TYPE_REAL :
+ case PDF_TYPE_NUMERIC:
+ case PDF_TYPE_REAL:
if (is_float($value[1]) && $value[1] != 0) {
- $this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') .' ');
+ $this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' ');
} else {
$this->_straightOut($value[1] . ' ');
}
break;
- case PDF_TYPE_ARRAY :
+ case PDF_TYPE_ARRAY:
// An array. Output the proper
// structure and move on.
@@ -387,7 +454,7 @@ class FPDI extends FPDF_TPL {
$this->_out(']');
break;
- case PDF_TYPE_DICTIONARY :
+ case PDF_TYPE_DICTIONARY:
// A dictionary.
$this->_straightOut('<<');
@@ -402,30 +469,30 @@ class FPDI extends FPDF_TPL {
$this->_straightOut('>>');
break;
- case PDF_TYPE_OBJREF :
+ case PDF_TYPE_OBJREF:
// An indirect object reference
// Fill the object stack if needed
$cpfn =& $this->current_parser->filename;
if (!isset($this->_don_obj_stack[$cpfn][$value[1]])) {
- $this->_newobj(false,true);
+ $this->_newobj(false, true);
$this->_obj_stack[$cpfn][$value[1]] = array($this->n, $value);
$this->_don_obj_stack[$cpfn][$value[1]] = array($this->n, $value); // Value is maybee obsolete!!!
}
$objid = $this->_don_obj_stack[$cpfn][$value[1]][0];
- $this->_out($objid.' 0 R');
+ $this->_out($objid . ' 0 R');
break;
- case PDF_TYPE_STRING :
+ case PDF_TYPE_STRING:
// A string.
- $this->_straightOut('('.$value[1].')');
+ $this->_straightOut('(' . $value[1] . ')');
break;
- case PDF_TYPE_STREAM :
+ case PDF_TYPE_STREAM:
// A stream. First, output the
// stream dictionary, then the
@@ -435,15 +502,16 @@ class FPDI extends FPDF_TPL {
$this->_out($value[2][1]);
$this->_out('endstream');
break;
- case PDF_TYPE_HEX :
- $this->_straightOut('<'.$value[1].'>');
+
+ case PDF_TYPE_HEX:
+ $this->_straightOut('<' . $value[1] . '>');
break;
- case PDF_TYPE_BOOLEAN :
+ case PDF_TYPE_BOOLEAN:
$this->_straightOut($value[1] ? 'true ' : 'false ');
break;
- case PDF_TYPE_NULL :
+ case PDF_TYPE_NULL:
// The null object.
$this->_straightOut('null ');
@@ -467,7 +535,7 @@ class FPDI extends FPDF_TPL {
// puts data before page footer
$page = substr($this->getPageBuffer($this->page), 0, -$this->footerlen[$this->page]);
$footer = substr($this->getPageBuffer($this->page), -$this->footerlen[$this->page]);
- $this->setPageBuffer($this->page, $page.' '.$s."\n".$footer);
+ $this->setPageBuffer($this->page, $page . ' ' . $s . "\n" . $footer);
} else {
$this->setPageBuffer($this->page, $s, true);
}
@@ -500,5 +568,4 @@ class FPDI extends FPDF_TPL {
}
return false;
}
-
}
\ No newline at end of file
diff --git a/include/fpdi/fpdi2tcpdf_bridge.php b/include/fpdi/fpdi2tcpdf_bridge.php
index 20a5cccd..8a031a38 100644
--- a/include/fpdi/fpdi2tcpdf_bridge.php
+++ b/include/fpdi/fpdi2tcpdf_bridge.php
@@ -1,8 +1,8 @@
PDFVersion;
- case 'k':
- return $this->k;
- default:
- // Error handling
- $this->Error('Cannot access protected property '.get_class($this).':$'.$name.' / Undefined property: '.get_class($this).'::$'.$name);
+ function _putstream($s) {
+ $this->_out($this->_getstream($s));
+ }
+
+ function _getxobjectdict() {
+ $out = parent::_getxobjectdict();
+ if (count($this->tpls)) {
+ foreach($this->tpls as $tplidx => $tpl) {
+ $out .= sprintf('%s%d %d 0 R', $this->tplprefix, $tplidx, $tpl['n']);
+ }
}
+
+ return $out;
}
-
- function __set($name, $value) {
- switch ($name) {
- case 'PDFVersion':
- $this->PDFVersion = $value;
- break;
- default:
- // Error handling
- $this->Error('Cannot access protected property '.get_class($this).':$'.$name.' / Undefined property: '.get_class($this).'::$'.$name);
- }
- }
-
+
/**
* Encryption of imported data by FPDI
*
@@ -58,24 +50,28 @@ class FPDF extends TCPDF {
*/
function pdf_write_value(&$value) {
switch ($value[0]) {
- case PDF_TYPE_STRING :
+ case PDF_TYPE_STRING:
if ($this->encrypted) {
$value[1] = $this->_unescape($value[1]);
- $value[1] = $this->_RC4($this->_objectkey($this->_current_obj_id), $value[1]);
+ $value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]);
$value[1] = $this->_escape($value[1]);
}
break;
- case PDF_TYPE_STREAM :
+ case PDF_TYPE_STREAM:
if ($this->encrypted) {
- $value[2][1] = $this->_RC4($this->_objectkey($this->_current_obj_id), $value[2][1]);
+ $value[2][1] = $this->_encrypt_data($this->_current_obj_id, $value[2][1]);
+ $value[1][1]['/Length'] = array(
+ PDF_TYPE_NUMERIC,
+ strlen($value[2][1])
+ );
}
break;
- case PDF_TYPE_HEX :
+ case PDF_TYPE_HEX:
if ($this->encrypted) {
$value[1] = $this->hex2str($value[1]);
- $value[1] = $this->_RC4($this->_objectkey($this->_current_obj_id), $value[1]);
+ $value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]);
// remake hexstring of encrypted string
$value[1] = $this->str2hex($value[1]);
diff --git a/include/fpdi/fpdi_pdf_parser.php b/include/fpdi/fpdi_pdf_parser.php
index b1fb5626..fd2b4414 100644
--- a/include/fpdi/fpdi_pdf_parser.php
+++ b/include/fpdi/fpdi_pdf_parser.php
@@ -1,8 +1,8 @@
pages[$this->pageno][1][1]['/Contents'])) {
$contents = $this->_getPageContent($this->pages[$this->pageno][1][1]['/Contents']);
foreach($contents AS $tmp_content) {
- $buffer .= $this->_rebuildContentStream($tmp_content).' ';
+ $buffer .= $this->_rebuildContentStream($tmp_content) . ' ';
}
}
@@ -213,6 +213,11 @@ class fpdi_pdf_parser extends pdf_parser {
if (isset($obj[1][1]['/Filter'])) {
$_filter = $obj[1][1]['/Filter'];
+ if ($_filter[0] == PDF_TYPE_OBJREF) {
+ $tmpFilter = $this->pdf_resolve_object($this->c, $_filter);
+ $_filter = $tmpFilter[1];
+ }
+
if ($_filter[0] == PDF_TYPE_TOKEN) {
$filters[] = $_filter;
} else if ($_filter[0] == PDF_TYPE_ARRAY) {
@@ -225,30 +230,35 @@ class fpdi_pdf_parser extends pdf_parser {
foreach ($filters AS $_filter) {
switch ($_filter[1]) {
case '/FlateDecode':
- if (function_exists('gzuncompress')) {
- $stream = (strlen($stream) > 0) ? @gzuncompress($stream) : '';
+ case '/Fl':
+ // $stream .= "\x0F\x0D"; // in an errorious stream this suffix could work
+ // $stream .= "\x0A";
+ // $stream .= "\x0D";
+ if (function_exists('gzuncompress')) {
+ $stream = (strlen($stream) > 0) ? @gzuncompress($stream) : '';
} else {
- $this->fpdi->error(sprintf('To handle %s filter, please compile php with zlib support.',$_filter[1]));
+ $this->error(sprintf('To handle %s filter, please compile php with zlib support.',$_filter[1]));
}
+
if ($stream === false) {
- $this->fpdi->error('Error while decompressing stream.');
+ $this->error('Error while decompressing stream.');
}
break;
+ case '/LZWDecode':
+ include_once('filters/FilterLZW_FPDI.php');
+ $decoder = new FilterLZW_FPDI($this->fpdi);
+ $stream = $decoder->decode($stream);
+ break;
+ case '/ASCII85Decode':
+ include_once('filters/FilterASCII85_FPDI.php');
+ $decoder = new FilterASCII85_FPDI($this->fpdi);
+ $stream = $decoder->decode($stream);
+ break;
case null:
$stream = $stream;
break;
default:
- if (preg_match('/^\/[a-z85]*$/i', $_filter[1], $filterName) && @include_once('decoders'.$_filter[1].'.php')) {
- $filterName = substr($_filter[1],1);
- if (class_exists($filterName)) {
- $decoder = new $filterName($this->fpdi);
- $stream = $decoder->decode($stream);
- } else {
- $this->fpdi->error(sprintf('Unsupported Filter: %s',$_filter[1]));
- }
- } else {
- $this->fpdi->error(sprintf('Unsupported Filter: %s',$_filter[1]));
- }
+ $this->error(sprintf('Unsupported Filter: %s',$_filter[1]));
}
}
@@ -262,52 +272,60 @@ class fpdi_pdf_parser extends pdf_parser {
*
* @param array $page a /Page
* @param string $box_index Type of Box @see $availableBoxes
+ * @param float Scale factor from user space units to points
* @return array
*/
- function getPageBox($page, $box_index) {
- $page = $this->pdf_resolve_object($this->c,$page);
+ function getPageBox($page, $box_index, $k) {
+ $page = $this->pdf_resolve_object($this->c, $page);
$box = null;
if (isset($page[1][1][$box_index]))
$box =& $page[1][1][$box_index];
if (!is_null($box) && $box[0] == PDF_TYPE_OBJREF) {
- $tmp_box = $this->pdf_resolve_object($this->c,$box);
+ $tmp_box = $this->pdf_resolve_object($this->c, $box);
$box = $tmp_box[1];
}
if (!is_null($box) && $box[0] == PDF_TYPE_ARRAY) {
$b =& $box[1];
- return array('x' => $b[0][1]/$this->fpdi->k,
- 'y' => $b[1][1]/$this->fpdi->k,
- 'w' => abs($b[0][1]-$b[2][1])/$this->fpdi->k,
- 'h' => abs($b[1][1]-$b[3][1])/$this->fpdi->k,
- 'llx' => $b[0][1]/$this->fpdi->k,
- 'lly' => $b[1][1]/$this->fpdi->k,
- 'urx' => $b[2][1]/$this->fpdi->k,
- 'ury' => $b[3][1]/$this->fpdi->k,
+ 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->pdf_resolve_object($this->c, $page[1][1]['/Parent']), $box_index);
+ return $this->getPageBox($this->pdf_resolve_object($this->c, $page[1][1]['/Parent']), $box_index, $k);
}
}
- function getPageBoxes($pageno) {
- return $this->_getPageBoxes($this->pages[$pageno-1]);
+ /**
+ * Get all page boxes by page no
+ *
+ * @param int The page number
+ * @param float Scale factor from user space units to points
+ * @return array
+ */
+ function getPageBoxes($pageno, $k) {
+ return $this->_getPageBoxes($this->pages[$pageno - 1], $k);
}
/**
- * Get all Boxes from /Page
+ * Get all boxes from /Page
*
* @param array a /Page
* @return array
*/
- function _getPageBoxes($page) {
+ function _getPageBoxes($page, $k) {
$boxes = array();
foreach($this->availableBoxes AS $box) {
- if ($_box = $this->getPageBox($page,$box)) {
+ if ($_box = $this->getPageBox($page, $box, $k)) {
$boxes[$box] = $_box;
}
}
@@ -322,10 +340,10 @@ class fpdi_pdf_parser extends pdf_parser {
* @return array
*/
function getPageRotation($pageno) {
- return $this->_getPageRotation($this->pages[$pageno-1]);
+ return $this->_getPageRotation($this->pages[$pageno - 1]);
}
- function _getPageRotation ($obj) { // $obj = /Page
+ function _getPageRotation($obj) { // $obj = /Page
$obj = $this->pdf_resolve_object($this->c, $obj);
if (isset ($obj[1][1]['/Rotate'])) {
$res = $this->pdf_resolve_object($this->c, $obj[1][1]['/Rotate']);
@@ -351,18 +369,25 @@ class fpdi_pdf_parser extends pdf_parser {
* @param array /Pages
* @param array the result-array
*/
- function read_pages (&$c, &$pages, &$result) {
+ function read_pages(&$c, &$pages, &$result) {
// Get the kids dictionary
- $kids = $this->pdf_resolve_object ($c, $pages[1][1]['/Kids']);
-
- if (!is_array($kids))
- $this->fpdi->Error('Cannot find /Kids in current /Page-Dictionary');
- foreach ($kids[1] as $v) {
+ $_kids = $this->pdf_resolve_object ($c, $pages[1][1]['/Kids']);
+
+ if (!is_array($_kids))
+ $this->error('Cannot find /Kids in current /Page-Dictionary');
+
+ if ($_kids[1][0] == PDF_TYPE_ARRAY) {
+ $kids = $_kids[1][1];
+ } else {
+ $kids = $_kids[1];
+ }
+
+ foreach ($kids as $v) {
$pg = $this->pdf_resolve_object ($c, $v);
if ($pg[1][1]['/Type'][1] === '/Pages') {
// If one of the kids is an embedded
// /Pages array, resolve it as well.
- $this->read_pages ($c, $pg, $result);
+ $this->read_pages($c, $pg, $result);
} else {
$result[] = $pg;
}
@@ -378,7 +403,6 @@ class fpdi_pdf_parser extends pdf_parser {
*/
function getPDFVersion() {
parent::getPDFVersion();
- $this->fpdi->PDFVersion = max($this->fpdi->PDFVersion, $this->pdfVersion);
+ $this->fpdi->setPDFVersion(max($this->fpdi->getPDFVersion(), $this->pdfVersion));
}
-
}
\ No newline at end of file
diff --git a/include/fpdi/pdf_context.php b/include/fpdi/pdf_context.php
index cddc9fb9..435df1e8 100644
--- a/include/fpdi/pdf_context.php
+++ b/include/fpdi/pdf_context.php
@@ -1,8 +1,8 @@
file =& $f;
- if (is_string($this->file))
- $this->_mode = 1;
- $this->reset();
- }
-
- // Optionally move the file
- // pointer to a new location
- // and reset the buffered data
-
- function reset($pos = null, $l = 100) {
- if ($this->_mode == 0) {
- if (!is_null ($pos)) {
- fseek ($this->file, $pos);
+ class pdf_context {
+
+ /**
+ * Modi
+ *
+ * @var integer 0 = file | 1 = string
+ */
+ var $_mode = 0;
+
+ var $file;
+ var $buffer;
+ var $offset;
+ var $length;
+
+ var $stack;
+
+ // Constructor
+
+ function pdf_context(&$f) {
+ $this->file =& $f;
+ if (is_string($this->file))
+ $this->_mode = 1;
+ $this->reset();
+ }
+
+ // Optionally move the file
+ // pointer to a new location
+ // and reset the buffered data
+
+ 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->increase_length($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
+
+ function ensure_content() {
+ if ($this->offset >= $this->length - 1) {
+ return $this->increase_length();
+ } else {
+ return true;
}
+ }
- $this->buffer = $l > 0 ? fread($this->file, $l) : '';
- $this->length = strlen($this->buffer);
- if ($this->length < $l)
- $this->increase_length($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
-
- function ensure_content() {
- if ($this->offset >= $this->length - 1) {
- return $this->increase_length();
- } else {
- return true;
+ // Forcefully read more data into the buffer
+
+ function increase_length($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;
+ }
}
- }
-
- // Forcefully read more data into the buffer
-
- function increase_length($l=100) {
- if ($this->_mode == 0 && feof($this->file)) {
- return false;
- } else if ($this->_mode == 0) {
- $totalLength = $this->length + $l;
- do {
- $this->buffer .= fread($this->file, $totalLength-$this->length);
- } while ((($this->length = strlen($this->buffer)) != $totalLength) && !feof($this->file));
-
- return true;
- } else {
- return false;
- }
- }
+ }
}
\ No newline at end of file
diff --git a/include/fpdi/pdf_parser.php b/include/fpdi/pdf_parser.php
index 77186519..4c6dd8e8 100644
--- a/include/fpdi/pdf_parser.php
+++ b/include/fpdi/pdf_parser.php
@@ -1,8 +1,8 @@
filename = $filename;
+ class pdf_parser {
+
+ /**
+ * Filename
+ * @var string
+ */
+ var $filename;
- $this->f = @fopen($this->filename, 'rb');
-
- if (!$this->f)
- $this->error(sprintf('Cannot open %s !', $filename));
-
- $this->getPDFVersion();
-
- $this->c = new pdf_context($this->f);
+ /**
+ * File resource
+ * @var resource
+ */
+ var $f;
- // Read xref-Data
- $this->pdf_read_xref($this->xref, $this->pdf_find_xref());
+ /**
+ * PDF Context
+ * @var object pdf_context-Instance
+ */
+ var $c;
- // Check for Encryption
- $this->getEncryption();
-
- // Read root
- $this->pdf_read_root();
- }
+ /**
+ * xref-Data
+ * @var array
+ */
+ var $xref;
- /**
- * Close the opened file
- */
- function closeFile() {
- if (isset($this->f)) {
- fclose($this->f);
- unset($this->f);
- }
- }
-
- /**
- * Print Error and die
- *
- * @param string $msg Error-Message
- */
- function error($msg) {
- die('PDF-Parser Error: '.$msg);
- }
-
- /**
- * Check Trailer for Encryption
- */
- function getEncryption() {
- if (isset($this->xref['trailer'][1]['/Encrypt'])) {
- $this->error('File is encrypted!');
- }
- }
-
- /**
- * Find/Return /Root
- *
- * @return array
- */
- function pdf_find_root() {
- if ($this->xref['trailer'][1]['/Root'][0] != PDF_TYPE_OBJREF) {
- $this->error('Wrong Type of Root-Element! Must be an indirect reference');
- }
- return $this->xref['trailer'][1]['/Root'];
- }
-
- /**
- * Read the /Root
- */
- function pdf_read_root() {
- // read root
- $this->root = $this->pdf_resolve_object($this->c, $this->pdf_find_root());
- }
-
- /**
- * Get PDF-Version
- *
- * And reset the PDF Version used in FPDI if needed
- */
- function getPDFVersion() {
- fseek($this->f, 0);
- preg_match('/\d\.\d/',fread($this->f,16),$m);
- if (isset($m[0]))
- $this->pdfVersion = $m[0];
- return $this->pdfVersion;
- }
-
- /**
- * Find the xref-Table
- */
- function pdf_find_xref() {
- $toRead = 1500;
-
- $stat = fseek ($this->f, -$toRead, SEEK_END);
- if ($stat === -1) {
- fseek ($this->f, 0);
- }
- $data = fread($this->f, $toRead);
+ /**
+ * root-Object
+ * @var array
+ */
+ var $root;
+
+ /**
+ * PDF version of the loaded document
+ * @var string
+ */
+ var $pdfVersion;
- $pos = strlen($data) - strpos(strrev($data), strrev('startxref'));
- $data = substr($data, $pos);
-
- if (!preg_match('/\s*(\d+).*$/s', $data, $matches)) {
- $this->error('Unable to find pointer to xref table');
- }
-
- return (int) $matches[1];
- }
-
- /**
- * Read xref-table
- *
- * @param array $result Array of xref-table
- * @param integer $offset of xref-table
- */
- function pdf_read_xref(&$result, $offset) {
-
- fseek($this->f, $o_pos = $offset-20); // set some bytes backwards to fetch errorious docs
+ /**
+ * For reading encrypted documents and xref/objectstreams are in use
+ *
+ * @var boolean
+ */
+ var $readPlain = true;
+
+ /**
+ * Constructor
+ *
+ * @param string $filename Source-Filename
+ */
+ function pdf_parser($filename) {
+ $this->filename = $filename;
- $data = fread($this->f, 100);
-
- $xrefPos = strpos($data, 'xref');
-
- if ($xrefPos === false) {
- $this->error('Unable to find xref table.');
+ $this->f = @fopen($this->filename, 'rb');
+
+ if (!$this->f)
+ $this->error(sprintf('Cannot open %s !', $filename));
+
+ $this->getPDFVersion();
+
+ $this->c = new pdf_context($this->f);
+
+ // Read xref-Data
+ $this->xref = array();
+ $this->pdf_read_xref($this->xref, $this->pdf_find_xref());
+
+ // Check for Encryption
+ $this->getEncryption();
+
+ // Read root
+ $this->pdf_read_root();
}
- if (!isset($result['xref_location'])) {
- $result['xref_location'] = $o_pos+$xrefPos;
- $result['max_object'] = 0;
- }
-
- $cylces = -1;
- $bytesPerCycle = 100;
-
- fseek($this->f, $o_pos = $o_pos+$xrefPos+4); // set the handle directly after the "xref"-keyword
- $data = fread($this->f, $bytesPerCycle);
-
- while (($trailerPos = strpos($data, 'trailer', max($bytesPerCycle*$cylces++, 0))) === false && !feof($this->f)) {
- $data .= fread($this->f, $bytesPerCycle);
+ /**
+ * Close the opened file
+ */
+ function closeFile() {
+ if (isset($this->f) && is_resource($this->f)) {
+ fclose($this->f);
+ unset($this->f);
+ }
}
- if ($trailerPos === false) {
- $this->error('Trailer keyword not found after xref table');
+ /**
+ * Print Error and die
+ *
+ * @param string $msg Error-Message
+ */
+ function error($msg) {
+ die('PDF-Parser Error: ' . $msg);
}
- $data = substr($data, 0, $trailerPos);
-
- // get Line-Ending
- preg_match_all("/(\r\n|\n|\r)/", substr($data, 0, 100), $m); // check the first 100 bytes for linebreaks
-
- $differentLineEndings = count(array_unique($m[0]));
- if ($differentLineEndings > 1) {
- $lines = preg_split("/(\r\n|\n|\r)/", $data, -1, PREG_SPLIT_NO_EMPTY);
- } else {
- $lines = explode($m[0][1], $data);
- }
-
- $data = $differentLineEndings = $m = null;
- unset($data, $differentLineEndings, $m);
-
- $linesCount = count($lines);
-
- $start = 1;
-
- for ($i = 0; $i < $linesCount; $i++) {
- $line = trim($lines[$i]);
- if ($line) {
- $pieces = explode(' ', $line);
- $c = count($pieces);
- switch($c) {
- case 2:
- $start = (int)$pieces[0];
- $end = $start+(int)$pieces[1];
- if ($end > $result['max_object'])
- $result['max_object'] = $end;
- break;
- case 3:
- if (!isset($result['xref'][$start]))
- $result['xref'][$start] = array();
-
- if (!array_key_exists($gen = (int) $pieces[1], $result['xref'][$start])) {
- $result['xref'][$start][$gen] = $pieces[2] == 'n' ? (int) $pieces[0] : null;
- }
- $start++;
- break;
- default:
- $this->error('Unexpected data in xref table');
- }
+ /**
+ * Check Trailer for Encryption
+ */
+ function getEncryption() {
+ if (isset($this->xref['trailer'][1]['/Encrypt'])) {
+ $this->error('File is encrypted!');
}
}
- $lines = $pieces = $line = $start = $end = $gen = null;
- unset($lines, $pieces, $line, $start, $end, $gen);
-
- fseek($this->f, $o_pos+$trailerPos+7);
-
- $c = new pdf_context($this->f);
- $trailer = $this->pdf_read_value($c);
-
- $c = null;
- unset($c);
-
- if (!isset($result['trailer'])) {
- $result['trailer'] = $trailer;
- }
-
- if (isset($trailer[1]['/Prev'])) {
- $this->pdf_read_xref($result, $trailer[1]['/Prev'][1]);
- }
-
- $trailer = null;
- unset($trailer);
-
- return true;
- }
+ /**
+ * Find/Return /Root
+ *
+ * @return array
+ */
+ function pdf_find_root() {
+ if ($this->xref['trailer'][1]['/Root'][0] != PDF_TYPE_OBJREF) {
+ $this->error('Wrong Type of Root-Element! Must be an indirect reference');
+ }
+
+ return $this->xref['trailer'][1]['/Root'];
+ }
- /**
- * Reads an Value
- *
- * @param object $c pdf_context
- * @param string $token a Token
- * @return mixed
- */
- function pdf_read_value(&$c, $token = null) {
- if (is_null($token)) {
- $token = $this->pdf_read_token($c);
- }
-
- if ($token === false) {
- return false;
- }
-
- switch ($token) {
- case '<':
- // This is a hex string.
- // Read the value, then the terminator
-
- $pos = $c->offset;
-
- while(1) {
-
- $match = strpos ($c->buffer, '>', $pos);
-
- // If you can't find it, try
- // reading more data from the stream
-
- if ($match === false) {
- if (!$c->increase_length()) {
- return false;
- } else {
- continue;
- }
- }
-
- $result = substr ($c->buffer, $c->offset, $match - $c->offset);
- $c->offset = $match + 1;
-
- return array (PDF_TYPE_HEX, $result);
+ /**
+ * Read the /Root
+ */
+ function pdf_read_root() {
+ // read root
+ $this->root = $this->pdf_resolve_object($this->c, $this->pdf_find_root());
+ }
+
+ /**
+ * Get PDF-Version
+ *
+ * And reset the PDF Version used in FPDI if needed
+ */
+ function getPDFVersion() {
+ fseek($this->f, 0);
+ preg_match('/\d\.\d/',fread($this->f, 16), $m);
+ if (isset($m[0]))
+ $this->pdfVersion = $m[0];
+ return $this->pdfVersion;
+ }
+
+ /**
+ * Find the xref-Table
+ */
+ function pdf_find_xref() {
+ $toRead = 1500;
+
+ $stat = fseek ($this->f, -$toRead, SEEK_END);
+ if ($stat === -1) {
+ fseek ($this->f, 0);
+ }
+ $data = fread($this->f, $toRead);
+
+ $pos = strlen($data) - strpos(strrev($data), strrev('startxref'));
+ $data = substr($data, $pos);
+
+ if (!preg_match('/\s*(\d+).*$/s', $data, $matches)) {
+ $this->error('Unable to find pointer to xref table');
+ }
+
+ return (int) $matches[1];
+ }
+
+ /**
+ * Read xref-table
+ *
+ * @param array $result Array of xref-table
+ * @param integer $offset of xref-table
+ */
+ function pdf_read_xref(&$result, $offset) {
+ $o_pos = $offset-min(20, $offset);
+ fseek($this->f, $o_pos); // set some bytes backwards to fetch errorious docs
+
+ $data = fread($this->f, 100);
+
+ $xrefPos = strrpos($data, 'xref');
+
+ if ($xrefPos === false) {
+ fseek($this->f, $offset);
+ $c = new pdf_context($this->f);
+ $xrefStreamObjDec = $this->pdf_read_value($c);
+
+ if (is_array($xrefStreamObjDec) && isset($xrefStreamObjDec[0]) && $xrefStreamObjDec[0] == PDF_TYPE_OBJDEC) {
+ $this->error(sprintf('This document (%s) probably uses a compression technique which is not supported by the free parser shipped with FPDI.', $this->filename));
+ } else {
+ $this->error('Unable to find xref table.');
}
-
- break;
- case '<<':
- // This is a dictionary.
-
- $result = array();
-
- // Recurse into this function until we reach
- // the end of the dictionary.
- while (($key = $this->pdf_read_token($c)) !== '>>') {
- if ($key === false) {
- return false;
- }
-
- if (($value = $this->pdf_read_value($c)) === false) {
- return false;
- }
-
- // Catch missing value
- if ($value[0] == PDF_TYPE_TOKEN && $value[1] == '>>') {
- $result[$key] = array(PDF_TYPE_NULL);
- break;
- }
-
- $result[$key] = $value;
- }
-
- return array (PDF_TYPE_DICTIONARY, $result);
-
- case '[':
- // This is an array.
-
- $result = array();
-
- // Recurse into this function until we reach
- // the end of the array.
- while (($token = $this->pdf_read_token($c)) !== ']') {
- if ($token === false) {
- return false;
- }
-
- if (($value = $this->pdf_read_value($c, $token)) === false) {
- return false;
- }
-
- $result[] = $value;
- }
-
- return array (PDF_TYPE_ARRAY, $result);
-
- case '(' :
- // This is a string
- $pos = $c->offset;
-
- $openBrackets = 1;
- do {
- for (; $openBrackets != 0 && $pos < $c->length; $pos++) {
- switch (ord($c->buffer[$pos])) {
- case 0x28: // '('
- $openBrackets++;
- break;
- case 0x29: // ')'
- $openBrackets--;
- break;
- case 0x5C: // backslash
- $pos++;
- }
+ }
+
+ if (!isset($result['xref_location'])) {
+ $result['xref_location'] = $o_pos + $xrefPos;
+ $result['max_object'] = 0;
+ }
+
+ $cylces = -1;
+ $bytesPerCycle = 100;
+
+ fseek($this->f, $o_pos = $o_pos + $xrefPos + 4); // set the handle directly after the "xref"-keyword
+ $data = fread($this->f, $bytesPerCycle);
+
+ while (($trailerPos = strpos($data, 'trailer', max($bytesPerCycle * $cylces++, 0))) === false && !feof($this->f)) {
+ $data .= fread($this->f, $bytesPerCycle);
+ }
+
+ if ($trailerPos === false) {
+ $this->error('Trailer keyword not found after xref table');
+ }
+
+ $data = substr($data, 0, $trailerPos);
+
+ // get Line-Ending
+ preg_match_all("/(\r\n|\n|\r)/", substr($data, 0, 100), $m); // check the first 100 bytes for linebreaks
+
+ $differentLineEndings = count(array_unique($m[0]));
+ if ($differentLineEndings > 1) {
+ $lines = preg_split("/(\r\n|\n|\r)/", $data, -1, PREG_SPLIT_NO_EMPTY);
+ } else {
+ $lines = explode($m[0][1], $data);
+ }
+
+ $data = $differentLineEndings = $m = null;
+ unset($data, $differentLineEndings, $m);
+
+ $linesCount = count($lines);
+
+ $start = 1;
+
+ for ($i = 0; $i < $linesCount; $i++) {
+ $line = trim($lines[$i]);
+ if ($line) {
+ $pieces = explode(' ', $line);
+ $c = count($pieces);
+ switch($c) {
+ case 2:
+ $start = (int)$pieces[0];
+ $end = $start + (int)$pieces[1];
+ if ($end > $result['max_object'])
+ $result['max_object'] = $end;
+ break;
+ case 3:
+ if (!isset($result['xref'][$start]))
+ $result['xref'][$start] = array();
+
+ if (!array_key_exists($gen = (int) $pieces[1], $result['xref'][$start])) {
+ $result['xref'][$start][$gen] = $pieces[2] == 'n' ? (int) $pieces[0] : null;
+ }
+ $start++;
+ break;
+ default:
+ $this->error('Unexpected data in xref table');
}
- } while($openBrackets != 0 && $c->increase_length());
-
- $result = substr($c->buffer, $c->offset, $pos - $c->offset - 1);
- $c->offset = $pos;
-
- return array (PDF_TYPE_STRING, $result);
-
-
- case 'stream':
- $o_pos = ftell($c->file)-strlen($c->buffer);
- $o_offset = $c->offset;
-
- $c->reset($startpos = $o_pos + $o_offset);
-
- $e = 0; // ensure line breaks in front of the stream
- if ($c->buffer[0] == chr(10) || $c->buffer[0] == chr(13))
- $e++;
- if ($c->buffer[1] == chr(10) && $c->buffer[0] != chr(10))
- $e++;
-
- if ($this->actual_obj[1][1]['/Length'][0] == PDF_TYPE_OBJREF) {
- $tmp_c = new pdf_context($this->f);
- $tmp_length = $this->pdf_resolve_object($tmp_c,$this->actual_obj[1][1]['/Length']);
- $length = $tmp_length[1][1];
- } else {
- $length = $this->actual_obj[1][1]['/Length'][1];
- }
-
- if ($length > 0) {
- $c->reset($startpos+$e,$length);
- $v = $c->buffer;
- } else {
- $v = '';
- }
- $c->reset($startpos+$e+$length+9); // 9 = strlen("endstream")
-
- return array(PDF_TYPE_STREAM, $v);
-
- default :
- if (is_numeric ($token)) {
- // A numeric token. Make sure that
- // it is not part of something else.
- if (($tok2 = $this->pdf_read_token ($c)) !== false) {
- if (is_numeric ($tok2)) {
-
- // Two numeric tokens in a row.
- // In this case, we're probably in
- // front of either an object reference
- // or an object specification.
- // Determine the case and return the data
- if (($tok3 = $this->pdf_read_token ($c)) !== false) {
- switch ($tok3) {
- case 'obj' :
- return array (PDF_TYPE_OBJDEC, (int) $token, (int) $tok2);
- case 'R' :
- return array (PDF_TYPE_OBJREF, (int) $token, (int) $tok2);
- }
- // If we get to this point, that numeric value up
- // there was just a numeric value. Push the extra
- // tokens back into the stack and return the value.
- array_push ($c->stack, $tok3);
- }
- }
-
- array_push ($c->stack, $tok2);
- }
-
- if ($token === (string)((int)$token))
- return array (PDF_TYPE_NUMERIC, (int)$token);
- else
- return array (PDF_TYPE_REAL, (float)$token);
- } else if ($token == 'true' || $token == 'false') {
- return array (PDF_TYPE_BOOLEAN, $token == 'true');
- } else if ($token == 'null') {
- return array (PDF_TYPE_NULL);
- } else {
-
- // Just a token. Return it.
- return array (PDF_TYPE_TOKEN, $token);
- }
-
- }
- }
-
- /**
- * Resolve an object
- *
- * @param object $c pdf_context
- * @param array $obj_spec The object-data
- * @param boolean $encapsulate Must set to true, cause the parsing and fpdi use this method only without this para
- */
- function pdf_resolve_object(&$c, $obj_spec, $encapsulate = true) {
- // Exit if we get invalid data
- if (!is_array($obj_spec)) {
- return false;
- }
-
- if ($obj_spec[0] == PDF_TYPE_OBJREF) {
-
- // This is a reference, resolve it
- if (isset($this->xref['xref'][$obj_spec[1]][$obj_spec[2]])) {
-
- // Save current file position
- // This is needed if you want to resolve
- // references while you're reading another object
- // (e.g.: if you need to determine the length
- // of a stream)
-
- $old_pos = ftell($c->file);
-
- // Reposition the file pointer and
- // load the object header.
-
- $c->reset($this->xref['xref'][$obj_spec[1]][$obj_spec[2]]);
-
- $header = $this->pdf_read_value($c);
-
- if ($header[0] != PDF_TYPE_OBJDEC || $header[1] != $obj_spec[1] || $header[2] != $obj_spec[2]) {
- $this->error("Unable to find object ({$obj_spec[1]}, {$obj_spec[2]}) at expected location");
- }
-
- // If we're being asked to store all the information
- // about the object, we add the object ID and generation
- // number for later use
- $this->actual_obj =& $result;
- if ($encapsulate) {
- $result = array (
- PDF_TYPE_OBJECT,
- 'obj' => $obj_spec[1],
- 'gen' => $obj_spec[2]
- );
- } else {
- $result = array();
- }
-
- // Now simply read the object data until
- // we encounter an end-of-object marker
- while(1) {
- $value = $this->pdf_read_value($c);
- if ($value === false || count($result) > 4) {
- // in this case the parser coudn't find an endobj so we break here
- break;
- }
-
- if ($value[0] == PDF_TYPE_TOKEN && $value[1] === 'endobj') {
- break;
- }
-
- $result[] = $value;
- }
-
- $c->reset($old_pos);
-
- if (isset($result[2][0]) && $result[2][0] == PDF_TYPE_STREAM) {
- $result[0] = PDF_TYPE_STREAM;
}
-
- return $result;
- }
- } else {
- return $obj_spec;
- }
- }
-
+ }
+
+ $lines = $pieces = $line = $start = $end = $gen = null;
+ unset($lines, $pieces, $line, $start, $end, $gen);
+
+ fseek($this->f, $o_pos + $trailerPos + 7);
+
+ $c = new pdf_context($this->f);
+ $trailer = $this->pdf_read_value($c);
+
+ $c = null;
+ unset($c);
+
+ if (!isset($result['trailer'])) {
+ $result['trailer'] = $trailer;
+ }
+
+ if (isset($trailer[1]['/Prev'])) {
+ $this->pdf_read_xref($result, $trailer[1]['/Prev'][1]);
+ }
+
+ $trailer = null;
+ unset($trailer);
+
+ return true;
+ }
+
+ /**
+ * Reads an Value
+ *
+ * @param object $c pdf_context
+ * @param string $token a Token
+ * @return mixed
+ */
+ function pdf_read_value(&$c, $token = null) {
+ if (is_null($token)) {
+ $token = $this->pdf_read_token($c);
+ }
+
+ if ($token === false) {
+ return false;
+ }
+ switch ($token) {
+ case '<':
+ // This is a hex string.
+ // Read the value, then the terminator
- /**
- * Reads a token from the file
- *
- * @param object $c pdf_context
- * @return mixed
- */
- function pdf_read_token(&$c)
- {
- // If there is a token available
- // on the stack, pop it out and
- // return it.
-
- if (count($c->stack)) {
- return array_pop($c->stack);
- }
-
- // Strip away any whitespace
-
- do {
- if (!$c->ensure_content()) {
- return false;
- }
- $c->offset += strspn($c->buffer, " \n\r\t", $c->offset);
- } while ($c->offset >= $c->length - 1);
-
- // Get the first character in the stream
-
- $char = $c->buffer[$c->offset++];
-
- switch ($char) {
-
- case '[' :
- case ']' :
- case '(' :
- case ')' :
-
- // This is either an array or literal string
- // delimiter, Return it
-
- return $char;
-
- case '<' :
- case '>' :
-
- // This could either be a hex string or
- // dictionary delimiter. Determine the
- // appropriate case and return the token
-
- if ($c->buffer[$c->offset] == $char) {
- if (!$c->ensure_content()) {
- return false;
- }
- $c->offset++;
- return $char . $char;
- } else {
- return $char;
- }
-
- case '%' :
-
- // This is a comment - jump over it!
-
- $pos = $c->offset;
- while(1) {
- $match = preg_match("/(\r\n|\r|\n)/", $c->buffer, $m, PREG_OFFSET_CAPTURE, $pos);
- if ($match === 0) {
- if (!$c->increase_length()) {
- return false;
- } else {
- continue;
- }
- }
-
- $c->offset = $m[0][1]+strlen($m[0][0]);
+ $pos = $c->offset;
+
+ while(1) {
+
+ $match = strpos ($c->buffer, '>', $pos);
- return $this->pdf_read_token($c);
- }
-
- default :
-
- // This is "another" type of token (probably
- // a dictionary entry or a numeric value)
- // Find the end and return it.
-
- if (!$c->ensure_content()) {
- return false;
- }
-
- while(1) {
-
- // Determine the length of the token
-
- $pos = strcspn($c->buffer, " %[]<>()\r\n\t/", $c->offset);
- if ($c->offset + $pos <= $c->length - 1) {
- break;
- } else {
- // If the script reaches this point,
- // the token may span beyond the end
- // of the current buffer. Therefore,
- // we increase the size of the buffer
- // and try again--just to be safe.
-
- $c->increase_length();
- }
- }
-
- $result = substr($c->buffer, $c->offset - 1, $pos + 1);
-
- $c->offset += $pos;
- return $result;
- }
+ // If you can't find it, try
+ // reading more data from the stream
+
+ if ($match === false) {
+ if (!$c->increase_length()) {
+ return false;
+ } else {
+ continue;
+ }
+ }
+
+ $result = substr ($c->buffer, $c->offset, $match - $c->offset);
+ $c->offset = $match + 1;
+
+ return array (PDF_TYPE_HEX, $result);
+ }
+
+ break;
+ case '<<':
+ // This is a dictionary.
+
+ $result = array();
+
+ // Recurse into this function until we reach
+ // the end of the dictionary.
+ while (($key = $this->pdf_read_token($c)) !== '>>') {
+ if ($key === false) {
+ return false;
+ }
+
+ if (($value = $this->pdf_read_value($c)) === false) {
+ return false;
+ }
+
+ // Catch missing value
+ if ($value[0] == PDF_TYPE_TOKEN && $value[1] == '>>') {
+ $result[$key] = array(PDF_TYPE_NULL);
+ break;
+ }
+
+ $result[$key] = $value;
+ }
+
+ return array (PDF_TYPE_DICTIONARY, $result);
+
+ case '[':
+ // This is an array.
+
+ $result = array();
+
+ // Recurse into this function until we reach
+ // the end of the array.
+ while (($token = $this->pdf_read_token($c)) !== ']') {
+ if ($token === false) {
+ return false;
+ }
+
+ if (($value = $this->pdf_read_value($c, $token)) === false) {
+ return false;
+ }
+
+ $result[] = $value;
+ }
+
+ return array (PDF_TYPE_ARRAY, $result);
+
+ case '(' :
+ // This is a string
+ $pos = $c->offset;
+
+ $openBrackets = 1;
+ do {
+ for (; $openBrackets != 0 && $pos < $c->length; $pos++) {
+ switch (ord($c->buffer[$pos])) {
+ case 0x28: // '('
+ $openBrackets++;
+ break;
+ case 0x29: // ')'
+ $openBrackets--;
+ break;
+ case 0x5C: // backslash
+ $pos++;
+ }
+ }
+ } while($openBrackets != 0 && $c->increase_length());
+
+ $result = substr($c->buffer, $c->offset, $pos - $c->offset - 1);
+ $c->offset = $pos;
+
+ return array (PDF_TYPE_STRING, $result);
+
+ case 'stream':
+ $o_pos = ftell($c->file)-strlen($c->buffer);
+ $o_offset = $c->offset;
+
+ $c->reset($startpos = $o_pos + $o_offset);
+
+ $e = 0; // ensure line breaks in front of the stream
+ if ($c->buffer[0] == chr(10) || $c->buffer[0] == chr(13))
+ $e++;
+ if ($c->buffer[1] == chr(10) && $c->buffer[0] != chr(10))
+ $e++;
+
+ if ($this->actual_obj[1][1]['/Length'][0] == PDF_TYPE_OBJREF) {
+ $tmp_c = new pdf_context($this->f);
+ $tmp_length = $this->pdf_resolve_object($tmp_c, $this->actual_obj[1][1]['/Length']);
+ $length = $tmp_length[1][1];
+ } else {
+ $length = $this->actual_obj[1][1]['/Length'][1];
+ }
+
+ if ($length > 0) {
+ $c->reset($startpos + $e,$length);
+ $v = $c->buffer;
+ } else {
+ $v = '';
+ }
+ $c->reset($startpos + $e + $length + 9); // 9 = strlen("endstream")
+
+ return array(PDF_TYPE_STREAM, $v);
+
+ default :
+ if (is_numeric ($token)) {
+ // A numeric token. Make sure that
+ // it is not part of something else.
+ if (($tok2 = $this->pdf_read_token ($c)) !== false) {
+ if (is_numeric ($tok2)) {
+
+ // Two numeric tokens in a row.
+ // In this case, we're probably in
+ // front of either an object reference
+ // or an object specification.
+ // Determine the case and return the data
+ if (($tok3 = $this->pdf_read_token ($c)) !== false) {
+ switch ($tok3) {
+ case 'obj':
+ return array (PDF_TYPE_OBJDEC, (int) $token, (int) $tok2);
+ case 'R':
+ return array (PDF_TYPE_OBJREF, (int) $token, (int) $tok2);
+ }
+ // If we get to this point, that numeric value up
+ // there was just a numeric value. Push the extra
+ // tokens back into the stack and return the value.
+ array_push ($c->stack, $tok3);
+ }
+ }
+
+ array_push ($c->stack, $tok2);
+ }
+
+ if ($token === (string)((int)$token))
+ return array (PDF_TYPE_NUMERIC, (int)$token);
+ else
+ return array (PDF_TYPE_REAL, (float)$token);
+ } else if ($token == 'true' || $token == 'false') {
+ return array (PDF_TYPE_BOOLEAN, $token == 'true');
+ } else if ($token == 'null') {
+ return array (PDF_TYPE_NULL);
+ } else {
+ // Just a token. Return it.
+ return array (PDF_TYPE_TOKEN, $token);
+ }
+ }
+ }
+
+ /**
+ * Resolve an object
+ *
+ * @param object $c pdf_context
+ * @param array $obj_spec The object-data
+ * @param boolean $encapsulate Must set to true, cause the parsing and fpdi use this method only without this para
+ */
+ function pdf_resolve_object(&$c, $obj_spec, $encapsulate = true) {
+ // Exit if we get invalid data
+ if (!is_array($obj_spec)) {
+ $ret = false;
+ return $ret;
+ }
+
+ if ($obj_spec[0] == PDF_TYPE_OBJREF) {
+
+ // This is a reference, resolve it
+ if (isset($this->xref['xref'][$obj_spec[1]][$obj_spec[2]])) {
+
+ // Save current file position
+ // This is needed if you want to resolve
+ // references while you're reading another object
+ // (e.g.: if you need to determine the length
+ // of a stream)
+
+ $old_pos = ftell($c->file);
+
+ // Reposition the file pointer and
+ // load the object header.
+
+ $c->reset($this->xref['xref'][$obj_spec[1]][$obj_spec[2]]);
+
+ $header = $this->pdf_read_value($c);
+
+ if ($header[0] != PDF_TYPE_OBJDEC || $header[1] != $obj_spec[1] || $header[2] != $obj_spec[2]) {
+ $toSearchFor = $obj_spec[1] . ' ' . $obj_spec[2] . ' obj';
+ if (preg_match('/' . $toSearchFor . '/', $c->buffer)) {
+ $c->offset = strpos($c->buffer, $toSearchFor) + strlen($toSearchFor);
+ // reset stack
+ $c->stack = array();
+ } else {
+ $this->error("Unable to find object ({$obj_spec[1]}, {$obj_spec[2]}) at expected location");
+ }
+ }
+
+ // If we're being asked to store all the information
+ // about the object, we add the object ID and generation
+ // number for later use
+ $result = array();
+ $this->actual_obj =& $result;
+ if ($encapsulate) {
+ $result = array (
+ PDF_TYPE_OBJECT,
+ 'obj' => $obj_spec[1],
+ 'gen' => $obj_spec[2]
+ );
+ }
+
+ // Now simply read the object data until
+ // we encounter an end-of-object marker
+ while(1) {
+ $value = $this->pdf_read_value($c);
+ if ($value === false || count($result) > 4) {
+ // in this case the parser coudn't find an endobj so we break here
+ break;
+ }
+
+ if ($value[0] == PDF_TYPE_TOKEN && $value[1] === 'endobj') {
+ break;
+ }
+
+ $result[] = $value;
+ }
+
+ $c->reset($old_pos);
+
+ if (isset($result[2][0]) && $result[2][0] == PDF_TYPE_STREAM) {
+ $result[0] = PDF_TYPE_STREAM;
+ }
+
+ return $result;
+ }
+ } else {
+ return $obj_spec;
+ }
+ }
+
+
+
+ /**
+ * Reads a token from the file
+ *
+ * @param object $c pdf_context
+ * @return mixed
+ */
+ function pdf_read_token(&$c)
+ {
+ // If there is a token available
+ // on the stack, pop it out and
+ // return it.
+
+ if (count($c->stack)) {
+ return array_pop($c->stack);
+ }
+
+ // Strip away any whitespace
+
+ do {
+ if (!$c->ensure_content()) {
+ return false;
+ }
+ $c->offset += strspn($c->buffer, " \n\r\t", $c->offset);
+ } while ($c->offset >= $c->length - 1);
+
+ // Get the first character in the stream
+
+ $char = $c->buffer[$c->offset++];
+
+ switch ($char) {
+
+ case '[':
+ case ']':
+ case '(':
+ case ')':
+
+ // This is either an array or literal string
+ // delimiter, Return it
+
+ return $char;
+
+ case '<':
+ case '>':
+
+ // This could either be a hex string or
+ // dictionary delimiter. Determine the
+ // appropriate case and return the token
+
+ if ($c->buffer[$c->offset] == $char) {
+ if (!$c->ensure_content()) {
+ return false;
+ }
+ $c->offset++;
+ return $char . $char;
+ } else {
+ return $char;
+ }
+
+ case '%':
+
+ // This is a comment - jump over it!
+
+ $pos = $c->offset;
+ while(1) {
+ $match = preg_match("/(\r\n|\r|\n)/", $c->buffer, $m, PREG_OFFSET_CAPTURE, $pos);
+ if ($match === 0) {
+ if (!$c->increase_length()) {
+ return false;
+ } else {
+ continue;
+ }
+ }
+
+ $c->offset = $m[0][1]+strlen($m[0][0]);
+
+ return $this->pdf_read_token($c);
+ }
+
+ default:
+
+ // This is "another" type of token (probably
+ // a dictionary entry or a numeric value)
+ // Find the end and return it.
+
+ if (!$c->ensure_content()) {
+ return false;
+ }
+
+ while(1) {
+
+ // Determine the length of the token
+
+ $pos = strcspn($c->buffer, " %[]<>()\r\n\t/", $c->offset);
+
+ if ($c->offset + $pos <= $c->length - 1) {
+ break;
+ } else {
+ // If the script reaches this point,
+ // the token may span beyond the end
+ // of the current buffer. Therefore,
+ // we increase the size of the buffer
+ // and try again--just to be safe.
+
+ $c->increase_length();
+ }
+ }
+
+ $result = substr($c->buffer, $c->offset - 1, $pos + 1);
+
+ $c->offset += $pos;
+ return $result;
+ }
+ }
}
-}
\ No newline at end of file
+}