Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
 
3
require_once(HTML2PS_DIR.'ot.class.php');
4
 
5
/**
6
 * @return Array font metrics hash or null of TTF file could not be parsed
7
 */
8
function ReadTTF($fontfile, $map) {
9
  if (!is_readable($fontfile)) { return null; };
10
 
11
  /**
12
   * Open font file and read metrics information
13
   */
14
  $font = new OpenTypeFile();
15
  $font->open($fontfile);
16
 
17
  $head =& $font->getTable('head');
18
  $name =& $font->getTable('name');
19
  $cmap =& $font->getTable('cmap');
20
  $hmtx =& $font->getTable('hmtx');
21
  $hhea =& $font->getTable('hhea');
22
  $post =& $font->getTable('post');
23
  $subtable =& $cmap->findSubtable(OT_CMAP_PLATFORM_WINDOWS,
24
                                   OT_CMAP_PLATFORM_WINDOWS_UNICODE);
25
 
26
  /**
27
   * Prepare initial data
28
   */
29
  $widths = array();
30
 
31
  for ($i=0; $i<256; $i++) {
32
    $code = chr($i);
33
    if (!isset($map[$code])) {
34
      $widths[] = 1000;
35
      continue;
36
    };
37
    $ucs2 = $map[$code];
38
 
39
    /**
40
     * If the font is monospaced, only one entry need be in the array,
41
     * but  that entry  is required.  The  last entry  applies to  all
42
     * subsequent glyphs.
43
     */
44
    $glyphIndex = $subtable->lookup($ucs2);
45
 
46
    if (!is_null($glyphIndex)) {
47
      $realIndex = min($glyphIndex, $hhea->_numberOfHMetrics-1);
48
      $widths[]  = floor($hmtx->_hMetrics[$realIndex]['advanceWidth']*1000/$head->_unitsPerEm);
49
    } else {
50
      $widths[] = 1000;
51
    };
52
  };
53
 
54
  $font_info = array();
55
 
56
  /**
57
   * Here we use a hack; as, acording to OT specifications,
58
   *
59
   * When  translated  to  ASCII,  these  [...]  strings  must  be
60
   * identical; no  longer than 63  characters; and restricted  to the
61
   * printable ASCII subset,  codes 33 through 126, except  for the 10
62
   * characters: '[', ']', '(', ')', '{', '}', '<', '>', '/', '%'.
63
   *
64
   * we can assume that UCS-2 encoded string we receive can be easily
65
   * translated to ASCII by removing the high-byte of all two-byte characters
66
   */
67
  $ps_name_ucs2 = $name->lookup(OT_CMAP_PLATFORM_WINDOWS,
68
                                OT_CMAP_PLATFORM_WINDOWS_UNICODE,
69
                                OT_CMAP_LANGUAGE_WINDOWS_ENGLISH_AMERICAN,
70
                                OT_NAME_ID_POSTSCRIPT_NAME);
71
  $ps_name_ascii = "";
72
  for ($i=0; $i<strlen($ps_name_ucs2); $i+=2) {
73
    $ps_name_ascii .= $ps_name_ucs2{$i+1};
74
  };
75
 
76
  $font_info['FontName']           = $ps_name_ascii;
77
 
78
  $font_info['Weight']             = $name->lookup(null, null, null, OT_NAME_ID_SUBFAMILY_NAME);
79
  $font_info['ItalicAngle']        = $post->_italicAngle;
80
  $font_info['IsFixedPitch']       = (bool)$post->_isFixedPitch;
81
  // $font_info['CapHeight']
82
  // $font_info['StdVW']
83
  $font_info['Ascender']           = floor($hhea->_ascender*1000/$head->_unitsPerEm);
84
  $font_info['Descender']          = floor($hhea->_descender*1000/$head->_unitsPerEm);
85
  $font_info['UnderlineThickness'] = floor($post->_underlineThickness*1000/$head->_unitsPerEm);
86
  $font_info['UnderlinePosition']  = floor($post->_underlinePosition*1000/$head->_unitsPerEm);
87
  $font_info['FontBBox']           = array($head->_xMin*1000/$head->_unitsPerEm,
88
                                           $head->_yMin*1000/$head->_unitsPerEm,
89
                                           $head->_xMax*1000/$head->_unitsPerEm,
90
                                           $head->_yMax*1000/$head->_unitsPerEm);
91
  $font_info['Widths']             = $widths;
92
 
93
  $font->_delete();
94
  unset($font);
95
 
96
  return $font_info;
97
}
98
 
