Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / groff / src / roff / troff / node.cc
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
3    Free Software Foundation, Inc.
4      Written by James Clark (jjc@jclark.com)
5
6 This file is part of groff.
7
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
12
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING.  If not, write to the Free Software
20 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21
22 #include "troff.h"
23
24 #ifdef HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27
28 #include "symbol.h"
29 #include "dictionary.h"
30 #include "hvunits.h"
31 #include "env.h"
32 #include "request.h"
33 #include "node.h"
34 #include "token.h"
35 #include "charinfo.h"
36 #include "font.h"
37 #include "reg.h"
38 #include "input.h"
39 #include "div.h"
40 #include "geometry.h"
41
42 #include "nonposix.h"
43
44 #ifdef _POSIX_VERSION
45
46 #include <sys/wait.h>
47
48 #else /* not _POSIX_VERSION */
49
50 /* traditional Unix */
51
52 #define WIFEXITED(s) (((s) & 0377) == 0)
53 #define WEXITSTATUS(s) (((s) >> 8) & 0377)
54 #define WTERMSIG(s) ((s) & 0177)
55 #define WIFSTOPPED(s) (((s) & 0377) == 0177)
56 #define WSTOPSIG(s) (((s) >> 8) & 0377)
57 #define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
58
59 #endif /* not _POSIX_VERSION */
60
61 /*
62  *  how many boundaries of images have been written? Useful for
63  *  debugging grohtml
64  */
65
66 int image_no = 0;
67 static int suppress_start_page = 0;
68
69 #define STORE_WIDTH 1
70
71 symbol HYPHEN_SYMBOL("hy");
72
73 // Character used when a hyphen is inserted at a line break.
74 static charinfo *soft_hyphen_char;
75
76 enum constant_space_type {
77   CONSTANT_SPACE_NONE,
78   CONSTANT_SPACE_RELATIVE,
79   CONSTANT_SPACE_ABSOLUTE
80   };
81
82 struct special_font_list {
83   int n;
84   special_font_list *next;
85 };
86
87 special_font_list *global_special_fonts;
88 static int global_ligature_mode = 1;
89 static int global_kern_mode = 1;
90
91 class track_kerning_function {
92   int non_zero;
93   units min_size;
94   hunits min_amount;
95   units max_size;
96   hunits max_amount;
97 public:
98   track_kerning_function();
99   track_kerning_function(units, hunits, units, hunits);
100   int operator==(const track_kerning_function &);
101   int operator!=(const track_kerning_function &);
102   hunits compute(int point_size);
103 };
104
105 // embolden fontno when this is the current font
106
107 struct conditional_bold {
108   conditional_bold *next;
109   int fontno;
110   hunits offset;
111   conditional_bold(int, hunits, conditional_bold * = 0);
112 };
113
114 struct tfont;
115
116 class font_info {
117   tfont *last_tfont;
118   int number;
119   font_size last_size;
120   int last_height;
121   int last_slant;
122   symbol internal_name;
123   symbol external_name;
124   font *fm;
125   char is_bold;
126   hunits bold_offset;
127   track_kerning_function track_kern;
128   constant_space_type is_constant_spaced;
129   units constant_space;
130   int last_ligature_mode;
131   int last_kern_mode;
132   conditional_bold *cond_bold_list;
133   void flush();
134 public:
135   special_font_list *sf;
136   font_info(symbol nm, int n, symbol enm, font *f);
137   int contains(charinfo *);
138   void set_bold(hunits);
139   void unbold();
140   void set_conditional_bold(int, hunits);
141   void conditional_unbold(int);
142   void set_track_kern(track_kerning_function &);
143   void set_constant_space(constant_space_type, units = 0);
144   int is_named(symbol);
145   symbol get_name();
146   tfont *get_tfont(font_size, int, int, int);
147   hunits get_space_width(font_size, int);
148   hunits get_narrow_space_width(font_size);
149   hunits get_half_narrow_space_width(font_size);
150   int get_bold(hunits *);
151   int is_special();
152   int is_style();
153   friend symbol get_font_name(int, environment *);
154 };
155
156 class tfont_spec {
157 protected:
158   symbol name;
159   int input_position;
160   font *fm;
161   font_size size;
162   char is_bold;
163   char is_constant_spaced;
164   int ligature_mode;
165   int kern_mode;
166   hunits bold_offset;
167   hunits track_kern;                    // add this to the width
168   hunits constant_space_width;
169   int height;
170   int slant;
171 public:
172   tfont_spec(symbol nm, int pos, font *, font_size, int, int);
173   tfont_spec(const tfont_spec &spec) { *this = spec; }
174   tfont_spec plain();
175   int operator==(const tfont_spec &);
176   friend tfont *font_info::get_tfont(font_size fs, int, int, int);
177 };
178
179 class tfont : public tfont_spec {
180   static tfont *tfont_list;
181   tfont *next;
182   tfont *plain_version;
183 public:
184   tfont(tfont_spec &);
185   int contains(charinfo *);
186   hunits get_width(charinfo *c);
187   int get_bold(hunits *);
188   int get_constant_space(hunits *);
189   hunits get_track_kern();
190   tfont *get_plain();
191   font_size get_size();
192   symbol get_name();
193   charinfo *get_lig(charinfo *c1, charinfo *c2);
194   int get_kern(charinfo *c1, charinfo *c2, hunits *res);
195   int get_input_position();
196   int get_character_type(charinfo *);
197   int get_height();
198   int get_slant();
199   vunits get_char_height(charinfo *);
200   vunits get_char_depth(charinfo *);
201   hunits get_char_skew(charinfo *);
202   hunits get_italic_correction(charinfo *);
203   hunits get_left_italic_correction(charinfo *);
204   hunits get_subscript_correction(charinfo *);
205   friend tfont *make_tfont(tfont_spec &);
206 };
207
208 inline int env_definite_font(environment *env)
209 {
210   return env->get_family()->make_definite(env->get_font());
211 }
212
213 /* font_info functions */
214
215 static font_info **font_table = 0;
216 static int font_table_size = 0;
217
218 font_info::font_info(symbol nm, int n, symbol enm, font *f)
219 : last_tfont(0), number(n), last_size(0),
220   internal_name(nm), external_name(enm), fm(f),
221   is_bold(0), is_constant_spaced(CONSTANT_SPACE_NONE), last_ligature_mode(1),
222   last_kern_mode(1), cond_bold_list(0), sf(0)
223 {
224 }
225
226 inline int font_info::contains(charinfo *ci)
227 {
228   return fm != 0 && fm->contains(ci->get_index());
229 }
230
231 inline int font_info::is_special()
232 {
233   return fm != 0 && fm->is_special();
234 }
235
236 inline int font_info::is_style()
237 {
238   return fm == 0;
239 }
240
241 tfont *make_tfont(tfont_spec &spec)
242 {
243   for (tfont *p = tfont::tfont_list; p; p = p->next)
244     if (*p == spec)
245       return p;
246   return new tfont(spec);
247 }
248
249 // this is the current_font, fontno is where we found the character,
250 // presumably a special font
251
252 tfont *font_info::get_tfont(font_size fs, int height, int slant, int fontno)
253 {
254   if (last_tfont == 0 || fs != last_size
255       || height != last_height || slant != last_slant
256       || global_ligature_mode != last_ligature_mode
257       || global_kern_mode != last_kern_mode
258       || fontno != number) {
259         font_info *f = font_table[fontno];
260         tfont_spec spec(f->external_name, f->number, f->fm, fs, height, slant);
261         for (conditional_bold *p = cond_bold_list; p; p = p->next)
262           if (p->fontno == fontno) {
263             spec.is_bold = 1;
264             spec.bold_offset = p->offset;
265             break;
266           }
267         if (!spec.is_bold && is_bold) {
268           spec.is_bold = 1;
269           spec.bold_offset = bold_offset;
270         }
271         spec.track_kern = track_kern.compute(fs.to_scaled_points());
272         spec.ligature_mode = global_ligature_mode;
273         spec.kern_mode = global_kern_mode;
274         switch (is_constant_spaced) {
275         case CONSTANT_SPACE_NONE:
276           break;
277         case CONSTANT_SPACE_ABSOLUTE:
278           spec.is_constant_spaced = 1;
279           spec.constant_space_width = constant_space;
280           break;
281         case CONSTANT_SPACE_RELATIVE:
282           spec.is_constant_spaced = 1;
283           spec.constant_space_width
284             = scale(constant_space*fs.to_scaled_points(),
285                     units_per_inch,
286                     36*72*sizescale);
287           break;
288         default:
289           assert(0);
290         }
291         if (fontno != number)
292           return make_tfont(spec);
293         last_tfont = make_tfont(spec);
294         last_size = fs;
295         last_height = height;
296         last_slant = slant;
297         last_ligature_mode = global_ligature_mode;
298         last_kern_mode = global_kern_mode;
299       }
300   return last_tfont;
301 }
302
303 int font_info::get_bold(hunits *res)
304 {
305   if (is_bold) {
306     *res = bold_offset;
307     return 1;
308   }
309   else
310     return 0;
311 }
312
313 void font_info::unbold()
314 {
315   if (is_bold) {
316     is_bold = 0;
317     flush();
318   }
319 }
320
321 void font_info::set_bold(hunits offset)
322 {
323   if (!is_bold || offset != bold_offset) {
324     is_bold = 1;
325     bold_offset = offset;
326     flush();
327   }
328 }
329
330 void font_info::set_conditional_bold(int fontno, hunits offset)
331 {
332   for (conditional_bold *p = cond_bold_list; p; p = p->next)
333     if (p->fontno == fontno) {
334       if (offset != p->offset) {
335         p->offset = offset;
336         flush();
337       }
338       return;
339     }
340   cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
341 }
342
343 conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
344 : next(x), fontno(f), offset(h)
345 {
346 }
347
348 void font_info::conditional_unbold(int fontno)
349 {
350   for (conditional_bold **p = &cond_bold_list; *p; p = &(*p)->next)
351     if ((*p)->fontno == fontno) {
352       conditional_bold *tem = *p;
353       *p = (*p)->next;
354       delete tem;
355       flush();
356       return;
357     }
358 }
359
360 void font_info::set_constant_space(constant_space_type type, units x)
361 {
362   if (type != is_constant_spaced
363       || (type != CONSTANT_SPACE_NONE && x != constant_space)) {
364     flush();
365     is_constant_spaced = type;
366     constant_space = x;
367   }
368 }
369
370 void font_info::set_track_kern(track_kerning_function &tk)
371 {
372   if (track_kern != tk) {
373     track_kern = tk;
374     flush();
375   }
376 }
377
378 void font_info::flush()
379 {
380   last_tfont = 0;
381 }
382
383 int font_info::is_named(symbol s)
384 {
385   return internal_name == s;
386 }
387
388 symbol font_info::get_name()
389 {
390   return internal_name;
391 }
392
393 symbol get_font_name(int fontno, environment *env)
394 {
395   symbol f = font_table[fontno]->get_name();
396   if (font_table[fontno]->is_style()) {
397     return concat(env->get_family()->nm, f);
398   }
399   return f;
400 }
401
402 hunits font_info::get_space_width(font_size fs, int space_size)
403 {
404   if (is_constant_spaced == CONSTANT_SPACE_NONE)
405     return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
406                         space_size, 12);
407   else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
408     return constant_space;
409   else
410     return scale(constant_space*fs.to_scaled_points(),
411                  units_per_inch, 36*72*sizescale);
412 }
413
414 hunits font_info::get_narrow_space_width(font_size fs)
415 {
416   charinfo *ci = get_charinfo(symbol("|"));
417   if (fm->contains(ci->get_index()))
418     return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
419   else
420     return hunits(fs.to_units()/6);
421 }
422
423 hunits font_info::get_half_narrow_space_width(font_size fs)
424 {
425   charinfo *ci = get_charinfo(symbol("^"));
426   if (fm->contains(ci->get_index()))
427     return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
428   else
429     return hunits(fs.to_units()/12);
430 }
431
432 /* tfont */
433
434 tfont_spec::tfont_spec(symbol nm, int n, font *f,
435                        font_size s, int h, int sl)
436 : name(nm), input_position(n), fm(f), size(s),
437   is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1),
438   height(h), slant(sl)
439 {
440   if (height == size.to_scaled_points())
441     height = 0;
442 }
443
444 int tfont_spec::operator==(const tfont_spec &spec)
445 {
446   if (fm == spec.fm
447       && size == spec.size
448       && input_position == spec.input_position
449       && name == spec.name
450       && height == spec.height
451       && slant == spec.slant
452       && (is_bold
453           ? (spec.is_bold && bold_offset == spec.bold_offset)
454           : !spec.is_bold)
455       && track_kern == spec.track_kern
456       && (is_constant_spaced
457           ? (spec.is_constant_spaced
458              && constant_space_width == spec.constant_space_width)
459           : !spec.is_constant_spaced)
460       && ligature_mode == spec.ligature_mode
461       && kern_mode == spec.kern_mode)
462     return 1;
463   else
464     return 0;
465 }
466
467 tfont_spec tfont_spec::plain()
468 {
469   return tfont_spec(name, input_position, fm, size, height, slant);
470 }
471
472 hunits tfont::get_width(charinfo *c)
473 {
474   if (is_constant_spaced)
475     return constant_space_width;
476   else if (is_bold)
477     return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
478             + track_kern + bold_offset);
479   else
480     return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
481             + track_kern);
482 }
483
484 vunits tfont::get_char_height(charinfo *c)
485 {
486   vunits v = fm->get_height(c->get_index(), size.to_scaled_points());
487   if (height != 0 && height != size.to_scaled_points())
488     return scale(v, height, size.to_scaled_points());
489   else
490     return v;
491 }
492
493 vunits tfont::get_char_depth(charinfo *c)
494 {
495   vunits v = fm->get_depth(c->get_index(), size.to_scaled_points());
496   if (height != 0 && height != size.to_scaled_points())
497     return scale(v, height, size.to_scaled_points());
498   else
499     return v;
500 }
501
502 hunits tfont::get_char_skew(charinfo *c)
503 {
504   return hunits(fm->get_skew(c->get_index(), size.to_scaled_points(), slant));
505 }
506
507 hunits tfont::get_italic_correction(charinfo *c)
508 {
509   return hunits(fm->get_italic_correction(c->get_index(), size.to_scaled_points()));
510 }
511
512 hunits tfont::get_left_italic_correction(charinfo *c)
513 {
514   return hunits(fm->get_left_italic_correction(c->get_index(),
515                                                size.to_scaled_points()));
516 }
517
518 hunits tfont::get_subscript_correction(charinfo *c)
519 {
520   return hunits(fm->get_subscript_correction(c->get_index(),
521                                              size.to_scaled_points()));
522 }
523
524 inline int tfont::get_input_position()
525 {
526   return input_position;
527 }
528
529 inline int tfont::contains(charinfo *ci)
530 {
531   return fm->contains(ci->get_index());
532 }
533
534 inline int tfont::get_character_type(charinfo *ci)
535 {
536   return fm->get_character_type(ci->get_index());
537 }
538
539 inline int tfont::get_bold(hunits *res)
540 {
541   if (is_bold) {
542     *res = bold_offset;
543     return 1;
544   }
545   else
546     return 0;
547 }
548
549 inline int tfont::get_constant_space(hunits *res)
550 {
551   if (is_constant_spaced) {
552     *res = constant_space_width;
553     return 1;
554   }
555   else
556     return 0;
557 }
558
559 inline hunits tfont::get_track_kern()
560 {
561   return track_kern;
562 }
563
564 inline tfont *tfont::get_plain()
565 {
566   return plain_version;
567 }
568
569 inline font_size tfont::get_size()
570 {
571   return size;
572 }
573
574 inline symbol tfont::get_name()
575 {
576   return name;
577 }
578
579 inline int tfont::get_height()
580 {
581   return height;
582 }
583
584 inline int tfont::get_slant()
585 {
586   return slant;
587 }
588
589 symbol SYMBOL_ff("ff");
590 symbol SYMBOL_fi("fi");
591 symbol SYMBOL_fl("fl");
592 symbol SYMBOL_Fi("Fi");
593 symbol SYMBOL_Fl("Fl");
594
595 charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
596 {
597   if (ligature_mode == 0)
598     return 0;
599   charinfo *ci = 0;
600   if (c1->get_ascii_code() == 'f') {
601     switch (c2->get_ascii_code()) {
602     case 'f':
603       if (fm->has_ligature(font::LIG_ff))
604         ci = get_charinfo(SYMBOL_ff);
605       break;
606     case 'i':
607       if (fm->has_ligature(font::LIG_fi))
608         ci = get_charinfo(SYMBOL_fi);
609       break;
610     case 'l':
611       if (fm->has_ligature(font::LIG_fl))
612         ci = get_charinfo(SYMBOL_fl);
613       break;
614     }
615   }
616   else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
617     switch (c2->get_ascii_code()) {
618     case 'i':
619       if (fm->has_ligature(font::LIG_ffi))
620         ci = get_charinfo(SYMBOL_Fi);
621       break;
622     case 'l':
623       if (fm->has_ligature(font::LIG_ffl))
624         ci = get_charinfo(SYMBOL_Fl);
625       break;
626     }
627   }
628   if (ci != 0 && fm->contains(ci->get_index()))
629     return ci;
630   return 0;
631 }
632
633 inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res)
634 {
635   if (kern_mode == 0)
636     return 0;
637   else {
638     int n = fm->get_kern(c1->get_index(),
639                          c2->get_index(),
640                          size.to_scaled_points());
641     if (n) {
642       *res = hunits(n);
643       return 1;
644     }
645     else
646       return 0;
647   }
648 }
649
650 tfont *tfont::tfont_list = 0;
651
652 tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
653 {
654   next = tfont_list;
655   tfont_list = this;
656   tfont_spec plain_spec = plain();
657   tfont *p;
658   for (p = tfont_list; p; p = p->next)
659     if (*p == plain_spec) {
660       plain_version = p;
661       break;
662     }
663   if (!p)
664     plain_version = new tfont(plain_spec);
665 }
666
667 /* output_file */
668
669 class real_output_file : public output_file {
670 #ifndef POPEN_MISSING
671   int piped;
672 #endif
673   int printing;        // decision via optional page list
674   int output_on;       // \O[0] or \O[1] escape calls
675   virtual void really_transparent_char(unsigned char) = 0;
676   virtual void really_print_line(hunits x, vunits y, node *n,
677                                  vunits before, vunits after, hunits width) = 0;
678   virtual void really_begin_page(int pageno, vunits page_length) = 0;
679   virtual void really_copy_file(hunits x, vunits y, const char *filename);
680   virtual void really_put_filename(const char *filename);
681   virtual void really_on();
682   virtual void really_off();
683 protected:
684   FILE *fp;
685 public:
686   real_output_file();
687   ~real_output_file();
688   void flush();
689   void transparent_char(unsigned char);
690   void print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
691   void begin_page(int pageno, vunits page_length);
692   void put_filename(const char *filename);
693   void on();
694   void off();
695   int is_on();
696   int is_printing();
697   void copy_file(hunits x, vunits y, const char *filename);
698 };
699
700 class suppress_output_file : public real_output_file {
701 public:
702   suppress_output_file();
703   void really_transparent_char(unsigned char);
704   void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
705   void really_begin_page(int pageno, vunits page_length);
706 };
707
708 class ascii_output_file : public real_output_file {
709 public:
710   ascii_output_file();
711   void really_transparent_char(unsigned char);
712   void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
713   void really_begin_page(int pageno, vunits page_length);
714   void outc(unsigned char c);
715   void outs(const char *s);
716 };
717
718 void ascii_output_file::outc(unsigned char c)
719 {
720   fputc(c, fp);
721 }
722
723 void ascii_output_file::outs(const char *s)
724 {
725   fputc('<', fp);
726   if (s)
727     fputs(s, fp);
728   fputc('>', fp);
729 }
730
731 struct hvpair;
732
733 class troff_output_file : public real_output_file {
734   units hpos;
735   units vpos;
736   units output_vpos;
737   units output_hpos;
738   int force_motion;
739   int current_size;
740   int current_slant;
741   int current_height;
742   tfont *current_tfont;
743   color *current_fill_color;
744   color *current_glyph_color;
745   int current_font_number;
746   symbol *font_position;
747   int nfont_positions;
748   enum { TBUF_SIZE = 256 };
749   char tbuf[TBUF_SIZE];
750   int tbuf_len;
751   int tbuf_kern;
752   int begun_page;
753   void do_motion();
754   void put(char c);
755   void put(unsigned char c);
756   void put(int i);
757   void put(unsigned int i);
758   void put(const char *s);
759   void set_font(tfont *tf);
760   void flush_tbuf();
761 public:
762   troff_output_file();
763   ~troff_output_file();
764   void trailer(vunits page_length);
765   void put_char(charinfo *, tfont *, color *, color *);
766   void put_char_width(charinfo *, tfont *, color *, color *, hunits, hunits);
767   void right(hunits);
768   void down(vunits);
769   void moveto(hunits, vunits);
770   void start_special(tfont *, color *, color *, int = 0);
771   void start_special();
772   void special_char(unsigned char c);
773   void end_special();
774   void word_marker();
775   void really_transparent_char(unsigned char c);
776   void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
777   void really_begin_page(int pageno, vunits page_length);
778   void really_copy_file(hunits x, vunits y, const char *filename);
779   void really_put_filename(const char *filename);
780   void really_on();
781   void really_off();
782   void draw(char, hvpair *, int, font_size, color *, color *);
783   void determine_line_limits (char code, hvpair *point, int npoints);
784   void check_charinfo(tfont *tf, charinfo *ci);
785   void glyph_color(color *c);
786   void fill_color(color *c);
787   int get_hpos() { return hpos; }
788   int get_vpos() { return vpos; }
789 };
790
791 static void put_string(const char *s, FILE *fp)
792 {
793   for (; *s != '\0'; ++s)
794     putc(*s, fp);
795 }
796
797 inline void troff_output_file::put(char c)
798 {
799   putc(c, fp);
800 }
801
802 inline void troff_output_file::put(unsigned char c)
803 {
804   putc(c, fp);
805 }
806
807 inline void troff_output_file::put(const char *s)
808 {
809   put_string(s, fp);
810 }
811
812 inline void troff_output_file::put(int i)
813 {
814   put_string(i_to_a(i), fp);
815 }
816
817 inline void troff_output_file::put(unsigned int i)
818 {
819   put_string(ui_to_a(i), fp);
820 }
821
822 void troff_output_file::start_special(tfont *tf, color *gcol, color *fcol,
823                                       int no_init_string)
824 {
825   flush_tbuf();
826   do_motion();
827   if (tf != current_tfont)
828     set_font(tf);
829   glyph_color(gcol);
830   fill_color(fcol);
831   if (!no_init_string)
832     put("x X ");
833 }
834
835 void troff_output_file::start_special()
836 {
837   flush_tbuf();
838   do_motion();
839   put("x X ");
840 }
841
842 void troff_output_file::special_char(unsigned char c)
843 {
844   put(c);
845   if (c == '\n')
846     put('+');
847 }
848
849 void troff_output_file::end_special()
850 {
851   put('\n');
852 }
853
854 inline void troff_output_file::moveto(hunits h, vunits v)
855 {
856   hpos = h.to_units();
857   vpos = v.to_units();
858 }
859
860 void troff_output_file::really_print_line(hunits x, vunits y, node *n,
861                                           vunits before, vunits after, hunits width)
862 {
863   moveto(x, y);
864   while (n != 0) {
865     n->tprint(this);
866     n = n->next;
867   }
868   flush_tbuf();
869   // This ensures that transparent throughput will have a more predictable
870   // position.
871   do_motion();
872   force_motion = 1;
873   hpos = 0;
874   put('n');
875   put(before.to_units());
876   put(' ');
877   put(after.to_units());
878   put('\n');
879 }
880
881 inline void troff_output_file::word_marker()
882 {
883   flush_tbuf();
884   if (is_on())
885     put('w');
886 }
887
888 inline void troff_output_file::right(hunits n)
889 {
890   hpos += n.to_units();
891 }
892
893 inline void troff_output_file::down(vunits n)
894 {
895   vpos += n.to_units();
896 }
897
898 void troff_output_file::do_motion()
899 {
900   if (force_motion) {
901     put('V');
902     put(vpos);
903     put('\n');
904     put('H');
905     put(hpos);
906     put('\n');
907   }
908   else {
909     if (hpos != output_hpos) {
910       units n = hpos - output_hpos;
911       if (n > 0 && n < hpos) {
912         put('h');
913         put(n);
914       }
915       else {
916         put('H');
917         put(hpos);
918       }
919       put('\n');
920     }
921     if (vpos != output_vpos) {
922       units n = vpos - output_vpos;
923       if (n > 0 && n < vpos) {
924         put('v');
925         put(n);
926       }
927       else {
928         put('V');
929         put(vpos);
930       }
931       put('\n');
932     }
933   }
934   output_vpos = vpos;
935   output_hpos = hpos;
936   force_motion = 0;
937 }
938
939 void troff_output_file::flush_tbuf()
940 {
941   if (!is_on()) {
942     tbuf_len = 0;
943     return;
944   }
945
946   if (tbuf_len == 0)
947     return;
948   if (tbuf_kern == 0)
949     put('t');
950   else {
951     put('u');
952     put(tbuf_kern);
953     put(' ');
954   }
955   check_output_limits(hpos, vpos);
956   check_output_limits(hpos, vpos - current_size);
957
958   for (int i = 0; i < tbuf_len; i++)
959     put(tbuf[i]);
960   put('\n');
961   tbuf_len = 0;
962 }
963
964 void troff_output_file::check_charinfo(tfont *tf, charinfo *ci)
965 {
966   if (!is_on())
967     return;
968
969   int height = tf->get_char_height(ci).to_units();
970   int width = tf->get_width(ci).to_units()
971               + tf->get_italic_correction(ci).to_units();
972   int depth = tf->get_char_depth(ci).to_units();
973   check_output_limits(output_hpos, output_vpos - height);
974   check_output_limits(output_hpos + width, output_vpos + depth);
975 }
976
977 void troff_output_file::put_char_width(charinfo *ci, tfont *tf,
978                                        color *gcol, color *fcol,
979                                        hunits w, hunits k)
980 {
981   int kk = k.to_units();
982   if (!is_on()) {
983     flush_tbuf();
984     hpos += w.to_units() + kk;
985     return;
986   }
987   if (tf != current_tfont) {
988     flush_tbuf();
989     set_font(tf);
990   }
991   char c = ci->get_ascii_code();
992   if (c == '\0') {
993     flush_tbuf();
994     do_motion();
995     glyph_color(gcol);
996     fill_color(fcol);
997     check_charinfo(tf, ci);
998     if (ci->numbered()) {
999       put('N');
1000       put(ci->get_number());
1001     }
1002     else {
1003       put('C');
1004       const char *s = ci->nm.contents();
1005       if (s[1] == 0) {
1006         put('\\');
1007         put(s[0]);
1008       }
1009       else
1010         put(s);
1011     }
1012     put('\n');
1013     hpos += w.to_units() + kk;
1014   }
1015   else if (tcommand_flag) {
1016     if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
1017         && (!gcol || gcol == current_glyph_color)
1018         && (!fcol || fcol == current_fill_color)
1019         && kk == tbuf_kern
1020         && tbuf_len < TBUF_SIZE) {
1021       check_charinfo(tf, ci);
1022       tbuf[tbuf_len++] = c;
1023       output_hpos += w.to_units() + kk;
1024       hpos = output_hpos;
1025       return;
1026     }
1027     flush_tbuf();
1028     do_motion();
1029     glyph_color(gcol);
1030     fill_color(fcol);
1031     check_charinfo(tf, ci);
1032     tbuf[tbuf_len++] = c;
1033     output_hpos += w.to_units() + kk;
1034     tbuf_kern = kk;
1035     hpos = output_hpos;
1036   }
1037   else {
1038     // flush_tbuf();
1039     int n = hpos - output_hpos;
1040     check_charinfo(tf, ci);
1041     // check_output_limits(output_hpos, output_vpos);
1042     if (vpos == output_vpos
1043         && (!gcol || gcol == current_glyph_color)
1044         && (!fcol || fcol == current_fill_color)
1045         && n > 0 && n < 100 && !force_motion) {
1046       put(char(n/10 + '0'));
1047       put(char(n%10 + '0'));
1048       put(c);
1049       output_hpos = hpos;
1050     }
1051     else {
1052       do_motion();
1053       put('c');
1054       put(c);
1055     }
1056     hpos += w.to_units() + kk;
1057   }
1058 }
1059
1060 void troff_output_file::put_char(charinfo *ci, tfont *tf,
1061                                  color *gcol, color *fcol)
1062 {
1063   flush_tbuf();
1064   if (!is_on())
1065     return;
1066   if (tf != current_tfont)
1067     set_font(tf);
1068   char c = ci->get_ascii_code();
1069   if (c == '\0') {
1070     do_motion();
1071     glyph_color(gcol);
1072     fill_color(fcol);
1073     if (ci->numbered()) {
1074       put('N');
1075       put(ci->get_number());
1076     }
1077     else {
1078       put('C');
1079       const char *s = ci->nm.contents();
1080       if (s[1] == 0) {
1081         put('\\');
1082         put(s[0]);
1083       }
1084       else
1085         put(s);
1086     }
1087     put('\n');
1088   }
1089   else {
1090     int n = hpos - output_hpos;
1091     if (vpos == output_vpos
1092         && (!gcol || gcol == current_glyph_color)
1093         && (!fcol || fcol == current_fill_color)
1094         && n > 0 && n < 100) {
1095       put(char(n/10 + '0'));
1096       put(char(n%10 + '0'));
1097       put(c);
1098       output_hpos = hpos;
1099     }
1100     else {
1101       do_motion();
1102       glyph_color(gcol);
1103       fill_color(fcol);
1104       put('c');
1105       put(c);
1106     }
1107   }
1108 }
1109
1110 void troff_output_file::set_font(tfont *tf)
1111 {
1112   if (current_tfont == tf)
1113     return;
1114   int n = tf->get_input_position();
1115   symbol nm = tf->get_name();
1116   if (n >= nfont_positions || font_position[n] != nm) {
1117     put("x font ");
1118     put(n);
1119     put(' ');
1120     put(nm.contents());
1121     put('\n');
1122     if (n >= nfont_positions) {
1123       int old_nfont_positions = nfont_positions;
1124       symbol *old_font_position = font_position;
1125       nfont_positions *= 3;
1126       nfont_positions /= 2;
1127       if (nfont_positions <= n)
1128         nfont_positions = n + 10;
1129       font_position = new symbol[nfont_positions];
1130       memcpy(font_position, old_font_position,
1131              old_nfont_positions*sizeof(symbol));
1132       a_delete old_font_position;
1133     }
1134     font_position[n] = nm;
1135   }
1136   if (current_font_number != n) {
1137     put('f');
1138     put(n);
1139     put('\n');
1140     current_font_number = n;
1141   }
1142   int size = tf->get_size().to_scaled_points();
1143   if (current_size != size) {
1144     put('s');
1145     put(size);
1146     put('\n');
1147     current_size = size;
1148   }
1149   int slant = tf->get_slant();
1150   if (current_slant != slant) {
1151     put("x Slant ");
1152     put(slant);
1153     put('\n');
1154     current_slant = slant;
1155   }
1156   int height = tf->get_height();
1157   if (current_height != height) {
1158     put("x Height ");
1159     put(height == 0 ? current_size : height);
1160     put('\n');
1161     current_height = height;
1162   }
1163   current_tfont = tf;
1164 }
1165
1166 void troff_output_file::fill_color(color *col)
1167 {
1168   if (!col || current_fill_color == col || !color_flag)
1169     return;
1170   flush_tbuf();
1171   put("DF");
1172   unsigned int components[4];
1173   color_scheme cs;
1174   cs = col->get_components(components);
1175   switch (cs) {
1176   case DEFAULT:
1177     put('d');
1178     break;
1179   case RGB:
1180     put("r ");
1181     put(Red);
1182     put(' ');
1183     put(Green);
1184     put(' ');
1185     put(Blue);
1186     break;
1187   case CMY:
1188     put("c ");
1189     put(Cyan);
1190     put(' ');
1191     put(Magenta);
1192     put(' ');
1193     put(Yellow);
1194     break;
1195   case CMYK:
1196     put("k ");
1197     put(Cyan);
1198     put(' ');
1199     put(Magenta);
1200     put(' ');
1201     put(Yellow);
1202     put(' ');
1203     put(Black);
1204     break;
1205   case GRAY:
1206     put("g ");
1207     put(Gray);
1208     break;
1209   }
1210   put('\n');
1211   current_fill_color = col;
1212 }
1213
1214 void troff_output_file::glyph_color(color *col)
1215 {
1216   if (!col || current_glyph_color == col || !color_flag)
1217     return;
1218   flush_tbuf();
1219   put("m");
1220   unsigned int components[4];
1221   color_scheme cs;
1222   cs = col->get_components(components);
1223   switch (cs) {
1224   case DEFAULT:
1225     put('d');
1226     break;
1227   case RGB:
1228     put("r ");
1229     put(Red);
1230     put(' ');
1231     put(Green);
1232     put(' ');
1233     put(Blue);
1234     break;
1235   case CMY:
1236     put("c ");
1237     put(Cyan);
1238     put(' ');
1239     put(Magenta);
1240     put(' ');
1241     put(Yellow);
1242     break;
1243   case CMYK:
1244     put("k ");
1245     put(Cyan);
1246     put(' ');
1247     put(Magenta);
1248     put(' ');
1249     put(Yellow);
1250     put(' ');
1251     put(Black);
1252     break;
1253   case GRAY:
1254     put("g ");
1255     put(Gray);
1256     break;
1257   }
1258   put('\n');
1259   current_glyph_color = col;
1260 }
1261
1262 // determine_line_limits - works out the smallest box which will contain
1263 //                         the entity, code, built from the point array.
1264 void troff_output_file::determine_line_limits(char code, hvpair *point,
1265                                               int npoints)
1266 {
1267   int i, x, y;
1268
1269   if (!is_on())
1270     return;
1271
1272   switch (code) {
1273   case 'c':
1274   case 'C':
1275     // only the h field is used when defining a circle
1276     check_output_limits(output_hpos,
1277                         output_vpos - point[0].h.to_units()/2);
1278     check_output_limits(output_hpos + point[0].h.to_units(),
1279                         output_vpos + point[0].h.to_units()/2);
1280     break;
1281   case 'E':
1282   case 'e':
1283     check_output_limits(output_hpos,
1284                         output_vpos - point[0].v.to_units()/2);
1285     check_output_limits(output_hpos + point[0].h.to_units(),
1286                         output_vpos + point[0].v.to_units()/2);
1287     break;
1288   case 'P':
1289   case 'p':
1290     x = output_hpos;
1291     y = output_vpos;
1292     check_output_limits(x, y);
1293     for (i = 0; i < npoints; i++) {
1294       x += point[i].h.to_units();
1295       y += point[i].v.to_units();
1296       check_output_limits(x, y);
1297     }
1298     break;
1299   case 't':
1300     x = output_hpos;
1301     y = output_vpos;
1302     for (i = 0; i < npoints; i++) {
1303       x += point[i].h.to_units();
1304       y += point[i].v.to_units();
1305       check_output_limits(x, y);
1306     }
1307     break;
1308   case 'a':
1309     double c[2];
1310     int p[4];
1311     int minx, miny, maxx, maxy;
1312     x = output_hpos;
1313     y = output_vpos;
1314     p[0] = point[0].h.to_units();
1315     p[1] = point[0].v.to_units();
1316     p[2] = point[1].h.to_units();
1317     p[3] = point[1].v.to_units();
1318     if (adjust_arc_center(p, c)) {
1319       check_output_arc_limits(x, y,
1320                               p[0], p[1], p[2], p[3],
1321                               c[0], c[1],
1322                               &minx, &maxx, &miny, &maxy);
1323       check_output_limits(minx, miny);
1324       check_output_limits(maxx, maxy);
1325       break;
1326     }
1327     // fall through
1328   case 'l':
1329     x = output_hpos;
1330     y = output_vpos;
1331     check_output_limits(x, y);
1332     for (i = 0; i < npoints; i++) {
1333       x += point[i].h.to_units();
1334       y += point[i].v.to_units();
1335       check_output_limits(x, y);
1336     }
1337     break;
1338   default:
1339     x = output_hpos;
1340     y = output_vpos;
1341     for (i = 0; i < npoints; i++) {
1342       x += point[i].h.to_units();
1343       y += point[i].v.to_units();
1344       check_output_limits(x, y);
1345     }
1346   }
1347 }
1348
1349 void troff_output_file::draw(char code, hvpair *point, int npoints,
1350                              font_size fsize, color *gcol, color *fcol)
1351 {
1352   int i;
1353   flush_tbuf();
1354   do_motion();
1355   glyph_color(gcol);
1356   fill_color(fcol);
1357   if (is_on()) {
1358     int size = fsize.to_scaled_points();
1359     if (current_size != size) {
1360       put('s');
1361       put(size);
1362       put('\n');
1363       current_size = size;
1364       current_tfont = 0;
1365     }
1366     put('D');
1367     put(code);
1368     if (code == 'c') {
1369       put(' ');
1370       put(point[0].h.to_units());
1371     }
1372     else
1373       for (i = 0; i < npoints; i++) {
1374         put(' ');
1375         put(point[i].h.to_units());
1376         put(' ');
1377         put(point[i].v.to_units());
1378       }
1379     determine_line_limits(code, point, npoints);
1380   }
1381
1382   for (i = 0; i < npoints; i++)
1383     output_hpos += point[i].h.to_units();
1384   hpos = output_hpos;
1385   if (code != 'e') {
1386     for (i = 0; i < npoints; i++)
1387       output_vpos += point[i].v.to_units();
1388     vpos = output_vpos;
1389   }
1390   if (is_on())
1391     put('\n');
1392 }
1393
1394 void troff_output_file::really_on()
1395 {
1396   flush_tbuf();
1397   force_motion = 1;
1398   do_motion();
1399 }
1400
1401 void troff_output_file::really_off()
1402 {
1403   flush_tbuf();
1404 }
1405
1406 void troff_output_file::really_put_filename(const char *filename)
1407 {
1408   flush_tbuf();
1409   put("F ");
1410   put(filename);
1411   put('\n');
1412 }
1413
1414 void troff_output_file::really_begin_page(int pageno, vunits page_length)
1415 {
1416   flush_tbuf();
1417   if (begun_page) {
1418     if (page_length > V0) {
1419       put('V');
1420       put(page_length.to_units());
1421       put('\n');
1422     }
1423   }
1424   else
1425     begun_page = 1;
1426   current_tfont = 0;
1427   current_font_number = -1;
1428   current_size = 0;
1429   // current_height = 0;
1430   // current_slant = 0;
1431   hpos = 0;
1432   vpos = 0;
1433   output_hpos = 0;
1434   output_vpos = 0;
1435   force_motion = 1;
1436   for (int i = 0; i < nfont_positions; i++)
1437     font_position[i] = NULL_SYMBOL;
1438   put('p');
1439   put(pageno);
1440   put('\n');
1441 }
1442
1443 void troff_output_file::really_copy_file(hunits x, vunits y, const char *filename)
1444 {
1445   moveto(x, y);
1446   flush_tbuf();
1447   do_motion();
1448   errno = 0;
1449   FILE *ifp = fopen(filename, "r");
1450   if (ifp == 0)
1451     error("can't open `%1': %2", filename, strerror(errno));
1452   else {
1453     int c;
1454     while ((c = getc(ifp)) != EOF)
1455       put(char(c));
1456     fclose(ifp);
1457   }
1458   force_motion = 1;
1459   current_size = 0;
1460   current_tfont = 0;
1461   current_font_number = -1;
1462   for (int i = 0; i < nfont_positions; i++)
1463     font_position[i] = NULL_SYMBOL;
1464 }
1465
1466 void troff_output_file::really_transparent_char(unsigned char c)
1467 {
1468   put(c);
1469 }
1470
1471 troff_output_file::~troff_output_file()
1472 {
1473   a_delete font_position;
1474 }
1475
1476 void troff_output_file::trailer(vunits page_length)
1477 {
1478   flush_tbuf();
1479   if (page_length > V0) {
1480     put("x trailer\n");
1481     put('V');
1482     put(page_length.to_units());
1483     put('\n');
1484   }
1485   put("x stop\n");
1486 }
1487
1488 troff_output_file::troff_output_file()
1489 : current_slant(0), current_height(0), nfont_positions(10), tbuf_len(0),
1490   begun_page(0)
1491 {
1492   font_position = new symbol[nfont_positions];
1493   put("x T ");
1494   put(device);
1495   put('\n');
1496   put("x res ");
1497   put(units_per_inch);
1498   put(' ');
1499   put(hresolution);
1500   put(' ');
1501   put(vresolution);
1502   put('\n');
1503   put("x init\n");
1504 }
1505
1506 /* output_file */
1507
1508 output_file *the_output = 0;
1509
1510 output_file::output_file()
1511 {
1512 }
1513
1514 output_file::~output_file()
1515 {
1516 }
1517
1518 void output_file::trailer(vunits)
1519 {
1520 }
1521
1522 void output_file::put_filename(const char *filename)
1523 {
1524 }
1525
1526 void output_file::on()
1527 {
1528 }
1529
1530 void output_file::off()
1531 {
1532 }
1533
1534 real_output_file::real_output_file()
1535 : printing(0), output_on(1)
1536 {
1537 #ifndef POPEN_MISSING
1538   if (pipe_command) {
1539     if ((fp = popen(pipe_command, POPEN_WT)) != 0) {
1540       piped = 1;
1541       return;
1542     }
1543     error("pipe open failed: %1", strerror(errno));
1544   }
1545   piped = 0;
1546 #endif /* not POPEN_MISSING */
1547   fp = stdout;
1548 }
1549
1550 real_output_file::~real_output_file()
1551 {
1552   if (!fp)
1553     return;
1554   // To avoid looping, set fp to 0 before calling fatal().
1555   if (ferror(fp) || fflush(fp) < 0) {
1556     fp = 0;
1557     fatal("error writing output file");
1558   }
1559 #ifndef POPEN_MISSING
1560   if (piped) {
1561     int result = pclose(fp);
1562     fp = 0;
1563     if (result < 0)
1564       fatal("pclose failed");
1565     if (!WIFEXITED(result))
1566       error("output process `%1' got fatal signal %2",
1567             pipe_command,
1568             WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
1569     else {
1570       int exit_status = WEXITSTATUS(result);
1571       if (exit_status != 0)
1572         error("output process `%1' exited with status %2",
1573               pipe_command, exit_status);
1574     }
1575   }
1576   else
1577 #endif /* not POPEN MISSING */
1578   if (fclose(fp) < 0) {
1579     fp = 0;
1580     fatal("error closing output file");
1581   }
1582 }
1583
1584 void real_output_file::flush()
1585 {
1586   if (fflush(fp) < 0)
1587     fatal("error writing output file");
1588 }
1589
1590 int real_output_file::is_printing()
1591 {
1592   return printing;
1593 }
1594
1595 void real_output_file::begin_page(int pageno, vunits page_length)
1596 {
1597   printing = in_output_page_list(pageno);
1598   if (printing)
1599     really_begin_page(pageno, page_length);
1600 }
1601
1602 void real_output_file::copy_file(hunits x, vunits y, const char *filename)
1603 {
1604   if (printing && output_on)
1605     really_copy_file(x, y, filename);
1606   check_output_limits(x.to_units(), y.to_units());
1607 }
1608
1609 void real_output_file::transparent_char(unsigned char c)
1610 {
1611   if (printing && output_on)
1612     really_transparent_char(c);
1613 }
1614
1615 void real_output_file::print_line(hunits x, vunits y, node *n,
1616                              vunits before, vunits after, hunits width)
1617 {
1618   if (printing)
1619     really_print_line(x, y, n, before, after, width);
1620   delete_node_list(n);
1621 }
1622
1623 void real_output_file::really_copy_file(hunits, vunits, const char *)
1624 {
1625   // do nothing
1626 }
1627
1628 void real_output_file::put_filename(const char *filename)
1629 {
1630   really_put_filename(filename);
1631 }
1632
1633 void real_output_file::really_put_filename(const char *filename)
1634 {
1635 }
1636
1637 void real_output_file::on()
1638 {
1639   really_on();
1640   if (output_on == 0)
1641     output_on = 1;
1642 }
1643
1644 void real_output_file::off()
1645 {
1646   really_off();
1647   output_on = 0;
1648 }
1649
1650 int real_output_file::is_on()
1651 {
1652   return output_on;
1653 }
1654
1655 void real_output_file::really_on()
1656 {
1657 }
1658
1659 void real_output_file::really_off()
1660 {
1661 }
1662
1663 /* ascii_output_file */
1664
1665 void ascii_output_file::really_transparent_char(unsigned char c)
1666 {
1667   putc(c, fp);
1668 }
1669
1670 void ascii_output_file::really_print_line(hunits, vunits, node *n, vunits, vunits, hunits width)
1671 {
1672   while (n != 0) {
1673     n->ascii_print(this);
1674     n = n->next;
1675   }
1676   fputc('\n', fp);
1677 }
1678
1679 void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/)
1680 {
1681   fputs("<beginning of page>\n", fp);
1682 }
1683
1684 ascii_output_file::ascii_output_file()
1685 {
1686 }
1687
1688 /* suppress_output_file */
1689
1690 suppress_output_file::suppress_output_file()
1691 {
1692 }
1693
1694 void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits, hunits)
1695 {
1696 }
1697
1698 void suppress_output_file::really_begin_page(int, vunits)
1699 {
1700 }
1701
1702 void suppress_output_file::really_transparent_char(unsigned char)
1703 {
1704 }
1705
1706 /* glyphs, ligatures, kerns, discretionary breaks */
1707
1708 class charinfo_node : public node {
1709 protected:
1710   charinfo *ci;
1711 public:
1712   charinfo_node(charinfo *, node * = 0);
1713   int ends_sentence();
1714   int overlaps_vertically();
1715   int overlaps_horizontally();
1716 };
1717
1718 charinfo_node::charinfo_node(charinfo *c, node *x)
1719 : node(x), ci(c)
1720 {
1721 }
1722
1723 int charinfo_node::ends_sentence()
1724 {
1725   if (ci->ends_sentence())
1726     return 1;
1727   else if (ci->transparent())
1728     return 2;
1729   else
1730     return 0;
1731 }
1732
1733 int charinfo_node::overlaps_horizontally()
1734 {
1735   return ci->overlaps_horizontally();
1736 }
1737
1738 int charinfo_node::overlaps_vertically()
1739 {
1740   return ci->overlaps_vertically();
1741 }
1742
1743 class glyph_node : public charinfo_node {
1744   static glyph_node *free_list;
1745 protected:
1746   tfont *tf;
1747   color *gcol;
1748   color *fcol;          /* this is needed for grotty */
1749 #ifdef STORE_WIDTH
1750   hunits wid;
1751   glyph_node(charinfo *, tfont *, color *, color *, hunits, node * = 0);
1752 #endif
1753 public:
1754   void *operator new(size_t);
1755   void operator delete(void *);
1756   glyph_node(charinfo *, tfont *, color *, color *, node * = 0);
1757   ~glyph_node() {}
1758   node *copy();
1759   node *merge_glyph_node(glyph_node *);
1760   node *merge_self(node *);
1761   hunits width();
1762   node *last_char_node();
1763   units size();
1764   void vertical_extent(vunits *, vunits *);
1765   hunits subscript_correction();
1766   hunits italic_correction();
1767   hunits left_italic_correction();
1768   hunits skew();
1769   hyphenation_type get_hyphenation_type();
1770   tfont *get_tfont();
1771   color *get_glyph_color();
1772   color *get_fill_color();
1773   void tprint(troff_output_file *);
1774   void zero_width_tprint(troff_output_file *);
1775   hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
1776   node *add_self(node *, hyphen_list **);
1777   void ascii_print(ascii_output_file *);
1778   void asciify(macro *);
1779   int character_type();
1780   int same(node *);
1781   const char *type();
1782   int force_tprint();
1783 };
1784
1785 glyph_node *glyph_node::free_list = 0;
1786
1787 class ligature_node : public glyph_node {
1788   node *n1;
1789   node *n2;
1790 #ifdef STORE_WIDTH
1791   ligature_node(charinfo *, tfont *, color *, color *, hunits,
1792                 node *, node *, node * = 0);
1793 #endif
1794 public:
1795   void *operator new(size_t);
1796   void operator delete(void *);
1797   ligature_node(charinfo *, tfont *, color *, color *,
1798                 node *, node *, node * = 0);
1799   ~ligature_node();
1800   node *copy();
1801   node *add_self(node *, hyphen_list **);
1802   hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
1803   void ascii_print(ascii_output_file *);
1804   void asciify(macro *);
1805   int same(node *);
1806   const char *type();
1807   int force_tprint();
1808 };
1809
1810 class kern_pair_node : public node {
1811   hunits amount;
1812   node *n1;
1813   node *n2;
1814 public:
1815   kern_pair_node(hunits n, node *first, node *second, node *x = 0);
1816   ~kern_pair_node();
1817   node *copy();
1818   node *merge_glyph_node(glyph_node *);
1819   node *add_self(node *, hyphen_list **);
1820   hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
1821   node *add_discretionary_hyphen();
1822   hunits width();
1823   node *last_char_node();
1824   hunits italic_correction();
1825   hunits subscript_correction();
1826   void tprint(troff_output_file *);
1827   hyphenation_type get_hyphenation_type();
1828   int ends_sentence();
1829   void ascii_print(ascii_output_file *);
1830   void asciify(macro *);
1831   int same(node *);
1832   const char *type();
1833   int force_tprint();
1834   void vertical_extent(vunits *, vunits *);
1835 };
1836
1837 class dbreak_node : public node {
1838   node *none;
1839   node *pre;
1840   node *post;
1841 public:
1842   dbreak_node(node *n, node *p, node *x = 0);
1843   ~dbreak_node();
1844   node *copy();
1845   node *merge_glyph_node(glyph_node *);
1846   node *add_discretionary_hyphen();
1847   hunits width();
1848   node *last_char_node();
1849   hunits italic_correction();
1850   hunits subscript_correction();
1851   void tprint(troff_output_file *);
1852   breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
1853                               int is_inner = 0);
1854   int nbreaks();
1855   int ends_sentence();
1856   void split(int, node **, node **);
1857   hyphenation_type get_hyphenation_type();
1858   void ascii_print(ascii_output_file *);
1859   void asciify(macro *);
1860   int same(node *);
1861   const char *type();
1862   int force_tprint();
1863 };
1864
1865 void *glyph_node::operator new(size_t n)
1866 {
1867   assert(n == sizeof(glyph_node));
1868   if (!free_list) {
1869     const int BLOCK = 1024;
1870     free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
1871     for (int i = 0; i < BLOCK - 1; i++)
1872       free_list[i].next = free_list + i + 1;
1873     free_list[BLOCK-1].next = 0;
1874   }
1875   glyph_node *p = free_list;
1876   free_list = (glyph_node *)(free_list->next);
1877   p->next = 0;
1878   return p;
1879 }
1880
1881 void *ligature_node::operator new(size_t n)
1882 {
1883   return new char[n];
1884 }
1885
1886 void glyph_node::operator delete(void *p)
1887 {
1888   if (p) {
1889     ((glyph_node *)p)->next = free_list;
1890     free_list = (glyph_node *)p;
1891   }
1892 }
1893
1894 void ligature_node::operator delete(void *p)
1895 {
1896   delete[] (char *)p;
1897 }
1898
1899 glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc, node *x)
1900 : charinfo_node(c, x), tf(t), gcol(gc), fcol(fc)
1901 {
1902 #ifdef STORE_WIDTH
1903   wid = tf->get_width(ci);
1904 #endif
1905 }
1906
1907 #ifdef STORE_WIDTH
1908 glyph_node::glyph_node(charinfo *c, tfont *t,
1909                        color *gc, color *fc, hunits w, node *x)
1910 : charinfo_node(c, x), tf(t), gcol(gc), fcol(fc), wid(w)
1911 {
1912 }
1913 #endif
1914
1915 node *glyph_node::copy()
1916 {
1917 #ifdef STORE_WIDTH
1918   return new glyph_node(ci, tf, gcol, fcol, wid);
1919 #else
1920   return new glyph_node(ci, tf, gcol, fcol);
1921 #endif
1922 }
1923
1924 node *glyph_node::merge_self(node *nd)
1925 {
1926   return nd->merge_glyph_node(this);
1927 }
1928
1929 int glyph_node::character_type()
1930 {
1931   return tf->get_character_type(ci);
1932 }
1933
1934 node *glyph_node::add_self(node *n, hyphen_list **p)
1935 {
1936   assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
1937   next = 0;
1938   node *nn;
1939   if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
1940     next = n;
1941     nn = this;
1942   }
1943   if ((*p)->hyphen)
1944     nn = nn->add_discretionary_hyphen();
1945   hyphen_list *pp = *p;
1946   *p = (*p)->next;
1947   delete pp;
1948   return nn;
1949 }
1950
1951 units glyph_node::size()
1952 {
1953   return tf->get_size().to_units();
1954 }
1955
1956 hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail)
1957 {
1958   return new hyphen_list(ci->get_hyphenation_code(), tail);
1959 }
1960
1961 tfont *node::get_tfont()
1962 {
1963   return 0;
1964 }
1965
1966 tfont *glyph_node::get_tfont()
1967 {
1968   return tf;
1969 }
1970
1971 color *node::get_glyph_color()
1972 {
1973   return 0;
1974 }
1975
1976 color *glyph_node::get_glyph_color()
1977 {
1978   return gcol;
1979 }
1980
1981 color *node::get_fill_color()
1982 {
1983   return 0;
1984 }
1985
1986 color *glyph_node::get_fill_color()
1987 {
1988   return fcol;
1989 }
1990
1991 node *node::merge_glyph_node(glyph_node * /*gn*/)
1992 {
1993   return 0;
1994 }
1995
1996 node *glyph_node::merge_glyph_node(glyph_node *gn)
1997 {
1998   if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
1999     charinfo *lig;
2000     if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
2001       node *next1 = next;
2002       next = 0;
2003       return new ligature_node(lig, tf, gcol, fcol, this, gn, next1);
2004     }
2005     hunits kern;
2006     if (tf->get_kern(ci, gn->ci, &kern)) {
2007       node *next1 = next;
2008       next = 0;
2009       return new kern_pair_node(kern, this, gn, next1);
2010     }
2011   }
2012   return 0;
2013 }
2014
2015 #ifdef STORE_WIDTH
2016 inline
2017 #endif
2018 hunits glyph_node::width()
2019 {
2020 #ifdef STORE_WIDTH
2021   return wid;
2022 #else
2023   return tf->get_width(ci);
2024 #endif
2025 }
2026
2027 node *glyph_node::last_char_node()
2028 {
2029   return this;
2030 }
2031
2032 void glyph_node::vertical_extent(vunits *min, vunits *max)
2033 {
2034   *min = -tf->get_char_height(ci);
2035   *max = tf->get_char_depth(ci);
2036 }
2037
2038 hunits glyph_node::skew()
2039 {
2040   return tf->get_char_skew(ci);
2041 }
2042
2043 hunits glyph_node::subscript_correction()
2044 {
2045   return tf->get_subscript_correction(ci);
2046 }
2047
2048 hunits glyph_node::italic_correction()
2049 {
2050   return tf->get_italic_correction(ci);
2051 }
2052
2053 hunits glyph_node::left_italic_correction()
2054 {
2055   return tf->get_left_italic_correction(ci);
2056 }
2057
2058 hyphenation_type glyph_node::get_hyphenation_type()
2059 {
2060   return HYPHEN_MIDDLE;
2061 }
2062
2063 void glyph_node::ascii_print(ascii_output_file *ascii)
2064 {
2065   unsigned char c = ci->get_ascii_code();
2066   if (c != 0)
2067     ascii->outc(c);
2068   else
2069     ascii->outs(ci->nm.contents());
2070 }
2071
2072 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2073                              node *gn1, node *gn2, node *x)
2074 : glyph_node(c, t, gc, fc, x), n1(gn1), n2(gn2)
2075 {
2076 }
2077
2078 #ifdef STORE_WIDTH
2079 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2080                              hunits w, node *gn1, node *gn2, node *x)
2081 : glyph_node(c, t, gc, fc, w, x), n1(gn1), n2(gn2)
2082 {
2083 }
2084 #endif
2085
2086 ligature_node::~ligature_node()
2087 {
2088   delete n1;
2089   delete n2;
2090 }
2091
2092 node *ligature_node::copy()
2093 {
2094 #ifdef STORE_WIDTH
2095   return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy());
2096 #else
2097   return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy());
2098 #endif
2099 }
2100
2101 void ligature_node::ascii_print(ascii_output_file *ascii)
2102 {
2103   n1->ascii_print(ascii);
2104   n2->ascii_print(ascii);
2105 }
2106
2107 hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail)
2108 {
2109   return n1->get_hyphen_list(n2->get_hyphen_list(tail));
2110 }
2111
2112 node *ligature_node::add_self(node *n, hyphen_list **p)
2113 {
2114   n = n1->add_self(n, p);
2115   n = n2->add_self(n, p);
2116   n1 = n2 = 0;
2117   delete this;
2118   return n;
2119 }
2120
2121 kern_pair_node::kern_pair_node(hunits n, node *first, node *second, node *x)
2122 : node(x), amount(n), n1(first), n2(second)
2123 {
2124 }
2125
2126 dbreak_node::dbreak_node(node *n, node *p, node *x)
2127 : node(x), none(n), pre(p), post(0)
2128 {
2129 }
2130
2131 node *dbreak_node::merge_glyph_node(glyph_node *gn)
2132 {
2133   glyph_node *gn2 = (glyph_node *)gn->copy();
2134   node *new_none = none ? none->merge_glyph_node(gn) : 0;
2135   node *new_post = post ? post->merge_glyph_node(gn2) : 0;
2136   if (new_none == 0 && new_post == 0) {
2137     delete gn2;
2138     return 0;
2139   }
2140   if (new_none != 0)
2141     none = new_none;
2142   else {
2143     gn->next = none;
2144     none = gn;
2145   }
2146   if (new_post != 0)
2147     post = new_post;
2148   else {
2149     gn2->next = post;
2150     post = gn2;
2151   }
2152   return this;
2153 }
2154
2155 node *kern_pair_node::merge_glyph_node(glyph_node *gn)
2156 {
2157   node *nd = n2->merge_glyph_node(gn);
2158   if (nd == 0)
2159     return 0;
2160   n2 = nd;
2161   nd = n2->merge_self(n1);
2162   if (nd) {
2163     nd->next = next;
2164     n1 = 0;
2165     n2 = 0;
2166     delete this;
2167     return nd;
2168   }
2169   return this;
2170 }
2171
2172 hunits kern_pair_node::italic_correction()
2173 {
2174   return n2->italic_correction();
2175 }
2176
2177 hunits kern_pair_node::subscript_correction()
2178 {
2179   return n2->subscript_correction();
2180 }
2181
2182 void kern_pair_node::vertical_extent(vunits *min, vunits *max)
2183 {
2184   n1->vertical_extent(min, max);
2185   vunits min2, max2;
2186   n2->vertical_extent(&min2, &max2);
2187   if (min2 < *min)
2188     *min = min2;
2189   if (max2 > *max)
2190     *max = max2;
2191 }
2192
2193 node *kern_pair_node::add_discretionary_hyphen()
2194 {
2195   tfont *tf = n2->get_tfont();
2196   if (tf) {
2197     if (tf->contains(soft_hyphen_char)) {
2198       color *gcol = n2->get_glyph_color();
2199       color *fcol = n2->get_fill_color();
2200       node *next1 = next;
2201       next = 0;
2202       node *n = copy();
2203       glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol);
2204       node *nn = n->merge_glyph_node(gn);
2205       if (nn == 0) {
2206         gn->next = n;
2207         nn = gn;
2208       }
2209       return new dbreak_node(this, nn, next1);
2210     }
2211   }
2212   return this;
2213 }
2214
2215 kern_pair_node::~kern_pair_node()
2216 {
2217   if (n1 != 0)
2218     delete n1;
2219   if (n2 != 0)
2220     delete n2;
2221 }
2222
2223 dbreak_node::~dbreak_node()
2224 {
2225   delete_node_list(pre);
2226   delete_node_list(post);
2227   delete_node_list(none);
2228 }
2229
2230 node *kern_pair_node::copy()
2231 {
2232   return new kern_pair_node(amount, n1->copy(), n2->copy());
2233 }
2234
2235 node *copy_node_list(node *n)
2236 {
2237   node *p = 0;
2238   while (n != 0) {
2239     node *nn = n->copy();
2240     nn->next = p;
2241     p = nn;
2242     n = n->next;
2243   }
2244   while (p != 0) {
2245     node *pp = p->next;
2246     p->next = n;
2247     n = p;
2248     p = pp;
2249   }
2250   return n;
2251 }
2252
2253 void delete_node_list(node *n)
2254 {
2255   while (n != 0) {
2256     node *tem = n;
2257     n = n->next;
2258     delete tem;
2259   }
2260 }
2261
2262 node *dbreak_node::copy()
2263 {
2264   dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre));
2265   p->post = copy_node_list(post);
2266   return p;
2267 }
2268
2269 hyphen_list *node::get_hyphen_list(hyphen_list *tail)
2270 {
2271   return tail;
2272 }
2273
2274 hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail)
2275 {
2276   return n1->get_hyphen_list(n2->get_hyphen_list(tail));
2277 }
2278
2279 class hyphen_inhibitor_node : public node {
2280 public:
2281   hyphen_inhibitor_node(node *nd = 0);
2282   node *copy();
2283   int same(node *);
2284   const char *type();
2285   int force_tprint();
2286   hyphenation_type get_hyphenation_type();
2287 };
2288
2289 hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
2290 {
2291 }
2292
2293 node *hyphen_inhibitor_node::copy()
2294 {
2295   return new hyphen_inhibitor_node;
2296 }
2297
2298 int hyphen_inhibitor_node::same(node *)
2299 {
2300   return 1;
2301 }
2302
2303 const char *hyphen_inhibitor_node::type()
2304 {
2305   return "hyphen_inhibitor_node";
2306 }
2307
2308 int hyphen_inhibitor_node::force_tprint()
2309 {
2310   return 0;
2311 }
2312
2313 hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
2314 {
2315   return HYPHEN_INHIBIT;
2316 }
2317
2318 /* add_discretionary_hyphen methods */
2319
2320 node *dbreak_node::add_discretionary_hyphen()
2321 {
2322   if (post)
2323     post = post->add_discretionary_hyphen();
2324   if (none)
2325     none = none->add_discretionary_hyphen();
2326   return this;
2327 }
2328
2329 node *node::add_discretionary_hyphen()
2330 {
2331   tfont *tf = get_tfont();
2332   if (!tf)
2333     return new hyphen_inhibitor_node(this);
2334   if (tf->contains(soft_hyphen_char)) {
2335     color *gcol = get_glyph_color();
2336     color *fcol = get_fill_color();
2337     node *next1 = next;
2338     next = 0;
2339     node *n = copy();
2340     glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol);
2341     node *n1 = n->merge_glyph_node(gn);
2342     if (n1 == 0) {
2343       gn->next = n;
2344       n1 = gn;
2345     }
2346     return new dbreak_node(this, n1, next1);
2347   }
2348   return this;
2349 }
2350
2351 node *node::merge_self(node *)
2352 {
2353   return 0;
2354 }
2355
2356 node *node::add_self(node *n, hyphen_list ** /*p*/)
2357 {
2358   next = n;
2359   return this;
2360 }
2361
2362 node *kern_pair_node::add_self(node *n, hyphen_list **p)
2363 {
2364   n = n1->add_self(n, p);
2365   n = n2->add_self(n, p);
2366   n1 = n2 = 0;
2367   delete this;
2368   return n;
2369 }
2370
2371 hunits node::width()
2372 {
2373   return H0;
2374 }
2375
2376 node *node::last_char_node()
2377 {
2378   return 0;
2379 }
2380
2381 int node::force_tprint()
2382 {
2383   return 0;
2384 }
2385
2386 hunits hmotion_node::width()
2387 {
2388   return n;
2389 }
2390
2391 units node::size()
2392 {
2393   return points_to_units(10);
2394 }
2395
2396 hunits kern_pair_node::width()
2397 {
2398   return n1->width() + n2->width() + amount;
2399 }
2400
2401 node *kern_pair_node::last_char_node()
2402 {
2403   node *nd = n2->last_char_node();
2404   if (nd)
2405     return nd;
2406   return n1->last_char_node();
2407 }
2408
2409 hunits dbreak_node::width()
2410 {
2411   hunits x = H0;
2412   for (node *n = none; n != 0; n = n->next)
2413     x += n->width();
2414   return x;
2415 }
2416
2417 node *dbreak_node::last_char_node()
2418 {
2419   for (node *n = none; n; n = n->next) {
2420     node *last = n->last_char_node();
2421     if (last)
2422       return last;
2423   }
2424   return 0;
2425 }
2426
2427 hunits dbreak_node::italic_correction()
2428 {
2429   return none ? none->italic_correction() : H0;
2430 }
2431
2432 hunits dbreak_node::subscript_correction()
2433 {
2434   return none ? none->subscript_correction() : H0;
2435 }
2436
2437 class italic_corrected_node : public node {
2438   node *n;
2439   hunits x;
2440 public:
2441   italic_corrected_node(node *, hunits, node * = 0);
2442   ~italic_corrected_node();
2443   node *copy();
2444   void ascii_print(ascii_output_file *);
2445   void asciify(macro *);
2446   hunits width();
2447   node *last_char_node();
2448   void vertical_extent(vunits *, vunits *);
2449   int ends_sentence();
2450   int overlaps_horizontally();
2451   int overlaps_vertically();
2452   int same(node *);
2453   hyphenation_type get_hyphenation_type();
2454   tfont *get_tfont();
2455   hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
2456   int character_type();
2457   void tprint(troff_output_file *);
2458   hunits subscript_correction();
2459   hunits skew();
2460   node *add_self(node *, hyphen_list **);
2461   const char *type();
2462   int force_tprint();
2463 };
2464
2465 node *node::add_italic_correction(hunits *width)
2466 {
2467   hunits ic = italic_correction();
2468   if (ic.is_zero())
2469     return this;
2470   else {
2471     node *next1 = next;
2472     next = 0;
2473     *width += ic;
2474     return new italic_corrected_node(this, ic, next1);
2475   }
2476 }
2477
2478 italic_corrected_node::italic_corrected_node(node *nn, hunits xx, node *p)
2479 : node(p), n(nn), x(xx)
2480 {
2481   assert(n != 0);
2482 }
2483
2484 italic_corrected_node::~italic_corrected_node()
2485 {
2486   delete n;
2487 }
2488
2489 node *italic_corrected_node::copy()
2490 {
2491   return new italic_corrected_node(n->copy(), x);
2492 }
2493
2494 hunits italic_corrected_node::width()
2495 {
2496   return n->width() + x;
2497 }
2498
2499 void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
2500 {
2501   n->vertical_extent(min, max);
2502 }
2503
2504 void italic_corrected_node::tprint(troff_output_file *out)
2505 {
2506   n->tprint(out);
2507   out->right(x);
2508 }
2509
2510 hunits italic_corrected_node::skew()
2511 {
2512   return n->skew() - x/2;
2513 }
2514
2515 hunits italic_corrected_node::subscript_correction()
2516 {
2517   return n->subscript_correction() - x;
2518 }
2519
2520 void italic_corrected_node::ascii_print(ascii_output_file *out)
2521 {
2522   n->ascii_print(out);
2523 }
2524
2525 int italic_corrected_node::ends_sentence()
2526 {
2527   return n->ends_sentence();
2528 }
2529
2530 int italic_corrected_node::overlaps_horizontally()
2531 {
2532   return n->overlaps_horizontally();
2533 }
2534
2535 int italic_corrected_node::overlaps_vertically()
2536 {
2537   return n->overlaps_vertically();
2538 }
2539
2540 node *italic_corrected_node::last_char_node()
2541 {
2542   return n->last_char_node();
2543 }
2544
2545 tfont *italic_corrected_node::get_tfont()
2546 {
2547   return n->get_tfont();
2548 }
2549
2550 hyphenation_type italic_corrected_node::get_hyphenation_type()
2551 {
2552   return n->get_hyphenation_type();
2553 }
2554
2555 node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
2556 {
2557   nd = n->add_self(nd, p);
2558   hunits not_interested;
2559   nd = nd->add_italic_correction(&not_interested);
2560   n = 0;
2561   delete this;
2562   return nd;
2563 }
2564
2565 hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail)
2566 {
2567   return n->get_hyphen_list(tail);
2568 }
2569
2570 int italic_corrected_node::character_type()
2571 {
2572   return n->character_type();
2573 }
2574
2575 class break_char_node : public node {
2576   node *ch;
2577   char break_code;
2578   color *col;
2579 public:
2580   break_char_node(node *, int, color *, node * = 0);
2581   ~break_char_node();
2582   node *copy();
2583   hunits width();
2584   vunits vertical_width();
2585   node *last_char_node();
2586   int character_type();
2587   int ends_sentence();
2588   node *add_self(node *, hyphen_list **);
2589   hyphen_list *get_hyphen_list(hyphen_list *s = 0);
2590   void tprint(troff_output_file *);
2591   void zero_width_tprint(troff_output_file *);
2592   void ascii_print(ascii_output_file *);
2593   void asciify(macro *);
2594   hyphenation_type get_hyphenation_type();
2595   int overlaps_vertically();
2596   int overlaps_horizontally();
2597   units size();
2598   tfont *get_tfont();
2599   int same(node *);
2600   const char *type();
2601   int force_tprint();
2602 };
2603
2604 break_char_node::break_char_node(node *n, int bc, color *c, node *x)
2605 : node(x), ch(n), break_code(bc), col(c)
2606 {
2607 }
2608
2609 break_char_node::~break_char_node()
2610 {
2611   delete ch;
2612 }
2613
2614 node *break_char_node::copy()
2615 {
2616   return new break_char_node(ch->copy(), break_code, col);
2617 }
2618
2619 hunits break_char_node::width()
2620 {
2621   return ch->width();
2622 }
2623
2624 vunits break_char_node::vertical_width()
2625 {
2626   return ch->vertical_width();
2627 }
2628
2629 node *break_char_node::last_char_node()
2630 {
2631   return ch->last_char_node();
2632 }
2633
2634 int break_char_node::character_type()
2635 {
2636   return ch->character_type();
2637 }
2638
2639 int break_char_node::ends_sentence()
2640 {
2641   return ch->ends_sentence();
2642 }
2643
2644 node *break_char_node::add_self(node *n, hyphen_list **p)
2645 {
2646   assert((*p)->hyphenation_code == 0);
2647   if ((*p)->breakable && (break_code & 1)) {
2648     n = new space_node(H0, col, n);
2649     n->freeze_space();
2650   }
2651   next = n;
2652   n = this;
2653   if ((*p)->breakable && (break_code & 2)) {
2654     n = new space_node(H0, col, n);
2655     n->freeze_space();
2656   }
2657   hyphen_list *pp = *p;
2658   *p = (*p)->next;
2659   delete pp;
2660   return n;
2661 }
2662
2663 hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail)
2664 {
2665   return new hyphen_list(0, tail);
2666 }
2667
2668 hyphenation_type break_char_node::get_hyphenation_type()
2669 {
2670   return HYPHEN_MIDDLE;
2671 }
2672
2673 void break_char_node::ascii_print(ascii_output_file *ascii)
2674 {
2675   ch->ascii_print(ascii);
2676 }
2677
2678 int break_char_node::overlaps_vertically()
2679 {
2680   return ch->overlaps_vertically();
2681 }
2682
2683 int break_char_node::overlaps_horizontally()
2684 {
2685   return ch->overlaps_horizontally();
2686 }
2687
2688 units break_char_node::size()
2689 {
2690   return ch->size();
2691 }
2692
2693 tfont *break_char_node::get_tfont()
2694 {
2695   return ch->get_tfont();
2696 }
2697
2698 node *extra_size_node::copy()
2699 {
2700   return new extra_size_node(n);
2701 }
2702
2703 node *vertical_size_node::copy()
2704 {
2705   return new vertical_size_node(n);
2706 }
2707
2708 node *hmotion_node::copy()
2709 {
2710   return new hmotion_node(n, was_tab, unformat, col);
2711 }
2712
2713 node *space_char_hmotion_node::copy()
2714 {
2715   return new space_char_hmotion_node(n, col);
2716 }
2717
2718 node *vmotion_node::copy()
2719 {
2720   return new vmotion_node(n, col);
2721 }
2722
2723 node *dummy_node::copy()
2724 {
2725   return new dummy_node;
2726 }
2727
2728 node *transparent_dummy_node::copy()
2729 {
2730   return new transparent_dummy_node;
2731 }
2732
2733 hline_node::~hline_node()
2734 {
2735   if (n)
2736     delete n;
2737 }
2738
2739 node *hline_node::copy()
2740 {
2741   return new hline_node(x, n ? n->copy() : 0);
2742 }
2743
2744 hunits hline_node::width()
2745 {
2746   return x < H0 ? H0 : x;
2747 }
2748
2749 vline_node::~vline_node()
2750 {
2751   if (n)
2752     delete n;
2753 }
2754
2755 node *vline_node::copy()
2756 {
2757   return new vline_node(x, n ? n->copy() : 0);
2758 }
2759
2760 hunits vline_node::width()
2761 {
2762   return n == 0 ? H0 : n->width();
2763 }
2764
2765 zero_width_node::zero_width_node(node *nd) : n(nd)
2766 {
2767 }
2768
2769 zero_width_node::~zero_width_node()
2770 {
2771   delete_node_list(n);
2772 }
2773
2774 node *zero_width_node::copy()
2775 {
2776   return new zero_width_node(copy_node_list(n));
2777 }
2778
2779 int node_list_character_type(node *p)
2780 {
2781   int t = 0;
2782   for (; p; p = p->next)
2783     t |= p->character_type();
2784   return t;
2785 }
2786
2787 int zero_width_node::character_type()
2788 {
2789   return node_list_character_type(n);
2790 }
2791
2792 void node_list_vertical_extent(node *p, vunits *min, vunits *max)
2793 {
2794   *min = V0;
2795   *max = V0;
2796   vunits cur_vpos = V0;
2797   vunits v1, v2;
2798   for (; p; p = p->next) {
2799     p->vertical_extent(&v1, &v2);
2800     v1 += cur_vpos;
2801     if (v1 < *min)
2802       *min = v1;
2803     v2 += cur_vpos;
2804     if (v2 > *max)
2805       *max = v2;
2806     cur_vpos += p->vertical_width();
2807   }
2808 }
2809
2810 void zero_width_node::vertical_extent(vunits *min, vunits *max)
2811 {
2812   node_list_vertical_extent(n, min, max);
2813 }
2814
2815 overstrike_node::overstrike_node() : list(0), max_width(H0)
2816 {
2817 }
2818
2819 overstrike_node::~overstrike_node()
2820 {
2821   delete_node_list(list);
2822 }
2823
2824 node *overstrike_node::copy()
2825 {
2826   overstrike_node *on = new overstrike_node;
2827   for (node *tem = list; tem; tem = tem->next)
2828     on->overstrike(tem->copy());
2829   return on;
2830 }
2831
2832 void overstrike_node::overstrike(node *n)
2833 {
2834   if (n == 0)
2835     return;
2836   hunits w = n->width();
2837   if (w > max_width)
2838     max_width = w;
2839   node **p;
2840   for (p = &list; *p; p = &(*p)->next)
2841     ;
2842   n->next = 0;
2843   *p = n;
2844 }
2845
2846 hunits overstrike_node::width()
2847 {
2848   return max_width;
2849 }
2850
2851 bracket_node::bracket_node() : list(0), max_width(H0)
2852 {
2853 }
2854
2855 bracket_node::~bracket_node()
2856 {
2857   delete_node_list(list);
2858 }
2859
2860 node *bracket_node::copy()
2861 {
2862   bracket_node *on = new bracket_node;
2863   node *last = 0;
2864   node *tem;
2865   if (list)
2866     list->last = 0;
2867   for (tem = list; tem; tem = tem->next) {
2868     if (tem->next)
2869       tem->next->last = tem;
2870     last = tem;
2871   }
2872   for (tem = last; tem; tem = tem->last)
2873     on->bracket(tem->copy());
2874   return on;
2875 }
2876
2877 void bracket_node::bracket(node *n)
2878 {
2879   if (n == 0)
2880     return;
2881   hunits w = n->width();
2882   if (w > max_width)
2883     max_width = w;
2884   n->next = list;
2885   list = n;
2886 }
2887
2888 hunits bracket_node::width()
2889 {
2890   return max_width;
2891 }
2892
2893 int node::nspaces()
2894 {
2895   return 0;
2896 }
2897
2898 int node::merge_space(hunits, hunits, hunits)
2899 {
2900   return 0;
2901 }
2902
2903 #if 0
2904 space_node *space_node::free_list = 0;
2905
2906 void *space_node::operator new(size_t n)
2907 {
2908   assert(n == sizeof(space_node));
2909   if (!free_list) {
2910     free_list = (space_node *)new char[sizeof(space_node)*BLOCK];
2911     for (int i = 0; i < BLOCK - 1; i++)
2912       free_list[i].next = free_list + i + 1;
2913     free_list[BLOCK-1].next = 0;
2914   }
2915   space_node *p = free_list;
2916   free_list = (space_node *)(free_list->next);
2917   p->next = 0;
2918   return p;
2919 }
2920
2921 inline void space_node::operator delete(void *p)
2922 {
2923   if (p) {
2924     ((space_node *)p)->next = free_list;
2925     free_list = (space_node *)p;
2926   }
2927 }
2928 #endif
2929
2930 space_node::space_node(hunits nn, color *c, node *p)
2931 : node(p), n(nn), set(0), was_escape_colon(0), col(c)
2932 {
2933 }
2934
2935 space_node::space_node(hunits nn, int s, int flag, color *c, node *p)
2936 : node(p), n(nn), set(s), was_escape_colon(flag), col(c)
2937 {
2938 }
2939
2940 #if 0
2941 space_node::~space_node()
2942 {
2943 }
2944 #endif
2945
2946 node *space_node::copy()
2947 {
2948   return new space_node(n, set, was_escape_colon, col);
2949 }
2950
2951 int space_node::force_tprint()
2952 {
2953   return 0;
2954 }
2955
2956 int space_node::nspaces()
2957 {
2958   return set ? 0 : 1;
2959 }
2960
2961 int space_node::merge_space(hunits h, hunits, hunits)
2962 {
2963   n += h;
2964   return 1;
2965 }
2966
2967 hunits space_node::width()
2968 {
2969   return n;
2970 }
2971
2972 void node::spread_space(int*, hunits*)
2973 {
2974 }
2975
2976 void space_node::spread_space(int *nspaces, hunits *desired_space)
2977 {
2978   if (!set) {
2979     assert(*nspaces > 0);
2980     if (*nspaces == 1) {
2981       n += *desired_space;
2982       *desired_space = H0;
2983     }
2984     else {
2985       hunits extra = *desired_space / *nspaces;
2986       *desired_space -= extra;
2987       n += extra;
2988     }
2989     *nspaces -= 1;
2990     set = 1;
2991   }
2992 }
2993
2994 void node::freeze_space()
2995 {
2996 }
2997
2998 void space_node::freeze_space()
2999 {
3000   set = 1;
3001 }
3002
3003 void node::is_escape_colon()
3004 {
3005 }
3006
3007 void space_node::is_escape_colon()
3008 {
3009   was_escape_colon = 1;
3010 }
3011
3012 diverted_space_node::diverted_space_node(vunits d, node *p)
3013 : node(p), n(d)
3014 {
3015 }
3016
3017 node *diverted_space_node::copy()
3018 {
3019   return new diverted_space_node(n);
3020 }
3021
3022 diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
3023 : node(p), filename(s)
3024 {
3025 }
3026
3027 node *diverted_copy_file_node::copy()
3028 {
3029   return new diverted_copy_file_node(filename);
3030 }
3031
3032 int node::ends_sentence()
3033 {
3034   return 0;
3035 }
3036
3037 int kern_pair_node::ends_sentence()
3038 {
3039   switch (n2->ends_sentence()) {
3040   case 0:
3041     return 0;
3042   case 1:
3043     return 1;
3044   case 2:
3045     break;
3046   default:
3047     assert(0);
3048   }
3049   return n1->ends_sentence();
3050 }
3051
3052 int node_list_ends_sentence(node *n)
3053 {
3054   for (; n != 0; n = n->next)
3055     switch (n->ends_sentence()) {
3056     case 0:
3057       return 0;
3058     case 1:
3059       return 1;
3060     case 2:
3061       break;
3062     default:
3063       assert(0);
3064     }
3065   return 2;
3066 }
3067
3068 int dbreak_node::ends_sentence()
3069 {
3070   return node_list_ends_sentence(none);
3071 }
3072
3073 int node::overlaps_horizontally()
3074 {
3075   return 0;
3076 }
3077
3078 int node::overlaps_vertically()
3079 {
3080   return 0;
3081 }
3082
3083 int node::discardable()
3084 {
3085   return 0;
3086 }
3087
3088 int space_node::discardable()
3089 {
3090   return set ? 0 : 1;
3091 }
3092
3093 vunits node::vertical_width()
3094 {
3095   return V0;
3096 }
3097
3098 vunits vline_node::vertical_width()
3099 {
3100   return x;
3101 }
3102
3103 vunits vmotion_node::vertical_width()
3104 {
3105   return n;
3106 }
3107
3108 int node::set_unformat_flag()
3109 {
3110   return 1;
3111 }
3112
3113 int node::character_type()
3114 {
3115   return 0;
3116 }
3117
3118 hunits node::subscript_correction()
3119 {
3120   return H0;
3121 }
3122
3123 hunits node::italic_correction()
3124 {
3125   return H0;
3126 }
3127
3128 hunits node::left_italic_correction()
3129 {
3130   return H0;
3131 }
3132
3133 hunits node::skew()
3134 {
3135   return H0;
3136 }
3137
3138 /* vertical_extent methods */
3139
3140 void node::vertical_extent(vunits *min, vunits *max)
3141 {
3142   vunits v = vertical_width();
3143   if (v < V0) {
3144     *min = v;
3145     *max = V0;
3146   }
3147   else {
3148     *max = v;
3149     *min = V0;
3150   }
3151 }
3152
3153 void vline_node::vertical_extent(vunits *min, vunits *max)
3154 {
3155   if (n == 0)
3156     node::vertical_extent(min, max);
3157   else {
3158     vunits cmin, cmax;
3159     n->vertical_extent(&cmin, &cmax);
3160     vunits h = n->size();
3161     if (x < V0) {
3162       if (-x < h) {
3163         *min = x;
3164         *max = V0;
3165       }
3166       else {
3167         // we print the first character and then move up, so
3168         *max = cmax;
3169         // we print the last character and then move up h
3170         *min = cmin + h;
3171         if (*min > V0)
3172           *min = V0;
3173         *min += x;
3174       }
3175     }
3176     else {
3177       if (x < h) {
3178         *max = x;
3179         *min = V0;
3180       }
3181       else {
3182         // we move down by h and then print the first character, so
3183         *min = cmin + h;
3184         if (*min > V0)
3185           *min = V0;
3186         *max = x + cmax;
3187       }
3188     }
3189   }
3190 }
3191
3192 /* ascii_print methods */
3193
3194 static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n)
3195 {
3196   if (n == 0)
3197     return;
3198   ascii_print_reverse_node_list(ascii, n->next);
3199   n->ascii_print(ascii);
3200 }
3201
3202 void dbreak_node::ascii_print(ascii_output_file *ascii)
3203 {
3204   ascii_print_reverse_node_list(ascii, none);
3205 }
3206
3207 void kern_pair_node::ascii_print(ascii_output_file *ascii)
3208 {
3209   n1->ascii_print(ascii);
3210   n2->ascii_print(ascii);
3211 }
3212
3213 void node::ascii_print(ascii_output_file *)
3214 {
3215 }
3216
3217 void space_node::ascii_print(ascii_output_file *ascii)
3218 {
3219   if (!n.is_zero())
3220     ascii->outc(' ');
3221 }
3222
3223 void hmotion_node::ascii_print(ascii_output_file *ascii)
3224 {
3225   // this is pretty arbitrary
3226   if (n >= points_to_units(2))
3227     ascii->outc(' ');
3228 }
3229
3230 void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
3231 {
3232   ascii->outc(' ');
3233 }
3234
3235 /* asciify methods */
3236
3237 void node::asciify(macro *m)
3238 {
3239   m->append(this);
3240 }
3241
3242 void glyph_node::asciify(macro *m)
3243 {
3244   unsigned char c = ci->get_asciify_code();
3245   if (c == 0)
3246     c = ci->get_ascii_code();
3247   if (c != 0) {
3248     m->append(c);
3249     delete this;
3250   }
3251   else
3252     m->append(this);
3253 }
3254
3255 void kern_pair_node::asciify(macro *m)
3256 {
3257   n1->asciify(m);
3258   n2->asciify(m);
3259   n1 = n2 = 0;
3260   delete this;
3261 }
3262
3263 static void asciify_reverse_node_list(macro *m, node *n)
3264 {
3265   if (n == 0)
3266     return;
3267   asciify_reverse_node_list(m, n->next);
3268   n->asciify(m);
3269 }
3270
3271 void dbreak_node::asciify(macro *m)
3272 {
3273   asciify_reverse_node_list(m, none);
3274   none = 0;
3275   delete this;
3276 }
3277
3278 void ligature_node::asciify(macro *m)
3279 {
3280   n1->asciify(m);
3281   n2->asciify(m);
3282   n1 = n2 = 0;
3283   delete this;
3284 }
3285
3286 void break_char_node::asciify(macro *m)
3287 {
3288   ch->asciify(m);
3289   ch = 0;
3290   delete this;
3291 }
3292
3293 void italic_corrected_node::asciify(macro *m)
3294 {
3295   n->asciify(m);
3296   n = 0;
3297   delete this;
3298 }
3299
3300 void left_italic_corrected_node::asciify(macro *m)
3301 {
3302   if (n) {
3303     n->asciify(m);
3304     n = 0;
3305   }
3306   delete this;
3307 }
3308
3309 void hmotion_node::asciify(macro *m)
3310 {
3311   if (was_tab) {
3312     m->append('\t');
3313     delete this;
3314   }
3315   else
3316     m->append(this);
3317 }
3318
3319 space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3320                                                  node *next)
3321 : hmotion_node(i, c, next)
3322 {
3323 }
3324
3325 void space_char_hmotion_node::asciify(macro *m)
3326 {
3327   m->append(ESCAPE_SPACE);
3328   delete this;
3329 }
3330
3331 void space_node::asciify(macro *m)
3332 {
3333   if (was_escape_colon) {
3334     m->append(ESCAPE_COLON);
3335     delete this;
3336   }
3337   else
3338     m->append(this);
3339 }
3340
3341 void word_space_node::asciify(macro *m)
3342 {
3343   for (width_list *w = orig_width; w; w = w->next)
3344     m->append(' ');
3345   delete this;
3346 }
3347
3348 void unbreakable_space_node::asciify(macro *m)
3349 {
3350   m->append(ESCAPE_TILDE);
3351   delete this;
3352 }
3353
3354 void line_start_node::asciify(macro *)
3355 {
3356   delete this;
3357 }
3358
3359 void vertical_size_node::asciify(macro *)
3360 {
3361   delete this;
3362 }
3363
3364 breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/,
3365                                   breakpoint *rest, int /*is_inner*/)
3366 {
3367   return rest;
3368 }
3369
3370 int node::nbreaks()
3371 {
3372   return 0;
3373 }
3374
3375 breakpoint *space_node::get_breakpoints(hunits width, int ns,
3376                                         breakpoint *rest, int is_inner)
3377 {
3378   if (next->discardable())
3379     return rest;
3380   breakpoint *bp = new breakpoint;
3381   bp->next = rest;
3382   bp->width = width;
3383   bp->nspaces = ns;
3384   bp->hyphenated = 0;
3385   if (is_inner) {
3386     assert(rest != 0);
3387     bp->index = rest->index + 1;
3388     bp->nd = rest->nd;
3389   }
3390   else {
3391     bp->nd = this;
3392     bp->index = 0;
3393   }
3394   return bp;
3395 }
3396
3397 int space_node::nbreaks()
3398 {
3399   if (next->discardable())
3400     return 0;
3401   else
3402     return 1;
3403 }
3404
3405 static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
3406                                              int ns, breakpoint *rest)
3407 {
3408   if (p != 0) {
3409     rest = p->get_breakpoints(*widthp,
3410                               ns,
3411                               node_list_get_breakpoints(p->next, widthp, ns,
3412                                                         rest),
3413                               1);
3414     *widthp += p->width();
3415   }
3416   return rest;
3417 }
3418
3419 breakpoint *dbreak_node::get_breakpoints(hunits width, int ns,
3420                                          breakpoint *rest, int is_inner)
3421 {
3422   breakpoint *bp = new breakpoint;
3423   bp->next = rest;
3424   bp->width = width;
3425   for (node *tem = pre; tem != 0; tem = tem->next)
3426     bp->width += tem->width();
3427   bp->nspaces = ns;
3428   bp->hyphenated = 1;
3429   if (is_inner) {
3430     assert(rest != 0);
3431     bp->index = rest->index + 1;
3432     bp->nd = rest->nd;
3433   }
3434   else {
3435     bp->nd = this;
3436     bp->index = 0;
3437   }
3438   return node_list_get_breakpoints(none, &width, ns, bp);
3439 }
3440
3441 int dbreak_node::nbreaks()
3442 {
3443   int i = 1;
3444   for (node *tem = none; tem != 0; tem = tem->next)
3445     i += tem->nbreaks();
3446   return i;
3447 }
3448
3449 void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
3450 {
3451   assert(0);
3452 }
3453
3454 void space_node::split(int where, node **pre, node **post)
3455 {
3456   assert(where == 0);
3457   *pre = next;
3458   *post = 0;
3459   delete this;
3460 }
3461
3462 static void node_list_split(node *p, int *wherep, node **prep, node **postp)
3463 {
3464   if (p == 0)
3465     return;
3466   int nb = p->nbreaks();
3467   node_list_split(p->next, wherep, prep, postp);
3468   if (*wherep < 0) {
3469     p->next = *postp;
3470     *postp = p;
3471   }
3472   else if (*wherep < nb) {
3473     p->next = *prep;
3474     p->split(*wherep, prep, postp);
3475   }
3476   else {
3477     p->next = *prep;
3478     *prep = p;
3479   }
3480   *wherep -= nb;
3481 }
3482
3483 void dbreak_node::split(int where, node **prep, node **postp)
3484 {
3485   assert(where >= 0);
3486   if (where == 0) {
3487     *postp = post;
3488     post = 0;
3489     if (pre == 0)
3490       *prep = next;
3491     else {
3492       node *tem;
3493       for (tem = pre; tem->next != 0; tem = tem->next)
3494         ;
3495       tem->next = next;
3496       *prep = pre;
3497     }
3498     pre = 0;
3499     delete this;
3500   }
3501   else {
3502     *prep = next;
3503     where -= 1;
3504     node_list_split(none, &where, prep, postp);
3505     none = 0;
3506     delete this;
3507   }
3508 }
3509
3510 hyphenation_type node::get_hyphenation_type()
3511 {
3512   return HYPHEN_BOUNDARY;
3513 }
3514
3515 hyphenation_type dbreak_node::get_hyphenation_type()
3516 {
3517   return HYPHEN_INHIBIT;
3518 }
3519
3520 hyphenation_type kern_pair_node::get_hyphenation_type()
3521 {
3522   return HYPHEN_MIDDLE;
3523 }
3524
3525 hyphenation_type dummy_node::get_hyphenation_type()
3526 {
3527   return HYPHEN_MIDDLE;
3528 }
3529
3530 hyphenation_type transparent_dummy_node::get_hyphenation_type()
3531 {
3532   return HYPHEN_MIDDLE;
3533 }
3534
3535 hyphenation_type hmotion_node::get_hyphenation_type()
3536 {
3537   return HYPHEN_MIDDLE;
3538 }
3539
3540 hyphenation_type space_char_hmotion_node::get_hyphenation_type()
3541 {
3542   return HYPHEN_MIDDLE;
3543 }
3544
3545 hyphenation_type overstrike_node::get_hyphenation_type()
3546 {
3547   return HYPHEN_MIDDLE;
3548 }
3549
3550 hyphenation_type space_node::get_hyphenation_type()
3551 {
3552   if (was_escape_colon)
3553     return HYPHEN_MIDDLE;
3554   return HYPHEN_BOUNDARY;
3555 }
3556
3557 hyphenation_type unbreakable_space_node::get_hyphenation_type()
3558 {
3559   return HYPHEN_MIDDLE;
3560 }
3561
3562 int node::interpret(macro *)
3563 {
3564   return 0;
3565 }
3566
3567 special_node::special_node(const macro &m, int n)
3568 : mac(m), no_init_string(n)
3569 {
3570   font_size fs = curenv->get_font_size();
3571   int char_height = curenv->get_char_height();
3572   int char_slant = curenv->get_char_slant();
3573   int fontno = env_definite_font(curenv);
3574   tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fontno);
3575   if (curenv->is_composite())
3576     tf = tf->get_plain();
3577   gcol = curenv->get_glyph_color();
3578   fcol = curenv->get_fill_color();
3579 }
3580
3581 special_node::special_node(const macro &m, tfont *t,
3582                            color *gc, color *fc, int n)
3583 : mac(m), tf(t), gcol(gc), fcol(fc), no_init_string(n)
3584 {
3585 }
3586
3587 int special_node::same(node *n)
3588 {
3589   return mac == ((special_node *)n)->mac
3590          && tf == ((special_node *)n)->tf
3591          && gcol == ((special_node *)n)->gcol
3592          && fcol == ((special_node *)n)->fcol
3593          && no_init_string == ((special_node *)n)->no_init_string;
3594 }
3595
3596 const char *special_node::type()
3597 {
3598   return "special_node";
3599 }
3600
3601 int special_node::ends_sentence()
3602 {
3603   return 2;
3604 }
3605
3606 int special_node::force_tprint()
3607 {
3608   return 0;
3609 }
3610
3611 node *special_node::copy()
3612 {
3613   return new special_node(mac, tf, gcol, fcol, no_init_string);
3614 }
3615
3616 void special_node::tprint_start(troff_output_file *out)
3617 {
3618   out->start_special(tf, gcol, fcol, no_init_string);
3619 }
3620
3621 void special_node::tprint_char(troff_output_file *out, unsigned char c)
3622 {
3623   out->special_char(c);
3624 }
3625
3626 void special_node::tprint_end(troff_output_file *out)
3627 {
3628   out->end_special();
3629 }
3630
3631 tfont *special_node::get_tfont()
3632 {
3633   return tf;
3634 }
3635
3636 /* suppress_node */
3637
3638 suppress_node::suppress_node(int on_or_off, int issue_limits)
3639 : is_on(on_or_off), emit_limits(issue_limits),
3640   filename(0), position(0), image_id(0)
3641 {
3642 }
3643
3644 suppress_node::suppress_node(symbol f, char p, int id)
3645 : is_on(2), emit_limits(0), filename(f), position(p), image_id(id)
3646 {
3647 }
3648
3649 suppress_node::suppress_node(int issue_limits, int on_or_off,
3650                              symbol f, char p, int id)
3651 : is_on(on_or_off), emit_limits(issue_limits),
3652   filename(f), position(p), image_id(id)
3653 {
3654 }
3655
3656 int suppress_node::same(node *n)
3657 {
3658   return ((is_on == ((suppress_node *)n)->is_on)
3659           && (emit_limits == ((suppress_node *)n)->emit_limits)
3660           && (filename == ((suppress_node *)n)->filename)
3661           && (position == ((suppress_node *)n)->position)
3662           && (image_id == ((suppress_node *)n)->image_id));
3663 }
3664
3665 const char *suppress_node::type()
3666 {
3667   return "suppress_node";
3668 }
3669
3670 node *suppress_node::copy()
3671 {
3672   return new suppress_node(emit_limits, is_on, filename, position, image_id);
3673 }
3674
3675 int get_reg_int(const char *p)
3676 {
3677   reg *r = (reg *)number_reg_dictionary.lookup(p);
3678   units prev_value;
3679   if (r && (r->get_value(&prev_value)))
3680     return (int)prev_value;
3681   else
3682     warning(WARN_REG, "number register `%1' not defined", p);
3683   return 0;
3684 }
3685
3686 const char *get_reg_str(const char *p)
3687 {
3688   reg *r = (reg *)number_reg_dictionary.lookup(p);
3689   if (r)
3690     return r->get_string();
3691   else
3692     warning(WARN_REG, "register `%1' not defined", p);
3693   return 0;
3694 }
3695
3696 void suppress_node::put(troff_output_file *out, const char *s)
3697 {
3698   int i = 0;
3699   while (s[i] != (char)0) {
3700     out->special_char(s[i]);
3701     i++;
3702   }
3703 }
3704
3705 /*
3706  *  We need to remember the start of the image and its name.
3707  */
3708
3709 static char last_position = 0;
3710 static const char *last_image_filename = 0;
3711 static int last_image_id = 0;
3712
3713 inline int min(int a, int b)
3714 {
3715   return a < b ? a : b;
3716 }
3717
3718 /*
3719  *  tprint - if (is_on == 2)
3720  *               remember current position (l, r, c, i) and filename
3721  *           else
3722  *               if (emit_limits)
3723  *                   if (html)
3724  *                      emit image tag
3725  *                   else
3726  *                      emit postscript bounds for image
3727  *               else
3728  *                  if (suppress boolean differs from current state)
3729  *                      alter state
3730  *                  reset registers
3731  *                  record current page
3732  *                  set low water mark.
3733  */
3734
3735 void suppress_node::tprint(troff_output_file *out)
3736 {
3737   int current_page = topdiv->get_page_number();
3738   // firstly check to see whether this suppress node contains
3739   // an image filename & position.
3740   if (is_on == 2) {
3741     // remember position and filename
3742     last_position = position;
3743     last_image_filename = strdup(filename.contents());
3744     last_image_id = image_id;
3745     // printf("start of image and page = %d\n", current_page);
3746   }
3747   else {
3748     // now check whether the suppress node requires us to issue limits.
3749     if (emit_limits) {
3750       char name[8192];
3751       // remember that the filename will contain a %d in which the
3752       // last_image_id is placed
3753       sprintf(name, last_image_filename, last_image_id);
3754       if (is_html) {
3755         switch (last_position) {
3756         case 'c':
3757           out->start_special();
3758           put(out, "html-tag:.centered-image");
3759           break;
3760         case 'r':
3761           out->start_special();
3762           put(out, "html-tag:.right-image");
3763           break;
3764         case 'l':
3765           out->start_special();
3766           put(out, "html-tag:.left-image");
3767           break;
3768         case 'i':
3769           ;
3770         default:
3771           ;
3772         }
3773         out->end_special();
3774         out->start_special();
3775         put(out, "html-tag:.auto-image ");
3776         put(out, name);
3777         out->end_special();
3778       }
3779       else {
3780         // postscript (or other device)
3781         if (suppress_start_page > 0 && current_page != suppress_start_page)
3782           error("suppression limit registers span more than one page;\n"
3783                 "image description %1 will be wrong", image_no);
3784         // if (topdiv->get_page_number() != suppress_start_page)
3785         //  fprintf(stderr, "end of image and topdiv page = %d   and  suppress_start_page = %d\n",
3786         //        topdiv->get_page_number(), suppress_start_page);
3787
3788         // remember that the filename will contain a %d in which the
3789         // image_no is placed
3790         fprintf(stderr,
3791                 "grohtml-info:page %d  %d  %d  %d  %d  %d  %s  %d  %d  %s\n",
3792                 topdiv->get_page_number(),
3793                 get_reg_int("opminx"), get_reg_int("opminy"),
3794                 get_reg_int("opmaxx"), get_reg_int("opmaxy"),
3795                 // page offset + line length
3796                 get_reg_int(".o") + get_reg_int(".l"),
3797                 name, hresolution, vresolution, get_reg_str(".F"));
3798         fflush(stderr);
3799       }
3800     }
3801     else {
3802       if (is_on) {
3803         out->on();
3804         // lastly we reset the output registers
3805         reset_output_registers(out->get_vpos());
3806       }
3807       else
3808         out->off();
3809       suppress_start_page = current_page;
3810     }
3811   }
3812 }
3813
3814 int suppress_node::force_tprint()
3815 {
3816   return is_on;
3817 }
3818
3819 hunits suppress_node::width()
3820 {
3821   return H0;
3822 }
3823
3824 /* composite_node */
3825
3826 class composite_node : public charinfo_node {
3827   node *n;
3828   tfont *tf;
3829 public:
3830   composite_node(node *, charinfo *, tfont *, node * = 0);
3831   ~composite_node();
3832   node *copy();
3833   hunits width();
3834   node *last_char_node();
3835   units size();
3836   void tprint(troff_output_file *);
3837   hyphenation_type get_hyphenation_type();
3838   void ascii_print(ascii_output_file *);
3839   void asciify(macro *);
3840   hyphen_list *get_hyphen_list(hyphen_list *tail);
3841   node *add_self(node *, hyphen_list **);
3842   tfont *get_tfont();
3843   int same(node *);
3844   const char *type();
3845   int force_tprint();
3846   void vertical_extent(vunits *, vunits *);
3847   vunits vertical_width();
3848 };
3849
3850 composite_node::composite_node(node *p, charinfo *c, tfont *t, node *x)
3851 : charinfo_node(c, x), n(p), tf(t)
3852 {
3853 }
3854
3855 composite_node::~composite_node()
3856 {
3857   delete_node_list(n);
3858 }
3859
3860 node *composite_node::copy()
3861 {
3862   return new composite_node(copy_node_list(n), ci, tf);
3863 }
3864
3865 hunits composite_node::width()
3866 {
3867   hunits x;
3868   if (tf->get_constant_space(&x))
3869     return x;
3870   x = H0;
3871   for (node *tem = n; tem; tem = tem->next)
3872     x += tem->width();
3873   hunits offset;
3874   if (tf->get_bold(&offset))
3875     x += offset;
3876   x += tf->get_track_kern();
3877   return x;
3878 }
3879
3880 node *composite_node::last_char_node()
3881 {
3882   return this;
3883 }
3884
3885 vunits composite_node::vertical_width()
3886 {
3887   vunits v = V0;
3888   for (node *tem = n; tem; tem = tem->next)
3889     v += tem->vertical_width();
3890   return v;
3891 }
3892
3893 units composite_node::size()
3894 {
3895   return tf->get_size().to_units();
3896 }
3897
3898 hyphenation_type composite_node::get_hyphenation_type()
3899 {
3900   return HYPHEN_MIDDLE;
3901 }
3902
3903 void composite_node::asciify(macro *m)
3904 {
3905   unsigned char c = ci->get_asciify_code();
3906   if (c == 0)
3907     c = ci->get_ascii_code();
3908   if (c != 0) {
3909     m->append(c);
3910     delete this;
3911   }
3912   else
3913     m->append(this);
3914 }
3915
3916 void composite_node::ascii_print(ascii_output_file *ascii)
3917 {
3918   unsigned char c = ci->get_ascii_code();
3919   if (c != 0)
3920     ascii->outc(c);
3921   else
3922     ascii->outs(ci->nm.contents());
3923
3924 }
3925
3926 hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail)
3927 {
3928   return new hyphen_list(ci->get_hyphenation_code(), tail);
3929 }
3930
3931 node *composite_node::add_self(node *nn, hyphen_list **p)
3932 {
3933   assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
3934   next = nn;
3935   nn = this;
3936   if ((*p)->hyphen)
3937     nn = nn->add_discretionary_hyphen();
3938   hyphen_list *pp = *p;
3939   *p = (*p)->next;
3940   delete pp;
3941   return nn;
3942 }
3943
3944 tfont *composite_node::get_tfont()
3945 {
3946   return tf;
3947 }
3948
3949 node *reverse_node_list(node *n)
3950 {
3951   node *r = 0;
3952   while (n) {
3953     node *tem = n;
3954     n = n->next;
3955     tem->next = r;
3956     r = tem;
3957   }
3958   return r;
3959 }
3960
3961 void composite_node::vertical_extent(vunits *min, vunits *max)
3962 {
3963   n = reverse_node_list(n);
3964   node_list_vertical_extent(n, min, max);
3965   n = reverse_node_list(n);
3966 }
3967
3968 width_list::width_list(hunits w, hunits s)
3969 : width(w), sentence_width(s), next(0)
3970 {
3971 }
3972
3973 width_list::width_list(width_list *w)
3974 : width(w->width), sentence_width(w->sentence_width), next(0)
3975 {
3976 }
3977
3978 word_space_node::word_space_node(hunits d, color *c, width_list *w, node *x)
3979 : space_node(d, c, x), orig_width(w), unformat(0)
3980 {
3981 }
3982
3983 word_space_node::word_space_node(hunits d, int s, color *c, width_list *w,
3984                                  int flag, node *x)
3985 : space_node(d, s, 0, c, x), orig_width(w), unformat(flag)
3986 {
3987 }
3988
3989 word_space_node::~word_space_node()
3990 {
3991   width_list *w = orig_width;
3992   while (w != 0) {
3993     width_list *tmp = w;
3994     w = w->next;
3995     delete tmp;
3996   }
3997 }
3998
3999 node *word_space_node::copy()
4000 {
4001   assert(orig_width != 0);
4002   width_list *w_old_curr = orig_width;
4003   width_list *w_new_curr = new width_list(w_old_curr);
4004   width_list *w_new = w_new_curr;
4005   w_old_curr = w_old_curr->next;
4006   while (w_old_curr != 0) {
4007     w_new_curr->next = new width_list(w_old_curr);
4008     w_new_curr = w_new_curr->next;
4009     w_old_curr = w_old_curr->next;
4010   }
4011   return new word_space_node(n, set, col, w_new, unformat);
4012 }
4013
4014 int word_space_node::set_unformat_flag()
4015 {
4016   unformat = 1;
4017   return 1;
4018 }
4019
4020 void word_space_node::tprint(troff_output_file *out)
4021 {
4022   out->fill_color(col);
4023   out->word_marker();
4024   out->right(n);
4025 }
4026
4027 int word_space_node::merge_space(hunits h, hunits sw, hunits ssw)
4028 {
4029   n += h;
4030   assert(orig_width != 0);
4031   width_list *w = orig_width; 
4032   for (; w->next; w = w->next)
4033     ;
4034   w->next = new width_list(sw, ssw);
4035   return 1;
4036 }
4037
4038 unbreakable_space_node::unbreakable_space_node(hunits d, color *c, node *x)
4039 : word_space_node(d, c, 0, x)
4040 {
4041 }
4042
4043 unbreakable_space_node::unbreakable_space_node(hunits d, int s,
4044                                                color *c, node *x)
4045 : word_space_node(d, s, c, 0, 0, x)
4046 {
4047 }
4048
4049 node *unbreakable_space_node::copy()
4050 {
4051   return new unbreakable_space_node(n, set, col);
4052 }
4053
4054 int unbreakable_space_node::force_tprint()
4055 {
4056   return 0;
4057 }
4058
4059 breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
4060                                                     breakpoint *rest, int)
4061 {
4062   return rest;
4063 }
4064
4065 int unbreakable_space_node::nbreaks()
4066 {
4067   return 0;
4068 }
4069
4070 void unbreakable_space_node::split(int, node **, node **)
4071 {
4072   assert(0);
4073 }
4074
4075 int unbreakable_space_node::merge_space(hunits, hunits, hunits)
4076 {
4077   return 0;
4078 }
4079
4080 hvpair::hvpair()
4081 {
4082 }
4083
4084 draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4085                      color *gc, color *fc)
4086 : npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4087 {
4088   point = new hvpair[npoints];
4089   for (int i = 0; i < npoints; i++)
4090     point[i] = p[i];
4091 }
4092
4093 int draw_node::same(node *n)
4094 {
4095   draw_node *nd = (draw_node *)n;
4096   if (code != nd->code || npoints != nd->npoints || sz != nd->sz
4097       || gcol != nd->gcol || fcol != nd->fcol)
4098     return 0;
4099   for (int i = 0; i < npoints; i++)
4100     if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
4101       return 0;
4102   return 1;
4103 }
4104
4105 const char *draw_node::type()
4106 {
4107   return "draw_node";
4108 }
4109
4110 int draw_node::force_tprint()
4111 {
4112   return 0;
4113 }
4114
4115 draw_node::~draw_node()
4116 {
4117   if (point)
4118     a_delete point;
4119 }
4120
4121 hunits draw_node::width()
4122 {
4123   hunits x = H0;
4124   for (int i = 0; i < npoints; i++)
4125     x += point[i].h;
4126   return x;
4127 }
4128
4129 vunits draw_node::vertical_width()
4130 {
4131   if (code == 'e')
4132     return V0;
4133   vunits x = V0;
4134   for (int i = 0; i < npoints; i++)
4135     x += point[i].v;
4136   return x;
4137 }
4138
4139 node *draw_node::copy()
4140 {
4141   return new draw_node(code, point, npoints, sz, gcol, fcol);
4142 }
4143
4144 void draw_node::tprint(troff_output_file *out)
4145 {
4146   out->draw(code, point, npoints, sz, gcol, fcol);
4147 }
4148
4149 /* tprint methods */
4150
4151 void glyph_node::tprint(troff_output_file *out)
4152 {
4153   tfont *ptf = tf->get_plain();
4154   if (ptf == tf)
4155     out->put_char_width(ci, ptf, gcol, fcol, width(), H0);
4156   else {
4157     hunits offset;
4158     int bold = tf->get_bold(&offset);
4159     hunits w = ptf->get_width(ci);
4160     hunits k = H0;
4161     hunits x;
4162     int cs = tf->get_constant_space(&x);
4163     if (cs) {
4164       x -= w;
4165       if (bold)
4166         x -= offset;
4167       hunits x2 = x/2;
4168       out->right(x2);
4169       k = x - x2;
4170     }
4171     else
4172       k = tf->get_track_kern();
4173     if (bold) {
4174       out->put_char(ci, ptf, gcol, fcol);
4175       out->right(offset);
4176     }
4177     out->put_char_width(ci, ptf, gcol, fcol, w, k);
4178   }
4179 }
4180
4181 void glyph_node::zero_width_tprint(troff_output_file *out)
4182 {
4183   tfont *ptf = tf->get_plain();
4184   hunits offset;
4185   int bold = tf->get_bold(&offset);
4186   hunits x;
4187   int cs = tf->get_constant_space(&x);
4188   if (cs) {
4189     x -= ptf->get_width(ci);
4190     if (bold)
4191       x -= offset;
4192     x = x/2;
4193     out->right(x);
4194   }
4195   out->put_char(ci, ptf, gcol, fcol);
4196   if (bold) {
4197     out->right(offset);
4198     out->put_char(ci, ptf, gcol, fcol);
4199     out->right(-offset);
4200   }
4201   if (cs)
4202     out->right(-x);
4203 }
4204
4205 void break_char_node::tprint(troff_output_file *t)
4206 {
4207   ch->tprint(t);
4208 }
4209
4210 void break_char_node::zero_width_tprint(troff_output_file *t)
4211 {
4212   ch->zero_width_tprint(t);
4213 }
4214
4215 void hline_node::tprint(troff_output_file *out)
4216 {
4217   if (x < H0) {
4218     out->right(x);
4219     x = -x;
4220   }
4221   if (n == 0) {
4222     out->right(x);
4223     return;
4224   }
4225   hunits w = n->width();
4226   if (w <= H0) {
4227     error("horizontal line drawing character must have positive width");
4228     out->right(x);
4229     return;
4230   }
4231   int i = int(x/w);
4232   if (i == 0) {
4233     hunits xx = x - w;
4234     hunits xx2 = xx/2;
4235     out->right(xx2);
4236     if (out->is_on())
4237       n->tprint(out);
4238     out->right(xx - xx2);
4239   }
4240   else {
4241     hunits rem = x - w*i;
4242     if (rem > H0)
4243       if (n->overlaps_horizontally()) {
4244         if (out->is_on())
4245           n->tprint(out);
4246         out->right(rem - w);
4247       }
4248       else
4249         out->right(rem);
4250     while (--i >= 0)
4251       if (out->is_on())
4252         n->tprint(out);
4253   }
4254 }
4255
4256 void vline_node::tprint(troff_output_file *out)
4257 {
4258   if (n == 0) {
4259     out->down(x);
4260     return;
4261   }
4262   vunits h = n->size();
4263   int overlaps = n->overlaps_vertically();
4264   vunits y = x;
4265   if (y < V0) {
4266     y = -y;
4267     int i = y / h;
4268     vunits rem = y - i*h;
4269     if (i == 0) {
4270       out->right(n->width());
4271       out->down(-rem);
4272     }
4273     else {
4274       while (--i > 0) {
4275         n->zero_width_tprint(out);
4276         out->down(-h);
4277       }
4278       if (overlaps) {
4279         n->zero_width_tprint(out);
4280         out->down(-rem);
4281         if (out->is_on())
4282           n->tprint(out);
4283         out->down(-h);
4284       }
4285       else {
4286         if (out->is_on())
4287           n->tprint(out);
4288         out->down(-h - rem);
4289       }
4290     }
4291   }
4292   else {
4293     int i = y / h;
4294     vunits rem = y - i*h;
4295     if (i == 0) {
4296       out->down(rem);
4297       out->right(n->width());
4298     }
4299     else {
4300       out->down(h);
4301       if (overlaps)
4302         n->zero_width_tprint(out);
4303       out->down(rem);
4304       while (--i > 0) {
4305         n->zero_width_tprint(out);
4306         out->down(h);
4307       }
4308       if (out->is_on())
4309         n->tprint(out);
4310     }
4311   }
4312 }
4313
4314 void zero_width_node::tprint(troff_output_file *out)
4315 {
4316   if (!n)
4317     return;
4318   if (!n->next) {
4319     n->zero_width_tprint(out);
4320     return;
4321   }
4322   int hpos = out->get_hpos();
4323   int vpos = out->get_vpos();
4324   node *tem = n;
4325   while (tem) {
4326     tem->tprint(out);
4327     tem = tem->next;
4328   }
4329   out->moveto(hpos, vpos);
4330 }
4331
4332 void overstrike_node::tprint(troff_output_file *out)
4333 {
4334   hunits pos = H0;
4335   for (node *tem = list; tem; tem = tem->next) {
4336     hunits x = (max_width - tem->width())/2;
4337     out->right(x - pos);
4338     pos = x;
4339     tem->zero_width_tprint(out);
4340   }
4341   out->right(max_width - pos);
4342 }
4343
4344 void bracket_node::tprint(troff_output_file *out)
4345 {
4346   if (list == 0)
4347     return;
4348   int npieces = 0;
4349   node *tem;
4350   for (tem = list; tem; tem = tem->next)
4351     ++npieces;
4352   vunits h = list->size();
4353   vunits totalh = h*npieces;
4354   vunits y = (totalh - h)/2;
4355   out->down(y);
4356   for (tem = list; tem; tem = tem->next) {
4357     tem->zero_width_tprint(out);
4358     out->down(-h);
4359   }
4360   out->right(max_width);
4361   out->down(totalh - y);
4362 }
4363
4364 void node::tprint(troff_output_file *)
4365 {
4366 }
4367
4368 void node::zero_width_tprint(troff_output_file *out)
4369 {
4370   int hpos = out->get_hpos();
4371   int vpos = out->get_vpos();
4372   tprint(out);
4373   out->moveto(hpos, vpos);
4374 }
4375
4376 void space_node::tprint(troff_output_file *out)
4377 {
4378   out->fill_color(col);
4379   out->right(n);
4380 }
4381
4382 void hmotion_node::tprint(troff_output_file *out)
4383 {
4384   out->fill_color(col);
4385   out->right(n);
4386 }
4387
4388 void vmotion_node::tprint(troff_output_file *out)
4389 {
4390   out->fill_color(col);
4391   out->down(n);
4392 }
4393
4394 void kern_pair_node::tprint(troff_output_file *out)
4395 {
4396   n1->tprint(out);
4397   out->right(amount);
4398   n2->tprint(out);
4399 }
4400
4401 static void tprint_reverse_node_list(troff_output_file *out, node *n)
4402 {
4403   if (n == 0)
4404     return;
4405   tprint_reverse_node_list(out, n->next);
4406   n->tprint(out);
4407 }
4408
4409 void dbreak_node::tprint(troff_output_file *out)
4410 {
4411   tprint_reverse_node_list(out, none);
4412 }
4413
4414 void composite_node::tprint(troff_output_file *out)
4415 {
4416   hunits bold_offset;
4417   int is_bold = tf->get_bold(&bold_offset);
4418   hunits track_kern = tf->get_track_kern();
4419   hunits constant_space;
4420   int is_constant_spaced = tf->get_constant_space(&constant_space);
4421   hunits x = H0;
4422   if (is_constant_spaced) {
4423     x = constant_space;
4424     for (node *tem = n; tem; tem = tem->next)
4425       x -= tem->width();
4426     if (is_bold)
4427       x -= bold_offset;
4428     hunits x2 = x/2;
4429     out->right(x2);
4430     x -= x2;
4431   }
4432   if (is_bold) {
4433     int hpos = out->get_hpos();
4434     int vpos = out->get_vpos();
4435     tprint_reverse_node_list(out, n);
4436     out->moveto(hpos, vpos);
4437     out->right(bold_offset);
4438   }
4439   tprint_reverse_node_list(out, n);
4440   if (is_constant_spaced)
4441     out->right(x);
4442   else
4443     out->right(track_kern);
4444 }
4445
4446 node *make_composite_node(charinfo *s, environment *env)
4447 {
4448   int fontno = env_definite_font(env);
4449   if (fontno < 0) {
4450     error("no current font");
4451     return 0;
4452   }
4453   assert(fontno < font_table_size && font_table[fontno] != 0);
4454   node *n = charinfo_to_node_list(s, env);
4455   font_size fs = env->get_font_size();
4456   int char_height = env->get_char_height();
4457   int char_slant = env->get_char_slant();
4458   tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
4459                                             fontno);
4460   if (env->is_composite())
4461     tf = tf->get_plain();
4462   return new composite_node(n, s, tf);
4463 }
4464
4465 node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0)
4466 {
4467   int fontno = env_definite_font(env);
4468   if (fontno < 0) {
4469     error("no current font");
4470     return 0;
4471   }
4472   assert(fontno < font_table_size && font_table[fontno] != 0);
4473   int fn = fontno;
4474   int found = font_table[fontno]->contains(s);
4475   if (!found) {
4476     if (s->is_fallback())
4477       return make_composite_node(s, env);
4478     if (s->numbered()) {
4479       if (!no_error_message)
4480         warning(WARN_CHAR, "can't find numbered character %1",
4481                 s->get_number());
4482       return 0;
4483     }
4484     special_font_list *sf = font_table[fontno]->sf;
4485     while (sf != 0 && !found) {
4486       fn = sf->n;
4487       if (font_table[fn])
4488         found = font_table[fn]->contains(s);
4489       sf = sf->next;
4490     }
4491     if (!found) {
4492       sf = global_special_fonts;
4493       while (sf != 0 && !found) {
4494         fn = sf->n;
4495         if (font_table[fn])
4496           found = font_table[fn]->contains(s);
4497         sf = sf->next;
4498       }
4499     }
4500     if (!found
4501 #if 0   
4502         && global_special_fonts == 0 && font_table[fontno]->sf == 0
4503 #endif
4504         ) {
4505       for (fn = 0; fn < font_table_size; fn++)
4506         if (font_table[fn]
4507             && font_table[fn]->is_special()
4508             && font_table[fn]->contains(s)) {
4509               found = 1;
4510               break;
4511             }
4512     }
4513     if (!found) {
4514       if (!no_error_message && s->first_time_not_found()) {
4515         unsigned char input_code = s->get_ascii_code();
4516         if (input_code != 0) {
4517           if (csgraph(input_code))
4518             warning(WARN_CHAR, "can't find character `%1'", input_code);
4519           else
4520             warning(WARN_CHAR, "can't find character with input code %1",
4521                     int(input_code));
4522         }
4523         else if (s->nm.contents())
4524           warning(WARN_CHAR, "can't find special character `%1'",
4525                   s->nm.contents());
4526       }
4527       return 0;
4528     }
4529   }
4530   font_size fs = env->get_font_size();
4531   int char_height = env->get_char_height();
4532   int char_slant = env->get_char_slant();
4533   tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn);
4534   if (env->is_composite())
4535     tf = tf->get_plain();
4536   color *gcol = env->get_glyph_color();
4537   color *fcol = env->get_fill_color();
4538   return new glyph_node(s, tf, gcol, fcol);
4539 }
4540
4541 node *make_node(charinfo *ci, environment *env)
4542 {
4543   switch (ci->get_special_translation()) {
4544   case charinfo::TRANSLATE_SPACE:
4545     return new space_char_hmotion_node(env->get_space_width(),
4546                                        env->get_fill_color());
4547   case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4548     return new unbreakable_space_node(env->get_space_width(),
4549                                       env->get_fill_color());
4550   case charinfo::TRANSLATE_DUMMY:
4551     return new dummy_node;
4552   case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4553     error("translation to \\% ignored in this context");
4554     break;
4555   }
4556   charinfo *tem = ci->get_translation();
4557   if (tem)
4558     ci = tem;
4559   macro *mac = ci->get_macro();
4560   if (mac && !ci->is_fallback())
4561     return make_composite_node(ci, env);
4562   else
4563     return make_glyph_node(ci, env);
4564 }
4565
4566 int character_exists(charinfo *ci, environment *env)
4567 {
4568   if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
4569     return 1;
4570   charinfo *tem = ci->get_translation();
4571   if (tem)
4572     ci = tem;
4573   if (ci->get_macro())
4574     return 1;
4575   node *nd = make_glyph_node(ci, env, 1);
4576   if (nd) {
4577     delete nd;
4578     return 1;
4579   }
4580   return 0;
4581 }
4582
4583 node *node::add_char(charinfo *ci, environment *env,
4584                      hunits *widthp, int *spacep)
4585 {
4586   node *res;
4587   switch (ci->get_special_translation()) {
4588   case charinfo::TRANSLATE_SPACE:
4589     res = new space_char_hmotion_node(env->get_space_width(),
4590                                       env->get_fill_color(), this);
4591     *widthp += res->width();
4592     return res;
4593   case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4594     res = new unbreakable_space_node(env->get_space_width(),
4595                                      env->get_fill_color(), this);
4596     res->freeze_space();
4597     *widthp += res->width();
4598     *spacep += res->nspaces();
4599     return res;
4600   case charinfo::TRANSLATE_DUMMY:
4601     return new dummy_node(this);
4602   case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4603     return add_discretionary_hyphen();
4604   }
4605   charinfo *tem = ci->get_translation();
4606   if (tem)
4607     ci = tem;
4608   macro *mac = ci->get_macro();
4609   if (mac && !ci->is_fallback()) {
4610     res = make_composite_node(ci, env);
4611     if (res) {
4612       res->next = this;
4613       *widthp += res->width();
4614     }
4615     else
4616       return this;
4617   }
4618   else {
4619     node *gn = make_glyph_node(ci, env);
4620     if (gn == 0)
4621       return this;
4622     else {
4623       hunits old_width = width();
4624       node *p = gn->merge_self(this);
4625       if (p == 0) {
4626         *widthp += gn->width();
4627         gn->next = this;
4628         res = gn;
4629       }
4630       else {
4631         *widthp += p->width() - old_width;
4632         res = p;
4633       }
4634     }
4635   }
4636   int break_code = 0;
4637   if (ci->can_break_before())
4638     break_code = 1;
4639   if (ci->can_break_after())
4640     break_code |= 2;
4641   if (break_code) {
4642     node *next1 = res->next;
4643     res->next = 0;
4644     res = new break_char_node(res, break_code, env->get_fill_color(), next1);
4645   }
4646   return res;
4647 }
4648
4649 #ifdef __GNUG__
4650 inline
4651 #endif
4652 int same_node(node *n1, node *n2)
4653 {
4654   if (n1 != 0) {
4655     if (n2 != 0)
4656       return n1->type() == n2->type() && n1->same(n2);
4657     else
4658       return 0;
4659   }
4660   else
4661     return n2 == 0;
4662 }
4663
4664 int same_node_list(node *n1, node *n2)
4665 {
4666   while (n1 && n2) {
4667     if (n1->type() != n2->type() || !n1->same(n2))
4668       return 0;
4669     n1 = n1->next;
4670     n2 = n2->next;
4671   }
4672   return !n1 && !n2;
4673 }
4674
4675 int extra_size_node::same(node *nd)
4676 {
4677   return n == ((extra_size_node *)nd)->n;
4678 }
4679
4680 const char *extra_size_node::type()
4681 {
4682   return "extra_size_node";
4683 }
4684
4685 int extra_size_node::force_tprint()
4686 {
4687   return 0;
4688 }
4689
4690 int vertical_size_node::same(node *nd)
4691 {
4692   return n == ((vertical_size_node *)nd)->n;
4693 }
4694
4695 const char *vertical_size_node::type()
4696 {
4697   return "vertical_size_node";
4698 }
4699
4700 int vertical_size_node::set_unformat_flag()
4701 {
4702   return 0;
4703 }
4704
4705 int vertical_size_node::force_tprint()
4706 {
4707   return 0;
4708 }
4709
4710 int hmotion_node::same(node *nd)
4711 {
4712   return n == ((hmotion_node *)nd)->n
4713          && col == ((hmotion_node *)nd)->col;
4714 }
4715
4716 const char *hmotion_node::type()
4717 {
4718   return "hmotion_node";
4719 }
4720
4721 int hmotion_node::set_unformat_flag()
4722 {
4723   unformat = 1;
4724   return 1;
4725 }
4726
4727 int hmotion_node::force_tprint()
4728 {
4729   return 0;
4730 }
4731
4732 node *hmotion_node::add_self(node *n, hyphen_list **p)
4733 {
4734   next = n;
4735   hyphen_list *pp = *p;
4736   *p = (*p)->next;
4737   delete pp;
4738   return this;
4739 }
4740
4741 hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail)
4742 {
4743   return new hyphen_list(0, tail);
4744 }
4745
4746 int space_char_hmotion_node::same(node *nd)
4747 {
4748   return n == ((space_char_hmotion_node *)nd)->n
4749          && col == ((space_char_hmotion_node *)nd)->col;
4750 }
4751
4752 const char *space_char_hmotion_node::type()
4753 {
4754   return "space_char_hmotion_node";
4755 }
4756
4757 int space_char_hmotion_node::force_tprint()
4758 {
4759   return 0;
4760 }
4761
4762 node *space_char_hmotion_node::add_self(node *n, hyphen_list **p)
4763 {
4764   next = n;
4765   hyphen_list *pp = *p;
4766   *p = (*p)->next;
4767   delete pp;
4768   return this;
4769 }
4770
4771 hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail)
4772 {
4773   return new hyphen_list(0, tail);
4774 }
4775
4776 int vmotion_node::same(node *nd)
4777 {
4778   return n == ((vmotion_node *)nd)->n
4779          && col == ((vmotion_node *)nd)->col;
4780 }
4781
4782 const char *vmotion_node::type()
4783 {
4784   return "vmotion_node";
4785 }
4786
4787 int vmotion_node::force_tprint()
4788 {
4789   return 0;
4790 }
4791
4792 int hline_node::same(node *nd)
4793 {
4794   return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n);
4795 }
4796
4797 const char *hline_node::type()
4798 {
4799   return "hline_node";
4800 }
4801
4802 int hline_node::force_tprint()
4803 {
4804   return 0;
4805 }
4806
4807 int vline_node::same(node *nd)
4808 {
4809   return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n);
4810 }
4811
4812 const char *vline_node::type()
4813 {
4814   return "vline_node";
4815 }
4816
4817 int vline_node::force_tprint()
4818 {
4819   return 0;
4820 }
4821
4822 int dummy_node::same(node * /*nd*/)
4823 {
4824   return 1;
4825 }
4826
4827 const char *dummy_node::type()
4828 {
4829   return "dummy_node";
4830 }
4831
4832 int dummy_node::force_tprint()
4833 {
4834   return 0;
4835 }
4836
4837 int transparent_dummy_node::same(node * /*nd*/)
4838 {
4839   return 1;
4840 }
4841
4842 const char *transparent_dummy_node::type()
4843 {
4844   return "transparent_dummy_node";
4845 }
4846
4847 int transparent_dummy_node::force_tprint()
4848 {
4849   return 0;
4850 }
4851
4852 int transparent_dummy_node::ends_sentence()
4853 {
4854   return 2;
4855 }
4856
4857 int zero_width_node::same(node *nd)
4858 {
4859   return same_node_list(n, ((zero_width_node *)nd)->n);
4860 }
4861
4862 const char *zero_width_node::type()
4863 {
4864   return "zero_width_node";
4865 }
4866
4867 int zero_width_node::force_tprint()
4868 {
4869   return 0;
4870 }
4871
4872 int italic_corrected_node::same(node *nd)
4873 {
4874   return (x == ((italic_corrected_node *)nd)->x
4875           && same_node(n, ((italic_corrected_node *)nd)->n));
4876 }
4877
4878 const char *italic_corrected_node::type()
4879 {
4880   return "italic_corrected_node";
4881 }
4882
4883 int italic_corrected_node::force_tprint()
4884 {
4885   return 0;
4886 }
4887
4888 left_italic_corrected_node::left_italic_corrected_node(node *x)
4889 : node(x), n(0)
4890 {
4891 }
4892
4893 left_italic_corrected_node::~left_italic_corrected_node()
4894 {
4895   delete n;
4896 }
4897
4898 node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
4899 {
4900   if (n == 0) {
4901     hunits lic = gn->left_italic_correction();
4902     if (!lic.is_zero()) {
4903       x = lic;
4904       n = gn;
4905       return this;
4906     }
4907   }
4908   else {
4909     node *nd = n->merge_glyph_node(gn);
4910     if (nd) {
4911       n = nd;
4912       x = n->left_italic_correction();
4913       return this;
4914     }
4915   }
4916   return 0;
4917 }
4918
4919 node *left_italic_corrected_node::copy()
4920 {
4921   left_italic_corrected_node *nd = new left_italic_corrected_node;
4922   if (n) {
4923     nd->n = n->copy();
4924     nd->x = x;
4925   }
4926   return nd;
4927 }
4928
4929 void left_italic_corrected_node::tprint(troff_output_file *out)
4930 {
4931   if (n) {
4932     out->right(x);
4933     n->tprint(out);
4934   }
4935 }
4936
4937 const char *left_italic_corrected_node::type()
4938 {
4939   return "left_italic_corrected_node";
4940 }
4941
4942 int left_italic_corrected_node::force_tprint()
4943 {
4944   return 0;
4945 }
4946
4947 int left_italic_corrected_node::same(node *nd)
4948 {
4949   return (x == ((left_italic_corrected_node *)nd)->x
4950           && same_node(n, ((left_italic_corrected_node *)nd)->n));
4951 }
4952
4953 void left_italic_corrected_node::ascii_print(ascii_output_file *out)
4954 {
4955   if (n)
4956     n->ascii_print(out);
4957 }
4958
4959 hunits left_italic_corrected_node::width()
4960 {
4961   return n ? n->width() + x : H0;
4962 }
4963
4964 void left_italic_corrected_node::vertical_extent(vunits *min, vunits *max)
4965 {
4966   if (n)
4967     n->vertical_extent(min, max);
4968   else
4969     node::vertical_extent(min, max);
4970 }
4971
4972 hunits left_italic_corrected_node::skew()
4973 {
4974   return n ? n->skew() + x/2 : H0;
4975 }
4976
4977 hunits left_italic_corrected_node::subscript_correction()
4978 {
4979   return n ? n->subscript_correction() : H0;
4980 }
4981
4982 hunits left_italic_corrected_node::italic_correction()
4983 {
4984   return n ? n->italic_correction() : H0;
4985 }
4986
4987 int left_italic_corrected_node::ends_sentence()
4988 {
4989   return n ? n->ends_sentence() : 0;
4990 }
4991
4992 int left_italic_corrected_node::overlaps_horizontally()
4993 {
4994   return n ? n->overlaps_horizontally() : 0;
4995 }
4996
4997 int left_italic_corrected_node::overlaps_vertically()
4998 {
4999   return n ? n->overlaps_vertically() : 0;
5000 }
5001
5002 node *left_italic_corrected_node::last_char_node()
5003 {
5004   return n ? n->last_char_node() : 0;
5005 }
5006
5007 tfont *left_italic_corrected_node::get_tfont()
5008 {
5009   return n ? n->get_tfont() : 0;
5010 }
5011
5012 hyphenation_type left_italic_corrected_node::get_hyphenation_type()
5013 {
5014   if (n)
5015     return n->get_hyphenation_type();
5016   else
5017     return HYPHEN_MIDDLE;
5018 }
5019
5020 hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail)
5021 {
5022   return n ? n->get_hyphen_list(tail) : tail;
5023 }
5024
5025 node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
5026 {
5027   if (n) {
5028     nd = new left_italic_corrected_node(nd);
5029     nd = n->add_self(nd, p);
5030     n = 0;
5031     delete this;
5032   }
5033   return nd;
5034 }
5035
5036 int left_italic_corrected_node::character_type()
5037 {
5038   return n ? n->character_type() : 0;
5039 }
5040
5041 int overstrike_node::same(node *nd)
5042 {
5043   return same_node_list(list, ((overstrike_node *)nd)->list);
5044 }
5045
5046 const char *overstrike_node::type()
5047 {
5048   return "overstrike_node";
5049 }
5050
5051 int overstrike_node::force_tprint()
5052 {
5053   return 0;
5054 }
5055
5056 node *overstrike_node::add_self(node *n, hyphen_list **p)
5057 {
5058   next = n;
5059   hyphen_list *pp = *p;
5060   *p = (*p)->next;
5061   delete pp;
5062   return this;
5063 }
5064
5065 hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail)
5066 {
5067   return new hyphen_list(0, tail);
5068 }
5069
5070 int bracket_node::same(node *nd)
5071 {
5072   return same_node_list(list, ((bracket_node *)nd)->list);
5073 }
5074
5075 const char *bracket_node::type()
5076 {
5077   return "bracket_node";
5078 }
5079
5080 int bracket_node::force_tprint()
5081 {
5082   return 0;
5083 }
5084
5085 int composite_node::same(node *nd)
5086 {
5087   return ci == ((composite_node *)nd)->ci
5088     && same_node_list(n, ((composite_node *)nd)->n);
5089 }
5090
5091 const char *composite_node::type()
5092 {
5093   return "composite_node";
5094 }
5095
5096 int composite_node::force_tprint()
5097 {
5098   return 0;
5099 }
5100
5101 int glyph_node::same(node *nd)
5102 {
5103   return ci == ((glyph_node *)nd)->ci
5104          && tf == ((glyph_node *)nd)->tf
5105          && gcol == ((glyph_node *)nd)->gcol
5106          && fcol == ((glyph_node *)nd)->fcol;
5107 }
5108
5109 const char *glyph_node::type()
5110 {
5111   return "glyph_node";
5112 }
5113
5114 int glyph_node::force_tprint()
5115 {
5116   return 0;
5117 }
5118
5119 int ligature_node::same(node *nd)
5120 {
5121   return (same_node(n1, ((ligature_node *)nd)->n1)
5122           && same_node(n2, ((ligature_node *)nd)->n2)
5123           && glyph_node::same(nd));
5124 }
5125
5126 const char *ligature_node::type()
5127 {
5128   return "ligature_node";
5129 }
5130
5131 int ligature_node::force_tprint()
5132 {
5133   return 0;
5134 }
5135
5136 int kern_pair_node::same(node *nd)
5137 {
5138   return (amount == ((kern_pair_node *)nd)->amount
5139           && same_node(n1, ((kern_pair_node *)nd)->n1)
5140           && same_node(n2, ((kern_pair_node *)nd)->n2));
5141 }
5142
5143 const char *kern_pair_node::type()
5144 {
5145   return "kern_pair_node";
5146 }
5147
5148 int kern_pair_node::force_tprint()
5149 {
5150   return 0;
5151 }
5152
5153 int dbreak_node::same(node *nd)
5154 {
5155   return (same_node_list(none, ((dbreak_node *)nd)->none)
5156           && same_node_list(pre, ((dbreak_node *)nd)->pre)
5157           && same_node_list(post, ((dbreak_node *)nd)->post));
5158 }
5159
5160 const char *dbreak_node::type()
5161 {
5162   return "dbreak_node";
5163 }
5164
5165 int dbreak_node::force_tprint()
5166 {
5167   return 0;
5168 }
5169
5170 int break_char_node::same(node *nd)
5171 {
5172   return break_code == ((break_char_node *)nd)->break_code
5173          && col == ((break_char_node *)nd)->col
5174          && same_node(ch, ((break_char_node *)nd)->ch);
5175 }
5176
5177 const char *break_char_node::type()
5178 {
5179   return "break_char_node";
5180 }
5181
5182 int break_char_node::force_tprint()
5183 {
5184   return 0;
5185 }
5186
5187 int line_start_node::same(node * /*nd*/)
5188 {
5189   return 1;
5190 }
5191
5192 const char *line_start_node::type()
5193 {
5194   return "line_start_node";
5195 }
5196
5197 int line_start_node::force_tprint()
5198 {
5199   return 0;
5200 }
5201
5202 int space_node::same(node *nd)
5203 {
5204   return n == ((space_node *)nd)->n
5205               && set == ((space_node *)nd)->set
5206               && col == ((space_node *)nd)->col;
5207 }
5208
5209 const char *space_node::type()
5210 {
5211   return "space_node";
5212 }
5213
5214 int word_space_node::same(node *nd)
5215 {
5216   return n == ((word_space_node *)nd)->n
5217          && set == ((word_space_node *)nd)->set
5218          && col == ((word_space_node *)nd)->col;
5219 }
5220
5221 const char *word_space_node::type()
5222 {
5223   return "word_space_node";
5224 }
5225
5226 int word_space_node::force_tprint()
5227 {
5228   return 0;
5229 }
5230
5231 int unbreakable_space_node::same(node *nd)
5232 {
5233   return n == ((unbreakable_space_node *)nd)->n
5234          && set == ((unbreakable_space_node *)nd)->set
5235          && col == ((unbreakable_space_node *)nd)->col;
5236 }
5237
5238 const char *unbreakable_space_node::type()
5239 {
5240   return "unbreakable_space_node";
5241 }
5242
5243 node *unbreakable_space_node::add_self(node *n, hyphen_list **p)
5244 {
5245   next = n;
5246   hyphen_list *pp = *p;
5247   *p = (*p)->next;
5248   delete pp;
5249   return this;
5250 }
5251
5252 hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail)
5253 {
5254   return new hyphen_list(0, tail);
5255 }
5256
5257 int diverted_space_node::same(node *nd)
5258 {
5259   return n == ((diverted_space_node *)nd)->n;
5260 }
5261
5262 const char *diverted_space_node::type()
5263 {
5264   return "diverted_space_node";
5265 }
5266
5267 int diverted_space_node::force_tprint()
5268 {
5269   return 0;
5270 }
5271
5272 int diverted_copy_file_node::same(node *nd)
5273 {
5274   return filename == ((diverted_copy_file_node *)nd)->filename;
5275 }
5276
5277 const char *diverted_copy_file_node::type()
5278 {
5279   return "diverted_copy_file_node";
5280 }
5281
5282 int diverted_copy_file_node::force_tprint()
5283 {
5284   return 0;
5285 }
5286
5287 // Grow the font_table so that its size is > n.
5288
5289 static void grow_font_table(int n)
5290 {
5291   assert(n >= font_table_size);
5292   font_info **old_font_table = font_table;
5293   int old_font_table_size = font_table_size;
5294   font_table_size = font_table_size ? (font_table_size*3)/2 : 10;
5295   if (font_table_size <= n)
5296     font_table_size = n + 10;
5297   font_table = new font_info *[font_table_size];
5298   if (old_font_table_size)
5299     memcpy(font_table, old_font_table,
5300            old_font_table_size*sizeof(font_info *));
5301   a_delete old_font_table;
5302   for (int i = old_font_table_size; i < font_table_size; i++)
5303     font_table[i] = 0;
5304 }
5305
5306 dictionary font_translation_dictionary(17);
5307
5308 static symbol get_font_translation(symbol nm)
5309 {
5310   void *p = font_translation_dictionary.lookup(nm);
5311   return p ? symbol((char *)p) : nm;
5312 }
5313
5314 dictionary font_dictionary(50);
5315
5316 static int mount_font_no_translate(int n, symbol name, symbol external_name)
5317 {
5318   assert(n >= 0);
5319   // We store the address of this char in font_dictionary to indicate
5320   // that we've previously tried to mount the font and failed.
5321   static char a_char;
5322   font *fm = 0;
5323   void *p = font_dictionary.lookup(external_name);
5324   if (p == 0) {
5325     int not_found;
5326     fm = font::load_font(external_name.contents(), &not_found);
5327     if (!fm) {
5328       if (not_found)
5329         warning(WARN_FONT, "can't find font `%1'", external_name.contents());
5330       font_dictionary.lookup(external_name, &a_char);
5331       return 0;
5332     }
5333     font_dictionary.lookup(name, fm);
5334   }
5335   else if (p == &a_char) {
5336 #if 0
5337     error("invalid font `%1'", external_name.contents());
5338 #endif
5339     return 0;
5340   }
5341   else
5342     fm = (font*)p;
5343   if (n >= font_table_size) {
5344     if (n - font_table_size > 1000) {
5345       error("font position too much larger than first unused position");
5346       return 0;
5347     }
5348     grow_font_table(n);
5349   }
5350   else if (font_table[n] != 0)
5351     delete font_table[n];
5352   font_table[n] = new font_info(name, n, external_name, fm);
5353   font_family::invalidate_fontno(n);
5354   return 1;
5355 }
5356
5357 int mount_font(int n, symbol name, symbol external_name)
5358 {
5359   assert(n >= 0);
5360   name = get_font_translation(name);
5361   if (external_name.is_null())
5362     external_name = name;
5363   else
5364     external_name = get_font_translation(external_name);
5365   return mount_font_no_translate(n, name, external_name);
5366 }
5367
5368 void mount_style(int n, symbol name)
5369 {
5370   assert(n >= 0);
5371   if (n >= font_table_size) {
5372     if (n - font_table_size > 1000) {
5373       error("font position too much larger than first unused position");
5374       return;
5375     }
5376     grow_font_table(n);
5377   }
5378   else if (font_table[n] != 0)
5379     delete font_table[n];
5380   font_table[n] = new font_info(get_font_translation(name), n, NULL_SYMBOL, 0);
5381   font_family::invalidate_fontno(n);
5382 }
5383
5384 /* global functions */
5385
5386 void font_translate()
5387 {
5388   symbol from = get_name(1);
5389   if (!from.is_null()) {
5390     symbol to = get_name();
5391     if (to.is_null() || from == to)
5392       font_translation_dictionary.remove(from);
5393     else
5394       font_translation_dictionary.lookup(from, (void *)to.contents());
5395   }
5396   skip_line();
5397 }
5398
5399 void font_position()
5400 {
5401   int n;
5402   if (get_integer(&n)) {
5403     if (n < 0)
5404       error("negative font position");
5405     else {
5406       symbol internal_name = get_name(1);
5407       if (!internal_name.is_null()) {
5408         symbol external_name = get_long_name(0);
5409         mount_font(n, internal_name, external_name); // ignore error
5410       }
5411     }
5412   }
5413   skip_line();
5414 }
5415
5416 font_family::font_family(symbol s)
5417 : map_size(10), nm(s)
5418 {
5419   map = new int[map_size];
5420   for (int i = 0; i < map_size; i++)
5421     map[i] = -1;
5422 }
5423
5424 font_family::~font_family()
5425 {
5426   a_delete map;
5427 }
5428
5429 int font_family::make_definite(int i)
5430 {
5431   if (i >= 0) {
5432     if (i < map_size && map[i] >= 0)
5433       return map[i];
5434     else {
5435       if (i < font_table_size && font_table[i] != 0) {
5436         if (i >= map_size) {
5437           int old_map_size = map_size;
5438           int *old_map = map;
5439           map_size *= 3;
5440           map_size /= 2;
5441           if (i >= map_size)
5442             map_size = i + 10;
5443           map = new int[map_size];
5444           memcpy(map, old_map, old_map_size*sizeof(int));
5445           a_delete old_map;
5446           for (int j = old_map_size; j < map_size; j++)
5447             map[j] = -1;
5448         }
5449         if (font_table[i]->is_style()) {
5450           symbol sty = font_table[i]->get_name();
5451           symbol f = concat(nm, sty);
5452           int n;
5453           // don't use symbol_fontno, because that might return a style
5454           // and because we don't want to translate the name
5455           for (n = 0; n < font_table_size; n++)
5456             if (font_table[n] != 0 && font_table[n]->is_named(f)
5457                 && !font_table[n]->is_style())
5458               break;
5459           if (n >= font_table_size) {
5460             n = next_available_font_position();
5461             if (!mount_font_no_translate(n, f, f))
5462               return -1;
5463           }
5464           return map[i] = n;
5465         }
5466         else
5467           return map[i] = i;
5468       }
5469       else
5470         return -1;
5471     }
5472   }
5473   else
5474     return -1;
5475 }
5476
5477 dictionary family_dictionary(5);
5478
5479 font_family *lookup_family(symbol nm)
5480 {
5481   font_family *f = (font_family *)family_dictionary.lookup(nm);
5482   if (!f) {
5483     f = new font_family(nm);
5484     (void)family_dictionary.lookup(nm, f);
5485   }
5486   return f;
5487 }
5488
5489 void font_family::invalidate_fontno(int n)
5490 {
5491   assert(n >= 0 && n < font_table_size);
5492   dictionary_iterator iter(family_dictionary);
5493   symbol nm;
5494   font_family *fam;
5495   while (iter.get(&nm, (void **)&fam)) {
5496     int map_size = fam->map_size;
5497     if (n < map_size)
5498       fam->map[n] = -1;
5499     for (int i = 0; i < map_size; i++)
5500       if (fam->map[i] == n)
5501         fam->map[i] = -1;
5502   }
5503 }
5504
5505 void style()
5506 {
5507   int n;
5508   if (get_integer(&n)) {
5509     if (n < 0)
5510       error("negative font position");
5511     else {
5512       symbol internal_name = get_name(1);
5513       if (!internal_name.is_null())
5514         mount_style(n, internal_name);
5515     }
5516   }
5517   skip_line();
5518 }
5519
5520 static int get_fontno()
5521 {
5522   int n;
5523   tok.skip();
5524   if (tok.delimiter()) {
5525     symbol s = get_name(1);
5526     if (!s.is_null()) {
5527       n = symbol_fontno(s);
5528       if (n < 0) {
5529         n = next_available_font_position();
5530         if (!mount_font(n, s))
5531           return -1;
5532       }
5533       return curenv->get_family()->make_definite(n);
5534     }
5535   }
5536   else if (get_integer(&n)) {
5537     if (n < 0 || n >= font_table_size || font_table[n] == 0)
5538       error("bad font number");
5539     else
5540       return curenv->get_family()->make_definite(n);
5541   }
5542   return -1;
5543 }
5544
5545 static int underline_fontno = 2;
5546
5547 void underline_font()
5548 {
5549   int n = get_fontno();
5550   if (n >= 0)
5551     underline_fontno = n;
5552   skip_line();
5553 }
5554
5555 int get_underline_fontno()
5556 {
5557   return underline_fontno;
5558 }
5559
5560 static void read_special_fonts(special_font_list **sp)
5561 {
5562   special_font_list *s = *sp;
5563   *sp = 0;
5564   while (s != 0) {
5565     special_font_list *tem = s;
5566     s = s->next;
5567     delete tem;
5568   }
5569   special_font_list **p = sp;
5570   while (has_arg()) {
5571     int i = get_fontno();
5572     if (i >= 0) {
5573       special_font_list *tem = new special_font_list;
5574       tem->n = i;
5575       tem->next = 0;
5576       *p = tem;
5577       p = &(tem->next);
5578     }
5579   }
5580 }
5581
5582 void font_special_request()
5583 {
5584   int n = get_fontno();
5585   if (n >= 0)
5586     read_special_fonts(&font_table[n]->sf);
5587   skip_line();
5588 }
5589
5590 void special_request()
5591 {
5592   read_special_fonts(&global_special_fonts);
5593   skip_line();
5594 }
5595
5596 int next_available_font_position()
5597 {
5598   int i;
5599   for (i = 1; i < font_table_size && font_table[i] != 0; i++)
5600     ;
5601   return i;
5602 }
5603
5604 int symbol_fontno(symbol s)
5605 {
5606   s = get_font_translation(s);
5607   for (int i = 0; i < font_table_size; i++)
5608     if (font_table[i] != 0 && font_table[i]->is_named(s))
5609       return i;
5610   return -1;
5611 }
5612
5613 int is_good_fontno(int n)
5614 {
5615   return n >= 0 && n < font_table_size && font_table[n] != 0;
5616 }
5617
5618 int get_bold_fontno(int n)
5619 {
5620   if (n >= 0 && n < font_table_size && font_table[n] != 0) {
5621     hunits offset;
5622     if (font_table[n]->get_bold(&offset))
5623       return offset.to_units() + 1;
5624     else
5625       return 0;
5626   }
5627   else
5628     return 0;
5629 }
5630
5631 hunits env_digit_width(environment *env)
5632 {
5633   node *n = make_glyph_node(charset_table['0'], env);
5634   if (n) {
5635     hunits x = n->width();
5636     delete n;
5637     return x;
5638   }
5639   else
5640     return H0;
5641 }
5642
5643 hunits env_space_width(environment *env)
5644 {
5645   int fn = env_definite_font(env);
5646   font_size fs = env->get_font_size();
5647   if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5648     return scale(fs.to_units()/3, env->get_space_size(), 12);
5649   else
5650     return font_table[fn]->get_space_width(fs, env->get_space_size());
5651 }
5652
5653 hunits env_sentence_space_width(environment *env)
5654 {
5655   int fn = env_definite_font(env);
5656   font_size fs = env->get_font_size();
5657   if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5658     return scale(fs.to_units()/3, env->get_sentence_space_size(), 12);
5659   else
5660     return font_table[fn]->get_space_width(fs, env->get_sentence_space_size());
5661 }
5662
5663 hunits env_half_narrow_space_width(environment *env)
5664 {
5665   int fn = env_definite_font(env);
5666   font_size fs = env->get_font_size();
5667   if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5668     return 0;
5669   else
5670     return font_table[fn]->get_half_narrow_space_width(fs);
5671 }
5672
5673 hunits env_narrow_space_width(environment *env)
5674 {
5675   int fn = env_definite_font(env);
5676   font_size fs = env->get_font_size();
5677   if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5678     return 0;
5679   else
5680     return font_table[fn]->get_narrow_space_width(fs);
5681 }
5682
5683 void bold_font()
5684 {
5685   int n = get_fontno();
5686   if (n >= 0) {
5687     if (has_arg()) {
5688       if (tok.delimiter()) {
5689         int f = get_fontno();
5690         if (f >= 0) {
5691           units offset;
5692           if (has_arg() && get_number(&offset, 'u') && offset >= 1)
5693             font_table[f]->set_conditional_bold(n, hunits(offset - 1));
5694           else
5695             font_table[f]->conditional_unbold(n);
5696         }
5697       }
5698       else {
5699         units offset;
5700         if (get_number(&offset, 'u') && offset >= 1)
5701           font_table[n]->set_bold(hunits(offset - 1));
5702         else
5703           font_table[n]->unbold();
5704       }
5705     }
5706     else
5707       font_table[n]->unbold();
5708   }
5709   skip_line();
5710 }
5711
5712 track_kerning_function::track_kerning_function() : non_zero(0)
5713 {
5714 }
5715
5716 track_kerning_function::track_kerning_function(int min_s, hunits min_a,
5717                                                int max_s, hunits max_a)
5718 : non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s),
5719   max_amount(max_a)
5720 {
5721 }
5722
5723 int track_kerning_function::operator==(const track_kerning_function &tk)
5724 {
5725   if (non_zero)
5726     return (tk.non_zero
5727             && min_size == tk.min_size
5728             && min_amount == tk.min_amount
5729             && max_size == tk.max_size
5730             && max_amount == tk.max_amount);
5731   else
5732     return !tk.non_zero;
5733 }
5734
5735 int track_kerning_function::operator!=(const track_kerning_function &tk)
5736 {
5737   if (non_zero)
5738     return (!tk.non_zero
5739             || min_size != tk.min_size
5740             || min_amount != tk.min_amount
5741             || max_size != tk.max_size
5742             || max_amount != tk.max_amount);
5743   else
5744     return tk.non_zero;
5745 }
5746
5747 hunits track_kerning_function::compute(int size)
5748 {
5749   if (non_zero) {
5750     if (max_size <= min_size)
5751       return min_amount;
5752     else if (size <= min_size)
5753       return min_amount;
5754     else if (size >= max_size)
5755       return max_amount;
5756     else
5757       return (scale(max_amount, size - min_size, max_size - min_size)
5758               + scale(min_amount, max_size - size, max_size - min_size));
5759   }
5760   else
5761     return H0;
5762 }
5763
5764 void track_kern()
5765 {
5766   int n = get_fontno();
5767   if (n >= 0) {
5768     int min_s, max_s;
5769     hunits min_a, max_a;
5770     if (has_arg()
5771         && get_number(&min_s, 'z')
5772         && get_hunits(&min_a, 'p')
5773         && get_number(&max_s, 'z')
5774         && get_hunits(&max_a, 'p')) {
5775       track_kerning_function tk(min_s, min_a, max_s, max_a);
5776       font_table[n]->set_track_kern(tk);
5777     }
5778     else {
5779       track_kerning_function tk;
5780       font_table[n]->set_track_kern(tk);
5781     }
5782   }
5783   skip_line();
5784 }
5785
5786 void constant_space()
5787 {
5788   int n = get_fontno();
5789   if (n >= 0) {
5790     int x, y;
5791     if (!has_arg() || !get_integer(&x))
5792       font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
5793     else {
5794       if (!has_arg() || !get_number(&y, 'z'))
5795         font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
5796       else
5797         font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
5798                                           scale(y*x,
5799                                                 units_per_inch,
5800                                                 36*72*sizescale));
5801     }
5802   }
5803   skip_line();
5804 }
5805
5806 void ligature()
5807 {
5808   int lig;
5809   if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2)
5810     global_ligature_mode = lig;
5811   else
5812     global_ligature_mode = 1;
5813   skip_line();
5814 }
5815
5816 void kern_request()
5817 {
5818   int k;
5819   if (has_arg() && get_integer(&k))
5820     global_kern_mode = k != 0;
5821   else
5822     global_kern_mode = 1;
5823   skip_line();
5824 }
5825
5826 void set_soft_hyphen_char()
5827 {
5828   soft_hyphen_char = get_optional_char();
5829   if (!soft_hyphen_char)
5830     soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
5831   skip_line();
5832 }
5833
5834 void init_output()
5835 {
5836   if (suppress_output_flag)
5837     the_output = new suppress_output_file;
5838   else if (ascii_output_flag)
5839     the_output = new ascii_output_file;
5840   else
5841     the_output = new troff_output_file;
5842 }
5843
5844 class next_available_font_position_reg : public reg {
5845 public:
5846   const char *get_string();
5847 };
5848
5849 const char *next_available_font_position_reg::get_string()
5850 {
5851   return i_to_a(next_available_font_position());
5852 }
5853
5854 class printing_reg : public reg {
5855 public:
5856   const char *get_string();
5857 };
5858
5859 const char *printing_reg::get_string()
5860 {
5861   if (the_output)
5862     return the_output->is_printing() ? "1" : "0";
5863   else
5864     return "0";
5865 }
5866
5867 void init_node_requests()
5868 {
5869   init_request("fp", font_position);
5870   init_request("sty", style);
5871   init_request("cs", constant_space);
5872   init_request("bd", bold_font);
5873   init_request("uf", underline_font);
5874   init_request("lg", ligature);
5875   init_request("kern", kern_request);
5876   init_request("tkf", track_kern);
5877   init_request("special", special_request);
5878   init_request("fspecial", font_special_request);
5879   init_request("ftr", font_translate);
5880   init_request("shc", set_soft_hyphen_char);
5881   number_reg_dictionary.define(".fp", new next_available_font_position_reg);
5882   number_reg_dictionary.define(".kern",
5883                                new constant_int_reg(&global_kern_mode));
5884   number_reg_dictionary.define(".lg",
5885                                new constant_int_reg(&global_ligature_mode));
5886   number_reg_dictionary.define(".P", new printing_reg);
5887   soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
5888 }