Update to groff 1.19.2.
[dragonfly.git] / contrib / groff-1.19 / src / roff / troff / node.cpp
blob0d874d35758d6cde42cfd3b5bd38ca9d24e111cf
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 */
64 // declarations to avoid friend name injections
65 class tfont;
66 class tfont_spec;
67 tfont *make_tfont(tfont_spec &);
71 * how many boundaries of images have been written? Useful for
72 * debugging grohtml
75 int image_no = 0;
76 static int suppress_start_page = 0;
78 #define STORE_WIDTH 1
80 symbol HYPHEN_SYMBOL("hy");
82 // Character used when a hyphen is inserted at a line break.
83 static charinfo *soft_hyphen_char;
85 enum constant_space_type {
86 CONSTANT_SPACE_NONE,
87 CONSTANT_SPACE_RELATIVE,
88 CONSTANT_SPACE_ABSOLUTE
91 struct special_font_list {
92 int n;
93 special_font_list *next;
96 special_font_list *global_special_fonts;
97 static int global_ligature_mode = 1;
98 static int global_kern_mode = 1;
100 class track_kerning_function {
101 int non_zero;
102 units min_size;
103 hunits min_amount;
104 units max_size;
105 hunits max_amount;
106 public:
107 track_kerning_function();
108 track_kerning_function(units, hunits, units, hunits);
109 int operator==(const track_kerning_function &);
110 int operator!=(const track_kerning_function &);
111 hunits compute(int point_size);
114 // embolden fontno when this is the current font
116 struct conditional_bold {
117 conditional_bold *next;
118 int fontno;
119 hunits offset;
120 conditional_bold(int, hunits, conditional_bold * = 0);
123 class font_info {
124 tfont *last_tfont;
125 int number;
126 font_size last_size;
127 int last_height;
128 int last_slant;
129 symbol internal_name;
130 symbol external_name;
131 font *fm;
132 char is_bold;
133 hunits bold_offset;
134 track_kerning_function track_kern;
135 constant_space_type is_constant_spaced;
136 units constant_space;
137 int last_ligature_mode;
138 int last_kern_mode;
139 conditional_bold *cond_bold_list;
140 void flush();
141 public:
142 special_font_list *sf;
143 font_info(symbol, int, symbol, font *);
144 int contains(charinfo *);
145 void set_bold(hunits);
146 void unbold();
147 void set_conditional_bold(int, hunits);
148 void conditional_unbold(int);
149 void set_track_kern(track_kerning_function &);
150 void set_constant_space(constant_space_type, units = 0);
151 int is_named(symbol);
152 symbol get_name();
153 tfont *get_tfont(font_size, int, int, int);
154 hunits get_space_width(font_size, int);
155 hunits get_narrow_space_width(font_size);
156 hunits get_half_narrow_space_width(font_size);
157 int get_bold(hunits *);
158 int is_special();
159 int is_style();
160 friend symbol get_font_name(int, environment *);
161 friend symbol get_style_name(int);
164 class tfont_spec {
165 protected:
166 symbol name;
167 int input_position;
168 font *fm;
169 font_size size;
170 char is_bold;
171 char is_constant_spaced;
172 int ligature_mode;
173 int kern_mode;
174 hunits bold_offset;
175 hunits track_kern; // add this to the width
176 hunits constant_space_width;
177 int height;
178 int slant;
179 public:
180 tfont_spec(symbol, int, font *, font_size, int, int);
181 tfont_spec(const tfont_spec &spec) { *this = spec; }
182 tfont_spec plain();
183 int operator==(const tfont_spec &);
184 friend tfont *font_info::get_tfont(font_size fs, int, int, int);
187 class tfont : public tfont_spec {
188 static tfont *tfont_list;
189 tfont *next;
190 tfont *plain_version;
191 public:
192 tfont(tfont_spec &);
193 int contains(charinfo *);
194 hunits get_width(charinfo *c);
195 int get_bold(hunits *);
196 int get_constant_space(hunits *);
197 hunits get_track_kern();
198 tfont *get_plain();
199 font_size get_size();
200 symbol get_name();
201 charinfo *get_lig(charinfo *c1, charinfo *c2);
202 int get_kern(charinfo *c1, charinfo *c2, hunits *res);
203 int get_input_position();
204 int get_character_type(charinfo *);
205 int get_height();
206 int get_slant();
207 vunits get_char_height(charinfo *);
208 vunits get_char_depth(charinfo *);
209 hunits get_char_skew(charinfo *);
210 hunits get_italic_correction(charinfo *);
211 hunits get_left_italic_correction(charinfo *);
212 hunits get_subscript_correction(charinfo *);
213 friend tfont *make_tfont(tfont_spec &);
216 inline int env_definite_font(environment *env)
218 return env->get_family()->make_definite(env->get_font());
221 /* font_info functions */
223 static font_info **font_table = 0;
224 static int font_table_size = 0;
226 font_info::font_info(symbol nm, int n, symbol enm, font *f)
227 : last_tfont(0), number(n), last_size(0),
228 internal_name(nm), external_name(enm), fm(f),
229 is_bold(0), is_constant_spaced(CONSTANT_SPACE_NONE), last_ligature_mode(1),
230 last_kern_mode(1), cond_bold_list(0), sf(0)
234 inline int font_info::contains(charinfo *ci)
236 return fm != 0 && fm->contains(ci->get_index());
239 inline int font_info::is_special()
241 return fm != 0 && fm->is_special();
244 inline int font_info::is_style()
246 return fm == 0;
249 tfont *make_tfont(tfont_spec &spec)
251 for (tfont *p = tfont::tfont_list; p; p = p->next)
252 if (*p == spec)
253 return p;
254 return new tfont(spec);
257 // this is the current_font, fontno is where we found the character,
258 // presumably a special font
260 tfont *font_info::get_tfont(font_size fs, int height, int slant, int fontno)
262 if (last_tfont == 0 || fs != last_size
263 || height != last_height || slant != last_slant
264 || global_ligature_mode != last_ligature_mode
265 || global_kern_mode != last_kern_mode
266 || fontno != number) {
267 font_info *f = font_table[fontno];
268 tfont_spec spec(f->external_name, f->number, f->fm, fs, height, slant);
269 for (conditional_bold *p = cond_bold_list; p; p = p->next)
270 if (p->fontno == fontno) {
271 spec.is_bold = 1;
272 spec.bold_offset = p->offset;
273 break;
275 if (!spec.is_bold && is_bold) {
276 spec.is_bold = 1;
277 spec.bold_offset = bold_offset;
279 spec.track_kern = track_kern.compute(fs.to_scaled_points());
280 spec.ligature_mode = global_ligature_mode;
281 spec.kern_mode = global_kern_mode;
282 switch (is_constant_spaced) {
283 case CONSTANT_SPACE_NONE:
284 break;
285 case CONSTANT_SPACE_ABSOLUTE:
286 spec.is_constant_spaced = 1;
287 spec.constant_space_width = constant_space;
288 break;
289 case CONSTANT_SPACE_RELATIVE:
290 spec.is_constant_spaced = 1;
291 spec.constant_space_width
292 = scale(constant_space*fs.to_scaled_points(),
293 units_per_inch,
294 36*72*sizescale);
295 break;
296 default:
297 assert(0);
299 if (fontno != number)
300 return make_tfont(spec);
301 last_tfont = make_tfont(spec);
302 last_size = fs;
303 last_height = height;
304 last_slant = slant;
305 last_ligature_mode = global_ligature_mode;
306 last_kern_mode = global_kern_mode;
308 return last_tfont;
311 int font_info::get_bold(hunits *res)
313 if (is_bold) {
314 *res = bold_offset;
315 return 1;
317 else
318 return 0;
321 void font_info::unbold()
323 if (is_bold) {
324 is_bold = 0;
325 flush();
329 void font_info::set_bold(hunits offset)
331 if (!is_bold || offset != bold_offset) {
332 is_bold = 1;
333 bold_offset = offset;
334 flush();
338 void font_info::set_conditional_bold(int fontno, hunits offset)
340 for (conditional_bold *p = cond_bold_list; p; p = p->next)
341 if (p->fontno == fontno) {
342 if (offset != p->offset) {
343 p->offset = offset;
344 flush();
346 return;
348 cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
351 conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
352 : next(x), fontno(f), offset(h)
356 void font_info::conditional_unbold(int fontno)
358 for (conditional_bold **p = &cond_bold_list; *p; p = &(*p)->next)
359 if ((*p)->fontno == fontno) {
360 conditional_bold *tem = *p;
361 *p = (*p)->next;
362 delete tem;
363 flush();
364 return;
368 void font_info::set_constant_space(constant_space_type type, units x)
370 if (type != is_constant_spaced
371 || (type != CONSTANT_SPACE_NONE && x != constant_space)) {
372 flush();
373 is_constant_spaced = type;
374 constant_space = x;
378 void font_info::set_track_kern(track_kerning_function &tk)
380 if (track_kern != tk) {
381 track_kern = tk;
382 flush();
386 void font_info::flush()
388 last_tfont = 0;
391 int font_info::is_named(symbol s)
393 return internal_name == s;
396 symbol font_info::get_name()
398 return internal_name;
401 symbol get_font_name(int fontno, environment *env)
403 symbol f = font_table[fontno]->get_name();
404 if (font_table[fontno]->is_style()) {
405 return concat(env->get_family()->nm, f);
407 return f;
410 symbol get_style_name(int fontno)
412 if (font_table[fontno]->is_style())
413 return font_table[fontno]->get_name();
414 else
415 return EMPTY_SYMBOL;
418 hunits font_info::get_space_width(font_size fs, int space_sz)
420 if (is_constant_spaced == CONSTANT_SPACE_NONE)
421 return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
422 space_sz, 12);
423 else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
424 return constant_space;
425 else
426 return scale(constant_space*fs.to_scaled_points(),
427 units_per_inch, 36*72*sizescale);
430 hunits font_info::get_narrow_space_width(font_size fs)
432 charinfo *ci = get_charinfo(symbol("|"));
433 if (fm->contains(ci->get_index()))
434 return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
435 else
436 return hunits(fs.to_units()/6);
439 hunits font_info::get_half_narrow_space_width(font_size fs)
441 charinfo *ci = get_charinfo(symbol("^"));
442 if (fm->contains(ci->get_index()))
443 return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
444 else
445 return hunits(fs.to_units()/12);
448 /* tfont */
450 tfont_spec::tfont_spec(symbol nm, int n, font *f,
451 font_size s, int h, int sl)
452 : name(nm), input_position(n), fm(f), size(s),
453 is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1),
454 height(h), slant(sl)
456 if (height == size.to_scaled_points())
457 height = 0;
460 int tfont_spec::operator==(const tfont_spec &spec)
462 if (fm == spec.fm
463 && size == spec.size
464 && input_position == spec.input_position
465 && name == spec.name
466 && height == spec.height
467 && slant == spec.slant
468 && (is_bold
469 ? (spec.is_bold && bold_offset == spec.bold_offset)
470 : !spec.is_bold)
471 && track_kern == spec.track_kern
472 && (is_constant_spaced
473 ? (spec.is_constant_spaced
474 && constant_space_width == spec.constant_space_width)
475 : !spec.is_constant_spaced)
476 && ligature_mode == spec.ligature_mode
477 && kern_mode == spec.kern_mode)
478 return 1;
479 else
480 return 0;
483 tfont_spec tfont_spec::plain()
485 return tfont_spec(name, input_position, fm, size, height, slant);
488 hunits tfont::get_width(charinfo *c)
490 if (is_constant_spaced)
491 return constant_space_width;
492 else if (is_bold)
493 return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
494 + track_kern + bold_offset);
495 else
496 return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
497 + track_kern);
500 vunits tfont::get_char_height(charinfo *c)
502 vunits v = fm->get_height(c->get_index(), size.to_scaled_points());
503 if (height != 0 && height != size.to_scaled_points())
504 return scale(v, height, size.to_scaled_points());
505 else
506 return v;
509 vunits tfont::get_char_depth(charinfo *c)
511 vunits v = fm->get_depth(c->get_index(), size.to_scaled_points());
512 if (height != 0 && height != size.to_scaled_points())
513 return scale(v, height, size.to_scaled_points());
514 else
515 return v;
518 hunits tfont::get_char_skew(charinfo *c)
520 return hunits(fm->get_skew(c->get_index(), size.to_scaled_points(), slant));
523 hunits tfont::get_italic_correction(charinfo *c)
525 return hunits(fm->get_italic_correction(c->get_index(), size.to_scaled_points()));
528 hunits tfont::get_left_italic_correction(charinfo *c)
530 return hunits(fm->get_left_italic_correction(c->get_index(),
531 size.to_scaled_points()));
534 hunits tfont::get_subscript_correction(charinfo *c)
536 return hunits(fm->get_subscript_correction(c->get_index(),
537 size.to_scaled_points()));
540 inline int tfont::get_input_position()
542 return input_position;
545 inline int tfont::contains(charinfo *ci)
547 return fm->contains(ci->get_index());
550 inline int tfont::get_character_type(charinfo *ci)
552 return fm->get_character_type(ci->get_index());
555 inline int tfont::get_bold(hunits *res)
557 if (is_bold) {
558 *res = bold_offset;
559 return 1;
561 else
562 return 0;
565 inline int tfont::get_constant_space(hunits *res)
567 if (is_constant_spaced) {
568 *res = constant_space_width;
569 return 1;
571 else
572 return 0;
575 inline hunits tfont::get_track_kern()
577 return track_kern;
580 inline tfont *tfont::get_plain()
582 return plain_version;
585 inline font_size tfont::get_size()
587 return size;
590 inline symbol tfont::get_name()
592 return name;
595 inline int tfont::get_height()
597 return height;
600 inline int tfont::get_slant()
602 return slant;
605 symbol SYMBOL_ff("ff");
606 symbol SYMBOL_fi("fi");
607 symbol SYMBOL_fl("fl");
608 symbol SYMBOL_Fi("Fi");
609 symbol SYMBOL_Fl("Fl");
611 charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
613 if (ligature_mode == 0)
614 return 0;
615 charinfo *ci = 0;
616 if (c1->get_ascii_code() == 'f') {
617 switch (c2->get_ascii_code()) {
618 case 'f':
619 if (fm->has_ligature(font::LIG_ff))
620 ci = get_charinfo(SYMBOL_ff);
621 break;
622 case 'i':
623 if (fm->has_ligature(font::LIG_fi))
624 ci = get_charinfo(SYMBOL_fi);
625 break;
626 case 'l':
627 if (fm->has_ligature(font::LIG_fl))
628 ci = get_charinfo(SYMBOL_fl);
629 break;
632 else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
633 switch (c2->get_ascii_code()) {
634 case 'i':
635 if (fm->has_ligature(font::LIG_ffi))
636 ci = get_charinfo(SYMBOL_Fi);
637 break;
638 case 'l':
639 if (fm->has_ligature(font::LIG_ffl))
640 ci = get_charinfo(SYMBOL_Fl);
641 break;
644 if (ci != 0 && fm->contains(ci->get_index()))
645 return ci;
646 return 0;
649 inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res)
651 if (kern_mode == 0)
652 return 0;
653 else {
654 int n = fm->get_kern(c1->get_index(),
655 c2->get_index(),
656 size.to_scaled_points());
657 if (n) {
658 *res = hunits(n);
659 return 1;
661 else
662 return 0;
666 tfont *tfont::tfont_list = 0;
668 tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
670 next = tfont_list;
671 tfont_list = this;
672 tfont_spec plain_spec = plain();
673 tfont *p;
674 for (p = tfont_list; p; p = p->next)
675 if (*p == plain_spec) {
676 plain_version = p;
677 break;
679 if (!p)
680 plain_version = new tfont(plain_spec);
683 /* output_file */
685 class real_output_file : public output_file {
686 #ifndef POPEN_MISSING
687 int piped;
688 #endif
689 int printing; // decision via optional page list
690 int output_on; // \O[0] or \O[1] escape calls
691 virtual void really_transparent_char(unsigned char) = 0;
692 virtual void really_print_line(hunits x, vunits y, node *n,
693 vunits before, vunits after, hunits width) = 0;
694 virtual void really_begin_page(int pageno, vunits page_length) = 0;
695 virtual void really_copy_file(hunits x, vunits y, const char *filename);
696 virtual void really_put_filename(const char *filename);
697 virtual void really_on();
698 virtual void really_off();
699 public:
700 FILE *fp;
701 real_output_file();
702 ~real_output_file();
703 void flush();
704 void transparent_char(unsigned char);
705 void print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
706 void begin_page(int pageno, vunits page_length);
707 void put_filename(const char *filename);
708 void on();
709 void off();
710 int is_on();
711 int is_printing();
712 void copy_file(hunits x, vunits y, const char *filename);
715 class suppress_output_file : public real_output_file {
716 public:
717 suppress_output_file();
718 void really_transparent_char(unsigned char);
719 void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
720 void really_begin_page(int pageno, vunits page_length);
723 class ascii_output_file : public real_output_file {
724 public:
725 ascii_output_file();
726 void really_transparent_char(unsigned char);
727 void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
728 void really_begin_page(int pageno, vunits page_length);
729 void outc(unsigned char c);
730 void outs(const char *s);
733 void ascii_output_file::outc(unsigned char c)
735 fputc(c, fp);
738 void ascii_output_file::outs(const char *s)
740 fputc('<', fp);
741 if (s)
742 fputs(s, fp);
743 fputc('>', fp);
746 struct hvpair;
748 class troff_output_file : public real_output_file {
749 units hpos;
750 units vpos;
751 units output_vpos;
752 units output_hpos;
753 int force_motion;
754 int current_size;
755 int current_slant;
756 int current_height;
757 tfont *current_tfont;
758 color *current_fill_color;
759 color *current_glyph_color;
760 int current_font_number;
761 symbol *font_position;
762 int nfont_positions;
763 enum { TBUF_SIZE = 256 };
764 char tbuf[TBUF_SIZE];
765 int tbuf_len;
766 int tbuf_kern;
767 int begun_page;
768 int cur_div_level;
769 string tag_list;
770 void do_motion();
771 void put(char c);
772 void put(unsigned char c);
773 void put(int i);
774 void put(unsigned int i);
775 void put(const char *s);
776 void set_font(tfont *tf);
777 void flush_tbuf();
778 public:
779 troff_output_file();
780 ~troff_output_file();
781 void trailer(vunits page_length);
782 void put_char(charinfo *, tfont *, color *, color *);
783 void put_char_width(charinfo *, tfont *, color *, color *, hunits, hunits);
784 void right(hunits);
785 void down(vunits);
786 void moveto(hunits, vunits);
787 void start_special(tfont *, color *, color *, int = 0);
788 void start_special();
789 void special_char(unsigned char c);
790 void end_special();
791 void word_marker();
792 void really_transparent_char(unsigned char c);
793 void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
794 void really_begin_page(int pageno, vunits page_length);
795 void really_copy_file(hunits x, vunits y, const char *filename);
796 void really_put_filename(const char *filename);
797 void really_on();
798 void really_off();
799 void draw(char, hvpair *, int, font_size, color *, color *);
800 void determine_line_limits (char code, hvpair *point, int npoints);
801 void check_charinfo(tfont *tf, charinfo *ci);
802 void glyph_color(color *c);
803 void fill_color(color *c);
804 int get_hpos() { return hpos; }
805 int get_vpos() { return vpos; }
806 void add_to_tag_list(string s);
807 friend void space_char_hmotion_node::tprint(troff_output_file *);
808 friend void unbreakable_space_node::tprint(troff_output_file *);
811 static void put_string(const char *s, FILE *fp)
813 for (; *s != '\0'; ++s)
814 putc(*s, fp);
817 inline void troff_output_file::put(char c)
819 putc(c, fp);
822 inline void troff_output_file::put(unsigned char c)
824 putc(c, fp);
827 inline void troff_output_file::put(const char *s)
829 put_string(s, fp);
832 inline void troff_output_file::put(int i)
834 put_string(i_to_a(i), fp);
837 inline void troff_output_file::put(unsigned int i)
839 put_string(ui_to_a(i), fp);
842 void troff_output_file::start_special(tfont *tf, color *gcol, color *fcol,
843 int no_init_string)
845 set_font(tf);
846 glyph_color(gcol);
847 fill_color(fcol);
848 flush_tbuf();
849 do_motion();
850 if (!no_init_string)
851 put("x X ");
854 void troff_output_file::start_special()
856 flush_tbuf();
857 do_motion();
858 put("x X ");
861 void troff_output_file::special_char(unsigned char c)
863 put(c);
864 if (c == '\n')
865 put('+');
868 void troff_output_file::end_special()
870 put('\n');
873 inline void troff_output_file::moveto(hunits h, vunits v)
875 hpos = h.to_units();
876 vpos = v.to_units();
879 void troff_output_file::really_print_line(hunits x, vunits y, node *n,
880 vunits before, vunits after, hunits)
882 moveto(x, y);
883 while (n != 0) {
884 // Check whether we should push the current troff state and use
885 // the state at the start of the invocation of this diversion.
886 if (n->div_nest_level > cur_div_level && n->push_state) {
887 state.push_state(n->push_state);
888 cur_div_level = n->div_nest_level;
890 // Has the current diversion level decreased? Then we must pop the
891 // troff state.
892 while (n->div_nest_level < cur_div_level) {
893 state.pop_state();
894 cur_div_level = n->div_nest_level;
896 // Now check whether the state has changed.
897 if ((is_on() || n->force_tprint())
898 && (state.changed(n->state) || n->is_tag() || n->is_special)) {
899 flush_tbuf();
900 do_motion();
901 force_motion = 1;
902 flush();
903 state.flush(fp, n->state, tag_list);
904 tag_list = string("");
905 flush();
907 n->tprint(this);
908 n = n->next;
910 flush_tbuf();
911 // This ensures that transparent throughput will have a more predictable
912 // position.
913 do_motion();
914 force_motion = 1;
915 hpos = 0;
916 put('n');
917 put(before.to_units());
918 put(' ');
919 put(after.to_units());
920 put('\n');
923 inline void troff_output_file::word_marker()
925 flush_tbuf();
926 if (is_on())
927 put('w');
930 inline void troff_output_file::right(hunits n)
932 hpos += n.to_units();
935 inline void troff_output_file::down(vunits n)
937 vpos += n.to_units();
940 void troff_output_file::do_motion()
942 if (force_motion) {
943 put('V');
944 put(vpos);
945 put('\n');
946 put('H');
947 put(hpos);
948 put('\n');
950 else {
951 if (hpos != output_hpos) {
952 units n = hpos - output_hpos;
953 if (n > 0 && n < hpos) {
954 put('h');
955 put(n);
957 else {
958 put('H');
959 put(hpos);
961 put('\n');
963 if (vpos != output_vpos) {
964 units n = vpos - output_vpos;
965 if (n > 0 && n < vpos) {
966 put('v');
967 put(n);
969 else {
970 put('V');
971 put(vpos);
973 put('\n');
976 output_vpos = vpos;
977 output_hpos = hpos;
978 force_motion = 0;
981 void troff_output_file::flush_tbuf()
983 if (!is_on()) {
984 tbuf_len = 0;
985 return;
988 if (tbuf_len == 0)
989 return;
990 if (tbuf_kern == 0)
991 put('t');
992 else {
993 put('u');
994 put(tbuf_kern);
995 put(' ');
997 check_output_limits(hpos, vpos);
998 check_output_limits(hpos, vpos - current_size);
1000 for (int i = 0; i < tbuf_len; i++)
1001 put(tbuf[i]);
1002 put('\n');
1003 tbuf_len = 0;
1006 void troff_output_file::check_charinfo(tfont *tf, charinfo *ci)
1008 if (!is_on())
1009 return;
1011 int height = tf->get_char_height(ci).to_units();
1012 int width = tf->get_width(ci).to_units()
1013 + tf->get_italic_correction(ci).to_units();
1014 int depth = tf->get_char_depth(ci).to_units();
1015 check_output_limits(output_hpos, output_vpos - height);
1016 check_output_limits(output_hpos + width, output_vpos + depth);
1019 void troff_output_file::put_char_width(charinfo *ci, tfont *tf,
1020 color *gcol, color *fcol,
1021 hunits w, hunits k)
1023 int kk = k.to_units();
1024 if (!is_on()) {
1025 flush_tbuf();
1026 hpos += w.to_units() + kk;
1027 return;
1029 set_font(tf);
1030 unsigned char c = ci->get_ascii_code();
1031 if (c == '\0') {
1032 glyph_color(gcol);
1033 fill_color(fcol);
1034 flush_tbuf();
1035 do_motion();
1036 check_charinfo(tf, ci);
1037 if (ci->numbered()) {
1038 put('N');
1039 put(ci->get_number());
1041 else {
1042 put('C');
1043 const char *s = ci->nm.contents();
1044 if (s[1] == 0) {
1045 put('\\');
1046 put(s[0]);
1048 else
1049 put(s);
1051 put('\n');
1052 hpos += w.to_units() + kk;
1054 else if (tcommand_flag) {
1055 if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
1056 && (!gcol || gcol == current_glyph_color)
1057 && (!fcol || fcol == current_fill_color)
1058 && kk == tbuf_kern
1059 && tbuf_len < TBUF_SIZE) {
1060 check_charinfo(tf, ci);
1061 tbuf[tbuf_len++] = c;
1062 output_hpos += w.to_units() + kk;
1063 hpos = output_hpos;
1064 return;
1066 glyph_color(gcol);
1067 fill_color(fcol);
1068 flush_tbuf();
1069 do_motion();
1070 check_charinfo(tf, ci);
1071 tbuf[tbuf_len++] = c;
1072 output_hpos += w.to_units() + kk;
1073 tbuf_kern = kk;
1074 hpos = output_hpos;
1076 else {
1077 // flush_tbuf();
1078 int n = hpos - output_hpos;
1079 check_charinfo(tf, ci);
1080 // check_output_limits(output_hpos, output_vpos);
1081 if (vpos == output_vpos
1082 && (!gcol || gcol == current_glyph_color)
1083 && (!fcol || fcol == current_fill_color)
1084 && n > 0 && n < 100 && !force_motion) {
1085 put(char(n/10 + '0'));
1086 put(char(n%10 + '0'));
1087 put(c);
1088 output_hpos = hpos;
1090 else {
1091 glyph_color(gcol);
1092 fill_color(fcol);
1093 do_motion();
1094 put('c');
1095 put(c);
1097 hpos += w.to_units() + kk;
1101 void troff_output_file::put_char(charinfo *ci, tfont *tf,
1102 color *gcol, color *fcol)
1104 flush_tbuf();
1105 if (!is_on())
1106 return;
1107 set_font(tf);
1108 unsigned char c = ci->get_ascii_code();
1109 if (c == '\0') {
1110 glyph_color(gcol);
1111 fill_color(fcol);
1112 flush_tbuf();
1113 do_motion();
1114 if (ci->numbered()) {
1115 put('N');
1116 put(ci->get_number());
1118 else {
1119 put('C');
1120 const char *s = ci->nm.contents();
1121 if (s[1] == 0) {
1122 put('\\');
1123 put(s[0]);
1125 else
1126 put(s);
1128 put('\n');
1130 else {
1131 int n = hpos - output_hpos;
1132 if (vpos == output_vpos
1133 && (!gcol || gcol == current_glyph_color)
1134 && (!fcol || fcol == current_fill_color)
1135 && n > 0 && n < 100) {
1136 put(char(n/10 + '0'));
1137 put(char(n%10 + '0'));
1138 put(c);
1139 output_hpos = hpos;
1141 else {
1142 glyph_color(gcol);
1143 fill_color(fcol);
1144 flush_tbuf();
1145 do_motion();
1146 put('c');
1147 put(c);
1152 // set_font calls `flush_tbuf' if necessary.
1154 void troff_output_file::set_font(tfont *tf)
1156 if (current_tfont == tf)
1157 return;
1158 flush_tbuf();
1159 int n = tf->get_input_position();
1160 symbol nm = tf->get_name();
1161 if (n >= nfont_positions || font_position[n] != nm) {
1162 put("x font ");
1163 put(n);
1164 put(' ');
1165 put(nm.contents());
1166 put('\n');
1167 if (n >= nfont_positions) {
1168 int old_nfont_positions = nfont_positions;
1169 symbol *old_font_position = font_position;
1170 nfont_positions *= 3;
1171 nfont_positions /= 2;
1172 if (nfont_positions <= n)
1173 nfont_positions = n + 10;
1174 font_position = new symbol[nfont_positions];
1175 memcpy(font_position, old_font_position,
1176 old_nfont_positions*sizeof(symbol));
1177 a_delete old_font_position;
1179 font_position[n] = nm;
1181 if (current_font_number != n) {
1182 put('f');
1183 put(n);
1184 put('\n');
1185 current_font_number = n;
1187 int size = tf->get_size().to_scaled_points();
1188 if (current_size != size) {
1189 put('s');
1190 put(size);
1191 put('\n');
1192 current_size = size;
1194 int slant = tf->get_slant();
1195 if (current_slant != slant) {
1196 put("x Slant ");
1197 put(slant);
1198 put('\n');
1199 current_slant = slant;
1201 int height = tf->get_height();
1202 if (current_height != height) {
1203 put("x Height ");
1204 put(height == 0 ? current_size : height);
1205 put('\n');
1206 current_height = height;
1208 current_tfont = tf;
1211 // fill_color calls `flush_tbuf' and `do_motion' if necessary.
1213 void troff_output_file::fill_color(color *col)
1215 if (!col || current_fill_color == col)
1216 return;
1217 current_fill_color = col;
1218 if (!color_flag)
1219 return;
1220 flush_tbuf();
1221 do_motion();
1222 put("DF");
1223 unsigned int components[4];
1224 color_scheme cs;
1225 cs = col->get_components(components);
1226 switch (cs) {
1227 case DEFAULT:
1228 put('d');
1229 break;
1230 case RGB:
1231 put("r ");
1232 put(Red);
1233 put(' ');
1234 put(Green);
1235 put(' ');
1236 put(Blue);
1237 break;
1238 case CMY:
1239 put("c ");
1240 put(Cyan);
1241 put(' ');
1242 put(Magenta);
1243 put(' ');
1244 put(Yellow);
1245 break;
1246 case CMYK:
1247 put("k ");
1248 put(Cyan);
1249 put(' ');
1250 put(Magenta);
1251 put(' ');
1252 put(Yellow);
1253 put(' ');
1254 put(Black);
1255 break;
1256 case GRAY:
1257 put("g ");
1258 put(Gray);
1259 break;
1261 put('\n');
1264 // glyph_color calls `flush_tbuf' and `do_motion' if necessary.
1266 void troff_output_file::glyph_color(color *col)
1268 if (!col || current_glyph_color == col)
1269 return;
1270 current_glyph_color = col;
1271 if (!color_flag)
1272 return;
1273 flush_tbuf();
1274 // grotty doesn't like a color command if the vertical position is zero.
1275 do_motion();
1276 put("m");
1277 unsigned int components[4];
1278 color_scheme cs;
1279 cs = col->get_components(components);
1280 switch (cs) {
1281 case DEFAULT:
1282 put('d');
1283 break;
1284 case RGB:
1285 put("r ");
1286 put(Red);
1287 put(' ');
1288 put(Green);
1289 put(' ');
1290 put(Blue);
1291 break;
1292 case CMY:
1293 put("c ");
1294 put(Cyan);
1295 put(' ');
1296 put(Magenta);
1297 put(' ');
1298 put(Yellow);
1299 break;
1300 case CMYK:
1301 put("k ");
1302 put(Cyan);
1303 put(' ');
1304 put(Magenta);
1305 put(' ');
1306 put(Yellow);
1307 put(' ');
1308 put(Black);
1309 break;
1310 case GRAY:
1311 put("g ");
1312 put(Gray);
1313 break;
1315 put('\n');
1318 void troff_output_file::add_to_tag_list(string s)
1320 if (tag_list == string(""))
1321 tag_list = s;
1322 else {
1323 tag_list += string("\n");
1324 tag_list += s;
1328 // determine_line_limits - works out the smallest box which will contain
1329 // the entity, code, built from the point array.
1330 void troff_output_file::determine_line_limits(char code, hvpair *point,
1331 int npoints)
1333 int i, x, y;
1335 if (!is_on())
1336 return;
1338 switch (code) {
1339 case 'c':
1340 case 'C':
1341 // only the h field is used when defining a circle
1342 check_output_limits(output_hpos,
1343 output_vpos - point[0].h.to_units()/2);
1344 check_output_limits(output_hpos + point[0].h.to_units(),
1345 output_vpos + point[0].h.to_units()/2);
1346 break;
1347 case 'E':
1348 case 'e':
1349 check_output_limits(output_hpos,
1350 output_vpos - point[0].v.to_units()/2);
1351 check_output_limits(output_hpos + point[0].h.to_units(),
1352 output_vpos + point[0].v.to_units()/2);
1353 break;
1354 case 'P':
1355 case 'p':
1356 x = output_hpos;
1357 y = output_vpos;
1358 check_output_limits(x, y);
1359 for (i = 0; i < npoints; i++) {
1360 x += point[i].h.to_units();
1361 y += point[i].v.to_units();
1362 check_output_limits(x, y);
1364 break;
1365 case 't':
1366 x = output_hpos;
1367 y = output_vpos;
1368 for (i = 0; i < npoints; i++) {
1369 x += point[i].h.to_units();
1370 y += point[i].v.to_units();
1371 check_output_limits(x, y);
1373 break;
1374 case 'a':
1375 double c[2];
1376 int p[4];
1377 int minx, miny, maxx, maxy;
1378 x = output_hpos;
1379 y = output_vpos;
1380 p[0] = point[0].h.to_units();
1381 p[1] = point[0].v.to_units();
1382 p[2] = point[1].h.to_units();
1383 p[3] = point[1].v.to_units();
1384 if (adjust_arc_center(p, c)) {
1385 check_output_arc_limits(x, y,
1386 p[0], p[1], p[2], p[3],
1387 c[0], c[1],
1388 &minx, &maxx, &miny, &maxy);
1389 check_output_limits(minx, miny);
1390 check_output_limits(maxx, maxy);
1391 break;
1393 // fall through
1394 case 'l':
1395 x = output_hpos;
1396 y = output_vpos;
1397 check_output_limits(x, y);
1398 for (i = 0; i < npoints; i++) {
1399 x += point[i].h.to_units();
1400 y += point[i].v.to_units();
1401 check_output_limits(x, y);
1403 break;
1404 default:
1405 x = output_hpos;
1406 y = output_vpos;
1407 for (i = 0; i < npoints; i++) {
1408 x += point[i].h.to_units();
1409 y += point[i].v.to_units();
1410 check_output_limits(x, y);
1415 void troff_output_file::draw(char code, hvpair *point, int npoints,
1416 font_size fsize, color *gcol, color *fcol)
1418 int i;
1419 glyph_color(gcol);
1420 fill_color(fcol);
1421 flush_tbuf();
1422 do_motion();
1423 if (is_on()) {
1424 int size = fsize.to_scaled_points();
1425 if (current_size != size) {
1426 put('s');
1427 put(size);
1428 put('\n');
1429 current_size = size;
1430 current_tfont = 0;
1432 put('D');
1433 put(code);
1434 if (code == 'c') {
1435 put(' ');
1436 put(point[0].h.to_units());
1438 else
1439 for (i = 0; i < npoints; i++) {
1440 put(' ');
1441 put(point[i].h.to_units());
1442 put(' ');
1443 put(point[i].v.to_units());
1445 determine_line_limits(code, point, npoints);
1448 for (i = 0; i < npoints; i++)
1449 output_hpos += point[i].h.to_units();
1450 hpos = output_hpos;
1451 if (code != 'e') {
1452 for (i = 0; i < npoints; i++)
1453 output_vpos += point[i].v.to_units();
1454 vpos = output_vpos;
1456 if (is_on())
1457 put('\n');
1460 void troff_output_file::really_on()
1462 flush_tbuf();
1463 force_motion = 1;
1464 do_motion();
1467 void troff_output_file::really_off()
1469 flush_tbuf();
1472 void troff_output_file::really_put_filename(const char *filename)
1474 flush_tbuf();
1475 put("F ");
1476 put(filename);
1477 put('\n');
1480 void troff_output_file::really_begin_page(int pageno, vunits page_length)
1482 flush_tbuf();
1483 if (begun_page) {
1484 if (page_length > V0) {
1485 put('V');
1486 put(page_length.to_units());
1487 put('\n');
1490 else
1491 begun_page = 1;
1492 current_tfont = 0;
1493 current_font_number = -1;
1494 current_size = 0;
1495 // current_height = 0;
1496 // current_slant = 0;
1497 hpos = 0;
1498 vpos = 0;
1499 output_hpos = 0;
1500 output_vpos = 0;
1501 force_motion = 1;
1502 for (int i = 0; i < nfont_positions; i++)
1503 font_position[i] = NULL_SYMBOL;
1504 put('p');
1505 put(pageno);
1506 put('\n');
1509 void troff_output_file::really_copy_file(hunits x, vunits y,
1510 const char *filename)
1512 moveto(x, y);
1513 flush_tbuf();
1514 do_motion();
1515 errno = 0;
1516 FILE *ifp = include_search_path.open_file_cautious(filename);
1517 if (ifp == 0)
1518 error("can't open `%1': %2", filename, strerror(errno));
1519 else {
1520 int c;
1521 while ((c = getc(ifp)) != EOF)
1522 put(char(c));
1523 fclose(ifp);
1525 force_motion = 1;
1526 current_size = 0;
1527 current_tfont = 0;
1528 current_font_number = -1;
1529 for (int i = 0; i < nfont_positions; i++)
1530 font_position[i] = NULL_SYMBOL;
1533 void troff_output_file::really_transparent_char(unsigned char c)
1535 put(c);
1538 troff_output_file::~troff_output_file()
1540 a_delete font_position;
1543 void troff_output_file::trailer(vunits page_length)
1545 flush_tbuf();
1546 if (page_length > V0) {
1547 put("x trailer\n");
1548 put('V');
1549 put(page_length.to_units());
1550 put('\n');
1552 put("x stop\n");
1555 troff_output_file::troff_output_file()
1556 : current_slant(0), current_height(0), current_fill_color(0),
1557 current_glyph_color(0), nfont_positions(10), tbuf_len(0), begun_page(0),
1558 cur_div_level(0)
1560 font_position = new symbol[nfont_positions];
1561 put("x T ");
1562 put(device);
1563 put('\n');
1564 put("x res ");
1565 put(units_per_inch);
1566 put(' ');
1567 put(hresolution);
1568 put(' ');
1569 put(vresolution);
1570 put('\n');
1571 put("x init\n");
1574 /* output_file */
1576 output_file *the_output = 0;
1578 output_file::output_file()
1582 output_file::~output_file()
1586 void output_file::trailer(vunits)
1590 void output_file::put_filename(const char *)
1594 void output_file::on()
1598 void output_file::off()
1602 real_output_file::real_output_file()
1603 : printing(0), output_on(1)
1605 #ifndef POPEN_MISSING
1606 if (pipe_command) {
1607 if ((fp = popen(pipe_command, POPEN_WT)) != 0) {
1608 piped = 1;
1609 return;
1611 error("pipe open failed: %1", strerror(errno));
1613 piped = 0;
1614 #endif /* not POPEN_MISSING */
1615 fp = stdout;
1618 real_output_file::~real_output_file()
1620 if (!fp)
1621 return;
1622 // To avoid looping, set fp to 0 before calling fatal().
1623 if (ferror(fp) || fflush(fp) < 0) {
1624 fp = 0;
1625 fatal("error writing output file");
1627 #ifndef POPEN_MISSING
1628 if (piped) {
1629 int result = pclose(fp);
1630 fp = 0;
1631 if (result < 0)
1632 fatal("pclose failed");
1633 if (!WIFEXITED(result))
1634 error("output process `%1' got fatal signal %2",
1635 pipe_command,
1636 WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
1637 else {
1638 int exit_status = WEXITSTATUS(result);
1639 if (exit_status != 0)
1640 error("output process `%1' exited with status %2",
1641 pipe_command, exit_status);
1644 else
1645 #endif /* not POPEN MISSING */
1646 if (fclose(fp) < 0) {
1647 fp = 0;
1648 fatal("error closing output file");
1652 void real_output_file::flush()
1654 if (fflush(fp) < 0)
1655 fatal("error writing output file");
1658 int real_output_file::is_printing()
1660 return printing;
1663 void real_output_file::begin_page(int pageno, vunits page_length)
1665 printing = in_output_page_list(pageno);
1666 if (printing)
1667 really_begin_page(pageno, page_length);
1670 void real_output_file::copy_file(hunits x, vunits y, const char *filename)
1672 if (printing && output_on)
1673 really_copy_file(x, y, filename);
1674 check_output_limits(x.to_units(), y.to_units());
1677 void real_output_file::transparent_char(unsigned char c)
1679 if (printing && output_on)
1680 really_transparent_char(c);
1683 void real_output_file::print_line(hunits x, vunits y, node *n,
1684 vunits before, vunits after, hunits width)
1686 if (printing)
1687 really_print_line(x, y, n, before, after, width);
1688 delete_node_list(n);
1691 void real_output_file::really_copy_file(hunits, vunits, const char *)
1693 // do nothing
1696 void real_output_file::put_filename(const char *filename)
1698 really_put_filename(filename);
1701 void real_output_file::really_put_filename(const char *)
1705 void real_output_file::on()
1707 really_on();
1708 if (output_on == 0)
1709 output_on = 1;
1712 void real_output_file::off()
1714 really_off();
1715 output_on = 0;
1718 int real_output_file::is_on()
1720 return output_on;
1723 void real_output_file::really_on()
1727 void real_output_file::really_off()
1731 /* ascii_output_file */
1733 void ascii_output_file::really_transparent_char(unsigned char c)
1735 putc(c, fp);
1738 void ascii_output_file::really_print_line(hunits, vunits, node *n,
1739 vunits, vunits, hunits)
1741 while (n != 0) {
1742 n->ascii_print(this);
1743 n = n->next;
1745 fputc('\n', fp);
1748 void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/)
1750 fputs("<beginning of page>\n", fp);
1753 ascii_output_file::ascii_output_file()
1757 /* suppress_output_file */
1759 suppress_output_file::suppress_output_file()
1763 void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits, hunits)
1767 void suppress_output_file::really_begin_page(int, vunits)
1771 void suppress_output_file::really_transparent_char(unsigned char)
1775 /* glyphs, ligatures, kerns, discretionary breaks */
1777 class charinfo_node : public node {
1778 protected:
1779 charinfo *ci;
1780 public:
1781 charinfo_node(charinfo *, statem *, int, node * = 0);
1782 int ends_sentence();
1783 int overlaps_vertically();
1784 int overlaps_horizontally();
1787 charinfo_node::charinfo_node(charinfo *c, statem *s, int pop, node *x)
1788 : node(x, s, pop), ci(c)
1792 int charinfo_node::ends_sentence()
1794 if (ci->ends_sentence())
1795 return 1;
1796 else if (ci->transparent())
1797 return 2;
1798 else
1799 return 0;
1802 int charinfo_node::overlaps_horizontally()
1804 return ci->overlaps_horizontally();
1807 int charinfo_node::overlaps_vertically()
1809 return ci->overlaps_vertically();
1812 class glyph_node : public charinfo_node {
1813 static glyph_node *free_list;
1814 protected:
1815 tfont *tf;
1816 color *gcol;
1817 color *fcol; /* this is needed for grotty */
1818 #ifdef STORE_WIDTH
1819 hunits wid;
1820 glyph_node(charinfo *, tfont *, color *, color *, hunits,
1821 statem *, int, node * = 0);
1822 #endif
1823 public:
1824 void *operator new(size_t);
1825 void operator delete(void *);
1826 glyph_node(charinfo *, tfont *, color *, color *,
1827 statem *, int, node * = 0);
1828 ~glyph_node() {}
1829 node *copy();
1830 node *merge_glyph_node(glyph_node *);
1831 node *merge_self(node *);
1832 hunits width();
1833 node *last_char_node();
1834 units size();
1835 void vertical_extent(vunits *, vunits *);
1836 hunits subscript_correction();
1837 hunits italic_correction();
1838 hunits left_italic_correction();
1839 hunits skew();
1840 hyphenation_type get_hyphenation_type();
1841 tfont *get_tfont();
1842 color *get_glyph_color();
1843 color *get_fill_color();
1844 void tprint(troff_output_file *);
1845 void zero_width_tprint(troff_output_file *);
1846 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1847 node *add_self(node *, hyphen_list **);
1848 void ascii_print(ascii_output_file *);
1849 void asciify(macro *);
1850 int character_type();
1851 int same(node *);
1852 const char *type();
1853 int force_tprint();
1854 int is_tag();
1855 void debug_node();
1858 glyph_node *glyph_node::free_list = 0;
1860 class ligature_node : public glyph_node {
1861 node *n1;
1862 node *n2;
1863 #ifdef STORE_WIDTH
1864 ligature_node(charinfo *, tfont *, color *, color *, hunits,
1865 node *, node *, statem *, int, node * = 0);
1866 #endif
1867 public:
1868 void *operator new(size_t);
1869 void operator delete(void *);
1870 ligature_node(charinfo *, tfont *, color *, color *,
1871 node *, node *, statem *, int, node * = 0);
1872 ~ligature_node();
1873 node *copy();
1874 node *add_self(node *, hyphen_list **);
1875 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1876 void ascii_print(ascii_output_file *);
1877 void asciify(macro *);
1878 int same(node *);
1879 const char *type();
1880 int force_tprint();
1881 int is_tag();
1884 class kern_pair_node : public node {
1885 hunits amount;
1886 node *n1;
1887 node *n2;
1888 public:
1889 kern_pair_node(hunits, node *, node *, statem *, int, node * = 0);
1890 ~kern_pair_node();
1891 node *copy();
1892 node *merge_glyph_node(glyph_node *);
1893 node *add_self(node *, hyphen_list **);
1894 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1895 node *add_discretionary_hyphen();
1896 hunits width();
1897 node *last_char_node();
1898 hunits italic_correction();
1899 hunits subscript_correction();
1900 void tprint(troff_output_file *);
1901 hyphenation_type get_hyphenation_type();
1902 int ends_sentence();
1903 void ascii_print(ascii_output_file *);
1904 void asciify(macro *);
1905 int same(node *);
1906 const char *type();
1907 int force_tprint();
1908 int is_tag();
1909 void vertical_extent(vunits *, vunits *);
1912 class dbreak_node : public node {
1913 node *none;
1914 node *pre;
1915 node *post;
1916 public:
1917 dbreak_node(node *, node *, statem *, int, node * = 0);
1918 ~dbreak_node();
1919 node *copy();
1920 node *merge_glyph_node(glyph_node *);
1921 node *add_discretionary_hyphen();
1922 hunits width();
1923 node *last_char_node();
1924 hunits italic_correction();
1925 hunits subscript_correction();
1926 void tprint(troff_output_file *);
1927 breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
1928 int is_inner = 0);
1929 int nbreaks();
1930 int ends_sentence();
1931 void split(int, node **, node **);
1932 hyphenation_type get_hyphenation_type();
1933 void ascii_print(ascii_output_file *);
1934 void asciify(macro *);
1935 int same(node *);
1936 const char *type();
1937 int force_tprint();
1938 int is_tag();
1941 void *glyph_node::operator new(size_t n)
1943 assert(n == sizeof(glyph_node));
1944 if (!free_list) {
1945 const int BLOCK = 1024;
1946 free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
1947 for (int i = 0; i < BLOCK - 1; i++)
1948 free_list[i].next = free_list + i + 1;
1949 free_list[BLOCK-1].next = 0;
1951 glyph_node *p = free_list;
1952 free_list = (glyph_node *)(free_list->next);
1953 p->next = 0;
1954 return p;
1957 void *ligature_node::operator new(size_t n)
1959 return new char[n];
1962 void glyph_node::operator delete(void *p)
1964 if (p) {
1965 ((glyph_node *)p)->next = free_list;
1966 free_list = (glyph_node *)p;
1970 void ligature_node::operator delete(void *p)
1972 delete[] (char *)p;
1975 glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc,
1976 statem *s, int pop, node *x)
1977 : charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc)
1979 #ifdef STORE_WIDTH
1980 wid = tf->get_width(ci);
1981 #endif
1984 #ifdef STORE_WIDTH
1985 glyph_node::glyph_node(charinfo *c, tfont *t,
1986 color *gc, color *fc, hunits w,
1987 statem *s, int pop, node *x)
1988 : charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc), wid(w)
1991 #endif
1993 node *glyph_node::copy()
1995 #ifdef STORE_WIDTH
1996 return new glyph_node(ci, tf, gcol, fcol, wid, state, div_nest_level);
1997 #else
1998 return new glyph_node(ci, tf, gcol, fcol, state, div_nest_level);
1999 #endif
2002 node *glyph_node::merge_self(node *nd)
2004 return nd->merge_glyph_node(this);
2007 int glyph_node::character_type()
2009 return tf->get_character_type(ci);
2012 node *glyph_node::add_self(node *n, hyphen_list **p)
2014 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
2015 next = 0;
2016 node *nn;
2017 if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
2018 next = n;
2019 nn = this;
2021 if ((*p)->hyphen)
2022 nn = nn->add_discretionary_hyphen();
2023 hyphen_list *pp = *p;
2024 *p = (*p)->next;
2025 delete pp;
2026 return nn;
2029 units glyph_node::size()
2031 return tf->get_size().to_units();
2034 hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count)
2036 (*count)++;
2037 return new hyphen_list(ci->get_hyphenation_code(), tail);
2040 tfont *node::get_tfont()
2042 return 0;
2045 tfont *glyph_node::get_tfont()
2047 return tf;
2050 color *node::get_glyph_color()
2052 return 0;
2055 color *glyph_node::get_glyph_color()
2057 return gcol;
2060 color *node::get_fill_color()
2062 return 0;
2065 color *glyph_node::get_fill_color()
2067 return fcol;
2070 node *node::merge_glyph_node(glyph_node *)
2072 return 0;
2075 node *glyph_node::merge_glyph_node(glyph_node *gn)
2077 if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
2078 charinfo *lig;
2079 if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
2080 node *next1 = next;
2081 next = 0;
2082 return new ligature_node(lig, tf, gcol, fcol, this, gn, state,
2083 gn->div_nest_level, next1);
2085 hunits kern;
2086 if (tf->get_kern(ci, gn->ci, &kern)) {
2087 node *next1 = next;
2088 next = 0;
2089 return new kern_pair_node(kern, this, gn, state,
2090 gn->div_nest_level, next1);
2093 return 0;
2096 #ifdef STORE_WIDTH
2097 inline
2098 #endif
2099 hunits glyph_node::width()
2101 #ifdef STORE_WIDTH
2102 return wid;
2103 #else
2104 return tf->get_width(ci);
2105 #endif
2108 node *glyph_node::last_char_node()
2110 return this;
2113 void glyph_node::vertical_extent(vunits *min, vunits *max)
2115 *min = -tf->get_char_height(ci);
2116 *max = tf->get_char_depth(ci);
2119 hunits glyph_node::skew()
2121 return tf->get_char_skew(ci);
2124 hunits glyph_node::subscript_correction()
2126 return tf->get_subscript_correction(ci);
2129 hunits glyph_node::italic_correction()
2131 return tf->get_italic_correction(ci);
2134 hunits glyph_node::left_italic_correction()
2136 return tf->get_left_italic_correction(ci);
2139 hyphenation_type glyph_node::get_hyphenation_type()
2141 return HYPHEN_MIDDLE;
2144 void glyph_node::ascii_print(ascii_output_file *ascii)
2146 unsigned char c = ci->get_ascii_code();
2147 if (c != 0)
2148 ascii->outc(c);
2149 else
2150 ascii->outs(ci->nm.contents());
2153 void glyph_node::debug_node()
2155 unsigned char c = ci->get_ascii_code();
2156 fprintf(stderr, "{ %s [", type());
2157 if (c)
2158 fprintf(stderr, "%c", c);
2159 else
2160 fprintf(stderr, ci->nm.contents());
2161 if (push_state)
2162 fprintf(stderr, " <push_state>");
2163 if (state)
2164 state->display_state();
2165 fprintf(stderr, " nest level %d", div_nest_level);
2166 fprintf(stderr, "]}\n");
2167 fflush(stderr);
2170 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2171 node *gn1, node *gn2, statem *s,
2172 int pop, node *x)
2173 : glyph_node(c, t, gc, fc, s, pop, x), n1(gn1), n2(gn2)
2177 #ifdef STORE_WIDTH
2178 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2179 hunits w, node *gn1, node *gn2, statem *s,
2180 int pop, node *x)
2181 : glyph_node(c, t, gc, fc, w, s, pop, x), n1(gn1), n2(gn2)
2184 #endif
2186 ligature_node::~ligature_node()
2188 delete n1;
2189 delete n2;
2192 node *ligature_node::copy()
2194 #ifdef STORE_WIDTH
2195 return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy(),
2196 state, div_nest_level);
2197 #else
2198 return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy(),
2199 state, div_nest_level);
2200 #endif
2203 void ligature_node::ascii_print(ascii_output_file *ascii)
2205 n1->ascii_print(ascii);
2206 n2->ascii_print(ascii);
2209 hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail, int *count)
2211 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2212 return n1->get_hyphen_list(hl, count);
2215 node *ligature_node::add_self(node *n, hyphen_list **p)
2217 n = n1->add_self(n, p);
2218 n = n2->add_self(n, p);
2219 n1 = n2 = 0;
2220 delete this;
2221 return n;
2224 kern_pair_node::kern_pair_node(hunits n, node *first, node *second,
2225 statem* s, int pop, node *x)
2226 : node(x, s, pop), amount(n), n1(first), n2(second)
2230 dbreak_node::dbreak_node(node *n, node *p, statem *s, int pop, node *x)
2231 : node(x, s, pop), none(n), pre(p), post(0)
2235 node *dbreak_node::merge_glyph_node(glyph_node *gn)
2237 glyph_node *gn2 = (glyph_node *)gn->copy();
2238 node *new_none = none ? none->merge_glyph_node(gn) : 0;
2239 node *new_post = post ? post->merge_glyph_node(gn2) : 0;
2240 if (new_none == 0 && new_post == 0) {
2241 delete gn2;
2242 return 0;
2244 if (new_none != 0)
2245 none = new_none;
2246 else {
2247 gn->next = none;
2248 none = gn;
2250 if (new_post != 0)
2251 post = new_post;
2252 else {
2253 gn2->next = post;
2254 post = gn2;
2256 return this;
2259 node *kern_pair_node::merge_glyph_node(glyph_node *gn)
2261 node *nd = n2->merge_glyph_node(gn);
2262 if (nd == 0)
2263 return 0;
2264 n2 = nd;
2265 nd = n2->merge_self(n1);
2266 if (nd) {
2267 nd->next = next;
2268 n1 = 0;
2269 n2 = 0;
2270 delete this;
2271 return nd;
2273 return this;
2276 hunits kern_pair_node::italic_correction()
2278 return n2->italic_correction();
2281 hunits kern_pair_node::subscript_correction()
2283 return n2->subscript_correction();
2286 void kern_pair_node::vertical_extent(vunits *min, vunits *max)
2288 n1->vertical_extent(min, max);
2289 vunits min2, max2;
2290 n2->vertical_extent(&min2, &max2);
2291 if (min2 < *min)
2292 *min = min2;
2293 if (max2 > *max)
2294 *max = max2;
2297 node *kern_pair_node::add_discretionary_hyphen()
2299 tfont *tf = n2->get_tfont();
2300 if (tf) {
2301 if (tf->contains(soft_hyphen_char)) {
2302 color *gcol = n2->get_glyph_color();
2303 color *fcol = n2->get_fill_color();
2304 node *next1 = next;
2305 next = 0;
2306 node *n = copy();
2307 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2308 state, div_nest_level);
2309 node *nn = n->merge_glyph_node(gn);
2310 if (nn == 0) {
2311 gn->next = n;
2312 nn = gn;
2314 return new dbreak_node(this, nn, state, div_nest_level, next1);
2317 return this;
2320 kern_pair_node::~kern_pair_node()
2322 if (n1 != 0)
2323 delete n1;
2324 if (n2 != 0)
2325 delete n2;
2328 dbreak_node::~dbreak_node()
2330 delete_node_list(pre);
2331 delete_node_list(post);
2332 delete_node_list(none);
2335 node *kern_pair_node::copy()
2337 return new kern_pair_node(amount, n1->copy(), n2->copy(), state,
2338 div_nest_level);
2341 node *copy_node_list(node *n)
2343 node *p = 0;
2344 while (n != 0) {
2345 node *nn = n->copy();
2346 nn->next = p;
2347 p = nn;
2348 n = n->next;
2350 while (p != 0) {
2351 node *pp = p->next;
2352 p->next = n;
2353 n = p;
2354 p = pp;
2356 return n;
2359 void delete_node_list(node *n)
2361 while (n != 0) {
2362 node *tem = n;
2363 n = n->next;
2364 delete tem;
2368 node *dbreak_node::copy()
2370 dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre),
2371 state, div_nest_level);
2372 p->post = copy_node_list(post);
2373 return p;
2376 hyphen_list *node::get_hyphen_list(hyphen_list *tail, int *)
2378 return tail;
2381 hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail, int *count)
2383 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2384 return n1->get_hyphen_list(hl, count);
2387 class hyphen_inhibitor_node : public node {
2388 public:
2389 hyphen_inhibitor_node(node * = 0);
2390 node *copy();
2391 int same(node *);
2392 const char *type();
2393 int force_tprint();
2394 int is_tag();
2395 hyphenation_type get_hyphenation_type();
2398 hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
2402 node *hyphen_inhibitor_node::copy()
2404 return new hyphen_inhibitor_node;
2407 int hyphen_inhibitor_node::same(node *)
2409 return 1;
2412 const char *hyphen_inhibitor_node::type()
2414 return "hyphen_inhibitor_node";
2417 int hyphen_inhibitor_node::force_tprint()
2419 return 0;
2422 int hyphen_inhibitor_node::is_tag()
2424 return 0;
2427 hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
2429 return HYPHEN_INHIBIT;
2432 /* add_discretionary_hyphen methods */
2434 node *dbreak_node::add_discretionary_hyphen()
2436 if (post)
2437 post = post->add_discretionary_hyphen();
2438 if (none)
2439 none = none->add_discretionary_hyphen();
2440 return this;
2443 node *node::add_discretionary_hyphen()
2445 tfont *tf = get_tfont();
2446 if (!tf)
2447 return new hyphen_inhibitor_node(this);
2448 if (tf->contains(soft_hyphen_char)) {
2449 color *gcol = get_glyph_color();
2450 color *fcol = get_fill_color();
2451 node *next1 = next;
2452 next = 0;
2453 node *n = copy();
2454 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2455 state, div_nest_level);
2456 node *n1 = n->merge_glyph_node(gn);
2457 if (n1 == 0) {
2458 gn->next = n;
2459 n1 = gn;
2461 return new dbreak_node(this, n1, state, div_nest_level, next1);
2463 return this;
2466 node *node::merge_self(node *)
2468 return 0;
2471 node *node::add_self(node *n, hyphen_list ** /*p*/)
2473 next = n;
2474 return this;
2477 node *kern_pair_node::add_self(node *n, hyphen_list **p)
2479 n = n1->add_self(n, p);
2480 n = n2->add_self(n, p);
2481 n1 = n2 = 0;
2482 delete this;
2483 return n;
2486 hunits node::width()
2488 return H0;
2491 node *node::last_char_node()
2493 return 0;
2496 int node::force_tprint()
2498 return 0;
2501 int node::is_tag()
2503 return 0;
2506 hunits hmotion_node::width()
2508 return n;
2511 units node::size()
2513 return points_to_units(10);
2516 void node::debug_node()
2518 fprintf(stderr, "{ %s ", type());
2519 if (push_state)
2520 fprintf(stderr, " <push_state>");
2521 if (state)
2522 fprintf(stderr, " <state>");
2523 fprintf(stderr, " nest level %d", div_nest_level);
2524 fprintf(stderr, " }\n");
2525 fflush(stderr);
2528 void node::debug_node_list()
2530 node *n = next;
2532 debug_node();
2533 while (n != 0) {
2534 n->debug_node();
2535 n = n->next;
2539 hunits kern_pair_node::width()
2541 return n1->width() + n2->width() + amount;
2544 node *kern_pair_node::last_char_node()
2546 node *nd = n2->last_char_node();
2547 if (nd)
2548 return nd;
2549 return n1->last_char_node();
2552 hunits dbreak_node::width()
2554 hunits x = H0;
2555 for (node *n = none; n != 0; n = n->next)
2556 x += n->width();
2557 return x;
2560 node *dbreak_node::last_char_node()
2562 for (node *n = none; n; n = n->next) {
2563 node *last_node = n->last_char_node();
2564 if (last_node)
2565 return last_node;
2567 return 0;
2570 hunits dbreak_node::italic_correction()
2572 return none ? none->italic_correction() : H0;
2575 hunits dbreak_node::subscript_correction()
2577 return none ? none->subscript_correction() : H0;
2580 class italic_corrected_node : public node {
2581 node *n;
2582 hunits x;
2583 public:
2584 italic_corrected_node(node *, hunits, statem *, int, node * = 0);
2585 ~italic_corrected_node();
2586 node *copy();
2587 void ascii_print(ascii_output_file *);
2588 void asciify(macro *);
2589 hunits width();
2590 node *last_char_node();
2591 void vertical_extent(vunits *, vunits *);
2592 int ends_sentence();
2593 int overlaps_horizontally();
2594 int overlaps_vertically();
2595 int same(node *);
2596 hyphenation_type get_hyphenation_type();
2597 tfont *get_tfont();
2598 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2599 int character_type();
2600 void tprint(troff_output_file *);
2601 hunits subscript_correction();
2602 hunits skew();
2603 node *add_self(node *, hyphen_list **);
2604 const char *type();
2605 int force_tprint();
2606 int is_tag();
2609 node *node::add_italic_correction(hunits *wd)
2611 hunits ic = italic_correction();
2612 if (ic.is_zero())
2613 return this;
2614 else {
2615 node *next1 = next;
2616 next = 0;
2617 *wd += ic;
2618 return new italic_corrected_node(this, ic, state, div_nest_level, next1);
2622 italic_corrected_node::italic_corrected_node(node *nn, hunits xx, statem *s,
2623 int pop, node *p)
2624 : node(p, s, pop), n(nn), x(xx)
2626 assert(n != 0);
2629 italic_corrected_node::~italic_corrected_node()
2631 delete n;
2634 node *italic_corrected_node::copy()
2636 return new italic_corrected_node(n->copy(), x, state, div_nest_level);
2639 hunits italic_corrected_node::width()
2641 return n->width() + x;
2644 void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
2646 n->vertical_extent(min, max);
2649 void italic_corrected_node::tprint(troff_output_file *out)
2651 n->tprint(out);
2652 out->right(x);
2655 hunits italic_corrected_node::skew()
2657 return n->skew() - x/2;
2660 hunits italic_corrected_node::subscript_correction()
2662 return n->subscript_correction() - x;
2665 void italic_corrected_node::ascii_print(ascii_output_file *out)
2667 n->ascii_print(out);
2670 int italic_corrected_node::ends_sentence()
2672 return n->ends_sentence();
2675 int italic_corrected_node::overlaps_horizontally()
2677 return n->overlaps_horizontally();
2680 int italic_corrected_node::overlaps_vertically()
2682 return n->overlaps_vertically();
2685 node *italic_corrected_node::last_char_node()
2687 return n->last_char_node();
2690 tfont *italic_corrected_node::get_tfont()
2692 return n->get_tfont();
2695 hyphenation_type italic_corrected_node::get_hyphenation_type()
2697 return n->get_hyphenation_type();
2700 node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
2702 nd = n->add_self(nd, p);
2703 hunits not_interested;
2704 nd = nd->add_italic_correction(&not_interested);
2705 n = 0;
2706 delete this;
2707 return nd;
2710 hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail,
2711 int *count)
2713 return n->get_hyphen_list(tail, count);
2716 int italic_corrected_node::character_type()
2718 return n->character_type();
2721 class break_char_node : public node {
2722 node *ch;
2723 char break_code;
2724 color *col;
2725 public:
2726 break_char_node(node *, int, color *, node * = 0);
2727 break_char_node(node *, int, color *, statem *, int, node * = 0);
2728 ~break_char_node();
2729 node *copy();
2730 hunits width();
2731 vunits vertical_width();
2732 node *last_char_node();
2733 int character_type();
2734 int ends_sentence();
2735 node *add_self(node *, hyphen_list **);
2736 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2737 void tprint(troff_output_file *);
2738 void zero_width_tprint(troff_output_file *);
2739 void ascii_print(ascii_output_file *);
2740 void asciify(macro *);
2741 hyphenation_type get_hyphenation_type();
2742 int overlaps_vertically();
2743 int overlaps_horizontally();
2744 units size();
2745 tfont *get_tfont();
2746 int same(node *);
2747 const char *type();
2748 int force_tprint();
2749 int is_tag();
2752 break_char_node::break_char_node(node *n, int bc, color *c, node *x)
2753 : node(x), ch(n), break_code(bc), col(c)
2757 break_char_node::break_char_node(node *n, int bc, color *c, statem *s,
2758 int pop, node *x)
2759 : node(x, s, pop), ch(n), break_code(bc), col(c)
2763 break_char_node::~break_char_node()
2765 delete ch;
2768 node *break_char_node::copy()
2770 return new break_char_node(ch->copy(), break_code, col, state,
2771 div_nest_level);
2774 hunits break_char_node::width()
2776 return ch->width();
2779 vunits break_char_node::vertical_width()
2781 return ch->vertical_width();
2784 node *break_char_node::last_char_node()
2786 return ch->last_char_node();
2789 int break_char_node::character_type()
2791 return ch->character_type();
2794 int break_char_node::ends_sentence()
2796 return ch->ends_sentence();
2799 node *break_char_node::add_self(node *n, hyphen_list **p)
2801 assert((*p)->hyphenation_code == 0);
2802 if ((*p)->breakable && (break_code & 1)) {
2803 n = new space_node(H0, col, n);
2804 n->freeze_space();
2806 next = n;
2807 n = this;
2808 if ((*p)->breakable && (break_code & 2)) {
2809 n = new space_node(H0, col, n);
2810 n->freeze_space();
2812 hyphen_list *pp = *p;
2813 *p = (*p)->next;
2814 delete pp;
2815 return n;
2818 hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail, int *)
2820 return new hyphen_list(0, tail);
2823 hyphenation_type break_char_node::get_hyphenation_type()
2825 return HYPHEN_MIDDLE;
2828 void break_char_node::ascii_print(ascii_output_file *ascii)
2830 ch->ascii_print(ascii);
2833 int break_char_node::overlaps_vertically()
2835 return ch->overlaps_vertically();
2838 int break_char_node::overlaps_horizontally()
2840 return ch->overlaps_horizontally();
2843 units break_char_node::size()
2845 return ch->size();
2848 tfont *break_char_node::get_tfont()
2850 return ch->get_tfont();
2853 node *extra_size_node::copy()
2855 return new extra_size_node(n, state, div_nest_level);
2858 extra_size_node::extra_size_node(vunits i, statem *s, int pop)
2859 : node(0, s, pop), n(i)
2863 extra_size_node::extra_size_node(vunits i)
2864 : n(i)
2868 node *vertical_size_node::copy()
2870 return new vertical_size_node(n, state, div_nest_level);
2873 vertical_size_node::vertical_size_node(vunits i, statem *s, int pop)
2874 : node(0, s, pop), n(i)
2878 vertical_size_node::vertical_size_node(vunits i)
2879 : n(i)
2883 node *hmotion_node::copy()
2885 return new hmotion_node(n, was_tab, unformat, col, state, div_nest_level);
2888 node *space_char_hmotion_node::copy()
2890 return new space_char_hmotion_node(n, col, state, div_nest_level);
2893 vmotion_node::vmotion_node(vunits i, color *c)
2894 : n(i), col(c)
2898 vmotion_node::vmotion_node(vunits i, color *c, statem *s, int pop)
2899 : node(0, s, pop), n(i), col(c)
2903 node *vmotion_node::copy()
2905 return new vmotion_node(n, col, state, div_nest_level);
2908 node *dummy_node::copy()
2910 return new dummy_node;
2913 node *transparent_dummy_node::copy()
2915 return new transparent_dummy_node;
2918 hline_node::~hline_node()
2920 if (n)
2921 delete n;
2924 hline_node::hline_node(hunits i, node *c, node *nxt)
2925 : node(nxt), x(i), n(c)
2929 hline_node::hline_node(hunits i, node *c, statem *s, int pop, node *nxt)
2930 : node(nxt, s, pop), x(i), n(c)
2934 node *hline_node::copy()
2936 return new hline_node(x, n ? n->copy() : 0, state, div_nest_level);
2939 hunits hline_node::width()
2941 return x < H0 ? H0 : x;
2944 vline_node::vline_node(vunits i, node *c, node *nxt)
2945 : node(nxt), x(i), n(c)
2949 vline_node::vline_node(vunits i, node *c, statem *s, int pop, node *nxt)
2950 : node(nxt, s, pop), x(i), n(c)
2954 vline_node::~vline_node()
2956 if (n)
2957 delete n;
2960 node *vline_node::copy()
2962 return new vline_node(x, n ? n->copy() : 0, state, div_nest_level);
2965 hunits vline_node::width()
2967 return n == 0 ? H0 : n->width();
2970 zero_width_node::zero_width_node(node *nd, statem *s, int pop)
2971 : node(0, s, pop), n(nd)
2975 zero_width_node::zero_width_node(node *nd)
2976 : n(nd)
2980 zero_width_node::~zero_width_node()
2982 delete_node_list(n);
2985 node *zero_width_node::copy()
2987 return new zero_width_node(copy_node_list(n), state, div_nest_level);
2990 int node_list_character_type(node *p)
2992 int t = 0;
2993 for (; p; p = p->next)
2994 t |= p->character_type();
2995 return t;
2998 int zero_width_node::character_type()
3000 return node_list_character_type(n);
3003 void node_list_vertical_extent(node *p, vunits *min, vunits *max)
3005 *min = V0;
3006 *max = V0;
3007 vunits cur_vpos = V0;
3008 vunits v1, v2;
3009 for (; p; p = p->next) {
3010 p->vertical_extent(&v1, &v2);
3011 v1 += cur_vpos;
3012 if (v1 < *min)
3013 *min = v1;
3014 v2 += cur_vpos;
3015 if (v2 > *max)
3016 *max = v2;
3017 cur_vpos += p->vertical_width();
3021 void zero_width_node::vertical_extent(vunits *min, vunits *max)
3023 node_list_vertical_extent(n, min, max);
3026 overstrike_node::overstrike_node()
3027 : list(0), max_width(H0)
3031 overstrike_node::overstrike_node(statem *s, int pop)
3032 : node(0, s, pop), list(0), max_width(H0)
3036 overstrike_node::~overstrike_node()
3038 delete_node_list(list);
3041 node *overstrike_node::copy()
3043 overstrike_node *on = new overstrike_node(state, div_nest_level);
3044 for (node *tem = list; tem; tem = tem->next)
3045 on->overstrike(tem->copy());
3046 return on;
3049 void overstrike_node::overstrike(node *n)
3051 if (n == 0)
3052 return;
3053 hunits w = n->width();
3054 if (w > max_width)
3055 max_width = w;
3056 node **p;
3057 for (p = &list; *p; p = &(*p)->next)
3059 n->next = 0;
3060 *p = n;
3063 hunits overstrike_node::width()
3065 return max_width;
3068 bracket_node::bracket_node()
3069 : list(0), max_width(H0)
3073 bracket_node::bracket_node(statem *s, int pop)
3074 : node(0, s, pop), list(0), max_width(H0)
3078 bracket_node::~bracket_node()
3080 delete_node_list(list);
3083 node *bracket_node::copy()
3085 bracket_node *on = new bracket_node(state, div_nest_level);
3086 node *last_node = 0;
3087 node *tem;
3088 if (list)
3089 list->last = 0;
3090 for (tem = list; tem; tem = tem->next) {
3091 if (tem->next)
3092 tem->next->last = tem;
3093 last_node = tem;
3095 for (tem = last_node; tem; tem = tem->last)
3096 on->bracket(tem->copy());
3097 return on;
3100 void bracket_node::bracket(node *n)
3102 if (n == 0)
3103 return;
3104 hunits w = n->width();
3105 if (w > max_width)
3106 max_width = w;
3107 n->next = list;
3108 list = n;
3111 hunits bracket_node::width()
3113 return max_width;
3116 int node::nspaces()
3118 return 0;
3121 int node::merge_space(hunits, hunits, hunits)
3123 return 0;
3126 #if 0
3127 space_node *space_node::free_list = 0;
3129 void *space_node::operator new(size_t n)
3131 assert(n == sizeof(space_node));
3132 if (!free_list) {
3133 free_list = (space_node *)new char[sizeof(space_node)*BLOCK];
3134 for (int i = 0; i < BLOCK - 1; i++)
3135 free_list[i].next = free_list + i + 1;
3136 free_list[BLOCK-1].next = 0;
3138 space_node *p = free_list;
3139 free_list = (space_node *)(free_list->next);
3140 p->next = 0;
3141 return p;
3144 inline void space_node::operator delete(void *p)
3146 if (p) {
3147 ((space_node *)p)->next = free_list;
3148 free_list = (space_node *)p;
3151 #endif
3153 space_node::space_node(hunits nn, color *c, node *p)
3154 : node(p, 0, 0), n(nn), set(0), was_escape_colon(0), col(c)
3158 space_node::space_node(hunits nn, color *c, statem *s, int pop, node *p)
3159 : node(p, s, pop), n(nn), set(0), was_escape_colon(0), col(c)
3163 space_node::space_node(hunits nn, int s, int flag, color *c, statem *st,
3164 int pop, node *p)
3165 : node(p, st, pop), n(nn), set(s), was_escape_colon(flag), col(c)
3169 #if 0
3170 space_node::~space_node()
3173 #endif
3175 node *space_node::copy()
3177 return new space_node(n, set, was_escape_colon, col, state, div_nest_level);
3180 int space_node::force_tprint()
3182 return 0;
3185 int space_node::is_tag()
3187 return 0;
3190 int space_node::nspaces()
3192 return set ? 0 : 1;
3195 int space_node::merge_space(hunits h, hunits, hunits)
3197 n += h;
3198 return 1;
3201 hunits space_node::width()
3203 return n;
3206 void node::spread_space(int*, hunits*)
3210 void space_node::spread_space(int *n_spaces, hunits *desired_space)
3212 if (!set) {
3213 assert(*n_spaces > 0);
3214 if (*n_spaces == 1) {
3215 n += *desired_space;
3216 *desired_space = H0;
3218 else {
3219 hunits extra = *desired_space / *n_spaces;
3220 *desired_space -= extra;
3221 n += extra;
3223 *n_spaces -= 1;
3224 set = 1;
3228 void node::freeze_space()
3232 void space_node::freeze_space()
3234 set = 1;
3237 void node::is_escape_colon()
3241 void space_node::is_escape_colon()
3243 was_escape_colon = 1;
3246 diverted_space_node::diverted_space_node(vunits d, statem *s, int pop,
3247 node *p)
3248 : node(p, s, pop), n(d)
3252 diverted_space_node::diverted_space_node(vunits d, node *p)
3253 : node(p), n(d)
3257 node *diverted_space_node::copy()
3259 return new diverted_space_node(n, state, div_nest_level);
3262 diverted_copy_file_node::diverted_copy_file_node(symbol s, statem *st,
3263 int pop, node *p)
3264 : node(p, st, pop), filename(s)
3268 diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
3269 : node(p), filename(s)
3273 node *diverted_copy_file_node::copy()
3275 return new diverted_copy_file_node(filename, state, div_nest_level);
3278 int node::ends_sentence()
3280 return 0;
3283 int kern_pair_node::ends_sentence()
3285 switch (n2->ends_sentence()) {
3286 case 0:
3287 return 0;
3288 case 1:
3289 return 1;
3290 case 2:
3291 break;
3292 default:
3293 assert(0);
3295 return n1->ends_sentence();
3298 int node_list_ends_sentence(node *n)
3300 for (; n != 0; n = n->next)
3301 switch (n->ends_sentence()) {
3302 case 0:
3303 return 0;
3304 case 1:
3305 return 1;
3306 case 2:
3307 break;
3308 default:
3309 assert(0);
3311 return 2;
3314 int dbreak_node::ends_sentence()
3316 return node_list_ends_sentence(none);
3319 int node::overlaps_horizontally()
3321 return 0;
3324 int node::overlaps_vertically()
3326 return 0;
3329 int node::discardable()
3331 return 0;
3334 int space_node::discardable()
3336 return set ? 0 : 1;
3339 vunits node::vertical_width()
3341 return V0;
3344 vunits vline_node::vertical_width()
3346 return x;
3349 vunits vmotion_node::vertical_width()
3351 return n;
3354 int node::set_unformat_flag()
3356 return 1;
3359 int node::character_type()
3361 return 0;
3364 hunits node::subscript_correction()
3366 return H0;
3369 hunits node::italic_correction()
3371 return H0;
3374 hunits node::left_italic_correction()
3376 return H0;
3379 hunits node::skew()
3381 return H0;
3384 /* vertical_extent methods */
3386 void node::vertical_extent(vunits *min, vunits *max)
3388 vunits v = vertical_width();
3389 if (v < V0) {
3390 *min = v;
3391 *max = V0;
3393 else {
3394 *max = v;
3395 *min = V0;
3399 void vline_node::vertical_extent(vunits *min, vunits *max)
3401 if (n == 0)
3402 node::vertical_extent(min, max);
3403 else {
3404 vunits cmin, cmax;
3405 n->vertical_extent(&cmin, &cmax);
3406 vunits h = n->size();
3407 if (x < V0) {
3408 if (-x < h) {
3409 *min = x;
3410 *max = V0;
3412 else {
3413 // we print the first character and then move up, so
3414 *max = cmax;
3415 // we print the last character and then move up h
3416 *min = cmin + h;
3417 if (*min > V0)
3418 *min = V0;
3419 *min += x;
3422 else {
3423 if (x < h) {
3424 *max = x;
3425 *min = V0;
3427 else {
3428 // we move down by h and then print the first character, so
3429 *min = cmin + h;
3430 if (*min > V0)
3431 *min = V0;
3432 *max = x + cmax;
3438 /* ascii_print methods */
3440 static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n)
3442 if (n == 0)
3443 return;
3444 ascii_print_reverse_node_list(ascii, n->next);
3445 n->ascii_print(ascii);
3448 void dbreak_node::ascii_print(ascii_output_file *ascii)
3450 ascii_print_reverse_node_list(ascii, none);
3453 void kern_pair_node::ascii_print(ascii_output_file *ascii)
3455 n1->ascii_print(ascii);
3456 n2->ascii_print(ascii);
3459 void node::ascii_print(ascii_output_file *)
3463 void space_node::ascii_print(ascii_output_file *ascii)
3465 if (!n.is_zero())
3466 ascii->outc(' ');
3469 void hmotion_node::ascii_print(ascii_output_file *ascii)
3471 // this is pretty arbitrary
3472 if (n >= points_to_units(2))
3473 ascii->outc(' ');
3476 void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
3478 ascii->outc(' ');
3481 /* asciify methods */
3483 void node::asciify(macro *m)
3485 m->append(this);
3488 void glyph_node::asciify(macro *m)
3490 unsigned char c = ci->get_asciify_code();
3491 if (c == 0)
3492 c = ci->get_ascii_code();
3493 if (c != 0) {
3494 m->append(c);
3495 delete this;
3497 else
3498 m->append(this);
3501 void kern_pair_node::asciify(macro *m)
3503 n1->asciify(m);
3504 n2->asciify(m);
3505 n1 = n2 = 0;
3506 delete this;
3509 static void asciify_reverse_node_list(macro *m, node *n)
3511 if (n == 0)
3512 return;
3513 asciify_reverse_node_list(m, n->next);
3514 n->asciify(m);
3517 void dbreak_node::asciify(macro *m)
3519 asciify_reverse_node_list(m, none);
3520 none = 0;
3521 delete this;
3524 void ligature_node::asciify(macro *m)
3526 n1->asciify(m);
3527 n2->asciify(m);
3528 n1 = n2 = 0;
3529 delete this;
3532 void break_char_node::asciify(macro *m)
3534 ch->asciify(m);
3535 ch = 0;
3536 delete this;
3539 void italic_corrected_node::asciify(macro *m)
3541 n->asciify(m);
3542 n = 0;
3543 delete this;
3546 void left_italic_corrected_node::asciify(macro *m)
3548 if (n) {
3549 n->asciify(m);
3550 n = 0;
3552 delete this;
3555 void hmotion_node::asciify(macro *m)
3557 if (was_tab) {
3558 m->append('\t');
3559 delete this;
3561 else
3562 m->append(this);
3565 space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3566 statem *s, int pop,
3567 node *nxt)
3568 : hmotion_node(i, c, s, pop, nxt)
3572 space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3573 node *nxt)
3574 : hmotion_node(i, c, 0, 0, nxt)
3578 void space_char_hmotion_node::asciify(macro *m)
3580 m->append(ESCAPE_SPACE);
3581 delete this;
3584 void space_node::asciify(macro *m)
3586 if (was_escape_colon) {
3587 m->append(ESCAPE_COLON);
3588 delete this;
3590 else
3591 m->append(this);
3594 void word_space_node::asciify(macro *m)
3596 for (width_list *w = orig_width; w; w = w->next)
3597 m->append(' ');
3598 delete this;
3601 void unbreakable_space_node::asciify(macro *m)
3603 m->append(ESCAPE_TILDE);
3604 delete this;
3607 void line_start_node::asciify(macro *)
3609 delete this;
3612 void vertical_size_node::asciify(macro *)
3614 delete this;
3617 breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/,
3618 breakpoint *rest, int /*is_inner*/)
3620 return rest;
3623 int node::nbreaks()
3625 return 0;
3628 breakpoint *space_node::get_breakpoints(hunits wd, int ns,
3629 breakpoint *rest, int is_inner)
3631 if (next && next->discardable())
3632 return rest;
3633 breakpoint *bp = new breakpoint;
3634 bp->next = rest;
3635 bp->width = wd;
3636 bp->nspaces = ns;
3637 bp->hyphenated = 0;
3638 if (is_inner) {
3639 assert(rest != 0);
3640 bp->index = rest->index + 1;
3641 bp->nd = rest->nd;
3643 else {
3644 bp->nd = this;
3645 bp->index = 0;
3647 return bp;
3650 int space_node::nbreaks()
3652 if (next && next->discardable())
3653 return 0;
3654 else
3655 return 1;
3658 static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
3659 int ns, breakpoint *rest)
3661 if (p != 0) {
3662 rest = p->get_breakpoints(*widthp,
3664 node_list_get_breakpoints(p->next, widthp, ns,
3665 rest),
3667 *widthp += p->width();
3669 return rest;
3672 breakpoint *dbreak_node::get_breakpoints(hunits wd, int ns,
3673 breakpoint *rest, int is_inner)
3675 breakpoint *bp = new breakpoint;
3676 bp->next = rest;
3677 bp->width = wd;
3678 for (node *tem = pre; tem != 0; tem = tem->next)
3679 bp->width += tem->width();
3680 bp->nspaces = ns;
3681 bp->hyphenated = 1;
3682 if (is_inner) {
3683 assert(rest != 0);
3684 bp->index = rest->index + 1;
3685 bp->nd = rest->nd;
3687 else {
3688 bp->nd = this;
3689 bp->index = 0;
3691 return node_list_get_breakpoints(none, &wd, ns, bp);
3694 int dbreak_node::nbreaks()
3696 int i = 1;
3697 for (node *tem = none; tem != 0; tem = tem->next)
3698 i += tem->nbreaks();
3699 return i;
3702 void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
3704 assert(0);
3707 void space_node::split(int where, node **pre, node **post)
3709 assert(where == 0);
3710 *pre = next;
3711 *post = 0;
3712 delete this;
3715 static void node_list_split(node *p, int *wherep, node **prep, node **postp)
3717 if (p == 0)
3718 return;
3719 int nb = p->nbreaks();
3720 node_list_split(p->next, wherep, prep, postp);
3721 if (*wherep < 0) {
3722 p->next = *postp;
3723 *postp = p;
3725 else if (*wherep < nb) {
3726 p->next = *prep;
3727 p->split(*wherep, prep, postp);
3729 else {
3730 p->next = *prep;
3731 *prep = p;
3733 *wherep -= nb;
3736 void dbreak_node::split(int where, node **prep, node **postp)
3738 assert(where >= 0);
3739 if (where == 0) {
3740 *postp = post;
3741 post = 0;
3742 if (pre == 0)
3743 *prep = next;
3744 else {
3745 node *tem;
3746 for (tem = pre; tem->next != 0; tem = tem->next)
3748 tem->next = next;
3749 *prep = pre;
3751 pre = 0;
3752 delete this;
3754 else {
3755 *prep = next;
3756 where -= 1;
3757 node_list_split(none, &where, prep, postp);
3758 none = 0;
3759 delete this;
3763 hyphenation_type node::get_hyphenation_type()
3765 return HYPHEN_BOUNDARY;
3768 hyphenation_type dbreak_node::get_hyphenation_type()
3770 return HYPHEN_INHIBIT;
3773 hyphenation_type kern_pair_node::get_hyphenation_type()
3775 return HYPHEN_MIDDLE;
3778 hyphenation_type dummy_node::get_hyphenation_type()
3780 return HYPHEN_MIDDLE;
3783 hyphenation_type transparent_dummy_node::get_hyphenation_type()
3785 return HYPHEN_MIDDLE;
3788 hyphenation_type hmotion_node::get_hyphenation_type()
3790 return HYPHEN_MIDDLE;
3793 hyphenation_type space_char_hmotion_node::get_hyphenation_type()
3795 return HYPHEN_MIDDLE;
3798 hyphenation_type overstrike_node::get_hyphenation_type()
3800 return HYPHEN_MIDDLE;
3803 hyphenation_type space_node::get_hyphenation_type()
3805 if (was_escape_colon)
3806 return HYPHEN_MIDDLE;
3807 return HYPHEN_BOUNDARY;
3810 hyphenation_type unbreakable_space_node::get_hyphenation_type()
3812 return HYPHEN_MIDDLE;
3815 int node::interpret(macro *)
3817 return 0;
3820 special_node::special_node(const macro &m, int n)
3821 : mac(m), no_init_string(n)
3823 font_size fs = curenv->get_font_size();
3824 int char_height = curenv->get_char_height();
3825 int char_slant = curenv->get_char_slant();
3826 int fontno = env_definite_font(curenv);
3827 tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fontno);
3828 if (curenv->is_composite())
3829 tf = tf->get_plain();
3830 gcol = curenv->get_glyph_color();
3831 fcol = curenv->get_fill_color();
3832 is_special = 1;
3835 special_node::special_node(const macro &m, tfont *t,
3836 color *gc, color *fc,
3837 statem *s, int pop,
3838 int n)
3839 : node(0, s, pop), mac(m), tf(t), gcol(gc), fcol(fc), no_init_string(n)
3841 is_special = 1;
3844 int special_node::same(node *n)
3846 return mac == ((special_node *)n)->mac
3847 && tf == ((special_node *)n)->tf
3848 && gcol == ((special_node *)n)->gcol
3849 && fcol == ((special_node *)n)->fcol
3850 && no_init_string == ((special_node *)n)->no_init_string;
3853 const char *special_node::type()
3855 return "special_node";
3858 int special_node::ends_sentence()
3860 return 2;
3863 int special_node::force_tprint()
3865 return 0;
3868 int special_node::is_tag()
3870 return 0;
3873 node *special_node::copy()
3875 return new special_node(mac, tf, gcol, fcol, state, div_nest_level,
3876 no_init_string);
3879 void special_node::tprint_start(troff_output_file *out)
3881 out->start_special(tf, gcol, fcol, no_init_string);
3884 void special_node::tprint_char(troff_output_file *out, unsigned char c)
3886 out->special_char(c);
3889 void special_node::tprint_end(troff_output_file *out)
3891 out->end_special();
3894 tfont *special_node::get_tfont()
3896 return tf;
3899 /* suppress_node */
3901 suppress_node::suppress_node(int on_or_off, int issue_limits)
3902 : is_on(on_or_off), emit_limits(issue_limits), filename(0), position(0),
3903 image_id(0)
3907 suppress_node::suppress_node(symbol f, char p, int id)
3908 : is_on(2), emit_limits(0), filename(f), position(p), image_id(id)
3910 is_special = 1;
3913 suppress_node::suppress_node(int issue_limits, int on_or_off,
3914 symbol f, char p, int id,
3915 statem *s, int pop)
3916 : node(0, s, pop), is_on(on_or_off), emit_limits(issue_limits), filename(f),
3917 position(p), image_id(id)
3921 int suppress_node::same(node *n)
3923 return ((is_on == ((suppress_node *)n)->is_on)
3924 && (emit_limits == ((suppress_node *)n)->emit_limits)
3925 && (filename == ((suppress_node *)n)->filename)
3926 && (position == ((suppress_node *)n)->position)
3927 && (image_id == ((suppress_node *)n)->image_id));
3930 const char *suppress_node::type()
3932 return "suppress_node";
3935 node *suppress_node::copy()
3937 return new suppress_node(emit_limits, is_on, filename, position, image_id,
3938 state, div_nest_level);
3941 /* tag_node */
3943 tag_node::tag_node()
3944 : delayed(0)
3946 is_special = 1;
3949 tag_node::tag_node(string s, int delay)
3950 : tag_string(s), delayed(delay)
3952 is_special = !delay;
3955 tag_node::tag_node(string s, statem *st, int pop, int delay)
3956 : node(0, st, pop), tag_string(s), delayed(delay)
3958 is_special = !delay;
3961 node *tag_node::copy()
3963 return new tag_node(tag_string, state, div_nest_level, delayed);
3966 void tag_node::tprint(troff_output_file *out)
3968 if (delayed)
3969 out->add_to_tag_list(tag_string);
3970 else
3971 out->state.add_tag(out->fp, tag_string);
3974 int tag_node::same(node *nd)
3976 return tag_string == ((tag_node *)nd)->tag_string
3977 && delayed == ((tag_node *)nd)->delayed;
3980 const char *tag_node::type()
3982 return "tag_node";
3985 int tag_node::force_tprint()
3987 return !delayed;
3990 int tag_node::is_tag()
3992 return !delayed;
3995 int tag_node::ends_sentence()
3997 return 2;
4000 int get_reg_int(const char *p)
4002 reg *r = (reg *)number_reg_dictionary.lookup(p);
4003 units prev_value;
4004 if (r && (r->get_value(&prev_value)))
4005 return (int)prev_value;
4006 else
4007 warning(WARN_REG, "number register `%1' not defined", p);
4008 return 0;
4011 const char *get_reg_str(const char *p)
4013 reg *r = (reg *)number_reg_dictionary.lookup(p);
4014 if (r)
4015 return r->get_string();
4016 else
4017 warning(WARN_REG, "register `%1' not defined", p);
4018 return 0;
4021 void suppress_node::put(troff_output_file *out, const char *s)
4023 int i = 0;
4024 while (s[i] != (char)0) {
4025 out->special_char(s[i]);
4026 i++;
4031 * We need to remember the start of the image and its name.
4034 static char last_position = 0;
4035 static const char *last_image_filename = 0;
4036 static int last_image_id = 0;
4038 inline int min(int a, int b)
4040 return a < b ? a : b;
4044 * tprint - if (is_on == 2)
4045 * remember current position (l, r, c, i) and filename
4046 * else
4047 * if (emit_limits)
4048 * if (html)
4049 * emit image tag
4050 * else
4051 * emit postscript bounds for image
4052 * else
4053 * if (suppress boolean differs from current state)
4054 * alter state
4055 * reset registers
4056 * record current page
4057 * set low water mark.
4060 void suppress_node::tprint(troff_output_file *out)
4062 int current_page = topdiv->get_page_number();
4063 // firstly check to see whether this suppress node contains
4064 // an image filename & position.
4065 if (is_on == 2) {
4066 // remember position and filename
4067 last_position = position;
4068 char *tem = (char *)last_image_filename;
4069 last_image_filename = strsave(filename.contents());
4070 if (tem)
4071 a_delete tem;
4072 last_image_id = image_id;
4073 // printf("start of image and page = %d\n", current_page);
4075 else {
4076 // now check whether the suppress node requires us to issue limits.
4077 if (emit_limits) {
4078 char name[8192];
4079 // remember that the filename will contain a %d in which the
4080 // last_image_id is placed
4081 if (last_image_filename == (char *) 0)
4082 *name = '\0';
4083 else
4084 sprintf(name, last_image_filename, last_image_id);
4085 if (is_html) {
4086 switch (last_position) {
4087 case 'c':
4088 out->start_special();
4089 put(out, "devtag:.centered-image");
4090 break;
4091 case 'r':
4092 out->start_special();
4093 put(out, "devtag:.right-image");
4094 break;
4095 case 'l':
4096 out->start_special();
4097 put(out, "devtag:.left-image");
4098 break;
4099 case 'i':
4101 default:
4104 out->end_special();
4105 out->start_special();
4106 put(out, "devtag:.auto-image ");
4107 put(out, name);
4108 out->end_special();
4110 else {
4111 // postscript (or other device)
4112 if (suppress_start_page > 0 && current_page != suppress_start_page)
4113 error("suppression limit registers span more than one page;\n"
4114 "image description %1 will be wrong", image_no);
4115 // if (topdiv->get_page_number() != suppress_start_page)
4116 // fprintf(stderr, "end of image and topdiv page = %d and suppress_start_page = %d\n",
4117 // topdiv->get_page_number(), suppress_start_page);
4119 // remember that the filename will contain a %d in which the
4120 // image_no is placed
4121 fprintf(stderr,
4122 "grohtml-info:page %d %d %d %d %d %d %s %d %d %s\n",
4123 topdiv->get_page_number(),
4124 get_reg_int("opminx"), get_reg_int("opminy"),
4125 get_reg_int("opmaxx"), get_reg_int("opmaxy"),
4126 // page offset + line length
4127 get_reg_int(".o") + get_reg_int(".l"),
4128 name, hresolution, vresolution, get_reg_str(".F"));
4129 fflush(stderr);
4132 else {
4133 if (is_on) {
4134 out->on();
4135 // lastly we reset the output registers
4136 reset_output_registers();
4138 else
4139 out->off();
4140 suppress_start_page = current_page;
4145 int suppress_node::force_tprint()
4147 return is_on;
4150 int suppress_node::is_tag()
4152 return is_on;
4155 hunits suppress_node::width()
4157 return H0;
4160 /* composite_node */
4162 class composite_node : public charinfo_node {
4163 node *n;
4164 tfont *tf;
4165 public:
4166 composite_node(node *, charinfo *, tfont *, statem *, int, node * = 0);
4167 ~composite_node();
4168 node *copy();
4169 hunits width();
4170 node *last_char_node();
4171 units size();
4172 void tprint(troff_output_file *);
4173 hyphenation_type get_hyphenation_type();
4174 void ascii_print(ascii_output_file *);
4175 void asciify(macro *);
4176 hyphen_list *get_hyphen_list(hyphen_list *, int *);
4177 node *add_self(node *, hyphen_list **);
4178 tfont *get_tfont();
4179 int same(node *);
4180 const char *type();
4181 int force_tprint();
4182 int is_tag();
4183 void vertical_extent(vunits *, vunits *);
4184 vunits vertical_width();
4187 composite_node::composite_node(node *p, charinfo *c, tfont *t, statem *s,
4188 int pop, node *x)
4189 : charinfo_node(c, s, pop, x), n(p), tf(t)
4193 composite_node::~composite_node()
4195 delete_node_list(n);
4198 node *composite_node::copy()
4200 return new composite_node(copy_node_list(n), ci, tf, state, div_nest_level);
4203 hunits composite_node::width()
4205 hunits x;
4206 if (tf->get_constant_space(&x))
4207 return x;
4208 x = H0;
4209 for (node *tem = n; tem; tem = tem->next)
4210 x += tem->width();
4211 hunits offset;
4212 if (tf->get_bold(&offset))
4213 x += offset;
4214 x += tf->get_track_kern();
4215 return x;
4218 node *composite_node::last_char_node()
4220 return this;
4223 vunits composite_node::vertical_width()
4225 vunits v = V0;
4226 for (node *tem = n; tem; tem = tem->next)
4227 v += tem->vertical_width();
4228 return v;
4231 units composite_node::size()
4233 return tf->get_size().to_units();
4236 hyphenation_type composite_node::get_hyphenation_type()
4238 return HYPHEN_MIDDLE;
4241 void composite_node::asciify(macro *m)
4243 unsigned char c = ci->get_asciify_code();
4244 if (c == 0)
4245 c = ci->get_ascii_code();
4246 if (c != 0) {
4247 m->append(c);
4248 delete this;
4250 else
4251 m->append(this);
4254 void composite_node::ascii_print(ascii_output_file *ascii)
4256 unsigned char c = ci->get_ascii_code();
4257 if (c != 0)
4258 ascii->outc(c);
4259 else
4260 ascii->outs(ci->nm.contents());
4264 hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail, int *count)
4266 (*count)++;
4267 return new hyphen_list(ci->get_hyphenation_code(), tail);
4270 node *composite_node::add_self(node *nn, hyphen_list **p)
4272 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
4273 next = nn;
4274 nn = this;
4275 if ((*p)->hyphen)
4276 nn = nn->add_discretionary_hyphen();
4277 hyphen_list *pp = *p;
4278 *p = (*p)->next;
4279 delete pp;
4280 return nn;
4283 tfont *composite_node::get_tfont()
4285 return tf;
4288 node *reverse_node_list(node *n)
4290 node *r = 0;
4291 while (n) {
4292 node *tem = n;
4293 n = n->next;
4294 tem->next = r;
4295 r = tem;
4297 return r;
4300 void composite_node::vertical_extent(vunits *minimum, vunits *maximum)
4302 n = reverse_node_list(n);
4303 node_list_vertical_extent(n, minimum, maximum);
4304 n = reverse_node_list(n);
4307 width_list::width_list(hunits w, hunits s)
4308 : width(w), sentence_width(s), next(0)
4312 width_list::width_list(width_list *w)
4313 : width(w->width), sentence_width(w->sentence_width), next(0)
4317 word_space_node::word_space_node(hunits d, color *c, width_list *w, node *x)
4318 : space_node(d, c, x), orig_width(w), unformat(0)
4322 word_space_node::word_space_node(hunits d, int s, color *c, width_list *w,
4323 int flag, statem *st, int pop, node *x)
4324 : space_node(d, s, 0, c, st, pop, x), orig_width(w), unformat(flag)
4328 word_space_node::~word_space_node()
4330 width_list *w = orig_width;
4331 while (w != 0) {
4332 width_list *tmp = w;
4333 w = w->next;
4334 delete tmp;
4338 node *word_space_node::copy()
4340 assert(orig_width != 0);
4341 width_list *w_old_curr = orig_width;
4342 width_list *w_new_curr = new width_list(w_old_curr);
4343 width_list *w_new = w_new_curr;
4344 w_old_curr = w_old_curr->next;
4345 while (w_old_curr != 0) {
4346 w_new_curr->next = new width_list(w_old_curr);
4347 w_new_curr = w_new_curr->next;
4348 w_old_curr = w_old_curr->next;
4350 return new word_space_node(n, set, col, w_new, unformat, state,
4351 div_nest_level);
4354 int word_space_node::set_unformat_flag()
4356 unformat = 1;
4357 return 1;
4360 void word_space_node::tprint(troff_output_file *out)
4362 out->fill_color(col);
4363 out->word_marker();
4364 out->right(n);
4367 int word_space_node::merge_space(hunits h, hunits sw, hunits ssw)
4369 n += h;
4370 assert(orig_width != 0);
4371 width_list *w = orig_width;
4372 for (; w->next; w = w->next)
4374 w->next = new width_list(sw, ssw);
4375 return 1;
4378 unbreakable_space_node::unbreakable_space_node(hunits d, color *c, node *x)
4379 : word_space_node(d, c, 0, x)
4383 unbreakable_space_node::unbreakable_space_node(hunits d, int s,
4384 color *c, statem *st, int pop,
4385 node *x)
4386 : word_space_node(d, s, c, 0, 0, st, pop, x)
4390 node *unbreakable_space_node::copy()
4392 return new unbreakable_space_node(n, set, col, state, div_nest_level);
4395 int unbreakable_space_node::force_tprint()
4397 return 0;
4400 int unbreakable_space_node::is_tag()
4402 return 0;
4405 breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
4406 breakpoint *rest, int)
4408 return rest;
4411 int unbreakable_space_node::nbreaks()
4413 return 0;
4416 void unbreakable_space_node::split(int, node **, node **)
4418 assert(0);
4421 int unbreakable_space_node::merge_space(hunits, hunits, hunits)
4423 return 0;
4426 hvpair::hvpair()
4430 draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4431 color *gc, color *fc)
4432 : npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4434 point = new hvpair[npoints];
4435 for (int i = 0; i < npoints; i++)
4436 point[i] = p[i];
4439 draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4440 color *gc, color *fc, statem *st, int pop)
4441 : node(0, st, pop), npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4443 point = new hvpair[npoints];
4444 for (int i = 0; i < npoints; i++)
4445 point[i] = p[i];
4448 int draw_node::same(node *n)
4450 draw_node *nd = (draw_node *)n;
4451 if (code != nd->code || npoints != nd->npoints || sz != nd->sz
4452 || gcol != nd->gcol || fcol != nd->fcol)
4453 return 0;
4454 for (int i = 0; i < npoints; i++)
4455 if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
4456 return 0;
4457 return 1;
4460 const char *draw_node::type()
4462 return "draw_node";
4465 int draw_node::force_tprint()
4467 return 0;
4470 int draw_node::is_tag()
4472 return 0;
4475 draw_node::~draw_node()
4477 if (point)
4478 a_delete point;
4481 hunits draw_node::width()
4483 hunits x = H0;
4484 for (int i = 0; i < npoints; i++)
4485 x += point[i].h;
4486 return x;
4489 vunits draw_node::vertical_width()
4491 if (code == 'e')
4492 return V0;
4493 vunits x = V0;
4494 for (int i = 0; i < npoints; i++)
4495 x += point[i].v;
4496 return x;
4499 node *draw_node::copy()
4501 return new draw_node(code, point, npoints, sz, gcol, fcol, state,
4502 div_nest_level);
4505 void draw_node::tprint(troff_output_file *out)
4507 out->draw(code, point, npoints, sz, gcol, fcol);
4510 /* tprint methods */
4512 void glyph_node::tprint(troff_output_file *out)
4514 tfont *ptf = tf->get_plain();
4515 if (ptf == tf)
4516 out->put_char_width(ci, ptf, gcol, fcol, width(), H0);
4517 else {
4518 hunits offset;
4519 int bold = tf->get_bold(&offset);
4520 hunits w = ptf->get_width(ci);
4521 hunits k = H0;
4522 hunits x;
4523 int cs = tf->get_constant_space(&x);
4524 if (cs) {
4525 x -= w;
4526 if (bold)
4527 x -= offset;
4528 hunits x2 = x/2;
4529 out->right(x2);
4530 k = x - x2;
4532 else
4533 k = tf->get_track_kern();
4534 if (bold) {
4535 out->put_char(ci, ptf, gcol, fcol);
4536 out->right(offset);
4538 out->put_char_width(ci, ptf, gcol, fcol, w, k);
4542 void glyph_node::zero_width_tprint(troff_output_file *out)
4544 tfont *ptf = tf->get_plain();
4545 hunits offset;
4546 int bold = tf->get_bold(&offset);
4547 hunits x;
4548 int cs = tf->get_constant_space(&x);
4549 if (cs) {
4550 x -= ptf->get_width(ci);
4551 if (bold)
4552 x -= offset;
4553 x = x/2;
4554 out->right(x);
4556 out->put_char(ci, ptf, gcol, fcol);
4557 if (bold) {
4558 out->right(offset);
4559 out->put_char(ci, ptf, gcol, fcol);
4560 out->right(-offset);
4562 if (cs)
4563 out->right(-x);
4566 void break_char_node::tprint(troff_output_file *t)
4568 ch->tprint(t);
4571 void break_char_node::zero_width_tprint(troff_output_file *t)
4573 ch->zero_width_tprint(t);
4576 void hline_node::tprint(troff_output_file *out)
4578 if (x < H0) {
4579 out->right(x);
4580 x = -x;
4582 if (n == 0) {
4583 out->right(x);
4584 return;
4586 hunits w = n->width();
4587 if (w <= H0) {
4588 error("horizontal line drawing character must have positive width");
4589 out->right(x);
4590 return;
4592 int i = int(x/w);
4593 if (i == 0) {
4594 hunits xx = x - w;
4595 hunits xx2 = xx/2;
4596 out->right(xx2);
4597 if (out->is_on())
4598 n->tprint(out);
4599 out->right(xx - xx2);
4601 else {
4602 hunits rem = x - w*i;
4603 if (rem > H0)
4604 if (n->overlaps_horizontally()) {
4605 if (out->is_on())
4606 n->tprint(out);
4607 out->right(rem - w);
4609 else
4610 out->right(rem);
4611 while (--i >= 0)
4612 if (out->is_on())
4613 n->tprint(out);
4617 void vline_node::tprint(troff_output_file *out)
4619 if (n == 0) {
4620 out->down(x);
4621 return;
4623 vunits h = n->size();
4624 int overlaps = n->overlaps_vertically();
4625 vunits y = x;
4626 if (y < V0) {
4627 y = -y;
4628 int i = y / h;
4629 vunits rem = y - i*h;
4630 if (i == 0) {
4631 out->right(n->width());
4632 out->down(-rem);
4634 else {
4635 while (--i > 0) {
4636 n->zero_width_tprint(out);
4637 out->down(-h);
4639 if (overlaps) {
4640 n->zero_width_tprint(out);
4641 out->down(-rem);
4642 if (out->is_on())
4643 n->tprint(out);
4644 out->down(-h);
4646 else {
4647 if (out->is_on())
4648 n->tprint(out);
4649 out->down(-h - rem);
4653 else {
4654 int i = y / h;
4655 vunits rem = y - i*h;
4656 if (i == 0) {
4657 out->down(rem);
4658 out->right(n->width());
4660 else {
4661 out->down(h);
4662 if (overlaps)
4663 n->zero_width_tprint(out);
4664 out->down(rem);
4665 while (--i > 0) {
4666 n->zero_width_tprint(out);
4667 out->down(h);
4669 if (out->is_on())
4670 n->tprint(out);
4675 void zero_width_node::tprint(troff_output_file *out)
4677 if (!n)
4678 return;
4679 if (!n->next) {
4680 n->zero_width_tprint(out);
4681 return;
4683 int hpos = out->get_hpos();
4684 int vpos = out->get_vpos();
4685 node *tem = n;
4686 while (tem) {
4687 tem->tprint(out);
4688 tem = tem->next;
4690 out->moveto(hpos, vpos);
4693 void overstrike_node::tprint(troff_output_file *out)
4695 hunits pos = H0;
4696 for (node *tem = list; tem; tem = tem->next) {
4697 hunits x = (max_width - tem->width())/2;
4698 out->right(x - pos);
4699 pos = x;
4700 tem->zero_width_tprint(out);
4702 out->right(max_width - pos);
4705 void bracket_node::tprint(troff_output_file *out)
4707 if (list == 0)
4708 return;
4709 int npieces = 0;
4710 node *tem;
4711 for (tem = list; tem; tem = tem->next)
4712 ++npieces;
4713 vunits h = list->size();
4714 vunits totalh = h*npieces;
4715 vunits y = (totalh - h)/2;
4716 out->down(y);
4717 for (tem = list; tem; tem = tem->next) {
4718 tem->zero_width_tprint(out);
4719 out->down(-h);
4721 out->right(max_width);
4722 out->down(totalh - y);
4725 void node::tprint(troff_output_file *)
4729 void node::zero_width_tprint(troff_output_file *out)
4731 int hpos = out->get_hpos();
4732 int vpos = out->get_vpos();
4733 tprint(out);
4734 out->moveto(hpos, vpos);
4737 void space_node::tprint(troff_output_file *out)
4739 out->fill_color(col);
4740 out->right(n);
4743 void hmotion_node::tprint(troff_output_file *out)
4745 out->fill_color(col);
4746 out->right(n);
4749 void space_char_hmotion_node::tprint(troff_output_file *out)
4751 out->fill_color(col);
4752 if (is_html) {
4753 // we emit the space width as a negative glyph index
4754 out->flush_tbuf();
4755 out->do_motion();
4756 out->put('N');
4757 out->put(-n.to_units());
4758 out->put('\n');
4760 out->right(n);
4763 void vmotion_node::tprint(troff_output_file *out)
4765 out->fill_color(col);
4766 out->down(n);
4769 void kern_pair_node::tprint(troff_output_file *out)
4771 n1->tprint(out);
4772 out->right(amount);
4773 n2->tprint(out);
4776 static void tprint_reverse_node_list(troff_output_file *out, node *n)
4778 if (n == 0)
4779 return;
4780 tprint_reverse_node_list(out, n->next);
4781 n->tprint(out);
4784 void dbreak_node::tprint(troff_output_file *out)
4786 tprint_reverse_node_list(out, none);
4789 void composite_node::tprint(troff_output_file *out)
4791 hunits bold_offset;
4792 int is_bold = tf->get_bold(&bold_offset);
4793 hunits track_kern = tf->get_track_kern();
4794 hunits constant_space;
4795 int is_constant_spaced = tf->get_constant_space(&constant_space);
4796 hunits x = H0;
4797 if (is_constant_spaced) {
4798 x = constant_space;
4799 for (node *tem = n; tem; tem = tem->next)
4800 x -= tem->width();
4801 if (is_bold)
4802 x -= bold_offset;
4803 hunits x2 = x/2;
4804 out->right(x2);
4805 x -= x2;
4807 if (is_bold) {
4808 int hpos = out->get_hpos();
4809 int vpos = out->get_vpos();
4810 tprint_reverse_node_list(out, n);
4811 out->moveto(hpos, vpos);
4812 out->right(bold_offset);
4814 tprint_reverse_node_list(out, n);
4815 if (is_constant_spaced)
4816 out->right(x);
4817 else
4818 out->right(track_kern);
4821 node *make_composite_node(charinfo *s, environment *env)
4823 int fontno = env_definite_font(env);
4824 if (fontno < 0) {
4825 error("no current font");
4826 return 0;
4828 assert(fontno < font_table_size && font_table[fontno] != 0);
4829 node *n = charinfo_to_node_list(s, env);
4830 font_size fs = env->get_font_size();
4831 int char_height = env->get_char_height();
4832 int char_slant = env->get_char_slant();
4833 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
4834 fontno);
4835 if (env->is_composite())
4836 tf = tf->get_plain();
4837 return new composite_node(n, s, tf, 0, 0, 0);
4840 node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0)
4842 int fontno = env_definite_font(env);
4843 if (fontno < 0) {
4844 error("no current font");
4845 return 0;
4847 assert(fontno < font_table_size && font_table[fontno] != 0);
4848 int fn = fontno;
4849 int found = font_table[fontno]->contains(s);
4850 if (!found) {
4851 macro *mac = s->get_macro();
4852 if (mac && s->is_fallback())
4853 return make_composite_node(s, env);
4854 if (s->numbered()) {
4855 if (!no_error_message)
4856 warning(WARN_CHAR, "can't find numbered character %1",
4857 s->get_number());
4858 return 0;
4860 special_font_list *sf = font_table[fontno]->sf;
4861 while (sf != 0 && !found) {
4862 fn = sf->n;
4863 if (font_table[fn])
4864 found = font_table[fn]->contains(s);
4865 sf = sf->next;
4867 if (!found) {
4868 symbol f = font_table[fontno]->get_name();
4869 string gl(f.contents());
4870 gl += ' ';
4871 gl += s->nm.contents();
4872 gl += '\0';
4873 charinfo *ci = get_charinfo(symbol(gl.contents()));
4874 if (ci && ci->get_macro())
4875 return make_composite_node(ci, env);
4877 if (!found) {
4878 sf = global_special_fonts;
4879 while (sf != 0 && !found) {
4880 fn = sf->n;
4881 if (font_table[fn])
4882 found = font_table[fn]->contains(s);
4883 sf = sf->next;
4886 if (!found)
4887 if (mac && s->is_special())
4888 return make_composite_node(s, env);
4889 if (!found) {
4890 for (fn = 0; fn < font_table_size; fn++)
4891 if (font_table[fn]
4892 && font_table[fn]->is_special()
4893 && font_table[fn]->contains(s)) {
4894 found = 1;
4895 break;
4898 if (!found) {
4899 if (!no_error_message && s->first_time_not_found()) {
4900 unsigned char input_code = s->get_ascii_code();
4901 if (input_code != 0) {
4902 if (csgraph(input_code))
4903 warning(WARN_CHAR, "can't find character `%1'", input_code);
4904 else
4905 warning(WARN_CHAR, "can't find character with input code %1",
4906 int(input_code));
4908 else if (s->nm.contents())
4909 warning(WARN_CHAR, "can't find special character `%1'",
4910 s->nm.contents());
4912 return 0;
4915 font_size fs = env->get_font_size();
4916 int char_height = env->get_char_height();
4917 int char_slant = env->get_char_slant();
4918 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn);
4919 if (env->is_composite())
4920 tf = tf->get_plain();
4921 color *gcol = env->get_glyph_color();
4922 color *fcol = env->get_fill_color();
4923 return new glyph_node(s, tf, gcol, fcol, 0, 0);
4926 node *make_node(charinfo *ci, environment *env)
4928 switch (ci->get_special_translation()) {
4929 case charinfo::TRANSLATE_SPACE:
4930 return new space_char_hmotion_node(env->get_space_width(),
4931 env->get_fill_color());
4932 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4933 return new unbreakable_space_node(env->get_space_width(),
4934 env->get_fill_color());
4935 case charinfo::TRANSLATE_DUMMY:
4936 return new dummy_node;
4937 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4938 error("translation to \\% ignored in this context");
4939 break;
4941 charinfo *tem = ci->get_translation();
4942 if (tem)
4943 ci = tem;
4944 macro *mac = ci->get_macro();
4945 if (mac && ci->is_normal())
4946 return make_composite_node(ci, env);
4947 else
4948 return make_glyph_node(ci, env);
4951 int character_exists(charinfo *ci, environment *env)
4953 if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
4954 return 1;
4955 charinfo *tem = ci->get_translation();
4956 if (tem)
4957 ci = tem;
4958 if (ci->get_macro())
4959 return 1;
4960 node *nd = make_glyph_node(ci, env, 1);
4961 if (nd) {
4962 delete nd;
4963 return 1;
4965 return 0;
4968 node *node::add_char(charinfo *ci, environment *env,
4969 hunits *widthp, int *spacep, node **glyph_comp_np)
4971 node *res;
4972 switch (ci->get_special_translation()) {
4973 case charinfo::TRANSLATE_SPACE:
4974 res = new space_char_hmotion_node(env->get_space_width(),
4975 env->get_fill_color(), this);
4976 *widthp += res->width();
4977 return res;
4978 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4979 res = new unbreakable_space_node(env->get_space_width(),
4980 env->get_fill_color(), this);
4981 res->freeze_space();
4982 *widthp += res->width();
4983 *spacep += res->nspaces();
4984 return res;
4985 case charinfo::TRANSLATE_DUMMY:
4986 return new dummy_node(this);
4987 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4988 return add_discretionary_hyphen();
4990 charinfo *tem = ci->get_translation();
4991 if (tem)
4992 ci = tem;
4993 macro *mac = ci->get_macro();
4994 if (mac && ci->is_normal()) {
4995 res = make_composite_node(ci, env);
4996 if (res) {
4997 res->next = this;
4998 *widthp += res->width();
4999 if (glyph_comp_np)
5000 *glyph_comp_np = res;
5002 else {
5003 if (glyph_comp_np)
5004 *glyph_comp_np = res;
5005 return this;
5008 else {
5009 node *gn = make_glyph_node(ci, env);
5010 if (gn == 0)
5011 return this;
5012 else {
5013 hunits old_width = width();
5014 node *p = gn->merge_self(this);
5015 if (p == 0) {
5016 *widthp += gn->width();
5017 gn->next = this;
5018 res = gn;
5020 else {
5021 *widthp += p->width() - old_width;
5022 res = p;
5024 if (glyph_comp_np)
5025 *glyph_comp_np = res;
5028 int break_code = 0;
5029 if (ci->can_break_before())
5030 break_code = 1;
5031 if (ci->can_break_after())
5032 break_code |= 2;
5033 if (break_code) {
5034 node *next1 = res->next;
5035 res->next = 0;
5036 res = new break_char_node(res, break_code, env->get_fill_color(), next1);
5038 return res;
5041 #ifdef __GNUG__
5042 inline
5043 #endif
5044 int same_node(node *n1, node *n2)
5046 if (n1 != 0) {
5047 if (n2 != 0)
5048 return n1->type() == n2->type() && n1->same(n2);
5049 else
5050 return 0;
5052 else
5053 return n2 == 0;
5056 int same_node_list(node *n1, node *n2)
5058 while (n1 && n2) {
5059 if (n1->type() != n2->type() || !n1->same(n2))
5060 return 0;
5061 n1 = n1->next;
5062 n2 = n2->next;
5064 return !n1 && !n2;
5067 int extra_size_node::same(node *nd)
5069 return n == ((extra_size_node *)nd)->n;
5072 const char *extra_size_node::type()
5074 return "extra_size_node";
5077 int extra_size_node::force_tprint()
5079 return 0;
5082 int extra_size_node::is_tag()
5084 return 0;
5087 int vertical_size_node::same(node *nd)
5089 return n == ((vertical_size_node *)nd)->n;
5092 const char *vertical_size_node::type()
5094 return "vertical_size_node";
5097 int vertical_size_node::set_unformat_flag()
5099 return 0;
5102 int vertical_size_node::force_tprint()
5104 return 0;
5107 int vertical_size_node::is_tag()
5109 return 0;
5112 int hmotion_node::same(node *nd)
5114 return n == ((hmotion_node *)nd)->n
5115 && col == ((hmotion_node *)nd)->col;
5118 const char *hmotion_node::type()
5120 return "hmotion_node";
5123 int hmotion_node::set_unformat_flag()
5125 unformat = 1;
5126 return 1;
5129 int hmotion_node::force_tprint()
5131 return 0;
5134 int hmotion_node::is_tag()
5136 return 0;
5139 node *hmotion_node::add_self(node *nd, hyphen_list **p)
5141 next = nd;
5142 hyphen_list *pp = *p;
5143 *p = (*p)->next;
5144 delete pp;
5145 return this;
5148 hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail, int *)
5150 return new hyphen_list(0, tail);
5153 int space_char_hmotion_node::same(node *nd)
5155 return n == ((space_char_hmotion_node *)nd)->n
5156 && col == ((space_char_hmotion_node *)nd)->col;
5159 const char *space_char_hmotion_node::type()
5161 return "space_char_hmotion_node";
5164 int space_char_hmotion_node::force_tprint()
5166 return 0;
5169 int space_char_hmotion_node::is_tag()
5171 return 0;
5174 node *space_char_hmotion_node::add_self(node *nd, hyphen_list **p)
5176 next = nd;
5177 hyphen_list *pp = *p;
5178 *p = (*p)->next;
5179 delete pp;
5180 return this;
5183 hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail,
5184 int *)
5186 return new hyphen_list(0, tail);
5189 int vmotion_node::same(node *nd)
5191 return n == ((vmotion_node *)nd)->n
5192 && col == ((vmotion_node *)nd)->col;
5195 const char *vmotion_node::type()
5197 return "vmotion_node";
5200 int vmotion_node::force_tprint()
5202 return 0;
5205 int vmotion_node::is_tag()
5207 return 0;
5210 int hline_node::same(node *nd)
5212 return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n);
5215 const char *hline_node::type()
5217 return "hline_node";
5220 int hline_node::force_tprint()
5222 return 0;
5225 int hline_node::is_tag()
5227 return 0;
5230 int vline_node::same(node *nd)
5232 return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n);
5235 const char *vline_node::type()
5237 return "vline_node";
5240 int vline_node::force_tprint()
5242 return 0;
5245 int vline_node::is_tag()
5247 return 0;
5250 int dummy_node::same(node * /*nd*/)
5252 return 1;
5255 const char *dummy_node::type()
5257 return "dummy_node";
5260 int dummy_node::force_tprint()
5262 return 0;
5265 int dummy_node::is_tag()
5267 return 0;
5270 int transparent_dummy_node::same(node * /*nd*/)
5272 return 1;
5275 const char *transparent_dummy_node::type()
5277 return "transparent_dummy_node";
5280 int transparent_dummy_node::force_tprint()
5282 return 0;
5285 int transparent_dummy_node::is_tag()
5287 return 0;
5290 int transparent_dummy_node::ends_sentence()
5292 return 2;
5295 int zero_width_node::same(node *nd)
5297 return same_node_list(n, ((zero_width_node *)nd)->n);
5300 const char *zero_width_node::type()
5302 return "zero_width_node";
5305 int zero_width_node::force_tprint()
5307 return 0;
5310 int zero_width_node::is_tag()
5312 return 0;
5315 int italic_corrected_node::same(node *nd)
5317 return (x == ((italic_corrected_node *)nd)->x
5318 && same_node(n, ((italic_corrected_node *)nd)->n));
5321 const char *italic_corrected_node::type()
5323 return "italic_corrected_node";
5326 int italic_corrected_node::force_tprint()
5328 return 0;
5331 int italic_corrected_node::is_tag()
5333 return 0;
5336 left_italic_corrected_node::left_italic_corrected_node(node *xx)
5337 : node(xx), n(0)
5341 left_italic_corrected_node::left_italic_corrected_node(statem *s, int pop,
5342 node *xx)
5343 : node(xx, s, pop), n(0)
5347 left_italic_corrected_node::~left_italic_corrected_node()
5349 delete n;
5352 node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
5354 if (n == 0) {
5355 hunits lic = gn->left_italic_correction();
5356 if (!lic.is_zero()) {
5357 x = lic;
5358 n = gn;
5359 return this;
5362 else {
5363 node *nd = n->merge_glyph_node(gn);
5364 if (nd) {
5365 n = nd;
5366 x = n->left_italic_correction();
5367 return this;
5370 return 0;
5373 node *left_italic_corrected_node::copy()
5375 left_italic_corrected_node *nd =
5376 new left_italic_corrected_node(state, div_nest_level);
5377 if (n) {
5378 nd->n = n->copy();
5379 nd->x = x;
5381 return nd;
5384 void left_italic_corrected_node::tprint(troff_output_file *out)
5386 if (n) {
5387 out->right(x);
5388 n->tprint(out);
5392 const char *left_italic_corrected_node::type()
5394 return "left_italic_corrected_node";
5397 int left_italic_corrected_node::force_tprint()
5399 return 0;
5402 int left_italic_corrected_node::is_tag()
5404 return 0;
5407 int left_italic_corrected_node::same(node *nd)
5409 return (x == ((left_italic_corrected_node *)nd)->x
5410 && same_node(n, ((left_italic_corrected_node *)nd)->n));
5413 void left_italic_corrected_node::ascii_print(ascii_output_file *out)
5415 if (n)
5416 n->ascii_print(out);
5419 hunits left_italic_corrected_node::width()
5421 return n ? n->width() + x : H0;
5424 void left_italic_corrected_node::vertical_extent(vunits *minimum,
5425 vunits *maximum)
5427 if (n)
5428 n->vertical_extent(minimum, maximum);
5429 else
5430 node::vertical_extent(minimum, maximum);
5433 hunits left_italic_corrected_node::skew()
5435 return n ? n->skew() + x/2 : H0;
5438 hunits left_italic_corrected_node::subscript_correction()
5440 return n ? n->subscript_correction() : H0;
5443 hunits left_italic_corrected_node::italic_correction()
5445 return n ? n->italic_correction() : H0;
5448 int left_italic_corrected_node::ends_sentence()
5450 return n ? n->ends_sentence() : 0;
5453 int left_italic_corrected_node::overlaps_horizontally()
5455 return n ? n->overlaps_horizontally() : 0;
5458 int left_italic_corrected_node::overlaps_vertically()
5460 return n ? n->overlaps_vertically() : 0;
5463 node *left_italic_corrected_node::last_char_node()
5465 return n ? n->last_char_node() : 0;
5468 tfont *left_italic_corrected_node::get_tfont()
5470 return n ? n->get_tfont() : 0;
5473 hyphenation_type left_italic_corrected_node::get_hyphenation_type()
5475 if (n)
5476 return n->get_hyphenation_type();
5477 else
5478 return HYPHEN_MIDDLE;
5481 hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail,
5482 int *count)
5484 return n ? n->get_hyphen_list(tail, count) : tail;
5487 node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
5489 if (n) {
5490 nd = new left_italic_corrected_node(state, div_nest_level, nd);
5491 nd = n->add_self(nd, p);
5492 n = 0;
5493 delete this;
5495 return nd;
5498 int left_italic_corrected_node::character_type()
5500 return n ? n->character_type() : 0;
5503 int overstrike_node::same(node *nd)
5505 return same_node_list(list, ((overstrike_node *)nd)->list);
5508 const char *overstrike_node::type()
5510 return "overstrike_node";
5513 int overstrike_node::force_tprint()
5515 return 0;
5518 int overstrike_node::is_tag()
5520 return 0;
5523 node *overstrike_node::add_self(node *n, hyphen_list **p)
5525 next = n;
5526 hyphen_list *pp = *p;
5527 *p = (*p)->next;
5528 delete pp;
5529 return this;
5532 hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail, int *)
5534 return new hyphen_list(0, tail);
5537 int bracket_node::same(node *nd)
5539 return same_node_list(list, ((bracket_node *)nd)->list);
5542 const char *bracket_node::type()
5544 return "bracket_node";
5547 int bracket_node::force_tprint()
5549 return 0;
5552 int bracket_node::is_tag()
5554 return 0;
5557 int composite_node::same(node *nd)
5559 return ci == ((composite_node *)nd)->ci
5560 && same_node_list(n, ((composite_node *)nd)->n);
5563 const char *composite_node::type()
5565 return "composite_node";
5568 int composite_node::force_tprint()
5570 return 0;
5573 int composite_node::is_tag()
5575 return 0;
5578 int glyph_node::same(node *nd)
5580 return ci == ((glyph_node *)nd)->ci
5581 && tf == ((glyph_node *)nd)->tf
5582 && gcol == ((glyph_node *)nd)->gcol
5583 && fcol == ((glyph_node *)nd)->fcol;
5586 const char *glyph_node::type()
5588 return "glyph_node";
5591 int glyph_node::force_tprint()
5593 return 0;
5596 int glyph_node::is_tag()
5598 return 0;
5601 int ligature_node::same(node *nd)
5603 return (same_node(n1, ((ligature_node *)nd)->n1)
5604 && same_node(n2, ((ligature_node *)nd)->n2)
5605 && glyph_node::same(nd));
5608 const char *ligature_node::type()
5610 return "ligature_node";
5613 int ligature_node::force_tprint()
5615 return 0;
5618 int ligature_node::is_tag()
5620 return 0;
5623 int kern_pair_node::same(node *nd)
5625 return (amount == ((kern_pair_node *)nd)->amount
5626 && same_node(n1, ((kern_pair_node *)nd)->n1)
5627 && same_node(n2, ((kern_pair_node *)nd)->n2));
5630 const char *kern_pair_node::type()
5632 return "kern_pair_node";
5635 int kern_pair_node::force_tprint()
5637 return 0;
5640 int kern_pair_node::is_tag()
5642 return 0;
5645 int dbreak_node::same(node *nd)
5647 return (same_node_list(none, ((dbreak_node *)nd)->none)
5648 && same_node_list(pre, ((dbreak_node *)nd)->pre)
5649 && same_node_list(post, ((dbreak_node *)nd)->post));
5652 const char *dbreak_node::type()
5654 return "dbreak_node";
5657 int dbreak_node::force_tprint()
5659 return 0;
5662 int dbreak_node::is_tag()
5664 return 0;
5667 int break_char_node::same(node *nd)
5669 return break_code == ((break_char_node *)nd)->break_code
5670 && col == ((break_char_node *)nd)->col
5671 && same_node(ch, ((break_char_node *)nd)->ch);
5674 const char *break_char_node::type()
5676 return "break_char_node";
5679 int break_char_node::force_tprint()
5681 return 0;
5684 int break_char_node::is_tag()
5686 return 0;
5689 int line_start_node::same(node * /*nd*/)
5691 return 1;
5694 const char *line_start_node::type()
5696 return "line_start_node";
5699 int line_start_node::force_tprint()
5701 return 0;
5704 int line_start_node::is_tag()
5706 return 0;
5709 int space_node::same(node *nd)
5711 return n == ((space_node *)nd)->n
5712 && set == ((space_node *)nd)->set
5713 && col == ((space_node *)nd)->col;
5716 const char *space_node::type()
5718 return "space_node";
5721 int word_space_node::same(node *nd)
5723 return n == ((word_space_node *)nd)->n
5724 && set == ((word_space_node *)nd)->set
5725 && col == ((word_space_node *)nd)->col;
5728 const char *word_space_node::type()
5730 return "word_space_node";
5733 int word_space_node::force_tprint()
5735 return 0;
5738 int word_space_node::is_tag()
5740 return 0;
5743 void unbreakable_space_node::tprint(troff_output_file *out)
5745 out->fill_color(col);
5746 if (is_html) {
5747 // we emit the space width as a negative glyph index
5748 out->flush_tbuf();
5749 out->do_motion();
5750 out->put('N');
5751 out->put(-n.to_units());
5752 out->put('\n');
5754 out->right(n);
5757 int unbreakable_space_node::same(node *nd)
5759 return n == ((unbreakable_space_node *)nd)->n
5760 && set == ((unbreakable_space_node *)nd)->set
5761 && col == ((unbreakable_space_node *)nd)->col;
5764 const char *unbreakable_space_node::type()
5766 return "unbreakable_space_node";
5769 node *unbreakable_space_node::add_self(node *nd, hyphen_list **p)
5771 next = nd;
5772 hyphen_list *pp = *p;
5773 *p = (*p)->next;
5774 delete pp;
5775 return this;
5778 hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail, int *)
5780 return new hyphen_list(0, tail);
5783 int diverted_space_node::same(node *nd)
5785 return n == ((diverted_space_node *)nd)->n;
5788 const char *diverted_space_node::type()
5790 return "diverted_space_node";
5793 int diverted_space_node::force_tprint()
5795 return 0;
5798 int diverted_space_node::is_tag()
5800 return 0;
5803 int diverted_copy_file_node::same(node *nd)
5805 return filename == ((diverted_copy_file_node *)nd)->filename;
5808 const char *diverted_copy_file_node::type()
5810 return "diverted_copy_file_node";
5813 int diverted_copy_file_node::force_tprint()
5815 return 0;
5818 int diverted_copy_file_node::is_tag()
5820 return 0;
5823 // Grow the font_table so that its size is > n.
5825 static void grow_font_table(int n)
5827 assert(n >= font_table_size);
5828 font_info **old_font_table = font_table;
5829 int old_font_table_size = font_table_size;
5830 font_table_size = font_table_size ? (font_table_size*3)/2 : 10;
5831 if (font_table_size <= n)
5832 font_table_size = n + 10;
5833 font_table = new font_info *[font_table_size];
5834 if (old_font_table_size)
5835 memcpy(font_table, old_font_table,
5836 old_font_table_size*sizeof(font_info *));
5837 a_delete old_font_table;
5838 for (int i = old_font_table_size; i < font_table_size; i++)
5839 font_table[i] = 0;
5842 dictionary font_translation_dictionary(17);
5844 static symbol get_font_translation(symbol nm)
5846 void *p = font_translation_dictionary.lookup(nm);
5847 return p ? symbol((char *)p) : nm;
5850 dictionary font_dictionary(50);
5852 static int mount_font_no_translate(int n, symbol name, symbol external_name,
5853 int check_only = 0)
5855 assert(n >= 0);
5856 // We store the address of this char in font_dictionary to indicate
5857 // that we've previously tried to mount the font and failed.
5858 static char a_char;
5859 font *fm = 0;
5860 void *p = font_dictionary.lookup(external_name);
5861 if (p == 0) {
5862 int not_found;
5863 fm = font::load_font(external_name.contents(), &not_found, check_only);
5864 if (check_only)
5865 return fm != 0;
5866 if (!fm) {
5867 if (not_found)
5868 warning(WARN_FONT, "can't find font `%1'", external_name.contents());
5869 (void)font_dictionary.lookup(external_name, &a_char);
5870 return 0;
5872 (void)font_dictionary.lookup(name, fm);
5874 else if (p == &a_char) {
5875 #if 0
5876 error("invalid font `%1'", external_name.contents());
5877 #endif
5878 return 0;
5880 else
5881 fm = (font*)p;
5882 if (check_only)
5883 return 1;
5884 if (n >= font_table_size) {
5885 if (n - font_table_size > 1000) {
5886 error("font position too much larger than first unused position");
5887 return 0;
5889 grow_font_table(n);
5891 else if (font_table[n] != 0)
5892 delete font_table[n];
5893 font_table[n] = new font_info(name, n, external_name, fm);
5894 font_family::invalidate_fontno(n);
5895 return 1;
5898 int mount_font(int n, symbol name, symbol external_name)
5900 assert(n >= 0);
5901 name = get_font_translation(name);
5902 if (external_name.is_null())
5903 external_name = name;
5904 else
5905 external_name = get_font_translation(external_name);
5906 return mount_font_no_translate(n, name, external_name);
5909 int check_font(symbol fam, symbol name)
5911 if (check_style(name))
5912 name = concat(fam, name);
5913 return mount_font_no_translate(0, name, name, 1);
5916 int check_style(symbol s)
5918 int i = symbol_fontno(s);
5919 return i < 0 ? 0 : font_table[i]->is_style();
5922 void mount_style(int n, symbol name)
5924 assert(n >= 0);
5925 if (n >= font_table_size) {
5926 if (n - font_table_size > 1000) {
5927 error("font position too much larger than first unused position");
5928 return;
5930 grow_font_table(n);
5932 else if (font_table[n] != 0)
5933 delete font_table[n];
5934 font_table[n] = new font_info(get_font_translation(name), n, NULL_SYMBOL, 0);
5935 font_family::invalidate_fontno(n);
5938 /* global functions */
5940 void font_translate()
5942 symbol from = get_name(1);
5943 if (!from.is_null()) {
5944 symbol to = get_name();
5945 if (to.is_null() || from == to)
5946 font_translation_dictionary.remove(from);
5947 else
5948 (void)font_translation_dictionary.lookup(from, (void *)to.contents());
5950 skip_line();
5953 void font_position()
5955 int n;
5956 if (get_integer(&n)) {
5957 if (n < 0)
5958 error("negative font position");
5959 else {
5960 symbol internal_name = get_name(1);
5961 if (!internal_name.is_null()) {
5962 symbol external_name = get_long_name();
5963 mount_font(n, internal_name, external_name); // ignore error
5967 skip_line();
5970 font_family::font_family(symbol s)
5971 : map_size(10), nm(s)
5973 map = new int[map_size];
5974 for (int i = 0; i < map_size; i++)
5975 map[i] = -1;
5978 font_family::~font_family()
5980 a_delete map;
5983 int font_family::make_definite(int i)
5985 if (i >= 0) {
5986 if (i < map_size && map[i] >= 0)
5987 return map[i];
5988 else {
5989 if (i < font_table_size && font_table[i] != 0) {
5990 if (i >= map_size) {
5991 int old_map_size = map_size;
5992 int *old_map = map;
5993 map_size *= 3;
5994 map_size /= 2;
5995 if (i >= map_size)
5996 map_size = i + 10;
5997 map = new int[map_size];
5998 memcpy(map, old_map, old_map_size*sizeof(int));
5999 a_delete old_map;
6000 for (int j = old_map_size; j < map_size; j++)
6001 map[j] = -1;
6003 if (font_table[i]->is_style()) {
6004 symbol sty = font_table[i]->get_name();
6005 symbol f = concat(nm, sty);
6006 int n;
6007 // don't use symbol_fontno, because that might return a style
6008 // and because we don't want to translate the name
6009 for (n = 0; n < font_table_size; n++)
6010 if (font_table[n] != 0 && font_table[n]->is_named(f)
6011 && !font_table[n]->is_style())
6012 break;
6013 if (n >= font_table_size) {
6014 n = next_available_font_position();
6015 if (!mount_font_no_translate(n, f, f))
6016 return -1;
6018 return map[i] = n;
6020 else
6021 return map[i] = i;
6023 else
6024 return -1;
6027 else
6028 return -1;
6031 dictionary family_dictionary(5);
6033 font_family *lookup_family(symbol nm)
6035 font_family *f = (font_family *)family_dictionary.lookup(nm);
6036 if (!f) {
6037 f = new font_family(nm);
6038 (void)family_dictionary.lookup(nm, f);
6040 return f;
6043 void font_family::invalidate_fontno(int n)
6045 assert(n >= 0 && n < font_table_size);
6046 dictionary_iterator iter(family_dictionary);
6047 symbol nam;
6048 font_family *fam;
6049 while (iter.get(&nam, (void **)&fam)) {
6050 int mapsize = fam->map_size;
6051 if (n < mapsize)
6052 fam->map[n] = -1;
6053 for (int i = 0; i < mapsize; i++)
6054 if (fam->map[i] == n)
6055 fam->map[i] = -1;
6059 void style()
6061 int n;
6062 if (get_integer(&n)) {
6063 if (n < 0)
6064 error("negative font position");
6065 else {
6066 symbol internal_name = get_name(1);
6067 if (!internal_name.is_null())
6068 mount_style(n, internal_name);
6071 skip_line();
6074 static int get_fontno()
6076 int n;
6077 tok.skip();
6078 if (tok.delimiter()) {
6079 symbol s = get_name(1);
6080 if (!s.is_null()) {
6081 n = symbol_fontno(s);
6082 if (n < 0) {
6083 n = next_available_font_position();
6084 if (!mount_font(n, s))
6085 return -1;
6087 return curenv->get_family()->make_definite(n);
6090 else if (get_integer(&n)) {
6091 if (n < 0 || n >= font_table_size || font_table[n] == 0)
6092 error("bad font number");
6093 else
6094 return curenv->get_family()->make_definite(n);
6096 return -1;
6099 static int underline_fontno = 2;
6101 void underline_font()
6103 int n = get_fontno();
6104 if (n >= 0)
6105 underline_fontno = n;
6106 skip_line();
6109 int get_underline_fontno()
6111 return underline_fontno;
6114 void define_font_special_character()
6116 int n = get_fontno();
6117 if (n < 0) {
6118 skip_line();
6119 return;
6121 symbol f = font_table[n]->get_name();
6122 do_define_character(CHAR_FONT_SPECIAL, f.contents());
6125 void remove_font_special_character()
6127 int n = get_fontno();
6128 if (n < 0) {
6129 skip_line();
6130 return;
6132 symbol f = font_table[n]->get_name();
6133 while (!tok.newline() && !tok.eof()) {
6134 if (!tok.space() && !tok.tab()) {
6135 charinfo *s = tok.get_char(1);
6136 string gl(f.contents());
6137 gl += ' ';
6138 gl += s->nm.contents();
6139 gl += '\0';
6140 charinfo *ci = get_charinfo(symbol(gl.contents()));
6141 if (!ci)
6142 break;
6143 macro *m = ci->set_macro(0);
6144 if (m)
6145 delete m;
6147 tok.next();
6149 skip_line();
6152 static void read_special_fonts(special_font_list **sp)
6154 special_font_list *s = *sp;
6155 *sp = 0;
6156 while (s != 0) {
6157 special_font_list *tem = s;
6158 s = s->next;
6159 delete tem;
6161 special_font_list **p = sp;
6162 while (has_arg()) {
6163 int i = get_fontno();
6164 if (i >= 0) {
6165 special_font_list *tem = new special_font_list;
6166 tem->n = i;
6167 tem->next = 0;
6168 *p = tem;
6169 p = &(tem->next);
6174 void font_special_request()
6176 int n = get_fontno();
6177 if (n >= 0)
6178 read_special_fonts(&font_table[n]->sf);
6179 skip_line();
6182 void special_request()
6184 read_special_fonts(&global_special_fonts);
6185 skip_line();
6188 int next_available_font_position()
6190 int i;
6191 for (i = 1; i < font_table_size && font_table[i] != 0; i++)
6193 return i;
6196 int symbol_fontno(symbol s)
6198 s = get_font_translation(s);
6199 for (int i = 0; i < font_table_size; i++)
6200 if (font_table[i] != 0 && font_table[i]->is_named(s))
6201 return i;
6202 return -1;
6205 int is_good_fontno(int n)
6207 return n >= 0 && n < font_table_size && font_table[n] != 0;
6210 int get_bold_fontno(int n)
6212 if (n >= 0 && n < font_table_size && font_table[n] != 0) {
6213 hunits offset;
6214 if (font_table[n]->get_bold(&offset))
6215 return offset.to_units() + 1;
6216 else
6217 return 0;
6219 else
6220 return 0;
6223 hunits env_digit_width(environment *env)
6225 node *n = make_glyph_node(charset_table['0'], env);
6226 if (n) {
6227 hunits x = n->width();
6228 delete n;
6229 return x;
6231 else
6232 return H0;
6235 hunits env_space_width(environment *env)
6237 int fn = env_definite_font(env);
6238 font_size fs = env->get_font_size();
6239 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6240 return scale(fs.to_units()/3, env->get_space_size(), 12);
6241 else
6242 return font_table[fn]->get_space_width(fs, env->get_space_size());
6245 hunits env_sentence_space_width(environment *env)
6247 int fn = env_definite_font(env);
6248 font_size fs = env->get_font_size();
6249 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6250 return scale(fs.to_units()/3, env->get_sentence_space_size(), 12);
6251 else
6252 return font_table[fn]->get_space_width(fs, env->get_sentence_space_size());
6255 hunits env_half_narrow_space_width(environment *env)
6257 int fn = env_definite_font(env);
6258 font_size fs = env->get_font_size();
6259 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6260 return 0;
6261 else
6262 return font_table[fn]->get_half_narrow_space_width(fs);
6265 hunits env_narrow_space_width(environment *env)
6267 int fn = env_definite_font(env);
6268 font_size fs = env->get_font_size();
6269 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6270 return 0;
6271 else
6272 return font_table[fn]->get_narrow_space_width(fs);
6275 void bold_font()
6277 int n = get_fontno();
6278 if (n >= 0) {
6279 if (has_arg()) {
6280 if (tok.delimiter()) {
6281 int f = get_fontno();
6282 if (f >= 0) {
6283 units offset;
6284 if (has_arg() && get_number(&offset, 'u') && offset >= 1)
6285 font_table[f]->set_conditional_bold(n, hunits(offset - 1));
6286 else
6287 font_table[f]->conditional_unbold(n);
6290 else {
6291 units offset;
6292 if (get_number(&offset, 'u') && offset >= 1)
6293 font_table[n]->set_bold(hunits(offset - 1));
6294 else
6295 font_table[n]->unbold();
6298 else
6299 font_table[n]->unbold();
6301 skip_line();
6304 track_kerning_function::track_kerning_function() : non_zero(0)
6308 track_kerning_function::track_kerning_function(int min_s, hunits min_a,
6309 int max_s, hunits max_a)
6310 : non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s),
6311 max_amount(max_a)
6315 int track_kerning_function::operator==(const track_kerning_function &tk)
6317 if (non_zero)
6318 return (tk.non_zero
6319 && min_size == tk.min_size
6320 && min_amount == tk.min_amount
6321 && max_size == tk.max_size
6322 && max_amount == tk.max_amount);
6323 else
6324 return !tk.non_zero;
6327 int track_kerning_function::operator!=(const track_kerning_function &tk)
6329 if (non_zero)
6330 return (!tk.non_zero
6331 || min_size != tk.min_size
6332 || min_amount != tk.min_amount
6333 || max_size != tk.max_size
6334 || max_amount != tk.max_amount);
6335 else
6336 return tk.non_zero;
6339 hunits track_kerning_function::compute(int size)
6341 if (non_zero) {
6342 if (max_size <= min_size)
6343 return min_amount;
6344 else if (size <= min_size)
6345 return min_amount;
6346 else if (size >= max_size)
6347 return max_amount;
6348 else
6349 return (scale(max_amount, size - min_size, max_size - min_size)
6350 + scale(min_amount, max_size - size, max_size - min_size));
6352 else
6353 return H0;
6356 void track_kern()
6358 int n = get_fontno();
6359 if (n >= 0) {
6360 int min_s, max_s;
6361 hunits min_a, max_a;
6362 if (has_arg()
6363 && get_number(&min_s, 'z')
6364 && get_hunits(&min_a, 'p')
6365 && get_number(&max_s, 'z')
6366 && get_hunits(&max_a, 'p')) {
6367 track_kerning_function tk(min_s, min_a, max_s, max_a);
6368 font_table[n]->set_track_kern(tk);
6370 else {
6371 track_kerning_function tk;
6372 font_table[n]->set_track_kern(tk);
6375 skip_line();
6378 void constant_space()
6380 int n = get_fontno();
6381 if (n >= 0) {
6382 int x, y;
6383 if (!has_arg() || !get_integer(&x))
6384 font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
6385 else {
6386 if (!has_arg() || !get_number(&y, 'z'))
6387 font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
6388 else
6389 font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
6390 scale(y*x,
6391 units_per_inch,
6392 36*72*sizescale));
6395 skip_line();
6398 void ligature()
6400 int lig;
6401 if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2)
6402 global_ligature_mode = lig;
6403 else
6404 global_ligature_mode = 1;
6405 skip_line();
6408 void kern_request()
6410 int k;
6411 if (has_arg() && get_integer(&k))
6412 global_kern_mode = k != 0;
6413 else
6414 global_kern_mode = 1;
6415 skip_line();
6418 void set_soft_hyphen_char()
6420 soft_hyphen_char = get_optional_char();
6421 if (!soft_hyphen_char)
6422 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
6423 skip_line();
6426 void init_output()
6428 if (suppress_output_flag)
6429 the_output = new suppress_output_file;
6430 else if (ascii_output_flag)
6431 the_output = new ascii_output_file;
6432 else
6433 the_output = new troff_output_file;
6436 class next_available_font_position_reg : public reg {
6437 public:
6438 const char *get_string();
6441 const char *next_available_font_position_reg::get_string()
6443 return i_to_a(next_available_font_position());
6446 class printing_reg : public reg {
6447 public:
6448 const char *get_string();
6451 const char *printing_reg::get_string()
6453 if (the_output)
6454 return the_output->is_printing() ? "1" : "0";
6455 else
6456 return "0";
6459 void init_node_requests()
6461 init_request("bd", bold_font);
6462 init_request("cs", constant_space);
6463 init_request("fp", font_position);
6464 init_request("fschar", define_font_special_character);
6465 init_request("fspecial", font_special_request);
6466 init_request("ftr", font_translate);
6467 init_request("kern", kern_request);
6468 init_request("lg", ligature);
6469 init_request("rfschar", remove_font_special_character);
6470 init_request("shc", set_soft_hyphen_char);
6471 init_request("special", special_request);
6472 init_request("sty", style);
6473 init_request("tkf", track_kern);
6474 init_request("uf", underline_font);
6475 number_reg_dictionary.define(".fp", new next_available_font_position_reg);
6476 number_reg_dictionary.define(".kern",
6477 new constant_int_reg(&global_kern_mode));
6478 number_reg_dictionary.define(".lg",
6479 new constant_int_reg(&global_ligature_mode));
6480 number_reg_dictionary.define(".P", new printing_reg);
6481 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);