1 
2 /******************************************************************************
3 * MODULE     : load_tfm.cpp
4 * DESCRIPTION: load TeX font metric file
5 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
11 
12 #include "load_tex.hpp"
13 #include "analyze.hpp"
14 #include "timer.hpp"
15 
16 RESOURCE_CODE(tex_font_metric);
17 
18 /******************************************************************************
19 * Constructors and destructors for tex_font_metric
20 ******************************************************************************/
21 
22 // FIXME: work around compiler bug
23 typedef rep<tex_font_metric> rep_tex_font_metric;
24 
tex_font_metric_rep(string name)25 tex_font_metric_rep::tex_font_metric_rep (string name):
26   rep_tex_font_metric (name)
27 {
28   header   = NULL;
29   char_info= NULL;
30   width    = NULL;
31   height   = NULL;
32   depth    = NULL;
33   italic   = NULL;
34   lig_kern = NULL;
35   kern     = NULL;
36   exten    = NULL;
37   param    = NULL;
38 }
39 
~tex_font_metric_rep()40 tex_font_metric_rep::~tex_font_metric_rep () {
41   if (header != NULL) tm_delete_array (header);
42   if (char_info != NULL) tm_delete_array (char_info);
43   if (width != NULL) tm_delete_array (width);
44   if (height != NULL) tm_delete_array (height);
45   if (depth != NULL) tm_delete_array (depth);
46   if (italic != NULL) tm_delete_array (italic);
47   if (lig_kern != NULL) tm_delete_array (lig_kern);
48   if (kern != NULL) tm_delete_array (kern);
49   if (exten != NULL) tm_delete_array (exten);
50   if (param != NULL) tm_delete_array (param);
51 }
52 
53 /******************************************************************************
54 * Interpretation of tex_font_metric instances
55 ******************************************************************************/
56 
57 #define byte0(i) (((i)>>24)&255)
58 #define byte1(i) (((i)>>16)&255)
59 #define byte2(i) (((i)>>8)&255)
60 #define byte3(i) ((i)&255)
61 #define word0(i) (((i)>>16)&65535)
62 #define word1(i) ((i)&65535)
63 
64 #define byte1a(i) (((i)>>20)&15)
65 #define byte1b(i) (((i)>>16)&15)
66 #define byte2x(i) (((i)>>10)&63)
67 #define word1x(i) ((i)&32767)
68 
w(QN c)69 int tex_font_metric_rep::w (QN c) {
70   if ((c<bc) || (c>ec)) return 0;
71   return width [byte0 (char_info[c-bc])]; }
h(QN c)72 int tex_font_metric_rep::h (QN c) {
73   if ((c<bc) || (c>ec)) return 0;
74   return height [byte1a (char_info[c-bc])]; }
d(QN c)75 int tex_font_metric_rep::d (QN c) {
76   if ((c<bc) || (c>ec)) return 0;
77   return depth [byte1b (char_info[c-bc])]; }
i(QN c)78 int tex_font_metric_rep::i (QN c) {
79   if ((c<bc) || (c>ec)) return 0;
80   return italic [byte2x (char_info[c-bc])]; }
tag(QN c)81 int tex_font_metric_rep::tag (QN c) { return (char_info [c-bc]>>8)&3; }
rem(QN c)82 int tex_font_metric_rep::rem (QN c) { return char_info  [c-bc] & 255; }
top(QN c)83 QN  tex_font_metric_rep::top (QN c) { return (QN) byte0 (exten [rem (c)]); }
mid(QN c)84 QN  tex_font_metric_rep::mid (QN c) { return (QN) byte1 (exten [rem (c)]); }
bot(QN c)85 QN  tex_font_metric_rep::bot (QN c) { return (QN) byte2 (exten [rem (c)]); }
rep(QN c)86 QN  tex_font_metric_rep::rep (QN c) { return (QN) byte3 (exten [rem (c)]); }
design_size()87 int tex_font_metric_rep::design_size () { return header[1]; }
parameter(int i)88 int tex_font_metric_rep::parameter (int i) { return (i<np)? param [i]: 0; }
spc()89 int tex_font_metric_rep::spc () { return parameter (1); }
spc_stretch()90 int tex_font_metric_rep::spc_stretch () { return parameter (2); }
spc_shrink()91 int tex_font_metric_rep::spc_shrink () { return parameter (3); }
x_height()92 int tex_font_metric_rep::x_height () { return parameter (4); }
spc_quad()93 int tex_font_metric_rep::spc_quad () { return parameter (5); }
spc_extra()94 int tex_font_metric_rep::spc_extra () { return parameter (6); }
95 
96 int
list_len(QN c)97 tex_font_metric_rep::list_len (QN c) {
98   if (tag(c)!=2) return 1;
99   return list_len (rem (c)) + 1;
100 }
101 
102 QN
nth_in_list(QN c,int n)103 tex_font_metric_rep::nth_in_list (QN c, int n) {
104   if ((n==1) || (tag(c)!=2)) return c;
105   return nth_in_list (rem (c), n-1);
106 }
107 
108 double
slope()109 tex_font_metric_rep::slope () {
110   double slope= ((double) parameter(0)) / ((double) (1<<20));
111   if (slope >= 1.0) slope= 0.25;
112   if (slope <= -1.0) slope= -0.25;
113   return slope;
114 }
115 
116 /******************************************************************************
117 * Execution of the ligature kerning program
118 *------------------------------------------------------------------------------
119 * (s, n) the input string of length n
120 * buf    the output string of maximal length m
121 * ker    the output kerning array of maximal length m
122 * m      at input : maximal length of buf and ker;
123 *        at output: the length of buf and ker.
124 ******************************************************************************/
125 
126 void
execute(int * s,int n,int * buf,int * ker,int & m)127 tex_font_metric_rep::execute (int* s, int n, int* buf, int* ker, int& m) {
128   STACK_NEW_ARRAY (stack, int, m);
129   int bp, sp=0, i;
130 
131   for (i=0; i<n; i++) stack[sp++]= s[n-1-i];
132   sp--; bp= 0;
133 
134   while (sp>=0) {
135     int cur_char= stack [sp]& 255;
136     // cout << "Processing " << (char) cur_char << "\n";
137 
138     /***************** the ligature-kerning program ******************/
139     if ((cur_char<bc) || (cur_char>ec)) sp--;
140     else if ((tag (cur_char)==1) && (sp>0)) {
141       register int next_char= stack [sp-1]& 255;
142       register int pc= rem (cur_char);
143       if (byte0 (lig_kern [pc]) > 128) pc= word1 (lig_kern [pc]);
144 
145       while (true) {
146         register int instr= lig_kern [pc];
147 
148         //if (byte0 (instr) >= 128) { // halt
149         //  // cout << "  Halt\n";
150         //  ker [bp]  = 0;
151         //  buf [bp++]= stack[sp--];
152         //  break;
153         //}
154 
155         if (byte1 (instr) != next_char) { // continue
156       	  // cout << "  " << (char) byte1 (instr) << " != " << (char) next_char
157 	        //      << " => pc := pc + " << (byte0 (instr)+1) << "\n";
158           int skip_byte = byte0(instr);
159           if (skip_byte >= 128) { // current instruction is the final instruction
160       	    // cout << "  Halt\n";
161             ker [bp]  = 0;
162             buf [bp++]= stack[sp--];
163             break;
164           }
165           else {
166             pc += skip_byte+1;
167             continue;
168           }
169         }
170 
171         // cout << "  " << (char) byte1 (instr) << " == "
172         //      << (char) next_char << " => ";
173 
174         if (byte2 (instr) < 128) { // ligature
175           // cout << "Ligature ";
176           int code= byte2 (instr);
177           int a   = code>>2;
178           int b   = (code>>1)&1;
179           int c   = code&1;
180             // cout << "(" << a << "," << b << "," << c << ")\n";
181           if (b==0) sp--;
182           stack [sp++]= byte3 (instr);
183           if (c!=0) stack[sp++]= cur_char;
184           sp--;
185           while (a>0) {
186             ker [bp]  = 0;
187             buf [bp++]= stack [sp--];
188             a--;
189           }
190           break;
191         }
192 
193         else { // kerning
194           // cout << "Kerning (" << kern  [word1x (instr)] << ")\n";
195           ker [bp]  = kern  [word1x (instr)];
196           buf [bp++]= stack [sp--];
197           break;
198         }
199       }
200     }
201     else {
202       ker [bp]  = 0;
203       buf [bp++]= stack [sp--];
204     }
205     /***************** end ligature-kerning program ******************/
206 
207     if ((bp>=m-2) || (sp>=m-2)) {
208       failed_error << "\nString is ";
209       for (i=0; i<n; i++) failed_error << (char) s[i];
210       failed_error << "\n";
211       FAILED ("string too complex for ligature kerning");
212     }
213   }
214 
215   m= bp;
216   STACK_DELETE_ARRAY (stack);
217 }
218 
219 /******************************************************************************
220 * Get the individual horzontal offsets of characters
221 ******************************************************************************/
222 
223 #define conv(x) ((SI) (((double) (x))*unit))
224 
225 #define ADVANCE(k)                         \
226   x += conv (w(stack[sp--]) + k);          \
227   x_bis= x;                                \
228   if (pos < n-sp) xpos [pos++] = x;
229 
230 #define SKIP                               \
231   sp--;                                    \
232   if (pos < n-sp) {                        \
233     x_bis += conv (w(stack[sp+1]));        \
234     xpos [pos++] = x_bis;                  \
235   }
236 
237 void
get_xpositions(int * s,int n,double unit,SI * xpos,bool ligf)238 tex_font_metric_rep::get_xpositions (int* s, int n, double unit,
239                                      SI* xpos, bool ligf) {
240   (void) ligf;
241   SI  x    = 0;
242   SI  x_bis= 0;
243   int pos  = 1;
244 
245   int m= n + 16;
246   STACK_NEW_ARRAY (stack, int, m);
247   int bp, sp=0, i;
248 
249   for (i=0; i<n; i++) stack[sp++]= s[n-1-i];
250   sp--; bp= 0;
251 
252   while (sp>=0) {
253     int cur_char= stack [sp]& 255;
254 
255     /***************** the ligature-kerning program ******************/
256     if ((cur_char<bc) || (cur_char>ec)) { SKIP; }
257     else if ((tag (cur_char)==1) && (sp>0)) {
258       register int next_char= stack [sp-1]& 255;
259       register int pc= rem (cur_char);
260       if (byte0 (lig_kern [pc]) > 128) pc= word1 (lig_kern [pc]);
261 
262       while (true) {
263 	register int instr= lig_kern [pc];
264 	if (byte0 (instr) >= 128) { ADVANCE (0); break; }
265 	if (byte1 (instr) != next_char) { pc += byte0 (instr)+1; continue; }
266 	if (byte2 (instr) < 128) {
267           if (!ligf) { ADVANCE (0); break; }
268 	  int code= byte2 (instr);
269 	  int a   = code>>2;
270 	  int b   = (code>>1)&1;
271 	  int c   = code&1;
272 	  if (b==0) SKIP;
273 	  stack [sp++]= byte3 (instr);
274 	  if (c!=0) stack [sp++]= cur_char;
275 	  SKIP;
276 	  while (a>0) { ADVANCE (0); a--; }
277 	  break;
278 	}
279 	else { ADVANCE (kern [word1x (instr)]); break; }
280       }
281     }
282     else ADVANCE (0);
283     /***************** end ligature-kerning program ******************/
284 
285     if ((bp>=m-2) || (sp>=m-2)) {
286       failed_error << "\nString is ";
287       for (i=0; i<n; i++) failed_error << (char) s[i];
288       failed_error << "\n";
289       FAILED ("string too complex for ligature kerning");
290     }
291   }
292   STACK_DELETE_ARRAY (stack);
293 }
294 
295 #undef SKIP
296 #undef ADVANCE
297 #undef unit
298 
299 /******************************************************************************
300 * Output of tex_font_metric instances
301 ******************************************************************************/
302 
303 static const char* HOR_RULE= "---------------------------------------------------------------------------\n";
304 
305 double
fixed(int i)306 fixed (int i) {
307   double x= ((double) i) / ((double) (1<<20));
308   int j= (int) (1000*x);
309   return ((double) j)*0.001;
310 }
311 
312 void
print(tex_font_metric tfm)313 print (tex_font_metric tfm) {
314   int i;
315   cout << HOR_RULE;
316   cout << "name:        " << tfm->res_name << "\n";
317   cout << HOR_RULE;
318   cout << "checksum:    " << tfm->header[0] << "\n";
319   cout << "design size: " << fixed (tfm->header[1]) << "\n";
320 
321   cout << HOR_RULE;
322   for (i=tfm->bc; i<=tfm->ec; i++) {
323     cout << "character ";
324     if ((i&127)<32) cout << i << ":\t";
325     else cout << ((char) i) << ":\t";
326     cout << "w=" << fixed (tfm->w(i)) << ", ";
327     cout << "h=" << fixed (tfm->h(i)) << ", ";
328     cout << "d=" << fixed (tfm->d(i)) << ", ";
329     cout << "i=" << fixed (tfm->i(i));
330     switch (tfm->tag (i)) {
331     case 1: cout << " [lig " << tfm->rem(i) << "]"; break;
332     case 2: cout << " [list " << tfm->rem(i) << "]"; break;
333     case 3: cout << " [ext "
334 		 << (int) tfm->top(i) << ", "
335 		 << (int) tfm->mid(i) << ", "
336 		 << (int) tfm->bot(i) << ", "
337 		 << (int) tfm->rep(i) << "]"; break;
338     }
339     cout << "\n";
340   }
341 
342   cout << HOR_RULE;
343   if (tfm->left!=-1)
344     cout << "Left boundary character:  " << tfm->left << "\n";
345   if (tfm->right!=-1)
346     cout << "Right boundary character: " << tfm->right << "\n";
347   if (tfm->left_prog!=-1)
348     cout << "Left boundary program:    " << tfm->left_prog << "\n";
349   if (tfm->right_prog!=-1)
350     cout << "Right boundary program:   " << tfm->right_prog << "\n";
351   if ((tfm->left==-1) && (tfm->right==-1) &&
352       (tfm->left_prog==-1) && (tfm->right_prog==-1))
353     cout << "No boundary characters or programs\n";
354 
355   cout << HOR_RULE;
356   cout << "Slope:         " << tfm->slope () << "\n";
357   cout << "Space:         " << fixed (tfm->spc ()) << "\n";
358   cout << "Space_stretch: " << fixed (tfm->spc_stretch ()) << "\n";
359   cout << "Space_shrink:  " << fixed (tfm->spc_shrink ()) << "\n";
360   cout << "X height:      " << fixed (tfm->x_height ()) << "\n";
361   cout << "Quad space:    " << fixed (tfm->spc_quad ()) << "\n";
362   cout << "Extra space:   " << fixed (tfm->spc_extra ()) << "\n";
363 
364   cout << HOR_RULE;
365   for (i=7; i<tfm->np; i++)
366     cout << "Parameter " << i << ": " << fixed (tfm->parameter (i)) << "\n";
367 
368   cout << HOR_RULE;
369 }
370 
371 /******************************************************************************
372 * Main program for loading
373 ******************************************************************************/
374 
375 tex_font_metric
load_tfm(url file_name,string family,int size)376 load_tfm (url file_name, string family, int size) {
377   tex_font_metric tfm=
378     tm_new<tex_font_metric_rep> (family * as_string (size) * ".tfm");
379 
380   int i= 0;
381   string s;
382   (void) load_string (file_name, s, true);
383   bench_start ("decode tfm");
384 
385   parse (s, i, tfm->lf);
386   parse (s, i, tfm->lh);
387   parse (s, i, tfm->bc);
388   parse (s, i, tfm->ec);
389   parse (s, i, tfm->nw);
390   parse (s, i, tfm->nh);
391   parse (s, i, tfm->nd);
392   parse (s, i, tfm->ni);
393   parse (s, i, tfm->nl);
394   parse (s, i, tfm->nk);
395   parse (s, i, tfm->ne);
396   parse (s, i, tfm->np);
397 
398   if ((tfm->lf-6) !=
399       (tfm->lh + (tfm->ec + 1 - tfm->bc) +
400        tfm->nw + tfm->nh + tfm->nd + tfm->ni +
401        tfm->nl + tfm->nk + tfm->ne + tfm->np))
402     FAILED ("invalid tfm file");
403 
404   parse (s, i, tfm->header, tfm->lh);
405   parse (s, i, tfm->char_info, tfm->ec+1- tfm->bc);
406   parse (s, i, tfm->width, tfm->nw);
407   parse (s, i, tfm->height, tfm->nh);
408   parse (s, i, tfm->depth, tfm->nd);
409   parse (s, i, tfm->italic, tfm->ni);
410   parse (s, i, tfm->lig_kern, tfm->nl);
411   parse (s, i, tfm->kern, tfm->nk);
412   parse (s, i, tfm->exten, tfm->ne);
413   parse (s, i, tfm->param, tfm->np);
414 
415   tfm->left= tfm->right= tfm->left_prog= tfm->right_prog= -1;
416   if (tfm->nl > 0) {
417     int l= tfm->lig_kern [0];
418     int r= tfm->lig_kern [tfm->nl- 1];
419     if (byte0 (l) == 255) tfm->right= byte1 (l);
420     if (byte0 (r) == 255) tfm->left_prog= word1 (r);
421   }
422 
423   tfm->size= (tfm->header[1] + (1<<19)) >> 20;
424 
425   // Fixes for fonts by Dobkin which should be replaced by TeX Gyre fonts
426   if (starts (family, "avant-garde-ti") || starts (family, "avant-garde-bi"))
427     tfm->param[0]= (int) (0.185339 * ((double) (1<<20)));
428   if (starts (family, "bookman-ti") || starts (family, "bookman-bi"))
429     tfm->param[0]= (int) (0.176327 * ((double) (1<<20)));
430   if (starts (family, "courier-ti") || starts (family, "courier-bi"))
431     tfm->param[0]= (int) (0.212557 * ((double) (1<<20)));
432   if (starts (family, "helvetica-ti") || starts (family, "helvetica-bi"))
433     tfm->param[0]= (int) (0.212557 * ((double) (1<<20)));
434   if (starts (family, "nc-schoolbook-ti") || starts (family, "nc-schoolbook-bi"))
435     tfm->param[0]= (int) (0.286745 * ((double) (1<<20)));
436   if (starts (family, "palatino-ti") || starts (family, "palatino-bi"))
437     tfm->param[0]= (int) (0.176327 * ((double) (1<<20)));
438   if (starts (family, "palatino-sl") || starts (family, "palatino-bl"))
439     tfm->param[0]= (int) (0.167 * ((double) (1<<20)));
440   if (starts (family, "times-ti"))
441     tfm->param[0]= (int) (0.277325 * ((double) (1<<20)));
442   if (starts (family, "times-bi"))
443     tfm->param[0]= (int) (0.267949 * ((double) (1<<20)));
444   if (starts (family, "times-sl") || starts (family, "times-bl"))
445     tfm->param[0]= (int) (0.167 * ((double) (1<<20)));
446   // End fixes
447 
448   bench_cumul ("decode tfm");
449   return tfm;
450 }
451