diff --git a/application/controllers/Labels.php b/application/controllers/Labels.php
index b161713b..4f339ca9 100644
--- a/application/controllers/Labels.php
+++ b/application/controllers/Labels.php
@@ -1,7 +1,8 @@
session->set_flashdata('error', 'Something went wrong! The label could not be generated. Check label size and font size.');
redirect('labels');
}
+ define('FPDF_FONTPATH', './src/Label/font/');
$pdf->AddPage();
+ if ($label->font == 'DejaVuSans') {
+ $pdf->AddFont($label->font,'','DejaVuSansMono.ttf',true);
+ $pdf->SetFont($label->font);
+ } else {
+ $pdf->AddFont($label->font);
+ $pdf->SetFont($label->font);
+ }
+
+
if ($result->num_rows() > 0) {
if ($label->qsos == 1) {
$this->makeOneQsoLabel($result->result(), $pdf);
diff --git a/application/migrations/124_create_label_types_table.php b/application/migrations/124_create_label_types_table.php
index 091064b3..5ce71d2d 100644
--- a/application/migrations/124_create_label_types_table.php
+++ b/application/migrations/124_create_label_types_table.php
@@ -88,6 +88,12 @@ class Migration_create_label_types_table extends CI_Migration {
'null' => TRUE,
),
+ 'font' => array(
+ 'type' => 'VARCHAR',
+ 'constraint' => '250',
+ 'null' => TRUE,
+ ),
+
'qsos' => array(
'type' => 'INT',
'constraint' => '5',
diff --git a/application/models/Labels_model.php b/application/models/Labels_model.php
index 78246ba7..5de81d42 100644
--- a/application/models/Labels_model.php
+++ b/application/models/Labels_model.php
@@ -17,6 +17,7 @@ class Labels_model extends CI_Model {
'height' => xss_clean($this->input->post('height', true)),
'font_size' => xss_clean($this->input->post('font_size', true)),
'qsos' => xss_clean($this->input->post('label_qsos', true)),
+ 'font' => xss_clean($this->input->post('font', true)),
'last_modified' => date('Y-m-d H:i:s'),
);
@@ -48,6 +49,7 @@ class Labels_model extends CI_Model {
'height' => xss_clean($this->input->post('height', true)),
'font_size' => xss_clean($this->input->post('font_size', true)),
'qsos' => xss_clean($this->input->post('label_qsos', true)),
+ 'font' => xss_clean($this->input->post('font', true)),
'last_modified' => date('Y-m-d H:i:s'),
);
diff --git a/application/views/labels/create.php b/application/views/labels/create.php
index 0fe235d4..046d9674 100644
--- a/application/views/labels/create.php
+++ b/application/views/labels/create.php
@@ -111,6 +111,29 @@
+
+
diff --git a/application/views/labels/edit.php b/application/views/labels/edit.php
index d7aa1400..076524d9 100644
--- a/application/views/labels/edit.php
+++ b/application/views/labels/edit.php
@@ -111,6 +111,29 @@
+
+
diff --git a/src/Label/PDF_Label.php b/src/Label/PDF_Label.php
index 53069120..eb93bec6 100644
--- a/src/Label/PDF_Label.php
+++ b/src/Label/PDF_Label.php
@@ -39,7 +39,7 @@
**/
namespace Cloudlog\Label;
-class PDF_Label extends fpdf {
+class PDF_Label extends tfpdf {
// Private properties
protected $_Margin_Left; // Left margin of labels
diff --git a/src/Label/font/unifont/DejaVuSans-Bold.ttf b/src/Label/font/unifont/DejaVuSans-Bold.ttf
new file mode 100644
index 00000000..6d65fa7d
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSans-Bold.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSans-BoldOblique.ttf b/src/Label/font/unifont/DejaVuSans-BoldOblique.ttf
new file mode 100644
index 00000000..753f2d80
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSans-BoldOblique.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSans-ExtraLight.ttf b/src/Label/font/unifont/DejaVuSans-ExtraLight.ttf
new file mode 100644
index 00000000..b09f32d7
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSans-ExtraLight.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSans-Oblique.ttf b/src/Label/font/unifont/DejaVuSans-Oblique.ttf
new file mode 100644
index 00000000..999bac77
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSans-Oblique.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSans.ttf b/src/Label/font/unifont/DejaVuSans.ttf
new file mode 100644
index 00000000..e5f7eecc
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSans.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSansCondensed-Bold.ttf b/src/Label/font/unifont/DejaVuSansCondensed-Bold.ttf
new file mode 100644
index 00000000..22987c62
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSansCondensed-Bold.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSansCondensed-BoldOblique.ttf b/src/Label/font/unifont/DejaVuSansCondensed-BoldOblique.ttf
new file mode 100644
index 00000000..f5fa0ca2
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSansCondensed-BoldOblique.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSansCondensed-Oblique.ttf b/src/Label/font/unifont/DejaVuSansCondensed-Oblique.ttf
new file mode 100644
index 00000000..7fde9078
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSansCondensed-Oblique.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSansCondensed.ttf b/src/Label/font/unifont/DejaVuSansCondensed.ttf
new file mode 100644
index 00000000..3259bc21
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSansCondensed.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSansMono-Bold.ttf b/src/Label/font/unifont/DejaVuSansMono-Bold.ttf
new file mode 100644
index 00000000..8184ced8
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSansMono-Bold.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSansMono-BoldOblique.ttf b/src/Label/font/unifont/DejaVuSansMono-BoldOblique.ttf
new file mode 100644
index 00000000..754dca73
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSansMono-BoldOblique.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSansMono-Oblique.ttf b/src/Label/font/unifont/DejaVuSansMono-Oblique.ttf
new file mode 100644
index 00000000..4c858d40
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSansMono-Oblique.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSansMono.ttf b/src/Label/font/unifont/DejaVuSansMono.ttf
new file mode 100644
index 00000000..f5786022
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSansMono.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSerif-Bold.ttf b/src/Label/font/unifont/DejaVuSerif-Bold.ttf
new file mode 100644
index 00000000..3bb755fa
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSerif-Bold.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSerif-BoldItalic.ttf b/src/Label/font/unifont/DejaVuSerif-BoldItalic.ttf
new file mode 100644
index 00000000..a36dd4b7
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSerif-BoldItalic.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSerif-Italic.ttf b/src/Label/font/unifont/DejaVuSerif-Italic.ttf
new file mode 100644
index 00000000..805daf22
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSerif-Italic.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSerif.ttf b/src/Label/font/unifont/DejaVuSerif.ttf
new file mode 100644
index 00000000..0b803d20
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSerif.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSerifCondensed-Bold.ttf b/src/Label/font/unifont/DejaVuSerifCondensed-Bold.ttf
new file mode 100644
index 00000000..222bf134
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSerifCondensed-Bold.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSerifCondensed-BoldItalic.ttf b/src/Label/font/unifont/DejaVuSerifCondensed-BoldItalic.ttf
new file mode 100644
index 00000000..e4466369
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSerifCondensed-BoldItalic.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSerifCondensed-Italic.ttf b/src/Label/font/unifont/DejaVuSerifCondensed-Italic.ttf
new file mode 100644
index 00000000..c529df31
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSerifCondensed-Italic.ttf differ
diff --git a/src/Label/font/unifont/DejaVuSerifCondensed.ttf b/src/Label/font/unifont/DejaVuSerifCondensed.ttf
new file mode 100644
index 00000000..d3959b32
Binary files /dev/null and b/src/Label/font/unifont/DejaVuSerifCondensed.ttf differ
diff --git a/src/Label/font/unifont/DejaVu_LICENSE.txt b/src/Label/font/unifont/DejaVu_LICENSE.txt
new file mode 100644
index 00000000..254e2cc4
--- /dev/null
+++ b/src/Label/font/unifont/DejaVu_LICENSE.txt
@@ -0,0 +1,99 @@
+Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
+Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
+
+Bitstream Vera Fonts Copyright
+------------------------------
+
+Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
+a trademark of Bitstream, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of the fonts accompanying this license ("Fonts") and associated
+documentation files (the "Font Software"), to reproduce and distribute the
+Font Software, including without limitation the rights to use, copy, merge,
+publish, distribute, and/or sell copies of the Font Software, and to permit
+persons to whom the Font Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright and trademark notices and this permission notice shall
+be included in all copies of one or more of the Font Software typefaces.
+
+The Font Software may be modified, altered, or added to, and in particular
+the designs of glyphs or characters in the Fonts may be modified and
+additional glyphs or characters may be added to the Fonts, only if the fonts
+are renamed to names not containing either the words "Bitstream" or the word
+"Vera".
+
+This License becomes null and void to the extent applicable to Fonts or Font
+Software that has been modified and is distributed under the "Bitstream
+Vera" names.
+
+The Font Software may be sold as part of a larger software package but no
+copy of one or more of the Font Software typefaces may be sold by itself.
+
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
+TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
+FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
+ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
+FONT SOFTWARE.
+
+Except as contained in this notice, the names of Gnome, the Gnome
+Foundation, and Bitstream Inc., shall not be used in advertising or
+otherwise to promote the sale, use or other dealings in this Font Software
+without prior written authorization from the Gnome Foundation or Bitstream
+Inc., respectively. For further information, contact: fonts at gnome dot
+org.
+
+Arev Fonts Copyright
+------------------------------
+
+Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the fonts accompanying this license ("Fonts") and
+associated documentation files (the "Font Software"), to reproduce
+and distribute the modifications to the Bitstream Vera Font Software,
+including without limitation the rights to use, copy, merge, publish,
+distribute, and/or sell copies of the Font Software, and to permit
+persons to whom the Font Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright and trademark notices and this permission notice
+shall be included in all copies of one or more of the Font Software
+typefaces.
+
+The Font Software may be modified, altered, or added to, and in
+particular the designs of glyphs or characters in the Fonts may be
+modified and additional glyphs or characters may be added to the
+Fonts, only if the fonts are renamed to names not containing either
+the words "Tavmjong Bah" or the word "Arev".
+
+This License becomes null and void to the extent applicable to Fonts
+or Font Software that has been modified and is distributed under the
+"Tavmjong Bah Arev" names.
+
+The Font Software may be sold as part of a larger software package but
+no copy of one or more of the Font Software typefaces may be sold by
+itself.
+
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
+TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
+
+Except as contained in this notice, the name of Tavmjong Bah shall not
+be used in advertising or otherwise to promote the sale, use or other
+dealings in this Font Software without prior written authorization
+from Tavmjong Bah. For further information, contact: tavmjong @ free
+. fr.
+
+$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $
diff --git a/src/Label/font/unifont/dejavusansmono.cw.dat b/src/Label/font/unifont/dejavusansmono.cw.dat
new file mode 100644
index 00000000..e9592b15
Binary files /dev/null and b/src/Label/font/unifont/dejavusansmono.cw.dat differ
diff --git a/src/Label/font/unifont/dejavusansmono.cw127.php b/src/Label/font/unifont/dejavusansmono.cw127.php
new file mode 100644
index 00000000..cab316c8
--- /dev/null
+++ b/src/Label/font/unifont/dejavusansmono.cw127.php
@@ -0,0 +1,107 @@
+
+ array (
+ 0 => 602,
+ 1 => 602,
+ 'interval' => true,
+ 2 => 602,
+ 3 => 602,
+ 4 => 602,
+ 5 => 602,
+ 6 => 602,
+ 7 => 602,
+ 8 => 602,
+ 9 => 602,
+ 10 => 602,
+ 11 => 602,
+ 12 => 602,
+ 13 => 602,
+ 14 => 602,
+ 15 => 602,
+ 16 => 602,
+ 17 => 602,
+ 18 => 602,
+ 19 => 602,
+ 20 => 602,
+ 21 => 602,
+ 22 => 602,
+ 23 => 602,
+ 24 => 602,
+ 25 => 602,
+ 26 => 602,
+ 27 => 602,
+ 28 => 602,
+ 29 => 602,
+ 30 => 602,
+ 31 => 602,
+ 32 => 602,
+ 33 => 602,
+ 34 => 602,
+ 35 => 602,
+ 36 => 602,
+ 37 => 602,
+ 38 => 602,
+ 39 => 602,
+ 40 => 602,
+ 41 => 602,
+ 42 => 602,
+ 43 => 602,
+ 44 => 602,
+ 45 => 602,
+ 46 => 602,
+ 47 => 602,
+ 48 => 602,
+ 49 => 602,
+ 50 => 602,
+ 51 => 602,
+ 52 => 602,
+ 53 => 602,
+ 54 => 602,
+ 55 => 602,
+ 56 => 602,
+ 57 => 602,
+ 58 => 602,
+ 59 => 602,
+ 60 => 602,
+ 61 => 602,
+ 62 => 602,
+ 63 => 602,
+ 64 => 602,
+ 65 => 602,
+ 66 => 602,
+ 67 => 602,
+ 68 => 602,
+ 69 => 602,
+ 70 => 602,
+ 71 => 602,
+ 72 => 602,
+ 73 => 602,
+ 74 => 602,
+ 75 => 602,
+ 76 => 602,
+ 77 => 602,
+ 78 => 602,
+ 79 => 602,
+ 80 => 602,
+ 81 => 602,
+ 82 => 602,
+ 83 => 602,
+ 84 => 602,
+ 85 => 602,
+ 86 => 602,
+ 87 => 602,
+ 88 => 602,
+ 89 => 602,
+ 90 => 602,
+ 91 => 602,
+ 92 => 602,
+ 93 => 602,
+ 94 => 602,
+ ),
+);
+?>
\ No newline at end of file
diff --git a/src/Label/font/unifont/dejavusansmono.mtx.php b/src/Label/font/unifont/dejavusansmono.mtx.php
new file mode 100644
index 00000000..be802332
--- /dev/null
+++ b/src/Label/font/unifont/dejavusansmono.mtx.php
@@ -0,0 +1,19 @@
+ 928.0,
+ 'Descent' => -236.0,
+ 'CapHeight' => 928.0,
+ 'Flags' => 5,
+ 'FontBBox' => '[-558 -375 718 1028]',
+ 'ItalicAngle' => 0.0,
+ 'StemV' => 87.0,
+ 'MissingWidth' => 602.0,
+);
+$up=-63;
+$ut=44;
+$ttffile='./src/Label/font/unifont/DejaVuSansMono.ttf';
+$originalsize=340712;
+$fontkey='dejavusans';
+?>
\ No newline at end of file
diff --git a/src/Label/font/unifont/ttfonts.php b/src/Label/font/unifont/ttfonts.php
new file mode 100644
index 00000000..79cade6d
--- /dev/null
+++ b/src/Label/font/unifont/ttfonts.php
@@ -0,0 +1,1090 @@
+ *
+* License: LGPL *
+* Copyright (c) Ian Back, 2010 *
+* This header must be retained in any redistribution or *
+* modification of the file. *
+* *
+*******************************************************************************/
+namespace Cloudlog\Label;
+// Define the value used in the "head" table of a created TTF file
+// 0x74727565 "true" for Mac
+// 0x00010000 for Windows
+// Either seems to work for a font embedded in a PDF file
+// when read by Adobe Reader on a Windows PC(!)
+define("_TTF_MAC_HEADER", false);
+
+
+// TrueType Font Glyph operators
+define("GF_WORDS",(1 << 0));
+define("GF_SCALE",(1 << 3));
+define("GF_MORE",(1 << 5));
+define("GF_XYSCALE",(1 << 6));
+define("GF_TWOBYTWO",(1 << 7));
+
+
+
+class TTFontFile {
+
+public $maxUni;
+public $maxUniChar;
+public $sFamilyClass;
+public $sFamilySubClass;
+public $_pos;
+public $numTables;
+public $searchRange;
+public $entrySelector;
+public $rangeShift;
+public $tables;
+public $otables;
+public $filename;
+public $fh;
+public $hmetrics;
+public $glyphPos;
+public $charToGlyph;
+public $codeToGlyph;
+public $glyphdata;
+public $ascent;
+public $descent;
+public $TTCFonts;
+public $version;
+public $name;
+public $familyName;
+public $styleName;
+public $fullName;
+public $uniqueFontID;
+public $unitsPerEm;
+public $bbox;
+public $capHeight;
+public $stemV;
+public $italicAngle;
+public $flags;
+public $underlinePosition;
+public $underlineThickness;
+public $charWidths;
+public $defaultWidth;
+public $maxStrLenRead;
+
+ function __construct() {
+ $this->maxStrLenRead = 200000; // Maximum size of glyf table to read in as string (otherwise reads each glyph from file)
+ }
+
+
+ function getMetrics($file) {
+ $this->filename = $file;
+ $this->fh = fopen($file,'rb') or die('Can\'t open file ' . $file);
+ $this->_pos = 0;
+ $this->charWidths = '';
+ $this->glyphPos = array();
+ $this->charToGlyph = array();
+ $this->tables = array();
+ $this->otables = array();
+ $this->ascent = 0;
+ $this->descent = 0;
+ $this->TTCFonts = array();
+ $this->version = $version = $this->read_ulong();
+ if ($version==0x4F54544F)
+ die("Postscript outlines are not supported");
+ if ($version==0x74746366)
+ die("ERROR - TrueType Fonts Collections not supported");
+ if (!in_array($version, array(0x00010000,0x74727565)))
+ die("Not a TrueType font: version=".$version);
+ $this->readTableDirectory();
+ $this->extractInfo();
+ fclose($this->fh);
+ }
+
+
+ function readTableDirectory() {
+ $this->numTables = $this->read_ushort();
+ $this->searchRange = $this->read_ushort();
+ $this->entrySelector = $this->read_ushort();
+ $this->rangeShift = $this->read_ushort();
+ $this->tables = array();
+ for ($i=0;$i<$this->numTables;$i++) {
+ $record = array();
+ $record['tag'] = $this->read_tag();
+ $record['checksum'] = array($this->read_ushort(),$this->read_ushort());
+ $record['offset'] = $this->read_ulong();
+ $record['length'] = $this->read_ulong();
+ $this->tables[$record['tag']] = $record;
+ }
+ }
+
+
+ function sub32($x, $y) {
+ $xlo = $x[1];
+ $xhi = $x[0];
+ $ylo = $y[1];
+ $yhi = $y[0];
+ if ($ylo > $xlo) { $xlo += 1 << 16; $yhi += 1; }
+ $reslo = $xlo-$ylo;
+ if ($yhi > $xhi) { $xhi += 1 << 16; }
+ $reshi = $xhi-$yhi;
+ $reshi = $reshi & 0xFFFF;
+ return array($reshi, $reslo);
+ }
+
+ function calcChecksum($data) {
+ if (strlen($data) % 4) { $data .= str_repeat("\0",(4-(strlen($data) % 4))); }
+ $hi=0x0000;
+ $lo=0x0000;
+ for($i=0;$i> 16;
+ $lo = $lo & 0xFFFF;
+ $hi = $hi & 0xFFFF;
+ }
+ return array($hi, $lo);
+ }
+
+ function get_table_pos($tag) {
+ $offset = $this->tables[$tag]['offset'];
+ $length = $this->tables[$tag]['length'];
+ return array($offset, $length);
+ }
+
+ function seek($pos) {
+ $this->_pos = $pos;
+ fseek($this->fh,$this->_pos);
+ }
+
+ function skip($delta) {
+ $this->_pos = $this->_pos + $delta;
+ fseek($this->fh,$this->_pos);
+ }
+
+ function seek_table($tag, $offset_in_table = 0) {
+ $tpos = $this->get_table_pos($tag);
+ $this->_pos = $tpos[0] + $offset_in_table;
+ fseek($this->fh, $this->_pos);
+ return $this->_pos;
+ }
+
+ function read_tag() {
+ $this->_pos += 4;
+ return fread($this->fh,4);
+ }
+
+ function read_short() {
+ $this->_pos += 2;
+ $s = fread($this->fh,2);
+ $a = (ord($s[0])<<8) + ord($s[1]);
+ if ($a & (1 << 15) ) { $a = ($a - (1 << 16)) ; }
+ return $a;
+ }
+
+ function unpack_short($s) {
+ $a = (ord($s[0])<<8) + ord($s[1]);
+ if ($a & (1 << 15) ) {
+ $a = ($a - (1 << 16));
+ }
+ return $a;
+ }
+
+ function read_ushort() {
+ $this->_pos += 2;
+ $s = fread($this->fh,2);
+ return (ord($s[0])<<8) + ord($s[1]);
+ }
+
+ function read_ulong() {
+ $this->_pos += 4;
+ $s = fread($this->fh,4);
+ // if large uInt32 as an integer, PHP converts it to -ve
+ return (ord($s[0])*16777216) + (ord($s[1])<<16) + (ord($s[2])<<8) + ord($s[3]); // 16777216 = 1<<24
+ }
+
+ function get_ushort($pos) {
+ fseek($this->fh,$pos);
+ $s = fread($this->fh,2);
+ return (ord($s[0])<<8) + ord($s[1]);
+ }
+
+ function get_ulong($pos) {
+ fseek($this->fh,$pos);
+ $s = fread($this->fh,4);
+ // iF large uInt32 as an integer, PHP converts it to -ve
+ return (ord($s[0])*16777216) + (ord($s[1])<<16) + (ord($s[2])<<8) + ord($s[3]); // 16777216 = 1<<24
+ }
+
+ function pack_short($val) {
+ if ($val<0) {
+ $val = abs($val);
+ $val = ~$val;
+ $val += 1;
+ }
+ return pack("n",$val);
+ }
+
+ function splice($stream, $offset, $value) {
+ return substr($stream,0,$offset) . $value . substr($stream,$offset+strlen($value));
+ }
+
+ function _set_ushort($stream, $offset, $value) {
+ $up = pack("n", $value);
+ return $this->splice($stream, $offset, $up);
+ }
+
+ function _set_short($stream, $offset, $val) {
+ if ($val<0) {
+ $val = abs($val);
+ $val = ~$val;
+ $val += 1;
+ }
+ $up = pack("n",$val);
+ return $this->splice($stream, $offset, $up);
+ }
+
+ function get_chunk($pos, $length) {
+ fseek($this->fh,$pos);
+ if ($length <1) { return ''; }
+ return (fread($this->fh,$length));
+ }
+
+ function get_table($tag) {
+ list($pos, $length) = $this->get_table_pos($tag);
+ if ($length == 0) { die('Truetype font ('.$this->filename.'): error reading table: '.$tag); }
+ fseek($this->fh,$pos);
+ return (fread($this->fh,$length));
+ }
+
+ function add($tag, $data) {
+ if ($tag == 'head') {
+ $data = $this->splice($data, 8, "\0\0\0\0");
+ }
+ $this->otables[$tag] = $data;
+ }
+
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+ function extractInfo() {
+ ///////////////////////////////////
+ // name - Naming table
+ ///////////////////////////////////
+ $this->sFamilyClass = 0;
+ $this->sFamilySubClass = 0;
+
+ $name_offset = $this->seek_table("name");
+ $format = $this->read_ushort();
+ if ($format != 0)
+ die("Unknown name table format ".$format);
+ $numRecords = $this->read_ushort();
+ $string_data_offset = $name_offset + $this->read_ushort();
+ $names = array(1=>'',2=>'',3=>'',4=>'',6=>'');
+ $K = array_keys($names);
+ $nameCount = count($names);
+ for ($i=0;$i<$numRecords; $i++) {
+ $platformId = $this->read_ushort();
+ $encodingId = $this->read_ushort();
+ $languageId = $this->read_ushort();
+ $nameId = $this->read_ushort();
+ $length = $this->read_ushort();
+ $offset = $this->read_ushort();
+ if (!in_array($nameId,$K)) continue;
+ $N = '';
+ if ($platformId == 3 && $encodingId == 1 && $languageId == 0x409) { // Microsoft, Unicode, US English, PS Name
+ $opos = $this->_pos;
+ $this->seek($string_data_offset + $offset);
+ if ($length % 2 != 0)
+ die("PostScript name is UTF-16BE string of odd length");
+ $length /= 2;
+ $N = '';
+ while ($length > 0) {
+ $char = $this->read_ushort();
+ $N .= (chr($char));
+ $length -= 1;
+ }
+ $this->_pos = $opos;
+ $this->seek($opos);
+ }
+ else if ($platformId == 1 && $encodingId == 0 && $languageId == 0) { // Macintosh, Roman, English, PS Name
+ $opos = $this->_pos;
+ $N = $this->get_chunk($string_data_offset + $offset, $length);
+ $this->_pos = $opos;
+ $this->seek($opos);
+ }
+ if ($N && $names[$nameId]=='') {
+ $names[$nameId] = $N;
+ $nameCount -= 1;
+ if ($nameCount==0) break;
+ }
+ }
+ if ($names[6])
+ $psName = $names[6];
+ else if ($names[4])
+ $psName = preg_replace('/ /','-',$names[4]);
+ else if ($names[1])
+ $psName = preg_replace('/ /','-',$names[1]);
+ else
+ $psName = '';
+ if (!$psName)
+ die("Could not find PostScript font name");
+ $this->name = $psName;
+ if ($names[1]) { $this->familyName = $names[1]; } else { $this->familyName = $psName; }
+ if ($names[2]) { $this->styleName = $names[2]; } else { $this->styleName = 'Regular'; }
+ if ($names[4]) { $this->fullName = $names[4]; } else { $this->fullName = $psName; }
+ if ($names[3]) { $this->uniqueFontID = $names[3]; } else { $this->uniqueFontID = $psName; }
+ if ($names[6]) { $this->fullName = $names[6]; }
+
+ ///////////////////////////////////
+ // head - Font header table
+ ///////////////////////////////////
+ $this->seek_table("head");
+ $this->skip(18);
+ $this->unitsPerEm = $unitsPerEm = $this->read_ushort();
+ $scale = 1000 / $unitsPerEm;
+ $this->skip(16);
+ $xMin = $this->read_short();
+ $yMin = $this->read_short();
+ $xMax = $this->read_short();
+ $yMax = $this->read_short();
+ $this->bbox = array(($xMin*$scale), ($yMin*$scale), ($xMax*$scale), ($yMax*$scale));
+ $this->skip(3*2);
+ $indexToLocFormat = $this->read_ushort();
+ $glyphDataFormat = $this->read_ushort();
+ if ($glyphDataFormat != 0)
+ die('Unknown glyph data format '.$glyphDataFormat);
+
+ ///////////////////////////////////
+ // hhea metrics table
+ ///////////////////////////////////
+ // ttf2t1 seems to use this value rather than the one in OS/2 - so put in for compatibility
+ if (isset($this->tables["hhea"])) {
+ $this->seek_table("hhea");
+ $this->skip(4);
+ $hheaAscender = $this->read_short();
+ $hheaDescender = $this->read_short();
+ $this->ascent = ($hheaAscender *$scale);
+ $this->descent = ($hheaDescender *$scale);
+ }
+
+ ///////////////////////////////////
+ // OS/2 - OS/2 and Windows metrics table
+ ///////////////////////////////////
+ if (isset($this->tables["OS/2"])) {
+ $this->seek_table("OS/2");
+ $version = $this->read_ushort();
+ $this->skip(2);
+ $usWeightClass = $this->read_ushort();
+ $this->skip(2);
+ $fsType = $this->read_ushort();
+ if ($fsType == 0x0002 || ($fsType & 0x0300) != 0) {
+ die('ERROR - Font file '.$this->filename.' cannot be embedded due to copyright restrictions.');
+ $this->restrictedUse = true;
+ }
+ $this->skip(20);
+ $sF = $this->read_short();
+ $this->sFamilyClass = ($sF >> 8);
+ $this->sFamilySubClass = ($sF & 0xFF);
+ $this->_pos += 10; //PANOSE = 10 byte length
+ $panose = fread($this->fh,10);
+ $this->skip(26);
+ $sTypoAscender = $this->read_short();
+ $sTypoDescender = $this->read_short();
+ if (!$this->ascent) $this->ascent = ($sTypoAscender*$scale);
+ if (!$this->descent) $this->descent = ($sTypoDescender*$scale);
+ if ($version > 1) {
+ $this->skip(16);
+ $sCapHeight = $this->read_short();
+ $this->capHeight = ($sCapHeight*$scale);
+ }
+ else {
+ $this->capHeight = $this->ascent;
+ }
+ }
+ else {
+ $usWeightClass = 500;
+ if (!$this->ascent) $this->ascent = ($yMax*$scale);
+ if (!$this->descent) $this->descent = ($yMin*$scale);
+ $this->capHeight = $this->ascent;
+ }
+ $this->stemV = 50 + intval(pow(($usWeightClass / 65.0),2));
+
+ ///////////////////////////////////
+ // post - PostScript table
+ ///////////////////////////////////
+ $this->seek_table("post");
+ $this->skip(4);
+ $this->italicAngle = $this->read_short() + $this->read_ushort() / 65536.0;
+ $this->underlinePosition = $this->read_short() * $scale;
+ $this->underlineThickness = $this->read_short() * $scale;
+ $isFixedPitch = $this->read_ulong();
+
+ $this->flags = 4;
+
+ if ($this->italicAngle!= 0)
+ $this->flags = $this->flags | 64;
+ if ($usWeightClass >= 600)
+ $this->flags = $this->flags | 262144;
+ if ($isFixedPitch)
+ $this->flags = $this->flags | 1;
+
+ ///////////////////////////////////
+ // hhea - Horizontal header table
+ ///////////////////////////////////
+ $this->seek_table("hhea");
+ $this->skip(32);
+ $metricDataFormat = $this->read_ushort();
+ if ($metricDataFormat != 0)
+ die('Unknown horizontal metric data format '.$metricDataFormat);
+ $numberOfHMetrics = $this->read_ushort();
+ if ($numberOfHMetrics == 0)
+ die('Number of horizontal metrics is 0');
+
+ ///////////////////////////////////
+ // maxp - Maximum profile table
+ ///////////////////////////////////
+ $this->seek_table("maxp");
+ $this->skip(4);
+ $numGlyphs = $this->read_ushort();
+
+
+ ///////////////////////////////////
+ // cmap - Character to glyph index mapping table
+ ///////////////////////////////////
+ $cmap_offset = $this->seek_table("cmap");
+ $this->skip(2);
+ $cmapTableCount = $this->read_ushort();
+ $unicode_cmap_offset = 0;
+ for ($i=0;$i<$cmapTableCount;$i++) {
+ $platformID = $this->read_ushort();
+ $encodingID = $this->read_ushort();
+ $offset = $this->read_ulong();
+ $save_pos = $this->_pos;
+ if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode
+ $format = $this->get_ushort($cmap_offset + $offset);
+ if ($format == 4) {
+ if (!$unicode_cmap_offset) $unicode_cmap_offset = $cmap_offset + $offset;
+ break;
+ }
+ }
+ $this->seek($save_pos );
+ }
+ if (!$unicode_cmap_offset)
+ die('Font ('.$this->filename .') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)');
+
+
+ $glyphToChar = array();
+ $charToGlyph = array();
+ $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph );
+
+ ///////////////////////////////////
+ // hmtx - Horizontal metrics table
+ ///////////////////////////////////
+ $this->getHMTX($numberOfHMetrics, $numGlyphs, $glyphToChar, $scale);
+
+ }
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+
+ function makeSubset($file, &$subset) {
+ $this->filename = $file;
+ $this->fh = fopen($file ,'rb') or die('Can\'t open file ' . $file);
+ $this->_pos = 0;
+ $this->charWidths = '';
+ $this->glyphPos = array();
+ $this->charToGlyph = array();
+ $this->tables = array();
+ $this->otables = array();
+ $this->ascent = 0;
+ $this->descent = 0;
+ $this->skip(4);
+ $this->maxUni = 0;
+ $this->readTableDirectory();
+
+
+ ///////////////////////////////////
+ // head - Font header table
+ ///////////////////////////////////
+ $this->seek_table("head");
+ $this->skip(50);
+ $indexToLocFormat = $this->read_ushort();
+ $glyphDataFormat = $this->read_ushort();
+
+ ///////////////////////////////////
+ // hhea - Horizontal header table
+ ///////////////////////////////////
+ $this->seek_table("hhea");
+ $this->skip(32);
+ $metricDataFormat = $this->read_ushort();
+ $orignHmetrics = $numberOfHMetrics = $this->read_ushort();
+
+ ///////////////////////////////////
+ // maxp - Maximum profile table
+ ///////////////////////////////////
+ $this->seek_table("maxp");
+ $this->skip(4);
+ $numGlyphs = $this->read_ushort();
+
+
+ ///////////////////////////////////
+ // cmap - Character to glyph index mapping table
+ ///////////////////////////////////
+ $cmap_offset = $this->seek_table("cmap");
+ $this->skip(2);
+ $cmapTableCount = $this->read_ushort();
+ $unicode_cmap_offset = 0;
+ for ($i=0;$i<$cmapTableCount;$i++) {
+ $platformID = $this->read_ushort();
+ $encodingID = $this->read_ushort();
+ $offset = $this->read_ulong();
+ $save_pos = $this->_pos;
+ if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode
+ $format = $this->get_ushort($cmap_offset + $offset);
+ if ($format == 4) {
+ $unicode_cmap_offset = $cmap_offset + $offset;
+ break;
+ }
+ }
+ $this->seek($save_pos );
+ }
+
+ if (!$unicode_cmap_offset)
+ die('Font ('.$this->filename .') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)');
+
+
+ $glyphToChar = array();
+ $charToGlyph = array();
+ $this->getCMAP4($unicode_cmap_offset, $glyphToChar, $charToGlyph );
+
+ $this->charToGlyph = $charToGlyph;
+
+ ///////////////////////////////////
+ // hmtx - Horizontal metrics table
+ ///////////////////////////////////
+ $scale = 1; // not used
+ $this->getHMTX($numberOfHMetrics, $numGlyphs, $glyphToChar, $scale);
+
+ ///////////////////////////////////
+ // loca - Index to location
+ ///////////////////////////////////
+ $this->getLOCA($indexToLocFormat, $numGlyphs);
+
+ $subsetglyphs = array(0=>0);
+ $subsetCharToGlyph = array();
+ foreach($subset AS $code) {
+ if (isset($this->charToGlyph[$code])) {
+ $subsetglyphs[$this->charToGlyph[$code]] = $code; // Old Glyph ID => Unicode
+ $subsetCharToGlyph[$code] = $this->charToGlyph[$code]; // Unicode to old GlyphID
+
+ }
+ $this->maxUni = max($this->maxUni, $code);
+ }
+
+ list($start,$dummy) = $this->get_table_pos('glyf');
+
+ $glyphSet = array();
+ ksort($subsetglyphs);
+ $n = 0;
+ $fsLastCharIndex = 0; // maximum Unicode index (character code) in this font, according to the cmap subtable for platform ID 3 and platform- specific encoding ID 0 or 1.
+ foreach($subsetglyphs AS $originalGlyphIdx => $uni) {
+ $fsLastCharIndex = max($fsLastCharIndex , $uni);
+ $glyphSet[$originalGlyphIdx] = $n; // old glyphID to new glyphID
+ $n++;
+ }
+
+ ksort($subsetCharToGlyph);
+ foreach($subsetCharToGlyph AS $uni => $originalGlyphIdx) {
+ $codeToGlyph[$uni] = $glyphSet[$originalGlyphIdx] ;
+ }
+ $this->codeToGlyph = $codeToGlyph;
+
+ ksort($subsetglyphs);
+ foreach($subsetglyphs AS $originalGlyphIdx => $uni) {
+ $this->getGlyphs($originalGlyphIdx, $start, $glyphSet, $subsetglyphs);
+ }
+
+ $numGlyphs = $numberOfHMetrics = count($subsetglyphs );
+
+ //tables copied from the original
+ $tags = array ('name');
+ foreach($tags AS $tag) { $this->add($tag, $this->get_table($tag)); }
+ $tags = array ('cvt ', 'fpgm', 'prep', 'gasp');
+ foreach($tags AS $tag) {
+ if (isset($this->tables[$tag])) { $this->add($tag, $this->get_table($tag)); }
+ }
+
+ // post - PostScript
+ $opost = $this->get_table('post');
+ $post = "\x00\x03\x00\x00" . substr($opost,4,12) . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
+ $this->add('post', $post);
+
+ // Sort CID2GID map into segments of contiguous codes
+ ksort($codeToGlyph);
+ unset($codeToGlyph[0]);
+ //unset($codeToGlyph[65535]);
+ $rangeid = 0;
+ $range = array();
+ $prevcid = -2;
+ $prevglidx = -1;
+ // for each character
+ foreach ($codeToGlyph as $cid => $glidx) {
+ if ($cid == ($prevcid + 1) && $glidx == ($prevglidx + 1)) {
+ $range[$rangeid][] = $glidx;
+ } else {
+ // new range
+ $rangeid = $cid;
+ $range[$rangeid] = array();
+ $range[$rangeid][] = $glidx;
+ }
+ $prevcid = $cid;
+ $prevglidx = $glidx;
+ }
+
+ // cmap - Character to glyph mapping - Format 4 (MS / )
+ $segCount = count($range) + 1; // + 1 Last segment has missing character 0xFFFF
+ $searchRange = 1;
+ $entrySelector = 0;
+ while ($searchRange * 2 <= $segCount ) {
+ $searchRange = $searchRange * 2;
+ $entrySelector = $entrySelector + 1;
+ }
+ $searchRange = $searchRange * 2;
+ $rangeShift = $segCount * 2 - $searchRange;
+ $length = 16 + (8*$segCount ) + ($numGlyphs+1);
+ $cmap = array(0, 1, // Index : version, number of encoding subtables
+ 3, 1, // Encoding Subtable : platform (MS=3), encoding (Unicode)
+ 0, 12, // Encoding Subtable : offset (hi,lo)
+ 4, $length, 0, // Format 4 Mapping subtable: format, length, language
+ $segCount*2,
+ $searchRange,
+ $entrySelector,
+ $rangeShift);
+
+ // endCode(s)
+ foreach($range AS $start=>$subrange) {
+ $endCode = $start + (count($subrange)-1);
+ $cmap[] = $endCode; // endCode(s)
+ }
+ $cmap[] = 0xFFFF; // endCode of last Segment
+ $cmap[] = 0; // reservedPad
+
+ // startCode(s)
+ foreach($range AS $start=>$subrange) {
+ $cmap[] = $start; // startCode(s)
+ }
+ $cmap[] = 0xFFFF; // startCode of last Segment
+ // idDelta(s)
+ foreach($range AS $start=>$subrange) {
+ $idDelta = -($start-$subrange[0]);
+ $n += count($subrange);
+ $cmap[] = $idDelta; // idDelta(s)
+ }
+ $cmap[] = 1; // idDelta of last Segment
+ // idRangeOffset(s)
+ foreach($range AS $subrange) {
+ $cmap[] = 0; // idRangeOffset[segCount] Offset in bytes to glyph indexArray, or 0
+
+ }
+ $cmap[] = 0; // idRangeOffset of last Segment
+ foreach($range AS $subrange) {
+ foreach($subrange AS $glidx) {
+ $cmap[] = $glidx;
+ }
+ }
+ $cmap[] = 0; // Mapping for last character
+ $cmapstr = '';
+ foreach($cmap AS $cm) { $cmapstr .= pack("n",$cm); }
+ $this->add('cmap', $cmapstr);
+
+
+ // glyf - Glyph data
+ list($glyfOffset,$glyfLength) = $this->get_table_pos('glyf');
+ if ($glyfLength < $this->maxStrLenRead) {
+ $glyphData = $this->get_table('glyf');
+ }
+
+ $offsets = array();
+ $glyf = '';
+ $pos = 0;
+
+ $hmtxstr = '';
+ $xMinT = 0;
+ $yMinT = 0;
+ $xMaxT = 0;
+ $yMaxT = 0;
+ $advanceWidthMax = 0;
+ $minLeftSideBearing = 0;
+ $minRightSideBearing = 0;
+ $xMaxExtent = 0;
+ $maxPoints = 0; // points in non-compound glyph
+ $maxContours = 0; // contours in non-compound glyph
+ $maxComponentPoints = 0; // points in compound glyph
+ $maxComponentContours = 0; // contours in compound glyph
+ $maxComponentElements = 0; // number of glyphs referenced at top level
+ $maxComponentDepth = 0; // levels of recursion, set to 0 if font has only simple glyphs
+ $this->glyphdata = array();
+
+ foreach($subsetglyphs AS $originalGlyphIdx => $uni) {
+ // hmtx - Horizontal Metrics
+ $hm = $this->getHMetric($orignHmetrics, $originalGlyphIdx);
+ $hmtxstr .= $hm;
+
+ $offsets[] = $pos;
+ $glyphPos = $this->glyphPos[$originalGlyphIdx];
+ $glyphLen = $this->glyphPos[$originalGlyphIdx + 1] - $glyphPos;
+ if ($glyfLength < $this->maxStrLenRead) {
+ $data = substr($glyphData,$glyphPos,$glyphLen);
+ }
+ else {
+ if ($glyphLen > 0) $data = $this->get_chunk($glyfOffset+$glyphPos,$glyphLen);
+ else $data = '';
+ }
+
+ if ($glyphLen > 0) {
+ $up = unpack("n", substr($data,0,2));
+ }
+
+ if ($glyphLen > 2 && ($up[1] & (1 << 15)) ) { // If number of contours <= -1 i.e. composite glyph
+ $pos_in_glyph = 10;
+ $flags = GF_MORE;
+ $nComponentElements = 0;
+ while ($flags & GF_MORE) {
+ $nComponentElements += 1; // number of glyphs referenced at top level
+ $up = unpack("n", substr($data,$pos_in_glyph,2));
+ $flags = $up[1];
+ $up = unpack("n", substr($data,$pos_in_glyph+2,2));
+ $glyphIdx = $up[1];
+ $this->glyphdata[$originalGlyphIdx]['compGlyphs'][] = $glyphIdx;
+ $data = $this->_set_ushort($data, $pos_in_glyph + 2, $glyphSet[$glyphIdx]);
+ $pos_in_glyph += 4;
+ if ($flags & GF_WORDS) { $pos_in_glyph += 4; }
+ else { $pos_in_glyph += 2; }
+ if ($flags & GF_SCALE) { $pos_in_glyph += 2; }
+ else if ($flags & GF_XYSCALE) { $pos_in_glyph += 4; }
+ else if ($flags & GF_TWOBYTWO) { $pos_in_glyph += 8; }
+ }
+ $maxComponentElements = max($maxComponentElements, $nComponentElements);
+ }
+
+ $glyf .= $data;
+ $pos += $glyphLen;
+ if ($pos % 4 != 0) {
+ $padding = 4 - ($pos % 4);
+ $glyf .= str_repeat("\0",$padding);
+ $pos += $padding;
+ }
+ }
+
+ $offsets[] = $pos;
+ $this->add('glyf', $glyf);
+
+ // hmtx - Horizontal Metrics
+ $this->add('hmtx', $hmtxstr);
+
+ // loca - Index to location
+ $locastr = '';
+ if ((($pos + 1) >> 1) > 0xFFFF) {
+ $indexToLocFormat = 1; // long format
+ foreach($offsets AS $offset) { $locastr .= pack("N",$offset); }
+ }
+ else {
+ $indexToLocFormat = 0; // short format
+ foreach($offsets AS $offset) { $locastr .= pack("n",($offset/2)); }
+ }
+ $this->add('loca', $locastr);
+
+ // head - Font header
+ $head = $this->get_table('head');
+ $head = $this->_set_ushort($head, 50, $indexToLocFormat);
+ $this->add('head', $head);
+
+
+ // hhea - Horizontal Header
+ $hhea = $this->get_table('hhea');
+ $hhea = $this->_set_ushort($hhea, 34, $numberOfHMetrics);
+ $this->add('hhea', $hhea);
+
+ // maxp - Maximum Profile
+ $maxp = $this->get_table('maxp');
+ $maxp = $this->_set_ushort($maxp, 4, $numGlyphs);
+ $this->add('maxp', $maxp);
+
+
+ // OS/2 - OS/2
+ $os2 = $this->get_table('OS/2');
+ $this->add('OS/2', $os2 );
+
+ fclose($this->fh);
+
+ // Put the TTF file together
+ $stm = '';
+ $this->endTTFile($stm);
+ return $stm ;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // Recursively get composite glyph data
+ function getGlyphData($originalGlyphIdx, &$maxdepth, &$depth, &$points, &$contours) {
+ $depth++;
+ $maxdepth = max($maxdepth, $depth);
+ if (count($this->glyphdata[$originalGlyphIdx]['compGlyphs'])) {
+ foreach($this->glyphdata[$originalGlyphIdx]['compGlyphs'] AS $glyphIdx) {
+ $this->getGlyphData($glyphIdx, $maxdepth, $depth, $points, $contours);
+ }
+ }
+ else if (($this->glyphdata[$originalGlyphIdx]['nContours'] > 0) && $depth > 0) { // simple
+ $contours += $this->glyphdata[$originalGlyphIdx]['nContours'];
+ $points += $this->glyphdata[$originalGlyphIdx]['nPoints'];
+ }
+ $depth--;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // Recursively get composite glyphs
+ function getGlyphs($originalGlyphIdx, &$start, &$glyphSet, &$subsetglyphs) {
+ $glyphPos = $this->glyphPos[$originalGlyphIdx];
+ $glyphLen = $this->glyphPos[$originalGlyphIdx + 1] - $glyphPos;
+ if (!$glyphLen) {
+ return;
+ }
+ $this->seek($start + $glyphPos);
+ $numberOfContours = $this->read_short();
+ if ($numberOfContours < 0) {
+ $this->skip(8);
+ $flags = GF_MORE;
+ while ($flags & GF_MORE) {
+ $flags = $this->read_ushort();
+ $glyphIdx = $this->read_ushort();
+ if (!isset($glyphSet[$glyphIdx])) {
+ $glyphSet[$glyphIdx] = count($subsetglyphs); // old glyphID to new glyphID
+ $subsetglyphs[$glyphIdx] = true;
+ }
+ $savepos = ftell($this->fh);
+ $this->getGlyphs($glyphIdx, $start, $glyphSet, $subsetglyphs);
+ $this->seek($savepos);
+ if ($flags & GF_WORDS)
+ $this->skip(4);
+ else
+ $this->skip(2);
+ if ($flags & GF_SCALE)
+ $this->skip(2);
+ else if ($flags & GF_XYSCALE)
+ $this->skip(4);
+ else if ($flags & GF_TWOBYTWO)
+ $this->skip(8);
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+
+ function getHMTX($numberOfHMetrics, $numGlyphs, &$glyphToChar, $scale) {
+ $start = $this->seek_table("hmtx");
+ $aw = 0;
+ $this->charWidths = str_pad('', 256*256*2, "\x00");
+ $nCharWidths = 0;
+ if (($numberOfHMetrics*4) < $this->maxStrLenRead) {
+ $data = $this->get_chunk($start,($numberOfHMetrics*4));
+ $arr = unpack("n*", $data);
+ }
+ else { $this->seek($start); }
+ for( $glyph=0; $glyph<$numberOfHMetrics; $glyph++) {
+
+ if (($numberOfHMetrics*4) < $this->maxStrLenRead) {
+ $aw = $arr[($glyph*2)+1];
+ }
+ else {
+ $aw = $this->read_ushort();
+ $lsb = $this->read_ushort();
+ }
+ if (isset($glyphToChar[$glyph]) || $glyph == 0) {
+
+ if ($aw >= (1 << 15) ) { $aw = 0; } // 1.03 Some (arabic) fonts have -ve values for width
+ // although should be unsigned value - comes out as e.g. 65108 (intended -50)
+ if ($glyph == 0) {
+ $this->defaultWidth = $scale*$aw;
+ continue;
+ }
+ foreach($glyphToChar[$glyph] AS $char) {
+ if ($char != 0 && $char != 65535) {
+ $w = intval(round($scale*$aw));
+ if ($w == 0) { $w = 65535; }
+ if ($char < 196608) {
+ $this->charWidths[$char*2] = chr($w >> 8);
+ $this->charWidths[$char*2 + 1] = chr($w & 0xFF);
+ $nCharWidths++;
+ }
+ }
+ }
+ }
+ }
+ $data = $this->get_chunk(($start+$numberOfHMetrics*4),($numGlyphs*2));
+ $arr = unpack("n*", $data);
+ $diff = $numGlyphs-$numberOfHMetrics;
+ for( $pos=0; $pos<$diff; $pos++) {
+ $glyph = $pos + $numberOfHMetrics;
+ if (isset($glyphToChar[$glyph])) {
+ foreach($glyphToChar[$glyph] AS $char) {
+ if ($char != 0 && $char != 65535) {
+ $w = intval(round($scale*$aw));
+ if ($w == 0) { $w = 65535; }
+ if ($char < 196608) {
+ $this->charWidths[$char*2] = chr($w >> 8);
+ $this->charWidths[$char*2 + 1] = chr($w & 0xFF);
+ $nCharWidths++;
+ }
+ }
+ }
+ }
+ }
+ // NB 65535 is a set width of 0
+ // First bytes define number of chars in font
+ $this->charWidths[0] = chr($nCharWidths >> 8);
+ $this->charWidths[1] = chr($nCharWidths & 0xFF);
+ }
+
+ function getHMetric($numberOfHMetrics, $gid) {
+ $start = $this->seek_table("hmtx");
+ if ($gid < $numberOfHMetrics) {
+ $this->seek($start+($gid*4));
+ $hm = fread($this->fh,4);
+ }
+ else {
+ $this->seek($start+(($numberOfHMetrics-1)*4));
+ $hm = fread($this->fh,2);
+ $this->seek($start+($numberOfHMetrics*2)+($gid*2));
+ $hm .= fread($this->fh,2);
+ }
+ return $hm;
+ }
+
+ function getLOCA($indexToLocFormat, $numGlyphs) {
+ $start = $this->seek_table('loca');
+ $this->glyphPos = array();
+ if ($indexToLocFormat == 0) {
+ $data = $this->get_chunk($start,($numGlyphs*2)+2);
+ $arr = unpack("n*", $data);
+ for ($n=0; $n<=$numGlyphs; $n++) {
+ $this->glyphPos[] = ($arr[$n+1] * 2);
+ }
+ }
+ else if ($indexToLocFormat == 1) {
+ $data = $this->get_chunk($start,($numGlyphs*4)+4);
+ $arr = unpack("N*", $data);
+ for ($n=0; $n<=$numGlyphs; $n++) {
+ $this->glyphPos[] = ($arr[$n+1]);
+ }
+ }
+ else
+ die('Unknown location table format '.$indexToLocFormat);
+ }
+
+
+ // CMAP Format 4
+ function getCMAP4($unicode_cmap_offset, &$glyphToChar, &$charToGlyph ) {
+ $this->maxUniChar = 0;
+ $this->seek($unicode_cmap_offset + 2);
+ $length = $this->read_ushort();
+ $limit = $unicode_cmap_offset + $length;
+ $this->skip(2);
+
+ $segCount = $this->read_ushort() / 2;
+ $this->skip(6);
+ $endCount = array();
+ for($i=0; $i<$segCount; $i++) { $endCount[] = $this->read_ushort(); }
+ $this->skip(2);
+ $startCount = array();
+ for($i=0; $i<$segCount; $i++) { $startCount[] = $this->read_ushort(); }
+ $idDelta = array();
+ for($i=0; $i<$segCount; $i++) { $idDelta[] = $this->read_short(); } // ???? was unsigned short
+ $idRangeOffset_start = $this->_pos;
+ $idRangeOffset = array();
+ for($i=0; $i<$segCount; $i++) { $idRangeOffset[] = $this->read_ushort(); }
+
+ for ($n=0;$n<$segCount;$n++) {
+ $endpoint = ($endCount[$n] + 1);
+ for ($unichar=$startCount[$n];$unichar<$endpoint;$unichar++) {
+ if ($idRangeOffset[$n] == 0)
+ $glyph = ($unichar + $idDelta[$n]) & 0xFFFF;
+ else {
+ $offset = ($unichar - $startCount[$n]) * 2 + $idRangeOffset[$n];
+ $offset = $idRangeOffset_start + 2 * $n + $offset;
+ if ($offset >= $limit)
+ $glyph = 0;
+ else {
+ $glyph = $this->get_ushort($offset);
+ if ($glyph != 0)
+ $glyph = ($glyph + $idDelta[$n]) & 0xFFFF;
+ }
+ }
+ $charToGlyph[$unichar] = $glyph;
+ if ($unichar < 196608) { $this->maxUniChar = max($unichar,$this->maxUniChar); }
+ $glyphToChar[$glyph][] = $unichar;
+ }
+ }
+ }
+
+
+ // Put the TTF file together
+ function endTTFile(&$stm) {
+ $stm = '';
+ $numTables = count($this->otables);
+ $searchRange = 1;
+ $entrySelector = 0;
+ while ($searchRange * 2 <= $numTables) {
+ $searchRange = $searchRange * 2;
+ $entrySelector = $entrySelector + 1;
+ }
+ $searchRange = $searchRange * 16;
+ $rangeShift = $numTables * 16 - $searchRange;
+
+ // Header
+ if (_TTF_MAC_HEADER) {
+ $stm .= (pack("Nnnnn", 0x74727565, $numTables, $searchRange, $entrySelector, $rangeShift)); // Mac
+ }
+ else {
+ $stm .= (pack("Nnnnn", 0x00010000 , $numTables, $searchRange, $entrySelector, $rangeShift)); // Windows
+ }
+
+ // Table directory
+ $tables = $this->otables;
+
+ ksort ($tables);
+ $offset = 12 + $numTables * 16;
+ foreach ($tables AS $tag=>$data) {
+ if ($tag == 'head') { $head_start = $offset; }
+ $stm .= $tag;
+ $checksum = $this->calcChecksum($data);
+ $stm .= pack("nn", $checksum[0],$checksum[1]);
+ $stm .= pack("NN", $offset, strlen($data));
+ $paddedLength = (strlen($data)+3)&~3;
+ $offset = $offset + $paddedLength;
+ }
+
+ // Table data
+ foreach ($tables AS $tag=>$data) {
+ $data .= "\0\0\0";
+ $stm .= substr($data,0,(strlen($data)&~3));
+ }
+
+ $checksum = $this->calcChecksum($stm);
+ $checksum = $this->sub32(array(0xB1B0,0xAFBA), $checksum);
+ $chk = pack("nn", $checksum[0],$checksum[1]);
+ $stm = $this->splice($stm,($head_start + 8),$chk);
+ return $stm ;
+ }
+
+}
+
+?>
diff --git a/src/Label/tfpdf.php b/src/Label/tfpdf.php
new file mode 100644
index 00000000..a6d6825d
--- /dev/null
+++ b/src/Label/tfpdf.php
@@ -0,0 +1,2371 @@
+ *
+* Tycho Veltmeijer (versions 1.30+) *
+* License: LGPL *
+*******************************************************************************/
+namespace Cloudlog\Label;
+class tFPDF
+{
+const VERSION = '1.33';
+protected $unifontSubset;
+protected $page; // current page number
+protected $n; // current object number
+protected $offsets; // array of object offsets
+protected $buffer; // buffer holding in-memory PDF
+protected $pages; // array containing pages
+protected $state; // current document state
+protected $compress; // compression flag
+protected $k; // scale factor (number of points in user unit)
+protected $DefOrientation; // default orientation
+protected $CurOrientation; // current orientation
+protected $StdPageSizes; // standard page sizes
+protected $DefPageSize; // default page size
+protected $CurPageSize; // current page size
+protected $CurRotation; // current page rotation
+protected $PageInfo; // page-related data
+protected $wPt, $hPt; // dimensions of current page in points
+protected $w, $h; // dimensions of current page in user unit
+protected $lMargin; // left margin
+protected $tMargin; // top margin
+protected $rMargin; // right margin
+protected $bMargin; // page break margin
+protected $cMargin; // cell margin
+protected $x, $y; // current position in user unit
+protected $lasth; // height of last printed cell
+protected $LineWidth; // line width in user unit
+protected $fontpath; // path containing fonts
+protected $CoreFonts; // array of core font names
+protected $fonts; // array of used fonts
+protected $FontFiles; // array of font files
+protected $encodings; // array of encodings
+protected $cmaps; // array of ToUnicode CMaps
+protected $FontFamily; // current font family
+protected $FontStyle; // current font style
+protected $underline; // underlining flag
+protected $CurrentFont; // current font info
+protected $FontSizePt; // current font size in points
+protected $FontSize; // current font size in user unit
+protected $DrawColor; // commands for drawing color
+protected $FillColor; // commands for filling color
+protected $TextColor; // commands for text color
+protected $ColorFlag; // indicates whether fill and text colors are different
+protected $WithAlpha; // indicates whether alpha channel is used
+protected $ws; // word spacing
+protected $images; // array of used images
+protected $PageLinks; // array of links in pages
+protected $links; // array of internal links
+protected $AutoPageBreak; // automatic page breaking
+protected $PageBreakTrigger; // threshold used to trigger page breaks
+protected $InHeader; // flag set when processing header
+protected $InFooter; // flag set when processing footer
+protected $AliasNbPages; // alias for total number of pages
+protected $ZoomMode; // zoom display mode
+protected $LayoutMode; // layout display mode
+protected $metadata; // document properties
+protected $CreationDate; // document creation date
+protected $PDFVersion; // PDF version number
+
+/*******************************************************************************
+* Public methods *
+*******************************************************************************/
+
+function __construct($orientation='P', $unit='mm', $size='A4')
+{
+ // Some checks
+ $this->_dochecks();
+ // Initialization of properties
+ $this->state = 0;
+ $this->page = 0;
+ $this->n = 2;
+ $this->buffer = '';
+ $this->pages = array();
+ $this->PageInfo = array();
+ $this->fonts = array();
+ $this->FontFiles = array();
+ $this->encodings = array();
+ $this->cmaps = array();
+ $this->images = array();
+ $this->links = array();
+ $this->InHeader = false;
+ $this->InFooter = false;
+ $this->lasth = 0;
+ $this->FontFamily = '';
+ $this->FontStyle = '';
+ $this->FontSizePt = 12;
+ $this->underline = false;
+ $this->DrawColor = '0 G';
+ $this->FillColor = '0 g';
+ $this->TextColor = '0 g';
+ $this->ColorFlag = false;
+ $this->WithAlpha = false;
+ $this->ws = 0;
+ // Font path
+ if(defined('FPDF_FONTPATH'))
+ {
+ $this->fontpath = FPDF_FONTPATH;
+ if(substr($this->fontpath,-1)!='/' && substr($this->fontpath,-1)!='\\')
+ $this->fontpath .= '/';
+ }
+ elseif(is_dir(dirname(__FILE__).'/font'))
+ $this->fontpath = dirname(__FILE__).'/font/';
+ else
+ $this->fontpath = '';
+ // Core fonts
+ $this->CoreFonts = array('courier', 'helvetica', 'times', 'symbol', 'zapfdingbats');
+ // Scale factor
+ if($unit=='pt')
+ $this->k = 1;
+ elseif($unit=='mm')
+ $this->k = 72/25.4;
+ elseif($unit=='cm')
+ $this->k = 72/2.54;
+ elseif($unit=='in')
+ $this->k = 72;
+ else
+ $this->Error('Incorrect unit: '.$unit);
+ // Page sizes
+ $this->StdPageSizes = array('a3'=>array(841.89,1190.55), 'a4'=>array(595.28,841.89), 'a5'=>array(420.94,595.28),
+ 'letter'=>array(612,792), 'legal'=>array(612,1008));
+ $size = $this->_getpagesize($size);
+ $this->DefPageSize = $size;
+ $this->CurPageSize = $size;
+ // Page orientation
+ $orientation = strtolower($orientation);
+ if($orientation=='p' || $orientation=='portrait')
+ {
+ $this->DefOrientation = 'P';
+ $this->w = $size[0];
+ $this->h = $size[1];
+ }
+ elseif($orientation=='l' || $orientation=='landscape')
+ {
+ $this->DefOrientation = 'L';
+ $this->w = $size[1];
+ $this->h = $size[0];
+ }
+ else
+ $this->Error('Incorrect orientation: '.$orientation);
+ $this->CurOrientation = $this->DefOrientation;
+ $this->wPt = $this->w*$this->k;
+ $this->hPt = $this->h*$this->k;
+ // Page rotation
+ $this->CurRotation = 0;
+ // Page margins (1 cm)
+ $margin = 28.35/$this->k;
+ $this->SetMargins($margin,$margin);
+ // Interior cell margin (1 mm)
+ $this->cMargin = $margin/10;
+ // Line width (0.2 mm)
+ $this->LineWidth = .567/$this->k;
+ // Automatic page break
+ $this->SetAutoPageBreak(true,2*$margin);
+ // Default display mode
+ $this->SetDisplayMode('default');
+ // Enable compression
+ $this->SetCompression(true);
+ // Metadata
+ $this->metadata = array('Producer'=>'tFPDF '.self::VERSION);
+ // Set default PDF version number
+ $this->PDFVersion = '1.3';
+}
+
+function SetMargins($left, $top, $right=null)
+{
+ // Set left, top and right margins
+ $this->lMargin = $left;
+ $this->tMargin = $top;
+ if($right===null)
+ $right = $left;
+ $this->rMargin = $right;
+}
+
+function SetLeftMargin($margin)
+{
+ // Set left margin
+ $this->lMargin = $margin;
+ if($this->page>0 && $this->x<$margin)
+ $this->x = $margin;
+}
+
+function SetTopMargin($margin)
+{
+ // Set top margin
+ $this->tMargin = $margin;
+}
+
+function SetRightMargin($margin)
+{
+ // Set right margin
+ $this->rMargin = $margin;
+}
+
+function SetAutoPageBreak($auto, $margin=0)
+{
+ // Set auto page break mode and triggering margin
+ $this->AutoPageBreak = $auto;
+ $this->bMargin = $margin;
+ $this->PageBreakTrigger = $this->h-$margin;
+}
+
+function SetDisplayMode($zoom, $layout='default')
+{
+ // Set display mode in viewer
+ if($zoom=='fullpage' || $zoom=='fullwidth' || $zoom=='real' || $zoom=='default' || !is_string($zoom))
+ $this->ZoomMode = $zoom;
+ else
+ $this->Error('Incorrect zoom display mode: '.$zoom);
+ if($layout=='single' || $layout=='continuous' || $layout=='two' || $layout=='default')
+ $this->LayoutMode = $layout;
+ else
+ $this->Error('Incorrect layout display mode: '.$layout);
+}
+
+function SetCompression($compress)
+{
+ // Set page compression
+ if(function_exists('gzcompress'))
+ $this->compress = $compress;
+ else
+ $this->compress = false;
+}
+
+function SetTitle($title, $isUTF8=false)
+{
+ // Title of document
+ $this->metadata['Title'] = $isUTF8 ? $title : $this->_UTF8encode($title);
+}
+
+function SetAuthor($author, $isUTF8=false)
+{
+ // Author of document
+ $this->metadata['Author'] = $isUTF8 ? $author : $this->_UTF8encode($author);
+}
+
+function SetSubject($subject, $isUTF8=false)
+{
+ // Subject of document
+ $this->metadata['Subject'] = $isUTF8 ? $subject : $this->_UTF8encode($subject);
+}
+
+function SetKeywords($keywords, $isUTF8=false)
+{
+ // Keywords of document
+ $this->metadata['Keywords'] = $isUTF8 ? $keywords : $this->_UTF8encode($keywords);
+}
+
+function SetCreator($creator, $isUTF8=false)
+{
+ // Creator of document
+ $this->metadata['Creator'] = $isUTF8 ? $creator : $this->_UTF8encode($creator);
+}
+
+function AliasNbPages($alias='{nb}')
+{
+ // Define an alias for total number of pages
+ $this->AliasNbPages = $alias;
+}
+
+function Error($msg)
+{
+ // Fatal error
+ throw new Exception('tFPDF error: '.$msg);
+}
+
+function Close()
+{
+ // Terminate document
+ if($this->state==3)
+ return;
+ if($this->page==0)
+ $this->AddPage();
+ // Page footer
+ $this->InFooter = true;
+ $this->Footer();
+ $this->InFooter = false;
+ // Close page
+ $this->_endpage();
+ // Close document
+ $this->_enddoc();
+}
+
+function AddPage($orientation='', $size='', $rotation=0)
+{
+ // Start a new page
+ if($this->state==3)
+ $this->Error('The document is closed');
+ $family = $this->FontFamily;
+ $style = $this->FontStyle.($this->underline ? 'U' : '');
+ $fontsize = $this->FontSizePt;
+ $lw = $this->LineWidth;
+ $dc = $this->DrawColor;
+ $fc = $this->FillColor;
+ $tc = $this->TextColor;
+ $cf = $this->ColorFlag;
+ if($this->page>0)
+ {
+ // Page footer
+ $this->InFooter = true;
+ $this->Footer();
+ $this->InFooter = false;
+ // Close page
+ $this->_endpage();
+ }
+ // Start new page
+ $this->_beginpage($orientation,$size,$rotation);
+ // Set line cap style to square
+ $this->_out('2 J');
+ // Set line width
+ $this->LineWidth = $lw;
+ $this->_out(sprintf('%.2F w',$lw*$this->k));
+ // Set font
+ if($family)
+ $this->SetFont($family,$style,$fontsize);
+ // Set colors
+ $this->DrawColor = $dc;
+ if($dc!='0 G')
+ $this->_out($dc);
+ $this->FillColor = $fc;
+ if($fc!='0 g')
+ $this->_out($fc);
+ $this->TextColor = $tc;
+ $this->ColorFlag = $cf;
+ // Page header
+ $this->InHeader = true;
+ $this->Header();
+ $this->InHeader = false;
+ // Restore line width
+ if($this->LineWidth!=$lw)
+ {
+ $this->LineWidth = $lw;
+ $this->_out(sprintf('%.2F w',$lw*$this->k));
+ }
+ // Restore font
+ if($family)
+ $this->SetFont($family,$style,$fontsize);
+ // Restore colors
+ if($this->DrawColor!=$dc)
+ {
+ $this->DrawColor = $dc;
+ $this->_out($dc);
+ }
+ if($this->FillColor!=$fc)
+ {
+ $this->FillColor = $fc;
+ $this->_out($fc);
+ }
+ $this->TextColor = $tc;
+ $this->ColorFlag = $cf;
+}
+
+function Header()
+{
+ // To be implemented in your own inherited class
+}
+
+function Footer()
+{
+ // To be implemented in your own inherited class
+}
+
+function PageNo()
+{
+ // Get current page number
+ return $this->page;
+}
+
+function SetDrawColor($r, $g=null, $b=null)
+{
+ // Set color for all stroking operations
+ if(($r==0 && $g==0 && $b==0) || $g===null)
+ $this->DrawColor = sprintf('%.3F G',$r/255);
+ else
+ $this->DrawColor = sprintf('%.3F %.3F %.3F RG',$r/255,$g/255,$b/255);
+ if($this->page>0)
+ $this->_out($this->DrawColor);
+}
+
+function SetFillColor($r, $g=null, $b=null)
+{
+ // Set color for all filling operations
+ if(($r==0 && $g==0 && $b==0) || $g===null)
+ $this->FillColor = sprintf('%.3F g',$r/255);
+ else
+ $this->FillColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255);
+ $this->ColorFlag = ($this->FillColor!=$this->TextColor);
+ if($this->page>0)
+ $this->_out($this->FillColor);
+}
+
+function SetTextColor($r, $g=null, $b=null)
+{
+ // Set color for text
+ if(($r==0 && $g==0 && $b==0) || $g===null)
+ $this->TextColor = sprintf('%.3F g',$r/255);
+ else
+ $this->TextColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255);
+ $this->ColorFlag = ($this->FillColor!=$this->TextColor);
+}
+
+function GetStringWidth($s)
+{
+ // Get width of a string in the current font
+ $s = (string)$s;
+ $cw = $this->CurrentFont['cw'];
+ $w=0;
+ if ($this->unifontSubset) {
+ $unicode = $this->UTF8StringToArray($s);
+ foreach($unicode as $char) {
+ if (isset($cw[2*$char])) { $w += (ord($cw[2*$char])<<8) + ord($cw[2*$char+1]); }
+ else if($char>0 && $char<128 && isset($cw[chr($char)])) { $w += $cw[chr($char)]; }
+ else if(isset($this->CurrentFont['desc']['MissingWidth'])) { $w += $this->CurrentFont['desc']['MissingWidth']; }
+ else if(isset($this->CurrentFont['MissingWidth'])) { $w += $this->CurrentFont['MissingWidth']; }
+ else { $w += 500; }
+ }
+ }
+ else {
+ $l = strlen($s);
+ for($i=0;$i<$l;$i++)
+ $w += $cw[$s[$i]];
+ }
+ return $w*$this->FontSize/1000;
+}
+
+function SetLineWidth($width)
+{
+ // Set line width
+ $this->LineWidth = $width;
+ if($this->page>0)
+ $this->_out(sprintf('%.2F w',$width*$this->k));
+}
+
+function Line($x1, $y1, $x2, $y2)
+{
+ // Draw a line
+ $this->_out(sprintf('%.2F %.2F m %.2F %.2F l S',$x1*$this->k,($this->h-$y1)*$this->k,$x2*$this->k,($this->h-$y2)*$this->k));
+}
+
+function Rect($x, $y, $w, $h, $style='')
+{
+ // Draw a rectangle
+ if($style=='F')
+ $op = 'f';
+ elseif($style=='FD' || $style=='DF')
+ $op = 'B';
+ else
+ $op = 'S';
+ $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op));
+}
+
+function AddFont($family, $style='', $file='', $uni=false)
+{
+ // Add a TrueType, OpenType or Type1 font
+ $family = strtolower($family);
+ $style = strtoupper($style);
+ if($style=='IB')
+ $style = 'BI';
+ if($file=='') {
+ if ($uni) {
+ $file = str_replace(' ','',$family).strtolower($style).'.ttf';
+ }
+ else {
+ $file = str_replace(' ','',$family).strtolower($style).'.php';
+ }
+ }
+ $fontkey = $family.$style;
+ if(isset($this->fonts[$fontkey]))
+ return;
+ if ($uni) {
+ if (defined("_SYSTEM_TTFONTS") && file_exists(_SYSTEM_TTFONTS.$file )) { $ttffilename = _SYSTEM_TTFONTS.$file ; }
+ else { $ttffilename = $this->fontpath.'unifont/'.$file ; }
+ $unifilename = $this->fontpath.'unifont/'.strtolower(substr($file ,0,(strpos($file ,'.'))));
+ $name = '';
+ $originalsize = 0;
+ $ttfstat = stat($ttffilename);
+ if (file_exists($unifilename.'.mtx.php')) {
+ include($unifilename.'.mtx.php');
+ }
+ if (!isset($type) || !isset($name) || $originalsize != $ttfstat['size']) {
+ $ttffile = $ttffilename;
+ //require_once($this->fontpath.'unifont/ttfonts.php');
+ $ttf = new TTFontFile();
+ $ttf->getMetrics($ttffile);
+ $cw = $ttf->charWidths;
+ $name = preg_replace('/[ ()]/','',$ttf->fullName);
+
+ $desc= array('Ascent'=>round($ttf->ascent),
+ 'Descent'=>round($ttf->descent),
+ 'CapHeight'=>round($ttf->capHeight),
+ 'Flags'=>$ttf->flags,
+ 'FontBBox'=>'['.round($ttf->bbox[0])." ".round($ttf->bbox[1])." ".round($ttf->bbox[2])." ".round($ttf->bbox[3]).']',
+ 'ItalicAngle'=>$ttf->italicAngle,
+ 'StemV'=>round($ttf->stemV),
+ 'MissingWidth'=>round($ttf->defaultWidth));
+ $up = round($ttf->underlinePosition);
+ $ut = round($ttf->underlineThickness);
+ $originalsize = $ttfstat['size']+0;
+ $type = 'TTF';
+ // Generate metrics .php file
+ $s='";
+ if (is_writable(dirname($this->fontpath.'unifont/'.'x'))) {
+ $fh = fopen($unifilename.'.mtx.php',"w");
+ fwrite($fh,$s,strlen($s));
+ fclose($fh);
+ $fh = fopen($unifilename.'.cw.dat',"wb");
+ fwrite($fh,$cw,strlen($cw));
+ fclose($fh);
+ @unlink($unifilename.'.cw127.php');
+ }
+ unset($ttf);
+ }
+ else {
+ $cw = @file_get_contents($unifilename.'.cw.dat');
+ }
+ $i = count($this->fonts)+1;
+ if(!empty($this->AliasNbPages))
+ $sbarr = range(0,57);
+ else
+ $sbarr = range(0,32);
+ $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'ttffile'=>$ttffile, 'fontkey'=>$fontkey, 'subset'=>$sbarr, 'unifilename'=>$unifilename);
+
+ $this->FontFiles[$fontkey]=array('length1'=>$originalsize, 'type'=>"TTF", 'ttffile'=>$ttffile);
+ $this->FontFiles[$file]=array('type'=>"TTF");
+ unset($cw);
+ }
+ else {
+ $info = $this->_loadfont($file);
+ $info['i'] = count($this->fonts)+1;
+ if(!empty($info['file']))
+ {
+ // Embedded font
+ if($info['type']=='TrueType')
+ $this->FontFiles[$info['file']] = array('length1'=>$info['originalsize']);
+ else
+ $this->FontFiles[$info['file']] = array('length1'=>$info['size1'], 'length2'=>$info['size2']);
+ }
+ $this->fonts[$fontkey] = $info;
+ }
+}
+
+function SetFont($family, $style='', $size=0)
+{
+ // Select a font; size given in points
+ if($family=='')
+ $family = $this->FontFamily;
+ else
+ $family = strtolower($family);
+ $style = strtoupper($style);
+ if(strpos($style,'U')!==false)
+ {
+ $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 && $this->FontStyle==$style && $this->FontSizePt==$size)
+ return;
+
+ // Test if font is already loaded
+ $fontkey = $family.$style;
+ if(!isset($this->fonts[$fontkey]))
+ {
+ // Test if one of the core fonts
+ if($family=='arial')
+ $family = 'helvetica';
+ if(in_array($family,$this->CoreFonts))
+ {
+ if($family=='symbol' || $family=='zapfdingbats')
+ $style = '';
+ $fontkey = $family.$style;
+ if(!isset($this->fonts[$fontkey]))
+ $this->AddFont($family,$style);
+ }
+ 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->fonts[$fontkey]['type']=='TTF') { $this->unifontSubset = true; }
+ else { $this->unifontSubset = false; }
+ if($this->page>0)
+ $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
+}
+
+function SetFontSize($size)
+{
+ // Set font size in points
+ if($this->FontSizePt==$size)
+ return;
+ $this->FontSizePt = $size;
+ $this->FontSize = $size/$this->k;
+ if($this->page>0)
+ $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
+}
+
+function AddLink()
+{
+ // Create a new internal link
+ $n = count($this->links)+1;
+ $this->links[$n] = array(0, 0);
+ return $n;
+}
+
+function SetLink($link, $y=0, $page=-1)
+{
+ // Set destination of internal link
+ if($y==-1)
+ $y = $this->y;
+ if($page==-1)
+ $page = $this->page;
+ $this->links[$link] = array($page, $y);
+}
+
+function Link($x, $y, $w, $h, $link)
+{
+ // Put a link on the page
+ $this->PageLinks[$this->page][] = array($x*$this->k, $this->hPt-$y*$this->k, $w*$this->k, $h*$this->k, $link);
+}
+
+function Text($x, $y, $txt)
+{
+ // Output a string
+ $txt = (string)$txt;
+ if(!isset($this->CurrentFont))
+ $this->Error('No font has been set');
+ if ($this->unifontSubset)
+ {
+ $txt2 = '('.$this->_escape($this->UTF8ToUTF16BE($txt, false)).')';
+ foreach($this->UTF8StringToArray($txt) as $uni)
+ $this->CurrentFont['subset'][$uni] = $uni;
+ }
+ else
+ $txt2 = '('.$this->_escape($txt).')';
+ $s = sprintf('BT %.2F %.2F Td %s Tj ET',$x*$this->k,($this->h-$y)*$this->k,$txt2);
+ if($this->underline && $txt!='')
+ $s .= ' '.$this->_dounderline($x,$y,$txt);
+ if($this->ColorFlag)
+ $s = 'q '.$this->TextColor.' '.$s.' Q';
+ $this->_out($s);
+}
+
+function AcceptPageBreak()
+{
+ // Accept automatic page break or not
+ return $this->AutoPageBreak;
+}
+
+function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='')
+{
+ // Output a cell
+ $txt = (string)$txt;
+ $k = $this->k;
+ if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak())
+ {
+ // Automatic page break
+ $x = $this->x;
+ $ws = $this->ws;
+ if($ws>0)
+ {
+ $this->ws = 0;
+ $this->_out('0 Tw');
+ }
+ $this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation);
+ $this->x = $x;
+ if($ws>0)
+ {
+ $this->ws = $ws;
+ $this->_out(sprintf('%.3F Tw',$ws*$k));
+ }
+ }
+ if($w==0)
+ $w = $this->w-$this->rMargin-$this->x;
+ $s = '';
+ if($fill || $border==1)
+ {
+ if($fill)
+ $op = ($border==1) ? 'B' : 'f';
+ else
+ $op = 'S';
+ $s = sprintf('%.2F %.2F %.2F %.2F re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op);
+ }
+ if(is_string($border))
+ {
+ $x = $this->x;
+ $y = $this->y;
+ if(strpos($border,'L')!==false)
+ $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k);
+ if(strpos($border,'T')!==false)
+ $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k);
+ if(strpos($border,'R')!==false)
+ $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
+ if(strpos($border,'B')!==false)
+ $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
+ }
+ if($txt!=='')
+ {
+ if(!isset($this->CurrentFont))
+ $this->Error('No font has been set');
+ if($align=='R')
+ $dx = $w-$this->cMargin-$this->GetStringWidth($txt);
+ elseif($align=='C')
+ $dx = ($w-$this->GetStringWidth($txt))/2;
+ else
+ $dx = $this->cMargin;
+ if($this->ColorFlag)
+ $s .= 'q '.$this->TextColor.' ';
+ // If multibyte, Tw has no effect - do word spacing using an adjustment before each space
+ if ($this->ws && $this->unifontSubset) {
+ foreach($this->UTF8StringToArray($txt) as $uni)
+ $this->CurrentFont['subset'][$uni] = $uni;
+ $space = $this->_escape($this->UTF8ToUTF16BE(' ', false));
+ $s .= sprintf('BT 0 Tw %.2F %.2F Td [',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k);
+ $t = explode(' ',$txt);
+ $numt = count($t);
+ for($i=0;$i<$numt;$i++) {
+ $tx = $t[$i];
+ $tx = '('.$this->_escape($this->UTF8ToUTF16BE($tx, false)).')';
+ $s .= sprintf('%s ',$tx);
+ if (($i+1)<$numt) {
+ $adj = -($this->ws*$this->k)*1000/$this->FontSizePt;
+ $s .= sprintf('%d(%s) ',$adj,$space);
+ }
+ }
+ $s .= '] TJ';
+ $s .= ' ET';
+ }
+ else {
+ if ($this->unifontSubset)
+ {
+ $txt2 = '('.$this->_escape($this->UTF8ToUTF16BE($txt, false)).')';
+ foreach($this->UTF8StringToArray($txt) as $uni)
+ $this->CurrentFont['subset'][$uni] = $uni;
+ }
+ else
+ $txt2='('.$this->_escape($txt).')';
+ $s .= sprintf('BT %.2F %.2F Td %s Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$txt2);
+ }
+ if($this->underline)
+ $s .= ' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$txt);
+ if($this->ColorFlag)
+ $s .= ' Q';
+ if($link)
+ $this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$this->GetStringWidth($txt),$this->FontSize,$link);
+ }
+ if($s)
+ $this->_out($s);
+ $this->lasth = $h;
+ if($ln>0)
+ {
+ // Go to next line
+ $this->y += $h;
+ if($ln==1)
+ $this->x = $this->lMargin;
+ }
+ else
+ $this->x += $w;
+}
+
+function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false)
+{
+ // Output text with automatic or explicit line breaks
+ if(!isset($this->CurrentFont))
+ $this->Error('No font has been set');
+ $cw = $this->CurrentFont['cw'];
+ if($w==0)
+ $w = $this->w-$this->rMargin-$this->x;
+ $wmax = ($w-2*$this->cMargin);
+ //$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
+ $s = str_replace("\r",'',(string)$txt);
+ if ($this->unifontSubset) {
+ $nb=mb_strlen($s, 'utf-8');
+ while($nb>0 && mb_substr($s,$nb-1,1,'utf-8')=="\n") $nb--;
+ }
+ else {
+ $nb = strlen($s);
+ if($nb>0 && $s[$nb-1]=="\n")
+ $nb--;
+ }
+ $b = 0;
+ if($border)
+ {
+ if($border==1)
+ {
+ $border = 'LTRB';
+ $b = 'LRT';
+ $b2 = 'LR';
+ }
+ else
+ {
+ $b2 = '';
+ if(strpos($border,'L')!==false)
+ $b2 .= 'L';
+ if(strpos($border,'R')!==false)
+ $b2 .= 'R';
+ $b = (strpos($border,'T')!==false) ? $b2.'T' : $b2;
+ }
+ }
+ $sep = -1;
+ $i = 0;
+ $j = 0;
+ $l = 0;
+ $ns = 0;
+ $nl = 1;
+ while($i<$nb)
+ {
+ // Get next character
+ if ($this->unifontSubset) {
+ $c = mb_substr($s,$i,1,'UTF-8');
+ }
+ else {
+ $c=$s[$i];
+ }
+ if($c=="\n")
+ {
+ // Explicit line break
+ if($this->ws>0)
+ {
+ $this->ws = 0;
+ $this->_out('0 Tw');
+ }
+ if ($this->unifontSubset) {
+ $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),$b,2,$align,$fill);
+ }
+ else {
+ $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
+ }
+ $i++;
+ $sep = -1;
+ $j = $i;
+ $l = 0;
+ $ns = 0;
+ $nl++;
+ if($border && $nl==2)
+ $b = $b2;
+ continue;
+ }
+ if($c==' ')
+ {
+ $sep = $i;
+ $ls = $l;
+ $ns++;
+ }
+
+ if ($this->unifontSubset) { $l += $this->GetStringWidth($c); }
+ else { $l += $cw[$c]*$this->FontSize/1000; }
+
+ if($l>$wmax)
+ {
+ // Automatic line break
+ if($sep==-1)
+ {
+ if($i==$j)
+ $i++;
+ if($this->ws>0)
+ {
+ $this->ws = 0;
+ $this->_out('0 Tw');
+ }
+ if ($this->unifontSubset) {
+ $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),$b,2,$align,$fill);
+ }
+ else {
+ $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
+ }
+ }
+ else
+ {
+ if($align=='J')
+ {
+ $this->ws = ($ns>1) ? ($wmax-$ls)/($ns-1) : 0;
+ $this->_out(sprintf('%.3F Tw',$this->ws*$this->k));
+ }
+ if ($this->unifontSubset) {
+ $this->Cell($w,$h,mb_substr($s,$j,$sep-$j,'UTF-8'),$b,2,$align,$fill);
+ }
+ else {
+ $this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill);
+ }
+ $i = $sep+1;
+ }
+ $sep = -1;
+ $j = $i;
+ $l = 0;
+ $ns = 0;
+ $nl++;
+ if($border && $nl==2)
+ $b = $b2;
+ }
+ else
+ $i++;
+ }
+ // Last chunk
+ if($this->ws>0)
+ {
+ $this->ws = 0;
+ $this->_out('0 Tw');
+ }
+ if($border && strpos($border,'B')!==false)
+ $b .= 'B';
+ if ($this->unifontSubset) {
+ $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),$b,2,$align,$fill);
+ }
+ else {
+ $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
+ }
+ $this->x = $this->lMargin;
+}
+
+function Write($h, $txt, $link='')
+{
+ // Output text in flowing mode
+ if(!isset($this->CurrentFont))
+ $this->Error('No font has been set');
+ $cw = $this->CurrentFont['cw'];
+ $w = $this->w-$this->rMargin-$this->x;
+ $wmax = ($w-2*$this->cMargin);
+ $s = str_replace("\r",'',(string)$txt);
+ if ($this->unifontSubset) {
+ $nb = mb_strlen($s, 'UTF-8');
+ if($nb==1 && $s==" ") {
+ $this->x += $this->GetStringWidth($s);
+ return;
+ }
+ }
+ else {
+ $nb = strlen($s);
+ }
+ $sep = -1;
+ $i = 0;
+ $j = 0;
+ $l = 0;
+ $nl = 1;
+ while($i<$nb)
+ {
+ // Get next character
+ if ($this->unifontSubset) {
+ $c = mb_substr($s,$i,1,'UTF-8');
+ }
+ else {
+ $c = $s[$i];
+ }
+ if($c=="\n")
+ {
+ // Explicit line break
+ if ($this->unifontSubset) {
+ $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,2,'',false,$link);
+ }
+ else {
+ $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',false,$link);
+ }
+ $i++;
+ $sep = -1;
+ $j = $i;
+ $l = 0;
+ if($nl==1)
+ {
+ $this->x = $this->lMargin;
+ $w = $this->w-$this->rMargin-$this->x;
+ $wmax = ($w-2*$this->cMargin);
+ }
+ $nl++;
+ continue;
+ }
+ if($c==' ')
+ $sep = $i;
+
+ if ($this->unifontSubset) { $l += $this->GetStringWidth($c); }
+ else { $l += $cw[$c]*$this->FontSize/1000; }
+
+ if($l>$wmax)
+ {
+ // Automatic line break
+ if($sep==-1)
+ {
+ if($this->x>$this->lMargin)
+ {
+ // Move to next line
+ $this->x = $this->lMargin;
+ $this->y += $h;
+ $w = $this->w-$this->rMargin-$this->x;
+ $wmax = ($w-2*$this->cMargin);
+ $i++;
+ $nl++;
+ continue;
+ }
+ if($i==$j)
+ $i++;
+ if ($this->unifontSubset) {
+ $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,2,'',false,$link);
+ }
+ else {
+ $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',false,$link);
+ }
+ }
+ else
+ {
+ if ($this->unifontSubset) {
+ $this->Cell($w,$h,mb_substr($s,$j,$sep-$j,'UTF-8'),0,2,'',false,$link);
+ }
+ else {
+ $this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',false,$link);
+ }
+ $i = $sep+1;
+ }
+ $sep = -1;
+ $j = $i;
+ $l = 0;
+ if($nl==1)
+ {
+ $this->x = $this->lMargin;
+ $w = $this->w-$this->rMargin-$this->x;
+ $wmax = ($w-2*$this->cMargin);
+ }
+ $nl++;
+ }
+ else
+ $i++;
+ }
+ // Last chunk
+ if($i!=$j) {
+ if ($this->unifontSubset) {
+ $this->Cell($l,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,0,'',false,$link);
+ }
+ else {
+ $this->Cell($l,$h,substr($s,$j),0,0,'',false,$link);
+ }
+ }
+}
+
+function Ln($h=null)
+{
+ // Line feed; default value is the last cell height
+ $this->x = $this->lMargin;
+ if($h===null)
+ $this->y += $this->lasth;
+ else
+ $this->y += $h;
+}
+
+function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='')
+{
+ // Put an image on the page
+ if($file=='')
+ $this->Error('Image file name is empty');
+ if(!isset($this->images[$file]))
+ {
+ // First use of this image, get info
+ if($type=='')
+ {
+ $pos = strrpos($file,'.');
+ if(!$pos)
+ $this->Error('Image file has no extension and no type was specified: '.$file);
+ $type = substr($file,$pos+1);
+ }
+ $type = strtolower($type);
+ if($type=='jpeg')
+ $type = 'jpg';
+ $mtd = '_parse'.$type;
+ if(!method_exists($this,$mtd))
+ $this->Error('Unsupported image type: '.$type);
+ $info = $this->$mtd($file);
+ $info['i'] = count($this->images)+1;
+ $this->images[$file] = $info;
+ }
+ else
+ $info = $this->images[$file];
+
+ // Automatic width and height calculation if needed
+ if($w==0 && $h==0)
+ {
+ // Put image at 96 dpi
+ $w = -96;
+ $h = -96;
+ }
+ if($w<0)
+ $w = -$info['w']*72/$w/$this->k;
+ if($h<0)
+ $h = -$info['h']*72/$h/$this->k;
+ if($w==0)
+ $w = $h*$info['w']/$info['h'];
+ if($h==0)
+ $h = $w*$info['h']/$info['w'];
+
+ // Flowing mode
+ if($y===null)
+ {
+ if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak())
+ {
+ // Automatic page break
+ $x2 = $this->x;
+ $this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation);
+ $this->x = $x2;
+ }
+ $y = $this->y;
+ $this->y += $h;
+ }
+
+ if($x===null)
+ $x = $this->x;
+ $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q',$w*$this->k,$h*$this->k,$x*$this->k,($this->h-($y+$h))*$this->k,$info['i']));
+ if($link)
+ $this->Link($x,$y,$w,$h,$link);
+}
+
+function GetPageWidth()
+{
+ // Get current page width
+ return $this->w;
+}
+
+function GetPageHeight()
+{
+ // Get current page height
+ return $this->h;
+}
+
+function GetX()
+{
+ // Get x position
+ return $this->x;
+}
+
+function SetX($x)
+{
+ // Set x position
+ if($x>=0)
+ $this->x = $x;
+ else
+ $this->x = $this->w+$x;
+}
+
+function GetY()
+{
+ // Get y position
+ return $this->y;
+}
+
+function SetY($y, $resetX=true)
+{
+ // Set y position and optionally reset x
+ if($y>=0)
+ $this->y = $y;
+ else
+ $this->y = $this->h+$y;
+ if($resetX)
+ $this->x = $this->lMargin;
+}
+
+function SetXY($x, $y)
+{
+ // Set x and y positions
+ $this->SetX($x);
+ $this->SetY($y,false);
+}
+
+function Output($dest='', $name='', $isUTF8=false)
+{
+ // Output PDF to some destination
+ $this->Close();
+ if(strlen($name)==1 && strlen($dest)!=1)
+ {
+ // Fix parameter order
+ $tmp = $dest;
+ $dest = $name;
+ $name = $tmp;
+ }
+ if($dest=='')
+ $dest = 'I';
+ if($name=='')
+ $name = 'doc.pdf';
+ switch(strtoupper($dest))
+ {
+ case 'I':
+ // Send to standard output
+ $this->_checkoutput();
+ if(PHP_SAPI!='cli')
+ {
+ // We send to a browser
+ header('Content-Type: application/pdf');
+ header('Content-Disposition: inline; '.$this->_httpencode('filename',$name,$isUTF8));
+ header('Cache-Control: private, max-age=0, must-revalidate');
+ header('Pragma: public');
+ }
+ echo $this->buffer;
+ break;
+ case 'D':
+ // Download file
+ $this->_checkoutput();
+ header('Content-Type: application/pdf');
+ header('Content-Disposition: attachment; '.$this->_httpencode('filename',$name,$isUTF8));
+ header('Cache-Control: private, max-age=0, must-revalidate');
+ header('Pragma: public');
+ echo $this->buffer;
+ break;
+ case 'F':
+ // Save to local file
+ if(!file_put_contents($name,$this->buffer))
+ $this->Error('Unable to create output file: '.$name);
+ break;
+ case 'S':
+ // Return as a string
+ return $this->buffer;
+ default:
+ $this->Error('Incorrect output destination: '.$dest);
+ }
+ return '';
+}
+
+/*******************************************************************************
+* Protected methods *
+*******************************************************************************/
+
+protected function _dochecks()
+{
+ // Check availability of mbstring
+ if(!function_exists('mb_strlen'))
+ $this->Error('mbstring extension is not available');
+}
+
+protected function _checkoutput()
+{
+ if(PHP_SAPI!='cli')
+ {
+ if(headers_sent($file,$line))
+ $this->Error("Some data has already been output, can't send PDF file (output started at $file:$line)");
+ }
+ if(ob_get_length())
+ {
+ // The output buffer is not empty
+ if(preg_match('/^(\xEF\xBB\xBF)?\s*$/',ob_get_contents()))
+ {
+ // It contains only a UTF-8 BOM and/or whitespace, let's clean it
+ ob_clean();
+ }
+ else
+ $this->Error("Some data has already been output, can't send PDF file");
+ }
+}
+
+protected function _getpagesize($size)
+{
+ if(is_string($size))
+ {
+ $size = strtolower($size);
+ if(!isset($this->StdPageSizes[$size]))
+ $this->Error('Unknown page size: '.$size);
+ $a = $this->StdPageSizes[$size];
+ return array($a[0]/$this->k, $a[1]/$this->k);
+ }
+ else
+ {
+ if($size[0]>$size[1])
+ return array($size[1], $size[0]);
+ else
+ return $size;
+ }
+}
+
+protected function _beginpage($orientation, $size, $rotation)
+{
+ $this->page++;
+ $this->pages[$this->page] = '';
+ $this->PageLinks[$this->page] = array();
+ $this->state = 2;
+ $this->x = $this->lMargin;
+ $this->y = $this->tMargin;
+ $this->FontFamily = '';
+ // Check page size and orientation
+ if($orientation=='')
+ $orientation = $this->DefOrientation;
+ else
+ $orientation = strtoupper($orientation[0]);
+ if($size=='')
+ $size = $this->DefPageSize;
+ else
+ $size = $this->_getpagesize($size);
+ if($orientation!=$this->CurOrientation || $size[0]!=$this->CurPageSize[0] || $size[1]!=$this->CurPageSize[1])
+ {
+ // New size or orientation
+ if($orientation=='P')
+ {
+ $this->w = $size[0];
+ $this->h = $size[1];
+ }
+ else
+ {
+ $this->w = $size[1];
+ $this->h = $size[0];
+ }
+ $this->wPt = $this->w*$this->k;
+ $this->hPt = $this->h*$this->k;
+ $this->PageBreakTrigger = $this->h-$this->bMargin;
+ $this->CurOrientation = $orientation;
+ $this->CurPageSize = $size;
+ }
+ if($orientation!=$this->DefOrientation || $size[0]!=$this->DefPageSize[0] || $size[1]!=$this->DefPageSize[1])
+ $this->PageInfo[$this->page]['size'] = array($this->wPt, $this->hPt);
+ if($rotation!=0)
+ {
+ if($rotation%90!=0)
+ $this->Error('Incorrect rotation value: '.$rotation);
+ $this->PageInfo[$this->page]['rotation'] = $rotation;
+ }
+ $this->CurRotation = $rotation;
+}
+
+protected function _endpage()
+{
+ $this->state = 1;
+}
+
+protected function _loadfont($font)
+{
+ // Load a font definition file from the font directory
+ if(strpos($font,'/')!==false || strpos($font,"\\")!==false)
+ $this->Error('Incorrect font definition file name: '.$font);
+ include($this->fontpath.$font);
+ if(!isset($name))
+ $this->Error('Could not include font definition file');
+ if(isset($enc))
+ $enc = strtolower($enc);
+ if(!isset($subsetted))
+ $subsetted = false;
+ return get_defined_vars();
+}
+
+protected function _isascii($s)
+{
+ // Test if string is ASCII
+ $nb = strlen($s);
+ for($i=0;$i<$nb;$i++)
+ {
+ if(ord($s[$i])>127)
+ return false;
+ }
+ return true;
+}
+
+protected function _httpencode($param, $value, $isUTF8)
+{
+ // Encode HTTP header field parameter
+ if($this->_isascii($value))
+ return $param.'="'.$value.'"';
+ if(!$isUTF8)
+ $value = $this->_UTF8encode($value);
+ return $param."*=UTF-8''".rawurlencode($value);
+}
+
+protected function _UTF8encode($s)
+{
+ // Convert ISO-8859-1 to UTF-8
+ return mb_convert_encoding($s,'UTF-8','ISO-8859-1');
+}
+
+protected function _UTF8toUTF16($s)
+{
+ // Convert UTF-8 to UTF-16BE with BOM
+ return "\xFE\xFF".mb_convert_encoding($s,'UTF-16BE','UTF-8');
+}
+
+protected function _escape($s)
+{
+ // Escape special characters
+ if(strpos($s,'(')!==false || strpos($s,')')!==false || strpos($s,'\\')!==false || strpos($s,"\r")!==false)
+ return str_replace(array('\\','(',')',"\r"), array('\\\\','\\(','\\)','\\r'), $s);
+ else
+ return $s;
+}
+
+protected function _textstring($s)
+{
+ // Format a text string
+ if(!$this->_isascii($s))
+ $s = $this->_UTF8toUTF16($s);
+ return '('.$this->_escape($s).')';
+}
+
+protected function _dounderline($x, $y, $txt)
+{
+ // Underline text
+ $up = $this->CurrentFont['up'];
+ $ut = $this->CurrentFont['ut'];
+ $w = $this->GetStringWidth($txt)+$this->ws*substr_count($txt,' ');
+ return sprintf('%.2F %.2F %.2F %.2F re f',$x*$this->k,($this->h-($y-$up/1000*$this->FontSize))*$this->k,$w*$this->k,-$ut/1000*$this->FontSizePt);
+}
+
+protected function _parsejpg($file)
+{
+ // Extract info from a JPEG file
+ $a = getimagesize($file);
+ if(!$a)
+ $this->Error('Missing or incorrect image file: '.$file);
+ if($a[2]!=2)
+ $this->Error('Not a JPEG file: '.$file);
+ if(!isset($a['channels']) || $a['channels']==3)
+ $colspace = 'DeviceRGB';
+ elseif($a['channels']==4)
+ $colspace = 'DeviceCMYK';
+ else
+ $colspace = 'DeviceGray';
+ $bpc = isset($a['bits']) ? $a['bits'] : 8;
+ $data = file_get_contents($file);
+ return array('w'=>$a[0], 'h'=>$a[1], 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'DCTDecode', 'data'=>$data);
+}
+
+protected function _parsepng($file)
+{
+ // Extract info from a PNG file
+ $f = fopen($file,'rb');
+ if(!$f)
+ $this->Error('Can\'t open image file: '.$file);
+ $info = $this->_parsepngstream($f,$file);
+ fclose($f);
+ return $info;
+}
+
+protected function _parsepngstream($f, $file)
+{
+ // Check signature
+ if($this->_readstream($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10))
+ $this->Error('Not a PNG file: '.$file);
+
+ // Read header chunk
+ $this->_readstream($f,4);
+ if($this->_readstream($f,4)!='IHDR')
+ $this->Error('Incorrect PNG file: '.$file);
+ $w = $this->_readint($f);
+ $h = $this->_readint($f);
+ $bpc = ord($this->_readstream($f,1));
+ if($bpc>8)
+ $this->Error('16-bit depth not supported: '.$file);
+ $ct = ord($this->_readstream($f,1));
+ if($ct==0 || $ct==4)
+ $colspace = 'DeviceGray';
+ elseif($ct==2 || $ct==6)
+ $colspace = 'DeviceRGB';
+ elseif($ct==3)
+ $colspace = 'Indexed';
+ else
+ $this->Error('Unknown color type: '.$file);
+ if(ord($this->_readstream($f,1))!=0)
+ $this->Error('Unknown compression method: '.$file);
+ if(ord($this->_readstream($f,1))!=0)
+ $this->Error('Unknown filter method: '.$file);
+ if(ord($this->_readstream($f,1))!=0)
+ $this->Error('Interlacing not supported: '.$file);
+ $this->_readstream($f,4);
+ $dp = '/Predictor 15 /Colors '.($colspace=='DeviceRGB' ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w;
+
+ // Scan chunks looking for palette, transparency and image data
+ $pal = '';
+ $trns = '';
+ $data = '';
+ do
+ {
+ $n = $this->_readint($f);
+ $type = $this->_readstream($f,4);
+ if($type=='PLTE')
+ {
+ // Read palette
+ $pal = $this->_readstream($f,$n);
+ $this->_readstream($f,4);
+ }
+ elseif($type=='tRNS')
+ {
+ // Read transparency info
+ $t = $this->_readstream($f,$n);
+ if($ct==0)
+ $trns = array(ord(substr($t,1,1)));
+ elseif($ct==2)
+ $trns = array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1)));
+ else
+ {
+ $pos = strpos($t,chr(0));
+ if($pos!==false)
+ $trns = array($pos);
+ }
+ $this->_readstream($f,4);
+ }
+ elseif($type=='IDAT')
+ {
+ // Read image data block
+ $data .= $this->_readstream($f,$n);
+ $this->_readstream($f,4);
+ }
+ elseif($type=='IEND')
+ break;
+ else
+ $this->_readstream($f,$n+4);
+ }
+ while($n);
+
+ if($colspace=='Indexed' && empty($pal))
+ $this->Error('Missing palette in '.$file);
+ $info = array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'dp'=>$dp, 'pal'=>$pal, 'trns'=>$trns);
+ if($ct>=4)
+ {
+ // Extract alpha channel
+ if(!function_exists('gzuncompress'))
+ $this->Error('Zlib not available, can\'t handle alpha channel: '.$file);
+ $data = gzuncompress($data);
+ $color = '';
+ $alpha = '';
+ if($ct==4)
+ {
+ // Gray image
+ $len = 2*$w;
+ for($i=0;$i<$h;$i++)
+ {
+ $pos = (1+$len)*$i;
+ $color .= $data[$pos];
+ $alpha .= $data[$pos];
+ $line = substr($data,$pos+1,$len);
+ $color .= preg_replace('/(.)./s','$1',$line);
+ $alpha .= preg_replace('/.(.)/s','$1',$line);
+ }
+ }
+ else
+ {
+ // RGB image
+ $len = 4*$w;
+ for($i=0;$i<$h;$i++)
+ {
+ $pos = (1+$len)*$i;
+ $color .= $data[$pos];
+ $alpha .= $data[$pos];
+ $line = substr($data,$pos+1,$len);
+ $color .= preg_replace('/(.{3})./s','$1',$line);
+ $alpha .= preg_replace('/.{3}(.)/s','$1',$line);
+ }
+ }
+ unset($data);
+ $data = gzcompress($color);
+ $info['smask'] = gzcompress($alpha);
+ $this->WithAlpha = true;
+ if($this->PDFVersion<'1.4')
+ $this->PDFVersion = '1.4';
+ }
+ $info['data'] = $data;
+ return $info;
+}
+
+protected function _readstream($f, $n)
+{
+ // Read n bytes from stream
+ $res = '';
+ while($n>0 && !feof($f))
+ {
+ $s = fread($f,$n);
+ if($s===false)
+ $this->Error('Error while reading stream');
+ $n -= strlen($s);
+ $res .= $s;
+ }
+ if($n>0)
+ $this->Error('Unexpected end of stream');
+ return $res;
+}
+
+protected function _readint($f)
+{
+ // Read a 4-byte integer from stream
+ $a = unpack('Ni',$this->_readstream($f,4));
+ return $a['i'];
+}
+
+protected function _parsegif($file)
+{
+ // Extract info from a GIF file (via PNG conversion)
+ if(!function_exists('imagepng'))
+ $this->Error('GD extension is required for GIF support');
+ if(!function_exists('imagecreatefromgif'))
+ $this->Error('GD has no GIF read support');
+ $im = imagecreatefromgif($file);
+ if(!$im)
+ $this->Error('Missing or incorrect image file: '.$file);
+ imageinterlace($im,0);
+ ob_start();
+ imagepng($im);
+ $data = ob_get_clean();
+ imagedestroy($im);
+ $f = fopen('php://temp','rb+');
+ if(!$f)
+ $this->Error('Unable to create memory stream');
+ fwrite($f,$data);
+ rewind($f);
+ $info = $this->_parsepngstream($f,$file);
+ fclose($f);
+ return $info;
+}
+
+protected function _out($s)
+{
+ // Add a line to the document
+ if($this->state==2)
+ $this->pages[$this->page] .= $s."\n";
+ elseif($this->state==1)
+ $this->_put($s);
+ elseif($this->state==0)
+ $this->Error('No page has been added yet');
+ elseif($this->state==3)
+ $this->Error('The document is closed');
+}
+
+protected function _put($s)
+{
+ $this->buffer .= $s."\n";
+}
+
+protected function _getoffset()
+{
+ return strlen($this->buffer);
+}
+
+protected function _newobj($n=null)
+{
+ // Begin a new object
+ if($n===null)
+ $n = ++$this->n;
+ $this->offsets[$n] = $this->_getoffset();
+ $this->_put($n.' 0 obj');
+}
+
+protected function _putstream($data)
+{
+ $this->_put('stream');
+ $this->_put($data);
+ $this->_put('endstream');
+}
+
+protected function _putstreamobject($data)
+{
+ if($this->compress)
+ {
+ $entries = '/Filter /FlateDecode ';
+ $data = gzcompress($data);
+ }
+ else
+ $entries = '';
+ $entries .= '/Length '.strlen($data);
+ $this->_newobj();
+ $this->_put('<<'.$entries.'>>');
+ $this->_putstream($data);
+ $this->_put('endobj');
+}
+
+protected function _putlinks($n)
+{
+ foreach($this->PageLinks[$n] as $pl)
+ {
+ $this->_newobj();
+ $rect = sprintf('%.2F %.2F %.2F %.2F',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]);
+ $s = '<_textstring($pl[4]).'>>>>';
+ else
+ {
+ $l = $this->links[$pl[4]];
+ if(isset($this->PageInfo[$l[0]]['size']))
+ $h = $this->PageInfo[$l[0]]['size'][1];
+ else
+ $h = ($this->DefOrientation=='P') ? $this->DefPageSize[1]*$this->k : $this->DefPageSize[0]*$this->k;
+ $s .= sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]>>',$this->PageInfo[$l[0]]['n'],$h-$l[1]*$this->k);
+ }
+ $this->_put($s);
+ $this->_put('endobj');
+ }
+}
+
+protected function _putpage($n)
+{
+ $this->_newobj();
+ $this->_put('<_put('/Parent 1 0 R');
+ if(isset($this->PageInfo[$n]['size']))
+ $this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->PageInfo[$n]['size'][0],$this->PageInfo[$n]['size'][1]));
+ if(isset($this->PageInfo[$n]['rotation']))
+ $this->_put('/Rotate '.$this->PageInfo[$n]['rotation']);
+ $this->_put('/Resources 2 0 R');
+ if(!empty($this->PageLinks[$n]))
+ {
+ $s = '/Annots [';
+ foreach($this->PageLinks[$n] as $pl)
+ $s .= $pl[5].' 0 R ';
+ $s .= ']';
+ $this->_put($s);
+ }
+ if($this->WithAlpha)
+ $this->_put('/Group <>');
+ $this->_put('/Contents '.($this->n+1).' 0 R>>');
+ $this->_put('endobj');
+ // Page content
+ if(!empty($this->AliasNbPages)) {
+ $alias = $this->UTF8ToUTF16BE($this->AliasNbPages, false);
+ $r = $this->UTF8ToUTF16BE($this->page, false);
+ $this->pages[$n] = str_replace($alias,$r,$this->pages[$n]);
+ // Now repeat for no pages in non-subset fonts
+ $this->pages[$n] = str_replace($this->AliasNbPages,$this->page,$this->pages[$n]);
+ }
+ $this->_putstreamobject($this->pages[$n]);
+ // Link annotations
+ $this->_putlinks($n);
+}
+
+protected function _putpages()
+{
+ $nb = $this->page;
+ $n = $this->n;
+ for($i=1;$i<=$nb;$i++)
+ {
+ $this->PageInfo[$i]['n'] = ++$n;
+ $n++;
+ foreach($this->PageLinks[$i] as &$pl)
+ $pl[5] = ++$n;
+ unset($pl);
+ }
+ for($i=1;$i<=$nb;$i++)
+ $this->_putpage($i);
+ // Pages root
+ $this->_newobj(1);
+ $this->_put('<PageInfo[$i]['n'].' 0 R ';
+ $kids .= ']';
+ $this->_put($kids);
+ $this->_put('/Count '.$nb);
+ if($this->DefOrientation=='P')
+ {
+ $w = $this->DefPageSize[0];
+ $h = $this->DefPageSize[1];
+ }
+ else
+ {
+ $w = $this->DefPageSize[1];
+ $h = $this->DefPageSize[0];
+ }
+ $this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]',$w*$this->k,$h*$this->k));
+ $this->_put('>>');
+ $this->_put('endobj');
+}
+
+protected function _putfonts()
+{
+ foreach($this->FontFiles as $file=>$info)
+ {
+ if (!isset($info['type']) || $info['type']!='TTF') {
+ // Font file embedding
+ $this->_newobj();
+ $this->FontFiles[$file]['n'] = $this->n;
+ $font = file_get_contents($this->fontpath.$file,true);
+ if(!$font)
+ $this->Error('Font file not found: '.$file);
+ $compressed = (substr($file,-2)=='.z');
+ if(!$compressed && isset($info['length2']))
+ $font = substr($font,6,$info['length1']).substr($font,6+$info['length1']+6,$info['length2']);
+ $this->_put('<_put('/Filter /FlateDecode');
+ $this->_put('/Length1 '.$info['length1']);
+ if(isset($info['length2']))
+ $this->_put('/Length2 '.$info['length2'].' /Length3 0');
+ $this->_put('>>');
+ $this->_putstream($font);
+ $this->_put('endobj');
+ }
+ }
+ foreach($this->fonts as $k=>$font)
+ {
+ // Encoding
+ if(isset($font['diff']))
+ {
+ if(!isset($this->encodings[$font['enc']]))
+ {
+ $this->_newobj();
+ $this->_put('<>');
+ $this->_put('endobj');
+ $this->encodings[$font['enc']] = $this->n;
+ }
+ }
+ // ToUnicode CMap
+ if(isset($font['uv']))
+ {
+ if(isset($font['enc']))
+ $cmapkey = $font['enc'];
+ else
+ $cmapkey = $font['name'];
+ if(!isset($this->cmaps[$cmapkey]))
+ {
+ $cmap = $this->_tounicodecmap($font['uv']);
+ $this->_putstreamobject($cmap);
+ $this->cmaps[$cmapkey] = $this->n;
+ }
+ }
+ // Font object
+ $type = $font['type'];
+ $name = $font['name'];
+ if($type=='Core')
+ {
+ // Core font
+ $this->fonts[$k]['n'] = $this->n+1;
+ $this->_newobj();
+ $this->_put('<_put('/BaseFont /'.$name);
+ $this->_put('/Subtype /Type1');
+ if($name!='Symbol' && $name!='ZapfDingbats')
+ $this->_put('/Encoding /WinAnsiEncoding');
+ if(isset($font['uv']))
+ $this->_put('/ToUnicode '.$this->cmaps[$cmapkey].' 0 R');
+ $this->_put('>>');
+ $this->_put('endobj');
+ }
+ elseif($type=='Type1' || $type=='TrueType')
+ {
+ // Additional Type1 or TrueType/OpenType font
+ if(isset($font['subsetted']) && $font['subsetted'])
+ $name = 'AAAAAA+'.$name;
+ $this->fonts[$k]['n'] = $this->n+1;
+ $this->_newobj();
+ $this->_put('<_put('/BaseFont /'.$name);
+ $this->_put('/Subtype /'.$type);
+ $this->_put('/FirstChar 32 /LastChar 255');
+ $this->_put('/Widths '.($this->n+1).' 0 R');
+ $this->_put('/FontDescriptor '.($this->n+2).' 0 R');
+
+ if($font['enc'])
+ {
+ if(isset($font['diff']))
+ $this->_put('/Encoding '.$this->encodings[$font['enc']].' 0 R');
+ else
+ $this->_put('/Encoding /WinAnsiEncoding');
+ }
+
+ if(isset($font['uv']))
+ $this->_put('/ToUnicode '.$this->cmaps[$cmapkey].' 0 R');
+ $this->_put('>>');
+ $this->_put('endobj');
+ // Widths
+ $this->_newobj();
+ $cw = $font['cw'];
+ $s = '[';
+ for($i=32;$i<=255;$i++)
+ $s .= $cw[chr($i)].' ';
+ $this->_put($s.']');
+ $this->_put('endobj');
+ // Descriptor
+ $this->_newobj();
+ $s = '<$v)
+ $s .= ' /'.$k.' '.$v;
+
+ if(!empty($font['file']))
+ $s .= ' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
+ $this->_put($s.'>>');
+ $this->_put('endobj');
+ }
+ // TrueType embedded SUBSETS or FULL
+ else if ($type=='TTF') {
+ $this->fonts[$k]['n']=$this->n+1;
+ require_once($this->fontpath.'unifont/ttfonts.php');
+ $ttf = new TTFontFile();
+ $fontname = 'MPDFAA'.'+'.$font['name'];
+ $subset = $font['subset'];
+ unset($subset[0]);
+ $ttfontstream = $ttf->makeSubset($font['ttffile'], $subset);
+ $ttfontsize = strlen($ttfontstream);
+ $fontstream = gzcompress($ttfontstream);
+ $codeToGlyph = $ttf->codeToGlyph;
+ unset($codeToGlyph[0]);
+
+ // Type0 Font
+ // A composite font - a font composed of other fonts, organized hierarchically
+ $this->_newobj();
+ $this->_put('<_put('/Subtype /Type0');
+ $this->_put('/BaseFont /'.$fontname.'');
+ $this->_put('/Encoding /Identity-H');
+ $this->_put('/DescendantFonts ['.($this->n + 1).' 0 R]');
+ $this->_put('/ToUnicode '.($this->n + 2).' 0 R');
+ $this->_put('>>');
+ $this->_put('endobj');
+
+ // CIDFontType2
+ // A CIDFont whose glyph descriptions are based on TrueType font technology
+ $this->_newobj();
+ $this->_put('<_put('/Subtype /CIDFontType2');
+ $this->_put('/BaseFont /'.$fontname.'');
+ $this->_put('/CIDSystemInfo '.($this->n + 2).' 0 R');
+ $this->_put('/FontDescriptor '.($this->n + 3).' 0 R');
+ if (isset($font['desc']['MissingWidth'])){
+ $this->_out('/DW '.$font['desc']['MissingWidth'].'');
+ }
+
+ $this->_putTTfontwidths($font, $ttf->maxUni);
+
+ $this->_put('/CIDToGIDMap '.($this->n + 4).' 0 R');
+ $this->_put('>>');
+ $this->_put('endobj');
+
+ // ToUnicode
+ $this->_newobj();
+ $toUni = "/CIDInit /ProcSet findresource begin\n";
+ $toUni .= "12 dict begin\n";
+ $toUni .= "begincmap\n";
+ $toUni .= "/CIDSystemInfo\n";
+ $toUni .= "<> def\n";
+ $toUni .= "/CMapName /Adobe-Identity-UCS def\n";
+ $toUni .= "/CMapType 2 def\n";
+ $toUni .= "1 begincodespacerange\n";
+ $toUni .= "<0000> \n";
+ $toUni .= "endcodespacerange\n";
+ $toUni .= "1 beginbfrange\n";
+ $toUni .= "<0000> <0000>\n";
+ $toUni .= "endbfrange\n";
+ $toUni .= "endcmap\n";
+ $toUni .= "CMapName currentdict /CMap defineresource pop\n";
+ $toUni .= "end\n";
+ $toUni .= "end";
+ $this->_put('<>');
+ $this->_putstream($toUni);
+ $this->_put('endobj');
+
+ // CIDSystemInfo dictionary
+ $this->_newobj();
+ $this->_put('<_put('/Ordering (UCS)');
+ $this->_put('/Supplement 0');
+ $this->_put('>>');
+ $this->_put('endobj');
+
+ // Font descriptor
+ $this->_newobj();
+ $this->_put('<_put('/FontName /'.$fontname);
+ foreach($font['desc'] as $kd=>$v) {
+ if ($kd == 'Flags') { $v = $v | 4; $v = $v & ~32; } // SYMBOLIC font flag
+ $this->_out(' /'.$kd.' '.$v);
+ }
+ $this->_put('/FontFile2 '.($this->n + 2).' 0 R');
+ $this->_put('>>');
+ $this->_put('endobj');
+
+ // Embed CIDToGIDMap
+ // A specification of the mapping from CIDs to glyph indices
+ $cidtogidmap = '';
+ $cidtogidmap = str_pad('', 256*256*2, "\x00");
+ foreach($codeToGlyph as $cc=>$glyph) {
+ $cidtogidmap[$cc*2] = chr($glyph >> 8);
+ $cidtogidmap[$cc*2 + 1] = chr($glyph & 0xFF);
+ }
+ $cidtogidmap = gzcompress($cidtogidmap);
+ $this->_newobj();
+ $this->_put('<_put('/Filter /FlateDecode');
+ $this->_put('>>');
+ $this->_putstream($cidtogidmap);
+ $this->_put('endobj');
+
+ //Font file
+ $this->_newobj();
+ $this->_put('<_put('/Filter /FlateDecode');
+ $this->_put('/Length1 '.$ttfontsize);
+ $this->_put('>>');
+ $this->_putstream($fontstream);
+ $this->_put('endobj');
+ unset($ttf);
+ }
+ else
+ {
+ // Allow for additional types
+ $this->fonts[$k]['n'] = $this->n+1;
+ $mtd = '_put'.strtolower($type);
+ if(!method_exists($this,$mtd))
+ $this->Error('Unsupported font type: '.$type);
+ $this->$mtd($font);
+ }
+ }
+}
+
+protected function _putTTfontwidths($font, $maxUni) {
+ if (file_exists($font['unifilename'].'.cw127.php')) {
+ include($font['unifilename'].'.cw127.php') ;
+ $startcid = 128;
+ }
+ else {
+ $rangeid = 0;
+ $range = array();
+ $prevcid = -2;
+ $prevwidth = -1;
+ $interval = false;
+ $startcid = 1;
+ }
+ $cwlen = $maxUni + 1;
+
+ // for each character
+ for ($cid=$startcid; $cid<$cwlen; $cid++) {
+ if ($cid==128 && (!file_exists($font['unifilename'].'.cw127.php'))) {
+ if (is_writable(dirname($this->fontpath.'unifont/x'))) {
+ $fh = fopen($font['unifilename'].'.cw127.php',"wb");
+ $cw127='";
+ fwrite($fh,$cw127,strlen($cw127));
+ fclose($fh);
+ }
+ }
+ if ((!isset($font['cw'][$cid*2]) || !isset($font['cw'][$cid*2+1])) ||
+ ($font['cw'][$cid*2] == "\00" && $font['cw'][$cid*2+1] == "\00")) { continue; }
+
+ $width = (ord($font['cw'][$cid*2]) << 8) + ord($font['cw'][$cid*2+1]);
+ if ($width == 65535) { $width = 0; }
+ if ($cid > 255 && (!isset($font['subset'][$cid]) || !$font['subset'][$cid])) { continue; }
+ if (!isset($font['dw']) || (isset($font['dw']) && $width != $font['dw'])) {
+ if ($cid == ($prevcid + 1)) {
+ if ($width == $prevwidth) {
+ if ($width == $range[$rangeid][0]) {
+ $range[$rangeid][] = $width;
+ }
+ else {
+ array_pop($range[$rangeid]);
+ // new range
+ $rangeid = $prevcid;
+ $range[$rangeid] = array();
+ $range[$rangeid][] = $prevwidth;
+ $range[$rangeid][] = $width;
+ }
+ $interval = true;
+ $range[$rangeid]['interval'] = true;
+ } else {
+ if ($interval) {
+ // new range
+ $rangeid = $cid;
+ $range[$rangeid] = array();
+ $range[$rangeid][] = $width;
+ }
+ else { $range[$rangeid][] = $width; }
+ $interval = false;
+ }
+ } else {
+ $rangeid = $cid;
+ $range[$rangeid] = array();
+ $range[$rangeid][] = $width;
+ $interval = false;
+ }
+ $prevcid = $cid;
+ $prevwidth = $width;
+ }
+ }
+ $prevk = -1;
+ $nextk = -1;
+ $prevint = false;
+ foreach ($range as $k => $ws) {
+ $cws = count($ws);
+ if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
+ if (isset($range[$k]['interval'])) { unset($range[$k]['interval']); }
+ $range[$prevk] = array_merge($range[$prevk], $range[$k]);
+ unset($range[$k]);
+ }
+ else { $prevk = $k; }
+ $nextk = $k + $cws;
+ if (isset($ws['interval'])) {
+ if ($cws > 3) { $prevint = true; }
+ else { $prevint = false; }
+ unset($range[$k]['interval']);
+ --$nextk;
+ }
+ else { $prevint = false; }
+ }
+ $w = '';
+ foreach ($range as $k => $ws) {
+ if (count(array_count_values($ws)) == 1) { $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0]; }
+ else { $w .= ' '.$k.' [ '.implode(' ', $ws).' ]' . "\n"; }
+ }
+ $this->_out('/W ['.$w.' ]');
+}
+
+protected function _tounicodecmap($uv)
+{
+ $ranges = '';
+ $nbr = 0;
+ $chars = '';
+ $nbc = 0;
+ foreach($uv as $c=>$v)
+ {
+ if(is_array($v))
+ {
+ $ranges .= sprintf("<%02X> <%02X> <%04X>\n",$c,$c+$v[1]-1,$v[0]);
+ $nbr++;
+ }
+ else
+ {
+ $chars .= sprintf("<%02X> <%04X>\n",$c,$v);
+ $nbc++;
+ }
+ }
+ $s = "/CIDInit /ProcSet findresource begin\n";
+ $s .= "12 dict begin\n";
+ $s .= "begincmap\n";
+ $s .= "/CIDSystemInfo\n";
+ $s .= "<> def\n";
+ $s .= "/CMapName /Adobe-Identity-UCS def\n";
+ $s .= "/CMapType 2 def\n";
+ $s .= "1 begincodespacerange\n";
+ $s .= "<00> \n";
+ $s .= "endcodespacerange\n";
+ if($nbr>0)
+ {
+ $s .= "$nbr beginbfrange\n";
+ $s .= $ranges;
+ $s .= "endbfrange\n";
+ }
+ if($nbc>0)
+ {
+ $s .= "$nbc beginbfchar\n";
+ $s .= $chars;
+ $s .= "endbfchar\n";
+ }
+ $s .= "endcmap\n";
+ $s .= "CMapName currentdict /CMap defineresource pop\n";
+ $s .= "end\n";
+ $s .= "end";
+ return $s;
+}
+
+protected function _putimages()
+{
+ foreach(array_keys($this->images) as $file)
+ {
+ $this->_putimage($this->images[$file]);
+ unset($this->images[$file]['data']);
+ unset($this->images[$file]['smask']);
+ }
+}
+
+protected function _putimage(&$info)
+{
+ $this->_newobj();
+ $info['n'] = $this->n;
+ $this->_put('<_put('/Subtype /Image');
+ $this->_put('/Width '.$info['w']);
+ $this->_put('/Height '.$info['h']);
+ if($info['cs']=='Indexed')
+ $this->_put('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]');
+ else
+ {
+ $this->_put('/ColorSpace /'.$info['cs']);
+ if($info['cs']=='DeviceCMYK')
+ $this->_put('/Decode [1 0 1 0 1 0 1 0]');
+ }
+ $this->_put('/BitsPerComponent '.$info['bpc']);
+ if(isset($info['f']))
+ $this->_put('/Filter /'.$info['f']);
+ if(isset($info['dp']))
+ $this->_put('/DecodeParms <<'.$info['dp'].'>>');
+ if(isset($info['trns']) && is_array($info['trns']))
+ {
+ $trns = '';
+ for($i=0;$i_put('/Mask ['.$trns.']');
+ }
+ if(isset($info['smask']))
+ $this->_put('/SMask '.($this->n+1).' 0 R');
+ $this->_put('/Length '.strlen($info['data']).'>>');
+ $this->_putstream($info['data']);
+ $this->_put('endobj');
+ // Soft mask
+ if(isset($info['smask']))
+ {
+ $dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns '.$info['w'];
+ $smask = array('w'=>$info['w'], 'h'=>$info['h'], 'cs'=>'DeviceGray', 'bpc'=>8, 'f'=>$info['f'], 'dp'=>$dp, 'data'=>$info['smask']);
+ $this->_putimage($smask);
+ }
+ // Palette
+ if($info['cs']=='Indexed')
+ $this->_putstreamobject($info['pal']);
+}
+
+protected function _putxobjectdict()
+{
+ foreach($this->images as $image)
+ $this->_put('/I'.$image['i'].' '.$image['n'].' 0 R');
+}
+
+protected function _putresourcedict()
+{
+ $this->_put('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
+ $this->_put('/Font <<');
+ foreach($this->fonts as $font)
+ $this->_put('/F'.$font['i'].' '.$font['n'].' 0 R');
+ $this->_put('>>');
+ $this->_put('/XObject <<');
+ $this->_putxobjectdict();
+ $this->_put('>>');
+}
+
+protected function _putresources()
+{
+ $this->_putfonts();
+ $this->_putimages();
+ // Resource dictionary
+ $this->_newobj(2);
+ $this->_put('<<');
+ $this->_putresourcedict();
+ $this->_put('>>');
+ $this->_put('endobj');
+}
+
+protected function _putinfo()
+{
+ $date = @date('YmdHisO',$this->CreationDate);
+ $this->metadata['CreationDate'] = 'D:'.substr($date,0,-2)."'".substr($date,-2)."'";
+ foreach($this->metadata as $key=>$value)
+ $this->_put('/'.$key.' '.$this->_textstring($value));
+}
+
+protected function _putcatalog()
+{
+ $n = $this->PageInfo[1]['n'];
+ $this->_put('/Type /Catalog');
+ $this->_put('/Pages 1 0 R');
+ if($this->ZoomMode=='fullpage')
+ $this->_put('/OpenAction ['.$n.' 0 R /Fit]');
+ elseif($this->ZoomMode=='fullwidth')
+ $this->_put('/OpenAction ['.$n.' 0 R /FitH null]');
+ elseif($this->ZoomMode=='real')
+ $this->_put('/OpenAction ['.$n.' 0 R /XYZ null null 1]');
+ elseif(!is_string($this->ZoomMode))
+ $this->_put('/OpenAction ['.$n.' 0 R /XYZ null null '.sprintf('%.2F',$this->ZoomMode/100).']');
+ if($this->LayoutMode=='single')
+ $this->_put('/PageLayout /SinglePage');
+ elseif($this->LayoutMode=='continuous')
+ $this->_put('/PageLayout /OneColumn');
+ elseif($this->LayoutMode=='two')
+ $this->_put('/PageLayout /TwoColumnLeft');
+}
+
+protected function _putheader()
+{
+ $this->_put('%PDF-'.$this->PDFVersion);
+}
+
+protected function _puttrailer()
+{
+ $this->_put('/Size '.($this->n+1));
+ $this->_put('/Root '.$this->n.' 0 R');
+ $this->_put('/Info '.($this->n-1).' 0 R');
+}
+
+protected function _enddoc()
+{
+ $this->CreationDate = time();
+ $this->_putheader();
+ $this->_putpages();
+ $this->_putresources();
+ // Info
+ $this->_newobj();
+ $this->_put('<<');
+ $this->_putinfo();
+ $this->_put('>>');
+ $this->_put('endobj');
+ // Catalog
+ $this->_newobj();
+ $this->_put('<<');
+ $this->_putcatalog();
+ $this->_put('>>');
+ $this->_put('endobj');
+ // Cross-ref
+ $offset = $this->_getoffset();
+ $this->_put('xref');
+ $this->_put('0 '.($this->n+1));
+ $this->_put('0000000000 65535 f ');
+ for($i=1;$i<=$this->n;$i++)
+ $this->_put(sprintf('%010d 00000 n ',$this->offsets[$i]));
+ // Trailer
+ $this->_put('trailer');
+ $this->_put('<<');
+ $this->_puttrailer();
+ $this->_put('>>');
+ $this->_put('startxref');
+ $this->_put($offset);
+ $this->_put('%%EOF');
+ $this->state = 3;
+}
+
+// ********* NEW FUNCTIONS *********
+// Converts UTF-8 strings to UTF16-BE.
+protected function UTF8ToUTF16BE($str, $setbom=true) {
+ $outstr = "";
+ if ($setbom) {
+ $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
+ }
+ $outstr .= mb_convert_encoding($str, 'UTF-16BE', 'UTF-8');
+ return $outstr;
+}
+
+// Converts UTF-8 strings to codepoints array
+protected function UTF8StringToArray($str) {
+ $out = array();
+ $len = strlen($str);
+ for ($i = 0; $i < $len; $i++) {
+ $uni = -1;
+ $h = ord($str[$i]);
+ if ( $h <= 0x7F )
+ $uni = $h;
+ elseif ( $h >= 0xC2 ) {
+ if ( ($h <= 0xDF) && ($i < $len -1) )
+ $uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F);
+ elseif ( ($h <= 0xEF) && ($i < $len -2) )
+ $uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6
+ | (ord($str[++$i]) & 0x3F);
+ elseif ( ($h <= 0xF4) && ($i < $len -3) )
+ $uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12
+ | (ord($str[++$i]) & 0x3F) << 6
+ | (ord($str[++$i]) & 0x3F);
+ }
+ if ($uni >= 0) {
+ $out[] = $uni;
+ }
+ }
+ return $out;
+}
+
+}
+?>