* All affected files: Update postal address of FSF.
[s-roff.git] / src / roff / troff / node.cpp
blob046140f591243db59e1666620cb5aee6afab50c6
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
22 extern int debug_state;
24 #include "troff.h"
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
30 #include "dictionary.h"
31 #include "hvunits.h"
32 #include "stringclass.h"
33 #include "mtsm.h"
34 #include "env.h"
35 #include "request.h"
36 #include "node.h"
37 #include "token.h"
38 #include "div.h"
39 #include "reg.h"
40 #include "charinfo.h"
41 #include "font.h"
42 #include "input.h"
43 #include "geometry.h"
45 #include "nonposix.h"
47 #ifdef _POSIX_VERSION
49 #include <sys/wait.h>
51 #else /* not _POSIX_VERSION */
53 /* traditional Unix */
55 #define WIFEXITED(s) (((s) & 0377) == 0)
56 #define WEXITSTATUS(s) (((s) >> 8) & 0377)
57 #define WTERMSIG(s) ((s) & 0177)
58 #define WIFSTOPPED(s) (((s) & 0377) == 0177)
59 #define WSTOPSIG(s) (((s) >> 8) & 0377)
60 #define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
62 #endif /* not _POSIX_VERSION */
65 * how many boundaries of images have been written? Useful for
66 * debugging grohtml
69 int image_no = 0;
70 static int suppress_start_page = 0;
72 #define STORE_WIDTH 1
74 symbol HYPHEN_SYMBOL("hy");
76 // Character used when a hyphen is inserted at a line break.
77 static charinfo *soft_hyphen_char;
79 enum constant_space_type {
80 CONSTANT_SPACE_NONE,
81 CONSTANT_SPACE_RELATIVE,
82 CONSTANT_SPACE_ABSOLUTE
85 struct special_font_list {
86 int n;
87 special_font_list *next;
90 special_font_list *global_special_fonts;
91 static int global_ligature_mode = 1;
92 static int global_kern_mode = 1;
94 class track_kerning_function {
95 int non_zero;
96 units min_size;
97 hunits min_amount;
98 units max_size;
99 hunits max_amount;
100 public:
101 track_kerning_function();
102 track_kerning_function(units, hunits, units, hunits);
103 int operator==(const track_kerning_function &);
104 int operator!=(const track_kerning_function &);
105 hunits compute(int point_size);
108 // embolden fontno when this is the current font
110 struct conditional_bold {
111 conditional_bold *next;
112 int fontno;
113 hunits offset;
114 conditional_bold(int, hunits, conditional_bold * = 0);
117 class tfont;
119 class font_info {
120 tfont *last_tfont;
121 int number;
122 font_size last_size;
123 int last_height;
124 int last_slant;
125 symbol internal_name;
126 symbol external_name;
127 font *fm;
128 char is_bold;
129 hunits bold_offset;
130 track_kerning_function track_kern;
131 constant_space_type is_constant_spaced;
132 units constant_space;
133 int last_ligature_mode;
134 int last_kern_mode;
135 conditional_bold *cond_bold_list;
136 void flush();
137 public:
138 special_font_list *sf;
139 font_info(symbol, int, symbol, font *);
140 int contains(charinfo *);
141 void set_bold(hunits);
142 void unbold();
143 void set_conditional_bold(int, hunits);
144 void conditional_unbold(int);
145 void set_track_kern(track_kerning_function &);
146 void set_constant_space(constant_space_type, units = 0);
147 int is_named(symbol);
148 symbol get_name();
149 tfont *get_tfont(font_size, int, int, int);
150 hunits get_space_width(font_size, int);
151 hunits get_narrow_space_width(font_size);
152 hunits get_half_narrow_space_width(font_size);
153 int get_bold(hunits *);
154 int is_special();
155 int is_style();
156 friend symbol get_font_name(int, environment *);
157 friend symbol get_style_name(int);
160 class tfont_spec {
161 protected:
162 symbol name;
163 int input_position;
164 font *fm;
165 font_size size;
166 char is_bold;
167 char is_constant_spaced;
168 int ligature_mode;
169 int kern_mode;
170 hunits bold_offset;
171 hunits track_kern; // add this to the width
172 hunits constant_space_width;
173 int height;
174 int slant;
175 public:
176 tfont_spec(symbol, int, font *, font_size, int, int);
177 tfont_spec(const tfont_spec &spec) { *this = spec; }
178 tfont_spec plain();
179 int operator==(const tfont_spec &);
180 friend tfont *font_info::get_tfont(font_size fs, int, int, int);
183 class tfont : public tfont_spec {
184 static tfont *tfont_list;
185 tfont *next;
186 tfont *plain_version;
187 public:
188 tfont(tfont_spec &);
189 int contains(charinfo *);
190 hunits get_width(charinfo *c);
191 int get_bold(hunits *);
192 int get_constant_space(hunits *);
193 hunits get_track_kern();
194 tfont *get_plain();
195 font_size get_size();
196 symbol get_name();
197 charinfo *get_lig(charinfo *c1, charinfo *c2);
198 int get_kern(charinfo *c1, charinfo *c2, hunits *res);
199 int get_input_position();
200 int get_character_type(charinfo *);
201 int get_height();
202 int get_slant();
203 vunits get_char_height(charinfo *);
204 vunits get_char_depth(charinfo *);
205 hunits get_char_skew(charinfo *);
206 hunits get_italic_correction(charinfo *);
207 hunits get_left_italic_correction(charinfo *);
208 hunits get_subscript_correction(charinfo *);
209 friend tfont *make_tfont(tfont_spec &);
212 inline int env_definite_font(environment *env)
214 return env->get_family()->make_definite(env->get_font());
217 /* font_info functions */
219 static font_info **font_table = 0;
220 static int font_table_size = 0;
222 font_info::font_info(symbol nm, int n, symbol enm, font *f)
223 : last_tfont(0), number(n), last_size(0),
224 internal_name(nm), external_name(enm), fm(f),
225 is_bold(0), is_constant_spaced(CONSTANT_SPACE_NONE), last_ligature_mode(1),
226 last_kern_mode(1), cond_bold_list(0), sf(0)
230 inline int font_info::contains(charinfo *ci)
232 return fm != 0 && fm->contains(ci->get_index());
235 inline int font_info::is_special()
237 return fm != 0 && fm->is_special();
240 inline int font_info::is_style()
242 return fm == 0;
245 tfont *make_tfont(tfont_spec &spec)
247 for (tfont *p = tfont::tfont_list; p; p = p->next)
248 if (*p == spec)
249 return p;
250 return new tfont(spec);
253 // this is the current_font, fontno is where we found the character,
254 // presumably a special font
256 tfont *font_info::get_tfont(font_size fs, int height, int slant, int fontno)
258 if (last_tfont == 0 || fs != last_size
259 || height != last_height || slant != last_slant
260 || global_ligature_mode != last_ligature_mode
261 || global_kern_mode != last_kern_mode
262 || fontno != number) {
263 font_info *f = font_table[fontno];
264 tfont_spec spec(f->external_name, f->number, f->fm, fs, height, slant);
265 for (conditional_bold *p = cond_bold_list; p; p = p->next)
266 if (p->fontno == fontno) {
267 spec.is_bold = 1;
268 spec.bold_offset = p->offset;
269 break;
271 if (!spec.is_bold && is_bold) {
272 spec.is_bold = 1;
273 spec.bold_offset = bold_offset;
275 spec.track_kern = track_kern.compute(fs.to_scaled_points());
276 spec.ligature_mode = global_ligature_mode;
277 spec.kern_mode = global_kern_mode;
278 switch (is_constant_spaced) {
279 case CONSTANT_SPACE_NONE:
280 break;
281 case CONSTANT_SPACE_ABSOLUTE:
282 spec.is_constant_spaced = 1;
283 spec.constant_space_width = constant_space;
284 break;
285 case CONSTANT_SPACE_RELATIVE:
286 spec.is_constant_spaced = 1;
287 spec.constant_space_width
288 = scale(constant_space*fs.to_scaled_points(),
289 units_per_inch,
290 36*72*sizescale);
291 break;
292 default:
293 assert(0);
295 if (fontno != number)
296 return make_tfont(spec);
297 last_tfont = make_tfont(spec);
298 last_size = fs;
299 last_height = height;
300 last_slant = slant;
301 last_ligature_mode = global_ligature_mode;
302 last_kern_mode = global_kern_mode;
304 return last_tfont;
307 int font_info::get_bold(hunits *res)
309 if (is_bold) {
310 *res = bold_offset;
311 return 1;
313 else
314 return 0;
317 void font_info::unbold()
319 if (is_bold) {
320 is_bold = 0;
321 flush();
325 void font_info::set_bold(hunits offset)
327 if (!is_bold || offset != bold_offset) {
328 is_bold = 1;
329 bold_offset = offset;
330 flush();
334 void font_info::set_conditional_bold(int fontno, hunits offset)
336 for (conditional_bold *p = cond_bold_list; p; p = p->next)
337 if (p->fontno == fontno) {
338 if (offset != p->offset) {
339 p->offset = offset;
340 flush();
342 return;
344 cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
347 conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
348 : next(x), fontno(f), offset(h)
352 void font_info::conditional_unbold(int fontno)
354 for (conditional_bold **p = &cond_bold_list; *p; p = &(*p)->next)
355 if ((*p)->fontno == fontno) {
356 conditional_bold *tem = *p;
357 *p = (*p)->next;
358 delete tem;
359 flush();
360 return;
364 void font_info::set_constant_space(constant_space_type type, units x)
366 if (type != is_constant_spaced
367 || (type != CONSTANT_SPACE_NONE && x != constant_space)) {
368 flush();
369 is_constant_spaced = type;
370 constant_space = x;
374 void font_info::set_track_kern(track_kerning_function &tk)
376 if (track_kern != tk) {
377 track_kern = tk;
378 flush();
382 void font_info::flush()
384 last_tfont = 0;
387 int font_info::is_named(symbol s)
389 return internal_name == s;
392 symbol font_info::get_name()
394 return internal_name;
397 symbol get_font_name(int fontno, environment *env)
399 symbol f = font_table[fontno]->get_name();
400 if (font_table[fontno]->is_style()) {
401 return concat(env->get_family()->nm, f);
403 return f;
406 symbol get_style_name(int fontno)
408 if (font_table[fontno]->is_style())
409 return font_table[fontno]->get_name();
410 else
411 return EMPTY_SYMBOL;
414 hunits font_info::get_space_width(font_size fs, int space_sz)
416 if (is_constant_spaced == CONSTANT_SPACE_NONE)
417 return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
418 space_sz, 12);
419 else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
420 return constant_space;
421 else
422 return scale(constant_space*fs.to_scaled_points(),
423 units_per_inch, 36*72*sizescale);
426 hunits font_info::get_narrow_space_width(font_size fs)
428 charinfo *ci = get_charinfo(symbol("|"));
429 if (fm->contains(ci->get_index()))
430 return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
431 else
432 return hunits(fs.to_units()/6);
435 hunits font_info::get_half_narrow_space_width(font_size fs)
437 charinfo *ci = get_charinfo(symbol("^"));
438 if (fm->contains(ci->get_index()))
439 return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
440 else
441 return hunits(fs.to_units()/12);
444 /* tfont */
446 tfont_spec::tfont_spec(symbol nm, int n, font *f,
447 font_size s, int h, int sl)
448 : name(nm), input_position(n), fm(f), size(s),
449 is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1),
450 height(h), slant(sl)
452 if (height == size.to_scaled_points())
453 height = 0;
456 int tfont_spec::operator==(const tfont_spec &spec)
458 if (fm == spec.fm
459 && size == spec.size
460 && input_position == spec.input_position
461 && name == spec.name
462 && height == spec.height
463 && slant == spec.slant
464 && (is_bold
465 ? (spec.is_bold && bold_offset == spec.bold_offset)
466 : !spec.is_bold)
467 && track_kern == spec.track_kern
468 && (is_constant_spaced
469 ? (spec.is_constant_spaced
470 && constant_space_width == spec.constant_space_width)
471 : !spec.is_constant_spaced)
472 && ligature_mode == spec.ligature_mode
473 && kern_mode == spec.kern_mode)
474 return 1;
475 else
476 return 0;
479 tfont_spec tfont_spec::plain()
481 return tfont_spec(name, input_position, fm, size, height, slant);
484 hunits tfont::get_width(charinfo *c)
486 if (is_constant_spaced)
487 return constant_space_width;
488 else if (is_bold)
489 return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
490 + track_kern + bold_offset);
491 else
492 return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
493 + track_kern);
496 vunits tfont::get_char_height(charinfo *c)
498 vunits v = fm->get_height(c->get_index(), size.to_scaled_points());
499 if (height != 0 && height != size.to_scaled_points())
500 return scale(v, height, size.to_scaled_points());
501 else
502 return v;
505 vunits tfont::get_char_depth(charinfo *c)
507 vunits v = fm->get_depth(c->get_index(), size.to_scaled_points());
508 if (height != 0 && height != size.to_scaled_points())
509 return scale(v, height, size.to_scaled_points());
510 else
511 return v;
514 hunits tfont::get_char_skew(charinfo *c)
516 return hunits(fm->get_skew(c->get_index(), size.to_scaled_points(), slant));
519 hunits tfont::get_italic_correction(charinfo *c)
521 return hunits(fm->get_italic_correction(c->get_index(), size.to_scaled_points()));
524 hunits tfont::get_left_italic_correction(charinfo *c)
526 return hunits(fm->get_left_italic_correction(c->get_index(),
527 size.to_scaled_points()));
530 hunits tfont::get_subscript_correction(charinfo *c)
532 return hunits(fm->get_subscript_correction(c->get_index(),
533 size.to_scaled_points()));
536 inline int tfont::get_input_position()
538 return input_position;
541 inline int tfont::contains(charinfo *ci)
543 return fm->contains(ci->get_index());
546 inline int tfont::get_character_type(charinfo *ci)
548 return fm->get_character_type(ci->get_index());
551 inline int tfont::get_bold(hunits *res)
553 if (is_bold) {
554 *res = bold_offset;
555 return 1;
557 else
558 return 0;
561 inline int tfont::get_constant_space(hunits *res)
563 if (is_constant_spaced) {
564 *res = constant_space_width;
565 return 1;
567 else
568 return 0;
571 inline hunits tfont::get_track_kern()
573 return track_kern;
576 inline tfont *tfont::get_plain()
578 return plain_version;
581 inline font_size tfont::get_size()
583 return size;
586 inline symbol tfont::get_name()
588 return name;
591 inline int tfont::get_height()
593 return height;
596 inline int tfont::get_slant()
598 return slant;
601 symbol SYMBOL_ff("ff");
602 symbol SYMBOL_fi("fi");
603 symbol SYMBOL_fl("fl");
604 symbol SYMBOL_Fi("Fi");
605 symbol SYMBOL_Fl("Fl");
607 charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
609 if (ligature_mode == 0)
610 return 0;
611 charinfo *ci = 0;
612 if (c1->get_ascii_code() == 'f') {
613 switch (c2->get_ascii_code()) {
614 case 'f':
615 if (fm->has_ligature(font::LIG_ff))
616 ci = get_charinfo(SYMBOL_ff);
617 break;
618 case 'i':
619 if (fm->has_ligature(font::LIG_fi))
620 ci = get_charinfo(SYMBOL_fi);
621 break;
622 case 'l':
623 if (fm->has_ligature(font::LIG_fl))
624 ci = get_charinfo(SYMBOL_fl);
625 break;
628 else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
629 switch (c2->get_ascii_code()) {
630 case 'i':
631 if (fm->has_ligature(font::LIG_ffi))
632 ci = get_charinfo(SYMBOL_Fi);
633 break;
634 case 'l':
635 if (fm->has_ligature(font::LIG_ffl))
636 ci = get_charinfo(SYMBOL_Fl);
637 break;
640 if (ci != 0 && fm->contains(ci->get_index()))
641 return ci;
642 return 0;
645 inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res)
647 if (kern_mode == 0)
648 return 0;
649 else {
650 int n = fm->get_kern(c1->get_index(),
651 c2->get_index(),
652 size.to_scaled_points());
653 if (n) {
654 *res = hunits(n);
655 return 1;
657 else
658 return 0;
662 tfont *tfont::tfont_list = 0;
664 tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
666 next = tfont_list;
667 tfont_list = this;
668 tfont_spec plain_spec = plain();
669 tfont *p;
670 for (p = tfont_list; p; p = p->next)
671 if (*p == plain_spec) {
672 plain_version = p;
673 break;
675 if (!p)
676 plain_version = new tfont(plain_spec);
679 /* output_file */
681 class real_output_file : public output_file {
682 #ifndef POPEN_MISSING
683 int piped;
684 #endif
685 int printing; // decision via optional page list
686 int output_on; // \O[0] or \O[1] escape calls
687 virtual void really_transparent_char(unsigned char) = 0;
688 virtual void really_print_line(hunits x, vunits y, node *n,
689 vunits before, vunits after, hunits width) = 0;
690 virtual void really_begin_page(int pageno, vunits page_length) = 0;
691 virtual void really_copy_file(hunits x, vunits y, const char *filename);
692 virtual void really_put_filename(const char *filename);
693 virtual void really_on();
694 virtual void really_off();
695 public:
696 FILE *fp;
697 real_output_file();
698 ~real_output_file();
699 void flush();
700 void transparent_char(unsigned char);
701 void print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
702 void begin_page(int pageno, vunits page_length);
703 void put_filename(const char *filename);
704 void on();
705 void off();
706 int is_on();
707 int is_printing();
708 void copy_file(hunits x, vunits y, const char *filename);
711 class suppress_output_file : public real_output_file {
712 public:
713 suppress_output_file();
714 void really_transparent_char(unsigned char);
715 void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
716 void really_begin_page(int pageno, vunits page_length);
719 class ascii_output_file : public real_output_file {
720 public:
721 ascii_output_file();
722 void really_transparent_char(unsigned char);
723 void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
724 void really_begin_page(int pageno, vunits page_length);
725 void outc(unsigned char c);
726 void outs(const char *s);
729 void ascii_output_file::outc(unsigned char c)
731 fputc(c, fp);
734 void ascii_output_file::outs(const char *s)
736 fputc('<', fp);
737 if (s)
738 fputs(s, fp);
739 fputc('>', fp);
742 struct hvpair;
744 class troff_output_file : public real_output_file {
745 units hpos;
746 units vpos;
747 units output_vpos;
748 units output_hpos;
749 int force_motion;
750 int current_size;
751 int current_slant;
752 int current_height;
753 tfont *current_tfont;
754 color *current_fill_color;
755 color *current_glyph_color;
756 int current_font_number;
757 symbol *font_position;
758 int nfont_positions;
759 enum { TBUF_SIZE = 256 };
760 char tbuf[TBUF_SIZE];
761 int tbuf_len;
762 int tbuf_kern;
763 int begun_page;
764 int cur_div_level;
765 string tag_list;
766 void do_motion();
767 void put(char c);
768 void put(unsigned char c);
769 void put(int i);
770 void put(unsigned int i);
771 void put(const char *s);
772 void set_font(tfont *tf);
773 void flush_tbuf();
774 public:
775 troff_output_file();
776 ~troff_output_file();
777 void trailer(vunits page_length);
778 void put_char(charinfo *, tfont *, color *, color *);
779 void put_char_width(charinfo *, tfont *, color *, color *, hunits, hunits);
780 void right(hunits);
781 void down(vunits);
782 void moveto(hunits, vunits);
783 void start_special(tfont *, color *, color *, int = 0);
784 void start_special();
785 void special_char(unsigned char c);
786 void end_special();
787 void word_marker();
788 void really_transparent_char(unsigned char c);
789 void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
790 void really_begin_page(int pageno, vunits page_length);
791 void really_copy_file(hunits x, vunits y, const char *filename);
792 void really_put_filename(const char *filename);
793 void really_on();
794 void really_off();
795 void draw(char, hvpair *, int, font_size, color *, color *);
796 void determine_line_limits (char code, hvpair *point, int npoints);
797 void check_charinfo(tfont *tf, charinfo *ci);
798 void glyph_color(color *c);
799 void fill_color(color *c);
800 int get_hpos() { return hpos; }
801 int get_vpos() { return vpos; }
802 void add_to_tag_list(string s);
803 friend void space_char_hmotion_node::tprint(troff_output_file *);
804 friend void unbreakable_space_node::tprint(troff_output_file *);
807 static void put_string(const char *s, FILE *fp)
809 for (; *s != '\0'; ++s)
810 putc(*s, fp);
813 inline void troff_output_file::put(char c)
815 putc(c, fp);
818 inline void troff_output_file::put(unsigned char c)
820 putc(c, fp);
823 inline void troff_output_file::put(const char *s)
825 put_string(s, fp);
828 inline void troff_output_file::put(int i)
830 put_string(i_to_a(i), fp);
833 inline void troff_output_file::put(unsigned int i)
835 put_string(ui_to_a(i), fp);
838 void troff_output_file::start_special(tfont *tf, color *gcol, color *fcol,
839 int no_init_string)
841 set_font(tf);
842 glyph_color(gcol);
843 fill_color(fcol);
844 flush_tbuf();
845 do_motion();
846 if (!no_init_string)
847 put("x X ");
850 void troff_output_file::start_special()
852 flush_tbuf();
853 do_motion();
854 put("x X ");
857 void troff_output_file::special_char(unsigned char c)
859 put(c);
860 if (c == '\n')
861 put('+');
864 void troff_output_file::end_special()
866 put('\n');
869 inline void troff_output_file::moveto(hunits h, vunits v)
871 hpos = h.to_units();
872 vpos = v.to_units();
875 void troff_output_file::really_print_line(hunits x, vunits y, node *n,
876 vunits before, vunits after, hunits)
878 moveto(x, y);
879 while (n != 0) {
880 // Check whether we should push the current troff state and use
881 // the state at the start of the invocation of this diversion.
882 if (n->div_nest_level > cur_div_level && n->push_state) {
883 state.push_state(n->push_state);
884 cur_div_level = n->div_nest_level;
886 // Has the current diversion level decreased? Then we must pop the
887 // troff state.
888 while (n->div_nest_level < cur_div_level) {
889 state.pop_state();
890 cur_div_level = n->div_nest_level;
892 // Now check whether the state has changed.
893 if ((is_on() || n->force_tprint())
894 && (state.changed(n->state) || n->is_tag() || n->is_special)) {
895 flush_tbuf();
896 do_motion();
897 force_motion = 1;
898 flush();
899 state.flush(fp, n->state, tag_list);
900 tag_list = string("");
901 flush();
903 n->tprint(this);
904 n = n->next;
906 flush_tbuf();
907 // This ensures that transparent throughput will have a more predictable
908 // position.
909 do_motion();
910 force_motion = 1;
911 hpos = 0;
912 put('n');
913 put(before.to_units());
914 put(' ');
915 put(after.to_units());
916 put('\n');
919 inline void troff_output_file::word_marker()
921 flush_tbuf();
922 if (is_on())
923 put('w');
926 inline void troff_output_file::right(hunits n)
928 hpos += n.to_units();
931 inline void troff_output_file::down(vunits n)
933 vpos += n.to_units();
936 void troff_output_file::do_motion()
938 if (force_motion) {
939 put('V');
940 put(vpos);
941 put('\n');
942 put('H');
943 put(hpos);
944 put('\n');
946 else {
947 if (hpos != output_hpos) {
948 units n = hpos - output_hpos;
949 if (n > 0 && n < hpos) {
950 put('h');
951 put(n);
953 else {
954 put('H');
955 put(hpos);
957 put('\n');
959 if (vpos != output_vpos) {
960 units n = vpos - output_vpos;
961 if (n > 0 && n < vpos) {
962 put('v');
963 put(n);
965 else {
966 put('V');
967 put(vpos);
969 put('\n');
972 output_vpos = vpos;
973 output_hpos = hpos;
974 force_motion = 0;
977 void troff_output_file::flush_tbuf()
979 if (!is_on()) {
980 tbuf_len = 0;
981 return;
984 if (tbuf_len == 0)
985 return;
986 if (tbuf_kern == 0)
987 put('t');
988 else {
989 put('u');
990 put(tbuf_kern);
991 put(' ');
993 check_output_limits(hpos, vpos);
994 check_output_limits(hpos, vpos - current_size);
996 for (int i = 0; i < tbuf_len; i++)
997 put(tbuf[i]);
998 put('\n');
999 tbuf_len = 0;
1002 void troff_output_file::check_charinfo(tfont *tf, charinfo *ci)
1004 if (!is_on())
1005 return;
1007 int height = tf->get_char_height(ci).to_units();
1008 int width = tf->get_width(ci).to_units()
1009 + tf->get_italic_correction(ci).to_units();
1010 int depth = tf->get_char_depth(ci).to_units();
1011 check_output_limits(output_hpos, output_vpos - height);
1012 check_output_limits(output_hpos + width, output_vpos + depth);
1015 void troff_output_file::put_char_width(charinfo *ci, tfont *tf,
1016 color *gcol, color *fcol,
1017 hunits w, hunits k)
1019 int kk = k.to_units();
1020 if (!is_on()) {
1021 flush_tbuf();
1022 hpos += w.to_units() + kk;
1023 return;
1025 set_font(tf);
1026 unsigned char c = ci->get_ascii_code();
1027 if (c == '\0') {
1028 glyph_color(gcol);
1029 fill_color(fcol);
1030 flush_tbuf();
1031 do_motion();
1032 check_charinfo(tf, ci);
1033 if (ci->numbered()) {
1034 put('N');
1035 put(ci->get_number());
1037 else {
1038 put('C');
1039 const char *s = ci->nm.contents();
1040 if (s[1] == 0) {
1041 put('\\');
1042 put(s[0]);
1044 else
1045 put(s);
1047 put('\n');
1048 hpos += w.to_units() + kk;
1050 else if (tcommand_flag) {
1051 if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
1052 && (!gcol || gcol == current_glyph_color)
1053 && (!fcol || fcol == current_fill_color)
1054 && kk == tbuf_kern
1055 && tbuf_len < TBUF_SIZE) {
1056 check_charinfo(tf, ci);
1057 tbuf[tbuf_len++] = c;
1058 output_hpos += w.to_units() + kk;
1059 hpos = output_hpos;
1060 return;
1062 glyph_color(gcol);
1063 fill_color(fcol);
1064 flush_tbuf();
1065 do_motion();
1066 check_charinfo(tf, ci);
1067 tbuf[tbuf_len++] = c;
1068 output_hpos += w.to_units() + kk;
1069 tbuf_kern = kk;
1070 hpos = output_hpos;
1072 else {
1073 // flush_tbuf();
1074 int n = hpos - output_hpos;
1075 check_charinfo(tf, ci);
1076 // check_output_limits(output_hpos, output_vpos);
1077 if (vpos == output_vpos
1078 && (!gcol || gcol == current_glyph_color)
1079 && (!fcol || fcol == current_fill_color)
1080 && n > 0 && n < 100 && !force_motion) {
1081 put(char(n/10 + '0'));
1082 put(char(n%10 + '0'));
1083 put(c);
1084 output_hpos = hpos;
1086 else {
1087 glyph_color(gcol);
1088 fill_color(fcol);
1089 do_motion();
1090 put('c');
1091 put(c);
1093 hpos += w.to_units() + kk;
1097 void troff_output_file::put_char(charinfo *ci, tfont *tf,
1098 color *gcol, color *fcol)
1100 flush_tbuf();
1101 if (!is_on())
1102 return;
1103 set_font(tf);
1104 unsigned char c = ci->get_ascii_code();
1105 if (c == '\0') {
1106 glyph_color(gcol);
1107 fill_color(fcol);
1108 flush_tbuf();
1109 do_motion();
1110 if (ci->numbered()) {
1111 put('N');
1112 put(ci->get_number());
1114 else {
1115 put('C');
1116 const char *s = ci->nm.contents();
1117 if (s[1] == 0) {
1118 put('\\');
1119 put(s[0]);
1121 else
1122 put(s);
1124 put('\n');
1126 else {
1127 int n = hpos - output_hpos;
1128 if (vpos == output_vpos
1129 && (!gcol || gcol == current_glyph_color)
1130 && (!fcol || fcol == current_fill_color)
1131 && n > 0 && n < 100) {
1132 put(char(n/10 + '0'));
1133 put(char(n%10 + '0'));
1134 put(c);
1135 output_hpos = hpos;
1137 else {
1138 glyph_color(gcol);
1139 fill_color(fcol);
1140 flush_tbuf();
1141 do_motion();
1142 put('c');
1143 put(c);
1148 // set_font calls `flush_tbuf' if necessary.
1150 void troff_output_file::set_font(tfont *tf)
1152 if (current_tfont == tf)
1153 return;
1154 flush_tbuf();
1155 int n = tf->get_input_position();
1156 symbol nm = tf->get_name();
1157 if (n >= nfont_positions || font_position[n] != nm) {
1158 put("x font ");
1159 put(n);
1160 put(' ');
1161 put(nm.contents());
1162 put('\n');
1163 if (n >= nfont_positions) {
1164 int old_nfont_positions = nfont_positions;
1165 symbol *old_font_position = font_position;
1166 nfont_positions *= 3;
1167 nfont_positions /= 2;
1168 if (nfont_positions <= n)
1169 nfont_positions = n + 10;
1170 font_position = new symbol[nfont_positions];
1171 memcpy(font_position, old_font_position,
1172 old_nfont_positions*sizeof(symbol));
1173 a_delete old_font_position;
1175 font_position[n] = nm;
1177 if (current_font_number != n) {
1178 put('f');
1179 put(n);
1180 put('\n');
1181 current_font_number = n;
1183 int size = tf->get_size().to_scaled_points();
1184 if (current_size != size) {
1185 put('s');
1186 put(size);
1187 put('\n');
1188 current_size = size;
1190 int slant = tf->get_slant();
1191 if (current_slant != slant) {
1192 put("x Slant ");
1193 put(slant);
1194 put('\n');
1195 current_slant = slant;
1197 int height = tf->get_height();
1198 if (current_height != height) {
1199 put("x Height ");
1200 put(height == 0 ? current_size : height);
1201 put('\n');
1202 current_height = height;
1204 current_tfont = tf;
1207 // fill_color calls `flush_tbuf' and `do_motion' if necessary.
1209 void troff_output_file::fill_color(color *col)
1211 if (!col || current_fill_color == col)
1212 return;
1213 current_fill_color = col;
1214 if (!color_flag)
1215 return;
1216 flush_tbuf();
1217 do_motion();
1218 put("DF");
1219 unsigned int components[4];
1220 color_scheme cs;
1221 cs = col->get_components(components);
1222 switch (cs) {
1223 case DEFAULT:
1224 put('d');
1225 break;
1226 case RGB:
1227 put("r ");
1228 put(Red);
1229 put(' ');
1230 put(Green);
1231 put(' ');
1232 put(Blue);
1233 break;
1234 case CMY:
1235 put("c ");
1236 put(Cyan);
1237 put(' ');
1238 put(Magenta);
1239 put(' ');
1240 put(Yellow);
1241 break;
1242 case CMYK:
1243 put("k ");
1244 put(Cyan);
1245 put(' ');
1246 put(Magenta);
1247 put(' ');
1248 put(Yellow);
1249 put(' ');
1250 put(Black);
1251 break;
1252 case GRAY:
1253 put("g ");
1254 put(Gray);
1255 break;
1257 put('\n');
1260 // glyph_color calls `flush_tbuf' and `do_motion' if necessary.
1262 void troff_output_file::glyph_color(color *col)
1264 if (!col || current_glyph_color == col)
1265 return;
1266 current_glyph_color = col;
1267 if (!color_flag)
1268 return;
1269 flush_tbuf();
1270 // grotty doesn't like a color command if the vertical position is zero.
1271 do_motion();
1272 put("m");
1273 unsigned int components[4];
1274 color_scheme cs;
1275 cs = col->get_components(components);
1276 switch (cs) {
1277 case DEFAULT:
1278 put('d');
1279 break;
1280 case RGB:
1281 put("r ");
1282 put(Red);
1283 put(' ');
1284 put(Green);
1285 put(' ');
1286 put(Blue);
1287 break;
1288 case CMY:
1289 put("c ");
1290 put(Cyan);
1291 put(' ');
1292 put(Magenta);
1293 put(' ');
1294 put(Yellow);
1295 break;
1296 case CMYK:
1297 put("k ");
1298 put(Cyan);
1299 put(' ');
1300 put(Magenta);
1301 put(' ');
1302 put(Yellow);
1303 put(' ');
1304 put(Black);
1305 break;
1306 case GRAY:
1307 put("g ");
1308 put(Gray);
1309 break;
1311 put('\n');
1314 void troff_output_file::add_to_tag_list(string s)
1316 if (tag_list == string(""))
1317 tag_list = s;
1318 else {
1319 tag_list += string("\n");
1320 tag_list += s;
1324 // determine_line_limits - works out the smallest box which will contain
1325 // the entity, code, built from the point array.
1326 void troff_output_file::determine_line_limits(char code, hvpair *point,
1327 int npoints)
1329 int i, x, y;
1331 if (!is_on())
1332 return;
1334 switch (code) {
1335 case 'c':
1336 case 'C':
1337 // only the h field is used when defining a circle
1338 check_output_limits(output_hpos,
1339 output_vpos - point[0].h.to_units()/2);
1340 check_output_limits(output_hpos + point[0].h.to_units(),
1341 output_vpos + point[0].h.to_units()/2);
1342 break;
1343 case 'E':
1344 case 'e':
1345 check_output_limits(output_hpos,
1346 output_vpos - point[0].v.to_units()/2);
1347 check_output_limits(output_hpos + point[0].h.to_units(),
1348 output_vpos + point[0].v.to_units()/2);
1349 break;
1350 case 'P':
1351 case 'p':
1352 x = output_hpos;
1353 y = output_vpos;
1354 check_output_limits(x, y);
1355 for (i = 0; i < npoints; i++) {
1356 x += point[i].h.to_units();
1357 y += point[i].v.to_units();
1358 check_output_limits(x, y);
1360 break;
1361 case 't':
1362 x = output_hpos;
1363 y = output_vpos;
1364 for (i = 0; i < npoints; i++) {
1365 x += point[i].h.to_units();
1366 y += point[i].v.to_units();
1367 check_output_limits(x, y);
1369 break;
1370 case 'a':
1371 double c[2];
1372 int p[4];
1373 int minx, miny, maxx, maxy;
1374 x = output_hpos;
1375 y = output_vpos;
1376 p[0] = point[0].h.to_units();
1377 p[1] = point[0].v.to_units();
1378 p[2] = point[1].h.to_units();
1379 p[3] = point[1].v.to_units();
1380 if (adjust_arc_center(p, c)) {
1381 check_output_arc_limits(x, y,
1382 p[0], p[1], p[2], p[3],
1383 c[0], c[1],
1384 &minx, &maxx, &miny, &maxy);
1385 check_output_limits(minx, miny);
1386 check_output_limits(maxx, maxy);
1387 break;
1389 // fall through
1390 case 'l':
1391 x = output_hpos;
1392 y = output_vpos;
1393 check_output_limits(x, y);
1394 for (i = 0; i < npoints; i++) {
1395 x += point[i].h.to_units();
1396 y += point[i].v.to_units();
1397 check_output_limits(x, y);
1399 break;
1400 default:
1401 x = output_hpos;
1402 y = output_vpos;
1403 for (i = 0; i < npoints; i++) {
1404 x += point[i].h.to_units();
1405 y += point[i].v.to_units();
1406 check_output_limits(x, y);
1411 void troff_output_file::draw(char code, hvpair *point, int npoints,
1412 font_size fsize, color *gcol, color *fcol)
1414 int i;
1415 glyph_color(gcol);
1416 fill_color(fcol);
1417 flush_tbuf();
1418 do_motion();
1419 if (is_on()) {
1420 int size = fsize.to_scaled_points();
1421 if (current_size != size) {
1422 put('s');
1423 put(size);
1424 put('\n');
1425 current_size = size;
1426 current_tfont = 0;
1428 put('D');
1429 put(code);
1430 if (code == 'c') {
1431 put(' ');
1432 put(point[0].h.to_units());
1434 else
1435 for (i = 0; i < npoints; i++) {
1436 put(' ');
1437 put(point[i].h.to_units());
1438 put(' ');
1439 put(point[i].v.to_units());
1441 determine_line_limits(code, point, npoints);
1444 for (i = 0; i < npoints; i++)
1445 output_hpos += point[i].h.to_units();
1446 hpos = output_hpos;
1447 if (code != 'e') {
1448 for (i = 0; i < npoints; i++)
1449 output_vpos += point[i].v.to_units();
1450 vpos = output_vpos;
1452 if (is_on())
1453 put('\n');
1456 void troff_output_file::really_on()
1458 flush_tbuf();
1459 force_motion = 1;
1460 do_motion();
1463 void troff_output_file::really_off()
1465 flush_tbuf();
1468 void troff_output_file::really_put_filename(const char *filename)
1470 flush_tbuf();
1471 put("F ");
1472 put(filename);
1473 put('\n');
1476 void troff_output_file::really_begin_page(int pageno, vunits page_length)
1478 flush_tbuf();
1479 if (begun_page) {
1480 if (page_length > V0) {
1481 put('V');
1482 put(page_length.to_units());
1483 put('\n');
1486 else
1487 begun_page = 1;
1488 current_tfont = 0;
1489 current_font_number = -1;
1490 current_size = 0;
1491 // current_height = 0;
1492 // current_slant = 0;
1493 hpos = 0;
1494 vpos = 0;
1495 output_hpos = 0;
1496 output_vpos = 0;
1497 force_motion = 1;
1498 for (int i = 0; i < nfont_positions; i++)
1499 font_position[i] = NULL_SYMBOL;
1500 put('p');
1501 put(pageno);
1502 put('\n');
1505 void troff_output_file::really_copy_file(hunits x, vunits y,
1506 const char *filename)
1508 moveto(x, y);
1509 flush_tbuf();
1510 do_motion();
1511 errno = 0;
1512 FILE *ifp = include_search_path.open_file_cautious(filename);
1513 if (ifp == 0)
1514 error("can't open `%1': %2", filename, strerror(errno));
1515 else {
1516 int c;
1517 while ((c = getc(ifp)) != EOF)
1518 put(char(c));
1519 fclose(ifp);
1521 force_motion = 1;
1522 current_size = 0;
1523 current_tfont = 0;
1524 current_font_number = -1;
1525 for (int i = 0; i < nfont_positions; i++)
1526 font_position[i] = NULL_SYMBOL;
1529 void troff_output_file::really_transparent_char(unsigned char c)
1531 put(c);
1534 troff_output_file::~troff_output_file()
1536 a_delete font_position;
1539 void troff_output_file::trailer(vunits page_length)
1541 flush_tbuf();
1542 if (page_length > V0) {
1543 put("x trailer\n");
1544 put('V');
1545 put(page_length.to_units());
1546 put('\n');
1548 put("x stop\n");
1551 troff_output_file::troff_output_file()
1552 : current_slant(0), current_height(0), current_fill_color(0),
1553 current_glyph_color(0), nfont_positions(10), tbuf_len(0), begun_page(0),
1554 cur_div_level(0)
1556 font_position = new symbol[nfont_positions];
1557 put("x T ");
1558 put(device);
1559 put('\n');
1560 put("x res ");
1561 put(units_per_inch);
1562 put(' ');
1563 put(hresolution);
1564 put(' ');
1565 put(vresolution);
1566 put('\n');
1567 put("x init\n");
1570 /* output_file */
1572 output_file *the_output = 0;
1574 output_file::output_file()
1578 output_file::~output_file()
1582 void output_file::trailer(vunits)
1586 void output_file::put_filename(const char *)
1590 void output_file::on()
1594 void output_file::off()
1598 real_output_file::real_output_file()
1599 : printing(0), output_on(1)
1601 #ifndef POPEN_MISSING
1602 if (pipe_command) {
1603 if ((fp = popen(pipe_command, POPEN_WT)) != 0) {
1604 piped = 1;
1605 return;
1607 error("pipe open failed: %1", strerror(errno));
1609 piped = 0;
1610 #endif /* not POPEN_MISSING */
1611 fp = stdout;
1614 real_output_file::~real_output_file()
1616 if (!fp)
1617 return;
1618 // To avoid looping, set fp to 0 before calling fatal().
1619 if (ferror(fp) || fflush(fp) < 0) {
1620 fp = 0;
1621 fatal("error writing output file");
1623 #ifndef POPEN_MISSING
1624 if (piped) {
1625 int result = pclose(fp);
1626 fp = 0;
1627 if (result < 0)
1628 fatal("pclose failed");
1629 if (!WIFEXITED(result))
1630 error("output process `%1' got fatal signal %2",
1631 pipe_command,
1632 WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
1633 else {
1634 int exit_status = WEXITSTATUS(result);
1635 if (exit_status != 0)
1636 error("output process `%1' exited with status %2",
1637 pipe_command, exit_status);
1640 else
1641 #endif /* not POPEN MISSING */
1642 if (fclose(fp) < 0) {
1643 fp = 0;
1644 fatal("error closing output file");
1648 void real_output_file::flush()
1650 if (fflush(fp) < 0)
1651 fatal("error writing output file");
1654 int real_output_file::is_printing()
1656 return printing;
1659 void real_output_file::begin_page(int pageno, vunits page_length)
1661 printing = in_output_page_list(pageno);
1662 if (printing)
1663 really_begin_page(pageno, page_length);
1666 void real_output_file::copy_file(hunits x, vunits y, const char *filename)
1668 if (printing && output_on)
1669 really_copy_file(x, y, filename);
1670 check_output_limits(x.to_units(), y.to_units());
1673 void real_output_file::transparent_char(unsigned char c)
1675 if (printing && output_on)
1676 really_transparent_char(c);
1679 void real_output_file::print_line(hunits x, vunits y, node *n,
1680 vunits before, vunits after, hunits width)
1682 if (printing)
1683 really_print_line(x, y, n, before, after, width);
1684 delete_node_list(n);
1687 void real_output_file::really_copy_file(hunits, vunits, const char *)
1689 // do nothing
1692 void real_output_file::put_filename(const char *filename)
1694 really_put_filename(filename);
1697 void real_output_file::really_put_filename(const char *)
1701 void real_output_file::on()
1703 really_on();
1704 if (output_on == 0)
1705 output_on = 1;
1708 void real_output_file::off()
1710 really_off();
1711 output_on = 0;
1714 int real_output_file::is_on()
1716 return output_on;
1719 void real_output_file::really_on()
1723 void real_output_file::really_off()
1727 /* ascii_output_file */
1729 void ascii_output_file::really_transparent_char(unsigned char c)
1731 putc(c, fp);
1734 void ascii_output_file::really_print_line(hunits, vunits, node *n,
1735 vunits, vunits, hunits)
1737 while (n != 0) {
1738 n->ascii_print(this);
1739 n = n->next;
1741 fputc('\n', fp);
1744 void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/)
1746 fputs("<beginning of page>\n", fp);
1749 ascii_output_file::ascii_output_file()
1753 /* suppress_output_file */
1755 suppress_output_file::suppress_output_file()
1759 void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits, hunits)
1763 void suppress_output_file::really_begin_page(int, vunits)
1767 void suppress_output_file::really_transparent_char(unsigned char)
1771 /* glyphs, ligatures, kerns, discretionary breaks */
1773 class charinfo_node : public node {
1774 protected:
1775 charinfo *ci;
1776 public:
1777 charinfo_node(charinfo *, statem *, int, node * = 0);
1778 int ends_sentence();
1779 int overlaps_vertically();
1780 int overlaps_horizontally();
1783 charinfo_node::charinfo_node(charinfo *c, statem *s, int pop, node *x)
1784 : node(x, s, pop), ci(c)
1788 int charinfo_node::ends_sentence()
1790 if (ci->ends_sentence())
1791 return 1;
1792 else if (ci->transparent())
1793 return 2;
1794 else
1795 return 0;
1798 int charinfo_node::overlaps_horizontally()
1800 return ci->overlaps_horizontally();
1803 int charinfo_node::overlaps_vertically()
1805 return ci->overlaps_vertically();
1808 class glyph_node : public charinfo_node {
1809 static glyph_node *free_list;
1810 protected:
1811 tfont *tf;
1812 color *gcol;
1813 color *fcol; /* this is needed for grotty */
1814 #ifdef STORE_WIDTH
1815 hunits wid;
1816 glyph_node(charinfo *, tfont *, color *, color *, hunits,
1817 statem *, int, node * = 0);
1818 #endif
1819 public:
1820 void *operator new(size_t);
1821 void operator delete(void *);
1822 glyph_node(charinfo *, tfont *, color *, color *,
1823 statem *, int, node * = 0);
1824 ~glyph_node() {}
1825 node *copy();
1826 node *merge_glyph_node(glyph_node *);
1827 node *merge_self(node *);
1828 hunits width();
1829 node *last_char_node();
1830 units size();
1831 void vertical_extent(vunits *, vunits *);
1832 hunits subscript_correction();
1833 hunits italic_correction();
1834 hunits left_italic_correction();
1835 hunits skew();
1836 hyphenation_type get_hyphenation_type();
1837 tfont *get_tfont();
1838 color *get_glyph_color();
1839 color *get_fill_color();
1840 void tprint(troff_output_file *);
1841 void zero_width_tprint(troff_output_file *);
1842 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1843 node *add_self(node *, hyphen_list **);
1844 void ascii_print(ascii_output_file *);
1845 void asciify(macro *);
1846 int character_type();
1847 int same(node *);
1848 const char *type();
1849 int force_tprint();
1850 int is_tag();
1851 void debug_node();
1854 glyph_node *glyph_node::free_list = 0;
1856 class ligature_node : public glyph_node {
1857 node *n1;
1858 node *n2;
1859 #ifdef STORE_WIDTH
1860 ligature_node(charinfo *, tfont *, color *, color *, hunits,
1861 node *, node *, statem *, int, node * = 0);
1862 #endif
1863 public:
1864 void *operator new(size_t);
1865 void operator delete(void *);
1866 ligature_node(charinfo *, tfont *, color *, color *,
1867 node *, node *, statem *, int, node * = 0);
1868 ~ligature_node();
1869 node *copy();
1870 node *add_self(node *, hyphen_list **);
1871 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1872 void ascii_print(ascii_output_file *);
1873 void asciify(macro *);
1874 int same(node *);
1875 const char *type();
1876 int force_tprint();
1877 int is_tag();
1880 class kern_pair_node : public node {
1881 hunits amount;
1882 node *n1;
1883 node *n2;
1884 public:
1885 kern_pair_node(hunits, node *, node *, statem *, int, node * = 0);
1886 ~kern_pair_node();
1887 node *copy();
1888 node *merge_glyph_node(glyph_node *);
1889 node *add_self(node *, hyphen_list **);
1890 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1891 node *add_discretionary_hyphen();
1892 hunits width();
1893 node *last_char_node();
1894 hunits italic_correction();
1895 hunits subscript_correction();
1896 void tprint(troff_output_file *);
1897 hyphenation_type get_hyphenation_type();
1898 int ends_sentence();
1899 void ascii_print(ascii_output_file *);
1900 void asciify(macro *);
1901 int same(node *);
1902 const char *type();
1903 int force_tprint();
1904 int is_tag();
1905 void vertical_extent(vunits *, vunits *);
1908 class dbreak_node : public node {
1909 node *none;
1910 node *pre;
1911 node *post;
1912 public:
1913 dbreak_node(node *, node *, statem *, int, node * = 0);
1914 ~dbreak_node();
1915 node *copy();
1916 node *merge_glyph_node(glyph_node *);
1917 node *add_discretionary_hyphen();
1918 hunits width();
1919 node *last_char_node();
1920 hunits italic_correction();
1921 hunits subscript_correction();
1922 void tprint(troff_output_file *);
1923 breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
1924 int is_inner = 0);
1925 int nbreaks();
1926 int ends_sentence();
1927 void split(int, node **, node **);
1928 hyphenation_type get_hyphenation_type();
1929 void ascii_print(ascii_output_file *);
1930 void asciify(macro *);
1931 int same(node *);
1932 const char *type();
1933 int force_tprint();
1934 int is_tag();
1937 void *glyph_node::operator new(size_t n)
1939 assert(n == sizeof(glyph_node));
1940 if (!free_list) {
1941 const int BLOCK = 1024;
1942 free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
1943 for (int i = 0; i < BLOCK - 1; i++)
1944 free_list[i].next = free_list + i + 1;
1945 free_list[BLOCK-1].next = 0;
1947 glyph_node *p = free_list;
1948 free_list = (glyph_node *)(free_list->next);
1949 p->next = 0;
1950 return p;
1953 void *ligature_node::operator new(size_t n)
1955 return new char[n];
1958 void glyph_node::operator delete(void *p)
1960 if (p) {
1961 ((glyph_node *)p)->next = free_list;
1962 free_list = (glyph_node *)p;
1966 void ligature_node::operator delete(void *p)
1968 delete[] (char *)p;
1971 glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc,
1972 statem *s, int pop, node *x)
1973 : charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc)
1975 #ifdef STORE_WIDTH
1976 wid = tf->get_width(ci);
1977 #endif
1980 #ifdef STORE_WIDTH
1981 glyph_node::glyph_node(charinfo *c, tfont *t,
1982 color *gc, color *fc, hunits w,
1983 statem *s, int pop, node *x)
1984 : charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc), wid(w)
1987 #endif
1989 node *glyph_node::copy()
1991 #ifdef STORE_WIDTH
1992 return new glyph_node(ci, tf, gcol, fcol, wid, state, div_nest_level);
1993 #else
1994 return new glyph_node(ci, tf, gcol, fcol, state, div_nest_level);
1995 #endif
1998 node *glyph_node::merge_self(node *nd)
2000 return nd->merge_glyph_node(this);
2003 int glyph_node::character_type()
2005 return tf->get_character_type(ci);
2008 node *glyph_node::add_self(node *n, hyphen_list **p)
2010 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
2011 next = 0;
2012 node *nn;
2013 if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
2014 next = n;
2015 nn = this;
2017 if ((*p)->hyphen)
2018 nn = nn->add_discretionary_hyphen();
2019 hyphen_list *pp = *p;
2020 *p = (*p)->next;
2021 delete pp;
2022 return nn;
2025 units glyph_node::size()
2027 return tf->get_size().to_units();
2030 hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count)
2032 (*count)++;
2033 return new hyphen_list(ci->get_hyphenation_code(), tail);
2036 tfont *node::get_tfont()
2038 return 0;
2041 tfont *glyph_node::get_tfont()
2043 return tf;
2046 color *node::get_glyph_color()
2048 return 0;
2051 color *glyph_node::get_glyph_color()
2053 return gcol;
2056 color *node::get_fill_color()
2058 return 0;
2061 color *glyph_node::get_fill_color()
2063 return fcol;
2066 node *node::merge_glyph_node(glyph_node *)
2068 return 0;
2071 node *glyph_node::merge_glyph_node(glyph_node *gn)
2073 if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
2074 charinfo *lig;
2075 if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
2076 node *next1 = next;
2077 next = 0;
2078 return new ligature_node(lig, tf, gcol, fcol, this, gn, state,
2079 gn->div_nest_level, next1);
2081 hunits kern;
2082 if (tf->get_kern(ci, gn->ci, &kern)) {
2083 node *next1 = next;
2084 next = 0;
2085 return new kern_pair_node(kern, this, gn, state,
2086 gn->div_nest_level, next1);
2089 return 0;
2092 #ifdef STORE_WIDTH
2093 inline
2094 #endif
2095 hunits glyph_node::width()
2097 #ifdef STORE_WIDTH
2098 return wid;
2099 #else
2100 return tf->get_width(ci);
2101 #endif
2104 node *glyph_node::last_char_node()
2106 return this;
2109 void glyph_node::vertical_extent(vunits *min, vunits *max)
2111 *min = -tf->get_char_height(ci);
2112 *max = tf->get_char_depth(ci);
2115 hunits glyph_node::skew()
2117 return tf->get_char_skew(ci);
2120 hunits glyph_node::subscript_correction()
2122 return tf->get_subscript_correction(ci);
2125 hunits glyph_node::italic_correction()
2127 return tf->get_italic_correction(ci);
2130 hunits glyph_node::left_italic_correction()
2132 return tf->get_left_italic_correction(ci);
2135 hyphenation_type glyph_node::get_hyphenation_type()
2137 return HYPHEN_MIDDLE;
2140 void glyph_node::ascii_print(ascii_output_file *ascii)
2142 unsigned char c = ci->get_ascii_code();
2143 if (c != 0)
2144 ascii->outc(c);
2145 else
2146 ascii->outs(ci->nm.contents());
2149 void glyph_node::debug_node()
2151 unsigned char c = ci->get_ascii_code();
2152 fprintf(stderr, "{ %s [", type());
2153 if (c)
2154 fprintf(stderr, "%c", c);
2155 else
2156 fprintf(stderr, ci->nm.contents());
2157 if (push_state)
2158 fprintf(stderr, " <push_state>");
2159 if (state)
2160 state->display_state();
2161 fprintf(stderr, " nest level %d", div_nest_level);
2162 fprintf(stderr, "]}\n");
2163 fflush(stderr);
2166 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2167 node *gn1, node *gn2, statem *s,
2168 int pop, node *x)
2169 : glyph_node(c, t, gc, fc, s, pop, x), n1(gn1), n2(gn2)
2173 #ifdef STORE_WIDTH
2174 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2175 hunits w, node *gn1, node *gn2, statem *s,
2176 int pop, node *x)
2177 : glyph_node(c, t, gc, fc, w, s, pop, x), n1(gn1), n2(gn2)
2180 #endif
2182 ligature_node::~ligature_node()
2184 delete n1;
2185 delete n2;
2188 node *ligature_node::copy()
2190 #ifdef STORE_WIDTH
2191 return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy(),
2192 state, div_nest_level);
2193 #else
2194 return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy(),
2195 state, div_nest_level);
2196 #endif
2199 void ligature_node::ascii_print(ascii_output_file *ascii)
2201 n1->ascii_print(ascii);
2202 n2->ascii_print(ascii);
2205 hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail, int *count)
2207 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2208 return n1->get_hyphen_list(hl, count);
2211 node *ligature_node::add_self(node *n, hyphen_list **p)
2213 n = n1->add_self(n, p);
2214 n = n2->add_self(n, p);
2215 n1 = n2 = 0;
2216 delete this;
2217 return n;
2220 kern_pair_node::kern_pair_node(hunits n, node *first, node *second,
2221 statem* s, int pop, node *x)
2222 : node(x, s, pop), amount(n), n1(first), n2(second)
2226 dbreak_node::dbreak_node(node *n, node *p, statem *s, int pop, node *x)
2227 : node(x, s, pop), none(n), pre(p), post(0)
2231 node *dbreak_node::merge_glyph_node(glyph_node *gn)
2233 glyph_node *gn2 = (glyph_node *)gn->copy();
2234 node *new_none = none ? none->merge_glyph_node(gn) : 0;
2235 node *new_post = post ? post->merge_glyph_node(gn2) : 0;
2236 if (new_none == 0 && new_post == 0) {
2237 delete gn2;
2238 return 0;
2240 if (new_none != 0)
2241 none = new_none;
2242 else {
2243 gn->next = none;
2244 none = gn;
2246 if (new_post != 0)
2247 post = new_post;
2248 else {
2249 gn2->next = post;
2250 post = gn2;
2252 return this;
2255 node *kern_pair_node::merge_glyph_node(glyph_node *gn)
2257 node *nd = n2->merge_glyph_node(gn);
2258 if (nd == 0)
2259 return 0;
2260 n2 = nd;
2261 nd = n2->merge_self(n1);
2262 if (nd) {
2263 nd->next = next;
2264 n1 = 0;
2265 n2 = 0;
2266 delete this;
2267 return nd;
2269 return this;
2272 hunits kern_pair_node::italic_correction()
2274 return n2->italic_correction();
2277 hunits kern_pair_node::subscript_correction()
2279 return n2->subscript_correction();
2282 void kern_pair_node::vertical_extent(vunits *min, vunits *max)
2284 n1->vertical_extent(min, max);
2285 vunits min2, max2;
2286 n2->vertical_extent(&min2, &max2);
2287 if (min2 < *min)
2288 *min = min2;
2289 if (max2 > *max)
2290 *max = max2;
2293 node *kern_pair_node::add_discretionary_hyphen()
2295 tfont *tf = n2->get_tfont();
2296 if (tf) {
2297 if (tf->contains(soft_hyphen_char)) {
2298 color *gcol = n2->get_glyph_color();
2299 color *fcol = n2->get_fill_color();
2300 node *next1 = next;
2301 next = 0;
2302 node *n = copy();
2303 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2304 state, div_nest_level);
2305 node *nn = n->merge_glyph_node(gn);
2306 if (nn == 0) {
2307 gn->next = n;
2308 nn = gn;
2310 return new dbreak_node(this, nn, state, div_nest_level, next1);
2313 return this;
2316 kern_pair_node::~kern_pair_node()
2318 if (n1 != 0)
2319 delete n1;
2320 if (n2 != 0)
2321 delete n2;
2324 dbreak_node::~dbreak_node()
2326 delete_node_list(pre);
2327 delete_node_list(post);
2328 delete_node_list(none);
2331 node *kern_pair_node::copy()
2333 return new kern_pair_node(amount, n1->copy(), n2->copy(), state,
2334 div_nest_level);
2337 node *copy_node_list(node *n)
2339 node *p = 0;
2340 while (n != 0) {
2341 node *nn = n->copy();
2342 nn->next = p;
2343 p = nn;
2344 n = n->next;
2346 while (p != 0) {
2347 node *pp = p->next;
2348 p->next = n;
2349 n = p;
2350 p = pp;
2352 return n;
2355 void delete_node_list(node *n)
2357 while (n != 0) {
2358 node *tem = n;
2359 n = n->next;
2360 delete tem;
2364 node *dbreak_node::copy()
2366 dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre),
2367 state, div_nest_level);
2368 p->post = copy_node_list(post);
2369 return p;
2372 hyphen_list *node::get_hyphen_list(hyphen_list *tail, int *)
2374 return tail;
2377 hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail, int *count)
2379 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2380 return n1->get_hyphen_list(hl, count);
2383 class hyphen_inhibitor_node : public node {
2384 public:
2385 hyphen_inhibitor_node(node * = 0);
2386 node *copy();
2387 int same(node *);
2388 const char *type();
2389 int force_tprint();
2390 int is_tag();
2391 hyphenation_type get_hyphenation_type();
2394 hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
2398 node *hyphen_inhibitor_node::copy()
2400 return new hyphen_inhibitor_node;
2403 int hyphen_inhibitor_node::same(node *)
2405 return 1;
2408 const char *hyphen_inhibitor_node::type()
2410 return "hyphen_inhibitor_node";
2413 int hyphen_inhibitor_node::force_tprint()
2415 return 0;
2418 int hyphen_inhibitor_node::is_tag()
2420 return 0;
2423 hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
2425 return HYPHEN_INHIBIT;
2428 /* add_discretionary_hyphen methods */
2430 node *dbreak_node::add_discretionary_hyphen()
2432 if (post)
2433 post = post->add_discretionary_hyphen();
2434 if (none)
2435 none = none->add_discretionary_hyphen();
2436 return this;
2439 node *node::add_discretionary_hyphen()
2441 tfont *tf = get_tfont();
2442 if (!tf)
2443 return new hyphen_inhibitor_node(this);
2444 if (tf->contains(soft_hyphen_char)) {
2445 color *gcol = get_glyph_color();
2446 color *fcol = get_fill_color();
2447 node *next1 = next;
2448 next = 0;
2449 node *n = copy();
2450 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2451 state, div_nest_level);
2452 node *n1 = n->merge_glyph_node(gn);
2453 if (n1 == 0) {
2454 gn->next = n;
2455 n1 = gn;
2457 return new dbreak_node(this, n1, state, div_nest_level, next1);
2459 return this;
2462 node *node::merge_self(node *)
2464 return 0;
2467 node *node::add_self(node *n, hyphen_list ** /*p*/)
2469 next = n;
2470 return this;
2473 node *kern_pair_node::add_self(node *n, hyphen_list **p)
2475 n = n1->add_self(n, p);
2476 n = n2->add_self(n, p);
2477 n1 = n2 = 0;
2478 delete this;
2479 return n;
2482 hunits node::width()
2484 return H0;
2487 node *node::last_char_node()
2489 return 0;
2492 int node::force_tprint()
2494 return 0;
2497 int node::is_tag()
2499 return 0;
2502 hunits hmotion_node::width()
2504 return n;
2507 units node::size()
2509 return points_to_units(10);
2512 void node::debug_node()
2514 fprintf(stderr, "{ %s ", type());
2515 if (push_state)
2516 fprintf(stderr, " <push_state>");
2517 if (state)
2518 fprintf(stderr, " <state>");
2519 fprintf(stderr, " nest level %d", div_nest_level);
2520 fprintf(stderr, " }\n");
2521 fflush(stderr);
2524 void node::debug_node_list()
2526 node *n = next;
2528 debug_node();
2529 while (n != 0) {
2530 n->debug_node();
2531 n = n->next;
2535 hunits kern_pair_node::width()
2537 return n1->width() + n2->width() + amount;
2540 node *kern_pair_node::last_char_node()
2542 node *nd = n2->last_char_node();
2543 if (nd)
2544 return nd;
2545 return n1->last_char_node();
2548 hunits dbreak_node::width()
2550 hunits x = H0;
2551 for (node *n = none; n != 0; n = n->next)
2552 x += n->width();
2553 return x;
2556 node *dbreak_node::last_char_node()
2558 for (node *n = none; n; n = n->next) {
2559 node *last_node = n->last_char_node();
2560 if (last_node)
2561 return last_node;
2563 return 0;
2566 hunits dbreak_node::italic_correction()
2568 return none ? none->italic_correction() : H0;
2571 hunits dbreak_node::subscript_correction()
2573 return none ? none->subscript_correction() : H0;
2576 class italic_corrected_node : public node {
2577 node *n;
2578 hunits x;
2579 public:
2580 italic_corrected_node(node *, hunits, statem *, int, node * = 0);
2581 ~italic_corrected_node();
2582 node *copy();
2583 void ascii_print(ascii_output_file *);
2584 void asciify(macro *);
2585 hunits width();
2586 node *last_char_node();
2587 void vertical_extent(vunits *, vunits *);
2588 int ends_sentence();
2589 int overlaps_horizontally();
2590 int overlaps_vertically();
2591 int same(node *);
2592 hyphenation_type get_hyphenation_type();
2593 tfont *get_tfont();
2594 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2595 int character_type();
2596 void tprint(troff_output_file *);
2597 hunits subscript_correction();
2598 hunits skew();
2599 node *add_self(node *, hyphen_list **);
2600 const char *type();
2601 int force_tprint();
2602 int is_tag();
2605 node *node::add_italic_correction(hunits *wd)
2607 hunits ic = italic_correction();
2608 if (ic.is_zero())
2609 return this;
2610 else {
2611 node *next1 = next;
2612 next = 0;
2613 *wd += ic;
2614 return new italic_corrected_node(this, ic, state, div_nest_level, next1);
2618 italic_corrected_node::italic_corrected_node(node *nn, hunits xx, statem *s,
2619 int pop, node *p)
2620 : node(p, s, pop), n(nn), x(xx)
2622 assert(n != 0);
2625 italic_corrected_node::~italic_corrected_node()
2627 delete n;
2630 node *italic_corrected_node::copy()
2632 return new italic_corrected_node(n->copy(), x, state, div_nest_level);
2635 hunits italic_corrected_node::width()
2637 return n->width() + x;
2640 void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
2642 n->vertical_extent(min, max);
2645 void italic_corrected_node::tprint(troff_output_file *out)
2647 n->tprint(out);
2648 out->right(x);
2651 hunits italic_corrected_node::skew()
2653 return n->skew() - x/2;
2656 hunits italic_corrected_node::subscript_correction()
2658 return n->subscript_correction() - x;
2661 void italic_corrected_node::ascii_print(ascii_output_file *out)
2663 n->ascii_print(out);
2666 int italic_corrected_node::ends_sentence()
2668 return n->ends_sentence();
2671 int italic_corrected_node::overlaps_horizontally()
2673 return n->overlaps_horizontally();
2676 int italic_corrected_node::overlaps_vertically()
2678 return n->overlaps_vertically();
2681 node *italic_corrected_node::last_char_node()
2683 return n->last_char_node();
2686 tfont *italic_corrected_node::get_tfont()
2688 return n->get_tfont();
2691 hyphenation_type italic_corrected_node::get_hyphenation_type()
2693 return n->get_hyphenation_type();
2696 node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
2698 nd = n->add_self(nd, p);
2699 hunits not_interested;
2700 nd = nd->add_italic_correction(&not_interested);
2701 n = 0;
2702 delete this;
2703 return nd;
2706 hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail,
2707 int *count)
2709 return n->get_hyphen_list(tail, count);
2712 int italic_corrected_node::character_type()
2714 return n->character_type();
2717 class break_char_node : public node {
2718 node *ch;
2719 char break_code;
2720 color *col;
2721 public:
2722 break_char_node(node *, int, color *, node * = 0);
2723 break_char_node(node *, int, color *, statem *, int, node * = 0);
2724 ~break_char_node();
2725 node *copy();
2726 hunits width();
2727 vunits vertical_width();
2728 node *last_char_node();
2729 int character_type();
2730 int ends_sentence();
2731 node *add_self(node *, hyphen_list **);
2732 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2733 void tprint(troff_output_file *);
2734 void zero_width_tprint(troff_output_file *);
2735 void ascii_print(ascii_output_file *);
2736 void asciify(macro *);
2737 hyphenation_type get_hyphenation_type();
2738 int overlaps_vertically();
2739 int overlaps_horizontally();
2740 units size();
2741 tfont *get_tfont();
2742 int same(node *);
2743 const char *type();
2744 int force_tprint();
2745 int is_tag();
2748 break_char_node::break_char_node(node *n, int bc, color *c, node *x)
2749 : node(x), ch(n), break_code(bc), col(c)
2753 break_char_node::break_char_node(node *n, int bc, color *c, statem *s,
2754 int pop, node *x)
2755 : node(x, s, pop), ch(n), break_code(bc), col(c)
2759 break_char_node::~break_char_node()
2761 delete ch;
2764 node *break_char_node::copy()
2766 return new break_char_node(ch->copy(), break_code, col, state,
2767 div_nest_level);
2770 hunits break_char_node::width()
2772 return ch->width();
2775 vunits break_char_node::vertical_width()
2777 return ch->vertical_width();
2780 node *break_char_node::last_char_node()
2782 return ch->last_char_node();
2785 int break_char_node::character_type()
2787 return ch->character_type();
2790 int break_char_node::ends_sentence()
2792 return ch->ends_sentence();
2795 node *break_char_node::add_self(node *n, hyphen_list **p)
2797 assert((*p)->hyphenation_code == 0);
2798 if ((*p)->breakable && (break_code & 1)) {
2799 n = new space_node(H0, col, n);
2800 n->freeze_space();
2802 next = n;
2803 n = this;
2804 if ((*p)->breakable && (break_code & 2)) {
2805 n = new space_node(H0, col, n);
2806 n->freeze_space();
2808 hyphen_list *pp = *p;
2809 *p = (*p)->next;
2810 delete pp;
2811 return n;
2814 hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail, int *)
2816 return new hyphen_list(0, tail);
2819 hyphenation_type break_char_node::get_hyphenation_type()
2821 return HYPHEN_MIDDLE;
2824 void break_char_node::ascii_print(ascii_output_file *ascii)
2826 ch->ascii_print(ascii);
2829 int break_char_node::overlaps_vertically()
2831 return ch->overlaps_vertically();
2834 int break_char_node::overlaps_horizontally()
2836 return ch->overlaps_horizontally();
2839 units break_char_node::size()
2841 return ch->size();
2844 tfont *break_char_node::get_tfont()
2846 return ch->get_tfont();
2849 node *extra_size_node::copy()
2851 return new extra_size_node(n, state, div_nest_level);
2854 extra_size_node::extra_size_node(vunits i, statem *s, int pop)
2855 : node(0, s, pop), n(i)
2859 extra_size_node::extra_size_node(vunits i)
2860 : n(i)
2864 node *vertical_size_node::copy()
2866 return new vertical_size_node(n, state, div_nest_level);
2869 vertical_size_node::vertical_size_node(vunits i, statem *s, int pop)
2870 : node(0, s, pop), n(i)
2874 vertical_size_node::vertical_size_node(vunits i)
2875 : n(i)
2879 node *hmotion_node::copy()
2881 return new hmotion_node(n, was_tab, unformat, col, state, div_nest_level);
2884 node *space_char_hmotion_node::copy()
2886 return new space_char_hmotion_node(n, col, state, div_nest_level);
2889 vmotion_node::vmotion_node(vunits i, color *c)
2890 : n(i), col(c)
2894 vmotion_node::vmotion_node(vunits i, color *c, statem *s, int pop)
2895 : node(0, s, pop), n(i), col(c)
2899 node *vmotion_node::copy()
2901 return new vmotion_node(n, col, state, div_nest_level);
2904 node *dummy_node::copy()
2906 return new dummy_node;
2909 node *transparent_dummy_node::copy()
2911 return new transparent_dummy_node;
2914 hline_node::~hline_node()
2916 if (n)
2917 delete n;
2920 hline_node::hline_node(hunits i, node *c, node *nxt)
2921 : node(nxt), x(i), n(c)
2925 hline_node::hline_node(hunits i, node *c, statem *s, int pop, node *nxt)
2926 : node(nxt, s, pop), x(i), n(c)
2930 node *hline_node::copy()
2932 return new hline_node(x, n ? n->copy() : 0, state, div_nest_level);
2935 hunits hline_node::width()
2937 return x < H0 ? H0 : x;
2940 vline_node::vline_node(vunits i, node *c, node *nxt)
2941 : node(nxt), x(i), n(c)
2945 vline_node::vline_node(vunits i, node *c, statem *s, int pop, node *nxt)
2946 : node(nxt, s, pop), x(i), n(c)
2950 vline_node::~vline_node()
2952 if (n)
2953 delete n;
2956 node *vline_node::copy()
2958 return new vline_node(x, n ? n->copy() : 0, state, div_nest_level);
2961 hunits vline_node::width()
2963 return n == 0 ? H0 : n->width();
2966 zero_width_node::zero_width_node(node *nd, statem *s, int pop)
2967 : node(0, s, pop), n(nd)
2971 zero_width_node::zero_width_node(node *nd)
2972 : n(nd)
2976 zero_width_node::~zero_width_node()
2978 delete_node_list(n);
2981 node *zero_width_node::copy()
2983 return new zero_width_node(copy_node_list(n), state, div_nest_level);
2986 int node_list_character_type(node *p)
2988 int t = 0;
2989 for (; p; p = p->next)
2990 t |= p->character_type();
2991 return t;
2994 int zero_width_node::character_type()
2996 return node_list_character_type(n);
2999 void node_list_vertical_extent(node *p, vunits *min, vunits *max)
3001 *min = V0;
3002 *max = V0;
3003 vunits cur_vpos = V0;
3004 vunits v1, v2;
3005 for (; p; p = p->next) {
3006 p->vertical_extent(&v1, &v2);
3007 v1 += cur_vpos;
3008 if (v1 < *min)
3009 *min = v1;
3010 v2 += cur_vpos;
3011 if (v2 > *max)
3012 *max = v2;
3013 cur_vpos += p->vertical_width();
3017 void zero_width_node::vertical_extent(vunits *min, vunits *max)
3019 node_list_vertical_extent(n, min, max);
3022 overstrike_node::overstrike_node()
3023 : list(0), max_width(H0)
3027 overstrike_node::overstrike_node(statem *s, int pop)
3028 : node(0, s, pop), list(0), max_width(H0)
3032 overstrike_node::~overstrike_node()
3034 delete_node_list(list);
3037 node *overstrike_node::copy()
3039 overstrike_node *on = new overstrike_node(state, div_nest_level);
3040 for (node *tem = list; tem; tem = tem->next)
3041 on->overstrike(tem->copy());
3042 return on;
3045 void overstrike_node::overstrike(node *n)
3047 if (n == 0)
3048 return;
3049 hunits w = n->width();
3050 if (w > max_width)
3051 max_width = w;
3052 node **p;
3053 for (p = &list; *p; p = &(*p)->next)
3055 n->next = 0;
3056 *p = n;
3059 hunits overstrike_node::width()
3061 return max_width;
3064 bracket_node::bracket_node()
3065 : list(0), max_width(H0)
3069 bracket_node::bracket_node(statem *s, int pop)
3070 : node(0, s, pop), list(0), max_width(H0)
3074 bracket_node::~bracket_node()
3076 delete_node_list(list);
3079 node *bracket_node::copy()
3081 bracket_node *on = new bracket_node(state, div_nest_level);
3082 node *last_node = 0;
3083 node *tem;
3084 if (list)
3085 list->last = 0;
3086 for (tem = list; tem; tem = tem->next) {
3087 if (tem->next)
3088 tem->next->last = tem;
3089 last_node = tem;
3091 for (tem = last_node; tem; tem = tem->last)
3092 on->bracket(tem->copy());
3093 return on;
3096 void bracket_node::bracket(node *n)
3098 if (n == 0)
3099 return;
3100 hunits w = n->width();
3101 if (w > max_width)
3102 max_width = w;
3103 n->next = list;
3104 list = n;
3107 hunits bracket_node::width()
3109 return max_width;
3112 int node::nspaces()
3114 return 0;
3117 int node::merge_space(hunits, hunits, hunits)
3119 return 0;
3122 #if 0
3123 space_node *space_node::free_list = 0;
3125 void *space_node::operator new(size_t n)
3127 assert(n == sizeof(space_node));
3128 if (!free_list) {
3129 free_list = (space_node *)new char[sizeof(space_node)*BLOCK];
3130 for (int i = 0; i < BLOCK - 1; i++)
3131 free_list[i].next = free_list + i + 1;
3132 free_list[BLOCK-1].next = 0;
3134 space_node *p = free_list;
3135 free_list = (space_node *)(free_list->next);
3136 p->next = 0;
3137 return p;
3140 inline void space_node::operator delete(void *p)
3142 if (p) {
3143 ((space_node *)p)->next = free_list;
3144 free_list = (space_node *)p;
3147 #endif
3149 space_node::space_node(hunits nn, color *c, node *p)
3150 : node(p, 0, 0), n(nn), set(0), was_escape_colon(0), col(c)
3154 space_node::space_node(hunits nn, color *c, statem *s, int pop, node *p)
3155 : node(p, s, pop), n(nn), set(0), was_escape_colon(0), col(c)
3159 space_node::space_node(hunits nn, int s, int flag, color *c, statem *st,
3160 int pop, node *p)
3161 : node(p, st, pop), n(nn), set(s), was_escape_colon(flag), col(c)
3165 #if 0
3166 space_node::~space_node()
3169 #endif
3171 node *space_node::copy()
3173 return new space_node(n, set, was_escape_colon, col, state, div_nest_level);
3176 int space_node::force_tprint()
3178 return 0;
3181 int space_node::is_tag()
3183 return 0;
3186 int space_node::nspaces()
3188 return set ? 0 : 1;
3191 int space_node::merge_space(hunits h, hunits, hunits)
3193 n += h;
3194 return 1;
3197 hunits space_node::width()
3199 return n;
3202 void node::spread_space(int*, hunits*)
3206 void space_node::spread_space(int *n_spaces, hunits *desired_space)
3208 if (!set) {
3209 assert(*n_spaces > 0);
3210 if (*n_spaces == 1) {
3211 n += *desired_space;
3212 *desired_space = H0;
3214 else {
3215 hunits extra = *desired_space / *n_spaces;
3216 *desired_space -= extra;
3217 n += extra;
3219 *n_spaces -= 1;
3220 set = 1;
3224 void node::freeze_space()
3228 void space_node::freeze_space()
3230 set = 1;
3233 void node::is_escape_colon()
3237 void space_node::is_escape_colon()
3239 was_escape_colon = 1;
3242 diverted_space_node::diverted_space_node(vunits d, statem *s, int pop,
3243 node *p)
3244 : node(p, s, pop), n(d)
3248 diverted_space_node::diverted_space_node(vunits d, node *p)
3249 : node(p), n(d)
3253 node *diverted_space_node::copy()
3255 return new diverted_space_node(n, state, div_nest_level);
3258 diverted_copy_file_node::diverted_copy_file_node(symbol s, statem *st,
3259 int pop, node *p)
3260 : node(p, st, pop), filename(s)
3264 diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
3265 : node(p), filename(s)
3269 node *diverted_copy_file_node::copy()
3271 return new diverted_copy_file_node(filename, state, div_nest_level);
3274 int node::ends_sentence()
3276 return 0;
3279 int kern_pair_node::ends_sentence()
3281 switch (n2->ends_sentence()) {
3282 case 0:
3283 return 0;
3284 case 1:
3285 return 1;
3286 case 2:
3287 break;
3288 default:
3289 assert(0);
3291 return n1->ends_sentence();
3294 int node_list_ends_sentence(node *n)
3296 for (; n != 0; n = n->next)
3297 switch (n->ends_sentence()) {
3298 case 0:
3299 return 0;
3300 case 1:
3301 return 1;
3302 case 2:
3303 break;
3304 default:
3305 assert(0);
3307 return 2;
3310 int dbreak_node::ends_sentence()
3312 return node_list_ends_sentence(none);
3315 int node::overlaps_horizontally()
3317 return 0;
3320 int node::overlaps_vertically()
3322 return 0;
3325 int node::discardable()
3327 return 0;
3330 int space_node::discardable()
3332 return set ? 0 : 1;
3335 vunits node::vertical_width()
3337 return V0;
3340 vunits vline_node::vertical_width()
3342 return x;
3345 vunits vmotion_node::vertical_width()
3347 return n;
3350 int node::set_unformat_flag()
3352 return 1;
3355 int node::character_type()
3357 return 0;
3360 hunits node::subscript_correction()
3362 return H0;
3365 hunits node::italic_correction()
3367 return H0;
3370 hunits node::left_italic_correction()
3372 return H0;
3375 hunits node::skew()
3377 return H0;
3380 /* vertical_extent methods */
3382 void node::vertical_extent(vunits *min, vunits *max)
3384 vunits v = vertical_width();
3385 if (v < V0) {
3386 *min = v;
3387 *max = V0;
3389 else {
3390 *max = v;
3391 *min = V0;
3395 void vline_node::vertical_extent(vunits *min, vunits *max)
3397 if (n == 0)
3398 node::vertical_extent(min, max);
3399 else {
3400 vunits cmin, cmax;
3401 n->vertical_extent(&cmin, &cmax);
3402 vunits h = n->size();
3403 if (x < V0) {
3404 if (-x < h) {
3405 *min = x;
3406 *max = V0;
3408 else {
3409 // we print the first character and then move up, so
3410 *max = cmax;
3411 // we print the last character and then move up h
3412 *min = cmin + h;
3413 if (*min > V0)
3414 *min = V0;
3415 *min += x;
3418 else {
3419 if (x < h) {
3420 *max = x;
3421 *min = V0;
3423 else {
3424 // we move down by h and then print the first character, so
3425 *min = cmin + h;
3426 if (*min > V0)
3427 *min = V0;
3428 *max = x + cmax;
3434 /* ascii_print methods */
3436 static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n)
3438 if (n == 0)
3439 return;
3440 ascii_print_reverse_node_list(ascii, n->next);
3441 n->ascii_print(ascii);
3444 void dbreak_node::ascii_print(ascii_output_file *ascii)
3446 ascii_print_reverse_node_list(ascii, none);
3449 void kern_pair_node::ascii_print(ascii_output_file *ascii)
3451 n1->ascii_print(ascii);
3452 n2->ascii_print(ascii);
3455 void node::ascii_print(ascii_output_file *)
3459 void space_node::ascii_print(ascii_output_file *ascii)
3461 if (!n.is_zero())
3462 ascii->outc(' ');
3465 void hmotion_node::ascii_print(ascii_output_file *ascii)
3467 // this is pretty arbitrary
3468 if (n >= points_to_units(2))
3469 ascii->outc(' ');
3472 void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
3474 ascii->outc(' ');
3477 /* asciify methods */
3479 void node::asciify(macro *m)
3481 m->append(this);
3484 void glyph_node::asciify(macro *m)
3486 unsigned char c = ci->get_asciify_code();
3487 if (c == 0)
3488 c = ci->get_ascii_code();
3489 if (c != 0) {
3490 m->append(c);
3491 delete this;
3493 else
3494 m->append(this);
3497 void kern_pair_node::asciify(macro *m)
3499 n1->asciify(m);
3500 n2->asciify(m);
3501 n1 = n2 = 0;
3502 delete this;
3505 static void asciify_reverse_node_list(macro *m, node *n)
3507 if (n == 0)
3508 return;
3509 asciify_reverse_node_list(m, n->next);
3510 n->asciify(m);
3513 void dbreak_node::asciify(macro *m)
3515 asciify_reverse_node_list(m, none);
3516 none = 0;
3517 delete this;
3520 void ligature_node::asciify(macro *m)
3522 n1->asciify(m);
3523 n2->asciify(m);
3524 n1 = n2 = 0;
3525 delete this;
3528 void break_char_node::asciify(macro *m)
3530 ch->asciify(m);
3531 ch = 0;
3532 delete this;
3535 void italic_corrected_node::asciify(macro *m)
3537 n->asciify(m);
3538 n = 0;
3539 delete this;
3542 void left_italic_corrected_node::asciify(macro *m)
3544 if (n) {
3545 n->asciify(m);
3546 n = 0;
3548 delete this;
3551 void hmotion_node::asciify(macro *m)
3553 if (was_tab) {
3554 m->append('\t');
3555 delete this;
3557 else
3558 m->append(this);
3561 space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3562 statem *s, int pop,
3563 node *nxt)
3564 : hmotion_node(i, c, s, pop, nxt)
3568 space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3569 node *nxt)
3570 : hmotion_node(i, c, 0, 0, nxt)
3574 void space_char_hmotion_node::asciify(macro *m)
3576 m->append(ESCAPE_SPACE);
3577 delete this;
3580 void space_node::asciify(macro *m)
3582 if (was_escape_colon) {
3583 m->append(ESCAPE_COLON);
3584 delete this;
3586 else
3587 m->append(this);
3590 void word_space_node::asciify(macro *m)
3592 for (width_list *w = orig_width; w; w = w->next)
3593 m->append(' ');
3594 delete this;
3597 void unbreakable_space_node::asciify(macro *m)
3599 m->append(ESCAPE_TILDE);
3600 delete this;
3603 void line_start_node::asciify(macro *)
3605 delete this;
3608 void vertical_size_node::asciify(macro *)
3610 delete this;
3613 breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/,
3614 breakpoint *rest, int /*is_inner*/)
3616 return rest;
3619 int node::nbreaks()
3621 return 0;
3624 breakpoint *space_node::get_breakpoints(hunits wd, int ns,
3625 breakpoint *rest, int is_inner)
3627 if (next && next->discardable())
3628 return rest;
3629 breakpoint *bp = new breakpoint;
3630 bp->next = rest;
3631 bp->width = wd;
3632 bp->nspaces = ns;
3633 bp->hyphenated = 0;
3634 if (is_inner) {
3635 assert(rest != 0);
3636 bp->index = rest->index + 1;
3637 bp->nd = rest->nd;
3639 else {
3640 bp->nd = this;
3641 bp->index = 0;
3643 return bp;
3646 int space_node::nbreaks()
3648 if (next && next->discardable())
3649 return 0;
3650 else
3651 return 1;
3654 static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
3655 int ns, breakpoint *rest)
3657 if (p != 0) {
3658 rest = p->get_breakpoints(*widthp,
3660 node_list_get_breakpoints(p->next, widthp, ns,
3661 rest),
3663 *widthp += p->width();
3665 return rest;
3668 breakpoint *dbreak_node::get_breakpoints(hunits wd, int ns,
3669 breakpoint *rest, int is_inner)
3671 breakpoint *bp = new breakpoint;
3672 bp->next = rest;
3673 bp->width = wd;
3674 for (node *tem = pre; tem != 0; tem = tem->next)
3675 bp->width += tem->width();
3676 bp->nspaces = ns;
3677 bp->hyphenated = 1;
3678 if (is_inner) {
3679 assert(rest != 0);
3680 bp->index = rest->index + 1;
3681 bp->nd = rest->nd;
3683 else {
3684 bp->nd = this;
3685 bp->index = 0;
3687 return node_list_get_breakpoints(none, &wd, ns, bp);
3690 int dbreak_node::nbreaks()
3692 int i = 1;
3693 for (node *tem = none; tem != 0; tem = tem->next)
3694 i += tem->nbreaks();
3695 return i;
3698 void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
3700 assert(0);
3703 void space_node::split(int where, node **pre, node **post)
3705 assert(where == 0);
3706 *pre = next;
3707 *post = 0;
3708 delete this;
3711 static void node_list_split(node *p, int *wherep, node **prep, node **postp)
3713 if (p == 0)
3714 return;
3715 int nb = p->nbreaks();
3716 node_list_split(p->next, wherep, prep, postp);
3717 if (*wherep < 0) {
3718 p->next = *postp;
3719 *postp = p;
3721 else if (*wherep < nb) {
3722 p->next = *prep;
3723 p->split(*wherep, prep, postp);
3725 else {
3726 p->next = *prep;
3727 *prep = p;
3729 *wherep -= nb;
3732 void dbreak_node::split(int where, node **prep, node **postp)
3734 assert(where >= 0);
3735 if (where == 0) {
3736 *postp = post;
3737 post = 0;
3738 if (pre == 0)
3739 *prep = next;
3740 else {
3741 node *tem;
3742 for (tem = pre; tem->next != 0; tem = tem->next)
3744 tem->next = next;
3745 *prep = pre;
3747 pre = 0;
3748 delete this;
3750 else {
3751 *prep = next;
3752 where -= 1;
3753 node_list_split(none, &where, prep, postp);
3754 none = 0;
3755 delete this;
3759 hyphenation_type node::get_hyphenation_type()
3761 return HYPHEN_BOUNDARY;
3764 hyphenation_type dbreak_node::get_hyphenation_type()
3766 return HYPHEN_INHIBIT;
3769 hyphenation_type kern_pair_node::get_hyphenation_type()
3771 return HYPHEN_MIDDLE;
3774 hyphenation_type dummy_node::get_hyphenation_type()
3776 return HYPHEN_MIDDLE;
3779 hyphenation_type transparent_dummy_node::get_hyphenation_type()
3781 return HYPHEN_MIDDLE;
3784 hyphenation_type hmotion_node::get_hyphenation_type()
3786 return HYPHEN_MIDDLE;
3789 hyphenation_type space_char_hmotion_node::get_hyphenation_type()
3791 return HYPHEN_MIDDLE;
3794 hyphenation_type overstrike_node::get_hyphenation_type()
3796 return HYPHEN_MIDDLE;
3799 hyphenation_type space_node::get_hyphenation_type()
3801 if (was_escape_colon)
3802 return HYPHEN_MIDDLE;
3803 return HYPHEN_BOUNDARY;
3806 hyphenation_type unbreakable_space_node::get_hyphenation_type()
3808 return HYPHEN_MIDDLE;
3811 int node::interpret(macro *)
3813 return 0;
3816 special_node::special_node(const macro &m, int n)
3817 : mac(m), no_init_string(n)
3819 font_size fs = curenv->get_font_size();
3820 int char_height = curenv->get_char_height();
3821 int char_slant = curenv->get_char_slant();
3822 int fontno = env_definite_font(curenv);
3823 tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fontno);
3824 if (curenv->is_composite())
3825 tf = tf->get_plain();
3826 gcol = curenv->get_glyph_color();
3827 fcol = curenv->get_fill_color();
3828 is_special = 1;
3831 special_node::special_node(const macro &m, tfont *t,
3832 color *gc, color *fc,
3833 statem *s, int pop,
3834 int n)
3835 : node(0, s, pop), mac(m), tf(t), gcol(gc), fcol(fc), no_init_string(n)
3837 is_special = 1;
3840 int special_node::same(node *n)
3842 return mac == ((special_node *)n)->mac
3843 && tf == ((special_node *)n)->tf
3844 && gcol == ((special_node *)n)->gcol
3845 && fcol == ((special_node *)n)->fcol
3846 && no_init_string == ((special_node *)n)->no_init_string;
3849 const char *special_node::type()
3851 return "special_node";
3854 int special_node::ends_sentence()
3856 return 2;
3859 int special_node::force_tprint()
3861 return 0;
3864 int special_node::is_tag()
3866 return 0;
3869 node *special_node::copy()
3871 return new special_node(mac, tf, gcol, fcol, state, div_nest_level,
3872 no_init_string);
3875 void special_node::tprint_start(troff_output_file *out)
3877 out->start_special(tf, gcol, fcol, no_init_string);
3880 void special_node::tprint_char(troff_output_file *out, unsigned char c)
3882 out->special_char(c);
3885 void special_node::tprint_end(troff_output_file *out)
3887 out->end_special();
3890 tfont *special_node::get_tfont()
3892 return tf;
3895 /* suppress_node */
3897 suppress_node::suppress_node(int on_or_off, int issue_limits)
3898 : is_on(on_or_off), emit_limits(issue_limits), filename(0), position(0),
3899 image_id(0)
3903 suppress_node::suppress_node(symbol f, char p, int id)
3904 : is_on(2), emit_limits(0), filename(f), position(p), image_id(id)
3906 is_special = 1;
3909 suppress_node::suppress_node(int issue_limits, int on_or_off,
3910 symbol f, char p, int id,
3911 statem *s, int pop)
3912 : node(0, s, pop), is_on(on_or_off), emit_limits(issue_limits), filename(f),
3913 position(p), image_id(id)
3917 int suppress_node::same(node *n)
3919 return ((is_on == ((suppress_node *)n)->is_on)
3920 && (emit_limits == ((suppress_node *)n)->emit_limits)
3921 && (filename == ((suppress_node *)n)->filename)
3922 && (position == ((suppress_node *)n)->position)
3923 && (image_id == ((suppress_node *)n)->image_id));
3926 const char *suppress_node::type()
3928 return "suppress_node";
3931 node *suppress_node::copy()
3933 return new suppress_node(emit_limits, is_on, filename, position, image_id,
3934 state, div_nest_level);
3937 /* tag_node */
3939 tag_node::tag_node()
3940 : delayed(0)
3942 is_special = 1;
3945 tag_node::tag_node(string s, int delay)
3946 : tag_string(s), delayed(delay)
3948 is_special = !delay;
3951 tag_node::tag_node(string s, statem *st, int pop, int delay)
3952 : node(0, st, pop), tag_string(s), delayed(delay)
3954 is_special = !delay;
3957 node *tag_node::copy()
3959 return new tag_node(tag_string, state, div_nest_level, delayed);
3962 void tag_node::tprint(troff_output_file *out)
3964 if (delayed)
3965 out->add_to_tag_list(tag_string);
3966 else
3967 out->state.add_tag(out->fp, tag_string);
3970 int tag_node::same(node *nd)
3972 return tag_string == ((tag_node *)nd)->tag_string
3973 && delayed == ((tag_node *)nd)->delayed;
3976 const char *tag_node::type()
3978 return "tag_node";
3981 int tag_node::force_tprint()
3983 return !delayed;
3986 int tag_node::is_tag()
3988 return !delayed;
3991 int tag_node::ends_sentence()
3993 return 2;
3996 int get_reg_int(const char *p)
3998 reg *r = (reg *)number_reg_dictionary.lookup(p);
3999 units prev_value;
4000 if (r && (r->get_value(&prev_value)))
4001 return (int)prev_value;
4002 else
4003 warning(WARN_REG, "number register `%1' not defined", p);
4004 return 0;
4007 const char *get_reg_str(const char *p)
4009 reg *r = (reg *)number_reg_dictionary.lookup(p);
4010 if (r)
4011 return r->get_string();
4012 else
4013 warning(WARN_REG, "register `%1' not defined", p);
4014 return 0;
4017 void suppress_node::put(troff_output_file *out, const char *s)
4019 int i = 0;
4020 while (s[i] != (char)0) {
4021 out->special_char(s[i]);
4022 i++;
4027 * We need to remember the start of the image and its name.
4030 static char last_position = 0;
4031 static const char *last_image_filename = 0;
4032 static int last_image_id = 0;
4034 inline int min(int a, int b)
4036 return a < b ? a : b;
4040 * tprint - if (is_on == 2)
4041 * remember current position (l, r, c, i) and filename
4042 * else
4043 * if (emit_limits)
4044 * if (html)
4045 * emit image tag
4046 * else
4047 * emit postscript bounds for image
4048 * else
4049 * if (suppress boolean differs from current state)
4050 * alter state
4051 * reset registers
4052 * record current page
4053 * set low water mark.
4056 void suppress_node::tprint(troff_output_file *out)
4058 int current_page = topdiv->get_page_number();
4059 // firstly check to see whether this suppress node contains
4060 // an image filename & position.
4061 if (is_on == 2) {
4062 // remember position and filename
4063 last_position = position;
4064 char *tem = (char *)last_image_filename;
4065 last_image_filename = strsave(filename.contents());
4066 if (tem)
4067 a_delete tem;
4068 last_image_id = image_id;
4069 // printf("start of image and page = %d\n", current_page);
4071 else {
4072 // now check whether the suppress node requires us to issue limits.
4073 if (emit_limits) {
4074 char name[8192];
4075 // remember that the filename will contain a %d in which the
4076 // last_image_id is placed
4077 if (last_image_filename == (char *) 0)
4078 *name = '\0';
4079 else
4080 sprintf(name, last_image_filename, last_image_id);
4081 if (is_html) {
4082 switch (last_position) {
4083 case 'c':
4084 out->start_special();
4085 put(out, "devtag:.centered-image");
4086 break;
4087 case 'r':
4088 out->start_special();
4089 put(out, "devtag:.right-image");
4090 break;
4091 case 'l':
4092 out->start_special();
4093 put(out, "devtag:.left-image");
4094 break;
4095 case 'i':
4097 default:
4100 out->end_special();
4101 out->start_special();
4102 put(out, "devtag:.auto-image ");
4103 put(out, name);
4104 out->end_special();
4106 else {
4107 // postscript (or other device)
4108 if (suppress_start_page > 0 && current_page != suppress_start_page)
4109 error("suppression limit registers span more than one page;\n"
4110 "image description %1 will be wrong", image_no);
4111 // if (topdiv->get_page_number() != suppress_start_page)
4112 // fprintf(stderr, "end of image and topdiv page = %d and suppress_start_page = %d\n",
4113 // topdiv->get_page_number(), suppress_start_page);
4115 // remember that the filename will contain a %d in which the
4116 // image_no is placed
4117 fprintf(stderr,
4118 "grohtml-info:page %d %d %d %d %d %d %s %d %d %s\n",
4119 topdiv->get_page_number(),
4120 get_reg_int("opminx"), get_reg_int("opminy"),
4121 get_reg_int("opmaxx"), get_reg_int("opmaxy"),
4122 // page offset + line length
4123 get_reg_int(".o") + get_reg_int(".l"),
4124 name, hresolution, vresolution, get_reg_str(".F"));
4125 fflush(stderr);
4128 else {
4129 if (is_on) {
4130 out->on();
4131 // lastly we reset the output registers
4132 reset_output_registers();
4134 else
4135 out->off();
4136 suppress_start_page = current_page;
4141 int suppress_node::force_tprint()
4143 return is_on;
4146 int suppress_node::is_tag()
4148 return is_on;
4151 hunits suppress_node::width()
4153 return H0;
4156 /* composite_node */
4158 class composite_node : public charinfo_node {
4159 node *n;
4160 tfont *tf;
4161 public:
4162 composite_node(node *, charinfo *, tfont *, statem *, int, node * = 0);
4163 ~composite_node();
4164 node *copy();
4165 hunits width();
4166 node *last_char_node();
4167 units size();
4168 void tprint(troff_output_file *);
4169 hyphenation_type get_hyphenation_type();
4170 void ascii_print(ascii_output_file *);
4171 void asciify(macro *);
4172 hyphen_list *get_hyphen_list(hyphen_list *, int *);
4173 node *add_self(node *, hyphen_list **);
4174 tfont *get_tfont();
4175 int same(node *);
4176 const char *type();
4177 int force_tprint();
4178 int is_tag();
4179 void vertical_extent(vunits *, vunits *);
4180 vunits vertical_width();
4183 composite_node::composite_node(node *p, charinfo *c, tfont *t, statem *s,
4184 int pop, node *x)
4185 : charinfo_node(c, s, pop, x), n(p), tf(t)
4189 composite_node::~composite_node()
4191 delete_node_list(n);
4194 node *composite_node::copy()
4196 return new composite_node(copy_node_list(n), ci, tf, state, div_nest_level);
4199 hunits composite_node::width()
4201 hunits x;
4202 if (tf->get_constant_space(&x))
4203 return x;
4204 x = H0;
4205 for (node *tem = n; tem; tem = tem->next)
4206 x += tem->width();
4207 hunits offset;
4208 if (tf->get_bold(&offset))
4209 x += offset;
4210 x += tf->get_track_kern();
4211 return x;
4214 node *composite_node::last_char_node()
4216 return this;
4219 vunits composite_node::vertical_width()
4221 vunits v = V0;
4222 for (node *tem = n; tem; tem = tem->next)
4223 v += tem->vertical_width();
4224 return v;
4227 units composite_node::size()
4229 return tf->get_size().to_units();
4232 hyphenation_type composite_node::get_hyphenation_type()
4234 return HYPHEN_MIDDLE;
4237 void composite_node::asciify(macro *m)
4239 unsigned char c = ci->get_asciify_code();
4240 if (c == 0)
4241 c = ci->get_ascii_code();
4242 if (c != 0) {
4243 m->append(c);
4244 delete this;
4246 else
4247 m->append(this);
4250 void composite_node::ascii_print(ascii_output_file *ascii)
4252 unsigned char c = ci->get_ascii_code();
4253 if (c != 0)
4254 ascii->outc(c);
4255 else
4256 ascii->outs(ci->nm.contents());
4260 hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail, int *count)
4262 (*count)++;
4263 return new hyphen_list(ci->get_hyphenation_code(), tail);
4266 node *composite_node::add_self(node *nn, hyphen_list **p)
4268 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
4269 next = nn;
4270 nn = this;
4271 if ((*p)->hyphen)
4272 nn = nn->add_discretionary_hyphen();
4273 hyphen_list *pp = *p;
4274 *p = (*p)->next;
4275 delete pp;
4276 return nn;
4279 tfont *composite_node::get_tfont()
4281 return tf;
4284 node *reverse_node_list(node *n)
4286 node *r = 0;
4287 while (n) {
4288 node *tem = n;
4289 n = n->next;
4290 tem->next = r;
4291 r = tem;
4293 return r;
4296 void composite_node::vertical_extent(vunits *minimum, vunits *maximum)
4298 n = reverse_node_list(n);
4299 node_list_vertical_extent(n, minimum, maximum);
4300 n = reverse_node_list(n);
4303 width_list::width_list(hunits w, hunits s)
4304 : width(w), sentence_width(s), next(0)
4308 width_list::width_list(width_list *w)
4309 : width(w->width), sentence_width(w->sentence_width), next(0)
4313 word_space_node::word_space_node(hunits d, color *c, width_list *w, node *x)
4314 : space_node(d, c, x), orig_width(w), unformat(0)
4318 word_space_node::word_space_node(hunits d, int s, color *c, width_list *w,
4319 int flag, statem *st, int pop, node *x)
4320 : space_node(d, s, 0, c, st, pop, x), orig_width(w), unformat(flag)
4324 word_space_node::~word_space_node()
4326 width_list *w = orig_width;
4327 while (w != 0) {
4328 width_list *tmp = w;
4329 w = w->next;
4330 delete tmp;
4334 node *word_space_node::copy()
4336 assert(orig_width != 0);
4337 width_list *w_old_curr = orig_width;
4338 width_list *w_new_curr = new width_list(w_old_curr);
4339 width_list *w_new = w_new_curr;
4340 w_old_curr = w_old_curr->next;
4341 while (w_old_curr != 0) {
4342 w_new_curr->next = new width_list(w_old_curr);
4343 w_new_curr = w_new_curr->next;
4344 w_old_curr = w_old_curr->next;
4346 return new word_space_node(n, set, col, w_new, unformat, state,
4347 div_nest_level);
4350 int word_space_node::set_unformat_flag()
4352 unformat = 1;
4353 return 1;
4356 void word_space_node::tprint(troff_output_file *out)
4358 out->fill_color(col);
4359 out->word_marker();
4360 out->right(n);
4363 int word_space_node::merge_space(hunits h, hunits sw, hunits ssw)
4365 n += h;
4366 assert(orig_width != 0);
4367 width_list *w = orig_width;
4368 for (; w->next; w = w->next)
4370 w->next = new width_list(sw, ssw);
4371 return 1;
4374 unbreakable_space_node::unbreakable_space_node(hunits d, color *c, node *x)
4375 : word_space_node(d, c, 0, x)
4379 unbreakable_space_node::unbreakable_space_node(hunits d, int s,
4380 color *c, statem *st, int pop,
4381 node *x)
4382 : word_space_node(d, s, c, 0, 0, st, pop, x)
4386 node *unbreakable_space_node::copy()
4388 return new unbreakable_space_node(n, set, col, state, div_nest_level);
4391 int unbreakable_space_node::force_tprint()
4393 return 0;
4396 int unbreakable_space_node::is_tag()
4398 return 0;
4401 breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
4402 breakpoint *rest, int)
4404 return rest;
4407 int unbreakable_space_node::nbreaks()
4409 return 0;
4412 void unbreakable_space_node::split(int, node **, node **)
4414 assert(0);
4417 int unbreakable_space_node::merge_space(hunits, hunits, hunits)
4419 return 0;
4422 hvpair::hvpair()
4426 draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4427 color *gc, color *fc)
4428 : npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4430 point = new hvpair[npoints];
4431 for (int i = 0; i < npoints; i++)
4432 point[i] = p[i];
4435 draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4436 color *gc, color *fc, statem *st, int pop)
4437 : node(0, st, pop), npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4439 point = new hvpair[npoints];
4440 for (int i = 0; i < npoints; i++)
4441 point[i] = p[i];
4444 int draw_node::same(node *n)
4446 draw_node *nd = (draw_node *)n;
4447 if (code != nd->code || npoints != nd->npoints || sz != nd->sz
4448 || gcol != nd->gcol || fcol != nd->fcol)
4449 return 0;
4450 for (int i = 0; i < npoints; i++)
4451 if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
4452 return 0;
4453 return 1;
4456 const char *draw_node::type()
4458 return "draw_node";
4461 int draw_node::force_tprint()
4463 return 0;
4466 int draw_node::is_tag()
4468 return 0;
4471 draw_node::~draw_node()
4473 if (point)
4474 a_delete point;
4477 hunits draw_node::width()
4479 hunits x = H0;
4480 for (int i = 0; i < npoints; i++)
4481 x += point[i].h;
4482 return x;
4485 vunits draw_node::vertical_width()
4487 if (code == 'e')
4488 return V0;
4489 vunits x = V0;
4490 for (int i = 0; i < npoints; i++)
4491 x += point[i].v;
4492 return x;
4495 node *draw_node::copy()
4497 return new draw_node(code, point, npoints, sz, gcol, fcol, state,
4498 div_nest_level);
4501 void draw_node::tprint(troff_output_file *out)
4503 out->draw(code, point, npoints, sz, gcol, fcol);
4506 /* tprint methods */
4508 void glyph_node::tprint(troff_output_file *out)
4510 tfont *ptf = tf->get_plain();
4511 if (ptf == tf)
4512 out->put_char_width(ci, ptf, gcol, fcol, width(), H0);
4513 else {
4514 hunits offset;
4515 int bold = tf->get_bold(&offset);
4516 hunits w = ptf->get_width(ci);
4517 hunits k = H0;
4518 hunits x;
4519 int cs = tf->get_constant_space(&x);
4520 if (cs) {
4521 x -= w;
4522 if (bold)
4523 x -= offset;
4524 hunits x2 = x/2;
4525 out->right(x2);
4526 k = x - x2;
4528 else
4529 k = tf->get_track_kern();
4530 if (bold) {
4531 out->put_char(ci, ptf, gcol, fcol);
4532 out->right(offset);
4534 out->put_char_width(ci, ptf, gcol, fcol, w, k);
4538 void glyph_node::zero_width_tprint(troff_output_file *out)
4540 tfont *ptf = tf->get_plain();
4541 hunits offset;
4542 int bold = tf->get_bold(&offset);
4543 hunits x;
4544 int cs = tf->get_constant_space(&x);
4545 if (cs) {
4546 x -= ptf->get_width(ci);
4547 if (bold)
4548 x -= offset;
4549 x = x/2;
4550 out->right(x);
4552 out->put_char(ci, ptf, gcol, fcol);
4553 if (bold) {
4554 out->right(offset);
4555 out->put_char(ci, ptf, gcol, fcol);
4556 out->right(-offset);
4558 if (cs)
4559 out->right(-x);
4562 void break_char_node::tprint(troff_output_file *t)
4564 ch->tprint(t);
4567 void break_char_node::zero_width_tprint(troff_output_file *t)
4569 ch->zero_width_tprint(t);
4572 void hline_node::tprint(troff_output_file *out)
4574 if (x < H0) {
4575 out->right(x);
4576 x = -x;
4578 if (n == 0) {
4579 out->right(x);
4580 return;
4582 hunits w = n->width();
4583 if (w <= H0) {
4584 error("horizontal line drawing character must have positive width");
4585 out->right(x);
4586 return;
4588 int i = int(x/w);
4589 if (i == 0) {
4590 hunits xx = x - w;
4591 hunits xx2 = xx/2;
4592 out->right(xx2);
4593 if (out->is_on())
4594 n->tprint(out);
4595 out->right(xx - xx2);
4597 else {
4598 hunits rem = x - w*i;
4599 if (rem > H0)
4600 if (n->overlaps_horizontally()) {
4601 if (out->is_on())
4602 n->tprint(out);
4603 out->right(rem - w);
4605 else
4606 out->right(rem);
4607 while (--i >= 0)
4608 if (out->is_on())
4609 n->tprint(out);
4613 void vline_node::tprint(troff_output_file *out)
4615 if (n == 0) {
4616 out->down(x);
4617 return;
4619 vunits h = n->size();
4620 int overlaps = n->overlaps_vertically();
4621 vunits y = x;
4622 if (y < V0) {
4623 y = -y;
4624 int i = y / h;
4625 vunits rem = y - i*h;
4626 if (i == 0) {
4627 out->right(n->width());
4628 out->down(-rem);
4630 else {
4631 while (--i > 0) {
4632 n->zero_width_tprint(out);
4633 out->down(-h);
4635 if (overlaps) {
4636 n->zero_width_tprint(out);
4637 out->down(-rem);
4638 if (out->is_on())
4639 n->tprint(out);
4640 out->down(-h);
4642 else {
4643 if (out->is_on())
4644 n->tprint(out);
4645 out->down(-h - rem);
4649 else {
4650 int i = y / h;
4651 vunits rem = y - i*h;
4652 if (i == 0) {
4653 out->down(rem);
4654 out->right(n->width());
4656 else {
4657 out->down(h);
4658 if (overlaps)
4659 n->zero_width_tprint(out);
4660 out->down(rem);
4661 while (--i > 0) {
4662 n->zero_width_tprint(out);
4663 out->down(h);
4665 if (out->is_on())
4666 n->tprint(out);
4671 void zero_width_node::tprint(troff_output_file *out)
4673 if (!n)
4674 return;
4675 if (!n->next) {
4676 n->zero_width_tprint(out);
4677 return;
4679 int hpos = out->get_hpos();
4680 int vpos = out->get_vpos();
4681 node *tem = n;
4682 while (tem) {
4683 tem->tprint(out);
4684 tem = tem->next;
4686 out->moveto(hpos, vpos);
4689 void overstrike_node::tprint(troff_output_file *out)
4691 hunits pos = H0;
4692 for (node *tem = list; tem; tem = tem->next) {
4693 hunits x = (max_width - tem->width())/2;
4694 out->right(x - pos);
4695 pos = x;
4696 tem->zero_width_tprint(out);
4698 out->right(max_width - pos);
4701 void bracket_node::tprint(troff_output_file *out)
4703 if (list == 0)
4704 return;
4705 int npieces = 0;
4706 node *tem;
4707 for (tem = list; tem; tem = tem->next)
4708 ++npieces;
4709 vunits h = list->size();
4710 vunits totalh = h*npieces;
4711 vunits y = (totalh - h)/2;
4712 out->down(y);
4713 for (tem = list; tem; tem = tem->next) {
4714 tem->zero_width_tprint(out);
4715 out->down(-h);
4717 out->right(max_width);
4718 out->down(totalh - y);
4721 void node::tprint(troff_output_file *)
4725 void node::zero_width_tprint(troff_output_file *out)
4727 int hpos = out->get_hpos();
4728 int vpos = out->get_vpos();
4729 tprint(out);
4730 out->moveto(hpos, vpos);
4733 void space_node::tprint(troff_output_file *out)
4735 out->fill_color(col);
4736 out->right(n);
4739 void hmotion_node::tprint(troff_output_file *out)
4741 out->fill_color(col);
4742 out->right(n);
4745 void space_char_hmotion_node::tprint(troff_output_file *out)
4747 out->fill_color(col);
4748 if (is_html) {
4749 // we emit the space width as a negative glyph index
4750 out->flush_tbuf();
4751 out->do_motion();
4752 out->put('N');
4753 out->put(-n.to_units());
4754 out->put('\n');
4756 out->right(n);
4759 void vmotion_node::tprint(troff_output_file *out)
4761 out->fill_color(col);
4762 out->down(n);
4765 void kern_pair_node::tprint(troff_output_file *out)
4767 n1->tprint(out);
4768 out->right(amount);
4769 n2->tprint(out);
4772 static void tprint_reverse_node_list(troff_output_file *out, node *n)
4774 if (n == 0)
4775 return;
4776 tprint_reverse_node_list(out, n->next);
4777 n->tprint(out);
4780 void dbreak_node::tprint(troff_output_file *out)
4782 tprint_reverse_node_list(out, none);
4785 void composite_node::tprint(troff_output_file *out)
4787 hunits bold_offset;
4788 int is_bold = tf->get_bold(&bold_offset);
4789 hunits track_kern = tf->get_track_kern();
4790 hunits constant_space;
4791 int is_constant_spaced = tf->get_constant_space(&constant_space);
4792 hunits x = H0;
4793 if (is_constant_spaced) {
4794 x = constant_space;
4795 for (node *tem = n; tem; tem = tem->next)
4796 x -= tem->width();
4797 if (is_bold)
4798 x -= bold_offset;
4799 hunits x2 = x/2;
4800 out->right(x2);
4801 x -= x2;
4803 if (is_bold) {
4804 int hpos = out->get_hpos();
4805 int vpos = out->get_vpos();
4806 tprint_reverse_node_list(out, n);
4807 out->moveto(hpos, vpos);
4808 out->right(bold_offset);
4810 tprint_reverse_node_list(out, n);
4811 if (is_constant_spaced)
4812 out->right(x);
4813 else
4814 out->right(track_kern);
4817 node *make_composite_node(charinfo *s, environment *env)
4819 int fontno = env_definite_font(env);
4820 if (fontno < 0) {
4821 error("no current font");
4822 return 0;
4824 assert(fontno < font_table_size && font_table[fontno] != 0);
4825 node *n = charinfo_to_node_list(s, env);
4826 font_size fs = env->get_font_size();
4827 int char_height = env->get_char_height();
4828 int char_slant = env->get_char_slant();
4829 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
4830 fontno);
4831 if (env->is_composite())
4832 tf = tf->get_plain();
4833 return new composite_node(n, s, tf, 0, 0, 0);
4836 node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0)
4838 int fontno = env_definite_font(env);
4839 if (fontno < 0) {
4840 error("no current font");
4841 return 0;
4843 assert(fontno < font_table_size && font_table[fontno] != 0);
4844 int fn = fontno;
4845 int found = font_table[fontno]->contains(s);
4846 if (!found) {
4847 macro *mac = s->get_macro();
4848 if (mac && s->is_fallback())
4849 return make_composite_node(s, env);
4850 if (s->numbered()) {
4851 if (!no_error_message)
4852 warning(WARN_CHAR, "can't find numbered character %1",
4853 s->get_number());
4854 return 0;
4856 special_font_list *sf = font_table[fontno]->sf;
4857 while (sf != 0 && !found) {
4858 fn = sf->n;
4859 if (font_table[fn])
4860 found = font_table[fn]->contains(s);
4861 sf = sf->next;
4863 if (!found) {
4864 symbol f = font_table[fontno]->get_name();
4865 string gl(f.contents());
4866 gl += ' ';
4867 gl += s->nm.contents();
4868 gl += '\0';
4869 charinfo *ci = get_charinfo(symbol(gl.contents()));
4870 if (ci && ci->get_macro())
4871 return make_composite_node(ci, env);
4873 if (!found) {
4874 sf = global_special_fonts;
4875 while (sf != 0 && !found) {
4876 fn = sf->n;
4877 if (font_table[fn])
4878 found = font_table[fn]->contains(s);
4879 sf = sf->next;
4882 if (!found)
4883 if (mac && s->is_special())
4884 return make_composite_node(s, env);
4885 if (!found) {
4886 for (fn = 0; fn < font_table_size; fn++)
4887 if (font_table[fn]
4888 && font_table[fn]->is_special()
4889 && font_table[fn]->contains(s)) {
4890 found = 1;
4891 break;
4894 if (!found) {
4895 if (!no_error_message && s->first_time_not_found()) {
4896 unsigned char input_code = s->get_ascii_code();
4897 if (input_code != 0) {
4898 if (csgraph(input_code))
4899 warning(WARN_CHAR, "can't find character `%1'", input_code);
4900 else
4901 warning(WARN_CHAR, "can't find character with input code %1",
4902 int(input_code));
4904 else if (s->nm.contents())
4905 warning(WARN_CHAR, "can't find special character `%1'",
4906 s->nm.contents());
4908 return 0;
4911 font_size fs = env->get_font_size();
4912 int char_height = env->get_char_height();
4913 int char_slant = env->get_char_slant();
4914 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn);
4915 if (env->is_composite())
4916 tf = tf->get_plain();
4917 color *gcol = env->get_glyph_color();
4918 color *fcol = env->get_fill_color();
4919 return new glyph_node(s, tf, gcol, fcol, 0, 0);
4922 node *make_node(charinfo *ci, environment *env)
4924 switch (ci->get_special_translation()) {
4925 case charinfo::TRANSLATE_SPACE:
4926 return new space_char_hmotion_node(env->get_space_width(),
4927 env->get_fill_color());
4928 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4929 return new unbreakable_space_node(env->get_space_width(),
4930 env->get_fill_color());
4931 case charinfo::TRANSLATE_DUMMY:
4932 return new dummy_node;
4933 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4934 error("translation to \\% ignored in this context");
4935 break;
4937 charinfo *tem = ci->get_translation();
4938 if (tem)
4939 ci = tem;
4940 macro *mac = ci->get_macro();
4941 if (mac && ci->is_normal())
4942 return make_composite_node(ci, env);
4943 else
4944 return make_glyph_node(ci, env);
4947 int character_exists(charinfo *ci, environment *env)
4949 if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
4950 return 1;
4951 charinfo *tem = ci->get_translation();
4952 if (tem)
4953 ci = tem;
4954 if (ci->get_macro())
4955 return 1;
4956 node *nd = make_glyph_node(ci, env, 1);
4957 if (nd) {
4958 delete nd;
4959 return 1;
4961 return 0;
4964 node *node::add_char(charinfo *ci, environment *env,
4965 hunits *widthp, int *spacep, node **glyph_comp_np)
4967 node *res;
4968 switch (ci->get_special_translation()) {
4969 case charinfo::TRANSLATE_SPACE:
4970 res = new space_char_hmotion_node(env->get_space_width(),
4971 env->get_fill_color(), this);
4972 *widthp += res->width();
4973 return res;
4974 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4975 res = new unbreakable_space_node(env->get_space_width(),
4976 env->get_fill_color(), this);
4977 res->freeze_space();
4978 *widthp += res->width();
4979 *spacep += res->nspaces();
4980 return res;
4981 case charinfo::TRANSLATE_DUMMY:
4982 return new dummy_node(this);
4983 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4984 return add_discretionary_hyphen();
4986 charinfo *tem = ci->get_translation();
4987 if (tem)
4988 ci = tem;
4989 macro *mac = ci->get_macro();
4990 if (mac && ci->is_normal()) {
4991 res = make_composite_node(ci, env);
4992 if (res) {
4993 res->next = this;
4994 *widthp += res->width();
4995 if (glyph_comp_np)
4996 *glyph_comp_np = res;
4998 else {
4999 if (glyph_comp_np)
5000 *glyph_comp_np = res;
5001 return this;
5004 else {
5005 node *gn = make_glyph_node(ci, env);
5006 if (gn == 0)
5007 return this;
5008 else {
5009 hunits old_width = width();
5010 node *p = gn->merge_self(this);
5011 if (p == 0) {
5012 *widthp += gn->width();
5013 gn->next = this;
5014 res = gn;
5016 else {
5017 *widthp += p->width() - old_width;
5018 res = p;
5020 if (glyph_comp_np)
5021 *glyph_comp_np = res;
5024 int break_code = 0;
5025 if (ci->can_break_before())
5026 break_code = 1;
5027 if (ci->can_break_after())
5028 break_code |= 2;
5029 if (break_code) {
5030 node *next1 = res->next;
5031 res->next = 0;
5032 res = new break_char_node(res, break_code, env->get_fill_color(), next1);
5034 return res;
5037 #ifdef __GNUG__
5038 inline
5039 #endif
5040 int same_node(node *n1, node *n2)
5042 if (n1 != 0) {
5043 if (n2 != 0)
5044 return n1->type() == n2->type() && n1->same(n2);
5045 else
5046 return 0;
5048 else
5049 return n2 == 0;
5052 int same_node_list(node *n1, node *n2)
5054 while (n1 && n2) {
5055 if (n1->type() != n2->type() || !n1->same(n2))
5056 return 0;
5057 n1 = n1->next;
5058 n2 = n2->next;
5060 return !n1 && !n2;
5063 int extra_size_node::same(node *nd)
5065 return n == ((extra_size_node *)nd)->n;
5068 const char *extra_size_node::type()
5070 return "extra_size_node";
5073 int extra_size_node::force_tprint()
5075 return 0;
5078 int extra_size_node::is_tag()
5080 return 0;
5083 int vertical_size_node::same(node *nd)
5085 return n == ((vertical_size_node *)nd)->n;
5088 const char *vertical_size_node::type()
5090 return "vertical_size_node";
5093 int vertical_size_node::set_unformat_flag()
5095 return 0;
5098 int vertical_size_node::force_tprint()
5100 return 0;
5103 int vertical_size_node::is_tag()
5105 return 0;
5108 int hmotion_node::same(node *nd)
5110 return n == ((hmotion_node *)nd)->n
5111 && col == ((hmotion_node *)nd)->col;
5114 const char *hmotion_node::type()
5116 return "hmotion_node";
5119 int hmotion_node::set_unformat_flag()
5121 unformat = 1;
5122 return 1;
5125 int hmotion_node::force_tprint()
5127 return 0;
5130 int hmotion_node::is_tag()
5132 return 0;
5135 node *hmotion_node::add_self(node *nd, hyphen_list **p)
5137 next = nd;
5138 hyphen_list *pp = *p;
5139 *p = (*p)->next;
5140 delete pp;
5141 return this;
5144 hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail, int *)
5146 return new hyphen_list(0, tail);
5149 int space_char_hmotion_node::same(node *nd)
5151 return n == ((space_char_hmotion_node *)nd)->n
5152 && col == ((space_char_hmotion_node *)nd)->col;
5155 const char *space_char_hmotion_node::type()
5157 return "space_char_hmotion_node";
5160 int space_char_hmotion_node::force_tprint()
5162 return 0;
5165 int space_char_hmotion_node::is_tag()
5167 return 0;
5170 node *space_char_hmotion_node::add_self(node *nd, hyphen_list **p)
5172 next = nd;
5173 hyphen_list *pp = *p;
5174 *p = (*p)->next;
5175 delete pp;
5176 return this;
5179 hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail,
5180 int *)
5182 return new hyphen_list(0, tail);
5185 int vmotion_node::same(node *nd)
5187 return n == ((vmotion_node *)nd)->n
5188 && col == ((vmotion_node *)nd)->col;
5191 const char *vmotion_node::type()
5193 return "vmotion_node";
5196 int vmotion_node::force_tprint()
5198 return 0;
5201 int vmotion_node::is_tag()
5203 return 0;
5206 int hline_node::same(node *nd)
5208 return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n);
5211 const char *hline_node::type()
5213 return "hline_node";
5216 int hline_node::force_tprint()
5218 return 0;
5221 int hline_node::is_tag()
5223 return 0;
5226 int vline_node::same(node *nd)
5228 return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n);
5231 const char *vline_node::type()
5233 return "vline_node";
5236 int vline_node::force_tprint()
5238 return 0;
5241 int vline_node::is_tag()
5243 return 0;
5246 int dummy_node::same(node * /*nd*/)
5248 return 1;
5251 const char *dummy_node::type()
5253 return "dummy_node";
5256 int dummy_node::force_tprint()
5258 return 0;
5261 int dummy_node::is_tag()
5263 return 0;
5266 int transparent_dummy_node::same(node * /*nd*/)
5268 return 1;
5271 const char *transparent_dummy_node::type()
5273 return "transparent_dummy_node";
5276 int transparent_dummy_node::force_tprint()
5278 return 0;
5281 int transparent_dummy_node::is_tag()
5283 return 0;
5286 int transparent_dummy_node::ends_sentence()
5288 return 2;
5291 int zero_width_node::same(node *nd)
5293 return same_node_list(n, ((zero_width_node *)nd)->n);
5296 const char *zero_width_node::type()
5298 return "zero_width_node";
5301 int zero_width_node::force_tprint()
5303 return 0;
5306 int zero_width_node::is_tag()
5308 return 0;
5311 int italic_corrected_node::same(node *nd)
5313 return (x == ((italic_corrected_node *)nd)->x
5314 && same_node(n, ((italic_corrected_node *)nd)->n));
5317 const char *italic_corrected_node::type()
5319 return "italic_corrected_node";
5322 int italic_corrected_node::force_tprint()
5324 return 0;
5327 int italic_corrected_node::is_tag()
5329 return 0;
5332 left_italic_corrected_node::left_italic_corrected_node(node *xx)
5333 : node(xx), n(0)
5337 left_italic_corrected_node::left_italic_corrected_node(statem *s, int pop,
5338 node *xx)
5339 : node(xx, s, pop), n(0)
5343 left_italic_corrected_node::~left_italic_corrected_node()
5345 delete n;
5348 node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
5350 if (n == 0) {
5351 hunits lic = gn->left_italic_correction();
5352 if (!lic.is_zero()) {
5353 x = lic;
5354 n = gn;
5355 return this;
5358 else {
5359 node *nd = n->merge_glyph_node(gn);
5360 if (nd) {
5361 n = nd;
5362 x = n->left_italic_correction();
5363 return this;
5366 return 0;
5369 node *left_italic_corrected_node::copy()
5371 left_italic_corrected_node *nd =
5372 new left_italic_corrected_node(state, div_nest_level);
5373 if (n) {
5374 nd->n = n->copy();
5375 nd->x = x;
5377 return nd;
5380 void left_italic_corrected_node::tprint(troff_output_file *out)
5382 if (n) {
5383 out->right(x);
5384 n->tprint(out);
5388 const char *left_italic_corrected_node::type()
5390 return "left_italic_corrected_node";
5393 int left_italic_corrected_node::force_tprint()
5395 return 0;
5398 int left_italic_corrected_node::is_tag()
5400 return 0;
5403 int left_italic_corrected_node::same(node *nd)
5405 return (x == ((left_italic_corrected_node *)nd)->x
5406 && same_node(n, ((left_italic_corrected_node *)nd)->n));
5409 void left_italic_corrected_node::ascii_print(ascii_output_file *out)
5411 if (n)
5412 n->ascii_print(out);
5415 hunits left_italic_corrected_node::width()
5417 return n ? n->width() + x : H0;
5420 void left_italic_corrected_node::vertical_extent(vunits *minimum,
5421 vunits *maximum)
5423 if (n)
5424 n->vertical_extent(minimum, maximum);
5425 else
5426 node::vertical_extent(minimum, maximum);
5429 hunits left_italic_corrected_node::skew()
5431 return n ? n->skew() + x/2 : H0;
5434 hunits left_italic_corrected_node::subscript_correction()
5436 return n ? n->subscript_correction() : H0;
5439 hunits left_italic_corrected_node::italic_correction()
5441 return n ? n->italic_correction() : H0;
5444 int left_italic_corrected_node::ends_sentence()
5446 return n ? n->ends_sentence() : 0;
5449 int left_italic_corrected_node::overlaps_horizontally()
5451 return n ? n->overlaps_horizontally() : 0;
5454 int left_italic_corrected_node::overlaps_vertically()
5456 return n ? n->overlaps_vertically() : 0;
5459 node *left_italic_corrected_node::last_char_node()
5461 return n ? n->last_char_node() : 0;
5464 tfont *left_italic_corrected_node::get_tfont()
5466 return n ? n->get_tfont() : 0;
5469 hyphenation_type left_italic_corrected_node::get_hyphenation_type()
5471 if (n)
5472 return n->get_hyphenation_type();
5473 else
5474 return HYPHEN_MIDDLE;
5477 hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail,
5478 int *count)
5480 return n ? n->get_hyphen_list(tail, count) : tail;
5483 node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
5485 if (n) {
5486 nd = new left_italic_corrected_node(state, div_nest_level, nd);
5487 nd = n->add_self(nd, p);
5488 n = 0;
5489 delete this;
5491 return nd;
5494 int left_italic_corrected_node::character_type()
5496 return n ? n->character_type() : 0;
5499 int overstrike_node::same(node *nd)
5501 return same_node_list(list, ((overstrike_node *)nd)->list);
5504 const char *overstrike_node::type()
5506 return "overstrike_node";
5509 int overstrike_node::force_tprint()
5511 return 0;
5514 int overstrike_node::is_tag()
5516 return 0;
5519 node *overstrike_node::add_self(node *n, hyphen_list **p)
5521 next = n;
5522 hyphen_list *pp = *p;
5523 *p = (*p)->next;
5524 delete pp;
5525 return this;
5528 hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail, int *)
5530 return new hyphen_list(0, tail);
5533 int bracket_node::same(node *nd)
5535 return same_node_list(list, ((bracket_node *)nd)->list);
5538 const char *bracket_node::type()
5540 return "bracket_node";
5543 int bracket_node::force_tprint()
5545 return 0;
5548 int bracket_node::is_tag()
5550 return 0;
5553 int composite_node::same(node *nd)
5555 return ci == ((composite_node *)nd)->ci
5556 && same_node_list(n, ((composite_node *)nd)->n);
5559 const char *composite_node::type()
5561 return "composite_node";
5564 int composite_node::force_tprint()
5566 return 0;
5569 int composite_node::is_tag()
5571 return 0;
5574 int glyph_node::same(node *nd)
5576 return ci == ((glyph_node *)nd)->ci
5577 && tf == ((glyph_node *)nd)->tf
5578 && gcol == ((glyph_node *)nd)->gcol
5579 && fcol == ((glyph_node *)nd)->fcol;
5582 const char *glyph_node::type()
5584 return "glyph_node";
5587 int glyph_node::force_tprint()
5589 return 0;
5592 int glyph_node::is_tag()
5594 return 0;
5597 int ligature_node::same(node *nd)
5599 return (same_node(n1, ((ligature_node *)nd)->n1)
5600 && same_node(n2, ((ligature_node *)nd)->n2)
5601 && glyph_node::same(nd));
5604 const char *ligature_node::type()
5606 return "ligature_node";
5609 int ligature_node::force_tprint()
5611 return 0;
5614 int ligature_node::is_tag()
5616 return 0;
5619 int kern_pair_node::same(node *nd)
5621 return (amount == ((kern_pair_node *)nd)->amount
5622 && same_node(n1, ((kern_pair_node *)nd)->n1)
5623 && same_node(n2, ((kern_pair_node *)nd)->n2));
5626 const char *kern_pair_node::type()
5628 return "kern_pair_node";
5631 int kern_pair_node::force_tprint()
5633 return 0;
5636 int kern_pair_node::is_tag()
5638 return 0;
5641 int dbreak_node::same(node *nd)
5643 return (same_node_list(none, ((dbreak_node *)nd)->none)
5644 && same_node_list(pre, ((dbreak_node *)nd)->pre)
5645 && same_node_list(post, ((dbreak_node *)nd)->post));
5648 const char *dbreak_node::type()
5650 return "dbreak_node";
5653 int dbreak_node::force_tprint()
5655 return 0;
5658 int dbreak_node::is_tag()
5660 return 0;
5663 int break_char_node::same(node *nd)
5665 return break_code == ((break_char_node *)nd)->break_code
5666 && col == ((break_char_node *)nd)->col
5667 && same_node(ch, ((break_char_node *)nd)->ch);
5670 const char *break_char_node::type()
5672 return "break_char_node";
5675 int break_char_node::force_tprint()
5677 return 0;
5680 int break_char_node::is_tag()
5682 return 0;
5685 int line_start_node::same(node * /*nd*/)
5687 return 1;
5690 const char *line_start_node::type()
5692 return "line_start_node";
5695 int line_start_node::force_tprint()
5697 return 0;
5700 int line_start_node::is_tag()
5702 return 0;
5705 int space_node::same(node *nd)
5707 return n == ((space_node *)nd)->n
5708 && set == ((space_node *)nd)->set
5709 && col == ((space_node *)nd)->col;
5712 const char *space_node::type()
5714 return "space_node";
5717 int word_space_node::same(node *nd)
5719 return n == ((word_space_node *)nd)->n
5720 && set == ((word_space_node *)nd)->set
5721 && col == ((word_space_node *)nd)->col;
5724 const char *word_space_node::type()
5726 return "word_space_node";
5729 int word_space_node::force_tprint()
5731 return 0;
5734 int word_space_node::is_tag()
5736 return 0;
5739 void unbreakable_space_node::tprint(troff_output_file *out)
5741 out->fill_color(col);
5742 if (is_html) {
5743 // we emit the space width as a negative glyph index
5744 out->flush_tbuf();
5745 out->do_motion();
5746 out->put('N');
5747 out->put(-n.to_units());
5748 out->put('\n');
5750 out->right(n);
5753 int unbreakable_space_node::same(node *nd)
5755 return n == ((unbreakable_space_node *)nd)->n
5756 && set == ((unbreakable_space_node *)nd)->set
5757 && col == ((unbreakable_space_node *)nd)->col;
5760 const char *unbreakable_space_node::type()
5762 return "unbreakable_space_node";
5765 node *unbreakable_space_node::add_self(node *nd, hyphen_list **p)
5767 next = nd;
5768 hyphen_list *pp = *p;
5769 *p = (*p)->next;
5770 delete pp;
5771 return this;
5774 hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail, int *)
5776 return new hyphen_list(0, tail);
5779 int diverted_space_node::same(node *nd)
5781 return n == ((diverted_space_node *)nd)->n;
5784 const char *diverted_space_node::type()
5786 return "diverted_space_node";
5789 int diverted_space_node::force_tprint()
5791 return 0;
5794 int diverted_space_node::is_tag()
5796 return 0;
5799 int diverted_copy_file_node::same(node *nd)
5801 return filename == ((diverted_copy_file_node *)nd)->filename;
5804 const char *diverted_copy_file_node::type()
5806 return "diverted_copy_file_node";
5809 int diverted_copy_file_node::force_tprint()
5811 return 0;
5814 int diverted_copy_file_node::is_tag()
5816 return 0;
5819 // Grow the font_table so that its size is > n.
5821 static void grow_font_table(int n)
5823 assert(n >= font_table_size);
5824 font_info **old_font_table = font_table;
5825 int old_font_table_size = font_table_size;
5826 font_table_size = font_table_size ? (font_table_size*3)/2 : 10;
5827 if (font_table_size <= n)
5828 font_table_size = n + 10;
5829 font_table = new font_info *[font_table_size];
5830 if (old_font_table_size)
5831 memcpy(font_table, old_font_table,
5832 old_font_table_size*sizeof(font_info *));
5833 a_delete old_font_table;
5834 for (int i = old_font_table_size; i < font_table_size; i++)
5835 font_table[i] = 0;
5838 dictionary font_translation_dictionary(17);
5840 static symbol get_font_translation(symbol nm)
5842 void *p = font_translation_dictionary.lookup(nm);
5843 return p ? symbol((char *)p) : nm;
5846 dictionary font_dictionary(50);
5848 static int mount_font_no_translate(int n, symbol name, symbol external_name,
5849 int check_only = 0)
5851 assert(n >= 0);
5852 // We store the address of this char in font_dictionary to indicate
5853 // that we've previously tried to mount the font and failed.
5854 static char a_char;
5855 font *fm = 0;
5856 void *p = font_dictionary.lookup(external_name);
5857 if (p == 0) {
5858 int not_found;
5859 fm = font::load_font(external_name.contents(), &not_found, check_only);
5860 if (check_only)
5861 return fm != 0;
5862 if (!fm) {
5863 if (not_found)
5864 warning(WARN_FONT, "can't find font `%1'", external_name.contents());
5865 (void)font_dictionary.lookup(external_name, &a_char);
5866 return 0;
5868 (void)font_dictionary.lookup(name, fm);
5870 else if (p == &a_char) {
5871 #if 0
5872 error("invalid font `%1'", external_name.contents());
5873 #endif
5874 return 0;
5876 else
5877 fm = (font*)p;
5878 if (check_only)
5879 return 1;
5880 if (n >= font_table_size) {
5881 if (n - font_table_size > 1000) {
5882 error("font position too much larger than first unused position");
5883 return 0;
5885 grow_font_table(n);
5887 else if (font_table[n] != 0)
5888 delete font_table[n];
5889 font_table[n] = new font_info(name, n, external_name, fm);
5890 font_family::invalidate_fontno(n);
5891 return 1;
5894 int mount_font(int n, symbol name, symbol external_name)
5896 assert(n >= 0);
5897 name = get_font_translation(name);
5898 if (external_name.is_null())
5899 external_name = name;
5900 else
5901 external_name = get_font_translation(external_name);
5902 return mount_font_no_translate(n, name, external_name);
5905 int check_font(symbol fam, symbol name)
5907 if (check_style(name))
5908 name = concat(fam, name);
5909 return mount_font_no_translate(0, name, name, 1);
5912 int check_style(symbol s)
5914 int i = symbol_fontno(s);
5915 return i < 0 ? 0 : font_table[i]->is_style();
5918 void mount_style(int n, symbol name)
5920 assert(n >= 0);
5921 if (n >= font_table_size) {
5922 if (n - font_table_size > 1000) {
5923 error("font position too much larger than first unused position");
5924 return;
5926 grow_font_table(n);
5928 else if (font_table[n] != 0)
5929 delete font_table[n];
5930 font_table[n] = new font_info(get_font_translation(name), n, NULL_SYMBOL, 0);
5931 font_family::invalidate_fontno(n);
5934 /* global functions */
5936 void font_translate()
5938 symbol from = get_name(1);
5939 if (!from.is_null()) {
5940 symbol to = get_name();
5941 if (to.is_null() || from == to)
5942 font_translation_dictionary.remove(from);
5943 else
5944 (void)font_translation_dictionary.lookup(from, (void *)to.contents());
5946 skip_line();
5949 void font_position()
5951 int n;
5952 if (get_integer(&n)) {
5953 if (n < 0)
5954 error("negative font position");
5955 else {
5956 symbol internal_name = get_name(1);
5957 if (!internal_name.is_null()) {
5958 symbol external_name = get_long_name();
5959 mount_font(n, internal_name, external_name); // ignore error
5963 skip_line();
5966 font_family::font_family(symbol s)
5967 : map_size(10), nm(s)
5969 map = new int[map_size];
5970 for (int i = 0; i < map_size; i++)
5971 map[i] = -1;
5974 font_family::~font_family()
5976 a_delete map;
5979 int font_family::make_definite(int i)
5981 if (i >= 0) {
5982 if (i < map_size && map[i] >= 0)
5983 return map[i];
5984 else {
5985 if (i < font_table_size && font_table[i] != 0) {
5986 if (i >= map_size) {
5987 int old_map_size = map_size;
5988 int *old_map = map;
5989 map_size *= 3;
5990 map_size /= 2;
5991 if (i >= map_size)
5992 map_size = i + 10;
5993 map = new int[map_size];
5994 memcpy(map, old_map, old_map_size*sizeof(int));
5995 a_delete old_map;
5996 for (int j = old_map_size; j < map_size; j++)
5997 map[j] = -1;
5999 if (font_table[i]->is_style()) {
6000 symbol sty = font_table[i]->get_name();
6001 symbol f = concat(nm, sty);
6002 int n;
6003 // don't use symbol_fontno, because that might return a style
6004 // and because we don't want to translate the name
6005 for (n = 0; n < font_table_size; n++)
6006 if (font_table[n] != 0 && font_table[n]->is_named(f)
6007 && !font_table[n]->is_style())
6008 break;
6009 if (n >= font_table_size) {
6010 n = next_available_font_position();
6011 if (!mount_font_no_translate(n, f, f))
6012 return -1;
6014 return map[i] = n;
6016 else
6017 return map[i] = i;
6019 else
6020 return -1;
6023 else
6024 return -1;
6027 dictionary family_dictionary(5);
6029 font_family *lookup_family(symbol nm)
6031 font_family *f = (font_family *)family_dictionary.lookup(nm);
6032 if (!f) {
6033 f = new font_family(nm);
6034 (void)family_dictionary.lookup(nm, f);
6036 return f;
6039 void font_family::invalidate_fontno(int n)
6041 assert(n >= 0 && n < font_table_size);
6042 dictionary_iterator iter(family_dictionary);
6043 symbol nam;
6044 font_family *fam;
6045 while (iter.get(&nam, (void **)&fam)) {
6046 int mapsize = fam->map_size;
6047 if (n < mapsize)
6048 fam->map[n] = -1;
6049 for (int i = 0; i < mapsize; i++)
6050 if (fam->map[i] == n)
6051 fam->map[i] = -1;
6055 void style()
6057 int n;
6058 if (get_integer(&n)) {
6059 if (n < 0)
6060 error("negative font position");
6061 else {
6062 symbol internal_name = get_name(1);
6063 if (!internal_name.is_null())
6064 mount_style(n, internal_name);
6067 skip_line();
6070 static int get_fontno()
6072 int n;
6073 tok.skip();
6074 if (tok.delimiter()) {
6075 symbol s = get_name(1);
6076 if (!s.is_null()) {
6077 n = symbol_fontno(s);
6078 if (n < 0) {
6079 n = next_available_font_position();
6080 if (!mount_font(n, s))
6081 return -1;
6083 return curenv->get_family()->make_definite(n);
6086 else if (get_integer(&n)) {
6087 if (n < 0 || n >= font_table_size || font_table[n] == 0)
6088 error("bad font number");
6089 else
6090 return curenv->get_family()->make_definite(n);
6092 return -1;
6095 static int underline_fontno = 2;
6097 void underline_font()
6099 int n = get_fontno();
6100 if (n >= 0)
6101 underline_fontno = n;
6102 skip_line();
6105 int get_underline_fontno()
6107 return underline_fontno;
6110 void define_font_special_character()
6112 int n = get_fontno();
6113 if (n < 0) {
6114 skip_line();
6115 return;
6117 symbol f = font_table[n]->get_name();
6118 do_define_character(CHAR_FONT_SPECIAL, f.contents());
6121 void remove_font_special_character()
6123 int n = get_fontno();
6124 if (n < 0) {
6125 skip_line();
6126 return;
6128 symbol f = font_table[n]->get_name();
6129 while (!tok.newline() && !tok.eof()) {
6130 if (!tok.space() && !tok.tab()) {
6131 charinfo *s = tok.get_char(1);
6132 string gl(f.contents());
6133 gl += ' ';
6134 gl += s->nm.contents();
6135 gl += '\0';
6136 charinfo *ci = get_charinfo(symbol(gl.contents()));
6137 if (!ci)
6138 break;
6139 macro *m = ci->set_macro(0);
6140 if (m)
6141 delete m;
6143 tok.next();
6145 skip_line();
6148 static void read_special_fonts(special_font_list **sp)
6150 special_font_list *s = *sp;
6151 *sp = 0;
6152 while (s != 0) {
6153 special_font_list *tem = s;
6154 s = s->next;
6155 delete tem;
6157 special_font_list **p = sp;
6158 while (has_arg()) {
6159 int i = get_fontno();
6160 if (i >= 0) {
6161 special_font_list *tem = new special_font_list;
6162 tem->n = i;
6163 tem->next = 0;
6164 *p = tem;
6165 p = &(tem->next);
6170 void font_special_request()
6172 int n = get_fontno();
6173 if (n >= 0)
6174 read_special_fonts(&font_table[n]->sf);
6175 skip_line();
6178 void special_request()
6180 read_special_fonts(&global_special_fonts);
6181 skip_line();
6184 int next_available_font_position()
6186 int i;
6187 for (i = 1; i < font_table_size && font_table[i] != 0; i++)
6189 return i;
6192 int symbol_fontno(symbol s)
6194 s = get_font_translation(s);
6195 for (int i = 0; i < font_table_size; i++)
6196 if (font_table[i] != 0 && font_table[i]->is_named(s))
6197 return i;
6198 return -1;
6201 int is_good_fontno(int n)
6203 return n >= 0 && n < font_table_size && font_table[n] != 0;
6206 int get_bold_fontno(int n)
6208 if (n >= 0 && n < font_table_size && font_table[n] != 0) {
6209 hunits offset;
6210 if (font_table[n]->get_bold(&offset))
6211 return offset.to_units() + 1;
6212 else
6213 return 0;
6215 else
6216 return 0;
6219 hunits env_digit_width(environment *env)
6221 node *n = make_glyph_node(charset_table['0'], env);
6222 if (n) {
6223 hunits x = n->width();
6224 delete n;
6225 return x;
6227 else
6228 return H0;
6231 hunits env_space_width(environment *env)
6233 int fn = env_definite_font(env);
6234 font_size fs = env->get_font_size();
6235 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6236 return scale(fs.to_units()/3, env->get_space_size(), 12);
6237 else
6238 return font_table[fn]->get_space_width(fs, env->get_space_size());
6241 hunits env_sentence_space_width(environment *env)
6243 int fn = env_definite_font(env);
6244 font_size fs = env->get_font_size();
6245 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6246 return scale(fs.to_units()/3, env->get_sentence_space_size(), 12);
6247 else
6248 return font_table[fn]->get_space_width(fs, env->get_sentence_space_size());
6251 hunits env_half_narrow_space_width(environment *env)
6253 int fn = env_definite_font(env);
6254 font_size fs = env->get_font_size();
6255 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6256 return 0;
6257 else
6258 return font_table[fn]->get_half_narrow_space_width(fs);
6261 hunits env_narrow_space_width(environment *env)
6263 int fn = env_definite_font(env);
6264 font_size fs = env->get_font_size();
6265 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6266 return 0;
6267 else
6268 return font_table[fn]->get_narrow_space_width(fs);
6271 void bold_font()
6273 int n = get_fontno();
6274 if (n >= 0) {
6275 if (has_arg()) {
6276 if (tok.delimiter()) {
6277 int f = get_fontno();
6278 if (f >= 0) {
6279 units offset;
6280 if (has_arg() && get_number(&offset, 'u') && offset >= 1)
6281 font_table[f]->set_conditional_bold(n, hunits(offset - 1));
6282 else
6283 font_table[f]->conditional_unbold(n);
6286 else {
6287 units offset;
6288 if (get_number(&offset, 'u') && offset >= 1)
6289 font_table[n]->set_bold(hunits(offset - 1));
6290 else
6291 font_table[n]->unbold();
6294 else
6295 font_table[n]->unbold();
6297 skip_line();
6300 track_kerning_function::track_kerning_function() : non_zero(0)
6304 track_kerning_function::track_kerning_function(int min_s, hunits min_a,
6305 int max_s, hunits max_a)
6306 : non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s),
6307 max_amount(max_a)
6311 int track_kerning_function::operator==(const track_kerning_function &tk)
6313 if (non_zero)
6314 return (tk.non_zero
6315 && min_size == tk.min_size
6316 && min_amount == tk.min_amount
6317 && max_size == tk.max_size
6318 && max_amount == tk.max_amount);
6319 else
6320 return !tk.non_zero;
6323 int track_kerning_function::operator!=(const track_kerning_function &tk)
6325 if (non_zero)
6326 return (!tk.non_zero
6327 || min_size != tk.min_size
6328 || min_amount != tk.min_amount
6329 || max_size != tk.max_size
6330 || max_amount != tk.max_amount);
6331 else
6332 return tk.non_zero;
6335 hunits track_kerning_function::compute(int size)
6337 if (non_zero) {
6338 if (max_size <= min_size)
6339 return min_amount;
6340 else if (size <= min_size)
6341 return min_amount;
6342 else if (size >= max_size)
6343 return max_amount;
6344 else
6345 return (scale(max_amount, size - min_size, max_size - min_size)
6346 + scale(min_amount, max_size - size, max_size - min_size));
6348 else
6349 return H0;
6352 void track_kern()
6354 int n = get_fontno();
6355 if (n >= 0) {
6356 int min_s, max_s;
6357 hunits min_a, max_a;
6358 if (has_arg()
6359 && get_number(&min_s, 'z')
6360 && get_hunits(&min_a, 'p')
6361 && get_number(&max_s, 'z')
6362 && get_hunits(&max_a, 'p')) {
6363 track_kerning_function tk(min_s, min_a, max_s, max_a);
6364 font_table[n]->set_track_kern(tk);
6366 else {
6367 track_kerning_function tk;
6368 font_table[n]->set_track_kern(tk);
6371 skip_line();
6374 void constant_space()
6376 int n = get_fontno();
6377 if (n >= 0) {
6378 int x, y;
6379 if (!has_arg() || !get_integer(&x))
6380 font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
6381 else {
6382 if (!has_arg() || !get_number(&y, 'z'))
6383 font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
6384 else
6385 font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
6386 scale(y*x,
6387 units_per_inch,
6388 36*72*sizescale));
6391 skip_line();
6394 void ligature()
6396 int lig;
6397 if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2)
6398 global_ligature_mode = lig;
6399 else
6400 global_ligature_mode = 1;
6401 skip_line();
6404 void kern_request()
6406 int k;
6407 if (has_arg() && get_integer(&k))
6408 global_kern_mode = k != 0;
6409 else
6410 global_kern_mode = 1;
6411 skip_line();
6414 void set_soft_hyphen_char()
6416 soft_hyphen_char = get_optional_char();
6417 if (!soft_hyphen_char)
6418 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
6419 skip_line();
6422 void init_output()
6424 if (suppress_output_flag)
6425 the_output = new suppress_output_file;
6426 else if (ascii_output_flag)
6427 the_output = new ascii_output_file;
6428 else
6429 the_output = new troff_output_file;
6432 class next_available_font_position_reg : public reg {
6433 public:
6434 const char *get_string();
6437 const char *next_available_font_position_reg::get_string()
6439 return i_to_a(next_available_font_position());
6442 class printing_reg : public reg {
6443 public:
6444 const char *get_string();
6447 const char *printing_reg::get_string()
6449 if (the_output)
6450 return the_output->is_printing() ? "1" : "0";
6451 else
6452 return "0";
6455 void init_node_requests()
6457 init_request("bd", bold_font);
6458 init_request("cs", constant_space);
6459 init_request("fp", font_position);
6460 init_request("fschar", define_font_special_character);
6461 init_request("fspecial", font_special_request);
6462 init_request("ftr", font_translate);
6463 init_request("kern", kern_request);
6464 init_request("lg", ligature);
6465 init_request("rfschar", remove_font_special_character);
6466 init_request("shc", set_soft_hyphen_char);
6467 init_request("special", special_request);
6468 init_request("sty", style);
6469 init_request("tkf", track_kern);
6470 init_request("uf", underline_font);
6471 number_reg_dictionary.define(".fp", new next_available_font_position_reg);
6472 number_reg_dictionary.define(".kern",
6473 new constant_int_reg(&global_kern_mode));
6474 number_reg_dictionary.define(".lg",
6475 new constant_int_reg(&global_ligature_mode));
6476 number_reg_dictionary.define(".P", new printing_reg);
6477 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);