From fdc3f42c4eb4c02184d806840cedb32af0a7daa2 Mon Sep 17 00:00:00 2001 From: nicolaasuni Date: Thu, 7 Jul 2011 15:10:33 +0200 Subject: [PATCH] 5.9.101 --- CHANGELOG.TXT | 7 +- README.TXT | 6 +- examples/example_052.php | 14 ++- tcpdf.php | 228 +++++++++++++++++++++++++++++++++------ 4 files changed, 212 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.TXT b/CHANGELOG.TXT index e412586..368af52 100755 --- a/CHANGELOG.TXT +++ b/CHANGELOG.TXT @@ -1,3 +1,8 @@ +5.9.101 (2011-07-07) + - Support for JPEG and PNG ICC Color Profiles was added. + - Method addEmptySignatureAppearance() was added to add empty signature fields (see example n. 52). + - Bug item #3354332 "Strange line spacing with reduced font-size in writeHTML" was fixed. + 5.9.100 (2011-06-29) - An SVG bug has been fixed. @@ -19,7 +24,7 @@ - Font subsetting was fixed to include all parts of composite fonts. 5.9.094 (2011-06-17) - - Bug item #3317898 "Page Group numbering broken in 5.9.093" was fixed. + - Bug item #3317898 "Page Group numbering broken in 5.9.093" was fixed. 5.9.093 (2011-06-16) - Method setStartingPageNumber() was added to set starting page number (for automatic page numbering). diff --git a/README.TXT b/README.TXT index e3f7ac6..4bfd86b 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.100 -Release date: 2011-06-29 +Version: 5.9.101 +Release date: 2011-07-07 Author: Nicola Asuni Copyright (c) 2002-2011: @@ -37,7 +37,7 @@ Main Features: * images, graphic (geometric figures) and transformation methods; * supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http: www.imagemagick.org/www/formats.html) * 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, QR-Code, PDF417; - * Grayscale, RGB, CMYK, Spot Colors and Transparencies; + * JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies; * automatic page header and footer management; * document encryption up to 256 bit and digital signature certifications; * transactions to UNDO commands; diff --git a/examples/example_052.php b/examples/example_052.php index 0e2d805..9ec9964 100644 --- a/examples/example_052.php +++ b/examples/example_052.php @@ -2,7 +2,7 @@ //============================================================+ // File name : example_052.php // Begin : 2009-05-07 -// Last Update : 2010-08-08 +// Last Update : 2011-07-06 // // Description : Example 052 for TCPDF class // Certification Signature (experimental) @@ -97,13 +97,19 @@ $pdf->AddPage(); $text = 'This is a digitally signed document using the default (example) tcpdf.crt certificate.
To validate this signature you have to load the tcpdf.fdf on the Arobat Reader to add the certificate to List of Trusted Identities.

For more information check the source code of this example and the source code documentation for the setSignature() method.

