From a3bcc0957faf1e3786de13f1a4d3d307766526b0 Mon Sep 17 00:00:00 2001 From: Nicola Asuni Date: Wed, 30 Jan 2013 17:13:12 +0000 Subject: [PATCH] 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. --- CHANGELOG.TXT | 5 ++ README.TXT | 4 +- composer.json | 2 +- config/tcpdf_config.php | 5 +- config/tcpdf_config_alt.php | 7 +- examples/example_012.pdf | Bin 0 -> 13216 bytes examples/example_015.php | 30 ++++++- tcpdf.php | 156 +++++++++++++++++++++++++++--------- 8 files changed, 161 insertions(+), 48 deletions(-) create mode 100644 examples/example_012.pdf 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 0000000000000000000000000000000000000000..a8e1cfec1be5b2fd6cd2a8d6929b6322c9b6fec5 GIT binary patch literal 13216 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, 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();