99
/**
100
 * @return Array font metrics hash or null of AFM file is missing
101
 */
102
function ReadAFM($file, $map) {
103
  if (!is_readable($file)) { return null; };
104
 
105
  $afm_lines = file($file);
106
  $widths=array();
107
  $fm=array();
108
 
109
  foreach ($afm_lines as $l) {
110
    $e=explode(' ',rtrim($l));
111
 
112
    if (count($e)<2) {
113
      continue;
114
    };
115
 
116
    $code=$e[0];
117
    $param=$e[1];
118
 
119
    if ($code=='C') {
120
      //Character metrics
121
      $cc=(int)$e[1];
122
      $w=$e[4];
123
      $gn=$e[7];
124
      if (substr($gn,-4)=='20AC') {
125
        $gn='Euro';
126
      };
127
 
128
      $widths[$gn]=$w;
129
 
130
      if ($gn=='.notdef') {
131
        $fm['MissingWidth']=$w;
132
      };
133
    }
134
    elseif($code=='FontName')
135
      $fm['FontName']=$param;
136
    elseif($code=='Weight')
137
      $fm['Weight']=$param;
138
    elseif($code=='ItalicAngle')
139
      $fm['ItalicAngle']=(double)$param;
140
    elseif($code=='Ascender')
141
      $fm['Ascender']=(int)$param;
142
    elseif($code=='Descender')
143
      $fm['Descender']=(int)$param;
144
    elseif($code=='UnderlineThickness')
145
      $fm['UnderlineThickness']=(int)$param;
146
    elseif($code=='UnderlinePosition')
147
      $fm['UnderlinePosition']=(int)$param;
148
    elseif($code=='IsFixedPitch')
149
      $fm['IsFixedPitch']=($param=='true');
150
    elseif($code=='FontBBox')
151
      $fm['FontBBox']=array($e[1],$e[2],$e[3],$e[4]);
152
    elseif($code=='CapHeight')
153
      $fm['CapHeight']=(int)$param;
154
    elseif($code=='StdVW')
155
      $fm['StdVW']=(int)$param;
156
  }
157
 
158
  if(!isset($fm['FontName'])) {
159
    die('FontName not found');
160
  };
161
 
162
  if (!isset($widths['.notdef'])) {
163
    $widths['.notdef']=600;
164
  };
165
 
166
  if (!isset($widths['Delta']) and isset($widths['increment'])) {
167
    $widths['Delta']=$widths['increment'];
168
  };
169
 
170
  // Order widths according to map
171
  for ($i=0; $i<=255; $i++) {
172
    if(!isset($widths[$map[chr($i)]])) {
173
      error_log('<B>Warning:</B> character '.$map[chr($i)].' is missing<BR>');
174
      $widths[$i]=$widths['.notdef'];
175
    } else {
176
      $widths[$i]=$widths[$map[chr($i)]];
177
    };
178
  };
179
 
180
  $fm['Widths']=$widths;
181
  return $fm;
182
}
183
 
