Sync-to-go: update copyright for 2015
[s-roff.git] / src / troff / node.cpp
blobaec26dba07c016cbf6ebb2aaa0b8bc622e5efec8
1 /*@
2 * Copyright (c) 2014 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
4 * Copyright (C) 1989 - 1992, 2000 - 2006, 2008 Free Software Foundation, Inc.
5 * Written by James Clark (jjc@jclark.com)
7 * This is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2, or (at your option) any later
10 * version.
12 * This is distributed in the hope that it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with groff; see the file COPYING. If not, write to the Free Software
19 * Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
22 #include "config.h"
23 #include "troff-config.h"
25 #include <sys/wait.h>
27 #include <unistd.h>
29 #include "file_case.h"
30 #include "font.h"
31 #include "geometry.h"
32 #include "nonposix.h"
33 #include "stringclass.h"
35 #include "charinfo.h"
36 #include "dictionary.h"
37 #include "div.h"
38 #include "env.h"
39 #include "hvunits.h"
40 #include "input.h"
41 #include "mtsm.h"
42 #include "node.h"
43 #include "reg.h"
44 #include "request.h"
45 #include "token.h"
46 #include "troff.h"
48 extern int debug_state;
49 // declarations to avoid friend name injections
50 class tfont;
51 class tfont_spec;
52 tfont *make_tfont(tfont_spec &);
54 // how many boundaries of images have been written? Useful for
55 // debugging grohtml
56 int image_no = 0;
57 static int suppress_start_page = 0;
59 #define STORE_WIDTH 1
61 symbol HYPHEN_SYMBOL("hy");
63 // Character used when a hyphen is inserted at a line break.
64 static charinfo *soft_hyphen_char;
66 enum constant_space_type {
67 CONSTANT_SPACE_NONE,
68 CONSTANT_SPACE_RELATIVE,
69 CONSTANT_SPACE_ABSOLUTE
72 struct special_font_list {
73 int n;
74 special_font_list *next;
77 special_font_list *global_special_fonts;
78 static int global_ligature_mode = 1;
79 static int global_kern_mode = 1;
81 class track_kerning_function
83 int non_zero;
84 units min_size;
85 hunits min_amount;
86 units max_size;
87 hunits max_amount;
89 public:
90 track_kerning_function();
91 track_kerning_function(units, hunits, units, hunits);
92 int operator==(const track_kerning_function &);
93 int operator!=(const track_kerning_function &);
94 hunits compute(int point_size);
97 // embolden fontno when this is the current font
98 class conditional_bold
100 public:
101 conditional_bold *next;
102 int fontno;
103 hunits offset;
105 conditional_bold(int, hunits, conditional_bold * = 0);
108 class font_info
110 tfont *last_tfont;
111 int number;
112 font_size last_size;
113 int last_height;
114 int last_slant;
115 symbol internal_name;
116 symbol external_name;
117 font *fm;
118 char is_bold;
119 hunits bold_offset;
120 track_kerning_function track_kern;
121 constant_space_type is_constant_spaced;
122 units constant_space;
123 int last_ligature_mode;
124 int last_kern_mode;
125 conditional_bold *cond_bold_list;
127 void flush();
129 public:
130 special_font_list *sf;
132 font_info(symbol, int, symbol, font *);
133 int contains(charinfo *);
134 void set_bold(hunits);
135 void unbold();
136 void set_conditional_bold(int, hunits);
137 void conditional_unbold(int);
138 void set_track_kern(track_kerning_function &);
139 void set_constant_space(constant_space_type, units = 0);
140 int is_named(symbol);
141 symbol get_name();
142 tfont *get_tfont(font_size, int, int, int);
143 hunits get_space_width(font_size, int);
144 hunits get_narrow_space_width(font_size);
145 hunits get_half_narrow_space_width(font_size);
146 int get_bold(hunits *);
147 int is_special();
148 int is_style();
149 void set_zoom(int);
150 int get_zoom();
151 friend symbol get_font_name(int, environment *);
152 friend symbol get_style_name(int);
155 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;
172 public:
173 tfont_spec(symbol, int, font *, font_size, int, int);
174 tfont_spec(const tfont_spec &spec) { *this = spec; }
175 tfont_spec plain();
176 int operator==(const tfont_spec &);
177 friend tfont *font_info::get_tfont(font_size fs, int, int, int);
180 class tfont
181 : public tfont_spec
183 static tfont *tfont_list;
185 tfont *next;
186 tfont *plain_version;
188 public:
189 tfont(tfont_spec &);
190 int contains(charinfo *);
191 hunits get_width(charinfo *c);
192 int get_bold(hunits *);
193 int get_constant_space(hunits *);
194 hunits get_track_kern();
195 tfont *get_plain();
196 font_size get_size();
197 int get_zoom();
198 symbol get_name();
199 charinfo *get_lig(charinfo *c1, charinfo *c2);
200 int get_kern(charinfo *c1, charinfo *c2, hunits *res);
201 int get_input_position();
202 int get_character_type(charinfo *);
203 int get_height();
204 int get_slant();
205 vunits get_char_height(charinfo *);
206 vunits get_char_depth(charinfo *);
207 hunits get_char_skew(charinfo *);
208 hunits get_italic_correction(charinfo *);
209 hunits get_left_italic_correction(charinfo *);
210 hunits get_subscript_correction(charinfo *);
211 friend tfont *make_tfont(tfont_spec &);
214 inline int env_definite_font(environment *env)
216 return env->get_family()->make_definite(env->get_font());
219 /* font_info functions */
221 static font_info **font_table = 0;
222 static int font_table_size = 0;
224 font_info::font_info(symbol nm, int n, symbol enm, font *f)
225 : last_tfont(0), number(n), last_size(0),
226 internal_name(nm), external_name(enm), fm(f),
227 is_bold(0), is_constant_spaced(CONSTANT_SPACE_NONE), last_ligature_mode(1),
228 last_kern_mode(1), cond_bold_list(0), sf(0)
232 inline int font_info::contains(charinfo *ci)
234 return fm != 0 && fm->contains(ci->as_glyph());
237 inline int font_info::is_special()
239 return fm != 0 && fm->is_special();
242 inline int font_info::is_style()
244 return fm == 0;
247 void font_info::set_zoom(int zoom)
249 assert(fm != 0);
250 fm->set_zoom(zoom);
253 inline int font_info::get_zoom()
255 if (is_style())
256 return 0;
257 return fm->get_zoom();
260 tfont *make_tfont(tfont_spec &spec)
262 for (tfont *p = tfont::tfont_list; p; p = p->next)
263 if (*p == spec)
264 return p;
265 return new tfont(spec);
268 int env_get_zoom(environment *env)
270 int fontno = env->get_family()->make_definite(env->get_font());
271 return font_table[fontno]->get_zoom();
274 // this is the current_font, fontno is where we found the character,
275 // presumably a special font
277 tfont *font_info::get_tfont(font_size fs, int height, int slant, int fontno)
279 if (last_tfont == 0 || fs != last_size
280 || height != last_height || slant != last_slant
281 || global_ligature_mode != last_ligature_mode
282 || global_kern_mode != last_kern_mode
283 || fontno != number) {
284 font_info *f = font_table[fontno];
285 tfont_spec spec(f->external_name, f->number, f->fm, fs, height, slant);
286 for (conditional_bold *p = cond_bold_list; p; p = p->next)
287 if (p->fontno == fontno) {
288 spec.is_bold = 1;
289 spec.bold_offset = p->offset;
290 break;
292 if (!spec.is_bold && is_bold) {
293 spec.is_bold = 1;
294 spec.bold_offset = bold_offset;
296 spec.track_kern = track_kern.compute(fs.to_scaled_points());
297 spec.ligature_mode = global_ligature_mode;
298 spec.kern_mode = global_kern_mode;
299 switch (is_constant_spaced) {
300 case CONSTANT_SPACE_NONE:
301 break;
302 case CONSTANT_SPACE_ABSOLUTE:
303 spec.is_constant_spaced = 1;
304 spec.constant_space_width = constant_space;
305 break;
306 case CONSTANT_SPACE_RELATIVE:
307 spec.is_constant_spaced = 1;
308 spec.constant_space_width
309 = scale(constant_space*fs.to_scaled_points(),
310 units_per_inch,
311 36*72*sizescale);
312 break;
313 default:
314 assert(0);
316 if (fontno != number)
317 return make_tfont(spec);
318 // save font for comparison purposes
319 last_tfont = make_tfont(spec);
320 // save font related values not contained in tfont
321 last_size = fs;
322 last_height = height;
323 last_slant = slant;
324 last_ligature_mode = global_ligature_mode;
325 last_kern_mode = global_kern_mode;
327 return last_tfont;
330 int font_info::get_bold(hunits *res)
332 if (is_bold) {
333 *res = bold_offset;
334 return 1;
336 else
337 return 0;
340 void font_info::unbold()
342 if (is_bold) {
343 is_bold = 0;
344 flush();
348 void font_info::set_bold(hunits offset)
350 if (!is_bold || offset != bold_offset) {
351 is_bold = 1;
352 bold_offset = offset;
353 flush();
357 void font_info::set_conditional_bold(int fontno, hunits offset)
359 for (conditional_bold *p = cond_bold_list; p; p = p->next)
360 if (p->fontno == fontno) {
361 if (offset != p->offset) {
362 p->offset = offset;
363 flush();
365 return;
367 cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
370 conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
371 : next(x), fontno(f), offset(h)
375 void font_info::conditional_unbold(int fontno)
377 for (conditional_bold **p = &cond_bold_list; *p; p = &(*p)->next)
378 if ((*p)->fontno == fontno) {
379 conditional_bold *tem = *p;
380 *p = (*p)->next;
381 delete tem;
382 flush();
383 return;
387 void font_info::set_constant_space(constant_space_type type, units x)
389 if (type != is_constant_spaced
390 || (type != CONSTANT_SPACE_NONE && x != constant_space)) {
391 flush();
392 is_constant_spaced = type;
393 constant_space = x;
397 void font_info::set_track_kern(track_kerning_function &tk)
399 if (track_kern != tk) {
400 track_kern = tk;
401 flush();
405 void font_info::flush()
407 last_tfont = 0;
410 int font_info::is_named(symbol s)
412 return internal_name == s;
415 symbol font_info::get_name()
417 return internal_name;
420 symbol get_font_name(int fontno, environment *env)
422 symbol f = font_table[fontno]->get_name();
423 if (font_table[fontno]->is_style()) {
424 return concat(env->get_family()->nm, f);
426 return f;
429 symbol get_style_name(int fontno)
431 if (font_table[fontno]->is_style())
432 return font_table[fontno]->get_name();
433 else
434 return EMPTY_SYMBOL;
437 hunits font_info::get_space_width(font_size fs, int space_sz)
439 if (is_constant_spaced == CONSTANT_SPACE_NONE)
440 return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
441 space_sz, 12);
442 else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
443 return constant_space;
444 else
445 return scale(constant_space*fs.to_scaled_points(),
446 units_per_inch, 36*72*sizescale);
449 hunits font_info::get_narrow_space_width(font_size fs)
451 charinfo *ci = get_charinfo(symbol("|"));
452 if (fm->contains(ci->as_glyph()))
453 return hunits(fm->get_width(ci->as_glyph(), fs.to_scaled_points()));
454 else
455 return hunits(fs.to_units()/6);
458 hunits font_info::get_half_narrow_space_width(font_size fs)
460 charinfo *ci = get_charinfo(symbol("^"));
461 if (fm->contains(ci->as_glyph()))
462 return hunits(fm->get_width(ci->as_glyph(), fs.to_scaled_points()));
463 else
464 return hunits(fs.to_units()/12);
467 /* tfont */
469 tfont_spec::tfont_spec(symbol nm, int n, font *f,
470 font_size s, int h, int sl)
471 : name(nm), input_position(n), fm(f), size(s),
472 is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1),
473 height(h), slant(sl)
475 if (height == size.to_scaled_points())
476 height = 0;
479 int tfont_spec::operator==(const tfont_spec &spec)
481 if (fm == spec.fm
482 && size == spec.size
483 && input_position == spec.input_position
484 && name == spec.name
485 && height == spec.height
486 && slant == spec.slant
487 && (is_bold
488 ? (spec.is_bold && bold_offset == spec.bold_offset)
489 : !spec.is_bold)
490 && track_kern == spec.track_kern
491 && (is_constant_spaced
492 ? (spec.is_constant_spaced
493 && constant_space_width == spec.constant_space_width)
494 : !spec.is_constant_spaced)
495 && ligature_mode == spec.ligature_mode
496 && kern_mode == spec.kern_mode)
497 return 1;
498 else
499 return 0;
502 tfont_spec tfont_spec::plain()
504 return tfont_spec(name, input_position, fm, size, height, slant);
507 hunits tfont::get_width(charinfo *c)
509 if (is_constant_spaced)
510 return constant_space_width;
511 else if (is_bold)
512 return (hunits(fm->get_width(c->as_glyph(), size.to_scaled_points()))
513 + track_kern + bold_offset);
514 else
515 return (hunits(fm->get_width(c->as_glyph(), size.to_scaled_points()))
516 + track_kern);
519 vunits tfont::get_char_height(charinfo *c)
521 vunits v = fm->get_height(c->as_glyph(), size.to_scaled_points());
522 if (height != 0 && height != size.to_scaled_points())
523 return scale(v, height, size.to_scaled_points());
524 else
525 return v;
528 vunits tfont::get_char_depth(charinfo *c)
530 vunits v = fm->get_depth(c->as_glyph(), size.to_scaled_points());
531 if (height != 0 && height != size.to_scaled_points())
532 return scale(v, height, size.to_scaled_points());
533 else
534 return v;
537 hunits tfont::get_char_skew(charinfo *c)
539 return hunits(fm->get_skew(c->as_glyph(), size.to_scaled_points(), slant));
542 hunits tfont::get_italic_correction(charinfo *c)
544 return hunits(fm->get_italic_correction(c->as_glyph(), size.to_scaled_points()));
547 hunits tfont::get_left_italic_correction(charinfo *c)
549 return hunits(fm->get_left_italic_correction(c->as_glyph(),
550 size.to_scaled_points()));
553 hunits tfont::get_subscript_correction(charinfo *c)
555 return hunits(fm->get_subscript_correction(c->as_glyph(),
556 size.to_scaled_points()));
559 inline int tfont::get_input_position()
561 return input_position;
564 inline int tfont::contains(charinfo *ci)
566 return fm->contains(ci->as_glyph());
569 inline int tfont::get_character_type(charinfo *ci)
571 return fm->get_character_type(ci->as_glyph());
574 inline int tfont::get_bold(hunits *res)
576 if (is_bold) {
577 *res = bold_offset;
578 return 1;
580 else
581 return 0;
584 inline int tfont::get_constant_space(hunits *res)
586 if (is_constant_spaced) {
587 *res = constant_space_width;
588 return 1;
590 else
591 return 0;
594 inline hunits tfont::get_track_kern()
596 return track_kern;
599 inline tfont *tfont::get_plain()
601 return plain_version;
604 inline font_size tfont::get_size()
606 return size;
609 inline int tfont::get_zoom()
611 return fm->get_zoom();
614 inline symbol tfont::get_name()
616 return name;
619 inline int tfont::get_height()
621 return height;
624 inline int tfont::get_slant()
626 return slant;
629 symbol SYMBOL_ff("ff");
630 symbol SYMBOL_fi("fi");
631 symbol SYMBOL_fl("fl");
632 symbol SYMBOL_Fi("Fi");
633 symbol SYMBOL_Fl("Fl");
635 charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
637 if (ligature_mode == 0)
638 return 0;
639 charinfo *ci = 0;
640 if (c1->get_ascii_code() == 'f') {
641 switch (c2->get_ascii_code()) {
642 case 'f':
643 if (fm->has_ligature(font::LIG_ff))
644 ci = get_charinfo(SYMBOL_ff);
645 break;
646 case 'i':
647 if (fm->has_ligature(font::LIG_fi))
648 ci = get_charinfo(SYMBOL_fi);
649 break;
650 case 'l':
651 if (fm->has_ligature(font::LIG_fl))
652 ci = get_charinfo(SYMBOL_fl);
653 break;
656 else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
657 switch (c2->get_ascii_code()) {
658 case 'i':
659 if (fm->has_ligature(font::LIG_ffi))
660 ci = get_charinfo(SYMBOL_Fi);
661 break;
662 case 'l':
663 if (fm->has_ligature(font::LIG_ffl))
664 ci = get_charinfo(SYMBOL_Fl);
665 break;
668 if (ci != 0 && fm->contains(ci->as_glyph()))
669 return ci;
670 return 0;
673 inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res)
675 if (kern_mode == 0)
676 return 0;
677 else {
678 int n = fm->get_kern(c1->as_glyph(),
679 c2->as_glyph(),
680 size.to_scaled_points());
681 if (n) {
682 *res = hunits(n);
683 return 1;
685 else
686 return 0;
690 tfont *tfont::tfont_list = 0;
692 tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
694 next = tfont_list;
695 tfont_list = this;
696 tfont_spec plain_spec = plain();
697 tfont *p;
698 for (p = tfont_list; p; p = p->next)
699 if (*p == plain_spec) {
700 plain_version = p;
701 break;
703 if (!p)
704 plain_version = new tfont(plain_spec);
707 /* output_file */
709 class real_output_file
710 : public output_file
712 #ifndef POPEN_MISSING
713 int piped;
714 #endif
715 int printing; // decision via optional page list
716 int output_on; // \O[0] or \O[1] escape calls
718 virtual void really_transparent_char(unsigned char) = 0;
719 virtual void really_print_line(hunits x, vunits y, node *n,
720 vunits before, vunits after, hunits width) = 0;
721 virtual void really_begin_page(int pageno, vunits page_length) = 0;
722 virtual void really_copy_file(hunits x, vunits y, const char *filename);
723 virtual void really_put_filename(const char *, int);
724 virtual void really_on();
725 virtual void really_off();
727 public:
728 FILE *fp;
730 real_output_file();
731 ~real_output_file();
732 void flush();
733 void transparent_char(unsigned char);
734 void print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
735 void begin_page(int pageno, vunits page_length);
736 void put_filename(const char *, int);
737 void on();
738 void off();
739 int is_on();
740 int is_printing();
741 void copy_file(hunits x, vunits y, const char *filename);
744 class suppress_output_file
745 : public real_output_file
747 public:
748 suppress_output_file();
749 void really_transparent_char(unsigned char);
750 void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
751 void really_begin_page(int pageno, vunits page_length);
754 class ascii_output_file
755 : public real_output_file
757 public:
758 ascii_output_file();
759 void really_transparent_char(unsigned char);
760 void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
761 void really_begin_page(int pageno, vunits page_length);
762 void outc(unsigned char c);
763 void outs(const char *s);
766 void ascii_output_file::outc(unsigned char c)
768 fputc(c, fp);
771 void ascii_output_file::outs(const char *s)
773 fputc('<', fp);
774 if (s)
775 fputs(s, fp);
776 fputc('>', fp);
779 struct hvpair;
781 class troff_output_file
782 : public real_output_file
784 friend void space_char_hmotion_node::tprint(troff_output_file *);
785 friend void unbreakable_space_node::tprint(troff_output_file *);
787 units hpos;
788 units vpos;
789 units output_vpos;
790 units output_hpos;
791 int force_motion;
792 int current_size;
793 int current_slant;
794 int current_height;
795 tfont *current_tfont;
796 color *current_fill_color;
797 color *current_glyph_color;
798 int current_font_number;
799 symbol *font_position;
800 int nfont_positions;
801 enum { TBUF_SIZE = 256 };
802 char tbuf[TBUF_SIZE];
803 int tbuf_len;
804 int tbuf_kern;
805 int begun_page;
806 int cur_div_level;
807 string tag_list;
809 void do_motion();
810 void put(char c);
811 void put(unsigned char c);
812 void put(int i);
813 void put(unsigned int i);
814 void put(const char *s);
815 void set_font(tfont *tf);
816 void flush_tbuf();
818 public:
819 troff_output_file();
820 ~troff_output_file();
821 void trailer(vunits page_length);
822 void put_char(charinfo *, tfont *, color *, color *);
823 void put_char_width(charinfo *, tfont *, color *, color *, hunits, hunits);
824 void right(hunits);
825 void down(vunits);
826 void moveto(hunits, vunits);
827 void start_special(tfont *, color *, color *, int = 0);
828 void start_special();
829 void special_char(unsigned char c);
830 void end_special();
831 void word_marker();
832 void really_transparent_char(unsigned char c);
833 void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
834 void really_begin_page(int pageno, vunits page_length);
835 void really_copy_file(hunits x, vunits y, const char *filename);
836 void really_put_filename(const char *, int);
837 void really_on();
838 void really_off();
839 void draw(char, hvpair *, int, font_size, color *, color *);
840 void determine_line_limits (char code, hvpair *point, int npoints);
841 void check_charinfo(tfont *tf, charinfo *ci);
842 void glyph_color(color *c);
843 void fill_color(color *c);
844 int get_hpos() { return hpos; }
845 int get_vpos() { return vpos; }
846 void add_to_tag_list(string s);
849 static void put_string(const char *s, FILE *fp)
851 for (; *s != '\0'; ++s)
852 putc(*s, fp);
855 inline void troff_output_file::put(char c)
857 putc(c, fp);
860 inline void troff_output_file::put(unsigned char c)
862 putc(c, fp);
865 inline void troff_output_file::put(const char *s)
867 put_string(s, fp);
870 inline void troff_output_file::put(int i)
872 put_string(i_to_a(i), fp);
875 inline void troff_output_file::put(unsigned int i)
877 put_string(ui_to_a(i), fp);
880 void troff_output_file::start_special(tfont *tf, color *gcol, color *fcol,
881 int no_init_string)
883 set_font(tf);
884 glyph_color(gcol);
885 fill_color(fcol);
886 flush_tbuf();
887 do_motion();
888 if (!no_init_string)
889 put("x X ");
892 void troff_output_file::start_special()
894 flush_tbuf();
895 do_motion();
896 put("x X ");
899 void troff_output_file::special_char(unsigned char c)
901 put(c);
902 if (c == '\n')
903 put('+');
906 void troff_output_file::end_special()
908 put('\n');
911 inline void troff_output_file::moveto(hunits h, vunits v)
913 hpos = h.to_units();
914 vpos = v.to_units();
917 void troff_output_file::really_print_line(hunits x, vunits y, node *n,
918 vunits before, vunits after, hunits)
920 moveto(x, y);
921 while (n != 0) {
922 // Check whether we should push the current troff state and use
923 // the state at the start of the invocation of this diversion.
924 if (n->div_nest_level > cur_div_level && n->push_state) {
925 state.push_state(n->push_state);
926 cur_div_level = n->div_nest_level;
928 // Has the current diversion level decreased? Then we must pop the
929 // troff state.
930 while (n->div_nest_level < cur_div_level) {
931 state.pop_state();
932 cur_div_level = n->div_nest_level;
934 // Now check whether the state has changed.
935 if ((is_on() || n->force_tprint())
936 && (state.changed(n->state) || n->is_tag() || n->is_special)) {
937 flush_tbuf();
938 do_motion();
939 force_motion = 1;
940 flush();
941 state.flush(fp, n->state, tag_list);
942 tag_list = string("");
943 flush();
945 n->tprint(this);
946 n = n->next;
948 flush_tbuf();
949 // This ensures that transparent throughput will have a more predictable
950 // position.
951 do_motion();
952 force_motion = 1;
953 hpos = 0;
954 put('n');
955 put(before.to_units());
956 put(' ');
957 put(after.to_units());
958 put('\n');
961 inline void troff_output_file::word_marker()
963 flush_tbuf();
964 if (is_on())
965 put('w');
968 inline void troff_output_file::right(hunits n)
970 hpos += n.to_units();
973 inline void troff_output_file::down(vunits n)
975 vpos += n.to_units();
978 void troff_output_file::do_motion()
980 if (force_motion) {
981 put('V');
982 put(vpos);
983 put('\n');
984 put('H');
985 put(hpos);
986 put('\n');
988 else {
989 if (hpos != output_hpos) {
990 units n = hpos - output_hpos;
991 if (n > 0 && n < hpos) {
992 put('h');
993 put(n);
995 else {
996 put('H');
997 put(hpos);
999 put('\n');
1001 if (vpos != output_vpos) {
1002 units n = vpos - output_vpos;
1003 if (n > 0 && n < vpos) {
1004 put('v');
1005 put(n);
1007 else {
1008 put('V');
1009 put(vpos);
1011 put('\n');
1014 output_vpos = vpos;
1015 output_hpos = hpos;
1016 force_motion = 0;
1019 void troff_output_file::flush_tbuf()
1021 if (!is_on()) {
1022 tbuf_len = 0;
1023 return;
1026 if (tbuf_len == 0)
1027 return;
1028 if (tbuf_kern == 0)
1029 put('t');
1030 else {
1031 put('u');
1032 put(tbuf_kern);
1033 put(' ');
1035 check_output_limits(hpos, vpos);
1036 check_output_limits(hpos, vpos - current_size);
1038 for (int i = 0; i < tbuf_len; i++)
1039 put(tbuf[i]);
1040 put('\n');
1041 tbuf_len = 0;
1044 void troff_output_file::check_charinfo(tfont *tf, charinfo *ci)
1046 if (!is_on())
1047 return;
1049 int height = tf->get_char_height(ci).to_units();
1050 int width = tf->get_width(ci).to_units()
1051 + tf->get_italic_correction(ci).to_units();
1052 int depth = tf->get_char_depth(ci).to_units();
1053 check_output_limits(output_hpos, output_vpos - height);
1054 check_output_limits(output_hpos + width, output_vpos + depth);
1057 void troff_output_file::put_char_width(charinfo *ci, tfont *tf,
1058 color *gcol, color *fcol,
1059 hunits w, hunits k)
1061 int kk = k.to_units();
1062 if (!is_on()) {
1063 flush_tbuf();
1064 hpos += w.to_units() + kk;
1065 return;
1067 set_font(tf);
1068 unsigned char c = ci->get_ascii_code();
1069 if (c == '\0') {
1070 glyph_color(gcol);
1071 fill_color(fcol);
1072 flush_tbuf();
1073 do_motion();
1074 check_charinfo(tf, ci);
1075 if (ci->numbered()) {
1076 put('N');
1077 put(ci->get_number());
1079 else {
1080 put('C');
1081 const char *s = ci->nm.contents();
1082 if (s[1] == 0) {
1083 put('\\');
1084 put(s[0]);
1086 else
1087 put(s);
1089 put('\n');
1090 hpos += w.to_units() + kk;
1092 else if (tcommand_flag) {
1093 if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
1094 && (!gcol || gcol == current_glyph_color)
1095 && (!fcol || fcol == current_fill_color)
1096 && kk == tbuf_kern
1097 && tbuf_len < TBUF_SIZE) {
1098 check_charinfo(tf, ci);
1099 tbuf[tbuf_len++] = c;
1100 output_hpos += w.to_units() + kk;
1101 hpos = output_hpos;
1102 return;
1104 glyph_color(gcol);
1105 fill_color(fcol);
1106 flush_tbuf();
1107 do_motion();
1108 check_charinfo(tf, ci);
1109 tbuf[tbuf_len++] = c;
1110 output_hpos += w.to_units() + kk;
1111 tbuf_kern = kk;
1112 hpos = output_hpos;
1114 else {
1115 // flush_tbuf();
1116 int n = hpos - output_hpos;
1117 check_charinfo(tf, ci);
1118 // check_output_limits(output_hpos, output_vpos);
1119 if (vpos == output_vpos
1120 && (!gcol || gcol == current_glyph_color)
1121 && (!fcol || fcol == current_fill_color)
1122 && n > 0 && n < 100 && !force_motion) {
1123 put(char(n/10 + '0'));
1124 put(char(n%10 + '0'));
1125 put(c);
1126 output_hpos = hpos;
1128 else {
1129 glyph_color(gcol);
1130 fill_color(fcol);
1131 do_motion();
1132 put('c');
1133 put(c);
1135 hpos += w.to_units() + kk;
1139 void troff_output_file::put_char(charinfo *ci, tfont *tf,
1140 color *gcol, color *fcol)
1142 flush_tbuf();
1143 if (!is_on())
1144 return;
1145 set_font(tf);
1146 unsigned char c = ci->get_ascii_code();
1147 if (c == '\0') {
1148 glyph_color(gcol);
1149 fill_color(fcol);
1150 flush_tbuf();
1151 do_motion();
1152 if (ci->numbered()) {
1153 put('N');
1154 put(ci->get_number());
1156 else {
1157 put('C');
1158 const char *s = ci->nm.contents();
1159 if (s[1] == 0) {
1160 put('\\');
1161 put(s[0]);
1163 else
1164 put(s);
1166 put('\n');
1168 else {
1169 int n = hpos - output_hpos;
1170 if (vpos == output_vpos
1171 && (!gcol || gcol == current_glyph_color)
1172 && (!fcol || fcol == current_fill_color)
1173 && n > 0 && n < 100) {
1174 put(char(n/10 + '0'));
1175 put(char(n%10 + '0'));
1176 put(c);
1177 output_hpos = hpos;
1179 else {
1180 glyph_color(gcol);
1181 fill_color(fcol);
1182 flush_tbuf();
1183 do_motion();
1184 put('c');
1185 put(c);
1190 // set_font calls `flush_tbuf' if necessary.
1192 void troff_output_file::set_font(tfont *tf)
1194 if (current_tfont == tf)
1195 return;
1196 flush_tbuf();
1197 int n = tf->get_input_position();
1198 symbol nm = tf->get_name();
1199 if (n >= nfont_positions || font_position[n] != nm) {
1200 put("x font ");
1201 put(n);
1202 put(' ');
1203 put(nm.contents());
1204 put('\n');
1205 if (n >= nfont_positions) {
1206 int old_nfont_positions = nfont_positions;
1207 symbol *old_font_position = font_position;
1208 nfont_positions *= 3;
1209 nfont_positions /= 2;
1210 if (nfont_positions <= n)
1211 nfont_positions = n + 10;
1212 font_position = new symbol[nfont_positions];
1213 memcpy(font_position, old_font_position,
1214 old_nfont_positions*sizeof(symbol));
1215 a_delete old_font_position;
1217 font_position[n] = nm;
1219 if (current_font_number != n) {
1220 put('f');
1221 put(n);
1222 put('\n');
1223 current_font_number = n;
1225 int zoom = tf->get_zoom();
1226 int size;
1227 if (zoom)
1228 size = scale(tf->get_size().to_scaled_points(),
1229 zoom, 1000);
1230 else
1231 size = tf->get_size().to_scaled_points();
1232 if (current_size != size) {
1233 put('s');
1234 put(size);
1235 put('\n');
1236 current_size = size;
1238 int slant = tf->get_slant();
1239 if (current_slant != slant) {
1240 put("x Slant ");
1241 put(slant);
1242 put('\n');
1243 current_slant = slant;
1245 int height = tf->get_height();
1246 if (current_height != height) {
1247 put("x Height ");
1248 put(height == 0 ? current_size : height);
1249 put('\n');
1250 current_height = height;
1252 current_tfont = tf;
1255 // fill_color calls `flush_tbuf' and `do_motion' if necessary.
1257 void troff_output_file::fill_color(color *col)
1259 if (!col || current_fill_color == col)
1260 return;
1261 current_fill_color = col;
1262 if (!color_flag)
1263 return;
1264 flush_tbuf();
1265 do_motion();
1266 put("DF");
1267 unsigned int components[4];
1268 color_scheme cs;
1269 cs = col->get_components(components);
1270 switch (cs) {
1271 case DEFAULT:
1272 put('d');
1273 break;
1274 case RGB:
1275 put("r ");
1276 put(Red);
1277 put(' ');
1278 put(Green);
1279 put(' ');
1280 put(Blue);
1281 break;
1282 case CMY:
1283 put("c ");
1284 put(Cyan);
1285 put(' ');
1286 put(Magenta);
1287 put(' ');
1288 put(Yellow);
1289 break;
1290 case CMYK:
1291 put("k ");
1292 put(Cyan);
1293 put(' ');
1294 put(Magenta);
1295 put(' ');
1296 put(Yellow);
1297 put(' ');
1298 put(Black);
1299 break;
1300 case GRAY:
1301 put("g ");
1302 put(Gray);
1303 break;
1305 put('\n');
1308 // glyph_color calls `flush_tbuf' and `do_motion' if necessary.
1310 void troff_output_file::glyph_color(color *col)
1312 if (!col || current_glyph_color == col)
1313 return;
1314 current_glyph_color = col;
1315 if (!color_flag)
1316 return;
1317 flush_tbuf();
1318 // grotty doesn't like a color command if the vertical position is zero.
1319 do_motion();
1320 put("m");
1321 unsigned int components[4];
1322 color_scheme cs;
1323 cs = col->get_components(components);
1324 switch (cs) {
1325 case DEFAULT:
1326 put('d');
1327 break;
1328 case RGB:
1329 put("r ");
1330 put(Red);
1331 put(' ');
1332 put(Green);
1333 put(' ');
1334 put(Blue);
1335 break;
1336 case CMY:
1337 put("c ");
1338 put(Cyan);
1339 put(' ');
1340 put(Magenta);
1341 put(' ');
1342 put(Yellow);
1343 break;
1344 case CMYK:
1345 put("k ");
1346 put(Cyan);
1347 put(' ');
1348 put(Magenta);
1349 put(' ');
1350 put(Yellow);
1351 put(' ');
1352 put(Black);
1353 break;
1354 case GRAY:
1355 put("g ");
1356 put(Gray);
1357 break;
1359 put('\n');
1362 void troff_output_file::add_to_tag_list(string s)
1364 if (tag_list == string(""))
1365 tag_list = s;
1366 else {
1367 tag_list += string("\n");
1368 tag_list += s;
1372 // determine_line_limits - works out the smallest box which will contain
1373 // the entity, code, built from the point array.
1374 void troff_output_file::determine_line_limits(char code, hvpair *point,
1375 int npoints)
1377 int i, x, y;
1379 if (!is_on())
1380 return;
1382 switch (code) {
1383 case 'c':
1384 case 'C':
1385 // only the h field is used when defining a circle
1386 check_output_limits(output_hpos,
1387 output_vpos - point[0].h.to_units()/2);
1388 check_output_limits(output_hpos + point[0].h.to_units(),
1389 output_vpos + point[0].h.to_units()/2);
1390 break;
1391 case 'E':
1392 case 'e':
1393 check_output_limits(output_hpos,
1394 output_vpos - point[0].v.to_units()/2);
1395 check_output_limits(output_hpos + point[0].h.to_units(),
1396 output_vpos + point[0].v.to_units()/2);
1397 break;
1398 case 'P':
1399 case 'p':
1400 x = output_hpos;
1401 y = output_vpos;
1402 check_output_limits(x, y);
1403 for (i = 0; i < npoints; i++) {
1404 x += point[i].h.to_units();
1405 y += point[i].v.to_units();
1406 check_output_limits(x, y);
1408 break;
1409 case 't':
1410 x = output_hpos;
1411 y = output_vpos;
1412 for (i = 0; i < npoints; i++) {
1413 x += point[i].h.to_units();
1414 y += point[i].v.to_units();
1415 check_output_limits(x, y);
1417 break;
1418 case 'a':
1419 double c[2];
1420 int p[4];
1421 int minx, miny, maxx, maxy;
1422 x = output_hpos;
1423 y = output_vpos;
1424 p[0] = point[0].h.to_units();
1425 p[1] = point[0].v.to_units();
1426 p[2] = point[1].h.to_units();
1427 p[3] = point[1].v.to_units();
1428 if (adjust_arc_center(p, c)) {
1429 check_output_arc_limits(x, y,
1430 p[0], p[1], p[2], p[3],
1431 c[0], c[1],
1432 &minx, &maxx, &miny, &maxy);
1433 check_output_limits(minx, miny);
1434 check_output_limits(maxx, maxy);
1435 break;
1437 // fall through
1438 case 'l':
1439 x = output_hpos;
1440 y = output_vpos;
1441 check_output_limits(x, y);
1442 for (i = 0; i < npoints; i++) {
1443 x += point[i].h.to_units();
1444 y += point[i].v.to_units();
1445 check_output_limits(x, y);
1447 break;
1448 default:
1449 x = output_hpos;
1450 y = output_vpos;
1451 for (i = 0; i < npoints; i++) {
1452 x += point[i].h.to_units();
1453 y += point[i].v.to_units();
1454 check_output_limits(x, y);
1459 void troff_output_file::draw(char code, hvpair *point, int npoints,
1460 font_size fsize, color *gcol, color *fcol)
1462 int i;
1463 glyph_color(gcol);
1464 fill_color(fcol);
1465 flush_tbuf();
1466 do_motion();
1467 if (is_on()) {
1468 int size = fsize.to_scaled_points();
1469 if (current_size != size) {
1470 put('s');
1471 put(size);
1472 put('\n');
1473 current_size = size;
1474 current_tfont = 0;
1476 put('D');
1477 put(code);
1478 if (code == 'c') {
1479 put(' ');
1480 put(point[0].h.to_units());
1482 else
1483 for (i = 0; i < npoints; i++) {
1484 put(' ');
1485 put(point[i].h.to_units());
1486 put(' ');
1487 put(point[i].v.to_units());
1489 determine_line_limits(code, point, npoints);
1492 for (i = 0; i < npoints; i++)
1493 output_hpos += point[i].h.to_units();
1494 hpos = output_hpos;
1495 if (code != 'e') {
1496 for (i = 0; i < npoints; i++)
1497 output_vpos += point[i].v.to_units();
1498 vpos = output_vpos;
1500 if (is_on())
1501 put('\n');
1504 void troff_output_file::really_on()
1506 flush_tbuf();
1507 force_motion = 1;
1508 do_motion();
1511 void troff_output_file::really_off()
1513 flush_tbuf();
1516 void troff_output_file::really_put_filename(const char *filename, int po)
1518 flush_tbuf();
1519 put("F ");
1520 if (po)
1521 put("<");
1522 put(filename);
1523 if (po)
1524 put(">");
1525 put('\n');
1528 void troff_output_file::really_begin_page(int pageno, vunits page_length)
1530 flush_tbuf();
1531 if (begun_page) {
1532 if (page_length > V0) {
1533 put('V');
1534 put(page_length.to_units());
1535 put('\n');
1538 else
1539 begun_page = 1;
1540 current_tfont = 0;
1541 current_font_number = -1;
1542 current_size = 0;
1543 // current_height = 0;
1544 // current_slant = 0;
1545 hpos = 0;
1546 vpos = 0;
1547 output_hpos = 0;
1548 output_vpos = 0;
1549 force_motion = 1;
1550 for (int i = 0; i < nfont_positions; i++)
1551 font_position[i] = NULL_SYMBOL;
1552 put('p');
1553 put(pageno);
1554 put('\n');
1557 void troff_output_file::really_copy_file(hunits x, vunits y,
1558 const char *filename)
1560 moveto(x, y);
1561 flush_tbuf();
1562 do_motion();
1564 file_case *fcp = include_search_path.open_file_cautious(filename,
1565 fcp->fc_const_path);
1566 if (fcp != NULL) {
1567 int c;
1568 while ((c = fcp->get_c()) != EOF)
1569 put(char(c));
1570 delete fcp;
1571 } else
1572 error("can't open `%1': %2", filename, strerror(errno));
1574 force_motion = 1;
1575 current_size = 0;
1576 current_tfont = 0;
1577 current_font_number = -1;
1578 for (int i = 0; i < nfont_positions; i++)
1579 font_position[i] = NULL_SYMBOL;
1582 void troff_output_file::really_transparent_char(unsigned char c)
1584 put(c);
1587 troff_output_file::~troff_output_file()
1589 a_delete font_position;
1592 void troff_output_file::trailer(vunits page_length)
1594 flush_tbuf();
1595 if (page_length > V0) {
1596 put("x trailer\n");
1597 put('V');
1598 put(page_length.to_units());
1599 put('\n');
1601 put("x stop\n");
1604 troff_output_file::troff_output_file()
1605 : current_slant(0), current_height(0), current_fill_color(0),
1606 current_glyph_color(0), nfont_positions(10), tbuf_len(0), begun_page(0),
1607 cur_div_level(0)
1609 font_position = new symbol[nfont_positions];
1610 put("x T ");
1611 put(device);
1612 put('\n');
1613 put("x res ");
1614 put(units_per_inch);
1615 put(' ');
1616 put(hresolution);
1617 put(' ');
1618 put(vresolution);
1619 put('\n');
1620 put("x init\n");
1623 /* output_file */
1625 output_file *the_output = 0;
1627 output_file::output_file()
1631 output_file::~output_file()
1635 void output_file::trailer(vunits)
1639 void output_file::put_filename(const char *, int)
1643 void output_file::on()
1647 void output_file::off()
1651 real_output_file::real_output_file()
1652 : printing(0), output_on(1)
1654 #ifndef POPEN_MISSING
1655 if (pipe_command) {
1656 if ((fp = popen(pipe_command, POPEN_WT)) != 0) {
1657 piped = 1;
1658 return;
1660 error("pipe open failed: %1", strerror(errno));
1662 piped = 0;
1663 #endif /* not POPEN_MISSING */
1664 fp = stdout;
1667 real_output_file::~real_output_file()
1669 if (!fp)
1670 return;
1671 // To avoid looping, set fp to 0 before calling fatal().
1672 if (ferror(fp) || fflush(fp) < 0) {
1673 fp = 0;
1674 fatal("error writing output file");
1676 #ifndef POPEN_MISSING
1677 if (piped) {
1678 int result = pclose(fp);
1679 fp = 0;
1680 if (result < 0)
1681 fatal("pclose failed");
1682 if (!WIFEXITED(result))
1683 error("output process `%1' got fatal signal %2",
1684 pipe_command,
1685 WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
1686 else {
1687 int exit_status = WEXITSTATUS(result);
1688 if (exit_status != 0)
1689 error("output process `%1' exited with status %2",
1690 pipe_command, exit_status);
1693 else
1694 #endif /* not POPEN MISSING */
1695 if (fclose(fp) < 0) {
1696 fp = 0;
1697 fatal("error closing output file");
1701 void real_output_file::flush()
1703 if (fflush(fp) < 0)
1704 fatal("error writing output file");
1707 int real_output_file::is_printing()
1709 return printing;
1712 void real_output_file::begin_page(int pageno, vunits page_length)
1714 printing = in_output_page_list(pageno);
1715 if (printing)
1716 really_begin_page(pageno, page_length);
1719 void real_output_file::copy_file(hunits x, vunits y, const char *filename)
1721 if (printing && output_on)
1722 really_copy_file(x, y, filename);
1723 check_output_limits(x.to_units(), y.to_units());
1726 void real_output_file::transparent_char(unsigned char c)
1728 if (printing && output_on)
1729 really_transparent_char(c);
1732 void real_output_file::print_line(hunits x, vunits y, node *n,
1733 vunits before, vunits after, hunits width)
1735 if (printing)
1736 really_print_line(x, y, n, before, after, width);
1737 delete_node_list(n);
1740 void real_output_file::really_copy_file(hunits, vunits, const char *)
1742 // do nothing
1745 void real_output_file::put_filename(const char *filename, int po)
1747 really_put_filename(filename, po);
1750 void real_output_file::really_put_filename(const char *, int)
1754 void real_output_file::on()
1756 really_on();
1757 if (output_on == 0)
1758 output_on = 1;
1761 void real_output_file::off()
1763 really_off();
1764 output_on = 0;
1767 int real_output_file::is_on()
1769 return output_on;
1772 void real_output_file::really_on()
1776 void real_output_file::really_off()
1780 /* ascii_output_file */
1782 void ascii_output_file::really_transparent_char(unsigned char c)
1784 putc(c, fp);
1787 void ascii_output_file::really_print_line(hunits, vunits, node *n,
1788 vunits, vunits, hunits)
1790 while (n != 0) {
1791 n->ascii_print(this);
1792 n = n->next;
1794 fputc('\n', fp);
1797 void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/)
1799 fputs("<beginning of page>\n", fp);
1802 ascii_output_file::ascii_output_file()
1806 /* suppress_output_file */
1808 suppress_output_file::suppress_output_file()
1812 void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits, hunits)
1816 void suppress_output_file::really_begin_page(int, vunits)
1820 void suppress_output_file::really_transparent_char(unsigned char)
1824 /* glyphs, ligatures, kerns, discretionary breaks */
1826 class charinfo_node
1827 : public node
1829 protected:
1830 charinfo *ci;
1832 public:
1833 charinfo_node(charinfo *, statem *, int, node * = 0);
1834 int ends_sentence();
1835 int overlaps_vertically();
1836 int overlaps_horizontally();
1839 charinfo_node::charinfo_node(charinfo *c, statem *s, int pop, node *x)
1840 : node(x, s, pop), ci(c)
1844 int charinfo_node::ends_sentence()
1846 if (ci->ends_sentence())
1847 return 1;
1848 else if (ci->transparent())
1849 return 2;
1850 else
1851 return 0;
1854 int charinfo_node::overlaps_horizontally()
1856 return ci->overlaps_horizontally();
1859 int charinfo_node::overlaps_vertically()
1861 return ci->overlaps_vertically();
1864 class glyph_node
1865 : public charinfo_node
1867 static glyph_node *free_list;
1869 protected:
1870 tfont *tf;
1871 color *gcol;
1872 color *fcol; /* this is needed for grotty */
1873 #ifdef STORE_WIDTH
1874 hunits wid;
1876 glyph_node(charinfo *, tfont *, color *, color *, hunits,
1877 statem *, int, node * = 0);
1878 #endif
1880 public:
1881 void *operator new(size_t);
1882 void operator delete(void *);
1883 glyph_node(charinfo *, tfont *, color *, color *,
1884 statem *, int, node * = 0);
1885 ~glyph_node() {}
1886 node *copy();
1887 node *merge_glyph_node(glyph_node *);
1888 node *merge_self(node *);
1889 hunits width();
1890 node *last_char_node();
1891 units size();
1892 void vertical_extent(vunits *, vunits *);
1893 hunits subscript_correction();
1894 hunits italic_correction();
1895 hunits left_italic_correction();
1896 hunits skew();
1897 hyphenation_type get_hyphenation_type();
1898 tfont *get_tfont();
1899 color *get_glyph_color();
1900 color *get_fill_color();
1901 void tprint(troff_output_file *);
1902 void zero_width_tprint(troff_output_file *);
1903 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1904 node *add_self(node *, hyphen_list **);
1905 void ascii_print(ascii_output_file *);
1906 void asciify(macro *);
1907 int character_type();
1908 int same(node *);
1909 const char *type();
1910 int force_tprint();
1911 int is_tag();
1912 void debug_node();
1915 glyph_node *glyph_node::free_list = 0;
1917 class ligature_node
1918 : public glyph_node
1920 node *n1;
1921 node *n2;
1923 #ifdef STORE_WIDTH
1924 ligature_node(charinfo *, tfont *, color *, color *, hunits,
1925 node *, node *, statem *, int, node * = 0);
1926 #endif
1928 public:
1929 void *operator new(size_t);
1930 void operator delete(void *);
1931 ligature_node(charinfo *, tfont *, color *, color *,
1932 node *, node *, statem *, int, node * = 0);
1933 ~ligature_node();
1934 node *copy();
1935 node *add_self(node *, hyphen_list **);
1936 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1937 void ascii_print(ascii_output_file *);
1938 void asciify(macro *);
1939 int same(node *);
1940 const char *type();
1941 int force_tprint();
1942 int is_tag();
1945 class kern_pair_node
1946 : public node
1948 hunits amount;
1949 node *n1;
1950 node *n2;
1952 public:
1953 kern_pair_node(hunits, node *, node *, statem *, int, node * = 0);
1954 ~kern_pair_node();
1955 node *copy();
1956 node *merge_glyph_node(glyph_node *);
1957 node *add_self(node *, hyphen_list **);
1958 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1959 node *add_discretionary_hyphen();
1960 hunits width();
1961 node *last_char_node();
1962 hunits italic_correction();
1963 hunits subscript_correction();
1964 void tprint(troff_output_file *);
1965 hyphenation_type get_hyphenation_type();
1966 int ends_sentence();
1967 void ascii_print(ascii_output_file *);
1968 void asciify(macro *);
1969 int same(node *);
1970 const char *type();
1971 int force_tprint();
1972 int is_tag();
1973 void vertical_extent(vunits *, vunits *);
1976 class dbreak_node
1977 : public node
1979 node *none;
1980 node *pre;
1981 node *post;
1983 public:
1984 dbreak_node(node *, node *, statem *, int, node * = 0);
1985 ~dbreak_node();
1986 node *copy();
1987 node *merge_glyph_node(glyph_node *);
1988 node *add_discretionary_hyphen();
1989 hunits width();
1990 node *last_char_node();
1991 hunits italic_correction();
1992 hunits subscript_correction();
1993 void tprint(troff_output_file *);
1994 breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
1995 int is_inner = 0);
1996 int nbreaks();
1997 int ends_sentence();
1998 void split(int, node **, node **);
1999 hyphenation_type get_hyphenation_type();
2000 void ascii_print(ascii_output_file *);
2001 void asciify(macro *);
2002 int same(node *);
2003 const char *type();
2004 int force_tprint();
2005 int is_tag();
2008 void *glyph_node::operator new(size_t n) // TODO -> ObjectCache!
2010 assert(n == sizeof(glyph_node));
2011 if (!free_list) {
2012 const int BLOCK = 1024;
2013 free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
2014 for (int i = 0; i < BLOCK - 1; i++)
2015 free_list[i].next = free_list + i + 1;
2016 free_list[BLOCK-1].next = 0;
2018 glyph_node *p = free_list;
2019 free_list = (glyph_node *)(free_list->next);
2020 p->next = 0;
2021 return p;
2024 void *ligature_node::operator new(size_t n) // TODO ?
2026 return new char[n];
2029 void glyph_node::operator delete(void *p) // TODO -> ObjectCache!
2031 if (p) {
2032 ((glyph_node *)p)->next = free_list;
2033 free_list = (glyph_node *)p;
2037 void ligature_node::operator delete(void *p) // TODO ?
2039 delete[] (char *)p;
2042 glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc,
2043 statem *s, int pop, node *x)
2044 : charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc)
2046 #ifdef STORE_WIDTH
2047 wid = tf->get_width(ci);
2048 #endif
2051 #ifdef STORE_WIDTH
2052 glyph_node::glyph_node(charinfo *c, tfont *t,
2053 color *gc, color *fc, hunits w,
2054 statem *s, int pop, node *x)
2055 : charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc), wid(w)
2058 #endif
2060 node *glyph_node::copy()
2062 #ifdef STORE_WIDTH
2063 return new glyph_node(ci, tf, gcol, fcol, wid, state, div_nest_level);
2064 #else
2065 return new glyph_node(ci, tf, gcol, fcol, state, div_nest_level);
2066 #endif
2069 node *glyph_node::merge_self(node *nd)
2071 return nd->merge_glyph_node(this);
2074 int glyph_node::character_type()
2076 return tf->get_character_type(ci);
2079 node *glyph_node::add_self(node *n, hyphen_list **p)
2081 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
2082 next = 0;
2083 node *nn;
2084 if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
2085 next = n;
2086 nn = this;
2088 if ((*p)->hyphen)
2089 nn = nn->add_discretionary_hyphen();
2090 hyphen_list *pp = *p;
2091 *p = (*p)->next;
2092 delete pp;
2093 return nn;
2096 units glyph_node::size()
2098 return tf->get_size().to_units();
2101 hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count)
2103 (*count)++;
2104 return new hyphen_list(ci->get_hyphenation_code(), tail);
2107 tfont *node::get_tfont()
2109 return 0;
2112 tfont *glyph_node::get_tfont()
2114 return tf;
2117 color *node::get_glyph_color()
2119 return 0;
2122 color *glyph_node::get_glyph_color()
2124 return gcol;
2127 color *node::get_fill_color()
2129 return 0;
2132 color *glyph_node::get_fill_color()
2134 return fcol;
2137 node *node::merge_glyph_node(glyph_node *)
2139 return 0;
2142 node *glyph_node::merge_glyph_node(glyph_node *gn)
2144 if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
2145 charinfo *lig;
2146 if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
2147 node *next1 = next;
2148 next = 0;
2149 return new ligature_node(lig, tf, gcol, fcol, this, gn, state,
2150 gn->div_nest_level, next1);
2152 hunits kern;
2153 if (tf->get_kern(ci, gn->ci, &kern)) {
2154 node *next1 = next;
2155 next = 0;
2156 return new kern_pair_node(kern, this, gn, state,
2157 gn->div_nest_level, next1);
2160 return 0;
2163 #ifdef STORE_WIDTH
2164 inline
2165 #endif
2166 hunits glyph_node::width()
2168 #ifdef STORE_WIDTH
2169 return wid;
2170 #else
2171 return tf->get_width(ci);
2172 #endif
2175 node *glyph_node::last_char_node()
2177 return this;
2180 void glyph_node::vertical_extent(vunits *min, vunits *max)
2182 *min = -tf->get_char_height(ci);
2183 *max = tf->get_char_depth(ci);
2186 hunits glyph_node::skew()
2188 return tf->get_char_skew(ci);
2191 hunits glyph_node::subscript_correction()
2193 return tf->get_subscript_correction(ci);
2196 hunits glyph_node::italic_correction()
2198 return tf->get_italic_correction(ci);
2201 hunits glyph_node::left_italic_correction()
2203 return tf->get_left_italic_correction(ci);
2206 hyphenation_type glyph_node::get_hyphenation_type()
2208 return HYPHEN_MIDDLE;
2211 void glyph_node::ascii_print(ascii_output_file *ascii)
2213 unsigned char c = ci->get_ascii_code();
2214 if (c != 0)
2215 ascii->outc(c);
2216 else
2217 ascii->outs(ci->nm.contents());
2220 void glyph_node::debug_node()
2222 unsigned char c = ci->get_ascii_code();
2223 fprintf(stderr, "{ %s [", type());
2224 if (c)
2225 fprintf(stderr, "%c", c);
2226 else
2227 fprintf(stderr, "%s", ci->nm.contents());
2228 if (push_state)
2229 fprintf(stderr, " <push_state>");
2230 if (state)
2231 state->display_state();
2232 fprintf(stderr, " nest level %d", div_nest_level);
2233 fprintf(stderr, "]}\n");
2234 fflush(stderr);
2237 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2238 node *gn1, node *gn2, statem *s,
2239 int pop, node *x)
2240 : glyph_node(c, t, gc, fc, s, pop, x), n1(gn1), n2(gn2)
2244 #ifdef STORE_WIDTH
2245 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2246 hunits w, node *gn1, node *gn2, statem *s,
2247 int pop, node *x)
2248 : glyph_node(c, t, gc, fc, w, s, pop, x), n1(gn1), n2(gn2)
2251 #endif
2253 ligature_node::~ligature_node()
2255 delete n1;
2256 delete n2;
2259 node *ligature_node::copy()
2261 #ifdef STORE_WIDTH
2262 return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy(),
2263 state, div_nest_level);
2264 #else
2265 return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy(),
2266 state, div_nest_level);
2267 #endif
2270 void ligature_node::ascii_print(ascii_output_file *ascii)
2272 n1->ascii_print(ascii);
2273 n2->ascii_print(ascii);
2276 hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail, int *count)
2278 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2279 return n1->get_hyphen_list(hl, count);
2282 node *ligature_node::add_self(node *n, hyphen_list **p)
2284 n = n1->add_self(n, p);
2285 n = n2->add_self(n, p);
2286 n1 = n2 = 0;
2287 delete this;
2288 return n;
2291 kern_pair_node::kern_pair_node(hunits n, node *first, node *second,
2292 statem* s, int pop, node *x)
2293 : node(x, s, pop), amount(n), n1(first), n2(second)
2297 dbreak_node::dbreak_node(node *n, node *p, statem *s, int pop, node *x)
2298 : node(x, s, pop), none(n), pre(p), post(0)
2302 node *dbreak_node::merge_glyph_node(glyph_node *gn)
2304 glyph_node *gn2 = (glyph_node *)gn->copy();
2305 node *new_none = none ? none->merge_glyph_node(gn) : 0;
2306 node *new_post = post ? post->merge_glyph_node(gn2) : 0;
2307 if (new_none == 0 && new_post == 0) {
2308 delete gn2;
2309 return 0;
2311 if (new_none != 0)
2312 none = new_none;
2313 else {
2314 gn->next = none;
2315 none = gn;
2317 if (new_post != 0)
2318 post = new_post;
2319 else {
2320 gn2->next = post;
2321 post = gn2;
2323 return this;
2326 node *kern_pair_node::merge_glyph_node(glyph_node *gn)
2328 node *nd = n2->merge_glyph_node(gn);
2329 if (nd == 0)
2330 return 0;
2331 n2 = nd;
2332 nd = n2->merge_self(n1);
2333 if (nd) {
2334 nd->next = next;
2335 n1 = 0;
2336 n2 = 0;
2337 delete this;
2338 return nd;
2340 return this;
2343 hunits kern_pair_node::italic_correction()
2345 return n2->italic_correction();
2348 hunits kern_pair_node::subscript_correction()
2350 return n2->subscript_correction();
2353 void kern_pair_node::vertical_extent(vunits *min, vunits *max)
2355 n1->vertical_extent(min, max);
2356 vunits min2, max2;
2357 n2->vertical_extent(&min2, &max2);
2358 if (min2 < *min)
2359 *min = min2;
2360 if (max2 > *max)
2361 *max = max2;
2364 node *kern_pair_node::add_discretionary_hyphen()
2366 tfont *tf = n2->get_tfont();
2367 if (tf) {
2368 if (tf->contains(soft_hyphen_char)) {
2369 color *gcol = n2->get_glyph_color();
2370 color *fcol = n2->get_fill_color();
2371 node *next1 = next;
2372 next = 0;
2373 node *n = copy();
2374 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2375 state, div_nest_level);
2376 node *nn = n->merge_glyph_node(gn);
2377 if (nn == 0) {
2378 gn->next = n;
2379 nn = gn;
2381 return new dbreak_node(this, nn, state, div_nest_level, next1);
2384 return this;
2387 kern_pair_node::~kern_pair_node()
2389 if (n1 != 0)
2390 delete n1;
2391 if (n2 != 0)
2392 delete n2;
2395 dbreak_node::~dbreak_node()
2397 delete_node_list(pre);
2398 delete_node_list(post);
2399 delete_node_list(none);
2402 node *kern_pair_node::copy()
2404 return new kern_pair_node(amount, n1->copy(), n2->copy(), state,
2405 div_nest_level);
2408 node *copy_node_list(node *n)
2410 node *p = 0;
2411 while (n != 0) {
2412 node *nn = n->copy();
2413 nn->next = p;
2414 p = nn;
2415 n = n->next;
2417 while (p != 0) {
2418 node *pp = p->next;
2419 p->next = n;
2420 n = p;
2421 p = pp;
2423 return n;
2426 void delete_node_list(node *n)
2428 while (n != 0) {
2429 node *tem = n;
2430 n = n->next;
2431 delete tem;
2435 node *dbreak_node::copy()
2437 dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre),
2438 state, div_nest_level);
2439 p->post = copy_node_list(post);
2440 return p;
2443 hyphen_list *node::get_hyphen_list(hyphen_list *tail, int *)
2445 return tail;
2448 hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail, int *count)
2450 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2451 return n1->get_hyphen_list(hl, count);
2454 class hyphen_inhibitor_node : public node {
2455 public:
2456 hyphen_inhibitor_node(node * = 0);
2457 node *copy();
2458 int same(node *);
2459 const char *type();
2460 int force_tprint();
2461 int is_tag();
2462 hyphenation_type get_hyphenation_type();
2465 hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
2469 node *hyphen_inhibitor_node::copy()
2471 return new hyphen_inhibitor_node;
2474 int hyphen_inhibitor_node::same(node *)
2476 return 1;
2479 const char *hyphen_inhibitor_node::type()
2481 return "hyphen_inhibitor_node";
2484 int hyphen_inhibitor_node::force_tprint()
2486 return 0;
2489 int hyphen_inhibitor_node::is_tag()
2491 return 0;
2494 hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
2496 return HYPHEN_INHIBIT;
2499 /* add_discretionary_hyphen methods */
2501 node *dbreak_node::add_discretionary_hyphen()
2503 if (post)
2504 post = post->add_discretionary_hyphen();
2505 if (none)
2506 none = none->add_discretionary_hyphen();
2507 return this;
2510 node *node::add_discretionary_hyphen()
2512 tfont *tf = get_tfont();
2513 if (!tf)
2514 return new hyphen_inhibitor_node(this);
2515 if (tf->contains(soft_hyphen_char)) {
2516 color *gcol = get_glyph_color();
2517 color *fcol = get_fill_color();
2518 node *next1 = next;
2519 next = 0;
2520 node *n = copy();
2521 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2522 state, div_nest_level);
2523 node *n1 = n->merge_glyph_node(gn);
2524 if (n1 == 0) {
2525 gn->next = n;
2526 n1 = gn;
2528 return new dbreak_node(this, n1, state, div_nest_level, next1);
2530 return this;
2533 node *node::merge_self(node *)
2535 return 0;
2538 node *node::add_self(node *n, hyphen_list ** /*p*/)
2540 next = n;
2541 return this;
2544 node *kern_pair_node::add_self(node *n, hyphen_list **p)
2546 n = n1->add_self(n, p);
2547 n = n2->add_self(n, p);
2548 n1 = n2 = 0;
2549 delete this;
2550 return n;
2553 hunits node::width()
2555 return H0;
2558 node *node::last_char_node()
2560 return 0;
2563 int node::force_tprint()
2565 return 0;
2568 int node::is_tag()
2570 return 0;
2573 hunits hmotion_node::width()
2575 return n;
2578 units node::size()
2580 return points_to_units(10);
2583 void node::debug_node()
2585 fprintf(stderr, "{ %s ", type());
2586 if (push_state)
2587 fprintf(stderr, " <push_state>");
2588 if (state)
2589 fprintf(stderr, " <state>");
2590 fprintf(stderr, " nest level %d", div_nest_level);
2591 fprintf(stderr, " }\n");
2592 fflush(stderr);
2595 void node::debug_node_list()
2597 node *n = next;
2599 debug_node();
2600 while (n != 0) {
2601 n->debug_node();
2602 n = n->next;
2606 hunits kern_pair_node::width()
2608 return n1->width() + n2->width() + amount;
2611 node *kern_pair_node::last_char_node()
2613 node *nd = n2->last_char_node();
2614 if (nd)
2615 return nd;
2616 return n1->last_char_node();
2619 hunits dbreak_node::width()
2621 hunits x = H0;
2622 for (node *n = none; n != 0; n = n->next)
2623 x += n->width();
2624 return x;
2627 node *dbreak_node::last_char_node()
2629 for (node *n = none; n; n = n->next) {
2630 node *last_node = n->last_char_node();
2631 if (last_node)
2632 return last_node;
2634 return 0;
2637 hunits dbreak_node::italic_correction()
2639 return none ? none->italic_correction() : H0;
2642 hunits dbreak_node::subscript_correction()
2644 return none ? none->subscript_correction() : H0;
2647 class italic_corrected_node
2648 : public node
2650 node *n;
2651 hunits x;
2653 public:
2654 italic_corrected_node(node *, hunits, statem *, int, node * = 0);
2655 ~italic_corrected_node();
2656 node *copy();
2657 void ascii_print(ascii_output_file *);
2658 void asciify(macro *);
2659 hunits width();
2660 node *last_char_node();
2661 void vertical_extent(vunits *, vunits *);
2662 int ends_sentence();
2663 int overlaps_horizontally();
2664 int overlaps_vertically();
2665 int same(node *);
2666 hyphenation_type get_hyphenation_type();
2667 tfont *get_tfont();
2668 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2669 int character_type();
2670 void tprint(troff_output_file *);
2671 hunits subscript_correction();
2672 hunits skew();
2673 node *add_self(node *, hyphen_list **);
2674 const char *type();
2675 int force_tprint();
2676 int is_tag();
2679 node *node::add_italic_correction(hunits *wd)
2681 hunits ic = italic_correction();
2682 if (ic.is_zero())
2683 return this;
2684 else {
2685 node *next1 = next;
2686 next = 0;
2687 *wd += ic;
2688 return new italic_corrected_node(this, ic, state, div_nest_level, next1);
2692 italic_corrected_node::italic_corrected_node(node *nn, hunits xx, statem *s,
2693 int pop, node *p)
2694 : node(p, s, pop), n(nn), x(xx)
2696 assert(n != 0);
2699 italic_corrected_node::~italic_corrected_node()
2701 delete n;
2704 node *italic_corrected_node::copy()
2706 return new italic_corrected_node(n->copy(), x, state, div_nest_level);
2709 hunits italic_corrected_node::width()
2711 return n->width() + x;
2714 void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
2716 n->vertical_extent(min, max);
2719 void italic_corrected_node::tprint(troff_output_file *out)
2721 n->tprint(out);
2722 out->right(x);
2725 hunits italic_corrected_node::skew()
2727 return n->skew() - x/2;
2730 hunits italic_corrected_node::subscript_correction()
2732 return n->subscript_correction() - x;
2735 void italic_corrected_node::ascii_print(ascii_output_file *out)
2737 n->ascii_print(out);
2740 int italic_corrected_node::ends_sentence()
2742 return n->ends_sentence();
2745 int italic_corrected_node::overlaps_horizontally()
2747 return n->overlaps_horizontally();
2750 int italic_corrected_node::overlaps_vertically()
2752 return n->overlaps_vertically();
2755 node *italic_corrected_node::last_char_node()
2757 return n->last_char_node();
2760 tfont *italic_corrected_node::get_tfont()
2762 return n->get_tfont();
2765 hyphenation_type italic_corrected_node::get_hyphenation_type()
2767 return n->get_hyphenation_type();
2770 node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
2772 nd = n->add_self(nd, p);
2773 hunits not_interested;
2774 nd = nd->add_italic_correction(&not_interested);
2775 n = 0;
2776 delete this;
2777 return nd;
2780 hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail,
2781 int *count)
2783 return n->get_hyphen_list(tail, count);
2786 int italic_corrected_node::character_type()
2788 return n->character_type();
2791 class break_char_node
2792 : public node
2794 node *ch;
2795 char break_code;
2796 color *col;
2798 public:
2799 break_char_node(node *, int, color *, node * = 0);
2800 break_char_node(node *, int, color *, statem *, int, node * = 0);
2801 ~break_char_node();
2802 node *copy();
2803 hunits width();
2804 vunits vertical_width();
2805 node *last_char_node();
2806 int character_type();
2807 int ends_sentence();
2808 node *add_self(node *, hyphen_list **);
2809 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2810 void tprint(troff_output_file *);
2811 void zero_width_tprint(troff_output_file *);
2812 void ascii_print(ascii_output_file *);
2813 void asciify(macro *);
2814 hyphenation_type get_hyphenation_type();
2815 int overlaps_vertically();
2816 int overlaps_horizontally();
2817 units size();
2818 tfont *get_tfont();
2819 int same(node *);
2820 const char *type();
2821 int force_tprint();
2822 int is_tag();
2825 break_char_node::break_char_node(node *n, int bc, color *c, node *x)
2826 : node(x), ch(n), break_code(bc), col(c)
2830 break_char_node::break_char_node(node *n, int bc, color *c, statem *s,
2831 int pop, node *x)
2832 : node(x, s, pop), ch(n), break_code(bc), col(c)
2836 break_char_node::~break_char_node()
2838 delete ch;
2841 node *break_char_node::copy()
2843 return new break_char_node(ch->copy(), break_code, col, state,
2844 div_nest_level);
2847 hunits break_char_node::width()
2849 return ch->width();
2852 vunits break_char_node::vertical_width()
2854 return ch->vertical_width();
2857 node *break_char_node::last_char_node()
2859 return ch->last_char_node();
2862 int break_char_node::character_type()
2864 return ch->character_type();
2867 int break_char_node::ends_sentence()
2869 return ch->ends_sentence();
2872 node *break_char_node::add_self(node *n, hyphen_list **p)
2874 assert((*p)->hyphenation_code == 0);
2875 if (break_code & 1) {
2876 if ((*p)->breakable || break_code & 4) {
2877 n = new space_node(H0, col, n);
2878 n->freeze_space();
2881 next = n;
2882 n = this;
2883 if (break_code & 2) {
2884 if ((*p)->breakable || break_code & 4) {
2885 n = new space_node(H0, col, n);
2886 n->freeze_space();
2889 hyphen_list *pp = *p;
2890 *p = (*p)->next;
2891 delete pp;
2892 return n;
2895 hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail, int *)
2897 return new hyphen_list(0, tail);
2900 hyphenation_type break_char_node::get_hyphenation_type()
2902 return HYPHEN_MIDDLE;
2905 void break_char_node::ascii_print(ascii_output_file *ascii)
2907 ch->ascii_print(ascii);
2910 int break_char_node::overlaps_vertically()
2912 return ch->overlaps_vertically();
2915 int break_char_node::overlaps_horizontally()
2917 return ch->overlaps_horizontally();
2920 units break_char_node::size()
2922 return ch->size();
2925 tfont *break_char_node::get_tfont()
2927 return ch->get_tfont();
2930 node *extra_size_node::copy()
2932 return new extra_size_node(n, state, div_nest_level);
2935 extra_size_node::extra_size_node(vunits i, statem *s, int pop)
2936 : node(0, s, pop), n(i)
2940 extra_size_node::extra_size_node(vunits i)
2941 : n(i)
2945 node *vertical_size_node::copy()
2947 return new vertical_size_node(n, state, div_nest_level);
2950 vertical_size_node::vertical_size_node(vunits i, statem *s, int pop)
2951 : node(0, s, pop), n(i)
2955 vertical_size_node::vertical_size_node(vunits i)
2956 : n(i)
2960 node *hmotion_node::copy()
2962 return new hmotion_node(n, was_tab, unformat, col, state, div_nest_level);
2965 node *space_char_hmotion_node::copy()
2967 return new space_char_hmotion_node(n, col, state, div_nest_level);
2970 vmotion_node::vmotion_node(vunits i, color *c)
2971 : n(i), col(c)
2975 vmotion_node::vmotion_node(vunits i, color *c, statem *s, int pop)
2976 : node(0, s, pop), n(i), col(c)
2980 node *vmotion_node::copy()
2982 return new vmotion_node(n, col, state, div_nest_level);
2985 node *dummy_node::copy()
2987 return new dummy_node;
2990 node *transparent_dummy_node::copy()
2992 return new transparent_dummy_node;
2995 hline_node::~hline_node()
2997 if (n)
2998 delete n;
3001 hline_node::hline_node(hunits i, node *c, node *nxt)
3002 : node(nxt), x(i), n(c)
3006 hline_node::hline_node(hunits i, node *c, statem *s, int pop, node *nxt)
3007 : node(nxt, s, pop), x(i), n(c)
3011 node *hline_node::copy()
3013 return new hline_node(x, n ? n->copy() : 0, state, div_nest_level);
3016 hunits hline_node::width()
3018 return x < H0 ? H0 : x;
3021 vline_node::vline_node(vunits i, node *c, node *nxt)
3022 : node(nxt), x(i), n(c)
3026 vline_node::vline_node(vunits i, node *c, statem *s, int pop, node *nxt)
3027 : node(nxt, s, pop), x(i), n(c)
3031 vline_node::~vline_node()
3033 if (n)
3034 delete n;
3037 node *vline_node::copy()
3039 return new vline_node(x, n ? n->copy() : 0, state, div_nest_level);
3042 hunits vline_node::width()
3044 return n == 0 ? H0 : n->width();
3047 zero_width_node::zero_width_node(node *nd, statem *s, int pop)
3048 : node(0, s, pop), n(nd)
3052 zero_width_node::zero_width_node(node *nd)
3053 : n(nd)
3057 zero_width_node::~zero_width_node()
3059 delete_node_list(n);
3062 node *zero_width_node::copy()
3064 return new zero_width_node(copy_node_list(n), state, div_nest_level);
3067 int node_list_character_type(node *p)
3069 int t = 0;
3070 for (; p; p = p->next)
3071 t |= p->character_type();
3072 return t;
3075 int zero_width_node::character_type()
3077 return node_list_character_type(n);
3080 void node_list_vertical_extent(node *p, vunits *min, vunits *max)
3082 *min = V0;
3083 *max = V0;
3084 vunits cur_vpos = V0;
3085 vunits v1, v2;
3086 for (; p; p = p->next) {
3087 p->vertical_extent(&v1, &v2);
3088 v1 += cur_vpos;
3089 if (v1 < *min)
3090 *min = v1;
3091 v2 += cur_vpos;
3092 if (v2 > *max)
3093 *max = v2;
3094 cur_vpos += p->vertical_width();
3098 void zero_width_node::vertical_extent(vunits *min, vunits *max)
3100 node_list_vertical_extent(n, min, max);
3103 overstrike_node::overstrike_node()
3104 : list(0), max_width(H0)
3108 overstrike_node::overstrike_node(statem *s, int pop)
3109 : node(0, s, pop), list(0), max_width(H0)
3113 overstrike_node::~overstrike_node()
3115 delete_node_list(list);
3118 node *overstrike_node::copy()
3120 overstrike_node *on = new overstrike_node(state, div_nest_level);
3121 for (node *tem = list; tem; tem = tem->next)
3122 on->overstrike(tem->copy());
3123 return on;
3126 void overstrike_node::overstrike(node *n)
3128 if (n == 0)
3129 return;
3130 hunits w = n->width();
3131 if (w > max_width)
3132 max_width = w;
3133 node **p;
3134 for (p = &list; *p; p = &(*p)->next)
3136 n->next = 0;
3137 *p = n;
3140 hunits overstrike_node::width()
3142 return max_width;
3145 bracket_node::bracket_node()
3146 : list(0), max_width(H0)
3150 bracket_node::bracket_node(statem *s, int pop)
3151 : node(0, s, pop), list(0), max_width(H0)
3155 bracket_node::~bracket_node()
3157 delete_node_list(list);
3160 node *bracket_node::copy()
3162 bracket_node *on = new bracket_node(state, div_nest_level);
3163 node *last_node = 0;
3164 node *tem;
3165 if (list)
3166 list->last = 0;
3167 for (tem = list; tem; tem = tem->next) {
3168 if (tem->next)
3169 tem->next->last = tem;
3170 last_node = tem;
3172 for (tem = last_node; tem; tem = tem->last)
3173 on->bracket(tem->copy());
3174 return on;
3177 void bracket_node::bracket(node *n)
3179 if (n == 0)
3180 return;
3181 hunits w = n->width();
3182 if (w > max_width)
3183 max_width = w;
3184 n->next = list;
3185 list = n;
3188 hunits bracket_node::width()
3190 return max_width;
3193 int node::nspaces()
3195 return 0;
3198 int node::merge_space(hunits, hunits, hunits)
3200 return 0;
3203 #if 0
3204 space_node *space_node::free_list = 0;
3206 void *space_node::operator new(size_t n)
3208 assert(n == sizeof(space_node));
3209 if (!free_list) {
3210 free_list = (space_node *)new char[sizeof(space_node)*BLOCK];
3211 for (int i = 0; i < BLOCK - 1; i++)
3212 free_list[i].next = free_list + i + 1;
3213 free_list[BLOCK-1].next = 0;
3215 space_node *p = free_list;
3216 free_list = (space_node *)(free_list->next);
3217 p->next = 0;
3218 return p;
3221 inline void space_node::operator delete(void *p)
3223 if (p) {
3224 ((space_node *)p)->next = free_list;
3225 free_list = (space_node *)p;
3228 #endif
3230 space_node::space_node(hunits nn, color *c, node *p)
3231 : node(p, 0, 0), n(nn), set(0), was_escape_colon(0), col(c)
3235 space_node::space_node(hunits nn, color *c, statem *s, int pop, node *p)
3236 : node(p, s, pop), n(nn), set(0), was_escape_colon(0), col(c)
3240 space_node::space_node(hunits nn, int s, int flag, color *c, statem *st,
3241 int pop, node *p)
3242 : node(p, st, pop), n(nn), set(s), was_escape_colon(flag), col(c)
3246 #if 0
3247 space_node::~space_node()
3250 #endif
3252 node *space_node::copy()
3254 return new space_node(n, set, was_escape_colon, col, state, div_nest_level);
3257 int space_node::force_tprint()
3259 return 0;
3262 int space_node::is_tag()
3264 return 0;
3267 int space_node::nspaces()
3269 return set ? 0 : 1;
3272 int space_node::merge_space(hunits h, hunits, hunits)
3274 n += h;
3275 return 1;
3278 hunits space_node::width()
3280 return n;
3283 void node::spread_space(int*, hunits*)
3287 void space_node::spread_space(int *n_spaces, hunits *desired_space)
3289 if (!set) {
3290 assert(*n_spaces > 0);
3291 if (*n_spaces == 1) {
3292 n += *desired_space;
3293 *desired_space = H0;
3295 else {
3296 hunits extra = *desired_space / *n_spaces;
3297 *desired_space -= extra;
3298 n += extra;
3300 *n_spaces -= 1;
3301 set = 1;
3305 void node::freeze_space()
3309 void space_node::freeze_space()
3311 set = 1;
3314 void node::is_escape_colon()
3318 void space_node::is_escape_colon()
3320 was_escape_colon = 1;
3323 diverted_space_node::diverted_space_node(vunits d, statem *s, int pop,
3324 node *p)
3325 : node(p, s, pop), n(d)
3329 diverted_space_node::diverted_space_node(vunits d, node *p)
3330 : node(p), n(d)
3334 node *diverted_space_node::copy()
3336 return new diverted_space_node(n, state, div_nest_level);
3339 diverted_copy_file_node::diverted_copy_file_node(symbol s, statem *st,
3340 int pop, node *p)
3341 : node(p, st, pop), filename(s)
3345 diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
3346 : node(p), filename(s)
3350 node *diverted_copy_file_node::copy()
3352 return new diverted_copy_file_node(filename, state, div_nest_level);
3355 int node::ends_sentence()
3357 return 0;
3360 int kern_pair_node::ends_sentence()
3362 switch (n2->ends_sentence()) {
3363 case 0:
3364 return 0;
3365 case 1:
3366 return 1;
3367 case 2:
3368 break;
3369 default:
3370 assert(0);
3372 return n1->ends_sentence();
3375 int node_list_ends_sentence(node *n)
3377 for (; n != 0; n = n->next)
3378 switch (n->ends_sentence()) {
3379 case 0:
3380 return 0;
3381 case 1:
3382 return 1;
3383 case 2:
3384 break;
3385 default:
3386 assert(0);
3388 return 2;
3391 int dbreak_node::ends_sentence()
3393 return node_list_ends_sentence(none);
3396 int node::overlaps_horizontally()
3398 return 0;
3401 int node::overlaps_vertically()
3403 return 0;
3406 int node::discardable()
3408 return 0;
3411 int space_node::discardable()
3413 return set ? 0 : 1;
3416 vunits node::vertical_width()
3418 return V0;
3421 vunits vline_node::vertical_width()
3423 return x;
3426 vunits vmotion_node::vertical_width()
3428 return n;
3431 int node::set_unformat_flag()
3433 return 1;
3436 int node::character_type()
3438 return 0;
3441 hunits node::subscript_correction()
3443 return H0;
3446 hunits node::italic_correction()
3448 return H0;
3451 hunits node::left_italic_correction()
3453 return H0;
3456 hunits node::skew()
3458 return H0;
3461 /* vertical_extent methods */
3463 void node::vertical_extent(vunits *min, vunits *max)
3465 vunits v = vertical_width();
3466 if (v < V0) {
3467 *min = v;
3468 *max = V0;
3470 else {
3471 *max = v;
3472 *min = V0;
3476 void vline_node::vertical_extent(vunits *min, vunits *max)
3478 if (n == 0)
3479 node::vertical_extent(min, max);
3480 else {
3481 vunits cmin, cmax;
3482 n->vertical_extent(&cmin, &cmax);
3483 vunits h = n->size();
3484 if (x < V0) {
3485 if (-x < h) {
3486 *min = x;
3487 *max = V0;
3489 else {
3490 // we print the first character and then move up, so
3491 *max = cmax;
3492 // we print the last character and then move up h
3493 *min = cmin + h;
3494 if (*min > V0)
3495 *min = V0;
3496 *min += x;
3499 else {
3500 if (x < h) {
3501 *max = x;
3502 *min = V0;
3504 else {
3505 // we move down by h and then print the first character, so
3506 *min = cmin + h;
3507 if (*min > V0)
3508 *min = V0;
3509 *max = x + cmax;
3515 /* ascii_print methods */
3517 static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n)
3519 if (n == 0)
3520 return;
3521 ascii_print_reverse_node_list(ascii, n->next);
3522 n->ascii_print(ascii);
3525 void dbreak_node::ascii_print(ascii_output_file *ascii)
3527 ascii_print_reverse_node_list(ascii, none);
3530 void kern_pair_node::ascii_print(ascii_output_file *ascii)
3532 n1->ascii_print(ascii);
3533 n2->ascii_print(ascii);
3536 void node::ascii_print(ascii_output_file *)
3540 void space_node::ascii_print(ascii_output_file *ascii)
3542 if (!n.is_zero())
3543 ascii->outc(' ');
3546 void hmotion_node::ascii_print(ascii_output_file *ascii)
3548 // this is pretty arbitrary
3549 if (n >= points_to_units(2))
3550 ascii->outc(' ');
3553 void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
3555 ascii->outc(' ');
3558 /* asciify methods */
3560 void node::asciify(macro *m)
3562 m->append(this);
3565 void glyph_node::asciify(macro *m)
3567 unsigned char c = ci->get_asciify_code();
3568 if (c == 0)
3569 c = ci->get_ascii_code();
3570 if (c != 0) {
3571 m->append(c);
3572 delete this;
3574 else
3575 m->append(this);
3578 void kern_pair_node::asciify(macro *m)
3580 n1->asciify(m);
3581 n2->asciify(m);
3582 n1 = n2 = 0;
3583 delete this;
3586 static void asciify_reverse_node_list(macro *m, node *n)
3588 if (n == 0)
3589 return;
3590 asciify_reverse_node_list(m, n->next);
3591 n->asciify(m);
3594 void dbreak_node::asciify(macro *m)
3596 asciify_reverse_node_list(m, none);
3597 none = 0;
3598 delete this;
3601 void ligature_node::asciify(macro *m)
3603 n1->asciify(m);
3604 n2->asciify(m);
3605 n1 = n2 = 0;
3606 delete this;
3609 void break_char_node::asciify(macro *m)
3611 ch->asciify(m);
3612 ch = 0;
3613 delete this;
3616 void italic_corrected_node::asciify(macro *m)
3618 n->asciify(m);
3619 n = 0;
3620 delete this;
3623 void left_italic_corrected_node::asciify(macro *m)
3625 if (n) {
3626 n->asciify(m);
3627 n = 0;
3629 delete this;
3632 void hmotion_node::asciify(macro *m)
3634 if (was_tab) {
3635 m->append('\t');
3636 delete this;
3638 else
3639 m->append(this);
3642 space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3643 statem *s, int pop,
3644 node *nxt)
3645 : hmotion_node(i, c, s, pop, nxt)
3649 space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3650 node *nxt)
3651 : hmotion_node(i, c, 0, 0, nxt)
3655 void space_char_hmotion_node::asciify(macro *m)
3657 m->append(ESCAPE_SPACE);
3658 delete this;
3661 void space_node::asciify(macro *m)
3663 if (was_escape_colon) {
3664 m->append(ESCAPE_COLON);
3665 delete this;
3667 else
3668 m->append(this);
3671 void word_space_node::asciify(macro *m)
3673 for (width_list *w = orig_width; w; w = w->next)
3674 m->append(' ');
3675 delete this;
3678 void unbreakable_space_node::asciify(macro *m)
3680 m->append(ESCAPE_TILDE);
3681 delete this;
3684 void line_start_node::asciify(macro *)
3686 delete this;
3689 void vertical_size_node::asciify(macro *)
3691 delete this;
3694 breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/,
3695 breakpoint *rest, int /*is_inner*/)
3697 return rest;
3700 int node::nbreaks()
3702 return 0;
3705 breakpoint *space_node::get_breakpoints(hunits wd, int ns,
3706 breakpoint *rest, int is_inner)
3708 if (next && next->discardable())
3709 return rest;
3710 breakpoint *bp = new breakpoint;
3711 bp->next = rest;
3712 bp->width = wd;
3713 bp->nspaces = ns;
3714 bp->hyphenated = 0;
3715 if (is_inner) {
3716 assert(rest != 0);
3717 bp->index = rest->index + 1;
3718 bp->nd = rest->nd;
3720 else {
3721 bp->nd = this;
3722 bp->index = 0;
3724 return bp;
3727 int space_node::nbreaks()
3729 if (next && next->discardable())
3730 return 0;
3731 else
3732 return 1;
3735 static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
3736 int ns, breakpoint *rest)
3738 if (p != 0) {
3739 rest = p->get_breakpoints(*widthp,
3741 node_list_get_breakpoints(p->next, widthp, ns,
3742 rest),
3744 *widthp += p->width();
3746 return rest;
3749 breakpoint *dbreak_node::get_breakpoints(hunits wd, int ns,
3750 breakpoint *rest, int is_inner)
3752 breakpoint *bp = new breakpoint;
3753 bp->next = rest;
3754 bp->width = wd;
3755 for (node *tem = pre; tem != 0; tem = tem->next)
3756 bp->width += tem->width();
3757 bp->nspaces = ns;
3758 bp->hyphenated = 1;
3759 if (is_inner) {
3760 assert(rest != 0);
3761 bp->index = rest->index + 1;
3762 bp->nd = rest->nd;
3764 else {
3765 bp->nd = this;
3766 bp->index = 0;
3768 return node_list_get_breakpoints(none, &wd, ns, bp);
3771 int dbreak_node::nbreaks()
3773 int i = 1;
3774 for (node *tem = none; tem != 0; tem = tem->next)
3775 i += tem->nbreaks();
3776 return i;
3779 void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
3781 assert(0);
3784 void space_node::split(int where, node **pre, node **post)
3786 assert(where == 0);
3787 *pre = next;
3788 *post = 0;
3789 delete this;
3792 static void node_list_split(node *p, int *wherep, node **prep, node **postp)
3794 if (p == 0)
3795 return;
3796 int nb = p->nbreaks();
3797 node_list_split(p->next, wherep, prep, postp);
3798 if (*wherep < 0) {
3799 p->next = *postp;
3800 *postp = p;
3802 else if (*wherep < nb) {
3803 p->next = *prep;
3804 p->split(*wherep, prep, postp);
3806 else {
3807 p->next = *prep;
3808 *prep = p;
3810 *wherep -= nb;
3813 void dbreak_node::split(int where, node **prep, node **postp)
3815 assert(where >= 0);
3816 if (where == 0) {
3817 *postp = post;
3818 post = 0;
3819 if (pre == 0)
3820 *prep = next;
3821 else {
3822 node *tem;
3823 for (tem = pre; tem->next != 0; tem = tem->next)
3825 tem->next = next;
3826 *prep = pre;
3828 pre = 0;
3829 delete this;
3831 else {
3832 *prep = next;
3833 where -= 1;
3834 node_list_split(none, &where, prep, postp);
3835 none = 0;
3836 delete this;
3840 hyphenation_type node::get_hyphenation_type()
3842 return HYPHEN_BOUNDARY;
3845 hyphenation_type dbreak_node::get_hyphenation_type()
3847 return HYPHEN_INHIBIT;
3850 hyphenation_type kern_pair_node::get_hyphenation_type()
3852 return HYPHEN_MIDDLE;
3855 hyphenation_type dummy_node::get_hyphenation_type()
3857 return HYPHEN_MIDDLE;
3860 hyphenation_type transparent_dummy_node::get_hyphenation_type()
3862 return HYPHEN_MIDDLE;
3865 hyphenation_type hmotion_node::get_hyphenation_type()
3867 return HYPHEN_MIDDLE;
3870 hyphenation_type space_char_hmotion_node::get_hyphenation_type()
3872 return HYPHEN_MIDDLE;
3875 hyphenation_type overstrike_node::get_hyphenation_type()
3877 return HYPHEN_MIDDLE;
3880 hyphenation_type space_node::get_hyphenation_type()
3882 if (was_escape_colon)
3883 return HYPHEN_MIDDLE;
3884 return HYPHEN_BOUNDARY;
3887 hyphenation_type unbreakable_space_node::get_hyphenation_type()
3889 return HYPHEN_MIDDLE;
3892 int node::interpret(macro *)
3894 return 0;
3897 special_node::special_node(const macro &m, int n)
3898 : mac(m), no_init_string(n)
3900 font_size fs = curenv->get_font_size();
3901 int char_height = curenv->get_char_height();
3902 int char_slant = curenv->get_char_slant();
3903 int fontno = env_definite_font(curenv);
3904 tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fontno);
3905 if (curenv->is_composite())
3906 tf = tf->get_plain();
3907 gcol = curenv->get_glyph_color();
3908 fcol = curenv->get_fill_color();
3909 is_special = 1;
3912 special_node::special_node(const macro &m, tfont *t,
3913 color *gc, color *fc,
3914 statem *s, int pop,
3915 int n)
3916 : node(0, s, pop), mac(m), tf(t), gcol(gc), fcol(fc), no_init_string(n)
3918 is_special = 1;
3921 int special_node::same(node *n)
3923 return mac == ((special_node *)n)->mac
3924 && tf == ((special_node *)n)->tf
3925 && gcol == ((special_node *)n)->gcol
3926 && fcol == ((special_node *)n)->fcol
3927 && no_init_string == ((special_node *)n)->no_init_string;
3930 const char *special_node::type()
3932 return "special_node";
3935 int special_node::ends_sentence()
3937 return 2;
3940 int special_node::force_tprint()
3942 return 0;
3945 int special_node::is_tag()
3947 return 0;
3950 node *special_node::copy()
3952 return new special_node(mac, tf, gcol, fcol, state, div_nest_level,
3953 no_init_string);
3956 void special_node::tprint_start(troff_output_file *out)
3958 out->start_special(tf, gcol, fcol, no_init_string);
3961 void special_node::tprint_char(troff_output_file *out, unsigned char c)
3963 out->special_char(c);
3966 void special_node::tprint_end(troff_output_file *out)
3968 out->end_special();
3971 tfont *special_node::get_tfont()
3973 return tf;
3976 /* suppress_node */
3978 suppress_node::suppress_node(int on_or_off, int issue_limits)
3979 : is_on(on_or_off), emit_limits(issue_limits), filename(0), position(0),
3980 image_id(0)
3984 suppress_node::suppress_node(symbol f, char p, int id)
3985 : is_on(2), emit_limits(0), filename(f), position(p), image_id(id)
3987 is_special = 1;
3990 suppress_node::suppress_node(int issue_limits, int on_or_off,
3991 symbol f, char p, int id,
3992 statem *s, int pop)
3993 : node(0, s, pop), is_on(on_or_off), emit_limits(issue_limits), filename(f),
3994 position(p), image_id(id)
3998 int suppress_node::same(node *n)
4000 return ((is_on == ((suppress_node *)n)->is_on)
4001 && (emit_limits == ((suppress_node *)n)->emit_limits)
4002 && (filename == ((suppress_node *)n)->filename)
4003 && (position == ((suppress_node *)n)->position)
4004 && (image_id == ((suppress_node *)n)->image_id));
4007 const char *suppress_node::type()
4009 return "suppress_node";
4012 node *suppress_node::copy()
4014 return new suppress_node(emit_limits, is_on, filename, position, image_id,
4015 state, div_nest_level);
4018 /* tag_node */
4020 tag_node::tag_node()
4021 : delayed(0)
4023 is_special = 1;
4026 tag_node::tag_node(string s, int delay)
4027 : tag_string(s), delayed(delay)
4029 is_special = !delay;
4032 tag_node::tag_node(string s, statem *st, int pop, int delay)
4033 : node(0, st, pop), tag_string(s), delayed(delay)
4035 is_special = !delay;
4038 node *tag_node::copy()
4040 return new tag_node(tag_string, state, div_nest_level, delayed);
4043 void tag_node::tprint(troff_output_file *out)
4045 if (delayed)
4046 out->add_to_tag_list(tag_string);
4047 else
4048 out->state.add_tag(out->fp, tag_string);
4051 int tag_node::same(node *nd)
4053 return tag_string == ((tag_node *)nd)->tag_string
4054 && delayed == ((tag_node *)nd)->delayed;
4057 const char *tag_node::type()
4059 return "tag_node";
4062 int tag_node::force_tprint()
4064 return !delayed;
4067 int tag_node::is_tag()
4069 return !delayed;
4072 int tag_node::ends_sentence()
4074 return 2;
4077 int get_reg_int(const char *p)
4079 reg *r = (reg *)number_reg_dictionary.lookup(p);
4080 units prev_value;
4081 if (r && (r->get_value(&prev_value)))
4082 return (int)prev_value;
4083 else
4084 warning(WARN_REG, "number register `%1' not defined", p);
4085 return 0;
4088 const char *get_reg_str(const char *p)
4090 reg *r = (reg *)number_reg_dictionary.lookup(p);
4091 if (r)
4092 return r->get_string();
4093 else
4094 warning(WARN_REG, "register `%1' not defined", p);
4095 return 0;
4098 void suppress_node::put(troff_output_file *out, const char *s)
4100 int i = 0;
4101 while (s[i] != (char)0) {
4102 out->special_char(s[i]);
4103 i++;
4108 * We need to remember the start of the image and its name.
4111 static char last_position = 0;
4112 static const char *last_image_filename = 0;
4113 static int last_image_id = 0;
4115 inline int min(int a, int b)
4117 return a < b ? a : b;
4121 * tprint - if (is_on == 2)
4122 * remember current position (l, r, c, i) and filename
4123 * else
4124 * if (emit_limits)
4125 * if (html)
4126 * emit image tag
4127 * else
4128 * emit postscript bounds for image
4129 * else
4130 * if (suppress boolean differs from current state)
4131 * alter state
4132 * reset registers
4133 * record current page
4134 * set low water mark.
4137 void suppress_node::tprint(troff_output_file *out)
4139 int current_page = topdiv->get_page_number();
4140 // firstly check to see whether this suppress node contains
4141 // an image filename & position.
4142 if (is_on == 2) {
4143 // remember position and filename
4144 last_position = position;
4145 char *tem = (char *)last_image_filename;
4146 last_image_filename = strsave(filename.contents());
4147 if (tem)
4148 a_delete tem;
4149 last_image_id = image_id;
4150 // printf("start of image and page = %d\n", current_page);
4152 else {
4153 // now check whether the suppress node requires us to issue limits.
4154 if (emit_limits) {
4155 char name[8192];
4156 // remember that the filename will contain a %d in which the
4157 // last_image_id is placed
4158 if (last_image_filename == (char *) 0)
4159 *name = '\0';
4160 else
4161 sprintf(name, last_image_filename, last_image_id);
4162 if (is_html) {
4163 switch (last_position) {
4164 case 'c':
4165 out->start_special();
4166 put(out, "devtag:.centered-image");
4167 break;
4168 case 'r':
4169 out->start_special();
4170 put(out, "devtag:.right-image");
4171 break;
4172 case 'l':
4173 out->start_special();
4174 put(out, "devtag:.left-image");
4175 break;
4176 case 'i':
4178 default:
4181 out->end_special();
4182 out->start_special();
4183 put(out, "devtag:.auto-image ");
4184 put(out, name);
4185 out->end_special();
4187 else {
4188 // postscript (or other device)
4189 if (suppress_start_page > 0 && current_page != suppress_start_page)
4190 error("suppression limit registers span more than one page;\n"
4191 "image description %1 will be wrong", image_no);
4192 // if (topdiv->get_page_number() != suppress_start_page)
4193 // fprintf(stderr, "end of image and topdiv page = %d and suppress_start_page = %d\n",
4194 // topdiv->get_page_number(), suppress_start_page);
4196 // remember that the filename will contain a %d in which the
4197 // image_no is placed
4198 fprintf(stderr,
4199 "grohtml-info:page %d %d %d %d %d %d %s %d %d %s\n",
4200 topdiv->get_page_number(),
4201 get_reg_int("opminx"), get_reg_int("opminy"),
4202 get_reg_int("opmaxx"), get_reg_int("opmaxy"),
4203 // page offset + line length
4204 get_reg_int(".o") + get_reg_int(".l"),
4205 name, hresolution, vresolution, get_reg_str(".F"));
4206 fflush(stderr);
4209 else {
4210 if (is_on) {
4211 out->on();
4212 // lastly we reset the output registers
4213 reset_output_registers();
4215 else
4216 out->off();
4217 suppress_start_page = current_page;
4222 int suppress_node::force_tprint()
4224 return is_on;
4227 int suppress_node::is_tag()
4229 return is_on;
4232 hunits suppress_node::width()
4234 return H0;
4237 /* composite_node */
4239 class composite_node
4240 : public charinfo_node
4242 node *n;
4243 tfont *tf;
4245 public:
4246 composite_node(node *, charinfo *, tfont *, statem *, int, node * = 0);
4247 ~composite_node();
4248 node *copy();
4249 hunits width();
4250 node *last_char_node();
4251 units size();
4252 void tprint(troff_output_file *);
4253 hyphenation_type get_hyphenation_type();
4254 void ascii_print(ascii_output_file *);
4255 void asciify(macro *);
4256 hyphen_list *get_hyphen_list(hyphen_list *, int *);
4257 node *add_self(node *, hyphen_list **);
4258 tfont *get_tfont();
4259 int same(node *);
4260 const char *type();
4261 int force_tprint();
4262 int is_tag();
4263 void vertical_extent(vunits *, vunits *);
4264 vunits vertical_width();
4267 composite_node::composite_node(node *p, charinfo *c, tfont *t, statem *s,
4268 int pop, node *x)
4269 : charinfo_node(c, s, pop, x), n(p), tf(t)
4273 composite_node::~composite_node()
4275 delete_node_list(n);
4278 node *composite_node::copy()
4280 return new composite_node(copy_node_list(n), ci, tf, state, div_nest_level);
4283 hunits composite_node::width()
4285 hunits x;
4286 if (tf->get_constant_space(&x))
4287 return x;
4288 x = H0;
4289 for (node *tem = n; tem; tem = tem->next)
4290 x += tem->width();
4291 hunits offset;
4292 if (tf->get_bold(&offset))
4293 x += offset;
4294 x += tf->get_track_kern();
4295 return x;
4298 node *composite_node::last_char_node()
4300 return this;
4303 vunits composite_node::vertical_width()
4305 vunits v = V0;
4306 for (node *tem = n; tem; tem = tem->next)
4307 v += tem->vertical_width();
4308 return v;
4311 units composite_node::size()
4313 return tf->get_size().to_units();
4316 hyphenation_type composite_node::get_hyphenation_type()
4318 return HYPHEN_MIDDLE;
4321 void composite_node::asciify(macro *m)
4323 unsigned char c = ci->get_asciify_code();
4324 if (c == 0)
4325 c = ci->get_ascii_code();
4326 if (c != 0) {
4327 m->append(c);
4328 delete this;
4330 else
4331 m->append(this);
4334 void composite_node::ascii_print(ascii_output_file *ascii)
4336 unsigned char c = ci->get_ascii_code();
4337 if (c != 0)
4338 ascii->outc(c);
4339 else
4340 ascii->outs(ci->nm.contents());
4344 hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail, int *count)
4346 (*count)++;
4347 return new hyphen_list(ci->get_hyphenation_code(), tail);
4350 node *composite_node::add_self(node *nn, hyphen_list **p)
4352 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
4353 next = nn;
4354 nn = this;
4355 if ((*p)->hyphen)
4356 nn = nn->add_discretionary_hyphen();
4357 hyphen_list *pp = *p;
4358 *p = (*p)->next;
4359 delete pp;
4360 return nn;
4363 tfont *composite_node::get_tfont()
4365 return tf;
4368 node *reverse_node_list(node *n)
4370 node *r = 0;
4371 while (n) {
4372 node *tem = n;
4373 n = n->next;
4374 tem->next = r;
4375 r = tem;
4377 return r;
4380 void composite_node::vertical_extent(vunits *minimum, vunits *maximum)
4382 n = reverse_node_list(n);
4383 node_list_vertical_extent(n, minimum, maximum);
4384 n = reverse_node_list(n);
4387 width_list::width_list(hunits w, hunits s)
4388 : width(w), sentence_width(s), next(0)
4392 width_list::width_list(width_list *w)
4393 : width(w->width), sentence_width(w->sentence_width), next(0)
4397 word_space_node::word_space_node(hunits d, color *c, width_list *w, node *x)
4398 : space_node(d, c, x), orig_width(w), unformat(0)
4402 word_space_node::word_space_node(hunits d, int s, color *c, width_list *w,
4403 int flag, statem *st, int pop, node *x)
4404 : space_node(d, s, 0, c, st, pop, x), orig_width(w), unformat(flag)
4408 word_space_node::~word_space_node()
4410 width_list *w = orig_width;
4411 while (w != 0) {
4412 width_list *tmp = w;
4413 w = w->next;
4414 delete tmp;
4418 node *word_space_node::copy()
4420 assert(orig_width != 0);
4421 width_list *w_old_curr = orig_width;
4422 width_list *w_new_curr = new width_list(w_old_curr);
4423 width_list *w_new = w_new_curr;
4424 w_old_curr = w_old_curr->next;
4425 while (w_old_curr != 0) {
4426 w_new_curr->next = new width_list(w_old_curr);
4427 w_new_curr = w_new_curr->next;
4428 w_old_curr = w_old_curr->next;
4430 return new word_space_node(n, set, col, w_new, unformat, state,
4431 div_nest_level);
4434 int word_space_node::set_unformat_flag()
4436 unformat = 1;
4437 return 1;
4440 void word_space_node::tprint(troff_output_file *out)
4442 out->fill_color(col);
4443 out->word_marker();
4444 out->right(n);
4447 int word_space_node::merge_space(hunits h, hunits sw, hunits ssw)
4449 n += h;
4450 assert(orig_width != 0);
4451 width_list *w = orig_width;
4452 for (; w->next; w = w->next)
4454 w->next = new width_list(sw, ssw);
4455 return 1;
4458 unbreakable_space_node::unbreakable_space_node(hunits d, color *c, node *x)
4459 : word_space_node(d, c, 0, x)
4463 unbreakable_space_node::unbreakable_space_node(hunits d, int s,
4464 color *c, statem *st, int pop,
4465 node *x)
4466 : word_space_node(d, s, c, 0, 0, st, pop, x)
4470 node *unbreakable_space_node::copy()
4472 return new unbreakable_space_node(n, set, col, state, div_nest_level);
4475 int unbreakable_space_node::force_tprint()
4477 return 0;
4480 int unbreakable_space_node::is_tag()
4482 return 0;
4485 breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
4486 breakpoint *rest, int)
4488 return rest;
4491 int unbreakable_space_node::nbreaks()
4493 return 0;
4496 void unbreakable_space_node::split(int, node **, node **)
4498 assert(0);
4501 int unbreakable_space_node::merge_space(hunits, hunits, hunits)
4503 return 0;
4506 hvpair::hvpair()
4510 draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4511 color *gc, color *fc)
4512 : npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4514 point = new hvpair[npoints];
4515 for (int i = 0; i < npoints; i++)
4516 point[i] = p[i];
4519 draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4520 color *gc, color *fc, statem *st, int pop)
4521 : node(0, st, pop), npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4523 point = new hvpair[npoints];
4524 for (int i = 0; i < npoints; i++)
4525 point[i] = p[i];
4528 int draw_node::same(node *n)
4530 draw_node *nd = (draw_node *)n;
4531 if (code != nd->code || npoints != nd->npoints || sz != nd->sz
4532 || gcol != nd->gcol || fcol != nd->fcol)
4533 return 0;
4534 for (int i = 0; i < npoints; i++)
4535 if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
4536 return 0;
4537 return 1;
4540 const char *draw_node::type()
4542 return "draw_node";
4545 int draw_node::force_tprint()
4547 return 0;
4550 int draw_node::is_tag()
4552 return 0;
4555 draw_node::~draw_node()
4557 if (point)
4558 a_delete point;
4561 hunits draw_node::width()
4563 hunits x = H0;
4564 for (int i = 0; i < npoints; i++)
4565 x += point[i].h;
4566 return x;
4569 vunits draw_node::vertical_width()
4571 if (code == 'e')
4572 return V0;
4573 vunits x = V0;
4574 for (int i = 0; i < npoints; i++)
4575 x += point[i].v;
4576 return x;
4579 node *draw_node::copy()
4581 return new draw_node(code, point, npoints, sz, gcol, fcol, state,
4582 div_nest_level);
4585 void draw_node::tprint(troff_output_file *out)
4587 out->draw(code, point, npoints, sz, gcol, fcol);
4590 /* tprint methods */
4592 void glyph_node::tprint(troff_output_file *out)
4594 tfont *ptf = tf->get_plain();
4595 if (ptf == tf)
4596 out->put_char_width(ci, ptf, gcol, fcol, width(), H0);
4597 else {
4598 hunits offset;
4599 int bold = tf->get_bold(&offset);
4600 hunits w = ptf->get_width(ci);
4601 hunits k = H0;
4602 hunits x;
4603 int cs = tf->get_constant_space(&x);
4604 if (cs) {
4605 x -= w;
4606 if (bold)
4607 x -= offset;
4608 hunits x2 = x/2;
4609 out->right(x2);
4610 k = x - x2;
4612 else
4613 k = tf->get_track_kern();
4614 if (bold) {
4615 out->put_char(ci, ptf, gcol, fcol);
4616 out->right(offset);
4618 out->put_char_width(ci, ptf, gcol, fcol, w, k);
4622 void glyph_node::zero_width_tprint(troff_output_file *out)
4624 tfont *ptf = tf->get_plain();
4625 hunits offset;
4626 int bold = tf->get_bold(&offset);
4627 hunits x;
4628 int cs = tf->get_constant_space(&x);
4629 if (cs) {
4630 x -= ptf->get_width(ci);
4631 if (bold)
4632 x -= offset;
4633 x = x/2;
4634 out->right(x);
4636 out->put_char(ci, ptf, gcol, fcol);
4637 if (bold) {
4638 out->right(offset);
4639 out->put_char(ci, ptf, gcol, fcol);
4640 out->right(-offset);
4642 if (cs)
4643 out->right(-x);
4646 void break_char_node::tprint(troff_output_file *t)
4648 ch->tprint(t);
4651 void break_char_node::zero_width_tprint(troff_output_file *t)
4653 ch->zero_width_tprint(t);
4656 void hline_node::tprint(troff_output_file *out)
4658 if (x < H0) {
4659 out->right(x);
4660 x = -x;
4662 if (n == 0) {
4663 out->right(x);
4664 return;
4666 hunits w = n->width();
4667 if (w <= H0) {
4668 error("horizontal line drawing character must have positive width");
4669 out->right(x);
4670 return;
4672 int i = int(x/w);
4673 if (i == 0) {
4674 hunits xx = x - w;
4675 hunits xx2 = xx/2;
4676 out->right(xx2);
4677 if (out->is_on())
4678 n->tprint(out);
4679 out->right(xx - xx2);
4681 else {
4682 hunits rem = x - w*i;
4683 if (rem > H0) {
4684 if (n->overlaps_horizontally()) {
4685 if (out->is_on())
4686 n->tprint(out);
4687 out->right(rem - w);
4689 else
4690 out->right(rem);
4692 while (--i >= 0)
4693 if (out->is_on())
4694 n->tprint(out);
4698 void vline_node::tprint(troff_output_file *out)
4700 if (n == 0) {
4701 out->down(x);
4702 return;
4704 vunits h = n->size();
4705 int overlaps = n->overlaps_vertically();
4706 vunits y = x;
4707 if (y < V0) {
4708 y = -y;
4709 int i = y / h;
4710 vunits rem = y - i*h;
4711 if (i == 0) {
4712 out->right(n->width());
4713 out->down(-rem);
4715 else {
4716 while (--i > 0) {
4717 n->zero_width_tprint(out);
4718 out->down(-h);
4720 if (overlaps) {
4721 n->zero_width_tprint(out);
4722 out->down(-rem);
4723 if (out->is_on())
4724 n->tprint(out);
4725 out->down(-h);
4727 else {
4728 if (out->is_on())
4729 n->tprint(out);
4730 out->down(-h - rem);
4734 else {
4735 int i = y / h;
4736 vunits rem = y - i*h;
4737 if (i == 0) {
4738 out->down(rem);
4739 out->right(n->width());
4741 else {
4742 out->down(h);
4743 if (overlaps)
4744 n->zero_width_tprint(out);
4745 out->down(rem);
4746 while (--i > 0) {
4747 n->zero_width_tprint(out);
4748 out->down(h);
4750 if (out->is_on())
4751 n->tprint(out);
4756 void zero_width_node::tprint(troff_output_file *out)
4758 if (!n)
4759 return;
4760 if (!n->next) {
4761 n->zero_width_tprint(out);
4762 return;
4764 int hpos = out->get_hpos();
4765 int vpos = out->get_vpos();
4766 node *tem = n;
4767 while (tem) {
4768 tem->tprint(out);
4769 tem = tem->next;
4771 out->moveto(hpos, vpos);
4774 void overstrike_node::tprint(troff_output_file *out)
4776 hunits pos = H0;
4777 for (node *tem = list; tem; tem = tem->next) {
4778 hunits x = (max_width - tem->width())/2;
4779 out->right(x - pos);
4780 pos = x;
4781 tem->zero_width_tprint(out);
4783 out->right(max_width - pos);
4786 void bracket_node::tprint(troff_output_file *out)
4788 if (list == 0)
4789 return;
4790 int npieces = 0;
4791 node *tem;
4792 for (tem = list; tem; tem = tem->next)
4793 ++npieces;
4794 vunits h = list->size();
4795 vunits totalh = h*npieces;
4796 vunits y = (totalh - h)/2;
4797 out->down(y);
4798 for (tem = list; tem; tem = tem->next) {
4799 tem->zero_width_tprint(out);
4800 out->down(-h);
4802 out->right(max_width);
4803 out->down(totalh - y);
4806 void node::tprint(troff_output_file *)
4810 void node::zero_width_tprint(troff_output_file *out)
4812 int hpos = out->get_hpos();
4813 int vpos = out->get_vpos();
4814 tprint(out);
4815 out->moveto(hpos, vpos);
4818 void space_node::tprint(troff_output_file *out)
4820 out->fill_color(col);
4821 out->right(n);
4824 void hmotion_node::tprint(troff_output_file *out)
4826 out->fill_color(col);
4827 out->right(n);
4830 void space_char_hmotion_node::tprint(troff_output_file *out)
4832 out->fill_color(col);
4833 if (is_html) {
4834 // we emit the space width as a negative glyph index
4835 out->flush_tbuf();
4836 out->do_motion();
4837 out->put('N');
4838 out->put(-n.to_units());
4839 out->put('\n');
4841 out->right(n);
4844 void vmotion_node::tprint(troff_output_file *out)
4846 out->fill_color(col);
4847 out->down(n);
4850 void kern_pair_node::tprint(troff_output_file *out)
4852 n1->tprint(out);
4853 out->right(amount);
4854 n2->tprint(out);
4857 static void tprint_reverse_node_list(troff_output_file *out, node *n)
4859 if (n == 0)
4860 return;
4861 tprint_reverse_node_list(out, n->next);
4862 n->tprint(out);
4865 void dbreak_node::tprint(troff_output_file *out)
4867 tprint_reverse_node_list(out, none);
4870 void composite_node::tprint(troff_output_file *out)
4872 hunits bold_offset;
4873 int is_bold = tf->get_bold(&bold_offset);
4874 hunits track_kern = tf->get_track_kern();
4875 hunits constant_space;
4876 int is_constant_spaced = tf->get_constant_space(&constant_space);
4877 hunits x = H0;
4878 if (is_constant_spaced) {
4879 x = constant_space;
4880 for (node *tem = n; tem; tem = tem->next)
4881 x -= tem->width();
4882 if (is_bold)
4883 x -= bold_offset;
4884 hunits x2 = x/2;
4885 out->right(x2);
4886 x -= x2;
4888 if (is_bold) {
4889 int hpos = out->get_hpos();
4890 int vpos = out->get_vpos();
4891 tprint_reverse_node_list(out, n);
4892 out->moveto(hpos, vpos);
4893 out->right(bold_offset);
4895 tprint_reverse_node_list(out, n);
4896 if (is_constant_spaced)
4897 out->right(x);
4898 else
4899 out->right(track_kern);
4902 node *make_composite_node(charinfo *s, environment *env)
4904 int fontno = env_definite_font(env);
4905 if (fontno < 0) {
4906 error("no current font");
4907 return 0;
4909 assert(fontno < font_table_size && font_table[fontno] != 0);
4910 node *n = charinfo_to_node_list(s, env);
4911 font_size fs = env->get_font_size();
4912 int char_height = env->get_char_height();
4913 int char_slant = env->get_char_slant();
4914 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
4915 fontno);
4916 if (env->is_composite())
4917 tf = tf->get_plain();
4918 return new composite_node(n, s, tf, 0, 0, 0);
4921 node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0)
4923 int fontno = env_definite_font(env);
4924 if (fontno < 0) {
4925 error("no current font");
4926 return 0;
4928 assert(fontno < font_table_size && font_table[fontno] != 0);
4929 int fn = fontno;
4930 int found = font_table[fontno]->contains(s);
4931 if (!found) {
4932 macro *mac = s->get_macro();
4933 if (mac && s->is_fallback())
4934 return make_composite_node(s, env);
4935 if (s->numbered()) {
4936 if (!no_error_message)
4937 warning(WARN_CHAR, "can't find numbered character %1",
4938 s->get_number());
4939 return 0;
4941 special_font_list *sf = font_table[fontno]->sf;
4942 while (sf != 0 && !found) {
4943 fn = sf->n;
4944 if (font_table[fn])
4945 found = font_table[fn]->contains(s);
4946 sf = sf->next;
4948 if (!found) {
4949 symbol f = font_table[fontno]->get_name();
4950 string gl(f.contents());
4951 gl += ' ';
4952 gl += s->nm.contents();
4953 gl += '\0';
4954 charinfo *ci = get_charinfo(symbol(gl.contents()));
4955 if (ci && ci->get_macro())
4956 return make_composite_node(ci, env);
4958 if (!found) {
4959 sf = global_special_fonts;
4960 while (sf != 0 && !found) {
4961 fn = sf->n;
4962 if (font_table[fn])
4963 found = font_table[fn]->contains(s);
4964 sf = sf->next;
4967 if (!found)
4968 if (mac && s->is_special())
4969 return make_composite_node(s, env);
4970 if (!found) {
4971 for (fn = 0; fn < font_table_size; fn++)
4972 if (font_table[fn]
4973 && font_table[fn]->is_special()
4974 && font_table[fn]->contains(s)) {
4975 found = 1;
4976 break;
4979 if (!found) {
4980 if (!no_error_message && s->first_time_not_found()) {
4981 unsigned char input_code = s->get_ascii_code();
4982 if (input_code != 0) {
4983 if (csgraph(input_code))
4984 warning(WARN_CHAR, "can't find character `%1'", input_code);
4985 else
4986 warning(WARN_CHAR, "can't find character with input code %1",
4987 int(input_code));
4989 else if (s->nm.contents()) {
4990 const char *nm = s->nm.contents();
4991 const char *backslash = (nm[1] == 0) ? "\\" : "";
4992 warning(WARN_CHAR, "can't find special character `%1%2'",
4993 backslash, nm);
4996 return 0;
4999 font_size fs = env->get_font_size();
5000 int char_height = env->get_char_height();
5001 int char_slant = env->get_char_slant();
5002 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn);
5003 if (env->is_composite())
5004 tf = tf->get_plain();
5005 color *gcol = env->get_glyph_color();
5006 color *fcol = env->get_fill_color();
5007 return new glyph_node(s, tf, gcol, fcol, 0, 0);
5010 node *make_node(charinfo *ci, environment *env)
5012 switch (ci->get_special_translation()) {
5013 case charinfo::TRANSLATE_SPACE:
5014 return new space_char_hmotion_node(env->get_space_width(),
5015 env->get_fill_color());
5016 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
5017 return new unbreakable_space_node(env->get_space_width(),
5018 env->get_fill_color());
5019 case charinfo::TRANSLATE_DUMMY:
5020 return new dummy_node;
5021 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
5022 error("translation to \\% ignored in this context");
5023 break;
5025 charinfo *tem = ci->get_translation();
5026 if (tem)
5027 ci = tem;
5028 macro *mac = ci->get_macro();
5029 if (mac && ci->is_normal())
5030 return make_composite_node(ci, env);
5031 else
5032 return make_glyph_node(ci, env);
5035 int character_exists(charinfo *ci, environment *env)
5037 if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
5038 return 1;
5039 charinfo *tem = ci->get_translation();
5040 if (tem)
5041 ci = tem;
5042 if (ci->get_macro())
5043 return 1;
5044 node *nd = make_glyph_node(ci, env, 1);
5045 if (nd) {
5046 delete nd;
5047 return 1;
5049 return 0;
5052 node *node::add_char(charinfo *ci, environment *env,
5053 hunits *widthp, int *spacep, node **glyph_comp_np)
5055 node *res;
5056 switch (ci->get_special_translation()) {
5057 case charinfo::TRANSLATE_SPACE:
5058 res = new space_char_hmotion_node(env->get_space_width(),
5059 env->get_fill_color(), this);
5060 *widthp += res->width();
5061 return res;
5062 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
5063 res = new unbreakable_space_node(env->get_space_width(),
5064 env->get_fill_color(), this);
5065 res->freeze_space();
5066 *widthp += res->width();
5067 *spacep += res->nspaces();
5068 return res;
5069 case charinfo::TRANSLATE_DUMMY:
5070 return new dummy_node(this);
5071 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
5072 return add_discretionary_hyphen();
5074 charinfo *tem = ci->get_translation();
5075 if (tem)
5076 ci = tem;
5077 macro *mac = ci->get_macro();
5078 if (mac && ci->is_normal()) {
5079 res = make_composite_node(ci, env);
5080 if (res) {
5081 res->next = this;
5082 *widthp += res->width();
5083 if (glyph_comp_np)
5084 *glyph_comp_np = res;
5086 else {
5087 if (glyph_comp_np)
5088 *glyph_comp_np = res;
5089 return this;
5092 else {
5093 node *gn = make_glyph_node(ci, env);
5094 if (gn == 0)
5095 return this;
5096 else {
5097 hunits old_width = width();
5098 node *p = gn->merge_self(this);
5099 if (p == 0) {
5100 *widthp += gn->width();
5101 gn->next = this;
5102 res = gn;
5104 else {
5105 *widthp += p->width() - old_width;
5106 res = p;
5108 if (glyph_comp_np)
5109 *glyph_comp_np = res;
5112 int break_code = 0;
5113 if (ci->can_break_before())
5114 break_code = 1;
5115 if (ci->can_break_after())
5116 break_code |= 2;
5117 if (ci->ignore_hcodes())
5118 break_code |= 4;
5119 if (break_code) {
5120 node *next1 = res->next;
5121 res->next = 0;
5122 res = new break_char_node(res, break_code, env->get_fill_color(), next1);
5124 return res;
5127 inline
5128 int same_node(node *n1, node *n2)
5130 if (n1 != 0) {
5131 if (n2 != 0)
5132 return n1->type() == n2->type() && n1->same(n2);
5133 else
5134 return 0;
5136 else
5137 return n2 == 0;
5140 int same_node_list(node *n1, node *n2)
5142 while (n1 && n2) {
5143 if (n1->type() != n2->type() || !n1->same(n2))
5144 return 0;
5145 n1 = n1->next;
5146 n2 = n2->next;
5148 return !n1 && !n2;
5151 int extra_size_node::same(node *nd)
5153 return n == ((extra_size_node *)nd)->n;
5156 const char *extra_size_node::type()
5158 return "extra_size_node";
5161 int extra_size_node::force_tprint()
5163 return 0;
5166 int extra_size_node::is_tag()
5168 return 0;
5171 int vertical_size_node::same(node *nd)
5173 return n == ((vertical_size_node *)nd)->n;
5176 const char *vertical_size_node::type()
5178 return "vertical_size_node";
5181 int vertical_size_node::set_unformat_flag()
5183 return 0;
5186 int vertical_size_node::force_tprint()
5188 return 0;
5191 int vertical_size_node::is_tag()
5193 return 0;
5196 int hmotion_node::same(node *nd)
5198 return n == ((hmotion_node *)nd)->n
5199 && col == ((hmotion_node *)nd)->col;
5202 const char *hmotion_node::type()
5204 return "hmotion_node";
5207 int hmotion_node::set_unformat_flag()
5209 unformat = 1;
5210 return 1;
5213 int hmotion_node::force_tprint()
5215 return 0;
5218 int hmotion_node::is_tag()
5220 return 0;
5223 node *hmotion_node::add_self(node *nd, hyphen_list **p)
5225 next = nd;
5226 hyphen_list *pp = *p;
5227 *p = (*p)->next;
5228 delete pp;
5229 return this;
5232 hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail, int *)
5234 return new hyphen_list(0, tail);
5237 int space_char_hmotion_node::same(node *nd)
5239 return n == ((space_char_hmotion_node *)nd)->n
5240 && col == ((space_char_hmotion_node *)nd)->col;
5243 const char *space_char_hmotion_node::type()
5245 return "space_char_hmotion_node";
5248 int space_char_hmotion_node::force_tprint()
5250 return 0;
5253 int space_char_hmotion_node::is_tag()
5255 return 0;
5258 node *space_char_hmotion_node::add_self(node *nd, hyphen_list **p)
5260 next = nd;
5261 hyphen_list *pp = *p;
5262 *p = (*p)->next;
5263 delete pp;
5264 return this;
5267 hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail,
5268 int *)
5270 return new hyphen_list(0, tail);
5273 int vmotion_node::same(node *nd)
5275 return n == ((vmotion_node *)nd)->n
5276 && col == ((vmotion_node *)nd)->col;
5279 const char *vmotion_node::type()
5281 return "vmotion_node";
5284 int vmotion_node::force_tprint()
5286 return 0;
5289 int vmotion_node::is_tag()
5291 return 0;
5294 int hline_node::same(node *nd)
5296 return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n);
5299 const char *hline_node::type()
5301 return "hline_node";
5304 int hline_node::force_tprint()
5306 return 0;
5309 int hline_node::is_tag()
5311 return 0;
5314 int vline_node::same(node *nd)
5316 return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n);
5319 const char *vline_node::type()
5321 return "vline_node";
5324 int vline_node::force_tprint()
5326 return 0;
5329 int vline_node::is_tag()
5331 return 0;
5334 int dummy_node::same(node * /*nd*/)
5336 return 1;
5339 const char *dummy_node::type()
5341 return "dummy_node";
5344 int dummy_node::force_tprint()
5346 return 0;
5349 int dummy_node::is_tag()
5351 return 0;
5354 int transparent_dummy_node::same(node * /*nd*/)
5356 return 1;
5359 const char *transparent_dummy_node::type()
5361 return "transparent_dummy_node";
5364 int transparent_dummy_node::force_tprint()
5366 return 0;
5369 int transparent_dummy_node::is_tag()
5371 return 0;
5374 int transparent_dummy_node::ends_sentence()
5376 return 2;
5379 int zero_width_node::same(node *nd)
5381 return same_node_list(n, ((zero_width_node *)nd)->n);
5384 const char *zero_width_node::type()
5386 return "zero_width_node";
5389 int zero_width_node::force_tprint()
5391 return 0;
5394 int zero_width_node::is_tag()
5396 return 0;
5399 int italic_corrected_node::same(node *nd)
5401 return (x == ((italic_corrected_node *)nd)->x
5402 && same_node(n, ((italic_corrected_node *)nd)->n));
5405 const char *italic_corrected_node::type()
5407 return "italic_corrected_node";
5410 int italic_corrected_node::force_tprint()
5412 return 0;
5415 int italic_corrected_node::is_tag()
5417 return 0;
5420 left_italic_corrected_node::left_italic_corrected_node(node *xx)
5421 : node(xx), n(0)
5425 left_italic_corrected_node::left_italic_corrected_node(statem *s, int pop,
5426 node *xx)
5427 : node(xx, s, pop), n(0)
5431 left_italic_corrected_node::~left_italic_corrected_node()
5433 delete n;
5436 node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
5438 if (n == 0) {
5439 hunits lic = gn->left_italic_correction();
5440 if (!lic.is_zero()) {
5441 x = lic;
5442 n = gn;
5443 return this;
5446 else {
5447 node *nd = n->merge_glyph_node(gn);
5448 if (nd) {
5449 n = nd;
5450 x = n->left_italic_correction();
5451 return this;
5454 return 0;
5457 node *left_italic_corrected_node::copy()
5459 left_italic_corrected_node *nd =
5460 new left_italic_corrected_node(state, div_nest_level);
5461 if (n) {
5462 nd->n = n->copy();
5463 nd->x = x;
5465 return nd;
5468 void left_italic_corrected_node::tprint(troff_output_file *out)
5470 if (n) {
5471 out->right(x);
5472 n->tprint(out);
5476 const char *left_italic_corrected_node::type()
5478 return "left_italic_corrected_node";
5481 int left_italic_corrected_node::force_tprint()
5483 return 0;
5486 int left_italic_corrected_node::is_tag()
5488 return 0;
5491 int left_italic_corrected_node::same(node *nd)
5493 return (x == ((left_italic_corrected_node *)nd)->x
5494 && same_node(n, ((left_italic_corrected_node *)nd)->n));
5497 void left_italic_corrected_node::ascii_print(ascii_output_file *out)
5499 if (n)
5500 n->ascii_print(out);
5503 hunits left_italic_corrected_node::width()
5505 return n ? n->width() + x : H0;
5508 void left_italic_corrected_node::vertical_extent(vunits *minimum,
5509 vunits *maximum)
5511 if (n)
5512 n->vertical_extent(minimum, maximum);
5513 else
5514 node::vertical_extent(minimum, maximum);
5517 hunits left_italic_corrected_node::skew()
5519 return n ? n->skew() + x/2 : H0;
5522 hunits left_italic_corrected_node::subscript_correction()
5524 return n ? n->subscript_correction() : H0;
5527 hunits left_italic_corrected_node::italic_correction()
5529 return n ? n->italic_correction() : H0;
5532 int left_italic_corrected_node::ends_sentence()
5534 return n ? n->ends_sentence() : 0;
5537 int left_italic_corrected_node::overlaps_horizontally()
5539 return n ? n->overlaps_horizontally() : 0;
5542 int left_italic_corrected_node::overlaps_vertically()
5544 return n ? n->overlaps_vertically() : 0;
5547 node *left_italic_corrected_node::last_char_node()
5549 return n ? n->last_char_node() : 0;
5552 tfont *left_italic_corrected_node::get_tfont()
5554 return n ? n->get_tfont() : 0;
5557 hyphenation_type left_italic_corrected_node::get_hyphenation_type()
5559 if (n)
5560 return n->get_hyphenation_type();
5561 else
5562 return HYPHEN_MIDDLE;
5565 hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail,
5566 int *count)
5568 return n ? n->get_hyphen_list(tail, count) : tail;
5571 node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
5573 if (n) {
5574 nd = new left_italic_corrected_node(state, div_nest_level, nd);
5575 nd = n->add_self(nd, p);
5576 n = 0;
5577 delete this;
5578 return nd;
5580 else {
5581 next = nd;
5582 return this;
5586 int left_italic_corrected_node::character_type()
5588 return n ? n->character_type() : 0;
5591 int overstrike_node::same(node *nd)
5593 return same_node_list(list, ((overstrike_node *)nd)->list);
5596 const char *overstrike_node::type()
5598 return "overstrike_node";
5601 int overstrike_node::force_tprint()
5603 return 0;
5606 int overstrike_node::is_tag()
5608 return 0;
5611 node *overstrike_node::add_self(node *n, hyphen_list **p)
5613 next = n;
5614 hyphen_list *pp = *p;
5615 *p = (*p)->next;
5616 delete pp;
5617 return this;
5620 hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail, int *)
5622 return new hyphen_list(0, tail);
5625 int bracket_node::same(node *nd)
5627 return same_node_list(list, ((bracket_node *)nd)->list);
5630 const char *bracket_node::type()
5632 return "bracket_node";
5635 int bracket_node::force_tprint()
5637 return 0;
5640 int bracket_node::is_tag()
5642 return 0;
5645 int composite_node::same(node *nd)
5647 return ci == ((composite_node *)nd)->ci
5648 && same_node_list(n, ((composite_node *)nd)->n);
5651 const char *composite_node::type()
5653 return "composite_node";
5656 int composite_node::force_tprint()
5658 return 0;
5661 int composite_node::is_tag()
5663 return 0;
5666 int glyph_node::same(node *nd)
5668 return ci == ((glyph_node *)nd)->ci
5669 && tf == ((glyph_node *)nd)->tf
5670 && gcol == ((glyph_node *)nd)->gcol
5671 && fcol == ((glyph_node *)nd)->fcol;
5674 const char *glyph_node::type()
5676 return "glyph_node";
5679 int glyph_node::force_tprint()
5681 return 0;
5684 int glyph_node::is_tag()
5686 return 0;
5689 int ligature_node::same(node *nd)
5691 return (same_node(n1, ((ligature_node *)nd)->n1)
5692 && same_node(n2, ((ligature_node *)nd)->n2)
5693 && glyph_node::same(nd));
5696 const char *ligature_node::type()
5698 return "ligature_node";
5701 int ligature_node::force_tprint()
5703 return 0;
5706 int ligature_node::is_tag()
5708 return 0;
5711 int kern_pair_node::same(node *nd)
5713 return (amount == ((kern_pair_node *)nd)->amount
5714 && same_node(n1, ((kern_pair_node *)nd)->n1)
5715 && same_node(n2, ((kern_pair_node *)nd)->n2));
5718 const char *kern_pair_node::type()
5720 return "kern_pair_node";
5723 int kern_pair_node::force_tprint()
5725 return 0;
5728 int kern_pair_node::is_tag()
5730 return 0;
5733 int dbreak_node::same(node *nd)
5735 return (same_node_list(none, ((dbreak_node *)nd)->none)
5736 && same_node_list(pre, ((dbreak_node *)nd)->pre)
5737 && same_node_list(post, ((dbreak_node *)nd)->post));
5740 const char *dbreak_node::type()
5742 return "dbreak_node";
5745 int dbreak_node::force_tprint()
5747 return 0;
5750 int dbreak_node::is_tag()
5752 return 0;
5755 int break_char_node::same(node *nd)
5757 return break_code == ((break_char_node *)nd)->break_code
5758 && col == ((break_char_node *)nd)->col
5759 && same_node(ch, ((break_char_node *)nd)->ch);
5762 const char *break_char_node::type()
5764 return "break_char_node";
5767 int break_char_node::force_tprint()
5769 return 0;
5772 int break_char_node::is_tag()
5774 return 0;
5777 int line_start_node::same(node * /*nd*/)
5779 return 1;
5782 const char *line_start_node::type()
5784 return "line_start_node";
5787 int line_start_node::force_tprint()
5789 return 0;
5792 int line_start_node::is_tag()
5794 return 0;
5797 int space_node::same(node *nd)
5799 return n == ((space_node *)nd)->n
5800 && set == ((space_node *)nd)->set
5801 && col == ((space_node *)nd)->col;
5804 const char *space_node::type()
5806 return "space_node";
5809 int word_space_node::same(node *nd)
5811 return n == ((word_space_node *)nd)->n
5812 && set == ((word_space_node *)nd)->set
5813 && col == ((word_space_node *)nd)->col;
5816 const char *word_space_node::type()
5818 return "word_space_node";
5821 int word_space_node::force_tprint()
5823 return 0;
5826 int word_space_node::is_tag()
5828 return 0;
5831 void unbreakable_space_node::tprint(troff_output_file *out)
5833 out->fill_color(col);
5834 if (is_html) {
5835 // we emit the space width as a negative glyph index
5836 out->flush_tbuf();
5837 out->do_motion();
5838 out->put('N');
5839 out->put(-n.to_units());
5840 out->put('\n');
5842 out->right(n);
5845 int unbreakable_space_node::same(node *nd)
5847 return n == ((unbreakable_space_node *)nd)->n
5848 && set == ((unbreakable_space_node *)nd)->set
5849 && col == ((unbreakable_space_node *)nd)->col;
5852 const char *unbreakable_space_node::type()
5854 return "unbreakable_space_node";
5857 node *unbreakable_space_node::add_self(node *nd, hyphen_list **p)
5859 next = nd;
5860 hyphen_list *pp = *p;
5861 *p = (*p)->next;
5862 delete pp;
5863 return this;
5866 hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail, int *)
5868 return new hyphen_list(0, tail);
5871 int diverted_space_node::same(node *nd)
5873 return n == ((diverted_space_node *)nd)->n;
5876 const char *diverted_space_node::type()
5878 return "diverted_space_node";
5881 int diverted_space_node::force_tprint()
5883 return 0;
5886 int diverted_space_node::is_tag()
5888 return 0;
5891 int diverted_copy_file_node::same(node *nd)
5893 return filename == ((diverted_copy_file_node *)nd)->filename;
5896 const char *diverted_copy_file_node::type()
5898 return "diverted_copy_file_node";
5901 int diverted_copy_file_node::force_tprint()
5903 return 0;
5906 int diverted_copy_file_node::is_tag()
5908 return 0;
5911 // Grow the font_table so that its size is > n.
5913 static void grow_font_table(int n)
5915 assert(n >= font_table_size);
5916 font_info **old_font_table = font_table;
5917 int old_font_table_size = font_table_size;
5918 font_table_size = font_table_size ? (font_table_size*3)/2 : 10;
5919 if (font_table_size <= n)
5920 font_table_size = n + 10;
5921 font_table = new font_info *[font_table_size];
5922 if (old_font_table_size)
5923 memcpy(font_table, old_font_table,
5924 old_font_table_size*sizeof(font_info *));
5925 a_delete old_font_table;
5926 for (int i = old_font_table_size; i < font_table_size; i++)
5927 font_table[i] = 0;
5930 dictionary font_translation_dictionary(17);
5932 static symbol get_font_translation(symbol nm)
5934 void *p = font_translation_dictionary.lookup(nm);
5935 return p ? symbol((char *)p) : nm;
5938 dictionary font_dictionary(50);
5940 static int mount_font_no_translate(int n, symbol name, symbol external_name,
5941 int check_only = 0)
5943 assert(n >= 0);
5944 // We store the address of this char in font_dictionary to indicate
5945 // that we've previously tried to mount the font and failed.
5946 static char a_char;
5947 font *fm = 0;
5948 void *p = font_dictionary.lookup(external_name);
5949 if (p == 0) {
5950 int not_found;
5951 fm = font::load_font(external_name.contents(), &not_found, check_only);
5952 if (check_only)
5953 return fm != 0;
5954 if (!fm) {
5955 if (not_found)
5956 warning(WARN_FONT, "can't find font `%1'", external_name.contents());
5957 (void)font_dictionary.lookup(external_name, &a_char);
5958 return 0;
5960 (void)font_dictionary.lookup(name, fm);
5962 else if (p == &a_char) {
5963 #if 0
5964 error("invalid font `%1'", external_name.contents());
5965 #endif
5966 return 0;
5968 else
5969 fm = (font*)p;
5970 if (check_only)
5971 return 1;
5972 if (n >= font_table_size) {
5973 if (n - font_table_size > 1000) {
5974 error("font position too much larger than first unused position");
5975 return 0;
5977 grow_font_table(n);
5979 else if (font_table[n] != 0)
5980 delete font_table[n];
5981 font_table[n] = new font_info(name, n, external_name, fm);
5982 font_family::invalidate_fontno(n);
5983 return 1;
5986 int mount_font(int n, symbol name, symbol external_name)
5988 assert(n >= 0);
5989 name = get_font_translation(name);
5990 if (external_name.is_null())
5991 external_name = name;
5992 else
5993 external_name = get_font_translation(external_name);
5994 return mount_font_no_translate(n, name, external_name);
5997 int check_font(symbol fam, symbol name)
5999 if (check_style(name))
6000 name = concat(fam, name);
6001 return mount_font_no_translate(0, name, name, 1);
6004 int check_style(symbol s)
6006 int i = symbol_fontno(s);
6007 return i < 0 ? 0 : font_table[i]->is_style();
6010 void mount_style(int n, symbol name)
6012 assert(n >= 0);
6013 if (n >= font_table_size) {
6014 if (n - font_table_size > 1000) {
6015 error("font position too much larger than first unused position");
6016 return;
6018 grow_font_table(n);
6020 else if (font_table[n] != 0)
6021 delete font_table[n];
6022 font_table[n] = new font_info(get_font_translation(name), n, NULL_SYMBOL, 0);
6023 font_family::invalidate_fontno(n);
6026 /* global functions */
6028 void font_translate()
6030 symbol from = get_name(1);
6031 if (!from.is_null()) {
6032 symbol to = get_name();
6033 if (to.is_null() || from == to)
6034 font_translation_dictionary.remove(from);
6035 else
6036 (void)font_translation_dictionary.lookup(from, (void *)to.contents());
6038 skip_line();
6041 void font_position()
6043 int n;
6044 if (get_integer(&n)) {
6045 if (n < 0)
6046 error("negative font position");
6047 else {
6048 symbol internal_name = get_name(1);
6049 if (!internal_name.is_null()) {
6050 symbol external_name = get_long_name();
6051 mount_font(n, internal_name, external_name); // ignore error
6055 skip_line();
6058 font_family::font_family(symbol s)
6059 : map_size(10), nm(s)
6061 map = new int[map_size];
6062 for (int i = 0; i < map_size; i++)
6063 map[i] = -1;
6066 font_family::~font_family()
6068 a_delete map;
6071 int font_family::make_definite(int i)
6073 if (i >= 0) {
6074 if (i < map_size && map[i] >= 0)
6075 return map[i];
6076 else {
6077 if (i < font_table_size && font_table[i] != 0) {
6078 if (i >= map_size) {
6079 int old_map_size = map_size;
6080 int *old_map = map;
6081 map_size *= 3;
6082 map_size /= 2;
6083 if (i >= map_size)
6084 map_size = i + 10;
6085 map = new int[map_size];
6086 memcpy(map, old_map, old_map_size*sizeof(int));
6087 a_delete old_map;
6088 for (int j = old_map_size; j < map_size; j++)
6089 map[j] = -1;
6091 if (font_table[i]->is_style()) {
6092 symbol sty = font_table[i]->get_name();
6093 symbol f = concat(nm, sty);
6094 int n;
6095 // don't use symbol_fontno, because that might return a style
6096 // and because we don't want to translate the name
6097 for (n = 0; n < font_table_size; n++)
6098 if (font_table[n] != 0 && font_table[n]->is_named(f)
6099 && !font_table[n]->is_style())
6100 break;
6101 if (n >= font_table_size) {
6102 n = next_available_font_position();
6103 if (!mount_font_no_translate(n, f, f))
6104 return -1;
6106 return map[i] = n;
6108 else
6109 return map[i] = i;
6111 else
6112 return -1;
6115 else
6116 return -1;
6119 dictionary family_dictionary(5);
6121 font_family *lookup_family(symbol nm)
6123 font_family *f = (font_family *)family_dictionary.lookup(nm);
6124 if (!f) {
6125 f = new font_family(nm);
6126 (void)family_dictionary.lookup(nm, f);
6128 return f;
6131 void font_family::invalidate_fontno(int n)
6133 assert(n >= 0 && n < font_table_size);
6134 dictionary_iterator iter(family_dictionary);
6135 symbol nam;
6136 font_family *fam;
6137 while (iter.get(&nam, (void **)&fam)) {
6138 int mapsize = fam->map_size;
6139 if (n < mapsize)
6140 fam->map[n] = -1;
6141 for (int i = 0; i < mapsize; i++)
6142 if (fam->map[i] == n)
6143 fam->map[i] = -1;
6147 void style()
6149 int n;
6150 if (get_integer(&n)) {
6151 if (n < 0)
6152 error("negative font position");
6153 else {
6154 symbol internal_name = get_name(1);
6155 if (!internal_name.is_null())
6156 mount_style(n, internal_name);
6159 skip_line();
6162 static int get_fontno()
6164 int n;
6165 tok.skip();
6166 if (tok.delimiter()) {
6167 symbol s = get_name(1);
6168 if (!s.is_null()) {
6169 n = symbol_fontno(s);
6170 if (n < 0) {
6171 n = next_available_font_position();
6172 if (!mount_font(n, s))
6173 return -1;
6175 return curenv->get_family()->make_definite(n);
6178 else if (get_integer(&n)) {
6179 if (n < 0 || n >= font_table_size || font_table[n] == 0)
6180 error("bad font number");
6181 else
6182 return curenv->get_family()->make_definite(n);
6184 return -1;
6187 static int underline_fontno = 2;
6189 void underline_font()
6191 int n = get_fontno();
6192 if (n >= 0)
6193 underline_fontno = n;
6194 skip_line();
6197 int get_underline_fontno()
6199 return underline_fontno;
6202 void define_font_special_character()
6204 int n = get_fontno();
6205 if (n < 0) {
6206 skip_line();
6207 return;
6209 symbol f = font_table[n]->get_name();
6210 do_define_character(CHAR_FONT_SPECIAL, f.contents());
6213 void remove_font_special_character()
6215 int n = get_fontno();
6216 if (n < 0) {
6217 skip_line();
6218 return;
6220 symbol f = font_table[n]->get_name();
6221 while (!tok.newline() && !tok.eof()) {
6222 if (!tok.space() && !tok.tab()) {
6223 charinfo *s = tok.get_char(1);
6224 string gl(f.contents());
6225 gl += ' ';
6226 gl += s->nm.contents();
6227 gl += '\0';
6228 charinfo *ci = get_charinfo(symbol(gl.contents()));
6229 if (!ci)
6230 break;
6231 macro *m = ci->set_macro(0);
6232 if (m)
6233 delete m;
6235 tok.next();
6237 skip_line();
6240 static void read_special_fonts(special_font_list **sp)
6242 special_font_list *s = *sp;
6243 *sp = 0;
6244 while (s != 0) {
6245 special_font_list *tem = s;
6246 s = s->next;
6247 delete tem;
6249 special_font_list **p = sp;
6250 while (has_arg()) {
6251 int i = get_fontno();
6252 if (i >= 0) {
6253 special_font_list *tem = new special_font_list;
6254 tem->n = i;
6255 tem->next = 0;
6256 *p = tem;
6257 p = &(tem->next);
6262 void font_special_request()
6264 int n = get_fontno();
6265 if (n >= 0)
6266 read_special_fonts(&font_table[n]->sf);
6267 skip_line();
6270 void special_request()
6272 read_special_fonts(&global_special_fonts);
6273 skip_line();
6276 void font_zoom_request()
6278 int n = get_fontno();
6279 if (n >= 0) {
6280 if (font_table[n]->is_style())
6281 warning(WARN_FONT, "can't set zoom factor for a style");
6282 else {
6283 int zoom;
6284 if (has_arg() && get_integer(&zoom)) {
6285 if (zoom < 0)
6286 warning(WARN_FONT, "can't use negative zoom factor");
6287 else
6288 font_table[n]->set_zoom(zoom);
6290 else
6291 font_table[n]->set_zoom(0);
6294 skip_line();
6297 int next_available_font_position()
6299 int i;
6300 for (i = 1; i < font_table_size && font_table[i] != 0; i++)
6302 return i;
6305 int symbol_fontno(symbol s)
6307 s = get_font_translation(s);
6308 for (int i = 0; i < font_table_size; i++)
6309 if (font_table[i] != 0 && font_table[i]->is_named(s))
6310 return i;
6311 return -1;
6314 int is_good_fontno(int n)
6316 return n >= 0 && n < font_table_size && font_table[n] != 0;
6319 int get_bold_fontno(int n)
6321 if (n >= 0 && n < font_table_size && font_table[n] != 0) {
6322 hunits offset;
6323 if (font_table[n]->get_bold(&offset))
6324 return offset.to_units() + 1;
6325 else
6326 return 0;
6328 else
6329 return 0;
6332 hunits env_digit_width(environment *env)
6334 node *n = make_glyph_node(charset_table['0'], env);
6335 if (n) {
6336 hunits x = n->width();
6337 delete n;
6338 return x;
6340 else
6341 return H0;
6344 hunits env_space_width(environment *env)
6346 int fn = env_definite_font(env);
6347 font_size fs = env->get_font_size();
6348 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6349 return scale(fs.to_units()/3, env->get_space_size(), 12);
6350 else
6351 return font_table[fn]->get_space_width(fs, env->get_space_size());
6354 hunits env_sentence_space_width(environment *env)
6356 int fn = env_definite_font(env);
6357 font_size fs = env->get_font_size();
6358 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6359 return scale(fs.to_units()/3, env->get_sentence_space_size(), 12);
6360 else
6361 return font_table[fn]->get_space_width(fs, env->get_sentence_space_size());
6364 hunits env_half_narrow_space_width(environment *env)
6366 int fn = env_definite_font(env);
6367 font_size fs = env->get_font_size();
6368 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6369 return 0;
6370 else
6371 return font_table[fn]->get_half_narrow_space_width(fs);
6374 hunits env_narrow_space_width(environment *env)
6376 int fn = env_definite_font(env);
6377 font_size fs = env->get_font_size();
6378 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6379 return 0;
6380 else
6381 return font_table[fn]->get_narrow_space_width(fs);
6384 void bold_font()
6386 int n = get_fontno();
6387 if (n >= 0) {
6388 if (has_arg()) {
6389 if (tok.delimiter()) {
6390 int f = get_fontno();
6391 if (f >= 0) {
6392 units offset;
6393 if (has_arg() && get_number(&offset, 'u') && offset >= 1)
6394 font_table[f]->set_conditional_bold(n, hunits(offset - 1));
6395 else
6396 font_table[f]->conditional_unbold(n);
6399 else {
6400 units offset;
6401 if (get_number(&offset, 'u') && offset >= 1)
6402 font_table[n]->set_bold(hunits(offset - 1));
6403 else
6404 font_table[n]->unbold();
6407 else
6408 font_table[n]->unbold();
6410 skip_line();
6413 track_kerning_function::track_kerning_function() : non_zero(0)
6417 track_kerning_function::track_kerning_function(int min_s, hunits min_a,
6418 int max_s, hunits max_a)
6419 : non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s),
6420 max_amount(max_a)
6424 int track_kerning_function::operator==(const track_kerning_function &tk)
6426 if (non_zero)
6427 return (tk.non_zero
6428 && min_size == tk.min_size
6429 && min_amount == tk.min_amount
6430 && max_size == tk.max_size
6431 && max_amount == tk.max_amount);
6432 else
6433 return !tk.non_zero;
6436 int track_kerning_function::operator!=(const track_kerning_function &tk)
6438 if (non_zero)
6439 return (!tk.non_zero
6440 || min_size != tk.min_size
6441 || min_amount != tk.min_amount
6442 || max_size != tk.max_size
6443 || max_amount != tk.max_amount);
6444 else
6445 return tk.non_zero;
6448 hunits track_kerning_function::compute(int size)
6450 if (non_zero) {
6451 if (max_size <= min_size)
6452 return min_amount;
6453 else if (size <= min_size)
6454 return min_amount;
6455 else if (size >= max_size)
6456 return max_amount;
6457 else
6458 return (scale(max_amount, size - min_size, max_size - min_size)
6459 + scale(min_amount, max_size - size, max_size - min_size));
6461 else
6462 return H0;
6465 void track_kern()
6467 int n = get_fontno();
6468 if (n >= 0) {
6469 int min_s, max_s;
6470 hunits min_a, max_a;
6471 if (has_arg()
6472 && get_number(&min_s, 'z')
6473 && get_hunits(&min_a, 'p')
6474 && get_number(&max_s, 'z')
6475 && get_hunits(&max_a, 'p')) {
6476 track_kerning_function tk(min_s, min_a, max_s, max_a);
6477 font_table[n]->set_track_kern(tk);
6479 else {
6480 track_kerning_function tk;
6481 font_table[n]->set_track_kern(tk);
6484 skip_line();
6487 void constant_space()
6489 int n = get_fontno();
6490 if (n >= 0) {
6491 int x, y;
6492 if (!has_arg() || !get_integer(&x))
6493 font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
6494 else {
6495 if (!has_arg() || !get_number(&y, 'z'))
6496 font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
6497 else
6498 font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
6499 scale(y*x,
6500 units_per_inch,
6501 36*72*sizescale));
6504 skip_line();
6507 void ligature()
6509 int lig;
6510 if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2)
6511 global_ligature_mode = lig;
6512 else
6513 global_ligature_mode = 1;
6514 skip_line();
6517 void kern_request()
6519 int k;
6520 if (has_arg() && get_integer(&k))
6521 global_kern_mode = k != 0;
6522 else
6523 global_kern_mode = 1;
6524 skip_line();
6527 void set_soft_hyphen_char()
6529 soft_hyphen_char = get_optional_char();
6530 if (!soft_hyphen_char)
6531 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
6532 skip_line();
6535 void init_output()
6537 if (suppress_output_flag)
6538 the_output = new suppress_output_file;
6539 else if (ascii_output_flag)
6540 the_output = new ascii_output_file;
6541 else
6542 the_output = new troff_output_file;
6545 class next_available_font_position_reg
6546 : public reg
6548 public:
6549 const char *get_string();
6552 const char *next_available_font_position_reg::get_string()
6554 return i_to_a(next_available_font_position());
6557 class printing_reg
6558 : public reg
6560 public:
6561 const char *get_string();
6564 const char *printing_reg::get_string()
6566 if (the_output)
6567 return the_output->is_printing() ? "1" : "0";
6568 else
6569 return "0";
6572 void init_node_requests() // TODO move static inits in here?!!
6574 init_request("bd", bold_font);
6575 init_request("cs", constant_space);
6576 init_request("fp", font_position);
6577 init_request("fschar", define_font_special_character);
6578 init_request("fspecial", font_special_request);
6579 init_request("fzoom", font_zoom_request);
6580 init_request("ftr", font_translate);
6581 init_request("kern", kern_request);
6582 init_request("lg", ligature);
6583 init_request("rfschar", remove_font_special_character);
6584 init_request("shc", set_soft_hyphen_char);
6585 init_request("special", special_request);
6586 init_request("sty", style);
6587 init_request("tkf", track_kern);
6588 init_request("uf", underline_font);
6589 number_reg_dictionary.define(".fp", new next_available_font_position_reg);
6590 number_reg_dictionary.define(".kern",
6591 new constant_int_reg(&global_kern_mode));
6592 number_reg_dictionary.define(".lg",
6593 new constant_int_reg(&global_ligature_mode));
6594 number_reg_dictionary.define(".P", new printing_reg);
6595 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
6598 // s-it2-mode