* Makefile.in (SEP): Replaced with...
[s-roff.git] / src / roff / troff / node.cpp
blobfd2637565338378ba5e3e6ac0ee7f9880a2aba0b
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22 #include "troff.h"
24 #ifdef HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
28 #include "dictionary.h"
29 #include "hvunits.h"
30 #include "env.h"
31 #include "request.h"
32 #include "node.h"
33 #include "token.h"
34 #include "charinfo.h"
35 #include "font.h"
36 #include "reg.h"
37 #include "input.h"
38 #include "div.h"
39 #include "geometry.h"
40 #include "stringclass.h"
42 #include "nonposix.h"
44 #ifdef _POSIX_VERSION
46 #include <sys/wait.h>
48 #else /* not _POSIX_VERSION */
50 /* traditional Unix */
52 #define WIFEXITED(s) (((s) & 0377) == 0)
53 #define WEXITSTATUS(s) (((s) >> 8) & 0377)
54 #define WTERMSIG(s) ((s) & 0177)
55 #define WIFSTOPPED(s) (((s) & 0377) == 0177)
56 #define WSTOPSIG(s) (((s) >> 8) & 0377)
57 #define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
59 #endif /* not _POSIX_VERSION */
62 * how many boundaries of images have been written? Useful for
63 * debugging grohtml
66 int image_no = 0;
67 static int suppress_start_page = 0;
69 #define STORE_WIDTH 1
71 symbol HYPHEN_SYMBOL("hy");
73 // Character used when a hyphen is inserted at a line break.
74 static charinfo *soft_hyphen_char;
76 enum constant_space_type {
77 CONSTANT_SPACE_NONE,
78 CONSTANT_SPACE_RELATIVE,
79 CONSTANT_SPACE_ABSOLUTE
82 struct special_font_list {
83 int n;
84 special_font_list *next;
87 special_font_list *global_special_fonts;
88 static int global_ligature_mode = 1;
89 static int global_kern_mode = 1;
91 class track_kerning_function {
92 int non_zero;
93 units min_size;
94 hunits min_amount;
95 units max_size;
96 hunits max_amount;
97 public:
98 track_kerning_function();
99 track_kerning_function(units, hunits, units, hunits);
100 int operator==(const track_kerning_function &);
101 int operator!=(const track_kerning_function &);
102 hunits compute(int point_size);
105 // embolden fontno when this is the current font
107 struct conditional_bold {
108 conditional_bold *next;
109 int fontno;
110 hunits offset;
111 conditional_bold(int, hunits, conditional_bold * = 0);
114 struct tfont;
116 class font_info {
117 tfont *last_tfont;
118 int number;
119 font_size last_size;
120 int last_height;
121 int last_slant;
122 symbol internal_name;
123 symbol external_name;
124 font *fm;
125 char is_bold;
126 hunits bold_offset;
127 track_kerning_function track_kern;
128 constant_space_type is_constant_spaced;
129 units constant_space;
130 int last_ligature_mode;
131 int last_kern_mode;
132 conditional_bold *cond_bold_list;
133 void flush();
134 public:
135 special_font_list *sf;
136 font_info(symbol nm, int n, symbol enm, font *f);
137 int contains(charinfo *);
138 void set_bold(hunits);
139 void unbold();
140 void set_conditional_bold(int, hunits);
141 void conditional_unbold(int);
142 void set_track_kern(track_kerning_function &);
143 void set_constant_space(constant_space_type, units = 0);
144 int is_named(symbol);
145 symbol get_name();
146 tfont *get_tfont(font_size, int, int, int);
147 hunits get_space_width(font_size, int);
148 hunits get_narrow_space_width(font_size);
149 hunits get_half_narrow_space_width(font_size);
150 int get_bold(hunits *);
151 int is_special();
152 int is_style();
153 friend symbol get_font_name(int, environment *);
156 class tfont_spec {
157 protected:
158 symbol name;
159 int input_position;
160 font *fm;
161 font_size size;
162 char is_bold;
163 char is_constant_spaced;
164 int ligature_mode;
165 int kern_mode;
166 hunits bold_offset;
167 hunits track_kern; // add this to the width
168 hunits constant_space_width;
169 int height;
170 int slant;
171 public:
172 tfont_spec(symbol nm, int pos, font *, font_size, int, int);
173 tfont_spec(const tfont_spec &spec) { *this = spec; }
174 tfont_spec plain();
175 int operator==(const tfont_spec &);
176 friend tfont *font_info::get_tfont(font_size fs, int, int, int);
179 class tfont : public tfont_spec {
180 static tfont *tfont_list;
181 tfont *next;
182 tfont *plain_version;
183 public:
184 tfont(tfont_spec &);
185 int contains(charinfo *);
186 hunits get_width(charinfo *c);
187 int get_bold(hunits *);
188 int get_constant_space(hunits *);
189 hunits get_track_kern();
190 tfont *get_plain();
191 font_size get_size();
192 symbol get_name();
193 charinfo *get_lig(charinfo *c1, charinfo *c2);
194 int get_kern(charinfo *c1, charinfo *c2, hunits *res);
195 int get_input_position();
196 int get_character_type(charinfo *);
197 int get_height();
198 int get_slant();
199 vunits get_char_height(charinfo *);
200 vunits get_char_depth(charinfo *);
201 hunits get_char_skew(charinfo *);
202 hunits get_italic_correction(charinfo *);
203 hunits get_left_italic_correction(charinfo *);
204 hunits get_subscript_correction(charinfo *);
205 friend tfont *make_tfont(tfont_spec &);
208 inline int env_definite_font(environment *env)
210 return env->get_family()->make_definite(env->get_font());
213 /* font_info functions */
215 static font_info **font_table = 0;
216 static int font_table_size = 0;
218 font_info::font_info(symbol nm, int n, symbol enm, font *f)
219 : last_tfont(0), number(n), last_size(0),
220 internal_name(nm), external_name(enm), fm(f),
221 is_bold(0), is_constant_spaced(CONSTANT_SPACE_NONE), last_ligature_mode(1),
222 last_kern_mode(1), cond_bold_list(0), sf(0)
226 inline int font_info::contains(charinfo *ci)
228 return fm != 0 && fm->contains(ci->get_index());
231 inline int font_info::is_special()
233 return fm != 0 && fm->is_special();
236 inline int font_info::is_style()
238 return fm == 0;
241 tfont *make_tfont(tfont_spec &spec)
243 for (tfont *p = tfont::tfont_list; p; p = p->next)
244 if (*p == spec)
245 return p;
246 return new tfont(spec);
249 // this is the current_font, fontno is where we found the character,
250 // presumably a special font
252 tfont *font_info::get_tfont(font_size fs, int height, int slant, int fontno)
254 if (last_tfont == 0 || fs != last_size
255 || height != last_height || slant != last_slant
256 || global_ligature_mode != last_ligature_mode
257 || global_kern_mode != last_kern_mode
258 || fontno != number) {
259 font_info *f = font_table[fontno];
260 tfont_spec spec(f->external_name, f->number, f->fm, fs, height, slant);
261 for (conditional_bold *p = cond_bold_list; p; p = p->next)
262 if (p->fontno == fontno) {
263 spec.is_bold = 1;
264 spec.bold_offset = p->offset;
265 break;
267 if (!spec.is_bold && is_bold) {
268 spec.is_bold = 1;
269 spec.bold_offset = bold_offset;
271 spec.track_kern = track_kern.compute(fs.to_scaled_points());
272 spec.ligature_mode = global_ligature_mode;
273 spec.kern_mode = global_kern_mode;
274 switch (is_constant_spaced) {
275 case CONSTANT_SPACE_NONE:
276 break;
277 case CONSTANT_SPACE_ABSOLUTE:
278 spec.is_constant_spaced = 1;
279 spec.constant_space_width = constant_space;
280 break;
281 case CONSTANT_SPACE_RELATIVE:
282 spec.is_constant_spaced = 1;
283 spec.constant_space_width
284 = scale(constant_space*fs.to_scaled_points(),
285 units_per_inch,
286 36*72*sizescale);
287 break;
288 default:
289 assert(0);
291 if (fontno != number)
292 return make_tfont(spec);
293 last_tfont = make_tfont(spec);
294 last_size = fs;
295 last_height = height;
296 last_slant = slant;
297 last_ligature_mode = global_ligature_mode;
298 last_kern_mode = global_kern_mode;
300 return last_tfont;
303 int font_info::get_bold(hunits *res)
305 if (is_bold) {
306 *res = bold_offset;
307 return 1;
309 else
310 return 0;
313 void font_info::unbold()
315 if (is_bold) {
316 is_bold = 0;
317 flush();
321 void font_info::set_bold(hunits offset)
323 if (!is_bold || offset != bold_offset) {
324 is_bold = 1;
325 bold_offset = offset;
326 flush();
330 void font_info::set_conditional_bold(int fontno, hunits offset)
332 for (conditional_bold *p = cond_bold_list; p; p = p->next)
333 if (p->fontno == fontno) {
334 if (offset != p->offset) {
335 p->offset = offset;
336 flush();
338 return;
340 cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
343 conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
344 : next(x), fontno(f), offset(h)
348 void font_info::conditional_unbold(int fontno)
350 for (conditional_bold **p = &cond_bold_list; *p; p = &(*p)->next)
351 if ((*p)->fontno == fontno) {
352 conditional_bold *tem = *p;
353 *p = (*p)->next;
354 delete tem;
355 flush();
356 return;
360 void font_info::set_constant_space(constant_space_type type, units x)
362 if (type != is_constant_spaced
363 || (type != CONSTANT_SPACE_NONE && x != constant_space)) {
364 flush();
365 is_constant_spaced = type;
366 constant_space = x;
370 void font_info::set_track_kern(track_kerning_function &tk)
372 if (track_kern != tk) {
373 track_kern = tk;
374 flush();
378 void font_info::flush()
380 last_tfont = 0;
383 int font_info::is_named(symbol s)
385 return internal_name == s;
388 symbol font_info::get_name()
390 return internal_name;
393 symbol get_font_name(int fontno, environment *env)
395 symbol f = font_table[fontno]->get_name();
396 if (font_table[fontno]->is_style()) {
397 return concat(env->get_family()->nm, f);
399 return f;
402 hunits font_info::get_space_width(font_size fs, int space_size)
404 if (is_constant_spaced == CONSTANT_SPACE_NONE)
405 return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
406 space_size, 12);
407 else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
408 return constant_space;
409 else
410 return scale(constant_space*fs.to_scaled_points(),
411 units_per_inch, 36*72*sizescale);
414 hunits font_info::get_narrow_space_width(font_size fs)
416 charinfo *ci = get_charinfo(symbol("|"));
417 if (fm->contains(ci->get_index()))
418 return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
419 else
420 return hunits(fs.to_units()/6);
423 hunits font_info::get_half_narrow_space_width(font_size fs)
425 charinfo *ci = get_charinfo(symbol("^"));
426 if (fm->contains(ci->get_index()))
427 return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
428 else
429 return hunits(fs.to_units()/12);
432 /* tfont */
434 tfont_spec::tfont_spec(symbol nm, int n, font *f,
435 font_size s, int h, int sl)
436 : name(nm), input_position(n), fm(f), size(s),
437 is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1),
438 height(h), slant(sl)
440 if (height == size.to_scaled_points())
441 height = 0;
444 int tfont_spec::operator==(const tfont_spec &spec)
446 if (fm == spec.fm
447 && size == spec.size
448 && input_position == spec.input_position
449 && name == spec.name
450 && height == spec.height
451 && slant == spec.slant
452 && (is_bold
453 ? (spec.is_bold && bold_offset == spec.bold_offset)
454 : !spec.is_bold)
455 && track_kern == spec.track_kern
456 && (is_constant_spaced
457 ? (spec.is_constant_spaced
458 && constant_space_width == spec.constant_space_width)
459 : !spec.is_constant_spaced)
460 && ligature_mode == spec.ligature_mode
461 && kern_mode == spec.kern_mode)
462 return 1;
463 else
464 return 0;
467 tfont_spec tfont_spec::plain()
469 return tfont_spec(name, input_position, fm, size, height, slant);
472 hunits tfont::get_width(charinfo *c)
474 if (is_constant_spaced)
475 return constant_space_width;
476 else if (is_bold)
477 return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
478 + track_kern + bold_offset);
479 else
480 return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
481 + track_kern);
484 vunits tfont::get_char_height(charinfo *c)
486 vunits v = fm->get_height(c->get_index(), size.to_scaled_points());
487 if (height != 0 && height != size.to_scaled_points())
488 return scale(v, height, size.to_scaled_points());
489 else
490 return v;
493 vunits tfont::get_char_depth(charinfo *c)
495 vunits v = fm->get_depth(c->get_index(), size.to_scaled_points());
496 if (height != 0 && height != size.to_scaled_points())
497 return scale(v, height, size.to_scaled_points());
498 else
499 return v;
502 hunits tfont::get_char_skew(charinfo *c)
504 return hunits(fm->get_skew(c->get_index(), size.to_scaled_points(), slant));
507 hunits tfont::get_italic_correction(charinfo *c)
509 return hunits(fm->get_italic_correction(c->get_index(), size.to_scaled_points()));
512 hunits tfont::get_left_italic_correction(charinfo *c)
514 return hunits(fm->get_left_italic_correction(c->get_index(),
515 size.to_scaled_points()));
518 hunits tfont::get_subscript_correction(charinfo *c)
520 return hunits(fm->get_subscript_correction(c->get_index(),
521 size.to_scaled_points()));
524 inline int tfont::get_input_position()
526 return input_position;
529 inline int tfont::contains(charinfo *ci)
531 return fm->contains(ci->get_index());
534 inline int tfont::get_character_type(charinfo *ci)
536 return fm->get_character_type(ci->get_index());
539 inline int tfont::get_bold(hunits *res)
541 if (is_bold) {
542 *res = bold_offset;
543 return 1;
545 else
546 return 0;
549 inline int tfont::get_constant_space(hunits *res)
551 if (is_constant_spaced) {
552 *res = constant_space_width;
553 return 1;
555 else
556 return 0;
559 inline hunits tfont::get_track_kern()
561 return track_kern;
564 inline tfont *tfont::get_plain()
566 return plain_version;
569 inline font_size tfont::get_size()
571 return size;
574 inline symbol tfont::get_name()
576 return name;
579 inline int tfont::get_height()
581 return height;
584 inline int tfont::get_slant()
586 return slant;
589 symbol SYMBOL_ff("ff");
590 symbol SYMBOL_fi("fi");
591 symbol SYMBOL_fl("fl");
592 symbol SYMBOL_Fi("Fi");
593 symbol SYMBOL_Fl("Fl");
595 charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
597 if (ligature_mode == 0)
598 return 0;
599 charinfo *ci = 0;
600 if (c1->get_ascii_code() == 'f') {
601 switch (c2->get_ascii_code()) {
602 case 'f':
603 if (fm->has_ligature(font::LIG_ff))
604 ci = get_charinfo(SYMBOL_ff);
605 break;
606 case 'i':
607 if (fm->has_ligature(font::LIG_fi))
608 ci = get_charinfo(SYMBOL_fi);
609 break;
610 case 'l':
611 if (fm->has_ligature(font::LIG_fl))
612 ci = get_charinfo(SYMBOL_fl);
613 break;
616 else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
617 switch (c2->get_ascii_code()) {
618 case 'i':
619 if (fm->has_ligature(font::LIG_ffi))
620 ci = get_charinfo(SYMBOL_Fi);
621 break;
622 case 'l':
623 if (fm->has_ligature(font::LIG_ffl))
624 ci = get_charinfo(SYMBOL_Fl);
625 break;
628 if (ci != 0 && fm->contains(ci->get_index()))
629 return ci;
630 return 0;
633 inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res)
635 if (kern_mode == 0)
636 return 0;
637 else {
638 int n = fm->get_kern(c1->get_index(),
639 c2->get_index(),
640 size.to_scaled_points());
641 if (n) {
642 *res = hunits(n);
643 return 1;
645 else
646 return 0;
650 tfont *tfont::tfont_list = 0;
652 tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
654 next = tfont_list;
655 tfont_list = this;
656 tfont_spec plain_spec = plain();
657 tfont *p;
658 for (p = tfont_list; p; p = p->next)
659 if (*p == plain_spec) {
660 plain_version = p;
661 break;
663 if (!p)
664 plain_version = new tfont(plain_spec);
667 /* output_file */
669 class real_output_file : public output_file {
670 #ifndef POPEN_MISSING
671 int piped;
672 #endif
673 int printing; // decision via optional page list
674 int output_on; // \O[0] or \O[1] escape calls
675 virtual void really_transparent_char(unsigned char) = 0;
676 virtual void really_print_line(hunits x, vunits y, node *n,
677 vunits before, vunits after, hunits width) = 0;
678 virtual void really_begin_page(int pageno, vunits page_length) = 0;
679 virtual void really_copy_file(hunits x, vunits y, const char *filename);
680 virtual void really_put_filename(const char *filename);
681 virtual void really_on();
682 virtual void really_off();
683 protected:
684 FILE *fp;
685 public:
686 real_output_file();
687 ~real_output_file();
688 void flush();
689 void transparent_char(unsigned char);
690 void print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
691 void begin_page(int pageno, vunits page_length);
692 void put_filename(const char *filename);
693 void on();
694 void off();
695 int is_on();
696 int is_printing();
697 void copy_file(hunits x, vunits y, const char *filename);
700 class suppress_output_file : public real_output_file {
701 public:
702 suppress_output_file();
703 void really_transparent_char(unsigned char);
704 void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
705 void really_begin_page(int pageno, vunits page_length);
708 class ascii_output_file : public real_output_file {
709 public:
710 ascii_output_file();
711 void really_transparent_char(unsigned char);
712 void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
713 void really_begin_page(int pageno, vunits page_length);
714 void outc(unsigned char c);
715 void outs(const char *s);
718 void ascii_output_file::outc(unsigned char c)
720 fputc(c, fp);
723 void ascii_output_file::outs(const char *s)
725 fputc('<', fp);
726 if (s)
727 fputs(s, fp);
728 fputc('>', fp);
731 struct hvpair;
733 class troff_output_file : public real_output_file {
734 units hpos;
735 units vpos;
736 units output_vpos;
737 units output_hpos;
738 int force_motion;
739 int current_size;
740 int current_slant;
741 int current_height;
742 tfont *current_tfont;
743 color *current_fill_color;
744 color *current_glyph_color;
745 int current_font_number;
746 symbol *font_position;
747 int nfont_positions;
748 enum { TBUF_SIZE = 256 };
749 char tbuf[TBUF_SIZE];
750 int tbuf_len;
751 int tbuf_kern;
752 int begun_page;
753 void do_motion();
754 void put(char c);
755 void put(unsigned char c);
756 void put(int i);
757 void put(unsigned int i);
758 void put(const char *s);
759 void set_font(tfont *tf);
760 void flush_tbuf();
761 public:
762 troff_output_file();
763 ~troff_output_file();
764 void trailer(vunits page_length);
765 void put_char(charinfo *, tfont *, color *, color *);
766 void put_char_width(charinfo *, tfont *, color *, color *, hunits, hunits);
767 void right(hunits);
768 void down(vunits);
769 void moveto(hunits, vunits);
770 void start_special(tfont *, color *, color *, int = 0);
771 void start_special();
772 void special_char(unsigned char c);
773 void end_special();
774 void word_marker();
775 void really_transparent_char(unsigned char c);
776 void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
777 void really_begin_page(int pageno, vunits page_length);
778 void really_copy_file(hunits x, vunits y, const char *filename);
779 void really_put_filename(const char *filename);
780 void really_on();
781 void really_off();
782 void draw(char, hvpair *, int, font_size, color *, color *);
783 void determine_line_limits (char code, hvpair *point, int npoints);
784 void check_charinfo(tfont *tf, charinfo *ci);
785 void glyph_color(color *c);
786 void fill_color(color *c);
787 int get_hpos() { return hpos; }
788 int get_vpos() { return vpos; }
789 friend void space_char_hmotion_node::tprint(troff_output_file *);
790 friend void unbreakable_space_node::tprint(troff_output_file *);
793 static void put_string(const char *s, FILE *fp)
795 for (; *s != '\0'; ++s)
796 putc(*s, fp);
799 inline void troff_output_file::put(char c)
801 putc(c, fp);
804 inline void troff_output_file::put(unsigned char c)
806 putc(c, fp);
809 inline void troff_output_file::put(const char *s)
811 put_string(s, fp);
814 inline void troff_output_file::put(int i)
816 put_string(i_to_a(i), fp);
819 inline void troff_output_file::put(unsigned int i)
821 put_string(ui_to_a(i), fp);
824 void troff_output_file::start_special(tfont *tf, color *gcol, color *fcol,
825 int no_init_string)
827 set_font(tf);
828 glyph_color(gcol);
829 fill_color(fcol);
830 flush_tbuf();
831 do_motion();
832 if (!no_init_string)
833 put("x X ");
836 void troff_output_file::start_special()
838 flush_tbuf();
839 do_motion();
840 put("x X ");
843 void troff_output_file::special_char(unsigned char c)
845 put(c);
846 if (c == '\n')
847 put('+');
850 void troff_output_file::end_special()
852 put('\n');
855 inline void troff_output_file::moveto(hunits h, vunits v)
857 hpos = h.to_units();
858 vpos = v.to_units();
861 void troff_output_file::really_print_line(hunits x, vunits y, node *n,
862 vunits before, vunits after, hunits)
864 moveto(x, y);
865 while (n != 0) {
866 n->tprint(this);
867 n = n->next;
869 flush_tbuf();
870 // This ensures that transparent throughput will have a more predictable
871 // position.
872 do_motion();
873 force_motion = 1;
874 hpos = 0;
875 put('n');
876 put(before.to_units());
877 put(' ');
878 put(after.to_units());
879 put('\n');
882 inline void troff_output_file::word_marker()
884 flush_tbuf();
885 if (is_on())
886 put('w');
889 inline void troff_output_file::right(hunits n)
891 hpos += n.to_units();
894 inline void troff_output_file::down(vunits n)
896 vpos += n.to_units();
899 void troff_output_file::do_motion()
901 if (force_motion) {
902 put('V');
903 put(vpos);
904 put('\n');
905 put('H');
906 put(hpos);
907 put('\n');
909 else {
910 if (hpos != output_hpos) {
911 units n = hpos - output_hpos;
912 if (n > 0 && n < hpos) {
913 put('h');
914 put(n);
916 else {
917 put('H');
918 put(hpos);
920 put('\n');
922 if (vpos != output_vpos) {
923 units n = vpos - output_vpos;
924 if (n > 0 && n < vpos) {
925 put('v');
926 put(n);
928 else {
929 put('V');
930 put(vpos);
932 put('\n');
935 output_vpos = vpos;
936 output_hpos = hpos;
937 force_motion = 0;
940 void troff_output_file::flush_tbuf()
942 if (!is_on()) {
943 tbuf_len = 0;
944 return;
947 if (tbuf_len == 0)
948 return;
949 if (tbuf_kern == 0)
950 put('t');
951 else {
952 put('u');
953 put(tbuf_kern);
954 put(' ');
956 check_output_limits(hpos, vpos);
957 check_output_limits(hpos, vpos - current_size);
959 for (int i = 0; i < tbuf_len; i++)
960 put(tbuf[i]);
961 put('\n');
962 tbuf_len = 0;
965 void troff_output_file::check_charinfo(tfont *tf, charinfo *ci)
967 if (!is_on())
968 return;
970 int height = tf->get_char_height(ci).to_units();
971 int width = tf->get_width(ci).to_units()
972 + tf->get_italic_correction(ci).to_units();
973 int depth = tf->get_char_depth(ci).to_units();
974 check_output_limits(output_hpos, output_vpos - height);
975 check_output_limits(output_hpos + width, output_vpos + depth);
978 void troff_output_file::put_char_width(charinfo *ci, tfont *tf,
979 color *gcol, color *fcol,
980 hunits w, hunits k)
982 int kk = k.to_units();
983 if (!is_on()) {
984 flush_tbuf();
985 hpos += w.to_units() + kk;
986 return;
988 set_font(tf);
989 unsigned char c = ci->get_ascii_code();
990 if (c == '\0') {
991 glyph_color(gcol);
992 fill_color(fcol);
993 flush_tbuf();
994 do_motion();
995 check_charinfo(tf, ci);
996 if (ci->numbered()) {
997 put('N');
998 put(ci->get_number());
1000 else {
1001 put('C');
1002 const char *s = ci->nm.contents();
1003 if (s[1] == 0) {
1004 put('\\');
1005 put(s[0]);
1007 else
1008 put(s);
1010 put('\n');
1011 hpos += w.to_units() + kk;
1013 else if (tcommand_flag) {
1014 if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
1015 && (!gcol || gcol == current_glyph_color)
1016 && (!fcol || fcol == current_fill_color)
1017 && kk == tbuf_kern
1018 && tbuf_len < TBUF_SIZE) {
1019 check_charinfo(tf, ci);
1020 tbuf[tbuf_len++] = c;
1021 output_hpos += w.to_units() + kk;
1022 hpos = output_hpos;
1023 return;
1025 glyph_color(gcol);
1026 fill_color(fcol);
1027 flush_tbuf();
1028 do_motion();
1029 check_charinfo(tf, ci);
1030 tbuf[tbuf_len++] = c;
1031 output_hpos += w.to_units() + kk;
1032 tbuf_kern = kk;
1033 hpos = output_hpos;
1035 else {
1036 // flush_tbuf();
1037 int n = hpos - output_hpos;
1038 check_charinfo(tf, ci);
1039 // check_output_limits(output_hpos, output_vpos);
1040 if (vpos == output_vpos
1041 && (!gcol || gcol == current_glyph_color)
1042 && (!fcol || fcol == current_fill_color)
1043 && n > 0 && n < 100 && !force_motion) {
1044 put(char(n/10 + '0'));
1045 put(char(n%10 + '0'));
1046 put(c);
1047 output_hpos = hpos;
1049 else {
1050 glyph_color(gcol);
1051 fill_color(fcol);
1052 do_motion();
1053 put('c');
1054 put(c);
1056 hpos += w.to_units() + kk;
1060 void troff_output_file::put_char(charinfo *ci, tfont *tf,
1061 color *gcol, color *fcol)
1063 flush_tbuf();
1064 if (!is_on())
1065 return;
1066 set_font(tf);
1067 unsigned char c = ci->get_ascii_code();
1068 if (c == '\0') {
1069 glyph_color(gcol);
1070 fill_color(fcol);
1071 flush_tbuf();
1072 do_motion();
1073 if (ci->numbered()) {
1074 put('N');
1075 put(ci->get_number());
1077 else {
1078 put('C');
1079 const char *s = ci->nm.contents();
1080 if (s[1] == 0) {
1081 put('\\');
1082 put(s[0]);
1084 else
1085 put(s);
1087 put('\n');
1089 else {
1090 int n = hpos - output_hpos;
1091 if (vpos == output_vpos
1092 && (!gcol || gcol == current_glyph_color)
1093 && (!fcol || fcol == current_fill_color)
1094 && n > 0 && n < 100) {
1095 put(char(n/10 + '0'));
1096 put(char(n%10 + '0'));
1097 put(c);
1098 output_hpos = hpos;
1100 else {
1101 glyph_color(gcol);
1102 fill_color(fcol);
1103 flush_tbuf();
1104 do_motion();
1105 put('c');
1106 put(c);
1111 // set_font calls `flush_tbuf' if necessary.
1113 void troff_output_file::set_font(tfont *tf)
1115 if (current_tfont == tf)
1116 return;
1117 flush_tbuf();
1118 int n = tf->get_input_position();
1119 symbol nm = tf->get_name();
1120 if (n >= nfont_positions || font_position[n] != nm) {
1121 put("x font ");
1122 put(n);
1123 put(' ');
1124 put(nm.contents());
1125 put('\n');
1126 if (n >= nfont_positions) {
1127 int old_nfont_positions = nfont_positions;
1128 symbol *old_font_position = font_position;
1129 nfont_positions *= 3;
1130 nfont_positions /= 2;
1131 if (nfont_positions <= n)
1132 nfont_positions = n + 10;
1133 font_position = new symbol[nfont_positions];
1134 memcpy(font_position, old_font_position,
1135 old_nfont_positions*sizeof(symbol));
1136 a_delete old_font_position;
1138 font_position[n] = nm;
1140 if (current_font_number != n) {
1141 put('f');
1142 put(n);
1143 put('\n');
1144 current_font_number = n;
1146 int size = tf->get_size().to_scaled_points();
1147 if (current_size != size) {
1148 put('s');
1149 put(size);
1150 put('\n');
1151 current_size = size;
1153 int slant = tf->get_slant();
1154 if (current_slant != slant) {
1155 put("x Slant ");
1156 put(slant);
1157 put('\n');
1158 current_slant = slant;
1160 int height = tf->get_height();
1161 if (current_height != height) {
1162 put("x Height ");
1163 put(height == 0 ? current_size : height);
1164 put('\n');
1165 current_height = height;
1167 current_tfont = tf;
1170 // fill_color calls `flush_tbuf' and `do_motion' if necessary.
1172 void troff_output_file::fill_color(color *col)
1174 if (!col || current_fill_color == col)
1175 return;
1176 current_fill_color = col;
1177 if (!color_flag)
1178 return;
1179 flush_tbuf();
1180 do_motion();
1181 put("DF");
1182 unsigned int components[4];
1183 color_scheme cs;
1184 cs = col->get_components(components);
1185 switch (cs) {
1186 case DEFAULT:
1187 put('d');
1188 break;
1189 case RGB:
1190 put("r ");
1191 put(Red);
1192 put(' ');
1193 put(Green);
1194 put(' ');
1195 put(Blue);
1196 break;
1197 case CMY:
1198 put("c ");
1199 put(Cyan);
1200 put(' ');
1201 put(Magenta);
1202 put(' ');
1203 put(Yellow);
1204 break;
1205 case CMYK:
1206 put("k ");
1207 put(Cyan);
1208 put(' ');
1209 put(Magenta);
1210 put(' ');
1211 put(Yellow);
1212 put(' ');
1213 put(Black);
1214 break;
1215 case GRAY:
1216 put("g ");
1217 put(Gray);
1218 break;
1220 put('\n');
1223 // glyph_color calls `flush_tbuf' and `do_motion' if necessary.
1225 void troff_output_file::glyph_color(color *col)
1227 if (!col || current_glyph_color == col)
1228 return;
1229 current_glyph_color = col;
1230 if (!color_flag)
1231 return;
1232 flush_tbuf();
1233 // grotty doesn't like a color command if the vertical position is zero.
1234 do_motion();
1235 put("m");
1236 unsigned int components[4];
1237 color_scheme cs;
1238 cs = col->get_components(components);
1239 switch (cs) {
1240 case DEFAULT:
1241 put('d');
1242 break;
1243 case RGB:
1244 put("r ");
1245 put(Red);
1246 put(' ');
1247 put(Green);
1248 put(' ');
1249 put(Blue);
1250 break;
1251 case CMY:
1252 put("c ");
1253 put(Cyan);
1254 put(' ');
1255 put(Magenta);
1256 put(' ');
1257 put(Yellow);
1258 break;
1259 case CMYK:
1260 put("k ");
1261 put(Cyan);
1262 put(' ');
1263 put(Magenta);
1264 put(' ');
1265 put(Yellow);
1266 put(' ');
1267 put(Black);
1268 break;
1269 case GRAY:
1270 put("g ");
1271 put(Gray);
1272 break;
1274 put('\n');
1277 // determine_line_limits - works out the smallest box which will contain
1278 // the entity, code, built from the point array.
1279 void troff_output_file::determine_line_limits(char code, hvpair *point,
1280 int npoints)
1282 int i, x, y;
1284 if (!is_on())
1285 return;
1287 switch (code) {
1288 case 'c':
1289 case 'C':
1290 // only the h field is used when defining a circle
1291 check_output_limits(output_hpos,
1292 output_vpos - point[0].h.to_units()/2);
1293 check_output_limits(output_hpos + point[0].h.to_units(),
1294 output_vpos + point[0].h.to_units()/2);
1295 break;
1296 case 'E':
1297 case 'e':
1298 check_output_limits(output_hpos,
1299 output_vpos - point[0].v.to_units()/2);
1300 check_output_limits(output_hpos + point[0].h.to_units(),
1301 output_vpos + point[0].v.to_units()/2);
1302 break;
1303 case 'P':
1304 case 'p':
1305 x = output_hpos;
1306 y = output_vpos;
1307 check_output_limits(x, y);
1308 for (i = 0; i < npoints; i++) {
1309 x += point[i].h.to_units();
1310 y += point[i].v.to_units();
1311 check_output_limits(x, y);
1313 break;
1314 case 't':
1315 x = output_hpos;
1316 y = output_vpos;
1317 for (i = 0; i < npoints; i++) {
1318 x += point[i].h.to_units();
1319 y += point[i].v.to_units();
1320 check_output_limits(x, y);
1322 break;
1323 case 'a':
1324 double c[2];
1325 int p[4];
1326 int minx, miny, maxx, maxy;
1327 x = output_hpos;
1328 y = output_vpos;
1329 p[0] = point[0].h.to_units();
1330 p[1] = point[0].v.to_units();
1331 p[2] = point[1].h.to_units();
1332 p[3] = point[1].v.to_units();
1333 if (adjust_arc_center(p, c)) {
1334 check_output_arc_limits(x, y,
1335 p[0], p[1], p[2], p[3],
1336 c[0], c[1],
1337 &minx, &maxx, &miny, &maxy);
1338 check_output_limits(minx, miny);
1339 check_output_limits(maxx, maxy);
1340 break;
1342 // fall through
1343 case 'l':
1344 x = output_hpos;
1345 y = output_vpos;
1346 check_output_limits(x, y);
1347 for (i = 0; i < npoints; i++) {
1348 x += point[i].h.to_units();
1349 y += point[i].v.to_units();
1350 check_output_limits(x, y);
1352 break;
1353 default:
1354 x = output_hpos;
1355 y = output_vpos;
1356 for (i = 0; i < npoints; i++) {
1357 x += point[i].h.to_units();
1358 y += point[i].v.to_units();
1359 check_output_limits(x, y);
1364 void troff_output_file::draw(char code, hvpair *point, int npoints,
1365 font_size fsize, color *gcol, color *fcol)
1367 int i;
1368 glyph_color(gcol);
1369 fill_color(fcol);
1370 flush_tbuf();
1371 do_motion();
1372 if (is_on()) {
1373 int size = fsize.to_scaled_points();
1374 if (current_size != size) {
1375 put('s');
1376 put(size);
1377 put('\n');
1378 current_size = size;
1379 current_tfont = 0;
1381 put('D');
1382 put(code);
1383 if (code == 'c') {
1384 put(' ');
1385 put(point[0].h.to_units());
1387 else
1388 for (i = 0; i < npoints; i++) {
1389 put(' ');
1390 put(point[i].h.to_units());
1391 put(' ');
1392 put(point[i].v.to_units());
1394 determine_line_limits(code, point, npoints);
1397 for (i = 0; i < npoints; i++)
1398 output_hpos += point[i].h.to_units();
1399 hpos = output_hpos;
1400 if (code != 'e') {
1401 for (i = 0; i < npoints; i++)
1402 output_vpos += point[i].v.to_units();
1403 vpos = output_vpos;
1405 if (is_on())
1406 put('\n');
1409 void troff_output_file::really_on()
1411 flush_tbuf();
1412 force_motion = 1;
1413 do_motion();
1416 void troff_output_file::really_off()
1418 flush_tbuf();
1421 void troff_output_file::really_put_filename(const char *filename)
1423 flush_tbuf();
1424 put("F ");
1425 put(filename);
1426 put('\n');
1429 void troff_output_file::really_begin_page(int pageno, vunits page_length)
1431 flush_tbuf();
1432 if (begun_page) {
1433 if (page_length > V0) {
1434 put('V');
1435 put(page_length.to_units());
1436 put('\n');
1439 else
1440 begun_page = 1;
1441 current_tfont = 0;
1442 current_font_number = -1;
1443 current_size = 0;
1444 // current_height = 0;
1445 // current_slant = 0;
1446 hpos = 0;
1447 vpos = 0;
1448 output_hpos = 0;
1449 output_vpos = 0;
1450 force_motion = 1;
1451 for (int i = 0; i < nfont_positions; i++)
1452 font_position[i] = NULL_SYMBOL;
1453 put('p');
1454 put(pageno);
1455 put('\n');
1458 void troff_output_file::really_copy_file(hunits x, vunits y,
1459 const char *filename)
1461 moveto(x, y);
1462 flush_tbuf();
1463 do_motion();
1464 errno = 0;
1465 FILE *ifp = include_search_path.open_file_cautious(filename);
1466 if (ifp == 0)
1467 error("can't open `%1': %2", filename, strerror(errno));
1468 else {
1469 int c;
1470 while ((c = getc(ifp)) != EOF)
1471 put(char(c));
1472 fclose(ifp);
1474 force_motion = 1;
1475 current_size = 0;
1476 current_tfont = 0;
1477 current_font_number = -1;
1478 for (int i = 0; i < nfont_positions; i++)
1479 font_position[i] = NULL_SYMBOL;
1482 void troff_output_file::really_transparent_char(unsigned char c)
1484 put(c);
1487 troff_output_file::~troff_output_file()
1489 a_delete font_position;
1492 void troff_output_file::trailer(vunits page_length)
1494 flush_tbuf();
1495 if (page_length > V0) {
1496 put("x trailer\n");
1497 put('V');
1498 put(page_length.to_units());
1499 put('\n');
1501 put("x stop\n");
1504 troff_output_file::troff_output_file()
1505 : current_slant(0), current_height(0), current_fill_color(0),
1506 current_glyph_color(0), nfont_positions(10), tbuf_len(0), begun_page(0)
1508 font_position = new symbol[nfont_positions];
1509 put("x T ");
1510 put(device);
1511 put('\n');
1512 put("x res ");
1513 put(units_per_inch);
1514 put(' ');
1515 put(hresolution);
1516 put(' ');
1517 put(vresolution);
1518 put('\n');
1519 put("x init\n");
1522 /* output_file */
1524 output_file *the_output = 0;
1526 output_file::output_file()
1530 output_file::~output_file()
1534 void output_file::trailer(vunits)
1538 void output_file::put_filename(const char *)
1542 void output_file::on()
1546 void output_file::off()
1550 real_output_file::real_output_file()
1551 : printing(0), output_on(1)
1553 #ifndef POPEN_MISSING
1554 if (pipe_command) {
1555 if ((fp = popen(pipe_command, POPEN_WT)) != 0) {
1556 piped = 1;
1557 return;
1559 error("pipe open failed: %1", strerror(errno));
1561 piped = 0;
1562 #endif /* not POPEN_MISSING */
1563 fp = stdout;
1566 real_output_file::~real_output_file()
1568 if (!fp)
1569 return;
1570 // To avoid looping, set fp to 0 before calling fatal().
1571 if (ferror(fp) || fflush(fp) < 0) {
1572 fp = 0;
1573 fatal("error writing output file");
1575 #ifndef POPEN_MISSING
1576 if (piped) {
1577 int result = pclose(fp);
1578 fp = 0;
1579 if (result < 0)
1580 fatal("pclose failed");
1581 if (!WIFEXITED(result))
1582 error("output process `%1' got fatal signal %2",
1583 pipe_command,
1584 WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
1585 else {
1586 int exit_status = WEXITSTATUS(result);
1587 if (exit_status != 0)
1588 error("output process `%1' exited with status %2",
1589 pipe_command, exit_status);
1592 else
1593 #endif /* not POPEN MISSING */
1594 if (fclose(fp) < 0) {
1595 fp = 0;
1596 fatal("error closing output file");
1600 void real_output_file::flush()
1602 if (fflush(fp) < 0)
1603 fatal("error writing output file");
1606 int real_output_file::is_printing()
1608 return printing;
1611 void real_output_file::begin_page(int pageno, vunits page_length)
1613 printing = in_output_page_list(pageno);
1614 if (printing)
1615 really_begin_page(pageno, page_length);
1618 void real_output_file::copy_file(hunits x, vunits y, const char *filename)
1620 if (printing && output_on)
1621 really_copy_file(x, y, filename);
1622 check_output_limits(x.to_units(), y.to_units());
1625 void real_output_file::transparent_char(unsigned char c)
1627 if (printing && output_on)
1628 really_transparent_char(c);
1631 void real_output_file::print_line(hunits x, vunits y, node *n,
1632 vunits before, vunits after, hunits width)
1634 if (printing)
1635 really_print_line(x, y, n, before, after, width);
1636 delete_node_list(n);
1639 void real_output_file::really_copy_file(hunits, vunits, const char *)
1641 // do nothing
1644 void real_output_file::put_filename(const char *filename)
1646 really_put_filename(filename);
1649 void real_output_file::really_put_filename(const char *)
1653 void real_output_file::on()
1655 really_on();
1656 if (output_on == 0)
1657 output_on = 1;
1660 void real_output_file::off()
1662 really_off();
1663 output_on = 0;
1666 int real_output_file::is_on()
1668 return output_on;
1671 void real_output_file::really_on()
1675 void real_output_file::really_off()
1679 /* ascii_output_file */
1681 void ascii_output_file::really_transparent_char(unsigned char c)
1683 putc(c, fp);
1686 void ascii_output_file::really_print_line(hunits, vunits, node *n,
1687 vunits, vunits, hunits)
1689 while (n != 0) {
1690 n->ascii_print(this);
1691 n = n->next;
1693 fputc('\n', fp);
1696 void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/)
1698 fputs("<beginning of page>\n", fp);
1701 ascii_output_file::ascii_output_file()
1705 /* suppress_output_file */
1707 suppress_output_file::suppress_output_file()
1711 void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits, hunits)
1715 void suppress_output_file::really_begin_page(int, vunits)
1719 void suppress_output_file::really_transparent_char(unsigned char)
1723 /* glyphs, ligatures, kerns, discretionary breaks */
1725 class charinfo_node : public node {
1726 protected:
1727 charinfo *ci;
1728 public:
1729 charinfo_node(charinfo *, node * = 0);
1730 int ends_sentence();
1731 int overlaps_vertically();
1732 int overlaps_horizontally();
1735 charinfo_node::charinfo_node(charinfo *c, node *x)
1736 : node(x), ci(c)
1740 int charinfo_node::ends_sentence()
1742 if (ci->ends_sentence())
1743 return 1;
1744 else if (ci->transparent())
1745 return 2;
1746 else
1747 return 0;
1750 int charinfo_node::overlaps_horizontally()
1752 return ci->overlaps_horizontally();
1755 int charinfo_node::overlaps_vertically()
1757 return ci->overlaps_vertically();
1760 class glyph_node : public charinfo_node {
1761 static glyph_node *free_list;
1762 protected:
1763 tfont *tf;
1764 color *gcol;
1765 color *fcol; /* this is needed for grotty */
1766 #ifdef STORE_WIDTH
1767 hunits wid;
1768 glyph_node(charinfo *, tfont *, color *, color *, hunits, node * = 0);
1769 #endif
1770 public:
1771 void *operator new(size_t);
1772 void operator delete(void *);
1773 glyph_node(charinfo *, tfont *, color *, color *, node * = 0);
1774 ~glyph_node() {}
1775 node *copy();
1776 node *merge_glyph_node(glyph_node *);
1777 node *merge_self(node *);
1778 hunits width();
1779 node *last_char_node();
1780 units size();
1781 void vertical_extent(vunits *, vunits *);
1782 hunits subscript_correction();
1783 hunits italic_correction();
1784 hunits left_italic_correction();
1785 hunits skew();
1786 hyphenation_type get_hyphenation_type();
1787 tfont *get_tfont();
1788 color *get_glyph_color();
1789 color *get_fill_color();
1790 void tprint(troff_output_file *);
1791 void zero_width_tprint(troff_output_file *);
1792 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1793 node *add_self(node *, hyphen_list **);
1794 void ascii_print(ascii_output_file *);
1795 void asciify(macro *);
1796 int character_type();
1797 int same(node *);
1798 const char *type();
1799 int force_tprint();
1802 glyph_node *glyph_node::free_list = 0;
1804 class ligature_node : public glyph_node {
1805 node *n1;
1806 node *n2;
1807 #ifdef STORE_WIDTH
1808 ligature_node(charinfo *, tfont *, color *, color *, hunits,
1809 node *, node *, node * = 0);
1810 #endif
1811 public:
1812 void *operator new(size_t);
1813 void operator delete(void *);
1814 ligature_node(charinfo *, tfont *, color *, color *,
1815 node *, node *, node * = 0);
1816 ~ligature_node();
1817 node *copy();
1818 node *add_self(node *, hyphen_list **);
1819 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1820 void ascii_print(ascii_output_file *);
1821 void asciify(macro *);
1822 int same(node *);
1823 const char *type();
1824 int force_tprint();
1827 class kern_pair_node : public node {
1828 hunits amount;
1829 node *n1;
1830 node *n2;
1831 public:
1832 kern_pair_node(hunits n, node *first, node *second, node *x = 0);
1833 ~kern_pair_node();
1834 node *copy();
1835 node *merge_glyph_node(glyph_node *);
1836 node *add_self(node *, hyphen_list **);
1837 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1838 node *add_discretionary_hyphen();
1839 hunits width();
1840 node *last_char_node();
1841 hunits italic_correction();
1842 hunits subscript_correction();
1843 void tprint(troff_output_file *);
1844 hyphenation_type get_hyphenation_type();
1845 int ends_sentence();
1846 void ascii_print(ascii_output_file *);
1847 void asciify(macro *);
1848 int same(node *);
1849 const char *type();
1850 int force_tprint();
1851 void vertical_extent(vunits *, vunits *);
1854 class dbreak_node : public node {
1855 node *none;
1856 node *pre;
1857 node *post;
1858 public:
1859 dbreak_node(node *n, node *p, node *x = 0);
1860 ~dbreak_node();
1861 node *copy();
1862 node *merge_glyph_node(glyph_node *);
1863 node *add_discretionary_hyphen();
1864 hunits width();
1865 node *last_char_node();
1866 hunits italic_correction();
1867 hunits subscript_correction();
1868 void tprint(troff_output_file *);
1869 breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
1870 int is_inner = 0);
1871 int nbreaks();
1872 int ends_sentence();
1873 void split(int, node **, node **);
1874 hyphenation_type get_hyphenation_type();
1875 void ascii_print(ascii_output_file *);
1876 void asciify(macro *);
1877 int same(node *);
1878 const char *type();
1879 int force_tprint();
1882 void *glyph_node::operator new(size_t n)
1884 assert(n == sizeof(glyph_node));
1885 if (!free_list) {
1886 const int BLOCK = 1024;
1887 free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
1888 for (int i = 0; i < BLOCK - 1; i++)
1889 free_list[i].next = free_list + i + 1;
1890 free_list[BLOCK-1].next = 0;
1892 glyph_node *p = free_list;
1893 free_list = (glyph_node *)(free_list->next);
1894 p->next = 0;
1895 return p;
1898 void *ligature_node::operator new(size_t n)
1900 return new char[n];
1903 void glyph_node::operator delete(void *p)
1905 if (p) {
1906 ((glyph_node *)p)->next = free_list;
1907 free_list = (glyph_node *)p;
1911 void ligature_node::operator delete(void *p)
1913 delete[] (char *)p;
1916 glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc, node *x)
1917 : charinfo_node(c, x), tf(t), gcol(gc), fcol(fc)
1919 #ifdef STORE_WIDTH
1920 wid = tf->get_width(ci);
1921 #endif
1924 #ifdef STORE_WIDTH
1925 glyph_node::glyph_node(charinfo *c, tfont *t,
1926 color *gc, color *fc, hunits w, node *x)
1927 : charinfo_node(c, x), tf(t), gcol(gc), fcol(fc), wid(w)
1930 #endif
1932 node *glyph_node::copy()
1934 #ifdef STORE_WIDTH
1935 return new glyph_node(ci, tf, gcol, fcol, wid);
1936 #else
1937 return new glyph_node(ci, tf, gcol, fcol);
1938 #endif
1941 node *glyph_node::merge_self(node *nd)
1943 return nd->merge_glyph_node(this);
1946 int glyph_node::character_type()
1948 return tf->get_character_type(ci);
1951 node *glyph_node::add_self(node *n, hyphen_list **p)
1953 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
1954 next = 0;
1955 node *nn;
1956 if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
1957 next = n;
1958 nn = this;
1960 if ((*p)->hyphen)
1961 nn = nn->add_discretionary_hyphen();
1962 hyphen_list *pp = *p;
1963 *p = (*p)->next;
1964 delete pp;
1965 return nn;
1968 units glyph_node::size()
1970 return tf->get_size().to_units();
1973 hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count)
1975 (*count)++;
1976 return new hyphen_list(ci->get_hyphenation_code(), tail);
1979 tfont *node::get_tfont()
1981 return 0;
1984 tfont *glyph_node::get_tfont()
1986 return tf;
1989 color *node::get_glyph_color()
1991 return 0;
1994 color *glyph_node::get_glyph_color()
1996 return gcol;
1999 color *node::get_fill_color()
2001 return 0;
2004 color *glyph_node::get_fill_color()
2006 return fcol;
2009 node *node::merge_glyph_node(glyph_node *)
2011 return 0;
2014 node *glyph_node::merge_glyph_node(glyph_node *gn)
2016 if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
2017 charinfo *lig;
2018 if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
2019 node *next1 = next;
2020 next = 0;
2021 return new ligature_node(lig, tf, gcol, fcol, this, gn, next1);
2023 hunits kern;
2024 if (tf->get_kern(ci, gn->ci, &kern)) {
2025 node *next1 = next;
2026 next = 0;
2027 return new kern_pair_node(kern, this, gn, next1);
2030 return 0;
2033 #ifdef STORE_WIDTH
2034 inline
2035 #endif
2036 hunits glyph_node::width()
2038 #ifdef STORE_WIDTH
2039 return wid;
2040 #else
2041 return tf->get_width(ci);
2042 #endif
2045 node *glyph_node::last_char_node()
2047 return this;
2050 void glyph_node::vertical_extent(vunits *min, vunits *max)
2052 *min = -tf->get_char_height(ci);
2053 *max = tf->get_char_depth(ci);
2056 hunits glyph_node::skew()
2058 return tf->get_char_skew(ci);
2061 hunits glyph_node::subscript_correction()
2063 return tf->get_subscript_correction(ci);
2066 hunits glyph_node::italic_correction()
2068 return tf->get_italic_correction(ci);
2071 hunits glyph_node::left_italic_correction()
2073 return tf->get_left_italic_correction(ci);
2076 hyphenation_type glyph_node::get_hyphenation_type()
2078 return HYPHEN_MIDDLE;
2081 void glyph_node::ascii_print(ascii_output_file *ascii)
2083 unsigned char c = ci->get_ascii_code();
2084 if (c != 0)
2085 ascii->outc(c);
2086 else
2087 ascii->outs(ci->nm.contents());
2090 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2091 node *gn1, node *gn2, node *x)
2092 : glyph_node(c, t, gc, fc, x), n1(gn1), n2(gn2)
2096 #ifdef STORE_WIDTH
2097 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2098 hunits w, node *gn1, node *gn2, node *x)
2099 : glyph_node(c, t, gc, fc, w, x), n1(gn1), n2(gn2)
2102 #endif
2104 ligature_node::~ligature_node()
2106 delete n1;
2107 delete n2;
2110 node *ligature_node::copy()
2112 #ifdef STORE_WIDTH
2113 return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy());
2114 #else
2115 return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy());
2116 #endif
2119 void ligature_node::ascii_print(ascii_output_file *ascii)
2121 n1->ascii_print(ascii);
2122 n2->ascii_print(ascii);
2125 hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail, int *count)
2127 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2128 return n1->get_hyphen_list(hl, count);
2131 node *ligature_node::add_self(node *n, hyphen_list **p)
2133 n = n1->add_self(n, p);
2134 n = n2->add_self(n, p);
2135 n1 = n2 = 0;
2136 delete this;
2137 return n;
2140 kern_pair_node::kern_pair_node(hunits n, node *first, node *second, node *x)
2141 : node(x), amount(n), n1(first), n2(second)
2145 dbreak_node::dbreak_node(node *n, node *p, node *x)
2146 : node(x), none(n), pre(p), post(0)
2150 node *dbreak_node::merge_glyph_node(glyph_node *gn)
2152 glyph_node *gn2 = (glyph_node *)gn->copy();
2153 node *new_none = none ? none->merge_glyph_node(gn) : 0;
2154 node *new_post = post ? post->merge_glyph_node(gn2) : 0;
2155 if (new_none == 0 && new_post == 0) {
2156 delete gn2;
2157 return 0;
2159 if (new_none != 0)
2160 none = new_none;
2161 else {
2162 gn->next = none;
2163 none = gn;
2165 if (new_post != 0)
2166 post = new_post;
2167 else {
2168 gn2->next = post;
2169 post = gn2;
2171 return this;
2174 node *kern_pair_node::merge_glyph_node(glyph_node *gn)
2176 node *nd = n2->merge_glyph_node(gn);
2177 if (nd == 0)
2178 return 0;
2179 n2 = nd;
2180 nd = n2->merge_self(n1);
2181 if (nd) {
2182 nd->next = next;
2183 n1 = 0;
2184 n2 = 0;
2185 delete this;
2186 return nd;
2188 return this;
2191 hunits kern_pair_node::italic_correction()
2193 return n2->italic_correction();
2196 hunits kern_pair_node::subscript_correction()
2198 return n2->subscript_correction();
2201 void kern_pair_node::vertical_extent(vunits *min, vunits *max)
2203 n1->vertical_extent(min, max);
2204 vunits min2, max2;
2205 n2->vertical_extent(&min2, &max2);
2206 if (min2 < *min)
2207 *min = min2;
2208 if (max2 > *max)
2209 *max = max2;
2212 node *kern_pair_node::add_discretionary_hyphen()
2214 tfont *tf = n2->get_tfont();
2215 if (tf) {
2216 if (tf->contains(soft_hyphen_char)) {
2217 color *gcol = n2->get_glyph_color();
2218 color *fcol = n2->get_fill_color();
2219 node *next1 = next;
2220 next = 0;
2221 node *n = copy();
2222 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol);
2223 node *nn = n->merge_glyph_node(gn);
2224 if (nn == 0) {
2225 gn->next = n;
2226 nn = gn;
2228 return new dbreak_node(this, nn, next1);
2231 return this;
2234 kern_pair_node::~kern_pair_node()
2236 if (n1 != 0)
2237 delete n1;
2238 if (n2 != 0)
2239 delete n2;
2242 dbreak_node::~dbreak_node()
2244 delete_node_list(pre);
2245 delete_node_list(post);
2246 delete_node_list(none);
2249 node *kern_pair_node::copy()
2251 return new kern_pair_node(amount, n1->copy(), n2->copy());
2254 node *copy_node_list(node *n)
2256 node *p = 0;
2257 while (n != 0) {
2258 node *nn = n->copy();
2259 nn->next = p;
2260 p = nn;
2261 n = n->next;
2263 while (p != 0) {
2264 node *pp = p->next;
2265 p->next = n;
2266 n = p;
2267 p = pp;
2269 return n;
2272 void delete_node_list(node *n)
2274 while (n != 0) {
2275 node *tem = n;
2276 n = n->next;
2277 delete tem;
2281 node *dbreak_node::copy()
2283 dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre));
2284 p->post = copy_node_list(post);
2285 return p;
2288 hyphen_list *node::get_hyphen_list(hyphen_list *tail, int *)
2290 return tail;
2293 hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail, int *count)
2295 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2296 return n1->get_hyphen_list(hl, count);
2299 class hyphen_inhibitor_node : public node {
2300 public:
2301 hyphen_inhibitor_node(node *nd = 0);
2302 node *copy();
2303 int same(node *);
2304 const char *type();
2305 int force_tprint();
2306 hyphenation_type get_hyphenation_type();
2309 hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
2313 node *hyphen_inhibitor_node::copy()
2315 return new hyphen_inhibitor_node;
2318 int hyphen_inhibitor_node::same(node *)
2320 return 1;
2323 const char *hyphen_inhibitor_node::type()
2325 return "hyphen_inhibitor_node";
2328 int hyphen_inhibitor_node::force_tprint()
2330 return 0;
2333 hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
2335 return HYPHEN_INHIBIT;
2338 /* add_discretionary_hyphen methods */
2340 node *dbreak_node::add_discretionary_hyphen()
2342 if (post)
2343 post = post->add_discretionary_hyphen();
2344 if (none)
2345 none = none->add_discretionary_hyphen();
2346 return this;
2349 node *node::add_discretionary_hyphen()
2351 tfont *tf = get_tfont();
2352 if (!tf)
2353 return new hyphen_inhibitor_node(this);
2354 if (tf->contains(soft_hyphen_char)) {
2355 color *gcol = get_glyph_color();
2356 color *fcol = get_fill_color();
2357 node *next1 = next;
2358 next = 0;
2359 node *n = copy();
2360 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol);
2361 node *n1 = n->merge_glyph_node(gn);
2362 if (n1 == 0) {
2363 gn->next = n;
2364 n1 = gn;
2366 return new dbreak_node(this, n1, next1);
2368 return this;
2371 node *node::merge_self(node *)
2373 return 0;
2376 node *node::add_self(node *n, hyphen_list ** /*p*/)
2378 next = n;
2379 return this;
2382 node *kern_pair_node::add_self(node *n, hyphen_list **p)
2384 n = n1->add_self(n, p);
2385 n = n2->add_self(n, p);
2386 n1 = n2 = 0;
2387 delete this;
2388 return n;
2391 hunits node::width()
2393 return H0;
2396 node *node::last_char_node()
2398 return 0;
2401 int node::force_tprint()
2403 return 0;
2406 hunits hmotion_node::width()
2408 return n;
2411 units node::size()
2413 return points_to_units(10);
2416 hunits kern_pair_node::width()
2418 return n1->width() + n2->width() + amount;
2421 node *kern_pair_node::last_char_node()
2423 node *nd = n2->last_char_node();
2424 if (nd)
2425 return nd;
2426 return n1->last_char_node();
2429 hunits dbreak_node::width()
2431 hunits x = H0;
2432 for (node *n = none; n != 0; n = n->next)
2433 x += n->width();
2434 return x;
2437 node *dbreak_node::last_char_node()
2439 for (node *n = none; n; n = n->next) {
2440 node *last = n->last_char_node();
2441 if (last)
2442 return last;
2444 return 0;
2447 hunits dbreak_node::italic_correction()
2449 return none ? none->italic_correction() : H0;
2452 hunits dbreak_node::subscript_correction()
2454 return none ? none->subscript_correction() : H0;
2457 class italic_corrected_node : public node {
2458 node *n;
2459 hunits x;
2460 public:
2461 italic_corrected_node(node *, hunits, node * = 0);
2462 ~italic_corrected_node();
2463 node *copy();
2464 void ascii_print(ascii_output_file *);
2465 void asciify(macro *);
2466 hunits width();
2467 node *last_char_node();
2468 void vertical_extent(vunits *, vunits *);
2469 int ends_sentence();
2470 int overlaps_horizontally();
2471 int overlaps_vertically();
2472 int same(node *);
2473 hyphenation_type get_hyphenation_type();
2474 tfont *get_tfont();
2475 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2476 int character_type();
2477 void tprint(troff_output_file *);
2478 hunits subscript_correction();
2479 hunits skew();
2480 node *add_self(node *, hyphen_list **);
2481 const char *type();
2482 int force_tprint();
2485 node *node::add_italic_correction(hunits *width)
2487 hunits ic = italic_correction();
2488 if (ic.is_zero())
2489 return this;
2490 else {
2491 node *next1 = next;
2492 next = 0;
2493 *width += ic;
2494 return new italic_corrected_node(this, ic, next1);
2498 italic_corrected_node::italic_corrected_node(node *nn, hunits xx, node *p)
2499 : node(p), n(nn), x(xx)
2501 assert(n != 0);
2504 italic_corrected_node::~italic_corrected_node()
2506 delete n;
2509 node *italic_corrected_node::copy()
2511 return new italic_corrected_node(n->copy(), x);
2514 hunits italic_corrected_node::width()
2516 return n->width() + x;
2519 void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
2521 n->vertical_extent(min, max);
2524 void italic_corrected_node::tprint(troff_output_file *out)
2526 n->tprint(out);
2527 out->right(x);
2530 hunits italic_corrected_node::skew()
2532 return n->skew() - x/2;
2535 hunits italic_corrected_node::subscript_correction()
2537 return n->subscript_correction() - x;
2540 void italic_corrected_node::ascii_print(ascii_output_file *out)
2542 n->ascii_print(out);
2545 int italic_corrected_node::ends_sentence()
2547 return n->ends_sentence();
2550 int italic_corrected_node::overlaps_horizontally()
2552 return n->overlaps_horizontally();
2555 int italic_corrected_node::overlaps_vertically()
2557 return n->overlaps_vertically();
2560 node *italic_corrected_node::last_char_node()
2562 return n->last_char_node();
2565 tfont *italic_corrected_node::get_tfont()
2567 return n->get_tfont();
2570 hyphenation_type italic_corrected_node::get_hyphenation_type()
2572 return n->get_hyphenation_type();
2575 node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
2577 nd = n->add_self(nd, p);
2578 hunits not_interested;
2579 nd = nd->add_italic_correction(&not_interested);
2580 n = 0;
2581 delete this;
2582 return nd;
2585 hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail,
2586 int *count)
2588 return n->get_hyphen_list(tail, count);
2591 int italic_corrected_node::character_type()
2593 return n->character_type();
2596 class break_char_node : public node {
2597 node *ch;
2598 char break_code;
2599 color *col;
2600 public:
2601 break_char_node(node *, int, color *, node * = 0);
2602 ~break_char_node();
2603 node *copy();
2604 hunits width();
2605 vunits vertical_width();
2606 node *last_char_node();
2607 int character_type();
2608 int ends_sentence();
2609 node *add_self(node *, hyphen_list **);
2610 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2611 void tprint(troff_output_file *);
2612 void zero_width_tprint(troff_output_file *);
2613 void ascii_print(ascii_output_file *);
2614 void asciify(macro *);
2615 hyphenation_type get_hyphenation_type();
2616 int overlaps_vertically();
2617 int overlaps_horizontally();
2618 units size();
2619 tfont *get_tfont();
2620 int same(node *);
2621 const char *type();
2622 int force_tprint();
2625 break_char_node::break_char_node(node *n, int bc, color *c, node *x)
2626 : node(x), ch(n), break_code(bc), col(c)
2630 break_char_node::~break_char_node()
2632 delete ch;
2635 node *break_char_node::copy()
2637 return new break_char_node(ch->copy(), break_code, col);
2640 hunits break_char_node::width()
2642 return ch->width();
2645 vunits break_char_node::vertical_width()
2647 return ch->vertical_width();
2650 node *break_char_node::last_char_node()
2652 return ch->last_char_node();
2655 int break_char_node::character_type()
2657 return ch->character_type();
2660 int break_char_node::ends_sentence()
2662 return ch->ends_sentence();
2665 node *break_char_node::add_self(node *n, hyphen_list **p)
2667 assert((*p)->hyphenation_code == 0);
2668 if ((*p)->breakable && (break_code & 1)) {
2669 n = new space_node(H0, col, n);
2670 n->freeze_space();
2672 next = n;
2673 n = this;
2674 if ((*p)->breakable && (break_code & 2)) {
2675 n = new space_node(H0, col, n);
2676 n->freeze_space();
2678 hyphen_list *pp = *p;
2679 *p = (*p)->next;
2680 delete pp;
2681 return n;
2684 hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail, int *)
2686 return new hyphen_list(0, tail);
2689 hyphenation_type break_char_node::get_hyphenation_type()
2691 return HYPHEN_MIDDLE;
2694 void break_char_node::ascii_print(ascii_output_file *ascii)
2696 ch->ascii_print(ascii);
2699 int break_char_node::overlaps_vertically()
2701 return ch->overlaps_vertically();
2704 int break_char_node::overlaps_horizontally()
2706 return ch->overlaps_horizontally();
2709 units break_char_node::size()
2711 return ch->size();
2714 tfont *break_char_node::get_tfont()
2716 return ch->get_tfont();
2719 node *extra_size_node::copy()
2721 return new extra_size_node(n);
2724 node *vertical_size_node::copy()
2726 return new vertical_size_node(n);
2729 node *hmotion_node::copy()
2731 return new hmotion_node(n, was_tab, unformat, col);
2734 node *space_char_hmotion_node::copy()
2736 return new space_char_hmotion_node(n, col);
2739 node *vmotion_node::copy()
2741 return new vmotion_node(n, col);
2744 node *dummy_node::copy()
2746 return new dummy_node;
2749 node *transparent_dummy_node::copy()
2751 return new transparent_dummy_node;
2754 hline_node::~hline_node()
2756 if (n)
2757 delete n;
2760 node *hline_node::copy()
2762 return new hline_node(x, n ? n->copy() : 0);
2765 hunits hline_node::width()
2767 return x < H0 ? H0 : x;
2770 vline_node::~vline_node()
2772 if (n)
2773 delete n;
2776 node *vline_node::copy()
2778 return new vline_node(x, n ? n->copy() : 0);
2781 hunits vline_node::width()
2783 return n == 0 ? H0 : n->width();
2786 zero_width_node::zero_width_node(node *nd) : n(nd)
2790 zero_width_node::~zero_width_node()
2792 delete_node_list(n);
2795 node *zero_width_node::copy()
2797 return new zero_width_node(copy_node_list(n));
2800 int node_list_character_type(node *p)
2802 int t = 0;
2803 for (; p; p = p->next)
2804 t |= p->character_type();
2805 return t;
2808 int zero_width_node::character_type()
2810 return node_list_character_type(n);
2813 void node_list_vertical_extent(node *p, vunits *min, vunits *max)
2815 *min = V0;
2816 *max = V0;
2817 vunits cur_vpos = V0;
2818 vunits v1, v2;
2819 for (; p; p = p->next) {
2820 p->vertical_extent(&v1, &v2);
2821 v1 += cur_vpos;
2822 if (v1 < *min)
2823 *min = v1;
2824 v2 += cur_vpos;
2825 if (v2 > *max)
2826 *max = v2;
2827 cur_vpos += p->vertical_width();
2831 void zero_width_node::vertical_extent(vunits *min, vunits *max)
2833 node_list_vertical_extent(n, min, max);
2836 overstrike_node::overstrike_node() : list(0), max_width(H0)
2840 overstrike_node::~overstrike_node()
2842 delete_node_list(list);
2845 node *overstrike_node::copy()
2847 overstrike_node *on = new overstrike_node;
2848 for (node *tem = list; tem; tem = tem->next)
2849 on->overstrike(tem->copy());
2850 return on;
2853 void overstrike_node::overstrike(node *n)
2855 if (n == 0)
2856 return;
2857 hunits w = n->width();
2858 if (w > max_width)
2859 max_width = w;
2860 node **p;
2861 for (p = &list; *p; p = &(*p)->next)
2863 n->next = 0;
2864 *p = n;
2867 hunits overstrike_node::width()
2869 return max_width;
2872 bracket_node::bracket_node() : list(0), max_width(H0)
2876 bracket_node::~bracket_node()
2878 delete_node_list(list);
2881 node *bracket_node::copy()
2883 bracket_node *on = new bracket_node;
2884 node *last = 0;
2885 node *tem;
2886 if (list)
2887 list->last = 0;
2888 for (tem = list; tem; tem = tem->next) {
2889 if (tem->next)
2890 tem->next->last = tem;
2891 last = tem;
2893 for (tem = last; tem; tem = tem->last)
2894 on->bracket(tem->copy());
2895 return on;
2898 void bracket_node::bracket(node *n)
2900 if (n == 0)
2901 return;
2902 hunits w = n->width();
2903 if (w > max_width)
2904 max_width = w;
2905 n->next = list;
2906 list = n;
2909 hunits bracket_node::width()
2911 return max_width;
2914 int node::nspaces()
2916 return 0;
2919 int node::merge_space(hunits, hunits, hunits)
2921 return 0;
2924 #if 0
2925 space_node *space_node::free_list = 0;
2927 void *space_node::operator new(size_t n)
2929 assert(n == sizeof(space_node));
2930 if (!free_list) {
2931 free_list = (space_node *)new char[sizeof(space_node)*BLOCK];
2932 for (int i = 0; i < BLOCK - 1; i++)
2933 free_list[i].next = free_list + i + 1;
2934 free_list[BLOCK-1].next = 0;
2936 space_node *p = free_list;
2937 free_list = (space_node *)(free_list->next);
2938 p->next = 0;
2939 return p;
2942 inline void space_node::operator delete(void *p)
2944 if (p) {
2945 ((space_node *)p)->next = free_list;
2946 free_list = (space_node *)p;
2949 #endif
2951 space_node::space_node(hunits nn, color *c, node *p)
2952 : node(p), n(nn), set(0), was_escape_colon(0), col(c)
2956 space_node::space_node(hunits nn, int s, int flag, color *c, node *p)
2957 : node(p), n(nn), set(s), was_escape_colon(flag), col(c)
2961 #if 0
2962 space_node::~space_node()
2965 #endif
2967 node *space_node::copy()
2969 return new space_node(n, set, was_escape_colon, col);
2972 int space_node::force_tprint()
2974 return 0;
2977 int space_node::nspaces()
2979 return set ? 0 : 1;
2982 int space_node::merge_space(hunits h, hunits, hunits)
2984 n += h;
2985 return 1;
2988 hunits space_node::width()
2990 return n;
2993 void node::spread_space(int*, hunits*)
2997 void space_node::spread_space(int *nspaces, hunits *desired_space)
2999 if (!set) {
3000 assert(*nspaces > 0);
3001 if (*nspaces == 1) {
3002 n += *desired_space;
3003 *desired_space = H0;
3005 else {
3006 hunits extra = *desired_space / *nspaces;
3007 *desired_space -= extra;
3008 n += extra;
3010 *nspaces -= 1;
3011 set = 1;
3015 void node::freeze_space()
3019 void space_node::freeze_space()
3021 set = 1;
3024 void node::is_escape_colon()
3028 void space_node::is_escape_colon()
3030 was_escape_colon = 1;
3033 diverted_space_node::diverted_space_node(vunits d, node *p)
3034 : node(p), n(d)
3038 node *diverted_space_node::copy()
3040 return new diverted_space_node(n);
3043 diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
3044 : node(p), filename(s)
3048 node *diverted_copy_file_node::copy()
3050 return new diverted_copy_file_node(filename);
3053 int node::ends_sentence()
3055 return 0;
3058 int kern_pair_node::ends_sentence()
3060 switch (n2->ends_sentence()) {
3061 case 0:
3062 return 0;
3063 case 1:
3064 return 1;
3065 case 2:
3066 break;
3067 default:
3068 assert(0);
3070 return n1->ends_sentence();
3073 int node_list_ends_sentence(node *n)
3075 for (; n != 0; n = n->next)
3076 switch (n->ends_sentence()) {
3077 case 0:
3078 return 0;
3079 case 1:
3080 return 1;
3081 case 2:
3082 break;
3083 default:
3084 assert(0);
3086 return 2;
3089 int dbreak_node::ends_sentence()
3091 return node_list_ends_sentence(none);
3094 int node::overlaps_horizontally()
3096 return 0;
3099 int node::overlaps_vertically()
3101 return 0;
3104 int node::discardable()
3106 return 0;
3109 int space_node::discardable()
3111 return set ? 0 : 1;
3114 vunits node::vertical_width()
3116 return V0;
3119 vunits vline_node::vertical_width()
3121 return x;
3124 vunits vmotion_node::vertical_width()
3126 return n;
3129 int node::set_unformat_flag()
3131 return 1;
3134 int node::character_type()
3136 return 0;
3139 hunits node::subscript_correction()
3141 return H0;
3144 hunits node::italic_correction()
3146 return H0;
3149 hunits node::left_italic_correction()
3151 return H0;
3154 hunits node::skew()
3156 return H0;
3159 /* vertical_extent methods */
3161 void node::vertical_extent(vunits *min, vunits *max)
3163 vunits v = vertical_width();
3164 if (v < V0) {
3165 *min = v;
3166 *max = V0;
3168 else {
3169 *max = v;
3170 *min = V0;
3174 void vline_node::vertical_extent(vunits *min, vunits *max)
3176 if (n == 0)
3177 node::vertical_extent(min, max);
3178 else {
3179 vunits cmin, cmax;
3180 n->vertical_extent(&cmin, &cmax);
3181 vunits h = n->size();
3182 if (x < V0) {
3183 if (-x < h) {
3184 *min = x;
3185 *max = V0;
3187 else {
3188 // we print the first character and then move up, so
3189 *max = cmax;
3190 // we print the last character and then move up h
3191 *min = cmin + h;
3192 if (*min > V0)
3193 *min = V0;
3194 *min += x;
3197 else {
3198 if (x < h) {
3199 *max = x;
3200 *min = V0;
3202 else {
3203 // we move down by h and then print the first character, so
3204 *min = cmin + h;
3205 if (*min > V0)
3206 *min = V0;
3207 *max = x + cmax;
3213 /* ascii_print methods */
3215 static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n)
3217 if (n == 0)
3218 return;
3219 ascii_print_reverse_node_list(ascii, n->next);
3220 n->ascii_print(ascii);
3223 void dbreak_node::ascii_print(ascii_output_file *ascii)
3225 ascii_print_reverse_node_list(ascii, none);
3228 void kern_pair_node::ascii_print(ascii_output_file *ascii)
3230 n1->ascii_print(ascii);
3231 n2->ascii_print(ascii);
3234 void node::ascii_print(ascii_output_file *)
3238 void space_node::ascii_print(ascii_output_file *ascii)
3240 if (!n.is_zero())
3241 ascii->outc(' ');
3244 void hmotion_node::ascii_print(ascii_output_file *ascii)
3246 // this is pretty arbitrary
3247 if (n >= points_to_units(2))
3248 ascii->outc(' ');
3251 void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
3253 ascii->outc(' ');
3256 /* asciify methods */
3258 void node::asciify(macro *m)
3260 m->append(this);
3263 void glyph_node::asciify(macro *m)
3265 unsigned char c = ci->get_asciify_code();
3266 if (c == 0)
3267 c = ci->get_ascii_code();
3268 if (c != 0) {
3269 m->append(c);
3270 delete this;
3272 else
3273 m->append(this);
3276 void kern_pair_node::asciify(macro *m)
3278 n1->asciify(m);
3279 n2->asciify(m);
3280 n1 = n2 = 0;
3281 delete this;
3284 static void asciify_reverse_node_list(macro *m, node *n)
3286 if (n == 0)
3287 return;
3288 asciify_reverse_node_list(m, n->next);
3289 n->asciify(m);
3292 void dbreak_node::asciify(macro *m)
3294 asciify_reverse_node_list(m, none);
3295 none = 0;
3296 delete this;
3299 void ligature_node::asciify(macro *m)
3301 n1->asciify(m);
3302 n2->asciify(m);
3303 n1 = n2 = 0;
3304 delete this;
3307 void break_char_node::asciify(macro *m)
3309 ch->asciify(m);
3310 ch = 0;
3311 delete this;
3314 void italic_corrected_node::asciify(macro *m)
3316 n->asciify(m);
3317 n = 0;
3318 delete this;
3321 void left_italic_corrected_node::asciify(macro *m)
3323 if (n) {
3324 n->asciify(m);
3325 n = 0;
3327 delete this;
3330 void hmotion_node::asciify(macro *m)
3332 if (was_tab) {
3333 m->append('\t');
3334 delete this;
3336 else
3337 m->append(this);
3340 space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3341 node *next)
3342 : hmotion_node(i, c, next)
3346 void space_char_hmotion_node::asciify(macro *m)
3348 m->append(ESCAPE_SPACE);
3349 delete this;
3352 void space_node::asciify(macro *m)
3354 if (was_escape_colon) {
3355 m->append(ESCAPE_COLON);
3356 delete this;
3358 else
3359 m->append(this);
3362 void word_space_node::asciify(macro *m)
3364 for (width_list *w = orig_width; w; w = w->next)
3365 m->append(' ');
3366 delete this;
3369 void unbreakable_space_node::asciify(macro *m)
3371 m->append(ESCAPE_TILDE);
3372 delete this;
3375 void line_start_node::asciify(macro *)
3377 delete this;
3380 void vertical_size_node::asciify(macro *)
3382 delete this;
3385 breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/,
3386 breakpoint *rest, int /*is_inner*/)
3388 return rest;
3391 int node::nbreaks()
3393 return 0;
3396 breakpoint *space_node::get_breakpoints(hunits width, int ns,
3397 breakpoint *rest, int is_inner)
3399 if (next && next->discardable())
3400 return rest;
3401 breakpoint *bp = new breakpoint;
3402 bp->next = rest;
3403 bp->width = width;
3404 bp->nspaces = ns;
3405 bp->hyphenated = 0;
3406 if (is_inner) {
3407 assert(rest != 0);
3408 bp->index = rest->index + 1;
3409 bp->nd = rest->nd;
3411 else {
3412 bp->nd = this;
3413 bp->index = 0;
3415 return bp;
3418 int space_node::nbreaks()
3420 if (next && next->discardable())
3421 return 0;
3422 else
3423 return 1;
3426 static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
3427 int ns, breakpoint *rest)
3429 if (p != 0) {
3430 rest = p->get_breakpoints(*widthp,
3432 node_list_get_breakpoints(p->next, widthp, ns,
3433 rest),
3435 *widthp += p->width();
3437 return rest;
3440 breakpoint *dbreak_node::get_breakpoints(hunits width, int ns,
3441 breakpoint *rest, int is_inner)
3443 breakpoint *bp = new breakpoint;
3444 bp->next = rest;
3445 bp->width = width;
3446 for (node *tem = pre; tem != 0; tem = tem->next)
3447 bp->width += tem->width();
3448 bp->nspaces = ns;
3449 bp->hyphenated = 1;
3450 if (is_inner) {
3451 assert(rest != 0);
3452 bp->index = rest->index + 1;
3453 bp->nd = rest->nd;
3455 else {
3456 bp->nd = this;
3457 bp->index = 0;
3459 return node_list_get_breakpoints(none, &width, ns, bp);
3462 int dbreak_node::nbreaks()
3464 int i = 1;
3465 for (node *tem = none; tem != 0; tem = tem->next)
3466 i += tem->nbreaks();
3467 return i;
3470 void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
3472 assert(0);
3475 void space_node::split(int where, node **pre, node **post)
3477 assert(where == 0);
3478 *pre = next;
3479 *post = 0;
3480 delete this;
3483 static void node_list_split(node *p, int *wherep, node **prep, node **postp)
3485 if (p == 0)
3486 return;
3487 int nb = p->nbreaks();
3488 node_list_split(p->next, wherep, prep, postp);
3489 if (*wherep < 0) {
3490 p->next = *postp;
3491 *postp = p;
3493 else if (*wherep < nb) {
3494 p->next = *prep;
3495 p->split(*wherep, prep, postp);
3497 else {
3498 p->next = *prep;
3499 *prep = p;
3501 *wherep -= nb;
3504 void dbreak_node::split(int where, node **prep, node **postp)
3506 assert(where >= 0);
3507 if (where == 0) {
3508 *postp = post;
3509 post = 0;
3510 if (pre == 0)
3511 *prep = next;
3512 else {
3513 node *tem;
3514 for (tem = pre; tem->next != 0; tem = tem->next)
3516 tem->next = next;
3517 *prep = pre;
3519 pre = 0;
3520 delete this;
3522 else {
3523 *prep = next;
3524 where -= 1;
3525 node_list_split(none, &where, prep, postp);
3526 none = 0;
3527 delete this;
3531 hyphenation_type node::get_hyphenation_type()
3533 return HYPHEN_BOUNDARY;
3536 hyphenation_type dbreak_node::get_hyphenation_type()
3538 return HYPHEN_INHIBIT;
3541 hyphenation_type kern_pair_node::get_hyphenation_type()
3543 return HYPHEN_MIDDLE;
3546 hyphenation_type dummy_node::get_hyphenation_type()
3548 return HYPHEN_MIDDLE;
3551 hyphenation_type transparent_dummy_node::get_hyphenation_type()
3553 return HYPHEN_MIDDLE;
3556 hyphenation_type hmotion_node::get_hyphenation_type()
3558 return HYPHEN_MIDDLE;
3561 hyphenation_type space_char_hmotion_node::get_hyphenation_type()
3563 return HYPHEN_MIDDLE;
3566 hyphenation_type overstrike_node::get_hyphenation_type()
3568 return HYPHEN_MIDDLE;
3571 hyphenation_type space_node::get_hyphenation_type()
3573 if (was_escape_colon)
3574 return HYPHEN_MIDDLE;
3575 return HYPHEN_BOUNDARY;
3578 hyphenation_type unbreakable_space_node::get_hyphenation_type()
3580 return HYPHEN_MIDDLE;
3583 int node::interpret(macro *)
3585 return 0;
3588 special_node::special_node(const macro &m, int n)
3589 : mac(m), no_init_string(n)
3591 font_size fs = curenv->get_font_size();
3592 int char_height = curenv->get_char_height();
3593 int char_slant = curenv->get_char_slant();
3594 int fontno = env_definite_font(curenv);
3595 tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fontno);
3596 if (curenv->is_composite())
3597 tf = tf->get_plain();
3598 gcol = curenv->get_glyph_color();
3599 fcol = curenv->get_fill_color();
3602 special_node::special_node(const macro &m, tfont *t,
3603 color *gc, color *fc, int n)
3604 : mac(m), tf(t), gcol(gc), fcol(fc), no_init_string(n)
3608 int special_node::same(node *n)
3610 return mac == ((special_node *)n)->mac
3611 && tf == ((special_node *)n)->tf
3612 && gcol == ((special_node *)n)->gcol
3613 && fcol == ((special_node *)n)->fcol
3614 && no_init_string == ((special_node *)n)->no_init_string;
3617 const char *special_node::type()
3619 return "special_node";
3622 int special_node::ends_sentence()
3624 return 2;
3627 int special_node::force_tprint()
3629 return 0;
3632 node *special_node::copy()
3634 return new special_node(mac, tf, gcol, fcol, no_init_string);
3637 void special_node::tprint_start(troff_output_file *out)
3639 out->start_special(tf, gcol, fcol, no_init_string);
3642 void special_node::tprint_char(troff_output_file *out, unsigned char c)
3644 out->special_char(c);
3647 void special_node::tprint_end(troff_output_file *out)
3649 out->end_special();
3652 tfont *special_node::get_tfont()
3654 return tf;
3657 /* suppress_node */
3659 suppress_node::suppress_node(int on_or_off, int issue_limits)
3660 : is_on(on_or_off), emit_limits(issue_limits),
3661 filename(0), position(0), image_id(0)
3665 suppress_node::suppress_node(symbol f, char p, int id)
3666 : is_on(2), emit_limits(0), filename(f), position(p), image_id(id)
3670 suppress_node::suppress_node(int issue_limits, int on_or_off,
3671 symbol f, char p, int id)
3672 : is_on(on_or_off), emit_limits(issue_limits),
3673 filename(f), position(p), image_id(id)
3677 int suppress_node::same(node *n)
3679 return ((is_on == ((suppress_node *)n)->is_on)
3680 && (emit_limits == ((suppress_node *)n)->emit_limits)
3681 && (filename == ((suppress_node *)n)->filename)
3682 && (position == ((suppress_node *)n)->position)
3683 && (image_id == ((suppress_node *)n)->image_id));
3686 const char *suppress_node::type()
3688 return "suppress_node";
3691 node *suppress_node::copy()
3693 return new suppress_node(emit_limits, is_on, filename, position, image_id);
3696 int get_reg_int(const char *p)
3698 reg *r = (reg *)number_reg_dictionary.lookup(p);
3699 units prev_value;
3700 if (r && (r->get_value(&prev_value)))
3701 return (int)prev_value;
3702 else
3703 warning(WARN_REG, "number register `%1' not defined", p);
3704 return 0;
3707 const char *get_reg_str(const char *p)
3709 reg *r = (reg *)number_reg_dictionary.lookup(p);
3710 if (r)
3711 return r->get_string();
3712 else
3713 warning(WARN_REG, "register `%1' not defined", p);
3714 return 0;
3717 void suppress_node::put(troff_output_file *out, const char *s)
3719 int i = 0;
3720 while (s[i] != (char)0) {
3721 out->special_char(s[i]);
3722 i++;
3727 * We need to remember the start of the image and its name.
3730 static char last_position = 0;
3731 static const char *last_image_filename = 0;
3732 static int last_image_id = 0;
3734 inline int min(int a, int b)
3736 return a < b ? a : b;
3740 * tprint - if (is_on == 2)
3741 * remember current position (l, r, c, i) and filename
3742 * else
3743 * if (emit_limits)
3744 * if (html)
3745 * emit image tag
3746 * else
3747 * emit postscript bounds for image
3748 * else
3749 * if (suppress boolean differs from current state)
3750 * alter state
3751 * reset registers
3752 * record current page
3753 * set low water mark.
3756 void suppress_node::tprint(troff_output_file *out)
3758 int current_page = topdiv->get_page_number();
3759 // firstly check to see whether this suppress node contains
3760 // an image filename & position.
3761 if (is_on == 2) {
3762 // remember position and filename
3763 last_position = position;
3764 char *tem = (char *)last_image_filename;
3765 last_image_filename = strsave(filename.contents());
3766 if (tem)
3767 a_delete tem;
3768 last_image_id = image_id;
3769 // printf("start of image and page = %d\n", current_page);
3771 else {
3772 // now check whether the suppress node requires us to issue limits.
3773 if (emit_limits) {
3774 char name[8192];
3775 // remember that the filename will contain a %d in which the
3776 // last_image_id is placed
3777 sprintf(name, last_image_filename, last_image_id);
3778 if (is_html) {
3779 switch (last_position) {
3780 case 'c':
3781 out->start_special();
3782 put(out, "html-tag:.centered-image");
3783 break;
3784 case 'r':
3785 out->start_special();
3786 put(out, "html-tag:.right-image");
3787 break;
3788 case 'l':
3789 out->start_special();
3790 put(out, "html-tag:.left-image");
3791 break;
3792 case 'i':
3794 default:
3797 out->end_special();
3798 out->start_special();
3799 put(out, "html-tag:.auto-image ");
3800 put(out, name);
3801 out->end_special();
3803 else {
3804 // postscript (or other device)
3805 if (suppress_start_page > 0 && current_page != suppress_start_page)
3806 error("suppression limit registers span more than one page;\n"
3807 "image description %1 will be wrong", image_no);
3808 // if (topdiv->get_page_number() != suppress_start_page)
3809 // fprintf(stderr, "end of image and topdiv page = %d and suppress_start_page = %d\n",
3810 // topdiv->get_page_number(), suppress_start_page);
3812 // remember that the filename will contain a %d in which the
3813 // image_no is placed
3814 fprintf(stderr,
3815 "grohtml-info:page %d %d %d %d %d %d %s %d %d %s\n",
3816 topdiv->get_page_number(),
3817 get_reg_int("opminx"), get_reg_int("opminy"),
3818 get_reg_int("opmaxx"), get_reg_int("opmaxy"),
3819 // page offset + line length
3820 get_reg_int(".o") + get_reg_int(".l"),
3821 name, hresolution, vresolution, get_reg_str(".F"));
3822 fflush(stderr);
3825 else {
3826 if (is_on) {
3827 out->on();
3828 // lastly we reset the output registers
3829 reset_output_registers();
3831 else
3832 out->off();
3833 suppress_start_page = current_page;
3838 int suppress_node::force_tprint()
3840 return is_on;
3843 hunits suppress_node::width()
3845 return H0;
3848 /* composite_node */
3850 class composite_node : public charinfo_node {
3851 node *n;
3852 tfont *tf;
3853 public:
3854 composite_node(node *, charinfo *, tfont *, node * = 0);
3855 ~composite_node();
3856 node *copy();
3857 hunits width();
3858 node *last_char_node();
3859 units size();
3860 void tprint(troff_output_file *);
3861 hyphenation_type get_hyphenation_type();
3862 void ascii_print(ascii_output_file *);
3863 void asciify(macro *);
3864 hyphen_list *get_hyphen_list(hyphen_list *, int *);
3865 node *add_self(node *, hyphen_list **);
3866 tfont *get_tfont();
3867 int same(node *);
3868 const char *type();
3869 int force_tprint();
3870 void vertical_extent(vunits *, vunits *);
3871 vunits vertical_width();
3874 composite_node::composite_node(node *p, charinfo *c, tfont *t, node *x)
3875 : charinfo_node(c, x), n(p), tf(t)
3879 composite_node::~composite_node()
3881 delete_node_list(n);
3884 node *composite_node::copy()
3886 return new composite_node(copy_node_list(n), ci, tf);
3889 hunits composite_node::width()
3891 hunits x;
3892 if (tf->get_constant_space(&x))
3893 return x;
3894 x = H0;
3895 for (node *tem = n; tem; tem = tem->next)
3896 x += tem->width();
3897 hunits offset;
3898 if (tf->get_bold(&offset))
3899 x += offset;
3900 x += tf->get_track_kern();
3901 return x;
3904 node *composite_node::last_char_node()
3906 return this;
3909 vunits composite_node::vertical_width()
3911 vunits v = V0;
3912 for (node *tem = n; tem; tem = tem->next)
3913 v += tem->vertical_width();
3914 return v;
3917 units composite_node::size()
3919 return tf->get_size().to_units();
3922 hyphenation_type composite_node::get_hyphenation_type()
3924 return HYPHEN_MIDDLE;
3927 void composite_node::asciify(macro *m)
3929 unsigned char c = ci->get_asciify_code();
3930 if (c == 0)
3931 c = ci->get_ascii_code();
3932 if (c != 0) {
3933 m->append(c);
3934 delete this;
3936 else
3937 m->append(this);
3940 void composite_node::ascii_print(ascii_output_file *ascii)
3942 unsigned char c = ci->get_ascii_code();
3943 if (c != 0)
3944 ascii->outc(c);
3945 else
3946 ascii->outs(ci->nm.contents());
3950 hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail, int *count)
3952 (*count)++;
3953 return new hyphen_list(ci->get_hyphenation_code(), tail);
3956 node *composite_node::add_self(node *nn, hyphen_list **p)
3958 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
3959 next = nn;
3960 nn = this;
3961 if ((*p)->hyphen)
3962 nn = nn->add_discretionary_hyphen();
3963 hyphen_list *pp = *p;
3964 *p = (*p)->next;
3965 delete pp;
3966 return nn;
3969 tfont *composite_node::get_tfont()
3971 return tf;
3974 node *reverse_node_list(node *n)
3976 node *r = 0;
3977 while (n) {
3978 node *tem = n;
3979 n = n->next;
3980 tem->next = r;
3981 r = tem;
3983 return r;
3986 void composite_node::vertical_extent(vunits *min, vunits *max)
3988 n = reverse_node_list(n);
3989 node_list_vertical_extent(n, min, max);
3990 n = reverse_node_list(n);
3993 width_list::width_list(hunits w, hunits s)
3994 : width(w), sentence_width(s), next(0)
3998 width_list::width_list(width_list *w)
3999 : width(w->width), sentence_width(w->sentence_width), next(0)
4003 word_space_node::word_space_node(hunits d, color *c, width_list *w, node *x)
4004 : space_node(d, c, x), orig_width(w), unformat(0)
4008 word_space_node::word_space_node(hunits d, int s, color *c, width_list *w,
4009 int flag, node *x)
4010 : space_node(d, s, 0, c, x), orig_width(w), unformat(flag)
4014 word_space_node::~word_space_node()
4016 width_list *w = orig_width;
4017 while (w != 0) {
4018 width_list *tmp = w;
4019 w = w->next;
4020 delete tmp;
4024 node *word_space_node::copy()
4026 assert(orig_width != 0);
4027 width_list *w_old_curr = orig_width;
4028 width_list *w_new_curr = new width_list(w_old_curr);
4029 width_list *w_new = w_new_curr;
4030 w_old_curr = w_old_curr->next;
4031 while (w_old_curr != 0) {
4032 w_new_curr->next = new width_list(w_old_curr);
4033 w_new_curr = w_new_curr->next;
4034 w_old_curr = w_old_curr->next;
4036 return new word_space_node(n, set, col, w_new, unformat);
4039 int word_space_node::set_unformat_flag()
4041 unformat = 1;
4042 return 1;
4045 void word_space_node::tprint(troff_output_file *out)
4047 out->fill_color(col);
4048 out->word_marker();
4049 out->right(n);
4052 int word_space_node::merge_space(hunits h, hunits sw, hunits ssw)
4054 n += h;
4055 assert(orig_width != 0);
4056 width_list *w = orig_width;
4057 for (; w->next; w = w->next)
4059 w->next = new width_list(sw, ssw);
4060 return 1;
4063 unbreakable_space_node::unbreakable_space_node(hunits d, color *c, node *x)
4064 : word_space_node(d, c, 0, x)
4068 unbreakable_space_node::unbreakable_space_node(hunits d, int s,
4069 color *c, node *x)
4070 : word_space_node(d, s, c, 0, 0, x)
4074 node *unbreakable_space_node::copy()
4076 return new unbreakable_space_node(n, set, col);
4079 int unbreakable_space_node::force_tprint()
4081 return 0;
4084 breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
4085 breakpoint *rest, int)
4087 return rest;
4090 int unbreakable_space_node::nbreaks()
4092 return 0;
4095 void unbreakable_space_node::split(int, node **, node **)
4097 assert(0);
4100 int unbreakable_space_node::merge_space(hunits, hunits, hunits)
4102 return 0;
4105 hvpair::hvpair()
4109 draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4110 color *gc, color *fc)
4111 : npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4113 point = new hvpair[npoints];
4114 for (int i = 0; i < npoints; i++)
4115 point[i] = p[i];
4118 int draw_node::same(node *n)
4120 draw_node *nd = (draw_node *)n;
4121 if (code != nd->code || npoints != nd->npoints || sz != nd->sz
4122 || gcol != nd->gcol || fcol != nd->fcol)
4123 return 0;
4124 for (int i = 0; i < npoints; i++)
4125 if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
4126 return 0;
4127 return 1;
4130 const char *draw_node::type()
4132 return "draw_node";
4135 int draw_node::force_tprint()
4137 return 0;
4140 draw_node::~draw_node()
4142 if (point)
4143 a_delete point;
4146 hunits draw_node::width()
4148 hunits x = H0;
4149 for (int i = 0; i < npoints; i++)
4150 x += point[i].h;
4151 return x;
4154 vunits draw_node::vertical_width()
4156 if (code == 'e')
4157 return V0;
4158 vunits x = V0;
4159 for (int i = 0; i < npoints; i++)
4160 x += point[i].v;
4161 return x;
4164 node *draw_node::copy()
4166 return new draw_node(code, point, npoints, sz, gcol, fcol);
4169 void draw_node::tprint(troff_output_file *out)
4171 out->draw(code, point, npoints, sz, gcol, fcol);
4174 /* tprint methods */
4176 void glyph_node::tprint(troff_output_file *out)
4178 tfont *ptf = tf->get_plain();
4179 if (ptf == tf)
4180 out->put_char_width(ci, ptf, gcol, fcol, width(), H0);
4181 else {
4182 hunits offset;
4183 int bold = tf->get_bold(&offset);
4184 hunits w = ptf->get_width(ci);
4185 hunits k = H0;
4186 hunits x;
4187 int cs = tf->get_constant_space(&x);
4188 if (cs) {
4189 x -= w;
4190 if (bold)
4191 x -= offset;
4192 hunits x2 = x/2;
4193 out->right(x2);
4194 k = x - x2;
4196 else
4197 k = tf->get_track_kern();
4198 if (bold) {
4199 out->put_char(ci, ptf, gcol, fcol);
4200 out->right(offset);
4202 out->put_char_width(ci, ptf, gcol, fcol, w, k);
4206 void glyph_node::zero_width_tprint(troff_output_file *out)
4208 tfont *ptf = tf->get_plain();
4209 hunits offset;
4210 int bold = tf->get_bold(&offset);
4211 hunits x;
4212 int cs = tf->get_constant_space(&x);
4213 if (cs) {
4214 x -= ptf->get_width(ci);
4215 if (bold)
4216 x -= offset;
4217 x = x/2;
4218 out->right(x);
4220 out->put_char(ci, ptf, gcol, fcol);
4221 if (bold) {
4222 out->right(offset);
4223 out->put_char(ci, ptf, gcol, fcol);
4224 out->right(-offset);
4226 if (cs)
4227 out->right(-x);
4230 void break_char_node::tprint(troff_output_file *t)
4232 ch->tprint(t);
4235 void break_char_node::zero_width_tprint(troff_output_file *t)
4237 ch->zero_width_tprint(t);
4240 void hline_node::tprint(troff_output_file *out)
4242 if (x < H0) {
4243 out->right(x);
4244 x = -x;
4246 if (n == 0) {
4247 out->right(x);
4248 return;
4250 hunits w = n->width();
4251 if (w <= H0) {
4252 error("horizontal line drawing character must have positive width");
4253 out->right(x);
4254 return;
4256 int i = int(x/w);
4257 if (i == 0) {
4258 hunits xx = x - w;
4259 hunits xx2 = xx/2;
4260 out->right(xx2);
4261 if (out->is_on())
4262 n->tprint(out);
4263 out->right(xx - xx2);
4265 else {
4266 hunits rem = x - w*i;
4267 if (rem > H0)
4268 if (n->overlaps_horizontally()) {
4269 if (out->is_on())
4270 n->tprint(out);
4271 out->right(rem - w);
4273 else
4274 out->right(rem);
4275 while (--i >= 0)
4276 if (out->is_on())
4277 n->tprint(out);
4281 void vline_node::tprint(troff_output_file *out)
4283 if (n == 0) {
4284 out->down(x);
4285 return;
4287 vunits h = n->size();
4288 int overlaps = n->overlaps_vertically();
4289 vunits y = x;
4290 if (y < V0) {
4291 y = -y;
4292 int i = y / h;
4293 vunits rem = y - i*h;
4294 if (i == 0) {
4295 out->right(n->width());
4296 out->down(-rem);
4298 else {
4299 while (--i > 0) {
4300 n->zero_width_tprint(out);
4301 out->down(-h);
4303 if (overlaps) {
4304 n->zero_width_tprint(out);
4305 out->down(-rem);
4306 if (out->is_on())
4307 n->tprint(out);
4308 out->down(-h);
4310 else {
4311 if (out->is_on())
4312 n->tprint(out);
4313 out->down(-h - rem);
4317 else {
4318 int i = y / h;
4319 vunits rem = y - i*h;
4320 if (i == 0) {
4321 out->down(rem);
4322 out->right(n->width());
4324 else {
4325 out->down(h);
4326 if (overlaps)
4327 n->zero_width_tprint(out);
4328 out->down(rem);
4329 while (--i > 0) {
4330 n->zero_width_tprint(out);
4331 out->down(h);
4333 if (out->is_on())
4334 n->tprint(out);
4339 void zero_width_node::tprint(troff_output_file *out)
4341 if (!n)
4342 return;
4343 if (!n->next) {
4344 n->zero_width_tprint(out);
4345 return;
4347 int hpos = out->get_hpos();
4348 int vpos = out->get_vpos();
4349 node *tem = n;
4350 while (tem) {
4351 tem->tprint(out);
4352 tem = tem->next;
4354 out->moveto(hpos, vpos);
4357 void overstrike_node::tprint(troff_output_file *out)
4359 hunits pos = H0;
4360 for (node *tem = list; tem; tem = tem->next) {
4361 hunits x = (max_width - tem->width())/2;
4362 out->right(x - pos);
4363 pos = x;
4364 tem->zero_width_tprint(out);
4366 out->right(max_width - pos);
4369 void bracket_node::tprint(troff_output_file *out)
4371 if (list == 0)
4372 return;
4373 int npieces = 0;
4374 node *tem;
4375 for (tem = list; tem; tem = tem->next)
4376 ++npieces;
4377 vunits h = list->size();
4378 vunits totalh = h*npieces;
4379 vunits y = (totalh - h)/2;
4380 out->down(y);
4381 for (tem = list; tem; tem = tem->next) {
4382 tem->zero_width_tprint(out);
4383 out->down(-h);
4385 out->right(max_width);
4386 out->down(totalh - y);
4389 void node::tprint(troff_output_file *)
4393 void node::zero_width_tprint(troff_output_file *out)
4395 int hpos = out->get_hpos();
4396 int vpos = out->get_vpos();
4397 tprint(out);
4398 out->moveto(hpos, vpos);
4401 void space_node::tprint(troff_output_file *out)
4403 out->fill_color(col);
4404 out->right(n);
4407 void hmotion_node::tprint(troff_output_file *out)
4409 out->fill_color(col);
4410 out->right(n);
4413 void space_char_hmotion_node::tprint(troff_output_file *out)
4415 out->fill_color(col);
4416 if (is_html) {
4417 // we emit the space width as a negative glyph index
4418 out->flush_tbuf();
4419 out->do_motion();
4420 out->put('N');
4421 out->put(-n.to_units());
4422 out->put('\n');
4424 out->right(n);
4427 void vmotion_node::tprint(troff_output_file *out)
4429 out->fill_color(col);
4430 out->down(n);
4433 void kern_pair_node::tprint(troff_output_file *out)
4435 n1->tprint(out);
4436 out->right(amount);
4437 n2->tprint(out);
4440 static void tprint_reverse_node_list(troff_output_file *out, node *n)
4442 if (n == 0)
4443 return;
4444 tprint_reverse_node_list(out, n->next);
4445 n->tprint(out);
4448 void dbreak_node::tprint(troff_output_file *out)
4450 tprint_reverse_node_list(out, none);
4453 void composite_node::tprint(troff_output_file *out)
4455 hunits bold_offset;
4456 int is_bold = tf->get_bold(&bold_offset);
4457 hunits track_kern = tf->get_track_kern();
4458 hunits constant_space;
4459 int is_constant_spaced = tf->get_constant_space(&constant_space);
4460 hunits x = H0;
4461 if (is_constant_spaced) {
4462 x = constant_space;
4463 for (node *tem = n; tem; tem = tem->next)
4464 x -= tem->width();
4465 if (is_bold)
4466 x -= bold_offset;
4467 hunits x2 = x/2;
4468 out->right(x2);
4469 x -= x2;
4471 if (is_bold) {
4472 int hpos = out->get_hpos();
4473 int vpos = out->get_vpos();
4474 tprint_reverse_node_list(out, n);
4475 out->moveto(hpos, vpos);
4476 out->right(bold_offset);
4478 tprint_reverse_node_list(out, n);
4479 if (is_constant_spaced)
4480 out->right(x);
4481 else
4482 out->right(track_kern);
4485 node *make_composite_node(charinfo *s, environment *env)
4487 int fontno = env_definite_font(env);
4488 if (fontno < 0) {
4489 error("no current font");
4490 return 0;
4492 assert(fontno < font_table_size && font_table[fontno] != 0);
4493 node *n = charinfo_to_node_list(s, env);
4494 font_size fs = env->get_font_size();
4495 int char_height = env->get_char_height();
4496 int char_slant = env->get_char_slant();
4497 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
4498 fontno);
4499 if (env->is_composite())
4500 tf = tf->get_plain();
4501 return new composite_node(n, s, tf);
4504 node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0)
4506 int fontno = env_definite_font(env);
4507 if (fontno < 0) {
4508 error("no current font");
4509 return 0;
4511 assert(fontno < font_table_size && font_table[fontno] != 0);
4512 int fn = fontno;
4513 int found = font_table[fontno]->contains(s);
4514 if (!found) {
4515 macro *mac = s->get_macro();
4516 if (mac && s->is_fallback())
4517 return make_composite_node(s, env);
4518 if (s->numbered()) {
4519 if (!no_error_message)
4520 warning(WARN_CHAR, "can't find numbered character %1",
4521 s->get_number());
4522 return 0;
4524 special_font_list *sf = font_table[fontno]->sf;
4525 while (sf != 0 && !found) {
4526 fn = sf->n;
4527 if (font_table[fn])
4528 found = font_table[fn]->contains(s);
4529 sf = sf->next;
4531 if (!found) {
4532 symbol f = font_table[fontno]->get_name();
4533 string gl(f.contents());
4534 gl += ' ';
4535 gl += s->nm.contents();
4536 gl += '\0';
4537 charinfo *ci = get_charinfo(symbol(gl.contents()));
4538 if (ci && ci->get_macro())
4539 return make_composite_node(ci, env);
4541 if (!found) {
4542 sf = global_special_fonts;
4543 while (sf != 0 && !found) {
4544 fn = sf->n;
4545 if (font_table[fn])
4546 found = font_table[fn]->contains(s);
4547 sf = sf->next;
4550 if (!found)
4551 if (mac && s->is_special())
4552 return make_composite_node(s, env);
4553 if (!found) {
4554 for (fn = 0; fn < font_table_size; fn++)
4555 if (font_table[fn]
4556 && font_table[fn]->is_special()
4557 && font_table[fn]->contains(s)) {
4558 found = 1;
4559 break;
4562 if (!found) {
4563 if (!no_error_message && s->first_time_not_found()) {
4564 unsigned char input_code = s->get_ascii_code();
4565 if (input_code != 0) {
4566 if (csgraph(input_code))
4567 warning(WARN_CHAR, "can't find character `%1'", input_code);
4568 else
4569 warning(WARN_CHAR, "can't find character with input code %1",
4570 int(input_code));
4572 else if (s->nm.contents())
4573 warning(WARN_CHAR, "can't find special character `%1'",
4574 s->nm.contents());
4576 return 0;
4579 font_size fs = env->get_font_size();
4580 int char_height = env->get_char_height();
4581 int char_slant = env->get_char_slant();
4582 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn);
4583 if (env->is_composite())
4584 tf = tf->get_plain();
4585 color *gcol = env->get_glyph_color();
4586 color *fcol = env->get_fill_color();
4587 return new glyph_node(s, tf, gcol, fcol);
4590 node *make_node(charinfo *ci, environment *env)
4592 switch (ci->get_special_translation()) {
4593 case charinfo::TRANSLATE_SPACE:
4594 return new space_char_hmotion_node(env->get_space_width(),
4595 env->get_fill_color());
4596 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4597 return new unbreakable_space_node(env->get_space_width(),
4598 env->get_fill_color());
4599 case charinfo::TRANSLATE_DUMMY:
4600 return new dummy_node;
4601 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4602 error("translation to \\% ignored in this context");
4603 break;
4605 charinfo *tem = ci->get_translation();
4606 if (tem)
4607 ci = tem;
4608 macro *mac = ci->get_macro();
4609 if (mac && ci->is_normal())
4610 return make_composite_node(ci, env);
4611 else
4612 return make_glyph_node(ci, env);
4615 int character_exists(charinfo *ci, environment *env)
4617 if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
4618 return 1;
4619 charinfo *tem = ci->get_translation();
4620 if (tem)
4621 ci = tem;
4622 if (ci->get_macro())
4623 return 1;
4624 node *nd = make_glyph_node(ci, env, 1);
4625 if (nd) {
4626 delete nd;
4627 return 1;
4629 return 0;
4632 node *node::add_char(charinfo *ci, environment *env,
4633 hunits *widthp, int *spacep)
4635 node *res;
4636 switch (ci->get_special_translation()) {
4637 case charinfo::TRANSLATE_SPACE:
4638 res = new space_char_hmotion_node(env->get_space_width(),
4639 env->get_fill_color(), this);
4640 *widthp += res->width();
4641 return res;
4642 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4643 res = new unbreakable_space_node(env->get_space_width(),
4644 env->get_fill_color(), this);
4645 res->freeze_space();
4646 *widthp += res->width();
4647 *spacep += res->nspaces();
4648 return res;
4649 case charinfo::TRANSLATE_DUMMY:
4650 return new dummy_node(this);
4651 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4652 return add_discretionary_hyphen();
4654 charinfo *tem = ci->get_translation();
4655 if (tem)
4656 ci = tem;
4657 macro *mac = ci->get_macro();
4658 if (mac && ci->is_normal()) {
4659 res = make_composite_node(ci, env);
4660 if (res) {
4661 res->next = this;
4662 *widthp += res->width();
4664 else
4665 return this;
4667 else {
4668 node *gn = make_glyph_node(ci, env);
4669 if (gn == 0)
4670 return this;
4671 else {
4672 hunits old_width = width();
4673 node *p = gn->merge_self(this);
4674 if (p == 0) {
4675 *widthp += gn->width();
4676 gn->next = this;
4677 res = gn;
4679 else {
4680 *widthp += p->width() - old_width;
4681 res = p;
4685 int break_code = 0;
4686 if (ci->can_break_before())
4687 break_code = 1;
4688 if (ci->can_break_after())
4689 break_code |= 2;
4690 if (break_code) {
4691 node *next1 = res->next;
4692 res->next = 0;
4693 res = new break_char_node(res, break_code, env->get_fill_color(), next1);
4695 return res;
4698 #ifdef __GNUG__
4699 inline
4700 #endif
4701 int same_node(node *n1, node *n2)
4703 if (n1 != 0) {
4704 if (n2 != 0)
4705 return n1->type() == n2->type() && n1->same(n2);
4706 else
4707 return 0;
4709 else
4710 return n2 == 0;
4713 int same_node_list(node *n1, node *n2)
4715 while (n1 && n2) {
4716 if (n1->type() != n2->type() || !n1->same(n2))
4717 return 0;
4718 n1 = n1->next;
4719 n2 = n2->next;
4721 return !n1 && !n2;
4724 int extra_size_node::same(node *nd)
4726 return n == ((extra_size_node *)nd)->n;
4729 const char *extra_size_node::type()
4731 return "extra_size_node";
4734 int extra_size_node::force_tprint()
4736 return 0;
4739 int vertical_size_node::same(node *nd)
4741 return n == ((vertical_size_node *)nd)->n;
4744 const char *vertical_size_node::type()
4746 return "vertical_size_node";
4749 int vertical_size_node::set_unformat_flag()
4751 return 0;
4754 int vertical_size_node::force_tprint()
4756 return 0;
4759 int hmotion_node::same(node *nd)
4761 return n == ((hmotion_node *)nd)->n
4762 && col == ((hmotion_node *)nd)->col;
4765 const char *hmotion_node::type()
4767 return "hmotion_node";
4770 int hmotion_node::set_unformat_flag()
4772 unformat = 1;
4773 return 1;
4776 int hmotion_node::force_tprint()
4778 return 0;
4781 node *hmotion_node::add_self(node *n, hyphen_list **p)
4783 next = n;
4784 hyphen_list *pp = *p;
4785 *p = (*p)->next;
4786 delete pp;
4787 return this;
4790 hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail, int *)
4792 return new hyphen_list(0, tail);
4795 int space_char_hmotion_node::same(node *nd)
4797 return n == ((space_char_hmotion_node *)nd)->n
4798 && col == ((space_char_hmotion_node *)nd)->col;
4801 const char *space_char_hmotion_node::type()
4803 return "space_char_hmotion_node";
4806 int space_char_hmotion_node::force_tprint()
4808 return 0;
4811 node *space_char_hmotion_node::add_self(node *n, hyphen_list **p)
4813 next = n;
4814 hyphen_list *pp = *p;
4815 *p = (*p)->next;
4816 delete pp;
4817 return this;
4820 hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail,
4821 int *)
4823 return new hyphen_list(0, tail);
4826 int vmotion_node::same(node *nd)
4828 return n == ((vmotion_node *)nd)->n
4829 && col == ((vmotion_node *)nd)->col;
4832 const char *vmotion_node::type()
4834 return "vmotion_node";
4837 int vmotion_node::force_tprint()
4839 return 0;
4842 int hline_node::same(node *nd)
4844 return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n);
4847 const char *hline_node::type()
4849 return "hline_node";
4852 int hline_node::force_tprint()
4854 return 0;
4857 int vline_node::same(node *nd)
4859 return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n);
4862 const char *vline_node::type()
4864 return "vline_node";
4867 int vline_node::force_tprint()
4869 return 0;
4872 int dummy_node::same(node * /*nd*/)
4874 return 1;
4877 const char *dummy_node::type()
4879 return "dummy_node";
4882 int dummy_node::force_tprint()
4884 return 0;
4887 int transparent_dummy_node::same(node * /*nd*/)
4889 return 1;
4892 const char *transparent_dummy_node::type()
4894 return "transparent_dummy_node";
4897 int transparent_dummy_node::force_tprint()
4899 return 0;
4902 int transparent_dummy_node::ends_sentence()
4904 return 2;
4907 int zero_width_node::same(node *nd)
4909 return same_node_list(n, ((zero_width_node *)nd)->n);
4912 const char *zero_width_node::type()
4914 return "zero_width_node";
4917 int zero_width_node::force_tprint()
4919 return 0;
4922 int italic_corrected_node::same(node *nd)
4924 return (x == ((italic_corrected_node *)nd)->x
4925 && same_node(n, ((italic_corrected_node *)nd)->n));
4928 const char *italic_corrected_node::type()
4930 return "italic_corrected_node";
4933 int italic_corrected_node::force_tprint()
4935 return 0;
4938 left_italic_corrected_node::left_italic_corrected_node(node *x)
4939 : node(x), n(0)
4943 left_italic_corrected_node::~left_italic_corrected_node()
4945 delete n;
4948 node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
4950 if (n == 0) {
4951 hunits lic = gn->left_italic_correction();
4952 if (!lic.is_zero()) {
4953 x = lic;
4954 n = gn;
4955 return this;
4958 else {
4959 node *nd = n->merge_glyph_node(gn);
4960 if (nd) {
4961 n = nd;
4962 x = n->left_italic_correction();
4963 return this;
4966 return 0;
4969 node *left_italic_corrected_node::copy()
4971 left_italic_corrected_node *nd = new left_italic_corrected_node;
4972 if (n) {
4973 nd->n = n->copy();
4974 nd->x = x;
4976 return nd;
4979 void left_italic_corrected_node::tprint(troff_output_file *out)
4981 if (n) {
4982 out->right(x);
4983 n->tprint(out);
4987 const char *left_italic_corrected_node::type()
4989 return "left_italic_corrected_node";
4992 int left_italic_corrected_node::force_tprint()
4994 return 0;
4997 int left_italic_corrected_node::same(node *nd)
4999 return (x == ((left_italic_corrected_node *)nd)->x
5000 && same_node(n, ((left_italic_corrected_node *)nd)->n));
5003 void left_italic_corrected_node::ascii_print(ascii_output_file *out)
5005 if (n)
5006 n->ascii_print(out);
5009 hunits left_italic_corrected_node::width()
5011 return n ? n->width() + x : H0;
5014 void left_italic_corrected_node::vertical_extent(vunits *min, vunits *max)
5016 if (n)
5017 n->vertical_extent(min, max);
5018 else
5019 node::vertical_extent(min, max);
5022 hunits left_italic_corrected_node::skew()
5024 return n ? n->skew() + x/2 : H0;
5027 hunits left_italic_corrected_node::subscript_correction()
5029 return n ? n->subscript_correction() : H0;
5032 hunits left_italic_corrected_node::italic_correction()
5034 return n ? n->italic_correction() : H0;
5037 int left_italic_corrected_node::ends_sentence()
5039 return n ? n->ends_sentence() : 0;
5042 int left_italic_corrected_node::overlaps_horizontally()
5044 return n ? n->overlaps_horizontally() : 0;
5047 int left_italic_corrected_node::overlaps_vertically()
5049 return n ? n->overlaps_vertically() : 0;
5052 node *left_italic_corrected_node::last_char_node()
5054 return n ? n->last_char_node() : 0;
5057 tfont *left_italic_corrected_node::get_tfont()
5059 return n ? n->get_tfont() : 0;
5062 hyphenation_type left_italic_corrected_node::get_hyphenation_type()
5064 if (n)
5065 return n->get_hyphenation_type();
5066 else
5067 return HYPHEN_MIDDLE;
5070 hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail,
5071 int *count)
5073 return n ? n->get_hyphen_list(tail, count) : tail;
5076 node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
5078 if (n) {
5079 nd = new left_italic_corrected_node(nd);
5080 nd = n->add_self(nd, p);
5081 n = 0;
5082 delete this;
5084 return nd;
5087 int left_italic_corrected_node::character_type()
5089 return n ? n->character_type() : 0;
5092 int overstrike_node::same(node *nd)
5094 return same_node_list(list, ((overstrike_node *)nd)->list);
5097 const char *overstrike_node::type()
5099 return "overstrike_node";
5102 int overstrike_node::force_tprint()
5104 return 0;
5107 node *overstrike_node::add_self(node *n, hyphen_list **p)
5109 next = n;
5110 hyphen_list *pp = *p;
5111 *p = (*p)->next;
5112 delete pp;
5113 return this;
5116 hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail, int *)
5118 return new hyphen_list(0, tail);
5121 int bracket_node::same(node *nd)
5123 return same_node_list(list, ((bracket_node *)nd)->list);
5126 const char *bracket_node::type()
5128 return "bracket_node";
5131 int bracket_node::force_tprint()
5133 return 0;
5136 int composite_node::same(node *nd)
5138 return ci == ((composite_node *)nd)->ci
5139 && same_node_list(n, ((composite_node *)nd)->n);
5142 const char *composite_node::type()
5144 return "composite_node";
5147 int composite_node::force_tprint()
5149 return 0;
5152 int glyph_node::same(node *nd)
5154 return ci == ((glyph_node *)nd)->ci
5155 && tf == ((glyph_node *)nd)->tf
5156 && gcol == ((glyph_node *)nd)->gcol
5157 && fcol == ((glyph_node *)nd)->fcol;
5160 const char *glyph_node::type()
5162 return "glyph_node";
5165 int glyph_node::force_tprint()
5167 return 0;
5170 int ligature_node::same(node *nd)
5172 return (same_node(n1, ((ligature_node *)nd)->n1)
5173 && same_node(n2, ((ligature_node *)nd)->n2)
5174 && glyph_node::same(nd));
5177 const char *ligature_node::type()
5179 return "ligature_node";
5182 int ligature_node::force_tprint()
5184 return 0;
5187 int kern_pair_node::same(node *nd)
5189 return (amount == ((kern_pair_node *)nd)->amount
5190 && same_node(n1, ((kern_pair_node *)nd)->n1)
5191 && same_node(n2, ((kern_pair_node *)nd)->n2));
5194 const char *kern_pair_node::type()
5196 return "kern_pair_node";
5199 int kern_pair_node::force_tprint()
5201 return 0;
5204 int dbreak_node::same(node *nd)
5206 return (same_node_list(none, ((dbreak_node *)nd)->none)
5207 && same_node_list(pre, ((dbreak_node *)nd)->pre)
5208 && same_node_list(post, ((dbreak_node *)nd)->post));
5211 const char *dbreak_node::type()
5213 return "dbreak_node";
5216 int dbreak_node::force_tprint()
5218 return 0;
5221 int break_char_node::same(node *nd)
5223 return break_code == ((break_char_node *)nd)->break_code
5224 && col == ((break_char_node *)nd)->col
5225 && same_node(ch, ((break_char_node *)nd)->ch);
5228 const char *break_char_node::type()
5230 return "break_char_node";
5233 int break_char_node::force_tprint()
5235 return 0;
5238 int line_start_node::same(node * /*nd*/)
5240 return 1;
5243 const char *line_start_node::type()
5245 return "line_start_node";
5248 int line_start_node::force_tprint()
5250 return 0;
5253 int space_node::same(node *nd)
5255 return n == ((space_node *)nd)->n
5256 && set == ((space_node *)nd)->set
5257 && col == ((space_node *)nd)->col;
5260 const char *space_node::type()
5262 return "space_node";
5265 int word_space_node::same(node *nd)
5267 return n == ((word_space_node *)nd)->n
5268 && set == ((word_space_node *)nd)->set
5269 && col == ((word_space_node *)nd)->col;
5272 const char *word_space_node::type()
5274 return "word_space_node";
5277 int word_space_node::force_tprint()
5279 return 0;
5282 void unbreakable_space_node::tprint(troff_output_file *out)
5284 out->fill_color(col);
5285 if (is_html) {
5286 // we emit the space width as a negative glyph index
5287 out->flush_tbuf();
5288 out->do_motion();
5289 out->put('N');
5290 out->put(-n.to_units());
5291 out->put('\n');
5293 out->right(n);
5296 int unbreakable_space_node::same(node *nd)
5298 return n == ((unbreakable_space_node *)nd)->n
5299 && set == ((unbreakable_space_node *)nd)->set
5300 && col == ((unbreakable_space_node *)nd)->col;
5303 const char *unbreakable_space_node::type()
5305 return "unbreakable_space_node";
5308 node *unbreakable_space_node::add_self(node *n, hyphen_list **p)
5310 next = n;
5311 hyphen_list *pp = *p;
5312 *p = (*p)->next;
5313 delete pp;
5314 return this;
5317 hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail, int *)
5319 return new hyphen_list(0, tail);
5322 int diverted_space_node::same(node *nd)
5324 return n == ((diverted_space_node *)nd)->n;
5327 const char *diverted_space_node::type()
5329 return "diverted_space_node";
5332 int diverted_space_node::force_tprint()
5334 return 0;
5337 int diverted_copy_file_node::same(node *nd)
5339 return filename == ((diverted_copy_file_node *)nd)->filename;
5342 const char *diverted_copy_file_node::type()
5344 return "diverted_copy_file_node";
5347 int diverted_copy_file_node::force_tprint()
5349 return 0;
5352 // Grow the font_table so that its size is > n.
5354 static void grow_font_table(int n)
5356 assert(n >= font_table_size);
5357 font_info **old_font_table = font_table;
5358 int old_font_table_size = font_table_size;
5359 font_table_size = font_table_size ? (font_table_size*3)/2 : 10;
5360 if (font_table_size <= n)
5361 font_table_size = n + 10;
5362 font_table = new font_info *[font_table_size];
5363 if (old_font_table_size)
5364 memcpy(font_table, old_font_table,
5365 old_font_table_size*sizeof(font_info *));
5366 a_delete old_font_table;
5367 for (int i = old_font_table_size; i < font_table_size; i++)
5368 font_table[i] = 0;
5371 dictionary font_translation_dictionary(17);
5373 static symbol get_font_translation(symbol nm)
5375 void *p = font_translation_dictionary.lookup(nm);
5376 return p ? symbol((char *)p) : nm;
5379 dictionary font_dictionary(50);
5381 static int mount_font_no_translate(int n, symbol name, symbol external_name)
5383 assert(n >= 0);
5384 // We store the address of this char in font_dictionary to indicate
5385 // that we've previously tried to mount the font and failed.
5386 static char a_char;
5387 font *fm = 0;
5388 void *p = font_dictionary.lookup(external_name);
5389 if (p == 0) {
5390 int not_found;
5391 fm = font::load_font(external_name.contents(), &not_found);
5392 if (!fm) {
5393 if (not_found)
5394 warning(WARN_FONT, "can't find font `%1'", external_name.contents());
5395 (void)font_dictionary.lookup(external_name, &a_char);
5396 return 0;
5398 (void)font_dictionary.lookup(name, fm);
5400 else if (p == &a_char) {
5401 #if 0
5402 error("invalid font `%1'", external_name.contents());
5403 #endif
5404 return 0;
5406 else
5407 fm = (font*)p;
5408 if (n >= font_table_size) {
5409 if (n - font_table_size > 1000) {
5410 error("font position too much larger than first unused position");
5411 return 0;
5413 grow_font_table(n);
5415 else if (font_table[n] != 0)
5416 delete font_table[n];
5417 font_table[n] = new font_info(name, n, external_name, fm);
5418 font_family::invalidate_fontno(n);
5419 return 1;
5422 int mount_font(int n, symbol name, symbol external_name)
5424 assert(n >= 0);
5425 name = get_font_translation(name);
5426 if (external_name.is_null())
5427 external_name = name;
5428 else
5429 external_name = get_font_translation(external_name);
5430 return mount_font_no_translate(n, name, external_name);
5433 void mount_style(int n, symbol name)
5435 assert(n >= 0);
5436 if (n >= font_table_size) {
5437 if (n - font_table_size > 1000) {
5438 error("font position too much larger than first unused position");
5439 return;
5441 grow_font_table(n);
5443 else if (font_table[n] != 0)
5444 delete font_table[n];
5445 font_table[n] = new font_info(get_font_translation(name), n, NULL_SYMBOL, 0);
5446 font_family::invalidate_fontno(n);
5449 /* global functions */
5451 void font_translate()
5453 symbol from = get_name(1);
5454 if (!from.is_null()) {
5455 symbol to = get_name();
5456 if (to.is_null() || from == to)
5457 font_translation_dictionary.remove(from);
5458 else
5459 (void)font_translation_dictionary.lookup(from, (void *)to.contents());
5461 skip_line();
5464 void font_position()
5466 int n;
5467 if (get_integer(&n)) {
5468 if (n < 0)
5469 error("negative font position");
5470 else {
5471 symbol internal_name = get_name(1);
5472 if (!internal_name.is_null()) {
5473 symbol external_name = get_long_name();
5474 mount_font(n, internal_name, external_name); // ignore error
5478 skip_line();
5481 font_family::font_family(symbol s)
5482 : map_size(10), nm(s)
5484 map = new int[map_size];
5485 for (int i = 0; i < map_size; i++)
5486 map[i] = -1;
5489 font_family::~font_family()
5491 a_delete map;
5494 int font_family::make_definite(int i)
5496 if (i >= 0) {
5497 if (i < map_size && map[i] >= 0)
5498 return map[i];
5499 else {
5500 if (i < font_table_size && font_table[i] != 0) {
5501 if (i >= map_size) {
5502 int old_map_size = map_size;
5503 int *old_map = map;
5504 map_size *= 3;
5505 map_size /= 2;
5506 if (i >= map_size)
5507 map_size = i + 10;
5508 map = new int[map_size];
5509 memcpy(map, old_map, old_map_size*sizeof(int));
5510 a_delete old_map;
5511 for (int j = old_map_size; j < map_size; j++)
5512 map[j] = -1;
5514 if (font_table[i]->is_style()) {
5515 symbol sty = font_table[i]->get_name();
5516 symbol f = concat(nm, sty);
5517 int n;
5518 // don't use symbol_fontno, because that might return a style
5519 // and because we don't want to translate the name
5520 for (n = 0; n < font_table_size; n++)
5521 if (font_table[n] != 0 && font_table[n]->is_named(f)
5522 && !font_table[n]->is_style())
5523 break;
5524 if (n >= font_table_size) {
5525 n = next_available_font_position();
5526 if (!mount_font_no_translate(n, f, f))
5527 return -1;
5529 return map[i] = n;
5531 else
5532 return map[i] = i;
5534 else
5535 return -1;
5538 else
5539 return -1;
5542 dictionary family_dictionary(5);
5544 font_family *lookup_family(symbol nm)
5546 font_family *f = (font_family *)family_dictionary.lookup(nm);
5547 if (!f) {
5548 f = new font_family(nm);
5549 (void)family_dictionary.lookup(nm, f);
5551 return f;
5554 void font_family::invalidate_fontno(int n)
5556 assert(n >= 0 && n < font_table_size);
5557 dictionary_iterator iter(family_dictionary);
5558 symbol nm;
5559 font_family *fam;
5560 while (iter.get(&nm, (void **)&fam)) {
5561 int map_size = fam->map_size;
5562 if (n < map_size)
5563 fam->map[n] = -1;
5564 for (int i = 0; i < map_size; i++)
5565 if (fam->map[i] == n)
5566 fam->map[i] = -1;
5570 void style()
5572 int n;
5573 if (get_integer(&n)) {
5574 if (n < 0)
5575 error("negative font position");
5576 else {
5577 symbol internal_name = get_name(1);
5578 if (!internal_name.is_null())
5579 mount_style(n, internal_name);
5582 skip_line();
5585 static int get_fontno()
5587 int n;
5588 tok.skip();
5589 if (tok.delimiter()) {
5590 symbol s = get_name(1);
5591 if (!s.is_null()) {
5592 n = symbol_fontno(s);
5593 if (n < 0) {
5594 n = next_available_font_position();
5595 if (!mount_font(n, s))
5596 return -1;
5598 return curenv->get_family()->make_definite(n);
5601 else if (get_integer(&n)) {
5602 if (n < 0 || n >= font_table_size || font_table[n] == 0)
5603 error("bad font number");
5604 else
5605 return curenv->get_family()->make_definite(n);
5607 return -1;
5610 static int underline_fontno = 2;
5612 void underline_font()
5614 int n = get_fontno();
5615 if (n >= 0)
5616 underline_fontno = n;
5617 skip_line();
5620 int get_underline_fontno()
5622 return underline_fontno;
5625 void define_font_special_character()
5627 int n = get_fontno();
5628 if (n < 0) {
5629 skip_line();
5630 return;
5632 symbol f = font_table[n]->get_name();
5633 do_define_character(CHAR_FONT_SPECIAL, f.contents());
5636 void remove_font_special_character()
5638 int n = get_fontno();
5639 if (n < 0) {
5640 skip_line();
5641 return;
5643 symbol f = font_table[n]->get_name();
5644 while (!tok.newline() && !tok.eof()) {
5645 if (!tok.space() && !tok.tab()) {
5646 charinfo *s = tok.get_char(1);
5647 string gl(f.contents());
5648 gl += ' ';
5649 gl += s->nm.contents();
5650 gl += '\0';
5651 charinfo *ci = get_charinfo(symbol(gl.contents()));
5652 if (!ci)
5653 break;
5654 macro *m = ci->set_macro(0);
5655 if (m)
5656 delete m;
5658 tok.next();
5660 skip_line();
5663 static void read_special_fonts(special_font_list **sp)
5665 special_font_list *s = *sp;
5666 *sp = 0;
5667 while (s != 0) {
5668 special_font_list *tem = s;
5669 s = s->next;
5670 delete tem;
5672 special_font_list **p = sp;
5673 while (has_arg()) {
5674 int i = get_fontno();
5675 if (i >= 0) {
5676 special_font_list *tem = new special_font_list;
5677 tem->n = i;
5678 tem->next = 0;
5679 *p = tem;
5680 p = &(tem->next);
5685 void font_special_request()
5687 int n = get_fontno();
5688 if (n >= 0)
5689 read_special_fonts(&font_table[n]->sf);
5690 skip_line();
5693 void special_request()
5695 read_special_fonts(&global_special_fonts);
5696 skip_line();
5699 int next_available_font_position()
5701 int i;
5702 for (i = 1; i < font_table_size && font_table[i] != 0; i++)
5704 return i;
5707 int symbol_fontno(symbol s)
5709 s = get_font_translation(s);
5710 for (int i = 0; i < font_table_size; i++)
5711 if (font_table[i] != 0 && font_table[i]->is_named(s))
5712 return i;
5713 return -1;
5716 int is_good_fontno(int n)
5718 return n >= 0 && n < font_table_size && font_table[n] != 0;
5721 int get_bold_fontno(int n)
5723 if (n >= 0 && n < font_table_size && font_table[n] != 0) {
5724 hunits offset;
5725 if (font_table[n]->get_bold(&offset))
5726 return offset.to_units() + 1;
5727 else
5728 return 0;
5730 else
5731 return 0;
5734 hunits env_digit_width(environment *env)
5736 node *n = make_glyph_node(charset_table['0'], env);
5737 if (n) {
5738 hunits x = n->width();
5739 delete n;
5740 return x;
5742 else
5743 return H0;
5746 hunits env_space_width(environment *env)
5748 int fn = env_definite_font(env);
5749 font_size fs = env->get_font_size();
5750 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5751 return scale(fs.to_units()/3, env->get_space_size(), 12);
5752 else
5753 return font_table[fn]->get_space_width(fs, env->get_space_size());
5756 hunits env_sentence_space_width(environment *env)
5758 int fn = env_definite_font(env);
5759 font_size fs = env->get_font_size();
5760 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5761 return scale(fs.to_units()/3, env->get_sentence_space_size(), 12);
5762 else
5763 return font_table[fn]->get_space_width(fs, env->get_sentence_space_size());
5766 hunits env_half_narrow_space_width(environment *env)
5768 int fn = env_definite_font(env);
5769 font_size fs = env->get_font_size();
5770 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5771 return 0;
5772 else
5773 return font_table[fn]->get_half_narrow_space_width(fs);
5776 hunits env_narrow_space_width(environment *env)
5778 int fn = env_definite_font(env);
5779 font_size fs = env->get_font_size();
5780 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
5781 return 0;
5782 else
5783 return font_table[fn]->get_narrow_space_width(fs);
5786 void bold_font()
5788 int n = get_fontno();
5789 if (n >= 0) {
5790 if (has_arg()) {
5791 if (tok.delimiter()) {
5792 int f = get_fontno();
5793 if (f >= 0) {
5794 units offset;
5795 if (has_arg() && get_number(&offset, 'u') && offset >= 1)
5796 font_table[f]->set_conditional_bold(n, hunits(offset - 1));
5797 else
5798 font_table[f]->conditional_unbold(n);
5801 else {
5802 units offset;
5803 if (get_number(&offset, 'u') && offset >= 1)
5804 font_table[n]->set_bold(hunits(offset - 1));
5805 else
5806 font_table[n]->unbold();
5809 else
5810 font_table[n]->unbold();
5812 skip_line();
5815 track_kerning_function::track_kerning_function() : non_zero(0)
5819 track_kerning_function::track_kerning_function(int min_s, hunits min_a,
5820 int max_s, hunits max_a)
5821 : non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s),
5822 max_amount(max_a)
5826 int track_kerning_function::operator==(const track_kerning_function &tk)
5828 if (non_zero)
5829 return (tk.non_zero
5830 && min_size == tk.min_size
5831 && min_amount == tk.min_amount
5832 && max_size == tk.max_size
5833 && max_amount == tk.max_amount);
5834 else
5835 return !tk.non_zero;
5838 int track_kerning_function::operator!=(const track_kerning_function &tk)
5840 if (non_zero)
5841 return (!tk.non_zero
5842 || min_size != tk.min_size
5843 || min_amount != tk.min_amount
5844 || max_size != tk.max_size
5845 || max_amount != tk.max_amount);
5846 else
5847 return tk.non_zero;
5850 hunits track_kerning_function::compute(int size)
5852 if (non_zero) {
5853 if (max_size <= min_size)
5854 return min_amount;
5855 else if (size <= min_size)
5856 return min_amount;
5857 else if (size >= max_size)
5858 return max_amount;
5859 else
5860 return (scale(max_amount, size - min_size, max_size - min_size)
5861 + scale(min_amount, max_size - size, max_size - min_size));
5863 else
5864 return H0;
5867 void track_kern()
5869 int n = get_fontno();
5870 if (n >= 0) {
5871 int min_s, max_s;
5872 hunits min_a, max_a;
5873 if (has_arg()
5874 && get_number(&min_s, 'z')
5875 && get_hunits(&min_a, 'p')
5876 && get_number(&max_s, 'z')
5877 && get_hunits(&max_a, 'p')) {
5878 track_kerning_function tk(min_s, min_a, max_s, max_a);
5879 font_table[n]->set_track_kern(tk);
5881 else {
5882 track_kerning_function tk;
5883 font_table[n]->set_track_kern(tk);
5886 skip_line();
5889 void constant_space()
5891 int n = get_fontno();
5892 if (n >= 0) {
5893 int x, y;
5894 if (!has_arg() || !get_integer(&x))
5895 font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
5896 else {
5897 if (!has_arg() || !get_number(&y, 'z'))
5898 font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
5899 else
5900 font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
5901 scale(y*x,
5902 units_per_inch,
5903 36*72*sizescale));
5906 skip_line();
5909 void ligature()
5911 int lig;
5912 if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2)
5913 global_ligature_mode = lig;
5914 else
5915 global_ligature_mode = 1;
5916 skip_line();
5919 void kern_request()
5921 int k;
5922 if (has_arg() && get_integer(&k))
5923 global_kern_mode = k != 0;
5924 else
5925 global_kern_mode = 1;
5926 skip_line();
5929 void set_soft_hyphen_char()
5931 soft_hyphen_char = get_optional_char();
5932 if (!soft_hyphen_char)
5933 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
5934 skip_line();
5937 void init_output()
5939 if (suppress_output_flag)
5940 the_output = new suppress_output_file;
5941 else if (ascii_output_flag)
5942 the_output = new ascii_output_file;
5943 else
5944 the_output = new troff_output_file;
5947 class next_available_font_position_reg : public reg {
5948 public:
5949 const char *get_string();
5952 const char *next_available_font_position_reg::get_string()
5954 return i_to_a(next_available_font_position());
5957 class printing_reg : public reg {
5958 public:
5959 const char *get_string();
5962 const char *printing_reg::get_string()
5964 if (the_output)
5965 return the_output->is_printing() ? "1" : "0";
5966 else
5967 return "0";
5970 void init_node_requests()
5972 init_request("bd", bold_font);
5973 init_request("cs", constant_space);
5974 init_request("fp", font_position);
5975 init_request("fschar", define_font_special_character);
5976 init_request("fspecial", font_special_request);
5977 init_request("ftr", font_translate);
5978 init_request("kern", kern_request);
5979 init_request("lg", ligature);
5980 init_request("rfschar", remove_font_special_character);
5981 init_request("shc", set_soft_hyphen_char);
5982 init_request("special", special_request);
5983 init_request("sty", style);
5984 init_request("tkf", track_kern);
5985 init_request("uf", underline_font);
5986 number_reg_dictionary.define(".fp", new next_available_font_position_reg);
5987 number_reg_dictionary.define(".kern",
5988 new constant_int_reg(&global_kern_mode));
5989 number_reg_dictionary.define(".lg",
5990 new constant_int_reg(&global_ligature_mode));
5991 number_reg_dictionary.define(".P", new printing_reg);
5992 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);