184
function MakeFontDescriptor($fm,$symbolic) {
185
  //Ascent
186
  $asc=(isset($fm['Ascender']) ? $fm['Ascender'] : 1000);
187
  $fd="array('Ascent'=>".$asc;
188
 
189
  //Descent
190
  $desc=(isset($fm['Descender']) ? $fm['Descender'] : -200);
191
  $fd.=",'Descent'=>".$desc;
192
 
193
  //CapHeight
194
  if (isset($fm['CapHeight'])) {
195
    $ch=$fm['CapHeight'];
196
  }  elseif(isset($fm['CapXHeight'])) {
197
    $ch=$fm['CapXHeight'];
198
  } else {
199
    $ch=$asc;
200
  };
201
  $fd.=",'CapHeight'=>".$ch;
202
 
203
  //Flags
204
  $flags=0;
205
  if (isset($fm['IsFixedPitch']) and $fm['IsFixedPitch']) {
206
    $flags+=1<<0;
207
  };
208
 
209
  if ($symbolic) {
210
    $flags+=1<<2;
211
  };
212
 
213
  if (!$symbolic) {
214
    $flags+=1<<5;
215
  };
216
 
217
  if (isset($fm['ItalicAngle']) and $fm['ItalicAngle']!=0) {
218
    $flags+=1<<6;
219
  };
220
 
221
  $fd.=",'Flags'=>".$flags;
222
 
223
  //FontBBox
224
  if (isset($fm['FontBBox'])) {
225
    $fbb=$fm['FontBBox'];
226
  } else {
227
    $fbb=array(0,$des-100,1000,$asc+100);
228
  };
229
 
230
  $fd.=",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'";
231
 
232
  //ItalicAngle
233
  $ia=(isset($fm['ItalicAngle']) ? $fm['ItalicAngle'] : 0);
234
  $fd.=",'ItalicAngle'=>".$ia;
235
 
236
  //StemV
237
  if (isset($fm['StdVW'])) {
238
    $stemv=$fm['StdVW'];
239
  } elseif(isset($fm['Weight']) and eregi('(bold|black)',$fm['Weight'])) {
240
    $stemv=120;
241
  } else {
242
    $stemv=70;
243
  };
244
  $fd.=",'StemV'=>".$stemv;
245
 
246
  //MissingWidth
247
  if (isset($fm['MissingWidth'])) {
248
    $fd.=",'MissingWidth'=>".$fm['MissingWidth'];
249
  };
250
  $fd.=')';
251
 
252
  return $fd;
253
}
254
 
255
function MakeWidthArray($fm) {
256
  //Make character width array
257
  $s="array(\n\t";
258
  $cw=$fm['Widths'];
259
  for ($i=0; $i<=255; $i++) {
260
    if (chr($i)=="'") {
261
      $s.="'\\''";
262
    } elseif (chr($i)=="\\") {
263
      $s.="'\\\\'";
264
    } elseif($i>=32 and $i<=126) {
265
      $s.="'".chr($i)."'";
266
    } else {
267
      $s.="chr($i)";
268
    };
269
    $s.='=>'.$fm['Widths'][$i];
270
    if ($i<255) {
271
      $s.=',';
272
    };
273
 
274
    if(($i+1)%22==0) {
275
      $s.="\n\t";
276
    };
277
  }
278
  $s.=')';
279
  return $s;
280
}
281
 
282
function MakeFontEncoding($map) {
283
  //Build differences from reference encoding
284
  $manager = ManagerEncoding::get();
285
  $ref = $manager->get_encoding_glyphs('windows-1252');
286
 
287
  $s='';
288
  $last=0;
289
  for($i=32;$i<=255;$i++) {
290
    if ($map[chr($i)]!=$ref[chr($i)]) {
291
      if ($i!=$last+1) {
292
        $s.=$i.' ';
293
      };
294
      $last=$i;
295
      $s.='/'.$map[chr($i)].' ';
296
    };
297
  }
298
 
299
  return rtrim($s);
300
}
301
 
