Uzytkownik "Alex Shi" <ch****@stonix. com> napisal w wiadomosci
news:F8******** **********@nntp-post.primus.ca. ..
In php, is there a way obtain the width of a charactor of a certain font?
Alex
--
=============== =============== =============== =====
Cell Phone Batteries at 30-50%+ off retail prices!
http://www.pocellular.com
=============== =============== =============== =====
Yes, but it's rather painful. With Type 1 fonts it's easier, as the width
info is stored in a plain text file. With TrueType fonts it's quite hard.
Look at the PDF functions and see if there's a function that yields the
width of a string. Some time ago I've written a PDF layout engine in PHP.
Exactly how it works I can't remember. There's a PDFTrueTypeFont class in
there. The GetTextWidth method should give you the width of a text string,
while GetCharWidth method--the width of a char.
[begin code flood]
<?php
$TTFDirectory = false;
$Type1Directory = false;
$imageDirectory = false;
$charMapDirecto ry = false;
class PDFObj {
var $id;
var $offset;
function SetId($id) {
$this->id = $id;
}
function SetOffset($pos) {
$this->offset = $pos;
}
function Registered() {
return $this->id ? True : False;
}
function Reference() {
return "$this->id 0 R";
}
function Written() {
return $this->offset ? True : False;
}
function Output(&$pdf_fi le) {
$pdf_file->Write("$this->id 0 obj\n");
$pdf_file->Write($this->ToString());
$pdf_file->Write("\nendob j\n");
}
}
class PDFBoolean extends PDFObj {
var $value;
function PDFBoolean($val ue) {
$this->value = $value;
}
function ToString() {
return ($this->value) ? 'true' : 'false';
}
}
class PDFInteger extends PDFObj {
var $value;
function PDFInteger($val ue) {
$this->value = $value;
}
function Add($value) {
$this->value += $value;
}
function ToString() {
return (string) $this->value;
}
}
class PDFHex extends PDFInteger {
function PDFHex($value) {
$this->PDFInteger($va lue);
}
function ToString() {
if($this->value < 256) $format = '<%02x>';
else if($this->value < 65536) $format = '<%04x>';
else $format = '<%08x>';
return sprintf($format , $this->value);
}
}
$string_escape_ table = array('(' => '\\(', ')' => '\\)', '\\' => '\\\\',
"\n" => '\n', "\r" => '\r', "\t" => '\t');
class PDFString extends PDFObj {
var $value;
function PDFString($valu e) {
$this->value = $value;
}
function Prepend($obj) {
$this->value = ((get_class($ob j) == 'pdfstring') ? $obj->value : $obj) .
$this->value;
}
function Append($obj) {
$this->value .= ((get_class($ob j) == 'pdfstring') ? $obj->value : $obj);
}
function IsEmpty() {
return (strlen($this->value) == 0);
}
function ToString() {
global $string_escape_ table;
return '(' . strtr($this->value, $string_escape_ table) . ')';
}
}
class PDFName extends PDFObj {
var $value;
function PDFName($value) {
$this->value = $value;
}
function ToString() {
return "/$this->value";
}
}
function Name($name) {
return new PDFName($name);
}
class PDFCollection extends PDFObj {
function NewObj($value) {
if(is_numeric($ value))
{
return new PDFInteger($val ue);
}
else if(is_string($v alue))
{
return new PDFString($valu e);
}
else if(is_array($va lue))
{
if(sizeof($valu e) > 0 && !isset($value[0])) // is associative array
{
$new_array = new PDFDictionary() ;
foreach($value as $akey => $avalue)
{
if(is_object($a value))
{
$new_array->AssignObj($ake y, $value[$akey]);
}
else
{
$new_array->Assign($akey , $avalue);
}
}
}
else
{
$new_array = new PDFArray();
foreach($value as $akey => $avalue)
{
if(is_object($a value))
{
$new_array->AddObj($valu e[$akey]);
}
else
{
$new_array->Add($avalue) ;
}
}
}
return $new_array;
}
else
{
return new PDFBoolean($val ue);
}
}
}
class PDFDictionary extends PDFCollection {
var $values;
function PDFDictionary($ type = false) {
$this->values = array();
if($type)
{
$this->values['Type'] = new PDFName($type);
}
}
function Assign($key, $value) {
if($value !== False)
{
if(is_object($v alue))
{
$this->values[$key] = $value;
}
else
{
$this->values[$key] = PDFCollection:: NewObj($value);
}
}
}
function AssignObj($key, &$obj) {
if(!is_object($ obj))
{
die("$key not an object!");
}
$this->values[$key] = &$obj;
}
function IsAssigned($key ) {
return isset($this->values[$key]);
}
function Unassign($key) {
unset($this->values[$key]);
}
function &Object($key ) {
return $this->values[$key];
}
function ToString() {
$s = '<<';
foreach($this->values as $key => $obj)
{
$s .= " /$key ";
if($obj->Registered() )
{
$s .= $obj->Reference();
}
else
{
$s .= $obj->ToString($valu e);
}
}
$s .= ' >>';
return $s;
}
}
class PDFArray extends PDFCollection {
var $values;
function PDFArray() {
$this->values = array();
}
function Count() {
return sizeof($this->values);
}
function Add($value) {
if($value !== False)
{
$index = count($this->values);
$this->values[] = PDFCollection:: NewObj($value);
return $index;
}
}
function AddObj(&$obj) {
$index = count($this->values);
$this->values[] = &$obj;
return $index;
}
function InsertObj(&$obj , $index) {
array_splice($t his->values, $index, 0, array(0));
$this->values[$index] = &$obj;
return $index;
}
function IsEmpty() {
return (sizeof($this->values) == 0);
}
function ToString() {
$s = '[';
foreach($this->values as $obj)
{
$s .= ' ';
if($obj->Registered() )
{
$s .= $obj->Reference();
}
else
{
$s .= $obj->ToString();
}
}
$s .= ' ]';
return $s;
}
}
class PDFStream extends PDFDictionary {
var $data;
function PDFStream($type = false) {
$this->PDFDictionary( $type);
}
function Write($s) {
$this->data .= $s;
}
function Output(&$pdf_fi le) {
$this->Assign('Length ', strlen($this->data));
$pdf_file->Write("$this->id 0 obj\n");
$pdf_file->Write($this->ToString());
$pdf_file->Write("\nstrea m\n");
$pdf_file->Write($this->data);
$pdf_file->Write("endstre am");
$pdf_file->Write("\nendob j\n");
}
}
class PDFFileStream extends PDFStream {
function PDFFileStream($ filePath) {
$this->PDFStream();
$filesize = filesize($fileP ath);
$file = fopen($filePath , "rb");
$this->data = fread($file, $filesize);
fclose($file);
}
}
if(function_exi sts('gzdeflate' ))
{
class PDFFlateStream extends PDFStream {
var $data;
function PDFFlateStream( ) {
$this->PDFStream();
$this->Assign('Filter ', Name('FlateDeco de'));
}
function Output(&$pdf_fi le) {
$deflated_data = gzdeflate($this->data);
$length = strlen($deflate d_data) + 2;
$this->Assign('Length ', $length);
$pdf_file->Write("$this->id 0 obj\n");
$pdf_file->Write($this->ToString());
$pdf_file->Write("\nstrea m\n");
$pdf_file->Write("H‰");
$pdf_file->Write($deflate d_data);
$pdf_file->Write("endstre am");
$pdf_file->Write("\nendob j\n");
}
}
class PDFFlateFileStr eam extends PDFFlateStream {
function PDFFlateFileStr eam($filePath) {
$this->PDFFlateStream ();
$filesize = filesize($fileP ath);
$file = fopen($filePath , "rb");
$this->data = fread($file, $filesize);
fclose($file);
}
}
}
else
{
class PDFFlateStream extends PDFStream {
var $data;
var $temp_file;
var $temp_filename;
function PDFFlateStream( ) {
$this->PDFStream();
$this->Assign('Filter ', Name('FlateDeco de'));
$this->temp_filenam e = tempnam('', 'strm');
$this->temp_file = fopen($this->temp_filenam e, "wb");
}
function Write($s) {
fwrite($this->temp_file, $s);
}
function Output(&$pdf_fi le) {
fclose($this->temp_file);
`gzip -9nf $this->temp_filename` ;
$this->temp_filenam e .= '.gz';
$filesize = filesize($this->temp_filename) ;
$gz = fopen($this->temp_filenam e, 'rb');
fseek($gz, 10);
$deflated_data = fread($gz, $filesize - 14);
fclose($gz);
unlink($this->temp_filename) ;
$length = $filesize - 14 + 2;
$this->Assign('Length ', $length);
$pdf_file->Write("$this->id 0 obj\n");
$pdf_file->Write($this->ToString());
$pdf_file->Write("\nstrea m\n");
$pdf_file->Write("H‰");
$pdf_file->Write($deflate d_data);
$pdf_file->Write("endstre am");
$pdf_file->Write("\nendob j\n");
}
}
class PDFFlateFileStr eam extends PDFStream {
var $filePath;
function PDFFlateFileStr eam($filePath) {
$this->PDFStream();
$this->filePath = $filePath;
}
function Output(&$pdf_fi le) {
$gzip_data = `gzip -9cnf $this->filePath`;
$deflated_data = substr($gzip_da ta, 10, -4);
$length = strlen($deflate d_data) + 2;
$this->Assign('Length ', $length);
$pdf_file->Write("$this->id 0 obj\n");
$pdf_file->Write($this->ToString());
$pdf_file->Write("\nstrea m\n");
$pdf_file->Write("H‰");
$pdf_file->Write($deflate d_data);
$pdf_file->Write("endstre am");
$pdf_file->Write("\nendob j\n");
}
}
}
class PDFTrueTypeFile Stream extends PDFFlateFileStr eam {
function PDFTrueTypeFile Stream($filePat h) {
$this->PDFFlateFileSt ream($filePath) ;
$this->Assign('Length 1', strlen($this->data));
}
}
class PDFType1FileStr eam extends PDFFileStream {
function PDFType1FileStr eam($filePath) {
$this->PDFFileStream( $filePath);
$encrypted_star t_index = strpos($this->data, "currentfil e eexec\r") + 18;
$encrypted_end_ index = strpos($this->data,
"00000000000000 000000000000000 000000000000000 000000000000000 00000");
$this->Assign('Length 1', $encrypted_star t_index);
$this->Assign('Length 2', $encrypted_end_ index - $encrypted_star t_index);
$this->Assign('Length 3', strlen($this->data) - $encrypted_end_ index);
}
}
class PDFRect extends PDFObj {
var $left;
var $top;
var $right;
var $bottom;
var $width;
var $height;
function PDFRect($left, $top, $right, $bottom) {
$this->left = (int) $left;
$this->top = (int) $top;
$this->right = (int) $right;
$this->bottom = (int) $bottom;
$this->width = $right - $left;
$this->height = $top - $bottom;
}
function ToString() {
return "[$this->left $this->top $this->right $this->bottom]";
}
}
class PDFDate extends PDFString {
function PDFDate($value = False) {
if(!$value) $value = time();
$this->PDFString('D :' . gmdate('YmdHis' ) . 'Z');
}
}
define('BLACK', 0);
define('WHITE', 1);
define('LIGHTGR AY', 0.9);
define('DARKGRA Y', 0.4);
class PDFGray extends PDFObj {
var $level;
function PDFGray($level) {
$this->level = $level;
}
function ToStringStrokin g() {
return "$this->level G\n";
}
function ToStringNonStro king() {
return "$this->level g\n";
}
}
class PDFRGB extends PDFObj {
var $r;
var $g;
var $b;
function PDFRGB($r, $g, $b) {
$this->r = $r;
$this->g = $g;
$this->b = $b;
}
function ToStringStrokin g() {
return "$this->r $this->g $this->b RG\n";
}
function ToStringNonStro king() {
return "$this->r $this->g $this->b rg\n";
}
}
function ToUnicodeStr($s , $encoding) {
$map = GetCharSetUnico deMap($encoding );
$us = "\xfe\xff";
for($i = 0, $len = strlen($s); $i < $len; $i++)
{
$c = ord($s[$i]);
$uc = $map[$c];
$us .= chr($uc >> 8) . chr($uc & 0x00FF);
}
return $us;
}
class PDFBookmark extends PDFDictionary {
function PDFBookmark(&$p age, $title, $encoding = 'WinAnsiEncodin g', $top =
false) {
$this->PDFDictionary( );
$this->Assign('Title' , ToUnicodeStr($t itle, $encoding));
$dest = ($top) ? array(&$page, Name('FitH'), $top) : array(&$page,
Name('Fit'));
$this->Assign('Dest ', $dest);
}
function SetParent(&$par ent) {
$this->Assign('Parent ', $parent);
if(!$parent->IsAssigned('Fi rst'))
{
$parent->AssignObj('Fir st', $this);
}
if($parent->IsAssigned('La st'))
{
$last =& $parent->Object('Last') ;
$last->AssignObj('Nex t', $this);
$this->AssignObj('Pre v', $last);
}
$parent->AssignObj('Las t', $this);
}
}
class PDFWebBookmark extends PDFDictionary {
function PDFWebBookmark( $title, $url) {
$this->PDFDictionary( );
$this->Assign('Title' , $title);
$action = new PDFDictionary() ;
$action->Assign('S', Name('URI'));
$action->Assign('URI' , $url);
$this->AssignObj('A ', $action);
}
function SetParent(&$par ent) {
$this->Assign('Parent ', $parent);
if(!$parent->IsAssigned('Fi rst'))
{
$parent->AssignObj('Fir st', $this);
}
if($parent->IsAssigned('La st'))
{
$last =& $parent->Object('Last') ;
$last->AssignObj('Nex t', $this);
$this->AssignObj('Pre v', $last);
}
$parent->AssignObj('Las t', $this);
}
}
class PDFPageLabel extends PDFDictionary {
function PDFPageLabel($s tyle, $prefix = False, $encoding =
'WinAnsiEncodin g') {
if($style)
{
$this->Assign('S', Name($style));
}
if($prefix)
{
$this->Assign('P', ToUnicodeStr($p refix, $encoding));
}
}
}
class PDFKernedString extends PDFArray {
function PDFKernedString ($value = False, $offset = False) {
$this->PDFArray();
if($value)
{
$this->Add($value);
if($offset) $this->Add($offset) ;
}
}
function Prepend($obj) {
if(sizeof($this->values) > 0)
{
$this->values[0]->Prepend($obj );
}
else
{
$this->Add($obj);
}
}
function Append($obj, $offset = False) {
$last_index = sizeof($this->values) - 1;
if($last_index < 0 || get_class($this->values[$last_index]) ==
'pdfinteger')
{
$this->Add($obj);
}
else
{
if(get_class($o bj) == 'pdfkernedstrin g')
{
$this->values[$last_index]->Append($obj->values[0]);
for($i = 1; $i < sizeof($obj->values); $i++)
{
$this->AddObj($obj->values[$i]);
}
}
else
{
$this->values[$last_index]->Append($obj) ;
}
}
if($offset)
{
$this->Add(- $offset);
}
}
}
class PDFFont extends PDFDictionary {
var $ref_name;
var $widths;
var $rExtents;
var $kerning_pairs;
var $properties;
function PDFFont() {
global $font_number;
$font_number++;
$ref_name = "F$font_number" ;
$this->PDFDictionary( 'Font');
$this->Assign('Name ', Name($ref_name) );
$this->ref_name = $ref_name;
}
function Transform($text , $size) {
$len = strlen($text);
if($this->kerning_pair s)
{
for($i = 0; $i < $len; $i++)
{
$char = ord($text{$i});
$width += $this->widths[$char];
if($kerning_off sets = $this->kerning_pair s[$char])
{
$next_index = $i + 1;
$next_char = ord($text{$next _index});
if($offset = $kerning_offset s[$next_char])
{
if(!$kerned_str ing) $kerned_string = new PDFKernedString ();
$kerned_string->Append(substr( $text, $last_kerned_ch ar, $next_index -
$last_kerned_ch ar), $offset);
$last_kerned_ch ar = $next_index;
$width += $offset;
}
}
}
}
else
{
for($i = 0; $i < $len; $i++)
{
$char = ord($text{$i});
$width += $this->widths[$char];
}
}
$pt_width = $width * $size / 1000;
$r_extent = $this->rExtents[ord($text{$len - 1})] * $size / 1000;
if($kerned_stri ng)
{
$kerned_string->Append(substr( $text, $last_kerned_ch ar));
return array($kerned_s tring, $pt_width, $r_extent);
}
else
{
return array(new PDFString($text ), $pt_width, $r_extent);
}
}
function GetCharWidth($c har, $size) {
return $this->widths[ord($char)] * $size / 1000;
}
function GetTextWidth($t ext, $size) {
$len = strlen($text);
if($this->kerning_pair s)
{
for($i = 0; $i < $len; $i++)
{
$char = ord($text{$i});
$width += $this->widths[$char];
if($kerning_off sets = $this->kerning_pair s[$char])
{
$next_index = $i + 1;
$next_char = ord($text{$next _index});
if($offset = $kerning_offset s[$next_char])
{
$width += $offset;
}
}
}
}
else
{
for($i = 0; $i < $len; $i++)
{
$char = ord($text{$i});
$width += $this->widths[$char];
}
}
$pt_width = $width * $size / 1000;
return $pt_width;
}
}
function ParseCPGFile($f ilePath) {
$char_to_unicod e_map = array();
$lines = file($filePath) ;
foreach($lines as $line)
{
if(list($unicod eHex, $charCodeHex) = explode("\t", $line))
{
if(!strncmp($un icodeHex, '0x', 2) && !strncmp($charC odeHex, '0x', 2))
{
$unicode = hexdec(substr($ unicodeHex, 2));
$charCode = hexdec(substr($ charCodeHex, 2));
$char_to_unicod e_map[$charCode] = $unicode;
}
}
}
return $char_to_unicod e_map;
}
function ParseENCFile($f ilePath) {
$char_to_name_m ap = array();
$lines = file($filePath) ;
foreach($lines as $line)
{
if(list($name, $charCode) = preg_split('/\\s+/', $line, -1,
PREG_SPLIT_NO_E MPTY))
{
if($name{0} != '%' && is_numeric($cha rCode))
{
$char_to_name_m ap[(int) $charCode] = $name;
}
}
}
return $char_to_name_m ap;
}
$winAnsiEncodin gNameMap = array(
32 => 'space', 33 => 'exclam', 34 => 'quotedbl', 35 => 'numbersign',
36 => 'dollar', 37 => 'percent', 38 => 'ampersand', 39 => 'quotesingle',
40 => 'parenleft', 41 => 'parenright', 42 => 'asterisk', 43 => 'plus',
44 => 'comma', 45 => 'hyphen', 46 => 'period', 47 => 'slash',
48 => 'zero', 49 => 'one', 50 => 'two', 51 => 'three',
52 => 'four', 53 => 'five', 54 => 'six', 55 => 'seven',
56 => 'eight', 57 => 'nine', 58 => 'colon', 59 => 'semicolon',
60 => 'less', 61 => 'equal', 62 => 'greater', 63 => 'question',
64 => 'at', 65 => 'A', 66 => 'B', 67 => 'C',
68 => 'D', 69 => 'E', 70 => 'F', 71 => 'G',
72 => 'H', 73 => 'I', 74 => 'J', 75 => 'K',
76 => 'L', 77 => 'M', 78 => 'N', 79 => 'O',
80 => 'P', 81 => 'Q', 82 => 'R', 83 => 'S',
84 => 'T', 85 => 'U', 86 => 'V', 87 => 'W',
88 => 'X', 89 => 'Y', 90 => 'Z', 91 => 'bracketleft',
92 => 'backslash', 93 => 'bracketright', 94 => 'asciicircum', 95 =>
'underscore',
96 => 'grave', 97 => 'a', 98 => 'b', 99 => 'c',
100 => 'd', 101 => 'e', 102 => 'f', 103 => 'g',
104 => 'h', 105 => 'i', 106 => 'j', 107 => 'k',
108 => 'l', 109 => 'm', 110 => 'n', 111 => 'o',
112 => 'p', 113 => 'q', 114 => 'r', 115 => 's', 116 => 't', 117 => 'u',
118 => 'v', 119 => 'w',
120 => 'x', 121 => 'y', 122 => 'z', 123 => 'braceleft',
124 => 'bar', 125 => 'braceright', 126 => 'asciitilde', 128 => 'Euro',
130 => 'quotesinglbase ',131 => 'florin', 132 => 'quotedblbase', 133 =>
'ellipsis',
134 => 'dagger', 135 => 'daggerdbl', 136 => 'circumflex', 137 =>
'perthousand',
138 => 'Scaron', 139 => 'guilsinglleft' , 140 => 'OE', 142 => 'Zcaron',
145 => 'quoteleft', 146 => 'quoteright', 147 => 'quotedblleft', 148 =>
'quotedblright' ,
150 => 'endash', 151 => 'emdash', 152 => 'tilde', 153 => 'trademark',
154 => 'scaron', 155 => 'guilsinglright ',156 => 'oe', 158 => 'zcaron',
159 => 'Ydieresis', 161 => 'exclamdown', 162 => 'cent', 163 => 'sterling',
164 => 'currency1', 165 => 'yen', 166 => 'brokenbar', 167 => 'section',
168 => 'dieresis', 169 => 'copyright', 170 => 'ordfeminine', 171 =>
'guillemotleft' ,
172 => 'logicalnot', 174 => 'registered', 175 => 'macron', 176 => 'degree',
177 => 'plusminus', 178 => 'twosuperior', 179 => 'threesuperior' , 180 =>
'acute',
181 => 'mu', 182 => 'paragraph', 183 => 'periodcentered ',184 => 'cedilla',
185 => 'onesuperior', 186 => 'ordmasculine', 187 => 'guillemotright ',188 =>
'onequarter',
189 => 'onehalf', 190 => 'threequarters' , 191 => 'questiondown', 192 =>
'Agrave',
193 => 'Aacute', 194 => 'Acircumflex', 195 => 'Atilde', 196 => 'Adieresis',
197 => 'Aring', 198 => 'AE', 199 => 'Ccedilla', 200 => 'Egrave',
201 => 'Eacute', 202 => 'Ecircumflex', 203 => 'Edieresis', 204 => 'Igrave',
205 => 'Iacute', 206 => 'Icircumflex', 207 => 'Idieresis', 208 => 'Eth',
209 => 'Ntilde', 210 => 'Ograve', 211 => 'Oacute', 212 => 'Ocircumflex',
213 => 'Otilde', 214 => 'Odieresis', 215 => 'multiply', 216 => 'Oslash',
217 => 'Ugrave', 218 => 'Uacute', 219 => 'Ucircumflex', 220 => 'Udieresis',
221 => 'Yacute', 222 => 'Thorn', 223 => 'germandbls', 224 => 'agrave',
225 => 'aacute', 226 => 'acircumflex', 227 => 'atilde', 228 => 'adieresis',
229 => 'aring', 230 => 'ae', 231 => 'ccedilla', 232 => 'egrave',
233 => 'eacute', 234 => 'ecircumflex', 235 => 'edieresis', 236 => 'igrave',
237 => 'iacute', 238 => 'icircumflex', 239 => 'idieresis', 240 => 'eth',
241 => 'ntilde', 242 => 'ograve', 243 => 'oacute', 244 => 'ocircumflex',
245 => 'otilde', 246 => 'odieresis', 247 => 'divide', 248 => 'oslash',
249 => 'ugrave', 250 => 'uacute', 251 => 'ucircumflex', 252 => 'udieresis',
253 => 'yacute', 254 => 'thorn', 255 => 'ydieresis');
$winAnsiEncodin gUnicodeMap = array(
0x20 => 0x20, 0x21 => 0x21, 0x22 => 0x22, 0x23 => 0x23,
0x24 => 0x24, 0x25 => 0x25, 0x26 => 0x26, 0x27 => 0x27,
0x28 => 0x28, 0x29 => 0x29, 0x2a => 0x2a, 0x2b => 0x2b,
0x2c => 0x2c, 0x2d => 0x2d, 0x2e => 0x2e, 0x2f => 0x2f,
0x30 => 0x30, 0x31 => 0x31, 0x32 => 0x32, 0x33 => 0x33,
0x34 => 0x34, 0x35 => 0x35, 0x36 => 0x36, 0x37 => 0x37,
0x38 => 0x38, 0x39 => 0x39, 0x3a => 0x3a, 0x3b => 0x3b,
0x3c => 0x3c, 0x3d => 0x3d, 0x3e => 0x3e, 0x3f => 0x3f,
0x40 => 0x40, 0x41 => 0x41, 0x42 => 0x42, 0x43 => 0x43,
0x44 => 0x44, 0x45 => 0x45, 0x46 => 0x46, 0x47 => 0x47,
0x48 => 0x48, 0x49 => 0x49, 0x4a => 0x4a, 0x4b => 0x4b,
0x4c => 0x4c, 0x4d => 0x4d, 0x4e => 0x4e, 0x4f => 0x4f,
0x50 => 0x50, 0x51 => 0x51, 0x52 => 0x52, 0x53 => 0x53,
0x54 => 0x54, 0x55 => 0x55, 0x56 => 0x56, 0x57 => 0x57,
0x58 => 0x58, 0x59 => 0x59, 0x5a => 0x5a, 0x5b => 0x5b,
0x5c => 0x5c, 0x5d => 0x5d, 0x5e => 0x5e, 0x5f => 0x5f,
0x60 => 0x60, 0x61 => 0x61, 0x62 => 0x62, 0x63 => 0x63,
0x64 => 0x64, 0x65 => 0x65, 0x66 => 0x66, 0x67 => 0x67,
0x68 => 0x68, 0x69 => 0x69, 0x6a => 0x6a, 0x6b => 0x6b,
0x6c => 0x6c, 0x6d => 0x6d, 0x6e => 0x6e, 0x6f => 0x6f,
0x70 => 0x70, 0x71 => 0x71, 0x72 => 0x72, 0x73 => 0x73,
0x74 => 0x74, 0x75 => 0x75, 0x76 => 0x76, 0x77 => 0x77,
0x78 => 0x78, 0x79 => 0x79, 0x7a => 0x7a, 0x7b => 0x7b,
0x7c => 0x7c, 0x7d => 0x7d, 0x7e => 0x7e, 0x80 => 0x20ac,
0x82 => 0x201a, 0x83 => 0x192, 0x84 => 0x201e, 0x85 => 0x2026,
0x86 => 0x2020, 0x87 => 0x2021, 0x88 => 0x2c6, 0x89 => 0x2030,
0x8a => 0x160, 0x8b => 0x2039, 0x8c => 0x152, 0x8e => 0x17d,
0x91 => 0x2018, 0x92 => 0x2019, 0x93 => 0x201c, 0x94 => 0x201d,
0x96 => 0x2013, 0x97 => 0x2014, 0x98 => 0x2dc, 0x99 => 0x2122,
0x9a => 0x161, 0x9b => 0x203a, 0x9c => 0x153, 0x9e => 0x17e,
0x9f => 0x178, 0xa1 => 0xa1, 0xa2 => 0xa2, 0xa3 => 0xa3,
0xa4 => 0xa4, 0xa5 => 0xa5, 0xa6 => 0xa6, 0xa7 => 0xa7,
0xa8 => 0xa8, 0xa9 => 0xa9, 0xaa => 0xaa, 0xab => 0xab,
0xac => 0xac, 0xae => 0xae, 0xaf => 0xaf, 0xb0 => 0xb0,
0xb1 => 0xb1, 0xb2 => 0xb2, 0xb3 => 0xb3, 0xb4 => 0xb4,
0xb5 => 0xb5, 0xb6 => 0xb6, 0xb7 => 0xb7, 0xb8 => 0xb8,
0xb9 => 0xb9, 0xba => 0xba, 0xbb => 0xbb, 0xbc => 0xbc,
0xbd => 0xbd, 0xbe => 0xbe, 0xbf => 0xbf, 0xc0 => 0xc0,
0xc1 => 0xc1, 0xc2 => 0xc2, 0xc3 => 0xc3, 0xc4 => 0xc4,
0xc5 => 0xc5, 0xc6 => 0xc6, 0xc7 => 0xc7, 0xc8 => 0xc8,
0xc9 => 0xc9, 0xca => 0xca, 0xcb => 0xcb, 0xcc => 0xcc,
0xcd => 0xcd, 0xce => 0xce, 0xcf => 0xcf, 0xd0 => 0xd0,
0xd1 => 0xd1, 0xd2 => 0xd2, 0xd3 => 0xd3, 0xd4 => 0xd4,
0xd5 => 0xd5, 0xd6 => 0xd6, 0xd7 => 0xd7, 0xd8 => 0xd8,
0xd9 => 0xd9, 0xda => 0xda, 0xdb => 0xdb, 0xdc => 0xdc,
0xdd => 0xdd, 0xde => 0xde, 0xdf => 0xdf, 0xe0 => 0xe0,
0xe1 => 0xe1, 0xe2 => 0xe2, 0xe3 => 0xe3, 0xe4 => 0xe4,
0xe5 => 0xe5, 0xe6 => 0xe6, 0xe7 => 0xe7, 0xe8 => 0xe8,
0xe9 => 0xe9, 0xea => 0xea, 0xeb => 0xeb, 0xec => 0xec,
0xed => 0xed, 0xee => 0xee, 0xef => 0xef, 0xf0 => 0xf0,
0xf1 => 0xf1, 0xf2 => 0xf2, 0xf3 => 0xf3, 0xf4 => 0xf4,
0xf5 => 0xf5, 0xf6 => 0xf6, 0xf7 => 0xf7, 0xf8 => 0xf8,
0xf9 => 0xf9, 0xfa => 0xfa, 0xfb => 0xfb, 0xfc => 0xfc,
0xfd => 0xfd, 0xfe => 0xfe, 0xff => 0xff );
$charSetNameMap s = array('WinAnsiE ncoding' => $winAnsiEncodin gNameMap);
$charSetUnicode Maps = array('WinAnsiE ncoding' =>
$winAnsiEncodin gUnicodeMap);
function GetCharSetUnico deMap($charSet) {
global $charMapDirecto ry, $charSetUnicode Maps;
if(!($map = $charSetUnicode Maps[$charSet]))
{
if(!$charMapDir ectory)
{
die("GetCharSet UnicodeMap: global variable \$charMapDirect ory not set");
}
$map = ParseCPGFile("$ charMapDirector y/$charSet.cpg");
$charSetUnicode Maps[$charSet] = $map;
}
return $map;
}
function GetCharSetNameM ap($charSet) {
global $charMapDirecto ry, $charSetNameMap s;
if(!($map = $charSetNameMap s[$charSet]))
{
if(!$charMapDir ectory)
{
die("GetCharSet NameMap: global variable \$charMapDirect ory not set");
}
$map = ParseENCFile("$ charMapDirector y/$charSet.enc");
$charSetNameMap s[$charSet] = $map;
}
return $map;
}
function GetInverseMap($ map) {
$imap = array();
foreach($map as $apple => $orange)
{
if(!$imap[$orange]) !$imap[$orange] = array();
$imap[$orange][] = $apple;
}
return $imap;
}
class PDFEncodingCMap extends PDFFlateStream {
var $CIDSystemInfo;
function PDFEncodingCMap ($charSet, $char_to_glyph_ map) {
$this->PDFFlateStream ('CMap');
$this->Assign('CMapNa me', $charSet);
$this->CIDSystemInf o = new PDFDictionary() ;
$this->CIDSystemInf o->Assign('Regist ry', $charSet);
$this->CIDSystemInf o->Assign('Orderi ng', $charSet);
$this->CIDSystemInf o->Assign('Supple ment', 0);
$this->AssignObj('CID SystemInfo', $this->CIDSystemInfo) ;
$this->Write(<<<CMAP_ HEADER
/CIDInit /ProcSet findresource
begin
12 dict
begin
begincmap
/CIDSystemInfo <</Registry ($charSet) /Ordering ($charSet) /Supplement 0 >>
def
/CMapName /$charSet def 1
/CMapType 1 def
CMAP_HEADER
);
$this->Write("1 begincodespacer ange\n<00> <FF>\nendcodesp acerange\n");
$glyphRangeStar t = false;
$nextGlyphId = -1;
$nextCharCode = -1;
$cidRangeEntrie s = array();
foreach($char_t o_glyph_map as $charCode => $glyphId)
{
if($nextCharCod e == $charCode && $glyphId == $nextGlyphId)
{
$rangeEnd = $charCode;
$nextGlyphId++;
$nextCharCode++ ;
}
else
{
if($glyphRangeS tart)
{
$cidRangeEntrie s[] = sprintf('<%02x> <%02x> %d', $rangeStart, $rangeEnd,
$glyphRangeStar t);
$glyphRangeStar t = false;
$nextGlyphId = -1;
$nextCharCode = -1;
}
if($glyphId)
{
$rangeStart = $charCode;
$rangeEnd = $charCode;
$glyphRangeStar t = $glyphId;
$nextGlyphId = $glyphId + 1;
$nextCharCode = $charCode + 1;
}
}
}
if($glyphRangeS tart)
{
$cidRangeEntrie s[] = sprintf('<%02x> <%02x> %d', $rangeStart, $rangeEnd,
$glyphRangeStar t);
}
for($j = 0, $j_bound = count($cidRange Entries); $j < $j_bound; $j += 100)
{
$c = min(100, $j_bound - $j);
$this->Write("$c begincidrange\n ");
$this->Write(implode( "\n", array_slice($ci dRangeEntries, $j, $c)));
$this->Write("\nendci drange\n");
}
$this->Write(<<<CMAP_ TRAILER
endcmap
CMapName
currentdict
/CMap
defineresource
pop
end
end
CMAP_TRAILER
);
}
}
class PDFToUnicodeCMa p extends PDFFlateStream {
function PDFToUnicodeCMA P($charSet, $char_to_unicod e_map) {
$this->PDFFlateStream ();
$this->Write(<<<CMAP_ HEADER
/CIDInit /ProcSet findresource
begin
12 dict
begin
begincmap
/CIDSystemInfo <</Registry ($charSet) /Ordering ($charSet) /Supplement 0 >>
def
/CMapName /$charSet def 1
/CMapType 1 def
CMAP_HEADER
);
$this->Write("1 begincodespacer ange\n<00> <FF>\nendcodesp acerange\n");
$unicodeRangeSt art = false;
$nextUnicode = -1;
$nextCharCode = -1;
$cidRangeEntrie s = array();
foreach($char_t o_unicode_map as $charCode => $unicode)
{
if($charCode == $nextCharCode && $unicode == $nextUnicode)
{
$rangeEnd = $charCode;
$nextUnicode++;
$nextCharCode++ ;
}
else
{
if($unicodeRang eStart)
{
$cidRangeEntrie s[] = sprintf('<%02x> <%02x> <%04x>', $rangeStart,
$rangeEnd, $unicodeRangeSt art);
$unicodeRangeSt art = false;
$nextUnicode = -1;
$nextCharCode = -1;
}
if($unicode)
{
$rangeStart = $charCode;
$rangeEnd = $charCode;
$unicodeRangeSt art = $unicode;
$nextUnicode = $unicode + 1;
$nextCharCode = $charCode + 1;
}
}
}
if($unicodeRang eStart)
{
$cidRangeEntrie s[] = sprintf('<%02x> <%02x> <%04x>', $rangeStart,
$rangeEnd, $unicodeRangeSt art);
}
for($j = 0, $j_bound = count($cidRange Entries); $j < $j_bound; $j += 100)
{
$c = min(100, $j_bound - $j);
$this->Write("$c beginbfrange\n" );
$this->Write(implode( "\n", array_slice($ci dRangeEntries, $j, $c)));
$this->Write("\nendbf range\n");
}
$this->Write(<<<CMAP_ TRAILER
endcmap
CMapName
currentdict
/CMap
defineresource
pop
end
end
CMAP_TRAILER
);
}
}
class PDFType1Font extends PDFFont {
var $encoding;
var $toUnicode;
var $widthArray;
var $descriptor;
var $stream;
function PDFType1Font($n ame, $charSet = 'WinAnsiEncodin g', $builtIn =
false) {
global $Type1Directory ;
if(!$Type1Direc tory)
{
die("PDFType1Fo nt: global variable \$Type1Director y not set");
}
$this->PDFFont();
if($charSet != 'WinAnsiEncodin g') $char_to_unicod e_map =
GetCharSetUnico deMap($charSet) ;
$char_to_name_m ap = GetCharSetNameM ap($charSet);
$name_to_char_m ap = GetInverseMap($ char_to_name_ma p);
$afm_file = fopen("$Type1Di rectory/$name.afm", "rt");
$properties = array();
$widths = array();
$rExtents = array();
$kerning_pairs = array();
$name_to_index_ map = array();
while($line = fgets($afm_file , 256))
{
if($in_char_met rics)
{
if(strstr($line , 'EndCharMetrics '))
{
$in_char_metric s = False;
}
else
{
$metric_items = explode(';', $line);
$char_name = substr($metric_ items[2], 3, -1);
if($char_name)
{
$bbox = explode(' ', substr($metric_ items[3], 3, -1));
$width = (int) substr($metric_ items[1], 4, -1);
$rExtent = $bbox[2] - $width;
if($a = $name_to_char_m ap[$char_name])
{
foreach($a as $charCode)
{
$widths[$charCode] = $width;
$rExtents[$charCode] = $rExtent;
$c = chr($charCode);
}
}
}
}
}
else if($in_kern_pai rs)
{
if(strstr($line , 'EndKernPairs') )
{
$in_kern_pairs = False;
}
else
{
$kern_pair_item s = explode(' ', $line);
$aLeft = $name_to_char_m ap[$kern_pair_item s[1]];
$aRight = $name_to_char_m ap[$kern_pair_item s[2]];
$offset = (int) $kern_pair_item s[3];
if($aLeft && $aRight)
{
foreach($aLeft as $charCodeLeft)
{
if(!$kerning_pa irs[$charCodeLeft]) $kerning_pairs[$charCodeLeft] =
array();
foreach($aRight as $charCodeRight)
{
$kerning_pairs[$charCodeLeft][$charCodeRight] = $offset;
}
}
}
}
}
else
{
$i = strpos($line, ' ');
$key = substr($line, 0, $i);
if($key == 'StartCharMetri cs')
{
$in_char_metric s = True;
}
else if($key == 'StartKernPairs ')
{
$in_kern_pairs = True;
}
else if($key)
{
$value = trim(substr($li ne, $i));
$properties[$key] = $value;
}
}
}
fclose($afm_fil e);
$this->Assign('Subtyp e', Name('Type1'));
if($charSet != 'WinAnsiEncodin g')
{
$this->encoding = new PDFDictionary(' Encoding');
$differences = new PDFArray();
$nextCharCode = -1;
foreach($char_t o_name_map as $charCode => $charName)
{
if($nextCharCod e != $charCode)
{
$differences->Add($charCode) ;
$nextCharCode = $charCode;
}
$differences->AddObj(Name($c harName));
$nextCharCode++ ;
}
$this->encoding->AssignObj('Dif ferences', $differences);
$this->AssignObj('Enc oding', $this->encoding);
$this->toUnicode = new PDFToUnicodeCma p($charSet, $char_to_unicod e_map);
$this->AssignObj('ToU nicode', $this->toUnicode);
}
else
{
$this->Assign('Encodi ng', Name('WinAnsiEn coding'));
}
if(!$builtIn)
{
$this->widthArray = new PDFArray();
$charCodes = array_keys($wid ths);
sort($charCodes );
$firstChar = $charCodes[0];
$lastChar = $charCodes[count($charCode s) - 1];
for($i = $firstChar; $i <= $lastChar; $i++)
{
$this->widthArray->Add((int) $widths[$i]);
}
$this->Assign('FirstC har', $firstChar);
$this->Assign('LastCh ar', $lastChar);
$this->AssignObj('Wid ths', $this->widthArray);
$this->descriptor = new PDFDictionary(' FontDescriptor' );
$this->descriptor->Assign('FontNa me', Name($propertie s['FontName']));
$bbox = explode(' ', $properties['FontBBox']);
$this->descriptor->Assign('FontBB ox', new PDFRect($bbox[0], $bbox[1],
$bbox[2], $bbox[3]));
$this->descriptor->Assign('Italic Angle', (float)
$properties['italicAngle']);
$this->descriptor->Assign('Ascent ', (int) $properties['Ascender']);
$this->descriptor->Assign('Descen t', (int) $properties['Descender']);
$this->descriptor->Assign('CapHei ght', (int) $properties['CapHeight']);
$this->descriptor->Assign('XHeigh t', (int) $properties['XHeight']);
$this->descriptor->Assign('StemH' , (int) $properties['StdHW']);
$this->descriptor->Assign('StemV' , (int) $properties['StdVW']);
$flags = (($properties['IsFixedPitch'] == 'true') ? 0x0001 : 0x0000) |
($properties['CharacterSet'] == 'Special') ? 0x0004 : 0x0020;
if($properties['italicAngle'] > 0) $flags |= 0x0040;
$this->descriptor->Assign('Flags' , $flags);
$this->stream = new PDFType1FileStr eam("$Type1Dire ctory/$name.pfb");
$this->descriptor->AssignObj('Fon tFile', $this->stream);
$this->AssignObj('Fon tDescriptor', $this->descriptor);
}
$this->Assign('BaseFo nt', Name($propertie s['FontName']));
$this->widths = $widths;
$this->rExtents = $rExtents;
$this->kerning_pair s = $kerning_pairs;
}
}
class PDFBuiltInFont extends PDFType1Font {
function PDFBuiltInFont( $name, $charSet = 'WinAnsiEncodin g') {
$this->PDFType1Font($ name, $charSet, true);
}
}
function UnpackTag($t) {
return chr($t >> 24) . chr(($t >> 16) & 0xFF) . chr(($t >> 8) & 0xFF) .
chr($t & 0xFF);
}
function CollapseUCS16St ring($s) {
$result = '';
for($i = 1, $i_bound = strlen($s); $i < $i_bound; $i += 2)
{
$result .= $s[$i];
}
return $result;
}
function FWord(&$d) {
if($d & 0x8000)
{
$d = - ($d ^ 0xFFFF);
}
}
function Fixed(&$d) {
$d = ($d / 65536);
}
define('SIZEOF_ BYTE', 1);
define('SIZEOF_ CHAR', 1);
define('SIZEOF_ USHORT', 2);
define('SIZEOF_ SHORT', 2);
define('SIZEOF_ ULONG', 4);
define('SIZEOF_ LONG', 4);
define('SIZEOF_ FIXED', 4);
class PDFTrueTypeFont extends PDFFont {
var $descriptor;
var $CIDFont;
var $encoding;
var $toUnicode;
var $stream;
function PDFTrueTypeFont ($filename, $charSet = 'WinAnsiEncodin g') {
global $TTFDirectory;
if(!$TTFDirecto ry)
{
die("PDFType1Fo nt: global variable \$TTFDirectory not set");
}
$this->PDFFont();
$char_to_unicod e_map = GetCharSetUnico deMap($charSet) ;
$file = fopen("$TTFDire ctory/$filename", "rb");
$offsetTableBin = fread($file, SIZEOF_FIXED + SIZEOF_USHORT * 4);
$offsetTable =
unpack("Nversio n/nnumTables/nsearchRange/nentrySelector/nrangeShift",
$offsetTableBin );
$tableDir = Array();
for($i = 0, $i_bound = $offsetTable['numTables']; $i < $i_bound; $i++)
{
$tableDirEntryB in = fread($file, SIZEOF_ULONG * 4);
$tableDirEntry = unpack("Ntag/NcheckSum/Noffset/Nlength",
$tableDirEntryB in);
$tag = UnpackTag($tabl eDirEntry['tag']);
$tableDir[$tag] = $tableDirEntry;
}
$headEntry = $tableDir['head'];
fseek($file, $headEntry['offset']);
$headBin = fread($file, $headEntry['length']);
$head =
unpack("NtableV ersion/NfontRevision/NcheckSumAdjust ment/NmagicNumber/nflags/
nunitsPerEm/a8created/a8modified/nxMin/nyMin/nxMax/nyMax/nmacStyle/nlowestRe
cPPEM/nfontDirectionH int/nindexToLocForm at/nglyphDataForma t", $headBin);
FWord($head['xMin']);
FWord($head['yMin']);
FWord($head['xMax']);
FWord($head['yMax']);
$pdf_unit_ratio = 1000 / $head['unitsPerEm'];
$postEntry = $tableDir['post'];
fseek($file, $postEntry['offset']);
$postBin = fread($file, $postEntry['length']);
$post =
unpack("Nformat Type/NitalicAngle/nunderlinePosit ion/nunderlineThick ness/NisF
ixedPitch/NminMemType42/NmaxMemType42/NminMemType1/NmaxMemType1", $postBin);
Fixed($post['formatType']);
Fixed($post['italicAngle']);
FWord($post['underlinePosit ion']);
$hheaEntry = $tableDir['hhea'];
fseek($file, $hheaEntry['offset']);
$hheaBin = fread($file, $hheaEntry['length']);
$hhea =
unpack("NtableV ersion/nascender/ndescender/nlineGap/nadvanceWidthMa x/nminLef
tSideBearing/nminRightSideBe aring/nxMaxExtent/ncaretSlopeRise/ncareSlopRun/n
5reserved/nmetricDataForm at/nnumMetrics", $hheaBin);
FWord($hhea['ascender']);
FWord($hhea['descender']);
FWord($hhea['lineGap']);
FWord($hhea['minLeftSideBea ring']);
FWord($hhea['minRightSideBe aring']);
FWord($hhea['xMaxExtent']);
$os2Entry = $tableDir['OS/2'];
fseek($file, $os2Entry['offset']);
$os2Bin = fread($file, $os2Entry['length']);
$os2 =
unpack("nversio n/navgCharWidth/nweightClass/nwidthClass/nfsType/nsubscriptXS
ize/nsubscriptYSize/nsubscriptXOffs et/nsubscriptYOffs et/nsuperscriptXSi ze/ns
uperscriptYSize/nsuperscriptXOf fset/nsuperscriptYOf fset/nstrikeoutSize/nstri
keoutPosition/nfamilyClass/a10panose/N4unicodeRange/a4vendId/nselection/nfir
stCharIndex/nlastCharIndex/ntypoAscender/ntypoDescender/ntypeLineGap/nwinAsc
ent/nwinDescent/NcodePageRange1/NcodePageRange2 ", $os2Bin);
FWord($os2['typoDescender']);
if($pcltEntry = $tableDir['PCLT'])
{
fseek($file, $pcltEntry['offset']);
$pcltBin = fread($file, $pcltEntry['length']);
$pclt =
unpack("Nversio n/NfontNumber/npitch/nxHeight/nstyle/ntypeFamily/ncapHeight/n
symbolSet/a16typeFace/a8characterComp lement/a6filename/CstrokeWeight/CwidthT
ype/CserifStyle/Creserved", $pcltBin);
}
$nameEntry = $tableDir['name'];
fseek($file, $nameEntry['offset']);
$nameBin = fread($file, $nameEntry['length']);
$name = unpack("nformat/nnumRecords/noffset", $nameBin);
$nameRecordSize = SIZEOF_USHORT * 6;
for($i = 0, $i_bound = $name['numRecords'], $offset = SIZEOF_USHORT * 3; $i
< $i_bound; $i++, $offset += $nameRecordSize )
{
$nameRecordBin = substr($nameBin , $offset, $nameRecordSize );
$nameRecord =
unpack("nplatfo rmId/nencodingId/nlangId/nnameId/nlength/noffset",
$nameRecordBin) ;
if($nameRecord['nameId'] == 6)
{
fseek($file, $nameEntry['offset'] + $name['offset'] +
$nameRecord['offset']);
$postscriptName = CollapseUCS16St ring(fread($fil e,
$nameRecord['length']));
break;
}
}
$cmapEntry = $tableDir['cmap'];
fseek($file, $cmapEntry['offset']);
$cmapBin = fread($file, $cmapEntry['length']);
$cmap = unpack("ntableV ersion/nnumEncodings", $cmapBin);
$encodingEntryS ize = (SIZEOF_USHORT * 2) + SIZEOF_ULONG;
for($i = 0, $i_bound = $cmap['numEncodings'], $offset = SIZEOF_USHORT * 2;
$i < $i_bound; $i++, $offset += $encodingEntryS ize)
{
$encodingEntryB in = substr($cmapBin , $offset, $encodingEntryS ize);
$encodingEntry = unpack("nplatfo rmId/nencodingId/Noffset",
$encodingEntryB in);
if($encodingEnt ry['platformId'] == 3 && $encodingEntry['encodingId'] == 1)
{
$unicodeMapBin = substr($cmapBin , $encodingEntry['offset'], SIZEOF_USHORT
* 7);
$unicodeMap =
unpack("nformat/nlength/nversion/nsegCountX2/nsearchRange/nentrySelector/nra
ngeShift", $unicodeMapBin) ;
$segCount = $unicodeMap['segCountX2'] / 2;
$offset = $encodingEntry['offset'] + SIZEOF_USHORT * 7;
$endCodesBin = substr($cmapBin , $offset, SIZEOF_USHORT * $segCount);
$endCodes = unpack("n$segCo unt", $endCodesBin);
$offset += SIZEOF_USHORT * $segCount + SIZEOF_USHORT;
$startCodesBin = substr($cmapBin , $offset, SIZEOF_USHORT * $segCount);
$startCodes = unpack("n$segCo unt", $startCodesBin) ;
$offset += SIZEOF_USHORT * $segCount;
$idDeltasBin = substr($cmapBin , $offset, SIZEOF_USHORT * $segCount);
$idDeltas = unpack("n$segCo unt", $idDeltasBin);
array_walk($idD eltas, 'FWord');
$offset += SIZEOF_USHORT * $segCount;
$rangeOffsetsBi n = substr($cmapBin , $offset, SIZEOF_USHORT * $segCount);
$rangeOffsets = unpack("n$segCo unt", $rangeOffsetsBi n);
$offset += SIZEOF_USHORT * $segCount;
$glyphIdCount = ($unicodeMap['length'] - SIZEOF_USHORT * (($segCount * 4
+ 8))) / SIZEOF_USHORT;
$glyphIdsBin = substr($cmapBin , $offset, SIZEOF_USHORT * $glyphIdCount);
$glyphIds = unpack("n$glyph IdCount", $glyphIdsBin);
$char_to_glyph_ map = array();
$glyph_to_char_ map = array();
$maxGlyphId = 0;
foreach($char_t o_unicode_map as $charCode => $unicode)
{
for($j = 1; $j < $segCount; $j++)
{
if($unicode <= $endCodes[$j])
{
if($unicode >= $startCodes[$j])
{
if($rangeOffset s[$j] == 0)
{
$glyphId = $unicode + $idDeltas[$j] - 1;
}
else
{
$offset = ($rangeOffsets[$j] / 2) - ($segCount - $j) + ($unicode -
$startCodes[$j]);
$glyphId = $glyphIds[$offset];
}
$char_to_glyph_ map[$charCode] = $glyphId;
// need inverse map for mapping kerning info
if(!$glyph_to_c har_map[$glyphId]) $glyph_to_char_ map[$glyphId] =
array();
$glyph_to_char_ map[$glyphId][] = $charCode;
$maxGlyphId = max($maxGlyphId , $glyphId);
}
else
{
break;
}
}
}
}
break;
}
}
$widths = array();
$gWidths = array();
$hmtxEntry = $tableDir['hmtx'];
fseek($file, $hmtxEntry['offset']);
$hmtxBin = fread($file, $hmtxEntry['length']);
foreach($char_t o_glyph_map as $charCode => $glyphId)
{
$offset = $glyphId * SIZEOF_USHORT * 2;
$metricBin = substr($hmtxBin , $offset, SIZEOF_USHORT * 2);
$metric = unpack("nadvanc eWidth/nlsb", $metricBin);
$width = round($metric['advanceWidth'] * $pdf_unit_ratio );
$widths[$charCode] = $width;
$gWidths[$glyphId] = $width;
}
if($kernEntry = $tableDir['kern'])
{
$kerning_pairs = array();
fseek($file, $kernEntry['offset']);
$kernBin = fread($file, $kernEntry['length']);
$kern = unpack("ntableV ersion/nnumSubTables", $cmapBin);
for($i = 0, $i_bound = $kern['numSubTables'], $offset = SIZEOF_USHORT * 2;
$i < $i_bound; $i++)
{
$kerningEntryBi n = substr($kernBin , $offset, SIZEOF_USHORT * 3);
$kerningEntry = unpack("nversio n/nlength/ncoverage", $kerningEntryBi n);
$format = $kerningEntry['coverage'] >> 8;
$horizontal = ($kerningEntry['coverage'] & 0x0001) ? true : false;
if($horizontal && $format == 0)
{
$kerningSubTabl eBin = substr($kernBin , $offset + SIZEOF_USHORT * 3,
SIZEOF_USHORT * 4);
$kerningSubTabl e =
unpack("nnumPai rs/nsearchRange/nentrySelector/nrangeShift",
$kerningSubTabl eBin);
$kerningPairSiz e = SIZEOF_USHORT * 3;
for($j = 0, $j_bound = $kerningSubTabl e['numPairs'], $offset = $offset +
SIZEOF_USHORT * 4; $j < $j_bound; $j++, $offset += $kerningPairSiz e)
{
$kerningPairBin = substr($kernBin , $offset, $kerningPairSiz e);
$kerningPair = unpack("nleft/nright/nvalue", $kerningPairBin );
FWord($kerningP air['value']);
$glyphIdLeft = $kerningPair['left'];
$glyphIdRight = $kerningPair['right'];
$aLeft = $glyph_to_char_ map[$glyphIdLeft];
$aRight = $glyph_to_char_ map[$glyphIdRight];
if($aLeft && $aRight)
{
$kerningOffset = $kerningPair['value'] * $pdf_unit_ratio ;
foreach($aLeft as $charCodeLeft)
{
if(!$kerning_pa irs[$charCodeLeft]) $kerning_pairs[$charCodeLeft] =
array();
foreach($aRight as $charCodeRight)
{
$kerning_pairs[$charCodeLeft][$charCodeRight] = $kerningOffset;
}
}
if($maxGlyphId < $glyphIdLeft && $maxGlyphId < $glyphIdRight) {
break; }
}
}
break;
}
$offset += $kerningEntry['length'];
}
}
$w = new PDFArray();
$rangeWidths = false;
$nextCharCode = -1;
foreach($gWidth s as $glyphId => $width)
{
if($glyphId == $nextGlyphId)
{
$rangeWidths[] = $width;
$nextGlyphId++;
}
else
{
if($rangeWidths )
{
$w->Add($rangeStar t);
$w->Add($rangeWidt hs);
}
$rangeStart = $glyphId;
$rangeWidths = array($width);
$nextGlyphId = $glyphId + 1;
}
}
$w->Add($rangeStar t);
$w->Add($rangeWidt hs);
$this->descriptor = new PDFDictionary(' FontDescriptor' );
$this->stream = new PDFTrueTypeFile Stream("$TTFDir ectory/$filename");
$this->encoding = new PDFEncodingCMap ($charSet, $char_to_glyph_ map);
$this->toUnicode = new PDFToUnicodeCMa p($charSet, $char_to_unicod e_map);
$this->Assign('Subtyp e', Name('Type0'));
$this->AssignObj('Enc oding', $this->encoding);
$this->AssignObj('ToU nicode', $this->toUnicode);
$this->CIDFont = new PDFDictionary(' Font');
$this->CIDFont->Assign('Subtyp e', Name('CIDFontTy pe2'));
$this->CIDFont->AssignObj('Fon tDescriptor', $this->descriptor);
$this->CIDFont->AssignObj('CID SystemInfo', $this->encoding->CIDSystemInfo) ;
$this->CIDFont->AssignObj('W ', $w);
$this->descriptor->AssignObj('Fon tFile2', $this->stream);
$descendantFont s = new PDFArray();
$descendantFont s->AddObj($this->CIDFont);
$this->AssignObj('Des cendantFonts', $descendantFont s);
$this->Assign('BaseFo nt', Name($postscrip tName));
$this->CIDFont->Assign('BaseFo nt', Name($postscrip tName));
$this->descriptor->Assign('FontNa me', Name($postscrip tName));
$left = round($head['xMin'] * $pdf_unit_ratio );
$bottom = round($head['yMin'] * $pdf_unit_ratio );
$right = round($head['xMax'] * $pdf_unit_ratio );
$top = round($head['yMax'] * $pdf_unit_ratio );
$this->descriptor->Assign('FontBB ox', new PDFRect($left, $bottom, $right,
$top));
$this->descriptor->Assign('Italic Angle', $post['italicAngle']);
$this->descriptor->Assign('Ascent ', round($os2['typoAscender'] *
$pdf_unit_ratio ));
$this->descriptor->Assign('Descen t', round($os2['typoDescender'] *
$pdf_unit_ratio ));
$this->descriptor->Assign('AvgWid th', round($os2['avgCharWidth'] *
$pdf_unit_ratio ));
$this->descriptor->Assign('Leadin g', round($os2['typeLineGap'] *
$pdf_unit_ratio ));
if($pclt)
{
$this->descriptor->Assign('CapHei ght', round($pclt['capHeight'] *
$pdf_unit_ratio ));
$this->descriptor->Assign('XHeigh t', round($pclt['xHeight'] *
$pdf_unit_ratio ));
}
else
{
$this->descriptor->Assign('CapHei ght', round($os2['typoAscender']*
$pdf_unit_ratio ));
}
$this->descriptor->Assign('StemV' , 45);
$flags = $post['isFixedPitch'] | ($os2['codePageRange1 '] & 0x8000000) ?
0x0004 : 0x0020;
if($os2['panose1'] == 3) $flags |= 0x0008;
if($os2['panose2'] != 11 && $os2['panose2'] != 12 && $os2['panose2'] != 13)
$flags |= 0x0002;
if($post['italicAngle']) $flags |= 0x0040;
$this->descriptor->Assign('Flags' , $flags);
$this->widths = $widths;
$this->kerning_pair s = $kerning_pairs;
}
}
class PDFPageLabels extends PDFDictionary {
var $nums;
function PDFPageLabels() {
$this->PDFDictionary( 'PageLabels');
$this->nums = new PDFArray();
$this->AssignObj('Num s', $this->nums);
}
function Add($start_inde x, &$label) {
$this->nums->Add($start_ind ex);
$this->nums->AddObj($label) ;
}
}
class PDFOutlines extends PDFDictionary {
function PDFOutlines() {
$this->PDFDictionary( 'Outlines');
$this->Assign('Count' , 0);
}
function IncrementCount( ) {
$count =& $this->Object('Count' );
$count->Add(1);
}
}
class Rect {
var $left;
var $right;
var $top;
var $bottom;
var $width;
var $height;
function Rect($left, $bottom, $right, $top) {
$this->left = $left;
$this->bottom = $bottom;
$this->right = $right;
$this->top = $top;
$this->width = $right - $left;
$this->height = $top - $bottom;
}
function Intersect($rect ) {
return (min($this->top, $rect->top) > max($this->bottom, $rect->bottom))
&& (min($this->right, $rect->right) > max($this->left, $rect->left));
}
}
class PDFSpacing {
var $left;
var $right;
var $top;
var $bottom;
function PDFSpacing($lef t, $bottom, $right, $top) {
$this->left = $left;
$this->bottom = $bottom;
$this->right = $right;
$this->top = $top;
}
}
class PDFPadding extends PDFSpacing {
function PDFPadding($lef t, $bottom, $right, $top) {
$this->PDFSpacing($le ft, $bottom, $right, $top);
}
}
class PDFBorder {
var $left;
var $right;
var $top;
var $bottom;
var $color;
function PDFBorder($left , $bottom, $right, $top, $color) {
$this->left = $left;
$this->bottom = $bottom;
$this->right = $right;
$this->top = $top;
$this->color = $color;
}
function Simple() {
return ($this->left === $this->right) && ($this->left === $this->top) &&
($this->left === $this->bottom);
}
}
class PDFRegion {
var $rects;
var $bounding_rects ;
var $padding;
var $spacing;
var $minWidth;
var $alloc_index;
var $alloc_top;
var $prev_left;
var $prev_right;
function PDFRegion($rect s, $spacing = 0, $padding = 0, $minWidth = 144) {
$this->rects = $rects;
$this->padding = is_object($padd ing) ? $padding : new PDFPadding($pad ding,
$padding, $padding, $padding);
$this->spacing = is_object($spac ing) ? $spacing : new PDFSpacing($spa cing,
$spacing, $spacing, $spacing);
$this->minWidth = $minWidth;
$this->alloc_index = 0;
$this->alloc_top = -1;
}
function AllocLineRect($ lineHeight) {
while($this->alloc_index < count($this->rects))
{
$a = $this->rects[$this->alloc_index];
$top = ($this->alloc_top != -1) ? $this->alloc_top : $a->top -
$this->padding->top;
$bottom = $top - $lineHeight;
$this->alloc_top = $bottom;
if($bottom >= $a->bottom + $this->padding->bottom)
{
if($top <= $a->top - $this->padding->top)
{
return new Rect($a->left + $this->padding->left, $bottom, $a->right -
$this->padding->right, $top);
}
else
{
return new Rect($this->prev_left + $this->padding->left, $bottom,
$this->prev_right - $this->padding->right, $top);
}
}
else
{
$b = $this->rects[$this->alloc_index + 1];
if($b->top == $a->bottom)
{
$this->alloc_index+ +;
if($bottom >= $b->bottom + $this->padding->bottom)
{
$this->prev_left = max($b->left, $a->left);
$this->prev_right = min($b->right, $a->right);
return new Rect($this->prev_left + $this->padding->left, $bottom,
$this->prev_right - $this->padding->right, $top);
}
}
else
{
// move to the next, disconnected rect
$this->alloc_index+ +;
$this->alloc_top = -1;
}
}
}
return null;
}
function RemoveRect($rec t) {
$newRects = array();
foreach($this->rects as $a)
{
if($a->Intersect($rec t))
{
$b1 = $b2 = $b3 = $b4 = false;
$top = $a->top;
$ctop = $rect->top + $this->spacing->top;
if($top > $ctop)
{
$b1 = new Rect($a->left, $ctop, $a->right, $top);
$top = $ctop;
}
$bottom = $a->bottom;
$cbottom = $rect->bottom - $this->spacing->bottom;
if($bottom < $cbottom)
{
$b4 = new Rect($a->left, $bottom, $a->right, $cbottom);
$bottom = $cbottom;
}
$left = $a->left;
$cleft = $rect->left - $this->spacing->left;
if($left < $cleft)
{
$b2 = new Rect($left, $bottom, $cleft, $top);
if($b2->width < $this->minWidth) $b2 = false;
}
$right = $a->right;
$cright = $rect->right + $this->spacing->right;
if($right > $cright)
{
$b3 = new Rect($cright, $bottom, $right, $top);
if($b3->width < $this->minWidth) $b3 = false;
}
if($b1) $newRects[] = $b1;
if($b2 || $b3)
{
if(!$b3)
{
$newRects[] = $b2;
}
else if(!$b2)
{
$newRects[] = $b3;
}
else
{
$newRects[] = ($b2->width >= $b3->width) ? $b2 : $b3;
}
}
if($b4) $newRects[] = $b4;
}
else
{
$newRects[] = $a;
}
}
$this->rects = $newRects;
}
function RemoveRects($re cts) {
foreach($rects as $rect)
{
$this->RemoveRect($re ct);
}
}
function GetTop() {
return ($this->rects) ? $this->rects[0]->top : 0;
}
}
class PDFRectangularR egion extends PDFRegion {
function PDFRectangularR egion($left, $bottom, $right, $top, $spacing = 0,
$padding = 0, $minWidth = 144) {
$this->PDFRegion(arra y(new Rect($left, $bottom, $right, $top)), $spacing,
$padding, $minWidth);
}
}
class PDFMultiColumnR egion extends PDFRegion {
function PDFMultiColumnR egion($left, $bottom, $right, $top, $col, $gutter =
18, $spacing = 0, $padding = 0, $minWidth = -1) {
if(is_scalar($p adding)) $padding = new PDFPadding($pad ding, $padding,
$padding, $padding);
$paddedWidth = $padding->left + $padding->right;
$colWidth = (int) round((($right - $left) - $paddedWidth - ($gutter *
($col - 1))) / $col);
$rects = array();
for($i = 0, $offset = 0; $i < $col; $i++)
{
$rects[] = new Rect($left + $offset, $bottom, $left + $offset + $colWidth
+ $paddedWidth, $top);
$offset += $colWidth + $gutter;
}
$this->PDFRegion($rec ts, $spacing, $padding, ($minWidth >= 0) ? $minWidth :
(int) $colWidth / 2);
}
}
function Bound($n, $min = 0, $max = 1) {
if($n > $max) return 1;
else if($n < $min) return 0;
return $n;
}
class PDFRGBChromaKey extends PDFArray {
function PDFRGBChromaKey ($r, $g, $b, $delta = 0.01) {
$this->PDFArray();
$this->Add(round(Boun d($r - $delta) * 255));
$this->Add(round(Boun d($r + $delta) * 255));
$this->Add(round(Boun d($g - $delta) * 255));
$this->Add(round(Boun d($g + $delta) * 255));
$this->Add(round(Boun d($g - $delta) * 255));
$this->Add(round(Boun d($g + $delta) * 255));
}
}
class PDFGrayChromaKe y extends PDFArray {
function PDFGrayChromaKe y($grayLevel, $delta = 0.01) {
$this->PDFArray();
$this->Add(round(Boun d($grayLevel - $delta) * 255));
$this->Add(round(Boun d($grayLevel + $delta) * 255));
}
function ToRGB() {
$this->values[2] = $this->values[0];
$this->values[3] = $this->values[1];
$this->values[4] = $this->values[0];
$this->values[5] = $this->values[1];
}
}
define('UPPER_L EFT', 0);
define('UPPER_R IGHT', 1);
define('LOWER_L EFT', 2);
define('LOWER_R IGHT', 3);
define('CENTER' , 4);
define('CENTER_ LEFT', 5);
define('CENTER_ RIGHT', 6);
$image_number = 0;
class PDFJPEGImageStr eam extends PDFFileStream {
var $ref_name;
var $width;
var $height;
var $is_color;
function PDFJPEGImageStr eam($filepath, $chromaKey = false) {
global $image_number, $imageDirectory ;
if(basename($fi lepath) == $filepath)
{
$filepath = "$imageDirector y/$filepath";
}
$this->PDFFileStream( $filepath);
$image_number++ ;
$this->ref_name = "Im$image_numbe r";
$this->PDFDictionary( 'XObject');
$this->Assign('Filter ', Name('DCTDecode '));
$this->size = filesize($filep ath);
$img_info = GetImageSize($f ilepath);
$this->width = $img_info[0];
$this->height = $img_info[1];
$this->is_color = ($img_info['channels'] == 3);
$this->Assign('Length ', $this->size);
$this->Assign('Subtyp e', Name('Image'));
$this->Assign('Width' , $this->width);
$this->Assign('Height ', $this->height);
$this->Assign('ColorS pace', Name($this->is_color ? 'DeviceRGB' :
'DeviceGray'));
$this->Assign('BitsPe rComponent', 8);
if($chromaKey)
{
// fix image/chroma-key mismatch
if($this->is_color && get_class($chro maKey) == 'pdfgraychromak ey')
{
$chromaKey->ToRGB();
}
else if(!$this->is_color && get_class($chro maKey) == 'pdfrgbchromake y')
{
// tricky situation: a color chroma-key was defined but the image turns
out to be monochromatic
// set choma-key to white, since that's the likeliest background color
$chromaKey = new PDFGrayChromaKe y(1);
}
$this->Assign('Mask ', $chromaKey);
}
}
function Width() {
return $this->width;
}
function Height() {
return $this->height;
}
function IsColor() {
return $this->is_color;
}
function GetRect($x, $y, $origin = UPPER_LEFT, $dpi = 96) {
$pt_width = round($this->width * 72 / $dpi, 4);
$pt_height = round($this->height * 72 / $dpi, 4);
switch($origin) {
case UPPER_LEFT:
return new Rect($x, $y - $pt_height, $x + $pt_width, $y);
break;
case UPPER_RIGHT:
return new Rect($x - $pt_width , $y - $pt_height, $x, $y);
break;
case LOWER_LEFT:
return new Rect($x, $y, $x + $pt_width, $y + $pt_height);
break;
case LOWER_RIGHT:
return new Rect($x - $pt_width, $y, $x, $y + $pt_height);
break;
case CENTER:
return new Rect($x - $pt_width / 2, $y - $pt_height / 2, $x + $pt_width /
2, $y + $pt_height / 2);
break;
case CENTER_LEFT:
return new Rect($x, $y - $pt_height / 2, $x + $pt_width, $y + $pt_height
/ 2);
break;
case CENTER_RIGHT:
return new Rect($x - $pt_width, $y - $pt_height / 2, $x, $y + $pt_height
/ 2);
break;
}
}
}
class TextStyle {
var $font;
var $size;
var $rise;
var $color;
}
function JoinWords(&$lin e, $obj, $addSpace) {
$last_index = sizeof($line) - 1;
if($last_index >= 0)
{
if($addSpace)
{
for($i = $last_index; $i >= 0; $i--)
{
if(get_class($l ine[$i]) != 'textstyle')
{
$obj->Prepend(' ');
break;
}
}
}
$last_obj = &$line[$last_index];
if(get_class($l ast_obj) == 'textstyle')
{
$line[] = $obj;
}
else
{
if(get_class($l ast_obj) == 'pdfstring' && get_class($obj) ==
'pdfkernedstrin g')
{
$obj->Prepend($last_ obj);
$line[$last_index] = $obj;
}
else
{
$last_obj->Append($obj) ;
}
}
}
else
{
$line[] = $obj;
}
}
define('DELIMIT ER_NOT_ADDED', 0x01);
define('WORD_NO T_ADDED', 0x02);
define('WORD_IS _TOO_LONG', 0x04);
define('JUSTIFY _LEFT', 0);
define('JUSTIFY _RIGHT', 1);
define('JUSTIFY _CENTER', 2);
define('JUSTIFY _FULL', 3);
class PDFTextArea {
var $region;
var $spacing;
var $current_justif y;
var $current_text_s tyle;
var $current_line_s pacing;
var $current_leadin g;
var $current_line;
var $current_line_w idth;
var $current_line_r ight_extent;
var $current_line_w ord_count;
var $current_line_c har_count;
var $current_line_s pace_count;
var $current_line_b ox;
var $current_font;
var $space_width;
var $last_text_styl e;
var $stream;
function PDFTextArea(&$s tream, $region) {
$this->stream = &$stream;
$this->current_line = array();
$this->current_line_w idth = 0;
$this->current_line_w ord_count = 0;
$this->current_justif y = JUSTIFY_FULL;
$this->current_text_s tyle = new TextStyle();
$this->region = $region;
}
function LineFeed() {
if($this->current_line_b ox)
{
$this->WriteCurrentLi ne(True);
unset($this->current_line_b ox);
}
else
{
$this->region->AllocLineRect( $this->current_leadin g);
}
}
function CarrierReturn() {
if($this->current_line_b ox)
{
$this->WriteCurrentLi ne(True);
}
}
function SetJustify($jus tify) {
$this->current_justif y = $justify;
}
function SetLineSpacing( $spacing) {
$this->current_line_s pacing = $spacing;
$this->SetLeading($th is->current_text_s tyle->size + $spacing);
}
function SetLeading($lea ding) {
$this->current_leadin g = $leading;
}
function SetColor($color ) {
if(is_scalar($c olor)) $color = new PDFGray($color) ;
$this->current_text_s tyle->color = $color;
$this->current_line[] = $this->current_text_s tyle;
}
function SetFont($font, $size, $text_rise = 0) {
$this->current_font = $font;
$this->current_text_s tyle->font = $font->ref_name;
$this->current_text_s tyle->size = $size;
$this->current_text_s tyle->rise = $text_rise;
$this->space_width = $font->GetCharWidth (' ', $size);
$this->current_line[] = $this->current_text_s tyle;
$this->SetLeading($si ze + $this->current_line_s pacing);
}
function Indent($width) {
$this->current_line_b ox->width -= $width;
switch($this->current_justif y)
{
case JUSTIFY_FULL:
case JUSTIFY_LEFT:
$this->current_line_b ox->left += $width;
break;
case JUSTIFY_CENTER:
$this->current_line_b ox->right -= (int) floor($width / 2);
$this->current_line_b ox->left += (int) ceil($width / 2);
case JUSTIFY_RIGHT:
$this->current_line_b ox->right -= $width;
}
}
function WriteCurrentLin e($line_break = False) {
$extra_width = $this->current_line_b ox->width - $this->current_line_w idth -
$this->current_line_r ight_extent;
$y = $this->current_line_b ox->bottom;
switch($this->current_justif y)
{
case JUSTIFY_FULL:
$x = $this->current_line_b ox->left;
if(!$line_break )
{
// fill in extra space by expanding both character spacing and word
spacing
$space_count = $this->current_line_s pace_count;
$char_gap_count = $this->current_line_c har_count - 1;
if($char_gap_co unt > 0)
{
$cs_portion = $extra_width / (($space_count * 0.25) + 1); // becomes
smaller with more words
if($space_count > 0)
{
$ws_portion = $extra_width - $cs_portion;
$this->ApplyWordSpaci ng(Round($ws_po rtion / $space_count, 4));
}
$this->ApplyCharSpaci ng(Round($cs_po rtion / $char_gap_count , 4));
}
}
else
{
$this->ApplyWordSpaci ng(0);
$this->ApplyCharSpaci ng(0);
}
break;
case JUSTIFY_LEFT:
$x = $this->current_line_b ox->left;
$this->ApplyWordSpaci ng(0);
break;
case JUSTIFY_RIGHT:
$x = $this->current_line_b ox->left + $extra_width;
$this->ApplyWordSpaci ng(0);
break;
case JUSTIFY_CENTER:
$x = $this->current_line_b ox->left + ($extra_width / 2);
$this->ApplyWordSpaci ng(0);
break;
}
$this->stream->Write("BT\n$ x $y Td\n");
foreach($this->current_line as $obj)
{
if(get_class($o bj) == 'textstyle')
{
$this->ApplyTextStyle ($obj);
}
else
{
$this->WriteText($obj );
}
}
$this->stream->Write("ET\n" );
$this->current_line = array();
$this->current_line_w idth = 0;
$this->current_line_w ord_count = 0;
$this->current_line_c har_count = 0;
$this->current_line_s pace_count = 0;
}
function WriteText($obj) {
if(!$obj->IsEmpty())
{
$this->stream->Write($obj->ToString());
$this->stream->Write(get_clas s($obj) == 'pdfkernedstrin g' ? " TJ\n" : "
Tj\n");
}
}
function ApplyTextStyle( $style) {
if($style != $this->last_text_styl e)
{
if(!$this->last_text_styl e || $this->last_text_styl e->font != $style->font
|| $this->last_text_styl e->size !=
$style->size)
{
if($style->font)
{
$this->stream->Write("/$style->font $style->size Tf\n");
}
}
if((!$this->last_text_styl e && $style->rise > 0) ||
$this->last_text_styl e->rise != $style->rise)
{
$this->stream->Write("$styl e->rise Ts\n");
}
if($style->color && (!$this->last_text_styl e ||
$this->last_text_styl e->color != $style->color))
{
$this->stream->Write($style->color->ToStringNonStr oking());
}
$this->last_text_styl e = $style;
}
}
function ApplyWordSpacin g($spacing) {
if($this->word_spacing != $spacing)
{
$this->stream->Write("$spacin g Tw\n");
$this->word_spacing = $spacing;
}
}
function ApplyCharSpacin g($spacing) {
$this->stream->Write("$spacin g Tc\n");
}
function AddWord($separa tor, $word) {
$flags = DELIMITER_NOT_A DDED | WORD_NOT_ADDED;
$char_count = strlen($word);
list($word_obj, $word_width, $right_extent) =
$this->current_font->Transform($wor d, $this->current_text_s tyle->size);
if($separator == ' ')
{
if($this->current_line_w ord_count > 0)
{
$separator_widt h = $this->space_width;
$separator_char _count = 1;
}
}
else if($separator == "\x09" && $this->current_line_w ord_count == 0)
{
if($this->current_line_b ox || $this->current_line_b ox =
$this->region->AllocLineRect( $this->current_leadin g))
{
$this->Indent($this->space_width * 4);
$flags &= ~DELIMITER_NOT_ ADDED;
}
}
else if($separator)
{
if($separator == "\x09") $separator = "\xA0\xA0\xA0\x A0"; // change tab
into 4 non-breaking spaces
list($separator _obj, $separator_widt h, $separator_righ t_extent) =
$this->current_font->Transform($sep arator, $this->current_text_s tyle->size);
$separator_char _count = strlen($separat or);
}
while($this->current_line_b ox || ($this->current_line_b ox =
$this->region->AllocLineRect( $this->current_leadin g)))
{
if($this->current_line_w idth + $separator_widt h + $word_width <=
$this->current_line_b ox->width)
{
if($separator_o bj)
{
JoinWords($this->current_line , $separator_obj, false);
JoinWords($this->current_line , $word_obj, false);
}
else
{
JoinWords($this->current_line , $word_obj, ($separator_cha r_count > 0));
}
$this->current_line_w idth += $separator_widt h + $word_width;
$this->current_line_w ord_count++;
$this->current_line_c har_count += $separator_char _count + $char_count;
$this->current_line_r ight_extent = $right_extent;
if($separator == ' ') $this->current_line_s pace_count +=
$separator_char _count;
$flags &= ~(DELIMITER_NOT _ADDED | WORD_NOT_ADDED) ;
break;
}
else if($separator_o bj && $this->current_line_w idth + $separator_widt h <=
$this->current_line_b ox->width) // add delimiter to current line if possible
{
JoinWords($this->current_line , $separator_obj, false);
$this->current_line_w idth += $separator_widt h;
$this->current_line_c har_count += $separator_char _count;
$this->current_line_r ight_extent = $separator_righ t_extent;
$this->WriteCurrentLi ne();
unset($this->current_line_b ox);
$separator_obj = null;
$separator_widt h = 0;
$separator_char _count = 0;
$flags &= ~DELIMITER_NOT_ ADDED;
}
else if($this->current_line_w idth > 0)
{
$this->WriteCurrentLi ne();
unset($this->current_line_b ox);
if($separator == ' ')
{
$separator_char _count = 0;
$separator_widt h = 0;
}
}
else
{
$flags |= WORD_IS_TOO_LON G;
break;
}
}
return $flags;
}
function AddText($text, $end_of_paragra ph = true) {
if(!$this->current_font )
{
die("Use PDFTextArea::Se tText() to set current font before adding text");
}
if(is_array($te xt))
{
foreach($text as $index => $line)
{
if($leftover = $this->AddText(chop($ line)))
{
$leftover_lines = array_slice($te xt, $index + 1);
array_unshift($ leftover_lines, $leftover);
return $leftover_lines ;
}
}
return array();
}
else
{
$written_count = 0;
$matches = array();
if(preg_match_a ll('/([\\x20\\x09\\x85 \\x97-]?)([^\\x20\\x09\\x8 5\\x97-]*)/',
$text, $matches, PREG_PATTERN_OR DER))
{
$separators = $matches[1];
$words = $matches[2];
foreach($words as $index => $word)
{
$separator = $separators[$index];
if($separator || $word)
{
$flags = $this->AddWord($separ ator, $word);
if(!($flags & DELIMITER_NOT_A DDED))
{
$written_count += strlen($separat or);
}
if(!($flags & WORD_NOT_ADDED) )
{
$written_count += strlen($word);
}
else
{
return substr($text, $written_count) ;
}
}
}
}
if($end_of_para graph)
{
$this->LineFeed();
}
return '';
}
}
function GetCurrentLineT op() {
return ($this->current_line_b ox) ? $this->current_line_b ox :
$this->region->GetTop();
}
}
define('BUTT_CA P', 0);
define('ROUND_C AP', 1);
define('PROJECT ING_SQUARE_CAP' , 2);
define('MITER_J OIN', 0);
define('ROUND_J OIN', 1);
define('BEVEL_J OIN', 2);
class PDFPage extends PDFDictionary {
var $width;
var $height;
var $stream;
var $occupied_rects ;
function PDFPage($width, $height) {
$this->PDFDictionary( 'Page');
$this->width = $width;
$this->height = $height;
$this->Assign('MediaB ox', new PDFRect(0, 0, $width, $height));
$this->stream = new PDFFlateStream( );
$this->AssignObj('Con tents', $this->stream);
$this->occupied_rec ts = array();
}
function AddOccupiedRect s($rects, $spacing) {
foreach($rects as $rect)
{
$this->occupied_rec ts[] = new Rect($rect->left - $spacing->left,
$rect->bottom - $spacing->bottom, $rect->right + $spacing->right, $rect->top
+ $spacing->top);
}
}
function MoveTo($x, $y) {
$this->stream->Write("$x $y m\n");
}
function LineTo($x, $y) {
$this->stream->Write("$x $y l\n");
}
function ClosePath() {
$this->stream->Write("h\n") ;
}
function SetLineColor($c olor) {
if(is_scalar($c olor)) $color = new PDFGray($color) ;
$this->stream->Write($color->ToStringStroki ng());
}
function SetFillColor($c olor) {
if(is_scalar($c olor)) $color = new PDFGray($color) ;
$this->stream->Write($color->ToStringNonStr oking());
}
function SetLineWidth($w idth) {
$this->stream->Write("$widt h w\n");
}
function SetLineCap($cap ) {
$this->stream->Write("$cap J\n");
}
function SetLineJoin($jo in) {
$this->stream->Write("$join j\n");
}
function DrawPath() {
$this->stream->Write("S\n") ;
}
function FillPath() {
$this->stream->Write("f\n") ;
}
function ClipPath() {
$this->stream->Write("W n\n");
}
function SaveGraphicStat e() {
$this->stream->Write("q\n") ;
}
function RestoreGraphicS tate() {
$this->stream->Write("Q\n") ;
}
function SetPath($region ) {
$moveTo = true;
$endIndex = count($region->rects) - 1;
for($i = 0; $i <= $endIndex; $i++)
{
$rect = $region->rects[$i];
$next_rect = $region->rects[$i + 1];
if($moveTo) // start at top, left corner
{
$this->MoveTo($rect->left, $rect->top);
$startIndex = $i;
$moveTo = false;
}
if($i == $endIndex || $next_rect->top != $rect->bottom)
{
$this->LineTo($rect->left, $rect->bottom);
$this->LineTo($rect->right, $rect->bottom);
for($j = $i; $j >= $startIndex; $j--)
{
$rect = $region->rects[$j];
$prev_rect = $region->rects[$j - 1];
if($j == $startIndex)
{
$this->LineTo($rect->right, $rect->top);
$this->ClosePath();
}
else if($prev_rect->right != $rect->right)
{
$this->LineTo($rect->right, $rect->top);
$this->LineTo($prev_r ect->right, $rect->top);
}
}
$moveTo = true;
}
else if($next_rect->left != $rect->left)
{
$this->LineTo($rect->left, $rect->bottom);
$this->LineTo($next_r ect->left, $rect->bottom);
}
}
}
function FillRegion($reg ion, $color) {
$this->SaveGraphicSta te();
$this->SetFillColor($ color);
$this->SetPath($regio n);
$this->FillPath();
$this->RestoreGraphic State();
}
function DrawBorder($reg ion, $border) {
if(is_scalar($b order)) $border = new PDFBorder($bord er, $border, $border,
$border, BLACK);
$this->SaveGraphicSta te();
$this->SetLineColor($ border->color);
if($border->Simple()) // if all four sides have the same border width
{
$this->SetLineWidth($ border->left);
$this->SetPath($regio n);
$this->DrawPath();
}
else
{
$endIndex = count($region->rects) - 1;
$borderWidth = false;
if($border->left !== false)
{
$borderWidth = $border->left;
$this->SetLineWidth($ borderWidth);
for($i = 0; $i <= $endIndex; $i++)
{
$rect = $region->rects[$i];
$next_rect = $region->rects[$i + 1];
if($i == 0)
{
$this->MoveTo($rect->left, $rect->top);
}
if($i == $endIndex)
{
$this->LineTo($rect->left, $rect->bottom);
}
else if($rect->bottom != $next_rect->top || $rect->left !=
$next_rect->left)
{
$this->LineTo($rect->left, $rect->bottom);
$this->MoveTo($next_r ect->left, $next_rect->top);
}
}
$this->DrawPath();
}
if($border->right !== false)
{
if($border->right !== $borderWidth)
{
$borderWidth = $border->right;
$this->SetLineWidth($ borderWidth);
}
for($i = 0; $i <= $endIndex; $i++)
{
$rect = $region->rects[$i];
$next_rect = $region->rects[$i + 1];
if($i == 0)
{
$this->MoveTo($rect->right, $rect->top);
}
if($i == $endIndex)
{
$this->LineTo($rect->right, $rect->bottom);
}
else if($rect->bottom != $next_rect->top || $rect->right !=
$next_rect->right)
{
$this->LineTo($rect->right, $rect->bottom);
$this->MoveTo($next_r ect->right, $next_rect->top);
}
}
$this->DrawPath();
}
if($border->top !== false)
{
if($border->top !== $borderWidth)
{
$borderWidth = $border->top;
$this->SetLineWidth($ borderWidth);
}
for($i = $endIndex; $i >= 0; $i--)
{
$rect = $region->rects[$i];
$prev_rect = $region->rects[$i - 1];
if($i == 0 || $rect->top != $prev_rect->bottom)
{
$this->MoveTo($rect->left, $rect->top);
$this->LineTo($rect->right, $rect->top);
}
else
{
if($rect->left < $prev_rect->left)
{
$this->MoveTo($rect->left, $rect->top);
$this->LineTo($prev_r ect->left, $rect->top);
}
if($rect->right > $prev_rect->right)
{
$this->MoveTo($rect->right, $rect->top);
$this->LineTo($prev_r ect->right, $rect->top);
}
}
}
$this->DrawPath();
}
if($border->bottom !== false)
{
if($border->bottom !== $borderWidth)
{
$borderWidth = $border->bottom;
$this->SetLineWidth($ borderWidth);
}
for($i = 0; $i <= $endIndex; $i++)
{
$rect = $region->rects[$i];
$next_rect = $region->rects[$i + 1];
if($i == $endIndex || $rect->bottom != $next_rect->top)
{
$this->MoveTo($rect->left, $rect->bottom);
$this->LineTo($rect->right, $rect->bottom);
}
else
{
if($rect->left < $next_rect->left)
{
$this->MoveTo($rect->left, $rect->bottom);
$this->LineTo($next_r ect->left, $rect->bottom);
}
if($rect->right > $next_rect->right)
{
$this->MoveTo($rect->right, $rect->bottom);
$this->LineTo($next_r ect->right, $rect->bottom);
}
}
}
$this->DrawPath();
}
}
$this->RestoreGraphic State();
}
function InsertTextArea( $region, $border = false, $backgroundColo r = false,
$overlay = false) {
if(!$overlay)
{
$region->RemoveRects($t his->occupied_rects );
$this->AddOccupiedRec ts($region->rects, $region->spacing);
}
if($backgroundC olor !== false)
{
$this->FillRegion($re gion, $backgroundColo r);
}
if($border !== false)
{
$this->DrawBorder($re gion, $border);
}
return new PDFTextArea($th is->stream, $region);
}
function InsertImage($im ageSrc, $rect, $spacing = 0, $border = false,
$overlay = false) {
if(!$imageSrc->Registered() )
{
die("Use PDF::AddImageSt ream() to add image to resource table first");
}
$region = new PDFRegion(array ($rect), $spacing, 0, 0);
if(!$overlay)
{
$region->RemoveRects($t his->occupied_rects );
$this->AddOccupiedRec ts($region->rects, $region->spacing);
}
if(is_scalar($s pacing))
{
$spacing = new PDFSpacing($spa cing, $spacing, $spacing, $spacing);
}
$this->SaveGraphicSta te();
$this->SetPath($regio n);
$this->ClipPath();
$this->stream->Write("$rect->width 0 0 $rect->height $rect->left
$rect->bottom cm\n/$imageSrc->ref_name Do\n");
$this->RestoreGraphic State();
if($border !== false)
{
$this->DrawBorder($re gion, $border);
}
}
}
class PDFPages extends PDFDictionary {
var $default_width;
var $default_height ;
var $kids;
function PDFPages() {
$this->PDFDictionary( 'Pages');
$this->kids = new PDFArray();
$this->AssignObj('Kid s', $this->kids);
}
function Add(&$page, $index) {
if($index >= 0)
{
$this->kids->InsertObj($pag e, $index);
}
else
{
$index = $this->kids->AddObj($page );
}
$page->AssignObj('Par ent', $this);
if(!$this->default_widt h || !$this->default_height )
{
$this->default_widt h = $page->width;
$this->default_heig ht = $page->height;
$this->Assign('MediaB ox', new PDFRect(0, 0, $this->default_widt h,
$this->default_height ));
$page->Unassign('Medi aBox');
}
else if($page->width == $this->default_widt h && $page->height ==
$this->default_height )
{
$page->Unassign('Medi aBox');
}
$this->Assign('Count' , $this->kids->Count());
return $index;
}
}
class PDFDocument {
var $file;
var $objects;
var $next_id;
var $current_offset ;
var $pages;
var $page_labels;
var $outlines;
var $fonts;
var $trailer;
var $info;
var $xobjects;
var $prevBookmarks;
function PDFDocument() {
$this->objects = array();
$this->next_id = 1;
$this->current_offs et = 0;
$this->fonts = new PDFDictionary() ;
$this->xobjects = new PDFDictionary() ;
$resources = new PDFDictionary() ;
$resources->Assign('ProcSe t', array(Name('PDF '), Name('Text'),
Name('ImageB')) );
$resources->AssignObj('Fon t', $this->fonts);
$resources->AssignObj('XOb ject', $this->xobjects);
$this->pages = new PDFPages();
$this->pages->AssignObj('Res ources', $resources);
$this->page_labels = new PDFPageLabels() ;
$this->outlines = new PDFOutlines();
$this->info = new PDFDictionary() ;
$this->info->Assign('Creato r', 'pdf_class.php' );
$this->info->Assign('Produc er', 'Chung Leong (ch***********@ hotmail.com)');
$this->info->Assign('Creati onDate', new PDFDate());
$catalog = new PDFDictionary(' Catalog');
$catalog->AssignObj('Pag es', $this->pages);
$catalog->AssignObj('Pag eLabels', $this->page_labels) ;
$catalog->AssignObj('Out lines', $this->outlines);
$this->trailer = new PDFDictionary() ;
$this->trailer->AssignObj('Roo t', $catalog);
$this->trailer->AssignObj('Inf o', $this->info);
$this->RegisterObject ($this->outlines);
$this->RegisterObject ($this->page_labels) ;
$this->RegisterObject ($this->pages);
$this->RegisterObject ($catalog);
$this->RegisterObject ($this->info);
$this->prevBookmark s = array();
}
function SetDocInfo($nam e, $value) {
$this->info->Assign($name , $value);
}
function RegisterObject( &$obj) {
$id = $this->next_id++;
$this->objects[] = &$obj;
$obj->SetId($id);
$child_objs = get_object_vars ($obj);
foreach($child_ objs as $child_name => $child_obj)
{
if(is_subclass_ of($child_obj, 'PDFObj') && !$child_obj->Registered() )
{
$this->RegisterObject ($obj->$child_name) ;
}
}
return $id;
}
function Write($s) {
if($this->file) fputs($this->file, $s);
else echo $s;
$this->current_offs et += strlen($s);
}
function WriteObject(&$o bj) {
if(!$obj->Registered() )
{
$this->RegisterObject ($obj);
}
$obj->SetOffset($thi s->current_offset );
$obj->Output($this );
}
function WritePage(&$pag e) {
$this->WriteObject($p age);
$this->WriteObject($p age->stream);
}
function Open($filename = false) {
if($filename)
{
$this->file = fopen($filename , "wb");
}
$this->Write("%PDF-1.3\n");
$this->Write("%üë++\n ");
}
function AddFont(&$font) {
$this->fonts->AssignObj($fon t->ref_name, $font);
$this->RegisterObject ($font);
}
function AddImageStream( &$stream) {
$this->xobjects->AssignObj($str eam->ref_name, $stream);
$this->RegisterObject ($stream);
}
function AddPage(&$page, $index = -1) {
$index = $this->pages->Add($page, $index);
$this->WritePage($pag e);
return $index;
}
function AddBookmark(&$b ookmark, $depth = 0) {
if($depth == 0)
{
$bookmark->SetParent($thi s->outlines);
}
else
{
if($parent =& $this->prevBookmark s[$depth - 1])
{
$bookmark->SetParent($par ent);
}
else
{
$pDepth = $depth - 1;
die("PDFPage::A ddBookmark: No bookmark exists at parent level
($pDepth)");
}
}
$this->prevBookmark s[$depth] =& $bookmark;
$this->outlines->IncrementCount ();
return $this->RegisterObject ($bookmark);
}
function AddPageLabels($ start_index, &$label) {
return $this->page_labels->Add($start_ind ex, $label);
}
function Close() {
$object_count = sizeof($this->objects);
for($i = 0; $i < $object_count; $i++)
{
$obj =& $this->objects[$i];
if(!$obj->Written())
{
$this->WriteObject($o bj);
}
}
$xref_count = $object_count + 1;
$xref_offset = $this->current_offset ;
$this->Write("xref\n" );
$this->Write("0 $xref_count\n") ;
$this->Write("0000000 000 65535 f\r\n");
for($i = 0; $i < $object_count; $i++)
{
$this->Write(sprintf( "%010d 00000 n\r\n", $this->objects[$i]->offset));
}
$this->Write("trailer \n");
$this->trailer->Assign('Size ', $xref_count);
$this->Write($this->trailer->ToString());
$this->Write("\nstart xref\n$xref_off set\n");
$this->Write("%%EOF\n ");
if($this->file)
{
fclose($this->file);
}
}
}
?>