* src/roff/troff/node.cpp (suppress_node::tprint): Don't expect
[s-roff.git] / src / roff / troff / node.cpp
blobe3d4d86d9040e6490a701bf60e156ae767792d4c
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22 #include "troff.h"
24 #ifdef HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
28 #include "dictionary.h"
29 #include "hvunits.h"
30 #include "env.h"
31 #include "request.h"
32 #include "node.h"
33 #include "token.h"
34 #include "charinfo.h"
35 #include "font.h"
36 #include "reg.h"
37 #include "input.h"
38 #include "div.h"
39 #include "geometry.h"
40 #include "stringclass.h"
42 #include "nonposix.h"
44 #ifdef _POSIX_VERSION
46 #include <sys/wait.h>
48 #else /* not _POSIX_VERSION */
50 /* traditional Unix */
52 #define WIFEXITED(s) (((s) & 0377) == 0)
53 #define WEXITSTATUS(s) (((s) >> 8) & 0377)
54 #define WTERMSIG(s) ((s) & 0177)
55 #define WIFSTOPPED(s) (((s) & 0377) == 0177)
56 #define WSTOPSIG(s) (((s) >> 8) & 0377)
57 #define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
59 #endif /* not _POSIX_VERSION */
62 * how many boundaries of images have been written? Useful for
63 * debugging grohtml
66 int image_no = 0;
67 static int suppress_start_page = 0;
69 #define STORE_WIDTH 1
71 symbol HYPHEN_SYMBOL("hy");
73 // Character used when a hyphen is inserted at a line break.
74 static charinfo *soft_hyphen_char;
76 enum constant_space_type {
77 CONSTANT_SPACE_NONE,
78 CONSTANT_SPACE_RELATIVE,
79 CONSTANT_SPACE_ABSOLUTE
82 struct special_font_list {
83 int n;
84 special_font_list *next;
87 special_font_list *global_special_fonts;
88 static int global_ligature_mode = 1;
89 static int global_kern_mode = 1;
91 class track_kerning_function {
92 int non_zero;
93 units min_size;
94 hunits min_amount;
95 units max_size;
96 hunits max_amount;
97 public:
98 track_kerning_function();
99 track_kerning_function(units, hunits, units, hunits);
100 int operator==(const track_kerning_function &);
101 int operator!=(const track_kerning_function &);
102 hunits compute(int point_size);
105 // embolden fontno when this is the current font
107 struct conditional_bold {
108 conditional_bold *next;
109 int fontno;
110 hunits offset;
111 conditional_bold(int, hunits, conditional_bold * = 0);
114 class tfont;
116 class font_info {
117 tfont *last_tfont;
118 int number;
119 font_size last_size;
120 int last_height;
121 int last_slant;
122 symbol internal_name;
123 symbol external_name;
124 font *fm;
125 char is_bold;
126 hunits bold_offset;
127 track_kerning_function track_kern;
128 constant_space_type is_constant_spaced;
129 units constant_space;
130 int last_ligature_mode;
131 int last_kern_mode;
132 conditional_bold *cond_bold_list;
133 void flush();
134 public:
135 special_font_list *sf;
136 font_info(symbol nm, int n, symbol enm, font *f);
137 int contains(charinfo *);
138 void set_bold(hunits);
139 void unbold();
140 void set_conditional_bold(int, hunits);
141 void conditional_unbold(int);
142 void set_track_kern(track_kerning_function &);
143 void set_constant_space(constant_space_type, units = 0);
144 int is_named(symbol);
145 symbol get_name();
146 tfont *get_tfont(font_size, int, int, int);
147 hunits get_space_width(font_size, int);
148 hunits get_narrow_space_width(font_size);
149 hunits get_half_narrow_space_width(font_size);
150 int get_bold(hunits *);
151 int is_special();
152 int is_style();
153 friend symbol get_font_name(int, environment *);
156 class tfont_spec {
157 protected:
158 symbol name;
159 int input_position;
160 font *fm;
161 font_size size;
162 char is_bold;
163 char is_constant_spaced;
164 int ligature_mode;
165 int kern_mode;
166 hunits bold_offset;
167 hunits track_kern; // add this to the width
168 hunits constant_space_width;
169 int height;
170 int slant;
171 public:
172 tfont_spec(symbol nm, int pos, font *, font_size, int, int);
173 tfont_spec(const tfont_spec &spec) { *this = spec; }
174 tfont_spec plain();
175 int operator==(const tfont_spec &);
176 friend tfont *font_info::get_tfont(font_size fs, int, int, int);
179 class tfont : public tfont_spec {
180 static tfont *tfont_list;
181 tfont *next;
182 tfont *plain_version;
183 public:
184 tfont(tfont_spec &);
185 int contains(charinfo *);
186 hunits get_width(charinfo *c);
187 int get_bold(hunits *);
188 int get_constant_space(hunits *);
189 hunits get_track_kern();
190 tfont *get_plain();
191 font_size get_size();
192 symbol get_name();
193 charinfo *get_lig(charinfo *c1, charinfo *c2);
194 int get_kern(charinfo *c1, charinfo *c2, hunits *res);
195 int get_input_position();
196 int get_character_type(charinfo *);
197 int get_height();
198 int get_slant();
199 vunits get_char_height(charinfo *);
200 vunits get_char_depth(charinfo *);
201 hunits get_char_skew(charinfo *);
202 hunits get_italic_correction(charinfo *);
203 hunits get_left_italic_correction(charinfo *);
204 hunits get_subscript_correction(charinfo *);
205 friend tfont *make_tfont(tfont_spec &);
208 inline int env_definite_font(environment *env)
210 return env->get_family()->make_definite(env->get_font());
213 /* font_info functions */
215 static font_info **font_table = 0;
216 static int font_table_size = 0;
218 font_info::font_info(symbol nm, int n, symbol enm, font *f)
219 : last_tfont(0), number(n), last_size(0),
220 internal_name(nm), external_name(enm), fm(f),
221 is_bold(0), is_constant_spaced(CONSTANT_SPACE_NONE), last_ligature_mode(1),
222 last_kern_mode(1), cond_bold_list(0), sf(0)
226 inline int font_info::contains(charinfo *ci)
228 return fm != 0 && fm->contains(ci->get_index());
231 inline int font_info::is_special()
233 return fm != 0 && fm->is_special();
236 inline int font_info::is_style()
238 return fm == 0;
241 tfont *make_tfont(tfont_spec &spec)
243 for (tfont *p = tfont::tfont_list; p; p = p->next)
244 if (*p == spec)
245 return p;
246 return new tfont(spec);
249 // this is the current_font, fontno is where we found the character,
250 // presumably a special font
252 tfont *font_info::get_tfont(font_size fs, int height, int slant, int fontno)
254 if (last_tfont == 0 || fs != last_size
255 || height != last_height || slant != last_slant
256 || global_ligature_mode != last_ligature_mode
257 || global_kern_mode != last_kern_mode
258 || fontno != number) {
259 font_info *f = font_table[fontno];
260 tfont_spec spec(f->external_name, f->number, f->fm, fs, height, slant);
261 for (conditional_bold *p = cond_bold_list; p; p = p->next)
262 if (p->fontno == fontno) {
263 spec.is_bold = 1;
264 spec.bold_offset = p->offset;
265 break;
267 if (!spec.is_bold && is_bold) {
268 spec.is_bold = 1;
269 spec.bold_offset = bold_offset;
271 spec.track_kern = track_kern.compute(fs.to_scaled_points());
272 spec.ligature_mode = global_ligature_mode;
273 spec.kern_mode = global_kern_mode;
274 switch (is_constant_spaced) {
275 case CONSTANT_SPACE_NONE:
276 break;
277 case CONSTANT_SPACE_ABSOLUTE:
278 spec.is_constant_spaced = 1;
279 spec.constant_space_width = constant_space;
280 break;
281 case CONSTANT_SPACE_RELATIVE:
282 spec.is_constant_spaced = 1;
283 spec.constant_space_width
284 = scale(constant_space*fs.to_scaled_points(),
285 units_per_inch,
286 36*72*sizescale);
287 break;
288 default:
289 assert(0);
291 if (fontno != number)
292 return make_tfont(spec);
293 last_tfont = make_tfont(spec);
294 last_size = fs;
295 last_height = height;
296 last_slant = slant;
297 last_ligature_mode = global_ligature_mode;
298 last_kern_mode = global_kern_mode;
300 return last_tfont;
303 int font_info::get_bold(hunits *res)
305 if (is_bold) {
306 *res = bold_offset;
307 return 1;
309 else
310 return 0;
313 void font_info::unbold()
315 if (is_bold) {
316 is_bold = 0;
317 flush();
321 void font_info::set_bold(hunits offset)
323 if (!is_bold || offset != bold_offset) {
324 is_bold = 1;
325 bold_offset = offset;
326 flush();
330 void font_info::set_conditional_bold(int fontno, hunits offset)
332 for (conditional_bold *p = cond_bold_list; p; p = p->next)
333 if (p->fontno == fontno) {
334 if (offset != p->offset) {
335 p->offset = offset;
336 flush();
338 return;
340 cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
343 conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
344 : next(x), fontno(f), offset(h)
348 void font_info::conditional_unbold(int fontno)
350 for (conditional_bold **p = &cond_bold_list; *p; p = &(*p)->next)
351 if ((*p)->fontno == fontno) {
352 conditional_bold *tem = *p;
353 *p = (*p)->next;
354 delete tem;
355 flush();
356 return;
360 void font_info::set_constant_space(constant_space_type type, units x)
362 if (type != is_constant_spaced
363 || (type != CONSTANT_SPACE_NONE && x != constant_space)) {
364 flush();
365 is_constant_spaced = type;
366 constant_space = x;
370 void font_info::set_track_kern(track_kerning_function &tk)
372 if (track_kern != tk) {
373 track_kern = tk;
374 flush();
378 void font_info::flush()
380 last_tfont = 0;
383 int font_info::is_named(symbol s)
385 return internal_name == s;
388 symbol font_info::get_name()
390 return internal_name;
393 symbol get_font_name(int fontno, environment *env)
395 symbol f = font_table[fontno]->get_name();
396 if (font_table[fontno]->is_style()) {
397 return concat(env->get_family()->nm, f);
399 return f;
402 hunits font_info::get_space_width(font_size fs, int space_sz)
404 if (is_constant_spaced == CONSTANT_SPACE_NONE)
405 return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
406 space_sz, 12);
407 else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
408 return constant_space;
409 else
410 return scale(constant_space*fs.to_scaled_points(),
411 units_per_inch, 36*72*sizescale);
414 hunits font_info::get_narrow_space_width(font_size fs)
416 charinfo *ci = get_charinfo(symbol("|"));
417 if (fm->contains(ci->get_index()))
418 return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
419 else
420 return hunits(fs.to_units()/6);
423 hunits font_info::get_half_narrow_space_width(font_size fs)
425 charinfo *ci = get_charinfo(symbol("^"));
426 if (fm->contains(ci->get_index()))
427 return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
428 else
429 return hunits(fs.to_units()/12);
432 /* tfont */
434 tfont_spec::tfont_spec(symbol nm, int n, font *f,
435 font_size s, int h, int sl)
436 : name(nm), input_position(n), fm(f), size(s),
437 is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1),
438 height(h), slant(sl)
440 if (height == size.to_scaled_points())
441 height = 0;
444 int tfont_spec::operator==(const tfont_spec &spec)
446 if (fm == spec.fm
447 && size == spec.size
448 && input_position == spec.input_position
449 && name == spec.name
450 && height == spec.height
451 && slant == spec.slant
452 && (is_bold
453 ? (spec.is_bold && bold_offset == spec.bold_offset)
454 : !spec.is_bold)
455 && track_kern == spec.track_kern
456 && (is_constant_spaced
457 ? (spec.is_constant_spaced
458 && constant_space_width == spec.constant_space_width)
459 : !spec.is_constant_spaced)
460 && ligature_mode == spec.ligature_mode
461 && kern_mode == spec.kern_mode)
462 return 1;
463 else
464 return 0;
467 tfont_spec tfont_spec::plain()
469 return tfont_spec(name, input_position, fm, size, height, slant);
472 hunits tfont::get_width(charinfo *c)
474 if (is_constant_spaced)
475 return constant_space_width;
476 else if (is_bold)
477 return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
478 + track_kern + bold_offset);
479 else
480 return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
481 + track_kern);
484 vunits tfont::get_char_height(charinfo *c)
486 vunits v = fm->get_height(c->get_index(), size.to_scaled_points());
487 if (height != 0 && height != size.to_scaled_points())
488 return scale(v, height, size.to_scaled_points());
489 else
490 return v;
493 vunits tfont::get_char_depth(charinfo *c)
495 vunits v = fm->get_depth(c->get_index(), size.to_scaled_points());
496 if (height != 0 && height != size.to_scaled_points())
497 return scale(v, height, size.to_scaled_points());
498 else
499 return v;
502 hunits tfont::get_char_skew(charinfo *c)
504 return hunits(fm->get_skew(c->get_index(), size.to_scaled_points(), slant));
507 hunits tfont::get_italic_correction(charinfo *c)
509 return hunits(fm->get_italic_correction(c->get_index(), size.to_scaled_points()));
512 hunits tfont::get_left_italic_correction(charinfo *c)
514 return hunits(fm->get_left_italic_correction(c->get_index(),
515 size.to_scaled_points()));
518 hunits tfont::get_subscript_correction(charinfo *c)
520 return hunits(fm->get_subscript_correction(c->get_index(),
521 size.to_scaled_points()));
524 inline int tfont::get_input_position()
526 return input_position;
529 inline int tfont::contains(charinfo *ci)
531 return fm->contains(ci->get_index());
534 inline int tfont::get_character_type(charinfo *ci)
536 return fm->get_character_type(ci->get_index());
539 inline int tfont::get_bold(hunits *res)
541 if (is_bold) {
542 *res = bold_offset;
543 return 1;
545 else
546 return 0;
549 inline int tfont::get_constant_space(hunits *res)
551 if (is_constant_spaced) {
552 *res = constant_space_width;
553 return 1;
555 else
556 return 0;
559 inline hunits tfont::get_track_kern()
561 return track_kern;
564 inline tfont *tfont::get_plain()
566 return plain_version;
569 inline font_size tfont::get_size()
571 return size;
574 inline symbol tfont::get_name()
576 return name;
579 inline int tfont::get_height()
581 return height;
584 inline int tfont::get_slant()
586 return slant;
589 symbol SYMBOL_ff("ff");
590 symbol SYMBOL_fi("fi");
591 symbol SYMBOL_fl("fl");
592 symbol SYMBOL_Fi("Fi");
593 symbol SYMBOL_Fl("Fl");
595 charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
597 if (ligature_mode == 0)
598 return 0;
599 charinfo *ci = 0;
600 if (c1->get_ascii_code() == 'f') {
601 switch (c2->get_ascii_code()) {
602 case 'f':
603 if (fm->has_ligature(font::LIG_ff))
604 ci = get_charinfo(SYMBOL_ff);
605 break;
606 case 'i':
607 if (fm->has_ligature(font::LIG_fi))
608 ci = get_charinfo(SYMBOL_fi);
609 break;
610 case 'l':
611 if (fm->has_ligature(font::LIG_fl))
612 ci = get_charinfo(SYMBOL_fl);
613 break;
616 else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
617 switch (c2->get_ascii_code()) {
618 case 'i':
619 if (fm->has_ligature(font::LIG_ffi))
620 ci = get_charinfo(SYMBOL_Fi);
621 break;
622 case 'l':
623 if (fm->has_ligature(font::LIG_ffl))
624 ci = get_charinfo(SYMBOL_Fl);
625 break;
628 if (ci != 0 && fm->contains(ci->get_index()))
629 return ci;
630 return 0;
633 inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res)
635 if (kern_mode == 0)
636 return 0;
637 else {
638 int n = fm->get_kern(c1->get_index(),
639 c2->get_index(),
640 size.to_scaled_points());
641 if (n) {
642 *res = hunits(n);
643 return 1;
645 else
646 return 0;
650 tfont *tfont::tfont_list = 0;
652 tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
654 next = tfont_list;
655 tfont_list = this;
656 tfont_spec plain_spec = plain();
657 tfont *p;
658 for (p = tfont_list; p; p = p->next)
659 if (*p == plain_spec) {
660 plain_version = p;
661 break;
663 if (!p)
664 plain_version = new tfont(plain_spec);
667 /* output_file */
669 class real_output_file : public output_file {
670 #ifndef POPEN_MISSING
671 int piped;
672 #endif
673 int printing; // decision via optional page list
674 int output_on; // \O[0] or \O[1] escape calls
675 virtual void really_transparent_char(unsigned char) = 0;
676 virtual void really_print_line(hunits x, vunits y, node *n,
677 vunits before, vunits after, hunits width) = 0;
678 virtual void really_begin_page(int pageno, vunits page_length) = 0;
679 virtual void really_copy_file(hunits x, vunits y, const char *filename);
680 virtual void really_put_filename(const char *filename);
681 virtual void really_on();
682 virtual void really_off();
683 protected:
684 FILE *fp;
685 public:
686 real_output_file();
687 ~real_output_file();
688 void flush();
689 void transparent_char(unsigned char);
690 void print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
691 void begin_page(int pageno, vunits page_length);
692 void put_filename(const char *filename);
693 void on();
694 void off();
695 int is_on();
696 int is_printing();
697 void copy_file(hunits x, vunits y, const char *filename);
700 class suppress_output_file : public real_output_file {
701 public:
702 suppress_output_file();
703 void really_transparent_char(unsigned char);
704 void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
705 void really_begin_page(int pageno, vunits page_length);
708 class ascii_output_file : public real_output_file {
709 public:
710 ascii_output_file();
711 void really_transparent_char(unsigned char);
712 void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
713 void really_begin_page(int pageno, vunits page_length);
714 void outc(unsigned char c);
715 void outs(const char *s);
718 void ascii_output_file::outc(unsigned char c)
720 fputc(c, fp);
723 void ascii_output_file::outs(const char *s)
725 fputc('<', fp);
726 if (s)
727 fputs(s, fp);
728 fputc('>', fp);
731 struct hvpair;
733 class troff_output_file : public real_output_file {
734 units hpos;
735 units vpos;
736 units output_vpos;
737 units output_hpos;
738 int force_motion;
739 int current_size;
740 int current_slant;
741 int current_height;
742 tfont *current_tfont;
743 color *current_fill_color;
744 color *current_glyph_color;
745 int current_font_number;
746 symbol *font_position;
747 int nfont_positions;
748 enum { TBUF_SIZE = 256 };
749 char tbuf[TBUF_SIZE];
750 int tbuf_len;
751 int tbuf_kern;
752 int begun_page;
753 void do_motion();
754 void put(char c);
755 void put(unsigned char c);
756 void put(int i);
757 void put(unsigned int i);
758 void put(const char *s);
759 void set_font(tfont *tf);
760 void flush_tbuf();
761 public:
762 troff_output_file();
763 ~troff_output_file();
764 void trailer(vunits page_length);
765 void put_char(charinfo *, tfont *, color *, color *);
766 void put_char_width(charinfo *, tfont *, color *, color *, hunits, hunits);
767 void right(hunits);
768 void down(vunits);
769 void moveto(hunits, vunits);
770 void start_special(tfont *, color *, color *, int = 0);
771 void start_special();
772 void special_char(unsigned char c);
773 void end_special();
774 void word_marker();
775 void really_transparent_char(unsigned char c);
776 void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
777 void really_begin_page(int pageno, vunits page_length);
778 void really_copy_file(hunits x, vunits y, const char *filename);
779 void really_put_filename(const char *filename);
780 void really_on();
781 void really_off();
782 void draw(char, hvpair *, int, font_size, color *, color *);
783 void determine_line_limits (char code, hvpair *point, int npoints);
784 void check_charinfo(tfont *tf, charinfo *ci);
785 void glyph_color(color *c);
786 void fill_color(color *c);
787 int get_hpos() { return hpos; }
788 int get_vpos() { return vpos; }
789 friend void space_char_hmotion_node::tprint(troff_output_file *);
790 friend void unbreakable_space_node::tprint(troff_output_file *);
793 static void put_string(const char *s, FILE *fp)
795 for (; *s != '\0'; ++s)
796 putc(*s, fp);
799 inline void troff_output_file::put(char c)
801 putc(c, fp);
804 inline void troff_output_file::put(unsigned char c)
806 putc(c, fp);
809 inline void troff_output_file::put(const char *s)
811 put_string(s, fp);
814 inline void troff_output_file::put(int i)
816 put_string(i_to_a(i), fp);
819 inline void troff_output_file::put(unsigned int i)
821 put_string(ui_to_a(i), fp);
824 void troff_output_file::start_special(tfont *tf, color *gcol, color *fcol,
825 int no_init_string)
827 set_font(tf);
828 glyph_color(gcol);
829 fill_color(fcol);
830 flush_tbuf();
831 do_motion();
832 if (!no_init_string)
833 put("x X ");
836 void troff_output_file::start_special()
838 flush_tbuf();
839 do_motion();
840 put("x X ");
843 void troff_output_file::special_char(unsigned char c)
845 put(c);
846 if (c == '\n')
847 put('+');
850 void troff_output_file::end_special()
852 put('\n');
855 inline void troff_output_file::moveto(hunits h, vunits v)
857 hpos = h.to_units();
858 vpos = v.to_units();
861 void troff_output_file::really_print_line(hunits x, vunits y, node *n,
862 vunits before, vunits after, hunits)
864 moveto(x, y);
865 while (n != 0) {
866 n->tprint(this);
867 n = n->next;
869 flush_tbuf();
870 // This ensures that transparent throughput will have a more predictable
871 // position.
872 do_motion();
873 force_motion = 1;
874 hpos = 0;
875 put('n');
876 put(before.to_units());
877 put(' ');
878 put(after.to_units());
879 put('\n');
882 inline void troff_output_file::word_marker()
884 flush_tbuf();
885 if (is_on())
886 put('w');
889 inline void troff_output_file::right(hunits n)
891 hpos += n.to_units();
894 inline void troff_output_file::down(vunits n)
896 vpos += n.to_units();
899 void troff_output_file::do_motion()
901 if (force_motion) {
902 put('V');
903 put(vpos);
904 put('\n');
905 put('H');
906 put(hpos);
907 put('\n');
909 else {
910 if (hpos != output_hpos) {
911 units n = hpos - output_hpos;
912 if (n > 0 && n < hpos) {
913 put('h');
914 put(n);
916 else {
917 put('H');
918 put(hpos);
920 put('\n');
922 if (vpos != output_vpos) {
923 units n = vpos - output_vpos;
924 if (n > 0 && n < vpos) {
925 put('v');
926 put(n);
928 else {
929 put('V');
930 put(vpos);
932 put('\n');
935 output_vpos = vpos;
936 output_hpos = hpos;
937 force_motion = 0;
940 void troff_output_file::flush_tbuf()
942 if (!is_on()) {
943 tbuf_len = 0;
944 return;
947 if (tbuf_len == 0)
948 return;
949 if (tbuf_kern == 0)
950 put('t');
951 else {
952 put('u');
953 put(tbuf_kern);
954 put(' ');
956 check_output_limits(hpos, vpos);
957 check_output_limits(hpos, vpos - current_size);
959 for (int i = 0; i < tbuf_len; i++)
960 put(tbuf[i]);
961 put('\n');
962 tbuf_len = 0;
965 void troff_output_file::check_charinfo(tfont *tf, charinfo *ci)
967 if (!is_on())
968 return;
970 int height = tf->get_char_height(ci).to_units();
971 int width = tf->get_width(ci).to_units()
972 + tf->get_italic_correction(ci).to_units();
973 int depth = tf->get_char_depth(ci).to_units();
974 check_output_limits(output_hpos, output_vpos - height);
975 check_output_limits(output_hpos + width, output_vpos + depth);
978 void troff_output_file::put_char_width(charinfo *ci, tfont *tf,
979 color *gcol, color *fcol,
980 hunits w, hunits k)
982 int kk = k.to_units();
983 if (!is_on()) {
984 flush_tbuf();
985 hpos += w.to_units() + kk;
986 return;
988 set_font(tf);
989 unsigned char c = ci->get_ascii_code();
990 if (c == '\0') {
991 glyph_color(gcol);
992 fill_color(fcol);
993 flush_tbuf();
994 do_motion();
995 check_charinfo(tf, ci);
996 if (ci->numbered()) {
997 put('N');
998 put(ci->get_number());
1000 else {
1001 put('C');
1002 const char *s = ci->nm.contents();
1003 if (s[1] == 0) {
1004 put('\\');
1005 put(s[0]);
1007 else
1008 put(s);
1010 put('\n');
1011 hpos += w.to_units() + kk;
1013 else if (tcommand_flag) {
1014 if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
1015 && (!gcol || gcol == current_glyph_color)
1016 && (!fcol || fcol == current_fill_color)
1017 && kk == tbuf_kern
1018 && tbuf_len < TBUF_SIZE) {
1019 check_charinfo(tf, ci);
1020 tbuf[tbuf_len++] = c;
1021 output_hpos += w.to_units() + kk;
1022 hpos = output_hpos;
1023 return;
1025 glyph_color(gcol);
1026 fill_color(fcol);
1027 flush_tbuf();
1028 do_motion();
1029 check_charinfo(tf, ci);
1030 tbuf[tbuf_len++] = c;
1031 output_hpos += w.to_units() + kk;
1032 tbuf_kern = kk;
1033 hpos = output_hpos;
1035 else {
1036 // flush_tbuf();
1037 int n = hpos - output_hpos;
1038 check_charinfo(tf, ci);
1039 // check_output_limits(output_hpos, output_vpos);
1040 if (vpos == output_vpos
1041 && (!gcol || gcol == current_glyph_color)
1042 && (!fcol || fcol == current_fill_color)
1043 && n > 0 && n < 100 && !force_motion) {
1044 put(char(n/10 + '0'));
1045 put(char(n%10 + '0'));
1046 put(c);
1047 output_hpos = hpos;
1049 else {
1050 glyph_color(gcol);
1051 fill_color(fcol);
1052 do_motion();
1053 put('c');
1054 put(c);
1056 hpos += w.to_units() + kk;
1060 void troff_output_file::put_char(charinfo *ci, tfont *tf,
1061 color *gcol, color *fcol)
1063 flush_tbuf();
1064 if (!is_on())
1065 return;
1066 set_font(tf);
1067 unsigned char c = ci->get_ascii_code();
1068 if (c == '\0') {
1069 glyph_color(gcol);
1070 fill_color(fcol);
1071 flush_tbuf();
1072 do_motion();
1073 if (ci->numbered()) {
1074 put('N');
1075 put(ci->get_number());
1077 else {
1078 put('C');
1079 const char *s = ci->nm.contents();
1080 if (s[1] == 0) {
1081 put('\\');
1082 put(s[0]);
1084 else
1085 put(s);
1087 put('\n');
1089 else {
1090 int n = hpos - output_hpos;
1091 if (vpos == output_vpos
1092 && (!gcol || gcol == current_glyph_color)
1093 && (!fcol || fcol == current_fill_color)
1094 && n > 0 && n < 100) {
1095 put(char(n/10 + '0'));
1096 put(char(n%10 + '0'));
1097 put(c);
1098 output_hpos = hpos;
1100 else {
1101 glyph_color(gcol);
1102 fill_color(fcol);
1103 flush_tbuf();
1104 do_motion();
1105 put('c');
1106 put(c);
1111 // set_font calls `flush_tbuf' if necessary.
1113 void troff_output_file::set_font(tfont *tf)
1115 if (current_tfont == tf)
1116 return;
1117 flush_tbuf();
1118 int n = tf->get_input_position();
1119 symbol nm = tf->get_name();
1120 if (n >= nfont_positions || font_position[n] != nm) {
1121 put("x font ");
1122 put(n);
1123 put(' ');
1124 put(nm.contents());
1125 put('\n');
1126 if (n >= nfont_positions) {
1127 int old_nfont_positions = nfont_positions;
1128 symbol *old_font_position = font_position;
1129 nfont_positions *= 3;
1130 nfont_positions /= 2;
1131 if (nfont_positions <= n)
1132 nfont_positions = n + 10;
1133 font_position = new symbol[nfont_positions];
1134 memcpy(font_position, old_font_position,
1135 old_nfont_positions*sizeof(symbol));
1136 a_delete old_font_position;
1138 font_position[n] = nm;
1140 if (current_font_number != n) {
1141 put('f');
1142 put(n);
1143 put('\n');
1144 current_font_number = n;
1146 int size = tf->get_size().to_scaled_points();
1147 if (current_size != size) {
1148 put('s');
1149 put(size);
1150 put('\n');
1151 current_size = size;
1153 int slant = tf->get_slant();
1154 if (current_slant != slant) {
1155 put("x Slant ");
1156 put(slant);
1157 put('\n');
1158 current_slant = slant;
1160 int height = tf->get_height();
1161 if (current_height != height) {
1162 put("x Height ");
1163 put(height == 0 ? current_size : height);
1164 put('\n');
1165 current_height = height;
1167 current_tfont = tf;
1170 // fill_color calls `flush_tbuf' and `do_motion' if necessary.
1172 void troff_output_file::fill_color(color *col)
1174 if (!col || current_fill_color == col)
1175 return;
1176 current_fill_color = col;
1177 if (!color_flag)
1178 return;
1179 flush_tbuf();
1180 do_motion();
1181 put("DF");
1182 unsigned int components[4];
1183 color_scheme cs;
1184 cs = col->get_components(components);
1185 switch (cs) {
1186 case DEFAULT:
1187 put('d');
1188 break;
1189 case RGB:
1190 put("r ");
1191 put(Red);
1192 put(' ');
1193 put(Green);
1194 put(' ');
1195 put(Blue);
1196 break;
1197 case CMY:
1198 put("c ");
1199 put(Cyan);
1200 put(' ');
1201 put(Magenta);
1202 put(' ');
1203 put(Yellow);
1204 break;
1205 case CMYK:
1206 put("k ");
1207 put(Cyan);
1208 put(' ');
1209 put(Magenta);
1210 put(' ');
1211 put(Yellow);
1212 put(' ');
1213 put(Black);
1214 break;
1215 case GRAY:
1216 put("g ");
1217 put(Gray);
1218 break;
1220 put('\n');
1223 // glyph_color calls `flush_tbuf' and `do_motion' if necessary.
1225 void troff_output_file::glyph_color(color *col)
1227 if (!col || current_glyph_color == col)
1228 return;
1229 current_glyph_color = col;
1230 if (!color_flag)
1231 return;
1232 flush_tbuf();
1233 // grotty doesn't like a color command if the vertical position is zero.
1234 do_motion();
1235 put("m");
1236 unsigned int components[4];
1237 color_scheme cs;
1238 cs = col->get_components(components);
1239 switch (cs) {
1240 case DEFAULT:
1241 put('d');
1242 break;
1243 case RGB:
1244 put("r ");
1245 put(Red);
1246 put(' ');
1247 put(Green);
1248 put(' ');
1249 put(Blue);
1250 break;
1251 case CMY:
1252 put("c ");
1253 put(Cyan);
1254 put(' ');
1255 put(Magenta);
1256 put(' ');
1257 put(Yellow);
1258 break;
1259 case CMYK:
1260 put("k ");
1261 put(Cyan);
1262 put(' ');
1263 put(Magenta);
1264 put(' ');
1265 put(Yellow);
1266 put(' ');
1267 put(Black);
1268 break;
1269 case GRAY:
1270 put("g ");
1271 put(Gray);
1272 break;
1274 put('\n');
1277 // determine_line_limits - works out the smallest box which will contain
1278 // the entity, code, built from the point array.
1279 void troff_output_file::determine_line_limits(char code, hvpair *point,
1280 int npoints)
1282 int i, x, y;
1284 if (!is_on())
1285 return;
1287 switch (code) {
1288 case 'c':
1289 case 'C':
1290 // only the h field is used when defining a circle
1291 check_output_limits(output_hpos,
1292 output_vpos - point[0].h.to_units()/2);
1293 check_output_limits(output_hpos + point[0].h.to_units(),
1294 output_vpos + point[0].h.to_units()/2);
1295 break;
1296 case 'E':
1297 case 'e':
1298 check_output_limits(output_hpos,
1299 output_vpos - point[0].v.to_units()/2);
1300 check_output_limits(output_hpos + point[0].h.to_units(),
1301 output_vpos + point[0].v.to_units()/2);
1302 break;
1303 case 'P':
1304 case 'p':
1305 x = output_hpos;
1306 y = output_vpos;
1307 check_output_limits(x, y);
1308 for (i = 0; i < npoints; i++) {
1309 x += point[i].h.to_units();
1310 y += point[i].v.to_units();
1311 check_output_limits(x, y);
1313 break;
1314 case 't':
1315 x = output_hpos;
1316 y = output_vpos;
1317 for (i = 0; i < npoints; i++) {
1318 x += point[i].h.to_units();
1319 y += point[i].v.to_units();
1320 check_output_limits(x, y);
1322 break;
1323 case 'a':
1324 double c[2];
1325 int p[4];
1326 int minx, miny, maxx, maxy;
1327 x = output_hpos;
1328 y = output_vpos;
1329 p[0] = point[0].h.to_units();
1330 p[1] = point[0].v.to_units();
1331 p[2] = point[1].h.to_units();
1332 p[3] = point[1].v.to_units();
1333 if (adjust_arc_center(p, c)) {
1334 check_output_arc_limits(x, y,
1335 p[0], p[1], p[2], p[3],
1336 c[0], c[1],
1337 &minx, &maxx, &miny, &maxy);
1338 check_output_limits(minx, miny);
1339 check_output_limits(maxx, maxy);
1340 break;
1342 // fall through
1343 case 'l':
1344 x = output_hpos;
1345 y = output_vpos;
1346 check_output_limits(x, y);
1347 for (i = 0; i < npoints; i++) {
1348 x += point[i].h.to_units();
1349 y += point[i].v.to_units();
1350 check_output_limits(x, y);
1352 break;
1353 default:
1354 x = output_hpos;
1355 y = output_vpos;
1356 for (i = 0; i < npoints; i++) {
1357 x += point[i].h.to_units();
1358 y += point[i].v.to_units();
1359 check_output_limits(x, y);
1364 void troff_output_file::draw(char code, hvpair *point, int npoints,
1365 font_size fsize, color *gcol, color *fcol)
1367 int i;
1368 glyph_color(gcol);
1369 fill_color(fcol);
1370 flush_tbuf();
1371 do_motion();
1372 if (is_on()) {
1373 int size = fsize.to_scaled_points();
1374 if (current_size != size) {
1375 put('s');
1376 put(size);
1377 put('\n');
1378 current_size = size;
1379 current_tfont = 0;
1381 put('D');
1382 put(code);
1383 if (code == 'c') {
1384 put(' ');
1385 put(point[0].h.to_units());
1387 else
1388 for (i = 0; i < npoints; i++) {
1389 put(' ');
1390 put(point[i].h.to_units());
1391 put(' ');
1392 put(point[i].v.to_units());
1394 determine_line_limits(code, point, npoints);
1397 for (i = 0; i < npoints; i++)
1398 output_hpos += point[i].h.to_units();
1399 hpos = output_hpos;
1400 if (code != 'e') {
1401 for (i = 0; i < npoints; i++)
1402 output_vpos += point[i].v.to_units();
1403 vpos = output_vpos;
1405 if (is_on())
1406 put('\n');
1409 void troff_output_file::really_on()
1411 flush_tbuf();
1412 force_motion = 1;
1413 do_motion();
1416 void troff_output_file::really_off()
1418 flush_tbuf();
1421 void troff_output_file::really_put_filename(const char *filename)
1423 flush_tbuf();
1424 put("F ");
1425 put(filename);
1426 put('\n');
1429 void troff_output_file::really_begin_page(int pageno, vunits page_length)
1431 flush_tbuf();
1432 if (begun_page) {
1433 if (page_length > V0) {
1434 put('V');
1435 put(page_length.to_units());
1436 put('\n');
1439 else
1440 begun_page = 1;
1441 current_tfont = 0;
1442 current_font_number = -1;
1443 current_size = 0;
1444 // current_height = 0;
1445 // current_slant = 0;
1446 hpos = 0;
1447 vpos = 0;
1448 output_hpos = 0;
1449 output_vpos = 0;
1450 force_motion = 1;
1451 for (int i = 0; i < nfont_positions; i++)
1452 font_position[i] = NULL_SYMBOL;
1453 put('p');
1454 put(pageno);
1455 put('\n');
1458 void troff_output_file::really_copy_file(hunits x, vunits y,
1459 const char *filename)
1461 moveto(x, y);
1462 flush_tbuf();
1463 do_motion();
1464 errno = 0;
1465 FILE *ifp = include_search_path.open_file_cautious(filename);
1466 if (ifp == 0)
1467 error("can't open `%1': %2", filename, strerror(errno));
1468 else {
1469 int c;
1470 while ((c = getc(ifp)) != EOF)
1471 put(char(c));
1472 fclose(ifp);
1474 force_motion = 1;
1475 current_size = 0;
1476 current_tfont = 0;
1477 current_font_number = -1;
1478 for (int i = 0; i < nfont_positions; i++)
1479 font_position[i] = NULL_SYMBOL;
1482 void troff_output_file::really_transparent_char(unsigned char c)
1484 put(c);
1487 troff_output_file::~troff_output_file()
1489 a_delete font_position;
1492 void troff_output_file::trailer(vunits page_length)
1494 flush_tbuf();
1495 if (page_length > V0) {
1496 put("x trailer\n");
1497 put('V');
1498 put(page_length.to_units());
1499 put('\n');
1501 put("x stop\n");
1504 troff_output_file::troff_output_file()
1505 : current_slant(0), current_height(0), current_fill_color(0),
1506 current_glyph_color(0), nfont_positions(10), tbuf_len(0), begun_page(0)
1508 font_position = new symbol[nfont_positions];
1509 put("x T ");
1510 put(device);
1511 put('\n');
1512 put("x res ");
1513 put(units_per_inch);
1514 put(' ');
1515 put(hresolution);
1516 put(' ');
1517 put(vresolution);
1518 put('\n');
1519 put("x init\n");
1522 /* output_file */
1524 output_file *the_output = 0;
1526 output_file::output_file()
1530 output_file::~output_file()
1534 void output_file::trailer(vunits)
1538 void output_file::put_filename(const char *)
1542 void output_file::on()
1546 void output_file::off()
1550 real_output_file::real_output_file()
1551 : printing(0), output_on(1)
1553 #ifndef POPEN_MISSING
1554 if (pipe_command) {
1555 if ((fp = popen(pipe_command, POPEN_WT)) != 0) {
1556 piped = 1;
1557 return;
1559 error("pipe open failed: %1", strerror(errno));
1561 piped = 0;
1562 #endif /* not POPEN_MISSING */
1563 fp = stdout;
1566 real_output_file::~real_output_file()
1568 if (!fp)
1569 return;
1570 // To avoid looping, set fp to 0 before calling fatal().
1571 if (ferror(fp) || fflush(fp) < 0) {
1572 fp = 0;
1573 fatal("error writing output file");
1575 #ifndef POPEN_MISSING
1576 if (piped) {
1577 int result = pclose(fp);
1578 fp = 0;
1579 if (result < 0)
1580 fatal("pclose failed");
1581 if (!WIFEXITED(result))
1582 error("output process `%1' got fatal signal %2",
1583 pipe_command,
1584 WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
1585 else {
1586 int exit_status = WEXITSTATUS(result);
1587 if (exit_status != 0)
1588 error("output process `%1' exited with status %2",
1589 pipe_command, exit_status);
1592 else
1593 #endif /* not POPEN MISSING */
1594 if (fclose(fp) < 0) {
1595 fp = 0;
1596 fatal("error closing output file");
1600 void real_output_file::flush()
1602 if (fflush(fp) < 0)
1603 fatal("error writing output file");
1606 int real_output_file::is_printing()
1608 return printing;
1611 void real_output_file::begin_page(int pageno, vunits page_length)
1613 printing = in_output_page_list(pageno);
1614 if (printing)
1615 really_begin_page(pageno, page_length);
1618 void real_output_file::copy_file(hunits x, vunits y, const char *filename)
1620 if (printing && output_on)
1621 really_copy_file(x, y, filename);
1622 check_output_limits(x.to_units(), y.to_units());
1625 void real_output_file::transparent_char(unsigned char c)
1627 if (printing && output_on)
1628 really_transparent_char(c);
1631 void real_output_file::print_line(hunits x, vunits y, node *n,
1632 vunits before, vunits after, hunits width)
1634 if (printing)
1635 really_print_line(x, y, n, before, after, width);
1636 delete_node_list(n);
1639 void real_output_file::really_copy_file(hunits, vunits, const char *)
1641 // do nothing
1644 void real_output_file::put_filename(const char *filename)
1646 really_put_filename(filename);
1649 void real_output_file::really_put_filename(const char *)
1653 void real_output_file::on()
1655 really_on();
1656 if (output_on == 0)
1657 output_on = 1;
1660 void real_output_file::off()
1662 really_off();
1663 output_on = 0;
1666 int real_output_file::is_on()
1668 return output_on;
1671 void real_output_file::really_on()
1675 void real_output_file::really_off()
1679 /* ascii_output_file */
1681 void ascii_output_file::really_transparent_char(unsigned char c)
1683 putc(c, fp);
1686 void ascii_output_file::really_print_line(hunits, vunits, node *n,
1687 vunits, vunits, hunits)
1689 while (n != 0) {
1690 n->ascii_print(this);
1691 n = n->next;
1693 fputc('\n', fp);
1696 void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/)
1698 fputs("<beginning of page>\n", fp);
1701 ascii_output_file::ascii_output_file()
1705 /* suppress_output_file */
1707 suppress_output_file::suppress_output_file()
1711 void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits, hunits)
1715 void suppress_output_file::really_begin_page(int, vunits)
1719 void suppress_output_file::really_transparent_char(unsigned char)
1723 /* glyphs, ligatures, kerns, discretionary breaks */
1725 class charinfo_node : public node {
1726 protected:
1727 charinfo *ci;
1728 public:
1729 charinfo_node(charinfo *, node * = 0);
1730 int ends_sentence();
1731 int overlaps_vertically();
1732 int overlaps_horizontally();
1735 charinfo_node::charinfo_node(charinfo *c, node *x)
1736 : node(x), ci(c)
1740 int charinfo_node::ends_sentence()
1742 if (ci->ends_sentence())
1743 return 1;
1744 else if (ci->transparent())
1745 return 2;
1746 else
1747 return 0;
1750 int charinfo_node::overlaps_horizontally()
1752 return ci->overlaps_horizontally();
1755 int charinfo_node::overlaps_vertically()
1757 return ci->overlaps_vertically();
1760 class glyph_node : public charinfo_node {
1761 static glyph_node *free_list;
1762 protected:
1763 tfont *tf;
1764 color *gcol;
1765 color *fcol; /* this is needed for grotty */
1766 #ifdef STORE_WIDTH
1767 hunits wid;
1768 glyph_node(charinfo *, tfont *, color *, color *, hunits, node * = 0);
1769 #endif
1770 public:
1771 void *operator new(size_t);
1772 void operator delete(void *);
1773 glyph_node(charinfo *, tfont *, color *, color *, node * = 0);
1774 ~glyph_node() {}
1775 node *copy();
1776 node *merge_glyph_node(glyph_node *);
1777 node *merge_self(node *);
1778 hunits width();
1779 node *last_char_node();
1780 units size();
1781 void vertical_extent(vunits *, vunits *);
1782 hunits subscript_correction();
1783 hunits italic_correction();
1784 hunits left_italic_correction();
1785 hunits skew();
1786 hyphenation_type get_hyphenation_type();
1787 tfont *get_tfont();
1788 color *get_glyph_color();
1789 color *get_fill_color();
1790 void tprint(troff_output_file *);
1791 void zero_width_tprint(troff_output_file *);
1792 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1793 node *add_self(node *, hyphen_list **);
1794 void ascii_print(ascii_output_file *);
1795 void asciify(macro *);
1796 int character_type();
1797 int same(node *);
1798 const char *type();
1799 int force_tprint();
1802 glyph_node *glyph_node::free_list = 0;
1804 class ligature_node : public glyph_node {
1805 node *n1;
1806 node *n2;
1807 #ifdef STORE_WIDTH
1808 ligature_node(charinfo *, tfont *, color *, color *, hunits,
1809 node *, node *, node * = 0);
1810 #endif
1811 public:
1812 void *operator new(size_t);
1813 void operator delete(void *);
1814 ligature_node(charinfo *, tfont *, color *, color *,
1815 node *, node *, node * = 0);
1816 ~ligature_node();
1817 node *copy();
1818 node *add_self(node *, hyphen_list **);
1819 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1820 void ascii_print(ascii_output_file *);
1821 void asciify(macro *);
1822 int same(node *);
1823 const char *type();
1824 int force_tprint();
1827 class kern_pair_node : public node {
1828 hunits amount;
1829 node *n1;
1830 node *n2;
1831 public:
1832 kern_pair_node(hunits n, node *first, node *second, node *x = 0);
1833 ~kern_pair_node();
1834 node *copy();
1835 node *merge_glyph_node(glyph_node *);
1836 node *add_self(node *, hyphen_list **);
1837 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1838 node *add_discretionary_hyphen();
1839 hunits width();
1840 node *last_char_node();
1841 hunits italic_correction();
1842 hunits subscript_correction();
1843 void tprint(troff_output_file *);
1844 hyphenation_type get_hyphenation_type();
1845 int ends_sentence();
1846 void ascii_print(ascii_output_file *);
1847 void asciify(macro *);
1848 int same(node *);
1849 const char *type();
1850 int force_tprint();
1851 void vertical_extent(vunits *, vunits *);
1854 class dbreak_node : public node {
1855 node *none;
1856 node *pre;
1857 node *post;
1858 public:
1859 dbreak_node(node *n, node *p, node *x = 0);
1860 ~dbreak_node();
1861 node *copy();
1862 node *merge_glyph_node(glyph_node *);
1863 node *add_discretionary_hyphen();
1864 hunits width();
1865 node *last_char_node();
1866 hunits italic_correction();
1867 hunits subscript_correction();
1868 void tprint(troff_output_file *);
1869 breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
1870 int is_inner = 0);
1871 int nbreaks();
1872 int ends_sentence();
1873 void split(int, node **, node **);
1874 hyphenation_type get_hyphenation_type();
1875 void ascii_print(ascii_output_file *);
1876 void asciify(macro *);
1877 int same(node *);
1878 const char *type();
1879 int force_tprint();
1882 void *glyph_node::operator new(size_t n)
1884 assert(n == sizeof(glyph_node));
1885 if (!free_list) {
1886 const int BLOCK = 1024;
1887 free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
1888 for (int i = 0; i < BLOCK - 1; i++)
1889 free_list[i].next = free_list + i + 1;
1890 free_list[BLOCK-1].next = 0;
1892 glyph_node *p = free_list;
1893 free_list = (glyph_node *)(free_list->next);
1894 p->next = 0;
1895 return p;
1898 void *ligature_node::operator new(size_t n)
1900 return new char[n];
1903 void glyph_node::operator delete(void *p)
1905 if (p) {
1906 ((glyph_node *)p)->next = free_list;
1907 free_list = (glyph_node *)p;
1911 void ligature_node::operator delete(void *p)
1913 delete[] (char *)p;
1916 glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc, node *x)
1917 : charinfo_node(c, x), tf(t), gcol(gc), fcol(fc)
1919 #ifdef STORE_WIDTH
1920 wid = tf->get_width(ci);
1921 #endif
1924 #ifdef STORE_WIDTH
1925 glyph_node::glyph_node(charinfo *c, tfont *t,
1926 color *gc, color *fc, hunits w, node *x)
1927 : charinfo_node(c, x), tf(t), gcol(gc), fcol(fc), wid(w)
1930 #endif
1932 node *glyph_node::copy()
1934 #ifdef STORE_WIDTH
1935 return new glyph_node(ci, tf, gcol, fcol, wid);
1936 #else
1937 return new glyph_node(ci, tf, gcol, fcol);
1938 #endif
1941 node *glyph_node::merge_self(node *nd)
1943 return nd->merge_glyph_node(this);
1946 int glyph_node::character_type()
1948 return tf->get_character_type(ci);
1951 node *glyph_node::add_self(node *n, hyphen_list **p)
1953 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
1954 next = 0;
1955 node *nn;
1956 if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
1957 next = n;
1958 nn = this;
1960 if ((*p)->hyphen)
1961 nn = nn->add_discretionary_hyphen();
1962 hyphen_list *pp = *p;
1963 *p = (*p)->next;
1964 delete pp;
1965 return nn;
1968 units glyph_node::size()
1970 return tf->get_size().to_units();
1973 hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count)
1975 (*count)++;
1976 return new hyphen_list(ci->get_hyphenation_code(), tail);
1979 tfont *node::get_tfont()
1981 return 0;
1984 tfont *glyph_node::get_tfont()
1986 return tf;
1989 color *node::get_glyph_color()
1991 return 0;
1994 color *glyph_node::get_glyph_color()
1996 return gcol;
1999 color *node::get_fill_color()
2001 return 0;
2004 color *glyph_node::get_fill_color()
2006 return fcol;
2009 node *node::merge_glyph_node(glyph_node *)
2011 return 0;
2014 node *glyph_node::merge_glyph_node(glyph_node *gn)
2016 if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
2017 charinfo *lig;
2018 if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
2019 node *next1 = next;
2020 next = 0;
2021 return new ligature_node(lig, tf, gcol, fcol, this, gn, next1);
2023 hunits kern;
2024 if (tf->get_kern(ci, gn->ci, &kern)) {
2025 node *next1 = next;
2026 next = 0;
2027 return new kern_pair_node(kern, this, gn, next1);
2030 return 0;
2033 #ifdef STORE_WIDTH
2034 inline
2035 #endif
2036 hunits glyph_node::width()
2038 #ifdef STORE_WIDTH
2039 return wid;
2040 #else
2041 return tf->get_width(ci);
2042 #endif
2045 node *glyph_node::last_char_node()
2047 return this;
2050 void glyph_node::vertical_extent(vunits *min, vunits *max)
2052 *min = -tf->get_char_height(ci);
2053 *max = tf->get_char_depth(ci);
2056 hunits glyph_node::skew()
2058 return tf->get_char_skew(ci);
2061 hunits glyph_node::subscript_correction()
2063 return tf->get_subscript_correction(ci);
2066 hunits glyph_node::italic_correction()
2068 return tf->get_italic_correction(ci);
2071 hunits glyph_node::left_italic_correction()
2073 return tf->get_left_italic_correction(ci);
2076 hyphenation_type glyph_node::get_hyphenation_type()
2078 return HYPHEN_MIDDLE;
2081 void glyph_node::ascii_print(ascii_output_file *ascii)
2083 unsigned char c = ci->get_ascii_code();
2084 if (c != 0)
2085 ascii->outc(c);
2086 else
2087 ascii->outs(ci->nm.contents());
2090 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2091 node *gn1, node *gn2, node *x)
2092 : glyph_node(c, t, gc, fc, x), n1(gn1), n2(gn2)
2096 #ifdef STORE_WIDTH
2097 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2098 hunits w, node *gn1, node *gn2, node *x)
2099 : glyph_node(c, t, gc, fc, w, x), n1(gn1), n2(gn2)
2102 #endif
2104 ligature_node::~ligature_node()
2106 delete n1;
2107 delete n2;
2110 node *ligature_node::copy()
2112 #ifdef STORE_WIDTH
2113 return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy());
2114 #else
2115 return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy());
2116 #endif
2119 void ligature_node::ascii_print(ascii_output_file *ascii)
2121 n1->ascii_print(ascii);
2122 n2->ascii_print(ascii);
2125 hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail, int *count)
2127 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2128 return n1->get_hyphen_list(hl, count);
2131 node *ligature_node::add_self(node *n, hyphen_list **p)
2133 n = n1->add_self(n, p);
2134 n = n2->add_self(n, p);
2135 n1 = n2 = 0;
2136 delete this;
2137 return n;
2140 kern_pair_node::kern_pair_node(hunits n, node *first, node *second, node *x)
2141 : node(x), amount(n), n1(first), n2(second)
2145 dbreak_node::dbreak_node(node *n, node *p, node *x)
2146 : node(x), none(n), pre(p), post(0)
2150 node *dbreak_node::merge_glyph_node(glyph_node *gn)
2152 glyph_node *gn2 = (glyph_node *)gn->copy();
2153 node *new_none = none ? none->merge_glyph_node(gn) : 0;
2154 node *new_post = post ? post->merge_glyph_node(gn2) : 0;
2155 if (new_none == 0 && new_post == 0) {
2156 delete gn2;
2157 return 0;
2159 if (new_none != 0)
2160 none = new_none;
2161 else {
2162 gn->next = none;
2163 none = gn;
2165 if (new_post != 0)
2166 post = new_post;
2167 else {
2168 gn2->next = post;
2169 post = gn2;
2171 return this;
2174 node *kern_pair_node::merge_glyph_node(glyph_node *gn)
2176 node *nd = n2->merge_glyph_node(gn);
2177 if (nd == 0)
2178 return 0;
2179 n2 = nd;
2180 nd = n2->merge_self(n1);
2181 if (nd) {
2182 nd->next = next;
2183 n1 = 0;
2184 n2 = 0;
2185 delete this;
2186 return nd;
2188 return this;
2191 hunits kern_pair_node::italic_correction()
2193 return n2->italic_correction();
2196 hunits kern_pair_node::subscript_correction()
2198 return n2->subscript_correction();
2201 void kern_pair_node::vertical_extent(vunits *min, vunits *max)
2203 n1->vertical_extent(min, max);
2204 vunits min2, max2;
2205 n2->vertical_extent(&min2, &max2);
2206 if (min2 < *min)
2207 *min = min2;
2208 if (max2 > *max)
2209 *max = max2;
2212 node *kern_pair_node::add_discretionary_hyphen()
2214 tfont *tf = n2->get_tfont();
2215 if (tf) {
2216 if (tf->contains(soft_hyphen_char)) {
2217 color *gcol = n2->get_glyph_color();
2218 color *fcol = n2->get_fill_color();
2219 node *next1 = next;
2220 next = 0;
2221 node *n = copy();
2222 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol);
2223 node *nn = n->merge_glyph_node(gn);
2224 if (nn == 0) {
2225 gn->next = n;
2226 nn = gn;
2228 return new dbreak_node(this, nn, next1);
2231 return this;
2234 kern_pair_node::~kern_pair_node()
2236 if (n1 != 0)
2237 delete n1;
2238 if (n2 != 0)
2239 delete n2;
2242 dbreak_node::~dbreak_node()
2244 delete_node_list(pre);
2245 delete_node_list(post);
2246 delete_node_list(none);
2249 node *kern_pair_node::copy()
2251 return new kern_pair_node(amount, n1->copy(), n2->copy());
2254 node *copy_node_list(node *n)
2256 node *p = 0;
2257 while (n != 0) {
2258 node *nn = n->copy();
2259 nn->next = p;
2260 p = nn;
2261 n = n->next;
2263 while (p != 0) {
2264 node *pp = p->next;
2265 p->next = n;
2266 n = p;
2267 p = pp;
2269 return n;
2272 void delete_node_list(node *n)
2274 while (n != 0) {
2275 node *tem = n;
2276 n = n->next;
2277 delete tem;
2281 node *dbreak_node::copy()
2283 dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre));
2284 p->post = copy_node_list(post);
2285 return p;
2288 hyphen_list *node::get_hyphen_list(hyphen_list *tail, int *)
2290 return tail;
2293 hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail, int *count)
2295 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2296 return n1->get_hyphen_list(hl, count);
2299 class hyphen_inhibitor_node : public node {
2300 public:
2301 hyphen_inhibitor_node(node *nd = 0);
2302 node *copy();
2303 int same(node *);
2304 const char *type();
2305 int force_tprint();
2306 hyphenation_type get_hyphenation_type();
2309 hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
2313 node *hyphen_inhibitor_node::copy()
2315 return new hyphen_inhibitor_node;
2318 int hyphen_inhibitor_node::same(node *)
2320 return 1;
2323 const char *hyphen_inhibitor_node::type()
2325 return "hyphen_inhibitor_node";
2328 int hyphen_inhibitor_node::force_tprint()
2330 return 0;
2333 hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
2335 return HYPHEN_INHIBIT;
2338 /* add_discretionary_hyphen methods */
2340 node *dbreak_node::add_discretionary_hyphen()
2342 if (post)
2343 post = post->add_discretionary_hyphen();
2344 if (none)
2345 none = none->add_discretionary_hyphen();
2346 return this;
2349 node *node::add_discretionary_hyphen()
2351 tfont *tf = get_tfont();
2352 if (!tf)
2353 return new hyphen_inhibitor_node(this);
2354 if (tf->contains(soft_hyphen_char)) {
2355 color *gcol = get_glyph_color();
2356 color *fcol = get_fill_color();
2357 node *next1 = next;
2358 next = 0;
2359 node *n = copy();
2360 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol);
2361 node *n1 = n->merge_glyph_node(gn);
2362 if (n1 == 0) {
2363 gn->next = n;
2364 n1 = gn;
2366 return new dbreak_node(this, n1, next1);
2368 return this;
2371 node *node::merge_self(node *)
2373 return 0;
2376 node *node::add_self(node *n, hyphen_list ** /*p*/)
2378 next = n;
2379 return this;
2382 node *kern_pair_node::add_self(node *n, hyphen_list **p)
2384 n = n1->add_self(n, p);
2385 n = n2->add_self(n, p);
2386 n1 = n2 = 0;
2387 delete this;
2388 return n;
2391 hunits node::width()
2393 return H0;
2396 node *node::last_char_node()
2398 return 0;
2401 int node::force_tprint()
2403 return 0;
2406 hunits hmotion_node::width()
2408 return n;
2411 units node::size()
2413 return points_to_units(10);
2416 hunits kern_pair_node::width()
2418 return n1->width() + n2->width() + amount;
2421 node *kern_pair_node::last_char_node()
2423 node *nd = n2->last_char_node();
2424 if (nd)
2425 return nd;
2426 return n1->last_char_node();
2429 hunits dbreak_node::width()
2431 hunits x = H0;
2432 for (node *n = none; n != 0; n = n->next)
2433 x += n->width();
2434 return x;
2437 node *dbreak_node::last_char_node()
2439 for (node *n = none; n; n = n->next) {
2440 node *last_node = n->last_char_node();
2441 if (last_node)
2442 return last_node;
2444 return 0;
2447 hunits dbreak_node::italic_correction()
2449 return none ? none->italic_correction() : H0;
2452 hunits dbreak_node::subscript_correction()
2454 return none ? none->subscript_correction() : H0;
2457 class italic_corrected_node : public node {
2458 node *n;
2459 hunits x;
2460 public:
2461 italic_corrected_node(node *, hunits, node * = 0);
2462 ~italic_corrected_node();
2463 node *copy();
2464 void ascii_print(ascii_output_file *);
2465 void asciify(macro *);
2466 hunits width();
2467 node *last_char_node();
2468 void vertical_extent(vunits *, vunits *);
2469 int ends_sentence();
2470 int overlaps_horizontally();
2471 int overlaps_vertically();
2472 int same(node *);
2473 hyphenation_type get_hyphenation_type();
2474 tfont *get_tfont();
2475 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2476 int character_type();
2477 void tprint(troff_output_file *);
2478 hunits subscript_correction();
2479 hunits skew();
2480 node *add_self(node *, hyphen_list **);
2481 const char *type();
2482 int force_tprint();
2485 node *node::add_italic_correction(hunits *wd)
2487 hunits ic = italic_correction();
2488 if (ic.is_zero())
2489 return this;
2490 else {
2491 node *next1 = next;
2492 next = 0;
2493 *wd += ic;
2494 return new italic_corrected_node(this, ic, next1);
2498 italic_corrected_node::italic_corrected_node(node *nn, hunits xx, node *p)
2499 : node(p), n(nn), x(xx)
2501 assert(n != 0);
2504 italic_corrected_node::~italic_corrected_node()
2506 delete n;
2509 node *italic_corrected_node::copy()
2511 return new italic_corrected_node(n->copy(), x);
2514 hunits italic_corrected_node::width()
2516 return n->width() + x;
2519 void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
2521 n->vertical_extent(min, max);
2524 void italic_corrected_node::tprint(troff_output_file *out)
2526 n->tprint(out);
2527 out->right(x);
2530 hunits italic_corrected_node::skew()
2532 return n->skew() - x/2;
2535 hunits italic_corrected_node::subscript_correction()
2537 return n->subscript_correction() - x;
2540 void italic_corrected_node::ascii_print(ascii_output_file *out)
2542 n->ascii_print(out);
2545 int italic_corrected_node::ends_sentence()
2547 return n->ends_sentence();
2550 int italic_corrected_node::overlaps_horizontally()
2552 return n->overlaps_horizontally();
2555 int italic_corrected_node::overlaps_vertically()
2557 return n->overlaps_vertically();
2560 node *italic_corrected_node::last_char_node()
2562 return n->last_char_node();
2565 tfont *italic_corrected_node::get_tfont()
2567 return n->get_tfont();
2570 hyphenation_type italic_corrected_node::get_hyphenation_type()
2572 return n->get_hyphenation_type();
2575 node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
2577 nd = n->add_self(nd, p);
2578 hunits not_interested;
2579 nd = nd->add_italic_correction(&not_interested);
2580 n = 0;
2581 delete this;
2582 return nd;
2585 hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail,
2586 int *count)
2588 return n->get_hyphen_list(tail, count);
2591 int italic_corrected_node::character_type()
2593 return n->character_type();
2596 class break_char_node : public node {
2597 node *ch;
2598 char break_code;
2599 color *col;
2600 public:
2601 break_char_node(node *, int, color *, node * = 0);
2602 ~break_char_node();
2603 node *copy();
2604 hunits width();
2605 vunits vertical_width();
2606 node *last_char_node();
2607 int character_type();
2608 int ends_sentence();
2609 node *add_self(node *, hyphen_list **);
2610 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2611 void tprint(troff_output_file *);
2612 void zero_width_tprint(troff_output_file *);
2613 void ascii_print(ascii_output_file *);
2614 void asciify(macro *);
2615 hyphenation_type get_hyphenation_type();
2616 int overlaps_vertically();
2617 int overlaps_horizontally();
2618 units size();
2619 tfont *get_tfont();
2620 int same(node *);
2621 const char *type();
2622 int force_tprint();
2625 break_char_node::break_char_node(node *n, int bc, color *c, node *x)
2626 : node(x), ch(n), break_code(bc), col(c)
2630 break_char_node::~break_char_node()
2632 delete ch;
2635 node *break_char_node::copy()
2637 return new break_char_node(ch->copy(), break_code, col);
2640 hunits break_char_node::width()
2642 return ch->width();
2645 vunits break_char_node::vertical_width()
2647 return ch->vertical_width();
2650 node *break_char_node::last_char_node()
2652 return ch->last_char_node();
2655 int break_char_node::character_type()
2657 return ch->character_type();
2660 int break_char_node::ends_sentence()
2662 return ch->ends_sentence();
2665 node *break_char_node::add_self(node *n, hyphen_list **p)
2667 assert((*p)->hyphenation_code == 0);
2668 if ((*p)->breakable && (break_code & 1)) {
2669 n = new space_node(H0, col, n);
2670 n->freeze_space();
2672 next = n;
2673 n = this;
2674 if ((*p)->breakable && (break_code & 2)) {
2675 n = new space_node(H0, col, n);
2676 n->freeze_space();
2678 hyphen_list *pp = *p;
2679 *p = (*p)->next;
2680 delete pp;
2681 return n;
2684 hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail, int *)
2686 return new hyphen_list(0, tail);
2689 hyphenation_type break_char_node::get_hyphenation_type()
2691 return HYPHEN_MIDDLE;
2694 void break_char_node::ascii_print(ascii_output_file *ascii)
2696 ch->ascii_print(ascii);
2699 int break_char_node::overlaps_vertically()
2701 return ch->overlaps_vertically();
2704 int break_char_node::overlaps_horizontally()
2706 return ch->overlaps_horizontally();
2709 units break_char_node::size()
2711 return ch->size();
2714 tfont *break_char_node::get_tfont()
2716 return ch->get_tfont();
2719 node *extra_size_node::copy()
2721 return new extra_size_node(n);
2724 node *vertical_size_node::copy()
2726 return new vertical_size_node(n);
2729 node *hmotion_node::copy()
2731 return new hmotion_node(n, was_tab, unformat, col);
2734 node *space_char_hmotion_node::copy()
2736 return new space_char_hmotion_node(n, col);
2739 node *vmotion_node::copy()
2741 return new vmotion_node(n, col);
2744 node *dummy_node::copy()
2746 return new dummy_node;
2749 node *transparent_dummy_node::copy()
2751 return new transparent_dummy_node;
2754 hline_node::~hline_node()
2756 if (n)
2757 delete n;
2760 node *hline_node::copy()
2762 return new hline_node(x, n ? n->copy() : 0);
2765 hunits hline_node::width()
2767 return x < H0 ? H0 : x;
2770 vline_node::~vline_node()
2772 if (n)
2773 delete n;
2776 node *vline_node::copy()
2778 return new vline_node(x, n ? n->copy() : 0);
2781 hunits vline_node::width()
2783 return n == 0 ? H0 : n->width();
2786 zero_width_node::zero_width_node(node *nd) : n(nd)
2790 zero_width_node::~zero_width_node()
2792 delete_node_list(n);
2795 node *zero_width_node::copy()
2797 return new zero_width_node(copy_node_list(n));
2800 int node_list_character_type(node *p)
2802 int t = 0;
2803 for (; p; p = p->next)
2804 t |= p->character_type();
2805 return t;
2808 int zero_width_node::character_type()
2810 return node_list_character_type(n);
2813 void node_list_vertical_extent(node *p, vunits *min, vunits *max)
2815 *min = V0;
2816 *max = V0;
2817 vunits cur_vpos = V0;
2818 vunits v1, v2;
2819 for (; p; p = p->next) {
2820 p->vertical_extent(&v1, &v2);
2821 v1 += cur_vpos;
2822 if (v1 < *min)
2823 *min = v1;
2824 v2 += cur_vpos;
2825 if (v2 > *max)
2826 *max = v2;
2827 cur_vpos += p->vertical_width();
2831 void zero_width_node::vertical_extent(vunits *min, vunits *max)
2833 node_list_vertical_extent(n, min, max);
2836 overstrike_node::overstrike_node() : list(0), max_width(H0)
2840 overstrike_node::~overstrike_node()
2842 delete_node_list(list);
2845 node *overstrike_node::copy()
2847 overstrike_node *on = new overstrike_node;
2848 for (node *tem = list; tem; tem = tem->next)
2849 on->overstrike(tem->copy());
2850 return on;
2853 void overstrike_node::overstrike(node *n)
2855 if (n == 0)
2856 return;
2857 hunits w = n->width();
2858 if (w > max_width)
2859 max_width = w;
2860 node **p;
2861 for (p = &list; *p; p = &(*p)->next)
2863 n->next = 0;
2864 *p = n;
2867 hunits overstrike_node::width()
2869 return max_width;
2872 bracket_node::bracket_node() : list(0), max_width(H0)
2876 bracket_node::~bracket_node()
2878 delete_node_list(list);
2881 node *bracket_node::copy()
2883 bracket_node *on = new bracket_node;
2884 node *last_node = 0;
2885 node *tem;
2886 if (list)
2887 list->last = 0;
2888 for (tem = list; tem; tem = tem->next) {
2889 if (tem->next)
2890 tem->next->last = tem;
2891 last_node = tem;
2893 for (tem = last_node; tem; tem = tem->last)
2894 on->bracket(tem->copy());
2895 return on;
2898 void bracket_node::bracket(node *n)
2900 if (n == 0)
2901 return;
2902 hunits w = n->width();
2903 if (w > max_width)
2904 max_width = w;
2905 n->next = list;
2906 list = n;
2909 hunits bracket_node::width()
2911 return max_width;
2914 int node::nspaces()
2916 return 0;
2919 int node::merge_space(hunits, hunits, hunits)
2921 return 0;
2924 #if 0
2925 space_node *space_node::free_list = 0;
2927 void *space_node::operator new(size_t n)
2929 assert(n == sizeof(space_node));
2930 if (!free_list) {
2931 free_list = (space_node *)new char[sizeof(space_node)*BLOCK];
2932 for (int i = 0; i < BLOCK - 1; i++)
2933 free_list[i].next = free_list + i + 1;
2934 free_list[BLOCK-1].next = 0;
2936 space_node *p = free_list;
2937 free_list = (space_node *)(free_list->next);
2938 p->next = 0;
2939 return p;
2942 inline void space_node::operator delete(void *p)
2944 if (p) {
2945 ((space_node *)p)->next = free_list;
2946 free_list = (space_node *)p;
2949 #endif
2951 space_node::space_node(hunits nn, color *c, node *p)
2952 : node(p), n(nn), set(0), was_escape_colon(0), col(c)
2956 space_node::space_node(hunits nn, int s, int flag, color *c, node *p)
2957 : node(p), n(nn), set(s), was_escape_colon(flag), col(c)
2961 #if 0
2962 space_node::~space_node()
2965 #endif
2967 node *space_node::copy()
2969 return new space_node(n, set, was_escape_colon, col);
2972 int space_node::force_tprint()
2974 return 0;
2977 int space_node::nspaces()
2979 return set ? 0 : 1;
2982 int space_node::merge_space(hunits h, hunits, hunits)
2984 n += h;
2985 return 1;
2988 hunits space_node::width()
2990 return n;
2993 void node::spread_space(int*, hunits*)
2997 void space_node::spread_space(int *n_spaces, hunits *desired_space)
2999 if (!set) {
3000 assert(*n_spaces > 0);
3001 if (*n_spaces == 1) {
3002 n += *desired_space;
3003 *desired_space = H0;
3005 else {
3006 hunits extra = *desired_space / *n_spaces;
3007 *desired_space -= extra;
3008 n += extra;
3010 *n_spaces -= 1;
3011 set = 1;
3015 void node::freeze_space()
3019 void space_node::freeze_space()
3021 set = 1;
3024 void node::is_escape_colon()
3028 void space_node::is_escape_colon()
3030 was_escape_colon = 1;
3033 diverted_space_node::diverted_space_node(vunits d, node *p)
3034 : node(p), n(d)
3038 node *diverted_space_node::copy()
3040 return new diverted_space_node(n);
3043 diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
3044 : node(p), filename(s)
3048 node *diverted_copy_file_node::copy()
3050 return new diverted_copy_file_node(filename);
3053 int node::ends_sentence()
3055 return 0;
3058 int kern_pair_node::ends_sentence()
3060 switch (n2->ends_sentence()) {
3061 case 0:
3062 return 0;
3063 case 1:
3064 return 1;
3065 case 2:
3066 break;
3067 default:
3068 assert(0);
3070 return n1->ends_sentence();
3073 int node_list_ends_sentence(node *n)
3075 for (; n != 0; n = n->next)
3076 switch (n->ends_sentence()) {
3077 case 0:
3078 return 0;
3079 case 1:
3080 return 1;
3081 case 2:
3082 break;
3083 default:
3084 assert(0);
3086 return 2;
3089 int dbreak_node::ends_sentence()
3091 return node_list_ends_sentence(none);
3094 int node::overlaps_horizontally()
3096 return 0;
3099 int node::overlaps_vertically()
3101 return 0;
3104 int node::discardable()
3106 return 0;
3109 int space_node::discardable()
3111 return set ? 0 : 1;
3114 vunits node::vertical_width()
3116 return V0;
3119 vunits vline_node::vertical_width()
3121 return x;
3124 vunits vmotion_node::vertical_width()
3126 return n;
3129 int node::set_unformat_flag()
3131 return 1;
3134 int node::character_type()
3136 return 0;
3139 hunits node::subscript_correction()
3141 return H0;
3144 hunits node::italic_correction()
3146 return H0;
3149 hunits node::left_italic_correction()
3151 return H0;
3154 hunits node::skew()
3156 return H0;
3159 /* vertical_extent methods */
3161 void node::vertical_extent(vunits *min, vunits *max)
3163 vunits v = vertical_width();
3164 if (v < V0) {
3165 *min = v;
3166 *max = V0;
3168 else {
3169 *max = v;
3170 *min = V0;
3174 void vline_node::vertical_extent(vunits *min, vunits *max)
3176 if (n == 0)
3177 node::vertical_extent(min, max);
3178 else {
3179 vunits cmin, cmax;
3180 n->vertical_extent(&cmin, &cmax);
3181 vunits h = n->size();
3182 if (x < V0) {
3183 if (-x < h) {
3184 *min = x;
3185 *max = V0;
3187 else {
3188 // we print the first character and then move up, so
3189 *max = cmax;
3190 // we print the last character and then move up h
3191 *min = cmin + h;
3192 if (*min > V0)
3193 *min = V0;
3194 *min += x;
3197 else {
3198 if (x < h) {
3199 *max = x;
3200 *min = V0;
3202 else {
3203 // we move down by h and then print the first character, so
3204 *min = cmin + h;
3205 if (*min > V0)
3206 *min = V0;
3207 *max = x + cmax;
3213 /* ascii_print methods */
3215 static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n)
3217 if (n == 0)
3218 return;
3219 ascii_print_reverse_node_list(ascii, n->next);
3220 n->ascii_print(ascii);
3223 void dbreak_node::ascii_print(ascii_output_file *ascii)
3225 ascii_print_reverse_node_list(ascii, none);
3228 void kern_pair_node::ascii_print(ascii_output_file *ascii)
3230 n1->ascii_print(ascii);
3231 n2->ascii_print(ascii);
3234 void node::ascii_print(ascii_output_file *)
3238 void space_node::ascii_print(ascii_output_file *ascii)
3240 if (!n.is_zero())
3241 ascii->outc(' ');
3244 void hmotion_node::ascii_print(ascii_output_file *ascii)
3246 // this is pretty arbitrary
3247 if (n >= points_to_units(2))
3248 ascii->outc(' ');
3251 void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
3253 ascii->outc(' ');
3256 /* asciify methods */
3258 void node::asciify(macro *m)
3260 m->append(this);
3263 void glyph_node::asciify(macro *m)
3265 unsigned char c = ci->get_asciify_code();
3266 if (c == 0)
3267 c = ci->get_ascii_code();
3268 if (c != 0) {
3269 m->append(c);
3270 delete this;
3272 else
3273 m->append(this);
3276 void kern_pair_node::asciify(macro *m)
3278 n1->asciify(m);
3279 n2->asciify(m);
3280 n1 = n2 = 0;
3281 delete this;
3284 static void asciify_reverse_node_list(macro *m, node *n)
3286 if (n == 0)
3287 return;
3288 asciify_reverse_node_list(m, n->next);
3289 n->asciify(m);
3292 void dbreak_node::asciify(macro *m)
3294 asciify_reverse_node_list(m, none);
3295 none = 0;
3296 delete this;
3299 void ligature_node::asciify(macro *m)
3301 n1->asciify(m);
3302 n2->asciify(m);
3303 n1 = n2 = 0;
3304 delete this;
3307 void break_char_node::asciify(macro *m)
3309 ch->asciify(m);
3310 ch = 0;
3311 delete this;
3314 void italic_corrected_node::asciify(macro *m)
3316 n->asciify(m);
3317 n = 0;
3318 delete this;
3321 void left_italic_corrected_node::asciify(macro *m)
3323 if (n) {
3324 n->asciify(m);
3325 n = 0;
3327 delete this;
3330 void hmotion_node::asciify(macro *m)
3332 if (was_tab) {
3333 m->append('\t');
3334 delete this;
3336 else
3337 m->append(this);
3340 space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3341 node *nxt)
3342 : hmotion_node(i, c, nxt)
3346 void space_char_hmotion_node::asciify(macro *m)
3348 m->append(ESCAPE_SPACE);
3349 delete this;
3352 void space_node::asciify(macro *m)
3354 if (was_escape_colon) {
3355 m->append(ESCAPE_COLON);
3356 delete this;
3358 else
3359 m->append(this);
3362 void word_space_node::asciify(macro *m)
3364 for (width_list *w = orig_width; w; w = w->next)
3365 m->append(' ');
3366 delete this;
3369 void unbreakable_space_node::asciify(macro *m)
3371 m->append(ESCAPE_TILDE);
3372 delete this;
3375 void line_start_node::asciify(macro *)
3377 delete this;
3380 void vertical_size_node::asciify(macro *)
3382 delete this;
3385 breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/,
3386 breakpoint *rest, int /*is_inner*/)
3388 return rest;
3391 int node::nbreaks()
3393 return 0;
3396 breakpoint *space_node::get_breakpoints(hunits wd, int ns,
3397 breakpoint *rest, int is_inner)
3399 if (next && next->discardable())
3400 return rest;
3401 breakpoint *bp = new breakpoint;
3402 bp->next = rest;
3403 bp->width = wd;
3404 bp->nspaces = ns;
3405 bp->hyphenated = 0;
3406 if (is_inner) {
3407 assert(rest != 0);
3408 bp->index = rest->index + 1;
3409 bp->nd = rest->nd;
3411 else {
3412 bp->nd = this;
3413 bp->index = 0;
3415 return bp;
3418 int space_node::nbreaks()
3420 if (next && next->discardable())
3421 return 0;
3422 else
3423 return 1;
3426 static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
3427 int ns, breakpoint *rest)
3429 if (p != 0) {
3430 rest = p->get_breakpoints(*widthp,
3432 node_list_get_breakpoints(p->next, widthp, ns,
3433 rest),
3435 *widthp += p->width();
3437 return rest;
3440 breakpoint *dbreak_node::get_breakpoints(hunits wd, int ns,
3441 breakpoint *rest, int is_inner)
3443 breakpoint *bp = new breakpoint;
3444 bp->next = rest;
3445 bp->width = wd;
3446 for (node *tem = pre; tem != 0; tem = tem->next)
3447 bp->width += tem->width();
3448 bp->nspaces = ns;
3449 bp->hyphenated = 1;
3450 if (is_inner) {
3451 assert(rest != 0);
3452 bp->index = rest->index + 1;
3453 bp->nd = rest->nd;
3455 else {
3456 bp->nd = this;
3457 bp->index = 0;
3459 return node_list_get_breakpoints(none, &wd, ns, bp);
3462 int dbreak_node::nbreaks()
3464 int i = 1;
3465 for (node *tem = none; tem != 0; tem = tem->next)
3466 i += tem->nbreaks();
3467 return i;
3470 void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
3472 assert(0);
3475 void space_node::split(int where, node **pre, node **post)
3477 assert(where == 0);
3478 *pre = next;
3479 *post = 0;
3480 delete this;
3483 static void node_list_split(node *p, int *wherep, node **prep, node **postp)
3485 if (p == 0)
3486 return;
3487 int nb = p->nbreaks();
3488 node_list_split(p->next, wherep, prep, postp);
3489 if (*wherep < 0) {
3490 p->next = *postp;
3491 *postp = p;
3493 else if (*wherep < nb) {
3494 p->next = *prep;
3495 p->split(*wherep, prep, postp);
3497 else {
3498 p->next = *prep;
3499 *prep = p;
3501 *wherep -= nb;
3504 void dbreak_node::split(int where, node **prep, node **postp)
3506 assert(where >= 0);
3507 if (where == 0) {
3508 *postp = post;
3509 post = 0;
3510 if (pre == 0)
3511 *prep = next;
3512 else {
3513 node *tem;
3514 for (tem = pre; tem->next != 0; tem = tem->next)
3516 tem->next = next;
3517 *prep = pre;
3519 pre = 0;
3520 delete this;
3522 else {
3523 *prep = next;
3524 where -= 1;
3525 node_list_split(none, &where, prep, postp);
3526 none = 0;
3527 delete this;
3531 hyphenation_type node::get_hyphenation_type()
3533 return HYPHEN_BOUNDARY;
3536 hyphenation_type dbreak_node::get_hyphenation_type()
3538 return HYPHEN_INHIBIT;
3541 hyphenation_type kern_pair_node::get_hyphenation_type()
3543 return HYPHEN_MIDDLE;
3546 hyphenation_type dummy_node::get_hyphenation_type()
3548 return HYPHEN_MIDDLE;
3551 hyphenation_type transparent_dummy_node::get_hyphenation_type()
3553 return HYPHEN_MIDDLE;
3556 hyphenation_type hmotion_node::get_hyphenation_type()
3558 return HYPHEN_MIDDLE;
3561 hyphenation_type space_char_hmotion_node::get_hyphenation_type()
3563 return HYPHEN_MIDDLE;
3566 hyphenation_type overstrike_node::get_hyphenation_type()
3568 return HYPHEN_MIDDLE;
3571 hyphenation_type space_node::get_hyphenation_type()
3573 if (was_escape_colon)
3574 return HYPHEN_MIDDLE;
3575 return HYPHEN_BOUNDARY;
3578 hyphenation_type unbreakable_space_node::get_hyphenation_type()
3580 return HYPHEN_MIDDLE;
3583 int node::interpret(macro *)
3585 return 0;
3588 special_node::special_node(const macro &m, int n)
3589 : mac(m), no_init_string(n)
3591 font_size fs = curenv->get_font_size();
3592 int char_height = curenv->get_char_height();
3593 int char_slant = curenv->get_char_slant();
3594 int fontno = env_definite_font(curenv);
3595 tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fontno);
3596 if (curenv->is_composite())
3597 tf = tf->get_plain();
3598 gcol = curenv->get_glyph_color();
3599 fcol = curenv->get_fill_color();
3602 special_node::special_node(const macro &m, tfont *t,
3603 color *gc, color *fc, int n)
3604 : mac(m), tf(t), gcol(gc), fcol(fc), no_init_string(n)
3608 int special_node::same(node *n)
3610 return mac == ((special_node *)n)->mac
3611 && tf == ((special_node *)n)->tf
3612 && gcol == ((special_node *)n)->gcol
3613 && fcol == ((special_node *)n)->fcol
3614 && no_init_string == ((special_node *)n)->no_init_string;
3617 const char *special_node::type()
3619 return "special_node";
3622 int special_node::ends_sentence()
3624 return 2;
3627 int special_node::force_tprint()
3629 return 0;
3632 node *special_node::copy()
3634 return new special_node(mac, tf, gcol, fcol, no_init_string);
3637 void special_node::tprint_start(troff_output_file *out)
3639 out->start_special(tf, gcol, fcol, no_init_string);
3642 void special_node::tprint_char(troff_output_file *out, unsigned char c)
3644 out->special_char(c);
3647 void special_node::tprint_end(troff_output_file *out)
3649 out->end_special();
3652 tfont *special_node::get_tfont()
3654 return tf;
3657 /* suppress_node */
3659 suppress_node::suppress_node(int on_or_off, int issue_limits)
3660 : is_on(on_or_off), emit_limits(issue_limits),
3661 filename(0), position(0), image_id(0)
3665 suppress_node::suppress_node(symbol f, char p, int id)
3666 : is_on(2), emit_limits(0), filename(f), position(p), image_id(id)
3670 suppress_node::suppress_node(int issue_limits, int on_or_off,
3671 symbol f, char p, int id)
3672 : is_on(on_or_off), emit_limits(issue_limits),
3673 filename(f), position(p), image_id(id)
3677 int suppress_node::same(node *n)
3679 return ((is_on == ((suppress_node *)n)->is_on)
3680 && (emit_limits == ((suppress_node *)n)->emit_limits)
3681 && (filename == ((suppress_node *)n)->filename)
3682 && (position == ((suppress_node *)n)->position)
3683 && (image_id == ((suppress_node *)n)->image_id));
3686 const char *suppress_node::type()
3688 return "suppress_node";
3691 node *suppress_node::copy()
3693 return new suppress_node(emit_limits, is_on, filename, position, image_id);
3696 int get_reg_int(const char *p)
3698 reg *r = (reg *)number_reg_dictionary.lookup(p);
3699 units prev_value;
3700 if (r && (r->get_value(&prev_value)))
3701 return (int)prev_value;
3702 else
3703 warning(WARN_REG, "number register `%1' not defined", p);
3704 return 0;
3707 const char *get_reg_str(const char *p)
3709 reg *r = (reg *)number_reg_dictionary.lookup(p);
3710 if (r)
3711 return r->get_string();
3712 else
3713 warning(WARN_REG, "register `%1' not defined", p);
3714 return 0;
3717 void suppress_node::put(troff_output_file *out, const char *s)
3719 int i = 0;
3720 while (s[i] != (char)0) {
3721 out->special_char(s[i]);
3722 i++;
3727 * We need to remember the start of the image and its name.
3730 static char last_position = 0;
3731 static const char *last_image_filename = 0;
3732 static int last_image_id = 0;
3734 inline int min(int a, int b)
3736 return a < b ? a : b;
3740 * tprint - if (is_on == 2)
3741 * remember current position (l, r, c, i) and filename
3742 * else
3743 * if (emit_limits)
3744 * if (html)
3745 * emit image tag
3746 * else
3747 * emit postscript bounds for image
3748 * else
3749 * if (suppress boolean differs from current state)
3750 * alter state
3751 * reset registers
3752 * record current page
3753 * set low water mark.
3756 void suppress_node::tprint(troff_output_file *out)
3758 int current_page = topdiv->get_page_number();
3759 // firstly check to see whether this suppress node contains
3760 // an image filename & position.
3761 if (is_on == 2) {
3762 // remember position and filename
3763 last_position = position;
3764 char *tem = (char *)last_image_filename;
3765 last_image_filename = strsave(filename.contents());
3766 if (tem)
3767 a_delete tem;
3768 last_image_id = image_id;
3769 // printf("start of image and page = %d\n", current_page);
3771 else {
3772 // now check whether the suppress node requires us to issue limits.
3773 if (emit_limits) {
3774 char name[8192];
3775 // remember that the filename will contain a %d in which the
3776 // last_image_id is placed
3777 if (last_image_filename == (char *) 0)
3778 *name = '\0';
3779 else
3780 sprintf(name, last_image_filename, last_image_id);
3781 if (is_html) {
3782 switch (last_position) {
3783 case 'c':
3784 out->start_special();
3785 put(out, "html-tag:.centered-image");
3786 break;
3787 case 'r':
3788 out->start_special();
3789 put(out, "html-tag:.right-image");
3790 break;
3791 case 'l':
3792 out->start_special();
3793 put(out, "html-tag:.left-image");
3794 break;
3795 case 'i':
3797 default:
3800 out->end_special();
3801 out->start_special();
3802 put(out, "html-tag:.auto-image ");
3803 put(out, name);
3804 out->end_special();
3806 else {
3807 // postscript (or other device)
3808 if (suppress_start_page > 0 && current_page != suppress_start_page)
3809 error("suppression limit registers span more than one page;\n"
3810 "image description %1 will be wrong", image_no);
3811 // if (topdiv->get_page_number() != suppress_start_page)
3812 // fprintf(stderr, "end of image and topdiv page = %d and suppress_start_page = %d\n",
3813 // topdiv->get_page_number(), suppress_start_page);
3815 // remember that the filename will contain a %d in which the
3816 // image_no is placed
3817 fprintf(stderr,
3818 "grohtml-info:page %d %d %d %d %d %d %s %d %d %s\n",
3819 topdiv->get_page_number(),
3820 get_reg_int("opminx"), get_reg_int("opminy"),
3821 get_reg_int("opmaxx"), get_reg_int("opmaxy"),
3822 // page offset + line length
3823 get_reg_int(".o") + get_reg_int(".l"),
3824 name, hresolution, vresolution, get_reg_str(".F"));
3825 fflush(stderr);
3828 else {
3829 if (is_on) {
3830 out->on();
3831 // lastly we reset the output registers
3832 reset_output_registers();
3834 else
3835 out->off();
3836 suppress_start_page = current_page;
3841 int suppress_node::force_tprint()
3843 return is_on;
3846 hunits suppress_node::width()
3848 return H0;
3851 /* composite_node */
3853 class composite_node : public charinfo_node {
3854 node *n;
3855 tfont *tf;
3856 public:
3857 composite_node(node *, charinfo *, tfont *, node * = 0);
3858 ~composite_node();
3859 node *copy();
3860 hunits width();
3861 node *last_char_node();
3862 units size();
3863 void tprint(troff_output_file *);
3864 hyphenation_type get_hyphenation_type();
3865 void ascii_print(ascii_output_file *);
3866 void asciify(macro *);
3867 hyphen_list *get_hyphen_list(hyphen_list *, int *);
3868 node *add_self(node *, hyphen_list **);
3869 tfont *get_tfont();
3870 int same(node *);
3871 const char *type();
3872 int force_tprint();
3873 void vertical_extent(vunits *, vunits *);
3874 vunits vertical_width();
3877 composite_node::composite_node(node *p, charinfo *c, tfont *t, node *x)
3878 : charinfo_node(c, x), n(p), tf(t)
3882 composite_node::~composite_node()
3884 delete_node_list(n);
3887 node *composite_node::copy()
3889 return new composite_node(copy_node_list(n), ci, tf);
3892 hunits composite_node::width()
3894 hunits x;
3895 if (tf->get_constant_space(&x))
3896 return x;
3897 x = H0;
3898 for (node *tem = n; tem; tem = tem->next)
3899 x += tem->width();
3900 hunits offset;
3901 if (tf->get_bold(&offset))
3902 x += offset;
3903 x += tf->get_track_kern();
3904 return x;
3907 node *composite_node::last_char_node()
3909 return this;
3912 vunits composite_node::vertical_width()
3914 vunits v = V0;
3915 for (node *tem = n; tem; tem = tem->next)
3916 v += tem->vertical_width();
3917 return v;
3920 units composite_node::size()
3922 return tf->get_size().to_units();
3925 hyphenation_type composite_node::get_hyphenation_type()
3927 return HYPHEN_MIDDLE;
3930 void composite_node::asciify(macro *m)
3932 unsigned char c = ci->get_asciify_code();
3933 if (c == 0)
3934 c = ci->get_ascii_code();
3935 if (c != 0) {
3936 m->append(c);
3937 delete this;
3939 else
3940 m->append(this);
3943 void composite_node::ascii_print(ascii_output_file *ascii)
3945 unsigned char c = ci->get_ascii_code();
3946 if (c != 0)
3947 ascii->outc(c);
3948 else
3949 ascii->outs(ci->nm.contents());
3953 hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail, int *count)
3955 (*count)++;
3956 return new hyphen_list(ci->get_hyphenation_code(), tail);
3959 node *composite_node::add_self(node *nn, hyphen_list **p)
3961 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
3962 next = nn;
3963 nn = this;
3964 if ((*p)->hyphen)
3965 nn = nn->add_discretionary_hyphen();
3966 hyphen_list *pp = *p;
3967 *p = (*p)->next;
3968 delete pp;
3969 return nn;
3972 tfont *composite_node::get_tfont()
3974 return tf;
3977 node *reverse_node_list(node *n)
3979 node *r = 0;
3980 while (n) {
3981 node *tem = n;
3982 n = n->next;
3983 tem->next = r;
3984 r = tem;
3986 return r;
3989 void composite_node::vertical_extent(vunits *minimum, vunits *maximum)
3991 n = reverse_node_list(n);
3992 node_list_vertical_extent(n, minimum, maximum);
3993 n = reverse_node_list(n);
3996 width_list::width_list(hunits w, hunits s)
3997 : width(w), sentence_width(s), next(0)
4001 width_list::width_list(width_list *w)
4002 : width(w->width), sentence_width(w->sentence_width), next(0)
4006 word_space_node::word_space_node(hunits d, color *c, width_list *w, node *x)
4007 : space_node(d, c, x), orig_width(w), unformat(0)
4011 word_space_node::word_space_node(hunits d, int s, color *c, width_list *w,
4012 int flag, node *x)
4013 : space_node(d, s, 0, c, x), orig_width(w), unformat(flag)
4017 word_space_node::~word_space_node()
4019 width_list *w = orig_width;
4020 while (w != 0) {
4021 width_list *tmp = w;
4022 w = w->next;
4023 delete tmp;
4027 node *word_space_node::copy()
4029 assert(orig_width != 0);
4030 width_list *w_old_curr = orig_width;
4031 width_list *w_new_curr = new width_list(w_old_curr);
4032 width_list *w_new = w_new_curr;
4033 w_old_curr = w_old_curr->next;
4034 while (w_old_curr != 0) {
4035 w_new_curr->next = new width_list(w_old_curr);
4036 w_new_curr = w_new_curr->next;
4037 w_old_curr = w_old_curr->next;
4039 return new word_space_node(n, set, col, w_new, unformat);
4042 int word_space_node::set_unformat_flag()
4044 unformat = 1;
4045 return 1;
4048 void word_space_node::tprint(troff_output_file *out)
4050 out->fill_color(col);
4051 out->word_marker();
4052 out->right(n);
4055 int word_space_node::merge_space(hunits h, hunits sw, hunits ssw)
4057 n += h;
4058 assert(orig_width != 0);
4059 width_list *w = orig_width;
4060 for (; w->next; w = w->next)
4062 w->next = new width_list(sw, ssw);
4063 return 1;
4066 unbreakable_space_node::unbreakable_space_node(hunits d, color *c, node *x)
4067 : word_space_node(d, c, 0, x)
4071 unbreakable_space_node::unbreakable_space_node(hunits d, int s,
4072 color *c, node *x)
4073 : word_space_node(d, s, c, 0, 0, x)
4077 node *unbreakable_space_node::copy()
4079 return new unbreakable_space_node(n, set, col);
4082 int unbreakable_space_node::force_tprint()
4084 return 0;
4087 breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
4088 breakpoint *rest, int)
4090 return rest;
4093 int unbreakable_space_node::nbreaks()
4095 return 0;
4098 void unbreakable_space_node::split(int, node **, node **)
4100 assert(0);
4103 int unbreakable_space_node::merge_space(hunits, hunits, hunits)
4105 return 0;
4108 hvpair::hvpair()
4112 draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4113 color *gc, color *fc)
4114 : npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4116 point = new hvpair[npoints];
4117 for (int i = 0; i < npoints; i++)
4118 point[i] = p[i];
4121 int draw_node::same(node *n)
4123 draw_node *nd = (draw_node *)n;
4124 if (code != nd->code || npoints != nd->npoints || sz != nd->sz
4125 || gcol != nd->gcol || fcol != nd->fcol)
4126 return 0;
4127 for (int i = 0; i < npoints; i++)
4128 if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
4129 return 0;
4130 return 1;
4133 const char *draw_node::type()
4135 return "draw_node";
4138 int draw_node::force_tprint()
4140 return 0;
4143 draw_node::~draw_node()
4145 if (point)
4146 a_delete point;
4149 hunits draw_node::width()
4151 hunits x = H0;
4152 for (int i = 0; i < npoints; i++)
4153 x += point[i].h;
4154 return x;
4157 vunits draw_node::vertical_width()
4159 if (code == 'e')
4160 return V0;
4161 vunits x = V0;
4162 for (int i = 0; i < npoints; i++)
4163 x += point[i].v;
4164 return x;
4167 node *draw_node::copy()
4169 return new draw_node(code, point, npoints, sz, gcol, fcol);
4172 void draw_node::tprint(troff_output_file *out)
4174 out->draw(code, point, npoints, sz, gcol, fcol);
4177 /* tprint methods */
4179 void glyph_node::tprint(troff_output_file *out)
4181 tfont *ptf = tf->get_plain();
4182 if (ptf == tf)
4183 out->put_char_width(ci, ptf, gcol, fcol, width(), H0);
4184 else {
4185 hunits offset;
4186 int bold = tf->get_bold(&offset);
4187 hunits w = ptf->get_width(ci);
4188 hunits k = H0;
4189 hunits x;
4190 int cs = tf->get_constant_space(&x);
4191 if (cs) {
4192 x -= w;
4193 if (bold)
4194 x -= offset;
4195 hunits x2 = x/2;
4196 out->right(x2);
4197 k = x - x2;
4199 else
4200 k = tf->get_track_kern();
4201 if (bold) {
4202 out->put_char(ci, ptf, gcol, fcol);
4203 out->right(offset);
4205 out->put_char_width(ci, ptf, gcol, fcol, w, k);
4209 void glyph_node::zero_width_tprint(troff_output_file *out)
4211 tfont *ptf = tf->get_plain();
4212 hunits offset;
4213 int bold = tf->get_bold(&offset);
4214 hunits x;
4215 int cs = tf->get_constant_space(&x);
4216 if (cs) {
4217 x -= ptf->get_width(ci);
4218 if (bold)
4219 x -= offset;
4220 x = x/2;
4221 out->right(x);
4223 out->put_char(ci, ptf, gcol, fcol);
4224 if (bold) {
4225 out->right(offset);
4226 out->put_char(ci, ptf, gcol, fcol);
4227 out->right(-offset);
4229 if (cs)
4230 out->right(-x);
4233 void break_char_node::tprint(troff_output_file *t)
4235 ch->tprint(t);
4238 void break_char_node::zero_width_tprint(troff_output_file *t)
4240 ch->zero_width_tprint(t);
4243 void hline_node::tprint(troff_output_file *out)
4245 if (x < H0) {
4246 out->right(x);
4247 x = -x;
4249 if (n == 0) {
4250 out->right(x);
4251 return;
4253 hunits w = n->width();
4254 if (w <= H0) {
4255 error("horizontal line drawing character must have positive width");
4256 out->right(x);
4257 return;
4259 int i = int(x/w);
4260 if (i == 0) {
4261 hunits xx = x - w;
4262 hunits xx2 = xx/2;
4263 out->right(xx2);
4264 if (out->is_on())
4265 n->tprint(out);
4266 out->right(xx - xx2);
4268 else {
4269 hunits rem = x - w*i;
4270 if (rem > H0)
4271 if (n->overlaps_horizontally()) {
4272 if (out->is_on())
4273 n->tprint(out);
4274 out->right(rem - w);
4276 else
4277 out->right(rem);
4278 while (--i >= 0)
4279 if (out->is_on())
4280 n->tprint(out);
4284 void vline_node::tprint(troff_output_file *out)
4286 if (n == 0) {
4287 out->down(x);
4288 return;
4290 vunits h = n->size();
4291 int overlaps = n->overlaps_vertically();
4292 vunits y = x;
4293 if (y < V0) {
4294 y = -y;
4295 int i = y / h;
4296 vunits rem = y - i*h;
4297 if (i == 0) {
4298 out->right(n->width());
4299 out->down(-rem);
4301 else {
4302 while (--i > 0) {
4303 n->zero_width_tprint(out);
4304 out->down(-h);
4306 if (overlaps) {
4307 n->zero_width_tprint(out);
4308 out->down(-rem);
4309 if (out->is_on())
4310 n->tprint(out);
4311 out->down(-h);
4313 else {
4314 if (out->is_on())
4315 n->tprint(out);
4316 out->down(-h - rem);
4320 else {
4321 int i = y / h;
4322 vunits rem = y - i*h;
4323 if (i == 0) {
4324 out->down(rem);
4325 out->right(n->width());
4327 else {
4328 out->down(h);
4329 if (overlaps)
4330 n->zero_width_tprint(out);
4331 out->down(rem);
4332 while (--i > 0) {
4333 n->zero_width_tprint(out);
4334 out->down(h);
4336 if (out->is_on())
4337 n->tprint(out);
4342 void zero_width_node::tprint(troff_output_file *out)
4344 if (!n)
4345 return;
4346 if (!n->next) {
4347 n->zero_width_tprint(out);
4348 return;
4350 int hpos = out->get_hpos();
4351 int vpos = out->get_vpos();
4352 node *tem = n;
4353 while (tem) {
4354 tem->tprint(out);
4355 tem = tem->next;
4357 out->moveto(hpos, vpos);
4360 void overstrike_node::tprint(troff_output_file *out)
4362 hunits pos = H0;
4363 for (node *tem = list; tem; tem = tem->next) {
4364 hunits x = (max_width - tem->width())/2;
4365 out->right(x - pos);
4366 pos = x;
4367 tem->zero_width_tprint(out);
4369 out->right(max_width - pos);
4372 void bracket_node::tprint(troff_output_file *out)
4374 if (list == 0)
4375 return;
4376 int npieces = 0;
4377 node *tem;
4378 for (tem = list; tem; tem = tem->next)
4379 ++npieces;
4380 vunits h = list->size();
4381 vunits totalh = h*npieces;
4382 vunits y = (totalh - h)/2;
4383 out->down(y);
4384 for (tem = list; tem; tem = tem->next) {
4385 tem->zero_width_tprint(out);
4386 out->down(-h);
4388 out->right(max_width);
4389 out->down(totalh - y);
4392 void node::tprint(troff_output_file *)
4396 void node::zero_width_tprint(troff_output_file *out)
4398 int hpos = out->get_hpos();
4399 int vpos = out->get_vpos();
4400 tprint(out);
4401 out->moveto(hpos, vpos);
4404 void space_node::tprint(troff_output_file *out)
4406 out->fill_color(col);
4407 out->right(n);
4410 void hmotion_node::tprint(troff_output_file *out)
4412 out->fill_color(col);
4413 out->right(n);
4416 void space_char_hmotion_node::tprint(troff_output_file *out)
4418 out->fill_color(col);
4419 if (is_html) {
4420 // we emit the space width as a negative glyph index
4421 out->flush_tbuf();
4422 out->do_motion();
4423 out->put('N');
4424 out->put(-n.to_units());
4425 out->put('\n');
4427 out->right(n);
4430 void vmotion_node::tprint(troff_output_file *out)
4432 out->fill_color(col);
4433 out->down(n);
4436 void kern_pair_node::tprint(troff_output_file *out)
4438 n1->tprint(out);
4439 out->right(amount);
4440 n2->tprint(out);
4443 static void tprint_reverse_node_list(troff_output_file *out, node *n)
4445 if (n == 0)
4446 return;
4447 tprint_reverse_node_list(out, n->next);
4448 n->tprint(out);
4451 void dbreak_node::tprint(troff_output_file *out)
4453 tprint_reverse_node_list(out, none);
4456 void composite_node::tprint(troff_output_file *out)
4458 hunits bold_offset;
4459 int is_bold = tf->get_bold(&bold_offset);
4460 hunits track_kern = tf->get_track_kern();
4461 hunits constant_space;
4462 int is_constant_spaced = tf->get_constant_space(&constant_space);
4463 hunits x = H0;
4464 if (is_constant_spaced) {
4465 x = constant_space;
4466 for (node *tem = n; tem; tem = tem->next)
4467 x -= tem->width();
4468 if (is_bold)
4469 x -= bold_offset;
4470 hunits x2 = x/2;
4471 out->right(x2);
4472 x -= x2;
4474 if (is_bold) {
4475 int hpos = out->get_hpos();
4476 int vpos = out->get_vpos();
4477 tprint_reverse_node_list(out, n);
4478 out->moveto(hpos, vpos);
4479 out->right(bold_offset);
4481 tprint_reverse_node_list(out, n);
4482 if (is_constant_spaced)
4483 out->right(x);
4484 else
4485 out->right(track_kern);
4488 node *make_composite_node(charinfo *s, environment *env)
4490 int fontno = env_definite_font(env);
4491 if (fontno < 0) {
4492 error("no current font");
4493 return 0;
4495 assert(fontno < font_table_size && font_table[fontno] != 0);
4496 node *n = charinfo_to_node_list(s, env);
4497 font_size fs = env->get_font_size();
4498 int char_height = env->get_char_height();
4499 int char_slant = env->get_char_slant();
4500 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
4501 fontno);
4502 if (env->is_composite())
4503 tf = tf->get_plain();
4504 return new composite_node(n, s, tf);
4507 node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0)
4509 int fontno = env_definite_font(env);
4510 if (fontno < 0) {
4511 error("no current font");
4512 return 0;
4514 assert(fontno < font_table_size && font_table[fontno] != 0);
4515 int fn = fontno;
4516 int found = font_table[fontno]->contains(s);
4517 if (!found) {
4518 macro *mac = s->get_macro();
4519 if (mac && s->is_fallback())
4520 return make_composite_node(s, env);
4521 if (s->numbered()) {
4522 if (!no_error_message)
4523 warning(WARN_CHAR, "can't find numbered character %1",
4524 s->get_number());
4525 return 0;
4527 special_font_list *sf = font_table[fontno]->sf;
4528 while (sf != 0 && !found) {
4529 fn = sf->n;
4530 if (font_table[fn])
4531 found = font_table[fn]->contains(s);
4532 sf = sf->next;
4534 if (!found) {
4535 symbol f = font_table[fontno]->get_name();
4536 string gl(f.contents());
4537 gl += ' ';
4538 gl += s->nm.contents();
4539 gl += '\0';
4540 charinfo *ci = get_charinfo(symbol(gl.contents()));
4541 if (ci && ci->get_macro())
4542 return make_composite_node(ci, env);
4544 if (!found) {
4545 sf = global_special_fonts;
4546 while (sf != 0 && !found) {
4547 fn = sf->n;
4548 if (font_table[fn])
4549 found = font_table[fn]->contains(s);
4550 sf = sf->next;
4553 if (!found)
4554 if (mac && s->is_special())
4555 return make_composite_node(s, env);
4556 if (!found) {
4557 for (fn = 0; fn < font_table_size; fn++)
4558 if (font_table[fn]
4559 && font_table[fn]->is_special()
4560 && font_table[fn]->contains(s)) {
4561 found = 1;
4562 break;
4565 if (!found) {
4566 if (!no_error_message && s->first_time_not_found()) {
4567 unsigned char input_code = s->get_ascii_code();
4568 if (input_code != 0) {
4569 if (csgraph(input_code))
4570 warning(WARN_CHAR, "can't find character `%1'", input_code);
4571 else
4572 warning(WARN_CHAR, "can't find character with input code %1",
4573 int(input_code));
4575 else if (s->nm.contents())
4576 warning(WARN_CHAR, "can't find special character `%1'",
4577 s->nm.contents());
4579 return 0;
4582 font_size fs = env->get_font_size();
4583 int char_height = env->get_char_height();
4584 int char_slant = env->get_char_slant();
4585 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn);
4586 if (env->is_composite())
4587 tf = tf->get_plain();
4588 color *gcol = env->get_glyph_color();
4589 color *fcol = env->get_fill_color();
4590 return new glyph_node(s, tf, gcol, fcol);
4593 node *make_node(charinfo *ci, environment *env)
4595 switch (ci->get_special_translation()) {
4596 case charinfo::TRANSLATE_SPACE:
4597 return new space_char_hmotion_node(env->get_space_width(),
4598 env->get_fill_color());
4599 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4600 return new unbreakable_space_node(env->get_space_width(),
4601 env->get_fill_color());
4602 case charinfo::TRANSLATE_DUMMY:
4603 return new dummy_node;
4604 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4605 error("translation to \\% ignored in this context");
4606 break;
4608 charinfo *tem = ci->get_translation();
4609 if (tem)
4610 ci = tem;
4611 macro *mac = ci->get_macro();
4612 if (mac && ci->is_normal())
4613 return make_composite_node(ci, env);
4614 else
4615 return make_glyph_node(ci, env);
4618 int character_exists(charinfo *ci, environment *env)
4620 if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
4621 return 1;
4622 charinfo *tem = ci->get_translation();
4623 if (tem)
4624 ci = tem;
4625 if (ci->get_macro())
4626 return 1;
4627 node *nd = make_glyph_node(ci, env, 1);
4628 if (nd) {
4629 delete nd;
4630 return 1;
4632 return 0;
4635 node *node::add_char(charinfo *ci, environment *env,
4636 hunits *widthp, int *spacep)
4638 node *res;
4639 switch (ci->get_special_translation()) {
4640 case charinfo::TRANSLATE_SPACE:
4641 res = new space_char_hmotion_node(env->get_space_width(),
4642 env->get_fill_color(), this);
4643 *widthp += res->width();
4644 return res;
4645 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4646 res = new unbreakable_space_node(env->get_space_width(),
4647 env->get_fill_color(), this);
4648 res->freeze_space();
4649 *widthp += res->width();
4650 *spacep += res->nspaces();
4651 return res;
4652 case charinfo::TRANSLATE_DUMMY:
4653 return new dummy_node(this);
4654 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4655 return add_discretionary_hyphen();
4657 charinfo *tem = ci->get_translation();
4658 if (tem)
4659 ci = tem;
4660 macro *mac = ci->get_macro();
4661 if (mac && ci->is_normal()) {
4662 res = make_composite_node(ci, env);
4663 if (res) {
4664 res->next = this;
4665 *widthp += res->width();
4667 else
4668 return this;
4670 else {
4671 node *gn = make_glyph_node(ci, env);
4672 if (gn == 0)
4673 return this;
4674 else {
4675 hunits old_width = width();
4676 node *p = gn->merge_self(this);
4677 if (p == 0) {
4678 *widthp += gn->width();
4679 gn->next = this;
4680 res = gn;
4682 else {
4683 *widthp += p->width() - old_width;
4684 res = p;
4688 int break_code = 0;
4689 if (ci->can_break_before())
4690 break_code = 1;
4691 if (ci->can_break_after())
4692 break_code |= 2;
4693 if (break_code) {
4694 node *next1 = res->next;
4695 res->next = 0;
4696 res = new break_char_node(res, break_code, env->get_fill_color(), next1);
4698 return res;
4701 #ifdef __GNUG__
4702 inline
4703 #endif
4704 int same_node(node *n1, node *n2)
4706 if (n1 != 0) {
4707 if (n2 != 0)
4708 return n1->type() == n2->type() && n1->same(n2);
4709 else
4710 return 0;
4712 else
4713 return n2 == 0;
4716 int same_node_list(node *n1, node *n2)
4718 while (n1 && n2) {
4719 if (n1->type() != n2->type() || !n1->same(n2))
4720 return 0;
4721 n1 = n1->next;
4722 n2 = n2->next;
4724 return !n1 && !n2;
4727 int extra_size_node::same(node *nd)
4729 return n == ((extra_size_node *)nd)->n;
4732 const char *extra_size_node::type()
4734 return "extra_size_node";
4737 int extra_size_node::force_tprint()
4739 return 0;
4742 int vertical_size_node::same(node *nd)
4744 return n == ((vertical_size_node *)nd)->n;
4747 const char *vertical_size_node::type()
4749 return "vertical_size_node";
4752 int vertical_size_node::set_unformat_flag()
4754 return 0;
4757 int vertical_size_node::force_tprint()
4759 return 0;
4762 int hmotion_node::same(node *nd)
4764 return n == ((hmotion_node *)nd)->n
4765 && col == ((hmotion_node *)nd)->col;
4768 const char *hmotion_node::type()
4770 return "hmotion_node";
4773 int hmotion_node::set_unformat_flag()
4775 unformat = 1;
4776 return 1;
4779 int hmotion_node::force_tprint()
4781 return 0;
4784 node *hmotion_node::add_self(node *nd, hyphen_list **p)
4786 next = nd;
4787 hyphen_list *pp = *p;
4788 *p = (*p)->next;
4789 delete pp;
4790 return this;
4793 hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail, int *)
4795 return new hyphen_list(0, tail);
4798 int space_char_hmotion_node::same(node *nd)
4800 return n == ((space_char_hmotion_node *)nd)->n
4801 && col == ((space_char_hmotion_node *)nd)->col;
4804 const char *space_char_hmotion_node::type()
4806 return "space_char_hmotion_node";
4809 int space_char_hmotion_node::force_tprint()
4811 return 0;
4814 node *space_char_hmotion_node::add_self(node *nd, hyphen_list **p)
4816 next = nd;
4817 hyphen_list *pp = *p;
4818 *p = (*p)->next;
4819 delete pp;
4820 return this;
4823 hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail,
4824 int *)
4826 return new hyphen_list(0, tail);
4829 int vmotion_node::same(node *nd)
4831 return n == ((vmotion_node *)nd)->n
4832 && col == ((vmotion_node *)nd)->col;
4835 const char *vmotion_node::type()
4837 return "vmotion_node";
4840 int vmotion_node::force_tprint()
4842 return 0;
4845 int hline_node::same(node *nd)
4847 return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n);
4850 const char *hline_node::type()
4852 return "hline_node";
4855 int hline_node::force_tprint()
4857 return 0;
4860 int vline_node::same(node *nd)
4862 return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n);
4865 const char *vline_node::type()
4867 return "vline_node";
4870 int vline_node::force_tprint()
4872 return 0;
4875 int dummy_node::same(node * /*nd*/)
4877 return 1;
4880 const char *dummy_node::type()
4882 return "dummy_node";
4885 int dummy_node::force_tprint()
4887 return 0;
4890 int transparent_dummy_node::same(node * /*nd*/)
4892 return 1;
4895 const char *transparent_dummy_node::type()
4897 return "transparent_dummy_node";
4900 int transparent_dummy_node::force_tprint()
4902 return 0;
4905 int transparent_dummy_node::ends_sentence()
4907 return 2;
4910 int zero_width_node::same(node *nd)
4912 return same_node_list(n, ((zero_width_node *)nd)->n);
4915 const char *zero_width_node::type()
4917 return "zero_width_node";
4920 int zero_width_node::force_tprint()
4922 return 0;
4925 int italic_corrected_node::same(node *nd)
4927 return (x == ((italic_corrected_node *)nd)->x
4928 && same_node(n, ((italic_corrected_node *)nd)->n));
4931 const char *italic_corrected_node::type()
4933 return "italic_corrected_node";
4936 int italic_corrected_node::force_tprint()
4938 return 0;
4941 left_italic_corrected_node::left_italic_corrected_node(node *xx)
4942 : node(xx), n(0)
4946 left_italic_corrected_node::~left_italic_corrected_node()
4948 delete n;
4951 node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
4953 if (n == 0) {
4954 hunits lic = gn->left_italic_correction();
4955 if (!lic.is_zero()) {
4956 x = lic;
4957 n = gn;
4958 return this;
4961 else {
4962 node *nd = n->merge_glyph_node(gn);
4963 if (nd) {
4964 n = nd;
4965 x = n->left_italic_correction();
4966 return this;
4969 return 0;
4972 node *left_italic_corrected_node::copy()
4974 left_italic_corrected_node *nd = new left_italic_corrected_node;
4975 if (n) {
4976 nd->n = n->copy();
4977 nd->x = x;
4979 return nd;
4982 void left_italic_corrected_node::tprint(troff_output_file *out)
4984 if (n) {
4985 out->right(x);
4986 n->tprint(out);
4990 const char *left_italic_corrected_node::type()
4992 return "left_italic_corrected_node";
4995 int left_italic_corrected_node::force_tprint()
4997 return 0;
5000 int left_italic_corrected_node::same(node *nd)
5002 return (x == ((left_italic_corrected_node *)nd)->x
5003 && same_node(n, ((left_italic_corrected_node *)nd)->n));
5006 void left_italic_corrected_node::ascii_print(ascii_output_file *out)
5008 if (n)
5009 n->ascii_print(out);
5012 hunits left_italic_corrected_node::width()
5014 return n ? n->width() + x : H0;
5017 void left_italic_corrected_node::vertical_extent(vunits *minimum,
5018 vunits *maximum)
5020 if (n)
5021 n->vertical_extent(minimum, maximum);
5022 else
5023 node::vertical_extent(minimum, maximum);
5026 hunits left_italic_corrected_node::skew()
5028 return n ? n->skew() + x/2 : H0;
5031 hunits left_italic_corrected_node::subscript_correction()
5033 return n ? n->subscript_correction() : H0;
5036 hunits left_italic_corrected_node::italic_correction()
5038 return n ? n->italic_correction() : H0;
5041 int left_italic_corrected_node::ends_sentence()
5043 return n ? n->ends_sentence() : 0;
5046 int left_italic_corrected_node::overlaps_horizontally()
5048 return n ? n->overlaps_horizontally() : 0;
5051 int left_italic_corrected_node::overlaps_vertically()
5053 return n ? n->overlaps_vertically() : 0;
5056 node *left_italic_corrected_node::last_char_node()
5058 return n ? n->last_char_node() : 0;
5061 tfont *left_italic_corrected_node::get_tfont()
5063 return n ? n->get_tfont() : 0;
5066 hyphenation_type left_italic_corrected_node::get_hyphenation_type()
5068 if (n)
5069 return n->get_hyphenation_type();
5070 else
5071 return HYPHEN_MIDDLE;
5074 hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail,
5075 int *count)
5077 return n ? n->get_hyphen_list(tail, count) : tail;
5080 node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
5082 if (n) {
5083 nd = new left_italic_corrected_node(nd);
5084 nd = n->add_self(nd, p);
5085 n = 0;
5086 delete this;
5088 return nd;
5091 int left_italic_corrected_node::character_type()
5093 return n ? n->character_type() : 0;
5096 int overstrike_node::same(node *nd)
5098 return same_node_list(list, ((overstrike_node *)nd)->list);
5101 const char *overstrike_node::type()
5103 return "overstrike_node";
5106 int overstrike_node::force_tprint()
5108 return 0;
5111 node *overstrike_node::add_self(node *n, hyphen_list **p)
5113 next = n;
5114 hyphen_list *pp = *p;
5115 *p = (*p)->next;
5116 delete pp;
5117 return this;
5120 hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail, int *)
5122 return new hyphen_list(0, tail);
5125 int bracket_node::same(node *nd)
5127 return same_node_list(list, ((bracket_node *)nd)->list);
5130 const char *bracket_node::type()
5132 return "bracket_node";
5135 int bracket_node::force_tprint()
5137 return 0;
5140 int composite_node::same(node *nd)
5142 return ci == ((composite_node *)nd)->ci
5143 && same_node_list(n, ((composite_node *)nd)->n);
5146 const char *composite_node::type()
5148 return "composite_node";
5151 int composite_node::force_tprint()
5153 return 0;
5156 int glyph_node::same(node *nd)
5158 return ci == ((glyph_node *)nd)->ci
5159 && tf == ((glyph_node *)nd)->tf
5160 && gcol == ((glyph_node *)nd)->gcol
5161 && fcol == ((glyph_node *)nd)->fcol;
5164 const char *glyph_node::type()
5166 return "glyph_node";
5169 int glyph_node::force_tprint()
5171 return 0;
5174 int ligature_node::same(node *nd)
5176 return (same_node(n1, ((ligature_node *)nd)->n1)
5177 && same_node(n2, ((ligature_node *)nd)->n2)
5178 && glyph_node::same(nd));
5181 const char *ligature_node::type()
5183 return "ligature_node";
5186 int ligature_node::force_tprint()
5188 return 0;
5191 int kern_pair_node::same(node *nd)
5193 return (amount == ((kern_pair_node *)nd)->amount
5194 && same_node(n1, ((kern_pair_node *)nd)->n1)
5195 && same_node(n2, ((kern_pair_node *)nd)->n2));
5198 const char *kern_pair_node::type()
5200 return "kern_pair_node";
5203 int kern_pair_node::force_tprint()
5205 return 0;
5208 int dbreak_node::same(node *nd)
5210 return (same_node_list(none, ((dbreak_node *)nd)->none)
5211 && same_node_list(pre, ((dbreak_node *)nd)->pre)
5212 && same_node_list(post, ((dbreak_node *)nd)->post));
5215 const char *dbreak_node::type()
5217 return "dbreak_node";
5220 int dbreak_node::force_tprint()
5222 return 0;
5225 int break_char_node::same(node *nd)
5227 return break_code == ((break_char_node *)nd)->break_code
5228 && col == ((break_char_node *)nd)->col
5229 && same_node(ch, ((break_char_node *)nd)->ch);
5232 const char *break_char_node::type()
5234 return "break_char_node";
5237 int break_char_node::force_tprint()
5239 return 0;
5242 int line_start_node::same(node * /*nd*/)
5244 return 1;
5247 const char *line_start_node::type()
5249 return "line_start_node";
5252 int line_start_node::force_tprint()
5254 return 0;
5257 int space_node::same(node *nd)
5259 return n == ((space_node *)nd)->n
5260 && set == ((space_node *)nd)->set
5261 && col == ((space_node *)nd)->col;
5264 const char *space_node::type()
5266 return "space_node";
5269 int word_space_node::same(node *nd)
5271 return n == ((word_space_node *)nd)->n
5272 && set == ((word_space_node *)nd)->set
5273 && col == ((word_space_node *)nd)->col;
5276 const char *word_space_node::type()
5278 return "word_space_node";
5281 int word_space_node::force_tprint()
5283 return 0;
5286 void unbreakable_space_node::tprint(troff_output_file *out)
5288 out->fill_color(col);
5289 if (is_html) {
5290 // we emit the space width as a negative glyph index
5291 out->flush_tbuf();
5292 out->do_motion();
5293 out->put('N');
5294 out->put(-n.to_units());
5295 out->put('\n');
5297 out->right(n);
5300 int unbreakable_space_node::same(node *nd)
5302 return n == ((unbreakable_space_node *)nd)->n
5303 && set == ((unbreakable_space_node *)nd)->set
5304 && col == ((unbreakable_space_node *)nd)->col;
5307 const char *unbreakable_space_node::type()
5309 return "unbreakable_space_node";
5312 node *unbreakable_space_node::add_self(node *nd, hyphen_list **p)
5314 next = nd;
5315 hyphen_list *pp = *p;
5316 *p = (*p)->next;
5317 delete pp;
5318 return this;
5321 hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail, int *)
5323 return new hyphen_list(0, tail);
5326 int diverted_space_node::same(node *nd)
5328 return n == ((diverted_space_node *)nd)->n;
5331 const char *diverted_space_node::type()
5333 return "diverted_space_node";
5336 int diverted_space_node::force_tprint()
5338 return 0;
5341 int diverted_copy_file_node::same(node *nd)
5343 return filename == ((diverted_copy_file_node *)nd)->filename;
5346 const char *diverted_copy_file_node::type()
5348 return "diverted_copy_file_node";
5351 int diverted_copy_file_node::force_tprint()
5353 return 0;
5356 // Grow the font_table so that its size is > n.
5358 static void grow_font_table(int n)
5360 assert(n >= font_table_size);
5361 font_info **old_font_table = font_table;
5362 int old_font_table_size = font_table_size;
5363 font_table_size = font_table_size ? (font_table_size*3)/2 : 10;
5364 if (font_table_size <= n)
5365 font_table_size = n + 10;
5366 font_table = new font_info *[font_table_size];
5367 if (old_font_table_size)
5368 memcpy(font_table, old_font_table,
5369 old_font_table_size*sizeof(font_info *));
5370 a_delete old_font_table;
5371 for (int i = old_font_table_size; i < font_table_size; i++)
5372 font_table[i] = 0;
5375 dictionary font_translation_dictionary(17);
5377 static symbol get_font_translation(symbol nm)
5379 void *p = font_translation_dictionary.lookup(nm);
5380 return p ? symbol((char *)p) : nm;
5383 dictionary font_dictionary(50);
5385 static int mount_font_no_translate(int n, symbol name, symbol external_name)
5387 assert(n >= 0);
5388 // We store the address of this char in font_dictionary to indicate
5389 // that we've previously tried to mount the font and failed.
5390 static char a_char;
5391 font *fm = 0;
5392 void *p = font_dictionary.lookup(external_name);
5393 if (p == 0) {
5394 int not_found;
5395 fm = font::load_font(external_name.contents(), &not_found);
5396 if (!fm) {
5397 if (not_found)
5398 warning(WARN_FONT, "can't find font `%1'", external_name.contents());
5399 (void)font_dictionary.lookup(external_name, &a_char);
5400 return 0;
5402 (void)font_dictionary.lookup(name, fm);
5404 else if (p == &a_char) {
5405 #if 0
5406 error("invalid font `%1'", external_name.contents());
5407 #endif
5408 return 0;
5410 else
5411 fm = (font*)p;
5412 if (n >= font_table_size) {
5413 if (n - font_table_size > 1000) {
5414 error("font position too much larger than first unused position");
5415 return 0;
5417 grow_font_table(n);
5419 else if (font_table[n] != 0)
5420 delete font_table[n];
5421 font_table[n] = new font_info(name, n, external_name, fm);
5422 font_family::invalidate_fontno(n);
5423 return 1;
5426 int mount_font(int n, symbol name, symbol external_name)
5428 assert(n >= 0);
5429 name = get_font_translation(name);
5430 if (external_name.is_null())
5431 external_name = name;
5432 else
5433 external_name = get_font_translation(external_name);
5434 return mount_font_no_translate(n, name, external_name);
5437 void mount_style(int n, symbol name)
5439 assert(n >= 0);
5440 if (n >= font_table_size) {
5441 if (n - font_table_size > 1000) {
5442 error("font position too much larger than first unused position");
5443 return;
5445 grow_font_table(n);
5447 else if (font_table[n] != 0)
5448 delete font_table[n];
5449 font_table[n] = new font_info(get_font_translation(name), n, NULL_SYMBOL, 0);
5450 font_family::invalidate_fontno(n);
5453 /* global functions */
5455 void font_translate()
5457 symbol from = get_name(1);
5458 if (!from.is_null()) {
5459 symbol to = get_name();
5460 if (to.is_null() || from == to)
5461 font_translation_dictionary.remove(from);
5462 else
5463 (void)font_translation_dictionary.lookup(from, (void *)to.contents());
5465 skip_line();
5468 void font_position()
5470 int n;
5471 if (get_integer(&n)) {
5472 if (n < 0)
5473 error("negative font position");
5474 else {
5475 symbol internal_name = get_name(1);
5476 if (!internal_name.is_null()) {
5477 symbol external_name = get_long_name();
5478 mount_font(n, internal_name, external_name); // ignore error
5482 skip_line();
5485 font_family::font_family(symbol s)
5486 : map_size(10), nm(s)
5488 map = new int[map_size];
5489 for (int i = 0; i < map_size; i++)
5490 map[i] = -1;
5493 font_family::~font_family()
5495 a_delete map;
5498 int font_family::make_definite(int i)
5500 if (i >= 0) {
5501 if (i < map_size && map[i] >= 0)
5502 return map[i];
5503 else {
5504 if (i < font_table_size && font_table[i] != 0) {
5505 if (i >= map_size) {
5506 int old_map_size = map_size;
5507 int *old_map = map;
5508 map_size *= 3;
5509 map_size /= 2;
5510 if (i >= map_size)
5511 map_size = i + 10;
5512 map = new int[map_size];
5513 memcpy(map, old_map, old_map_size*sizeof(int));
5514 a_delete old_map;
5515 for (int j = old_map_size; j < map_size; j++)
5516 map[j] = -1;
5518 if (font_table[i]->is_style()) {
5519 symbol sty = font_table[i]->get_name();
5520 symbol f = concat(nm, sty);
5521 int n;
5522 // don't use symbol_fontno, because that might return a style
5523 // and because we don't want to translate the name
5524 for (n = 0; n < font_table_size; n++)
5525 if (font_table[n] != 0 && font_table[n]->is_named(f)
5526 && !font_table[n]->is_style())
5527 break;
5528 if (n >= font_table_size) {
5529 n = next_available_font_position();
5530 if (!mount_font_no_translate(n, f, f))
5531 return -1;
5533 return map[i] = n;
5535 else
5536 return map[i] = i;
5538 else
5539 return -1;
5542 else
5543 return -1;
5546 dictionary family_dictionary(5);
5548 font_family *lookup_family(symbol nm)
5550 font_family *f = (font_family *)family_dictionary.lookup(nm);
5551 if (!f) {
5552 f = new font_family(nm);
5553 (void)family_dictionary.lookup(nm, f);
5555 return f;
5558 void font_family::invalidate_fontno(int n)
5560 assert(n >= 0 && n < font_table_size);
5561 dictionary_iterator iter(family_dictionary);
5562 symbol nam;
5563 font_family *fam;
5564 while (iter.get(&nam, (void **)&fam)) {
5565 int mapsize = fam->map_size;
5566 if (n < mapsize)
5567 fam->map[n] = -1;
5568 for (int i = 0; i < mapsize; i++)
5569 if (fam->map[i] == n)
5570 fam->map[i] = -1;
5574 void style()
5576 int n;
5577 if (get_integer(&n)) {
5578 if (n < 0)
5579 error("negative font position");
5580 else {
5581 symbol internal_name = get_name(1);
5582 if (!internal_name.is_null())
5583 mount_style(n, internal_name);
5586 skip_line();
5589 static int get_fontno()
5591 int n;
5592 tok.skip();
5593 if (tok.delimiter()) {
5594 symbol s = get_name(1);
5595 if (!s.is_null()) {
5596 n = symbol_fontno(s);
5597 if (n < 0) {
5598 n = next_available_font_position();
5599 if (!mount_font(n, s))
5600 return -1;
5602 return curenv->get_family()->make_definite(n);
5605 else if (get_integer(&n)) {
5606 if (n < 0 || n >= font_table_size || font_table[n] == 0)
5607 error("bad font number");
5608 else
5609 return curenv->get_family()->make_definite(n);
5611 return -1;
5614 static int underline_fontno = 2;
5616 void underline_font()
5618 int n = get_fontno();
5619 if (n >= 0)
5620 underline_fontno = n;
5621 skip_line();
5624 int get_underline_fontno()
5626 return underline_fontno;
5629 void define_font_special_character()
5631 int n = get_fontno();
5632 if (n < 0) {
5633 skip_line();
5634 return;
5636 symbol f = font_table[n]->get_name();
5637 do_define_character(CHAR_FONT_SPECIAL, f.contents());
5640 void remove_font_special_character()
5642 int n = get_fontno();
5643 if (n < 0) {
5644 skip_line();
5645 return;
5647 symbol f = font_table[n]->get_name();
5648 while (!tok.newline() && !tok.eof()) {
5649 if (!tok.space() && !tok.tab()) {
5650 charinfo *s = tok.get_char(1);
5651 string gl(f.contents());
5652 gl += ' ';
5653 gl += s->nm.contents();
5654 gl += '\0';
5655 charinfo *ci = get_charinfo(symbol(gl.contents()));
5656 if (!ci)
5657 break;
5658 macro *m = ci->set_macro(0);
5659 if (m)
5660 delete m;
5662 tok.next();
5664 skip_line();
5667 static void read_special_fonts(special_font_list **sp)
5669 special_font_list *s = *sp;
5670 *sp = 0;
5671 while (s != 0) {
5672 special_font_list *tem = s;
5673 s = s->next;
5674 delete tem;
5676 special_font_list **p = sp;
5677 while (has_arg()) {
5678 int i = get_fontno();
5679 if (i >= 0) {
5680 special_font_list *tem = new special_font_list;
5681 tem->n = i;
5682 tem->next = 0;
5683 *p = tem;
5684 p = &(tem->next);
5689 void font_special_request()
5691 int n = get_fontno();
5692 if (n >= 0)
5693 read_special_fonts(&font_table[n]->sf);
5694 skip_line();
5697 void special_request()
5699 read_special_fonts(&global_special_fonts);
5700 skip_line();
5703 int next_available_font_position()
5705 int i;
5706 for (i = 1; i < font_table_size && font_table[i] != 0; i++)
5708 return i;
5711 int symbol_fontno(symbol s)
5713 s = get_font_translation(s);
5714 for (int i = 0; i < font_table_size; i++)
5715 if (font_table[i] != 0 && font_table[i]->is_named(s))
5716 return i;
5717 return -1;
5720 int is_good_fontno(int n)
5722 return n >= 0 && n < font_table_size && font_table[n] != 0;
5725 int get_bold_fontno(int n)
5727 if (n >= 0 && n < font_table_size && font_table[n] != 0) {
5728 hunits offset;
5729 if (font_table[n]->get_bold(&offset))
5730 return offset.to_units() + 1;
5731 else
5732 return 0;
5734 else
5735 return 0;
5738 hunits env_digit_width(environment *env)
5740 node *n = make_glyph_node(charset_table['0'], env);
5741 if (n) {
5742 hunits x = n->width();
5743 delete n;
5744 return x;
5746 else
5747 return H0;
5750 hunits env_space_width(environment *env)
5752 int fn = env_definite_font(env);
5753 font_size fs = env->get_font_size();
5754 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5755 return scale(fs.to_units()/3, env->get_space_size(), 12);
5756 else
5757 return font_table[fn]->get_space_width(fs, env->get_space_size());
5760 hunits env_sentence_space_width(environment *env)
5762 int fn = env_definite_font(env);
5763 font_size fs = env->get_font_size();
5764 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5765 return scale(fs.to_units()/3, env->get_sentence_space_size(), 12);
5766 else
5767 return font_table[fn]->get_space_width(fs, env->get_sentence_space_size());
5770 hunits env_half_narrow_space_width(environment *env)
5772 int fn = env_definite_font(env);
5773 font_size fs = env->get_font_size();
5774 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5775 return 0;
5776 else
5777 return font_table[fn]->get_half_narrow_space_width(fs);
5780 hunits env_narrow_space_width(environment *env)
5782 int fn = env_definite_font(env);
5783 font_size fs = env->get_font_size();
5784 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5785 return 0;
5786 else
5787 return font_table[fn]->get_narrow_space_width(fs);
5790 void bold_font()
5792 int n = get_fontno();
5793 if (n >= 0) {
5794 if (has_arg()) {
5795 if (tok.delimiter()) {
5796 int f = get_fontno();
5797 if (f >= 0) {
5798 units offset;
5799 if (has_arg() && get_number(&offset, 'u') && offset >= 1)
5800 font_table[f]->set_conditional_bold(n, hunits(offset - 1));
5801 else
5802 font_table[f]->conditional_unbold(n);
5805 else {
5806 units offset;
5807 if (get_number(&offset, 'u') && offset >= 1)
5808 font_table[n]->set_bold(hunits(offset - 1));
5809 else
5810 font_table[n]->unbold();
5813 else
5814 font_table[n]->unbold();
5816 skip_line();
5819 track_kerning_function::track_kerning_function() : non_zero(0)
5823 track_kerning_function::track_kerning_function(int min_s, hunits min_a,
5824 int max_s, hunits max_a)
5825 : non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s),
5826 max_amount(max_a)
5830 int track_kerning_function::operator==(const track_kerning_function &tk)
5832 if (non_zero)
5833 return (tk.non_zero
5834 && min_size == tk.min_size
5835 && min_amount == tk.min_amount
5836 && max_size == tk.max_size
5837 && max_amount == tk.max_amount);
5838 else
5839 return !tk.non_zero;
5842 int track_kerning_function::operator!=(const track_kerning_function &tk)
5844 if (non_zero)
5845 return (!tk.non_zero
5846 || min_size != tk.min_size
5847 || min_amount != tk.min_amount
5848 || max_size != tk.max_size
5849 || max_amount != tk.max_amount);
5850 else
5851 return tk.non_zero;
5854 hunits track_kerning_function::compute(int size)
5856 if (non_zero) {
5857 if (max_size <= min_size)
5858 return min_amount;
5859 else if (size <= min_size)
5860 return min_amount;
5861 else if (size >= max_size)
5862 return max_amount;
5863 else
5864 return (scale(max_amount, size - min_size, max_size - min_size)
5865 + scale(min_amount, max_size - size, max_size - min_size));
5867 else
5868 return H0;
5871 void track_kern()
5873 int n = get_fontno();
5874 if (n >= 0) {
5875 int min_s, max_s;
5876 hunits min_a, max_a;
5877 if (has_arg()
5878 && get_number(&min_s, 'z')
5879 && get_hunits(&min_a, 'p')
5880 && get_number(&max_s, 'z')
5881 && get_hunits(&max_a, 'p')) {
5882 track_kerning_function tk(min_s, min_a, max_s, max_a);
5883 font_table[n]->set_track_kern(tk);
5885 else {
5886 track_kerning_function tk;
5887 font_table[n]->set_track_kern(tk);
5890 skip_line();
5893 void constant_space()
5895 int n = get_fontno();
5896 if (n >= 0) {
5897 int x, y;
5898 if (!has_arg() || !get_integer(&x))
5899 font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
5900 else {
5901 if (!has_arg() || !get_number(&y, 'z'))
5902 font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
5903 else
5904 font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
5905 scale(y*x,
5906 units_per_inch,
5907 36*72*sizescale));
5910 skip_line();
5913 void ligature()
5915 int lig;
5916 if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2)
5917 global_ligature_mode = lig;
5918 else
5919 global_ligature_mode = 1;
5920 skip_line();
5923 void kern_request()
5925 int k;
5926 if (has_arg() && get_integer(&k))
5927 global_kern_mode = k != 0;
5928 else
5929 global_kern_mode = 1;
5930 skip_line();
5933 void set_soft_hyphen_char()
5935 soft_hyphen_char = get_optional_char();
5936 if (!soft_hyphen_char)
5937 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
5938 skip_line();
5941 void init_output()
5943 if (suppress_output_flag)
5944 the_output = new suppress_output_file;
5945 else if (ascii_output_flag)
5946 the_output = new ascii_output_file;
5947 else
5948 the_output = new troff_output_file;
5951 class next_available_font_position_reg : public reg {
5952 public:
5953 const char *get_string();
5956 const char *next_available_font_position_reg::get_string()
5958 return i_to_a(next_available_font_position());
5961 class printing_reg : public reg {
5962 public:
5963 const char *get_string();
5966 const char *printing_reg::get_string()
5968 if (the_output)
5969 return the_output->is_printing() ? "1" : "0";
5970 else
5971 return "0";
5974 void init_node_requests()
5976 init_request("bd", bold_font);
5977 init_request("cs", constant_space);
5978 init_request("fp", font_position);
5979 init_request("fschar", define_font_special_character);
5980 init_request("fspecial", font_special_request);
5981 init_request("ftr", font_translate);
5982 init_request("kern", kern_request);
5983 init_request("lg", ligature);
5984 init_request("rfschar", remove_font_special_character);
5985 init_request("shc", set_soft_hyphen_char);
5986 init_request("special", special_request);
5987 init_request("sty", style);
5988 init_request("tkf", track_kern);
5989 init_request("uf", underline_font);
5990 number_reg_dictionary.define(".fp", new next_available_font_position_reg);
5991 number_reg_dictionary.define(".kern",
5992 new constant_int_reg(&global_kern_mode));
5993 number_reg_dictionary.define(".lg",
5994 new constant_int_reg(&global_ligature_mode));
5995 number_reg_dictionary.define(".P", new printing_reg);
5996 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);