302
function MakeFontCMap($encoding) {
303
  //Build differences from reference encoding
304
  $manager = ManagerEncoding::get();
305
  $ref = $manager->get_encoding_vector($encoding);
306
 
307
  $s  = "array(\n";
308
  foreach ($ref as $char => $ucs) {
309
    $s .= sprintf("0x%02X => 0x%04X,\n", ord($char), $ucs);
310
  };
311
  $s .= ")";
312
 
313
  return trim($s);
314
}
315
 
316
function SaveToFile($file,$s,$mode='t')
317
{
318
  $f=fopen($file,'w'.$mode);
319
  if(!$f)
320
    die('Can\'t write to file '.$file);
321
  fwrite($f,$s,strlen($s));
322
  fclose($f);
323
}
324
 
325
function ReadShort($f)
326
{
327
  $a=unpack('n1n',fread($f,2));
328
  return $a['n'];
329
}
330
 
331
function ReadLong($f)
332
{
333
  $a=unpack('N1N',fread($f,4));
334
  return $a['N'];
335
}
336
 
337
function CheckTTF($file)
338
{
339
  //Check if font license allows embedding
340
  $f=fopen($file,'rb');
341
  if(!$f)
342
    die('<B>Error:</B> Can\'t open '.$file);
343
  //Extract number of tables
344
  fseek($f,4,SEEK_CUR);
345
  $nb=ReadShort($f);
346
  fseek($f,6,SEEK_CUR);
347
  //Seek OS/2 table
348
  $found=false;
349
 
350
  for ($i=0;$i<$nb;$i++) {
351
    if (fread($f,4)=='OS/2') {
352
      $found=true;
353
      break;
354
    }
355
    fseek($f,12,SEEK_CUR);
356
  };
357
 
358
  if (!$found) {
359
    fclose($f);
360
    return;
361
  };
362
 
363
  fseek($f,4,SEEK_CUR);
364
  $offset=ReadLong($f);
365
  fseek($f,$offset,SEEK_SET);
366
 
367
  //Extract fsType flags
368
  fseek($f,8,SEEK_CUR);
369
  $fsType=ReadShort($f);
370
  $rl=($fsType & 0x02)!=0;
371
  $pp=($fsType & 0x04)!=0;
372
  $e=($fsType & 0x08)!=0;
373
  fclose($f);
374
  if ($rl and !$pp and !$e) {
375
    echo '<B>Warning:</B> font license does not allow embedding';
376
  };
377
}
378
 
379
/*******************************************************************************
380
 * $fontfile : chemin du fichier TTF (ou chaîne vide si pas d'incorporation)    *
381
 * $afmfile :  chemin du fichier AFM                                            *
382
 * $enc :      encodage (ou chaîne vide si la police est symbolique)            *
383
 * $patch :    patch optionnel pour l'encodage                                  *
384
 * $type :     type de la police si $fontfile est vide                          *
385
 *******************************************************************************/
