1<?php 2 3$GLOBALS['g_last_assigned_font_id'] = 0; 4 5class Font { 6 var $underline_position; 7 var $underline_thickness; 8 var $ascender; 9 var $descender; 10 var $char_widths; 11 var $bbox; 12 13 function ascender() { 14 return $this->ascender; 15 } 16 17 function descender() { 18 return $this->descender; 19 } 20 21 function error_message() { 22 return $this->error_message; 23 } 24 25 function Font() {} 26 27 function linethrough_position() { 28 return $this->bbox[3]*0.25; 29 } 30 31 function name() { 32 return $this->name; 33 } 34 35 function overline_position() { 36 return $this->bbox[3]*0.8; 37 } 38 39 function points($fontsize, $dimension) { 40 return $dimension * $fontsize / 1000; 41 } 42 43 function stringwidth($string) { 44 $width = 0; 45 46 $length = strlen($string); 47 for ($i=0; $i<$length; $i++) { 48 $width += $this->char_widths[$string{$i}]; 49 }; 50 51 return $width; 52 } 53 54 function underline_position() { 55 return $this->underline_position; 56 } 57 58 function underline_thickness() { 59 return $this->underline_thickness; 60 } 61} 62 63class FontTrueType extends Font { 64 function create($fontfile, $encoding) { 65 $font = new FontTrueType(); 66 $font->_read(TTF_FONTS_REPOSITORY.$fontfile, $encoding); 67 return $font; 68 } 69 70 /** 71 * TODO: cache results; replace makefont with this utility 72 */ 73 function _read($file, $encoding) { 74 error_log(sprintf("Parsing font file file %s for encoding %s", $file, $encoding)); 75 76 $font = new OpenTypeFile(); 77 $font->open($file); 78 $hhea = $font->getTable('hhea'); 79 $head = $font->getTable('head'); 80 $hmtx = $font->getTable('hmtx'); 81 $post = $font->getTable('post'); 82 $cmap = $font->getTable('cmap'); 83 $subtable = $cmap->findSubtable(OT_CMAP_PLATFORM_WINDOWS, 84 OT_CMAP_PLATFORM_WINDOWS_UNICODE); 85 86 /** 87 * Read character widths for selected encoding 88 */ 89 $widths = array(); 90 $manager = ManagerEncoding::get(); 91 $map = $manager->get_encoding_vector($encoding); 92 foreach ($map as $code => $ucs2) { 93 $glyphIndex = $subtable->lookup($ucs2); 94 if (!is_null($glyphIndex)) { 95 $widths[$code] = floor($hmtx->_hMetrics[$glyphIndex]['advanceWidth']*1000/$head->_unitsPerEm); 96 } else { 97 $widths[$code] = DEFAULT_CHAR_WIDTH; 98 }; 99 }; 100 101 // Fill unknown characters with the default char width 102 for ($i=0; $i<256; $i++) { 103 if (!isset($widths[chr($i)])) { 104 $widths[chr($i)] = DEFAULT_CHAR_WIDTH; 105 }; 106 }; 107 108 $this->ascender = floor($hhea->_ascender*1000/$head->_unitsPerEm); 109 $this->descender = floor($hhea->_descender*1000/$head->_unitsPerEm); 110 $this->bbox = array($head->_xMin*1000/$head->_unitsPerEm, 111 $head->_yMin*1000/$head->_unitsPerEm, 112 $head->_xMax*1000/$head->_unitsPerEm, 113 $head->_yMax*1000/$head->_unitsPerEm); 114 $this->underline_position = floor($post->_underlinePosition*1000/$head->_unitsPerEm); 115 $this->underline_thickness = floor($post->_underlineThickness*1000/$head->_unitsPerEm); 116 $this->char_widths = $widths; 117 118 $font->close(); 119 } 120} 121 122// Note that ALL font dimensions are measured in 1/1000 of font size units; 123// 124class FontType1 extends Font { 125 function &create($typeface, $encoding, $font_resolver, &$error_message) { 126 $font = new FontType1(); 127 128 $font->underline_position = 0; 129 $font->underline_thickness = 0; 130 $font->ascender; 131 $font->descender; 132 $font->char_widths = array(); 133 $font->bbox = array(); 134 135 global $g_last_assigned_font_id; 136 $g_last_assigned_font_id++; 137 138 $font->name = "font".$g_last_assigned_font_id; 139 140 // Get and load the metrics file 141 $afm = $font_resolver->get_afm_mapping($typeface); 142 143 if (!$font->_parse_afm($afm, $typeface, $encoding)) { 144 $error_message = $font->error_message(); 145 $dummy = null; 146 return $dummy; 147 }; 148 149 return $font; 150 } 151 152 // Parse the AFM metric file; keep only sized of glyphs present in the chosen encoding 153 function _parse_afm($afm, $typeface, $encoding) { 154 global $g_manager_encodings; 155 $encoding_data = $g_manager_encodings->get_glyph_to_code_mapping($encoding); 156 157 $filename = TYPE1_FONTS_REPOSITORY.$afm.".afm"; 158 159 $file = @fopen($filename, 'r'); 160 if (!$file) { 161 $_filename = $filename; 162 $_typeface = $typeface; 163 164 ob_start(); 165 include(HTML2PS_DIR.'templates/error._missing_afm.tpl'); 166 $this->error_message = ob_get_contents(); 167 ob_end_clean(); 168 169 error_log(sprintf("Missing font metrics file: %s",$filename)); 170 return false; 171 }; 172 173 while ($line = fgets($file)) { 174 if (preg_match("/C\s-?\d+\s;\sWX\s(\d+)\s;\sN\s(\S+)\s;/",$line,$matches)) { 175 $glyph_width = $matches[1]; 176 $glyph_name = $matches[2]; 177 178 // This line is a character width definition 179 if (isset($encoding_data[$glyph_name])) { 180 foreach ($encoding_data[$glyph_name] as $c) { 181 $this->char_widths[$c] = $glyph_width; 182 }; 183 }; 184 185 } elseif (preg_match("/UnderlinePosition ([\d-]+)/",$line,$matches)) { 186 // This line is an underline position line 187 $this->underline_position = $matches[1]; 188 189 } elseif (preg_match("/UnderlineThickness ([\d-]+)/",$line,$matches)) { 190 // This line is an underline thickness line 191 $this->underline_thickness = $matches[1]; 192 193 } elseif (preg_match("/Ascender ([\d-]+)/",$line,$matches)) { 194 // This line is an ascender line 195 $this->ascender = $matches[1]; 196 197 } elseif (preg_match("/Descender ([\d-]+)/",$line,$matches)) { 198 // This line is an descender line 199 $this->descender = $matches[1]; 200 201 } elseif (preg_match("/FontBBox ([\d-]+) ([\d-]+) ([\d-]+) ([\d-]+)/",$line,$matches)) { 202 // This line is an font BBox line 203 $this->bbox = array($matches[1], $matches[2], $matches[3], $matches[4]); 204 }; 205 }; 206 207 fclose($file); 208 209 // Fill unknown characters with the default char width 210 for ($i=0; $i<256; $i++) { 211 if (!isset($this->char_widths[chr($i)])) { 212 $this->char_widths[chr($i)] = DEFAULT_CHAR_WIDTH; 213 }; 214 }; 215 216 return true; 217 } 218} 219?>