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