width of a charactor

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Alex Shi

    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!

    =============== =============== =============== =====
  • CountScubula

    #2
    Re: width of a charactor

    "Alex Shi" <chpshi@stonix. com> wrote in message
    news:F8CWb.2119 2$pX2.2359@nntp-post.primus.ca. ..[color=blue]
    > 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
    > =============== =============== =============== =====
    >[/color]

    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


    Comment

    • Chung Leong

      #3
      Re: width of a charactor

      Uzytkownik "Alex Shi" <chpshi@stonix. com> napisal w wiadomosci
      news:F8CWb.2119 2$pX2.2359@nntp-post.primus.ca. ..[color=blue]
      > 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
      > =============== =============== =============== =====[/color]

      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 (chernyshevsky@ 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);
      }
      }
      }

      ?>


      Comment

      Working...