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