From caa068932558d1619a0158493dae4399acc9f476 Mon Sep 17 00:00:00 2001 From: Andre Cianfarani Date: Fri, 17 Feb 2006 10:05:56 +0000 Subject: [PATCH] ajout lib fpdi qui complete fpdf et permet de travailler sur des pdf en tant que modele --- htdocs/includes/fpdf/fpdf/fpdf_tpl.php | 446 ++++++++++++ htdocs/includes/fpdf/fpdf/fpdi.php | 413 ++++++++++++ htdocs/includes/fpdf/fpdf/fpdi_pdf_parser.php | 482 +++++++++++++ htdocs/includes/fpdf/fpdf/pdf_context.php | 78 +++ htdocs/includes/fpdf/fpdf/pdf_parser.php | 634 ++++++++++++++++++ .../includes/fpdf/fpdf/wrapper_functions.php | 88 +++ 6 files changed, 2141 insertions(+) create mode 100644 htdocs/includes/fpdf/fpdf/fpdf_tpl.php create mode 100644 htdocs/includes/fpdf/fpdf/fpdi.php create mode 100644 htdocs/includes/fpdf/fpdf/fpdi_pdf_parser.php create mode 100644 htdocs/includes/fpdf/fpdf/pdf_context.php create mode 100644 htdocs/includes/fpdf/fpdf/pdf_parser.php create mode 100644 htdocs/includes/fpdf/fpdf/wrapper_functions.php diff --git a/htdocs/includes/fpdf/fpdf/fpdf_tpl.php b/htdocs/includes/fpdf/fpdf/fpdf_tpl.php new file mode 100644 index 00000000000..86b74ccc492 --- /dev/null +++ b/htdocs/includes/fpdf/fpdf/fpdf_tpl.php @@ -0,0 +1,446 @@ +page <= 0) + $this->error("You have to add a page to fpdf first!"); + + // Save settings + $this->tpl++; + $this->tpls[$this->tpl]['o_x'] = $this->x; + $this->tpls[$this->tpl]['o_y'] = $this->y; + $this->tpls[$this->tpl]['o_AutoPageBreak'] = $this->AutoPageBreak; + $this->tpls[$this->tpl]['o_bMargin'] = $this->bMargin; + $this->tpls[$this->tpl]['o_tMargin'] = $this->tMargin; + $this->tpls[$this->tpl]['o_lMargin'] = $this->lMargin; + $this->tpls[$this->tpl]['o_rMargin'] = $this->rMargin; + $this->tpls[$this->tpl]['o_h'] = $this->h; + $this->tpls[$this->tpl]['o_w'] = $this->w; + + $this->SetAutoPageBreak(false); + + if ($x == null) + $x = 0; + if ($y == null) + $y = 0; + if ($w == null) + $w = $this->w; + if ($h == null) + $h = $this->h; + + // Define own high and width to calculate possitions correct + $this->h = $h; + $this->w = $w; + + $this->tpls[$this->tpl]['buffer'] = ""; + $this->tpls[$this->tpl]['x'] = $x; + $this->tpls[$this->tpl]['y'] = $y; + $this->tpls[$this->tpl]['w'] = $w; + $this->tpls[$this->tpl]['h'] = $h; + + $this->intpl = true; + $this->SetXY($x+$this->lMargin,$y+$this->tMargin); + $this->SetRightMargin($this->w-$w+$this->rMargin); + + return $this->tpl; + } + + /** + * End Template + * + * This method ends a template and reset initiated variables on beginTemplate. + * + * @return mixed If a template is opened, the ID is returned. If not a false is returned. + */ + function endTemplate() { + if ($this->intpl) { + $this->intpl = false; + $this->SetAutoPageBreak($this->tpls[$this->tpl]['o_AutoPageBreak'],$this->tpls[$this->tpl]['o_bMargin']); + $this->SetXY($this->tpls[$this->tpl]['o_x'],$this->tpls[$this->tpl]['o_y']); + $this->tMargin = $this->tpls[$this->tpl]['o_tMargin']; + $this->lMargin = $this->tpls[$this->tpl]['o_lMargin']; + $this->rMargin = $this->tpls[$this->tpl]['o_rMargin']; + $this->h = $this->tpls[$this->tpl]['o_h']; + $this->w = $this->tpls[$this->tpl]['o_w']; + return $this->tpl; + } else { + return false; + } + } + + /** + * Use a Template in current Page or other Template + * + * You can use a template in a page or in another template. + * You can give the used template a new size like you use the Image()-method. + * All parameters are optional. The width or height is calculated automaticaly + * if one is given. If no parameter is given the origin size as defined in + * beginTemplate() is used. + * The calculated or used width and height are returned as an array. + * + * @param int $tplidx A valid template-Id + * @param int $_x The x-position + * @param int $_y The y-position + * @param int $_w The new width of the template + * @param int $_h The new height of the template + * @retrun array The height and width of the template + */ + function useTemplate($tplidx, $_x=null, $_y=null, $_w=0, $_h=0) { + if ($this->page <= 0) + $this->error("You have to add a page to fpdf first!"); + + if (!$this->tpls[$tplidx]) + $this->error("Template does not exist!"); + + if ($this->intpl) { + $this->res['tpl'][$this->tpl]['tpls'][$tplidx] =& $this->tpls[$tplidx]; + } + extract($this->tpls[$tplidx]); + + if ($_x == null) + $_x = $x; + if ($_y == null) + $_y = $y; + $wh = $this->getTemplateSize($tplidx,$_w,$_h); + $_w = $wh['w']; + $_h = $wh['h']; + + $this->_out(sprintf("q %.4f 0 0 %.4f %.2f %.2f cm", ($_w/$w), ($_h/$h), $_x*$this->k, ($this->h-($_y+$_h))*$this->k)); // Translate + $this->_out($this->tplprefix.$tplidx." Do Q"); + + return array("w" => $_w, "h" => $_h); + } + + /** + * Get The calculated Size of a Template + * + * If one size is given, this method calculates the other one. + * + * @param int $tplidx A valid template-Id + * @param int $_w The width of the template + * @param int $_h The height of the template + * @return array The height and width of the template + */ + function getTemplateSize($tplidx, $_w=0, $_h=0) { + if (!$this->tpls[$tplidx]) + return false; + + extract($this->tpls[$tplidx]); + if ($_w == 0 and $_h == 0) { + $_w = $w; + $_h = $h; + } + + if($_w==0) + $_w=$_h*$w/$h; + if($_h==0) + $_h=$_w*$h/$w; + + return array("w" => $_w, "h" => $_h); + } + + /** + * See FPDF-Documentation ;-) + */ + function SetFont($family,$style='',$size=0) { + //Select a font; size given in points + global $fpdf_charwidths; + + $family=strtolower($family); + if($family=='') + $family=$this->FontFamily; + if($family=='arial') + $family='helvetica'; + elseif($family=='symbol' or $family=='zapfdingbats') + $style=''; + $style=strtoupper($style); + if(is_int(strpos($style,'U'))) + { + $this->underline=true; + $style=str_replace('U','',$style); + } + else + $this->underline=false; + if($style=='IB') + $style='BI'; + if($size==0) + $size=$this->FontSizePt; + //Test if font is already selected + if($this->FontFamily==$family and $this->FontStyle==$style and $this->FontSizePt==$size and !$this->intpl) + return; + //Test if used for the first time + $fontkey=$family.$style; + if(!isset($this->fonts[$fontkey])) + { + //Check if one of the standard fonts + if(isset($this->CoreFonts[$fontkey])) + { + if(!isset($fpdf_charwidths[$fontkey])) + { + //Load metric file + $file=$family; + if($family=='times' or $family=='helvetica') + $file.=strtolower($style); + $file.='.php'; + if(defined('FPDF_FONTPATH')) + $file=FPDF_FONTPATH.$file; + include($file); + if(!isset($fpdf_charwidths[$fontkey])) + $this->Error('Could not include font metric file'); + } + $i = $this->findNextAvailFont(); + $this->fonts[$fontkey]=array('i'=>$i,'type'=>'core','name'=>$this->CoreFonts[$fontkey],'up'=>-100,'ut'=>50,'cw'=>$fpdf_charwidths[$fontkey]); + } + else + $this->Error('Undefined font: '.$family.' '.$style); + } + //Select it + $this->FontFamily=$family; + $this->FontStyle=$style; + $this->FontSizePt=$size; + $this->FontSize=$size/$this->k; + $this->CurrentFont=&$this->fonts[$fontkey]; + if($this->page>0) + $this->_out(sprintf('BT '.$this->fontprefix.'%d %.2f Tf ET',$this->CurrentFont['i'],$this->FontSizePt)); + + + if ($this->intpl) { + $this->res['tpl'][$this->tpl]['fonts'][$fontkey] =& $this->fonts[$fontkey]; + } else { + $this->res['page'][$this->page]['fonts'][$fontkey] =& $this->fonts[$fontkey]; + } + } + + /** + * Find the next available Font-No. + * + * @return int + */ + function findNextAvailFont() { + return count($this->fonts)+1; + } + + /** + * See FPDF-Documentation ;-) + */ + function Image($file,$x,$y,$w=0,$h=0,$type='',$link='') { + parent::Image($file,$x,$y,$w,$h,$type,$link); + if ($this->intpl) { + $this->res['tpl'][$this->tpl]['images'][$file] =& $this->images[$file]; + } else { + $this->res['page'][$this->page]['images'][$file] =& $this->images[$file]; + } + } + + /** + * See FPDF-Documentation ;-) + * + * AddPage is not available when you're "in" a template. + */ + function AddPage($orientation='') { + if ($this->intpl) + $this->Error('Adding pages in templates isn\'t possible!'); + parent::AddPage($orientation); + } + + /** + * Preserve adding Links in Templates ...won't work + */ + function Link($x,$y,$w,$h,$link) { + if ($this->intpl) + $this->Error('Using links in templates aren\'t possible!'); + parent::Link($x,$y,$w,$h,$link); + } + + function AddLink() { + if ($this->intpl) + $this->Error('Adding links in templates aren\'t possible!'); + return parent::AddLink(); + } + + function SetLink($link,$y=0,$page=-1) { + if ($this->intpl) + $this->Error('Setting links in templates aren\'t possible!'); + parent::SetLink($link,$y,$page); + } + + /** + * Private Method that writes the Resources-Objects + */ + function _puttemplates() { + $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; + reset($this->tpls); + foreach($this->tpls AS $tplidx => $tpl) { + + $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer']; + $this->_newobj(); + $this->tpls[$tplidx]['n'] = $this->n; + $this->_out('<<'.$filter.'/Type /XObject'); + $this->_out('/Subtype /Form'); + $this->_out('/FormType 1'); + $this->_out(sprintf('/BBox [%.2f %.2f %.2f %.2f]',$tpl['x']*$this->k, ($tpl['h']-$tpl['y'])*$this->k, $tpl['w']*$this->k, ($tpl['h']-$tpl['y']-$tpl['h'])*$this->k)); // ($this->h-$tpl['y'])*$this->k + $this->_out('/Resources '); + + $this->_out('<res['tpl'][$tplidx]['fonts'])) { + $this->_out('/Font <<'); + foreach($this->res['tpl'][$tplidx]['fonts'] as $font) + $this->_out($this->fontprefix.$font['i'].' '.$font['n'].' 0 R'); + $this->_out('>>'); + } + if(count($this->res['tpl'][$tplidx]['images']) || count($this->res['tpl'][$tplidx]['tpls'])) + { + $this->_out('/XObject <<'); + if (count($this->res['tpl'][$tplidx]['images'])) { + foreach($this->res['tpl'][$tplidx]['images'] as $image) + $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R'); + } + if (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->_out('>>'); + + $this->_out('/Length '.strlen($p).' >>'); + $this->_putstream($p); + $this->_out('endobj'); + } + } + + /** + * Private Method + */ + function _putresources() { + $this->_putfonts(); + $this->_putimages(); + $this->_puttemplates(); + //Resource dictionary + $this->offsets[2]=strlen($this->buffer); + $this->_out('2 0 obj'); + $this->_out('<_out('/Font <<'); + foreach($this->fonts as $font) + $this->_out($this->fontprefix.$font['i'].' '.$font['n'].' 0 R'); + $this->_out('>>'); + if(count($this->images) || count($this->tpls)) + { + $this->_out('/XObject <<'); + if (count($this->images)) { + foreach($this->images as $image) + $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R'); + } + if (count($this->tpls)) { + foreach($this->tpls as $tplidx => $tpl) + $this->_out($this->tplprefix.$tplidx.' '.$tpl['n'].' 0 R'); + } + $this->_out('>>'); + } + $this->_out('>>'); + $this->_out('endobj'); + } + + + /** + * Private Method + */ + function _out($s) { + //Add a line to the document + if ($this->state==2) { + if (!$this->intpl) + $this->pages[$this->page].=$s."\n"; + else + $this->tpls[$this->tpl]['buffer'] .= $s."\n"; + } else { + $this->buffer.=$s."\n"; + } + } +} + +?> \ No newline at end of file diff --git a/htdocs/includes/fpdf/fpdf/fpdi.php b/htdocs/includes/fpdf/fpdf/fpdi.php new file mode 100644 index 00000000000..f203c6fef94 --- /dev/null +++ b/htdocs/includes/fpdf/fpdf/fpdi.php @@ -0,0 +1,413 @@ +current_filename = $filename; + $fn =& $this->current_filename; + + $this->parsers[$fn] = new fpdi_pdf_parser($fn,$this); + $this->current_parser =& $this->parsers[$fn]; + + return $this->parsers[$fn]->getPageCount(); + } + + /** + * Import a page + * + * @param int $pageno pagenumber + * @return int Index of imported page - to use with fpdf_tpl::useTemplate() + */ + function ImportPage($pageno) { + $fn =& $this->current_filename; + + $this->parsers[$fn]->setPageno($pageno); + + $this->tpl++; + $this->tpls[$this->tpl] = array(); + $this->tpls[$this->tpl]['parser'] =& $this->parsers[$fn]; + $this->tpls[$this->tpl]['resources'] = $this->parsers[$fn]->getPageResources(); + $this->tpls[$this->tpl]['buffer'] = $this->parsers[$fn]->getContent(); + // $mediabox holds the dimensions of the source page + $mediabox = $this->parsers[$fn]->getPageMediaBox($pageno); + + // To build array that can used by pdf_tpl::useTemplate() + $this->tpls[$this->tpl] = array_merge($this->tpls[$this->tpl],$mediabox); + + return $this->tpl; + } + + /** + * Private method, that rebuilds all needed objects of source files + */ + function _putOobjects() { + if (is_array($this->parsers) && count($this->parsers) > 0) { + foreach($this->parsers AS $filename => $p) { + $this->current_parser =& $this->parsers[$filename]; + if (is_array($this->obj_stack[$filename])) { + while($n = key($this->obj_stack[$filename])) { + $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); + } else { + $this->pdf_write_value ($nObj[1]); + } + + $this->_out('endobj'); + $this->obj_stack[$filename][$n] = null; // free memory + unset($this->obj_stack[$filename][$n]); + reset($this->obj_stack[$filename]); + } + } + } + } + } + + /** + * Rewritten for handling own defined PDF-Versions + * only needed by FPDF 1.52 + */ + function _begindoc() { + //Start document + $this->state=1; + } + + /** + * Sets the PDF Version to the highest of imported documents + */ + function setVersion() { + if ($this->importVersion > $this->PDFVersion) + $this->PDFVersion = $this->importVersion; + + if (!method_exists($this, '_putheader')) { + $this->buffer = '%PDF-'.$this->PDFVersion."\n".$this->buffer; + } + } + + /** + * rewritten for handling higher PDF Versions + */ + function _enddoc() { + $this->setVersion(); + parent::_enddoc(); + } + + + /** + * Put resources + */ + function _putresources() { + $this->_putfonts(); + $this->_putimages(); + $this->_puttemplates(); + $this->_putOobjects(); + + //Resource dictionary + $this->offsets[2]=strlen($this->buffer); + $this->_out('2 0 obj'); + $this->_out('<_out('/Font <<'); + foreach($this->fonts as $font) + $this->_out($this->fontprefix.$font['i'].' '.$font['n'].' 0 R'); + $this->_out('>>'); + if(count($this->images) || count($this->tpls)) + { + $this->_out('/XObject <<'); + if (count($this->images)) { + foreach($this->images as $image) + $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R'); + } + if (count($this->tpls)) { + foreach($this->tpls as $tplidx => $tpl) + $this->_out($this->tplprefix.$tplidx.' '.$tpl['n'].' 0 R'); + } + $this->_out('>>'); + } + $this->_out('>>'); + $this->_out('endobj'); + } + + /** + * Private Method that writes /XObjects - "Templates" + */ + function _puttemplates() { + $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; + reset($this->tpls); + foreach($this->tpls AS $tplidx => $tpl) { + + $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer']; + $this->_newobj(); + $this->tpls[$tplidx]['n'] = $this->n; + $this->_out('<<'.$filter.'/Type /XObject'); + $this->_out('/Subtype /Form'); + $this->_out('/FormType 1'); + $this->_out(sprintf('/BBox [%.2f %.2f %.2f %.2f]',$tpl['x']*$this->k, ($tpl['h']-$tpl['y'])*$this->k, $tpl['w']*$this->k, ($tpl['h']-$tpl['y']-$tpl['h'])*$this->k)); + $this->_out('/Resources '); + + if ($tpl['resources']) { + $this->current_parser =& $tpl['parser']; + $this->pdf_write_value($tpl['resources']); + } else { + $this->_out('<res['tpl'][$tplidx]['fonts'])) { + $this->_out('/Font <<'); + foreach($this->res['tpl'][$tplidx]['fonts'] as $font) + $this->_out($this->fontprefix.$font['i'].' '.$font['n'].' 0 R'); + $this->_out('>>'); + } + if(count($this->res['tpl'][$tplidx]['images']) || count($this->res['tpl'][$tplidx]['tpls'])) + { + $this->_out('/XObject <<'); + if (count($this->res['tpl'][$tplidx]['images'])) { + foreach($this->res['tpl'][$tplidx]['images'] as $image) + $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R'); + } + if (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->_out('>>'); + } + + $this->_out('/Length '.strlen($p).' >>'); + $this->_putstream($p); + $this->_out('endobj'); + } + } + + /** + * Rewritten to handle existing own defined objects + */ + function _newobj($obj_id=false,$onlynewobj=false) { + if (!$obj_id) { + $obj_id = ++$this->n; + } + + //Begin a new object + if (!$onlynewobj) { + $this->offsets[$obj_id]=strlen($this->buffer); + $this->_out($obj_id.' 0 obj'); + $this->current_obj_id = $obj_id; // for later use with encryption + } + + } + + /** + * Writes a value + * Needed to rebuild the source document + * + * @param mixed $value A PDF-Value. Structure of values see cases in this method + */ + function pdf_write_value(&$value) + { + + switch ($value[0]) { + + case PDF_TYPE_NUMERIC : + case PDF_TYPE_TOKEN : + // A numeric value or a token. + // Simply output them + $this->_out($value[1]." "); + break; + + case PDF_TYPE_ARRAY : + + // An array. Output the proper + // structure and move on. + + $this->_out("[",false); + for ($i = 0; $i < count($value[1]); $i++) { + $this->pdf_write_value($value[1][$i]); + } + + $this->_out("]"); + break; + + case PDF_TYPE_DICTIONARY : + + // A dictionary. + $this->_out("<<",false); + + reset ($value[1]); + + while (list($k, $v) = each($value[1])) { + $this->_out($k . " ",false); + $this->pdf_write_value($v); + } + + $this->_out(">>"); + break; + + case PDF_TYPE_OBJREF : + + // An indirect object reference + // Fill the object stack if needed + if (!isset($this->don_obj_stack[$this->current_parser->filename][$value[1]])) { + $this->_newobj(false,true); + $this->obj_stack[$this->current_parser->filename][$value[1]] = array($this->n,$value); + $this->don_obj_stack[$this->current_parser->filename][$value[1]] = array($this->n,$value); + } + $objid = $this->don_obj_stack[$this->current_parser->filename][$value[1]][0]; + + $this->_out("{$objid} 0 R"); //{$value[2]} + break; + + case PDF_TYPE_STRING : + + // A string. + $this->_out('(' . $value[1] . ')'); + + break; + + case PDF_TYPE_STREAM : + + // A stream. First, output the + // stream dictionary, then the + // stream data itself. + $this->pdf_write_value($value[1]); + $this->_out("stream"); + $this->_out($value[2][1]); + $this->_out("endstream"); + break; + case PDF_TYPE_HEX : + + $this->_out("<" . $value[1] . ">"); + break; + + case PDF_TYPE_NULL : + // The null object. + + $this->_out("null"); + break; + } + } + + + /** + * Private Method + */ + function _out($s,$ln=true) { + //Add a line to the document + if ($this->state==2) { + if (!$this->intpl) + $this->pages[$this->page].=$s.($ln == true ? "\n" : ''); + else + $this->tpls[$this->tpl]['buffer'] .= $s.($ln == true ? "\n" : ''); + } else { + $this->buffer.=$s.($ln == true ? "\n" : ''); + } + } + + /** + * close all files opened by parsers + */ + function closeParsers() { + foreach ($this->parsers as $parser){ + $parser->closeFile(); + } + } + +} + +?> \ No newline at end of file diff --git a/htdocs/includes/fpdf/fpdf/fpdi_pdf_parser.php b/htdocs/includes/fpdf/fpdf/fpdi_pdf_parser.php new file mode 100644 index 00000000000..d0016776827 --- /dev/null +++ b/htdocs/includes/fpdf/fpdf/fpdi_pdf_parser.php @@ -0,0 +1,482 @@ +fpdi =& $fpdi; + $this->filename = $filename; + + parent::pdf_parser($filename); + + // Get Info + $this->getInfo(); + + // resolve Pages-Dictonary + $pages = $this->pdf_resolve_object($this->c, $this->root[1][1]['/Pages']); + + // Read pages + $this->read_pages($this->c, $pages, $this->pages); + + // count pages; + $this->page_count = count($this->pages); + } + + /** + * Overwrite parent::error() + * + * @param string $msg Error-Message + */ + function error($msg) { + $this->fpdi->error($msg); + } + + /** + * Get pagecount from sourcefile + * + * @return int + */ + function getPageCount() { + return $this->page_count; + } + + + /** + * Set pageno + * + * @param int $pageno Pagenumber to use + */ + function setPageno($pageno) { + $pageno-=1; + + if ($pageno < 0 || $pageno >= $this->getPageCount()) { + $this->fpdi->error("Pagenumber is wrong!"); + } + + $this->pageno = $pageno; + } + + /** + * Get page-resources from current page + * + * @return array + */ + function getPageResources() { + return $this->_getPageResources($this->pages[$this->pageno]); + } + + /** + * Get page-resources from /Page + * + * @param array $obj Array of pdf-data + */ + function _getPageResources ($obj) { // $obj = /Page + $obj = $this->pdf_resolve_object($this->c, $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->pdf_resolve_object($this->c, $obj[1][1]['/Resources']); + if ($res[0] == PDF_TYPE_OBJECT) + return $res[1]; + return $res; + } else { + if (!isset ($obj[1][1]['/Parent'])) { + return false; + } else { + $res = $this->_getPageResources($obj[1][1]['/Parent']); + if ($res[0] == PDF_TYPE_OBJECT) + return $res[1]; + return $res; + } + } + } + + + function getInfo() { + $avail_infos = array("Title", "Author", "Subject", "Keywords", "Creator", "Producer", "CreationDate", "ModDate", "Trapped"); + + $_infos = $this->pdf_resolve_object($this->c,$this->xref['trailer'][1]['/Info']); + $infos = array(); + + foreach ($avail_infos AS $info) { + if (isset($_infos[1][1]["/".$info])) { + if ($_infos[1][1]["/".$info][0] == PDF_TYPE_STRING) { + $infos[$info] = $this->deescapeString($_infos[1][1]["/".$info][1]); + } else if ($_infos[1][1]["/".$info][0] == PDF_TYPE_HEX) { + $infos[$info] = $this->hex2String($_infos[1][1]["/".$info][1]); + } + } + } + $this->infos = $infos; + } + + /** + * Rebuilds a hexstring to string + * + * @param string $hex hexstring + * @return string + */ + function hex2String($hex) { + $endian = false; + + if (preg_match("/^FEFF/",$hex)) { // is utf-16 aka big endian + $i = 4; + $endian = "big"; + } else if (preg_match("/^FFFE/",$hex)) { // is utf-16 aka little endian + $i = 4; + $endian = "little"; + } else { + $i = 0; + } + + $s = ""; + $l = strlen($hex); + for (; $i < $l; $i+=2) { + if (!$endian) { + $s .= chr(hexdec($hex[$i].(isset($hex[$i+1]) ? $hex[$i+1] : '0'))); + } else { + if ($endian == "big") { + $_c = $hex[$i].$hex[$i+1]; + $i+=2; + $c = $hex[$i].$hex[$i+1]; + + if ($_c != "00") { + $s .= "?"; + continue; + } else { + $s .= chr(hexdec($c)); + continue; + } + } else if ($endian == "little") { + $c = $hex[$i].$hex[$i+1]; + $i+=2; + $_c = $hex[$i].$hex[$i+1]; + + if ($_c != "00") { + $s .= "?"; + continue; + } else { + $s .= chr(hexdec($c)); + continue; + } + } + } + } + + return $s; + } + + function deescapeString($s) { + $torepl = array("/\\\(\d{1,3})/e" => "chr(octdec(\\1))", + "/\\\\\(/" => "(", + "/\\\\\)/" => ")"); + return preg_replace(array_keys($torepl),$torepl,$s); + } + + + + /** + * Get content of current page + * + * If more /Contents is an array, the streams are concated + * + * @return string + */ + function getContent() { + $buffer = ""; + + $contents = $this->getPageContent($this->pages[$this->pageno][1][1]['/Contents']); + foreach($contents AS $tmp_content) { + $buffer .= $this->rebuildContentStream($tmp_content); + } + + return $buffer; + } + + + /** + * Resolve all content-objects + * + * @param array $content_ref + * @return array + */ + function getPageContent($content_ref) { + $contents = array(); + + if ($content_ref[0] == PDF_TYPE_OBJREF) { + $content = $this->pdf_resolve_object($this->c, $content_ref); + if ($content[1][0] == PDF_TYPE_ARRAY) { + $contents = $this->getPageContent($content[1]); + } else { + $contents[] = $content; + } + } else if ($content_ref[0] == PDF_TYPE_ARRAY) { + foreach ($content_ref[1] AS $tmp_content_ref) { + $contents = array_merge($contents,$this->getPageContent($tmp_content_ref)); + } + } + + return $contents; + } + + + /** + * Rebuild content-streams + * only non-compressed streams and /FlateDecode are ready! + * + * @param array $obj + * @return string + */ + function rebuildContentStream($obj) { + $filters = array(); + + if (isset($obj[1][1]['/Filter'])) { + $_filter = $obj[1][1]['/Filter']; + + if ($_filter[0] == PDF_TYPE_TOKEN) { + $filters[] = $_filter; + } else if ($_filter[0] == PDF_TYPE_ARRAY) { + $filters = $_filter[1]; + } + } + + $stream = $obj[2][1]; + + foreach ($filters AS $_filter) { + switch ($_filter[1]) { + case "/FlateDecode": + if (function_exists('gzuncompress')) { + $stream = @gzuncompress($stream); + } else { + $this->fpdi->error(sprintf("To handle %s filter, please compile php with zlib support.",$_filter[1])); + } + if ($stream === false) { + $this->fpdi->error("Error while decompressing string."); + } + + break; + case "/LZWDecode": + @include_once("decoders/lzw.php"); + if (class_exists("LZWDecode")) { + $lzwdec = new LZWDecode($this->fpdi); + $stream = $lzwdec->decode($stream); + } else { + $this->fpdi->error(sprintf("Unsupported Filter: %s",$_filter[1])); + } + break; + case "/ASCII85Decode": + @include_once("decoders/ascii85.php"); + if (class_exists("ASCII85Decode")) { + $ascii85 = new ASCII85Decode($this->fpdi); + $stream = $ascii85->decode(trim($stream)); + } else { + $this->fpdi->error(sprintf("Unsupported Filter: %s",$_filter[1])); + } + break; + case null: + $stream = $stream; + break; + default: + $this->fpdi->error(sprintf("Unsupported Filter: %s",$_filter[1])); + } + } + + return $stream; + } + + /** + * Get MediaBox + * + * gets an array that describes the size of a page. + * + * @param integer $pageno + * @return array @see getPageBox() + */ + function getPageMediaBox($pageno) { + return $this->getPageBox($this->pages[$pageno-1],"/MediaBox"); + } + + + /** + * Get a Box from a page + * Arrayformat is same as used by fpdf_tpl + * + * @param array $page a /Page + * @param string $box_index Type of Box @see getPageBoxes() + * @return array + */ + function getPageBox($page, $box_index) { + $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); + $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" => $b[2][1]/$this->fpdi->k, + "h" => $b[3][1]/$this->fpdi->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); + } + } + + /** + * Get all Boxes from /Page + * + * @param array a /Page + * @return array + */ + function getPageBoxes($page) { + $_boxes = array("/MediaBox","/CropBox","/BleedBox","/TrimBox","/ArtBox"); + $boxes = array(); + + foreach($_boxes AS $box) { + if ($_box = $this->getPageBox($page,$box)) { + $boxes[$box] = $_box; + } + } + + return $boxes; + } + + + /** + * Read all /Page(es) + * + * @param object pdf_context + * @param array /Pages + * @param array the result-array + */ + 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) { + $pg = $this->pdf_resolve_object ($c, $v); + #print_r($pg); + + 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); + } else { + $result[] = $pg; + } + } + } + + + + /** + * Get PDF-Version + * + * And reset the PDF Version used in FPDI if needed + */ + function getPDFVersion() { + parent::getPDFVersion(); + + if (isset($this->fpdi->importVersion) && $this->pdfVersion > $this->fpdi->importVersion) { + $this->fpdi->importVersion = $this->pdfVersion; + } + } + +} + +?> \ No newline at end of file diff --git a/htdocs/includes/fpdf/fpdf/pdf_context.php b/htdocs/includes/fpdf/fpdf/pdf_context.php new file mode 100644 index 00000000000..9e1a86ee8e4 --- /dev/null +++ b/htdocs/includes/fpdf/fpdf/pdf_context.php @@ -0,0 +1,78 @@ +file = $f; + $this->reset(); + } + + // Optionally move the file + // pointer to a new location + // and reset the buffered data + + function reset($pos = null, $l = 100) { + if (!is_null ($pos)) { + fseek ($this->file, $pos); + } + + $this->buffer = fread($this->file, $l); + $this->offset = 0; + $this->length = strlen($this->buffer); + $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 (feof($this->file)) { + return false; + } else { + $this->buffer .= fread($this->file, $l); + $this->length = strlen($this->buffer); + return true; + } + } + +} +?> \ No newline at end of file diff --git a/htdocs/includes/fpdf/fpdf/pdf_parser.php b/htdocs/includes/fpdf/fpdf/pdf_parser.php new file mode 100644 index 00000000000..b525ffdfed6 --- /dev/null +++ b/htdocs/includes/fpdf/fpdf/pdf_parser.php @@ -0,0 +1,634 @@ +filename = $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); + // Read xref-Data + $this->pdf_read_xref($this->xref, $this->pdf_find_xref()); + + // Check for Encryption + $this->getEncryption(); + + // Read root + $this->pdf_read_root(); + } + + /** + * Close the opened file + */ + function closeFile() { + if (isset($this->f)) { + fclose($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); + $this->pdfVersion = $m[0]; + } + + /** + * Find the xref-Table + */ + function pdf_find_xref() { + fseek ($this->f, -50, SEEK_END); + $data = fread($this->f, 50); + + if (!preg_match('/startxref\s*(\d+)\s*%%EOF\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 + * @param integer $start start-position in xref-table + * @param integer $end end-position in xref-table + */ + function pdf_read_xref(&$result, $offset, $start = null, $end = null) { + if (is_null ($start) || is_null ($end)) { + fseek($this->f, $o_pos = $offset); + $data = trim(fgets($this->f)); + + if ($data !== 'xref') { + fseek($this->f, $o_pos); + $data = trim(_fgets($this->f, true)); + + if ($data !== 'xref') { + $this->error("Unable to find xref table - Maybe a Problem with 'auto_detect_line_endings'"); + } + } + + $o_pos = ftell($this->f); + $data = explode(' ', trim(fgets($this->f))); + if (count($data) != 2) { + fseek($this->f, $o_pos); + $data = explode(' ', trim(_fgets($this->f, true))); + + if (count($data) != 2) + $this->error("Unexpected header in xref table"); + } + $start = $data[0]; + $end = $start + $data[1]; + } + + if (!isset($result['xref_location'])) { + $result['xref_location'] = $offset; + } + + if (!isset($result['max_object']) || $end > $result['max_object']) { + $result['max_object'] = $end; + } + + for (; $start < $end; $start++) { + $data = fread($this->f, 20); // Spezifications says: 20 bytes including newlines + $offset = substr($data, 0, 10); + $generation = substr($data, 11, 5); + + if (!isset ($result['xref'][$start][(int) $generation])) { + $result['xref'][$start][(int) $generation] = (int) $offset; + } + } + + $o_pos = ftell($this->f); + $data = fgets($this->f); + + if (preg_match("/trailer/",$data)) { + if (preg_match("/(.*trailer[ \n\r]+)/",$data,$m)) { + fseek($this->f, $o_pos+strlen($m[1])); + } + + $c =& new pdf_context($this->f); + $trailer = $this->pdf_read_value($c); + + if (isset($trailer[1]['/Prev'])) { + $this->pdf_read_xref($result, $trailer[1]['/Prev'][1]); + $result['trailer'][1] = array_merge($result['trailer'][1], $trailer[1]); + } else { + $result['trailer'] = $trailer; + } + } else { + $data = explode(' ', trim($data)); + + if (count($data) != 2) { + fseek($this->f, $o_pos); + $data = explode(' ', trim (_fgets ($this->f, true))); + + if (count($data) != 2) { + $this->error("Unexpected data in xref table"); + } + } + + $this->pdf_read_xref($result, null, (int) $data[0], (int) $data[0] + (int) $data[1]); + } + } + + + /** + * 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); + } + + 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; + } + $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; + + while(1) { + + // Start by finding the next closed + // parenthesis + + $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; + } + } + + // Make sure that there is no backslash + // before the parenthesis. If there is, + // move on. Otherwise, return the string. + + if (isset($c->buffer[$match - 1]) && $c->buffer[$match - 1] !== '\\' || + isset($c->buffer[$match - 1]) && $c->buffer[$match - 1] === '\\' && isset($c->buffer[$match - 2]) && $c->buffer[$match - 2] === '\\') { + $result = substr ($c->buffer, $c->offset, $match - $c->offset); + $c->offset = $match + 1; + return array (PDF_TYPE_STRING, $result); + } else { + $pos = $match + 1; + + if ($pos > $c->offset + $c->length) { + $c->increase_length(); + } + } + } + + 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]; + } + + $c->reset($startpos+$e,$length); + $v = $c->buffer; + $c->reset($startpos+$e+$length+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); + } + + return array (PDF_TYPE_NUMERIC, $token); + } 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,null,true); + + 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; + } + } + + + + /** + * 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", $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; + } + + 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 diff --git a/htdocs/includes/fpdf/fpdf/wrapper_functions.php b/htdocs/includes/fpdf/fpdf/wrapper_functions.php new file mode 100644 index 00000000000..bfef3f8f79b --- /dev/null +++ b/htdocs/includes/fpdf/fpdf/wrapper_functions.php @@ -0,0 +1,88 @@ + \ No newline at end of file