386
function MakeFont($fontfile, $afmfile, $destdir, $destfile, $enc) {
387
  // Generate a font definition file
388
  set_magic_quotes_runtime(0);
389
  ini_set('auto_detect_line_endings','1');
390
 
391
  $manager = ManagerEncoding::get();
392
  $map     = $manager->get_encoding_glyphs($enc);
393
 
394
  $fm = ReadAFM($afmfile, $map);
395
 
396
  if (is_null($fm)) {
397
    error_log(sprintf("Notice: Missing AFM file '%s'; attempting to parse font file '%s' directly",
398
                      $afmfile,
399
                      $fontfile));
400
 
401
    $fm = ReadTTF($fontfile, $manager->get_encoding_vector($enc));
402
 
403
    if (is_null($fm)) {
404
      die(sprintf("Cannot get font metrics for '%s'", $fontfile));
405
    };
406
  }
407
 
408
  $diff = MakeFontEncoding($map);
409
  $cmap = MakeFontCMap($enc);
410
  $fd   = MakeFontDescriptor($fm,empty($map));
411
 
412
  //Find font type
413
  if ($fontfile) {
414
    $ext=strtolower(substr($fontfile,-3));
415
    if ($ext=='ttf') {
416
      $type='TrueType';
417
    }  elseif($ext=='pfb') {
418
      $type='Type1';
419
    } else {
420
      die('<B>Error:</B> unrecognized font file extension: '.$ext);
421
    };
422
  } else {
423
    if ($type!='TrueType' and $type!='Type1') {
424
      die('<B>Error:</B> incorrect font type: '.$type);
425
    };
426
  }
427
 
428
  //Start generation
429
  $s='<?php'."\n";
430
  $s.='$type=\''.$type."';\n";
431
  $s.='$name=\''.$fm['FontName']."';\n";
432
  $s.='$desc='.$fd.";\n";
433
  if (!isset($fm['UnderlinePosition'])) {
434
    $fm['UnderlinePosition']=-100;
435
  };
436
  if (!isset($fm['UnderlineThickness'])) {
437
    $fm['UnderlineThickness']=50;
438
  };
439
  $s.='$up='.$fm['UnderlinePosition'].";\n";
440
  $s.='$ut='.$fm['UnderlineThickness'].";\n";
441
  $w=MakeWidthArray($fm);
442
  $s.='$cw='.$w.";\n";
443
  $s.='$enc=\''.$enc."';\n";
444
  $s.='$diff=\''.$diff."';\n";
445
  $s.='$cmap='.$cmap.";\n";
446
 
447
  $basename=substr(basename($afmfile),0,-4);
448
 
449
  if ($fontfile) {
450
    //Embedded font
451
    if (!file_exists($fontfile)) {
452
      die('<B>Error:</B> font file not found: '.$fontfile);
453
    };
454
 
455
    if ($type=='TrueType') {
456
      CheckTTF($fontfile);
457
    };
458
 
459
    $f=fopen($fontfile,'rb');
460
    if (!$f) {
461
      die('<B>Error:</B> Can\'t open '.$fontfile);
462
    };
463
 
464
    $file=fread($f,filesize($fontfile));
465
    fclose($f);
466
    if ($type=='Type1') {
467
      //Find first two sections and discard third one
468
      $header=(ord($file{0})==128);
469
      if ($header) {
470
        //Strip first binary header
471
        $file=substr($file,6);
472
      }
473
      $pos=strpos($file,'eexec');
474
      if(!$pos) {
475
        die('<B>Error:</B> font file does not seem to be valid Type1');
476
      };
477
      $size1=$pos+6;
478
      if($header and ord($file{$size1})==128) {
479
        //Strip second binary header
480
        $file=substr($file,0,$size1).substr($file,$size1+6);
481
      }
482
      $pos=strpos($file,'00000000');
483
      if (!$pos) {
484
        die('<B>Error:</B> font file does not seem to be valid Type1');
485
      };
486
 
487
      $size2=$pos-$size1;
488
      $file=substr($file,0,$size1+$size2);
489
    }
490
 
491
    $gzcompress_exists = function_exists('gzcompress');
492
    if ($gzcompress_exists) {
493
      $cmp = $basename.'.z';
494
      SaveToFile($destdir.$cmp, gzcompress($file), 'b');
495
 
496
      $s.='$file=\''.$cmp."';\n";
497
    } else {
498
      $cmp = $basename.'.ttf';
499
      SaveToFile($destdir.$cmp, $file, 'b');
500
 
501
      $s.='$file=\''.basename($fontfile)."';\n";
502
      error_log('Notice: font file could not be compressed (zlib extension not available)');
503
    }
504
 
505
    if ($type=='Type1') {
506
      $s.='$size1='.$size1.";\n";
507
      $s.='$size2='.$size2.";\n";
508
    } else {
509
      $s.='$originalsize='.filesize($fontfile).";\n";
510
    }
511
  } else {
512
    //Not embedded font
513
    $s.='$file='."'';\n";
514
  }
515
 
516
  $s.="?>\n";
517
  SaveToFile($destdir.$destfile,$s);
518
}
519
?>