diff --git a/CHANGELOG.TXT b/CHANGELOG.TXT index 647962a..50b1eff 100755 --- a/CHANGELOG.TXT +++ b/CHANGELOG.TXT @@ -1,3 +1,8 @@ +5.9.204 (2013-01-23) + - The method Bookmark() was extended to include named destinations, URLs, internal links or embedded files (see example n. 15). + - automatic path calculation on configuration file was fixed. + - Error() method was extended to throw new Exception if PHP > 5. + 5.9.203 (2013-01-22) - Horizontal position of radiobuttons and checkboxes was adjusted. diff --git a/README.TXT b/README.TXT index c2eb497..51accdc 100755 --- a/README.TXT +++ b/README.TXT @@ -8,8 +8,8 @@ http://sourceforge.net/donate/index.php?group_id=128076 ------------------------------------------------------------ Name: TCPDF -Version: 5.9.203 -Release date: 2013-01-22 +Version: 5.9.204 +Release date: 2013-01-30 Author: Nicola Asuni Copyright (c) 2002-2013: diff --git a/composer.json b/composer.json index 0281074..a67b116 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "tecnick.com/tcpdf", - "version": "5.9.203", + "version": "5.9.204", "homepage": "http://www.tcpdf.org/", "type": "library", "description": "TCPDF is a PHP class for generating PDF documents.", diff --git a/config/tcpdf_config.php b/config/tcpdf_config.php index 4472d05..e47e5c8 100755 --- a/config/tcpdf_config.php +++ b/config/tcpdf_config.php @@ -2,7 +2,7 @@ //============================================================+ // File name : tcpdf_config.php // Begin : 2004-06-11 -// Last Update : 2011-04-15 +// Last Update : 2013-01-28 // // Description : Configuration file for TCPDF. // Author : Nicola Asuni - Tecnick.com LTD - Manor Coach House, Church Hill, Aldershot, Hants, GU12 4RQ, UK - www.tecnick.com - info@tecnick.com @@ -52,6 +52,9 @@ if (!defined('K_TCPDF_EXTERNAL_CONFIG')) { } } + // be sure that the end slash is present + $_SERVER['DOCUMENT_ROOT'] = str_replace('//', '/', $_SERVER['DOCUMENT_ROOT'].'/'); + // Automatic calculation for the following K_PATH_MAIN constant $k_path_main = str_replace( '\\', '/', realpath(substr(dirname(__FILE__), 0, 0-strlen('config')))); if (substr($k_path_main, -1) != '/') { diff --git a/config/tcpdf_config_alt.php b/config/tcpdf_config_alt.php index dc4f24a..8e61445 100755 --- a/config/tcpdf_config_alt.php +++ b/config/tcpdf_config_alt.php @@ -2,7 +2,7 @@ //============================================================+ // File name : tcpdf_config.php // Begin : 2004-06-11 -// Last Update : 2011-04-15 +// Last Update : 2013-01-28 // // Description : Alternative configuration file for TCPDF. // Author : Nicola Asuni - Tecnick.com LTD - Manor Coach House, Church Hill, Aldershot, Hants, GU12 4RQ, UK - www.tecnick.com - info@tecnick.com @@ -48,6 +48,9 @@ if ((!isset($_SERVER['DOCUMENT_ROOT'])) OR (empty($_SERVER['DOCUMENT_ROOT']))) { } } +// be sure that the end slash is present +$_SERVER['DOCUMENT_ROOT'] = str_replace('//', '/', $_SERVER['DOCUMENT_ROOT'].'/'); + // Automatic calculation for the following K_PATH_MAIN constant $k_path_main = str_replace( '\\', '/', realpath(substr(dirname(__FILE__), 0, 0-strlen('config')))); if (substr($k_path_main, -1) != '/') { @@ -214,7 +217,7 @@ define ('PDF_IMAGE_SCALE_RATIO', 1.25); define('HEAD_MAGNIFICATION', 1.1); /** - * height of cell respect font height + * height of cell repect font height */ define('K_CELL_HEIGHT_RATIO', 1.25); diff --git a/examples/example_012.pdf b/examples/example_012.pdf new file mode 100644 index 0000000..a8e1cfe Binary files /dev/null and b/examples/example_012.pdf differ diff --git a/examples/example_015.php b/examples/example_015.php index 5192444..6f7912f 100755 --- a/examples/example_015.php +++ b/examples/example_015.php @@ -2,7 +2,7 @@ //============================================================+ // File name : example_015.php // Begin : 2008-03-04 -// Last Update : 2011-04-15 +// Last Update : 2013-01-28 // // Description : Example 015 for TCPDF class // Bookmarks (Table of Content) @@ -108,7 +108,8 @@ $pdf->Cell(0, 10, 'Paragraph 1.3', 0, 1, 'L'); $pdf->AddPage(); // add a named destination so you can open this document at this page using the link: "example_015.pdf#chapter2" $pdf->setDestination('chapter2', 0, ''); -$pdf->Bookmark('Chapter 2', 0, 0, '', 'BI', array(128,0,0)); +// add a bookmark that points to a named destination +$pdf->Bookmark('Chapter 2', 0, 0, '', 'BI', array(128,0,0), -1, '#chapter2'); $pdf->Cell(0, 10, 'Chapter 2', 0, 1, 'L'); $pdf->SetFont('times', 'I', 14); $pdf->Write(0, 'Once saved, you can open this document at this page using the link: "example_015.pdf#chapter2".'); @@ -125,6 +126,31 @@ $pdf->SetFont('times', 'B', 20); $pdf->Bookmark('Chapter 4', 0, 0, '', 'B', array(0,64,128)); $pdf->Cell(0, 10, 'Chapter 4', 0, 1, 'L'); +$pdf->AddPage(); +$pdf->Bookmark('Chapter 5', 0, 0, '', 'B', array(0,128,0)); +$pdf->Cell(0, 10, 'Chapter 5', 0, 1, 'L'); +$txt = 'Example of File Attachment. +Double click on the icon to open the attached file.'; +$pdf->SetFont('helvetica', '', 10); +$pdf->Write(0, $txt, '', 0, 'L', true, 0, false, false, 0); + +// attach an external file TXT file +$pdf->Annotation(20, 50, 5, 5, 'TXT file', array('Subtype'=>'FileAttachment', 'Name' => 'PushPin', 'FS' => '../cache/utf8test.txt')); + +// attach an external file +$pdf->Annotation(50, 50, 5, 5, 'PDF file', array('Subtype'=>'FileAttachment', 'Name' => 'PushPin', 'FS' => 'example_012.pdf')); + +// add a bookmark that points to an embedded file +// NOTE: prefix the file name with the * character for generic file and with % character for PDF file +$pdf->Bookmark('TXT file', 0, 0, '', 'B', array(128,0,255), -1, '*utf8test.txt'); + +// add a bookmark that points to an embedded file +// NOTE: prefix the file name with the * character for generic file and with % character for PDF file +$pdf->Bookmark('PDF file', 0, 0, '', 'B', array(128,0,255), -1, '%example_012.pdf'); + +// add a bookmark that points to an external URL +$pdf->Bookmark('External URL', 0, 0, '', 'B', array(0,0,255), -1, 'http://www.tcpdf.org'); + // --------------------------------------------------------- //Close and output PDF document diff --git a/tcpdf.php b/tcpdf.php index 6afb779..4413cf5 100755 --- a/tcpdf.php +++ b/tcpdf.php @@ -1,9 +1,9 @@ * @package com.tecnick.tcpdf * @author Nicola Asuni - * @version 5.9.203 + * @version 5.9.204 */ // Main configuration file. Define the K_TCPDF_EXTERNAL_CONFIG constant to skip this file. @@ -151,7 +151,7 @@ require_once(dirname(__FILE__).'/config/tcpdf_config.php'); * TCPDF project (http://www.tcpdf.org) has been originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.
* @package com.tecnick.tcpdf * @brief PHP class for generating PDF documents without requiring external extensions. - * @version 5.9.203 + * @version 5.9.204 * @author Nicola Asuni - info@tecnick.com */ class TCPDF { @@ -162,7 +162,7 @@ class TCPDF { * Current TCPDF version. * @private */ - private $tcpdf_version = '5.9.203'; + private $tcpdf_version = '5.9.204'; // Protected properties @@ -1706,6 +1706,13 @@ class TCPDF { */ protected $n_dests; + /** + * Embedded Files Names + * @protected + * @since 5.9.204 (2013-01-23) + */ + protected $efnames = array(); + /** * Directory used for the last SVG image. * @protected @@ -3835,8 +3842,13 @@ class TCPDF { public function Error($msg) { // unset all class variables $this->_destroy(true); + $phpmainver = PHP_VERSION; // exit program and print error - die('TCPDF ERROR: '.$msg); + if (intval($phpmainver[0]) < 5) { + die('TCPDF ERROR: '.$msg); + } else { + throw new Exception('TCPDF ERROR: '.$msg); + } } /** @@ -5670,6 +5682,18 @@ class TCPDF { $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces); } + /** + * Check if the URL exist. + * @param $ur (string) URL to check. + * @return Boolean true if the URl exist, false otherwise. + * @public + * @since 5.9.204 (2013-01-28) + */ + public function isValidURL($url) { + $headers = @get_headers($url); + return (strpos($headers[0], '200') !== false); + } + /** * Puts a markup annotation on a rectangular area of the page. * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!! @@ -5747,12 +5771,12 @@ class TCPDF { if (!isset($this->PageAnnots[$page])) { $this->PageAnnots[$page] = array(); } - ++$this->n; - $this->PageAnnots[$page][] = array('n' => $this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces); + $this->PageAnnots[$page][] = array('n' => ++$this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces); if (!$this->pdfa_mode) { - if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!$this->empty_string($opt['FS'])) AND file_exists($opt['FS']) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) { - ++$this->n; - $this->embeddedfiles[basename($opt['FS'])] = array('n' => $this->n, 'file' => $opt['FS']); + if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!$this->empty_string($opt['FS'])) + AND (file_exists($opt['FS']) OR $this->isValidURL($opt['FS'])) + AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) { + $this->embeddedfiles[basename($opt['FS'])] = array('f' => ++$this->n, 'n' => ++$this->n, 'file' => $opt['FS']); } } // Add widgets annotation's icons @@ -5780,15 +5804,24 @@ class TCPDF { } reset($this->embeddedfiles); foreach ($this->embeddedfiles as $filename => $filedata) { + // update name tree + $this->efnames[$filename] = $filedata['f'].' 0 R'; + // embedded file specification object + $out = $this->_getobj($filedata['f'])."\n"; + $out .= '<_datastring($filename, $filedata['f']).' /EF <> >>'; + $out .= "\n".'endobj'; + $this->_out($out); + // embedded file object $data = file_get_contents($filedata['file']); $filter = ''; + $rawsize = strlen($data); if ($this->compress) { $data = gzcompress($data); $filter = ' /Filter /FlateDecode'; } $stream = $this->_getrawstream($data, $filedata['n']); $out = $this->_getobj($filedata['n'])."\n"; - $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' >>'; + $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <> >>'; $out .= ' stream'."\n".$stream."\n".'endstream'; $out .= "\n".'endobj'; $this->_out($out); @@ -9834,17 +9867,24 @@ class TCPDF { if ($pl['txt'][0] == '#') { // internal destination $annots .= ' /Dest /'.$this->encodeNameObject(substr($pl['txt'], 1)); + } elseif ($pl['txt'][0] == '%') { + // embedded PDF file + $filename = basename(substr($pl['txt'], 1)); + $annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>'; + } elseif ($pl['txt'][0] == '*') { + // embedded generic file + $filename = basename(substr($pl['txt'], 1)); + $jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});'; + $annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>'; } else { // external URI link $annots .= ' /A <_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>'; } - } else { - // internal link - if (isset($this->links[$pl['txt']])) { - $l = $this->links[$pl['txt']]; - if (isset($this->page_obj_id[($l[0])])) { - $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k))); - } + } elseif (isset($this->links[$pl['txt']])) { + // internal link ID + $l = $this->links[$pl['txt']]; + if (isset($this->page_obj_id[($l[0])])) { + $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k))); } } $hmodes = array('N', 'I', 'O', 'P'); @@ -9941,14 +9981,16 @@ class TCPDF { break; } $filename = basename($pl['opt']['fs']); - if (isset($this->embeddedfiles[$filename]['n'])) { - $annots .= ' /FS <_datastring($filename, $annot_obj_id).' /EF <embeddedfiles[$filename]['n'].' 0 R>> >>'; + if (isset($this->embeddedfiles[$filename]['f'])) { + $annots .= ' /FS '.$this->embeddedfiles[$filename]['f'].' 0 R'; $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag'); if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { $annots .= ' /Name /'.$pl['opt']['name']; } else { $annots .= ' /Name /PushPin'; } + // index (zero-based) of the annotation in the Annots array of this page + $this->embeddedfiles[$filename]['a'] = $key; } break; } @@ -9957,10 +9999,10 @@ class TCPDF { break; } $filename = basename($pl['opt']['fs']); - if (isset($this->embeddedfiles[$filename]['n'])) { + if (isset($this->embeddedfiles[$filename]['f'])) { // ... TO BE COMPLETED ... // /R /C /B /E /CO /CP - $annots .= ' /Sound <_datastring($filename, $annot_obj_id).' /EF <embeddedfiles[$filename]['n'].' 0 R>> >>'; + $annots .= ' /Sound '.$this->embeddedfiles[$filename]['f'].' 0 R'; $iconsapp = array('Speaker', 'Mic'); if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { $annots .= ' /Name /'.$pl['opt']['name']; @@ -12689,10 +12731,10 @@ class TCPDF { $this->_putxobjects(); $this->_putresourcedict(); $this->_putdests(); - $this->_putbookmarks(); $this->_putEmbeddedFiles(); $this->_putannotsobjs(); $this->_putjavascript(); + $this->_putbookmarks(); $this->_putencryption(); } @@ -12924,12 +12966,19 @@ class TCPDF { $out .= ' /Pages 1 0 R'; //$out .= ' /PageLabels ' //...; $out .= ' /Names <<'; - if ((!$this->pdfa_mode) AND ((!empty($this->javascript)) OR (!empty($this->js_objects)))) { - $out .= ' /JavaScript '.($this->n_js).' 0 R'; + if ((!$this->pdfa_mode) AND !empty($this->n_js)) { + $out .= ' /JavaScript '.$this->n_js; + } + if (!empty($this->efnames)) { + $out .= ' /EmbeddedFiles <efnames AS $fn => $fref) { + $out .= ' '.$this->_datastring($fn).' '.$fref; + } + $out .= ' ]>>'; } $out .= ' >>'; if (!empty($this->dests)) { - $out .= ' /Dests '.$this->n_dests.' 0 R'; + $out .= ' /Dests '.($this->n_dests).' 0 R'; } $out .= $this->_putviewerpreferences(); if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) { @@ -16880,10 +16929,12 @@ class TCPDF { * @param $page (int) Target page number (leave empty for current page). * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic. * @param $color (array) RGB color array (values from 0 to 255). + * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;). + * @param $link (mixed) URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name). * @public */ - public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0)) { - $this->Bookmark($txt, $level, $y, $page, $style, $color); + public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') { + $this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link); } /** @@ -16895,11 +16946,11 @@ class TCPDF { * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic. * @param $color (array) RGB color array (values from 0 to 255). * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;). + * @param $link (mixed) URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name). * @public - * @author Olivier Plathey, Nicola Asuni * @since 2.1.002 (2008-02-12) */ - public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1) { + public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') { if ($level < 0) { $level = 0; } @@ -16932,7 +16983,7 @@ class TCPDF { return; } } - $this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 's' => strtoupper($style), 'c' => $color); + $this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 's' => strtoupper($style), 'c' => $color, 'u' => $link); } /** @@ -17014,7 +17065,34 @@ class TCPDF { if (isset($o['last'])) { $out .= ' /Last '.($n + $o['last']).' 0 R'; } - if (isset($this->page_obj_id[($o['p'])])) { + if (isset($o['u']) AND !empty($o['u'])) { + // link + if (is_string($o['u'])) { + if ($o['u'][0] == '#') { + // internal destination + $out .= ' /Dest /'.$this->encodeNameObject(substr($o['u'], 1)); + } elseif ($o['u'][0] == '%') { + // embedded PDF file + $filename = basename(substr($o['u'], 1)); + $out .= ' /A <embeddedfiles[$filename]['a'].' >> >>'; + } elseif ($o['u'][0] == '*') { + // embedded generic file + $filename = basename(substr($o['u'], 1)); + $jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});'; + $out .= ' /A <_textstring($jsa, $oid).'>>'; + } else { + // external URI link + $out .= ' /A <_datastring($this->unhtmlentities($o['u']), $oid).'>>'; + } + } elseif (isset($this->links[$o['u']])) { + // internal link ID + $l = $this->links[$o['u']]; + if (isset($this->page_obj_id[($l[0])])) { + $out .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k))); + } + } + } elseif (isset($this->page_obj_id[($o['p'])])) { + // link to a page $out .= ' '.sprintf('/Dest [%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))); } // set font style @@ -17100,21 +17178,19 @@ class TCPDF { $jsb = "getField('tcpdfdocsaved').value='saved';"; $this->javascript = $jsa."\n".$this->javascript."\n".$jsb; } - $this->n_js = $this->_newobj(); - $out = ' << /Names ['; + // name tree for javascript + $this->n_js = '<< /Names ['; if (!empty($this->javascript)) { - $out .= ' (EmbeddedJS) '.($this->n + 1).' 0 R'; + $this->n_js .= ' (EmbeddedJS) '.($this->n + 1).' 0 R'; } if (!empty($this->js_objects)) { foreach ($this->js_objects as $key => $val) { if ($val['onload']) { - $out .= ' (JS'.$key.') '.$key.' 0 R'; + $this->n_js .= ' (JS'.$key.') '.$key.' 0 R'; } } } - $out .= ' ] >>'; - $out .= "\n".'endobj'; - $this->_out($out); + $this->n_js .= ' ] >>'; // default Javascript object if (!empty($this->javascript)) { $obj_id = $this->_newobj();