472,961 Members | 2,352 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,961 software developers and data experts.

width of a charactor

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
==================================================
Jul 17 '05 #1
2 3119
"Alex Shi" <ch****@stonix.com> wrote in message
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/no

Depends on where this chr is destined, if you are working with images, yes,
if you are working with web pages no. But if you need to set chr width, do
it in CSS.

--
Mike Bradley
http://www.gzentools.com -- free online php tools
Jul 17 '05 #2
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;
$charMapDirectory = 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_file) {
$pdf_file->Write("$this->id 0 obj\n");
$pdf_file->Write($this->ToString());
$pdf_file->Write("\nendobj\n");
}
}

class PDFBoolean extends PDFObj {
var $value;

function PDFBoolean($value) {
$this->value = $value;
}

function ToString() {
return ($this->value) ? 'true' : 'false';
}
}

class PDFInteger extends PDFObj {
var $value;

function PDFInteger($value) {
$this->value = $value;
}

function Add($value) {
$this->value += $value;
}

function ToString() {
return (string) $this->value;
}
}

class PDFHex extends PDFInteger {

function PDFHex($value) {
$this->PDFInteger($value);
}

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($value) {
$this->value = $value;
}

function Prepend($obj) {
$this->value = ((get_class($obj) == 'pdfstring') ? $obj->value : $obj) .
$this->value;
}

function Append($obj) {
$this->value .= ((get_class($obj) == '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($value);
}
else if(is_string($value))
{
return new PDFString($value);
}
else if(is_array($value))
{
if(sizeof($value) > 0 && !isset($value[0])) // is associative array
{
$new_array = new PDFDictionary();
foreach($value as $akey => $avalue)
{
if(is_object($avalue))
{
$new_array->AssignObj($akey, $value[$akey]);
}
else
{
$new_array->Assign($akey, $avalue);
}
}
}
else
{
$new_array = new PDFArray();
foreach($value as $akey => $avalue)
{
if(is_object($avalue))
{
$new_array->AddObj($value[$akey]);
}
else
{
$new_array->Add($avalue);
}
}
}
return $new_array;
}
else
{
return new PDFBoolean($value);
}
}
}

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($value))
{
$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($value);
}
}
$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($this->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_file) {
$this->Assign('Length', strlen($this->data));
$pdf_file->Write("$this->id 0 obj\n");
$pdf_file->Write($this->ToString());
$pdf_file->Write("\nstream\n");
$pdf_file->Write($this->data);
$pdf_file->Write("endstream");
$pdf_file->Write("\nendobj\n");
}
}

class PDFFileStream extends PDFStream {

function PDFFileStream($filePath) {
$this->PDFStream();
$filesize = filesize($filePath);
$file = fopen($filePath, "rb");
$this->data = fread($file, $filesize);
fclose($file);
}
}

if(function_exists('gzdeflate'))
{

class PDFFlateStream extends PDFStream {
var $data;

function PDFFlateStream() {
$this->PDFStream();
$this->Assign('Filter', Name('FlateDecode'));
}

function Output(&$pdf_file) {
$deflated_data = gzdeflate($this->data);
$length = strlen($deflated_data) + 2;
$this->Assign('Length', $length);
$pdf_file->Write("$this->id 0 obj\n");
$pdf_file->Write($this->ToString());
$pdf_file->Write("\nstream\n");
$pdf_file->Write("H‰");
$pdf_file->Write($deflated_data);
$pdf_file->Write("endstream");
$pdf_file->Write("\nendobj\n");
}
}

class PDFFlateFileStream extends PDFFlateStream {

function PDFFlateFileStream($filePath) {
$this->PDFFlateStream();
$filesize = filesize($filePath);
$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('FlateDecode'));
$this->temp_filename = tempnam('', 'strm');
$this->temp_file = fopen($this->temp_filename, "wb");
}

function Write($s) {
fwrite($this->temp_file, $s);
}

function Output(&$pdf_file) {
fclose($this->temp_file);
`gzip -9nf $this->temp_filename`;
$this->temp_filename .= '.gz';
$filesize = filesize($this->temp_filename);
$gz = fopen($this->temp_filename, '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("\nstream\n");
$pdf_file->Write("H‰");
$pdf_file->Write($deflated_data);
$pdf_file->Write("endstream");
$pdf_file->Write("\nendobj\n");
}
}

class PDFFlateFileStream extends PDFStream {
var $filePath;

function PDFFlateFileStream($filePath) {
$this->PDFStream();
$this->filePath = $filePath;
}

function Output(&$pdf_file) {
$gzip_data = `gzip -9cnf $this->filePath`;
$deflated_data = substr($gzip_data, 10, -4);
$length = strlen($deflated_data) + 2;
$this->Assign('Length', $length);
$pdf_file->Write("$this->id 0 obj\n");
$pdf_file->Write($this->ToString());
$pdf_file->Write("\nstream\n");
$pdf_file->Write("H‰");
$pdf_file->Write($deflated_data);
$pdf_file->Write("endstream");
$pdf_file->Write("\nendobj\n");
}
}

}

class PDFTrueTypeFileStream extends PDFFlateFileStream {

function PDFTrueTypeFileStream($filePath) {
$this->PDFFlateFileStream($filePath);
$this->Assign('Length1', strlen($this->data));
}
}

