open($fontfile);
$head =& $font->getTable('head');
$name =& $font->getTable('name');
$cmap =& $font->getTable('cmap');
$hmtx =& $font->getTable('hmtx');
$hhea =& $font->getTable('hhea');
$post =& $font->getTable('post');
$subtable =& $cmap->findSubtable(OT_CMAP_PLATFORM_WINDOWS,
OT_CMAP_PLATFORM_WINDOWS_UNICODE);
/**
* Prepare initial data
*/
$widths = array();
for ($i=0; $i<256; $i++) {
$code = chr($i);
if (!isset($map[$code])) {
$widths[] = 1000;
continue;
};
$ucs2 = $map[$code];
/**
* If the font is monospaced, only one entry need be in the array,
* but that entry is required. The last entry applies to all
* subsequent glyphs.
*/
$glyphIndex = $subtable->lookup($ucs2);
if (!is_null($glyphIndex)) {
$realIndex = min($glyphIndex, $hhea->_numberOfHMetrics-1);
$widths[] = floor($hmtx->_hMetrics[$realIndex]['advanceWidth']*1000/$head->_unitsPerEm);
} else {
$widths[] = 1000;
};
};
$font_info = array();
/**
* Here we use a hack; as, acording to OT specifications,
*
* When translated to ASCII, these [...] strings must be
* identical; no longer than 63 characters; and restricted to the
* printable ASCII subset, codes 33 through 126, except for the 10
* characters: '[', ']', '(', ')', '{', '}', '<', '>', '/', '%'.
*
* we can assume that UCS-2 encoded string we receive can be easily
* translated to ASCII by removing the high-byte of all two-byte characters
*/
$ps_name_ucs2 = $name->lookup(OT_CMAP_PLATFORM_WINDOWS,
OT_CMAP_PLATFORM_WINDOWS_UNICODE,
OT_CMAP_LANGUAGE_WINDOWS_ENGLISH_AMERICAN,
OT_NAME_ID_POSTSCRIPT_NAME);
$ps_name_ascii = "";
for ($i=0; $ilookup(null, null, null, OT_NAME_ID_SUBFAMILY_NAME);
$font_info['ItalicAngle'] = $post->_italicAngle;
$font_info['IsFixedPitch'] = (bool)$post->_isFixedPitch;
// $font_info['CapHeight']
// $font_info['StdVW']
$font_info['Ascender'] = floor($hhea->_ascender*1000/$head->_unitsPerEm);
$font_info['Descender'] = floor($hhea->_descender*1000/$head->_unitsPerEm);
$font_info['UnderlineThickness'] = floor($post->_underlineThickness*1000/$head->_unitsPerEm);
$font_info['UnderlinePosition'] = floor($post->_underlinePosition*1000/$head->_unitsPerEm);
$font_info['FontBBox'] = array($head->_xMin*1000/$head->_unitsPerEm,
$head->_yMin*1000/$head->_unitsPerEm,
$head->_xMax*1000/$head->_unitsPerEm,
$head->_yMax*1000/$head->_unitsPerEm);
$font_info['Widths'] = $widths;
$font->_delete();
unset($font);
return $font_info;
}
/**
* @return Array font metrics hash or null of AFM file is missing
*/
function ReadAFM($file, $map) {
if (!is_readable($file)) { return null; };
$afm_lines = file($file);
$widths=array();
$fm=array();
foreach ($afm_lines as $l) {
$e=explode(' ',rtrim($l));
if (count($e)<2) {
continue;
};
$code=$e[0];
$param=$e[1];
if ($code=='C') {
//Character metrics
$cc=(int)$e[1];
$w=$e[4];
$gn=$e[7];
if (substr($gn,-4)=='20AC') {
$gn='Euro';
};
$widths[$gn]=$w;
if ($gn=='.notdef') {
$fm['MissingWidth']=$w;
};
}
elseif($code=='FontName')
$fm['FontName']=$param;
elseif($code=='Weight')
$fm['Weight']=$param;
elseif($code=='ItalicAngle')
$fm['ItalicAngle']=(double)$param;
elseif($code=='Ascender')
$fm['Ascender']=(int)$param;
elseif($code=='Descender')
$fm['Descender']=(int)$param;
elseif($code=='UnderlineThickness')
$fm['UnderlineThickness']=(int)$param;
elseif($code=='UnderlinePosition')
$fm['UnderlinePosition']=(int)$param;
elseif($code=='IsFixedPitch')
$fm['IsFixedPitch']=($param=='true');
elseif($code=='FontBBox')
$fm['FontBBox']=array($e[1],$e[2],$e[3],$e[4]);
elseif($code=='CapHeight')
$fm['CapHeight']=(int)$param;
elseif($code=='StdVW')
$fm['StdVW']=(int)$param;
}
if(!isset($fm['FontName'])) {
die('FontName not found');
};
if (!isset($widths['.notdef'])) {
$widths['.notdef']=600;
};
if (!isset($widths['Delta']) and isset($widths['increment'])) {
$widths['Delta']=$widths['increment'];
};
// Order widths according to map
for ($i=0; $i<=255; $i++) {
if(!isset($widths[$map[chr($i)]])) {
error_log('Warning: character '.$map[chr($i)].' is missing
');
$widths[$i]=$widths['.notdef'];
} else {
$widths[$i]=$widths[$map[chr($i)]];
};
};
$fm['Widths']=$widths;
return $fm;
}
function MakeFontDescriptor($fm,$symbolic) {
//Ascent
$asc=(isset($fm['Ascender']) ? $fm['Ascender'] : 1000);
$fd="array('Ascent'=>".$asc;
//Descent
$desc=(isset($fm['Descender']) ? $fm['Descender'] : -200);
$fd.=",'Descent'=>".$desc;
//CapHeight
if (isset($fm['CapHeight'])) {
$ch=$fm['CapHeight'];
} elseif(isset($fm['CapXHeight'])) {
$ch=$fm['CapXHeight'];
} else {
$ch=$asc;
};
$fd.=",'CapHeight'=>".$ch;
//Flags
$flags=0;
if (isset($fm['IsFixedPitch']) and $fm['IsFixedPitch']) {
$flags+=1<<0;
};
if ($symbolic) {
$flags+=1<<2;
};
if (!$symbolic) {
$flags+=1<<5;
};
if (isset($fm['ItalicAngle']) and $fm['ItalicAngle']!=0) {
$flags+=1<<6;
};
$fd.=",'Flags'=>".$flags;
//FontBBox
if (isset($fm['FontBBox'])) {
$fbb=$fm['FontBBox'];
} else {
$fbb=array(0,$des-100,1000,$asc+100);
};
$fd.=",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'";
//ItalicAngle
$ia=(isset($fm['ItalicAngle']) ? $fm['ItalicAngle'] : 0);
$fd.=",'ItalicAngle'=>".$ia;
//StemV
if (isset($fm['StdVW'])) {
$stemv=$fm['StdVW'];
} elseif(isset($fm['Weight']) and eregi('(bold|black)',$fm['Weight'])) {
$stemv=120;
} else {
$stemv=70;
};
$fd.=",'StemV'=>".$stemv;
//MissingWidth
if (isset($fm['MissingWidth'])) {
$fd.=",'MissingWidth'=>".$fm['MissingWidth'];
};
$fd.=')';
return $fd;
}
function MakeWidthArray($fm) {
//Make character width array
$s="array(\n\t";
$cw=$fm['Widths'];
for ($i=0; $i<=255; $i++) {
if (chr($i)=="'") {
$s.="'\\''";
} elseif (chr($i)=="\\") {
$s.="'\\\\'";
} elseif($i>=32 and $i<=126) {
$s.="'".chr($i)."'";
} else {
$s.="chr($i)";
};
$s.='=>'.$fm['Widths'][$i];
if ($i<255) {
$s.=',';
};
if(($i+1)%22==0) {
$s.="\n\t";
};
}
$s.=')';
return $s;
}
function MakeFontEncoding($map) {
//Build differences from reference encoding
$manager = ManagerEncoding::get();
$ref = $manager->get_encoding_glyphs('windows-1252');
$s='';
$last=0;
for($i=32;$i<=255;$i++) {
if ($map[chr($i)]!=$ref[chr($i)]) {
if ($i!=$last+1) {
$s.=$i.' ';
};
$last=$i;
$s.='/'.$map[chr($i)].' ';
};
}
return rtrim($s);
}
function MakeFontCMap($encoding) {
//Build differences from reference encoding
$manager = ManagerEncoding::get();
$ref = $manager->get_encoding_vector($encoding);
$s = "array(\n";
foreach ($ref as $char => $ucs) {
$s .= sprintf("0x%02X => 0x%04X,\n", ord($char), $ucs);
};
$s .= ")";
return trim($s);
}
function SaveToFile($file,$s,$mode='t')
{
$f=fopen($file,'w'.$mode);
if(!$f)
die('Can\'t write to file '.$file);
fwrite($f,$s,strlen($s));
fclose($f);
}
function ReadShort($f)
{
$a=unpack('n1n',fread($f,2));
return $a['n'];
}
function ReadLong($f)
{
$a=unpack('N1N',fread($f,4));
return $a['N'];
}
function CheckTTF($file)
{
//Check if font license allows embedding
$f=fopen($file,'rb');
if(!$f)
die('Error: Can\'t open '.$file);
//Extract number of tables
fseek($f,4,SEEK_CUR);
$nb=ReadShort($f);
fseek($f,6,SEEK_CUR);
//Seek OS/2 table
$found=false;
for ($i=0;$i<$nb;$i++) {
if (fread($f,4)=='OS/2') {
$found=true;
break;
}
fseek($f,12,SEEK_CUR);
};
if (!$found) {
fclose($f);
return;
};
fseek($f,4,SEEK_CUR);
$offset=ReadLong($f);
fseek($f,$offset,SEEK_SET);
//Extract fsType flags
fseek($f,8,SEEK_CUR);
$fsType=ReadShort($f);
$rl=($fsType & 0x02)!=0;
$pp=($fsType & 0x04)!=0;
$e=($fsType & 0x08)!=0;
fclose($f);
if ($rl and !$pp and !$e) {
echo 'Warning: font license does not allow embedding';
};
}
/*******************************************************************************
* $fontfile : chemin du fichier TTF (ou chaîne vide si pas d'incorporation) *
* $afmfile : chemin du fichier AFM *
* $enc : encodage (ou chaîne vide si la police est symbolique) *
* $patch : patch optionnel pour l'encodage *
* $type : type de la police si $fontfile est vide *
*******************************************************************************/
function MakeFont($fontfile, $afmfile, $destdir, $destfile, $enc) {
// Generate a font definition file
set_magic_quotes_runtime(0);
ini_set('auto_detect_line_endings','1');
$manager = ManagerEncoding::get();
$map = $manager->get_encoding_glyphs($enc);
$fm = ReadAFM($afmfile, $map);
if (is_null($fm)) {
error_log(sprintf("Notice: Missing AFM file '%s'; attempting to parse font file '%s' directly",
$afmfile,
$fontfile));
$fm = ReadTTF($fontfile, $manager->get_encoding_vector($enc));
if (is_null($fm)) {
die(sprintf("Cannot get font metrics for '%s'", $fontfile));
};
}
$diff = MakeFontEncoding($map);
$cmap = MakeFontCMap($enc);
$fd = MakeFontDescriptor($fm,empty($map));
//Find font type
if ($fontfile) {
$ext=strtolower(substr($fontfile,-3));
if ($ext=='ttf') {
$type='TrueType';
} elseif($ext=='pfb') {
$type='Type1';
} else {
die('Error: unrecognized font file extension: '.$ext);
};
} else {
if ($type!='TrueType' and $type!='Type1') {
die('Error: incorrect font type: '.$type);
};
}
//Start generation
$s='Error: font file not found: '.$fontfile);
};
if ($type=='TrueType') {
CheckTTF($fontfile);
};
$f=fopen($fontfile,'rb');
if (!$f) {
die('Error: Can\'t open '.$fontfile);
};
$file=fread($f,filesize($fontfile));
fclose($f);
if ($type=='Type1') {
//Find first two sections and discard third one
$header=(ord($file{0})==128);
if ($header) {
//Strip first binary header
$file=substr($file,6);
}
$pos=strpos($file,'eexec');
if(!$pos) {
die('Error: font file does not seem to be valid Type1');
};
$size1=$pos+6;
if($header and ord($file{$size1})==128) {
//Strip second binary header
$file=substr($file,0,$size1).substr($file,$size1+6);
}
$pos=strpos($file,'00000000');
if (!$pos) {
die('Error: font file does not seem to be valid Type1');
};
$size2=$pos-$size1;
$file=substr($file,0,$size1+$size2);
}
$gzcompress_exists = function_exists('gzcompress');
if ($gzcompress_exists) {
$cmp = $basename.'.z';
SaveToFile($destdir.$cmp, gzcompress($file), 'b');
$s.='$file=\''.$cmp."';\n";
} else {
$cmp = $basename.'.ttf';
SaveToFile($destdir.$cmp, $file, 'b');
$s.='$file=\''.basename($fontfile)."';\n";
error_log('Notice: font file could not be compressed (zlib extension not available)');
}
if ($type=='Type1') {
$s.='$size1='.$size1.";\n";
$s.='$size2='.$size2.";\n";
} else {
$s.='$originalsize='.filesize($fontfile).";\n";
}
} else {
//Not embedded font
$s.='$file='."'';\n";
}
$s.="?>\n";
SaveToFile($destdir.$destfile,$s);
}
?>