groff before CVS: release 1.07
[s-roff.git] / troff / node.cc
blob10e31cb14c09927ba8785365a03d95fdbbf2bf72
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
21 #include "troff.h"
22 #include "symbol.h"
23 #include "dictionary.h"
24 #include "hvunits.h"
25 #include "env.h"
26 #include "request.h"
27 #include "node.h"
28 #include "token.h"
29 #include "charinfo.h"
30 #include "font.h"
31 #include "reg.h"
33 #define STORE_WIDTH 1
35 symbol HYPHEN_SYMBOL("hy");
37 // Character used when a hyphen is inserted at a line break.
38 static charinfo *soft_hyphen_char;
40 enum constant_space_type {
41 CONSTANT_SPACE_NONE,
42 CONSTANT_SPACE_RELATIVE,
43 CONSTANT_SPACE_ABSOLUTE
46 struct special_font_list {
47 int n;
48 special_font_list *next;
51 special_font_list *global_special_fonts;
52 static int global_ligature_mode = 1;
53 static int global_kern_mode = 1;
55 class track_kerning_function {
56 int non_zero;
57 units min_size;
58 hunits min_amount;
59 units max_size;
60 hunits max_amount;
61 public:
62 track_kerning_function();
63 track_kerning_function(units, hunits, units, hunits);
64 int operator==(const track_kerning_function &);
65 int operator!=(const track_kerning_function &);
66 hunits compute(int point_size);
69 // embolden fontno when this is the current font
71 struct conditional_bold {
72 conditional_bold *next;
73 int fontno;
74 hunits offset;
75 conditional_bold(int, hunits, conditional_bold * = 0);
78 struct tfont;
80 class font_info {
81 tfont *last_tfont;
82 int number;
83 font_size last_size;
84 int last_height;
85 int last_slant;
86 symbol internal_name;
87 symbol external_name;
88 font *fm;
89 char is_bold;
90 hunits bold_offset;
91 track_kerning_function track_kern;
92 constant_space_type is_constant_spaced;
93 units constant_space;
94 int last_ligature_mode;
95 int last_kern_mode;
96 conditional_bold *cond_bold_list;
97 void flush();
98 public:
99 special_font_list *sf;
101 font_info(symbol nm, int n, symbol enm, font *f);
102 int contains(charinfo *);
103 void set_bold(hunits);
104 void unbold();
105 void set_conditional_bold(int, hunits);
106 void conditional_unbold(int);
107 void set_track_kern(track_kerning_function &);
108 void set_constant_space(constant_space_type, units = 0);
109 int is_named(symbol);
110 symbol get_name();
111 tfont *get_tfont(font_size, int, int, int);
112 hunits get_space_width(font_size, int);
113 hunits get_narrow_space_width(font_size);
114 hunits get_half_narrow_space_width(font_size);
115 int get_bold(hunits *);
116 int is_special();
117 int is_style();
120 class tfont_spec {
121 protected:
122 symbol name;
123 int input_position;
124 font *fm;
125 font_size size;
126 char is_bold;
127 char is_constant_spaced;
128 int ligature_mode;
129 int kern_mode;
130 hunits bold_offset;
131 hunits track_kern; // add this to the width
132 hunits constant_space_width;
133 int height;
134 int slant;
135 public:
136 tfont_spec(symbol nm, int pos, font *, font_size, int, int);
137 tfont_spec plain();
138 int operator==(const tfont_spec &);
139 friend tfont *font_info::get_tfont(font_size fs, int, int, int);
142 class tfont : public tfont_spec {
143 static tfont *tfont_list;
144 tfont *next;
145 tfont *plain_version;
146 public:
147 tfont(tfont_spec &);
148 int contains(charinfo *);
149 hunits get_width(charinfo *c);
150 int get_bold(hunits *);
151 int get_constant_space(hunits *);
152 hunits get_track_kern();
153 tfont *get_plain();
154 font_size get_size();
155 symbol get_name();
156 charinfo *get_lig(charinfo *c1, charinfo *c2);
157 int get_kern(charinfo *c1, charinfo *c2, hunits *res);
158 int get_input_position();
159 int get_character_type(charinfo *);
160 int get_height();
161 int get_slant();
162 vunits get_char_height(charinfo *);
163 vunits get_char_depth(charinfo *);
164 hunits get_char_skew(charinfo *);
165 hunits get_italic_correction(charinfo *);
166 hunits get_left_italic_correction(charinfo *);
167 hunits get_subscript_correction(charinfo *);
168 friend tfont *make_tfont(tfont_spec &);
171 inline int env_definite_font(environment *env)
173 return env->get_family()->make_definite(env->get_font());
176 static void invalidate_fontno(int n);
178 /* font_info functions */
180 static font_info **font_table = 0;
181 static int font_table_size = 0;
183 font_info::font_info(symbol nm, int n, symbol enm, font *f)
184 : internal_name(nm), external_name(enm), fm(f), number(n),
185 is_constant_spaced(CONSTANT_SPACE_NONE),
186 sf(0), is_bold(0), cond_bold_list(0),
187 last_ligature_mode(1), last_kern_mode(1),
188 last_tfont(0), last_size(0)
192 inline int font_info::contains(charinfo *ci)
194 return fm != 0 && fm->contains(ci->get_index());
197 inline int font_info::is_special()
199 return fm != 0 && fm->is_special();
202 inline int font_info::is_style()
204 return fm == 0;
207 // this is the current_font, fontno is where we found the character,
208 // presumably a special font
210 tfont *font_info::get_tfont(font_size fs, int height, int slant, int fontno)
212 if (last_tfont == 0 || fs != last_size
213 || height != last_height || slant != last_slant
214 || global_ligature_mode != last_ligature_mode
215 || global_kern_mode != last_kern_mode
216 || fontno != number) {
217 font_info *f = font_table[fontno];
218 tfont_spec spec(f->external_name, f->number, f->fm, fs, height, slant);
219 for (conditional_bold *p = cond_bold_list; p; p = p->next)
220 if (p->fontno == fontno) {
221 spec.is_bold = 1;
222 spec.bold_offset = p->offset;
223 break;
225 if (!spec.is_bold && is_bold) {
226 spec.is_bold = 1;
227 spec.bold_offset = bold_offset;
229 spec.track_kern = track_kern.compute(fs.to_scaled_points());
230 spec.ligature_mode = global_ligature_mode;
231 spec.kern_mode = global_kern_mode;
232 switch (is_constant_spaced) {
233 case CONSTANT_SPACE_NONE:
234 break;
235 case CONSTANT_SPACE_ABSOLUTE:
236 spec.is_constant_spaced = 1;
237 spec.constant_space_width = constant_space;
238 break;
239 case CONSTANT_SPACE_RELATIVE:
240 spec.is_constant_spaced = 1;
241 spec.constant_space_width
242 = scale(constant_space*fs.to_scaled_points(),
243 units_per_inch,
244 36*72*sizescale);
245 break;
246 default:
247 assert(0);
249 if (fontno != number)
250 return make_tfont(spec);
251 last_tfont = make_tfont(spec);
252 last_size = fs;
253 last_height = height;
254 last_slant = slant;
255 last_ligature_mode = global_ligature_mode;
256 last_kern_mode = global_kern_mode;
258 return last_tfont;
261 int font_info::get_bold(hunits *res)
263 if (is_bold) {
264 *res = bold_offset;
265 return 1;
267 else
268 return 0;
271 void font_info::unbold()
273 if (is_bold) {
274 is_bold = 0;
275 flush();
279 void font_info::set_bold(hunits offset)
281 if (!is_bold || offset != bold_offset) {
282 is_bold = 1;
283 bold_offset = offset;
284 flush();
288 void font_info::set_conditional_bold(int fontno, hunits offset)
290 for (conditional_bold *p = cond_bold_list; p; p = p->next)
291 if (p->fontno == fontno) {
292 if (offset != p->offset) {
293 p->offset = offset;
294 flush();
296 return;
298 cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
301 conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
302 : fontno(f), offset(h), next(x)
306 void font_info::conditional_unbold(int fontno)
308 for (conditional_bold **p = &cond_bold_list; *p; p = &(*p)->next)
309 if ((*p)->fontno == fontno) {
310 conditional_bold *tem = *p;
311 *p = (*p)->next;
312 delete tem;
313 flush();
314 return;
318 void font_info::set_constant_space(constant_space_type type, units x)
320 if (type != is_constant_spaced
321 || (type != CONSTANT_SPACE_NONE && x != constant_space)) {
322 flush();
323 is_constant_spaced = type;
324 constant_space = x;
328 void font_info::set_track_kern(track_kerning_function &tk)
330 if (track_kern != tk) {
331 track_kern = tk;
332 flush();
336 void font_info::flush()
338 last_tfont = 0;
341 int font_info::is_named(symbol s)
343 return internal_name == s;
346 symbol font_info::get_name()
348 return internal_name;
351 hunits font_info::get_space_width(font_size fs, int space_size)
353 if (is_constant_spaced == CONSTANT_SPACE_NONE)
354 return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
355 space_size, 12);
356 else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
357 return constant_space;
358 else
359 return scale(constant_space*fs.to_scaled_points(),
360 units_per_inch, 36*72*sizescale);
363 hunits font_info::get_narrow_space_width(font_size fs)
365 charinfo *ci = get_charinfo(symbol("|"));
366 if (fm->contains(ci->get_index()))
367 return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
368 else
369 return hunits(fs.to_units()/6);
372 hunits font_info::get_half_narrow_space_width(font_size fs)
374 charinfo *ci = get_charinfo(symbol("^"));
375 if (fm->contains(ci->get_index()))
376 return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
377 else
378 return hunits(fs.to_units()/12);
381 /* tfont */
383 tfont_spec::tfont_spec(symbol nm, int n, font *f,
384 font_size s, int h, int sl)
385 : name(nm), input_position(n), fm(f), size(s),
386 is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1),
387 height(h), slant(sl)
389 if (height == size.to_scaled_points())
390 height = 0;
393 int tfont_spec::operator==(const tfont_spec &spec)
395 if (fm == spec.fm
396 && size == spec.size
397 && input_position == spec.input_position
398 && name == spec.name
399 && height == spec.height
400 && slant == spec.slant
401 && (is_bold
402 ? (spec.is_bold && bold_offset == spec.bold_offset)
403 : !spec.is_bold)
404 && track_kern == spec.track_kern
405 && (is_constant_spaced
406 ? (spec.is_constant_spaced
407 && constant_space_width == spec.constant_space_width)
408 : !spec.is_constant_spaced)
409 && ligature_mode == spec.ligature_mode
410 && kern_mode == spec.kern_mode)
411 return 1;
412 else
413 return 0;
416 tfont_spec tfont_spec::plain()
418 return tfont_spec(name, input_position, fm, size, height, slant);
421 hunits tfont::get_width(charinfo *c)
423 if (is_constant_spaced)
424 return constant_space_width;
425 else if (is_bold)
426 return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
427 + track_kern + bold_offset);
428 else
429 return (hunits(fm->get_width(c->get_index(), size.to_scaled_points())) + track_kern);
432 vunits tfont::get_char_height(charinfo *c)
434 vunits v = fm->get_height(c->get_index(), size.to_scaled_points());
435 if (height != 0 && height != size.to_scaled_points())
436 return scale(v, height, size.to_scaled_points());
437 else
438 return v;
441 vunits tfont::get_char_depth(charinfo *c)
443 vunits v = fm->get_depth(c->get_index(), size.to_scaled_points());
444 if (height != 0 && height != size.to_scaled_points())
445 return scale(v, height, size.to_scaled_points());
446 else
447 return v;
450 hunits tfont::get_char_skew(charinfo *c)
452 return hunits(fm->get_skew(c->get_index(), size.to_scaled_points(), slant));
455 hunits tfont::get_italic_correction(charinfo *c)
457 return hunits(fm->get_italic_correction(c->get_index(), size.to_scaled_points()));
460 hunits tfont::get_left_italic_correction(charinfo *c)
462 return hunits(fm->get_left_italic_correction(c->get_index(),
463 size.to_scaled_points()));
466 hunits tfont::get_subscript_correction(charinfo *c)
468 return hunits(fm->get_subscript_correction(c->get_index(),
469 size.to_scaled_points()));
472 inline int tfont::get_input_position()
474 return input_position;
477 inline int tfont::contains(charinfo *ci)
479 return fm->contains(ci->get_index());
482 inline int tfont::get_character_type(charinfo *ci)
484 return fm->get_character_type(ci->get_index());
487 inline int tfont::get_bold(hunits *res)
489 if (is_bold) {
490 *res = bold_offset;
491 return 1;
493 else
494 return 0;
497 inline int tfont::get_constant_space(hunits *res)
499 if (is_constant_spaced) {
500 *res = constant_space_width;
501 return 1;
503 else
504 return 0;
507 inline hunits tfont::get_track_kern()
509 return track_kern;
512 inline tfont *tfont::get_plain()
514 return plain_version;
517 inline font_size tfont::get_size()
519 return size;
522 inline symbol tfont::get_name()
524 return name;
527 inline int tfont::get_height()
529 return height;
532 inline int tfont::get_slant()
534 return slant;
537 symbol SYMBOL_ff("ff");
538 symbol SYMBOL_fi("fi");
539 symbol SYMBOL_fl("fl");
540 symbol SYMBOL_Fi("Fi");
541 symbol SYMBOL_Fl("Fl");
543 charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
545 if (ligature_mode == 0)
546 return 0;
547 charinfo *ci = 0;
548 if (c1->get_ascii_code() == 'f') {
549 switch (c2->get_ascii_code()) {
550 case 'f':
551 if (fm->has_ligature(font::LIG_ff))
552 ci = get_charinfo(SYMBOL_ff);
553 break;
554 case 'i':
555 if (fm->has_ligature(font::LIG_fi))
556 ci = get_charinfo(SYMBOL_fi);
557 break;
558 case 'l':
559 if (fm->has_ligature(font::LIG_fl))
560 ci = get_charinfo(SYMBOL_fl);
561 break;
564 else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
565 switch (c2->get_ascii_code()) {
566 case 'i':
567 if (fm->has_ligature(font::LIG_ffi))
568 ci = get_charinfo(SYMBOL_Fi);
569 break;
570 case 'l':
571 if (fm->has_ligature(font::LIG_ffl))
572 ci = get_charinfo(SYMBOL_Fl);
573 break;
576 if (ci != 0 && fm->contains(ci->get_index()))
577 return ci;
578 return 0;
581 inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res)
583 if (kern_mode == 0)
584 return 0;
585 else {
586 int n = fm->get_kern(c1->get_index(),
587 c2->get_index(),
588 size.to_scaled_points());
589 if (n) {
590 *res = hunits(n);
591 return 1;
593 else
594 return 0;
598 tfont *make_tfont(tfont_spec &spec)
600 for (tfont *p = tfont::tfont_list; p; p = p->next)
601 if (*p == spec)
602 return p;
603 return new tfont(spec);
606 tfont *tfont::tfont_list = 0;
608 tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
610 next = tfont_list;
611 tfont_list = this;
612 tfont_spec plain_spec = plain();
613 for (tfont *p = tfont_list; p; p = p->next)
614 if (*p == plain_spec) {
615 plain_version = p;
616 break;
618 if (!p)
619 plain_version = new tfont(plain_spec);
622 /* output_file */
624 class real_output_file : public output_file {
625 int piped;
626 int printing;
627 virtual void really_transparent_char(unsigned char) = 0;
628 virtual void really_print_line(hunits x, vunits y, node *n,
629 vunits before, vunits after) = 0;
630 virtual void really_begin_page(int pageno, vunits page_length) = 0;
631 virtual void really_copy_file(hunits x, vunits y, const char *filename);
632 protected:
633 FILE *fp;
634 public:
635 real_output_file();
636 ~real_output_file();
637 void flush();
638 void transparent_char(unsigned char);
639 void print_line(hunits x, vunits y, node *n, vunits before, vunits after);
640 void begin_page(int pageno, vunits page_length);
641 int is_printing();
642 void copy_file(hunits x, vunits y, const char *filename);
645 class suppress_output_file : public real_output_file {
646 public:
647 suppress_output_file();
648 void really_transparent_char(unsigned char);
649 void really_print_line(hunits x, vunits y, node *n, vunits, vunits);
650 void really_begin_page(int pageno, vunits page_length);
653 class ascii_output_file : public real_output_file {
654 public:
655 ascii_output_file();
656 void really_transparent_char(unsigned char);
657 void really_print_line(hunits x, vunits y, node *n, vunits, vunits);
658 void really_begin_page(int pageno, vunits page_length);
659 void outc(unsigned char c);
660 void outs(const char *s);
663 void ascii_output_file::outc(unsigned char c)
665 fputc(c, fp);
668 void ascii_output_file::outs(const char *s)
670 fputc('<', fp);
671 if (s)
672 fputs(s, fp);
673 fputc('>', fp);
676 struct hvpair;
678 class troff_output_file : public real_output_file {
679 units hpos;
680 units vpos;
681 units output_vpos;
682 units output_hpos;
683 int force_motion;
684 int current_size;
685 int current_slant;
686 int current_height;
687 tfont *current_tfont;
688 int current_font_number;
689 symbol *font_position;
690 int nfont_positions;
691 enum { TBUF_SIZE = 256 };
692 char tbuf[TBUF_SIZE];
693 int tbuf_len;
694 int tbuf_kern;
695 void do_motion();
696 vunits page_length;
697 void put(char c);
698 void put(unsigned char c);
699 void put(int i);
700 void put(const char *s);
701 void set_font(tfont *tf);
702 void flush_tbuf();
703 public:
704 troff_output_file();
705 ~troff_output_file();
706 void put_char(charinfo *ci, tfont *tf);
707 void put_char_width(charinfo *ci, tfont *tf, hunits w, hunits k);
708 void right(hunits);
709 void down(vunits);
710 void moveto(hunits, vunits);
711 void start_special();
712 void special_char(unsigned char c);
713 void end_special();
714 void word_marker();
715 void really_transparent_char(unsigned char c);
716 void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after);
717 void really_begin_page(int pageno, vunits page_length);
718 void really_copy_file(hunits x, vunits y, const char *filename);
719 void draw(char, hvpair *, int, font_size);
720 int get_hpos() { return hpos; }
721 int get_vpos() { return vpos; }
724 static void put_string(const char *s, FILE *fp)
726 for (; *s != '\0'; ++s)
727 putc(*s, fp);
730 inline void troff_output_file::put(char c)
732 putc(c, fp);
735 inline void troff_output_file::put(unsigned char c)
737 putc(c, fp);
740 inline void troff_output_file::put(const char *s)
742 put_string(s, fp);
745 inline void troff_output_file::put(int i)
747 put_string(itoa(i), fp);
750 void troff_output_file::start_special()
752 flush_tbuf();
753 do_motion();
754 put("x X ");
757 void troff_output_file::special_char(unsigned char c)
759 put(c);
760 if (c == '\n')
761 put('+');
764 void troff_output_file::end_special()
766 put('\n');
769 inline void troff_output_file::moveto(hunits h, vunits v)
771 hpos = h.to_units();
772 vpos = v.to_units();
775 void troff_output_file::really_print_line(hunits x, vunits y, node *n,
776 vunits before, vunits after)
778 moveto(x, y);
779 while (n != 0) {
780 n->tprint(this);
781 n = n->next;
783 flush_tbuf();
784 // This ensures that transparent throughput will have a more predictable
785 // position.
786 do_motion();
787 force_motion = 1;
788 hpos = 0;
789 put('n');
790 put(before.to_units());
791 put(' ');
792 put(after.to_units());
793 put('\n');
796 inline void troff_output_file::word_marker()
798 flush_tbuf();
799 put('w');
802 inline void troff_output_file::right(hunits n)
804 hpos += n.to_units();
807 inline void troff_output_file::down(vunits n)
809 vpos += n.to_units();
812 void troff_output_file::do_motion()
814 if (force_motion) {
815 put('V');
816 put(vpos);
817 put('\n');
818 put('H');
819 put(hpos);
820 put('\n');
822 else {
823 if (hpos != output_hpos) {
824 units n = hpos - output_hpos;
825 if (n > 0 && n < hpos) {
826 put('h');
827 put(n);
829 else {
830 put('H');
831 put(hpos);
833 put('\n');
835 if (vpos != output_vpos) {
836 units n = vpos - output_vpos;
837 if (n > 0 && n < vpos) {
838 put('v');
839 put(n);
841 else {
842 put('V');
843 put(vpos);
845 put('\n');
848 output_vpos = vpos;
849 output_hpos = hpos;
850 force_motion = 0;
853 void troff_output_file::flush_tbuf()
855 if (tbuf_len == 0)
856 return;
857 if (tbuf_kern == 0)
858 put('t');
859 else {
860 put('u');
861 put(tbuf_kern);
862 put(' ');
864 for (int i = 0; i < tbuf_len; i++)
865 put(tbuf[i]);
866 put('\n');
867 tbuf_len = 0;
870 void troff_output_file::put_char_width(charinfo *ci, tfont *tf, hunits w,
871 hunits k)
873 if (tf != current_tfont) {
874 flush_tbuf();
875 set_font(tf);
877 char c = ci->get_ascii_code();
878 int kk = k.to_units();
879 if (c == '\0') {
880 flush_tbuf();
881 do_motion();
882 if (ci->numbered()) {
883 put('N');
884 put(ci->get_number());
886 else {
887 put('C');
888 const char *s = ci->nm.contents();
889 if (s[1] == 0) {
890 put('\\');
891 put(s[0]);
893 else
894 put(s);
896 put('\n');
897 hpos += w.to_units() + kk;
899 else if (tcommand_flag) {
900 if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
901 && kk == tbuf_kern
902 && tbuf_len < TBUF_SIZE) {
903 tbuf[tbuf_len++] = c;
904 output_hpos += w.to_units() + kk;
905 hpos = output_hpos;
906 return;
908 flush_tbuf();
909 do_motion();
910 tbuf[tbuf_len++] = c;
911 output_hpos += w.to_units() + kk;
912 tbuf_kern = kk;
913 hpos = output_hpos;
915 else {
916 // flush_tbuf();
917 int n = hpos - output_hpos;
918 if (vpos == output_vpos && n > 0 && n < 100 && !force_motion) {
919 put(char(n/10 + '0'));
920 put(char(n%10 + '0'));
921 put(c);
922 output_hpos = hpos;
924 else {
925 do_motion();
926 put('c');
927 put(c);
929 hpos += w.to_units() + kk;
933 void troff_output_file::put_char(charinfo *ci, tfont *tf)
935 flush_tbuf();
936 if (tf != current_tfont)
937 set_font(tf);
938 char c = ci->get_ascii_code();
939 if (c == '\0') {
940 do_motion();
941 if (ci->numbered()) {
942 put('N');
943 put(ci->get_number());
945 else {
946 put('C');
947 const char *s = ci->nm.contents();
948 if (s[1] == 0) {
949 put('\\');
950 put(s[0]);
952 else
953 put(s);
955 put('\n');
957 else {
958 int n = hpos - output_hpos;
959 if (vpos == output_vpos && n > 0 && n < 100) {
960 put(char(n/10 + '0'));
961 put(char(n%10 + '0'));
962 put(c);
963 output_hpos = hpos;
965 else {
966 do_motion();
967 put('c');
968 put(c);
973 void troff_output_file::set_font(tfont *tf)
975 if (current_tfont == tf)
976 return;
977 int n = tf->get_input_position();
978 symbol nm = tf->get_name();
979 if (n >= nfont_positions || font_position[n] != nm) {
980 put("x font ");
981 put(n);
982 put(' ');
983 put(nm.contents());
984 put('\n');
985 if (n >= nfont_positions) {
986 int old_nfont_positions = nfont_positions;
987 symbol *old_font_position = font_position;
988 nfont_positions *= 3;
989 nfont_positions /= 2;
990 if (nfont_positions <= n)
991 nfont_positions = n + 10;
992 font_position = new symbol[nfont_positions];
993 memcpy(font_position, old_font_position,
994 old_nfont_positions*sizeof(symbol));
995 a_delete old_font_position;
997 font_position[n] = nm;
999 if (current_font_number != n) {
1000 put('f');
1001 put(n);
1002 put('\n');
1003 current_font_number = n;
1005 int size = tf->get_size().to_scaled_points();
1006 if (current_size != size) {
1007 put('s');
1008 put(size);
1009 put('\n');
1010 current_size = size;
1012 int slant = tf->get_slant();
1013 if (current_slant != slant) {
1014 put("x Slant ");
1015 put(slant);
1016 put('\n');
1017 current_slant = slant;
1019 int height = tf->get_height();
1020 if (current_height != height) {
1021 put("x Height ");
1022 put(height == 0 ? current_size : height);
1023 put('\n');
1024 current_height = height;
1026 current_tfont = tf;
1029 void troff_output_file::draw(char code, hvpair *point, int npoints,
1030 font_size fsize)
1032 flush_tbuf();
1033 do_motion();
1034 int size = fsize.to_scaled_points();
1035 if (current_size != size) {
1036 put('s');
1037 put(size);
1038 put('\n');
1039 current_size = size;
1040 current_tfont = 0;
1042 put('D');
1043 put(code);
1044 int i;
1045 if (code == 'c') {
1046 put(' ');
1047 put(point[0].h.to_units());
1049 else
1050 for (i = 0; i < npoints; i++) {
1051 put(' ');
1052 put(point[i].h.to_units());
1053 put(' ');
1054 put(point[i].v.to_units());
1056 for (i = 0; i < npoints; i++)
1057 output_hpos += point[i].h.to_units();
1058 hpos = output_hpos;
1059 if (code != 'e') {
1060 for (i = 0; i < npoints; i++)
1061 output_vpos += point[i].v.to_units();
1062 vpos = output_vpos;
1064 put('\n');
1067 void troff_output_file::really_begin_page(int pageno, vunits pl)
1069 flush_tbuf();
1070 if (page_length > V0) {
1071 put('V');
1072 put(page_length.to_units());
1073 put('\n');
1075 page_length = pl;
1076 current_tfont = 0;
1077 current_font_number = -1;
1078 current_size = 0;
1079 // current_height = 0;
1080 // current_slant = 0;
1081 hpos = 0;
1082 vpos = 0;
1083 output_hpos = 0;
1084 output_vpos = 0;
1085 force_motion = 1;
1086 for (int i = 0; i < nfont_positions; i++)
1087 font_position[i] = NULL_SYMBOL;
1088 put('p');
1089 put(pageno);
1090 put('\n');
1093 void troff_output_file::really_copy_file(hunits x, vunits y, const char *filename)
1095 moveto(x, y);
1096 flush_tbuf();
1097 do_motion();
1098 errno = 0;
1099 FILE *ifp = fopen(filename, "r");
1100 if (ifp == 0)
1101 error("can't open `%1': %2", filename, strerror(errno));
1102 else {
1103 int c;
1104 while ((c = getc(ifp)) != EOF)
1105 put(char(c));
1106 fclose(ifp);
1108 force_motion = 1;
1109 current_size = 0;
1110 current_tfont = 0;
1111 current_font_number = -1;
1112 for (int i = 0; i < nfont_positions; i++)
1113 font_position[i] = NULL_SYMBOL;
1116 void troff_output_file::really_transparent_char(unsigned char c)
1118 put(c);
1121 troff_output_file::~troff_output_file()
1123 flush_tbuf();
1124 if (page_length > V0) {
1125 put("x trailer\n");
1126 put('V');
1127 put(page_length.to_units());
1128 put('\n');
1130 put("x stop\n");
1131 a_delete font_position;
1134 troff_output_file::troff_output_file()
1135 : current_height(0), current_slant(0), tbuf_len(0), nfont_positions(10)
1137 font_position = new symbol[nfont_positions];
1138 put("x T ");
1139 put(device);
1140 put('\n');
1141 put("x res ");
1142 put(units_per_inch);
1143 put(' ');
1144 put(hresolution);
1145 put(' ');
1146 put(vresolution);
1147 put('\n');
1148 put("x init\n");
1151 /* output_file */
1153 output_file *the_output = 0;
1155 output_file::output_file()
1159 output_file::~output_file()
1163 real_output_file::real_output_file()
1164 : printing(0)
1166 if (pipe_command) {
1167 if ((fp = popen(pipe_command, "w")) != 0) {
1168 piped = 1;
1169 return;
1171 error("pipe open failed: %1", strerror(errno));
1173 piped = 0;
1174 fp = stdout;
1177 real_output_file::~real_output_file()
1179 if (!fp)
1180 return;
1181 // To avoid looping, set fp to 0 before calling fatal().
1182 if (ferror(fp) || fflush(fp) < 0) {
1183 fp = 0;
1184 fatal("error writing output file");
1186 if (piped) {
1187 int result = pclose(fp);
1188 fp = 0;
1189 if (result < 0)
1190 fatal("pclose failed");
1191 if ((result & 0x7f) != 0)
1192 error("output process `%1' got fatal signal %2",
1193 pipe_command, result & 0x7f);
1194 else {
1195 int exit_status = (result >> 8) & 0xff;
1196 if (exit_status != 0)
1197 error("output process `%1' exited with status %2",
1198 pipe_command, exit_status);
1201 else if (fclose(fp) < 0) {
1202 fp = 0;
1203 fatal("error closing output file");
1207 void real_output_file::flush()
1209 if (fflush(fp) < 0)
1210 fatal("error writing output file");
1213 int real_output_file::is_printing()
1215 return printing;
1218 void real_output_file::begin_page(int pageno, vunits page_length)
1220 printing = in_output_page_list(pageno);
1221 if (printing)
1222 really_begin_page(pageno, page_length);
1225 void real_output_file::copy_file(hunits x, vunits y, const char *filename)
1227 if (printing)
1228 really_copy_file(x, y, filename);
1231 void real_output_file::transparent_char(unsigned char c)
1233 if (printing)
1234 really_transparent_char(c);
1237 void real_output_file::print_line(hunits x, vunits y, node *n,
1238 vunits before, vunits after)
1240 if (printing)
1241 really_print_line(x, y, n, before, after);
1242 delete_node_list(n);
1245 void real_output_file::really_copy_file(hunits, vunits, const char *)
1247 // do nothing
1251 /* ascii_output_file */
1253 void ascii_output_file::really_transparent_char(unsigned char c)
1255 putc(c, fp);
1258 void ascii_output_file::really_print_line(hunits, vunits, node *n, vunits, vunits)
1260 while (n != 0) {
1261 n->ascii_print(this);
1262 n = n->next;
1264 fputc('\n', fp);
1267 void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/)
1269 fputs("<beginning of page>\n", fp);
1272 ascii_output_file::ascii_output_file()
1276 /* suppress_output_file */
1278 suppress_output_file::suppress_output_file()
1282 void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits)
1286 void suppress_output_file::really_begin_page(int, vunits)
1290 void suppress_output_file::really_transparent_char(unsigned char)
1294 /* glyphs, ligatures, kerns, discretionary breaks */
1296 class glyph_node : public node {
1297 static glyph_node *free_list;
1298 protected:
1299 charinfo *ci;
1300 tfont *tf;
1301 #ifdef STORE_WIDTH
1302 hunits wid;
1303 glyph_node(charinfo *, tfont *, hunits, node * = 0);
1304 #endif
1305 public:
1306 void *operator new(size_t);
1307 void operator delete(void *);
1308 glyph_node(charinfo *, tfont *, node * = 0);
1309 ~glyph_node() {}
1310 node *copy();
1311 node *merge_glyph_node(glyph_node *);
1312 node *merge_self(node *);
1313 hunits width();
1314 node *last_char_node();
1315 units size();
1316 void vertical_extent(vunits *, vunits *);
1317 hunits subscript_correction();
1318 hunits italic_correction();
1319 hunits left_italic_correction();
1320 hunits skew();
1321 hyphenation_type get_hyphenation_type();
1322 tfont *get_tfont();
1323 void tprint(troff_output_file *);
1324 void zero_width_tprint(troff_output_file *);
1325 hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
1326 node *add_self(node *, hyphen_list **);
1327 int ends_sentence();
1328 int overlaps_vertically();
1329 int overlaps_horizontally();
1330 void ascii_print(ascii_output_file *);
1331 void asciify(macro *);
1332 int character_type();
1333 int same(node *);
1334 const char *type();
1337 glyph_node *glyph_node::free_list = 0;
1339 class ligature_node : public glyph_node {
1340 node *n1;
1341 node *n2;
1342 #ifdef STORE_WIDTH
1343 ligature_node(charinfo *, tfont *, hunits, node *gn1, node *gn2, node *x = 0);
1344 #endif
1345 public:
1346 void *operator new(size_t);
1347 void operator delete(void *);
1348 ligature_node(charinfo *, tfont *, node *gn1, node *gn2, node *x = 0);
1349 ~ligature_node();
1350 node *copy();
1351 node *add_self(node *, hyphen_list **);
1352 hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
1353 void ascii_print(ascii_output_file *);
1354 void asciify(macro *);
1355 int same(node *);
1356 const char *type();
1359 class kern_pair_node : public node {
1360 hunits amount;
1361 node *n1;
1362 node *n2;
1363 public:
1364 kern_pair_node(hunits n, node *first, node *second, node *x = 0);
1365 ~kern_pair_node();
1366 node *copy();
1367 node *merge_glyph_node(glyph_node *);
1368 node *add_self(node *, hyphen_list **);
1369 hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
1370 node *add_discretionary_hyphen();
1371 hunits width();
1372 node *last_char_node();
1373 hunits italic_correction();
1374 hunits subscript_correction();
1375 void tprint(troff_output_file *);
1376 hyphenation_type get_hyphenation_type();
1377 int ends_sentence();
1378 void ascii_print(ascii_output_file *);
1379 void asciify(macro *);
1380 int same(node *);
1381 const char *type();
1384 class dbreak_node : public node {
1385 node *none;
1386 node *pre;
1387 node *post;
1388 public:
1389 dbreak_node(node *n, node *p, node *x = 0);
1390 ~dbreak_node();
1391 node *copy();
1392 node *merge_glyph_node(glyph_node *);
1393 node *add_discretionary_hyphen();
1394 hunits width();
1395 node *last_char_node();
1396 hunits italic_correction();
1397 hunits subscript_correction();
1398 void tprint(troff_output_file *);
1399 breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
1400 int is_inner = 0);
1401 int nbreaks();
1402 int ends_sentence();
1403 void split(int, node **, node **);
1404 hyphenation_type get_hyphenation_type();
1405 void ascii_print(ascii_output_file *);
1406 void asciify(macro *);
1407 int same(node *);
1408 const char *type();
1411 void *glyph_node::operator new(size_t n)
1413 assert(n == sizeof(glyph_node));
1414 if (!free_list) {
1415 const int BLOCK = 1024;
1416 free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
1417 for (int i = 0; i < BLOCK - 1; i++)
1418 free_list[i].next = free_list + i + 1;
1419 free_list[BLOCK-1].next = 0;
1421 glyph_node *p = free_list;
1422 free_list = (glyph_node *)(free_list->next);
1423 p->next = 0;
1424 return p;
1427 void *ligature_node::operator new(size_t n)
1429 return new char[n];
1432 void glyph_node::operator delete(void *p)
1434 if (p) {
1435 ((glyph_node *)p)->next = free_list;
1436 free_list = (glyph_node *)p;
1440 void ligature_node::operator delete(void *p)
1442 delete p;
1445 glyph_node::glyph_node(charinfo *c, tfont *t, node *x)
1446 : ci(c), tf(t), node(x)
1448 #ifdef STORE_WIDTH
1449 wid = tf->get_width(ci);
1450 #endif
1453 #ifdef STORE_WIDTH
1454 glyph_node::glyph_node(charinfo *c, tfont *t, hunits w, node *x)
1455 : ci(c), tf(t), wid(w), node(x)
1458 #endif
1460 node *glyph_node::copy()
1462 #ifdef STORE_WIDTH
1463 return new glyph_node(ci, tf, wid);
1464 #else
1465 return new glyph_node(ci, tf);
1466 #endif
1469 node *glyph_node::merge_self(node *nd)
1471 return nd->merge_glyph_node(this);
1474 int glyph_node::character_type()
1476 return tf->get_character_type(ci);
1479 node *glyph_node::add_self(node *n, hyphen_list **p)
1481 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
1482 next = 0;
1483 node *nn;
1484 if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
1485 next = n;
1486 nn = this;
1488 if ((*p)->hyphen)
1489 nn = nn->add_discretionary_hyphen();
1490 hyphen_list *pp = *p;
1491 *p = (*p)->next;
1492 delete pp;
1493 return nn;
1496 int glyph_node::overlaps_horizontally()
1498 return ci->overlaps_horizontally();
1501 int glyph_node::overlaps_vertically()
1503 return ci->overlaps_vertically();
1506 units glyph_node::size()
1508 return tf->get_size().to_units();
1511 hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail)
1513 return new hyphen_list(ci->get_hyphenation_code(), tail);
1517 tfont *node::get_tfont()
1519 return 0;
1522 tfont *glyph_node::get_tfont()
1524 return tf;
1527 node *node::merge_glyph_node(glyph_node * /*gn*/)
1529 return 0;
1532 node *glyph_node::merge_glyph_node(glyph_node *gn)
1534 if (tf == gn->tf) {
1535 charinfo *lig;
1536 if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
1537 node *next1 = next;
1538 next = 0;
1539 return new ligature_node(lig, tf, this, gn, next1);
1541 hunits kern;
1542 if (tf->get_kern(ci, gn->ci, &kern)) {
1543 node *next1 = next;
1544 next = 0;
1545 return new kern_pair_node(kern, this, gn, next1);
1548 return 0;
1551 #ifdef STORE_WIDTH
1552 inline
1553 #endif
1554 hunits glyph_node::width()
1556 #ifdef STORE_WIDTH
1557 return wid;
1558 #else
1559 return tf->get_width(ci);
1560 #endif
1563 node *glyph_node::last_char_node()
1565 return this;
1568 void glyph_node::vertical_extent(vunits *min, vunits *max)
1570 *min = -tf->get_char_height(ci);
1571 *max = tf->get_char_depth(ci);
1574 hunits glyph_node::skew()
1576 return tf->get_char_skew(ci);
1579 hunits glyph_node::subscript_correction()
1581 return tf->get_subscript_correction(ci);
1584 hunits glyph_node::italic_correction()
1586 return tf->get_italic_correction(ci);
1589 hunits glyph_node::left_italic_correction()
1591 return tf->get_left_italic_correction(ci);
1594 hyphenation_type glyph_node::get_hyphenation_type()
1596 return HYPHEN_MIDDLE;
1599 int glyph_node::ends_sentence()
1601 if (ci->ends_sentence())
1602 return 1;
1603 else if (ci->transparent())
1604 return 2;
1605 else
1606 return 0;
1609 void glyph_node::ascii_print(ascii_output_file *ascii)
1611 unsigned char c = ci->get_ascii_code();
1612 if (c != 0)
1613 ascii->outc(c);
1614 else
1615 ascii->outs(ci->nm.contents());
1618 ligature_node::ligature_node(charinfo *c, tfont *t,
1619 node *gn1, node *gn2, node *x)
1620 : glyph_node(c, t, x), n1(gn1), n2(gn2)
1624 #ifdef STORE_WIDTH
1625 ligature_node::ligature_node(charinfo *c, tfont *t, hunits w,
1626 node *gn1, node *gn2, node *x)
1627 : glyph_node(c, t, w, x), n1(gn1), n2(gn2)
1630 #endif
1632 ligature_node::~ligature_node()
1634 delete n1;
1635 delete n2;
1638 node *ligature_node::copy()
1640 #ifdef STORE_WIDTH
1641 return new ligature_node(ci, tf, wid, n1->copy(), n2->copy());
1642 #else
1643 return new ligature_node(ci, tf, n1->copy(), n2->copy());
1644 #endif
1647 void ligature_node::ascii_print(ascii_output_file *ascii)
1649 n1->ascii_print(ascii);
1650 n2->ascii_print(ascii);
1653 hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail)
1655 return n1->get_hyphen_list(n2->get_hyphen_list(tail));
1658 node *ligature_node::add_self(node *n, hyphen_list **p)
1660 n = n1->add_self(n, p);
1661 n = n2->add_self(n, p);
1662 n1 = n2 = 0;
1663 delete this;
1664 return n;
1667 kern_pair_node::kern_pair_node(hunits n, node *first, node *second, node *x)
1668 : node(x), n1(first), n2(second), amount(n)
1672 dbreak_node::dbreak_node(node *n, node *p, node *x)
1673 : node(x), none(n), pre(p), post(0)
1677 node *dbreak_node::merge_glyph_node(glyph_node *gn)
1679 glyph_node *gn2 = (glyph_node *)gn->copy();
1680 node *new_none = none ? none->merge_glyph_node(gn) : 0;
1681 node *new_post = post ? post->merge_glyph_node(gn2) : 0;
1682 if (new_none == 0 && new_post == 0) {
1683 delete gn2;
1684 return 0;
1686 if (new_none != 0)
1687 none = new_none;
1688 else {
1689 gn->next = none;
1690 none = gn;
1692 if (new_post != 0)
1693 post = new_post;
1694 else {
1695 gn2->next = post;
1696 post = gn2;
1698 return this;
1701 node *kern_pair_node::merge_glyph_node(glyph_node *gn)
1703 node *nd = n2->merge_glyph_node(gn);
1704 if (nd == 0)
1705 return 0;
1706 n2 = nd;
1707 nd = n2->merge_self(n1);
1708 if (nd) {
1709 nd->next = next;
1710 n1 = 0;
1711 n2 = 0;
1712 delete this;
1713 return nd;
1715 return this;
1719 hunits kern_pair_node::italic_correction()
1721 return n2->italic_correction();
1724 hunits kern_pair_node::subscript_correction()
1726 return n2->subscript_correction();
1729 node *kern_pair_node::add_discretionary_hyphen()
1731 tfont *tf = n2->get_tfont();
1732 if (tf) {
1733 if (tf->contains(soft_hyphen_char)) {
1734 node *next1 = next;
1735 next = 0;
1736 node *n = copy();
1737 glyph_node *gn = new glyph_node(soft_hyphen_char, tf);
1738 node *nn = n->merge_glyph_node(gn);
1739 if (nn == 0) {
1740 gn->next = n;
1741 nn = gn;
1743 return new dbreak_node(this, nn, next1);
1746 return this;
1750 kern_pair_node::~kern_pair_node()
1752 if (n1 != 0)
1753 delete n1;
1754 if (n2 != 0)
1755 delete n2;
1758 dbreak_node::~dbreak_node()
1760 delete_node_list(pre);
1761 delete_node_list(post);
1762 delete_node_list(none);
1765 node *kern_pair_node::copy()
1767 return new kern_pair_node(amount, n1->copy(), n2->copy());
1770 node *copy_node_list(node *n)
1772 node *p = 0;
1773 while (n != 0) {
1774 node *nn = n->copy();
1775 nn->next = p;
1776 p = nn;
1777 n = n->next;
1779 while (p != 0) {
1780 node *pp = p->next;
1781 p->next = n;
1782 n = p;
1783 p = pp;
1785 return n;
1788 void delete_node_list(node *n)
1790 while (n != 0) {
1791 node *tem = n;
1792 n = n->next;
1793 delete tem;
1797 node *dbreak_node::copy()
1799 dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre));
1800 p->post = copy_node_list(post);
1801 return p;
1804 hyphen_list *node::get_hyphen_list(hyphen_list *tail)
1806 return tail;
1810 hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail)
1812 return n1->get_hyphen_list(n2->get_hyphen_list(tail));
1815 class hyphen_inhibitor_node : public node {
1816 public:
1817 hyphen_inhibitor_node(node *nd = 0);
1818 node *copy();
1819 int same(node *);
1820 const char *type();
1821 hyphenation_type get_hyphenation_type();
1824 hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
1828 node *hyphen_inhibitor_node::copy()
1830 return new hyphen_inhibitor_node;
1833 int hyphen_inhibitor_node::same(node *)
1835 return 1;
1838 const char *hyphen_inhibitor_node::type()
1840 return "hyphen_inhibitor_node";
1843 hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
1845 return HYPHEN_INHIBIT;
1848 /* add_discretionary_hyphen methods */
1850 node *dbreak_node::add_discretionary_hyphen()
1852 if (post)
1853 post = post->add_discretionary_hyphen();
1854 if (none)
1855 none = none->add_discretionary_hyphen();
1856 return this;
1860 node *node::add_discretionary_hyphen()
1862 tfont *tf = get_tfont();
1863 if (!tf)
1864 return new hyphen_inhibitor_node(this);
1865 if (tf->contains(soft_hyphen_char)) {
1866 node *next1 = next;
1867 next = 0;
1868 node *n = copy();
1869 glyph_node *gn = new glyph_node(soft_hyphen_char, tf);
1870 node *n1 = n->merge_glyph_node(gn);
1871 if (n1 == 0) {
1872 gn->next = n;
1873 n1 = gn;
1875 return new dbreak_node(this, n1, next1);
1877 return this;
1881 node *node::merge_self(node *)
1883 return 0;
1886 node *node::add_self(node *n, hyphen_list ** /*p*/)
1888 next = n;
1889 return this;
1892 node *kern_pair_node::add_self(node *n, hyphen_list **p)
1894 n = n1->add_self(n, p);
1895 n = n2->add_self(n, p);
1896 n1 = n2 = 0;
1897 delete this;
1898 return n;
1902 hunits node::width()
1904 return H0;
1907 node *node::last_char_node()
1909 return 0;
1912 hunits hmotion_node::width()
1914 return n;
1917 units node::size()
1919 return points_to_units(10);
1922 hunits kern_pair_node::width()
1924 return n1->width() + n2->width() + amount;
1927 node *kern_pair_node::last_char_node()
1929 node *nd = n2->last_char_node();
1930 if (nd)
1931 return nd;
1932 return n1->last_char_node();
1935 hunits dbreak_node::width()
1937 hunits x = H0;
1938 for (node *n = none; n != 0; n = n->next)
1939 x += n->width();
1940 return x;
1943 node *dbreak_node::last_char_node()
1945 for (node *n = none; n; n = n->next) {
1946 node *last = n->last_char_node();
1947 if (last)
1948 return last;
1950 return 0;
1953 hunits dbreak_node::italic_correction()
1955 return none ? none->italic_correction() : H0;
1958 hunits dbreak_node::subscript_correction()
1960 return none ? none->subscript_correction() : H0;
1963 class italic_corrected_node : public node {
1964 node *n;
1965 hunits x;
1966 public:
1967 italic_corrected_node(node *, hunits, node * = 0);
1968 ~italic_corrected_node();
1969 node *copy();
1970 void ascii_print(ascii_output_file *);
1971 void asciify(macro *m);
1972 hunits width();
1973 node *last_char_node();
1974 void vertical_extent(vunits *, vunits *);
1975 int ends_sentence();
1976 int overlaps_horizontally();
1977 int overlaps_vertically();
1978 int same(node *);
1979 hyphenation_type get_hyphenation_type();
1980 tfont *get_tfont();
1981 hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
1982 int character_type();
1983 void tprint(troff_output_file *);
1984 hunits subscript_correction();
1985 hunits skew();
1986 node *add_self(node *, hyphen_list **);
1987 const char *type();
1990 node *node::add_italic_correction(hunits *width)
1992 hunits ic = italic_correction();
1993 if (ic.is_zero())
1994 return this;
1995 else {
1996 node *next1 = next;
1997 next = 0;
1998 *width += ic;
1999 return new italic_corrected_node(this, ic, next1);
2003 italic_corrected_node::italic_corrected_node(node *nn, hunits xx, node *p)
2004 : n(nn), x(xx), node(p)
2006 assert(n != 0);
2009 italic_corrected_node::~italic_corrected_node()
2011 delete n;
2014 node *italic_corrected_node::copy()
2016 return new italic_corrected_node(n->copy(), x);
2019 hunits italic_corrected_node::width()
2021 return n->width() + x;
2024 void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
2026 n->vertical_extent(min, max);
2029 void italic_corrected_node::tprint(troff_output_file *out)
2031 n->tprint(out);
2032 out->right(x);
2035 hunits italic_corrected_node::skew()
2037 return n->skew() - x/2;
2040 hunits italic_corrected_node::subscript_correction()
2042 return n->subscript_correction() - x;
2045 void italic_corrected_node::ascii_print(ascii_output_file *out)
2047 n->ascii_print(out);
2050 int italic_corrected_node::ends_sentence()
2052 return n->ends_sentence();
2055 int italic_corrected_node::overlaps_horizontally()
2057 return n->overlaps_horizontally();
2060 int italic_corrected_node::overlaps_vertically()
2062 return n->overlaps_vertically();
2065 node *italic_corrected_node::last_char_node()
2067 return n->last_char_node();
2070 tfont *italic_corrected_node::get_tfont()
2072 return n->get_tfont();
2075 hyphenation_type italic_corrected_node::get_hyphenation_type()
2077 return n->get_hyphenation_type();
2080 node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
2082 nd = n->add_self(nd, p);
2083 hunits not_interested;
2084 nd = nd->add_italic_correction(&not_interested);
2085 n = 0;
2086 delete this;
2087 return nd;
2090 hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail)
2092 return n->get_hyphen_list(tail);
2095 int italic_corrected_node::character_type()
2097 return n->character_type();
2100 class break_char_node : public node {
2101 node *ch;
2102 char break_code;
2103 public:
2104 break_char_node(node *, int, node * = 0);
2105 ~break_char_node();
2106 node *copy();
2107 hunits width();
2108 vunits vertical_width();
2109 node *last_char_node();
2110 int character_type();
2111 int ends_sentence();
2112 node *add_self(node *, hyphen_list **);
2113 hyphen_list *get_hyphen_list(hyphen_list *s = 0);
2114 void tprint(troff_output_file *);
2115 void zero_width_tprint(troff_output_file *);
2116 void ascii_print(ascii_output_file *);
2117 void asciify(macro *m);
2118 hyphenation_type get_hyphenation_type();
2119 int overlaps_vertically();
2120 int overlaps_horizontally();
2121 units size();
2122 tfont *get_tfont();
2123 int same(node *);
2124 const char *type();
2127 break_char_node::break_char_node(node *n, int c, node *x)
2128 : node(x), ch(n), break_code(c)
2132 break_char_node::~break_char_node()
2134 delete ch;
2137 node *break_char_node::copy()
2139 return new break_char_node(ch->copy(), break_code);
2142 hunits break_char_node::width()
2144 return ch->width();
2147 vunits break_char_node::vertical_width()
2149 return ch->vertical_width();
2152 node *break_char_node::last_char_node()
2154 return ch->last_char_node();
2157 int break_char_node::character_type()
2159 return ch->character_type();
2162 int break_char_node::ends_sentence()
2164 return ch->ends_sentence();
2167 node *break_char_node::add_self(node *n, hyphen_list **p)
2169 assert((*p)->hyphenation_code == 0);
2170 if ((*p)->breakable && (break_code & 1)) {
2171 n = new space_node(H0, n);
2172 n->freeze_space();
2174 next = n;
2175 n = this;
2176 if ((*p)->breakable && (break_code & 2)) {
2177 n = new space_node(H0, n);
2178 n->freeze_space();
2180 hyphen_list *pp = *p;
2181 *p = (*p)->next;
2182 delete pp;
2183 return n;
2186 hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail)
2188 return new hyphen_list(0, tail);
2191 hyphenation_type break_char_node::get_hyphenation_type()
2193 return HYPHEN_MIDDLE;
2196 void break_char_node::ascii_print(ascii_output_file *ascii)
2198 ch->ascii_print(ascii);
2201 int break_char_node::overlaps_vertically()
2203 return ch->overlaps_vertically();
2206 int break_char_node::overlaps_horizontally()
2208 return ch->overlaps_horizontally();
2211 units break_char_node::size()
2213 return ch->size();
2216 tfont *break_char_node::get_tfont()
2218 return ch->get_tfont();
2221 node *extra_size_node::copy()
2223 return new extra_size_node(n);
2226 node *vertical_size_node::copy()
2228 return new vertical_size_node(n);
2231 node *hmotion_node::copy()
2233 return new hmotion_node(n);
2236 node *space_char_hmotion_node::copy()
2238 return new space_char_hmotion_node(n);
2241 node *vmotion_node::copy()
2243 return new vmotion_node(n);
2246 node *dummy_node::copy()
2248 return new dummy_node;
2251 node *transparent_dummy_node::copy()
2253 return new transparent_dummy_node;
2256 hline_node::~hline_node()
2258 if (n)
2259 delete n;
2262 node *hline_node::copy()
2264 return new hline_node(x, n ? n->copy() : 0);
2267 hunits hline_node::width()
2269 return x < H0 ? H0 : x;
2273 vline_node::~vline_node()
2275 if (n)
2276 delete n;
2279 node *vline_node::copy()
2281 return new vline_node(x, n ? n->copy() : 0);
2284 hunits vline_node::width()
2286 return n == 0 ? H0 : n->width();
2290 zero_width_node::zero_width_node(node *nd) : n(nd)
2294 zero_width_node::~zero_width_node()
2296 delete_node_list(n);
2299 node *zero_width_node::copy()
2301 return new zero_width_node(copy_node_list(n));
2304 int node_list_character_type(node *p)
2306 int t = 0;
2307 for (; p; p = p->next)
2308 t |= p->character_type();
2309 return t;
2312 int zero_width_node::character_type()
2314 return node_list_character_type(n);
2317 void node_list_vertical_extent(node *p, vunits *min, vunits *max)
2319 *min = V0;
2320 *max = V0;
2321 vunits cur_vpos = V0;
2322 vunits v1, v2;
2323 for (; p; p = p->next) {
2324 p->vertical_extent(&v1, &v2);
2325 v1 += cur_vpos;
2326 if (v1 < *min)
2327 *min = v1;
2328 v2 += cur_vpos;
2329 if (v2 > *max)
2330 *max = v2;
2331 cur_vpos += p->vertical_width();
2335 void zero_width_node::vertical_extent(vunits *min, vunits *max)
2337 node_list_vertical_extent(n, min, max);
2340 overstrike_node::overstrike_node() : max_width(H0), list(0)
2344 overstrike_node::~overstrike_node()
2346 delete_node_list(list);
2349 node *overstrike_node::copy()
2351 overstrike_node *on = new overstrike_node;
2352 for (node *tem = list; tem; tem = tem->next)
2353 on->overstrike(tem->copy());
2354 return on;
2357 void overstrike_node::overstrike(node *n)
2359 if (n == 0)
2360 return;
2361 hunits w = n->width();
2362 if (w > max_width)
2363 max_width = w;
2364 for (node **p = &list; *p; p = &(*p)->next)
2366 n->next = 0;
2367 *p = n;
2370 hunits overstrike_node::width()
2372 return max_width;
2375 bracket_node::bracket_node() : max_width(H0), list(0)
2379 bracket_node::~bracket_node()
2381 delete_node_list(list);
2384 node *bracket_node::copy()
2386 bracket_node *on = new bracket_node;
2387 for (node *tem = list; tem; tem = tem->next)
2388 on->bracket(tem->copy());
2389 return on;
2393 void bracket_node::bracket(node *n)
2395 if (n == 0)
2396 return;
2397 hunits w = n->width();
2398 if (w > max_width)
2399 max_width = w;
2400 n->next = list;
2401 list = n;
2404 hunits bracket_node::width()
2406 return max_width;
2409 int node::nspaces()
2411 return 0;
2414 int node::merge_space(hunits)
2416 return 0;
2419 #if 0
2420 space_node *space_node::free_list = 0;
2422 void *space_node::operator new(size_t n)
2424 assert(n == sizeof(space_node));
2425 if (!free_list) {
2426 free_list = (space_node *)new char[sizeof(space_node)*BLOCK];
2427 for (int i = 0; i < BLOCK - 1; i++)
2428 free_list[i].next = free_list + i + 1;
2429 free_list[BLOCK-1].next = 0;
2431 space_node *p = free_list;
2432 free_list = (space_node *)(free_list->next);
2433 p->next = 0;
2434 return p;
2437 inline void space_node::operator delete(void *p)
2439 if (p) {
2440 ((space_node *)p)->next = free_list;
2441 free_list = (space_node *)p;
2444 #endif
2446 space_node::space_node(hunits nn, node *p) : node(p), n(nn), set(0)
2450 space_node::space_node(hunits nn, int s, node *p) : node(p), n(nn), set(s)
2454 #if 0
2455 space_node::~space_node()
2458 #endif
2460 node *space_node::copy()
2462 return new space_node(n, set);
2465 int space_node::nspaces()
2467 return set ? 0 : 1;
2470 int space_node::merge_space(hunits h)
2472 n += h;
2473 return 1;
2476 hunits space_node::width()
2478 return n;
2481 void node::spread_space(int*, hunits*)
2485 void space_node::spread_space(int *nspaces, hunits *desired_space)
2487 if (!set) {
2488 assert(*nspaces > 0);
2489 if (*nspaces == 1) {
2490 n += *desired_space;
2491 *desired_space = H0;
2493 else {
2494 hunits extra = *desired_space / *nspaces;
2495 *desired_space -= extra;
2496 n += extra;
2498 *nspaces -= 1;
2499 set = 1;
2503 void node::freeze_space()
2507 void space_node::freeze_space()
2509 set = 1;
2512 diverted_space_node::diverted_space_node(vunits d, node *p)
2513 : node(p), n(d)
2517 node *diverted_space_node::copy()
2519 return new diverted_space_node(n);
2522 diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
2523 : node(p), filename(s)
2527 node *diverted_copy_file_node::copy()
2529 return new diverted_copy_file_node(filename);
2532 int node::ends_sentence()
2534 return 0;
2537 int kern_pair_node::ends_sentence()
2539 switch (n2->ends_sentence()) {
2540 case 0:
2541 return 0;
2542 case 1:
2543 return 1;
2544 case 2:
2545 break;
2546 default:
2547 assert(0);
2549 return n1->ends_sentence();
2552 int node_list_ends_sentence(node *n)
2554 for (; n != 0; n = n->next)
2555 switch (n->ends_sentence()) {
2556 case 0:
2557 return 0;
2558 case 1:
2559 return 1;
2560 case 2:
2561 break;
2562 default:
2563 assert(0);
2565 return 2;
2569 int dbreak_node::ends_sentence()
2571 return node_list_ends_sentence(none);
2575 int node::overlaps_horizontally()
2577 return 0;
2580 int node::overlaps_vertically()
2582 return 0;
2585 int node::discardable()
2587 return 0;
2590 int space_node::discardable()
2592 return set ? 0 : 1;
2596 vunits node::vertical_width()
2598 return V0;
2601 vunits vline_node::vertical_width()
2603 return x;
2606 vunits vmotion_node::vertical_width()
2608 return n;
2611 int node::character_type()
2613 return 0;
2616 hunits node::subscript_correction()
2618 return H0;
2621 hunits node::italic_correction()
2623 return H0;
2626 hunits node::left_italic_correction()
2628 return H0;
2631 hunits node::skew()
2633 return H0;
2637 /* vertical_extent methods */
2639 void node::vertical_extent(vunits *min, vunits *max)
2641 vunits v = vertical_width();
2642 if (v < V0) {
2643 *min = v;
2644 *max = V0;
2646 else {
2647 *max = v;
2648 *min = V0;
2652 void vline_node::vertical_extent(vunits *min, vunits *max)
2654 if (n == 0)
2655 node::vertical_extent(min, max);
2656 else {
2657 vunits cmin, cmax;
2658 n->vertical_extent(&cmin, &cmax);
2659 vunits h = n->size();
2660 if (x < V0) {
2661 if (-x < h) {
2662 *min = x;
2663 *max = V0;
2665 else {
2666 // we print the first character and then move up, so
2667 *max = cmax;
2668 // we print the last character and then move up h
2669 *min = cmin + h;
2670 if (*min > V0)
2671 *min = V0;
2672 *min += x;
2675 else {
2676 if (x < h) {
2677 *max = x;
2678 *min = V0;
2680 else {
2681 // we move down by h and then print the first character, so
2682 *min = cmin + h;
2683 if (*min > V0)
2684 *min = V0;
2685 *max = x + cmax;
2691 /* ascii_print methods */
2694 static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n)
2696 if (n == 0)
2697 return;
2698 ascii_print_reverse_node_list(ascii, n->next);
2699 n->ascii_print(ascii);
2702 void dbreak_node::ascii_print(ascii_output_file *ascii)
2704 ascii_print_reverse_node_list(ascii, none);
2707 void kern_pair_node::ascii_print(ascii_output_file *ascii)
2709 n1->ascii_print(ascii);
2710 n2->ascii_print(ascii);
2714 void node::ascii_print(ascii_output_file *)
2718 void space_node::ascii_print(ascii_output_file *ascii)
2720 if (!n.is_zero())
2721 ascii->outc(' ');
2724 void hmotion_node::ascii_print(ascii_output_file *ascii)
2726 // this is pretty arbitrary
2727 if (n >= points_to_units(2))
2728 ascii->outc(' ');
2731 void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
2733 ascii->outc(' ');
2736 /* asciify methods */
2738 void node::asciify(macro *m)
2740 m->append(this);
2743 void glyph_node::asciify(macro *m)
2745 unsigned char c = ci->get_ascii_code();
2746 if (c != 0) {
2747 m->append(c);
2748 delete this;
2750 else
2751 m->append(this);
2754 void kern_pair_node::asciify(macro *m)
2756 n1->asciify(m);
2757 n2->asciify(m);
2758 n1 = n2 = 0;
2759 delete this;
2762 static void asciify_reverse_node_list(macro *m, node *n)
2764 if (n == 0)
2765 return;
2766 asciify_reverse_node_list(m, n->next);
2767 n->asciify(m);
2770 void dbreak_node::asciify(macro *m)
2772 asciify_reverse_node_list(m, none);
2773 none = 0;
2774 delete this;
2777 void ligature_node::asciify(macro *m)
2779 n1->asciify(m);
2780 n2->asciify(m);
2781 n1 = n2 = 0;
2782 delete this;
2785 void break_char_node::asciify(macro *m)
2787 ch->asciify(m);
2788 ch = 0;
2789 delete this;
2792 void italic_corrected_node::asciify(macro *m)
2794 n->asciify(m);
2795 n = 0;
2796 delete this;
2799 void left_italic_corrected_node::asciify(macro *m)
2801 if (n) {
2802 n->asciify(m);
2803 n = 0;
2805 delete this;
2808 space_char_hmotion_node::space_char_hmotion_node(hunits i, node *next)
2809 : hmotion_node(i, next)
2813 void space_char_hmotion_node::asciify(macro *m)
2815 m->append(' ');
2816 delete this;
2819 void line_start_node::asciify(macro *)
2821 delete this;
2824 void vertical_size_node::asciify(macro *)
2826 delete this;
2829 breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/,
2830 breakpoint *rest, int /*is_inner*/)
2832 return rest;
2835 int node::nbreaks()
2837 return 0;
2840 breakpoint *space_node::get_breakpoints(hunits width, int ns, breakpoint *rest,
2841 int is_inner)
2843 if (next->discardable())
2844 return rest;
2845 breakpoint *bp = new breakpoint;
2846 bp->next = rest;
2847 bp->width = width;
2848 bp->nspaces = ns;
2849 bp->hyphenated = 0;
2850 if (is_inner) {
2851 assert(rest != 0);
2852 bp->index = rest->index + 1;
2853 bp->nd = rest->nd;
2855 else {
2856 bp->nd = this;
2857 bp->index = 0;
2859 return bp;
2862 int space_node::nbreaks()
2864 if (next->discardable())
2865 return 0;
2866 else
2867 return 1;
2870 static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
2871 int ns, breakpoint *rest)
2873 if (p != 0) {
2874 rest = p->get_breakpoints(*widthp,
2875 ns,
2876 node_list_get_breakpoints(p->next, widthp, ns,
2877 rest),
2879 *widthp += p->width();
2881 return rest;
2885 breakpoint *dbreak_node::get_breakpoints(hunits width, int ns,
2886 breakpoint *rest, int is_inner)
2888 breakpoint *bp = new breakpoint;
2889 bp->next = rest;
2890 bp->width = width;
2891 for (node *tem = pre; tem != 0; tem = tem->next)
2892 bp->width += tem->width();
2893 bp->nspaces = ns;
2894 bp->hyphenated = 1;
2895 if (is_inner) {
2896 assert(rest != 0);
2897 bp->index = rest->index + 1;
2898 bp->nd = rest->nd;
2900 else {
2901 bp->nd = this;
2902 bp->index = 0;
2904 return node_list_get_breakpoints(none, &width, ns, bp);
2907 int dbreak_node::nbreaks()
2909 int i = 1;
2910 for (node *tem = none; tem != 0; tem = tem->next)
2911 i += tem->nbreaks();
2912 return i;
2915 void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
2917 assert(0);
2920 void space_node::split(int where, node **pre, node **post)
2922 assert(where == 0);
2923 *pre = next;
2924 *post = 0;
2925 delete this;
2928 static void node_list_split(node *p, int *wherep, node **prep, node **postp)
2930 if (p == 0)
2931 return;
2932 int nb = p->nbreaks();
2933 node_list_split(p->next, wherep, prep, postp);
2934 if (*wherep < 0) {
2935 p->next = *postp;
2936 *postp = p;
2938 else if (*wherep < nb) {
2939 p->next = *prep;
2940 p->split(*wherep, prep, postp);
2942 else {
2943 p->next = *prep;
2944 *prep = p;
2946 *wherep -= nb;
2949 void dbreak_node::split(int where, node **prep, node **postp)
2951 assert(where >= 0);
2952 if (where == 0) {
2953 *postp = post;
2954 post = 0;
2955 if (pre == 0)
2956 *prep = next;
2957 else {
2958 for (node *tem = pre; tem->next != 0; tem = tem->next)
2960 tem->next = next;
2961 *prep = pre;
2963 pre = 0;
2964 delete this;
2966 else {
2967 *prep = next;
2968 where -= 1;
2969 node_list_split(none, &where, prep, postp);
2970 none = 0;
2971 delete this;
2976 hyphenation_type node::get_hyphenation_type()
2978 return HYPHEN_BOUNDARY;
2982 hyphenation_type dbreak_node::get_hyphenation_type()
2984 return HYPHEN_INHIBIT;
2987 hyphenation_type kern_pair_node::get_hyphenation_type()
2989 return HYPHEN_MIDDLE;
2992 hyphenation_type dummy_node::get_hyphenation_type()
2994 return HYPHEN_MIDDLE;
2997 hyphenation_type transparent_dummy_node::get_hyphenation_type()
2999 return HYPHEN_MIDDLE;
3002 int node::interpret(macro *)
3004 return 0;
3007 special_node::special_node(const macro &m)
3008 : mac(m)
3012 int special_node::same(node *n)
3014 return mac == ((special_node *)n)->mac;
3017 const char *special_node::type()
3019 return "special_node";
3022 node *special_node::copy()
3024 return new special_node(mac);
3027 void special_node::tprint_start(troff_output_file *out)
3029 out->start_special();
3032 void special_node::tprint_char(troff_output_file *out, unsigned char c)
3034 out->special_char(c);
3037 void special_node::tprint_end(troff_output_file *out)
3039 out->end_special();
3042 /* composite_node */
3044 class composite_node : public node {
3045 charinfo *ci;
3046 node *n;
3047 tfont *tf;
3048 public:
3049 composite_node(node *, charinfo *, tfont *, node * = 0);
3050 ~composite_node();
3051 node *copy();
3052 hunits width();
3053 node *last_char_node();
3054 units size();
3055 void tprint(troff_output_file *);
3056 hyphenation_type get_hyphenation_type();
3057 int overlaps_horizontally();
3058 int overlaps_vertically();
3059 void ascii_print(ascii_output_file *);
3060 void asciify(macro *);
3061 hyphen_list *get_hyphen_list(hyphen_list *tail);
3062 node *add_self(node *, hyphen_list **);
3063 tfont *get_tfont();
3064 int same(node *);
3065 const char *type();
3066 void vertical_extent(vunits *, vunits *);
3067 vunits vertical_width();
3070 composite_node::composite_node(node *p, charinfo *c, tfont *t, node *x)
3071 : node(x), n(p), ci(c), tf(t)
3075 composite_node::~composite_node()
3077 delete_node_list(n);
3080 node *composite_node::copy()
3082 return new composite_node(copy_node_list(n), ci, tf);
3085 hunits composite_node::width()
3087 hunits x;
3088 if (tf->get_constant_space(&x))
3089 return x;
3090 x = H0;
3091 for (node *tem = n; tem; tem = tem->next)
3092 x += tem->width();
3093 hunits offset;
3094 if (tf->get_bold(&offset))
3095 x += offset;
3096 x += tf->get_track_kern();
3097 return x;
3100 node *composite_node::last_char_node()
3102 return this;
3105 vunits composite_node::vertical_width()
3107 vunits v = V0;
3108 for (node *tem = n; tem; tem = tem->next)
3109 v += tem->vertical_width();
3110 return v;
3113 units composite_node::size()
3115 return tf->get_size().to_units();
3118 hyphenation_type composite_node::get_hyphenation_type()
3120 return HYPHEN_MIDDLE;
3123 int composite_node::overlaps_horizontally()
3125 return ci->overlaps_horizontally();
3128 int composite_node::overlaps_vertically()
3130 return ci->overlaps_vertically();
3133 void composite_node::asciify(macro *m)
3135 unsigned char c = ci->get_ascii_code();
3136 if (c != 0) {
3137 m->append(c);
3138 delete this;
3140 else
3141 m->append(this);
3144 void composite_node::ascii_print(ascii_output_file *ascii)
3146 unsigned char c = ci->get_ascii_code();
3147 if (c != 0)
3148 ascii->outc(c);
3149 else
3150 ascii->outs(ci->nm.contents());
3154 hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail)
3156 return new hyphen_list(ci->get_hyphenation_code(), tail);
3160 node *composite_node::add_self(node *nn, hyphen_list **p)
3162 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
3163 next = nn;
3164 nn = this;
3165 if ((*p)->hyphen)
3166 nn = nn->add_discretionary_hyphen();
3167 hyphen_list *pp = *p;
3168 *p = (*p)->next;
3169 delete pp;
3170 return nn;
3173 tfont *composite_node::get_tfont()
3175 return tf;
3178 node *reverse_node_list(node *n)
3180 node *r = 0;
3181 while (n) {
3182 node *tem = n;
3183 n = n->next;
3184 tem->next = r;
3185 r = tem;
3187 return r;
3190 void composite_node::vertical_extent(vunits *min, vunits *max)
3192 n = reverse_node_list(n);
3193 node_list_vertical_extent(n, min, max);
3194 n = reverse_node_list(n);
3197 word_space_node::word_space_node(hunits d, node *x) : space_node(d, x)
3201 word_space_node::word_space_node(hunits d, int s, node *x)
3202 : space_node(d, s, x)
3206 node *word_space_node::copy()
3208 return new word_space_node(n, set);
3211 void word_space_node::tprint(troff_output_file *out)
3213 out->word_marker();
3214 space_node::tprint(out);
3217 unbreakable_space_node::unbreakable_space_node(hunits d, node *x)
3218 : word_space_node(d, x)
3222 unbreakable_space_node::unbreakable_space_node(hunits d, int s, node *x)
3223 : word_space_node(d, s, x)
3227 node *unbreakable_space_node::copy()
3229 return new unbreakable_space_node(n, set);
3232 breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
3233 breakpoint *rest, int)
3235 return rest;
3238 int unbreakable_space_node::nbreaks()
3240 return 0;
3243 void unbreakable_space_node::split(int, node **, node **)
3245 assert(0);
3248 int unbreakable_space_node::merge_space(hunits)
3250 return 0;
3253 hvpair::hvpair()
3257 draw_node::draw_node(char c, hvpair *p, int np, font_size s)
3258 : code(c), npoints(np), sz(s)
3260 point = new hvpair[npoints];
3261 for (int i = 0; i < npoints; i++)
3262 point[i] = p[i];
3265 int draw_node::same(node *n)
3267 draw_node *nd = (draw_node *)n;
3268 if (code != nd->code || npoints != nd->npoints || sz != nd->sz)
3269 return 0;
3270 for (int i = 0; i < npoints; i++)
3271 if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
3272 return 0;
3273 return 1;
3276 const char *draw_node::type()
3278 return "draw_node";
3281 draw_node::~draw_node()
3283 if (point)
3284 a_delete point;
3287 hunits draw_node::width()
3289 hunits x = H0;
3290 for (int i = 0; i < npoints; i++)
3291 x += point[i].h;
3292 return x;
3295 vunits draw_node::vertical_width()
3297 if (code == 'e')
3298 return V0;
3299 vunits x = V0;
3300 for (int i = 0; i < npoints; i++)
3301 x += point[i].v;
3302 return x;
3305 node *draw_node::copy()
3307 return new draw_node(code, point, npoints, sz);
3310 void draw_node::tprint(troff_output_file *out)
3312 out->draw(code, point, npoints, sz);
3315 /* tprint methods */
3317 void glyph_node::tprint(troff_output_file *out)
3319 tfont *ptf = tf->get_plain();
3320 if (ptf == tf)
3321 out->put_char_width(ci, ptf, width(), H0);
3322 else {
3323 hunits offset;
3324 int bold = tf->get_bold(&offset);
3325 hunits w = ptf->get_width(ci);
3326 hunits k = H0;
3327 hunits x;
3328 int cs = tf->get_constant_space(&x);
3329 if (cs) {
3330 x -= w;
3331 if (bold)
3332 x -= offset;
3333 hunits x2 = x/2;
3334 out->right(x2);
3335 k = x - x2;
3337 else
3338 k = tf->get_track_kern();
3339 if (bold) {
3340 out->put_char(ci, ptf);
3341 out->right(offset);
3343 out->put_char_width(ci, ptf, w, k);
3347 void glyph_node::zero_width_tprint(troff_output_file *out)
3349 tfont *ptf = tf->get_plain();
3350 hunits offset;
3351 int bold = tf->get_bold(&offset);
3352 hunits x;
3353 int cs = tf->get_constant_space(&x);
3354 if (cs) {
3355 x -= ptf->get_width(ci);
3356 if (bold)
3357 x -= offset;
3358 x = x/2;
3359 out->right(x);
3361 out->put_char(ci, ptf);
3362 if (bold) {
3363 out->right(offset);
3364 out->put_char(ci, ptf);
3365 out->right(-offset);
3367 if (cs)
3368 out->right(-x);
3371 void break_char_node::tprint(troff_output_file *t)
3373 ch->tprint(t);
3376 void break_char_node::zero_width_tprint(troff_output_file *t)
3378 ch->zero_width_tprint(t);
3381 void hline_node::tprint(troff_output_file *out)
3383 if (x < H0) {
3384 out->right(x);
3385 x = -x;
3387 if (n == 0) {
3388 out->right(x);
3389 return;
3391 hunits w = n->width();
3392 if (w <= H0) {
3393 error("horizontal line drawing character must have positive width");
3394 out->right(x);
3395 return;
3397 int i = int(x/w);
3398 if (i == 0) {
3399 hunits xx = x - w;
3400 hunits xx2 = xx/2;
3401 out->right(xx2);
3402 n->tprint(out);
3403 out->right(xx - xx2);
3405 else {
3406 hunits rem = x - w*i;
3407 if (rem > H0)
3408 if (n->overlaps_horizontally()) {
3409 n->tprint(out);
3410 out->right(rem - w);
3412 else
3413 out->right(rem);
3414 while (--i >= 0)
3415 n->tprint(out);
3419 void vline_node::tprint(troff_output_file *out)
3421 if (n == 0) {
3422 out->down(x);
3423 return;
3425 vunits h = n->size();
3426 int overlaps = n->overlaps_vertically();
3427 vunits y = x;
3428 if (y < V0) {
3429 y = -y;
3430 int i = y / h;
3431 vunits rem = y - i*h;
3432 if (i == 0) {
3433 out->right(n->width());
3434 out->down(-rem);
3436 else {
3437 while (--i > 0) {
3438 n->zero_width_tprint(out);
3439 out->down(-h);
3441 if (overlaps) {
3442 n->zero_width_tprint(out);
3443 out->down(-rem);
3444 n->tprint(out);
3445 out->down(-h);
3447 else {
3448 n->tprint(out);
3449 out->down(-h - rem);
3453 else {
3454 int i = y / h;
3455 vunits rem = y - i*h;
3456 if (i == 0) {
3457 out->down(rem);
3458 out->right(n->width());
3460 else {
3461 out->down(h);
3462 if (overlaps)
3463 n->zero_width_tprint(out);
3464 out->down(rem);
3465 while (--i > 0) {
3466 n->zero_width_tprint(out);
3467 out->down(h);
3469 n->tprint(out);
3474 void zero_width_node::tprint(troff_output_file *out)
3476 if (!n)
3477 return;
3478 if (!n->next) {
3479 n->zero_width_tprint(out);
3480 return;
3482 int hpos = out->get_hpos();
3483 int vpos = out->get_vpos();
3484 node *tem = n;
3485 while (tem) {
3486 tem->tprint(out);
3487 tem = tem->next;
3489 out->moveto(hpos, vpos);
3492 void overstrike_node::tprint(troff_output_file *out)
3494 hunits pos = H0;
3495 for (node *tem = list; tem; tem = tem->next) {
3496 hunits x = (max_width - tem->width())/2;
3497 out->right(x - pos);
3498 pos = x;
3499 tem->zero_width_tprint(out);
3501 out->right(max_width - pos);
3504 void bracket_node::tprint(troff_output_file *out)
3506 if (list == 0)
3507 return;
3508 int npieces = 0;
3509 for (node *tem = list; tem; tem = tem->next)
3510 ++npieces;
3511 vunits h = list->size();
3512 vunits totalh = h*npieces;
3513 vunits y = (totalh - h)/2;
3514 out->down(y);
3515 for (tem = list; tem; tem = tem->next) {
3516 tem->zero_width_tprint(out);
3517 out->down(-h);
3519 out->right(max_width);
3520 out->down(totalh - y);
3523 void node::tprint(troff_output_file *)
3527 void node::zero_width_tprint(troff_output_file *out)
3529 int hpos = out->get_hpos();
3530 int vpos = out->get_vpos();
3531 tprint(out);
3532 out->moveto(hpos, vpos);
3535 void space_node::tprint(troff_output_file *out)
3537 out->right(n);
3540 void hmotion_node::tprint(troff_output_file *out)
3542 out->right(n);
3545 void vmotion_node::tprint(troff_output_file *out)
3547 out->down(n);
3550 void kern_pair_node::tprint(troff_output_file *out)
3552 n1->tprint(out);
3553 out->right(amount);
3554 n2->tprint(out);
3557 static void tprint_reverse_node_list(troff_output_file *out, node *n)
3559 if (n == 0)
3560 return;
3561 tprint_reverse_node_list(out, n->next);
3562 n->tprint(out);
3565 void dbreak_node::tprint(troff_output_file *out)
3567 tprint_reverse_node_list(out, none);
3570 void composite_node::tprint(troff_output_file *out)
3572 hunits bold_offset;
3573 int is_bold = tf->get_bold(&bold_offset);
3574 hunits track_kern = tf->get_track_kern();
3575 hunits constant_space;
3576 int is_constant_spaced = tf->get_constant_space(&constant_space);
3577 hunits x = H0;
3578 if (is_constant_spaced) {
3579 x = constant_space;
3580 for (node *tem = n; tem; tem = tem->next)
3581 x -= tem->width();
3582 if (is_bold)
3583 x -= bold_offset;
3584 hunits x2 = x/2;
3585 out->right(x2);
3586 x -= x2;
3588 if (is_bold) {
3589 int hpos = out->get_hpos();
3590 int vpos = out->get_vpos();
3591 tprint_reverse_node_list(out, n);
3592 out->moveto(hpos, vpos);
3593 out->right(bold_offset);
3595 tprint_reverse_node_list(out, n);
3596 if (is_constant_spaced)
3597 out->right(x);
3598 else
3599 out->right(track_kern);
3602 node *make_composite_node(charinfo *s, environment *env)
3604 int fontno = env_definite_font(env);
3605 if (fontno < 0) {
3606 error("no current font");
3607 return 0;
3609 assert(fontno < font_table_size && font_table[fontno] != 0);
3610 node *n = charinfo_to_node_list(s, env);
3611 font_size fs = env->get_font_size();
3612 int char_height = env->get_char_height();
3613 int char_slant = env->get_char_slant();
3614 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
3615 fontno);
3616 if (env->is_composite())
3617 tf = tf->get_plain();
3618 return new composite_node(n, s, tf);
3621 node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0)
3623 int fontno = env_definite_font(env);
3624 if (fontno < 0) {
3625 error("no current font");
3626 return 0;
3628 assert(fontno < font_table_size && font_table[fontno] != 0);
3629 int fn = fontno;
3630 int found = font_table[fontno]->contains(s);
3631 if (!found) {
3632 if (s->numbered()) {
3633 if (!no_error_message)
3634 warning(WARN_CHAR, "can't find numbered character %1",
3635 s->get_number());
3636 return 0;
3638 special_font_list *sf = font_table[fontno]->sf;
3639 while (sf != 0 && !found) {
3640 fn = sf->n;
3641 if (font_table[fn])
3642 found = font_table[fn]->contains(s);
3643 sf = sf->next;
3645 if (!found) {
3646 sf = global_special_fonts;
3647 while (sf != 0 && !found) {
3648 fn = sf->n;
3649 if (font_table[fn])
3650 found = font_table[fn]->contains(s);
3651 sf = sf->next;
3654 if (!found
3655 #if 0
3656 && global_special_fonts == 0 && font_table[fontno]->sf == 0
3657 #endif
3659 for (fn = 0; fn < font_table_size; fn++)
3660 if (font_table[fn]
3661 && font_table[fn]->is_special()
3662 && font_table[fn]->contains(s)) {
3663 found = 1;
3664 break;
3667 if (!found) {
3668 if (!no_error_message && s->first_time_not_found()) {
3669 unsigned char input_code = s->get_ascii_code();
3670 if (input_code != 0) {
3671 if (csgraph(input_code))
3672 warning(WARN_CHAR, "can't find character `%1'", input_code);
3673 else
3674 warning(WARN_CHAR, "can't find character with input code %1",
3675 int(input_code));
3677 else
3678 warning(WARN_CHAR, "can't find special character `%1'",
3679 s->nm.contents());
3681 return 0;
3684 font_size fs = env->get_font_size();
3685 int char_height = env->get_char_height();
3686 int char_slant = env->get_char_slant();
3687 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn);
3688 if (env->is_composite())
3689 tf = tf->get_plain();
3690 return new glyph_node(s, tf);
3693 node *make_node(charinfo *ci, environment *env)
3695 switch (ci->get_special_translation()) {
3696 case charinfo::TRANSLATE_SPACE:
3697 return new space_char_hmotion_node(env->get_space_width());
3698 case charinfo::TRANSLATE_DUMMY:
3699 return new dummy_node;
3700 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
3701 error("translation to \\% ignored in this context");
3702 break;
3704 charinfo *tem = ci->get_translation();
3705 if (tem)
3706 ci = tem;
3707 macro *mac = ci->get_macro();
3708 if (mac)
3709 return make_composite_node(ci, env);
3710 else
3711 return make_glyph_node(ci, env);
3714 int character_exists(charinfo *ci, environment *env)
3716 if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
3717 return 1;
3718 charinfo *tem = ci->get_translation();
3719 if (tem)
3720 ci = tem;
3721 if (ci->get_macro())
3722 return 1;
3723 node *nd = make_glyph_node(ci, env, 1);
3724 if (nd) {
3725 delete nd;
3726 return 1;
3728 return 0;
3731 node *node::add_char(charinfo *ci, environment *env, hunits *widthp)
3733 node *res;
3734 switch (ci->get_special_translation()) {
3735 case charinfo::TRANSLATE_SPACE:
3736 res = new space_char_hmotion_node(env->get_space_width(), this);
3737 *widthp += res->width();
3738 return res;
3739 case charinfo::TRANSLATE_DUMMY:
3740 return new dummy_node(this);
3741 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
3742 return add_discretionary_hyphen();
3744 charinfo *tem = ci->get_translation();
3745 if (tem)
3746 ci = tem;
3747 macro *mac = ci->get_macro();
3748 if (mac) {
3749 res = make_composite_node(ci, env);
3750 if (res) {
3751 res->next = this;
3752 *widthp += res->width();
3754 else
3755 return this;
3757 else {
3758 node *gn = make_glyph_node(ci, env);
3759 if (gn == 0)
3760 return this;
3761 else {
3762 hunits old_width = width();
3763 node *p = gn->merge_self(this);
3764 if (p == 0) {
3765 *widthp += gn->width();
3766 gn->next = this;
3767 res = gn;
3769 else {
3770 *widthp += p->width() - old_width;
3771 res = p;
3775 int break_code = 0;
3776 if (ci->can_break_before())
3777 break_code = 1;
3778 if (ci->can_break_after())
3779 break_code |= 2;
3780 if (break_code) {
3781 node *next1 = res->next;
3782 res->next = 0;
3783 res = new break_char_node(res, break_code, next1);
3785 return res;
3789 #ifdef __GNUG__
3790 inline
3791 #endif
3792 int same_node(node *n1, node *n2)
3794 if (n1 != 0) {
3795 if (n2 != 0)
3796 return n1->type() == n2->type() && n1->same(n2);
3797 else
3798 return 0;
3800 else
3801 return n2 == 0;
3804 int same_node_list(node *n1, node *n2)
3806 while (n1 && n2) {
3807 if (n1->type() != n2->type() || !n1->same(n2))
3808 return 0;
3809 n1 = n1->next;
3810 n2 = n2->next;
3812 return !n1 && !n2;
3815 int extra_size_node::same(node *nd)
3817 return n == ((extra_size_node *)nd)->n;
3820 const char *extra_size_node::type()
3822 return "extra_size_node";
3825 int vertical_size_node::same(node *nd)
3827 return n == ((vertical_size_node *)nd)->n;
3830 const char *vertical_size_node::type()
3832 return "vertical_size_node";
3835 int hmotion_node::same(node *nd)
3837 return n == ((hmotion_node *)nd)->n;
3840 const char *hmotion_node::type()
3842 return "hmotion_node";
3845 int space_char_hmotion_node::same(node *nd)
3847 return n == ((space_char_hmotion_node *)nd)->n;
3850 const char *space_char_hmotion_node::type()
3852 return "space_char_hmotion_node";
3855 int vmotion_node::same(node *nd)
3857 return n == ((vmotion_node *)nd)->n;
3860 const char *vmotion_node::type()
3862 return "vmotion_node";
3865 int hline_node::same(node *nd)
3867 return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n);
3870 const char *hline_node::type()
3872 return "hline_node";
3875 int vline_node::same(node *nd)
3877 return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n);
3880 const char *vline_node::type()
3882 return "vline_node";
3885 int dummy_node::same(node * /*nd*/)
3887 return 1;
3890 const char *dummy_node::type()
3892 return "dummy_node";
3895 int transparent_dummy_node::same(node * /*nd*/)
3897 return 1;
3900 const char *transparent_dummy_node::type()
3902 return "transparent_dummy_node";
3905 int transparent_dummy_node::ends_sentence()
3907 return 2;
3910 int zero_width_node::same(node *nd)
3912 return same_node_list(n, ((zero_width_node *)nd)->n);
3915 const char *zero_width_node::type()
3917 return "zero_width_node";
3920 int italic_corrected_node::same(node *nd)
3922 return (x == ((italic_corrected_node *)nd)->x
3923 && same_node(n, ((italic_corrected_node *)nd)->n));
3926 const char *italic_corrected_node::type()
3928 return "italic_corrected_node";
3932 left_italic_corrected_node::left_italic_corrected_node(node *x)
3933 : n(0), node(x)
3937 left_italic_corrected_node::~left_italic_corrected_node()
3939 delete n;
3942 node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
3944 if (n == 0) {
3945 hunits lic = gn->left_italic_correction();
3946 if (!lic.is_zero()) {
3947 x = lic;
3948 n = gn;
3949 return this;
3952 else {
3953 node *nd = n->merge_glyph_node(gn);
3954 if (nd) {
3955 n = nd;
3956 x = n->left_italic_correction();
3957 return this;
3960 return 0;
3963 node *left_italic_corrected_node::copy()
3965 left_italic_corrected_node *nd = new left_italic_corrected_node;
3966 if (n) {
3967 nd->n = n->copy();
3968 nd->x = x;
3970 return nd;
3973 void left_italic_corrected_node::tprint(troff_output_file *out)
3975 if (n) {
3976 out->right(x);
3977 n->tprint(out);
3981 const char *left_italic_corrected_node::type()
3983 return "left_italic_corrected_node";
3986 int left_italic_corrected_node::same(node *nd)
3988 return (x == ((left_italic_corrected_node *)nd)->x
3989 && same_node(n, ((left_italic_corrected_node *)nd)->n));
3992 void left_italic_corrected_node::ascii_print(ascii_output_file *out)
3994 if (n)
3995 n->ascii_print(out);
3998 hunits left_italic_corrected_node::width()
4000 return n ? n->width() + x : H0;
4003 void left_italic_corrected_node::vertical_extent(vunits *min, vunits *max)
4005 if (n)
4006 n->vertical_extent(min, max);
4007 else
4008 node::vertical_extent(min, max);
4011 hunits left_italic_corrected_node::skew()
4013 return n ? n->skew() + x/2 : H0;
4016 hunits left_italic_corrected_node::subscript_correction()
4018 return n ? n->subscript_correction() : H0;
4021 hunits left_italic_corrected_node::italic_correction()
4023 return n ? n->italic_correction() : H0;
4026 int left_italic_corrected_node::ends_sentence()
4028 return n ? n->ends_sentence() : 0;
4031 int left_italic_corrected_node::overlaps_horizontally()
4033 return n ? n->overlaps_horizontally() : 0;
4036 int left_italic_corrected_node::overlaps_vertically()
4038 return n ? n->overlaps_vertically() : 0;
4041 node *left_italic_corrected_node::last_char_node()
4043 return n ? n->last_char_node() : 0;
4046 tfont *left_italic_corrected_node::get_tfont()
4048 return n ? n->get_tfont() : 0;
4051 hyphenation_type left_italic_corrected_node::get_hyphenation_type()
4053 if (n)
4054 return n->get_hyphenation_type();
4055 else
4056 return HYPHEN_MIDDLE;
4059 hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail)
4061 return n ? n->get_hyphen_list(tail) : tail;
4064 node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
4066 if (n) {
4067 nd = new left_italic_corrected_node(nd);
4068 nd = n->add_self(nd, p);
4069 n = 0;
4070 delete this;
4072 return nd;
4075 int left_italic_corrected_node::character_type()
4077 return n ? n->character_type() : 0;
4080 int overstrike_node::same(node *nd)
4082 return same_node_list(list, ((overstrike_node *)nd)->list);
4085 const char *overstrike_node::type()
4087 return "overstrike_node";
4090 int bracket_node::same(node *nd)
4092 return same_node_list(list, ((bracket_node *)nd)->list);
4095 const char *bracket_node::type()
4097 return "bracket_node";
4100 int composite_node::same(node *nd)
4102 return ci == ((composite_node *)nd)->ci
4103 && same_node_list(n, ((composite_node *)nd)->n);
4106 const char *composite_node::type()
4108 return "composite_node";
4111 int glyph_node::same(node *nd)
4113 return ci == ((glyph_node *)nd)->ci && tf == ((glyph_node *)nd)->tf;
4116 const char *glyph_node::type()
4118 return "glyph_node";
4121 int ligature_node::same(node *nd)
4123 return (same_node(n1, ((ligature_node *)nd)->n1)
4124 && same_node(n2, ((ligature_node *)nd)->n2)
4125 && glyph_node::same(nd));
4128 const char *ligature_node::type()
4130 return "ligature_node";
4133 int kern_pair_node::same(node *nd)
4135 return (amount == ((kern_pair_node *)nd)->amount
4136 && same_node(n1, ((kern_pair_node *)nd)->n1)
4137 && same_node(n2, ((kern_pair_node *)nd)->n2));
4140 const char *kern_pair_node::type()
4142 return "kern_pair_node";
4145 int dbreak_node::same(node *nd)
4147 return (same_node_list(none, ((dbreak_node *)nd)->none)
4148 && same_node_list(pre, ((dbreak_node *)nd)->pre)
4149 && same_node_list(post, ((dbreak_node *)nd)->post));
4152 const char *dbreak_node::type()
4154 return "dbreak_node";
4157 int break_char_node::same(node *nd)
4159 return (break_code == ((break_char_node *)nd)->break_code
4160 && same_node(ch, ((break_char_node *)nd)->ch));
4163 const char *break_char_node::type()
4165 return "break_char_node";
4168 int line_start_node::same(node * /*nd*/)
4170 return 1;
4173 const char *line_start_node::type()
4175 return "line_start_node";
4178 int space_node::same(node *nd)
4180 return n == ((space_node *)nd)->n && set == ((space_node *)nd)->set;
4183 const char *space_node::type()
4185 return "space_node";
4188 int word_space_node::same(node *nd)
4190 return (n == ((word_space_node *)nd)->n
4191 && set == ((word_space_node *)nd)->set);
4194 const char *word_space_node::type()
4196 return "word_space_node";
4199 int unbreakable_space_node::same(node *nd)
4201 return (n == ((unbreakable_space_node *)nd)->n
4202 && set == ((unbreakable_space_node *)nd)->set);
4205 const char *unbreakable_space_node::type()
4207 return "unbreakable_space_node";
4210 int diverted_space_node::same(node *nd)
4212 return n == ((diverted_space_node *)nd)->n;
4215 const char *diverted_space_node::type()
4217 return "diverted_space_node";
4220 int diverted_copy_file_node::same(node *nd)
4222 return filename == ((diverted_copy_file_node *)nd)->filename;
4225 const char *diverted_copy_file_node::type()
4227 return "diverted_copy_file_node";
4230 // Grow the font_table so that its size is > n.
4232 static void grow_font_table(int n)
4234 assert(n >= font_table_size);
4235 font_info **old_font_table = font_table;
4236 int old_font_table_size = font_table_size;
4237 font_table_size = font_table_size ? (font_table_size*3)/2 : 10;
4238 if (font_table_size <= n)
4239 font_table_size = n + 10;
4240 font_table = new font_info *[font_table_size];
4241 if (old_font_table_size)
4242 memcpy(font_table, old_font_table,
4243 old_font_table_size*sizeof(font_info *));
4244 a_delete old_font_table;
4245 for (int i = old_font_table_size; i < font_table_size; i++)
4246 font_table[i] = 0;
4249 dictionary font_translation_dictionary(17);
4251 static symbol get_font_translation(symbol nm)
4253 void *p = font_translation_dictionary.lookup(nm);
4254 return p ? symbol((char *)p) : nm;
4257 dictionary font_dictionary(50);
4259 static int mount_font_no_translate(int n, symbol name, symbol external_name)
4261 assert(n >= 0);
4262 // We store the address of this char in font_dictionary to indicate
4263 // that we've previously tried to mount the font and failed.
4264 static char a_char;
4265 font *fm = 0;
4266 void *p = font_dictionary.lookup(external_name);
4267 if (p == 0) {
4268 int not_found;
4269 fm = font::load_font(external_name.contents(), &not_found);
4270 if (!fm) {
4271 if (not_found)
4272 warning(WARN_FONT, "can't find font `%1'", external_name.contents());
4273 font_dictionary.lookup(external_name, &a_char);
4274 return 0;
4276 font_dictionary.lookup(name, fm);
4278 else if (p == &a_char) {
4279 #if 0
4280 error("invalid font `%1'", external_name.contents());
4281 #endif
4282 return 0;
4284 else
4285 fm = (font*)p;
4286 if (n >= font_table_size) {
4287 if (n - font_table_size > 1000) {
4288 error("font position too much larger than first unused position");
4289 return 0;
4291 grow_font_table(n);
4293 else if (font_table[n] != 0)
4294 delete font_table[n];
4295 font_table[n] = new font_info(name, n, external_name, fm);
4296 invalidate_fontno(n);
4297 return 1;
4300 int mount_font(int n, symbol name, symbol external_name)
4302 assert(n >= 0);
4303 name = get_font_translation(name);
4304 if (external_name.is_null())
4305 external_name = name;
4306 else
4307 external_name = get_font_translation(external_name);
4308 return mount_font_no_translate(n, name, external_name);
4311 void mount_style(int n, symbol name)
4313 assert(n >= 0);
4314 if (n >= font_table_size) {
4315 if (n - font_table_size > 1000) {
4316 error("font position too much larger than first unused position");
4317 return;
4319 grow_font_table(n);
4321 else if (font_table[n] != 0)
4322 delete font_table[n];
4323 font_table[n] = new font_info(get_font_translation(name), n, NULL_SYMBOL, 0);
4324 invalidate_fontno(n);
4327 /* global functions */
4329 void font_translate()
4331 symbol from = get_name(1);
4332 if (!from.is_null()) {
4333 symbol to = get_name();
4334 if (to.is_null() || from == to)
4335 font_translation_dictionary.remove(from);
4336 else
4337 font_translation_dictionary.lookup(from, (void *)to.contents());
4339 skip_line();
4342 void font_position()
4344 int n;
4345 if (get_integer(&n)) {
4346 if (n < 0)
4347 error("negative font position");
4348 else {
4349 symbol internal_name = get_name(1);
4350 if (!internal_name.is_null()) {
4351 symbol external_name = get_long_name(0);
4352 mount_font(n, internal_name, external_name); // ignore error
4356 skip_line();
4359 font_family::font_family(symbol s)
4360 : nm(s), map_size(10)
4362 map = new int[map_size];
4363 for (int i = 0; i < map_size; i++)
4364 map[i] = -1;
4367 font_family::~font_family()
4369 a_delete map;
4372 int font_family::make_definite(int i)
4374 if (i >= 0) {
4375 if (i < map_size && map[i] >= 0)
4376 return map[i];
4377 else {
4378 if (i < font_table_size && font_table[i] != 0) {
4379 if (i >= map_size) {
4380 int old_map_size = map_size;
4381 int *old_map = map;
4382 map_size *= 3;
4383 map_size /= 2;
4384 if (i >= map_size)
4385 map_size = i + 10;
4386 map = new int[map_size];
4387 memcpy(map, old_map, old_map_size*sizeof(int));
4388 a_delete old_map;
4389 for (int j = old_map_size; j < map_size; j++)
4390 map[j] = -1;
4392 if (font_table[i]->is_style()) {
4393 symbol sty = font_table[i]->get_name();
4394 symbol f = concat(nm, sty);
4395 int n;
4396 // don't use symbol_fontno, because that might return a style
4397 // and because we don't want to translate the name
4398 for (n = 0; n < font_table_size; n++)
4399 if (font_table[n] != 0 && font_table[n]->is_named(f)
4400 && !font_table[n]->is_style())
4401 break;
4402 if (n >= font_table_size) {
4403 n = next_available_font_position();
4404 if (!mount_font_no_translate(n, f, f))
4405 return -1;
4407 return map[i] = n;
4409 else
4410 return map[i] = i;
4412 else
4413 return -1;
4416 else
4417 return -1;
4420 dictionary family_dictionary(5);
4422 font_family *lookup_family(symbol nm)
4424 font_family *f = (font_family *)family_dictionary.lookup(nm);
4425 if (!f) {
4426 f = new font_family(nm);
4427 (void)family_dictionary.lookup(nm, f);
4429 return f;
4432 static void invalidate_fontno(int n)
4434 assert(n >= 0 && n < font_table_size);
4435 dictionary_iterator iter(family_dictionary);
4436 symbol nm;
4437 font_family *fam;
4438 while (iter.get(&nm, (void **)&fam)) {
4439 int map_size = fam->map_size;
4440 if (n < map_size)
4441 fam->map[n] = -1;
4442 for (int i = 0; i < map_size; i++)
4443 if (fam->map[i] == n)
4444 fam->map[i] = -1;
4448 void style()
4450 int n;
4451 if (get_integer(&n)) {
4452 if (n < 0)
4453 error("negative font position");
4454 else {
4455 symbol internal_name = get_name(1);
4456 if (!internal_name.is_null())
4457 mount_style(n, internal_name);
4460 skip_line();
4463 static int get_fontno()
4465 int n;
4466 tok.skip();
4467 if (tok.delimiter()) {
4468 symbol s = get_name(1);
4469 if (!s.is_null()) {
4470 n = symbol_fontno(s);
4471 if (n < 0) {
4472 n = next_available_font_position();
4473 if (!mount_font(n, s))
4474 return -1;
4476 return curenv->get_family()->make_definite(n);
4479 else if (get_integer(&n)) {
4480 if (n < 0 || n >= font_table_size || font_table[n] == 0)
4481 error("bad font number");
4482 else
4483 return curenv->get_family()->make_definite(n);
4485 return -1;
4488 static int underline_fontno = 2;
4490 void underline_font()
4492 int n = get_fontno();
4493 if (n >= 0)
4494 underline_fontno = n;
4495 skip_line();
4498 int get_underline_fontno()
4500 return underline_fontno;
4503 static void read_special_fonts(special_font_list **sp)
4505 special_font_list *s = *sp;
4506 *sp = 0;
4507 while (s != 0) {
4508 special_font_list *tem = s;
4509 s = s->next;
4510 delete tem;
4512 special_font_list **p = sp;
4513 while (has_arg()) {
4514 int i = get_fontno();
4515 if (i >= 0) {
4516 special_font_list *tem = new special_font_list;
4517 tem->n = i;
4518 tem->next = 0;
4519 *p = tem;
4520 p = &(tem->next);
4525 void font_special_request()
4527 int n = get_fontno();
4528 if (n >= 0)
4529 read_special_fonts(&font_table[n]->sf);
4530 skip_line();
4534 void special_request()
4536 read_special_fonts(&global_special_fonts);
4537 skip_line();
4540 int next_available_font_position()
4542 for (int i = 1; i < font_table_size && font_table[i] != 0; i++)
4544 return i;
4547 int symbol_fontno(symbol s)
4549 s = get_font_translation(s);
4550 for (int i = 0; i < font_table_size; i++)
4551 if (font_table[i] != 0 && font_table[i]->is_named(s))
4552 return i;
4553 return -1;
4556 int is_good_fontno(int n)
4558 return n >= 0 && n < font_table_size && font_table[n] != NULL;
4561 int get_bold_fontno(int n)
4563 if (n >= 0 && n < font_table_size && font_table[n] != 0) {
4564 hunits offset;
4565 if (font_table[n]->get_bold(&offset))
4566 return offset.to_units() + 1;
4567 else
4568 return 0;
4570 else
4571 return 0;
4574 hunits env_digit_width(environment *env)
4576 node *n = make_glyph_node(charset_table['0'], env);
4577 if (n) {
4578 hunits x = n->width();
4579 delete n;
4580 return x;
4582 else
4583 return H0;
4586 hunits env_space_width(environment *env)
4588 int fn = env_definite_font(env);
4589 font_size fs = env->get_font_size();
4590 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
4591 return scale(fs.to_units()/3, env->get_space_size(), 12);
4592 else
4593 return font_table[fn]->get_space_width(fs, env->get_space_size());
4596 hunits env_sentence_space_width(environment *env)
4598 int fn = env_definite_font(env);
4599 font_size fs = env->get_font_size();
4600 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
4601 return scale(fs.to_units()/3, env->get_sentence_space_size(), 12);
4602 else
4603 return font_table[fn]->get_space_width(fs, env->get_sentence_space_size());
4606 hunits env_half_narrow_space_width(environment *env)
4608 int fn = env_definite_font(env);
4609 font_size fs = env->get_font_size();
4610 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
4611 return 0;
4612 else
4613 return font_table[fn]->get_half_narrow_space_width(fs);
4616 hunits env_narrow_space_width(environment *env)
4618 int fn = env_definite_font(env);
4619 font_size fs = env->get_font_size();
4620 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
4621 return 0;
4622 else
4623 return font_table[fn]->get_narrow_space_width(fs);
4626 void bold_font()
4628 int n = get_fontno();
4629 if (n >= 0) {
4630 if (has_arg()) {
4631 if (tok.delimiter()) {
4632 int f = get_fontno();
4633 if (f >= 0) {
4634 units offset;
4635 if (has_arg() && get_number(&offset, 'u') && offset >= 1)
4636 font_table[f]->set_conditional_bold(n, hunits(offset - 1));
4637 else
4638 font_table[f]->conditional_unbold(n);
4641 else {
4642 units offset;
4643 if (get_number(&offset, 'u') && offset >= 1)
4644 font_table[n]->set_bold(hunits(offset - 1));
4645 else
4646 font_table[n]->unbold();
4649 else
4650 font_table[n]->unbold();
4652 skip_line();
4655 track_kerning_function::track_kerning_function() : non_zero(0)
4659 track_kerning_function::track_kerning_function(int min_s, hunits min_a,
4660 int max_s, hunits max_a)
4661 : non_zero(1),
4662 min_size(min_s), min_amount(min_a),
4663 max_size(max_s), max_amount(max_a)
4667 int track_kerning_function::operator==(const track_kerning_function &tk)
4669 if (non_zero)
4670 return (tk.non_zero
4671 && min_size == tk.min_size
4672 && min_amount == tk.min_amount
4673 && max_size == tk.max_size
4674 && max_amount == tk.max_amount);
4675 else
4676 return !tk.non_zero;
4679 int track_kerning_function::operator!=(const track_kerning_function &tk)
4681 if (non_zero)
4682 return (!tk.non_zero
4683 || min_size != tk.min_size
4684 || min_amount != tk.min_amount
4685 || max_size != tk.max_size
4686 || max_amount != tk.max_amount);
4687 else
4688 return tk.non_zero;
4691 hunits track_kerning_function::compute(int size)
4693 if (non_zero) {
4694 if (max_size <= min_size)
4695 return min_amount;
4696 else if (size <= min_size)
4697 return min_amount;
4698 else if (size >= max_size)
4699 return max_amount;
4700 else
4701 return (scale(max_amount, size - min_size, max_size - min_size)
4702 + scale(min_amount, max_size - size, max_size - min_size));
4704 else
4705 return H0;
4708 void track_kern()
4710 int n = get_fontno();
4711 if (n >= 0) {
4712 int min_s, max_s;
4713 hunits min_a, max_a;
4714 if (has_arg()
4715 && get_number(&min_s, 'z')
4716 && get_hunits(&min_a, 'p')
4717 && get_number(&max_s, 'z')
4718 && get_hunits(&max_a, 'p')) {
4719 track_kerning_function tk(min_s, min_a, max_s, max_a);
4720 font_table[n]->set_track_kern(tk);
4722 else {
4723 track_kerning_function tk;
4724 font_table[n]->set_track_kern(tk);
4727 skip_line();
4730 void constant_space()
4732 int n = get_fontno();
4733 if (n >= 0) {
4734 int x, y;
4735 if (!has_arg() || !get_integer(&x))
4736 font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
4737 else {
4738 if (!has_arg() || !get_number(&y, 'z'))
4739 font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
4740 else
4741 font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
4742 scale(y*x,
4743 units_per_inch,
4744 36*72*sizescale));
4747 skip_line();
4750 void ligature()
4752 int lig;
4753 if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2)
4754 global_ligature_mode = lig;
4755 else
4756 global_ligature_mode = 1;
4757 skip_line();
4760 void kern_request()
4762 int k;
4763 if (has_arg() && get_integer(&k))
4764 global_kern_mode = k != 0;
4765 else
4766 global_kern_mode = 1;
4767 skip_line();
4770 void set_soft_hyphen_char()
4772 soft_hyphen_char = get_optional_char();
4773 if (!soft_hyphen_char)
4774 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
4775 skip_line();
4778 void init_output()
4780 if (suppress_output_flag)
4781 the_output = new suppress_output_file;
4782 else if (ascii_output_flag)
4783 the_output = new ascii_output_file;
4784 else
4785 the_output = new troff_output_file;
4788 class next_available_font_position_reg : public reg {
4789 public:
4790 const char *get_string();
4793 const char *next_available_font_position_reg::get_string()
4795 return itoa(next_available_font_position());
4798 class printing_reg : public reg {
4799 public:
4800 const char *get_string();
4803 const char *printing_reg::get_string()
4805 if (the_output)
4806 return the_output->is_printing() ? "1" : "0";
4807 else
4808 return "0";
4811 void init_node_requests()
4813 init_request("fp", font_position);
4814 init_request("sty", style);
4815 init_request("cs", constant_space);
4816 init_request("bd", bold_font);
4817 init_request("uf", underline_font);
4818 init_request("lg", ligature);
4819 init_request("kern", kern_request);
4820 init_request("tkf", track_kern);
4821 init_request("special", special_request);
4822 init_request("fspecial", font_special_request);
4823 init_request("ftr", font_translate);
4824 init_request("shc", set_soft_hyphen_char);
4825 number_reg_dictionary.define(".fp", new next_available_font_position_reg);
4826 number_reg_dictionary.define(".kern",
4827 new constant_int_reg(&global_kern_mode));
4828 number_reg_dictionary.define(".lg",
4829 new constant_int_reg(&global_ligature_mode));
4830 number_reg_dictionary.define(".P", new printing_reg);
4831 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);