* src/roff/troff/input.cc (charinfo_to_node_list): Reset
[s-roff.git] / src / roff / troff / node.cc
blob53b9477e56bf64002c5bd51fa5fa974646e15991
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003
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 "symbol.h"
29 #include "dictionary.h"
30 #include "hvunits.h"
31 #include "env.h"
32 #include "request.h"
33 #include "node.h"
34 #include "token.h"
35 #include "charinfo.h"
36 #include "font.h"
37 #include "reg.h"
38 #include "input.h"
39 #include "div.h"
40 #include "geometry.h"
41 #include "stringclass.h"
43 #include "nonposix.h"
45 #ifdef _POSIX_VERSION
47 #include <sys/wait.h>
49 #else /* not _POSIX_VERSION */
51 /* traditional Unix */
53 #define WIFEXITED(s) (((s) & 0377) == 0)
54 #define WEXITSTATUS(s) (((s) >> 8) & 0377)
55 #define WTERMSIG(s) ((s) & 0177)
56 #define WIFSTOPPED(s) (((s) & 0377) == 0177)
57 #define WSTOPSIG(s) (((s) >> 8) & 0377)
58 #define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
60 #endif /* not _POSIX_VERSION */
63 * how many boundaries of images have been written? Useful for
64 * debugging grohtml
67 int image_no = 0;
68 static int suppress_start_page = 0;
70 #define STORE_WIDTH 1
72 symbol HYPHEN_SYMBOL("hy");
74 // Character used when a hyphen is inserted at a line break.
75 static charinfo *soft_hyphen_char;
77 enum constant_space_type {
78 CONSTANT_SPACE_NONE,
79 CONSTANT_SPACE_RELATIVE,
80 CONSTANT_SPACE_ABSOLUTE
83 struct special_font_list {
84 int n;
85 special_font_list *next;
88 special_font_list *global_special_fonts;
89 static int global_ligature_mode = 1;
90 static int global_kern_mode = 1;
92 class track_kerning_function {
93 int non_zero;
94 units min_size;
95 hunits min_amount;
96 units max_size;
97 hunits max_amount;
98 public:
99 track_kerning_function();
100 track_kerning_function(units, hunits, units, hunits);
101 int operator==(const track_kerning_function &);
102 int operator!=(const track_kerning_function &);
103 hunits compute(int point_size);
106 // embolden fontno when this is the current font
108 struct conditional_bold {
109 conditional_bold *next;
110 int fontno;
111 hunits offset;
112 conditional_bold(int, hunits, conditional_bold * = 0);
115 struct tfont;
117 class font_info {
118 tfont *last_tfont;
119 int number;
120 font_size last_size;
121 int last_height;
122 int last_slant;
123 symbol internal_name;
124 symbol external_name;
125 font *fm;
126 char is_bold;
127 hunits bold_offset;
128 track_kerning_function track_kern;
129 constant_space_type is_constant_spaced;
130 units constant_space;
131 int last_ligature_mode;
132 int last_kern_mode;
133 conditional_bold *cond_bold_list;
134 void flush();
135 public:
136 special_font_list *sf;
137 font_info(symbol nm, int n, symbol enm, font *f);
138 int contains(charinfo *);
139 void set_bold(hunits);
140 void unbold();
141 void set_conditional_bold(int, hunits);
142 void conditional_unbold(int);
143 void set_track_kern(track_kerning_function &);
144 void set_constant_space(constant_space_type, units = 0);
145 int is_named(symbol);
146 symbol get_name();
147 tfont *get_tfont(font_size, int, int, int);
148 hunits get_space_width(font_size, int);
149 hunits get_narrow_space_width(font_size);
150 hunits get_half_narrow_space_width(font_size);
151 int get_bold(hunits *);
152 int is_special();
153 int is_style();
154 friend symbol get_font_name(int, environment *);
157 class tfont_spec {
158 protected:
159 symbol name;
160 int input_position;
161 font *fm;
162 font_size size;
163 char is_bold;
164 char is_constant_spaced;
165 int ligature_mode;
166 int kern_mode;
167 hunits bold_offset;
168 hunits track_kern; // add this to the width
169 hunits constant_space_width;
170 int height;
171 int slant;
172 public:
173 tfont_spec(symbol nm, int pos, font *, font_size, int, int);
174 tfont_spec(const tfont_spec &spec) { *this = spec; }
175 tfont_spec plain();
176 int operator==(const tfont_spec &);
177 friend tfont *font_info::get_tfont(font_size fs, int, int, int);
180 class tfont : public tfont_spec {
181 static tfont *tfont_list;
182 tfont *next;
183 tfont *plain_version;
184 public:
185 tfont(tfont_spec &);
186 int contains(charinfo *);
187 hunits get_width(charinfo *c);
188 int get_bold(hunits *);
189 int get_constant_space(hunits *);
190 hunits get_track_kern();
191 tfont *get_plain();
192 font_size get_size();
193 symbol get_name();
194 charinfo *get_lig(charinfo *c1, charinfo *c2);
195 int get_kern(charinfo *c1, charinfo *c2, hunits *res);
196 int get_input_position();
197 int get_character_type(charinfo *);
198 int get_height();
199 int get_slant();
200 vunits get_char_height(charinfo *);
201 vunits get_char_depth(charinfo *);
202 hunits get_char_skew(charinfo *);
203 hunits get_italic_correction(charinfo *);
204 hunits get_left_italic_correction(charinfo *);
205 hunits get_subscript_correction(charinfo *);
206 friend tfont *make_tfont(tfont_spec &);
209 inline int env_definite_font(environment *env)
211 return env->get_family()->make_definite(env->get_font());
214 /* font_info functions */
216 static font_info **font_table = 0;
217 static int font_table_size = 0;
219 font_info::font_info(symbol nm, int n, symbol enm, font *f)
220 : last_tfont(0), number(n), last_size(0),
221 internal_name(nm), external_name(enm), fm(f),
222 is_bold(0), is_constant_spaced(CONSTANT_SPACE_NONE), last_ligature_mode(1),
223 last_kern_mode(1), cond_bold_list(0), sf(0)
227 inline int font_info::contains(charinfo *ci)
229 return fm != 0 && fm->contains(ci->get_index());
232 inline int font_info::is_special()
234 return fm != 0 && fm->is_special();
237 inline int font_info::is_style()
239 return fm == 0;
242 tfont *make_tfont(tfont_spec &spec)
244 for (tfont *p = tfont::tfont_list; p; p = p->next)
245 if (*p == spec)
246 return p;
247 return new tfont(spec);
250 // this is the current_font, fontno is where we found the character,
251 // presumably a special font
253 tfont *font_info::get_tfont(font_size fs, int height, int slant, int fontno)
255 if (last_tfont == 0 || fs != last_size
256 || height != last_height || slant != last_slant
257 || global_ligature_mode != last_ligature_mode
258 || global_kern_mode != last_kern_mode
259 || fontno != number) {
260 font_info *f = font_table[fontno];
261 tfont_spec spec(f->external_name, f->number, f->fm, fs, height, slant);
262 for (conditional_bold *p = cond_bold_list; p; p = p->next)
263 if (p->fontno == fontno) {
264 spec.is_bold = 1;
265 spec.bold_offset = p->offset;
266 break;
268 if (!spec.is_bold && is_bold) {
269 spec.is_bold = 1;
270 spec.bold_offset = bold_offset;
272 spec.track_kern = track_kern.compute(fs.to_scaled_points());
273 spec.ligature_mode = global_ligature_mode;
274 spec.kern_mode = global_kern_mode;
275 switch (is_constant_spaced) {
276 case CONSTANT_SPACE_NONE:
277 break;
278 case CONSTANT_SPACE_ABSOLUTE:
279 spec.is_constant_spaced = 1;
280 spec.constant_space_width = constant_space;
281 break;
282 case CONSTANT_SPACE_RELATIVE:
283 spec.is_constant_spaced = 1;
284 spec.constant_space_width
285 = scale(constant_space*fs.to_scaled_points(),
286 units_per_inch,
287 36*72*sizescale);
288 break;
289 default:
290 assert(0);
292 if (fontno != number)
293 return make_tfont(spec);
294 last_tfont = make_tfont(spec);
295 last_size = fs;
296 last_height = height;
297 last_slant = slant;
298 last_ligature_mode = global_ligature_mode;
299 last_kern_mode = global_kern_mode;
301 return last_tfont;
304 int font_info::get_bold(hunits *res)
306 if (is_bold) {
307 *res = bold_offset;
308 return 1;
310 else
311 return 0;
314 void font_info::unbold()
316 if (is_bold) {
317 is_bold = 0;
318 flush();
322 void font_info::set_bold(hunits offset)
324 if (!is_bold || offset != bold_offset) {
325 is_bold = 1;
326 bold_offset = offset;
327 flush();
331 void font_info::set_conditional_bold(int fontno, hunits offset)
333 for (conditional_bold *p = cond_bold_list; p; p = p->next)
334 if (p->fontno == fontno) {
335 if (offset != p->offset) {
336 p->offset = offset;
337 flush();
339 return;
341 cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
344 conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
345 : next(x), fontno(f), offset(h)
349 void font_info::conditional_unbold(int fontno)
351 for (conditional_bold **p = &cond_bold_list; *p; p = &(*p)->next)
352 if ((*p)->fontno == fontno) {
353 conditional_bold *tem = *p;
354 *p = (*p)->next;
355 delete tem;
356 flush();
357 return;
361 void font_info::set_constant_space(constant_space_type type, units x)
363 if (type != is_constant_spaced
364 || (type != CONSTANT_SPACE_NONE && x != constant_space)) {
365 flush();
366 is_constant_spaced = type;
367 constant_space = x;
371 void font_info::set_track_kern(track_kerning_function &tk)
373 if (track_kern != tk) {
374 track_kern = tk;
375 flush();
379 void font_info::flush()
381 last_tfont = 0;
384 int font_info::is_named(symbol s)
386 return internal_name == s;
389 symbol font_info::get_name()
391 return internal_name;
394 symbol get_font_name(int fontno, environment *env)
396 symbol f = font_table[fontno]->get_name();
397 if (font_table[fontno]->is_style()) {
398 return concat(env->get_family()->nm, f);
400 return f;
403 hunits font_info::get_space_width(font_size fs, int space_size)
405 if (is_constant_spaced == CONSTANT_SPACE_NONE)
406 return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
407 space_size, 12);
408 else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
409 return constant_space;
410 else
411 return scale(constant_space*fs.to_scaled_points(),
412 units_per_inch, 36*72*sizescale);
415 hunits font_info::get_narrow_space_width(font_size fs)
417 charinfo *ci = get_charinfo(symbol("|"));
418 if (fm->contains(ci->get_index()))
419 return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
420 else
421 return hunits(fs.to_units()/6);
424 hunits font_info::get_half_narrow_space_width(font_size fs)
426 charinfo *ci = get_charinfo(symbol("^"));
427 if (fm->contains(ci->get_index()))
428 return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
429 else
430 return hunits(fs.to_units()/12);
433 /* tfont */
435 tfont_spec::tfont_spec(symbol nm, int n, font *f,
436 font_size s, int h, int sl)
437 : name(nm), input_position(n), fm(f), size(s),
438 is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1),
439 height(h), slant(sl)
441 if (height == size.to_scaled_points())
442 height = 0;
445 int tfont_spec::operator==(const tfont_spec &spec)
447 if (fm == spec.fm
448 && size == spec.size
449 && input_position == spec.input_position
450 && name == spec.name
451 && height == spec.height
452 && slant == spec.slant
453 && (is_bold
454 ? (spec.is_bold && bold_offset == spec.bold_offset)
455 : !spec.is_bold)
456 && track_kern == spec.track_kern
457 && (is_constant_spaced
458 ? (spec.is_constant_spaced
459 && constant_space_width == spec.constant_space_width)
460 : !spec.is_constant_spaced)
461 && ligature_mode == spec.ligature_mode
462 && kern_mode == spec.kern_mode)
463 return 1;
464 else
465 return 0;
468 tfont_spec tfont_spec::plain()
470 return tfont_spec(name, input_position, fm, size, height, slant);
473 hunits tfont::get_width(charinfo *c)
475 if (is_constant_spaced)
476 return constant_space_width;
477 else if (is_bold)
478 return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
479 + track_kern + bold_offset);
480 else
481 return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
482 + track_kern);
485 vunits tfont::get_char_height(charinfo *c)
487 vunits v = fm->get_height(c->get_index(), size.to_scaled_points());
488 if (height != 0 && height != size.to_scaled_points())
489 return scale(v, height, size.to_scaled_points());
490 else
491 return v;
494 vunits tfont::get_char_depth(charinfo *c)
496 vunits v = fm->get_depth(c->get_index(), size.to_scaled_points());
497 if (height != 0 && height != size.to_scaled_points())
498 return scale(v, height, size.to_scaled_points());
499 else
500 return v;
503 hunits tfont::get_char_skew(charinfo *c)
505 return hunits(fm->get_skew(c->get_index(), size.to_scaled_points(), slant));
508 hunits tfont::get_italic_correction(charinfo *c)
510 return hunits(fm->get_italic_correction(c->get_index(), size.to_scaled_points()));
513 hunits tfont::get_left_italic_correction(charinfo *c)
515 return hunits(fm->get_left_italic_correction(c->get_index(),
516 size.to_scaled_points()));
519 hunits tfont::get_subscript_correction(charinfo *c)
521 return hunits(fm->get_subscript_correction(c->get_index(),
522 size.to_scaled_points()));
525 inline int tfont::get_input_position()
527 return input_position;
530 inline int tfont::contains(charinfo *ci)
532 return fm->contains(ci->get_index());
535 inline int tfont::get_character_type(charinfo *ci)
537 return fm->get_character_type(ci->get_index());
540 inline int tfont::get_bold(hunits *res)
542 if (is_bold) {
543 *res = bold_offset;
544 return 1;
546 else
547 return 0;
550 inline int tfont::get_constant_space(hunits *res)
552 if (is_constant_spaced) {
553 *res = constant_space_width;
554 return 1;
556 else
557 return 0;
560 inline hunits tfont::get_track_kern()
562 return track_kern;
565 inline tfont *tfont::get_plain()
567 return plain_version;
570 inline font_size tfont::get_size()
572 return size;
575 inline symbol tfont::get_name()
577 return name;
580 inline int tfont::get_height()
582 return height;
585 inline int tfont::get_slant()
587 return slant;
590 symbol SYMBOL_ff("ff");
591 symbol SYMBOL_fi("fi");
592 symbol SYMBOL_fl("fl");
593 symbol SYMBOL_Fi("Fi");
594 symbol SYMBOL_Fl("Fl");
596 charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
598 if (ligature_mode == 0)
599 return 0;
600 charinfo *ci = 0;
601 if (c1->get_ascii_code() == 'f') {
602 switch (c2->get_ascii_code()) {
603 case 'f':
604 if (fm->has_ligature(font::LIG_ff))
605 ci = get_charinfo(SYMBOL_ff);
606 break;
607 case 'i':
608 if (fm->has_ligature(font::LIG_fi))
609 ci = get_charinfo(SYMBOL_fi);
610 break;
611 case 'l':
612 if (fm->has_ligature(font::LIG_fl))
613 ci = get_charinfo(SYMBOL_fl);
614 break;
617 else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
618 switch (c2->get_ascii_code()) {
619 case 'i':
620 if (fm->has_ligature(font::LIG_ffi))
621 ci = get_charinfo(SYMBOL_Fi);
622 break;
623 case 'l':
624 if (fm->has_ligature(font::LIG_ffl))
625 ci = get_charinfo(SYMBOL_Fl);
626 break;
629 if (ci != 0 && fm->contains(ci->get_index()))
630 return ci;
631 return 0;
634 inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res)
636 if (kern_mode == 0)
637 return 0;
638 else {
639 int n = fm->get_kern(c1->get_index(),
640 c2->get_index(),
641 size.to_scaled_points());
642 if (n) {
643 *res = hunits(n);
644 return 1;
646 else
647 return 0;
651 tfont *tfont::tfont_list = 0;
653 tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
655 next = tfont_list;
656 tfont_list = this;
657 tfont_spec plain_spec = plain();
658 tfont *p;
659 for (p = tfont_list; p; p = p->next)
660 if (*p == plain_spec) {
661 plain_version = p;
662 break;
664 if (!p)
665 plain_version = new tfont(plain_spec);
668 /* output_file */
670 class real_output_file : public output_file {
671 #ifndef POPEN_MISSING
672 int piped;
673 #endif
674 int printing; // decision via optional page list
675 int output_on; // \O[0] or \O[1] escape calls
676 virtual void really_transparent_char(unsigned char) = 0;
677 virtual void really_print_line(hunits x, vunits y, node *n,
678 vunits before, vunits after, hunits width) = 0;
679 virtual void really_begin_page(int pageno, vunits page_length) = 0;
680 virtual void really_copy_file(hunits x, vunits y, const char *filename);
681 virtual void really_put_filename(const char *filename);
682 virtual void really_on();
683 virtual void really_off();
684 protected:
685 FILE *fp;
686 public:
687 real_output_file();
688 ~real_output_file();
689 void flush();
690 void transparent_char(unsigned char);
691 void print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
692 void begin_page(int pageno, vunits page_length);
693 void put_filename(const char *filename);
694 void on();
695 void off();
696 int is_on();
697 int is_printing();
698 void copy_file(hunits x, vunits y, const char *filename);
701 class suppress_output_file : public real_output_file {
702 public:
703 suppress_output_file();
704 void really_transparent_char(unsigned char);
705 void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
706 void really_begin_page(int pageno, vunits page_length);
709 class ascii_output_file : public real_output_file {
710 public:
711 ascii_output_file();
712 void really_transparent_char(unsigned char);
713 void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
714 void really_begin_page(int pageno, vunits page_length);
715 void outc(unsigned char c);
716 void outs(const char *s);
719 void ascii_output_file::outc(unsigned char c)
721 fputc(c, fp);
724 void ascii_output_file::outs(const char *s)
726 fputc('<', fp);
727 if (s)
728 fputs(s, fp);
729 fputc('>', fp);
732 struct hvpair;
734 class troff_output_file : public real_output_file {
735 units hpos;
736 units vpos;
737 units output_vpos;
738 units output_hpos;
739 int force_motion;
740 int current_size;
741 int current_slant;
742 int current_height;
743 tfont *current_tfont;
744 color *current_fill_color;
745 color *current_glyph_color;
746 int current_font_number;
747 symbol *font_position;
748 int nfont_positions;
749 enum { TBUF_SIZE = 256 };
750 char tbuf[TBUF_SIZE];
751 int tbuf_len;
752 int tbuf_kern;
753 int begun_page;
754 void do_motion();
755 void put(char c);
756 void put(unsigned char c);
757 void put(int i);
758 void put(unsigned int i);
759 void put(const char *s);
760 void set_font(tfont *tf);
761 void flush_tbuf();
762 public:
763 troff_output_file();
764 ~troff_output_file();
765 void trailer(vunits page_length);
766 void put_char(charinfo *, tfont *, color *, color *);
767 void put_char_width(charinfo *, tfont *, color *, color *, hunits, hunits);
768 void right(hunits);
769 void down(vunits);
770 void moveto(hunits, vunits);
771 void start_special(tfont *, color *, color *, int = 0);
772 void start_special();
773 void special_char(unsigned char c);
774 void end_special();
775 void word_marker();
776 void really_transparent_char(unsigned char c);
777 void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
778 void really_begin_page(int pageno, vunits page_length);
779 void really_copy_file(hunits x, vunits y, const char *filename);
780 void really_put_filename(const char *filename);
781 void really_on();
782 void really_off();
783 void draw(char, hvpair *, int, font_size, color *, color *);
784 void determine_line_limits (char code, hvpair *point, int npoints);
785 void check_charinfo(tfont *tf, charinfo *ci);
786 void glyph_color(color *c);
787 void fill_color(color *c);
788 int get_hpos() { return hpos; }
789 int get_vpos() { return vpos; }
792 static void put_string(const char *s, FILE *fp)
794 for (; *s != '\0'; ++s)
795 putc(*s, fp);
798 inline void troff_output_file::put(char c)
800 putc(c, fp);
803 inline void troff_output_file::put(unsigned char c)
805 putc(c, fp);
808 inline void troff_output_file::put(const char *s)
810 put_string(s, fp);
813 inline void troff_output_file::put(int i)
815 put_string(i_to_a(i), fp);
818 inline void troff_output_file::put(unsigned int i)
820 put_string(ui_to_a(i), fp);
823 void troff_output_file::start_special(tfont *tf, color *gcol, color *fcol,
824 int no_init_string)
826 set_font(tf);
827 glyph_color(gcol);
828 fill_color(fcol);
829 flush_tbuf();
830 do_motion();
831 if (!no_init_string)
832 put("x X ");
835 void troff_output_file::start_special()
837 flush_tbuf();
838 do_motion();
839 put("x X ");
842 void troff_output_file::special_char(unsigned char c)
844 put(c);
845 if (c == '\n')
846 put('+');
849 void troff_output_file::end_special()
851 put('\n');
854 inline void troff_output_file::moveto(hunits h, vunits v)
856 hpos = h.to_units();
857 vpos = v.to_units();
860 void troff_output_file::really_print_line(hunits x, vunits y, node *n,
861 vunits before, vunits after, hunits)
863 moveto(x, y);
864 while (n != 0) {
865 n->tprint(this);
866 n = n->next;
868 flush_tbuf();
869 // This ensures that transparent throughput will have a more predictable
870 // position.
871 do_motion();
872 force_motion = 1;
873 hpos = 0;
874 put('n');
875 put(before.to_units());
876 put(' ');
877 put(after.to_units());
878 put('\n');
881 inline void troff_output_file::word_marker()
883 flush_tbuf();
884 if (is_on())
885 put('w');
888 inline void troff_output_file::right(hunits n)
890 hpos += n.to_units();
893 inline void troff_output_file::down(vunits n)
895 vpos += n.to_units();
898 void troff_output_file::do_motion()
900 if (force_motion) {
901 put('V');
902 put(vpos);
903 put('\n');
904 put('H');
905 put(hpos);
906 put('\n');
908 else {
909 if (hpos != output_hpos) {
910 units n = hpos - output_hpos;
911 if (n > 0 && n < hpos) {
912 put('h');
913 put(n);
915 else {
916 put('H');
917 put(hpos);
919 put('\n');
921 if (vpos != output_vpos) {
922 units n = vpos - output_vpos;
923 if (n > 0 && n < vpos) {
924 put('v');
925 put(n);
927 else {
928 put('V');
929 put(vpos);
931 put('\n');
934 output_vpos = vpos;
935 output_hpos = hpos;
936 force_motion = 0;
939 void troff_output_file::flush_tbuf()
941 if (!is_on()) {
942 tbuf_len = 0;
943 return;
946 if (tbuf_len == 0)
947 return;
948 if (tbuf_kern == 0)
949 put('t');
950 else {
951 put('u');
952 put(tbuf_kern);
953 put(' ');
955 check_output_limits(hpos, vpos);
956 check_output_limits(hpos, vpos - current_size);
958 for (int i = 0; i < tbuf_len; i++)
959 put(tbuf[i]);
960 put('\n');
961 tbuf_len = 0;
964 void troff_output_file::check_charinfo(tfont *tf, charinfo *ci)
966 if (!is_on())
967 return;
969 int height = tf->get_char_height(ci).to_units();
970 int width = tf->get_width(ci).to_units()
971 + tf->get_italic_correction(ci).to_units();
972 int depth = tf->get_char_depth(ci).to_units();
973 check_output_limits(output_hpos, output_vpos - height);
974 check_output_limits(output_hpos + width, output_vpos + depth);
977 void troff_output_file::put_char_width(charinfo *ci, tfont *tf,
978 color *gcol, color *fcol,
979 hunits w, hunits k)
981 int kk = k.to_units();
982 if (!is_on()) {
983 flush_tbuf();
984 hpos += w.to_units() + kk;
985 return;
987 set_font(tf);
988 char c = ci->get_ascii_code();
989 if (c == '\0') {
990 glyph_color(gcol);
991 fill_color(fcol);
992 flush_tbuf();
993 do_motion();
994 check_charinfo(tf, ci);
995 if (ci->numbered()) {
996 put('N');
997 put(ci->get_number());
999 else {
1000 put('C');
1001 const char *s = ci->nm.contents();
1002 if (s[1] == 0) {
1003 put('\\');
1004 put(s[0]);
1006 else
1007 put(s);
1009 put('\n');
1010 hpos += w.to_units() + kk;
1012 else if (tcommand_flag) {
1013 if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
1014 && (!gcol || gcol == current_glyph_color)
1015 && (!fcol || fcol == current_fill_color)
1016 && kk == tbuf_kern
1017 && tbuf_len < TBUF_SIZE) {
1018 check_charinfo(tf, ci);
1019 tbuf[tbuf_len++] = c;
1020 output_hpos += w.to_units() + kk;
1021 hpos = output_hpos;
1022 return;
1024 glyph_color(gcol);
1025 fill_color(fcol);
1026 flush_tbuf();
1027 do_motion();
1028 check_charinfo(tf, ci);
1029 tbuf[tbuf_len++] = c;
1030 output_hpos += w.to_units() + kk;
1031 tbuf_kern = kk;
1032 hpos = output_hpos;
1034 else {
1035 // flush_tbuf();
1036 int n = hpos - output_hpos;
1037 check_charinfo(tf, ci);
1038 // check_output_limits(output_hpos, output_vpos);
1039 if (vpos == output_vpos
1040 && (!gcol || gcol == current_glyph_color)
1041 && (!fcol || fcol == current_fill_color)
1042 && n > 0 && n < 100 && !force_motion) {
1043 put(char(n/10 + '0'));
1044 put(char(n%10 + '0'));
1045 put(c);
1046 output_hpos = hpos;
1048 else {
1049 glyph_color(gcol);
1050 fill_color(fcol);
1051 do_motion();
1052 put('c');
1053 put(c);
1055 hpos += w.to_units() + kk;
1059 void troff_output_file::put_char(charinfo *ci, tfont *tf,
1060 color *gcol, color *fcol)
1062 flush_tbuf();
1063 if (!is_on())
1064 return;
1065 set_font(tf);
1066 char c = ci->get_ascii_code();
1067 if (c == '\0') {
1068 glyph_color(gcol);
1069 fill_color(fcol);
1070 flush_tbuf();
1071 do_motion();
1072 if (ci->numbered()) {
1073 put('N');
1074 put(ci->get_number());
1076 else {
1077 put('C');
1078 const char *s = ci->nm.contents();
1079 if (s[1] == 0) {
1080 put('\\');
1081 put(s[0]);
1083 else
1084 put(s);
1086 put('\n');
1088 else {
1089 int n = hpos - output_hpos;
1090 if (vpos == output_vpos
1091 && (!gcol || gcol == current_glyph_color)
1092 && (!fcol || fcol == current_fill_color)
1093 && n > 0 && n < 100) {
1094 put(char(n/10 + '0'));
1095 put(char(n%10 + '0'));
1096 put(c);
1097 output_hpos = hpos;
1099 else {
1100 glyph_color(gcol);
1101 fill_color(fcol);
1102 flush_tbuf();
1103 do_motion();
1104 put('c');
1105 put(c);
1110 // set_font calls `flush_tbuf' if necessary.
1112 void troff_output_file::set_font(tfont *tf)
1114 if (current_tfont == tf)
1115 return;
1116 flush_tbuf();
1117 int n = tf->get_input_position();
1118 symbol nm = tf->get_name();
1119 if (n >= nfont_positions || font_position[n] != nm) {
1120 put("x font ");
1121 put(n);
1122 put(' ');
1123 put(nm.contents());
1124 put('\n');
1125 if (n >= nfont_positions) {
1126 int old_nfont_positions = nfont_positions;
1127 symbol *old_font_position = font_position;
1128 nfont_positions *= 3;
1129 nfont_positions /= 2;
1130 if (nfont_positions <= n)
1131 nfont_positions = n + 10;
1132 font_position = new symbol[nfont_positions];
1133 memcpy(font_position, old_font_position,
1134 old_nfont_positions*sizeof(symbol));
1135 a_delete old_font_position;
1137 font_position[n] = nm;
1139 if (current_font_number != n) {
1140 put('f');
1141 put(n);
1142 put('\n');
1143 current_font_number = n;
1145 int size = tf->get_size().to_scaled_points();
1146 if (current_size != size) {
1147 put('s');
1148 put(size);
1149 put('\n');
1150 current_size = size;
1152 int slant = tf->get_slant();
1153 if (current_slant != slant) {
1154 put("x Slant ");
1155 put(slant);
1156 put('\n');
1157 current_slant = slant;
1159 int height = tf->get_height();
1160 if (current_height != height) {
1161 put("x Height ");
1162 put(height == 0 ? current_size : height);
1163 put('\n');
1164 current_height = height;
1166 current_tfont = tf;
1169 // fill_color calls `flush_tbuf' and `do_motion' if necessary.
1171 void troff_output_file::fill_color(color *col)
1173 if (!col || current_fill_color == col)
1174 return;
1175 current_fill_color = col;
1176 if (!color_flag)
1177 return;
1178 flush_tbuf();
1179 do_motion();
1180 put("DF");
1181 unsigned int components[4];
1182 color_scheme cs;
1183 cs = col->get_components(components);
1184 switch (cs) {
1185 case DEFAULT:
1186 put('d');
1187 break;
1188 case RGB:
1189 put("r ");
1190 put(Red);
1191 put(' ');
1192 put(Green);
1193 put(' ');
1194 put(Blue);
1195 break;
1196 case CMY:
1197 put("c ");
1198 put(Cyan);
1199 put(' ');
1200 put(Magenta);
1201 put(' ');
1202 put(Yellow);
1203 break;
1204 case CMYK:
1205 put("k ");
1206 put(Cyan);
1207 put(' ');
1208 put(Magenta);
1209 put(' ');
1210 put(Yellow);
1211 put(' ');
1212 put(Black);
1213 break;
1214 case GRAY:
1215 put("g ");
1216 put(Gray);
1217 break;
1219 put('\n');
1222 // glyph_color calls `flush_tbuf' and `do_motion' if necessary.
1224 void troff_output_file::glyph_color(color *col)
1226 if (!col || current_glyph_color == col)
1227 return;
1228 current_glyph_color = col;
1229 if (!color_flag)
1230 return;
1231 flush_tbuf();
1232 // grotty doesn't like a color command if the vertical position is zero.
1233 do_motion();
1234 put("m");
1235 unsigned int components[4];
1236 color_scheme cs;
1237 cs = col->get_components(components);
1238 switch (cs) {
1239 case DEFAULT:
1240 put('d');
1241 break;
1242 case RGB:
1243 put("r ");
1244 put(Red);
1245 put(' ');
1246 put(Green);
1247 put(' ');
1248 put(Blue);
1249 break;
1250 case CMY:
1251 put("c ");
1252 put(Cyan);
1253 put(' ');
1254 put(Magenta);
1255 put(' ');
1256 put(Yellow);
1257 break;
1258 case CMYK:
1259 put("k ");
1260 put(Cyan);
1261 put(' ');
1262 put(Magenta);
1263 put(' ');
1264 put(Yellow);
1265 put(' ');
1266 put(Black);
1267 break;
1268 case GRAY:
1269 put("g ");
1270 put(Gray);
1271 break;
1273 put('\n');
1276 // determine_line_limits - works out the smallest box which will contain
1277 // the entity, code, built from the point array.
1278 void troff_output_file::determine_line_limits(char code, hvpair *point,
1279 int npoints)
1281 int i, x, y;
1283 if (!is_on())
1284 return;
1286 switch (code) {
1287 case 'c':
1288 case 'C':
1289 // only the h field is used when defining a circle
1290 check_output_limits(output_hpos,
1291 output_vpos - point[0].h.to_units()/2);
1292 check_output_limits(output_hpos + point[0].h.to_units(),
1293 output_vpos + point[0].h.to_units()/2);
1294 break;
1295 case 'E':
1296 case 'e':
1297 check_output_limits(output_hpos,
1298 output_vpos - point[0].v.to_units()/2);
1299 check_output_limits(output_hpos + point[0].h.to_units(),
1300 output_vpos + point[0].v.to_units()/2);
1301 break;
1302 case 'P':
1303 case 'p':
1304 x = output_hpos;
1305 y = output_vpos;
1306 check_output_limits(x, y);
1307 for (i = 0; i < npoints; i++) {
1308 x += point[i].h.to_units();
1309 y += point[i].v.to_units();
1310 check_output_limits(x, y);
1312 break;
1313 case 't':
1314 x = output_hpos;
1315 y = output_vpos;
1316 for (i = 0; i < npoints; i++) {
1317 x += point[i].h.to_units();
1318 y += point[i].v.to_units();
1319 check_output_limits(x, y);
1321 break;
1322 case 'a':
1323 double c[2];
1324 int p[4];
1325 int minx, miny, maxx, maxy;
1326 x = output_hpos;
1327 y = output_vpos;
1328 p[0] = point[0].h.to_units();
1329 p[1] = point[0].v.to_units();
1330 p[2] = point[1].h.to_units();
1331 p[3] = point[1].v.to_units();
1332 if (adjust_arc_center(p, c)) {
1333 check_output_arc_limits(x, y,
1334 p[0], p[1], p[2], p[3],
1335 c[0], c[1],
1336 &minx, &maxx, &miny, &maxy);
1337 check_output_limits(minx, miny);
1338 check_output_limits(maxx, maxy);
1339 break;
1341 // fall through
1342 case 'l':
1343 x = output_hpos;
1344 y = output_vpos;
1345 check_output_limits(x, y);
1346 for (i = 0; i < npoints; i++) {
1347 x += point[i].h.to_units();
1348 y += point[i].v.to_units();
1349 check_output_limits(x, y);
1351 break;
1352 default:
1353 x = output_hpos;
1354 y = output_vpos;
1355 for (i = 0; i < npoints; i++) {
1356 x += point[i].h.to_units();
1357 y += point[i].v.to_units();
1358 check_output_limits(x, y);
1363 void troff_output_file::draw(char code, hvpair *point, int npoints,
1364 font_size fsize, color *gcol, color *fcol)
1366 int i;
1367 glyph_color(gcol);
1368 fill_color(fcol);
1369 flush_tbuf();
1370 do_motion();
1371 if (is_on()) {
1372 int size = fsize.to_scaled_points();
1373 if (current_size != size) {
1374 put('s');
1375 put(size);
1376 put('\n');
1377 current_size = size;
1378 current_tfont = 0;
1380 put('D');
1381 put(code);
1382 if (code == 'c') {
1383 put(' ');
1384 put(point[0].h.to_units());
1386 else
1387 for (i = 0; i < npoints; i++) {
1388 put(' ');
1389 put(point[i].h.to_units());
1390 put(' ');
1391 put(point[i].v.to_units());
1393 determine_line_limits(code, point, npoints);
1396 for (i = 0; i < npoints; i++)
1397 output_hpos += point[i].h.to_units();
1398 hpos = output_hpos;
1399 if (code != 'e') {
1400 for (i = 0; i < npoints; i++)
1401 output_vpos += point[i].v.to_units();
1402 vpos = output_vpos;
1404 if (is_on())
1405 put('\n');
1408 void troff_output_file::really_on()
1410 flush_tbuf();
1411 force_motion = 1;
1412 do_motion();
1415 void troff_output_file::really_off()
1417 flush_tbuf();
1420 void troff_output_file::really_put_filename(const char *filename)
1422 flush_tbuf();
1423 put("F ");
1424 put(filename);
1425 put('\n');
1428 void troff_output_file::really_begin_page(int pageno, vunits page_length)
1430 flush_tbuf();
1431 if (begun_page) {
1432 if (page_length > V0) {
1433 put('V');
1434 put(page_length.to_units());
1435 put('\n');
1438 else
1439 begun_page = 1;
1440 current_tfont = 0;
1441 current_font_number = -1;
1442 current_size = 0;
1443 // current_height = 0;
1444 // current_slant = 0;
1445 hpos = 0;
1446 vpos = 0;
1447 output_hpos = 0;
1448 output_vpos = 0;
1449 force_motion = 1;
1450 for (int i = 0; i < nfont_positions; i++)
1451 font_position[i] = NULL_SYMBOL;
1452 put('p');
1453 put(pageno);
1454 put('\n');
1457 void troff_output_file::really_copy_file(hunits x, vunits y, const char *filename)
1459 moveto(x, y);
1460 flush_tbuf();
1461 do_motion();
1462 errno = 0;
1463 FILE *ifp = fopen(filename, "r");
1464 if (ifp == 0)
1465 error("can't open `%1': %2", filename, strerror(errno));
1466 else {
1467 int c;
1468 while ((c = getc(ifp)) != EOF)
1469 put(char(c));
1470 fclose(ifp);
1472 force_motion = 1;
1473 current_size = 0;
1474 current_tfont = 0;
1475 current_font_number = -1;
1476 for (int i = 0; i < nfont_positions; i++)
1477 font_position[i] = NULL_SYMBOL;
1480 void troff_output_file::really_transparent_char(unsigned char c)
1482 put(c);
1485 troff_output_file::~troff_output_file()
1487 a_delete font_position;
1490 void troff_output_file::trailer(vunits page_length)
1492 flush_tbuf();
1493 if (page_length > V0) {
1494 put("x trailer\n");
1495 put('V');
1496 put(page_length.to_units());
1497 put('\n');
1499 put("x stop\n");
1502 troff_output_file::troff_output_file()
1503 : current_slant(0), current_height(0), current_fill_color(0),
1504 current_glyph_color(0), nfont_positions(10), tbuf_len(0), begun_page(0)
1506 font_position = new symbol[nfont_positions];
1507 put("x T ");
1508 put(device);
1509 put('\n');
1510 put("x res ");
1511 put(units_per_inch);
1512 put(' ');
1513 put(hresolution);
1514 put(' ');
1515 put(vresolution);
1516 put('\n');
1517 put("x init\n");
1520 /* output_file */
1522 output_file *the_output = 0;
1524 output_file::output_file()
1528 output_file::~output_file()
1532 void output_file::trailer(vunits)
1536 void output_file::put_filename(const char *)
1540 void output_file::on()
1544 void output_file::off()
1548 real_output_file::real_output_file()
1549 : printing(0), output_on(1)
1551 #ifndef POPEN_MISSING
1552 if (pipe_command) {
1553 if ((fp = popen(pipe_command, POPEN_WT)) != 0) {
1554 piped = 1;
1555 return;
1557 error("pipe open failed: %1", strerror(errno));
1559 piped = 0;
1560 #endif /* not POPEN_MISSING */
1561 fp = stdout;
1564 real_output_file::~real_output_file()
1566 if (!fp)
1567 return;
1568 // To avoid looping, set fp to 0 before calling fatal().
1569 if (ferror(fp) || fflush(fp) < 0) {
1570 fp = 0;
1571 fatal("error writing output file");
1573 #ifndef POPEN_MISSING
1574 if (piped) {
1575 int result = pclose(fp);
1576 fp = 0;
1577 if (result < 0)
1578 fatal("pclose failed");
1579 if (!WIFEXITED(result))
1580 error("output process `%1' got fatal signal %2",
1581 pipe_command,
1582 WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
1583 else {
1584 int exit_status = WEXITSTATUS(result);
1585 if (exit_status != 0)
1586 error("output process `%1' exited with status %2",
1587 pipe_command, exit_status);
1590 else
1591 #endif /* not POPEN MISSING */
1592 if (fclose(fp) < 0) {
1593 fp = 0;
1594 fatal("error closing output file");
1598 void real_output_file::flush()
1600 if (fflush(fp) < 0)
1601 fatal("error writing output file");
1604 int real_output_file::is_printing()
1606 return printing;
1609 void real_output_file::begin_page(int pageno, vunits page_length)
1611 printing = in_output_page_list(pageno);
1612 if (printing)
1613 really_begin_page(pageno, page_length);
1616 void real_output_file::copy_file(hunits x, vunits y, const char *filename)
1618 if (printing && output_on)
1619 really_copy_file(x, y, filename);
1620 check_output_limits(x.to_units(), y.to_units());
1623 void real_output_file::transparent_char(unsigned char c)
1625 if (printing && output_on)
1626 really_transparent_char(c);
1629 void real_output_file::print_line(hunits x, vunits y, node *n,
1630 vunits before, vunits after, hunits width)
1632 if (printing)
1633 really_print_line(x, y, n, before, after, width);
1634 delete_node_list(n);
1637 void real_output_file::really_copy_file(hunits, vunits, const char *)
1639 // do nothing
1642 void real_output_file::put_filename(const char *filename)
1644 really_put_filename(filename);
1647 void real_output_file::really_put_filename(const char *)
1651 void real_output_file::on()
1653 really_on();
1654 if (output_on == 0)
1655 output_on = 1;
1658 void real_output_file::off()
1660 really_off();
1661 output_on = 0;
1664 int real_output_file::is_on()
1666 return output_on;
1669 void real_output_file::really_on()
1673 void real_output_file::really_off()
1677 /* ascii_output_file */
1679 void ascii_output_file::really_transparent_char(unsigned char c)
1681 putc(c, fp);
1684 void ascii_output_file::really_print_line(hunits, vunits, node *n,
1685 vunits, vunits, hunits)
1687 while (n != 0) {
1688 n->ascii_print(this);
1689 n = n->next;
1691 fputc('\n', fp);
1694 void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/)
1696 fputs("<beginning of page>\n", fp);
1699 ascii_output_file::ascii_output_file()
1703 /* suppress_output_file */
1705 suppress_output_file::suppress_output_file()
1709 void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits, hunits)
1713 void suppress_output_file::really_begin_page(int, vunits)
1717 void suppress_output_file::really_transparent_char(unsigned char)
1721 /* glyphs, ligatures, kerns, discretionary breaks */
1723 class charinfo_node : public node {
1724 protected:
1725 charinfo *ci;
1726 public:
1727 charinfo_node(charinfo *, node * = 0);
1728 int ends_sentence();
1729 int overlaps_vertically();
1730 int overlaps_horizontally();
1733 charinfo_node::charinfo_node(charinfo *c, node *x)
1734 : node(x), ci(c)
1738 int charinfo_node::ends_sentence()
1740 if (ci->ends_sentence())
1741 return 1;
1742 else if (ci->transparent())
1743 return 2;
1744 else
1745 return 0;
1748 int charinfo_node::overlaps_horizontally()
1750 return ci->overlaps_horizontally();
1753 int charinfo_node::overlaps_vertically()
1755 return ci->overlaps_vertically();
1758 class glyph_node : public charinfo_node {
1759 static glyph_node *free_list;
1760 protected:
1761 tfont *tf;
1762 color *gcol;
1763 color *fcol; /* this is needed for grotty */
1764 #ifdef STORE_WIDTH
1765 hunits wid;
1766 glyph_node(charinfo *, tfont *, color *, color *, hunits, node * = 0);
1767 #endif
1768 public:
1769 void *operator new(size_t);
1770 void operator delete(void *);
1771 glyph_node(charinfo *, tfont *, color *, color *, node * = 0);
1772 ~glyph_node() {}
1773 node *copy();
1774 node *merge_glyph_node(glyph_node *);
1775 node *merge_self(node *);
1776 hunits width();
1777 node *last_char_node();
1778 units size();
1779 void vertical_extent(vunits *, vunits *);
1780 hunits subscript_correction();
1781 hunits italic_correction();
1782 hunits left_italic_correction();
1783 hunits skew();
1784 hyphenation_type get_hyphenation_type();
1785 tfont *get_tfont();
1786 color *get_glyph_color();
1787 color *get_fill_color();
1788 void tprint(troff_output_file *);
1789 void zero_width_tprint(troff_output_file *);
1790 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1791 node *add_self(node *, hyphen_list **);
1792 void ascii_print(ascii_output_file *);
1793 void asciify(macro *);
1794 int character_type();
1795 int same(node *);
1796 const char *type();
1797 int force_tprint();
1800 glyph_node *glyph_node::free_list = 0;
1802 class ligature_node : public glyph_node {
1803 node *n1;
1804 node *n2;
1805 #ifdef STORE_WIDTH
1806 ligature_node(charinfo *, tfont *, color *, color *, hunits,
1807 node *, node *, node * = 0);
1808 #endif
1809 public:
1810 void *operator new(size_t);
1811 void operator delete(void *);
1812 ligature_node(charinfo *, tfont *, color *, color *,
1813 node *, node *, node * = 0);
1814 ~ligature_node();
1815 node *copy();
1816 node *add_self(node *, hyphen_list **);
1817 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1818 void ascii_print(ascii_output_file *);
1819 void asciify(macro *);
1820 int same(node *);
1821 const char *type();
1822 int force_tprint();
1825 class kern_pair_node : public node {
1826 hunits amount;
1827 node *n1;
1828 node *n2;
1829 public:
1830 kern_pair_node(hunits n, node *first, node *second, node *x = 0);
1831 ~kern_pair_node();
1832 node *copy();
1833 node *merge_glyph_node(glyph_node *);
1834 node *add_self(node *, hyphen_list **);
1835 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1836 node *add_discretionary_hyphen();
1837 hunits width();
1838 node *last_char_node();
1839 hunits italic_correction();
1840 hunits subscript_correction();
1841 void tprint(troff_output_file *);
1842 hyphenation_type get_hyphenation_type();
1843 int ends_sentence();
1844 void ascii_print(ascii_output_file *);
1845 void asciify(macro *);
1846 int same(node *);
1847 const char *type();
1848 int force_tprint();
1849 void vertical_extent(vunits *, vunits *);
1852 class dbreak_node : public node {
1853 node *none;
1854 node *pre;
1855 node *post;
1856 public:
1857 dbreak_node(node *n, node *p, node *x = 0);
1858 ~dbreak_node();
1859 node *copy();
1860 node *merge_glyph_node(glyph_node *);
1861 node *add_discretionary_hyphen();
1862 hunits width();
1863 node *last_char_node();
1864 hunits italic_correction();
1865 hunits subscript_correction();
1866 void tprint(troff_output_file *);
1867 breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
1868 int is_inner = 0);
1869 int nbreaks();
1870 int ends_sentence();
1871 void split(int, node **, node **);
1872 hyphenation_type get_hyphenation_type();
1873 void ascii_print(ascii_output_file *);
1874 void asciify(macro *);
1875 int same(node *);
1876 const char *type();
1877 int force_tprint();
1880 void *glyph_node::operator new(size_t n)
1882 assert(n == sizeof(glyph_node));
1883 if (!free_list) {
1884 const int BLOCK = 1024;
1885 free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
1886 for (int i = 0; i < BLOCK - 1; i++)
1887 free_list[i].next = free_list + i + 1;
1888 free_list[BLOCK-1].next = 0;
1890 glyph_node *p = free_list;
1891 free_list = (glyph_node *)(free_list->next);
1892 p->next = 0;
1893 return p;
1896 void *ligature_node::operator new(size_t n)
1898 return new char[n];
1901 void glyph_node::operator delete(void *p)
1903 if (p) {
1904 ((glyph_node *)p)->next = free_list;
1905 free_list = (glyph_node *)p;
1909 void ligature_node::operator delete(void *p)
1911 delete[] (char *)p;
1914 glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc, node *x)
1915 : charinfo_node(c, x), tf(t), gcol(gc), fcol(fc)
1917 #ifdef STORE_WIDTH
1918 wid = tf->get_width(ci);
1919 #endif
1922 #ifdef STORE_WIDTH
1923 glyph_node::glyph_node(charinfo *c, tfont *t,
1924 color *gc, color *fc, hunits w, node *x)
1925 : charinfo_node(c, x), tf(t), gcol(gc), fcol(fc), wid(w)
1928 #endif
1930 node *glyph_node::copy()
1932 #ifdef STORE_WIDTH
1933 return new glyph_node(ci, tf, gcol, fcol, wid);
1934 #else
1935 return new glyph_node(ci, tf, gcol, fcol);
1936 #endif
1939 node *glyph_node::merge_self(node *nd)
1941 return nd->merge_glyph_node(this);
1944 int glyph_node::character_type()
1946 return tf->get_character_type(ci);
1949 node *glyph_node::add_self(node *n, hyphen_list **p)
1951 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
1952 next = 0;
1953 node *nn;
1954 if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
1955 next = n;
1956 nn = this;
1958 if ((*p)->hyphen)
1959 nn = nn->add_discretionary_hyphen();
1960 hyphen_list *pp = *p;
1961 *p = (*p)->next;
1962 delete pp;
1963 return nn;
1966 units glyph_node::size()
1968 return tf->get_size().to_units();
1971 hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count)
1973 (*count)++;
1974 return new hyphen_list(ci->get_hyphenation_code(), tail);
1977 tfont *node::get_tfont()
1979 return 0;
1982 tfont *glyph_node::get_tfont()
1984 return tf;
1987 color *node::get_glyph_color()
1989 return 0;
1992 color *glyph_node::get_glyph_color()
1994 return gcol;
1997 color *node::get_fill_color()
1999 return 0;
2002 color *glyph_node::get_fill_color()
2004 return fcol;
2007 node *node::merge_glyph_node(glyph_node *)
2009 return 0;
2012 node *glyph_node::merge_glyph_node(glyph_node *gn)
2014 if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
2015 charinfo *lig;
2016 if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
2017 node *next1 = next;
2018 next = 0;
2019 return new ligature_node(lig, tf, gcol, fcol, this, gn, next1);
2021 hunits kern;
2022 if (tf->get_kern(ci, gn->ci, &kern)) {
2023 node *next1 = next;
2024 next = 0;
2025 return new kern_pair_node(kern, this, gn, next1);
2028 return 0;
2031 #ifdef STORE_WIDTH
2032 inline
2033 #endif
2034 hunits glyph_node::width()
2036 #ifdef STORE_WIDTH
2037 return wid;
2038 #else
2039 return tf->get_width(ci);
2040 #endif
2043 node *glyph_node::last_char_node()
2045 return this;
2048 void glyph_node::vertical_extent(vunits *min, vunits *max)
2050 *min = -tf->get_char_height(ci);
2051 *max = tf->get_char_depth(ci);
2054 hunits glyph_node::skew()
2056 return tf->get_char_skew(ci);
2059 hunits glyph_node::subscript_correction()
2061 return tf->get_subscript_correction(ci);
2064 hunits glyph_node::italic_correction()
2066 return tf->get_italic_correction(ci);
2069 hunits glyph_node::left_italic_correction()
2071 return tf->get_left_italic_correction(ci);
2074 hyphenation_type glyph_node::get_hyphenation_type()
2076 return HYPHEN_MIDDLE;
2079 void glyph_node::ascii_print(ascii_output_file *ascii)
2081 unsigned char c = ci->get_ascii_code();
2082 if (c != 0)
2083 ascii->outc(c);
2084 else
2085 ascii->outs(ci->nm.contents());
2088 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2089 node *gn1, node *gn2, node *x)
2090 : glyph_node(c, t, gc, fc, x), n1(gn1), n2(gn2)
2094 #ifdef STORE_WIDTH
2095 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2096 hunits w, node *gn1, node *gn2, node *x)
2097 : glyph_node(c, t, gc, fc, w, x), n1(gn1), n2(gn2)
2100 #endif
2102 ligature_node::~ligature_node()
2104 delete n1;
2105 delete n2;
2108 node *ligature_node::copy()
2110 #ifdef STORE_WIDTH
2111 return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy());
2112 #else
2113 return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy());
2114 #endif
2117 void ligature_node::ascii_print(ascii_output_file *ascii)
2119 n1->ascii_print(ascii);
2120 n2->ascii_print(ascii);
2123 hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail, int *count)
2125 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2126 return n1->get_hyphen_list(hl, count);
2129 node *ligature_node::add_self(node *n, hyphen_list **p)
2131 n = n1->add_self(n, p);
2132 n = n2->add_self(n, p);
2133 n1 = n2 = 0;
2134 delete this;
2135 return n;
2138 kern_pair_node::kern_pair_node(hunits n, node *first, node *second, node *x)
2139 : node(x), amount(n), n1(first), n2(second)
2143 dbreak_node::dbreak_node(node *n, node *p, node *x)
2144 : node(x), none(n), pre(p), post(0)
2148 node *dbreak_node::merge_glyph_node(glyph_node *gn)
2150 glyph_node *gn2 = (glyph_node *)gn->copy();
2151 node *new_none = none ? none->merge_glyph_node(gn) : 0;
2152 node *new_post = post ? post->merge_glyph_node(gn2) : 0;
2153 if (new_none == 0 && new_post == 0) {
2154 delete gn2;
2155 return 0;
2157 if (new_none != 0)
2158 none = new_none;
2159 else {
2160 gn->next = none;
2161 none = gn;
2163 if (new_post != 0)
2164 post = new_post;
2165 else {
2166 gn2->next = post;
2167 post = gn2;
2169 return this;
2172 node *kern_pair_node::merge_glyph_node(glyph_node *gn)
2174 node *nd = n2->merge_glyph_node(gn);
2175 if (nd == 0)
2176 return 0;
2177 n2 = nd;
2178 nd = n2->merge_self(n1);
2179 if (nd) {
2180 nd->next = next;
2181 n1 = 0;
2182 n2 = 0;
2183 delete this;
2184 return nd;
2186 return this;
2189 hunits kern_pair_node::italic_correction()
2191 return n2->italic_correction();
2194 hunits kern_pair_node::subscript_correction()
2196 return n2->subscript_correction();
2199 void kern_pair_node::vertical_extent(vunits *min, vunits *max)
2201 n1->vertical_extent(min, max);
2202 vunits min2, max2;
2203 n2->vertical_extent(&min2, &max2);
2204 if (min2 < *min)
2205 *min = min2;
2206 if (max2 > *max)
2207 *max = max2;
2210 node *kern_pair_node::add_discretionary_hyphen()
2212 tfont *tf = n2->get_tfont();
2213 if (tf) {
2214 if (tf->contains(soft_hyphen_char)) {
2215 color *gcol = n2->get_glyph_color();
2216 color *fcol = n2->get_fill_color();
2217 node *next1 = next;
2218 next = 0;
2219 node *n = copy();
2220 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol);
2221 node *nn = n->merge_glyph_node(gn);
2222 if (nn == 0) {
2223 gn->next = n;
2224 nn = gn;
2226 return new dbreak_node(this, nn, next1);
2229 return this;
2232 kern_pair_node::~kern_pair_node()
2234 if (n1 != 0)
2235 delete n1;
2236 if (n2 != 0)
2237 delete n2;
2240 dbreak_node::~dbreak_node()
2242 delete_node_list(pre);
2243 delete_node_list(post);
2244 delete_node_list(none);
2247 node *kern_pair_node::copy()
2249 return new kern_pair_node(amount, n1->copy(), n2->copy());
2252 node *copy_node_list(node *n)
2254 node *p = 0;
2255 while (n != 0) {
2256 node *nn = n->copy();
2257 nn->next = p;
2258 p = nn;
2259 n = n->next;
2261 while (p != 0) {
2262 node *pp = p->next;
2263 p->next = n;
2264 n = p;
2265 p = pp;
2267 return n;
2270 void delete_node_list(node *n)
2272 while (n != 0) {
2273 node *tem = n;
2274 n = n->next;
2275 delete tem;
2279 node *dbreak_node::copy()
2281 dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre));
2282 p->post = copy_node_list(post);
2283 return p;
2286 hyphen_list *node::get_hyphen_list(hyphen_list *tail, int *)
2288 return tail;
2291 hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail, int *count)
2293 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2294 return n1->get_hyphen_list(hl, count);
2297 class hyphen_inhibitor_node : public node {
2298 public:
2299 hyphen_inhibitor_node(node *nd = 0);
2300 node *copy();
2301 int same(node *);
2302 const char *type();
2303 int force_tprint();
2304 hyphenation_type get_hyphenation_type();
2307 hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
2311 node *hyphen_inhibitor_node::copy()
2313 return new hyphen_inhibitor_node;
2316 int hyphen_inhibitor_node::same(node *)
2318 return 1;
2321 const char *hyphen_inhibitor_node::type()
2323 return "hyphen_inhibitor_node";
2326 int hyphen_inhibitor_node::force_tprint()
2328 return 0;
2331 hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
2333 return HYPHEN_INHIBIT;
2336 /* add_discretionary_hyphen methods */
2338 node *dbreak_node::add_discretionary_hyphen()
2340 if (post)
2341 post = post->add_discretionary_hyphen();
2342 if (none)
2343 none = none->add_discretionary_hyphen();
2344 return this;
2347 node *node::add_discretionary_hyphen()
2349 tfont *tf = get_tfont();
2350 if (!tf)
2351 return new hyphen_inhibitor_node(this);
2352 if (tf->contains(soft_hyphen_char)) {
2353 color *gcol = get_glyph_color();
2354 color *fcol = get_fill_color();
2355 node *next1 = next;
2356 next = 0;
2357 node *n = copy();
2358 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol);
2359 node *n1 = n->merge_glyph_node(gn);
2360 if (n1 == 0) {
2361 gn->next = n;
2362 n1 = gn;
2364 return new dbreak_node(this, n1, next1);
2366 return this;
2369 node *node::merge_self(node *)
2371 return 0;
2374 node *node::add_self(node *n, hyphen_list ** /*p*/)
2376 next = n;
2377 return this;
2380 node *kern_pair_node::add_self(node *n, hyphen_list **p)
2382 n = n1->add_self(n, p);
2383 n = n2->add_self(n, p);
2384 n1 = n2 = 0;
2385 delete this;
2386 return n;
2389 hunits node::width()
2391 return H0;
2394 node *node::last_char_node()
2396 return 0;
2399 int node::force_tprint()
2401 return 0;
2404 hunits hmotion_node::width()
2406 return n;
2409 units node::size()
2411 return points_to_units(10);
2414 hunits kern_pair_node::width()
2416 return n1->width() + n2->width() + amount;
2419 node *kern_pair_node::last_char_node()
2421 node *nd = n2->last_char_node();
2422 if (nd)
2423 return nd;
2424 return n1->last_char_node();
2427 hunits dbreak_node::width()
2429 hunits x = H0;
2430 for (node *n = none; n != 0; n = n->next)
2431 x += n->width();
2432 return x;
2435 node *dbreak_node::last_char_node()
2437 for (node *n = none; n; n = n->next) {
2438 node *last = n->last_char_node();
2439 if (last)
2440 return last;
2442 return 0;
2445 hunits dbreak_node::italic_correction()
2447 return none ? none->italic_correction() : H0;
2450 hunits dbreak_node::subscript_correction()
2452 return none ? none->subscript_correction() : H0;
2455 class italic_corrected_node : public node {
2456 node *n;
2457 hunits x;
2458 public:
2459 italic_corrected_node(node *, hunits, node * = 0);
2460 ~italic_corrected_node();
2461 node *copy();
2462 void ascii_print(ascii_output_file *);
2463 void asciify(macro *);
2464 hunits width();
2465 node *last_char_node();
2466 void vertical_extent(vunits *, vunits *);
2467 int ends_sentence();
2468 int overlaps_horizontally();
2469 int overlaps_vertically();
2470 int same(node *);
2471 hyphenation_type get_hyphenation_type();
2472 tfont *get_tfont();
2473 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2474 int character_type();
2475 void tprint(troff_output_file *);
2476 hunits subscript_correction();
2477 hunits skew();
2478 node *add_self(node *, hyphen_list **);
2479 const char *type();
2480 int force_tprint();
2483 node *node::add_italic_correction(hunits *width)
2485 hunits ic = italic_correction();
2486 if (ic.is_zero())
2487 return this;
2488 else {
2489 node *next1 = next;
2490 next = 0;
2491 *width += ic;
2492 return new italic_corrected_node(this, ic, next1);
2496 italic_corrected_node::italic_corrected_node(node *nn, hunits xx, node *p)
2497 : node(p), n(nn), x(xx)
2499 assert(n != 0);
2502 italic_corrected_node::~italic_corrected_node()
2504 delete n;
2507 node *italic_corrected_node::copy()
2509 return new italic_corrected_node(n->copy(), x);
2512 hunits italic_corrected_node::width()
2514 return n->width() + x;
2517 void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
2519 n->vertical_extent(min, max);
2522 void italic_corrected_node::tprint(troff_output_file *out)
2524 n->tprint(out);
2525 out->right(x);
2528 hunits italic_corrected_node::skew()
2530 return n->skew() - x/2;
2533 hunits italic_corrected_node::subscript_correction()
2535 return n->subscript_correction() - x;
2538 void italic_corrected_node::ascii_print(ascii_output_file *out)
2540 n->ascii_print(out);
2543 int italic_corrected_node::ends_sentence()
2545 return n->ends_sentence();
2548 int italic_corrected_node::overlaps_horizontally()
2550 return n->overlaps_horizontally();
2553 int italic_corrected_node::overlaps_vertically()
2555 return n->overlaps_vertically();
2558 node *italic_corrected_node::last_char_node()
2560 return n->last_char_node();
2563 tfont *italic_corrected_node::get_tfont()
2565 return n->get_tfont();
2568 hyphenation_type italic_corrected_node::get_hyphenation_type()
2570 return n->get_hyphenation_type();
2573 node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
2575 nd = n->add_self(nd, p);
2576 hunits not_interested;
2577 nd = nd->add_italic_correction(&not_interested);
2578 n = 0;
2579 delete this;
2580 return nd;
2583 hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail,
2584 int *count)
2586 return n->get_hyphen_list(tail, count);
2589 int italic_corrected_node::character_type()
2591 return n->character_type();
2594 class break_char_node : public node {
2595 node *ch;
2596 char break_code;
2597 color *col;
2598 public:
2599 break_char_node(node *, int, color *, node * = 0);
2600 ~break_char_node();
2601 node *copy();
2602 hunits width();
2603 vunits vertical_width();
2604 node *last_char_node();
2605 int character_type();
2606 int ends_sentence();
2607 node *add_self(node *, hyphen_list **);
2608 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2609 void tprint(troff_output_file *);
2610 void zero_width_tprint(troff_output_file *);
2611 void ascii_print(ascii_output_file *);
2612 void asciify(macro *);
2613 hyphenation_type get_hyphenation_type();
2614 int overlaps_vertically();
2615 int overlaps_horizontally();
2616 units size();
2617 tfont *get_tfont();
2618 int same(node *);
2619 const char *type();
2620 int force_tprint();
2623 break_char_node::break_char_node(node *n, int bc, color *c, node *x)
2624 : node(x), ch(n), break_code(bc), col(c)
2628 break_char_node::~break_char_node()
2630 delete ch;
2633 node *break_char_node::copy()
2635 return new break_char_node(ch->copy(), break_code, col);
2638 hunits break_char_node::width()
2640 return ch->width();
2643 vunits break_char_node::vertical_width()
2645 return ch->vertical_width();
2648 node *break_char_node::last_char_node()
2650 return ch->last_char_node();
2653 int break_char_node::character_type()
2655 return ch->character_type();
2658 int break_char_node::ends_sentence()
2660 return ch->ends_sentence();
2663 node *break_char_node::add_self(node *n, hyphen_list **p)
2665 assert((*p)->hyphenation_code == 0);
2666 if ((*p)->breakable && (break_code & 1)) {
2667 n = new space_node(H0, col, n);
2668 n->freeze_space();
2670 next = n;
2671 n = this;
2672 if ((*p)->breakable && (break_code & 2)) {
2673 n = new space_node(H0, col, n);
2674 n->freeze_space();
2676 hyphen_list *pp = *p;
2677 *p = (*p)->next;
2678 delete pp;
2679 return n;
2682 hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail, int *)
2684 return new hyphen_list(0, tail);
2687 hyphenation_type break_char_node::get_hyphenation_type()
2689 return HYPHEN_MIDDLE;
2692 void break_char_node::ascii_print(ascii_output_file *ascii)
2694 ch->ascii_print(ascii);
2697 int break_char_node::overlaps_vertically()
2699 return ch->overlaps_vertically();
2702 int break_char_node::overlaps_horizontally()
2704 return ch->overlaps_horizontally();
2707 units break_char_node::size()
2709 return ch->size();
2712 tfont *break_char_node::get_tfont()
2714 return ch->get_tfont();
2717 node *extra_size_node::copy()
2719 return new extra_size_node(n);
2722 node *vertical_size_node::copy()
2724 return new vertical_size_node(n);
2727 node *hmotion_node::copy()
2729 return new hmotion_node(n, was_tab, unformat, col);
2732 node *space_char_hmotion_node::copy()
2734 return new space_char_hmotion_node(n, col);
2737 node *vmotion_node::copy()
2739 return new vmotion_node(n, col);
2742 node *dummy_node::copy()
2744 return new dummy_node;
2747 node *transparent_dummy_node::copy()
2749 return new transparent_dummy_node;
2752 hline_node::~hline_node()
2754 if (n)
2755 delete n;
2758 node *hline_node::copy()
2760 return new hline_node(x, n ? n->copy() : 0);
2763 hunits hline_node::width()
2765 return x < H0 ? H0 : x;
2768 vline_node::~vline_node()
2770 if (n)
2771 delete n;
2774 node *vline_node::copy()
2776 return new vline_node(x, n ? n->copy() : 0);
2779 hunits vline_node::width()
2781 return n == 0 ? H0 : n->width();
2784 zero_width_node::zero_width_node(node *nd) : n(nd)
2788 zero_width_node::~zero_width_node()
2790 delete_node_list(n);
2793 node *zero_width_node::copy()
2795 return new zero_width_node(copy_node_list(n));
2798 int node_list_character_type(node *p)
2800 int t = 0;
2801 for (; p; p = p->next)
2802 t |= p->character_type();
2803 return t;
2806 int zero_width_node::character_type()
2808 return node_list_character_type(n);
2811 void node_list_vertical_extent(node *p, vunits *min, vunits *max)
2813 *min = V0;
2814 *max = V0;
2815 vunits cur_vpos = V0;
2816 vunits v1, v2;
2817 for (; p; p = p->next) {
2818 p->vertical_extent(&v1, &v2);
2819 v1 += cur_vpos;
2820 if (v1 < *min)
2821 *min = v1;
2822 v2 += cur_vpos;
2823 if (v2 > *max)
2824 *max = v2;
2825 cur_vpos += p->vertical_width();
2829 void zero_width_node::vertical_extent(vunits *min, vunits *max)
2831 node_list_vertical_extent(n, min, max);
2834 overstrike_node::overstrike_node() : list(0), max_width(H0)
2838 overstrike_node::~overstrike_node()
2840 delete_node_list(list);
2843 node *overstrike_node::copy()
2845 overstrike_node *on = new overstrike_node;
2846 for (node *tem = list; tem; tem = tem->next)
2847 on->overstrike(tem->copy());
2848 return on;
2851 void overstrike_node::overstrike(node *n)
2853 if (n == 0)
2854 return;
2855 hunits w = n->width();
2856 if (w > max_width)
2857 max_width = w;
2858 node **p;
2859 for (p = &list; *p; p = &(*p)->next)
2861 n->next = 0;
2862 *p = n;
2865 hunits overstrike_node::width()
2867 return max_width;
2870 bracket_node::bracket_node() : list(0), max_width(H0)
2874 bracket_node::~bracket_node()
2876 delete_node_list(list);
2879 node *bracket_node::copy()
2881 bracket_node *on = new bracket_node;
2882 node *last = 0;
2883 node *tem;
2884 if (list)
2885 list->last = 0;
2886 for (tem = list; tem; tem = tem->next) {
2887 if (tem->next)
2888 tem->next->last = tem;
2889 last = tem;
2891 for (tem = last; tem; tem = tem->last)
2892 on->bracket(tem->copy());
2893 return on;
2896 void bracket_node::bracket(node *n)
2898 if (n == 0)
2899 return;
2900 hunits w = n->width();
2901 if (w > max_width)
2902 max_width = w;
2903 n->next = list;
2904 list = n;
2907 hunits bracket_node::width()
2909 return max_width;
2912 int node::nspaces()
2914 return 0;
2917 int node::merge_space(hunits, hunits, hunits)
2919 return 0;
2922 #if 0
2923 space_node *space_node::free_list = 0;
2925 void *space_node::operator new(size_t n)
2927 assert(n == sizeof(space_node));
2928 if (!free_list) {
2929 free_list = (space_node *)new char[sizeof(space_node)*BLOCK];
2930 for (int i = 0; i < BLOCK - 1; i++)
2931 free_list[i].next = free_list + i + 1;
2932 free_list[BLOCK-1].next = 0;
2934 space_node *p = free_list;
2935 free_list = (space_node *)(free_list->next);
2936 p->next = 0;
2937 return p;
2940 inline void space_node::operator delete(void *p)
2942 if (p) {
2943 ((space_node *)p)->next = free_list;
2944 free_list = (space_node *)p;
2947 #endif
2949 space_node::space_node(hunits nn, color *c, node *p)
2950 : node(p), n(nn), set(0), was_escape_colon(0), col(c)
2954 space_node::space_node(hunits nn, int s, int flag, color *c, node *p)
2955 : node(p), n(nn), set(s), was_escape_colon(flag), col(c)
2959 #if 0
2960 space_node::~space_node()
2963 #endif
2965 node *space_node::copy()
2967 return new space_node(n, set, was_escape_colon, col);
2970 int space_node::force_tprint()
2972 return 0;
2975 int space_node::nspaces()
2977 return set ? 0 : 1;
2980 int space_node::merge_space(hunits h, hunits, hunits)
2982 n += h;
2983 return 1;
2986 hunits space_node::width()
2988 return n;
2991 void node::spread_space(int*, hunits*)
2995 void space_node::spread_space(int *nspaces, hunits *desired_space)
2997 if (!set) {
2998 assert(*nspaces > 0);
2999 if (*nspaces == 1) {
3000 n += *desired_space;
3001 *desired_space = H0;
3003 else {
3004 hunits extra = *desired_space / *nspaces;
3005 *desired_space -= extra;
3006 n += extra;
3008 *nspaces -= 1;
3009 set = 1;
3013 void node::freeze_space()
3017 void space_node::freeze_space()
3019 set = 1;
3022 void node::is_escape_colon()
3026 void space_node::is_escape_colon()
3028 was_escape_colon = 1;
3031 diverted_space_node::diverted_space_node(vunits d, node *p)
3032 : node(p), n(d)
3036 node *diverted_space_node::copy()
3038 return new diverted_space_node(n);
3041 diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
3042 : node(p), filename(s)
3046 node *diverted_copy_file_node::copy()
3048 return new diverted_copy_file_node(filename);
3051 int node::ends_sentence()
3053 return 0;
3056 int kern_pair_node::ends_sentence()
3058 switch (n2->ends_sentence()) {
3059 case 0:
3060 return 0;
3061 case 1:
3062 return 1;
3063 case 2:
3064 break;
3065 default:
3066 assert(0);
3068 return n1->ends_sentence();
3071 int node_list_ends_sentence(node *n)
3073 for (; n != 0; n = n->next)
3074 switch (n->ends_sentence()) {
3075 case 0:
3076 return 0;
3077 case 1:
3078 return 1;
3079 case 2:
3080 break;
3081 default:
3082 assert(0);
3084 return 2;
3087 int dbreak_node::ends_sentence()
3089 return node_list_ends_sentence(none);
3092 int node::overlaps_horizontally()
3094 return 0;
3097 int node::overlaps_vertically()
3099 return 0;
3102 int node::discardable()
3104 return 0;
3107 int space_node::discardable()
3109 return set ? 0 : 1;
3112 vunits node::vertical_width()
3114 return V0;
3117 vunits vline_node::vertical_width()
3119 return x;
3122 vunits vmotion_node::vertical_width()
3124 return n;
3127 int node::set_unformat_flag()
3129 return 1;
3132 int node::character_type()
3134 return 0;
3137 hunits node::subscript_correction()
3139 return H0;
3142 hunits node::italic_correction()
3144 return H0;
3147 hunits node::left_italic_correction()
3149 return H0;
3152 hunits node::skew()
3154 return H0;
3157 /* vertical_extent methods */
3159 void node::vertical_extent(vunits *min, vunits *max)
3161 vunits v = vertical_width();
3162 if (v < V0) {
3163 *min = v;
3164 *max = V0;
3166 else {
3167 *max = v;
3168 *min = V0;
3172 void vline_node::vertical_extent(vunits *min, vunits *max)
3174 if (n == 0)
3175 node::vertical_extent(min, max);
3176 else {
3177 vunits cmin, cmax;
3178 n->vertical_extent(&cmin, &cmax);
3179 vunits h = n->size();
3180 if (x < V0) {
3181 if (-x < h) {
3182 *min = x;
3183 *max = V0;
3185 else {
3186 // we print the first character and then move up, so
3187 *max = cmax;
3188 // we print the last character and then move up h
3189 *min = cmin + h;
3190 if (*min > V0)
3191 *min = V0;
3192 *min += x;
3195 else {
3196 if (x < h) {
3197 *max = x;
3198 *min = V0;
3200 else {
3201 // we move down by h and then print the first character, so
3202 *min = cmin + h;
3203 if (*min > V0)
3204 *min = V0;
3205 *max = x + cmax;
3211 /* ascii_print methods */
3213 static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n)
3215 if (n == 0)
3216 return;
3217 ascii_print_reverse_node_list(ascii, n->next);
3218 n->ascii_print(ascii);
3221 void dbreak_node::ascii_print(ascii_output_file *ascii)
3223 ascii_print_reverse_node_list(ascii, none);
3226 void kern_pair_node::ascii_print(ascii_output_file *ascii)
3228 n1->ascii_print(ascii);
3229 n2->ascii_print(ascii);
3232 void node::ascii_print(ascii_output_file *)
3236 void space_node::ascii_print(ascii_output_file *ascii)
3238 if (!n.is_zero())
3239 ascii->outc(' ');
3242 void hmotion_node::ascii_print(ascii_output_file *ascii)
3244 // this is pretty arbitrary
3245 if (n >= points_to_units(2))
3246 ascii->outc(' ');
3249 void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
3251 ascii->outc(' ');
3254 /* asciify methods */
3256 void node::asciify(macro *m)
3258 m->append(this);
3261 void glyph_node::asciify(macro *m)
3263 unsigned char c = ci->get_asciify_code();
3264 if (c == 0)
3265 c = ci->get_ascii_code();
3266 if (c != 0) {
3267 m->append(c);
3268 delete this;
3270 else
3271 m->append(this);
3274 void kern_pair_node::asciify(macro *m)
3276 n1->asciify(m);
3277 n2->asciify(m);
3278 n1 = n2 = 0;
3279 delete this;
3282 static void asciify_reverse_node_list(macro *m, node *n)
3284 if (n == 0)
3285 return;
3286 asciify_reverse_node_list(m, n->next);
3287 n->asciify(m);
3290 void dbreak_node::asciify(macro *m)
3292 asciify_reverse_node_list(m, none);
3293 none = 0;
3294 delete this;
3297 void ligature_node::asciify(macro *m)
3299 n1->asciify(m);
3300 n2->asciify(m);
3301 n1 = n2 = 0;
3302 delete this;
3305 void break_char_node::asciify(macro *m)
3307 ch->asciify(m);
3308 ch = 0;
3309 delete this;
3312 void italic_corrected_node::asciify(macro *m)
3314 n->asciify(m);
3315 n = 0;
3316 delete this;
3319 void left_italic_corrected_node::asciify(macro *m)
3321 if (n) {
3322 n->asciify(m);
3323 n = 0;
3325 delete this;
3328 void hmotion_node::asciify(macro *m)
3330 if (was_tab) {
3331 m->append('\t');
3332 delete this;
3334 else
3335 m->append(this);
3338 space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3339 node *next)
3340 : hmotion_node(i, c, next)
3344 void space_char_hmotion_node::asciify(macro *m)
3346 m->append(ESCAPE_SPACE);
3347 delete this;
3350 void space_node::asciify(macro *m)
3352 if (was_escape_colon) {
3353 m->append(ESCAPE_COLON);
3354 delete this;
3356 else
3357 m->append(this);
3360 void word_space_node::asciify(macro *m)
3362 for (width_list *w = orig_width; w; w = w->next)
3363 m->append(' ');
3364 delete this;
3367 void unbreakable_space_node::asciify(macro *m)
3369 m->append(ESCAPE_TILDE);
3370 delete this;
3373 void line_start_node::asciify(macro *)
3375 delete this;
3378 void vertical_size_node::asciify(macro *)
3380 delete this;
3383 breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/,
3384 breakpoint *rest, int /*is_inner*/)
3386 return rest;
3389 int node::nbreaks()
3391 return 0;
3394 breakpoint *space_node::get_breakpoints(hunits width, int ns,
3395 breakpoint *rest, int is_inner)
3397 if (next->discardable())
3398 return rest;
3399 breakpoint *bp = new breakpoint;
3400 bp->next = rest;
3401 bp->width = width;
3402 bp->nspaces = ns;
3403 bp->hyphenated = 0;
3404 if (is_inner) {
3405 assert(rest != 0);
3406 bp->index = rest->index + 1;
3407 bp->nd = rest->nd;
3409 else {
3410 bp->nd = this;
3411 bp->index = 0;
3413 return bp;
3416 int space_node::nbreaks()
3418 if (next->discardable())
3419 return 0;
3420 else
3421 return 1;
3424 static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
3425 int ns, breakpoint *rest)
3427 if (p != 0) {
3428 rest = p->get_breakpoints(*widthp,
3430 node_list_get_breakpoints(p->next, widthp, ns,
3431 rest),
3433 *widthp += p->width();
3435 return rest;
3438 breakpoint *dbreak_node::get_breakpoints(hunits width, int ns,
3439 breakpoint *rest, int is_inner)
3441 breakpoint *bp = new breakpoint;
3442 bp->next = rest;
3443 bp->width = width;
3444 for (node *tem = pre; tem != 0; tem = tem->next)
3445 bp->width += tem->width();
3446 bp->nspaces = ns;
3447 bp->hyphenated = 1;
3448 if (is_inner) {
3449 assert(rest != 0);
3450 bp->index = rest->index + 1;
3451 bp->nd = rest->nd;
3453 else {
3454 bp->nd = this;
3455 bp->index = 0;
3457 return node_list_get_breakpoints(none, &width, ns, bp);
3460 int dbreak_node::nbreaks()
3462 int i = 1;
3463 for (node *tem = none; tem != 0; tem = tem->next)
3464 i += tem->nbreaks();
3465 return i;
3468 void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
3470 assert(0);
3473 void space_node::split(int where, node **pre, node **post)
3475 assert(where == 0);
3476 *pre = next;
3477 *post = 0;
3478 delete this;
3481 static void node_list_split(node *p, int *wherep, node **prep, node **postp)
3483 if (p == 0)
3484 return;
3485 int nb = p->nbreaks();
3486 node_list_split(p->next, wherep, prep, postp);
3487 if (*wherep < 0) {
3488 p->next = *postp;
3489 *postp = p;
3491 else if (*wherep < nb) {
3492 p->next = *prep;
3493 p->split(*wherep, prep, postp);
3495 else {
3496 p->next = *prep;
3497 *prep = p;
3499 *wherep -= nb;
3502 void dbreak_node::split(int where, node **prep, node **postp)
3504 assert(where >= 0);
3505 if (where == 0) {
3506 *postp = post;
3507 post = 0;
3508 if (pre == 0)
3509 *prep = next;
3510 else {
3511 node *tem;
3512 for (tem = pre; tem->next != 0; tem = tem->next)
3514 tem->next = next;
3515 *prep = pre;
3517 pre = 0;
3518 delete this;
3520 else {
3521 *prep = next;
3522 where -= 1;
3523 node_list_split(none, &where, prep, postp);
3524 none = 0;
3525 delete this;
3529 hyphenation_type node::get_hyphenation_type()
3531 return HYPHEN_BOUNDARY;
3534 hyphenation_type dbreak_node::get_hyphenation_type()
3536 return HYPHEN_INHIBIT;
3539 hyphenation_type kern_pair_node::get_hyphenation_type()
3541 return HYPHEN_MIDDLE;
3544 hyphenation_type dummy_node::get_hyphenation_type()
3546 return HYPHEN_MIDDLE;
3549 hyphenation_type transparent_dummy_node::get_hyphenation_type()
3551 return HYPHEN_MIDDLE;
3554 hyphenation_type hmotion_node::get_hyphenation_type()
3556 return HYPHEN_MIDDLE;
3559 hyphenation_type space_char_hmotion_node::get_hyphenation_type()
3561 return HYPHEN_MIDDLE;
3564 hyphenation_type overstrike_node::get_hyphenation_type()
3566 return HYPHEN_MIDDLE;
3569 hyphenation_type space_node::get_hyphenation_type()
3571 if (was_escape_colon)
3572 return HYPHEN_MIDDLE;
3573 return HYPHEN_BOUNDARY;
3576 hyphenation_type unbreakable_space_node::get_hyphenation_type()
3578 return HYPHEN_MIDDLE;
3581 int node::interpret(macro *)
3583 return 0;
3586 special_node::special_node(const macro &m, int n)
3587 : mac(m), no_init_string(n)
3589 font_size fs = curenv->get_font_size();
3590 int char_height = curenv->get_char_height();
3591 int char_slant = curenv->get_char_slant();
3592 int fontno = env_definite_font(curenv);
3593 tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fontno);
3594 if (curenv->is_composite())
3595 tf = tf->get_plain();
3596 gcol = curenv->get_glyph_color();
3597 fcol = curenv->get_fill_color();
3600 special_node::special_node(const macro &m, tfont *t,
3601 color *gc, color *fc, int n)
3602 : mac(m), tf(t), gcol(gc), fcol(fc), no_init_string(n)
3606 int special_node::same(node *n)
3608 return mac == ((special_node *)n)->mac
3609 && tf == ((special_node *)n)->tf
3610 && gcol == ((special_node *)n)->gcol
3611 && fcol == ((special_node *)n)->fcol
3612 && no_init_string == ((special_node *)n)->no_init_string;
3615 const char *special_node::type()
3617 return "special_node";
3620 int special_node::ends_sentence()
3622 return 2;
3625 int special_node::force_tprint()
3627 return 0;
3630 node *special_node::copy()
3632 return new special_node(mac, tf, gcol, fcol, no_init_string);
3635 void special_node::tprint_start(troff_output_file *out)
3637 out->start_special(tf, gcol, fcol, no_init_string);
3640 void special_node::tprint_char(troff_output_file *out, unsigned char c)
3642 out->special_char(c);
3645 void special_node::tprint_end(troff_output_file *out)
3647 out->end_special();
3650 tfont *special_node::get_tfont()
3652 return tf;
3655 /* suppress_node */
3657 suppress_node::suppress_node(int on_or_off, int issue_limits)
3658 : is_on(on_or_off), emit_limits(issue_limits),
3659 filename(0), position(0), image_id(0)
3663 suppress_node::suppress_node(symbol f, char p, int id)
3664 : is_on(2), emit_limits(0), filename(f), position(p), image_id(id)
3668 suppress_node::suppress_node(int issue_limits, int on_or_off,
3669 symbol f, char p, int id)
3670 : is_on(on_or_off), emit_limits(issue_limits),
3671 filename(f), position(p), image_id(id)
3675 int suppress_node::same(node *n)
3677 return ((is_on == ((suppress_node *)n)->is_on)
3678 && (emit_limits == ((suppress_node *)n)->emit_limits)
3679 && (filename == ((suppress_node *)n)->filename)
3680 && (position == ((suppress_node *)n)->position)
3681 && (image_id == ((suppress_node *)n)->image_id));
3684 const char *suppress_node::type()
3686 return "suppress_node";
3689 node *suppress_node::copy()
3691 return new suppress_node(emit_limits, is_on, filename, position, image_id);
3694 int get_reg_int(const char *p)
3696 reg *r = (reg *)number_reg_dictionary.lookup(p);
3697 units prev_value;
3698 if (r && (r->get_value(&prev_value)))
3699 return (int)prev_value;
3700 else
3701 warning(WARN_REG, "number register `%1' not defined", p);
3702 return 0;
3705 const char *get_reg_str(const char *p)
3707 reg *r = (reg *)number_reg_dictionary.lookup(p);
3708 if (r)
3709 return r->get_string();
3710 else
3711 warning(WARN_REG, "register `%1' not defined", p);
3712 return 0;
3715 void suppress_node::put(troff_output_file *out, const char *s)
3717 int i = 0;
3718 while (s[i] != (char)0) {
3719 out->special_char(s[i]);
3720 i++;
3725 * We need to remember the start of the image and its name.
3728 static char last_position = 0;
3729 static const char *last_image_filename = 0;
3730 static int last_image_id = 0;
3732 inline int min(int a, int b)
3734 return a < b ? a : b;
3738 * tprint - if (is_on == 2)
3739 * remember current position (l, r, c, i) and filename
3740 * else
3741 * if (emit_limits)
3742 * if (html)
3743 * emit image tag
3744 * else
3745 * emit postscript bounds for image
3746 * else
3747 * if (suppress boolean differs from current state)
3748 * alter state
3749 * reset registers
3750 * record current page
3751 * set low water mark.
3754 void suppress_node::tprint(troff_output_file *out)
3756 int current_page = topdiv->get_page_number();
3757 // firstly check to see whether this suppress node contains
3758 // an image filename & position.
3759 if (is_on == 2) {
3760 // remember position and filename
3761 last_position = position;
3762 const char *tem = last_image_filename;
3763 last_image_filename = strsave(filename.contents());
3764 if (tem)
3765 a_delete(tem);
3766 last_image_id = image_id;
3767 // printf("start of image and page = %d\n", current_page);
3769 else {
3770 // now check whether the suppress node requires us to issue limits.
3771 if (emit_limits) {
3772 char name[8192];
3773 // remember that the filename will contain a %d in which the
3774 // last_image_id is placed
3775 sprintf(name, last_image_filename, last_image_id);
3776 if (is_html) {
3777 switch (last_position) {
3778 case 'c':
3779 out->start_special();
3780 put(out, "html-tag:.centered-image");
3781 break;
3782 case 'r':
3783 out->start_special();
3784 put(out, "html-tag:.right-image");
3785 break;
3786 case 'l':
3787 out->start_special();
3788 put(out, "html-tag:.left-image");
3789 break;
3790 case 'i':
3792 default:
3795 out->end_special();
3796 out->start_special();
3797 put(out, "html-tag:.auto-image ");
3798 put(out, name);
3799 out->end_special();
3801 else {
3802 // postscript (or other device)
3803 if (suppress_start_page > 0 && current_page != suppress_start_page)
3804 error("suppression limit registers span more than one page;\n"
3805 "image description %1 will be wrong", image_no);
3806 // if (topdiv->get_page_number() != suppress_start_page)
3807 // fprintf(stderr, "end of image and topdiv page = %d and suppress_start_page = %d\n",
3808 // topdiv->get_page_number(), suppress_start_page);
3810 // remember that the filename will contain a %d in which the
3811 // image_no is placed
3812 fprintf(stderr,
3813 "grohtml-info:page %d %d %d %d %d %d %s %d %d %s\n",
3814 topdiv->get_page_number(),
3815 get_reg_int("opminx"), get_reg_int("opminy"),
3816 get_reg_int("opmaxx"), get_reg_int("opmaxy"),
3817 // page offset + line length
3818 get_reg_int(".o") + get_reg_int(".l"),
3819 name, hresolution, vresolution, get_reg_str(".F"));
3820 fflush(stderr);
3823 else {
3824 if (is_on) {
3825 out->on();
3826 // lastly we reset the output registers
3827 reset_output_registers();
3829 else
3830 out->off();
3831 suppress_start_page = current_page;
3836 int suppress_node::force_tprint()
3838 return is_on;
3841 hunits suppress_node::width()
3843 return H0;
3846 /* composite_node */
3848 class composite_node : public charinfo_node {
3849 node *n;
3850 tfont *tf;
3851 public:
3852 composite_node(node *, charinfo *, tfont *, node * = 0);
3853 ~composite_node();
3854 node *copy();
3855 hunits width();
3856 node *last_char_node();
3857 units size();
3858 void tprint(troff_output_file *);
3859 hyphenation_type get_hyphenation_type();
3860 void ascii_print(ascii_output_file *);
3861 void asciify(macro *);
3862 hyphen_list *get_hyphen_list(hyphen_list *, int *);
3863 node *add_self(node *, hyphen_list **);
3864 tfont *get_tfont();
3865 int same(node *);
3866 const char *type();
3867 int force_tprint();
3868 void vertical_extent(vunits *, vunits *);
3869 vunits vertical_width();
3872 composite_node::composite_node(node *p, charinfo *c, tfont *t, node *x)
3873 : charinfo_node(c, x), n(p), tf(t)
3877 composite_node::~composite_node()
3879 delete_node_list(n);
3882 node *composite_node::copy()
3884 return new composite_node(copy_node_list(n), ci, tf);
3887 hunits composite_node::width()
3889 hunits x;
3890 if (tf->get_constant_space(&x))
3891 return x;
3892 x = H0;
3893 for (node *tem = n; tem; tem = tem->next)
3894 x += tem->width();
3895 hunits offset;
3896 if (tf->get_bold(&offset))
3897 x += offset;
3898 x += tf->get_track_kern();
3899 return x;
3902 node *composite_node::last_char_node()
3904 return this;
3907 vunits composite_node::vertical_width()
3909 vunits v = V0;
3910 for (node *tem = n; tem; tem = tem->next)
3911 v += tem->vertical_width();
3912 return v;
3915 units composite_node::size()
3917 return tf->get_size().to_units();
3920 hyphenation_type composite_node::get_hyphenation_type()
3922 return HYPHEN_MIDDLE;
3925 void composite_node::asciify(macro *m)
3927 unsigned char c = ci->get_asciify_code();
3928 if (c == 0)
3929 c = ci->get_ascii_code();
3930 if (c != 0) {
3931 m->append(c);
3932 delete this;
3934 else
3935 m->append(this);
3938 void composite_node::ascii_print(ascii_output_file *ascii)
3940 unsigned char c = ci->get_ascii_code();
3941 if (c != 0)
3942 ascii->outc(c);
3943 else
3944 ascii->outs(ci->nm.contents());
3948 hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail, int *count)
3950 (*count)++;
3951 return new hyphen_list(ci->get_hyphenation_code(), tail);
3954 node *composite_node::add_self(node *nn, hyphen_list **p)
3956 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
3957 next = nn;
3958 nn = this;
3959 if ((*p)->hyphen)
3960 nn = nn->add_discretionary_hyphen();
3961 hyphen_list *pp = *p;
3962 *p = (*p)->next;
3963 delete pp;
3964 return nn;
3967 tfont *composite_node::get_tfont()
3969 return tf;
3972 node *reverse_node_list(node *n)
3974 node *r = 0;
3975 while (n) {
3976 node *tem = n;
3977 n = n->next;
3978 tem->next = r;
3979 r = tem;
3981 return r;
3984 void composite_node::vertical_extent(vunits *min, vunits *max)
3986 n = reverse_node_list(n);
3987 node_list_vertical_extent(n, min, max);
3988 n = reverse_node_list(n);
3991 width_list::width_list(hunits w, hunits s)
3992 : width(w), sentence_width(s), next(0)
3996 width_list::width_list(width_list *w)
3997 : width(w->width), sentence_width(w->sentence_width), next(0)
4001 word_space_node::word_space_node(hunits d, color *c, width_list *w, node *x)
4002 : space_node(d, c, x), orig_width(w), unformat(0)
4006 word_space_node::word_space_node(hunits d, int s, color *c, width_list *w,
4007 int flag, node *x)
4008 : space_node(d, s, 0, c, x), orig_width(w), unformat(flag)
4012 word_space_node::~word_space_node()
4014 width_list *w = orig_width;
4015 while (w != 0) {
4016 width_list *tmp = w;
4017 w = w->next;
4018 delete tmp;
4022 node *word_space_node::copy()
4024 assert(orig_width != 0);
4025 width_list *w_old_curr = orig_width;
4026 width_list *w_new_curr = new width_list(w_old_curr);
4027 width_list *w_new = w_new_curr;
4028 w_old_curr = w_old_curr->next;
4029 while (w_old_curr != 0) {
4030 w_new_curr->next = new width_list(w_old_curr);
4031 w_new_curr = w_new_curr->next;
4032 w_old_curr = w_old_curr->next;
4034 return new word_space_node(n, set, col, w_new, unformat);
4037 int word_space_node::set_unformat_flag()
4039 unformat = 1;
4040 return 1;
4043 void word_space_node::tprint(troff_output_file *out)
4045 out->fill_color(col);
4046 out->word_marker();
4047 out->right(n);
4050 int word_space_node::merge_space(hunits h, hunits sw, hunits ssw)
4052 n += h;
4053 assert(orig_width != 0);
4054 width_list *w = orig_width;
4055 for (; w->next; w = w->next)
4057 w->next = new width_list(sw, ssw);
4058 return 1;
4061 unbreakable_space_node::unbreakable_space_node(hunits d, color *c, node *x)
4062 : word_space_node(d, c, 0, x)
4066 unbreakable_space_node::unbreakable_space_node(hunits d, int s,
4067 color *c, node *x)
4068 : word_space_node(d, s, c, 0, 0, x)
4072 node *unbreakable_space_node::copy()
4074 return new unbreakable_space_node(n, set, col);
4077 int unbreakable_space_node::force_tprint()
4079 return 0;
4082 breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
4083 breakpoint *rest, int)
4085 return rest;
4088 int unbreakable_space_node::nbreaks()
4090 return 0;
4093 void unbreakable_space_node::split(int, node **, node **)
4095 assert(0);
4098 int unbreakable_space_node::merge_space(hunits, hunits, hunits)
4100 return 0;
4103 hvpair::hvpair()
4107 draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4108 color *gc, color *fc)
4109 : npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4111 point = new hvpair[npoints];
4112 for (int i = 0; i < npoints; i++)
4113 point[i] = p[i];
4116 int draw_node::same(node *n)
4118 draw_node *nd = (draw_node *)n;
4119 if (code != nd->code || npoints != nd->npoints || sz != nd->sz
4120 || gcol != nd->gcol || fcol != nd->fcol)
4121 return 0;
4122 for (int i = 0; i < npoints; i++)
4123 if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
4124 return 0;
4125 return 1;
4128 const char *draw_node::type()
4130 return "draw_node";
4133 int draw_node::force_tprint()
4135 return 0;
4138 draw_node::~draw_node()
4140 if (point)
4141 a_delete point;
4144 hunits draw_node::width()
4146 hunits x = H0;
4147 for (int i = 0; i < npoints; i++)
4148 x += point[i].h;
4149 return x;
4152 vunits draw_node::vertical_width()
4154 if (code == 'e')
4155 return V0;
4156 vunits x = V0;
4157 for (int i = 0; i < npoints; i++)
4158 x += point[i].v;
4159 return x;
4162 node *draw_node::copy()
4164 return new draw_node(code, point, npoints, sz, gcol, fcol);
4167 void draw_node::tprint(troff_output_file *out)
4169 out->draw(code, point, npoints, sz, gcol, fcol);
4172 /* tprint methods */
4174 void glyph_node::tprint(troff_output_file *out)
4176 tfont *ptf = tf->get_plain();
4177 if (ptf == tf)
4178 out->put_char_width(ci, ptf, gcol, fcol, width(), H0);
4179 else {
4180 hunits offset;
4181 int bold = tf->get_bold(&offset);
4182 hunits w = ptf->get_width(ci);
4183 hunits k = H0;
4184 hunits x;
4185 int cs = tf->get_constant_space(&x);
4186 if (cs) {
4187 x -= w;
4188 if (bold)
4189 x -= offset;
4190 hunits x2 = x/2;
4191 out->right(x2);
4192 k = x - x2;
4194 else
4195 k = tf->get_track_kern();
4196 if (bold) {
4197 out->put_char(ci, ptf, gcol, fcol);
4198 out->right(offset);
4200 out->put_char_width(ci, ptf, gcol, fcol, w, k);
4204 void glyph_node::zero_width_tprint(troff_output_file *out)
4206 tfont *ptf = tf->get_plain();
4207 hunits offset;
4208 int bold = tf->get_bold(&offset);
4209 hunits x;
4210 int cs = tf->get_constant_space(&x);
4211 if (cs) {
4212 x -= ptf->get_width(ci);
4213 if (bold)
4214 x -= offset;
4215 x = x/2;
4216 out->right(x);
4218 out->put_char(ci, ptf, gcol, fcol);
4219 if (bold) {
4220 out->right(offset);
4221 out->put_char(ci, ptf, gcol, fcol);
4222 out->right(-offset);
4224 if (cs)
4225 out->right(-x);
4228 void break_char_node::tprint(troff_output_file *t)
4230 ch->tprint(t);
4233 void break_char_node::zero_width_tprint(troff_output_file *t)
4235 ch->zero_width_tprint(t);
4238 void hline_node::tprint(troff_output_file *out)
4240 if (x < H0) {
4241 out->right(x);
4242 x = -x;
4244 if (n == 0) {
4245 out->right(x);
4246 return;
4248 hunits w = n->width();
4249 if (w <= H0) {
4250 error("horizontal line drawing character must have positive width");
4251 out->right(x);
4252 return;
4254 int i = int(x/w);
4255 if (i == 0) {
4256 hunits xx = x - w;
4257 hunits xx2 = xx/2;
4258 out->right(xx2);
4259 if (out->is_on())
4260 n->tprint(out);
4261 out->right(xx - xx2);
4263 else {
4264 hunits rem = x - w*i;
4265 if (rem > H0)
4266 if (n->overlaps_horizontally()) {
4267 if (out->is_on())
4268 n->tprint(out);
4269 out->right(rem - w);
4271 else
4272 out->right(rem);
4273 while (--i >= 0)
4274 if (out->is_on())
4275 n->tprint(out);
4279 void vline_node::tprint(troff_output_file *out)
4281 if (n == 0) {
4282 out->down(x);
4283 return;
4285 vunits h = n->size();
4286 int overlaps = n->overlaps_vertically();
4287 vunits y = x;
4288 if (y < V0) {
4289 y = -y;
4290 int i = y / h;
4291 vunits rem = y - i*h;
4292 if (i == 0) {
4293 out->right(n->width());
4294 out->down(-rem);
4296 else {
4297 while (--i > 0) {
4298 n->zero_width_tprint(out);
4299 out->down(-h);
4301 if (overlaps) {
4302 n->zero_width_tprint(out);
4303 out->down(-rem);
4304 if (out->is_on())
4305 n->tprint(out);
4306 out->down(-h);
4308 else {
4309 if (out->is_on())
4310 n->tprint(out);
4311 out->down(-h - rem);
4315 else {
4316 int i = y / h;
4317 vunits rem = y - i*h;
4318 if (i == 0) {
4319 out->down(rem);
4320 out->right(n->width());
4322 else {
4323 out->down(h);
4324 if (overlaps)
4325 n->zero_width_tprint(out);
4326 out->down(rem);
4327 while (--i > 0) {
4328 n->zero_width_tprint(out);
4329 out->down(h);
4331 if (out->is_on())
4332 n->tprint(out);
4337 void zero_width_node::tprint(troff_output_file *out)
4339 if (!n)
4340 return;
4341 if (!n->next) {
4342 n->zero_width_tprint(out);
4343 return;
4345 int hpos = out->get_hpos();
4346 int vpos = out->get_vpos();
4347 node *tem = n;
4348 while (tem) {
4349 tem->tprint(out);
4350 tem = tem->next;
4352 out->moveto(hpos, vpos);
4355 void overstrike_node::tprint(troff_output_file *out)
4357 hunits pos = H0;
4358 for (node *tem = list; tem; tem = tem->next) {
4359 hunits x = (max_width - tem->width())/2;
4360 out->right(x - pos);
4361 pos = x;
4362 tem->zero_width_tprint(out);
4364 out->right(max_width - pos);
4367 void bracket_node::tprint(troff_output_file *out)
4369 if (list == 0)
4370 return;
4371 int npieces = 0;
4372 node *tem;
4373 for (tem = list; tem; tem = tem->next)
4374 ++npieces;
4375 vunits h = list->size();
4376 vunits totalh = h*npieces;
4377 vunits y = (totalh - h)/2;
4378 out->down(y);
4379 for (tem = list; tem; tem = tem->next) {
4380 tem->zero_width_tprint(out);
4381 out->down(-h);
4383 out->right(max_width);
4384 out->down(totalh - y);
4387 void node::tprint(troff_output_file *)
4391 void node::zero_width_tprint(troff_output_file *out)
4393 int hpos = out->get_hpos();
4394 int vpos = out->get_vpos();
4395 tprint(out);
4396 out->moveto(hpos, vpos);
4399 void space_node::tprint(troff_output_file *out)
4401 out->fill_color(col);
4402 out->right(n);
4405 void hmotion_node::tprint(troff_output_file *out)
4407 out->fill_color(col);
4408 out->right(n);
4411 void vmotion_node::tprint(troff_output_file *out)
4413 out->fill_color(col);
4414 out->down(n);
4417 void kern_pair_node::tprint(troff_output_file *out)
4419 n1->tprint(out);
4420 out->right(amount);
4421 n2->tprint(out);
4424 static void tprint_reverse_node_list(troff_output_file *out, node *n)
4426 if (n == 0)
4427 return;
4428 tprint_reverse_node_list(out, n->next);
4429 n->tprint(out);
4432 void dbreak_node::tprint(troff_output_file *out)
4434 tprint_reverse_node_list(out, none);
4437 void composite_node::tprint(troff_output_file *out)
4439 hunits bold_offset;
4440 int is_bold = tf->get_bold(&bold_offset);
4441 hunits track_kern = tf->get_track_kern();
4442 hunits constant_space;
4443 int is_constant_spaced = tf->get_constant_space(&constant_space);
4444 hunits x = H0;
4445 if (is_constant_spaced) {
4446 x = constant_space;
4447 for (node *tem = n; tem; tem = tem->next)
4448 x -= tem->width();
4449 if (is_bold)
4450 x -= bold_offset;
4451 hunits x2 = x/2;
4452 out->right(x2);
4453 x -= x2;
4455 if (is_bold) {
4456 int hpos = out->get_hpos();
4457 int vpos = out->get_vpos();
4458 tprint_reverse_node_list(out, n);
4459 out->moveto(hpos, vpos);
4460 out->right(bold_offset);
4462 tprint_reverse_node_list(out, n);
4463 if (is_constant_spaced)
4464 out->right(x);
4465 else
4466 out->right(track_kern);
4469 node *make_composite_node(charinfo *s, environment *env)
4471 int fontno = env_definite_font(env);
4472 if (fontno < 0) {
4473 error("no current font");
4474 return 0;
4476 assert(fontno < font_table_size && font_table[fontno] != 0);
4477 node *n = charinfo_to_node_list(s, env);
4478 font_size fs = env->get_font_size();
4479 int char_height = env->get_char_height();
4480 int char_slant = env->get_char_slant();
4481 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
4482 fontno);
4483 if (env->is_composite())
4484 tf = tf->get_plain();
4485 return new composite_node(n, s, tf);
4488 node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0)
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 int fn = fontno;
4497 int found = font_table[fontno]->contains(s);
4498 if (!found) {
4499 macro *mac = s->get_macro();
4500 if (mac && s->is_fallback())
4501 return make_composite_node(s, env);
4502 if (s->numbered()) {
4503 if (!no_error_message)
4504 warning(WARN_CHAR, "can't find numbered character %1",
4505 s->get_number());
4506 return 0;
4508 special_font_list *sf = font_table[fontno]->sf;
4509 while (sf != 0 && !found) {
4510 fn = sf->n;
4511 if (font_table[fn])
4512 found = font_table[fn]->contains(s);
4513 sf = sf->next;
4515 if (!found) {
4516 symbol f = font_table[fontno]->get_name();
4517 string gl(f.contents());
4518 gl += ' ';
4519 gl += s->nm.contents();
4520 gl += '\0';
4521 charinfo *ci = get_charinfo(symbol(gl.contents()));
4522 if (ci && ci->get_macro())
4523 return make_composite_node(ci, env);
4525 if (!found) {
4526 sf = global_special_fonts;
4527 while (sf != 0 && !found) {
4528 fn = sf->n;
4529 if (font_table[fn])
4530 found = font_table[fn]->contains(s);
4531 sf = sf->next;
4534 if (!found)
4535 if (mac && s->is_special())
4536 return make_composite_node(s, env);
4537 if (!found) {
4538 for (fn = 0; fn < font_table_size; fn++)
4539 if (font_table[fn]
4540 && font_table[fn]->is_special()
4541 && font_table[fn]->contains(s)) {
4542 found = 1;
4543 break;
4546 if (!found) {
4547 if (!no_error_message && s->first_time_not_found()) {
4548 unsigned char input_code = s->get_ascii_code();
4549 if (input_code != 0) {
4550 if (csgraph(input_code))
4551 warning(WARN_CHAR, "can't find character `%1'", input_code);
4552 else
4553 warning(WARN_CHAR, "can't find character with input code %1",
4554 int(input_code));
4556 else if (s->nm.contents())
4557 warning(WARN_CHAR, "can't find special character `%1'",
4558 s->nm.contents());
4560 return 0;
4563 font_size fs = env->get_font_size();
4564 int char_height = env->get_char_height();
4565 int char_slant = env->get_char_slant();
4566 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn);
4567 if (env->is_composite())
4568 tf = tf->get_plain();
4569 color *gcol = env->get_glyph_color();
4570 color *fcol = env->get_fill_color();
4571 return new glyph_node(s, tf, gcol, fcol);
4574 node *make_node(charinfo *ci, environment *env)
4576 switch (ci->get_special_translation()) {
4577 case charinfo::TRANSLATE_SPACE:
4578 return new space_char_hmotion_node(env->get_space_width(),
4579 env->get_fill_color());
4580 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4581 return new unbreakable_space_node(env->get_space_width(),
4582 env->get_fill_color());
4583 case charinfo::TRANSLATE_DUMMY:
4584 return new dummy_node;
4585 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4586 error("translation to \\% ignored in this context");
4587 break;
4589 charinfo *tem = ci->get_translation();
4590 if (tem)
4591 ci = tem;
4592 macro *mac = ci->get_macro();
4593 if (mac && ci->is_normal())
4594 return make_composite_node(ci, env);
4595 else
4596 return make_glyph_node(ci, env);
4599 int character_exists(charinfo *ci, environment *env)
4601 if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
4602 return 1;
4603 charinfo *tem = ci->get_translation();
4604 if (tem)
4605 ci = tem;
4606 if (ci->get_macro())
4607 return 1;
4608 node *nd = make_glyph_node(ci, env, 1);
4609 if (nd) {
4610 delete nd;
4611 return 1;
4613 return 0;
4616 node *node::add_char(charinfo *ci, environment *env,
4617 hunits *widthp, int *spacep)
4619 node *res;
4620 switch (ci->get_special_translation()) {
4621 case charinfo::TRANSLATE_SPACE:
4622 res = new space_char_hmotion_node(env->get_space_width(),
4623 env->get_fill_color(), this);
4624 *widthp += res->width();
4625 return res;
4626 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4627 res = new unbreakable_space_node(env->get_space_width(),
4628 env->get_fill_color(), this);
4629 res->freeze_space();
4630 *widthp += res->width();
4631 *spacep += res->nspaces();
4632 return res;
4633 case charinfo::TRANSLATE_DUMMY:
4634 return new dummy_node(this);
4635 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4636 return add_discretionary_hyphen();
4638 charinfo *tem = ci->get_translation();
4639 if (tem)
4640 ci = tem;
4641 macro *mac = ci->get_macro();
4642 if (mac && ci->is_normal()) {
4643 res = make_composite_node(ci, env);
4644 if (res) {
4645 res->next = this;
4646 *widthp += res->width();
4648 else
4649 return this;
4651 else {
4652 node *gn = make_glyph_node(ci, env);
4653 if (gn == 0)
4654 return this;
4655 else {
4656 hunits old_width = width();
4657 node *p = gn->merge_self(this);
4658 if (p == 0) {
4659 *widthp += gn->width();
4660 gn->next = this;
4661 res = gn;
4663 else {
4664 *widthp += p->width() - old_width;
4665 res = p;
4669 int break_code = 0;
4670 if (ci->can_break_before())
4671 break_code = 1;
4672 if (ci->can_break_after())
4673 break_code |= 2;
4674 if (break_code) {
4675 node *next1 = res->next;
4676 res->next = 0;
4677 res = new break_char_node(res, break_code, env->get_fill_color(), next1);
4679 return res;
4682 #ifdef __GNUG__
4683 inline
4684 #endif
4685 int same_node(node *n1, node *n2)
4687 if (n1 != 0) {
4688 if (n2 != 0)
4689 return n1->type() == n2->type() && n1->same(n2);
4690 else
4691 return 0;
4693 else
4694 return n2 == 0;
4697 int same_node_list(node *n1, node *n2)
4699 while (n1 && n2) {
4700 if (n1->type() != n2->type() || !n1->same(n2))
4701 return 0;
4702 n1 = n1->next;
4703 n2 = n2->next;
4705 return !n1 && !n2;
4708 int extra_size_node::same(node *nd)
4710 return n == ((extra_size_node *)nd)->n;
4713 const char *extra_size_node::type()
4715 return "extra_size_node";
4718 int extra_size_node::force_tprint()
4720 return 0;
4723 int vertical_size_node::same(node *nd)
4725 return n == ((vertical_size_node *)nd)->n;
4728 const char *vertical_size_node::type()
4730 return "vertical_size_node";
4733 int vertical_size_node::set_unformat_flag()
4735 return 0;
4738 int vertical_size_node::force_tprint()
4740 return 0;
4743 int hmotion_node::same(node *nd)
4745 return n == ((hmotion_node *)nd)->n
4746 && col == ((hmotion_node *)nd)->col;
4749 const char *hmotion_node::type()
4751 return "hmotion_node";
4754 int hmotion_node::set_unformat_flag()
4756 unformat = 1;
4757 return 1;
4760 int hmotion_node::force_tprint()
4762 return 0;
4765 node *hmotion_node::add_self(node *n, hyphen_list **p)
4767 next = n;
4768 hyphen_list *pp = *p;
4769 *p = (*p)->next;
4770 delete pp;
4771 return this;
4774 hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail, int *)
4776 return new hyphen_list(0, tail);
4779 int space_char_hmotion_node::same(node *nd)
4781 return n == ((space_char_hmotion_node *)nd)->n
4782 && col == ((space_char_hmotion_node *)nd)->col;
4785 const char *space_char_hmotion_node::type()
4787 return "space_char_hmotion_node";
4790 int space_char_hmotion_node::force_tprint()
4792 return 0;
4795 node *space_char_hmotion_node::add_self(node *n, hyphen_list **p)
4797 next = n;
4798 hyphen_list *pp = *p;
4799 *p = (*p)->next;
4800 delete pp;
4801 return this;
4804 hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail,
4805 int *)
4807 return new hyphen_list(0, tail);
4810 int vmotion_node::same(node *nd)
4812 return n == ((vmotion_node *)nd)->n
4813 && col == ((vmotion_node *)nd)->col;
4816 const char *vmotion_node::type()
4818 return "vmotion_node";
4821 int vmotion_node::force_tprint()
4823 return 0;
4826 int hline_node::same(node *nd)
4828 return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n);
4831 const char *hline_node::type()
4833 return "hline_node";
4836 int hline_node::force_tprint()
4838 return 0;
4841 int vline_node::same(node *nd)
4843 return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n);
4846 const char *vline_node::type()
4848 return "vline_node";
4851 int vline_node::force_tprint()
4853 return 0;
4856 int dummy_node::same(node * /*nd*/)
4858 return 1;
4861 const char *dummy_node::type()
4863 return "dummy_node";
4866 int dummy_node::force_tprint()
4868 return 0;
4871 int transparent_dummy_node::same(node * /*nd*/)
4873 return 1;
4876 const char *transparent_dummy_node::type()
4878 return "transparent_dummy_node";
4881 int transparent_dummy_node::force_tprint()
4883 return 0;
4886 int transparent_dummy_node::ends_sentence()
4888 return 2;
4891 int zero_width_node::same(node *nd)
4893 return same_node_list(n, ((zero_width_node *)nd)->n);
4896 const char *zero_width_node::type()
4898 return "zero_width_node";
4901 int zero_width_node::force_tprint()
4903 return 0;
4906 int italic_corrected_node::same(node *nd)
4908 return (x == ((italic_corrected_node *)nd)->x
4909 && same_node(n, ((italic_corrected_node *)nd)->n));
4912 const char *italic_corrected_node::type()
4914 return "italic_corrected_node";
4917 int italic_corrected_node::force_tprint()
4919 return 0;
4922 left_italic_corrected_node::left_italic_corrected_node(node *x)
4923 : node(x), n(0)
4927 left_italic_corrected_node::~left_italic_corrected_node()
4929 delete n;
4932 node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
4934 if (n == 0) {
4935 hunits lic = gn->left_italic_correction();
4936 if (!lic.is_zero()) {
4937 x = lic;
4938 n = gn;
4939 return this;
4942 else {
4943 node *nd = n->merge_glyph_node(gn);
4944 if (nd) {
4945 n = nd;
4946 x = n->left_italic_correction();
4947 return this;
4950 return 0;
4953 node *left_italic_corrected_node::copy()
4955 left_italic_corrected_node *nd = new left_italic_corrected_node;
4956 if (n) {
4957 nd->n = n->copy();
4958 nd->x = x;
4960 return nd;
4963 void left_italic_corrected_node::tprint(troff_output_file *out)
4965 if (n) {
4966 out->right(x);
4967 n->tprint(out);
4971 const char *left_italic_corrected_node::type()
4973 return "left_italic_corrected_node";
4976 int left_italic_corrected_node::force_tprint()
4978 return 0;
4981 int left_italic_corrected_node::same(node *nd)
4983 return (x == ((left_italic_corrected_node *)nd)->x
4984 && same_node(n, ((left_italic_corrected_node *)nd)->n));
4987 void left_italic_corrected_node::ascii_print(ascii_output_file *out)
4989 if (n)
4990 n->ascii_print(out);
4993 hunits left_italic_corrected_node::width()
4995 return n ? n->width() + x : H0;
4998 void left_italic_corrected_node::vertical_extent(vunits *min, vunits *max)
5000 if (n)
5001 n->vertical_extent(min, max);
5002 else
5003 node::vertical_extent(min, max);
5006 hunits left_italic_corrected_node::skew()
5008 return n ? n->skew() + x/2 : H0;
5011 hunits left_italic_corrected_node::subscript_correction()
5013 return n ? n->subscript_correction() : H0;
5016 hunits left_italic_corrected_node::italic_correction()
5018 return n ? n->italic_correction() : H0;
5021 int left_italic_corrected_node::ends_sentence()
5023 return n ? n->ends_sentence() : 0;
5026 int left_italic_corrected_node::overlaps_horizontally()
5028 return n ? n->overlaps_horizontally() : 0;
5031 int left_italic_corrected_node::overlaps_vertically()
5033 return n ? n->overlaps_vertically() : 0;
5036 node *left_italic_corrected_node::last_char_node()
5038 return n ? n->last_char_node() : 0;
5041 tfont *left_italic_corrected_node::get_tfont()
5043 return n ? n->get_tfont() : 0;
5046 hyphenation_type left_italic_corrected_node::get_hyphenation_type()
5048 if (n)
5049 return n->get_hyphenation_type();
5050 else
5051 return HYPHEN_MIDDLE;
5054 hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail,
5055 int *count)
5057 return n ? n->get_hyphen_list(tail, count) : tail;
5060 node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
5062 if (n) {
5063 nd = new left_italic_corrected_node(nd);
5064 nd = n->add_self(nd, p);
5065 n = 0;
5066 delete this;
5068 return nd;
5071 int left_italic_corrected_node::character_type()
5073 return n ? n->character_type() : 0;
5076 int overstrike_node::same(node *nd)
5078 return same_node_list(list, ((overstrike_node *)nd)->list);
5081 const char *overstrike_node::type()
5083 return "overstrike_node";
5086 int overstrike_node::force_tprint()
5088 return 0;
5091 node *overstrike_node::add_self(node *n, hyphen_list **p)
5093 next = n;
5094 hyphen_list *pp = *p;
5095 *p = (*p)->next;
5096 delete pp;
5097 return this;
5100 hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail, int *)
5102 return new hyphen_list(0, tail);
5105 int bracket_node::same(node *nd)
5107 return same_node_list(list, ((bracket_node *)nd)->list);
5110 const char *bracket_node::type()
5112 return "bracket_node";
5115 int bracket_node::force_tprint()
5117 return 0;
5120 int composite_node::same(node *nd)
5122 return ci == ((composite_node *)nd)->ci
5123 && same_node_list(n, ((composite_node *)nd)->n);
5126 const char *composite_node::type()
5128 return "composite_node";
5131 int composite_node::force_tprint()
5133 return 0;
5136 int glyph_node::same(node *nd)
5138 return ci == ((glyph_node *)nd)->ci
5139 && tf == ((glyph_node *)nd)->tf
5140 && gcol == ((glyph_node *)nd)->gcol
5141 && fcol == ((glyph_node *)nd)->fcol;
5144 const char *glyph_node::type()
5146 return "glyph_node";
5149 int glyph_node::force_tprint()
5151 return 0;
5154 int ligature_node::same(node *nd)
5156 return (same_node(n1, ((ligature_node *)nd)->n1)
5157 && same_node(n2, ((ligature_node *)nd)->n2)
5158 && glyph_node::same(nd));
5161 const char *ligature_node::type()
5163 return "ligature_node";
5166 int ligature_node::force_tprint()
5168 return 0;
5171 int kern_pair_node::same(node *nd)
5173 return (amount == ((kern_pair_node *)nd)->amount
5174 && same_node(n1, ((kern_pair_node *)nd)->n1)
5175 && same_node(n2, ((kern_pair_node *)nd)->n2));
5178 const char *kern_pair_node::type()
5180 return "kern_pair_node";
5183 int kern_pair_node::force_tprint()
5185 return 0;
5188 int dbreak_node::same(node *nd)
5190 return (same_node_list(none, ((dbreak_node *)nd)->none)
5191 && same_node_list(pre, ((dbreak_node *)nd)->pre)
5192 && same_node_list(post, ((dbreak_node *)nd)->post));
5195 const char *dbreak_node::type()
5197 return "dbreak_node";
5200 int dbreak_node::force_tprint()
5202 return 0;
5205 int break_char_node::same(node *nd)
5207 return break_code == ((break_char_node *)nd)->break_code
5208 && col == ((break_char_node *)nd)->col
5209 && same_node(ch, ((break_char_node *)nd)->ch);
5212 const char *break_char_node::type()
5214 return "break_char_node";
5217 int break_char_node::force_tprint()
5219 return 0;
5222 int line_start_node::same(node * /*nd*/)
5224 return 1;
5227 const char *line_start_node::type()
5229 return "line_start_node";
5232 int line_start_node::force_tprint()
5234 return 0;
5237 int space_node::same(node *nd)
5239 return n == ((space_node *)nd)->n
5240 && set == ((space_node *)nd)->set
5241 && col == ((space_node *)nd)->col;
5244 const char *space_node::type()
5246 return "space_node";
5249 int word_space_node::same(node *nd)
5251 return n == ((word_space_node *)nd)->n
5252 && set == ((word_space_node *)nd)->set
5253 && col == ((word_space_node *)nd)->col;
5256 const char *word_space_node::type()
5258 return "word_space_node";
5261 int word_space_node::force_tprint()
5263 return 0;
5266 int unbreakable_space_node::same(node *nd)
5268 return n == ((unbreakable_space_node *)nd)->n
5269 && set == ((unbreakable_space_node *)nd)->set
5270 && col == ((unbreakable_space_node *)nd)->col;
5273 const char *unbreakable_space_node::type()
5275 return "unbreakable_space_node";
5278 node *unbreakable_space_node::add_self(node *n, hyphen_list **p)
5280 next = n;
5281 hyphen_list *pp = *p;
5282 *p = (*p)->next;
5283 delete pp;
5284 return this;
5287 hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail, int *)
5289 return new hyphen_list(0, tail);
5292 int diverted_space_node::same(node *nd)
5294 return n == ((diverted_space_node *)nd)->n;
5297 const char *diverted_space_node::type()
5299 return "diverted_space_node";
5302 int diverted_space_node::force_tprint()
5304 return 0;
5307 int diverted_copy_file_node::same(node *nd)
5309 return filename == ((diverted_copy_file_node *)nd)->filename;
5312 const char *diverted_copy_file_node::type()
5314 return "diverted_copy_file_node";
5317 int diverted_copy_file_node::force_tprint()
5319 return 0;
5322 // Grow the font_table so that its size is > n.
5324 static void grow_font_table(int n)
5326 assert(n >= font_table_size);
5327 font_info **old_font_table = font_table;
5328 int old_font_table_size = font_table_size;
5329 font_table_size = font_table_size ? (font_table_size*3)/2 : 10;
5330 if (font_table_size <= n)
5331 font_table_size = n + 10;
5332 font_table = new font_info *[font_table_size];
5333 if (old_font_table_size)
5334 memcpy(font_table, old_font_table,
5335 old_font_table_size*sizeof(font_info *));
5336 a_delete old_font_table;
5337 for (int i = old_font_table_size; i < font_table_size; i++)
5338 font_table[i] = 0;
5341 dictionary font_translation_dictionary(17);
5343 static symbol get_font_translation(symbol nm)
5345 void *p = font_translation_dictionary.lookup(nm);
5346 return p ? symbol((char *)p) : nm;
5349 dictionary font_dictionary(50);
5351 static int mount_font_no_translate(int n, symbol name, symbol external_name)
5353 assert(n >= 0);
5354 // We store the address of this char in font_dictionary to indicate
5355 // that we've previously tried to mount the font and failed.
5356 static char a_char;
5357 font *fm = 0;
5358 void *p = font_dictionary.lookup(external_name);
5359 if (p == 0) {
5360 int not_found;
5361 fm = font::load_font(external_name.contents(), &not_found);
5362 if (!fm) {
5363 if (not_found)
5364 warning(WARN_FONT, "can't find font `%1'", external_name.contents());
5365 (void)font_dictionary.lookup(external_name, &a_char);
5366 return 0;
5368 (void)font_dictionary.lookup(name, fm);
5370 else if (p == &a_char) {
5371 #if 0
5372 error("invalid font `%1'", external_name.contents());
5373 #endif
5374 return 0;
5376 else
5377 fm = (font*)p;
5378 if (n >= font_table_size) {
5379 if (n - font_table_size > 1000) {
5380 error("font position too much larger than first unused position");
5381 return 0;
5383 grow_font_table(n);
5385 else if (font_table[n] != 0)
5386 delete font_table[n];
5387 font_table[n] = new font_info(name, n, external_name, fm);
5388 font_family::invalidate_fontno(n);
5389 return 1;
5392 int mount_font(int n, symbol name, symbol external_name)
5394 assert(n >= 0);
5395 name = get_font_translation(name);
5396 if (external_name.is_null())
5397 external_name = name;
5398 else
5399 external_name = get_font_translation(external_name);
5400 return mount_font_no_translate(n, name, external_name);
5403 void mount_style(int n, symbol name)
5405 assert(n >= 0);
5406 if (n >= font_table_size) {
5407 if (n - font_table_size > 1000) {
5408 error("font position too much larger than first unused position");
5409 return;
5411 grow_font_table(n);
5413 else if (font_table[n] != 0)
5414 delete font_table[n];
5415 font_table[n] = new font_info(get_font_translation(name), n, NULL_SYMBOL, 0);
5416 font_family::invalidate_fontno(n);
5419 /* global functions */
5421 void font_translate()
5423 symbol from = get_name(1);
5424 if (!from.is_null()) {
5425 symbol to = get_name();
5426 if (to.is_null() || from == to)
5427 font_translation_dictionary.remove(from);
5428 else
5429 (void)font_translation_dictionary.lookup(from, (void *)to.contents());
5431 skip_line();
5434 void font_position()
5436 int n;
5437 if (get_integer(&n)) {
5438 if (n < 0)
5439 error("negative font position");
5440 else {
5441 symbol internal_name = get_name(1);
5442 if (!internal_name.is_null()) {
5443 symbol external_name = get_long_name();
5444 mount_font(n, internal_name, external_name); // ignore error
5448 skip_line();
5451 font_family::font_family(symbol s)
5452 : map_size(10), nm(s)
5454 map = new int[map_size];
5455 for (int i = 0; i < map_size; i++)
5456 map[i] = -1;
5459 font_family::~font_family()
5461 a_delete map;
5464 int font_family::make_definite(int i)
5466 if (i >= 0) {
5467 if (i < map_size && map[i] >= 0)
5468 return map[i];
5469 else {
5470 if (i < font_table_size && font_table[i] != 0) {
5471 if (i >= map_size) {
5472 int old_map_size = map_size;
5473 int *old_map = map;
5474 map_size *= 3;
5475 map_size /= 2;
5476 if (i >= map_size)
5477 map_size = i + 10;
5478 map = new int[map_size];
5479 memcpy(map, old_map, old_map_size*sizeof(int));
5480 a_delete old_map;
5481 for (int j = old_map_size; j < map_size; j++)
5482 map[j] = -1;
5484 if (font_table[i]->is_style()) {
5485 symbol sty = font_table[i]->get_name();
5486 symbol f = concat(nm, sty);
5487 int n;
5488 // don't use symbol_fontno, because that might return a style
5489 // and because we don't want to translate the name
5490 for (n = 0; n < font_table_size; n++)
5491 if (font_table[n] != 0 && font_table[n]->is_named(f)
5492 && !font_table[n]->is_style())
5493 break;
5494 if (n >= font_table_size) {
5495 n = next_available_font_position();
5496 if (!mount_font_no_translate(n, f, f))
5497 return -1;
5499 return map[i] = n;
5501 else
5502 return map[i] = i;
5504 else
5505 return -1;
5508 else
5509 return -1;
5512 dictionary family_dictionary(5);
5514 font_family *lookup_family(symbol nm)
5516 font_family *f = (font_family *)family_dictionary.lookup(nm);
5517 if (!f) {
5518 f = new font_family(nm);
5519 (void)family_dictionary.lookup(nm, f);
5521 return f;
5524 void font_family::invalidate_fontno(int n)
5526 assert(n >= 0 && n < font_table_size);
5527 dictionary_iterator iter(family_dictionary);
5528 symbol nm;
5529 font_family *fam;
5530 while (iter.get(&nm, (void **)&fam)) {
5531 int map_size = fam->map_size;
5532 if (n < map_size)
5533 fam->map[n] = -1;
5534 for (int i = 0; i < map_size; i++)
5535 if (fam->map[i] == n)
5536 fam->map[i] = -1;
5540 void style()
5542 int n;
5543 if (get_integer(&n)) {
5544 if (n < 0)
5545 error("negative font position");
5546 else {
5547 symbol internal_name = get_name(1);
5548 if (!internal_name.is_null())
5549 mount_style(n, internal_name);
5552 skip_line();
5555 static int get_fontno()
5557 int n;
5558 tok.skip();
5559 if (tok.delimiter()) {
5560 symbol s = get_name(1);
5561 if (!s.is_null()) {
5562 n = symbol_fontno(s);
5563 if (n < 0) {
5564 n = next_available_font_position();
5565 if (!mount_font(n, s))
5566 return -1;
5568 return curenv->get_family()->make_definite(n);
5571 else if (get_integer(&n)) {
5572 if (n < 0 || n >= font_table_size || font_table[n] == 0)
5573 error("bad font number");
5574 else
5575 return curenv->get_family()->make_definite(n);
5577 return -1;
5580 static int underline_fontno = 2;
5582 void underline_font()
5584 int n = get_fontno();
5585 if (n >= 0)
5586 underline_fontno = n;
5587 skip_line();
5590 int get_underline_fontno()
5592 return underline_fontno;
5595 void define_font_special_character()
5597 int n = get_fontno();
5598 if (n < 0) {
5599 skip_line();
5600 return;
5602 symbol f = font_table[n]->get_name();
5603 do_define_character(CHAR_FONT_SPECIAL, f.contents());
5606 void remove_font_special_character()
5608 int n = get_fontno();
5609 if (n < 0) {
5610 skip_line();
5611 return;
5613 symbol f = font_table[n]->get_name();
5614 while (!tok.newline() && !tok.eof()) {
5615 if (!tok.space() && !tok.tab()) {
5616 charinfo *s = tok.get_char(1);
5617 string gl(f.contents());
5618 gl += ' ';
5619 gl += s->nm.contents();
5620 gl += '\0';
5621 charinfo *ci = get_charinfo(symbol(gl.contents()));
5622 if (!ci)
5623 break;
5624 macro *m = ci->set_macro(0);
5625 if (m)
5626 delete m;
5628 tok.next();
5630 skip_line();
5633 static void read_special_fonts(special_font_list **sp)
5635 special_font_list *s = *sp;
5636 *sp = 0;
5637 while (s != 0) {
5638 special_font_list *tem = s;
5639 s = s->next;
5640 delete tem;
5642 special_font_list **p = sp;
5643 while (has_arg()) {
5644 int i = get_fontno();
5645 if (i >= 0) {
5646 special_font_list *tem = new special_font_list;
5647 tem->n = i;
5648 tem->next = 0;
5649 *p = tem;
5650 p = &(tem->next);
5655 void font_special_request()
5657 int n = get_fontno();
5658 if (n >= 0)
5659 read_special_fonts(&font_table[n]->sf);
5660 skip_line();
5663 void special_request()
5665 read_special_fonts(&global_special_fonts);
5666 skip_line();
5669 int next_available_font_position()
5671 int i;
5672 for (i = 1; i < font_table_size && font_table[i] != 0; i++)
5674 return i;
5677 int symbol_fontno(symbol s)
5679 s = get_font_translation(s);
5680 for (int i = 0; i < font_table_size; i++)
5681 if (font_table[i] != 0 && font_table[i]->is_named(s))
5682 return i;
5683 return -1;
5686 int is_good_fontno(int n)
5688 return n >= 0 && n < font_table_size && font_table[n] != 0;
5691 int get_bold_fontno(int n)
5693 if (n >= 0 && n < font_table_size && font_table[n] != 0) {
5694 hunits offset;
5695 if (font_table[n]->get_bold(&offset))
5696 return offset.to_units() + 1;
5697 else
5698 return 0;
5700 else
5701 return 0;
5704 hunits env_digit_width(environment *env)
5706 node *n = make_glyph_node(charset_table['0'], env);
5707 if (n) {
5708 hunits x = n->width();
5709 delete n;
5710 return x;
5712 else
5713 return H0;
5716 hunits env_space_width(environment *env)
5718 int fn = env_definite_font(env);
5719 font_size fs = env->get_font_size();
5720 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5721 return scale(fs.to_units()/3, env->get_space_size(), 12);
5722 else
5723 return font_table[fn]->get_space_width(fs, env->get_space_size());
5726 hunits env_sentence_space_width(environment *env)
5728 int fn = env_definite_font(env);
5729 font_size fs = env->get_font_size();
5730 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5731 return scale(fs.to_units()/3, env->get_sentence_space_size(), 12);
5732 else
5733 return font_table[fn]->get_space_width(fs, env->get_sentence_space_size());
5736 hunits env_half_narrow_space_width(environment *env)
5738 int fn = env_definite_font(env);
5739 font_size fs = env->get_font_size();
5740 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5741 return 0;
5742 else
5743 return font_table[fn]->get_half_narrow_space_width(fs);
5746 hunits env_narrow_space_width(environment *env)
5748 int fn = env_definite_font(env);
5749 font_size fs = env->get_font_size();
5750 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5751 return 0;
5752 else
5753 return font_table[fn]->get_narrow_space_width(fs);
5756 void bold_font()
5758 int n = get_fontno();
5759 if (n >= 0) {
5760 if (has_arg()) {
5761 if (tok.delimiter()) {
5762 int f = get_fontno();
5763 if (f >= 0) {
5764 units offset;
5765 if (has_arg() && get_number(&offset, 'u') && offset >= 1)
5766 font_table[f]->set_conditional_bold(n, hunits(offset - 1));
5767 else
5768 font_table[f]->conditional_unbold(n);
5771 else {
5772 units offset;
5773 if (get_number(&offset, 'u') && offset >= 1)
5774 font_table[n]->set_bold(hunits(offset - 1));
5775 else
5776 font_table[n]->unbold();
5779 else
5780 font_table[n]->unbold();
5782 skip_line();
5785 track_kerning_function::track_kerning_function() : non_zero(0)
5789 track_kerning_function::track_kerning_function(int min_s, hunits min_a,
5790 int max_s, hunits max_a)
5791 : non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s),
5792 max_amount(max_a)
5796 int track_kerning_function::operator==(const track_kerning_function &tk)
5798 if (non_zero)
5799 return (tk.non_zero
5800 && min_size == tk.min_size
5801 && min_amount == tk.min_amount
5802 && max_size == tk.max_size
5803 && max_amount == tk.max_amount);
5804 else
5805 return !tk.non_zero;
5808 int track_kerning_function::operator!=(const track_kerning_function &tk)
5810 if (non_zero)
5811 return (!tk.non_zero
5812 || min_size != tk.min_size
5813 || min_amount != tk.min_amount
5814 || max_size != tk.max_size
5815 || max_amount != tk.max_amount);
5816 else
5817 return tk.non_zero;
5820 hunits track_kerning_function::compute(int size)
5822 if (non_zero) {
5823 if (max_size <= min_size)
5824 return min_amount;
5825 else if (size <= min_size)
5826 return min_amount;
5827 else if (size >= max_size)
5828 return max_amount;
5829 else
5830 return (scale(max_amount, size - min_size, max_size - min_size)
5831 + scale(min_amount, max_size - size, max_size - min_size));
5833 else
5834 return H0;
5837 void track_kern()
5839 int n = get_fontno();
5840 if (n >= 0) {
5841 int min_s, max_s;
5842 hunits min_a, max_a;
5843 if (has_arg()
5844 && get_number(&min_s, 'z')
5845 && get_hunits(&min_a, 'p')
5846 && get_number(&max_s, 'z')
5847 && get_hunits(&max_a, 'p')) {
5848 track_kerning_function tk(min_s, min_a, max_s, max_a);
5849 font_table[n]->set_track_kern(tk);
5851 else {
5852 track_kerning_function tk;
5853 font_table[n]->set_track_kern(tk);
5856 skip_line();
5859 void constant_space()
5861 int n = get_fontno();
5862 if (n >= 0) {
5863 int x, y;
5864 if (!has_arg() || !get_integer(&x))
5865 font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
5866 else {
5867 if (!has_arg() || !get_number(&y, 'z'))
5868 font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
5869 else
5870 font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
5871 scale(y*x,
5872 units_per_inch,
5873 36*72*sizescale));
5876 skip_line();
5879 void ligature()
5881 int lig;
5882 if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2)
5883 global_ligature_mode = lig;
5884 else
5885 global_ligature_mode = 1;
5886 skip_line();
5889 void kern_request()
5891 int k;
5892 if (has_arg() && get_integer(&k))
5893 global_kern_mode = k != 0;
5894 else
5895 global_kern_mode = 1;
5896 skip_line();
5899 void set_soft_hyphen_char()
5901 soft_hyphen_char = get_optional_char();
5902 if (!soft_hyphen_char)
5903 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
5904 skip_line();
5907 void init_output()
5909 if (suppress_output_flag)
5910 the_output = new suppress_output_file;
5911 else if (ascii_output_flag)
5912 the_output = new ascii_output_file;
5913 else
5914 the_output = new troff_output_file;
5917 class next_available_font_position_reg : public reg {
5918 public:
5919 const char *get_string();
5922 const char *next_available_font_position_reg::get_string()
5924 return i_to_a(next_available_font_position());
5927 class printing_reg : public reg {
5928 public:
5929 const char *get_string();
5932 const char *printing_reg::get_string()
5934 if (the_output)
5935 return the_output->is_printing() ? "1" : "0";
5936 else
5937 return "0";
5940 void init_node_requests()
5942 init_request("bd", bold_font);
5943 init_request("cs", constant_space);
5944 init_request("fp", font_position);
5945 init_request("fschar", define_font_special_character);
5946 init_request("fspecial", font_special_request);
5947 init_request("ftr", font_translate);
5948 init_request("kern", kern_request);
5949 init_request("lg", ligature);
5950 init_request("rfschar", remove_font_special_character);
5951 init_request("shc", set_soft_hyphen_char);
5952 init_request("special", special_request);
5953 init_request("sty", style);
5954 init_request("tkf", track_kern);
5955 init_request("uf", underline_font);
5956 number_reg_dictionary.define(".fp", new next_available_font_position_reg);
5957 number_reg_dictionary.define(".kern",
5958 new constant_int_reg(&global_kern_mode));
5959 number_reg_dictionary.define(".lg",
5960 new constant_int_reg(&global_ligature_mode));
5961 number_reg_dictionary.define(".P", new printing_reg);
5962 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);