Beispiel #1
0
 /**
  * Adds unicode fonts.<br>
  * Based on PDF Reference 1.3 (section 5)
  * @access protected
  * @author Nicola Asuni
  * @since 1.52.0.TC005 (2005-01-05)
  */
 protected function _puttruetypeembedded($font)
 {
     // Type0 Font
     // A composite font composed of other fonts, organized hierarchically
     $this->_newobj();
     $this->_out('<</Type /Font');
     $this->_out('/Subtype /Type0');
     $this->_out('/BaseFont /' . $font['name'] . '');
     $this->_out('/Encoding /Identity-H');
     //The horizontal identity mapping for 2-byte CIDs; may be used with CIDFonts using any Registry, Ordering, and Supplement values.
     $this->_out('/DescendantFonts [' . ($this->n + 1) . ' 0 R]');
     $this->_out('/ToUnicode ' . ($this->n + 2) . ' 0 R');
     $this->_out('>>');
     $this->_out('endobj');
     // CIDFontType2
     // A CIDFont whose glyph descriptions are based on TrueType font technology
     $this->_newobj();
     $this->_out('<</Type /Font');
     $this->_out('/Subtype /CIDFontType2');
     $this->_out('/BaseFont /' . $font['name'] . '');
     $this->_out('/CIDSystemInfo ' . ($this->n + 2) . ' 0 R');
     $this->_out('/FontDescriptor ' . ($this->n + 3) . ' 0 R');
     if (isset($font['desc']['MissingWidth'])) {
         $this->_out('/DW ' . $font['desc']['MissingWidth'] . '');
         // The default width for glyphs in the CIDFont MissingWidth
     }
     $w = "";
     $fm = $font['font_metric'];
     $fm->setDirection('H');
     $char_widths = $font['font_metric']->getCharacterWidths();
     foreach ($char_widths as $char => $width) {
         $cid = I2CE_UTF8::to_codepoints($char);
         if (empty($cid)) {
             continue;
         }
         $w .= '' . $cid[0] . ' [' . $width . '] ';
         // define a specific width for each individual CID
     }
     $this->_out('/W [' . $w . ']');
     // A description of the widths for the glyphs in the CIDFont
     $this->_out('/CIDToGIDMap ' . ($this->n + 4) . ' 0 R');
     $this->_out('>>');
     $this->_out('endobj');
     // ToUnicode
     // is a stream object that contains the definition of the CMap
     // (PDF Reference 1.3 chap. 5.9)
     $this->_newobj();
     $this->_out('<</Length 383>>');
     $this->_out('stream');
     $this->_out('/CIDInit /ProcSet findresource begin');
     $this->_out('12 dict begin');
     $this->_out('begincmap');
     $this->_out('/CIDSystemInfo');
     $this->_out('<</Registry (Adobe)');
     $this->_out('/Ordering (UCS)');
     $this->_out('/Supplement 0');
     $this->_out('>> def');
     $this->_out('/CMapName /Adobe-Identity-UCS def');
     $this->_out('/CMapType 2 def');
     $this->_out('1 begincodespacerange');
     $this->_out('<0000> <FFFF>');
     $this->_out('endcodespacerange');
     $this->_out('1 beginbfrange');
     $this->_out('<0000> <FFFF> <0000>');
     $this->_out('endbfrange');
     $this->_out('endcmap');
     $this->_out('CMapName currentdict /CMap defineresource pop');
     $this->_out('end');
     $this->_out('end');
     $this->_out('endstream');
     $this->_out('endobj');
     // CIDSystemInfo dictionary
     // A dictionary containing entries that define the character collection of the CIDFont.
     $this->_newobj();
     $this->_out('<</Registry (Adobe)');
     // A string identifying an issuer of character collections
     $this->_out('/Ordering (UCS)');
     // A string that uniquely names a character collection issued by a specific registry
     $this->_out('/Supplement 0');
     // The supplement number of the character collection.
     $this->_out('>>');
     $this->_out('endobj');
     // Font descriptor -- object $n+4
     // A font descriptor describing the CIDFont default metrics other than its glyph widths
     $this->_newobj();
     $this->_out('<</Type /FontDescriptor');
     $this->_out('/FontName /' . $font['name']);
     foreach ($font['desc'] as $key => $value) {
         $this->_out('/' . $key . ' ' . $value);
     }
     //the stream containing the TrueType font program
     $this->_out('/FontFile2 ' . $this->FontFiles[$font['file']]['n'] . ' 0 R');
     $this->_out('>>');
     $this->_out('endobj');
     // Embed CIDToGIDMap -- object $n+5
     // A specification of the mapping from CIDs to glyph indices
     $this->_newobj();
     $size = strlen($font['cidtogid']);
     $this->_out('<</Length ' . $size . '');
     if ($font['compress']) {
         $this->_out('/Filter /FlateDecode');
     }
     $this->_out('>>');
     $this->_putstream($font['cidtogid']);
     $this->_out('endobj');
 }
 public function getEncodedCharactersFromGlyphID($gid)
 {
     $cps = $this->GIDtoCPs[$gid];
     $out = mb_convert_encoding('', $this->getEncoding()->getEncodingType(), 'ASCII');
     if (is_array($cps)) {
         foreach ($cps as $cp) {
             if ($cp == -1) {
                 $cp = 0xfffd;
             }
             $out .= mb_convert_encoding(I2CE_UTF8::cp_to_code($cp), $this->getEncoding()->getEncodingType(), 'UTF-8');
         }
     }
     return $out;
 }
 /**
  *  Load the font metrics from an afm  file
  *  (Some of this code was stolen from makefont.php)
  *  @param I2CE_Encoding $encoding -- the character encoding  used in the file
  *  @param string $afmfile
  *  Caution:  Units for these files are 1/1000 of a point where a point is 1/72 of an inch
  */
 protected function loadFontMetricFromAFM($encoding, $afmfile)
 {
     $prev_dir = $this->getDirection();
     $sub = mb_substitute_character();
     mb_substitute_character("none");
     //Read a font metric file
     $found_afmfile = I2CE::getFileSearch()->search('AFM_PATH', $afmfile);
     if (!$found_afmfile) {
         die('Error: AFM file not found: ' . $afmfile);
     } else {
         $afmfile = $found_afmfile;
     }
     $a = file($found_afmfile);
     if (empty($a)) {
         die('File empty' . $afmfile);
     }
     $this->setGlobal();
     //global information is the default
     $this->setLinegap(0);
     //no line gap information is present in a AFM file
     $mode = 0;
     /**
      * Modes are 0: header or wirting direction
      * 10: Character metrics
      * 20: Kerning
      * 21: Kerning Tracking
      * 21: Kerning Pairs 
      * 30: Composites
      */
     foreach ($a as $l) {
         $e = explode(' ', rtrim($l));
         $code = $e[0];
         switch ($mode) {
             case 10:
                 //character metrics
                 $vals = array();
                 unset($cc);
                 unset($gn);
                 switch ($code) {
                     case 'EndCharacterMetrics':
                     case 'EndCharMetrics':
                         $mode = 0;
                         continue 2;
                         //break out of the switch($mode)
                     //break out of the switch($mode)
                     case 'C':
                         $cc = (int) $e[1];
                         break;
                     case 'CH':
                         $cc = hexdec($e[1]);
                         break;
                 }
                 if (!isset($cc)) {
                     break;
                 }
                 $i = 3;
                 while ($i < count($e)) {
                     $subcode = $e[$i];
                     switch ($subcode) {
                         case 'WX':
                         case 'W0X':
                         case 'W1X':
                         case 'WY':
                         case 'W0Y':
                         case 'W1Y':
                             //widths and heights
                             $vals[$subcode] = (double) $e[$i + 1];
                             $i = $i + 2;
                             break;
                         case 'W':
                         case 'W0':
                         case 'W1':
                         case 'VV':
                             //widths and heights
                             $vals[$subcode] = array((double) $e[$i + 1], (double) $e[$i + 2]);
                             $i = $i + 3;
                             break;
                         case 'N':
                             //postscript glyph name
                             $gn = $e[$i + 1];
                             $i = $i + 2;
                             break;
                         case 'B':
                             //bounding box
                             $vals[$subcode] = array((double) $e[$i + 1], (double) $e[$i + 2], (double) $e[$i + 3], (double) $e[$i + 4]);
                             $i = $i + 5;
                             break;
                         case 'L':
                             //Ligature sequence -- may have more than one
                             if (!isset($vals['L'])) {
                                 $vals['L'] = array();
                             }
                             $vals['L'][] = array($e[$i + 1], $e[$i + 2]);
                             $i = $i + 3;
                             $i++;
                             break;
                         default:
                             $i++;
                             break;
                     }
                 }
                 unset($uc);
                 if ($cc < 0) {
                     //not a valid character code
                     if ($gn !== null) {
                         //try to get the unicode code point of the glyphname based on our encoding
                         $uc = $encoding->UnicodeFromGlyphname($gn);
                         if ($uc === null) {
                             //we failed
                             //try to get a unicode code point from the glyphname
                             if (preg_match('/^uni([0-9A-F]{4})$/', $gn, $ucs)) {
                                 //we have a unicode codepoint
                                 $uc = $ucs[1];
                             }
                         }
                     }
                 } else {
                     //we have a valid character code
                     $uc = $encoding->UnicodeFromCharactercode($cc);
                     if ($gn !== null) {
                         $this->gn2cc[$gn] = $cc;
                     }
                 }
                 if ($uc !== null && $uc <= 0xffff && $uc >= 0 && $uc !== 0xfffd) {
                     //replacement character
                     $uc = I2CE_UTF8::cp_to_code($uc);
                     //convert to UTF8
                     $cc = mb_convert_encoding($uc, $this->getEncoding()->getEncodingType(), 'UTF-8');
                     if ($this->getEncoding()->useMB()) {
                         if (mb_strlen($cc, $this->getEncoding()->getEncodingType()) === 0) {
                             $cc = -1;
                         }
                     } else {
                         if (strlen($cc) === 0) {
                             $cc = -1;
                         }
                     }
                 } else {
                     $cc = -1;
                 }
                 foreach (array('WX', 'W0X') as $key) {
                     if (array_key_exists($key, $vals) && $vals[$key] !== null) {
                         $this->setDirection('H');
                         if ($cc !== -1) {
                             $this->setCharacterWidth($cc, $vals[$key]);
                         }
                         if ($gn !== null) {
                             $this->setCharacterWidth($gn, $vals[$key]);
                         }
                     }
                 }
                 if (array_key_exists('W1X', $vals) && $vals['W1X'] !== null) {
                     $this->setDirection('V');
                     if ($cc !== -1) {
                         $this->setCharacterWidth($cc, $vals['W1X']);
                     }
                     if (null !== $gn) {
                         $this->setCharacterWidth($gn, $vals['W1X']);
                     }
                 }
                 foreach (array('WY', 'W0Y') as $key) {
                     if (array_key_exists($key, $vals) && null !== $vals[$key]) {
                         $this->setDirection('H');
                         if ($cc !== -1) {
                             $this->setCharacterHeight($cc, $vals[$key]);
                         }
                         if (null !== $gn) {
                             $this->setCharacterHeight($gn, $vals[$key]);
                         }
                     }
                 }
                 if (array_key_exists('W1Y', $vals) && null !== $vals['W1Y']) {
                     $this->setDirection('V');
                     if ($cc !== -1) {
                         $this->setCharacterHeight($cc, $vals['W1Y']);
                     }
                     if (null !== $gn) {
                         $this->setCharacterHeight($gn, $vals['W1Y']);
                     }
                 }
                 foreach (array('W', 'W0') as $key) {
                     $this->setDirection('H');
                     if (array_key_exists($key, $vals) && null !== $vals[$key]) {
                         if ($cc !== -1) {
                             $this->setCharacterWidth($cc, $vals[$key]);
                             $this->setCharacterHeight($cc, $vals[$key]);
                         }
                         if (null !== $gn) {
                             $this->setCharacterWidth($gn, $vals[$key]);
                             $this->setCharacterHeight($gn, $vals[$key]);
                         }
                     }
                 }
                 if (array_key_exists('W1', $vals) && null !== $vals['W1']) {
                     $this->setDirection('V');
                     if ($cc !== -1) {
                         $this->setCharacterWidth($cc, $vals[$key]);
                         $this->setCharacterHeight($cc, $vals[$key]);
                     }
                     if (null !== $gn) {
                         $this->setCharacterWidth($gn, $vals[$key]);
                         $this->setCharacterHeight($gn, $vals[$key]);
                     }
                 }
                 foreach (array('VVector' => 'VV', 'BoundingBox' => 'B', 'Ligature' => 'L') as $name => $key) {
                     $this->setGlobal();
                     if (array_key_exists($key, $vals) && null !== $vals[$key]) {
                         if ($cc !== -1) {
                             $this->setCharacterInfo($cc, $name, $vals[$key]);
                         }
                         if (null !== $gn) {
                             $this->setCharacterInfo($gn, $name, $vals[$key]);
                         }
                     }
                 }
                 break;
             case 20:
                 //kerning
                 switch ($code) {
                     case 'EndKernData':
                         $mode = 0;
                         break;
                     case 'StartTrackKern':
                         $mode = 21;
                         break;
                     case 'StartKernPairs':
                     case 'StartKernPairs0':
                         $this->setDirection('H');
                         $mode = 22;
                         break;
                     case 'StartKernPairs1':
                         $this->setDirection('V');
                         $mode = 22;
                         break;
                 }
                 break;
             case 21:
                 //kerning tracking
                 switch ($code) {
                     case 'EndTrackKern':
                         $mode = 20;
                         break;
                     case '':
                         break;
                 }
                 break;
             case 22:
                 //kerning pairs
                 switch ($code) {
                     case 'EndKernPairs':
                         $mode = 21;
                         break;
                     case 'KP':
                         //kerning pairs are given by glyph name
                         $this->setDirection('H');
                         $this->setKerningByPair($e[1], $e[2], (double) $e[3]);
                         $this->setDirection('V');
                         $this->setKerningByPair($e[1], $e[2], (double) $e[4]);
                         //get the corresponding character codes and insert into the table
                         $this->setGlobal();
                         $cc1 = $this->getEncoding()->getCodeFromGlyphname($e[1]);
                         $cc2 = $this->getEncoding()->getCodeFromGlyphname($e[2]);
                         if ($cc1 !== -1 && $cc2 !== -1) {
                             $this->setDirection('H');
                             $this->setKerningByPair($cc1, $cc2, (double) $e[3]);
                             $this->setDirection('V');
                             $this->setKerningByPair($cc1, $cc2, (double) $e[4]);
                         }
                         break;
                     case 'KPH':
                         //not sure what is best to do here.
                         $ch1 = ltrim(rtrim($e[1], '>'), '<');
                         $ch2 = ltrim(rtrim($e[1], '>'), '<');
                         $this->setDirection('H');
                         $this->setKerningByPair($ch1, $ch2, (double) $e[3]);
                         $this->setDirection('V');
                         $this->setKerningByPair($ch1, $ch2, (double) $e[4]);
                         break;
                     case 'KPX':
                         $this->setDirection('H');
                         $this->setKerningByPair($e[1], $e[2], (double) $e[3]);
                         if (!array_key_exists($e[1], $this->gn2cc) || !array_key_exists($e[2], $this->gn2cc)) {
                             break;
                         }
                         $cc1 = $this->gn2cc[$e[1]];
                         $cc2 = $this->gn2cc[$e[2]];
                         if ($cc1 !== -1 && $cc2 !== -1) {
                             $this->setKerningByPair($cc1, $cc2, (double) $e[3]);
                         }
                         break;
                     case 'KPY':
                         $this->setDirection('V');
                         $this->setKerningByPair($e[1], $e[2], (double) $e[3]);
                         if (!array_key_exists($e[1], $this->gn2cc) || !array_key_exists($e[2], $this->gn2cc)) {
                             break;
                         }
                         $cc1 = $this->gn2cc[$e[1]];
                         $cc2 = $this->gn2cc[$e[2]];
                         if ($cc1 !== -1 && $cc2 !== -1) {
                             $this->setKerningByPair($cc1, $cc2, (double) $e[3]);
                         }
                         break;
                 }
                 break;
             case 30:
                 //composites
                 switch ($code) {
                     case 'EndComposites':
                         $mode = 0;
                         break;
                 }
                 break;
             default:
                 //header/global information or writing direction
                 switch ($code) {
                     case 'BeginCharacterMetrics':
                     case 'StartCharMetrics':
                         $mode = 10;
                         break;
                     case 'BeginKernData':
                     case 'StartKernData':
                         $mode = 20;
                         break;
                     case 'BeginComposites':
                     case 'StartComposites':
                         $mode = 03;
                         break;
                     case 'StartDirection':
                         //does not have to exist in which case we are in direction 0
                         if ($e[1] == '1') {
                             $this->setDirection('V');
                         } else {
                             $this->setDirection('H');
                         }
                         break;
                     case 'EndDirection':
                         $this->setGlobal();
                         break;
                     case 'CharWidth':
                         $dir = $this->getDirection();
                         //this is not global information
                         if ($dir === -1) {
                             // however the keyword StartDirection is optional
                             $this->setDirection('H');
                         }
                         $this->setFixedWidth(true);
                         $this->setFixedWidthSize((double) $e[1]);
                         $this->setFixedHeightSize((double) $e[2]);
                         $this->setDirection($dir);
                         break;
                     case 'UnderlinePosition':
                     case 'UnderlineThickness':
                     case 'ItalicAngle':
                         $dir = $this->getDirection();
                         //this is not global information
                         if ($dir === -1) {
                             // however the keyword StartDirection is optional
                             $this->setDirection('H');
                         }
                         $this->setFontCharacteristic($code, $e[1]);
                         $this->setDirection($dir);
                         break;
                     case 'IsFixedPitch':
                         $isfixed = strpos(strtolower($e[1]), 'true') === 0;
                         $dir = $this->getDirection();
                         //this is not global information
                         if ($dir === -1) {
                             // however the keyword StartDirection is optional
                             $this->setDirection('H');
                         }
                         $this->setFixedWidth($isfixed);
                         $this->setDirection($dir);
                         break;
                     case 'isFixedV':
                     case 'isBaseFont':
                         $this->setGlobal();
                         $this->setFontCharacteristic($code, strpos(strtolower($e[1]), 'true'));
                         break;
                     case 'MappingScheme':
                     case 'EscCar':
                     case 'Characters':
                         $this->setGlobal();
                         $this->setFontCharacteristic($code, (int) $e[1]);
                         break;
                     case 'Notice':
                     case 'Comment':
                         $this->setGlobal();
                         $val = $this->getFontCharacteristic($code);
                         if ($val === null) {
                             $val = "";
                         }
                         $this->setFontCharacteristic($code, $val . substr($l, strlen($code) + 1));
                         break;
                     case 'CapHeight':
                     case 'XHeight':
                         $this->setGlobal();
                         $this->setFontCharacteristic($code, (double) $e[1]);
                         break;
                     case 'Ascender':
                         $this->setGlobal();
                         $this->setAscender((double) $e[1]);
                         break;
                     case 'Descender':
                         $this->setGlobal();
                         $this->setDescender((double) $e[1]);
                         break;
                     case 'VVector':
                         $this->setGlobal();
                         $this->setFontCharacteristic($code, array((double) $e[1], (double) $e[2]));
                     case 'FontBBox':
                         $this->setGlobal();
                         $this->setBoundingBox(array((double) $e[1], (double) $e[2], (double) $e[3], (double) $e[4]));
                         break;
                     default:
                         //the rest are strings for global font information
                         $this->setGlobal();
                         $this->setFontCharacteristic($code, $e[1]);
                         break;
                 }
                 break;
         }
     }
     //normalize a few values.
     $this->setGlobal();
     $asc = $this->getAscender();
     if ($asc == 0) {
         $d = mb_convert_encoding('d', $this->getEncoding()->getEncodingType(), 'ASCII');
         $ht = $this->getCharacterHeight($d);
         if ($ht != 0) {
             $this->setAscender($ht);
         } else {
             $this->setDirection('H');
             $ht = $this->getCharacterHeight($d);
             if ($ht != 0) {
                 $this->setAscender($ht);
             } else {
                 $this->setGlobal();
                 $bbox = $this->getBoundingBox();
                 $this->setAscender($bbox[3]);
             }
         }
     }
     $this->setGlobal();
     $dsc = $this->getAscender();
     if ($dsc == 0) {
         $p = mb_convert_encoding('p', $this->getEncoding()->getEncodingType(), 'ASCII');
         $bbox = $this->getCharacterInfo($p, 'BoundingBox');
         if (!$bbox == null) {
             $this->setDescender($bbox[1]);
         } else {
             $this->setDirection('H');
             $bbox = $this->getCharacterInfo($p, 'BoundingBox');
             if (!$bbox == null) {
                 $this->setDescender($bbox[1]);
             } else {
                 $this->setGlobal();
                 $bbox = $this->getBoundingBox();
                 $this->setDescender($bbox[1]);
             }
         }
     }
     $this->setDirection($prev_dir);
     mb_substitute_character($sub);
 }