1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
3114402Sru   Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru#include "lib.h"
23114402Sru
24114402Sru#include <ctype.h>
25114402Sru#include <assert.h>
26114402Sru#include <math.h>
27114402Sru#include <stdlib.h>
28114402Sru#include "errarg.h"
29114402Sru#include "error.h"
30114402Sru#include "cset.h"
31114402Sru#include "font.h"
32114402Sru#include "paper.h"
33114402Sru
34114402Sruconst char *const WS = " \t\n\r";
35114402Sru
36114402Srustruct font_char_metric {
37114402Sru  char type;
38114402Sru  int code;
39114402Sru  int width;
40114402Sru  int height;
41114402Sru  int depth;
42114402Sru  int pre_math_space;
43114402Sru  int italic_correction;
44114402Sru  int subscript_correction;
45114402Sru  char *special_device_coding;
46114402Sru};
47114402Sru
48114402Srustruct font_kern_list {
49114402Sru  int i1;
50114402Sru  int i2;
51114402Sru  int amount;
52114402Sru  font_kern_list *next;
53114402Sru
54114402Sru  font_kern_list(int, int, int, font_kern_list * = 0);
55114402Sru};
56114402Sru
57114402Srustruct font_widths_cache {
58114402Sru  font_widths_cache *next;
59114402Sru  int point_size;
60114402Sru  int *width;
61114402Sru
62114402Sru  font_widths_cache(int, int, font_widths_cache * = 0);
63114402Sru  ~font_widths_cache();
64114402Sru};
65114402Sru
66114402Sru/* text_file */
67114402Sru
68114402Srustruct text_file {
69114402Sru  FILE *fp;
70114402Sru  char *path;
71114402Sru  int lineno;
72114402Sru  int size;
73114402Sru  int skip_comments;
74151497Sru  int silent;
75114402Sru  char *buf;
76114402Sru  text_file(FILE *fp, char *p);
77114402Sru  ~text_file();
78114402Sru  int next();
79114402Sru  void error(const char *format,
80114402Sru	     const errarg &arg1 = empty_errarg,
81114402Sru	     const errarg &arg2 = empty_errarg,
82114402Sru	     const errarg &arg3 = empty_errarg);
83114402Sru};
84114402Sru
85114402Srutext_file::text_file(FILE *p, char *s)
86151497Sru: fp(p), path(s), lineno(0), size(0), skip_comments(1), silent(0), buf(0)
87114402Sru{
88114402Sru}
89114402Sru
90114402Srutext_file::~text_file()
91114402Sru{
92114402Sru  a_delete buf;
93114402Sru  a_delete path;
94114402Sru  if (fp)
95114402Sru    fclose(fp);
96114402Sru}
97114402Sru
98114402Sruint text_file::next()
99114402Sru{
100114402Sru  if (fp == 0)
101114402Sru    return 0;
102114402Sru  if (buf == 0) {
103114402Sru    buf = new char[128];
104114402Sru    size = 128;
105114402Sru  }
106114402Sru  for (;;) {
107114402Sru    int i = 0;
108114402Sru    for (;;) {
109114402Sru      int c = getc(fp);
110114402Sru      if (c == EOF)
111114402Sru	break;
112114402Sru      if (invalid_input_char(c))
113114402Sru	error("invalid input character code `%1'", int(c));
114114402Sru      else {
115114402Sru	if (i + 1 >= size) {
116114402Sru	  char *old_buf = buf;
117114402Sru	  buf = new char[size*2];
118114402Sru	  memcpy(buf, old_buf, size);
119114402Sru	  a_delete old_buf;
120114402Sru	  size *= 2;
121114402Sru	}
122114402Sru	buf[i++] = c;
123114402Sru	if (c == '\n')
124114402Sru	  break;
125114402Sru      }
126114402Sru    }
127114402Sru    if (i == 0)
128114402Sru      break;
129114402Sru    buf[i] = '\0';
130114402Sru    lineno++;
131114402Sru    char *ptr = buf;
132114402Sru    while (csspace(*ptr))
133114402Sru      ptr++;
134114402Sru    if (*ptr != 0 && (!skip_comments || *ptr != '#'))
135114402Sru      return 1;
136114402Sru  }
137114402Sru  return 0;
138114402Sru}
139114402Sru
140114402Sruvoid text_file::error(const char *format,
141114402Sru		      const errarg &arg1,
142114402Sru		      const errarg &arg2,
143114402Sru		      const errarg &arg3)
144114402Sru{
145151497Sru  if (!silent)
146151497Sru    error_with_file_and_line(path, lineno, format, arg1, arg2, arg3);
147114402Sru}
148114402Sru
149114402Sru
150114402Sru/* font functions */
151114402Sru
152114402Srufont::font(const char *s)
153114402Sru: ligatures(0), kern_hash_table(0), space_width(0), ch_index(0), nindices(0),
154114402Sru  ch(0), ch_used(0), ch_size(0), special(0), widths_cache(0)
155114402Sru{
156114402Sru  name = new char[strlen(s) + 1];
157114402Sru  strcpy(name, s);
158114402Sru  internalname = 0;
159114402Sru  slant = 0.0;
160114402Sru  // load();			// for testing
161114402Sru}
162114402Sru
163114402Srufont::~font()
164114402Sru{
165114402Sru  for (int i = 0; i < ch_used; i++)
166114402Sru    if (ch[i].special_device_coding)
167114402Sru      a_delete ch[i].special_device_coding;
168114402Sru  a_delete ch;
169114402Sru  a_delete ch_index;
170114402Sru  if (kern_hash_table) {
171114402Sru    for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++) {
172114402Sru      font_kern_list *kerns = kern_hash_table[i];
173114402Sru      while (kerns) {
174114402Sru	font_kern_list *tem = kerns;
175114402Sru	kerns = kerns->next;
176114402Sru	delete tem;
177114402Sru      }
178114402Sru    }
179114402Sru    a_delete kern_hash_table;
180114402Sru  }
181114402Sru  a_delete name;
182114402Sru  a_delete internalname;
183114402Sru  while (widths_cache) {
184114402Sru    font_widths_cache *tem = widths_cache;
185114402Sru    widths_cache = widths_cache->next;
186114402Sru    delete tem;
187114402Sru  }
188114402Sru}
189114402Sru
190114402Srustatic int scale_round(int n, int x, int y)
191114402Sru{
192114402Sru  assert(x >= 0 && y > 0);
193114402Sru  int y2 = y/2;
194114402Sru  if (x == 0)
195114402Sru    return 0;
196114402Sru  if (n >= 0) {
197114402Sru    if (n <= (INT_MAX - y2)/x)
198114402Sru      return (n*x + y2)/y;
199114402Sru    return int(n*double(x)/double(y) + .5);
200114402Sru  }
201114402Sru  else {
202114402Sru    if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x)
203114402Sru      return (n*x - y2)/y;
204114402Sru    return int(n*double(x)/double(y) - .5);
205114402Sru  }
206114402Sru}
207114402Sru
208114402Sruinline int font::scale(int w, int sz)
209114402Sru{
210114402Sru  return sz == unitwidth ? w : scale_round(w, sz, unitwidth);
211114402Sru}
212114402Sru
213114402Sruint font::unit_scale(double *value, char unit)
214114402Sru{
215114402Sru  // we scale everything to inch
216114402Sru  double divisor = 0;
217114402Sru  switch (unit) {
218114402Sru  case 'i':
219114402Sru    divisor = 1;
220114402Sru    break;
221114402Sru  case 'p':
222114402Sru    divisor = 72;
223114402Sru    break;
224114402Sru  case 'P':
225114402Sru    divisor = 6;
226114402Sru    break;
227114402Sru  case 'c':
228114402Sru    divisor = 2.54;
229114402Sru    break;
230114402Sru  default:
231114402Sru    assert(0);
232114402Sru    break;
233114402Sru  }
234114402Sru  if (divisor) {
235114402Sru    *value /= divisor;
236114402Sru    return 1;
237114402Sru  }
238114402Sru  return 0;
239114402Sru}
240114402Sru
241114402Sruint font::get_skew(int c, int point_size, int sl)
242114402Sru{
243114402Sru  int h = get_height(c, point_size);
244114402Sru  return int(h*tan((slant+sl)*PI/180.0) + .5);
245114402Sru}
246114402Sru
247114402Sruint font::contains(int c)
248114402Sru{
249114402Sru  return c >= 0 && c < nindices && ch_index[c] >= 0;
250114402Sru}
251114402Sru
252114402Sruint font::is_special()
253114402Sru{
254114402Sru  return special;
255114402Sru}
256114402Sru
257114402Srufont_widths_cache::font_widths_cache(int ps, int ch_size,
258114402Sru				     font_widths_cache *p)
259114402Sru: next(p), point_size(ps)
260114402Sru{
261114402Sru  width = new int[ch_size];
262114402Sru  for (int i = 0; i < ch_size; i++)
263114402Sru    width[i] = -1;
264114402Sru}
265114402Sru
266114402Srufont_widths_cache::~font_widths_cache()
267114402Sru{
268114402Sru  a_delete width;
269114402Sru}
270114402Sru
271114402Sruint font::get_width(int c, int point_size)
272114402Sru{
273114402Sru  assert(c >= 0 && c < nindices);
274114402Sru  int i = ch_index[c];
275114402Sru  assert(i >= 0);
276114402Sru
277151497Sru  if (point_size == unitwidth || font::unscaled_charwidths)
278114402Sru    return ch[i].width;
279114402Sru
280114402Sru  if (!widths_cache)
281114402Sru    widths_cache = new font_widths_cache(point_size, ch_size);
282114402Sru  else if (widths_cache->point_size != point_size) {
283114402Sru    font_widths_cache **p;
284114402Sru    for (p = &widths_cache; *p; p = &(*p)->next)
285114402Sru      if ((*p)->point_size == point_size)
286114402Sru	break;
287114402Sru    if (*p) {
288114402Sru      font_widths_cache *tem = *p;
289114402Sru      *p = (*p)->next;
290114402Sru      tem->next = widths_cache;
291114402Sru      widths_cache = tem;
292114402Sru    }
293114402Sru    else
294114402Sru      widths_cache = new font_widths_cache(point_size, ch_size, widths_cache);
295114402Sru  }
296114402Sru  int &w = widths_cache->width[i];
297114402Sru  if (w < 0)
298114402Sru    w = scale(ch[i].width, point_size);
299114402Sru  return w;
300114402Sru}
301114402Sru
302114402Sruint font::get_height(int c, int point_size)
303114402Sru{
304114402Sru  assert(c >= 0 && c < nindices && ch_index[c] >= 0);
305114402Sru  return scale(ch[ch_index[c]].height, point_size);
306114402Sru}
307114402Sru
308114402Sruint font::get_depth(int c, int point_size)
309114402Sru{
310114402Sru  assert(c >= 0 && c < nindices && ch_index[c] >= 0);
311114402Sru  return scale(ch[ch_index[c]].depth, point_size);
312114402Sru}
313114402Sru
314114402Sruint font::get_italic_correction(int c, int point_size)
315114402Sru{
316114402Sru  assert(c >= 0 && c < nindices && ch_index[c] >= 0);
317114402Sru  return scale(ch[ch_index[c]].italic_correction, point_size);
318114402Sru}
319114402Sru
320114402Sruint font::get_left_italic_correction(int c, int point_size)
321114402Sru{
322114402Sru  assert(c >= 0 && c < nindices && ch_index[c] >= 0);
323114402Sru  return scale(ch[ch_index[c]].pre_math_space, point_size);
324114402Sru}
325114402Sru
326114402Sruint font::get_subscript_correction(int c, int point_size)
327114402Sru{
328114402Sru  assert(c >= 0 && c < nindices && ch_index[c] >= 0);
329114402Sru  return scale(ch[ch_index[c]].subscript_correction, point_size);
330114402Sru}
331114402Sru
332114402Sruint font::get_space_width(int point_size)
333114402Sru{
334114402Sru  return scale(space_width, point_size);
335114402Sru}
336114402Sru
337114402Srufont_kern_list::font_kern_list(int c1, int c2, int n, font_kern_list *p)
338114402Sru: i1(c1), i2(c2), amount(n), next(p)
339114402Sru{
340114402Sru}
341114402Sru
342114402Sruinline int font::hash_kern(int i1, int i2)
343114402Sru{
344114402Sru  int n = ((i1 << 10) + i2) % KERN_HASH_TABLE_SIZE;
345114402Sru  return n < 0 ? -n : n;
346114402Sru}
347114402Sru
348114402Sruvoid font::add_kern(int i1, int i2, int amount)
349114402Sru{
350114402Sru  if (!kern_hash_table) {
351114402Sru    kern_hash_table = new font_kern_list *[int(KERN_HASH_TABLE_SIZE)];
352114402Sru    for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++)
353114402Sru      kern_hash_table[i] = 0;
354114402Sru  }
355114402Sru  font_kern_list **p = kern_hash_table + hash_kern(i1, i2);
356114402Sru  *p = new font_kern_list(i1, i2, amount, *p);
357114402Sru}
358114402Sru
359114402Sruint font::get_kern(int i1, int i2, int point_size)
360114402Sru{
361114402Sru  if (kern_hash_table) {
362114402Sru    for (font_kern_list *p = kern_hash_table[hash_kern(i1, i2)]; p; p = p->next)
363114402Sru      if (i1 == p->i1 && i2 == p->i2)
364114402Sru	return scale(p->amount, point_size);
365114402Sru  }
366114402Sru  return 0;
367114402Sru}
368114402Sru
369114402Sruint font::has_ligature(int mask)
370114402Sru{
371114402Sru  return mask & ligatures;
372114402Sru}
373114402Sru
374114402Sruint font::get_character_type(int c)
375114402Sru{
376114402Sru  assert(c >= 0 && c < nindices && ch_index[c] >= 0);
377114402Sru  return ch[ch_index[c]].type;
378114402Sru}
379114402Sru
380114402Sruint font::get_code(int c)
381114402Sru{
382114402Sru  assert(c >= 0 && c < nindices && ch_index[c] >= 0);
383114402Sru  return ch[ch_index[c]].code;
384114402Sru}
385114402Sru
386114402Sruconst char *font::get_name()
387114402Sru{
388114402Sru  return name;
389114402Sru}
390114402Sru
391114402Sruconst char *font::get_internal_name()
392114402Sru{
393114402Sru  return internalname;
394114402Sru}
395114402Sru
396114402Sruconst char *font::get_special_device_encoding(int c)
397114402Sru{
398114402Sru  assert(c >= 0 && c < nindices && ch_index[c] >= 0);
399151497Sru  return ch[ch_index[c]].special_device_coding;
400114402Sru}
401114402Sru
402151497Sruconst char *font::get_image_generator()
403114402Sru{
404151497Sru  return image_generator;
405151497Sru}
406151497Sru
407151497Sruvoid font::alloc_ch_index(int idx)
408151497Sru{
409114402Sru  if (nindices == 0) {
410114402Sru    nindices = 128;
411151497Sru    if (idx >= nindices)
412151497Sru      nindices = idx + 10;
413151497Sru    ch_index = new int[nindices];
414114402Sru    for (int i = 0; i < nindices; i++)
415114402Sru      ch_index[i] = -1;
416114402Sru  }
417114402Sru  else {
418114402Sru    int old_nindices = nindices;
419114402Sru    nindices *= 2;
420151497Sru    if (idx >= nindices)
421151497Sru      nindices = idx + 10;
422151497Sru    int *old_ch_index = ch_index;
423151497Sru    ch_index = new int[nindices];
424151497Sru    memcpy(ch_index, old_ch_index, sizeof(int)*old_nindices);
425114402Sru    for (int i = old_nindices; i < nindices; i++)
426114402Sru      ch_index[i] = -1;
427114402Sru    a_delete old_ch_index;
428114402Sru  }
429114402Sru}
430114402Sru
431114402Sruvoid font::extend_ch()
432114402Sru{
433114402Sru  if (ch == 0)
434114402Sru    ch = new font_char_metric[ch_size = 16];
435114402Sru  else {
436114402Sru    int old_ch_size = ch_size;
437114402Sru    ch_size *= 2;
438114402Sru    font_char_metric *old_ch = ch;
439114402Sru    ch = new font_char_metric[ch_size];
440114402Sru    memcpy(ch, old_ch, old_ch_size*sizeof(font_char_metric));
441114402Sru    a_delete old_ch;
442114402Sru  }
443114402Sru}
444114402Sru
445114402Sruvoid font::compact()
446114402Sru{
447114402Sru  int i;
448114402Sru  for (i = nindices - 1; i >= 0; i--)
449114402Sru    if (ch_index[i] >= 0)
450114402Sru      break;
451114402Sru  i++;
452114402Sru  if (i < nindices) {
453151497Sru    int *old_ch_index = ch_index;
454151497Sru    ch_index = new int[i];
455151497Sru    memcpy(ch_index, old_ch_index, i*sizeof(int));
456114402Sru    a_delete old_ch_index;
457114402Sru    nindices = i;
458114402Sru  }
459114402Sru  if (ch_used < ch_size) {
460114402Sru    font_char_metric *old_ch = ch;
461114402Sru    ch = new font_char_metric[ch_used];
462114402Sru    memcpy(ch, old_ch, ch_used*sizeof(font_char_metric));
463114402Sru    a_delete old_ch;
464114402Sru    ch_size = ch_used;
465114402Sru  }
466114402Sru}
467114402Sru
468151497Sruvoid font::add_entry(int idx, const font_char_metric &metric)
469114402Sru{
470151497Sru  assert(idx >= 0);
471151497Sru  if (idx >= nindices)
472151497Sru    alloc_ch_index(idx);
473151497Sru  assert(idx < nindices);
474114402Sru  if (ch_used + 1 >= ch_size)
475114402Sru    extend_ch();
476114402Sru  assert(ch_used + 1 < ch_size);
477151497Sru  ch_index[idx] = ch_used;
478114402Sru  ch[ch_used++] = metric;
479114402Sru}
480114402Sru
481114402Sruvoid font::copy_entry(int new_index, int old_index)
482114402Sru{
483114402Sru  assert(new_index >= 0 && old_index >= 0 && old_index < nindices);
484114402Sru  if (new_index >= nindices)
485114402Sru    alloc_ch_index(new_index);
486114402Sru  ch_index[new_index] = ch_index[old_index];
487114402Sru}
488114402Sru
489151497Srufont *font::load_font(const char *s, int *not_found, int head_only)
490114402Sru{
491114402Sru  font *f = new font(s);
492151497Sru  if (!f->load(not_found, head_only)) {
493114402Sru    delete f;
494114402Sru    return 0;
495114402Sru  }
496114402Sru  return f;
497114402Sru}
498114402Sru
499114402Srustatic char *trim_arg(char *p)
500114402Sru{
501114402Sru  if (!p)
502114402Sru    return 0;
503114402Sru  while (csspace(*p))
504114402Sru    p++;
505114402Sru  char *q = strchr(p, '\0');
506114402Sru  while (q > p && csspace(q[-1]))
507114402Sru    q--;
508114402Sru  *q = '\0';
509114402Sru  return p;
510114402Sru}
511114402Sru
512114402Sruint font::scan_papersize(const char *p,
513114402Sru			 const char **size, double *length, double *width)
514114402Sru{
515114402Sru  double l, w;
516114402Sru  char lu[2], wu[2];
517114402Sru  const char *pp = p;
518114402Sru  int test_file = 1;
519114402Sru  char line[255];
520114402Sruagain:
521114402Sru  if (csdigit(*pp)) {
522114402Sru    if (sscanf(pp, "%lf%1[ipPc],%lf%1[ipPc]", &l, lu, &w, wu) == 4
523114402Sru	&& l > 0 && w > 0
524114402Sru	&& unit_scale(&l, lu[0]) && unit_scale(&w, wu[0])) {
525114402Sru      if (length)
526114402Sru	*length = l;
527114402Sru      if (width)
528114402Sru	*width = w;
529114402Sru      if (size)
530114402Sru	*size = "custom";
531114402Sru      return 1;
532114402Sru    }
533114402Sru  }
534114402Sru  else {
535114402Sru    int i;
536114402Sru    for (i = 0; i < NUM_PAPERSIZES; i++)
537114402Sru      if (strcasecmp(papersizes[i].name, pp) == 0) {
538114402Sru	if (length)
539114402Sru	  *length = papersizes[i].length;
540114402Sru	if (width)
541114402Sru	  *width = papersizes[i].width;
542114402Sru	if (size)
543114402Sru	  *size = papersizes[i].name;
544114402Sru	return 1;
545114402Sru      }
546114402Sru    if (test_file) {
547114402Sru      FILE *f = fopen(p, "r");
548114402Sru      if (f) {
549114402Sru	fgets(line, 254, f);
550114402Sru	fclose(f);
551114402Sru	test_file = 0;
552114402Sru	char *linep = strchr(line, '\0');
553114402Sru	// skip final newline, if any
554114402Sru	if (*(--linep) == '\n')
555114402Sru	  *linep = '\0';
556114402Sru	pp = line;
557114402Sru	goto again;
558114402Sru      }
559114402Sru    }
560114402Sru  }
561114402Sru  return 0;
562114402Sru}
563114402Sru
564114402Sru// If the font can't be found, then if not_found is non-NULL, it will be set
565114402Sru// to 1 otherwise a message will be printed.
566114402Sru
567151497Sruint font::load(int *not_found, int head_only)
568114402Sru{
569114402Sru  char *path;
570114402Sru  FILE *fp;
571114402Sru  if ((fp = open_file(name, &path)) == NULL) {
572114402Sru    if (not_found)
573114402Sru      *not_found = 1;
574114402Sru    else
575114402Sru      error("can't find font file `%1'", name);
576114402Sru    return 0;
577114402Sru  }
578114402Sru  text_file t(fp, path);
579114402Sru  t.skip_comments = 1;
580151497Sru  t.silent = head_only;
581114402Sru  char *p;
582114402Sru  for (;;) {
583114402Sru    if (!t.next()) {
584114402Sru      t.error("missing charset command");
585114402Sru      return 0;
586114402Sru    }
587114402Sru    p = strtok(t.buf, WS);
588114402Sru    if (strcmp(p, "name") == 0) {
589114402Sru    }
590114402Sru    else if (strcmp(p, "spacewidth") == 0) {
591114402Sru      p = strtok(0, WS);
592114402Sru      int n;
593114402Sru      if (p == 0 || sscanf(p, "%d", &n) != 1 || n <= 0) {
594114402Sru	t.error("bad argument for spacewidth command");
595114402Sru	return 0;
596114402Sru      }
597114402Sru      space_width = n;
598114402Sru    }
599114402Sru    else if (strcmp(p, "slant") == 0) {
600114402Sru      p = strtok(0, WS);
601114402Sru      double n;
602114402Sru      if (p == 0 || sscanf(p, "%lf", &n) != 1 || n >= 90.0 || n <= -90.0) {
603114402Sru	t.error("bad argument for slant command", p);
604114402Sru	return 0;
605114402Sru      }
606114402Sru      slant = n;
607114402Sru    }
608114402Sru    else if (strcmp(p, "ligatures") == 0) {
609114402Sru      for (;;) {
610114402Sru	p = strtok(0, WS);
611114402Sru	if (p == 0 || strcmp(p, "0") == 0)
612114402Sru	  break;
613114402Sru	if (strcmp(p, "ff") == 0)
614114402Sru	  ligatures |= LIG_ff;
615114402Sru	else if (strcmp(p, "fi") == 0)
616114402Sru	  ligatures |= LIG_fi;
617114402Sru	else if (strcmp(p, "fl") == 0)
618114402Sru	  ligatures |= LIG_fl;
619114402Sru	else if (strcmp(p, "ffi") == 0)
620114402Sru	  ligatures |= LIG_ffi;
621114402Sru	else if (strcmp(p, "ffl") == 0)
622114402Sru	  ligatures |= LIG_ffl;
623114402Sru	else {
624114402Sru	  t.error("unrecognised ligature `%1'", p);
625114402Sru	  return 0;
626114402Sru	}
627114402Sru      }
628114402Sru    }
629114402Sru    else if (strcmp(p, "internalname") == 0) {
630114402Sru      p = strtok(0, WS);
631114402Sru      if (!p) {
632114402Sru	t.error("`internalname command requires argument");
633114402Sru	return 0;
634114402Sru      }
635114402Sru      internalname = new char[strlen(p) + 1];
636114402Sru      strcpy(internalname, p);
637114402Sru    }
638114402Sru    else if (strcmp(p, "special") == 0) {
639114402Sru      special = 1;
640114402Sru    }
641114402Sru    else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0) {
642114402Sru      char *command = p;
643114402Sru      p = strtok(0, "\n");
644114402Sru      handle_unknown_font_command(command, trim_arg(p), t.path, t.lineno);
645114402Sru    }
646114402Sru    else
647114402Sru      break;
648114402Sru  }
649151497Sru  if (head_only)
650151497Sru    return 1;
651114402Sru  char *command = p;
652114402Sru  int had_charset = 0;
653114402Sru  t.skip_comments = 0;
654114402Sru  while (command) {
655114402Sru    if (strcmp(command, "kernpairs") == 0) {
656114402Sru      for (;;) {
657114402Sru	if (!t.next()) {
658114402Sru	  command = 0;
659114402Sru	  break;
660114402Sru	}
661114402Sru	char *c1 = strtok(t.buf, WS);
662114402Sru	if (c1 == 0)
663114402Sru	  continue;
664114402Sru	char *c2 = strtok(0, WS);
665114402Sru	if (c2 == 0) {
666114402Sru	  command = c1;
667114402Sru	  break;
668114402Sru	}
669114402Sru	p = strtok(0, WS);
670114402Sru	if (p == 0) {
671114402Sru	  t.error("missing kern amount");
672114402Sru	  return 0;
673114402Sru	}
674114402Sru	int n;
675114402Sru	if (sscanf(p, "%d", &n) != 1) {
676114402Sru	  t.error("bad kern amount `%1'", p);
677114402Sru	  return 0;
678114402Sru	}
679114402Sru	int i1 = name_to_index(c1);
680114402Sru	if (i1 < 0) {
681114402Sru	  t.error("invalid character `%1'", c1);
682114402Sru	  return 0;
683114402Sru	}
684114402Sru	int i2 = name_to_index(c2);
685114402Sru	if (i2 < 0) {
686114402Sru	  t.error("invalid character `%1'", c2);
687114402Sru	  return 0;
688114402Sru	}
689114402Sru	add_kern(i1, i2, n);
690114402Sru      }
691114402Sru    }
692114402Sru    else if (strcmp(command, "charset") == 0) {
693114402Sru      had_charset = 1;
694114402Sru      int last_index = -1;
695114402Sru      for (;;) {
696114402Sru	if (!t.next()) {
697114402Sru	  command = 0;
698114402Sru	  break;
699114402Sru	}
700114402Sru	char *nm = strtok(t.buf, WS);
701114402Sru	if (nm == 0)
702114402Sru	  continue;			// I dont think this should happen
703114402Sru	p = strtok(0, WS);
704114402Sru	if (p == 0) {
705114402Sru	  command = nm;
706114402Sru	  break;
707114402Sru	}
708114402Sru	if (p[0] == '"') {
709114402Sru	  if (last_index == -1) {
710114402Sru	    t.error("first charset entry is duplicate");
711114402Sru	    return 0;
712114402Sru	  }
713114402Sru	  if (strcmp(nm, "---") == 0) {
714114402Sru	    t.error("unnamed character cannot be duplicate");
715114402Sru	    return 0;
716114402Sru	  }
717151497Sru	  int idx = name_to_index(nm);
718151497Sru	  if (idx < 0) {
719114402Sru	    t.error("invalid character `%1'", nm);
720114402Sru	    return 0;
721114402Sru	  }
722151497Sru	  copy_entry(idx, last_index);
723114402Sru	}
724114402Sru	else {
725114402Sru	  font_char_metric metric;
726114402Sru	  metric.height = 0;
727114402Sru	  metric.depth = 0;
728114402Sru	  metric.pre_math_space = 0;
729114402Sru	  metric.italic_correction = 0;
730114402Sru	  metric.subscript_correction = 0;
731114402Sru	  int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d",
732114402Sru			      &metric.width, &metric.height, &metric.depth,
733114402Sru			      &metric.italic_correction,
734114402Sru			      &metric.pre_math_space,
735114402Sru			      &metric.subscript_correction);
736114402Sru	  if (nparms < 1) {
737114402Sru	    t.error("bad width for `%1'", nm);
738114402Sru	    return 0;
739114402Sru	  }
740114402Sru	  p = strtok(0, WS);
741114402Sru	  if (p == 0) {
742114402Sru	    t.error("missing character type for `%1'", nm);
743114402Sru	    return 0;
744114402Sru	  }
745114402Sru	  int type;
746114402Sru	  if (sscanf(p, "%d", &type) != 1) {
747114402Sru	    t.error("bad character type for `%1'", nm);
748114402Sru	    return 0;
749114402Sru	  }
750114402Sru	  if (type < 0 || type > 255) {
751114402Sru	    t.error("character type `%1' out of range", type);
752114402Sru	    return 0;
753114402Sru	  }
754114402Sru	  metric.type = type;
755114402Sru	  p = strtok(0, WS);
756114402Sru	  if (p == 0) {
757114402Sru	    t.error("missing code for `%1'", nm);
758114402Sru	    return 0;
759114402Sru	  }
760114402Sru	  char *ptr;
761114402Sru	  metric.code = (int)strtol(p, &ptr, 0);
762114402Sru	  if (metric.code == 0 && ptr == p) {
763114402Sru	    t.error("bad code `%1' for character `%2'", p, nm);
764114402Sru	    return 0;
765114402Sru	  }
766114402Sru	  p = strtok(0, WS);
767114402Sru	  if ((p == NULL) || (strcmp(p, "--") == 0)) {
768114402Sru	    metric.special_device_coding = NULL;
769114402Sru	  }
770114402Sru	  else {
771151497Sru	    char *nam = new char[strlen(p) + 1];
772151497Sru	    strcpy(nam, p);
773151497Sru	    metric.special_device_coding = nam;
774114402Sru	  }
775114402Sru	  if (strcmp(nm, "---") == 0) {
776114402Sru	    last_index = number_to_index(metric.code);
777114402Sru	    add_entry(last_index, metric);
778114402Sru	  }
779114402Sru	  else {
780114402Sru	    last_index = name_to_index(nm);
781114402Sru	    if (last_index < 0) {
782114402Sru	      t.error("invalid character `%1'", nm);
783114402Sru	      return 0;
784114402Sru	    }
785114402Sru	    add_entry(last_index, metric);
786114402Sru	    copy_entry(number_to_index(metric.code), last_index);
787114402Sru	  }
788114402Sru	}
789114402Sru      }
790114402Sru      if (last_index == -1) {
791114402Sru	t.error("I didn't seem to find any characters");
792114402Sru	return 0;
793114402Sru      }
794114402Sru    }
795114402Sru    else {
796114402Sru      t.error("unrecognised command `%1' after `kernpairs' or `charset' command", command);
797114402Sru      return 0;
798114402Sru    }
799114402Sru  }
800114402Sru  if (!had_charset) {
801114402Sru    t.error("missing charset command");
802114402Sru    return 0;
803114402Sru  }
804114402Sru  if (space_width == 0)
805114402Sru    space_width = scale_round(unitwidth, res, 72*3*sizescale);
806114402Sru  compact();
807114402Sru  return 1;
808114402Sru}
809114402Sru
810114402Srustatic struct {
811114402Sru  const char *command;
812114402Sru  int *ptr;
813114402Sru} table[] = {
814114402Sru  { "res", &font::res },
815114402Sru  { "hor", &font::hor },
816114402Sru  { "vert", &font::vert },
817114402Sru  { "unitwidth", &font::unitwidth },
818114402Sru  { "paperwidth", &font::paperwidth },
819114402Sru  { "paperlength", &font::paperlength },
820114402Sru  { "spare1", &font::biggestfont },
821114402Sru  { "biggestfont", &font::biggestfont },
822114402Sru  { "spare2", &font::spare2 },
823151497Sru  { "sizescale", &font::sizescale },
824114402Sru  };
825114402Sru
826114402Sruint font::load_desc()
827114402Sru{
828114402Sru  int nfonts = 0;
829114402Sru  FILE *fp;
830114402Sru  char *path;
831114402Sru  if ((fp = open_file("DESC", &path)) == 0) {
832114402Sru    error("can't find `DESC' file");
833114402Sru    return 0;
834114402Sru  }
835114402Sru  text_file t(fp, path);
836114402Sru  t.skip_comments = 1;
837114402Sru  res = 0;
838114402Sru  while (t.next()) {
839114402Sru    char *p = strtok(t.buf, WS);
840114402Sru    int found = 0;
841114402Sru    unsigned int idx;
842114402Sru    for (idx = 0; !found && idx < sizeof(table)/sizeof(table[0]); idx++)
843114402Sru      if (strcmp(table[idx].command, p) == 0)
844114402Sru	found = 1;
845114402Sru    if (found) {
846114402Sru      char *q = strtok(0, WS);
847114402Sru      if (!q) {
848114402Sru	t.error("missing value for command `%1'", p);
849114402Sru	return 0;
850114402Sru      }
851114402Sru      //int *ptr = &(this->*(table[idx-1].ptr));
852114402Sru      int *ptr = table[idx-1].ptr;
853114402Sru      if (sscanf(q, "%d", ptr) != 1) {
854114402Sru	t.error("bad number `%1'", q);
855114402Sru	return 0;
856114402Sru      }
857114402Sru    }
858114402Sru    else if (strcmp("family", p) == 0) {
859114402Sru      p = strtok(0, WS);
860114402Sru      if (!p) {
861114402Sru	t.error("family command requires an argument");
862114402Sru	return 0;
863114402Sru      }
864114402Sru      char *tem = new char[strlen(p)+1];
865114402Sru      strcpy(tem, p);
866114402Sru      family = tem;
867114402Sru    }
868114402Sru    else if (strcmp("fonts", p) == 0) {
869114402Sru      p = strtok(0, WS);
870114402Sru      if (!p || sscanf(p, "%d", &nfonts) != 1 || nfonts <= 0) {
871114402Sru	t.error("bad number of fonts `%1'", p);
872114402Sru	return 0;
873114402Sru      }
874114402Sru      font_name_table = (const char **)new char *[nfonts+1];
875114402Sru      for (int i = 0; i < nfonts; i++) {
876114402Sru	p = strtok(0, WS);
877114402Sru	while (p == 0) {
878114402Sru	  if (!t.next()) {
879114402Sru	    t.error("end of file while reading list of fonts");
880114402Sru	    return 0;
881114402Sru	  }
882114402Sru	  p = strtok(t.buf, WS);
883114402Sru	}
884114402Sru	char *temp = new char[strlen(p)+1];
885114402Sru	strcpy(temp, p);
886114402Sru	font_name_table[i] = temp;
887114402Sru      }
888114402Sru      p = strtok(0, WS);
889114402Sru      if (p != 0) {
890114402Sru	t.error("font count does not match number of fonts");
891114402Sru	return 0;
892114402Sru      }
893114402Sru      font_name_table[nfonts] = 0;
894114402Sru    }
895114402Sru    else if (strcmp("papersize", p) == 0) {
896114402Sru      p = strtok(0, WS);
897114402Sru      if (!p) {
898114402Sru	t.error("papersize command requires an argument");
899114402Sru	return 0;
900114402Sru      }
901114402Sru      int found_paper = 0;
902114402Sru      while (p) {
903114402Sru	double unscaled_paperwidth, unscaled_paperlength;
904114402Sru	if (scan_papersize(p, &papersize, &unscaled_paperlength,
905114402Sru			   &unscaled_paperwidth)) {
906114402Sru	  paperwidth = int(unscaled_paperwidth * res + 0.5);
907114402Sru	  paperlength = int(unscaled_paperlength * res + 0.5);
908114402Sru	  found_paper = 1;
909114402Sru	  break;
910114402Sru	}
911114402Sru	p = strtok(0, WS);
912114402Sru      }
913114402Sru      if (!found_paper) {
914114402Sru	t.error("bad paper size");
915114402Sru	return 0;
916114402Sru      }
917114402Sru    }
918151497Sru    else if (strcmp("unscaled_charwidths", p) == 0)
919151497Sru      unscaled_charwidths = 1;
920114402Sru    else if (strcmp("pass_filenames", p) == 0)
921114402Sru      pass_filenames = 1;
922114402Sru    else if (strcmp("sizes", p) == 0) {
923114402Sru      int n = 16;
924114402Sru      sizes = new int[n];
925114402Sru      int i = 0;
926114402Sru      for (;;) {
927114402Sru	p = strtok(0, WS);
928114402Sru	while (p == 0) {
929114402Sru	  if (!t.next()) {
930114402Sru	    t.error("list of sizes must be terminated by `0'");
931114402Sru	    return 0;
932114402Sru	  }
933114402Sru	  p = strtok(t.buf, WS);
934114402Sru	}
935114402Sru	int lower, upper;
936114402Sru	switch (sscanf(p, "%d-%d", &lower, &upper)) {
937114402Sru	case 1:
938114402Sru	  upper = lower;
939114402Sru	  // fall through
940114402Sru	case 2:
941114402Sru	  if (lower <= upper && lower >= 0)
942114402Sru	    break;
943114402Sru	  // fall through
944114402Sru	default:
945114402Sru	  t.error("bad size range `%1'", p);
946114402Sru	  return 0;
947114402Sru	}
948114402Sru	if (i + 2 > n) {
949114402Sru	  int *old_sizes = sizes;
950114402Sru	  sizes = new int[n*2];
951114402Sru	  memcpy(sizes, old_sizes, n*sizeof(int));
952114402Sru	  n *= 2;
953114402Sru	  a_delete old_sizes;
954114402Sru	}
955114402Sru	sizes[i++] = lower;
956114402Sru	if (lower == 0)
957114402Sru	  break;
958114402Sru	sizes[i++] = upper;
959114402Sru      }
960114402Sru      if (i == 1) {
961114402Sru	t.error("must have some sizes");
962114402Sru	return 0;
963114402Sru      }
964114402Sru    }
965114402Sru    else if (strcmp("styles", p) == 0) {
966114402Sru      int style_table_size = 5;
967114402Sru      style_table = (const char **)new char *[style_table_size];
968114402Sru      int j;
969114402Sru      for (j = 0; j < style_table_size; j++)
970114402Sru	style_table[j] = 0;
971114402Sru      int i = 0;
972114402Sru      for (;;) {
973114402Sru	p = strtok(0, WS);
974114402Sru	if (p == 0)
975114402Sru	  break;
976114402Sru	// leave room for terminating 0
977114402Sru	if (i + 1 >= style_table_size) {
978114402Sru	  const char **old_style_table = style_table;
979114402Sru	  style_table_size *= 2;
980114402Sru	  style_table = (const char **)new char*[style_table_size];
981114402Sru	  for (j = 0; j < i; j++)
982114402Sru	    style_table[j] = old_style_table[j];
983114402Sru	  for (; j < style_table_size; j++)
984114402Sru	    style_table[j] = 0;
985114402Sru	  a_delete old_style_table;
986114402Sru	}
987114402Sru	char *tem = new char[strlen(p) + 1];
988114402Sru	strcpy(tem, p);
989114402Sru	style_table[i++] = tem;
990114402Sru      }
991114402Sru    }
992114402Sru    else if (strcmp("tcommand", p) == 0)
993114402Sru      tcommand = 1;
994114402Sru    else if (strcmp("use_charnames_in_special", p) == 0)
995114402Sru      use_charnames_in_special = 1;
996151497Sru    else if (strcmp("image_generator", p) == 0) {
997151497Sru      p = strtok(0, WS);
998151497Sru      if (!p) {
999151497Sru	t.error("image_generator command requires an argument");
1000151497Sru	return 0;
1001151497Sru      }
1002151497Sru      image_generator = strsave(p);
1003151497Sru    }
1004114402Sru    else if (strcmp("charset", p) == 0)
1005114402Sru      break;
1006114402Sru    else if (unknown_desc_command_handler) {
1007114402Sru      char *command = p;
1008114402Sru      p = strtok(0, "\n");
1009114402Sru      (*unknown_desc_command_handler)(command, trim_arg(p), t.path, t.lineno);
1010114402Sru    }
1011114402Sru  }
1012114402Sru  if (res == 0) {
1013114402Sru    t.error("missing `res' command");
1014114402Sru    return 0;
1015114402Sru  }
1016114402Sru  if (unitwidth == 0) {
1017114402Sru    t.error("missing `unitwidth' command");
1018114402Sru    return 0;
1019114402Sru  }
1020114402Sru  if (font_name_table == 0) {
1021114402Sru    t.error("missing `fonts' command");
1022114402Sru    return 0;
1023114402Sru  }
1024114402Sru  if (sizes == 0) {
1025114402Sru    t.error("missing `sizes' command");
1026114402Sru    return 0;
1027114402Sru  }
1028114402Sru  if (sizescale < 1) {
1029114402Sru    t.error("bad `sizescale' value");
1030114402Sru    return 0;
1031114402Sru  }
1032114402Sru  if (hor < 1) {
1033114402Sru    t.error("bad `hor' value");
1034114402Sru    return 0;
1035114402Sru  }
1036114402Sru  if (vert < 1) {
1037114402Sru    t.error("bad `vert' value");
1038114402Sru    return 0;
1039114402Sru  }
1040114402Sru  return 1;
1041114402Sru}
1042114402Sru
1043114402Sruvoid font::handle_unknown_font_command(const char *, const char *,
1044114402Sru				       const char *, int)
1045114402Sru{
1046114402Sru}
1047114402Sru
1048114402SruFONT_COMMAND_HANDLER
1049114402Srufont::set_unknown_desc_command_handler(FONT_COMMAND_HANDLER func)
1050114402Sru{
1051114402Sru  FONT_COMMAND_HANDLER prev = unknown_desc_command_handler;
1052114402Sru  unknown_desc_command_handler = func;
1053114402Sru  return prev;
1054114402Sru}
1055