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();