www.tcpdf.org'; $pdf->writeHTML($text, true, 0, true, 0); - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // *** set signature appearance *** // create content for signature (image and/or text) -$pdf->Image($file='../images/tcpdf_signature.png', $x=180, $y=60, $w=15, $h=15, $type='PNG'); +$pdf->Image('../images/tcpdf_signature.png', 180, 60, 15, 15, 'PNG'); + // define active area for signature appearance -$pdf->setSignatureAppearance($x=180, $y=60, $w=15, $h=15); +$pdf->setSignatureAppearance(180, 60, 15, 15); + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +// *** set an empty signature appearance *** +$pdf->addEmptySignatureAppearance(180, 80, 15, 15); // --------------------------------------------------------- diff --git a/tcpdf.php b/tcpdf.php index 831d95a..c8c74e7 100755 --- a/tcpdf.php +++ b/tcpdf.php @@ -1,9 +1,9 @@ images, graphic (geometric figures) and transformation methods; *
  • supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)
  • *
  • 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, QR-Code, PDF417;
  • - *
  • Grayscale, RGB, CMYK, Spot Colors and Transparencies;
  • + *
  • JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;
  • *
  • automatic page header and footer management;
  • *
  • document encryption up to 256 bit and digital signature certifications;
  • *
  • transactions to UNDO commands;
  • @@ -134,7 +134,7 @@ * Tools to encode your unicode fonts are on fonts/utils directory.

    * @package com.tecnick.tcpdf * @author Nicola Asuni - * @version 5.9.100 + * @version 5.9.101 */ // Main configuration file. Define the K_TCPDF_EXTERNAL_CONFIG constant to skip this file. @@ -146,7 +146,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.100 + * @version 5.9.101 * @author Nicola Asuni - info@tecnick.com */ class TCPDF { @@ -157,7 +157,7 @@ class TCPDF { * Current TCPDF version. * @private */ - private $tcpdf_version = '5.9.100'; + private $tcpdf_version = '5.9.101'; // Protected properties @@ -1334,6 +1334,13 @@ class TCPDF { */ protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0'); + /** + * Array of empty digital signature appearances. + * @protected + * @since 5.9.101 (2011-07-06) + */ + protected $empty_signature_appearance = array(); + /** * Regular expression used to find blank characters (required for word-wrapping). * @protected @@ -1939,6 +1946,7 @@ class TCPDF { $this->ur['ef'] = '/Create/Delete/Modify/Import'; $this->ur['formex'] = ''; $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0'); + $this->empty_signature_appearance = array(); // set default JPEG quality $this->jpeg_quality = 75; // initialize some settings @@ -7849,16 +7857,63 @@ class TCPDF { if ($a[2] != 2) { $this->Error('Not a JPEG file: '.$file); } - if ((!isset($a['channels'])) OR ($a['channels'] == 3)) { - $colspace = 'DeviceRGB'; - } elseif ($a['channels'] == 4) { - $colspace = 'DeviceCMYK'; + // bits per pixel + $bpc = isset($a['bits']) ? intval($a['bits']) : 8; + // number of image channels + if (!isset($a['channels'])) { + $channels = 3; } else { - $colspace = 'DeviceGray'; + $channels = intval($a['channels']); } - $bpc = isset($a['bits']) ? $a['bits'] : 8; + // default colour space + switch ($channels) { + case 1: { + $colspace = 'DeviceGray'; + break; + } + case 3: { + $colspace = 'DeviceRGB'; + break; + } + case 4: { + $colspace = 'DeviceCMYK'; + break; + } + default: { + $channels = 3; + $colspace = 'DeviceRGB'; + break; + } + } + // get file content $data = file_get_contents($file); - return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data); + // check for embedded ICC profile + $icc = array(); + $offset = 0; + while (($pos = strpos($data, "ICC_PROFILE\0", $offset)) !== false) { + // get ICC sequence lenght + $length = ($this->_getUSHORT($data, ($pos - 2)) - 16); + // marker sequence number + $msn = max(1, ord($data{($pos + 12)})); + // number of markers (total of APP2 used) + $nom = max(1, ord($data{($pos + 13)})); + // get sequence segment + $icc[($msn - 1)] = substr($data, ($pos + 14), $length); + // move forward to next sequence + $offset = ($pos + 14 + $length); + } + // order and compact ICC segments + if (count($icc) > 0) { + ksort($icc); + $icc = implode('', $icc); + if ((ord($icc{36}) != 0x61) OR (ord($icc{37}) != 0x63) OR (ord($icc{38}) != 0x73) OR (ord($icc{39}) != 0x70)) { + // invalid ICC profile + $icc = false; + } + } else { + $icc = false; + } + return array('w' => $a[0], 'h' => $a[1], 'ch' => $channels, 'icc' => $icc, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data); } /** @@ -7917,25 +7972,27 @@ class TCPDF { return false; } fread($f, 4); - $parms = '/DecodeParms << /Predictor 15 /Colors '.($ct == 2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.' >>'; + $channels = ($ct == 2 ? 3 : 1); + $parms = '/DecodeParms << /Predictor 15 /Colors '.$channels.' /BitsPerComponent '.$bpc.' /Columns '.$w.' >>'; //Scan chunks looking for palette, transparency and image data $pal = ''; $trns = ''; $data = ''; + $icc = false; do { $n = $this->_freadint($f); $type = fread($f, 4); if ($type == 'PLTE') { - //Read palette + // read palette $pal = $this->rfread($f, $n); fread($f, 4); } elseif ($type == 'tRNS') { - //Read transparency info + // read transparency info $t = $this->rfread($f, $n); if ($ct == 0) { - $trns = array(ord(substr($t, 1, 1))); + $trns = array(ord($t{1})); } elseif ($ct == 2) { - $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1))); + $trns = array(ord($t{1}), ord($t{3}), ord($t{5})); } else { $pos = strpos($t, chr(0)); if ($pos !== false) { @@ -7944,9 +8001,26 @@ class TCPDF { } fread($f, 4); } elseif ($type == 'IDAT') { - //Read image data block + // read image data block $data .= $this->rfread($f, $n); fread($f, 4); + } elseif ($type == 'iCCP') { + // skip profile name and null separator + $len = 0; + while ((ord(fread($f, 1)) > 0) AND ($len < 80)) { + ++$len; + } + // get compression method + if (ord(fread($f, 1)) != 0) { + //$this->Error('Unknown filter method: '.$file); + fclose($f); + return false; + } + // read ICC Color Profile + $icc = $this->rfread($f, ($n - $len - 2)); + // decompress profile + $icc = gzuncompress($icc); + fread($f, 4); } elseif ($type == 'IEND') { break; } else { @@ -7959,7 +8033,7 @@ class TCPDF { return false; } fclose($f); - return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data); + return array('w' => $w, 'h' => $h, 'ch' => $channels, 'icc' => $icc, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data); } /** @@ -8807,6 +8881,14 @@ class TCPDF { // set reference for signature object $out .= ' '.$this->sig_obj_id.' 0 R'; } + if (!empty($this->empty_signature_appearance)) { + foreach ($this->empty_signature_appearance as $esa) { + if ($esa['page'] == $n) { + // set reference for empty signature objects + $out .= ' '.$esa['objid'].' 0 R'; + } + } + } $out .= ' ]'; return $out; } @@ -10749,13 +10831,21 @@ class TCPDF { if (array_key_exists('masked', $info)) { $out .= ' /SMask '.($this->n - 1).' 0 R'; } - if ($info['cs'] == 'Indexed') { + // set color space + $icc = false; + if (isset($info['icc']) AND ($info['icc'] !== false)) { + // ICC Colour Space + $icc = true; + $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]'; + } elseif ($info['cs'] == 'Indexed') { + // Indexed Colour Space $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]'; } else { + // Device Colour Space $out .= ' /ColorSpace /'.$info['cs']; - if ($info['cs'] == 'DeviceCMYK') { - $out .= ' /Decode [1 0 1 0 1 0 1 0]'; - } + } + if ($info['cs'] == 'DeviceCMYK') { + $out .= ' /Decode [1 0 1 0 1 0 1 0]'; } $out .= ' /BitsPerComponent '.$info['bpc']; if (isset($altoid) AND ($altoid > 0)) { @@ -10779,7 +10869,7 @@ class TCPDF { $out .= ' '.$info['parms']; } if (isset($info['trns']) AND is_array($info['trns'])) { - $trns=''; + $trns = ''; $count_info = count($info['trns']); for ($i=0; $i < $count_info; ++$i) { $trns .= $info['trns'][$i].' '.$info['trns'][$i].' '; @@ -10792,8 +10882,14 @@ class TCPDF { } $out .= "\n".'endobj'; $this->_out($out); - //Palette - if ($info['cs'] == 'Indexed') { + if ($icc) { + // ICC colour profile + $this->_newobj(); + $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc']; + $icc = $this->_getrawstream($icc); + $this->_out('<> stream'."\n".$icc."\n".'endstream'."\n".'endobj'); + } elseif ($info['cs'] == 'Indexed') { + // colour palette $this->_newobj(); $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal']; $pal = $this->_getrawstream($pal); @@ -11083,8 +11179,15 @@ class TCPDF { $out .= ' /AcroForm <<'; $objrefs = ''; if ($this->sign AND isset($this->signature_data['cert_type'])) { + // set reference for signature object $objrefs .= $this->sig_obj_id.' 0 R'; } + if (!empty($this->empty_signature_appearance)) { + foreach ($this->empty_signature_appearance as $esa) { + // set reference for empty signature objects + $objrefs .= ' '.$esa['objid'].' 0 R'; + } + } if (!empty($this->form_obj_id)) { foreach($this->form_obj_id as $objid) { $objrefs .= ' '.$objid.' 0 R'; @@ -11226,6 +11329,25 @@ class TCPDF { $this->_putheader(); $this->_putpages(); $this->_putresources(); + // empty signature fields + if (!empty($this->empty_signature_appearance)) { + foreach ($this->empty_signature_appearance as $key => $esa) { + // widget annotation for empty signature + $out = $this->_getobj($esa['objid'])."\n"; + $out .= '<< /Type /Annot'; + $out .= ' /Subtype /Widget'; + $out .= ' /Rect ['.$esa['rect'].']'; + $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page + $out .= ' /F 4'; + $out .= ' /FT /Sig'; + $signame = sprintf('Signature_%03d', ($key + 1)); + $out .= ' /T '.$this->_textstring($signame, $esa['objid']); + $out .= ' /Ff 0'; + $out .= ' >>'; + $out .= "\n".'endobj'; + $this->_out($out); + } + } // Signature if ($this->sign AND isset($this->signature_data['cert_type'])) { // widget annotation for signature @@ -11236,7 +11358,7 @@ class TCPDF { $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page $out .= ' /F 4'; $out .= ' /FT /Sig'; - $out .= ' /T '.$this->_textstring('Signature', $this->sig_obj_id); + $out .= ' /T '.$this->_textstring('Signature_000', $this->sig_obj_id); $out .= ' /Ff 0'; $out .= ' /V '.($this->sig_obj_id + 1).' 0 R'; $out .= ' >>'; @@ -16093,16 +16215,50 @@ class TCPDF { * @since 5.3.011 (2010-06-17) */ public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1) { + $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page); + } + + /** + * Add an empty digital signature appearance (a cliccable rectangle area to get signature properties) + * @param $x (float) Abscissa of the upper-left corner. + * @param $y (float) Ordinate of the upper-left corner. + * @param $w (float) Width of the signature area. + * @param $h (float) Height of the signature area. + * @param $page (int) option page number (if < 0 the current page is used). + * @public + * @author Nicola Asuni + * @since 5.9.101 (2011-07-06) + */ + public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1) { + ++$this->n; + $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page); + } + + /** + * Get the array that defines the signature appearance (page and rectangle coordinates). + * @param $x (float) Abscissa of the upper-left corner. + * @param $y (float) Ordinate of the upper-left corner. + * @param $w (float) Width of the signature area. + * @param $h (float) Height of the signature area. + * @param $page (int) option page number (if < 0 the current page is used). + * @return (array) Array defining page and rectangle coordinates of signature appearance. + * @protected + * @author Nicola Asuni + * @since 5.9.101 (2011-07-06) + */ + protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1) { + $sigapp = array(); if (($page < 1) OR ($page > $this->numpages)) { - $this->signature_appearance['page'] = $this->page; + $sigapp['page'] = $this->page; } else { - $this->signature_appearance['page'] = intval($page); + $sigapp['page'] = intval($page); } $a = $x * $this->k; - $b = $this->pagedim[($this->signature_appearance['page'])]['h'] - (($y + $h) * $this->k); + $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k); $c = $w * $this->k; $d = $h * $this->k; - $this->signature_appearance['rect'] = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b+$d); + $sigapp['rect'] = sprintf('%.2F %.2F %.2F %.2F', $a, $b, ($a + $c), ($b + $d)); + return $sigapp; } /** @@ -20102,7 +20258,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize; $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize); $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize); - if ( ($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize) + if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize) OR ($this->cell_height_ratio != $dom[$key]['line-height']) OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) { if (($key < ($maxel - 1)) AND ( @@ -20154,7 +20310,9 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $dom[$key]['line-height'] = $this->cell_height_ratio; } if (!$dom[$key]['block']) { - $this->y += (((($curfontsize * $this->cell_height_ratio ) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2; + if (!(isset($dom[($key + 1)]) AND $dom[($key + 1)]['tag'] AND (!$dom[($key + 1)]['opening']) AND ($dom[($key + 1)]['value'] != 'li') AND $dom[$key]['tag'] AND (!$dom[$key]['opening']))) { + $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2; + } if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) { $minstartliney = min($this->y, $minstartliney); $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney);