1<?php
2/*******************************************************************************
3* Utility to generate font definition files                                    *
4* Version: 1.12                                                                *
5* Date:    2003-12-30                                                          *
6*******************************************************************************/
7
8function ReadMap($enc)
9{
10	//Read a map file
11	$file=dirname(__FILE__).'/'.strtolower($enc).'.map';
12	$a=file($file);
13	if(empty($a))
14		die('<B>Error:</B> encoding not found: '.$enc);
15	$cc2gn=array();
16	foreach($a as $l)
17	{
18		if($l{0}=='!')
19		{
20			$e=preg_split('/[ \\t]+/',chop($l));
21			$cc=hexdec(substr($e[0],1));
22			$gn=$e[2];
23			$cc2gn[$cc]=$gn;
24		}
25	}
26	for($i=0;$i<=255;$i++)
27		if(!isset($cc2gn[$i]))
28			$cc2gn[$i]='.notdef';
29	return $cc2gn;
30}
31
32function ReadAFM($file,&$map)
33{
34	//Read a font metric file
35	$a=file($file);
36	if(empty($a))
37		die('File not found');
38	$widths=array();
39	$fm=array();
40	$fix=array('Edot'=>'Edotaccent','edot'=>'edotaccent','Idot'=>'Idotaccent','Zdot'=>'Zdotaccent','zdot'=>'zdotaccent',
41		'Odblacute'=>'Ohungarumlaut','odblacute'=>'ohungarumlaut','Udblacute'=>'Uhungarumlaut','udblacute'=>'uhungarumlaut',
42		'Gcedilla'=>'Gcommaaccent','gcedilla'=>'gcommaaccent','Kcedilla'=>'Kcommaaccent','kcedilla'=>'kcommaaccent',
43		'Lcedilla'=>'Lcommaaccent','lcedilla'=>'lcommaaccent','Ncedilla'=>'Ncommaaccent','ncedilla'=>'ncommaaccent',
44		'Rcedilla'=>'Rcommaaccent','rcedilla'=>'rcommaaccent','Scedilla'=>'Scommaaccent','scedilla'=>'scommaaccent',
45		'Tcedilla'=>'Tcommaaccent','tcedilla'=>'tcommaaccent','Dslash'=>'Dcroat','dslash'=>'dcroat','Dmacron'=>'Dcroat','dmacron'=>'dcroat',
46		'combininggraveaccent'=>'gravecomb','combininghookabove'=>'hookabovecomb','combiningtildeaccent'=>'tildecomb',
47		'combiningacuteaccent'=>'acutecomb','combiningdotbelow'=>'dotbelowcomb','dongsign'=>'dong');
48	foreach($a as $l)
49	{
50		$e=explode(' ',chop($l));
51		if(count($e)<2)
52			continue;
53		$code=$e[0];
54		$param=$e[1];
55		if($code=='C')
56		{
57			//Character metrics
58			$cc=(int)$e[1];
59			$w=$e[4];
60			$gn=$e[7];
61			if(substr($gn,-4)=='20AC')
62				$gn='Euro';
63			if(isset($fix[$gn]))
64			{
65				//Fix incorrect glyph name
66				foreach($map as $c=>$n)
67					if($n==$fix[$gn])
68						$map[$c]=$gn;
69			}
70			if(empty($map))
71			{
72				//Symbolic font: use built-in encoding
73				$widths[$cc]=$w;
74			}
75			else
76			{
77				$widths[$gn]=$w;
78				if($gn=='X')
79					$fm['CapXHeight']=$e[13];
80			}
81			if($gn=='.notdef')
82				$fm['MissingWidth']=$w;
83		}
84		elseif($code=='FontName')
85			$fm['FontName']=$param;
86		elseif($code=='Weight')
87			$fm['Weight']=$param;
88		elseif($code=='ItalicAngle')
89			$fm['ItalicAngle']=(double)$param;
90		elseif($code=='Ascender')
91			$fm['Ascender']=(int)$param;
92		elseif($code=='Descender')
93			$fm['Descender']=(int)$param;
94		elseif($code=='UnderlineThickness')
95			$fm['UnderlineThickness']=(int)$param;
96		elseif($code=='UnderlinePosition')
97			$fm['UnderlinePosition']=(int)$param;
98		elseif($code=='IsFixedPitch')
99			$fm['IsFixedPitch']=($param=='true');
100		elseif($code=='FontBBox')
101			$fm['FontBBox']=array($e[1],$e[2],$e[3],$e[4]);
102		elseif($code=='CapHeight')
103			$fm['CapHeight']=(int)$param;
104		elseif($code=='StdVW')
105			$fm['StdVW']=(int)$param;
106	}
107	if(!isset($fm['FontName']))
108		die('FontName not found');
109	if(!empty($map))
110	{
111		if(!isset($widths['.notdef']))
112			$widths['.notdef']=600;
113		if(!isset($widths['Delta']) and isset($widths['increment']))
114			$widths['Delta']=$widths['increment'];
115		//Order widths according to map
116		for($i=0;$i<=255;$i++)
117		{
118			if(!isset($widths[$map[$i]]))
119			{
120				echo '<B>Warning:</B> character '.$map[$i].' is missing<BR>';
121				$widths[$i]=$widths['.notdef'];
122			}
123			else
124				$widths[$i]=$widths[$map[$i]];
125		}
126	}
127	$fm['Widths']=$widths;
128	return $fm;
129}
130
131function MakeFontDescriptor($fm,$symbolic)
132{
133	//Ascent
134	$asc=(isset($fm['Ascender']) ? $fm['Ascender'] : 1000);
135	$fd="array('Ascent'=>".$asc;
136	//Descent
137	$desc=(isset($fm['Descender']) ? $fm['Descender'] : -200);
138	$fd.=",'Descent'=>".$desc;
139	//CapHeight
140	if(isset($fm['CapHeight']))
141		$ch=$fm['CapHeight'];
142	elseif(isset($fm['CapXHeight']))
143		$ch=$fm['CapXHeight'];
144	else
145		$ch=$asc;
146	$fd.=",'CapHeight'=>".$ch;
147	//Flags
148	$flags=0;
149	if(isset($fm['IsFixedPitch']) and $fm['IsFixedPitch'])
150		$flags+=1<<0;
151	if($symbolic)
152		$flags+=1<<2;
153	if(!$symbolic)
154		$flags+=1<<5;
155	if(isset($fm['ItalicAngle']) and $fm['ItalicAngle']!=0)
156		$flags+=1<<6;
157	$fd.=",'Flags'=>".$flags;
158	//FontBBox
159	if(isset($fm['FontBBox']))
160		$fbb=$fm['FontBBox'];
161	else
162		$fbb=array(0,$des-100,1000,$asc+100);
163	$fd.=",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'";
164	//ItalicAngle
165	$ia=(isset($fm['ItalicAngle']) ? $fm['ItalicAngle'] : 0);
166	$fd.=",'ItalicAngle'=>".$ia;
167	//StemV
168	if(isset($fm['StdVW']))
169		$stemv=$fm['StdVW'];
170	elseif(isset($fm['Weight']) and eregi('(bold|black)',$fm['Weight']))
171		$stemv=120;
172	else
173		$stemv=70;
174	$fd.=",'StemV'=>".$stemv;
175	//MissingWidth
176	if(isset($fm['MissingWidth']))
177		$fd.=",'MissingWidth'=>".$fm['MissingWidth'];
178	$fd.=')';
179	return $fd;
180}
181
182function MakeWidthArray($fm)
183{
184	//Make character width array
185	$s="array(\n\t";
186	$cw=$fm['Widths'];
187	for($i=0;$i<=255;$i++)
188	{
189		if(chr($i)=="'")
190			$s.="'\\''";
191		elseif(chr($i)=="\\")
192			$s.="'\\\\'";
193		elseif($i>=32 and $i<=126)
194			$s.="'".chr($i)."'";
195		else
196			$s.="chr($i)";
197		$s.='=>'.$fm['Widths'][$i];
198		if($i<255)
199			$s.=',';
200		if(($i+1)%22==0)
201			$s.="\n\t";
202	}
203	$s.=')';
204	return $s;
205}
206
207function MakeFontEncoding($map)
208{
209	//Build differences from reference encoding
210	$ref=ReadMap('cp1252');
211	$s='';
212	$last=0;
213	for($i=32;$i<=255;$i++)
214	{
215		if($map[$i]!=$ref[$i])
216		{
217			if($i!=$last+1)
218				$s.=$i.' ';
219			$last=$i;
220			$s.='/'.$map[$i].' ';
221		}
222	}
223	return chop($s);
224}
225
226function SaveToFile($file,$s,$mode='t')
227{
228	$f=fopen($file,'w'.$mode);
229	if(!$f)
230		die('Can\'t write to file '.$file);
231	fwrite($f,$s,strlen($s));
232	fclose($f);
233}
234
235function ReadShort($f)
236{
237	$a=unpack('n1n',fread($f,2));
238	return $a['n'];
239}
240
241function ReadLong($f)
242{
243	$a=unpack('N1N',fread($f,4));
244	return $a['N'];
245}
246
247function CheckTTF($file)
248{
249	//Check if font license allows embedding
250	$f=fopen($file,'rb');
251	if(!$f)
252		die('<B>Error:</B> Can\'t open '.$file);
253	//Extract number of tables
254	fseek($f,4,SEEK_CUR);
255	$nb=ReadShort($f);
256	fseek($f,6,SEEK_CUR);
257	//Seek OS/2 table
258	$found=false;
259	for($i=0;$i<$nb;$i++)
260	{
261		if(fread($f,4)=='OS/2')
262		{
263			$found=true;
264			break;
265		}
266		fseek($f,12,SEEK_CUR);
267	}
268	if(!$found)
269	{
270		fclose($f);
271		return;
272	}
273	fseek($f,4,SEEK_CUR);
274	$offset=ReadLong($f);
275	fseek($f,$offset,SEEK_SET);
276	//Extract fsType flags
277	fseek($f,8,SEEK_CUR);
278	$fsType=ReadShort($f);
279	$rl=($fsType & 0x02)!=0;
280	$pp=($fsType & 0x04)!=0;
281	$e=($fsType & 0x08)!=0;
282	fclose($f);
283	if($rl and !$pp and !$e)
284		echo '<B>Warning:</B> font license does not allow embedding';
285}
286
287/*******************************************************************************
288* $fontfile: path to TTF file (or empty string if not to be embedded)          *
289* $afmfile:  path to AFM file                                                  *
290* $enc:      font encoding (or empty string for symbolic fonts)                *
291* $patch:    optional patch for encoding                                       *
292* $type :    font type if $fontfile is empty                                   *
293*******************************************************************************/
294function MakeFont($fontfile,$afmfile,$enc='cp1252',$patch=array(),$type='TrueType')
295{
296	//Generate a font definition file
297	set_magic_quotes_runtime(0);
298	if($enc)
299	{
300		$map=ReadMap($enc);
301		foreach($patch as $cc=>$gn)
302			$map[$cc]=$gn;
303	}
304	else
305		$map=array();
306	if(!file_exists($afmfile))
307		die('<B>Error:</B> AFM file not found: '.$afmfile);
308	$fm=ReadAFM($afmfile,$map);
309	if($enc)
310		$diff=MakeFontEncoding($map);
311	else
312		$diff='';
313	$fd=MakeFontDescriptor($fm,empty($map));
314	//Find font type
315	if($fontfile)
316	{
317		$ext=strtolower(substr($fontfile,-3));
318		if($ext=='ttf')
319			$type='TrueType';
320		elseif($ext=='pfb')
321			$type='Type1';
322		else
323			die('<B>Error:</B> unrecognized font file extension: '.$ext);
324	}
325	else
326	{
327		if($type!='TrueType' and $type!='Type1')
328			die('<B>Error:</B> incorrect font type: '.$type);
329	}
330	//Start generation
331	$s='<?php'."\n";
332	$s.='$type=\''.$type."';\n";
333	$s.='$name=\''.$fm['FontName']."';\n";
334	$s.='$desc='.$fd.";\n";
335	if(!isset($fm['UnderlinePosition']))
336		$fm['UnderlinePosition']=-100;
337	if(!isset($fm['UnderlineThickness']))
338		$fm['UnderlineThickness']=50;
339	$s.='$up='.$fm['UnderlinePosition'].";\n";
340	$s.='$ut='.$fm['UnderlineThickness'].";\n";
341	$w=MakeWidthArray($fm);
342	$s.='$cw='.$w.";\n";
343	$s.='$enc=\''.$enc."';\n";
344	$s.='$diff=\''.$diff."';\n";
345	$basename=substr(basename($afmfile),0,-4);
346	if($fontfile)
347	{
348		//Embedded font
349		if(!file_exists($fontfile))
350			die('<B>Error:</B> font file not found: '.$fontfile);
351		if($type=='TrueType')
352			CheckTTF($fontfile);
353		$f=fopen($fontfile,'rb');
354		if(!$f)
355			die('<B>Error:</B> Can\'t open '.$fontfile);
356		$file=fread($f,filesize($fontfile));
357		fclose($f);
358		if($type=='Type1')
359		{
360			//Find first two sections and discard third one
361			$pos=strpos($file,'eexec');
362			if(!$pos)
363				die('<B>Error:</B> font file does not seem to be valid Type1');
364			$size1=$pos+6;
365			$pos=strpos($file,'00000000');
366			if(!$pos)
367				die('<B>Error:</B> font file does not seem to be valid Type1');
368			$size2=$pos-$size1;
369			$file=substr($file,0,$size1+$size2);
370		}
371		if(function_exists('gzcompress'))
372		{
373			$cmp=$basename.'.z';
374			SaveToFile($cmp,gzcompress($file),'b');
375			$s.='$file=\''.$cmp."';\n";
376			echo 'Font file compressed ('.$cmp.')<BR>';
377		}
378		else
379		{
380			$s.='$file=\''.basename($fontfile)."';\n";
381			echo '<B>Notice:</B> font file could not be compressed (gzcompress not available)<BR>';
382		}
383		if($type=='Type1')
384		{
385			$s.='$size1='.$size1.";\n";
386			$s.='$size2='.$size2.";\n";
387		}
388		else
389			$s.='$originalsize='.filesize($fontfile).";\n";
390	}
391	else
392	{
393		//Not embedded font
394		$s.='$file='."'';\n";
395	}
396	$s.="?>\n";
397	SaveToFile($basename.'.php',$s);
398	echo 'Font definition file generated ('.$basename.'.php'.')<BR>';
399}
400?>
401