class PDFType1FileStream extends PDFFileStream {

function PDFType1FileStream($filePath) {
$this->PDFFileStream($filePath);
$encrypted_start_index = strpos($this->data, "currentfile eexec\r") + 18;
$encrypted_end_index = strpos($this->data,
"0000000000000000000000000000000000000000000000000 000000000000000");
$this->Assign('Length1', $encrypted_start_index);
$this->Assign('Length2', $encrypted_end_index - $encrypted_start_index);
$this->Assign('Length3', 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('LIGHTGRAY', 0.9);
define('DARKGRAY', 0.4);

class PDFGray extends PDFObj {
var $level;

function PDFGray($level) {
$this->level = $level;
}

function ToStringStroking() {
return "$this->level G\n";
}

function ToStringNonStroking() {
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 ToStringStroking() {
return "$this->r $this->g $this->b RG\n";
}

function ToStringNonStroking() {
return "$this->r $this->g $this->b rg\n";
}
}

function ToUnicodeStr($s, $encoding) {
$map = GetCharSetUnicodeMap($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(&$page, $title, $encoding = 'WinAnsiEncoding', $top =
false) {
$this->PDFDictionary();
$this->Assign('Title', ToUnicodeStr($title, $encoding));
$dest = ($top) ? array(&$page, Name('FitH'), $top) : array(&$page,
Name('Fit'));
$this->Assign('Dest', $dest);
}

function SetParent(&$parent) {
$this->Assign('Parent', $parent);
if(!$parent->IsAssigned('First'))
{
$parent->AssignObj('First', $this);
}
if($parent->IsAssigned('Last'))
{
$last =& $parent->Object('Last');
$last->AssignObj('Next', $this);
$this->AssignObj('Prev', $last);
}
$parent->AssignObj('Last', $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(&$parent) {
$this->Assign('Parent', $parent);
if(!$parent->IsAssigned('First'))
{
$parent->AssignObj('First', $this);
}
if($parent->IsAssigned('Last'))
{
$last =& $parent->Object('Last');
$last->AssignObj('Next', $this);
$this->AssignObj('Prev', $last);
}
$parent->AssignObj('Last', $this);
}
}

class PDFPageLabel extends PDFDictionary {

function PDFPageLabel($style, $prefix = False, $encoding =
'WinAnsiEncoding') {
if($style)
{
$this->Assign('S', Name($style));
}
if($prefix)
{
$this->Assign('P', ToUnicodeStr($prefix, $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($obj) == 'pdfkernedstring')
{
$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_pairs)
{
for($i = 0; $i < $len; $i++)
{
$char = ord($text{$i});
$width += $this->widths[$char];
if($kerning_offsets = $this->kerning_pairs[$char])
{
$next_index = $i + 1;
$next_char = ord($text{$next_index});
if($offset = $kerning_offsets[$next_char])
{
if(!$kerned_string) $kerned_string = new PDFKernedString();
$kerned_string->Append(substr($text, $last_kerned_char, $next_index -
$last_kerned_char), $offset);
$last_kerned_char = $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_string)
{
$kerned_string->Append(substr($text, $last_kerned_char));
return array($kerned_string, $pt_width, $r_extent);
}
else
{
return array(new PDFString($text), $pt_width, $r_extent);
}
}

function GetCharWidth($char, $size) {
return $this->widths[ord($char)] * $size / 1000;
}

function GetTextWidth($text, $size) {
$len = strlen($text);
if($this->kerning_pairs)
{
for($i = 0; $i < $len; $i++)
{
$char = ord($text{$i});
$width += $this->widths[$char];
if($kerning_offsets = $this->kerning_pairs[$char])
{
$next_index = $i + 1;
$next_char = ord($text{$next_index});
if($offset = $kerning_offsets[$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($filePath) {
$char_to_unicode_map = array();
$lines = file($filePath);
foreach($lines as $line)
{
if(list($unicodeHex, $charCodeHex) = explode("\t", $line))
{
if(!strncmp($unicodeHex, '0x', 2) && !strncmp($charCodeHex, '0x', 2))
{
$unicode = hexdec(substr($unicodeHex, 2));
$charCode = hexdec(substr($charCodeHex, 2));
$char_to_unicode_map[$charCode] = $unicode;
}
}
}
return $char_to_unicode_map;
}

function ParseENCFile($filePath) {
$char_to_name_map = array();
$lines = file($filePath);
foreach($lines as $line)
{
if(list($name, $charCode) = preg_split('/\\s+/', $line, -1,
PREG_SPLIT_NO_EMPTY))
{
if($name{0} != '%' && is_numeric($charCode))
{
$char_to_name_map[(int) $charCode] = $name;
}
}
}
return $char_to_name_map;
}

$winAnsiEncodingNameMap = 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');

$winAnsiEncodingUnicodeMap = 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 );

$charSetNameMaps = array('WinAnsiEncoding' => $winAnsiEncodingNameMap);
$charSetUnicodeMaps = array('WinAnsiEncoding' =>
$winAnsiEncodingUnicodeMap);

function GetCharSetUnicodeMap($charSet) {
global $charMapDirectory, $charSetUnicodeMaps;
if(!($map = $charSetUnicodeMaps[$charSet]))
{
if(!$charMapDirectory)
{
die("GetCharSetUnicodeMap: global variable \$charMapDirectory not set");
}
$map = ParseCPGFile("$charMapDirectory/$charSet.cpg");
$charSetUnicodeMaps[$charSet] = $map;
}
return $map;
}

function GetCharSetNameMap($charSet) {
global $charMapDirectory, $charSetNameMaps;
if(!($map = $charSetNameMaps[$charSet]))
{
if(!$charMapDirectory)
{
die("GetCharSetNameMap: global variable \$charMapDirectory not set");
}
$map = ParseENCFile("$charMapDirectory/$charSet.enc");
$charSetNameMaps[$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('CMapName', $charSet);
$this->CIDSystemInfo = new PDFDictionary();
$this->CIDSystemInfo->Assign('Registry', $charSet);
$this->CIDSystemInfo->Assign('Ordering', $charSet);
$this->CIDSystemInfo->Assign('Supplement', 0);
$this->AssignObj('CIDSystemInfo', $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 begincodespacerange\n<00> <FF>\nendcodespacerange\n");
$glyphRangeStart = false;
$nextGlyphId = -1;
$nextCharCode = -1;
$cidRangeEntries = array();
foreach($char_to_glyph_map as $charCode => $glyphId)
{
if($nextCharCode == $charCode && $glyphId == $nextGlyphId)
{
$rangeEnd = $charCode;
$nextGlyphId++;
$nextCharCode++;
}
else
{
if($glyphRangeStart)
{
$cidRangeEntries[] = sprintf('<%02x> <%02x> %d', $rangeStart, $rangeEnd,
$glyphRangeStart);
$glyphRangeStart = false;
$nextGlyphId = -1;
$nextCharCode = -1;
}
if($glyphId)
{
$rangeStart = $charCode;
$rangeEnd = $charCode;
$glyphRangeStart = $glyphId;
$nextGlyphId = $glyphId + 1;
$nextCharCode = $charCode + 1;
}
}
}
if($glyphRangeStart)
{
$cidRangeEntries[] = sprintf('<%02x> <%02x> %d', $rangeStart, $rangeEnd,
$glyphRangeStart);
}
for($j = 0, $j_bound = count($cidRangeEntries); $j < $j_bound; $j += 100)
{
$c = min(100, $j_bound - $j);
$this->Write("$c begincidrange\n");
$this->Write(implode("\n", array_slice($cidRangeEntries, $j, $c)));
$this->Write("\nendcidrange\n");
}
$this->Write(<<<CMAP_TRAILER
endcmap
CMapName
currentdict
/CMap
defineresource
pop
end
end
CMAP_TRAILER
);
}
}

class PDFToUnicodeCMap extends PDFFlateStream {

function PDFToUnicodeCMAP($charSet, $char_to_unicode_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 begincodespacerange\n<00> <FF>\nendcodespacerange\n");
$unicodeRangeStart = false;
$nextUnicode = -1;
$nextCharCode = -1;
$cidRangeEntries = array();
foreach($char_to_unicode_map as $charCode => $unicode)
{
if($charCode == $nextCharCode && $unicode == $nextUnicode)
{
$rangeEnd = $charCode;
$nextUnicode++;
$nextCharCode++;
}
else
{
if($unicodeRangeStart)
{
$cidRangeEntries[] = sprintf('<%02x> <%02x> <%04x>', $rangeStart,
$rangeEnd, $unicodeRangeStart);
$unicodeRangeStart = false;
$nextUnicode = -1;
$nextCharCode = -1;
}
if($unicode)
{
$rangeStart = $charCode;
$rangeEnd = $charCode;
$unicodeRangeStart = $unicode;
$nextUnicode = $unicode + 1;
$nextCharCode = $charCode + 1;
}
}
}
if($unicodeRangeStart)
{
$cidRangeEntries[] = sprintf('<%02x> <%02x> <%04x>', $rangeStart,
$rangeEnd, $unicodeRangeStart);
}
for($j = 0, $j_bound = count($cidRangeEntries); $j < $j_bound; $j += 100)
{
$c = min(100, $j_bound - $j);
$this->Write("$c beginbfrange\n");
$this->Write(implode("\n", array_slice($cidRangeEntries, $j, $c)));
$this->Write("\nendbfrange\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($name, $charSet = 'WinAnsiEncoding', $builtIn =
false) {
global $Type1Directory;
if(!$Type1Directory)
{
die("PDFType1Font: global variable \$Type1Directory not set");
}
$this->PDFFont();
if($charSet != 'WinAnsiEncoding') $char_to_unicode_map =
GetCharSetUnicodeMap($charSet);
$char_to_name_map = GetCharSetNameMap($charSet);
$name_to_char_map = GetInverseMap($char_to_name_map);
$afm_file = fopen("$Type1Directory/$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_metrics)
{
if(strstr($line, 'EndCharMetrics'))
{
$in_char_metrics = 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_map[$char_name])
{
foreach($a as $charCode)
{
$widths[$charCode] = $width;
$rExtents[$charCode] = $rExtent;
$c = chr($charCode);
}
}
}
}
}
else if($in_kern_pairs)
{
if(strstr($line, 'EndKernPairs'))
{
$in_kern_pairs = False;
}
else
{
$kern_pair_items = explode(' ', $line);
$aLeft = $name_to_char_map[$kern_pair_items[1]];
$aRight = $name_to_char_map[$kern_pair_items[2]];
$offset = (int) $kern_pair_items[3];
if($aLeft && $aRight)
{
foreach($aLeft as $charCodeLeft)
{
if(!$kerning_pairs[$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 == 'StartCharMetrics')
{
$in_char_metrics = True;
}
else if($key == 'StartKernPairs')
{
$in_kern_pairs = True;
}
else if($key)
{
$value = trim(substr($line, $i));
$properties[$key] = $value;
}
}
}
fclose($afm_file);
$this->Assign('Subtype', Name('Type1'));
if($charSet != 'WinAnsiEncoding')
{
$this->encoding = new PDFDictionary('Encoding');
$differences = new PDFArray();
$nextCharCode = -1;
foreach($char_to_name_map as $charCode => $charName)
{
if($nextCharCode != $charCode)
{
$differences->Add($charCode);
$nextCharCode = $charCode;
}
$differences->AddObj(Name($charName));
$nextCharCode++;
}
$this->encoding->AssignObj('Differences', $differences);
$this->AssignObj('Encoding', $this->encoding);
$this->toUnicode = new PDFToUnicodeCmap($charSet, $char_to_unicode_map);
$this->AssignObj('ToUnicode', $this->toUnicode);
}
else
{
$this->Assign('Encoding', Name('WinAnsiEncoding'));
}
if(!$builtIn)
{
$this->widthArray = new PDFArray();
$charCodes = array_keys($widths);
sort($charCodes);
$firstChar = $charCodes[0];
$lastChar = $charCodes[count($charCodes) - 1];
for($i = $firstChar; $i <= $lastChar; $i++)
{
$this->widthArray->Add((int) $widths[$i]);
}
$this->Assign('FirstChar', $firstChar);
$this->Assign('LastChar', $lastChar);
$this->AssignObj('Widths', $this->widthArray);

$this->descriptor = new PDFDictionary('FontDescriptor');
$this->descriptor->Assign('FontName', Name($properties['FontName']));
$bbox = explode(' ', $properties['FontBBox']);
$this->descriptor->Assign('FontBBox', new PDFRect($bbox[0], $bbox[1],
$bbox[2], $bbox[3]));
$this->descriptor->Assign('ItalicAngle', (float)
$properties['italicAngle']);
$this->descriptor->Assign('Ascent', (int) $properties['Ascender']);
$this->descriptor->Assign('Descent', (int) $properties['Descender']);
$this->descriptor->Assign('CapHeight', (int) $properties['CapHeight']);
$this->descriptor->Assign('XHeight', (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 PDFType1FileStream("$Type1Directory/$name.pfb");
$this->descriptor->AssignObj('FontFile', $this->stream);
$this->AssignObj('FontDescriptor', $this->descriptor);
}
$this->Assign('BaseFont', Name($properties['FontName']));
$this->widths = $widths;
$this->rExtents = $rExtents;
$this->kerning_pairs = $kerning_pairs;
}
}

class PDFBuiltInFont extends PDFType1Font {

function PDFBuiltInFont($name, $charSet = 'WinAnsiEncoding') {
$this->PDFType1Font($name, $charSet, true);
}
}

function UnpackTag($t) {
return chr($t >> 24) . chr(($t >> 16) & 0xFF) . chr(($t >> 8) & 0xFF) .
chr($t & 0xFF);
}

function CollapseUCS16String($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 = 'WinAnsiEncoding') {
global $TTFDirectory;
if(!$TTFDirectory)
{
die("PDFType1Font: global variable \$TTFDirectory not set");
}
$this->PDFFont();
$char_to_unicode_map = GetCharSetUnicodeMap($charSet);
$file = fopen("$TTFDirectory/$filename", "rb");
$offsetTableBin = fread($file, SIZEOF_FIXED + SIZEOF_USHORT * 4);
$offsetTable =
unpack("Nversion/nnumTables/nsearchRange/nentrySelector/nrangeShift",
$offsetTableBin);
$tableDir = Array();
for($i = 0, $i_bound = $offsetTable['numTables']; $i < $i_bound; $i++)
{
$tableDirEntryBin = fread($file, SIZEOF_ULONG * 4);
$tableDirEntry = unpack("Ntag/NcheckSum/Noffset/Nlength",
$tableDirEntryBin);
$tag = UnpackTag($tableDirEntry['tag']);
$tableDir[$tag] = $tableDirEntry;
}
$headEntry = $tableDir['head'];
fseek($file, $headEntry['offset']);
$headBin = fread($file, $headEntry['length']);
$head =
unpack("NtableVersion/NfontRevision/NcheckSumAdjustment/NmagicNumber/nflags/
nunitsPerEm/a8created/a8modified/nxMin/nyMin/nxMax/nyMax/nmacStyle/nlowestRe
cPPEM/nfontDirectionHint/nindexToLocFormat/nglyphDataFormat", $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("NformatType/NitalicAngle/nunderlinePosition/nunderlineThickness/NisF
ixedPitch/NminMemType42/NmaxMemType42/NminMemType1/NmaxMemType1", $postBin);
Fixed($post['formatType']);
Fixed($post['italicAngle']);
FWord($post['underlinePosition']);

$hheaEntry = $tableDir['hhea'];
fseek($file, $hheaEntry['offset']);
$hheaBin = fread($file, $hheaEntry['length']);
$hhea =
unpack("NtableVersion/nascender/ndescender/nlineGap/nadvanceWidthMax/nminLef
tSideBearing/nminRightSideBearing/nxMaxExtent/ncaretSlopeRise/ncareSlopRun/n
5reserved/nmetricDataFormat/nnumMetrics", $hheaBin);
FWord($hhea['ascender']);
FWord($hhea['descender']);
FWord($hhea['lineGap']);
FWord($hhea['minLeftSideBearing']);
FWord($hhea['minRightSideBearing']);
FWord($hhea['xMaxExtent']);

$os2Entry = $tableDir['OS/2'];
fseek($file, $os2Entry['offset']);
$os2Bin = fread($file, $os2Entry['length']);
$os2 =
unpack("nversion/navgCharWidth/nweightClass/nwidthClass/nfsType/nsubscriptXS
ize/nsubscriptYSize/nsubscriptXOffset/nsubscriptYOffset/nsuperscriptXSize/ns
uperscriptYSize/nsuperscriptXOffset/nsuperscriptYOffset/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("Nversion/NfontNumber/npitch/nxHeight/nstyle/ntypeFamily/ncapHeight/n
symbolSet/a16typeFace/a8characterComplement/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("nplatformId/nencodingId/nlangId/nnameId/nlength/noffset",
$nameRecordBin);
if($nameRecord['nameId'] == 6)
{
fseek($file, $nameEntry['offset'] + $name['offset'] +
$nameRecord['offset']);
$postscriptName = CollapseUCS16String(fread($file,
$nameRecord['length']));
break;
}
}

$cmapEntry = $tableDir['cmap'];
fseek($file, $cmapEntry['offset']);
$cmapBin = fread($file, $cmapEntry['length']);
$cmap = unpack("ntableVersion/nnumEncodings", $cmapBin);
$encodingEntrySize = (SIZEOF_USHORT * 2) + SIZEOF_ULONG;
for($i = 0, $i_bound = $cmap['numEncodings'], $offset = SIZEOF_USHORT * 2;
$i < $i_bound; $i++, $offset += $encodingEntrySize)
{
$encodingEntryBin = substr($cmapBin, $offset, $encodingEntrySize);
$encodingEntry = unpack("nplatformId/nencodingId/Noffset",
$encodingEntryBin);
if($encodingEntry['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$segCount", $endCodesBin);
$offset += SIZEOF_USHORT * $segCount + SIZEOF_USHORT;
$startCodesBin = substr($cmapBin, $offset, SIZEOF_USHORT * $segCount);
$startCodes = unpack("n$segCount", $startCodesBin);
$offset += SIZEOF_USHORT * $segCount;
$idDeltasBin = substr($cmapBin, $offset, SIZEOF_USHORT * $segCount);
$idDeltas = unpack("n$segCount", $idDeltasBin);
array_walk($idDeltas, 'FWord');
$offset += SIZEOF_USHORT * $segCount;
$rangeOffsetsBin = substr($cmapBin, $offset, SIZEOF_USHORT * $segCount);
$rangeOffsets = unpack("n$segCount", $rangeOffsetsBin);
$offset += SIZEOF_USHORT * $segCount;
$glyphIdCount = ($unicodeMap['length'] - SIZEOF_USHORT * (($segCount * 4
+ 8))) / SIZEOF_USHORT;
$glyphIdsBin = substr($cmapBin, $offset, SIZEOF_USHORT * $glyphIdCount);
$glyphIds = unpack("n$glyphIdCount", $glyphIdsBin);
$char_to_glyph_map = array();
$glyph_to_char_map = array();
$maxGlyphId = 0;
foreach($char_to_unicode_map as $charCode => $unicode)
{
for($j = 1; $j < $segCount; $j++)
{
if($unicode <= $endCodes[$j])
{
if($unicode >= $startCodes[$j])
{
if($rangeOffsets[$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_char_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_to_glyph_map as $charCode => $glyphId)
{
$offset = $glyphId * SIZEOF_USHORT * 2;
$metricBin = substr($hmtxBin, $offset, SIZEOF_USHORT * 2);
$metric = unpack("nadvanceWidth/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("ntableVersion/nnumSubTables", $cmapBin);
for($i = 0, $i_bound = $kern['numSubTables'], $offset = SIZEOF_USHORT * 2;
$i < $i_bound; $i++)
{
$kerningEntryBin = substr($kernBin, $offset, SIZEOF_USHORT * 3);
$kerningEntry = unpack("nversion/nlength/ncoverage", $kerningEntryBin);
$format = $kerningEntry['coverage'] >> 8;
$horizontal = ($kerningEntry['coverage'] & 0x0001) ? true : false;
if($horizontal && $format == 0)
{
$kerningSubTableBin = substr($kernBin, $offset + SIZEOF_USHORT * 3,
SIZEOF_USHORT * 4);
$kerningSubTable =
unpack("nnumPairs/nsearchRange/nentrySelector/nrangeShift",
$kerningSubTableBin);
$kerningPairSize = SIZEOF_USHORT * 3;
for($j = 0, $j_bound = $kerningSubTable['numPairs'], $offset = $offset +
SIZEOF_USHORT * 4; $j < $j_bound; $j++, $offset += $kerningPairSize)
{
$kerningPairBin = substr($kernBin, $offset, $kerningPairSize);
$kerningPair = unpack("nleft/nright/nvalue", $kerningPairBin);
FWord($kerningPair['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_pairs[$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($gWidths as $glyphId => $width)
{
if($glyphId == $nextGlyphId)
{
$rangeWidths[] = $width;
$nextGlyphId++;
}
else
{
if($rangeWidths)
{
$w->Add($rangeStart);
$w->Add($rangeWidths);
}
$rangeStart = $glyphId;
$rangeWidths = array($width);
$nextGlyphId = $glyphId + 1;
}
}
$w->Add($rangeStart);
$w->Add($rangeWidths);

$this->descriptor = new PDFDictionary('FontDescriptor');
$this->stream = new PDFTrueTypeFileStream("$TTFDirectory/$filename");
$this->encoding = new PDFEncodingCMap($charSet, $char_to_glyph_map);
$this->toUnicode = new PDFToUnicodeCMap($charSet, $char_to_unicode_map);
$this->Assign('Subtype', Name('Type0'));
$this->AssignObj('Encoding', $this->encoding);
$this->AssignObj('ToUnicode', $this->toUnicode);
$this->CIDFont = new PDFDictionary('Font');
$this->CIDFont->Assign('Subtype', Name('CIDFontType2'));
$this->CIDFont->AssignObj('FontDescriptor', $this->descriptor);
$this->CIDFont->AssignObj('CIDSystemInfo', $this->encoding->CIDSystemInfo);
$this->CIDFont->AssignObj('W', $w);
$this->descriptor->AssignObj('FontFile2', $this->stream);
$descendantFonts = new PDFArray();
$descendantFonts->AddObj($this->CIDFont);
$this->AssignObj('DescendantFonts', $descendantFonts);

$this->Assign('BaseFont', Name($postscriptName));
$this->CIDFont->Assign('BaseFont', Name($postscriptName));
$this->descriptor->Assign('FontName', Name($postscriptName));
$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('FontBBox', new PDFRect($left, $bottom, $right,
$top));
$this->descriptor->Assign('ItalicAngle', $post['italicAngle']);
$this->descriptor->Assign('Ascent', round($os2['typoAscender'] *
$pdf_unit_ratio));
$this->descriptor->Assign('Descent', round($os2['typoDescender'] *
$pdf_unit_ratio));
$this->descriptor->Assign('AvgWidth', round($os2['avgCharWidth'] *
$pdf_unit_ratio));
$this->descriptor->Assign('Leading', round($os2['typeLineGap'] *
$pdf_unit_ratio));
if($pclt)
{
$this->descriptor->Assign('CapHeight', round($pclt['capHeight'] *
$pdf_unit_ratio));
$this->descriptor->Assign('XHeight', round($pclt['xHeight'] *
$pdf_unit_ratio));
}
else
{
$this->descriptor->Assign('CapHeight', 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_pairs = $kerning_pairs;
}
}

class PDFPageLabels extends PDFDictionary {
var $nums;

function PDFPageLabels() {
$this->PDFDictionary('PageLabels');
$this->nums = new PDFArray();
$this->AssignObj('Nums', $this->nums);
}

function Add($start_index, &$label) {
$this->nums->Add($start_index);
$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($left, $bottom, $right, $top) {
$this->left = $left;
$this->bottom = $bottom;
$this->right = $right;
$this->top = $top;
}
}

class PDFPadding extends PDFSpacing {
function PDFPadding($left, $bottom, $right, $top) {
$this->PDFSpacing($left, $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($rects, $spacing = 0, $padding = 0, $minWidth = 144) {
$this->rects = $rects;
$this->padding = is_object($padding) ? $padding : new PDFPadding($padding,
$padding, $padding, $padding);
$this->spacing = is_object($spacing) ? $spacing : new PDFSpacing($spacing,
$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($rect) {
$newRects = array();
foreach($this->rects as $a)
{
if($a->Intersect($rect))
{
$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($rects) {
foreach($rects as $rect)
{
$this->RemoveRect($rect);
}
}

function GetTop() {
return ($this->rects) ? $this->rects[0]->top : 0;
}
}

class PDFRectangularRegion extends PDFRegion {

function PDFRectangularRegion($left, $bottom, $right, $top, $spacing = 0,
$padding = 0, $minWidth = 144) {
$this->PDFRegion(array(new Rect($left, $bottom, $right, $top)), $spacing,
$padding, $minWidth);
}
}

class PDFMultiColumnRegion extends PDFRegion {

function PDFMultiColumnRegion($left, $bottom, $right, $top, $col, $gutter =
18, $spacing = 0, $padding = 0, $minWidth = -1) {
if(is_scalar($padding)) $padding = new PDFPadding($padding, $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($rects, $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(Bound($r - $delta) * 255));
$this->Add(round(Bound($r + $delta) * 255));
$this->Add(round(Bound($g - $delta) * 255));
$this->Add(round(Bound($g + $delta) * 255));
$this->Add(round(Bound($g - $delta) * 255));
$this->Add(round(Bound($g + $delta) * 255));
}
}

class PDFGrayChromaKey extends PDFArray {

function PDFGrayChromaKey($grayLevel, $delta = 0.01) {
$this->PDFArray();
$this->Add(round(Bound($grayLevel - $delta) * 255));
$this->Add(round(Bound($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_LEFT', 0);
define('UPPER_RIGHT', 1);
define('LOWER_LEFT', 2);
define('LOWER_RIGHT', 3);
define('CENTER', 4);
define('CENTER_LEFT', 5);
define('CENTER_RIGHT', 6);

$image_number = 0;

class PDFJPEGImageStream extends PDFFileStream {
var $ref_name;
var $width;
var $height;
var $is_color;

function PDFJPEGImageStream($filepath, $chromaKey = false) {
global $image_number, $imageDirectory;
if(basename($filepath) == $filepath)
{
$filepath = "$imageDirectory/$filepath";
}
$this->PDFFileStream($filepath);
$image_number++;
$this->ref_name = "Im$image_number";
$this->PDFDictionary('XObject');
$this->Assign('Filter', Name('DCTDecode'));
$this->size = filesize($filepath);
$img_info = GetImageSize($filepath);
$this->width = $img_info[0];
$this->height = $img_info[1];
$this->is_color = ($img_info['channels'] == 3);
$this->Assign('Length', $this->size);
$this->Assign('Subtype', Name('Image'));
$this->Assign('Width', $this->width);
$this->Assign('Height', $this->height);
$this->Assign('ColorSpace', Name($this->is_color ? 'DeviceRGB' :
'DeviceGray'));
$this->Assign('BitsPerComponent', 8);
if($chromaKey)
{
// fix image/chroma-key mismatch
if($this->is_color && get_class($chromaKey) == 'pdfgraychromakey')
{
$chromaKey->ToRGB();
}
else if(!$this->is_color && get_class($chromaKey) == 'pdfrgbchromakey')
{
// 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 PDFGrayChromaKey(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(&$line, $obj, $addSpace) {
$last_index = sizeof($line) - 1;
if($last_index >= 0)
{
if($addSpace)
{
for($i = $last_index; $i >= 0; $i--)
{
if(get_class($line[$i]) != 'textstyle')
{
$obj->Prepend(' ');
break;
}
}
}
$last_obj = &$line[$last_index];
if(get_class($last_obj) == 'textstyle')
{
$line[] = $obj;
}
else
{
if(get_class($last_obj) == 'pdfstring' && get_class($obj) ==
'pdfkernedstring')
{
$obj->Prepend($last_obj);
$line[$last_index] = $obj;
}
else
{
$last_obj->Append($obj);
}
}
}
else
{
$line[] = $obj;
}
}

define('DELIMITER_NOT_ADDED', 0x01);
define('WORD_NOT_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_justify;
var $current_text_style;
var $current_line_spacing;
var $current_leading;
var $current_line;
var $current_line_width;
var $current_line_right_extent;
var $current_line_word_count;
var $current_line_char_count;
var $current_line_space_count;
var $current_line_box;
var $current_font;
var $space_width;
var $last_text_style;
var $stream;

function PDFTextArea(&$stream, $region) {
$this->stream = &$stream;
$this->current_line = array();
$this->current_line_width = 0;
$this->current_line_word_count = 0;
$this->current_justify = JUSTIFY_FULL;
$this->current_text_style = new TextStyle();
$this->region = $region;
}

function LineFeed() {
if($this->current_line_box)
{
$this->WriteCurrentLine(True);
unset($this->current_line_box);
}
else
{
$this->region->AllocLineRect($this->current_leading);
}
}

function CarrierReturn() {
if($this->current_line_box)
{
$this->WriteCurrentLine(True);
}
}

function SetJustify($justify) {
$this->current_justify = $justify;
}

function SetLineSpacing($spacing) {
$this->current_line_spacing = $spacing;
$this->SetLeading($this->current_text_style->size + $spacing);
}

function SetLeading($leading) {
$this->current_leading = $leading;
}

function SetColor($color) {
if(is_scalar($color)) $color = new PDFGray($color);
$this->current_text_style->color = $color;
$this->current_line[] = $this->current_text_style;
}

function SetFont($font, $size, $text_rise = 0) {
$this->current_font = $font;
$this->current_text_style->font = $font->ref_name;
$this->current_text_style->size = $size;
$this->current_text_style->rise = $text_rise;
$this->space_width = $font->GetCharWidth(' ', $size);
$this->current_line[] = $this->current_text_style;
$this->SetLeading($size + $this->current_line_spacing);
}

function Indent($width) {
$this->current_line_box->width -= $width;
switch($this->current_justify)
{
case JUSTIFY_FULL:
case JUSTIFY_LEFT:
$this->current_line_box->left += $width;
break;
case JUSTIFY_CENTER:
$this->current_line_box->right -= (int) floor($width / 2);
$this->current_line_box->left += (int) ceil($width / 2);
case JUSTIFY_RIGHT:
$this->current_line_box->right -= $width;
}
}

function WriteCurrentLine($line_break = False) {
$extra_width = $this->current_line_box->width - $this->current_line_width -
$this->current_line_right_extent;
$y = $this->current_line_box->bottom;
switch($this->current_justify)
{
case JUSTIFY_FULL:
$x = $this->current_line_box->left;
if(!$line_break)
{
// fill in extra space by expanding both character spacing and word
spacing
$space_count = $this->current_line_space_count;
$char_gap_count = $this->current_line_char_count - 1;
if($char_gap_count > 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->ApplyWordSpacing(Round($ws_portion / $space_count, 4));
}
$this->ApplyCharSpacing(Round($cs_portion / $char_gap_count, 4));
}
}
else
{
$this->ApplyWordSpacing(0);
$this->ApplyCharSpacing(0);
}
break;
case JUSTIFY_LEFT:
$x = $this->current_line_box->left;
$this->ApplyWordSpacing(0);
break;
case JUSTIFY_RIGHT:
$x = $this->current_line_box->left + $extra_width;
$this->ApplyWordSpacing(0);
break;
case JUSTIFY_CENTER:
$x = $this->current_line_box->left + ($extra_width / 2);
$this->ApplyWordSpacing(0);
break;
}
$this->stream->Write("BT\n$x $y Td\n");
foreach($this->current_line as $obj)
{
if(get_class($obj) == 'textstyle')
{
$this->ApplyTextStyle($obj);
}
else
{
$this->WriteText($obj);
}
}
$this->stream->Write("ET\n");
$this->current_line = array();
$this->current_line_width = 0;
$this->current_line_word_count = 0;
$this->current_line_char_count = 0;
$this->current_line_space_count = 0;
}

function WriteText($obj) {
if(!$obj->IsEmpty())
{
$this->stream->Write($obj->ToString());
$this->stream->Write(get_class($obj) == 'pdfkernedstring' ? " TJ\n" : "
Tj\n");
}
}

function ApplyTextStyle($style) {
if($style != $this->last_text_style)
{
if(!$this->last_text_style || $this->last_text_style->font != $style->font
|| $this->last_text_style->size !=
$style->size)
{
if($style->font)
{
$this->stream->Write("/$style->font $style->size Tf\n");
}
}
if((!$this->last_text_style && $style->rise > 0) ||
$this->last_text_style->rise != $style->rise)
{
$this->stream->Write("$style->rise Ts\n");
}
if($style->color && (!$this->last_text_style ||
$this->last_text_style->color != $style->color))
{
$this->stream->Write($style->color->ToStringNonStroking());
}
$this->last_text_style = $style;
}
}

function ApplyWordSpacing($spacing) {
if($this->word_spacing != $spacing)
{
$this->stream->Write("$spacing Tw\n");
$this->word_spacing = $spacing;
}
}

function ApplyCharSpacing($spacing) {
$this->stream->Write("$spacing Tc\n");
}

function AddWord($separator, $word) {
$flags = DELIMITER_NOT_ADDED | WORD_NOT_ADDED;
$char_count = strlen($word);
list($word_obj, $word_width, $right_extent) =
$this->current_font->Transform($word, $this->current_text_style->size);
if($separator == ' ')
{
if($this->current_line_word_count > 0)
{
$separator_width = $this->space_width;
$separator_char_count = 1;
}
}
else if($separator == "\x09" && $this->current_line_word_count == 0)
{
if($this->current_line_box || $this->current_line_box =
$this->region->AllocLineRect($this->current_leading))
{
$this->Indent($this->space_width * 4);
$flags &= ~DELIMITER_NOT_ADDED;
}
}
else if($separator)
{
if($separator == "\x09") $separator = "\xA0\xA0\xA0\xA0"; // change tab
into 4 non-breaking spaces
list($separator_obj, $separator_width, $separator_right_extent) =
$this->current_font->Transform($separator, $this->current_text_style->size);
$separator_char_count = strlen($separator);
}
while($this->current_line_box || ($this->current_line_box =
$this->region->AllocLineRect($this->current_leading)))
{
if($this->current_line_width + $separator_width + $word_width <=
$this->current_line_box->width)
{
if($separator_obj)
{
JoinWords($this->current_line, $separator_obj, false);
JoinWords($this->current_line, $word_obj, false);
}
else
{
JoinWords($this->current_line, $word_obj, ($separator_char_count > 0));
}
$this->current_line_width += $separator_width + $word_width;
$this->current_line_word_count++;
$this->current_line_char_count += $separator_char_count + $char_count;
$this->current_line_right_extent = $right_extent;
if($separator == ' ') $this->current_line_space_count +=
$separator_char_count;
$flags &= ~(DELIMITER_NOT_ADDED | WORD_NOT_ADDED);
break;
}
else if($separator_obj && $this->current_line_width + $separator_width <=
$this->current_line_box->width) // add delimiter to current line if possible
{
JoinWords($this->current_line, $separator_obj, false);
$this->current_line_width += $separator_width;
$this->current_line_char_count += $separator_char_count;
$this->current_line_right_extent = $separator_right_extent;
$this->WriteCurrentLine();
unset($this->current_line_box);
$separator_obj = null;
$separator_width = 0;
$separator_char_count = 0;
$flags &= ~DELIMITER_NOT_ADDED;
}
else if($this->current_line_width > 0)
{
$this->WriteCurrentLine();
unset($this->current_line_box);
if($separator == ' ')
{
$separator_char_count = 0;
$separator_width = 0;
}
}
else
{
$flags |= WORD_IS_TOO_LONG;
break;
}
}
return $flags;
}

function AddText($text, $end_of_paragraph = true) {
if(!$this->current_font)
{
die("Use PDFTextArea::SetText() to set current font before adding text");
}
if(is_array($text))
{
foreach($text as $index => $line)
{
if($leftover = $this->AddText(chop($line)))
{
$leftover_lines = array_slice($text, $index + 1);
array_unshift($leftover_lines, $leftover);
return $leftover_lines;
}
}
return array();
}
else
{
$written_count = 0;
$matches = array();

if(preg_match_all('/([\\x20\\x09\\x85\\x97-]?)([^\\x20\\x09\\x85\\x97-]*)/',
$text, $matches, PREG_PATTERN_ORDER))
{
$separators = $matches[1];
$words = $matches[2];
foreach($words as $index => $word)
{
$separator = $separators[$index];
if($separator || $word)
{
$flags = $this->AddWord($separator, $word);
if(!($flags & DELIMITER_NOT_ADDED))
{
$written_count += strlen($separator);
}
if(!($flags & WORD_NOT_ADDED))
{
$written_count += strlen($word);
}
else
{
return substr($text, $written_count);
}
}
}
}
if($end_of_paragraph)
{
$this->LineFeed();
}
return '';
}
}

function GetCurrentLineTop() {
return ($this->current_line_box) ? $this->current_line_box :
$this->region->GetTop();
}
}

define('BUTT_CAP', 0);
define('ROUND_CAP', 1);
define('PROJECTING_SQUARE_CAP', 2);

define('MITER_JOIN', 0);
define('ROUND_JOIN', 1);
define('BEVEL_JOIN', 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('MediaBox', new PDFRect(0, 0, $width, $height));
$this->stream = new PDFFlateStream();
$this->AssignObj('Contents', $this->stream);
$this->occupied_rects = array();
}
function AddOccupiedRects($rects, $spacing) {
foreach($rects as $rect)
{
$this->occupied_rects[] = 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($color) {
if(is_scalar($color)) $color = new PDFGray($color);
$this->stream->Write($color->ToStringStroking());
}

function SetFillColor($color) {
if(is_scalar($color)) $color = new PDFGray($color);
$this->stream->Write($color->ToStringNonStroking());
}

function SetLineWidth($width) {
$this->stream->Write("$width w\n");
}

function SetLineCap($cap) {
$this->stream->Write("$cap J\n");
}

function SetLineJoin($join) {
$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 SaveGraphicState() {
$this->stream->Write("q\n");
}

function RestoreGraphicState() {
$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_rect->right, $rect->top);
}
}
$moveTo = true;
}
else if($next_rect->left != $rect->left)
{
$this->LineTo($rect->left, $rect->bottom);
$this->LineTo($next_rect->left, $rect->bottom);
}
}
}

function FillRegion($region, $color) {
$this->SaveGraphicState();
$this->SetFillColor($color);
$this->SetPath($region);
$this->FillPath();
$this->RestoreGraphicState();
}

function DrawBorder($region, $border) {
if(is_scalar($border)) $border = new PDFBorder($border, $border, $border,
$border, BLACK);
$this->SaveGraphicState();
$this->SetLineColor($border->color);
if($border->Simple()) // if all four sides have the same border width
{
$this->SetLineWidth($border->left);
$this->SetPath($region);
$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_rect->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_rect->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_rect->left, $rect->top);
}
if($rect->right > $prev_rect->right)
{
$this->MoveTo($rect->right, $rect->top);
$this->LineTo($prev_rect->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_rect->left, $rect->bottom);
}
if($rect->right > $next_rect->right)
{
$this->MoveTo($rect->right, $rect->bottom);
$this->LineTo($next_rect->right, $rect->bottom);
}
}
}
$this->DrawPath();
}
}
$this->RestoreGraphicState();
}

function InsertTextArea($region, $border = false, $backgroundColor = false,
$overlay = false) {
if(!$overlay)
{
$region->RemoveRects($this->occupied_rects);
$this->AddOccupiedRects($region->rects, $region->spacing);
}
if($backgroundColor !== false)
{
$this->FillRegion($region, $backgroundColor);
}
if($border !== false)
{
$this->DrawBorder($region, $border);
}
return new PDFTextArea($this->stream, $region);
}

function InsertImage($imageSrc, $rect, $spacing = 0, $border = false,
$overlay = false) {
if(!$imageSrc->Registered())
{
die("Use PDF::AddImageStream() to add image to resource table first");
}
$region = new PDFRegion(array($rect), $spacing, 0, 0);
if(!$overlay)
{
$region->RemoveRects($this->occupied_rects);
$this->AddOccupiedRects($region->rects, $region->spacing);
}
if(is_scalar($spacing))
{
$spacing = new PDFSpacing($spacing, $spacing, $spacing, $spacing);
}
$this->SaveGraphicState();
$this->SetPath($region);
$this->ClipPath();
$this->stream->Write("$rect->width 0 0 $rect->height $rect->left
$rect->bottom cm\n/$imageSrc->ref_name Do\n");
$this->RestoreGraphicState();
if($border !== false)
{
$this->DrawBorder($region, $border);
}
}

}

class PDFPages extends PDFDictionary {
var $default_width;
var $default_height;
var $kids;

function PDFPages() {
$this->PDFDictionary('Pages');
$this->kids = new PDFArray();
$this->AssignObj('Kids', $this->kids);
}

function Add(&$page, $index) {
if($index >= 0)
{
$this->kids->InsertObj($page, $index);
}
else
{
$index = $this->kids->AddObj($page);
}
$page->AssignObj('Parent', $this);
if(!$this->default_width || !$this->default_height)
{
$this->default_width = $page->width;
$this->default_height = $page->height;
$this->Assign('MediaBox', new PDFRect(0, 0, $this->default_width,
$this->default_height));
$page->Unassign('MediaBox');
}
else if($page->width == $this->default_width && $page->height ==
$this->default_height)
{
$page->Unassign('MediaBox');
}
$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_offset = 0;
$this->fonts = new PDFDictionary();
$this->xobjects = new PDFDictionary();
$resources = new PDFDictionary();
$resources->Assign('ProcSet', array(Name('PDF'), Name('Text'),
Name('ImageB')));
$resources->AssignObj('Font', $this->fonts);
$resources->AssignObj('XObject', $this->xobjects);
$this->pages = new PDFPages();
$this->pages->AssignObj('Resources', $resources);
$this->page_labels = new PDFPageLabels();
$this->outlines = new PDFOutlines();
$this->info = new PDFDictionary();
$this->info->Assign('Creator', 'pdf_class.php');
$this->info->Assign('Producer', 'Chung Leong (ch***********@hotmail.com)');
$this->info->Assign('CreationDate', new PDFDate());
$catalog = new PDFDictionary('Catalog');
$catalog->AssignObj('Pages', $this->pages);
$catalog->AssignObj('PageLabels', $this->page_labels);
$catalog->AssignObj('Outlines', $this->outlines);
$this->trailer = new PDFDictionary();
$this->trailer->AssignObj('Root', $catalog);
$this->trailer->AssignObj('Info', $this->info);
$this->RegisterObject($this->outlines);
$this->RegisterObject($this->page_labels);
$this->RegisterObject($this->pages);
$this->RegisterObject($catalog);
$this->RegisterObject($this->info);
$this->prevBookmarks = array();
}

function SetDocInfo($name, $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_offset += strlen($s);
}

function WriteObject(&$obj) {
if(!$obj->Registered())
{
$this->RegisterObject($obj);
}
$obj->SetOffset($this->current_offset);
$obj->Output($this);
}

function WritePage(&$page) {
$this->WriteObject($page);
$this->WriteObject($page->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($font->ref_name, $font);
$this->RegisterObject($font);
}

function AddImageStream(&$stream) {
$this->xobjects->AssignObj($stream->ref_name, $stream);
$this->RegisterObject($stream);
}

function AddPage(&$page, $index = -1) {
$index = $this->pages->Add($page, $index);
$this->WritePage($page);
return $index;
}

function AddBookmark(&$bookmark, $depth = 0) {
if($depth == 0)
{
$bookmark->SetParent($this->outlines);
}
else
{
if($parent =& $this->prevBookmarks[$depth - 1])
{
$bookmark->SetParent($parent);
}
else
{
$pDepth = $depth - 1;
die("PDFPage::AddBookmark: No bookmark exists at parent level
($pDepth)");
}
}
$this->prevBookmarks[$depth] =& $bookmark;
$this->outlines->IncrementCount();
return $this->RegisterObject($bookmark);
}

function AddPageLabels($start_index, &$label) {
return $this->page_labels->Add($start_index, $label);
}

function Close() {
$object_count = sizeof($this->objects);
for($i = 0; $i < $object_count; $i++)
{
$obj =& $this->objects[$i];
if(!$obj->Written())
{
$this->WriteObject($obj);
}
}
$xref_count = $object_count + 1;
$xref_offset = $this->current_offset;
$this->Write("xref\n");
$this->Write("0 $xref_count\n");
$this->Write("0000000000 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("\nstartxref\n$xref_offset\n");
$this->Write("%%EOF\n");
if($this->file)
{
fclose($this->file);
}
}
}

?>
Jul 17 '05 #3

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

7
by: Graham J | last post by:
Apologies for the partial post that mysteriously escaped as I was composing. I shall try again. For many years I have been happily and successfully coding tables such that the WIDTH specified...
25
by: Sune A | last post by:
Hi All! I'm having problems with www.suneworld.com The thing is that I'd like to lock the width of the page, so that it won't adjust dynamically. Any CSS people out there that can help me? ...
16
by: karlman | last post by:
I am trying to create a web page that shows a calendar schedules. I am attempting to use table cells with fixed widths and different colors to display this. It seems that IE6 doesn't always...
0
by: javaguy | last post by:
I have a table I hide show/hide through a pair of divs. The show/hide part works fine in both Mozilla and MSIE. When switching between the hide and show versions the Mozilla browser keeps them in...
5
by: Jean Pion | last post by:
Dear readers, Can anyone explain how to set column width of a table in ccs. I use the following style in an external stylesheet: table.tbl { table-layout:fixed; border-top: 5px solid #333;...
2
by: Stewart | last post by:
Originally posted in comp.lang.javascript: Newsgroups: comp.lang.javascript From: "Stewart" Date: 23 Aug 2005 02:50:04 -0700 Local: Tues, Aug 23 2005 10:50 am Subject: FireFox, RemoveChild,...
7
by: Yongjian | last post by:
Hello, I want to read a text file which contains French charactor, then modify some of the sentence, save it as a new text file. But I found the new text file lost all the French charactors....
1
by: sandipvdhande | last post by:
I have problem in printinh the extended ascii charactors to file in C.Please tell me the solution for this issue .Is there any function to convert the charactor ..or is there any function to change...
4
by: lilOlMe | last post by:
Hi there! I'm developing a "scrollable table". At first I started using CSS. It worked pretty well until I found a bug that happens when placing this table into a tabbing control... So I...
0
by: lllomh | last post by:
Define the method first this.state = { buttonBackgroundColor: 'green', isBlinking: false, // A new status is added to identify whether the button is blinking or not } autoStart=()=>{
2
by: DJRhino | last post by:
Was curious if anyone else was having this same issue or not.... I was just Up/Down graded to windows 11 and now my access combo boxes are not acting right. With win 10 I could start typing...
2
by: giovanniandrean | last post by:
The energy model is structured as follows and uses excel sheets to give input data: 1-Utility.py contains all the functions needed to calculate the variables and other minor things (mentions...
4
NeoPa
by: NeoPa | last post by:
Hello everyone. I find myself stuck trying to find the VBA way to get Access to create a PDF of the currently-selected (and open) object (Form or Report). I know it can be done by selecting :...
1
by: Teri B | last post by:
Hi, I have created a sub-form Roles. In my course form the user selects the roles assigned to the course. 0ne-to-many. One course many roles. Then I created a report based on the Course form and...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 1 Nov 2023 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM) Please note that the UK and Europe revert to winter time on...
0
NeoPa
by: NeoPa | last post by:
Introduction For this article I'll be focusing on the Report (clsReport) class. This simply handles making the calling Form invisible until all of the Reports opened by it have been closed, when it...
0
isladogs
by: isladogs | last post by:
The next online meeting of the Access Europe User Group will be on Wednesday 6 Dec 2023 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, Mike...
2
by: GKJR | last post by:
Does anyone have a recommendation to build a standalone application to replace an Access database? I have my bookkeeping software I developed in Access that I would like to make available to other...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.