* src/roff/groff/groff.cpp (help), src/devices/grops/ps.cpp (usage),
[s-roff.git] / src / roff / troff / node.cpp
blob320358fe3708fe913fa48da23d64547eb2be732f
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; }
790 friend void space_char_hmotion_node::tprint(troff_output_file *);
791 friend void unbreakable_space_node::tprint(troff_output_file *);
794 static void put_string(const char *s, FILE *fp)
796 for (; *s != '\0'; ++s)
797 putc(*s, fp);
800 inline void troff_output_file::put(char c)
802 putc(c, fp);
805 inline void troff_output_file::put(unsigned char c)
807 putc(c, fp);
810 inline void troff_output_file::put(const char *s)
812 put_string(s, fp);
815 inline void troff_output_file::put(int i)
817 put_string(i_to_a(i), fp);
820 inline void troff_output_file::put(unsigned int i)
822 put_string(ui_to_a(i), fp);
825 void troff_output_file::start_special(tfont *tf, color *gcol, color *fcol,
826 int no_init_string)
828 set_font(tf);
829 glyph_color(gcol);
830 fill_color(fcol);
831 flush_tbuf();
832 do_motion();
833 if (!no_init_string)
834 put("x X ");
837 void troff_output_file::start_special()
839 flush_tbuf();
840 do_motion();
841 put("x X ");
844 void troff_output_file::special_char(unsigned char c)
846 put(c);
847 if (c == '\n')
848 put('+');
851 void troff_output_file::end_special()
853 put('\n');
856 inline void troff_output_file::moveto(hunits h, vunits v)
858 hpos = h.to_units();
859 vpos = v.to_units();
862 void troff_output_file::really_print_line(hunits x, vunits y, node *n,
863 vunits before, vunits after, hunits)
865 moveto(x, y);
866 while (n != 0) {
867 n->tprint(this);
868 n = n->next;
870 flush_tbuf();
871 // This ensures that transparent throughput will have a more predictable
872 // position.
873 do_motion();
874 force_motion = 1;
875 hpos = 0;
876 put('n');
877 put(before.to_units());
878 put(' ');
879 put(after.to_units());
880 put('\n');
883 inline void troff_output_file::word_marker()
885 flush_tbuf();
886 if (is_on())
887 put('w');
890 inline void troff_output_file::right(hunits n)
892 hpos += n.to_units();
895 inline void troff_output_file::down(vunits n)
897 vpos += n.to_units();
900 void troff_output_file::do_motion()
902 if (force_motion) {
903 put('V');
904 put(vpos);
905 put('\n');
906 put('H');
907 put(hpos);
908 put('\n');
910 else {
911 if (hpos != output_hpos) {
912 units n = hpos - output_hpos;
913 if (n > 0 && n < hpos) {
914 put('h');
915 put(n);
917 else {
918 put('H');
919 put(hpos);
921 put('\n');
923 if (vpos != output_vpos) {
924 units n = vpos - output_vpos;
925 if (n > 0 && n < vpos) {
926 put('v');
927 put(n);
929 else {
930 put('V');
931 put(vpos);
933 put('\n');
936 output_vpos = vpos;
937 output_hpos = hpos;
938 force_motion = 0;
941 void troff_output_file::flush_tbuf()
943 if (!is_on()) {
944 tbuf_len = 0;
945 return;
948 if (tbuf_len == 0)
949 return;
950 if (tbuf_kern == 0)
951 put('t');
952 else {
953 put('u');
954 put(tbuf_kern);
955 put(' ');
957 check_output_limits(hpos, vpos);
958 check_output_limits(hpos, vpos - current_size);
960 for (int i = 0; i < tbuf_len; i++)
961 put(tbuf[i]);
962 put('\n');
963 tbuf_len = 0;
966 void troff_output_file::check_charinfo(tfont *tf, charinfo *ci)
968 if (!is_on())
969 return;
971 int height = tf->get_char_height(ci).to_units();
972 int width = tf->get_width(ci).to_units()
973 + tf->get_italic_correction(ci).to_units();
974 int depth = tf->get_char_depth(ci).to_units();
975 check_output_limits(output_hpos, output_vpos - height);
976 check_output_limits(output_hpos + width, output_vpos + depth);
979 void troff_output_file::put_char_width(charinfo *ci, tfont *tf,
980 color *gcol, color *fcol,
981 hunits w, hunits k)
983 int kk = k.to_units();
984 if (!is_on()) {
985 flush_tbuf();
986 hpos += w.to_units() + kk;
987 return;
989 set_font(tf);
990 char c = ci->get_ascii_code();
991 if (c == '\0') {
992 glyph_color(gcol);
993 fill_color(fcol);
994 flush_tbuf();
995 do_motion();
996 check_charinfo(tf, ci);
997 if (ci->numbered()) {
998 put('N');
999 put(ci->get_number());
1001 else {
1002 put('C');
1003 const char *s = ci->nm.contents();
1004 if (s[1] == 0) {
1005 put('\\');
1006 put(s[0]);
1008 else
1009 put(s);
1011 put('\n');
1012 hpos += w.to_units() + kk;
1014 else if (tcommand_flag) {
1015 if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
1016 && (!gcol || gcol == current_glyph_color)
1017 && (!fcol || fcol == current_fill_color)
1018 && kk == tbuf_kern
1019 && tbuf_len < TBUF_SIZE) {
1020 check_charinfo(tf, ci);
1021 tbuf[tbuf_len++] = c;
1022 output_hpos += w.to_units() + kk;
1023 hpos = output_hpos;
1024 return;
1026 glyph_color(gcol);
1027 fill_color(fcol);
1028 flush_tbuf();
1029 do_motion();
1030 check_charinfo(tf, ci);
1031 tbuf[tbuf_len++] = c;
1032 output_hpos += w.to_units() + kk;
1033 tbuf_kern = kk;
1034 hpos = output_hpos;
1036 else {
1037 // flush_tbuf();
1038 int n = hpos - output_hpos;
1039 check_charinfo(tf, ci);
1040 // check_output_limits(output_hpos, output_vpos);
1041 if (vpos == output_vpos
1042 && (!gcol || gcol == current_glyph_color)
1043 && (!fcol || fcol == current_fill_color)
1044 && n > 0 && n < 100 && !force_motion) {
1045 put(char(n/10 + '0'));
1046 put(char(n%10 + '0'));
1047 put(c);
1048 output_hpos = hpos;
1050 else {
1051 glyph_color(gcol);
1052 fill_color(fcol);
1053 do_motion();
1054 put('c');
1055 put(c);
1057 hpos += w.to_units() + kk;
1061 void troff_output_file::put_char(charinfo *ci, tfont *tf,
1062 color *gcol, color *fcol)
1064 flush_tbuf();
1065 if (!is_on())
1066 return;
1067 set_font(tf);
1068 char c = ci->get_ascii_code();
1069 if (c == '\0') {
1070 glyph_color(gcol);
1071 fill_color(fcol);
1072 flush_tbuf();
1073 do_motion();
1074 if (ci->numbered()) {
1075 put('N');
1076 put(ci->get_number());
1078 else {
1079 put('C');
1080 const char *s = ci->nm.contents();
1081 if (s[1] == 0) {
1082 put('\\');
1083 put(s[0]);
1085 else
1086 put(s);
1088 put('\n');
1090 else {
1091 int n = hpos - output_hpos;
1092 if (vpos == output_vpos
1093 && (!gcol || gcol == current_glyph_color)
1094 && (!fcol || fcol == current_fill_color)
1095 && n > 0 && n < 100) {
1096 put(char(n/10 + '0'));
1097 put(char(n%10 + '0'));
1098 put(c);
1099 output_hpos = hpos;
1101 else {
1102 glyph_color(gcol);
1103 fill_color(fcol);
1104 flush_tbuf();
1105 do_motion();
1106 put('c');
1107 put(c);
1112 // set_font calls `flush_tbuf' if necessary.
1114 void troff_output_file::set_font(tfont *tf)
1116 if (current_tfont == tf)
1117 return;
1118 flush_tbuf();
1119 int n = tf->get_input_position();
1120 symbol nm = tf->get_name();
1121 if (n >= nfont_positions || font_position[n] != nm) {
1122 put("x font ");
1123 put(n);
1124 put(' ');
1125 put(nm.contents());
1126 put('\n');
1127 if (n >= nfont_positions) {
1128 int old_nfont_positions = nfont_positions;
1129 symbol *old_font_position = font_position;
1130 nfont_positions *= 3;
1131 nfont_positions /= 2;
1132 if (nfont_positions <= n)
1133 nfont_positions = n + 10;
1134 font_position = new symbol[nfont_positions];
1135 memcpy(font_position, old_font_position,
1136 old_nfont_positions*sizeof(symbol));
1137 a_delete old_font_position;
1139 font_position[n] = nm;
1141 if (current_font_number != n) {
1142 put('f');
1143 put(n);
1144 put('\n');
1145 current_font_number = n;
1147 int size = tf->get_size().to_scaled_points();
1148 if (current_size != size) {
1149 put('s');
1150 put(size);
1151 put('\n');
1152 current_size = size;
1154 int slant = tf->get_slant();
1155 if (current_slant != slant) {
1156 put("x Slant ");
1157 put(slant);
1158 put('\n');
1159 current_slant = slant;
1161 int height = tf->get_height();
1162 if (current_height != height) {
1163 put("x Height ");
1164 put(height == 0 ? current_size : height);
1165 put('\n');
1166 current_height = height;
1168 current_tfont = tf;
1171 // fill_color calls `flush_tbuf' and `do_motion' if necessary.
1173 void troff_output_file::fill_color(color *col)
1175 if (!col || current_fill_color == col)
1176 return;
1177 current_fill_color = col;
1178 if (!color_flag)
1179 return;
1180 flush_tbuf();
1181 do_motion();
1182 put("DF");
1183 unsigned int components[4];
1184 color_scheme cs;
1185 cs = col->get_components(components);
1186 switch (cs) {
1187 case DEFAULT:
1188 put('d');
1189 break;
1190 case RGB:
1191 put("r ");
1192 put(Red);
1193 put(' ');
1194 put(Green);
1195 put(' ');
1196 put(Blue);
1197 break;
1198 case CMY:
1199 put("c ");
1200 put(Cyan);
1201 put(' ');
1202 put(Magenta);
1203 put(' ');
1204 put(Yellow);
1205 break;
1206 case CMYK:
1207 put("k ");
1208 put(Cyan);
1209 put(' ');
1210 put(Magenta);
1211 put(' ');
1212 put(Yellow);
1213 put(' ');
1214 put(Black);
1215 break;
1216 case GRAY:
1217 put("g ");
1218 put(Gray);
1219 break;
1221 put('\n');
1224 // glyph_color calls `flush_tbuf' and `do_motion' if necessary.
1226 void troff_output_file::glyph_color(color *col)
1228 if (!col || current_glyph_color == col)
1229 return;
1230 current_glyph_color = col;
1231 if (!color_flag)
1232 return;
1233 flush_tbuf();
1234 // grotty doesn't like a color command if the vertical position is zero.
1235 do_motion();
1236 put("m");
1237 unsigned int components[4];
1238 color_scheme cs;
1239 cs = col->get_components(components);
1240 switch (cs) {
1241 case DEFAULT:
1242 put('d');
1243 break;
1244 case RGB:
1245 put("r ");
1246 put(Red);
1247 put(' ');
1248 put(Green);
1249 put(' ');
1250 put(Blue);
1251 break;
1252 case CMY:
1253 put("c ");
1254 put(Cyan);
1255 put(' ');
1256 put(Magenta);
1257 put(' ');
1258 put(Yellow);
1259 break;
1260 case CMYK:
1261 put("k ");
1262 put(Cyan);
1263 put(' ');
1264 put(Magenta);
1265 put(' ');
1266 put(Yellow);
1267 put(' ');
1268 put(Black);
1269 break;
1270 case GRAY:
1271 put("g ");
1272 put(Gray);
1273 break;
1275 put('\n');
1278 // determine_line_limits - works out the smallest box which will contain
1279 // the entity, code, built from the point array.
1280 void troff_output_file::determine_line_limits(char code, hvpair *point,
1281 int npoints)
1283 int i, x, y;
1285 if (!is_on())
1286 return;
1288 switch (code) {
1289 case 'c':
1290 case 'C':
1291 // only the h field is used when defining a circle
1292 check_output_limits(output_hpos,
1293 output_vpos - point[0].h.to_units()/2);
1294 check_output_limits(output_hpos + point[0].h.to_units(),
1295 output_vpos + point[0].h.to_units()/2);
1296 break;
1297 case 'E':
1298 case 'e':
1299 check_output_limits(output_hpos,
1300 output_vpos - point[0].v.to_units()/2);
1301 check_output_limits(output_hpos + point[0].h.to_units(),
1302 output_vpos + point[0].v.to_units()/2);
1303 break;
1304 case 'P':
1305 case 'p':
1306 x = output_hpos;
1307 y = output_vpos;
1308 check_output_limits(x, y);
1309 for (i = 0; i < npoints; i++) {
1310 x += point[i].h.to_units();
1311 y += point[i].v.to_units();
1312 check_output_limits(x, y);
1314 break;
1315 case 't':
1316 x = output_hpos;
1317 y = output_vpos;
1318 for (i = 0; i < npoints; i++) {
1319 x += point[i].h.to_units();
1320 y += point[i].v.to_units();
1321 check_output_limits(x, y);
1323 break;
1324 case 'a':
1325 double c[2];
1326 int p[4];
1327 int minx, miny, maxx, maxy;
1328 x = output_hpos;
1329 y = output_vpos;
1330 p[0] = point[0].h.to_units();
1331 p[1] = point[0].v.to_units();
1332 p[2] = point[1].h.to_units();
1333 p[3] = point[1].v.to_units();
1334 if (adjust_arc_center(p, c)) {
1335 check_output_arc_limits(x, y,
1336 p[0], p[1], p[2], p[3],
1337 c[0], c[1],
1338 &minx, &maxx, &miny, &maxy);
1339 check_output_limits(minx, miny);
1340 check_output_limits(maxx, maxy);
1341 break;
1343 // fall through
1344 case 'l':
1345 x = output_hpos;
1346 y = output_vpos;
1347 check_output_limits(x, y);
1348 for (i = 0; i < npoints; i++) {
1349 x += point[i].h.to_units();
1350 y += point[i].v.to_units();
1351 check_output_limits(x, y);
1353 break;
1354 default:
1355 x = output_hpos;
1356 y = output_vpos;
1357 for (i = 0; i < npoints; i++) {
1358 x += point[i].h.to_units();
1359 y += point[i].v.to_units();
1360 check_output_limits(x, y);
1365 void troff_output_file::draw(char code, hvpair *point, int npoints,
1366 font_size fsize, color *gcol, color *fcol)
1368 int i;
1369 glyph_color(gcol);
1370 fill_color(fcol);
1371 flush_tbuf();
1372 do_motion();
1373 if (is_on()) {
1374 int size = fsize.to_scaled_points();
1375 if (current_size != size) {
1376 put('s');
1377 put(size);
1378 put('\n');
1379 current_size = size;
1380 current_tfont = 0;
1382 put('D');
1383 put(code);
1384 if (code == 'c') {
1385 put(' ');
1386 put(point[0].h.to_units());
1388 else
1389 for (i = 0; i < npoints; i++) {
1390 put(' ');
1391 put(point[i].h.to_units());
1392 put(' ');
1393 put(point[i].v.to_units());
1395 determine_line_limits(code, point, npoints);
1398 for (i = 0; i < npoints; i++)
1399 output_hpos += point[i].h.to_units();
1400 hpos = output_hpos;
1401 if (code != 'e') {
1402 for (i = 0; i < npoints; i++)
1403 output_vpos += point[i].v.to_units();
1404 vpos = output_vpos;
1406 if (is_on())
1407 put('\n');
1410 void troff_output_file::really_on()
1412 flush_tbuf();
1413 force_motion = 1;
1414 do_motion();
1417 void troff_output_file::really_off()
1419 flush_tbuf();
1422 void troff_output_file::really_put_filename(const char *filename)
1424 flush_tbuf();
1425 put("F ");
1426 put(filename);
1427 put('\n');
1430 void troff_output_file::really_begin_page(int pageno, vunits page_length)
1432 flush_tbuf();
1433 if (begun_page) {
1434 if (page_length > V0) {
1435 put('V');
1436 put(page_length.to_units());
1437 put('\n');
1440 else
1441 begun_page = 1;
1442 current_tfont = 0;
1443 current_font_number = -1;
1444 current_size = 0;
1445 // current_height = 0;
1446 // current_slant = 0;
1447 hpos = 0;
1448 vpos = 0;
1449 output_hpos = 0;
1450 output_vpos = 0;
1451 force_motion = 1;
1452 for (int i = 0; i < nfont_positions; i++)
1453 font_position[i] = NULL_SYMBOL;
1454 put('p');
1455 put(pageno);
1456 put('\n');
1459 void troff_output_file::really_copy_file(hunits x, vunits y,
1460 const char *filename)
1462 moveto(x, y);
1463 flush_tbuf();
1464 do_motion();
1465 errno = 0;
1466 FILE *ifp = include_search_path.open_file_cautious(filename);
1467 if (ifp == 0)
1468 error("can't open `%1': %2", filename, strerror(errno));
1469 else {
1470 int c;
1471 while ((c = getc(ifp)) != EOF)
1472 put(char(c));
1473 fclose(ifp);
1475 force_motion = 1;
1476 current_size = 0;
1477 current_tfont = 0;
1478 current_font_number = -1;
1479 for (int i = 0; i < nfont_positions; i++)
1480 font_position[i] = NULL_SYMBOL;
1483 void troff_output_file::really_transparent_char(unsigned char c)
1485 put(c);
1488 troff_output_file::~troff_output_file()
1490 a_delete font_position;
1493 void troff_output_file::trailer(vunits page_length)
1495 flush_tbuf();
1496 if (page_length > V0) {
1497 put("x trailer\n");
1498 put('V');
1499 put(page_length.to_units());
1500 put('\n');
1502 put("x stop\n");
1505 troff_output_file::troff_output_file()
1506 : current_slant(0), current_height(0), current_fill_color(0),
1507 current_glyph_color(0), nfont_positions(10), tbuf_len(0), begun_page(0)
1509 font_position = new symbol[nfont_positions];
1510 put("x T ");
1511 put(device);
1512 put('\n');
1513 put("x res ");
1514 put(units_per_inch);
1515 put(' ');
1516 put(hresolution);
1517 put(' ');
1518 put(vresolution);
1519 put('\n');
1520 put("x init\n");
1523 /* output_file */
1525 output_file *the_output = 0;
1527 output_file::output_file()
1531 output_file::~output_file()
1535 void output_file::trailer(vunits)
1539 void output_file::put_filename(const char *)
1543 void output_file::on()
1547 void output_file::off()
1551 real_output_file::real_output_file()
1552 : printing(0), output_on(1)
1554 #ifndef POPEN_MISSING
1555 if (pipe_command) {
1556 if ((fp = popen(pipe_command, POPEN_WT)) != 0) {
1557 piped = 1;
1558 return;
1560 error("pipe open failed: %1", strerror(errno));
1562 piped = 0;
1563 #endif /* not POPEN_MISSING */
1564 fp = stdout;
1567 real_output_file::~real_output_file()
1569 if (!fp)
1570 return;
1571 // To avoid looping, set fp to 0 before calling fatal().
1572 if (ferror(fp) || fflush(fp) < 0) {
1573 fp = 0;
1574 fatal("error writing output file");
1576 #ifndef POPEN_MISSING
1577 if (piped) {
1578 int result = pclose(fp);
1579 fp = 0;
1580 if (result < 0)
1581 fatal("pclose failed");
1582 if (!WIFEXITED(result))
1583 error("output process `%1' got fatal signal %2",
1584 pipe_command,
1585 WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
1586 else {
1587 int exit_status = WEXITSTATUS(result);
1588 if (exit_status != 0)
1589 error("output process `%1' exited with status %2",
1590 pipe_command, exit_status);
1593 else
1594 #endif /* not POPEN MISSING */
1595 if (fclose(fp) < 0) {
1596 fp = 0;
1597 fatal("error closing output file");
1601 void real_output_file::flush()
1603 if (fflush(fp) < 0)
1604 fatal("error writing output file");
1607 int real_output_file::is_printing()
1609 return printing;
1612 void real_output_file::begin_page(int pageno, vunits page_length)
1614 printing = in_output_page_list(pageno);
1615 if (printing)
1616 really_begin_page(pageno, page_length);
1619 void real_output_file::copy_file(hunits x, vunits y, const char *filename)
1621 if (printing && output_on)
1622 really_copy_file(x, y, filename);
1623 check_output_limits(x.to_units(), y.to_units());
1626 void real_output_file::transparent_char(unsigned char c)
1628 if (printing && output_on)
1629 really_transparent_char(c);
1632 void real_output_file::print_line(hunits x, vunits y, node *n,
1633 vunits before, vunits after, hunits width)
1635 if (printing)
1636 really_print_line(x, y, n, before, after, width);
1637 delete_node_list(n);
1640 void real_output_file::really_copy_file(hunits, vunits, const char *)
1642 // do nothing
1645 void real_output_file::put_filename(const char *filename)
1647 really_put_filename(filename);
1650 void real_output_file::really_put_filename(const char *)
1654 void real_output_file::on()
1656 really_on();
1657 if (output_on == 0)
1658 output_on = 1;
1661 void real_output_file::off()
1663 really_off();
1664 output_on = 0;
1667 int real_output_file::is_on()
1669 return output_on;
1672 void real_output_file::really_on()
1676 void real_output_file::really_off()
1680 /* ascii_output_file */
1682 void ascii_output_file::really_transparent_char(unsigned char c)
1684 putc(c, fp);
1687 void ascii_output_file::really_print_line(hunits, vunits, node *n,
1688 vunits, vunits, hunits)
1690 while (n != 0) {
1691 n->ascii_print(this);
1692 n = n->next;
1694 fputc('\n', fp);
1697 void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/)
1699 fputs("<beginning of page>\n", fp);
1702 ascii_output_file::ascii_output_file()
1706 /* suppress_output_file */
1708 suppress_output_file::suppress_output_file()
1712 void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits, hunits)
1716 void suppress_output_file::really_begin_page(int, vunits)
1720 void suppress_output_file::really_transparent_char(unsigned char)
1724 /* glyphs, ligatures, kerns, discretionary breaks */
1726 class charinfo_node : public node {
1727 protected:
1728 charinfo *ci;
1729 public:
1730 charinfo_node(charinfo *, node * = 0);
1731 int ends_sentence();
1732 int overlaps_vertically();
1733 int overlaps_horizontally();
1736 charinfo_node::charinfo_node(charinfo *c, node *x)
1737 : node(x), ci(c)
1741 int charinfo_node::ends_sentence()
1743 if (ci->ends_sentence())
1744 return 1;
1745 else if (ci->transparent())
1746 return 2;
1747 else
1748 return 0;
1751 int charinfo_node::overlaps_horizontally()
1753 return ci->overlaps_horizontally();
1756 int charinfo_node::overlaps_vertically()
1758 return ci->overlaps_vertically();
1761 class glyph_node : public charinfo_node {
1762 static glyph_node *free_list;
1763 protected:
1764 tfont *tf;
1765 color *gcol;
1766 color *fcol; /* this is needed for grotty */
1767 #ifdef STORE_WIDTH
1768 hunits wid;
1769 glyph_node(charinfo *, tfont *, color *, color *, hunits, node * = 0);
1770 #endif
1771 public:
1772 void *operator new(size_t);
1773 void operator delete(void *);
1774 glyph_node(charinfo *, tfont *, color *, color *, node * = 0);
1775 ~glyph_node() {}
1776 node *copy();
1777 node *merge_glyph_node(glyph_node *);
1778 node *merge_self(node *);
1779 hunits width();
1780 node *last_char_node();
1781 units size();
1782 void vertical_extent(vunits *, vunits *);
1783 hunits subscript_correction();
1784 hunits italic_correction();
1785 hunits left_italic_correction();
1786 hunits skew();
1787 hyphenation_type get_hyphenation_type();
1788 tfont *get_tfont();
1789 color *get_glyph_color();
1790 color *get_fill_color();
1791 void tprint(troff_output_file *);
1792 void zero_width_tprint(troff_output_file *);
1793 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1794 node *add_self(node *, hyphen_list **);
1795 void ascii_print(ascii_output_file *);
1796 void asciify(macro *);
1797 int character_type();
1798 int same(node *);
1799 const char *type();
1800 int force_tprint();
1803 glyph_node *glyph_node::free_list = 0;
1805 class ligature_node : public glyph_node {
1806 node *n1;
1807 node *n2;
1808 #ifdef STORE_WIDTH
1809 ligature_node(charinfo *, tfont *, color *, color *, hunits,
1810 node *, node *, node * = 0);
1811 #endif
1812 public:
1813 void *operator new(size_t);
1814 void operator delete(void *);
1815 ligature_node(charinfo *, tfont *, color *, color *,
1816 node *, node *, node * = 0);
1817 ~ligature_node();
1818 node *copy();
1819 node *add_self(node *, hyphen_list **);
1820 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1821 void ascii_print(ascii_output_file *);
1822 void asciify(macro *);
1823 int same(node *);
1824 const char *type();
1825 int force_tprint();
1828 class kern_pair_node : public node {
1829 hunits amount;
1830 node *n1;
1831 node *n2;
1832 public:
1833 kern_pair_node(hunits n, node *first, node *second, node *x = 0);
1834 ~kern_pair_node();
1835 node *copy();
1836 node *merge_glyph_node(glyph_node *);
1837 node *add_self(node *, hyphen_list **);
1838 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1839 node *add_discretionary_hyphen();
1840 hunits width();
1841 node *last_char_node();
1842 hunits italic_correction();
1843 hunits subscript_correction();
1844 void tprint(troff_output_file *);
1845 hyphenation_type get_hyphenation_type();
1846 int ends_sentence();
1847 void ascii_print(ascii_output_file *);
1848 void asciify(macro *);
1849 int same(node *);
1850 const char *type();
1851 int force_tprint();
1852 void vertical_extent(vunits *, vunits *);
1855 class dbreak_node : public node {
1856 node *none;
1857 node *pre;
1858 node *post;
1859 public:
1860 dbreak_node(node *n, node *p, node *x = 0);
1861 ~dbreak_node();
1862 node *copy();
1863 node *merge_glyph_node(glyph_node *);
1864 node *add_discretionary_hyphen();
1865 hunits width();
1866 node *last_char_node();
1867 hunits italic_correction();
1868 hunits subscript_correction();
1869 void tprint(troff_output_file *);
1870 breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
1871 int is_inner = 0);
1872 int nbreaks();
1873 int ends_sentence();
1874 void split(int, node **, node **);
1875 hyphenation_type get_hyphenation_type();
1876 void ascii_print(ascii_output_file *);
1877 void asciify(macro *);
1878 int same(node *);
1879 const char *type();
1880 int force_tprint();
1883 void *glyph_node::operator new(size_t n)
1885 assert(n == sizeof(glyph_node));
1886 if (!free_list) {
1887 const int BLOCK = 1024;
1888 free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
1889 for (int i = 0; i < BLOCK - 1; i++)
1890 free_list[i].next = free_list + i + 1;
1891 free_list[BLOCK-1].next = 0;
1893 glyph_node *p = free_list;
1894 free_list = (glyph_node *)(free_list->next);
1895 p->next = 0;
1896 return p;
1899 void *ligature_node::operator new(size_t n)
1901 return new char[n];
1904 void glyph_node::operator delete(void *p)
1906 if (p) {
1907 ((glyph_node *)p)->next = free_list;
1908 free_list = (glyph_node *)p;
1912 void ligature_node::operator delete(void *p)
1914 delete[] (char *)p;
1917 glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc, node *x)
1918 : charinfo_node(c, x), tf(t), gcol(gc), fcol(fc)
1920 #ifdef STORE_WIDTH
1921 wid = tf->get_width(ci);
1922 #endif
1925 #ifdef STORE_WIDTH
1926 glyph_node::glyph_node(charinfo *c, tfont *t,
1927 color *gc, color *fc, hunits w, node *x)
1928 : charinfo_node(c, x), tf(t), gcol(gc), fcol(fc), wid(w)
1931 #endif
1933 node *glyph_node::copy()
1935 #ifdef STORE_WIDTH
1936 return new glyph_node(ci, tf, gcol, fcol, wid);
1937 #else
1938 return new glyph_node(ci, tf, gcol, fcol);
1939 #endif
1942 node *glyph_node::merge_self(node *nd)
1944 return nd->merge_glyph_node(this);
1947 int glyph_node::character_type()
1949 return tf->get_character_type(ci);
1952 node *glyph_node::add_self(node *n, hyphen_list **p)
1954 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
1955 next = 0;
1956 node *nn;
1957 if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
1958 next = n;
1959 nn = this;
1961 if ((*p)->hyphen)
1962 nn = nn->add_discretionary_hyphen();
1963 hyphen_list *pp = *p;
1964 *p = (*p)->next;
1965 delete pp;
1966 return nn;
1969 units glyph_node::size()
1971 return tf->get_size().to_units();
1974 hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count)
1976 (*count)++;
1977 return new hyphen_list(ci->get_hyphenation_code(), tail);
1980 tfont *node::get_tfont()
1982 return 0;
1985 tfont *glyph_node::get_tfont()
1987 return tf;
1990 color *node::get_glyph_color()
1992 return 0;
1995 color *glyph_node::get_glyph_color()
1997 return gcol;
2000 color *node::get_fill_color()
2002 return 0;
2005 color *glyph_node::get_fill_color()
2007 return fcol;
2010 node *node::merge_glyph_node(glyph_node *)
2012 return 0;
2015 node *glyph_node::merge_glyph_node(glyph_node *gn)
2017 if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
2018 charinfo *lig;
2019 if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
2020 node *next1 = next;
2021 next = 0;
2022 return new ligature_node(lig, tf, gcol, fcol, this, gn, next1);
2024 hunits kern;
2025 if (tf->get_kern(ci, gn->ci, &kern)) {
2026 node *next1 = next;
2027 next = 0;
2028 return new kern_pair_node(kern, this, gn, next1);
2031 return 0;
2034 #ifdef STORE_WIDTH
2035 inline
2036 #endif
2037 hunits glyph_node::width()
2039 #ifdef STORE_WIDTH
2040 return wid;
2041 #else
2042 return tf->get_width(ci);
2043 #endif
2046 node *glyph_node::last_char_node()
2048 return this;
2051 void glyph_node::vertical_extent(vunits *min, vunits *max)
2053 *min = -tf->get_char_height(ci);
2054 *max = tf->get_char_depth(ci);
2057 hunits glyph_node::skew()
2059 return tf->get_char_skew(ci);
2062 hunits glyph_node::subscript_correction()
2064 return tf->get_subscript_correction(ci);
2067 hunits glyph_node::italic_correction()
2069 return tf->get_italic_correction(ci);
2072 hunits glyph_node::left_italic_correction()
2074 return tf->get_left_italic_correction(ci);
2077 hyphenation_type glyph_node::get_hyphenation_type()
2079 return HYPHEN_MIDDLE;
2082 void glyph_node::ascii_print(ascii_output_file *ascii)
2084 unsigned char c = ci->get_ascii_code();
2085 if (c != 0)
2086 ascii->outc(c);
2087 else
2088 ascii->outs(ci->nm.contents());
2091 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2092 node *gn1, node *gn2, node *x)
2093 : glyph_node(c, t, gc, fc, x), n1(gn1), n2(gn2)
2097 #ifdef STORE_WIDTH
2098 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2099 hunits w, node *gn1, node *gn2, node *x)
2100 : glyph_node(c, t, gc, fc, w, x), n1(gn1), n2(gn2)
2103 #endif
2105 ligature_node::~ligature_node()
2107 delete n1;
2108 delete n2;
2111 node *ligature_node::copy()
2113 #ifdef STORE_WIDTH
2114 return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy());
2115 #else
2116 return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy());
2117 #endif
2120 void ligature_node::ascii_print(ascii_output_file *ascii)
2122 n1->ascii_print(ascii);
2123 n2->ascii_print(ascii);
2126 hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail, int *count)
2128 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2129 return n1->get_hyphen_list(hl, count);
2132 node *ligature_node::add_self(node *n, hyphen_list **p)
2134 n = n1->add_self(n, p);
2135 n = n2->add_self(n, p);
2136 n1 = n2 = 0;
2137 delete this;
2138 return n;
2141 kern_pair_node::kern_pair_node(hunits n, node *first, node *second, node *x)
2142 : node(x), amount(n), n1(first), n2(second)
2146 dbreak_node::dbreak_node(node *n, node *p, node *x)
2147 : node(x), none(n), pre(p), post(0)
2151 node *dbreak_node::merge_glyph_node(glyph_node *gn)
2153 glyph_node *gn2 = (glyph_node *)gn->copy();
2154 node *new_none = none ? none->merge_glyph_node(gn) : 0;
2155 node *new_post = post ? post->merge_glyph_node(gn2) : 0;
2156 if (new_none == 0 && new_post == 0) {
2157 delete gn2;
2158 return 0;
2160 if (new_none != 0)
2161 none = new_none;
2162 else {
2163 gn->next = none;
2164 none = gn;
2166 if (new_post != 0)
2167 post = new_post;
2168 else {
2169 gn2->next = post;
2170 post = gn2;
2172 return this;
2175 node *kern_pair_node::merge_glyph_node(glyph_node *gn)
2177 node *nd = n2->merge_glyph_node(gn);
2178 if (nd == 0)
2179 return 0;
2180 n2 = nd;
2181 nd = n2->merge_self(n1);
2182 if (nd) {
2183 nd->next = next;
2184 n1 = 0;
2185 n2 = 0;
2186 delete this;
2187 return nd;
2189 return this;
2192 hunits kern_pair_node::italic_correction()
2194 return n2->italic_correction();
2197 hunits kern_pair_node::subscript_correction()
2199 return n2->subscript_correction();
2202 void kern_pair_node::vertical_extent(vunits *min, vunits *max)
2204 n1->vertical_extent(min, max);
2205 vunits min2, max2;
2206 n2->vertical_extent(&min2, &max2);
2207 if (min2 < *min)
2208 *min = min2;
2209 if (max2 > *max)
2210 *max = max2;
2213 node *kern_pair_node::add_discretionary_hyphen()
2215 tfont *tf = n2->get_tfont();
2216 if (tf) {
2217 if (tf->contains(soft_hyphen_char)) {
2218 color *gcol = n2->get_glyph_color();
2219 color *fcol = n2->get_fill_color();
2220 node *next1 = next;
2221 next = 0;
2222 node *n = copy();
2223 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol);
2224 node *nn = n->merge_glyph_node(gn);
2225 if (nn == 0) {
2226 gn->next = n;
2227 nn = gn;
2229 return new dbreak_node(this, nn, next1);
2232 return this;
2235 kern_pair_node::~kern_pair_node()
2237 if (n1 != 0)
2238 delete n1;
2239 if (n2 != 0)
2240 delete n2;
2243 dbreak_node::~dbreak_node()
2245 delete_node_list(pre);
2246 delete_node_list(post);
2247 delete_node_list(none);
2250 node *kern_pair_node::copy()
2252 return new kern_pair_node(amount, n1->copy(), n2->copy());
2255 node *copy_node_list(node *n)
2257 node *p = 0;
2258 while (n != 0) {
2259 node *nn = n->copy();
2260 nn->next = p;
2261 p = nn;
2262 n = n->next;
2264 while (p != 0) {
2265 node *pp = p->next;
2266 p->next = n;
2267 n = p;
2268 p = pp;
2270 return n;
2273 void delete_node_list(node *n)
2275 while (n != 0) {
2276 node *tem = n;
2277 n = n->next;
2278 delete tem;
2282 node *dbreak_node::copy()
2284 dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre));
2285 p->post = copy_node_list(post);
2286 return p;
2289 hyphen_list *node::get_hyphen_list(hyphen_list *tail, int *)
2291 return tail;
2294 hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail, int *count)
2296 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2297 return n1->get_hyphen_list(hl, count);
2300 class hyphen_inhibitor_node : public node {
2301 public:
2302 hyphen_inhibitor_node(node *nd = 0);
2303 node *copy();
2304 int same(node *);
2305 const char *type();
2306 int force_tprint();
2307 hyphenation_type get_hyphenation_type();
2310 hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
2314 node *hyphen_inhibitor_node::copy()
2316 return new hyphen_inhibitor_node;
2319 int hyphen_inhibitor_node::same(node *)
2321 return 1;
2324 const char *hyphen_inhibitor_node::type()
2326 return "hyphen_inhibitor_node";
2329 int hyphen_inhibitor_node::force_tprint()
2331 return 0;
2334 hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
2336 return HYPHEN_INHIBIT;
2339 /* add_discretionary_hyphen methods */
2341 node *dbreak_node::add_discretionary_hyphen()
2343 if (post)
2344 post = post->add_discretionary_hyphen();
2345 if (none)
2346 none = none->add_discretionary_hyphen();
2347 return this;
2350 node *node::add_discretionary_hyphen()
2352 tfont *tf = get_tfont();
2353 if (!tf)
2354 return new hyphen_inhibitor_node(this);
2355 if (tf->contains(soft_hyphen_char)) {
2356 color *gcol = get_glyph_color();
2357 color *fcol = get_fill_color();
2358 node *next1 = next;
2359 next = 0;
2360 node *n = copy();
2361 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol);
2362 node *n1 = n->merge_glyph_node(gn);
2363 if (n1 == 0) {
2364 gn->next = n;
2365 n1 = gn;
2367 return new dbreak_node(this, n1, next1);
2369 return this;
2372 node *node::merge_self(node *)
2374 return 0;
2377 node *node::add_self(node *n, hyphen_list ** /*p*/)
2379 next = n;
2380 return this;
2383 node *kern_pair_node::add_self(node *n, hyphen_list **p)
2385 n = n1->add_self(n, p);
2386 n = n2->add_self(n, p);
2387 n1 = n2 = 0;
2388 delete this;
2389 return n;
2392 hunits node::width()
2394 return H0;
2397 node *node::last_char_node()
2399 return 0;
2402 int node::force_tprint()
2404 return 0;
2407 hunits hmotion_node::width()
2409 return n;
2412 units node::size()
2414 return points_to_units(10);
2417 hunits kern_pair_node::width()
2419 return n1->width() + n2->width() + amount;
2422 node *kern_pair_node::last_char_node()
2424 node *nd = n2->last_char_node();
2425 if (nd)
2426 return nd;
2427 return n1->last_char_node();
2430 hunits dbreak_node::width()
2432 hunits x = H0;
2433 for (node *n = none; n != 0; n = n->next)
2434 x += n->width();
2435 return x;
2438 node *dbreak_node::last_char_node()
2440 for (node *n = none; n; n = n->next) {
2441 node *last = n->last_char_node();
2442 if (last)
2443 return last;
2445 return 0;
2448 hunits dbreak_node::italic_correction()
2450 return none ? none->italic_correction() : H0;
2453 hunits dbreak_node::subscript_correction()
2455 return none ? none->subscript_correction() : H0;
2458 class italic_corrected_node : public node {
2459 node *n;
2460 hunits x;
2461 public:
2462 italic_corrected_node(node *, hunits, node * = 0);
2463 ~italic_corrected_node();
2464 node *copy();
2465 void ascii_print(ascii_output_file *);
2466 void asciify(macro *);
2467 hunits width();
2468 node *last_char_node();
2469 void vertical_extent(vunits *, vunits *);
2470 int ends_sentence();
2471 int overlaps_horizontally();
2472 int overlaps_vertically();
2473 int same(node *);
2474 hyphenation_type get_hyphenation_type();
2475 tfont *get_tfont();
2476 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2477 int character_type();
2478 void tprint(troff_output_file *);
2479 hunits subscript_correction();
2480 hunits skew();
2481 node *add_self(node *, hyphen_list **);
2482 const char *type();
2483 int force_tprint();
2486 node *node::add_italic_correction(hunits *width)
2488 hunits ic = italic_correction();
2489 if (ic.is_zero())
2490 return this;
2491 else {
2492 node *next1 = next;
2493 next = 0;
2494 *width += ic;
2495 return new italic_corrected_node(this, ic, next1);
2499 italic_corrected_node::italic_corrected_node(node *nn, hunits xx, node *p)
2500 : node(p), n(nn), x(xx)
2502 assert(n != 0);
2505 italic_corrected_node::~italic_corrected_node()
2507 delete n;
2510 node *italic_corrected_node::copy()
2512 return new italic_corrected_node(n->copy(), x);
2515 hunits italic_corrected_node::width()
2517 return n->width() + x;
2520 void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
2522 n->vertical_extent(min, max);
2525 void italic_corrected_node::tprint(troff_output_file *out)
2527 n->tprint(out);
2528 out->right(x);
2531 hunits italic_corrected_node::skew()
2533 return n->skew() - x/2;
2536 hunits italic_corrected_node::subscript_correction()
2538 return n->subscript_correction() - x;
2541 void italic_corrected_node::ascii_print(ascii_output_file *out)
2543 n->ascii_print(out);
2546 int italic_corrected_node::ends_sentence()
2548 return n->ends_sentence();
2551 int italic_corrected_node::overlaps_horizontally()
2553 return n->overlaps_horizontally();
2556 int italic_corrected_node::overlaps_vertically()
2558 return n->overlaps_vertically();
2561 node *italic_corrected_node::last_char_node()
2563 return n->last_char_node();
2566 tfont *italic_corrected_node::get_tfont()
2568 return n->get_tfont();
2571 hyphenation_type italic_corrected_node::get_hyphenation_type()
2573 return n->get_hyphenation_type();
2576 node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
2578 nd = n->add_self(nd, p);
2579 hunits not_interested;
2580 nd = nd->add_italic_correction(&not_interested);
2581 n = 0;
2582 delete this;
2583 return nd;
2586 hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail,
2587 int *count)
2589 return n->get_hyphen_list(tail, count);
2592 int italic_corrected_node::character_type()
2594 return n->character_type();
2597 class break_char_node : public node {
2598 node *ch;
2599 char break_code;
2600 color *col;
2601 public:
2602 break_char_node(node *, int, color *, node * = 0);
2603 ~break_char_node();
2604 node *copy();
2605 hunits width();
2606 vunits vertical_width();
2607 node *last_char_node();
2608 int character_type();
2609 int ends_sentence();
2610 node *add_self(node *, hyphen_list **);
2611 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2612 void tprint(troff_output_file *);
2613 void zero_width_tprint(troff_output_file *);
2614 void ascii_print(ascii_output_file *);
2615 void asciify(macro *);
2616 hyphenation_type get_hyphenation_type();
2617 int overlaps_vertically();
2618 int overlaps_horizontally();
2619 units size();
2620 tfont *get_tfont();
2621 int same(node *);
2622 const char *type();
2623 int force_tprint();
2626 break_char_node::break_char_node(node *n, int bc, color *c, node *x)
2627 : node(x), ch(n), break_code(bc), col(c)
2631 break_char_node::~break_char_node()
2633 delete ch;
2636 node *break_char_node::copy()
2638 return new break_char_node(ch->copy(), break_code, col);
2641 hunits break_char_node::width()
2643 return ch->width();
2646 vunits break_char_node::vertical_width()
2648 return ch->vertical_width();
2651 node *break_char_node::last_char_node()
2653 return ch->last_char_node();
2656 int break_char_node::character_type()
2658 return ch->character_type();
2661 int break_char_node::ends_sentence()
2663 return ch->ends_sentence();
2666 node *break_char_node::add_self(node *n, hyphen_list **p)
2668 assert((*p)->hyphenation_code == 0);
2669 if ((*p)->breakable && (break_code & 1)) {
2670 n = new space_node(H0, col, n);
2671 n->freeze_space();
2673 next = n;
2674 n = this;
2675 if ((*p)->breakable && (break_code & 2)) {
2676 n = new space_node(H0, col, n);
2677 n->freeze_space();
2679 hyphen_list *pp = *p;
2680 *p = (*p)->next;
2681 delete pp;
2682 return n;
2685 hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail, int *)
2687 return new hyphen_list(0, tail);
2690 hyphenation_type break_char_node::get_hyphenation_type()
2692 return HYPHEN_MIDDLE;
2695 void break_char_node::ascii_print(ascii_output_file *ascii)
2697 ch->ascii_print(ascii);
2700 int break_char_node::overlaps_vertically()
2702 return ch->overlaps_vertically();
2705 int break_char_node::overlaps_horizontally()
2707 return ch->overlaps_horizontally();
2710 units break_char_node::size()
2712 return ch->size();
2715 tfont *break_char_node::get_tfont()
2717 return ch->get_tfont();
2720 node *extra_size_node::copy()
2722 return new extra_size_node(n);
2725 node *vertical_size_node::copy()
2727 return new vertical_size_node(n);
2730 node *hmotion_node::copy()
2732 return new hmotion_node(n, was_tab, unformat, col);
2735 node *space_char_hmotion_node::copy()
2737 return new space_char_hmotion_node(n, col);
2740 node *vmotion_node::copy()
2742 return new vmotion_node(n, col);
2745 node *dummy_node::copy()
2747 return new dummy_node;
2750 node *transparent_dummy_node::copy()
2752 return new transparent_dummy_node;
2755 hline_node::~hline_node()
2757 if (n)
2758 delete n;
2761 node *hline_node::copy()
2763 return new hline_node(x, n ? n->copy() : 0);
2766 hunits hline_node::width()
2768 return x < H0 ? H0 : x;
2771 vline_node::~vline_node()
2773 if (n)
2774 delete n;
2777 node *vline_node::copy()
2779 return new vline_node(x, n ? n->copy() : 0);
2782 hunits vline_node::width()
2784 return n == 0 ? H0 : n->width();
2787 zero_width_node::zero_width_node(node *nd) : n(nd)
2791 zero_width_node::~zero_width_node()
2793 delete_node_list(n);
2796 node *zero_width_node::copy()
2798 return new zero_width_node(copy_node_list(n));
2801 int node_list_character_type(node *p)
2803 int t = 0;
2804 for (; p; p = p->next)
2805 t |= p->character_type();
2806 return t;
2809 int zero_width_node::character_type()
2811 return node_list_character_type(n);
2814 void node_list_vertical_extent(node *p, vunits *min, vunits *max)
2816 *min = V0;
2817 *max = V0;
2818 vunits cur_vpos = V0;
2819 vunits v1, v2;
2820 for (; p; p = p->next) {
2821 p->vertical_extent(&v1, &v2);
2822 v1 += cur_vpos;
2823 if (v1 < *min)
2824 *min = v1;
2825 v2 += cur_vpos;
2826 if (v2 > *max)
2827 *max = v2;
2828 cur_vpos += p->vertical_width();
2832 void zero_width_node::vertical_extent(vunits *min, vunits *max)
2834 node_list_vertical_extent(n, min, max);
2837 overstrike_node::overstrike_node() : list(0), max_width(H0)
2841 overstrike_node::~overstrike_node()
2843 delete_node_list(list);
2846 node *overstrike_node::copy()
2848 overstrike_node *on = new overstrike_node;
2849 for (node *tem = list; tem; tem = tem->next)
2850 on->overstrike(tem->copy());
2851 return on;
2854 void overstrike_node::overstrike(node *n)
2856 if (n == 0)
2857 return;
2858 hunits w = n->width();
2859 if (w > max_width)
2860 max_width = w;
2861 node **p;
2862 for (p = &list; *p; p = &(*p)->next)
2864 n->next = 0;
2865 *p = n;
2868 hunits overstrike_node::width()
2870 return max_width;
2873 bracket_node::bracket_node() : list(0), max_width(H0)
2877 bracket_node::~bracket_node()
2879 delete_node_list(list);
2882 node *bracket_node::copy()
2884 bracket_node *on = new bracket_node;
2885 node *last = 0;
2886 node *tem;
2887 if (list)
2888 list->last = 0;
2889 for (tem = list; tem; tem = tem->next) {
2890 if (tem->next)
2891 tem->next->last = tem;
2892 last = tem;
2894 for (tem = last; tem; tem = tem->last)
2895 on->bracket(tem->copy());
2896 return on;
2899 void bracket_node::bracket(node *n)
2901 if (n == 0)
2902 return;
2903 hunits w = n->width();
2904 if (w > max_width)
2905 max_width = w;
2906 n->next = list;
2907 list = n;
2910 hunits bracket_node::width()
2912 return max_width;
2915 int node::nspaces()
2917 return 0;
2920 int node::merge_space(hunits, hunits, hunits)
2922 return 0;
2925 #if 0
2926 space_node *space_node::free_list = 0;
2928 void *space_node::operator new(size_t n)
2930 assert(n == sizeof(space_node));
2931 if (!free_list) {
2932 free_list = (space_node *)new char[sizeof(space_node)*BLOCK];
2933 for (int i = 0; i < BLOCK - 1; i++)
2934 free_list[i].next = free_list + i + 1;
2935 free_list[BLOCK-1].next = 0;
2937 space_node *p = free_list;
2938 free_list = (space_node *)(free_list->next);
2939 p->next = 0;
2940 return p;
2943 inline void space_node::operator delete(void *p)
2945 if (p) {
2946 ((space_node *)p)->next = free_list;
2947 free_list = (space_node *)p;
2950 #endif
2952 space_node::space_node(hunits nn, color *c, node *p)
2953 : node(p), n(nn), set(0), was_escape_colon(0), col(c)
2957 space_node::space_node(hunits nn, int s, int flag, color *c, node *p)
2958 : node(p), n(nn), set(s), was_escape_colon(flag), col(c)
2962 #if 0
2963 space_node::~space_node()
2966 #endif
2968 node *space_node::copy()
2970 return new space_node(n, set, was_escape_colon, col);
2973 int space_node::force_tprint()
2975 return 0;
2978 int space_node::nspaces()
2980 return set ? 0 : 1;
2983 int space_node::merge_space(hunits h, hunits, hunits)
2985 n += h;
2986 return 1;
2989 hunits space_node::width()
2991 return n;
2994 void node::spread_space(int*, hunits*)
2998 void space_node::spread_space(int *nspaces, hunits *desired_space)
3000 if (!set) {
3001 assert(*nspaces > 0);
3002 if (*nspaces == 1) {
3003 n += *desired_space;
3004 *desired_space = H0;
3006 else {
3007 hunits extra = *desired_space / *nspaces;
3008 *desired_space -= extra;
3009 n += extra;
3011 *nspaces -= 1;
3012 set = 1;
3016 void node::freeze_space()
3020 void space_node::freeze_space()
3022 set = 1;
3025 void node::is_escape_colon()
3029 void space_node::is_escape_colon()
3031 was_escape_colon = 1;
3034 diverted_space_node::diverted_space_node(vunits d, node *p)
3035 : node(p), n(d)
3039 node *diverted_space_node::copy()
3041 return new diverted_space_node(n);
3044 diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
3045 : node(p), filename(s)
3049 node *diverted_copy_file_node::copy()
3051 return new diverted_copy_file_node(filename);
3054 int node::ends_sentence()
3056 return 0;
3059 int kern_pair_node::ends_sentence()
3061 switch (n2->ends_sentence()) {
3062 case 0:
3063 return 0;
3064 case 1:
3065 return 1;
3066 case 2:
3067 break;
3068 default:
3069 assert(0);
3071 return n1->ends_sentence();
3074 int node_list_ends_sentence(node *n)
3076 for (; n != 0; n = n->next)
3077 switch (n->ends_sentence()) {
3078 case 0:
3079 return 0;
3080 case 1:
3081 return 1;
3082 case 2:
3083 break;
3084 default:
3085 assert(0);
3087 return 2;
3090 int dbreak_node::ends_sentence()
3092 return node_list_ends_sentence(none);
3095 int node::overlaps_horizontally()
3097 return 0;
3100 int node::overlaps_vertically()
3102 return 0;
3105 int node::discardable()
3107 return 0;
3110 int space_node::discardable()
3112 return set ? 0 : 1;
3115 vunits node::vertical_width()
3117 return V0;
3120 vunits vline_node::vertical_width()
3122 return x;
3125 vunits vmotion_node::vertical_width()
3127 return n;
3130 int node::set_unformat_flag()
3132 return 1;
3135 int node::character_type()
3137 return 0;
3140 hunits node::subscript_correction()
3142 return H0;
3145 hunits node::italic_correction()
3147 return H0;
3150 hunits node::left_italic_correction()
3152 return H0;
3155 hunits node::skew()
3157 return H0;
3160 /* vertical_extent methods */
3162 void node::vertical_extent(vunits *min, vunits *max)
3164 vunits v = vertical_width();
3165 if (v < V0) {
3166 *min = v;
3167 *max = V0;
3169 else {
3170 *max = v;
3171 *min = V0;
3175 void vline_node::vertical_extent(vunits *min, vunits *max)
3177 if (n == 0)
3178 node::vertical_extent(min, max);
3179 else {
3180 vunits cmin, cmax;
3181 n->vertical_extent(&cmin, &cmax);
3182 vunits h = n->size();
3183 if (x < V0) {
3184 if (-x < h) {
3185 *min = x;
3186 *max = V0;
3188 else {
3189 // we print the first character and then move up, so
3190 *max = cmax;
3191 // we print the last character and then move up h
3192 *min = cmin + h;
3193 if (*min > V0)
3194 *min = V0;
3195 *min += x;
3198 else {
3199 if (x < h) {
3200 *max = x;
3201 *min = V0;
3203 else {
3204 // we move down by h and then print the first character, so
3205 *min = cmin + h;
3206 if (*min > V0)
3207 *min = V0;
3208 *max = x + cmax;
3214 /* ascii_print methods */
3216 static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n)
3218 if (n == 0)
3219 return;
3220 ascii_print_reverse_node_list(ascii, n->next);
3221 n->ascii_print(ascii);
3224 void dbreak_node::ascii_print(ascii_output_file *ascii)
3226 ascii_print_reverse_node_list(ascii, none);
3229 void kern_pair_node::ascii_print(ascii_output_file *ascii)
3231 n1->ascii_print(ascii);
3232 n2->ascii_print(ascii);
3235 void node::ascii_print(ascii_output_file *)
3239 void space_node::ascii_print(ascii_output_file *ascii)
3241 if (!n.is_zero())
3242 ascii->outc(' ');
3245 void hmotion_node::ascii_print(ascii_output_file *ascii)
3247 // this is pretty arbitrary
3248 if (n >= points_to_units(2))
3249 ascii->outc(' ');
3252 void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
3254 ascii->outc(' ');
3257 /* asciify methods */
3259 void node::asciify(macro *m)
3261 m->append(this);
3264 void glyph_node::asciify(macro *m)
3266 unsigned char c = ci->get_asciify_code();
3267 if (c == 0)
3268 c = ci->get_ascii_code();
3269 if (c != 0) {
3270 m->append(c);
3271 delete this;
3273 else
3274 m->append(this);
3277 void kern_pair_node::asciify(macro *m)
3279 n1->asciify(m);
3280 n2->asciify(m);
3281 n1 = n2 = 0;
3282 delete this;
3285 static void asciify_reverse_node_list(macro *m, node *n)
3287 if (n == 0)
3288 return;
3289 asciify_reverse_node_list(m, n->next);
3290 n->asciify(m);
3293 void dbreak_node::asciify(macro *m)
3295 asciify_reverse_node_list(m, none);
3296 none = 0;
3297 delete this;
3300 void ligature_node::asciify(macro *m)
3302 n1->asciify(m);
3303 n2->asciify(m);
3304 n1 = n2 = 0;
3305 delete this;
3308 void break_char_node::asciify(macro *m)
3310 ch->asciify(m);
3311 ch = 0;
3312 delete this;
3315 void italic_corrected_node::asciify(macro *m)
3317 n->asciify(m);
3318 n = 0;
3319 delete this;
3322 void left_italic_corrected_node::asciify(macro *m)
3324 if (n) {
3325 n->asciify(m);
3326 n = 0;
3328 delete this;
3331 void hmotion_node::asciify(macro *m)
3333 if (was_tab) {
3334 m->append('\t');
3335 delete this;
3337 else
3338 m->append(this);
3341 space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3342 node *next)
3343 : hmotion_node(i, c, next)
3347 void space_char_hmotion_node::asciify(macro *m)
3349 m->append(ESCAPE_SPACE);
3350 delete this;
3353 void space_node::asciify(macro *m)
3355 if (was_escape_colon) {
3356 m->append(ESCAPE_COLON);
3357 delete this;
3359 else
3360 m->append(this);
3363 void word_space_node::asciify(macro *m)
3365 for (width_list *w = orig_width; w; w = w->next)
3366 m->append(' ');
3367 delete this;
3370 void unbreakable_space_node::asciify(macro *m)
3372 m->append(ESCAPE_TILDE);
3373 delete this;
3376 void line_start_node::asciify(macro *)
3378 delete this;
3381 void vertical_size_node::asciify(macro *)
3383 delete this;
3386 breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/,
3387 breakpoint *rest, int /*is_inner*/)
3389 return rest;
3392 int node::nbreaks()
3394 return 0;
3397 breakpoint *space_node::get_breakpoints(hunits width, int ns,
3398 breakpoint *rest, int is_inner)
3400 if (next->discardable())
3401 return rest;
3402 breakpoint *bp = new breakpoint;
3403 bp->next = rest;
3404 bp->width = width;
3405 bp->nspaces = ns;
3406 bp->hyphenated = 0;
3407 if (is_inner) {
3408 assert(rest != 0);
3409 bp->index = rest->index + 1;
3410 bp->nd = rest->nd;
3412 else {
3413 bp->nd = this;
3414 bp->index = 0;
3416 return bp;
3419 int space_node::nbreaks()
3421 if (next->discardable())
3422 return 0;
3423 else
3424 return 1;
3427 static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
3428 int ns, breakpoint *rest)
3430 if (p != 0) {
3431 rest = p->get_breakpoints(*widthp,
3433 node_list_get_breakpoints(p->next, widthp, ns,
3434 rest),
3436 *widthp += p->width();
3438 return rest;
3441 breakpoint *dbreak_node::get_breakpoints(hunits width, int ns,
3442 breakpoint *rest, int is_inner)
3444 breakpoint *bp = new breakpoint;
3445 bp->next = rest;
3446 bp->width = width;
3447 for (node *tem = pre; tem != 0; tem = tem->next)
3448 bp->width += tem->width();
3449 bp->nspaces = ns;
3450 bp->hyphenated = 1;
3451 if (is_inner) {
3452 assert(rest != 0);
3453 bp->index = rest->index + 1;
3454 bp->nd = rest->nd;
3456 else {
3457 bp->nd = this;
3458 bp->index = 0;
3460 return node_list_get_breakpoints(none, &width, ns, bp);
3463 int dbreak_node::nbreaks()
3465 int i = 1;
3466 for (node *tem = none; tem != 0; tem = tem->next)
3467 i += tem->nbreaks();
3468 return i;
3471 void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
3473 assert(0);
3476 void space_node::split(int where, node **pre, node **post)
3478 assert(where == 0);
3479 *pre = next;
3480 *post = 0;
3481 delete this;
3484 static void node_list_split(node *p, int *wherep, node **prep, node **postp)
3486 if (p == 0)
3487 return;
3488 int nb = p->nbreaks();
3489 node_list_split(p->next, wherep, prep, postp);
3490 if (*wherep < 0) {
3491 p->next = *postp;
3492 *postp = p;
3494 else if (*wherep < nb) {
3495 p->next = *prep;
3496 p->split(*wherep, prep, postp);
3498 else {
3499 p->next = *prep;
3500 *prep = p;
3502 *wherep -= nb;
3505 void dbreak_node::split(int where, node **prep, node **postp)
3507 assert(where >= 0);
3508 if (where == 0) {
3509 *postp = post;
3510 post = 0;
3511 if (pre == 0)
3512 *prep = next;
3513 else {
3514 node *tem;
3515 for (tem = pre; tem->next != 0; tem = tem->next)
3517 tem->next = next;
3518 *prep = pre;
3520 pre = 0;
3521 delete this;
3523 else {
3524 *prep = next;
3525 where -= 1;
3526 node_list_split(none, &where, prep, postp);
3527 none = 0;
3528 delete this;
3532 hyphenation_type node::get_hyphenation_type()
3534 return HYPHEN_BOUNDARY;
3537 hyphenation_type dbreak_node::get_hyphenation_type()
3539 return HYPHEN_INHIBIT;
3542 hyphenation_type kern_pair_node::get_hyphenation_type()
3544 return HYPHEN_MIDDLE;
3547 hyphenation_type dummy_node::get_hyphenation_type()
3549 return HYPHEN_MIDDLE;
3552 hyphenation_type transparent_dummy_node::get_hyphenation_type()
3554 return HYPHEN_MIDDLE;
3557 hyphenation_type hmotion_node::get_hyphenation_type()
3559 return HYPHEN_MIDDLE;
3562 hyphenation_type space_char_hmotion_node::get_hyphenation_type()
3564 return HYPHEN_MIDDLE;
3567 hyphenation_type overstrike_node::get_hyphenation_type()
3569 return HYPHEN_MIDDLE;
3572 hyphenation_type space_node::get_hyphenation_type()
3574 if (was_escape_colon)
3575 return HYPHEN_MIDDLE;
3576 return HYPHEN_BOUNDARY;
3579 hyphenation_type unbreakable_space_node::get_hyphenation_type()
3581 return HYPHEN_MIDDLE;
3584 int node::interpret(macro *)
3586 return 0;
3589 special_node::special_node(const macro &m, int n)
3590 : mac(m), no_init_string(n)
3592 font_size fs = curenv->get_font_size();
3593 int char_height = curenv->get_char_height();
3594 int char_slant = curenv->get_char_slant();
3595 int fontno = env_definite_font(curenv);
3596 tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fontno);
3597 if (curenv->is_composite())
3598 tf = tf->get_plain();
3599 gcol = curenv->get_glyph_color();
3600 fcol = curenv->get_fill_color();
3603 special_node::special_node(const macro &m, tfont *t,
3604 color *gc, color *fc, int n)
3605 : mac(m), tf(t), gcol(gc), fcol(fc), no_init_string(n)
3609 int special_node::same(node *n)
3611 return mac == ((special_node *)n)->mac
3612 && tf == ((special_node *)n)->tf
3613 && gcol == ((special_node *)n)->gcol
3614 && fcol == ((special_node *)n)->fcol
3615 && no_init_string == ((special_node *)n)->no_init_string;
3618 const char *special_node::type()
3620 return "special_node";
3623 int special_node::ends_sentence()
3625 return 2;
3628 int special_node::force_tprint()
3630 return 0;
3633 node *special_node::copy()
3635 return new special_node(mac, tf, gcol, fcol, no_init_string);
3638 void special_node::tprint_start(troff_output_file *out)
3640 out->start_special(tf, gcol, fcol, no_init_string);
3643 void special_node::tprint_char(troff_output_file *out, unsigned char c)
3645 out->special_char(c);
3648 void special_node::tprint_end(troff_output_file *out)
3650 out->end_special();
3653 tfont *special_node::get_tfont()
3655 return tf;
3658 /* suppress_node */
3660 suppress_node::suppress_node(int on_or_off, int issue_limits)
3661 : is_on(on_or_off), emit_limits(issue_limits),
3662 filename(0), position(0), image_id(0)
3666 suppress_node::suppress_node(symbol f, char p, int id)
3667 : is_on(2), emit_limits(0), filename(f), position(p), image_id(id)
3671 suppress_node::suppress_node(int issue_limits, int on_or_off,
3672 symbol f, char p, int id)
3673 : is_on(on_or_off), emit_limits(issue_limits),
3674 filename(f), position(p), image_id(id)
3678 int suppress_node::same(node *n)
3680 return ((is_on == ((suppress_node *)n)->is_on)
3681 && (emit_limits == ((suppress_node *)n)->emit_limits)
3682 && (filename == ((suppress_node *)n)->filename)
3683 && (position == ((suppress_node *)n)->position)
3684 && (image_id == ((suppress_node *)n)->image_id));
3687 const char *suppress_node::type()
3689 return "suppress_node";
3692 node *suppress_node::copy()
3694 return new suppress_node(emit_limits, is_on, filename, position, image_id);
3697 int get_reg_int(const char *p)
3699 reg *r = (reg *)number_reg_dictionary.lookup(p);
3700 units prev_value;
3701 if (r && (r->get_value(&prev_value)))
3702 return (int)prev_value;
3703 else
3704 warning(WARN_REG, "number register `%1' not defined", p);
3705 return 0;
3708 const char *get_reg_str(const char *p)
3710 reg *r = (reg *)number_reg_dictionary.lookup(p);
3711 if (r)
3712 return r->get_string();
3713 else
3714 warning(WARN_REG, "register `%1' not defined", p);
3715 return 0;
3718 void suppress_node::put(troff_output_file *out, const char *s)
3720 int i = 0;
3721 while (s[i] != (char)0) {
3722 out->special_char(s[i]);
3723 i++;
3728 * We need to remember the start of the image and its name.
3731 static char last_position = 0;
3732 static const char *last_image_filename = 0;
3733 static int last_image_id = 0;
3735 inline int min(int a, int b)
3737 return a < b ? a : b;
3741 * tprint - if (is_on == 2)
3742 * remember current position (l, r, c, i) and filename
3743 * else
3744 * if (emit_limits)
3745 * if (html)
3746 * emit image tag
3747 * else
3748 * emit postscript bounds for image
3749 * else
3750 * if (suppress boolean differs from current state)
3751 * alter state
3752 * reset registers
3753 * record current page
3754 * set low water mark.
3757 void suppress_node::tprint(troff_output_file *out)
3759 int current_page = topdiv->get_page_number();
3760 // firstly check to see whether this suppress node contains
3761 // an image filename & position.
3762 if (is_on == 2) {
3763 // remember position and filename
3764 last_position = position;
3765 const char *tem = last_image_filename;
3766 last_image_filename = strsave(filename.contents());
3767 if (tem)
3768 a_delete(tem);
3769 last_image_id = image_id;
3770 // printf("start of image and page = %d\n", current_page);
3772 else {
3773 // now check whether the suppress node requires us to issue limits.
3774 if (emit_limits) {
3775 char name[8192];
3776 // remember that the filename will contain a %d in which the
3777 // last_image_id is placed
3778 sprintf(name, last_image_filename, last_image_id);
3779 if (is_html) {
3780 switch (last_position) {
3781 case 'c':
3782 out->start_special();
3783 put(out, "html-tag:.centered-image");
3784 break;
3785 case 'r':
3786 out->start_special();
3787 put(out, "html-tag:.right-image");
3788 break;
3789 case 'l':
3790 out->start_special();
3791 put(out, "html-tag:.left-image");
3792 break;
3793 case 'i':
3795 default:
3798 out->end_special();
3799 out->start_special();
3800 put(out, "html-tag:.auto-image ");
3801 put(out, name);
3802 out->end_special();
3804 else {
3805 // postscript (or other device)
3806 if (suppress_start_page > 0 && current_page != suppress_start_page)
3807 error("suppression limit registers span more than one page;\n"
3808 "image description %1 will be wrong", image_no);
3809 // if (topdiv->get_page_number() != suppress_start_page)
3810 // fprintf(stderr, "end of image and topdiv page = %d and suppress_start_page = %d\n",
3811 // topdiv->get_page_number(), suppress_start_page);
3813 // remember that the filename will contain a %d in which the
3814 // image_no is placed
3815 fprintf(stderr,
3816 "grohtml-info:page %d %d %d %d %d %d %s %d %d %s\n",
3817 topdiv->get_page_number(),
3818 get_reg_int("opminx"), get_reg_int("opminy"),
3819 get_reg_int("opmaxx"), get_reg_int("opmaxy"),
3820 // page offset + line length
3821 get_reg_int(".o") + get_reg_int(".l"),
3822 name, hresolution, vresolution, get_reg_str(".F"));
3823 fflush(stderr);
3826 else {
3827 if (is_on) {
3828 out->on();
3829 // lastly we reset the output registers
3830 reset_output_registers();
3832 else
3833 out->off();
3834 suppress_start_page = current_page;
3839 int suppress_node::force_tprint()
3841 return is_on;
3844 hunits suppress_node::width()
3846 return H0;
3849 /* composite_node */
3851 class composite_node : public charinfo_node {
3852 node *n;
3853 tfont *tf;
3854 public:
3855 composite_node(node *, charinfo *, tfont *, node * = 0);
3856 ~composite_node();
3857 node *copy();
3858 hunits width();
3859 node *last_char_node();
3860 units size();
3861 void tprint(troff_output_file *);
3862 hyphenation_type get_hyphenation_type();
3863 void ascii_print(ascii_output_file *);
3864 void asciify(macro *);
3865 hyphen_list *get_hyphen_list(hyphen_list *, int *);
3866 node *add_self(node *, hyphen_list **);
3867 tfont *get_tfont();
3868 int same(node *);
3869 const char *type();
3870 int force_tprint();
3871 void vertical_extent(vunits *, vunits *);
3872 vunits vertical_width();
3875 composite_node::composite_node(node *p, charinfo *c, tfont *t, node *x)
3876 : charinfo_node(c, x), n(p), tf(t)
3880 composite_node::~composite_node()
3882 delete_node_list(n);
3885 node *composite_node::copy()
3887 return new composite_node(copy_node_list(n), ci, tf);
3890 hunits composite_node::width()
3892 hunits x;
3893 if (tf->get_constant_space(&x))
3894 return x;
3895 x = H0;
3896 for (node *tem = n; tem; tem = tem->next)
3897 x += tem->width();
3898 hunits offset;
3899 if (tf->get_bold(&offset))
3900 x += offset;
3901 x += tf->get_track_kern();
3902 return x;
3905 node *composite_node::last_char_node()
3907 return this;
3910 vunits composite_node::vertical_width()
3912 vunits v = V0;
3913 for (node *tem = n; tem; tem = tem->next)
3914 v += tem->vertical_width();
3915 return v;
3918 units composite_node::size()
3920 return tf->get_size().to_units();
3923 hyphenation_type composite_node::get_hyphenation_type()
3925 return HYPHEN_MIDDLE;
3928 void composite_node::asciify(macro *m)
3930 unsigned char c = ci->get_asciify_code();
3931 if (c == 0)
3932 c = ci->get_ascii_code();
3933 if (c != 0) {
3934 m->append(c);
3935 delete this;
3937 else
3938 m->append(this);
3941 void composite_node::ascii_print(ascii_output_file *ascii)
3943 unsigned char c = ci->get_ascii_code();
3944 if (c != 0)
3945 ascii->outc(c);
3946 else
3947 ascii->outs(ci->nm.contents());
3951 hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail, int *count)
3953 (*count)++;
3954 return new hyphen_list(ci->get_hyphenation_code(), tail);
3957 node *composite_node::add_self(node *nn, hyphen_list **p)
3959 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
3960 next = nn;
3961 nn = this;
3962 if ((*p)->hyphen)
3963 nn = nn->add_discretionary_hyphen();
3964 hyphen_list *pp = *p;
3965 *p = (*p)->next;
3966 delete pp;
3967 return nn;
3970 tfont *composite_node::get_tfont()
3972 return tf;
3975 node *reverse_node_list(node *n)
3977 node *r = 0;
3978 while (n) {
3979 node *tem = n;
3980 n = n->next;
3981 tem->next = r;
3982 r = tem;
3984 return r;
3987 void composite_node::vertical_extent(vunits *min, vunits *max)
3989 n = reverse_node_list(n);
3990 node_list_vertical_extent(n, min, max);
3991 n = reverse_node_list(n);
3994 width_list::width_list(hunits w, hunits s)
3995 : width(w), sentence_width(s), next(0)
3999 width_list::width_list(width_list *w)
4000 : width(w->width), sentence_width(w->sentence_width), next(0)
4004 word_space_node::word_space_node(hunits d, color *c, width_list *w, node *x)
4005 : space_node(d, c, x), orig_width(w), unformat(0)
4009 word_space_node::word_space_node(hunits d, int s, color *c, width_list *w,
4010 int flag, node *x)
4011 : space_node(d, s, 0, c, x), orig_width(w), unformat(flag)
4015 word_space_node::~word_space_node()
4017 width_list *w = orig_width;
4018 while (w != 0) {
4019 width_list *tmp = w;
4020 w = w->next;
4021 delete tmp;
4025 node *word_space_node::copy()
4027 assert(orig_width != 0);
4028 width_list *w_old_curr = orig_width;
4029 width_list *w_new_curr = new width_list(w_old_curr);
4030 width_list *w_new = w_new_curr;
4031 w_old_curr = w_old_curr->next;
4032 while (w_old_curr != 0) {
4033 w_new_curr->next = new width_list(w_old_curr);
4034 w_new_curr = w_new_curr->next;
4035 w_old_curr = w_old_curr->next;
4037 return new word_space_node(n, set, col, w_new, unformat);
4040 int word_space_node::set_unformat_flag()
4042 unformat = 1;
4043 return 1;
4046 void word_space_node::tprint(troff_output_file *out)
4048 out->fill_color(col);
4049 out->word_marker();
4050 out->right(n);
4053 int word_space_node::merge_space(hunits h, hunits sw, hunits ssw)
4055 n += h;
4056 assert(orig_width != 0);
4057 width_list *w = orig_width;
4058 for (; w->next; w = w->next)
4060 w->next = new width_list(sw, ssw);
4061 return 1;
4064 unbreakable_space_node::unbreakable_space_node(hunits d, color *c, node *x)
4065 : word_space_node(d, c, 0, x)
4069 unbreakable_space_node::unbreakable_space_node(hunits d, int s,
4070 color *c, node *x)
4071 : word_space_node(d, s, c, 0, 0, x)
4075 node *unbreakable_space_node::copy()
4077 return new unbreakable_space_node(n, set, col);
4080 int unbreakable_space_node::force_tprint()
4082 return 0;
4085 breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
4086 breakpoint *rest, int)
4088 return rest;
4091 int unbreakable_space_node::nbreaks()
4093 return 0;
4096 void unbreakable_space_node::split(int, node **, node **)
4098 assert(0);
4101 int unbreakable_space_node::merge_space(hunits, hunits, hunits)
4103 return 0;
4106 hvpair::hvpair()
4110 draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4111 color *gc, color *fc)
4112 : npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4114 point = new hvpair[npoints];
4115 for (int i = 0; i < npoints; i++)
4116 point[i] = p[i];
4119 int draw_node::same(node *n)
4121 draw_node *nd = (draw_node *)n;
4122 if (code != nd->code || npoints != nd->npoints || sz != nd->sz
4123 || gcol != nd->gcol || fcol != nd->fcol)
4124 return 0;
4125 for (int i = 0; i < npoints; i++)
4126 if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
4127 return 0;
4128 return 1;
4131 const char *draw_node::type()
4133 return "draw_node";
4136 int draw_node::force_tprint()
4138 return 0;
4141 draw_node::~draw_node()
4143 if (point)
4144 a_delete point;
4147 hunits draw_node::width()
4149 hunits x = H0;
4150 for (int i = 0; i < npoints; i++)
4151 x += point[i].h;
4152 return x;
4155 vunits draw_node::vertical_width()
4157 if (code == 'e')
4158 return V0;
4159 vunits x = V0;
4160 for (int i = 0; i < npoints; i++)
4161 x += point[i].v;
4162 return x;
4165 node *draw_node::copy()
4167 return new draw_node(code, point, npoints, sz, gcol, fcol);
4170 void draw_node::tprint(troff_output_file *out)
4172 out->draw(code, point, npoints, sz, gcol, fcol);
4175 /* tprint methods */
4177 void glyph_node::tprint(troff_output_file *out)
4179 tfont *ptf = tf->get_plain();
4180 if (ptf == tf)
4181 out->put_char_width(ci, ptf, gcol, fcol, width(), H0);
4182 else {
4183 hunits offset;
4184 int bold = tf->get_bold(&offset);
4185 hunits w = ptf->get_width(ci);
4186 hunits k = H0;
4187 hunits x;
4188 int cs = tf->get_constant_space(&x);
4189 if (cs) {
4190 x -= w;
4191 if (bold)
4192 x -= offset;
4193 hunits x2 = x/2;
4194 out->right(x2);
4195 k = x - x2;
4197 else
4198 k = tf->get_track_kern();
4199 if (bold) {
4200 out->put_char(ci, ptf, gcol, fcol);
4201 out->right(offset);
4203 out->put_char_width(ci, ptf, gcol, fcol, w, k);
4207 void glyph_node::zero_width_tprint(troff_output_file *out)
4209 tfont *ptf = tf->get_plain();
4210 hunits offset;
4211 int bold = tf->get_bold(&offset);
4212 hunits x;
4213 int cs = tf->get_constant_space(&x);
4214 if (cs) {
4215 x -= ptf->get_width(ci);
4216 if (bold)
4217 x -= offset;
4218 x = x/2;
4219 out->right(x);
4221 out->put_char(ci, ptf, gcol, fcol);
4222 if (bold) {
4223 out->right(offset);
4224 out->put_char(ci, ptf, gcol, fcol);
4225 out->right(-offset);
4227 if (cs)
4228 out->right(-x);
4231 void break_char_node::tprint(troff_output_file *t)
4233 ch->tprint(t);
4236 void break_char_node::zero_width_tprint(troff_output_file *t)
4238 ch->zero_width_tprint(t);
4241 void hline_node::tprint(troff_output_file *out)
4243 if (x < H0) {
4244 out->right(x);
4245 x = -x;
4247 if (n == 0) {
4248 out->right(x);
4249 return;
4251 hunits w = n->width();
4252 if (w <= H0) {
4253 error("horizontal line drawing character must have positive width");
4254 out->right(x);
4255 return;
4257 int i = int(x/w);
4258 if (i == 0) {
4259 hunits xx = x - w;
4260 hunits xx2 = xx/2;
4261 out->right(xx2);
4262 if (out->is_on())
4263 n->tprint(out);
4264 out->right(xx - xx2);
4266 else {
4267 hunits rem = x - w*i;
4268 if (rem > H0)
4269 if (n->overlaps_horizontally()) {
4270 if (out->is_on())
4271 n->tprint(out);
4272 out->right(rem - w);
4274 else
4275 out->right(rem);
4276 while (--i >= 0)
4277 if (out->is_on())
4278 n->tprint(out);
4282 void vline_node::tprint(troff_output_file *out)
4284 if (n == 0) {
4285 out->down(x);
4286 return;
4288 vunits h = n->size();
4289 int overlaps = n->overlaps_vertically();
4290 vunits y = x;
4291 if (y < V0) {
4292 y = -y;
4293 int i = y / h;
4294 vunits rem = y - i*h;
4295 if (i == 0) {
4296 out->right(n->width());
4297 out->down(-rem);
4299 else {
4300 while (--i > 0) {
4301 n->zero_width_tprint(out);
4302 out->down(-h);
4304 if (overlaps) {
4305 n->zero_width_tprint(out);
4306 out->down(-rem);
4307 if (out->is_on())
4308 n->tprint(out);
4309 out->down(-h);
4311 else {
4312 if (out->is_on())
4313 n->tprint(out);
4314 out->down(-h - rem);
4318 else {
4319 int i = y / h;
4320 vunits rem = y - i*h;
4321 if (i == 0) {
4322 out->down(rem);
4323 out->right(n->width());
4325 else {
4326 out->down(h);
4327 if (overlaps)
4328 n->zero_width_tprint(out);
4329 out->down(rem);
4330 while (--i > 0) {
4331 n->zero_width_tprint(out);
4332 out->down(h);
4334 if (out->is_on())
4335 n->tprint(out);
4340 void zero_width_node::tprint(troff_output_file *out)
4342 if (!n)
4343 return;
4344 if (!n->next) {
4345 n->zero_width_tprint(out);
4346 return;
4348 int hpos = out->get_hpos();
4349 int vpos = out->get_vpos();
4350 node *tem = n;
4351 while (tem) {
4352 tem->tprint(out);
4353 tem = tem->next;
4355 out->moveto(hpos, vpos);
4358 void overstrike_node::tprint(troff_output_file *out)
4360 hunits pos = H0;
4361 for (node *tem = list; tem; tem = tem->next) {
4362 hunits x = (max_width - tem->width())/2;
4363 out->right(x - pos);
4364 pos = x;
4365 tem->zero_width_tprint(out);
4367 out->right(max_width - pos);
4370 void bracket_node::tprint(troff_output_file *out)
4372 if (list == 0)
4373 return;
4374 int npieces = 0;
4375 node *tem;
4376 for (tem = list; tem; tem = tem->next)
4377 ++npieces;
4378 vunits h = list->size();
4379 vunits totalh = h*npieces;
4380 vunits y = (totalh - h)/2;
4381 out->down(y);
4382 for (tem = list; tem; tem = tem->next) {
4383 tem->zero_width_tprint(out);
4384 out->down(-h);
4386 out->right(max_width);
4387 out->down(totalh - y);
4390 void node::tprint(troff_output_file *)
4394 void node::zero_width_tprint(troff_output_file *out)
4396 int hpos = out->get_hpos();
4397 int vpos = out->get_vpos();
4398 tprint(out);
4399 out->moveto(hpos, vpos);
4402 void space_node::tprint(troff_output_file *out)
4404 out->fill_color(col);
4405 out->right(n);
4408 void hmotion_node::tprint(troff_output_file *out)
4410 out->fill_color(col);
4411 out->right(n);
4414 void space_char_hmotion_node::tprint(troff_output_file *out)
4416 out->fill_color(col);
4417 if (is_html) {
4418 // we emit the space width as a negative glyph index
4419 out->flush_tbuf();
4420 out->do_motion();
4421 out->put('N');
4422 out->put(-n.to_units());
4423 out->put('\n');
4425 out->right(n);
4428 void vmotion_node::tprint(troff_output_file *out)
4430 out->fill_color(col);
4431 out->down(n);
4434 void kern_pair_node::tprint(troff_output_file *out)
4436 n1->tprint(out);
4437 out->right(amount);
4438 n2->tprint(out);
4441 static void tprint_reverse_node_list(troff_output_file *out, node *n)
4443 if (n == 0)
4444 return;
4445 tprint_reverse_node_list(out, n->next);
4446 n->tprint(out);
4449 void dbreak_node::tprint(troff_output_file *out)
4451 tprint_reverse_node_list(out, none);
4454 void composite_node::tprint(troff_output_file *out)
4456 hunits bold_offset;
4457 int is_bold = tf->get_bold(&bold_offset);
4458 hunits track_kern = tf->get_track_kern();
4459 hunits constant_space;
4460 int is_constant_spaced = tf->get_constant_space(&constant_space);
4461 hunits x = H0;
4462 if (is_constant_spaced) {
4463 x = constant_space;
4464 for (node *tem = n; tem; tem = tem->next)
4465 x -= tem->width();
4466 if (is_bold)
4467 x -= bold_offset;
4468 hunits x2 = x/2;
4469 out->right(x2);
4470 x -= x2;
4472 if (is_bold) {
4473 int hpos = out->get_hpos();
4474 int vpos = out->get_vpos();
4475 tprint_reverse_node_list(out, n);
4476 out->moveto(hpos, vpos);
4477 out->right(bold_offset);
4479 tprint_reverse_node_list(out, n);
4480 if (is_constant_spaced)
4481 out->right(x);
4482 else
4483 out->right(track_kern);
4486 node *make_composite_node(charinfo *s, environment *env)
4488 int fontno = env_definite_font(env);
4489 if (fontno < 0) {
4490 error("no current font");
4491 return 0;
4493 assert(fontno < font_table_size && font_table[fontno] != 0);
4494 node *n = charinfo_to_node_list(s, env);
4495 font_size fs = env->get_font_size();
4496 int char_height = env->get_char_height();
4497 int char_slant = env->get_char_slant();
4498 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
4499 fontno);
4500 if (env->is_composite())
4501 tf = tf->get_plain();
4502 return new composite_node(n, s, tf);
4505 node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0)
4507 int fontno = env_definite_font(env);
4508 if (fontno < 0) {
4509 error("no current font");
4510 return 0;
4512 assert(fontno < font_table_size && font_table[fontno] != 0);
4513 int fn = fontno;
4514 int found = font_table[fontno]->contains(s);
4515 if (!found) {
4516 macro *mac = s->get_macro();
4517 if (mac && s->is_fallback())
4518 return make_composite_node(s, env);
4519 if (s->numbered()) {
4520 if (!no_error_message)
4521 warning(WARN_CHAR, "can't find numbered character %1",
4522 s->get_number());
4523 return 0;
4525 special_font_list *sf = font_table[fontno]->sf;
4526 while (sf != 0 && !found) {
4527 fn = sf->n;
4528 if (font_table[fn])
4529 found = font_table[fn]->contains(s);
4530 sf = sf->next;
4532 if (!found) {
4533 symbol f = font_table[fontno]->get_name();
4534 string gl(f.contents());
4535 gl += ' ';
4536 gl += s->nm.contents();
4537 gl += '\0';
4538 charinfo *ci = get_charinfo(symbol(gl.contents()));
4539 if (ci && ci->get_macro())
4540 return make_composite_node(ci, env);
4542 if (!found) {
4543 sf = global_special_fonts;
4544 while (sf != 0 && !found) {
4545 fn = sf->n;
4546 if (font_table[fn])
4547 found = font_table[fn]->contains(s);
4548 sf = sf->next;
4551 if (!found)
4552 if (mac && s->is_special())
4553 return make_composite_node(s, env);
4554 if (!found) {
4555 for (fn = 0; fn < font_table_size; fn++)
4556 if (font_table[fn]
4557 && font_table[fn]->is_special()
4558 && font_table[fn]->contains(s)) {
4559 found = 1;
4560 break;
4563 if (!found) {
4564 if (!no_error_message && s->first_time_not_found()) {
4565 unsigned char input_code = s->get_ascii_code();
4566 if (input_code != 0) {
4567 if (csgraph(input_code))
4568 warning(WARN_CHAR, "can't find character `%1'", input_code);
4569 else
4570 warning(WARN_CHAR, "can't find character with input code %1",
4571 int(input_code));
4573 else if (s->nm.contents())
4574 warning(WARN_CHAR, "can't find special character `%1'",
4575 s->nm.contents());
4577 return 0;
4580 font_size fs = env->get_font_size();
4581 int char_height = env->get_char_height();
4582 int char_slant = env->get_char_slant();
4583 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn);
4584 if (env->is_composite())
4585 tf = tf->get_plain();
4586 color *gcol = env->get_glyph_color();
4587 color *fcol = env->get_fill_color();
4588 return new glyph_node(s, tf, gcol, fcol);
4591 node *make_node(charinfo *ci, environment *env)
4593 switch (ci->get_special_translation()) {
4594 case charinfo::TRANSLATE_SPACE:
4595 return new space_char_hmotion_node(env->get_space_width(),
4596 env->get_fill_color());
4597 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4598 return new unbreakable_space_node(env->get_space_width(),
4599 env->get_fill_color());
4600 case charinfo::TRANSLATE_DUMMY:
4601 return new dummy_node;
4602 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4603 error("translation to \\% ignored in this context");
4604 break;
4606 charinfo *tem = ci->get_translation();
4607 if (tem)
4608 ci = tem;
4609 macro *mac = ci->get_macro();
4610 if (mac && ci->is_normal())
4611 return make_composite_node(ci, env);
4612 else
4613 return make_glyph_node(ci, env);
4616 int character_exists(charinfo *ci, environment *env)
4618 if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
4619 return 1;
4620 charinfo *tem = ci->get_translation();
4621 if (tem)
4622 ci = tem;
4623 if (ci->get_macro())
4624 return 1;
4625 node *nd = make_glyph_node(ci, env, 1);
4626 if (nd) {
4627 delete nd;
4628 return 1;
4630 return 0;
4633 node *node::add_char(charinfo *ci, environment *env,
4634 hunits *widthp, int *spacep)
4636 node *res;
4637 switch (ci->get_special_translation()) {
4638 case charinfo::TRANSLATE_SPACE:
4639 res = new space_char_hmotion_node(env->get_space_width(),
4640 env->get_fill_color(), this);
4641 *widthp += res->width();
4642 return res;
4643 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4644 res = new unbreakable_space_node(env->get_space_width(),
4645 env->get_fill_color(), this);
4646 res->freeze_space();
4647 *widthp += res->width();
4648 *spacep += res->nspaces();
4649 return res;
4650 case charinfo::TRANSLATE_DUMMY:
4651 return new dummy_node(this);
4652 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4653 return add_discretionary_hyphen();
4655 charinfo *tem = ci->get_translation();
4656 if (tem)
4657 ci = tem;
4658 macro *mac = ci->get_macro();
4659 if (mac && ci->is_normal()) {
4660 res = make_composite_node(ci, env);
4661 if (res) {
4662 res->next = this;
4663 *widthp += res->width();
4665 else
4666 return this;
4668 else {
4669 node *gn = make_glyph_node(ci, env);
4670 if (gn == 0)
4671 return this;
4672 else {
4673 hunits old_width = width();
4674 node *p = gn->merge_self(this);
4675 if (p == 0) {
4676 *widthp += gn->width();
4677 gn->next = this;
4678 res = gn;
4680 else {
4681 *widthp += p->width() - old_width;
4682 res = p;
4686 int break_code = 0;
4687 if (ci->can_break_before())
4688 break_code = 1;
4689 if (ci->can_break_after())
4690 break_code |= 2;
4691 if (break_code) {
4692 node *next1 = res->next;
4693 res->next = 0;
4694 res = new break_char_node(res, break_code, env->get_fill_color(), next1);
4696 return res;
4699 #ifdef __GNUG__
4700 inline
4701 #endif
4702 int same_node(node *n1, node *n2)
4704 if (n1 != 0) {
4705 if (n2 != 0)
4706 return n1->type() == n2->type() && n1->same(n2);
4707 else
4708 return 0;
4710 else
4711 return n2 == 0;
4714 int same_node_list(node *n1, node *n2)
4716 while (n1 && n2) {
4717 if (n1->type() != n2->type() || !n1->same(n2))
4718 return 0;
4719 n1 = n1->next;
4720 n2 = n2->next;
4722 return !n1 && !n2;
4725 int extra_size_node::same(node *nd)
4727 return n == ((extra_size_node *)nd)->n;
4730 const char *extra_size_node::type()
4732 return "extra_size_node";
4735 int extra_size_node::force_tprint()
4737 return 0;
4740 int vertical_size_node::same(node *nd)
4742 return n == ((vertical_size_node *)nd)->n;
4745 const char *vertical_size_node::type()
4747 return "vertical_size_node";
4750 int vertical_size_node::set_unformat_flag()
4752 return 0;
4755 int vertical_size_node::force_tprint()
4757 return 0;
4760 int hmotion_node::same(node *nd)
4762 return n == ((hmotion_node *)nd)->n
4763 && col == ((hmotion_node *)nd)->col;
4766 const char *hmotion_node::type()
4768 return "hmotion_node";
4771 int hmotion_node::set_unformat_flag()
4773 unformat = 1;
4774 return 1;
4777 int hmotion_node::force_tprint()
4779 return 0;
4782 node *hmotion_node::add_self(node *n, hyphen_list **p)
4784 next = n;
4785 hyphen_list *pp = *p;
4786 *p = (*p)->next;
4787 delete pp;
4788 return this;
4791 hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail, int *)
4793 return new hyphen_list(0, tail);
4796 int space_char_hmotion_node::same(node *nd)
4798 return n == ((space_char_hmotion_node *)nd)->n
4799 && col == ((space_char_hmotion_node *)nd)->col;
4802 const char *space_char_hmotion_node::type()
4804 return "space_char_hmotion_node";
4807 int space_char_hmotion_node::force_tprint()
4809 return 0;
4812 node *space_char_hmotion_node::add_self(node *n, hyphen_list **p)
4814 next = n;
4815 hyphen_list *pp = *p;
4816 *p = (*p)->next;
4817 delete pp;
4818 return this;
4821 hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail,
4822 int *)
4824 return new hyphen_list(0, tail);
4827 int vmotion_node::same(node *nd)
4829 return n == ((vmotion_node *)nd)->n
4830 && col == ((vmotion_node *)nd)->col;
4833 const char *vmotion_node::type()
4835 return "vmotion_node";
4838 int vmotion_node::force_tprint()
4840 return 0;
4843 int hline_node::same(node *nd)
4845 return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n);
4848 const char *hline_node::type()
4850 return "hline_node";
4853 int hline_node::force_tprint()
4855 return 0;
4858 int vline_node::same(node *nd)
4860 return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n);
4863 const char *vline_node::type()
4865 return "vline_node";
4868 int vline_node::force_tprint()
4870 return 0;
4873 int dummy_node::same(node * /*nd*/)
4875 return 1;
4878 const char *dummy_node::type()
4880 return "dummy_node";
4883 int dummy_node::force_tprint()
4885 return 0;
4888 int transparent_dummy_node::same(node * /*nd*/)
4890 return 1;
4893 const char *transparent_dummy_node::type()
4895 return "transparent_dummy_node";
4898 int transparent_dummy_node::force_tprint()
4900 return 0;
4903 int transparent_dummy_node::ends_sentence()
4905 return 2;
4908 int zero_width_node::same(node *nd)
4910 return same_node_list(n, ((zero_width_node *)nd)->n);
4913 const char *zero_width_node::type()
4915 return "zero_width_node";
4918 int zero_width_node::force_tprint()
4920 return 0;
4923 int italic_corrected_node::same(node *nd)
4925 return (x == ((italic_corrected_node *)nd)->x
4926 && same_node(n, ((italic_corrected_node *)nd)->n));
4929 const char *italic_corrected_node::type()
4931 return "italic_corrected_node";
4934 int italic_corrected_node::force_tprint()
4936 return 0;
4939 left_italic_corrected_node::left_italic_corrected_node(node *x)
4940 : node(x), n(0)
4944 left_italic_corrected_node::~left_italic_corrected_node()
4946 delete n;
4949 node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
4951 if (n == 0) {
4952 hunits lic = gn->left_italic_correction();
4953 if (!lic.is_zero()) {
4954 x = lic;
4955 n = gn;
4956 return this;
4959 else {
4960 node *nd = n->merge_glyph_node(gn);
4961 if (nd) {
4962 n = nd;
4963 x = n->left_italic_correction();
4964 return this;
4967 return 0;
4970 node *left_italic_corrected_node::copy()
4972 left_italic_corrected_node *nd = new left_italic_corrected_node;
4973 if (n) {
4974 nd->n = n->copy();
4975 nd->x = x;
4977 return nd;
4980 void left_italic_corrected_node::tprint(troff_output_file *out)
4982 if (n) {
4983 out->right(x);
4984 n->tprint(out);
4988 const char *left_italic_corrected_node::type()
4990 return "left_italic_corrected_node";
4993 int left_italic_corrected_node::force_tprint()
4995 return 0;
4998 int left_italic_corrected_node::same(node *nd)
5000 return (x == ((left_italic_corrected_node *)nd)->x
5001 && same_node(n, ((left_italic_corrected_node *)nd)->n));
5004 void left_italic_corrected_node::ascii_print(ascii_output_file *out)
5006 if (n)
5007 n->ascii_print(out);
5010 hunits left_italic_corrected_node::width()
5012 return n ? n->width() + x : H0;
5015 void left_italic_corrected_node::vertical_extent(vunits *min, vunits *max)
5017 if (n)
5018 n->vertical_extent(min, max);
5019 else
5020 node::vertical_extent(min, max);
5023 hunits left_italic_corrected_node::skew()
5025 return n ? n->skew() + x/2 : H0;
5028 hunits left_italic_corrected_node::subscript_correction()
5030 return n ? n->subscript_correction() : H0;
5033 hunits left_italic_corrected_node::italic_correction()
5035 return n ? n->italic_correction() : H0;
5038 int left_italic_corrected_node::ends_sentence()
5040 return n ? n->ends_sentence() : 0;
5043 int left_italic_corrected_node::overlaps_horizontally()
5045 return n ? n->overlaps_horizontally() : 0;
5048 int left_italic_corrected_node::overlaps_vertically()
5050 return n ? n->overlaps_vertically() : 0;
5053 node *left_italic_corrected_node::last_char_node()
5055 return n ? n->last_char_node() : 0;
5058 tfont *left_italic_corrected_node::get_tfont()
5060 return n ? n->get_tfont() : 0;
5063 hyphenation_type left_italic_corrected_node::get_hyphenation_type()
5065 if (n)
5066 return n->get_hyphenation_type();
5067 else
5068 return HYPHEN_MIDDLE;
5071 hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail,
5072 int *count)
5074 return n ? n->get_hyphen_list(tail, count) : tail;
5077 node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
5079 if (n) {
5080 nd = new left_italic_corrected_node(nd);
5081 nd = n->add_self(nd, p);
5082 n = 0;
5083 delete this;
5085 return nd;
5088 int left_italic_corrected_node::character_type()
5090 return n ? n->character_type() : 0;
5093 int overstrike_node::same(node *nd)
5095 return same_node_list(list, ((overstrike_node *)nd)->list);
5098 const char *overstrike_node::type()
5100 return "overstrike_node";
5103 int overstrike_node::force_tprint()
5105 return 0;
5108 node *overstrike_node::add_self(node *n, hyphen_list **p)
5110 next = n;
5111 hyphen_list *pp = *p;
5112 *p = (*p)->next;
5113 delete pp;
5114 return this;
5117 hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail, int *)
5119 return new hyphen_list(0, tail);
5122 int bracket_node::same(node *nd)
5124 return same_node_list(list, ((bracket_node *)nd)->list);
5127 const char *bracket_node::type()
5129 return "bracket_node";
5132 int bracket_node::force_tprint()
5134 return 0;
5137 int composite_node::same(node *nd)
5139 return ci == ((composite_node *)nd)->ci
5140 && same_node_list(n, ((composite_node *)nd)->n);
5143 const char *composite_node::type()
5145 return "composite_node";
5148 int composite_node::force_tprint()
5150 return 0;
5153 int glyph_node::same(node *nd)
5155 return ci == ((glyph_node *)nd)->ci
5156 && tf == ((glyph_node *)nd)->tf
5157 && gcol == ((glyph_node *)nd)->gcol
5158 && fcol == ((glyph_node *)nd)->fcol;
5161 const char *glyph_node::type()
5163 return "glyph_node";
5166 int glyph_node::force_tprint()
5168 return 0;
5171 int ligature_node::same(node *nd)
5173 return (same_node(n1, ((ligature_node *)nd)->n1)
5174 && same_node(n2, ((ligature_node *)nd)->n2)
5175 && glyph_node::same(nd));
5178 const char *ligature_node::type()
5180 return "ligature_node";
5183 int ligature_node::force_tprint()
5185 return 0;
5188 int kern_pair_node::same(node *nd)
5190 return (amount == ((kern_pair_node *)nd)->amount
5191 && same_node(n1, ((kern_pair_node *)nd)->n1)
5192 && same_node(n2, ((kern_pair_node *)nd)->n2));
5195 const char *kern_pair_node::type()
5197 return "kern_pair_node";
5200 int kern_pair_node::force_tprint()
5202 return 0;
5205 int dbreak_node::same(node *nd)
5207 return (same_node_list(none, ((dbreak_node *)nd)->none)
5208 && same_node_list(pre, ((dbreak_node *)nd)->pre)
5209 && same_node_list(post, ((dbreak_node *)nd)->post));
5212 const char *dbreak_node::type()
5214 return "dbreak_node";
5217 int dbreak_node::force_tprint()
5219 return 0;
5222 int break_char_node::same(node *nd)
5224 return break_code == ((break_char_node *)nd)->break_code
5225 && col == ((break_char_node *)nd)->col
5226 && same_node(ch, ((break_char_node *)nd)->ch);
5229 const char *break_char_node::type()
5231 return "break_char_node";
5234 int break_char_node::force_tprint()
5236 return 0;
5239 int line_start_node::same(node * /*nd*/)
5241 return 1;
5244 const char *line_start_node::type()
5246 return "line_start_node";
5249 int line_start_node::force_tprint()
5251 return 0;
5254 int space_node::same(node *nd)
5256 return n == ((space_node *)nd)->n
5257 && set == ((space_node *)nd)->set
5258 && col == ((space_node *)nd)->col;
5261 const char *space_node::type()
5263 return "space_node";
5266 int word_space_node::same(node *nd)
5268 return n == ((word_space_node *)nd)->n
5269 && set == ((word_space_node *)nd)->set
5270 && col == ((word_space_node *)nd)->col;
5273 const char *word_space_node::type()
5275 return "word_space_node";
5278 int word_space_node::force_tprint()
5280 return 0;
5283 void unbreakable_space_node::tprint(troff_output_file *out)
5285 out->fill_color(col);
5286 if (is_html) {
5287 // we emit the space width as a negative glyph index
5288 out->flush_tbuf();
5289 out->do_motion();
5290 out->put('N');
5291 out->put(-n.to_units());
5292 out->put('\n');
5294 out->right(n);
5297 int unbreakable_space_node::same(node *nd)
5299 return n == ((unbreakable_space_node *)nd)->n
5300 && set == ((unbreakable_space_node *)nd)->set
5301 && col == ((unbreakable_space_node *)nd)->col;
5304 const char *unbreakable_space_node::type()
5306 return "unbreakable_space_node";
5309 node *unbreakable_space_node::add_self(node *n, hyphen_list **p)
5311 next = n;
5312 hyphen_list *pp = *p;
5313 *p = (*p)->next;
5314 delete pp;
5315 return this;
5318 hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail, int *)
5320 return new hyphen_list(0, tail);
5323 int diverted_space_node::same(node *nd)
5325 return n == ((diverted_space_node *)nd)->n;
5328 const char *diverted_space_node::type()
5330 return "diverted_space_node";
5333 int diverted_space_node::force_tprint()
5335 return 0;
5338 int diverted_copy_file_node::same(node *nd)
5340 return filename == ((diverted_copy_file_node *)nd)->filename;
5343 const char *diverted_copy_file_node::type()
5345 return "diverted_copy_file_node";
5348 int diverted_copy_file_node::force_tprint()
5350 return 0;
5353 // Grow the font_table so that its size is > n.
5355 static void grow_font_table(int n)
5357 assert(n >= font_table_size);
5358 font_info **old_font_table = font_table;
5359 int old_font_table_size = font_table_size;
5360 font_table_size = font_table_size ? (font_table_size*3)/2 : 10;
5361 if (font_table_size <= n)
5362 font_table_size = n + 10;
5363 font_table = new font_info *[font_table_size];
5364 if (old_font_table_size)
5365 memcpy(font_table, old_font_table,
5366 old_font_table_size*sizeof(font_info *));
5367 a_delete old_font_table;
5368 for (int i = old_font_table_size; i < font_table_size; i++)
5369 font_table[i] = 0;
5372 dictionary font_translation_dictionary(17);
5374 static symbol get_font_translation(symbol nm)
5376 void *p = font_translation_dictionary.lookup(nm);
5377 return p ? symbol((char *)p) : nm;
5380 dictionary font_dictionary(50);
5382 static int mount_font_no_translate(int n, symbol name, symbol external_name)
5384 assert(n >= 0);
5385 // We store the address of this char in font_dictionary to indicate
5386 // that we've previously tried to mount the font and failed.
5387 static char a_char;
5388 font *fm = 0;
5389 void *p = font_dictionary.lookup(external_name);
5390 if (p == 0) {
5391 int not_found;
5392 fm = font::load_font(external_name.contents(), &not_found);
5393 if (!fm) {
5394 if (not_found)
5395 warning(WARN_FONT, "can't find font `%1'", external_name.contents());
5396 (void)font_dictionary.lookup(external_name, &a_char);
5397 return 0;
5399 (void)font_dictionary.lookup(name, fm);
5401 else if (p == &a_char) {
5402 #if 0
5403 error("invalid font `%1'", external_name.contents());
5404 #endif
5405 return 0;
5407 else
5408 fm = (font*)p;
5409 if (n >= font_table_size) {
5410 if (n - font_table_size > 1000) {
5411 error("font position too much larger than first unused position");
5412 return 0;
5414 grow_font_table(n);
5416 else if (font_table[n] != 0)
5417 delete font_table[n];
5418 font_table[n] = new font_info(name, n, external_name, fm);
5419 font_family::invalidate_fontno(n);
5420 return 1;
5423 int mount_font(int n, symbol name, symbol external_name)
5425 assert(n >= 0);
5426 name = get_font_translation(name);
5427 if (external_name.is_null())
5428 external_name = name;
5429 else
5430 external_name = get_font_translation(external_name);
5431 return mount_font_no_translate(n, name, external_name);
5434 void mount_style(int n, symbol name)
5436 assert(n >= 0);
5437 if (n >= font_table_size) {
5438 if (n - font_table_size > 1000) {
5439 error("font position too much larger than first unused position");
5440 return;
5442 grow_font_table(n);
5444 else if (font_table[n] != 0)
5445 delete font_table[n];
5446 font_table[n] = new font_info(get_font_translation(name), n, NULL_SYMBOL, 0);
5447 font_family::invalidate_fontno(n);
5450 /* global functions */
5452 void font_translate()
5454 symbol from = get_name(1);
5455 if (!from.is_null()) {
5456 symbol to = get_name();
5457 if (to.is_null() || from == to)
5458 font_translation_dictionary.remove(from);
5459 else
5460 (void)font_translation_dictionary.lookup(from, (void *)to.contents());
5462 skip_line();
5465 void font_position()
5467 int n;
5468 if (get_integer(&n)) {
5469 if (n < 0)
5470 error("negative font position");
5471 else {
5472 symbol internal_name = get_name(1);
5473 if (!internal_name.is_null()) {
5474 symbol external_name = get_long_name();
5475 mount_font(n, internal_name, external_name); // ignore error
5479 skip_line();
5482 font_family::font_family(symbol s)
5483 : map_size(10), nm(s)
5485 map = new int[map_size];
5486 for (int i = 0; i < map_size; i++)
5487 map[i] = -1;
5490 font_family::~font_family()
5492 a_delete map;
5495 int font_family::make_definite(int i)
5497 if (i >= 0) {
5498 if (i < map_size && map[i] >= 0)
5499 return map[i];
5500 else {
5501 if (i < font_table_size && font_table[i] != 0) {
5502 if (i >= map_size) {
5503 int old_map_size = map_size;
5504 int *old_map = map;
5505 map_size *= 3;
5506 map_size /= 2;
5507 if (i >= map_size)
5508 map_size = i + 10;
5509 map = new int[map_size];
5510 memcpy(map, old_map, old_map_size*sizeof(int));
5511 a_delete old_map;
5512 for (int j = old_map_size; j < map_size; j++)
5513 map[j] = -1;
5515 if (font_table[i]->is_style()) {
5516 symbol sty = font_table[i]->get_name();
5517 symbol f = concat(nm, sty);
5518 int n;
5519 // don't use symbol_fontno, because that might return a style
5520 // and because we don't want to translate the name
5521 for (n = 0; n < font_table_size; n++)
5522 if (font_table[n] != 0 && font_table[n]->is_named(f)
5523 && !font_table[n]->is_style())
5524 break;
5525 if (n >= font_table_size) {
5526 n = next_available_font_position();
5527 if (!mount_font_no_translate(n, f, f))
5528 return -1;
5530 return map[i] = n;
5532 else
5533 return map[i] = i;
5535 else
5536 return -1;
5539 else
5540 return -1;
5543 dictionary family_dictionary(5);
5545 font_family *lookup_family(symbol nm)
5547 font_family *f = (font_family *)family_dictionary.lookup(nm);
5548 if (!f) {
5549 f = new font_family(nm);
5550 (void)family_dictionary.lookup(nm, f);
5552 return f;
5555 void font_family::invalidate_fontno(int n)
5557 assert(n >= 0 && n < font_table_size);
5558 dictionary_iterator iter(family_dictionary);
5559 symbol nm;
5560 font_family *fam;
5561 while (iter.get(&nm, (void **)&fam)) {
5562 int map_size = fam->map_size;
5563 if (n < map_size)
5564 fam->map[n] = -1;
5565 for (int i = 0; i < map_size; i++)
5566 if (fam->map[i] == n)
5567 fam->map[i] = -1;
5571 void style()
5573 int n;
5574 if (get_integer(&n)) {
5575 if (n < 0)
5576 error("negative font position");
5577 else {
5578 symbol internal_name = get_name(1);
5579 if (!internal_name.is_null())
5580 mount_style(n, internal_name);
5583 skip_line();
5586 static int get_fontno()
5588 int n;
5589 tok.skip();
5590 if (tok.delimiter()) {
5591 symbol s = get_name(1);
5592 if (!s.is_null()) {
5593 n = symbol_fontno(s);
5594 if (n < 0) {
5595 n = next_available_font_position();
5596 if (!mount_font(n, s))
5597 return -1;
5599 return curenv->get_family()->make_definite(n);
5602 else if (get_integer(&n)) {
5603 if (n < 0 || n >= font_table_size || font_table[n] == 0)
5604 error("bad font number");
5605 else
5606 return curenv->get_family()->make_definite(n);
5608 return -1;
5611 static int underline_fontno = 2;
5613 void underline_font()
5615 int n = get_fontno();
5616 if (n >= 0)
5617 underline_fontno = n;
5618 skip_line();
5621 int get_underline_fontno()
5623 return underline_fontno;
5626 void define_font_special_character()
5628 int n = get_fontno();
5629 if (n < 0) {
5630 skip_line();
5631 return;
5633 symbol f = font_table[n]->get_name();
5634 do_define_character(CHAR_FONT_SPECIAL, f.contents());
5637 void remove_font_special_character()
5639 int n = get_fontno();
5640 if (n < 0) {
5641 skip_line();
5642 return;
5644 symbol f = font_table[n]->get_name();
5645 while (!tok.newline() && !tok.eof()) {
5646 if (!tok.space() && !tok.tab()) {
5647 charinfo *s = tok.get_char(1);
5648 string gl(f.contents());
5649 gl += ' ';
5650 gl += s->nm.contents();
5651 gl += '\0';
5652 charinfo *ci = get_charinfo(symbol(gl.contents()));
5653 if (!ci)
5654 break;
5655 macro *m = ci->set_macro(0);
5656 if (m)
5657 delete m;
5659 tok.next();
5661 skip_line();
5664 static void read_special_fonts(special_font_list **sp)
5666 special_font_list *s = *sp;
5667 *sp = 0;
5668 while (s != 0) {
5669 special_font_list *tem = s;
5670 s = s->next;
5671 delete tem;
5673 special_font_list **p = sp;
5674 while (has_arg()) {
5675 int i = get_fontno();
5676 if (i >= 0) {
5677 special_font_list *tem = new special_font_list;
5678 tem->n = i;
5679 tem->next = 0;
5680 *p = tem;
5681 p = &(tem->next);
5686 void font_special_request()
5688 int n = get_fontno();
5689 if (n >= 0)
5690 read_special_fonts(&font_table[n]->sf);
5691 skip_line();
5694 void special_request()
5696 read_special_fonts(&global_special_fonts);
5697 skip_line();
5700 int next_available_font_position()
5702 int i;
5703 for (i = 1; i < font_table_size && font_table[i] != 0; i++)
5705 return i;
5708 int symbol_fontno(symbol s)
5710 s = get_font_translation(s);
5711 for (int i = 0; i < font_table_size; i++)
5712 if (font_table[i] != 0 && font_table[i]->is_named(s))
5713 return i;
5714 return -1;
5717 int is_good_fontno(int n)
5719 return n >= 0 && n < font_table_size && font_table[n] != 0;
5722 int get_bold_fontno(int n)
5724 if (n >= 0 && n < font_table_size && font_table[n] != 0) {
5725 hunits offset;
5726 if (font_table[n]->get_bold(&offset))
5727 return offset.to_units() + 1;
5728 else
5729 return 0;
5731 else
5732 return 0;
5735 hunits env_digit_width(environment *env)
5737 node *n = make_glyph_node(charset_table['0'], env);
5738 if (n) {
5739 hunits x = n->width();
5740 delete n;
5741 return x;
5743 else
5744 return H0;
5747 hunits env_space_width(environment *env)
5749 int fn = env_definite_font(env);
5750 font_size fs = env->get_font_size();
5751 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5752 return scale(fs.to_units()/3, env->get_space_size(), 12);
5753 else
5754 return font_table[fn]->get_space_width(fs, env->get_space_size());
5757 hunits env_sentence_space_width(environment *env)
5759 int fn = env_definite_font(env);
5760 font_size fs = env->get_font_size();
5761 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5762 return scale(fs.to_units()/3, env->get_sentence_space_size(), 12);
5763 else
5764 return font_table[fn]->get_space_width(fs, env->get_sentence_space_size());
5767 hunits env_half_narrow_space_width(environment *env)
5769 int fn = env_definite_font(env);
5770 font_size fs = env->get_font_size();
5771 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5772 return 0;
5773 else
5774 return font_table[fn]->get_half_narrow_space_width(fs);
5777 hunits env_narrow_space_width(environment *env)
5779 int fn = env_definite_font(env);
5780 font_size fs = env->get_font_size();
5781 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5782 return 0;
5783 else
5784 return font_table[fn]->get_narrow_space_width(fs);
5787 void bold_font()
5789 int n = get_fontno();
5790 if (n >= 0) {
5791 if (has_arg()) {
5792 if (tok.delimiter()) {
5793 int f = get_fontno();
5794 if (f >= 0) {
5795 units offset;
5796 if (has_arg() && get_number(&offset, 'u') && offset >= 1)
5797 font_table[f]->set_conditional_bold(n, hunits(offset - 1));
5798 else
5799 font_table[f]->conditional_unbold(n);
5802 else {
5803 units offset;
5804 if (get_number(&offset, 'u') && offset >= 1)
5805 font_table[n]->set_bold(hunits(offset - 1));
5806 else
5807 font_table[n]->unbold();
5810 else
5811 font_table[n]->unbold();
5813 skip_line();
5816 track_kerning_function::track_kerning_function() : non_zero(0)
5820 track_kerning_function::track_kerning_function(int min_s, hunits min_a,
5821 int max_s, hunits max_a)
5822 : non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s),
5823 max_amount(max_a)
5827 int track_kerning_function::operator==(const track_kerning_function &tk)
5829 if (non_zero)
5830 return (tk.non_zero
5831 && min_size == tk.min_size
5832 && min_amount == tk.min_amount
5833 && max_size == tk.max_size
5834 && max_amount == tk.max_amount);
5835 else
5836 return !tk.non_zero;
5839 int track_kerning_function::operator!=(const track_kerning_function &tk)
5841 if (non_zero)
5842 return (!tk.non_zero
5843 || min_size != tk.min_size
5844 || min_amount != tk.min_amount
5845 || max_size != tk.max_size
5846 || max_amount != tk.max_amount);
5847 else
5848 return tk.non_zero;
5851 hunits track_kerning_function::compute(int size)
5853 if (non_zero) {
5854 if (max_size <= min_size)
5855 return min_amount;
5856 else if (size <= min_size)
5857 return min_amount;
5858 else if (size >= max_size)
5859 return max_amount;
5860 else
5861 return (scale(max_amount, size - min_size, max_size - min_size)
5862 + scale(min_amount, max_size - size, max_size - min_size));
5864 else
5865 return H0;
5868 void track_kern()
5870 int n = get_fontno();
5871 if (n >= 0) {
5872 int min_s, max_s;
5873 hunits min_a, max_a;
5874 if (has_arg()
5875 && get_number(&min_s, 'z')
5876 && get_hunits(&min_a, 'p')
5877 && get_number(&max_s, 'z')
5878 && get_hunits(&max_a, 'p')) {
5879 track_kerning_function tk(min_s, min_a, max_s, max_a);
5880 font_table[n]->set_track_kern(tk);
5882 else {
5883 track_kerning_function tk;
5884 font_table[n]->set_track_kern(tk);
5887 skip_line();
5890 void constant_space()
5892 int n = get_fontno();
5893 if (n >= 0) {
5894 int x, y;
5895 if (!has_arg() || !get_integer(&x))
5896 font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
5897 else {
5898 if (!has_arg() || !get_number(&y, 'z'))
5899 font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
5900 else
5901 font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
5902 scale(y*x,
5903 units_per_inch,
5904 36*72*sizescale));
5907 skip_line();
5910 void ligature()
5912 int lig;
5913 if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2)
5914 global_ligature_mode = lig;
5915 else
5916 global_ligature_mode = 1;
5917 skip_line();
5920 void kern_request()
5922 int k;
5923 if (has_arg() && get_integer(&k))
5924 global_kern_mode = k != 0;
5925 else
5926 global_kern_mode = 1;
5927 skip_line();
5930 void set_soft_hyphen_char()
5932 soft_hyphen_char = get_optional_char();
5933 if (!soft_hyphen_char)
5934 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
5935 skip_line();
5938 void init_output()
5940 if (suppress_output_flag)
5941 the_output = new suppress_output_file;
5942 else if (ascii_output_flag)
5943 the_output = new ascii_output_file;
5944 else
5945 the_output = new troff_output_file;
5948 class next_available_font_position_reg : public reg {
5949 public:
5950 const char *get_string();
5953 const char *next_available_font_position_reg::get_string()
5955 return i_to_a(next_available_font_position());
5958 class printing_reg : public reg {
5959 public:
5960 const char *get_string();
5963 const char *printing_reg::get_string()
5965 if (the_output)
5966 return the_output->is_printing() ? "1" : "0";
5967 else
5968 return "0";
5971 void init_node_requests()
5973 init_request("bd", bold_font);
5974 init_request("cs", constant_space);
5975 init_request("fp", font_position);
5976 init_request("fschar", define_font_special_character);
5977 init_request("fspecial", font_special_request);
5978 init_request("ftr", font_translate);
5979 init_request("kern", kern_request);
5980 init_request("lg", ligature);
5981 init_request("rfschar", remove_font_special_character);
5982 init_request("shc", set_soft_hyphen_char);
5983 init_request("special", special_request);
5984 init_request("sty", style);
5985 init_request("tkf", track_kern);
5986 init_request("uf", underline_font);
5987 number_reg_dictionary.define(".fp", new next_available_font_position_reg);
5988 number_reg_dictionary.define(".kern",
5989 new constant_int_reg(&global_kern_mode));
5990 number_reg_dictionary.define(".lg",
5991 new constant_int_reg(&global_ligature_mode));
5992 number_reg_dictionary.define(".P", new printing_reg);
5993 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);