* src/devics/grodvi/dvi.cc: Replace _setmode() (for MSC) with
[s-roff.git] / src / roff / troff / node.cc
blobd53bf333ae1198bf0790a221e8550bd80e48b6d3
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff 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 groff 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21 #ifdef HAVE_UNISTD_H
22 #include <unistd.h>
23 #endif
25 #include "troff.h"
26 #include "symbol.h"
27 #include "dictionary.h"
28 #include "hvunits.h"
29 #include "env.h"
30 #include "request.h"
31 #include "node.h"
32 #include "token.h"
33 #include "charinfo.h"
34 #include "font.h"
35 #include "reg.h"
37 #include "nonposix.h"
39 #ifdef _POSIX_VERSION
41 #include <sys/wait.h>
43 #else /* not _POSIX_VERSION */
45 /* traditional Unix */
47 #define WIFEXITED(s) (((s) & 0377) == 0)
48 #define WEXITSTATUS(s) (((s) >> 8) & 0377)
49 #define WTERMSIG(s) ((s) & 0177)
50 #define WIFSTOPPED(s) (((s) & 0377) == 0177)
51 #define WSTOPSIG(s) (((s) >> 8) & 0377)
52 #define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
54 #endif /* not _POSIX_VERSION */
56 #define STORE_WIDTH 1
58 symbol HYPHEN_SYMBOL("hy");
60 // Character used when a hyphen is inserted at a line break.
61 static charinfo *soft_hyphen_char;
63 enum constant_space_type {
64 CONSTANT_SPACE_NONE,
65 CONSTANT_SPACE_RELATIVE,
66 CONSTANT_SPACE_ABSOLUTE
69 struct special_font_list {
70 int n;
71 special_font_list *next;
74 special_font_list *global_special_fonts;
75 static int global_ligature_mode = 1;
76 static int global_kern_mode = 1;
78 class track_kerning_function {
79 int non_zero;
80 units min_size;
81 hunits min_amount;
82 units max_size;
83 hunits max_amount;
84 public:
85 track_kerning_function();
86 track_kerning_function(units, hunits, units, hunits);
87 int operator==(const track_kerning_function &);
88 int operator!=(const track_kerning_function &);
89 hunits compute(int point_size);
92 // embolden fontno when this is the current font
94 struct conditional_bold {
95 conditional_bold *next;
96 int fontno;
97 hunits offset;
98 conditional_bold(int, hunits, conditional_bold * = 0);
101 struct tfont;
103 class font_info {
104 tfont *last_tfont;
105 int number;
106 font_size last_size;
107 int last_height;
108 int last_slant;
109 symbol internal_name;
110 symbol external_name;
111 font *fm;
112 char is_bold;
113 hunits bold_offset;
114 track_kerning_function track_kern;
115 constant_space_type is_constant_spaced;
116 units constant_space;
117 int last_ligature_mode;
118 int last_kern_mode;
119 conditional_bold *cond_bold_list;
120 void flush();
121 public:
122 special_font_list *sf;
124 font_info(symbol nm, int n, symbol enm, font *f);
125 int contains(charinfo *);
126 void set_bold(hunits);
127 void unbold();
128 void set_conditional_bold(int, hunits);
129 void conditional_unbold(int);
130 void set_track_kern(track_kerning_function &);
131 void set_constant_space(constant_space_type, units = 0);
132 int is_named(symbol);
133 symbol get_name();
134 tfont *get_tfont(font_size, int, int, int);
135 hunits get_space_width(font_size, int);
136 hunits get_narrow_space_width(font_size);
137 hunits get_half_narrow_space_width(font_size);
138 int get_bold(hunits *);
139 int is_special();
140 int is_style();
143 class tfont_spec {
144 protected:
145 symbol name;
146 int input_position;
147 font *fm;
148 font_size size;
149 char is_bold;
150 char is_constant_spaced;
151 int ligature_mode;
152 int kern_mode;
153 hunits bold_offset;
154 hunits track_kern; // add this to the width
155 hunits constant_space_width;
156 int height;
157 int slant;
158 public:
159 tfont_spec(symbol nm, int pos, font *, font_size, int, int);
160 tfont_spec(const tfont_spec &spec) { *this = spec; }
161 tfont_spec plain();
162 int operator==(const tfont_spec &);
163 friend tfont *font_info::get_tfont(font_size fs, int, int, int);
166 class tfont : public tfont_spec {
167 static tfont *tfont_list;
168 tfont *next;
169 tfont *plain_version;
170 public:
171 tfont(tfont_spec &);
172 int contains(charinfo *);
173 hunits get_width(charinfo *c);
174 int get_bold(hunits *);
175 int get_constant_space(hunits *);
176 hunits get_track_kern();
177 tfont *get_plain();
178 font_size get_size();
179 symbol get_name();
180 charinfo *get_lig(charinfo *c1, charinfo *c2);
181 int get_kern(charinfo *c1, charinfo *c2, hunits *res);
182 int get_input_position();
183 int get_character_type(charinfo *);
184 int get_height();
185 int get_slant();
186 vunits get_char_height(charinfo *);
187 vunits get_char_depth(charinfo *);
188 hunits get_char_skew(charinfo *);
189 hunits get_italic_correction(charinfo *);
190 hunits get_left_italic_correction(charinfo *);
191 hunits get_subscript_correction(charinfo *);
192 friend tfont *make_tfont(tfont_spec &);
195 inline int env_definite_font(environment *env)
197 return env->get_family()->make_definite(env->get_font());
200 /* font_info functions */
202 static font_info **font_table = 0;
203 static int font_table_size = 0;
205 font_info::font_info(symbol nm, int n, symbol enm, font *f)
206 : last_tfont(0), number(n), last_size(0),
207 internal_name(nm), external_name(enm), fm(f),
208 is_bold(0), is_constant_spaced(CONSTANT_SPACE_NONE), last_ligature_mode(1),
209 last_kern_mode(1), cond_bold_list(0), sf(0)
213 inline int font_info::contains(charinfo *ci)
215 return fm != 0 && fm->contains(ci->get_index());
218 inline int font_info::is_special()
220 return fm != 0 && fm->is_special();
223 inline int font_info::is_style()
225 return fm == 0;
228 // this is the current_font, fontno is where we found the character,
229 // presumably a special font
231 tfont *font_info::get_tfont(font_size fs, int height, int slant, int fontno)
233 if (last_tfont == 0 || fs != last_size
234 || height != last_height || slant != last_slant
235 || global_ligature_mode != last_ligature_mode
236 || global_kern_mode != last_kern_mode
237 || fontno != number) {
238 font_info *f = font_table[fontno];
239 tfont_spec spec(f->external_name, f->number, f->fm, fs, height, slant);
240 for (conditional_bold *p = cond_bold_list; p; p = p->next)
241 if (p->fontno == fontno) {
242 spec.is_bold = 1;
243 spec.bold_offset = p->offset;
244 break;
246 if (!spec.is_bold && is_bold) {
247 spec.is_bold = 1;
248 spec.bold_offset = bold_offset;
250 spec.track_kern = track_kern.compute(fs.to_scaled_points());
251 spec.ligature_mode = global_ligature_mode;
252 spec.kern_mode = global_kern_mode;
253 switch (is_constant_spaced) {
254 case CONSTANT_SPACE_NONE:
255 break;
256 case CONSTANT_SPACE_ABSOLUTE:
257 spec.is_constant_spaced = 1;
258 spec.constant_space_width = constant_space;
259 break;
260 case CONSTANT_SPACE_RELATIVE:
261 spec.is_constant_spaced = 1;
262 spec.constant_space_width
263 = scale(constant_space*fs.to_scaled_points(),
264 units_per_inch,
265 36*72*sizescale);
266 break;
267 default:
268 assert(0);
270 if (fontno != number)
271 return make_tfont(spec);
272 last_tfont = make_tfont(spec);
273 last_size = fs;
274 last_height = height;
275 last_slant = slant;
276 last_ligature_mode = global_ligature_mode;
277 last_kern_mode = global_kern_mode;
279 return last_tfont;
282 int font_info::get_bold(hunits *res)
284 if (is_bold) {
285 *res = bold_offset;
286 return 1;
288 else
289 return 0;
292 void font_info::unbold()
294 if (is_bold) {
295 is_bold = 0;
296 flush();
300 void font_info::set_bold(hunits offset)
302 if (!is_bold || offset != bold_offset) {
303 is_bold = 1;
304 bold_offset = offset;
305 flush();
309 void font_info::set_conditional_bold(int fontno, hunits offset)
311 for (conditional_bold *p = cond_bold_list; p; p = p->next)
312 if (p->fontno == fontno) {
313 if (offset != p->offset) {
314 p->offset = offset;
315 flush();
317 return;
319 cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
322 conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
323 : next(x), fontno(f), offset(h)
327 void font_info::conditional_unbold(int fontno)
329 for (conditional_bold **p = &cond_bold_list; *p; p = &(*p)->next)
330 if ((*p)->fontno == fontno) {
331 conditional_bold *tem = *p;
332 *p = (*p)->next;
333 delete tem;
334 flush();
335 return;
339 void font_info::set_constant_space(constant_space_type type, units x)
341 if (type != is_constant_spaced
342 || (type != CONSTANT_SPACE_NONE && x != constant_space)) {
343 flush();
344 is_constant_spaced = type;
345 constant_space = x;
349 void font_info::set_track_kern(track_kerning_function &tk)
351 if (track_kern != tk) {
352 track_kern = tk;
353 flush();
357 void font_info::flush()
359 last_tfont = 0;
362 int font_info::is_named(symbol s)
364 return internal_name == s;
367 symbol font_info::get_name()
369 return internal_name;
372 hunits font_info::get_space_width(font_size fs, int space_size)
374 if (is_constant_spaced == CONSTANT_SPACE_NONE)
375 return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
376 space_size, 12);
377 else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
378 return constant_space;
379 else
380 return scale(constant_space*fs.to_scaled_points(),
381 units_per_inch, 36*72*sizescale);
384 hunits font_info::get_narrow_space_width(font_size fs)
386 charinfo *ci = get_charinfo(symbol("|"));
387 if (fm->contains(ci->get_index()))
388 return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
389 else
390 return hunits(fs.to_units()/6);
393 hunits font_info::get_half_narrow_space_width(font_size fs)
395 charinfo *ci = get_charinfo(symbol("^"));
396 if (fm->contains(ci->get_index()))
397 return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
398 else
399 return hunits(fs.to_units()/12);
402 /* tfont */
404 tfont_spec::tfont_spec(symbol nm, int n, font *f,
405 font_size s, int h, int sl)
406 : name(nm), input_position(n), fm(f), size(s),
407 is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1),
408 height(h), slant(sl)
410 if (height == size.to_scaled_points())
411 height = 0;
414 int tfont_spec::operator==(const tfont_spec &spec)
416 if (fm == spec.fm
417 && size == spec.size
418 && input_position == spec.input_position
419 && name == spec.name
420 && height == spec.height
421 && slant == spec.slant
422 && (is_bold
423 ? (spec.is_bold && bold_offset == spec.bold_offset)
424 : !spec.is_bold)
425 && track_kern == spec.track_kern
426 && (is_constant_spaced
427 ? (spec.is_constant_spaced
428 && constant_space_width == spec.constant_space_width)
429 : !spec.is_constant_spaced)
430 && ligature_mode == spec.ligature_mode
431 && kern_mode == spec.kern_mode)
432 return 1;
433 else
434 return 0;
437 tfont_spec tfont_spec::plain()
439 return tfont_spec(name, input_position, fm, size, height, slant);
442 hunits tfont::get_width(charinfo *c)
444 if (is_constant_spaced)
445 return constant_space_width;
446 else if (is_bold)
447 return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
448 + track_kern + bold_offset);
449 else
450 return (hunits(fm->get_width(c->get_index(), size.to_scaled_points())) + track_kern);
453 vunits tfont::get_char_height(charinfo *c)
455 vunits v = fm->get_height(c->get_index(), size.to_scaled_points());
456 if (height != 0 && height != size.to_scaled_points())
457 return scale(v, height, size.to_scaled_points());
458 else
459 return v;
462 vunits tfont::get_char_depth(charinfo *c)
464 vunits v = fm->get_depth(c->get_index(), size.to_scaled_points());
465 if (height != 0 && height != size.to_scaled_points())
466 return scale(v, height, size.to_scaled_points());
467 else
468 return v;
471 hunits tfont::get_char_skew(charinfo *c)
473 return hunits(fm->get_skew(c->get_index(), size.to_scaled_points(), slant));
476 hunits tfont::get_italic_correction(charinfo *c)
478 return hunits(fm->get_italic_correction(c->get_index(), size.to_scaled_points()));
481 hunits tfont::get_left_italic_correction(charinfo *c)
483 return hunits(fm->get_left_italic_correction(c->get_index(),
484 size.to_scaled_points()));
487 hunits tfont::get_subscript_correction(charinfo *c)
489 return hunits(fm->get_subscript_correction(c->get_index(),
490 size.to_scaled_points()));
493 inline int tfont::get_input_position()
495 return input_position;
498 inline int tfont::contains(charinfo *ci)
500 return fm->contains(ci->get_index());
503 inline int tfont::get_character_type(charinfo *ci)
505 return fm->get_character_type(ci->get_index());
508 inline int tfont::get_bold(hunits *res)
510 if (is_bold) {
511 *res = bold_offset;
512 return 1;
514 else
515 return 0;
518 inline int tfont::get_constant_space(hunits *res)
520 if (is_constant_spaced) {
521 *res = constant_space_width;
522 return 1;
524 else
525 return 0;
528 inline hunits tfont::get_track_kern()
530 return track_kern;
533 inline tfont *tfont::get_plain()
535 return plain_version;
538 inline font_size tfont::get_size()
540 return size;
543 inline symbol tfont::get_name()
545 return name;
548 inline int tfont::get_height()
550 return height;
553 inline int tfont::get_slant()
555 return slant;
558 symbol SYMBOL_ff("ff");
559 symbol SYMBOL_fi("fi");
560 symbol SYMBOL_fl("fl");
561 symbol SYMBOL_Fi("Fi");
562 symbol SYMBOL_Fl("Fl");
564 charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
566 if (ligature_mode == 0)
567 return 0;
568 charinfo *ci = 0;
569 if (c1->get_ascii_code() == 'f') {
570 switch (c2->get_ascii_code()) {
571 case 'f':
572 if (fm->has_ligature(font::LIG_ff))
573 ci = get_charinfo(SYMBOL_ff);
574 break;
575 case 'i':
576 if (fm->has_ligature(font::LIG_fi))
577 ci = get_charinfo(SYMBOL_fi);
578 break;
579 case 'l':
580 if (fm->has_ligature(font::LIG_fl))
581 ci = get_charinfo(SYMBOL_fl);
582 break;
585 else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
586 switch (c2->get_ascii_code()) {
587 case 'i':
588 if (fm->has_ligature(font::LIG_ffi))
589 ci = get_charinfo(SYMBOL_Fi);
590 break;
591 case 'l':
592 if (fm->has_ligature(font::LIG_ffl))
593 ci = get_charinfo(SYMBOL_Fl);
594 break;
597 if (ci != 0 && fm->contains(ci->get_index()))
598 return ci;
599 return 0;
602 inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res)
604 if (kern_mode == 0)
605 return 0;
606 else {
607 int n = fm->get_kern(c1->get_index(),
608 c2->get_index(),
609 size.to_scaled_points());
610 if (n) {
611 *res = hunits(n);
612 return 1;
614 else
615 return 0;
619 tfont *make_tfont(tfont_spec &spec)
621 for (tfont *p = tfont::tfont_list; p; p = p->next)
622 if (*p == spec)
623 return p;
624 return new tfont(spec);
627 tfont *tfont::tfont_list = 0;
629 tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
631 next = tfont_list;
632 tfont_list = this;
633 tfont_spec plain_spec = plain();
634 tfont *p;
635 for (p = tfont_list; p; p = p->next)
636 if (*p == plain_spec) {
637 plain_version = p;
638 break;
640 if (!p)
641 plain_version = new tfont(plain_spec);
644 /* output_file */
646 class real_output_file : public output_file {
647 #ifndef POPEN_MISSING
648 int piped;
649 #endif
650 int printing;
651 virtual void really_transparent_char(unsigned char) = 0;
652 virtual void really_print_line(hunits x, vunits y, node *n,
653 vunits before, vunits after) = 0;
654 virtual void really_begin_page(int pageno, vunits page_length) = 0;
655 virtual void really_copy_file(hunits x, vunits y, const char *filename);
656 virtual void really_put_filename(const char *filename);
657 protected:
658 FILE *fp;
659 public:
660 real_output_file();
661 ~real_output_file();
662 void flush();
663 void transparent_char(unsigned char);
664 void print_line(hunits x, vunits y, node *n, vunits before, vunits after);
665 void begin_page(int pageno, vunits page_length);
666 void put_filename(const char *filename);
667 int is_printing();
668 void copy_file(hunits x, vunits y, const char *filename);
671 class suppress_output_file : public real_output_file {
672 public:
673 suppress_output_file();
674 void really_transparent_char(unsigned char);
675 void really_print_line(hunits x, vunits y, node *n, vunits, vunits);
676 void really_begin_page(int pageno, vunits page_length);
679 class ascii_output_file : public real_output_file {
680 public:
681 ascii_output_file();
682 void really_transparent_char(unsigned char);
683 void really_print_line(hunits x, vunits y, node *n, vunits, vunits);
684 void really_begin_page(int pageno, vunits page_length);
685 void outc(unsigned char c);
686 void outs(const char *s);
689 void ascii_output_file::outc(unsigned char c)
691 fputc(c, fp);
694 void ascii_output_file::outs(const char *s)
696 fputc('<', fp);
697 if (s)
698 fputs(s, fp);
699 fputc('>', fp);
702 struct hvpair;
704 class troff_output_file : public real_output_file {
705 units hpos;
706 units vpos;
707 units output_vpos;
708 units output_hpos;
709 int force_motion;
710 int current_size;
711 int current_slant;
712 int current_height;
713 tfont *current_tfont;
714 int current_font_number;
715 symbol *font_position;
716 int nfont_positions;
717 enum { TBUF_SIZE = 256 };
718 char tbuf[TBUF_SIZE];
719 int tbuf_len;
720 int tbuf_kern;
721 int begun_page;
722 void do_motion();
723 void put(char c);
724 void put(unsigned char c);
725 void put(int i);
726 void put(const char *s);
727 void set_font(tfont *tf);
728 void flush_tbuf();
729 public:
730 troff_output_file();
731 ~troff_output_file();
732 void trailer(vunits page_length);
733 void put_char(charinfo *ci, tfont *tf);
734 void put_char_width(charinfo *ci, tfont *tf, hunits w, hunits k);
735 void right(hunits);
736 void down(vunits);
737 void moveto(hunits, vunits);
738 void start_special();
739 void special_char(unsigned char c);
740 void end_special();
741 void word_marker();
742 void really_transparent_char(unsigned char c);
743 void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after);
744 void really_begin_page(int pageno, vunits page_length);
745 void really_copy_file(hunits x, vunits y, const char *filename);
746 void really_put_filename(const char *filename);
747 void draw(char, hvpair *, int, font_size);
748 int get_hpos() { return hpos; }
749 int get_vpos() { return vpos; }
752 static void put_string(const char *s, FILE *fp)
754 for (; *s != '\0'; ++s)
755 putc(*s, fp);
758 inline void troff_output_file::put(char c)
760 putc(c, fp);
763 inline void troff_output_file::put(unsigned char c)
765 putc(c, fp);
768 inline void troff_output_file::put(const char *s)
770 put_string(s, fp);
773 inline void troff_output_file::put(int i)
775 put_string(i_to_a(i), fp);
778 void troff_output_file::start_special()
780 flush_tbuf();
781 do_motion();
782 put("x X ");
785 void troff_output_file::special_char(unsigned char c)
787 put(c);
788 if (c == '\n')
789 put('+');
792 void troff_output_file::end_special()
794 put('\n');
797 inline void troff_output_file::moveto(hunits h, vunits v)
799 hpos = h.to_units();
800 vpos = v.to_units();
803 void troff_output_file::really_print_line(hunits x, vunits y, node *n,
804 vunits before, vunits after)
806 moveto(x, y);
807 while (n != 0) {
808 n->tprint(this);
809 n = n->next;
811 flush_tbuf();
812 // This ensures that transparent throughput will have a more predictable
813 // position.
814 do_motion();
815 force_motion = 1;
816 hpos = 0;
817 put('n');
818 put(before.to_units());
819 put(' ');
820 put(after.to_units());
821 put('\n');
824 inline void troff_output_file::word_marker()
826 flush_tbuf();
827 put('w');
830 inline void troff_output_file::right(hunits n)
832 hpos += n.to_units();
835 inline void troff_output_file::down(vunits n)
837 vpos += n.to_units();
840 void troff_output_file::do_motion()
842 if (force_motion) {
843 put('V');
844 put(vpos);
845 put('\n');
846 put('H');
847 put(hpos);
848 put('\n');
850 else {
851 if (hpos != output_hpos) {
852 units n = hpos - output_hpos;
853 if (n > 0 && n < hpos) {
854 put('h');
855 put(n);
857 else {
858 put('H');
859 put(hpos);
861 put('\n');
863 if (vpos != output_vpos) {
864 units n = vpos - output_vpos;
865 if (n > 0 && n < vpos) {
866 put('v');
867 put(n);
869 else {
870 put('V');
871 put(vpos);
873 put('\n');
876 output_vpos = vpos;
877 output_hpos = hpos;
878 force_motion = 0;
881 void troff_output_file::flush_tbuf()
883 if (tbuf_len == 0)
884 return;
885 if (tbuf_kern == 0)
886 put('t');
887 else {
888 put('u');
889 put(tbuf_kern);
890 put(' ');
892 for (int i = 0; i < tbuf_len; i++)
893 put(tbuf[i]);
894 put('\n');
895 tbuf_len = 0;
898 void troff_output_file::put_char_width(charinfo *ci, tfont *tf, hunits w,
899 hunits k)
901 if (tf != current_tfont) {
902 flush_tbuf();
903 set_font(tf);
905 char c = ci->get_ascii_code();
906 int kk = k.to_units();
907 if (c == '\0') {
908 flush_tbuf();
909 do_motion();
910 if (ci->numbered()) {
911 put('N');
912 put(ci->get_number());
914 else {
915 put('C');
916 const char *s = ci->nm.contents();
917 if (s[1] == 0) {
918 put('\\');
919 put(s[0]);
921 else
922 put(s);
924 put('\n');
925 hpos += w.to_units() + kk;
927 else if (tcommand_flag) {
928 if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
929 && kk == tbuf_kern
930 && tbuf_len < TBUF_SIZE) {
931 tbuf[tbuf_len++] = c;
932 output_hpos += w.to_units() + kk;
933 hpos = output_hpos;
934 return;
936 flush_tbuf();
937 do_motion();
938 tbuf[tbuf_len++] = c;
939 output_hpos += w.to_units() + kk;
940 tbuf_kern = kk;
941 hpos = output_hpos;
943 else {
944 // flush_tbuf();
945 int n = hpos - output_hpos;
946 if (vpos == output_vpos && n > 0 && n < 100 && !force_motion) {
947 put(char(n/10 + '0'));
948 put(char(n%10 + '0'));
949 put(c);
950 output_hpos = hpos;
952 else {
953 do_motion();
954 put('c');
955 put(c);
957 hpos += w.to_units() + kk;
961 void troff_output_file::put_char(charinfo *ci, tfont *tf)
963 flush_tbuf();
964 if (tf != current_tfont)
965 set_font(tf);
966 char c = ci->get_ascii_code();
967 if (c == '\0') {
968 do_motion();
969 if (ci->numbered()) {
970 put('N');
971 put(ci->get_number());
973 else {
974 put('C');
975 const char *s = ci->nm.contents();
976 if (s[1] == 0) {
977 put('\\');
978 put(s[0]);
980 else
981 put(s);
983 put('\n');
985 else {
986 int n = hpos - output_hpos;
987 if (vpos == output_vpos && n > 0 && n < 100) {
988 put(char(n/10 + '0'));
989 put(char(n%10 + '0'));
990 put(c);
991 output_hpos = hpos;
993 else {
994 do_motion();
995 put('c');
996 put(c);
1001 void troff_output_file::set_font(tfont *tf)
1003 if (current_tfont == tf)
1004 return;
1005 int n = tf->get_input_position();
1006 symbol nm = tf->get_name();
1007 if (n >= nfont_positions || font_position[n] != nm) {
1008 put("x font ");
1009 put(n);
1010 put(' ');
1011 put(nm.contents());
1012 put('\n');
1013 if (n >= nfont_positions) {
1014 int old_nfont_positions = nfont_positions;
1015 symbol *old_font_position = font_position;
1016 nfont_positions *= 3;
1017 nfont_positions /= 2;
1018 if (nfont_positions <= n)
1019 nfont_positions = n + 10;
1020 font_position = new symbol[nfont_positions];
1021 memcpy(font_position, old_font_position,
1022 old_nfont_positions*sizeof(symbol));
1023 a_delete old_font_position;
1025 font_position[n] = nm;
1027 if (current_font_number != n) {
1028 put('f');
1029 put(n);
1030 put('\n');
1031 current_font_number = n;
1033 int size = tf->get_size().to_scaled_points();
1034 if (current_size != size) {
1035 put('s');
1036 put(size);
1037 put('\n');
1038 current_size = size;
1040 int slant = tf->get_slant();
1041 if (current_slant != slant) {
1042 put("x Slant ");
1043 put(slant);
1044 put('\n');
1045 current_slant = slant;
1047 int height = tf->get_height();
1048 if (current_height != height) {
1049 put("x Height ");
1050 put(height == 0 ? current_size : height);
1051 put('\n');
1052 current_height = height;
1054 current_tfont = tf;
1057 void troff_output_file::draw(char code, hvpair *point, int npoints,
1058 font_size fsize)
1060 flush_tbuf();
1061 do_motion();
1062 int size = fsize.to_scaled_points();
1063 if (current_size != size) {
1064 put('s');
1065 put(size);
1066 put('\n');
1067 current_size = size;
1068 current_tfont = 0;
1070 put('D');
1071 put(code);
1072 int i;
1073 if (code == 'c') {
1074 put(' ');
1075 put(point[0].h.to_units());
1077 else
1078 for (i = 0; i < npoints; i++) {
1079 put(' ');
1080 put(point[i].h.to_units());
1081 put(' ');
1082 put(point[i].v.to_units());
1084 for (i = 0; i < npoints; i++)
1085 output_hpos += point[i].h.to_units();
1086 hpos = output_hpos;
1087 if (code != 'e') {
1088 for (i = 0; i < npoints; i++)
1089 output_vpos += point[i].v.to_units();
1090 vpos = output_vpos;
1092 put('\n');
1095 void troff_output_file::really_put_filename(const char *filename)
1097 flush_tbuf();
1098 put("F ");
1099 put(filename);
1100 put('\n');
1103 void troff_output_file::really_begin_page(int pageno, vunits page_length)
1105 flush_tbuf();
1106 if (begun_page) {
1107 if (page_length > V0) {
1108 put('V');
1109 put(page_length.to_units());
1110 put('\n');
1113 else
1114 begun_page = 1;
1115 current_tfont = 0;
1116 current_font_number = -1;
1117 current_size = 0;
1118 // current_height = 0;
1119 // current_slant = 0;
1120 hpos = 0;
1121 vpos = 0;
1122 output_hpos = 0;
1123 output_vpos = 0;
1124 force_motion = 1;
1125 for (int i = 0; i < nfont_positions; i++)
1126 font_position[i] = NULL_SYMBOL;
1127 put('p');
1128 put(pageno);
1129 put('\n');
1132 void troff_output_file::really_copy_file(hunits x, vunits y, const char *filename)
1134 moveto(x, y);
1135 flush_tbuf();
1136 do_motion();
1137 errno = 0;
1138 FILE *ifp = fopen(filename, "r");
1139 if (ifp == 0)
1140 error("can't open `%1': %2", filename, strerror(errno));
1141 else {
1142 int c;
1143 while ((c = getc(ifp)) != EOF)
1144 put(char(c));
1145 fclose(ifp);
1147 force_motion = 1;
1148 current_size = 0;
1149 current_tfont = 0;
1150 current_font_number = -1;
1151 for (int i = 0; i < nfont_positions; i++)
1152 font_position[i] = NULL_SYMBOL;
1155 void troff_output_file::really_transparent_char(unsigned char c)
1157 put(c);
1160 troff_output_file::~troff_output_file()
1162 a_delete font_position;
1165 void troff_output_file::trailer(vunits page_length)
1167 flush_tbuf();
1168 if (page_length > V0) {
1169 put("x trailer\n");
1170 put('V');
1171 put(page_length.to_units());
1172 put('\n');
1174 put("x stop\n");
1177 troff_output_file::troff_output_file()
1178 : current_slant(0), current_height(0), nfont_positions(10), tbuf_len(0),
1179 begun_page(0)
1181 font_position = new symbol[nfont_positions];
1182 put("x T ");
1183 put(device);
1184 put('\n');
1185 put("x res ");
1186 put(units_per_inch);
1187 put(' ');
1188 put(hresolution);
1189 put(' ');
1190 put(vresolution);
1191 put('\n');
1192 put("x init\n");
1195 /* output_file */
1197 output_file *the_output = 0;
1199 output_file::output_file()
1203 output_file::~output_file()
1207 void output_file::trailer(vunits)
1212 void output_file::put_filename(const char *filename)
1216 real_output_file::real_output_file()
1217 : printing(0)
1219 #ifndef POPEN_MISSING
1220 if (pipe_command) {
1221 if ((fp = popen(pipe_command, POPEN_WT)) != 0) {
1222 piped = 1;
1223 return;
1225 error("pipe open failed: %1", strerror(errno));
1227 piped = 0;
1228 #endif /* not POPEN_MISSING */
1229 fp = stdout;
1232 real_output_file::~real_output_file()
1234 if (!fp)
1235 return;
1236 // To avoid looping, set fp to 0 before calling fatal().
1237 if (ferror(fp) || fflush(fp) < 0) {
1238 fp = 0;
1239 fatal("error writing output file");
1241 #ifndef POPEN_MISSING
1242 if (piped) {
1243 int result = pclose(fp);
1244 fp = 0;
1245 if (result < 0)
1246 fatal("pclose failed");
1247 if (!WIFEXITED(result))
1248 error("output process `%1' got fatal signal %2",
1249 pipe_command,
1250 WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
1251 else {
1252 int exit_status = WEXITSTATUS(result);
1253 if (exit_status != 0)
1254 error("output process `%1' exited with status %2",
1255 pipe_command, exit_status);
1258 else
1259 #endif /* not POPEN MISSING */
1260 if (fclose(fp) < 0) {
1261 fp = 0;
1262 fatal("error closing output file");
1266 void real_output_file::flush()
1268 if (fflush(fp) < 0)
1269 fatal("error writing output file");
1272 int real_output_file::is_printing()
1274 return printing;
1277 void real_output_file::begin_page(int pageno, vunits page_length)
1279 printing = in_output_page_list(pageno);
1280 if (printing)
1281 really_begin_page(pageno, page_length);
1284 void real_output_file::copy_file(hunits x, vunits y, const char *filename)
1286 if (printing)
1287 really_copy_file(x, y, filename);
1290 void real_output_file::transparent_char(unsigned char c)
1292 if (printing)
1293 really_transparent_char(c);
1296 void real_output_file::print_line(hunits x, vunits y, node *n,
1297 vunits before, vunits after)
1299 if (printing)
1300 really_print_line(x, y, n, before, after);
1301 delete_node_list(n);
1304 void real_output_file::really_copy_file(hunits, vunits, const char *)
1306 // do nothing
1309 void real_output_file::put_filename(const char *filename)
1311 really_put_filename(filename);
1314 void real_output_file::really_put_filename(const char *filename)
1318 /* ascii_output_file */
1320 void ascii_output_file::really_transparent_char(unsigned char c)
1322 putc(c, fp);
1325 void ascii_output_file::really_print_line(hunits, vunits, node *n, vunits, vunits)
1327 while (n != 0) {
1328 n->ascii_print(this);
1329 n = n->next;
1331 fputc('\n', fp);
1334 void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/)
1336 fputs("<beginning of page>\n", fp);
1339 ascii_output_file::ascii_output_file()
1343 /* suppress_output_file */
1345 suppress_output_file::suppress_output_file()
1349 void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits)
1353 void suppress_output_file::really_begin_page(int, vunits)
1357 void suppress_output_file::really_transparent_char(unsigned char)
1361 /* glyphs, ligatures, kerns, discretionary breaks */
1363 class charinfo_node : public node {
1364 protected:
1365 charinfo *ci;
1366 public:
1367 charinfo_node(charinfo *, node * = 0);
1368 int ends_sentence();
1369 int overlaps_vertically();
1370 int overlaps_horizontally();
1373 charinfo_node::charinfo_node(charinfo *c, node *x)
1374 : node(x), ci(c)
1378 int charinfo_node::ends_sentence()
1380 if (ci->ends_sentence())
1381 return 1;
1382 else if (ci->transparent())
1383 return 2;
1384 else
1385 return 0;
1388 int charinfo_node::overlaps_horizontally()
1390 return ci->overlaps_horizontally();
1393 int charinfo_node::overlaps_vertically()
1395 return ci->overlaps_vertically();
1398 class glyph_node : public charinfo_node {
1399 static glyph_node *free_list;
1400 protected:
1401 tfont *tf;
1402 #ifdef STORE_WIDTH
1403 hunits wid;
1404 glyph_node(charinfo *, tfont *, hunits, node * = 0);
1405 #endif
1406 public:
1407 void *operator new(size_t);
1408 void operator delete(void *);
1409 glyph_node(charinfo *, tfont *, node * = 0);
1410 ~glyph_node() {}
1411 node *copy();
1412 node *merge_glyph_node(glyph_node *);
1413 node *merge_self(node *);
1414 hunits width();
1415 node *last_char_node();
1416 units size();
1417 void vertical_extent(vunits *, vunits *);
1418 hunits subscript_correction();
1419 hunits italic_correction();
1420 hunits left_italic_correction();
1421 hunits skew();
1422 hyphenation_type get_hyphenation_type();
1423 tfont *get_tfont();
1424 void tprint(troff_output_file *);
1425 void zero_width_tprint(troff_output_file *);
1426 hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
1427 node *add_self(node *, hyphen_list **);
1428 void ascii_print(ascii_output_file *);
1429 void asciify(macro *);
1430 int character_type();
1431 int same(node *);
1432 const char *type();
1435 glyph_node *glyph_node::free_list = 0;
1437 class ligature_node : public glyph_node {
1438 node *n1;
1439 node *n2;
1440 #ifdef STORE_WIDTH
1441 ligature_node(charinfo *, tfont *, hunits, node *gn1, node *gn2, node *x = 0);
1442 #endif
1443 public:
1444 void *operator new(size_t);
1445 void operator delete(void *);
1446 ligature_node(charinfo *, tfont *, node *gn1, node *gn2, node *x = 0);
1447 ~ligature_node();
1448 node *copy();
1449 node *add_self(node *, hyphen_list **);
1450 hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
1451 void ascii_print(ascii_output_file *);
1452 void asciify(macro *);
1453 int same(node *);
1454 const char *type();
1457 class kern_pair_node : public node {
1458 hunits amount;
1459 node *n1;
1460 node *n2;
1461 public:
1462 kern_pair_node(hunits n, node *first, node *second, node *x = 0);
1463 ~kern_pair_node();
1464 node *copy();
1465 node *merge_glyph_node(glyph_node *);
1466 node *add_self(node *, hyphen_list **);
1467 hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
1468 node *add_discretionary_hyphen();
1469 hunits width();
1470 node *last_char_node();
1471 hunits italic_correction();
1472 hunits subscript_correction();
1473 void tprint(troff_output_file *);
1474 hyphenation_type get_hyphenation_type();
1475 int ends_sentence();
1476 void ascii_print(ascii_output_file *);
1477 void asciify(macro *);
1478 int same(node *);
1479 const char *type();
1480 void vertical_extent(vunits *, vunits *);
1483 class dbreak_node : public node {
1484 node *none;
1485 node *pre;
1486 node *post;
1487 public:
1488 dbreak_node(node *n, node *p, node *x = 0);
1489 ~dbreak_node();
1490 node *copy();
1491 node *merge_glyph_node(glyph_node *);
1492 node *add_discretionary_hyphen();
1493 hunits width();
1494 node *last_char_node();
1495 hunits italic_correction();
1496 hunits subscript_correction();
1497 void tprint(troff_output_file *);
1498 breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
1499 int is_inner = 0);
1500 int nbreaks();
1501 int ends_sentence();
1502 void split(int, node **, node **);
1503 hyphenation_type get_hyphenation_type();
1504 void ascii_print(ascii_output_file *);
1505 void asciify(macro *);
1506 int same(node *);
1507 const char *type();
1510 void *glyph_node::operator new(size_t n)
1512 assert(n == sizeof(glyph_node));
1513 if (!free_list) {
1514 const int BLOCK = 1024;
1515 free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
1516 for (int i = 0; i < BLOCK - 1; i++)
1517 free_list[i].next = free_list + i + 1;
1518 free_list[BLOCK-1].next = 0;
1520 glyph_node *p = free_list;
1521 free_list = (glyph_node *)(free_list->next);
1522 p->next = 0;
1523 return p;
1526 void *ligature_node::operator new(size_t n)
1528 return new char[n];
1531 void glyph_node::operator delete(void *p)
1533 if (p) {
1534 ((glyph_node *)p)->next = free_list;
1535 free_list = (glyph_node *)p;
1539 void ligature_node::operator delete(void *p)
1541 delete[] (char *)p;
1544 glyph_node::glyph_node(charinfo *c, tfont *t, node *x)
1545 : charinfo_node(c, x), tf(t)
1547 #ifdef STORE_WIDTH
1548 wid = tf->get_width(ci);
1549 #endif
1552 #ifdef STORE_WIDTH
1553 glyph_node::glyph_node(charinfo *c, tfont *t, hunits w, node *x)
1554 : charinfo_node(c, x), tf(t), wid(w)
1557 #endif
1559 node *glyph_node::copy()
1561 #ifdef STORE_WIDTH
1562 return new glyph_node(ci, tf, wid);
1563 #else
1564 return new glyph_node(ci, tf);
1565 #endif
1568 node *glyph_node::merge_self(node *nd)
1570 return nd->merge_glyph_node(this);
1573 int glyph_node::character_type()
1575 return tf->get_character_type(ci);
1578 node *glyph_node::add_self(node *n, hyphen_list **p)
1580 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
1581 next = 0;
1582 node *nn;
1583 if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
1584 next = n;
1585 nn = this;
1587 if ((*p)->hyphen)
1588 nn = nn->add_discretionary_hyphen();
1589 hyphen_list *pp = *p;
1590 *p = (*p)->next;
1591 delete pp;
1592 return nn;
1595 units glyph_node::size()
1597 return tf->get_size().to_units();
1600 hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail)
1602 return new hyphen_list(ci->get_hyphenation_code(), tail);
1606 tfont *node::get_tfont()
1608 return 0;
1611 tfont *glyph_node::get_tfont()
1613 return tf;
1616 node *node::merge_glyph_node(glyph_node * /*gn*/)
1618 return 0;
1621 node *glyph_node::merge_glyph_node(glyph_node *gn)
1623 if (tf == gn->tf) {
1624 charinfo *lig;
1625 if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
1626 node *next1 = next;
1627 next = 0;
1628 return new ligature_node(lig, tf, this, gn, next1);
1630 hunits kern;
1631 if (tf->get_kern(ci, gn->ci, &kern)) {
1632 node *next1 = next;
1633 next = 0;
1634 return new kern_pair_node(kern, this, gn, next1);
1637 return 0;
1640 #ifdef STORE_WIDTH
1641 inline
1642 #endif
1643 hunits glyph_node::width()
1645 #ifdef STORE_WIDTH
1646 return wid;
1647 #else
1648 return tf->get_width(ci);
1649 #endif
1652 node *glyph_node::last_char_node()
1654 return this;
1657 void glyph_node::vertical_extent(vunits *min, vunits *max)
1659 *min = -tf->get_char_height(ci);
1660 *max = tf->get_char_depth(ci);
1663 hunits glyph_node::skew()
1665 return tf->get_char_skew(ci);
1668 hunits glyph_node::subscript_correction()
1670 return tf->get_subscript_correction(ci);
1673 hunits glyph_node::italic_correction()
1675 return tf->get_italic_correction(ci);
1678 hunits glyph_node::left_italic_correction()
1680 return tf->get_left_italic_correction(ci);
1683 hyphenation_type glyph_node::get_hyphenation_type()
1685 return HYPHEN_MIDDLE;
1688 void glyph_node::ascii_print(ascii_output_file *ascii)
1690 unsigned char c = ci->get_ascii_code();
1691 if (c != 0)
1692 ascii->outc(c);
1693 else
1694 ascii->outs(ci->nm.contents());
1697 ligature_node::ligature_node(charinfo *c, tfont *t,
1698 node *gn1, node *gn2, node *x)
1699 : glyph_node(c, t, x), n1(gn1), n2(gn2)
1703 #ifdef STORE_WIDTH
1704 ligature_node::ligature_node(charinfo *c, tfont *t, hunits w,
1705 node *gn1, node *gn2, node *x)
1706 : glyph_node(c, t, w, x), n1(gn1), n2(gn2)
1709 #endif
1711 ligature_node::~ligature_node()
1713 delete n1;
1714 delete n2;
1717 node *ligature_node::copy()
1719 #ifdef STORE_WIDTH
1720 return new ligature_node(ci, tf, wid, n1->copy(), n2->copy());
1721 #else
1722 return new ligature_node(ci, tf, n1->copy(), n2->copy());
1723 #endif
1726 void ligature_node::ascii_print(ascii_output_file *ascii)
1728 n1->ascii_print(ascii);
1729 n2->ascii_print(ascii);
1732 hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail)
1734 return n1->get_hyphen_list(n2->get_hyphen_list(tail));
1737 node *ligature_node::add_self(node *n, hyphen_list **p)
1739 n = n1->add_self(n, p);
1740 n = n2->add_self(n, p);
1741 n1 = n2 = 0;
1742 delete this;
1743 return n;
1746 kern_pair_node::kern_pair_node(hunits n, node *first, node *second, node *x)
1747 : node(x), amount(n), n1(first), n2(second)
1751 dbreak_node::dbreak_node(node *n, node *p, node *x)
1752 : node(x), none(n), pre(p), post(0)
1756 node *dbreak_node::merge_glyph_node(glyph_node *gn)
1758 glyph_node *gn2 = (glyph_node *)gn->copy();
1759 node *new_none = none ? none->merge_glyph_node(gn) : 0;
1760 node *new_post = post ? post->merge_glyph_node(gn2) : 0;
1761 if (new_none == 0 && new_post == 0) {
1762 delete gn2;
1763 return 0;
1765 if (new_none != 0)
1766 none = new_none;
1767 else {
1768 gn->next = none;
1769 none = gn;
1771 if (new_post != 0)
1772 post = new_post;
1773 else {
1774 gn2->next = post;
1775 post = gn2;
1777 return this;
1780 node *kern_pair_node::merge_glyph_node(glyph_node *gn)
1782 node *nd = n2->merge_glyph_node(gn);
1783 if (nd == 0)
1784 return 0;
1785 n2 = nd;
1786 nd = n2->merge_self(n1);
1787 if (nd) {
1788 nd->next = next;
1789 n1 = 0;
1790 n2 = 0;
1791 delete this;
1792 return nd;
1794 return this;
1798 hunits kern_pair_node::italic_correction()
1800 return n2->italic_correction();
1803 hunits kern_pair_node::subscript_correction()
1805 return n2->subscript_correction();
1808 void kern_pair_node::vertical_extent(vunits *min, vunits *max)
1810 n1->vertical_extent(min, max);
1811 vunits min2, max2;
1812 n2->vertical_extent(&min2, &max2);
1813 if (min2 < *min)
1814 *min = min2;
1815 if (max2 > *max)
1816 *max = max2;
1819 node *kern_pair_node::add_discretionary_hyphen()
1821 tfont *tf = n2->get_tfont();
1822 if (tf) {
1823 if (tf->contains(soft_hyphen_char)) {
1824 node *next1 = next;
1825 next = 0;
1826 node *n = copy();
1827 glyph_node *gn = new glyph_node(soft_hyphen_char, tf);
1828 node *nn = n->merge_glyph_node(gn);
1829 if (nn == 0) {
1830 gn->next = n;
1831 nn = gn;
1833 return new dbreak_node(this, nn, next1);
1836 return this;
1840 kern_pair_node::~kern_pair_node()
1842 if (n1 != 0)
1843 delete n1;
1844 if (n2 != 0)
1845 delete n2;
1848 dbreak_node::~dbreak_node()
1850 delete_node_list(pre);
1851 delete_node_list(post);
1852 delete_node_list(none);
1855 node *kern_pair_node::copy()
1857 return new kern_pair_node(amount, n1->copy(), n2->copy());
1860 node *copy_node_list(node *n)
1862 node *p = 0;
1863 while (n != 0) {
1864 node *nn = n->copy();
1865 nn->next = p;
1866 p = nn;
1867 n = n->next;
1869 while (p != 0) {
1870 node *pp = p->next;
1871 p->next = n;
1872 n = p;
1873 p = pp;
1875 return n;
1878 void delete_node_list(node *n)
1880 while (n != 0) {
1881 node *tem = n;
1882 n = n->next;
1883 delete tem;
1887 node *dbreak_node::copy()
1889 dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre));
1890 p->post = copy_node_list(post);
1891 return p;
1894 hyphen_list *node::get_hyphen_list(hyphen_list *tail)
1896 return tail;
1900 hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail)
1902 return n1->get_hyphen_list(n2->get_hyphen_list(tail));
1905 class hyphen_inhibitor_node : public node {
1906 public:
1907 hyphen_inhibitor_node(node *nd = 0);
1908 node *copy();
1909 int same(node *);
1910 const char *type();
1911 hyphenation_type get_hyphenation_type();
1914 hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
1918 node *hyphen_inhibitor_node::copy()
1920 return new hyphen_inhibitor_node;
1923 int hyphen_inhibitor_node::same(node *)
1925 return 1;
1928 const char *hyphen_inhibitor_node::type()
1930 return "hyphen_inhibitor_node";
1933 hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
1935 return HYPHEN_INHIBIT;
1938 /* add_discretionary_hyphen methods */
1940 node *dbreak_node::add_discretionary_hyphen()
1942 if (post)
1943 post = post->add_discretionary_hyphen();
1944 if (none)
1945 none = none->add_discretionary_hyphen();
1946 return this;
1950 node *node::add_discretionary_hyphen()
1952 tfont *tf = get_tfont();
1953 if (!tf)
1954 return new hyphen_inhibitor_node(this);
1955 if (tf->contains(soft_hyphen_char)) {
1956 node *next1 = next;
1957 next = 0;
1958 node *n = copy();
1959 glyph_node *gn = new glyph_node(soft_hyphen_char, tf);
1960 node *n1 = n->merge_glyph_node(gn);
1961 if (n1 == 0) {
1962 gn->next = n;
1963 n1 = gn;
1965 return new dbreak_node(this, n1, next1);
1967 return this;
1971 node *node::merge_self(node *)
1973 return 0;
1976 node *node::add_self(node *n, hyphen_list ** /*p*/)
1978 next = n;
1979 return this;
1982 node *kern_pair_node::add_self(node *n, hyphen_list **p)
1984 n = n1->add_self(n, p);
1985 n = n2->add_self(n, p);
1986 n1 = n2 = 0;
1987 delete this;
1988 return n;
1992 hunits node::width()
1994 return H0;
1997 node *node::last_char_node()
1999 return 0;
2002 hunits hmotion_node::width()
2004 return n;
2007 units node::size()
2009 return points_to_units(10);
2012 hunits kern_pair_node::width()
2014 return n1->width() + n2->width() + amount;
2017 node *kern_pair_node::last_char_node()
2019 node *nd = n2->last_char_node();
2020 if (nd)
2021 return nd;
2022 return n1->last_char_node();
2025 hunits dbreak_node::width()
2027 hunits x = H0;
2028 for (node *n = none; n != 0; n = n->next)
2029 x += n->width();
2030 return x;
2033 node *dbreak_node::last_char_node()
2035 for (node *n = none; n; n = n->next) {
2036 node *last = n->last_char_node();
2037 if (last)
2038 return last;
2040 return 0;
2043 hunits dbreak_node::italic_correction()
2045 return none ? none->italic_correction() : H0;
2048 hunits dbreak_node::subscript_correction()
2050 return none ? none->subscript_correction() : H0;
2053 class italic_corrected_node : public node {
2054 node *n;
2055 hunits x;
2056 public:
2057 italic_corrected_node(node *, hunits, node * = 0);
2058 ~italic_corrected_node();
2059 node *copy();
2060 void ascii_print(ascii_output_file *);
2061 void asciify(macro *m);
2062 hunits width();
2063 node *last_char_node();
2064 void vertical_extent(vunits *, vunits *);
2065 int ends_sentence();
2066 int overlaps_horizontally();
2067 int overlaps_vertically();
2068 int same(node *);
2069 hyphenation_type get_hyphenation_type();
2070 tfont *get_tfont();
2071 hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
2072 int character_type();
2073 void tprint(troff_output_file *);
2074 hunits subscript_correction();
2075 hunits skew();
2076 node *add_self(node *, hyphen_list **);
2077 const char *type();
2080 node *node::add_italic_correction(hunits *width)
2082 hunits ic = italic_correction();
2083 if (ic.is_zero())
2084 return this;
2085 else {
2086 node *next1 = next;
2087 next = 0;
2088 *width += ic;
2089 return new italic_corrected_node(this, ic, next1);
2093 italic_corrected_node::italic_corrected_node(node *nn, hunits xx, node *p)
2094 : node(p), n(nn), x(xx)
2096 assert(n != 0);
2099 italic_corrected_node::~italic_corrected_node()
2101 delete n;
2104 node *italic_corrected_node::copy()
2106 return new italic_corrected_node(n->copy(), x);
2109 hunits italic_corrected_node::width()
2111 return n->width() + x;
2114 void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
2116 n->vertical_extent(min, max);
2119 void italic_corrected_node::tprint(troff_output_file *out)
2121 n->tprint(out);
2122 out->right(x);
2125 hunits italic_corrected_node::skew()
2127 return n->skew() - x/2;
2130 hunits italic_corrected_node::subscript_correction()
2132 return n->subscript_correction() - x;
2135 void italic_corrected_node::ascii_print(ascii_output_file *out)
2137 n->ascii_print(out);
2140 int italic_corrected_node::ends_sentence()
2142 return n->ends_sentence();
2145 int italic_corrected_node::overlaps_horizontally()
2147 return n->overlaps_horizontally();
2150 int italic_corrected_node::overlaps_vertically()
2152 return n->overlaps_vertically();
2155 node *italic_corrected_node::last_char_node()
2157 return n->last_char_node();
2160 tfont *italic_corrected_node::get_tfont()
2162 return n->get_tfont();
2165 hyphenation_type italic_corrected_node::get_hyphenation_type()
2167 return n->get_hyphenation_type();
2170 node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
2172 nd = n->add_self(nd, p);
2173 hunits not_interested;
2174 nd = nd->add_italic_correction(&not_interested);
2175 n = 0;
2176 delete this;
2177 return nd;
2180 hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail)
2182 return n->get_hyphen_list(tail);
2185 int italic_corrected_node::character_type()
2187 return n->character_type();
2190 class break_char_node : public node {
2191 node *ch;
2192 char break_code;
2193 public:
2194 break_char_node(node *, int, node * = 0);
2195 ~break_char_node();
2196 node *copy();
2197 hunits width();
2198 vunits vertical_width();
2199 node *last_char_node();
2200 int character_type();
2201 int ends_sentence();
2202 node *add_self(node *, hyphen_list **);
2203 hyphen_list *get_hyphen_list(hyphen_list *s = 0);
2204 void tprint(troff_output_file *);
2205 void zero_width_tprint(troff_output_file *);
2206 void ascii_print(ascii_output_file *);
2207 void asciify(macro *m);
2208 hyphenation_type get_hyphenation_type();
2209 int overlaps_vertically();
2210 int overlaps_horizontally();
2211 units size();
2212 tfont *get_tfont();
2213 int same(node *);
2214 const char *type();
2217 break_char_node::break_char_node(node *n, int c, node *x)
2218 : node(x), ch(n), break_code(c)
2222 break_char_node::~break_char_node()
2224 delete ch;
2227 node *break_char_node::copy()
2229 return new break_char_node(ch->copy(), break_code);
2232 hunits break_char_node::width()
2234 return ch->width();
2237 vunits break_char_node::vertical_width()
2239 return ch->vertical_width();
2242 node *break_char_node::last_char_node()
2244 return ch->last_char_node();
2247 int break_char_node::character_type()
2249 return ch->character_type();
2252 int break_char_node::ends_sentence()
2254 return ch->ends_sentence();
2257 node *break_char_node::add_self(node *n, hyphen_list **p)
2259 assert((*p)->hyphenation_code == 0);
2260 if ((*p)->breakable && (break_code & 1)) {
2261 n = new space_node(H0, n);
2262 n->freeze_space();
2264 next = n;
2265 n = this;
2266 if ((*p)->breakable && (break_code & 2)) {
2267 n = new space_node(H0, n);
2268 n->freeze_space();
2270 hyphen_list *pp = *p;
2271 *p = (*p)->next;
2272 delete pp;
2273 return n;
2276 hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail)
2278 return new hyphen_list(0, tail);
2281 hyphenation_type break_char_node::get_hyphenation_type()
2283 return HYPHEN_MIDDLE;
2286 void break_char_node::ascii_print(ascii_output_file *ascii)
2288 ch->ascii_print(ascii);
2291 int break_char_node::overlaps_vertically()
2293 return ch->overlaps_vertically();
2296 int break_char_node::overlaps_horizontally()
2298 return ch->overlaps_horizontally();
2301 units break_char_node::size()
2303 return ch->size();
2306 tfont *break_char_node::get_tfont()
2308 return ch->get_tfont();
2311 node *extra_size_node::copy()
2313 return new extra_size_node(n);
2316 node *vertical_size_node::copy()
2318 return new vertical_size_node(n);
2321 node *hmotion_node::copy()
2323 return new hmotion_node(n);
2326 node *space_char_hmotion_node::copy()
2328 return new space_char_hmotion_node(n);
2331 node *vmotion_node::copy()
2333 return new vmotion_node(n);
2336 node *dummy_node::copy()
2338 return new dummy_node;
2341 node *transparent_dummy_node::copy()
2343 return new transparent_dummy_node;
2346 hline_node::~hline_node()
2348 if (n)
2349 delete n;
2352 node *hline_node::copy()
2354 return new hline_node(x, n ? n->copy() : 0);
2357 hunits hline_node::width()
2359 return x < H0 ? H0 : x;
2363 vline_node::~vline_node()
2365 if (n)
2366 delete n;
2369 node *vline_node::copy()
2371 return new vline_node(x, n ? n->copy() : 0);
2374 hunits vline_node::width()
2376 return n == 0 ? H0 : n->width();
2380 zero_width_node::zero_width_node(node *nd) : n(nd)
2384 zero_width_node::~zero_width_node()
2386 delete_node_list(n);
2389 node *zero_width_node::copy()
2391 return new zero_width_node(copy_node_list(n));
2394 int node_list_character_type(node *p)
2396 int t = 0;
2397 for (; p; p = p->next)
2398 t |= p->character_type();
2399 return t;
2402 int zero_width_node::character_type()
2404 return node_list_character_type(n);
2407 void node_list_vertical_extent(node *p, vunits *min, vunits *max)
2409 *min = V0;
2410 *max = V0;
2411 vunits cur_vpos = V0;
2412 vunits v1, v2;
2413 for (; p; p = p->next) {
2414 p->vertical_extent(&v1, &v2);
2415 v1 += cur_vpos;
2416 if (v1 < *min)
2417 *min = v1;
2418 v2 += cur_vpos;
2419 if (v2 > *max)
2420 *max = v2;
2421 cur_vpos += p->vertical_width();
2425 void zero_width_node::vertical_extent(vunits *min, vunits *max)
2427 node_list_vertical_extent(n, min, max);
2430 overstrike_node::overstrike_node() : list(0), max_width(H0)
2434 overstrike_node::~overstrike_node()
2436 delete_node_list(list);
2439 node *overstrike_node::copy()
2441 overstrike_node *on = new overstrike_node;
2442 for (node *tem = list; tem; tem = tem->next)
2443 on->overstrike(tem->copy());
2444 return on;
2447 void overstrike_node::overstrike(node *n)
2449 if (n == 0)
2450 return;
2451 hunits w = n->width();
2452 if (w > max_width)
2453 max_width = w;
2454 node **p;
2455 for (p = &list; *p; p = &(*p)->next)
2457 n->next = 0;
2458 *p = n;
2461 hunits overstrike_node::width()
2463 return max_width;
2466 bracket_node::bracket_node() : list(0), max_width(H0)
2470 bracket_node::~bracket_node()
2472 delete_node_list(list);
2475 node *bracket_node::copy()
2477 bracket_node *on = new bracket_node;
2478 node *last = 0;
2479 node *tem;
2480 for (tem = list; tem; tem = tem->next) {
2481 if (tem->next)
2482 tem->next->last = tem;
2483 last = tem;
2485 for (tem = last; tem; tem = tem->last)
2486 on->bracket(tem->copy());
2487 return on;
2491 void bracket_node::bracket(node *n)
2493 if (n == 0)
2494 return;
2495 hunits w = n->width();
2496 if (w > max_width)
2497 max_width = w;
2498 n->next = list;
2499 list = n;
2502 hunits bracket_node::width()
2504 return max_width;
2507 int node::nspaces()
2509 return 0;
2512 int node::merge_space(hunits)
2514 return 0;
2517 #if 0
2518 space_node *space_node::free_list = 0;
2520 void *space_node::operator new(size_t n)
2522 assert(n == sizeof(space_node));
2523 if (!free_list) {
2524 free_list = (space_node *)new char[sizeof(space_node)*BLOCK];
2525 for (int i = 0; i < BLOCK - 1; i++)
2526 free_list[i].next = free_list + i + 1;
2527 free_list[BLOCK-1].next = 0;
2529 space_node *p = free_list;
2530 free_list = (space_node *)(free_list->next);
2531 p->next = 0;
2532 return p;
2535 inline void space_node::operator delete(void *p)
2537 if (p) {
2538 ((space_node *)p)->next = free_list;
2539 free_list = (space_node *)p;
2542 #endif
2544 space_node::space_node(hunits nn, node *p) : node(p), n(nn), set(0)
2548 space_node::space_node(hunits nn, int s, node *p) : node(p), n(nn), set(s)
2552 #if 0
2553 space_node::~space_node()
2556 #endif
2558 node *space_node::copy()
2560 return new space_node(n, set);
2563 int space_node::nspaces()
2565 return set ? 0 : 1;
2568 int space_node::merge_space(hunits h)
2570 n += h;
2571 return 1;
2574 hunits space_node::width()
2576 return n;
2579 void node::spread_space(int*, hunits*)
2583 void space_node::spread_space(int *nspaces, hunits *desired_space)
2585 if (!set) {
2586 assert(*nspaces > 0);
2587 if (*nspaces == 1) {
2588 n += *desired_space;
2589 *desired_space = H0;
2591 else {
2592 hunits extra = *desired_space / *nspaces;
2593 *desired_space -= extra;
2594 n += extra;
2596 *nspaces -= 1;
2597 set = 1;
2601 void node::freeze_space()
2605 void space_node::freeze_space()
2607 set = 1;
2610 diverted_space_node::diverted_space_node(vunits d, node *p)
2611 : node(p), n(d)
2615 node *diverted_space_node::copy()
2617 return new diverted_space_node(n);
2620 diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
2621 : node(p), filename(s)
2625 node *diverted_copy_file_node::copy()
2627 return new diverted_copy_file_node(filename);
2630 int node::ends_sentence()
2632 return 0;
2635 int kern_pair_node::ends_sentence()
2637 switch (n2->ends_sentence()) {
2638 case 0:
2639 return 0;
2640 case 1:
2641 return 1;
2642 case 2:
2643 break;
2644 default:
2645 assert(0);
2647 return n1->ends_sentence();
2650 int node_list_ends_sentence(node *n)
2652 for (; n != 0; n = n->next)
2653 switch (n->ends_sentence()) {
2654 case 0:
2655 return 0;
2656 case 1:
2657 return 1;
2658 case 2:
2659 break;
2660 default:
2661 assert(0);
2663 return 2;
2667 int dbreak_node::ends_sentence()
2669 return node_list_ends_sentence(none);
2673 int node::overlaps_horizontally()
2675 return 0;
2678 int node::overlaps_vertically()
2680 return 0;
2683 int node::discardable()
2685 return 0;
2688 int space_node::discardable()
2690 return set ? 0 : 1;
2694 vunits node::vertical_width()
2696 return V0;
2699 vunits vline_node::vertical_width()
2701 return x;
2704 vunits vmotion_node::vertical_width()
2706 return n;
2709 int node::character_type()
2711 return 0;
2714 hunits node::subscript_correction()
2716 return H0;
2719 hunits node::italic_correction()
2721 return H0;
2724 hunits node::left_italic_correction()
2726 return H0;
2729 hunits node::skew()
2731 return H0;
2735 /* vertical_extent methods */
2737 void node::vertical_extent(vunits *min, vunits *max)
2739 vunits v = vertical_width();
2740 if (v < V0) {
2741 *min = v;
2742 *max = V0;
2744 else {
2745 *max = v;
2746 *min = V0;
2750 void vline_node::vertical_extent(vunits *min, vunits *max)
2752 if (n == 0)
2753 node::vertical_extent(min, max);
2754 else {
2755 vunits cmin, cmax;
2756 n->vertical_extent(&cmin, &cmax);
2757 vunits h = n->size();
2758 if (x < V0) {
2759 if (-x < h) {
2760 *min = x;
2761 *max = V0;
2763 else {
2764 // we print the first character and then move up, so
2765 *max = cmax;
2766 // we print the last character and then move up h
2767 *min = cmin + h;
2768 if (*min > V0)
2769 *min = V0;
2770 *min += x;
2773 else {
2774 if (x < h) {
2775 *max = x;
2776 *min = V0;
2778 else {
2779 // we move down by h and then print the first character, so
2780 *min = cmin + h;
2781 if (*min > V0)
2782 *min = V0;
2783 *max = x + cmax;
2789 /* ascii_print methods */
2792 static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n)
2794 if (n == 0)
2795 return;
2796 ascii_print_reverse_node_list(ascii, n->next);
2797 n->ascii_print(ascii);
2800 void dbreak_node::ascii_print(ascii_output_file *ascii)
2802 ascii_print_reverse_node_list(ascii, none);
2805 void kern_pair_node::ascii_print(ascii_output_file *ascii)
2807 n1->ascii_print(ascii);
2808 n2->ascii_print(ascii);
2812 void node::ascii_print(ascii_output_file *)
2816 void space_node::ascii_print(ascii_output_file *ascii)
2818 if (!n.is_zero())
2819 ascii->outc(' ');
2822 void hmotion_node::ascii_print(ascii_output_file *ascii)
2824 // this is pretty arbitrary
2825 if (n >= points_to_units(2))
2826 ascii->outc(' ');
2829 void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
2831 ascii->outc(' ');
2834 /* asciify methods */
2836 void node::asciify(macro *m)
2838 m->append(this);
2841 void glyph_node::asciify(macro *m)
2843 unsigned char c = ci->get_ascii_code();
2844 if (c != 0) {
2845 m->append(c);
2846 delete this;
2848 else
2849 m->append(this);
2852 void kern_pair_node::asciify(macro *m)
2854 n1->asciify(m);
2855 n2->asciify(m);
2856 n1 = n2 = 0;
2857 delete this;
2860 static void asciify_reverse_node_list(macro *m, node *n)
2862 if (n == 0)
2863 return;
2864 asciify_reverse_node_list(m, n->next);
2865 n->asciify(m);
2868 void dbreak_node::asciify(macro *m)
2870 asciify_reverse_node_list(m, none);
2871 none = 0;
2872 delete this;
2875 void ligature_node::asciify(macro *m)
2877 n1->asciify(m);
2878 n2->asciify(m);
2879 n1 = n2 = 0;
2880 delete this;
2883 void break_char_node::asciify(macro *m)
2885 ch->asciify(m);
2886 ch = 0;
2887 delete this;
2890 void italic_corrected_node::asciify(macro *m)
2892 n->asciify(m);
2893 n = 0;
2894 delete this;
2897 void left_italic_corrected_node::asciify(macro *m)
2899 if (n) {
2900 n->asciify(m);
2901 n = 0;
2903 delete this;
2906 space_char_hmotion_node::space_char_hmotion_node(hunits i, node *next)
2907 : hmotion_node(i, next)
2911 void space_char_hmotion_node::asciify(macro *m)
2913 m->append(' ');
2914 delete this;
2917 void line_start_node::asciify(macro *)
2919 delete this;
2922 void vertical_size_node::asciify(macro *)
2924 delete this;
2927 breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/,
2928 breakpoint *rest, int /*is_inner*/)
2930 return rest;
2933 int node::nbreaks()
2935 return 0;
2938 breakpoint *space_node::get_breakpoints(hunits width, int ns, breakpoint *rest,
2939 int is_inner)
2941 if (next->discardable())
2942 return rest;
2943 breakpoint *bp = new breakpoint;
2944 bp->next = rest;
2945 bp->width = width;
2946 bp->nspaces = ns;
2947 bp->hyphenated = 0;
2948 if (is_inner) {
2949 assert(rest != 0);
2950 bp->index = rest->index + 1;
2951 bp->nd = rest->nd;
2953 else {
2954 bp->nd = this;
2955 bp->index = 0;
2957 return bp;
2960 int space_node::nbreaks()
2962 if (next->discardable())
2963 return 0;
2964 else
2965 return 1;
2968 static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
2969 int ns, breakpoint *rest)
2971 if (p != 0) {
2972 rest = p->get_breakpoints(*widthp,
2973 ns,
2974 node_list_get_breakpoints(p->next, widthp, ns,
2975 rest),
2977 *widthp += p->width();
2979 return rest;
2983 breakpoint *dbreak_node::get_breakpoints(hunits width, int ns,
2984 breakpoint *rest, int is_inner)
2986 breakpoint *bp = new breakpoint;
2987 bp->next = rest;
2988 bp->width = width;
2989 for (node *tem = pre; tem != 0; tem = tem->next)
2990 bp->width += tem->width();
2991 bp->nspaces = ns;
2992 bp->hyphenated = 1;
2993 if (is_inner) {
2994 assert(rest != 0);
2995 bp->index = rest->index + 1;
2996 bp->nd = rest->nd;
2998 else {
2999 bp->nd = this;
3000 bp->index = 0;
3002 return node_list_get_breakpoints(none, &width, ns, bp);
3005 int dbreak_node::nbreaks()
3007 int i = 1;
3008 for (node *tem = none; tem != 0; tem = tem->next)
3009 i += tem->nbreaks();
3010 return i;
3013 void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
3015 assert(0);
3018 void space_node::split(int where, node **pre, node **post)
3020 assert(where == 0);
3021 *pre = next;
3022 *post = 0;
3023 delete this;
3026 static void node_list_split(node *p, int *wherep, node **prep, node **postp)
3028 if (p == 0)
3029 return;
3030 int nb = p->nbreaks();
3031 node_list_split(p->next, wherep, prep, postp);
3032 if (*wherep < 0) {
3033 p->next = *postp;
3034 *postp = p;
3036 else if (*wherep < nb) {
3037 p->next = *prep;
3038 p->split(*wherep, prep, postp);
3040 else {
3041 p->next = *prep;
3042 *prep = p;
3044 *wherep -= nb;
3047 void dbreak_node::split(int where, node **prep, node **postp)
3049 assert(where >= 0);
3050 if (where == 0) {
3051 *postp = post;
3052 post = 0;
3053 if (pre == 0)
3054 *prep = next;
3055 else {
3056 node *tem;
3057 for (tem = pre; tem->next != 0; tem = tem->next)
3059 tem->next = next;
3060 *prep = pre;
3062 pre = 0;
3063 delete this;
3065 else {
3066 *prep = next;
3067 where -= 1;
3068 node_list_split(none, &where, prep, postp);
3069 none = 0;
3070 delete this;
3075 hyphenation_type node::get_hyphenation_type()
3077 return HYPHEN_BOUNDARY;
3081 hyphenation_type dbreak_node::get_hyphenation_type()
3083 return HYPHEN_INHIBIT;
3086 hyphenation_type kern_pair_node::get_hyphenation_type()
3088 return HYPHEN_MIDDLE;
3091 hyphenation_type dummy_node::get_hyphenation_type()
3093 return HYPHEN_MIDDLE;
3096 hyphenation_type transparent_dummy_node::get_hyphenation_type()
3098 return HYPHEN_MIDDLE;
3101 int node::interpret(macro *)
3103 return 0;
3106 special_node::special_node(const macro &m)
3107 : mac(m)
3111 int special_node::same(node *n)
3113 return mac == ((special_node *)n)->mac;
3116 const char *special_node::type()
3118 return "special_node";
3121 node *special_node::copy()
3123 return new special_node(mac);
3126 void special_node::tprint_start(troff_output_file *out)
3128 out->start_special();
3131 void special_node::tprint_char(troff_output_file *out, unsigned char c)
3133 out->special_char(c);
3136 void special_node::tprint_end(troff_output_file *out)
3138 out->end_special();
3141 /* composite_node */
3143 class composite_node : public charinfo_node {
3144 node *n;
3145 tfont *tf;
3146 public:
3147 composite_node(node *, charinfo *, tfont *, node * = 0);
3148 ~composite_node();
3149 node *copy();
3150 hunits width();
3151 node *last_char_node();
3152 units size();
3153 void tprint(troff_output_file *);
3154 hyphenation_type get_hyphenation_type();
3155 void ascii_print(ascii_output_file *);
3156 void asciify(macro *);
3157 hyphen_list *get_hyphen_list(hyphen_list *tail);
3158 node *add_self(node *, hyphen_list **);
3159 tfont *get_tfont();
3160 int same(node *);
3161 const char *type();
3162 void vertical_extent(vunits *, vunits *);
3163 vunits vertical_width();
3166 composite_node::composite_node(node *p, charinfo *c, tfont *t, node *x)
3167 : charinfo_node(c, x), n(p), tf(t)
3171 composite_node::~composite_node()
3173 delete_node_list(n);
3176 node *composite_node::copy()
3178 return new composite_node(copy_node_list(n), ci, tf);
3181 hunits composite_node::width()
3183 hunits x;
3184 if (tf->get_constant_space(&x))
3185 return x;
3186 x = H0;
3187 for (node *tem = n; tem; tem = tem->next)
3188 x += tem->width();
3189 hunits offset;
3190 if (tf->get_bold(&offset))
3191 x += offset;
3192 x += tf->get_track_kern();
3193 return x;
3196 node *composite_node::last_char_node()
3198 return this;
3201 vunits composite_node::vertical_width()
3203 vunits v = V0;
3204 for (node *tem = n; tem; tem = tem->next)
3205 v += tem->vertical_width();
3206 return v;
3209 units composite_node::size()
3211 return tf->get_size().to_units();
3214 hyphenation_type composite_node::get_hyphenation_type()
3216 return HYPHEN_MIDDLE;
3219 void composite_node::asciify(macro *m)
3221 unsigned char c = ci->get_ascii_code();
3222 if (c != 0) {
3223 m->append(c);
3224 delete this;
3226 else
3227 m->append(this);
3230 void composite_node::ascii_print(ascii_output_file *ascii)
3232 unsigned char c = ci->get_ascii_code();
3233 if (c != 0)
3234 ascii->outc(c);
3235 else
3236 ascii->outs(ci->nm.contents());
3240 hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail)
3242 return new hyphen_list(ci->get_hyphenation_code(), tail);
3246 node *composite_node::add_self(node *nn, hyphen_list **p)
3248 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
3249 next = nn;
3250 nn = this;
3251 if ((*p)->hyphen)
3252 nn = nn->add_discretionary_hyphen();
3253 hyphen_list *pp = *p;
3254 *p = (*p)->next;
3255 delete pp;
3256 return nn;
3259 tfont *composite_node::get_tfont()
3261 return tf;
3264 node *reverse_node_list(node *n)
3266 node *r = 0;
3267 while (n) {
3268 node *tem = n;
3269 n = n->next;
3270 tem->next = r;
3271 r = tem;
3273 return r;
3276 void composite_node::vertical_extent(vunits *min, vunits *max)
3278 n = reverse_node_list(n);
3279 node_list_vertical_extent(n, min, max);
3280 n = reverse_node_list(n);
3283 word_space_node::word_space_node(hunits d, node *x) : space_node(d, x)
3287 word_space_node::word_space_node(hunits d, int s, node *x)
3288 : space_node(d, s, x)
3292 node *word_space_node::copy()
3294 return new word_space_node(n, set);
3297 void word_space_node::tprint(troff_output_file *out)
3299 out->word_marker();
3300 space_node::tprint(out);
3303 unbreakable_space_node::unbreakable_space_node(hunits d, node *x)
3304 : word_space_node(d, x)
3308 unbreakable_space_node::unbreakable_space_node(hunits d, int s, node *x)
3309 : word_space_node(d, s, x)
3313 node *unbreakable_space_node::copy()
3315 return new unbreakable_space_node(n, set);
3318 breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
3319 breakpoint *rest, int)
3321 return rest;
3324 int unbreakable_space_node::nbreaks()
3326 return 0;
3329 void unbreakable_space_node::split(int, node **, node **)
3331 assert(0);
3334 int unbreakable_space_node::merge_space(hunits)
3336 return 0;
3339 hvpair::hvpair()
3343 draw_node::draw_node(char c, hvpair *p, int np, font_size s)
3344 : npoints(np), sz(s), code(c)
3346 point = new hvpair[npoints];
3347 for (int i = 0; i < npoints; i++)
3348 point[i] = p[i];
3351 int draw_node::same(node *n)
3353 draw_node *nd = (draw_node *)n;
3354 if (code != nd->code || npoints != nd->npoints || sz != nd->sz)
3355 return 0;
3356 for (int i = 0; i < npoints; i++)
3357 if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
3358 return 0;
3359 return 1;
3362 const char *draw_node::type()
3364 return "draw_node";
3367 draw_node::~draw_node()
3369 if (point)
3370 a_delete point;
3373 hunits draw_node::width()
3375 hunits x = H0;
3376 for (int i = 0; i < npoints; i++)
3377 x += point[i].h;
3378 return x;
3381 vunits draw_node::vertical_width()
3383 if (code == 'e')
3384 return V0;
3385 vunits x = V0;
3386 for (int i = 0; i < npoints; i++)
3387 x += point[i].v;
3388 return x;
3391 node *draw_node::copy()
3393 return new draw_node(code, point, npoints, sz);
3396 void draw_node::tprint(troff_output_file *out)
3398 out->draw(code, point, npoints, sz);
3401 /* tprint methods */
3403 void glyph_node::tprint(troff_output_file *out)
3405 tfont *ptf = tf->get_plain();
3406 if (ptf == tf)
3407 out->put_char_width(ci, ptf, width(), H0);
3408 else {
3409 hunits offset;
3410 int bold = tf->get_bold(&offset);
3411 hunits w = ptf->get_width(ci);
3412 hunits k = H0;
3413 hunits x;
3414 int cs = tf->get_constant_space(&x);
3415 if (cs) {
3416 x -= w;
3417 if (bold)
3418 x -= offset;
3419 hunits x2 = x/2;
3420 out->right(x2);
3421 k = x - x2;
3423 else
3424 k = tf->get_track_kern();
3425 if (bold) {
3426 out->put_char(ci, ptf);
3427 out->right(offset);
3429 out->put_char_width(ci, ptf, w, k);
3433 void glyph_node::zero_width_tprint(troff_output_file *out)
3435 tfont *ptf = tf->get_plain();
3436 hunits offset;
3437 int bold = tf->get_bold(&offset);
3438 hunits x;
3439 int cs = tf->get_constant_space(&x);
3440 if (cs) {
3441 x -= ptf->get_width(ci);
3442 if (bold)
3443 x -= offset;
3444 x = x/2;
3445 out->right(x);
3447 out->put_char(ci, ptf);
3448 if (bold) {
3449 out->right(offset);
3450 out->put_char(ci, ptf);
3451 out->right(-offset);
3453 if (cs)
3454 out->right(-x);
3457 void break_char_node::tprint(troff_output_file *t)
3459 ch->tprint(t);
3462 void break_char_node::zero_width_tprint(troff_output_file *t)
3464 ch->zero_width_tprint(t);
3467 void hline_node::tprint(troff_output_file *out)
3469 if (x < H0) {
3470 out->right(x);
3471 x = -x;
3473 if (n == 0) {
3474 out->right(x);
3475 return;
3477 hunits w = n->width();
3478 if (w <= H0) {
3479 error("horizontal line drawing character must have positive width");
3480 out->right(x);
3481 return;
3483 int i = int(x/w);
3484 if (i == 0) {
3485 hunits xx = x - w;
3486 hunits xx2 = xx/2;
3487 out->right(xx2);
3488 n->tprint(out);
3489 out->right(xx - xx2);
3491 else {
3492 hunits rem = x - w*i;
3493 if (rem > H0)
3494 if (n->overlaps_horizontally()) {
3495 n->tprint(out);
3496 out->right(rem - w);
3498 else
3499 out->right(rem);
3500 while (--i >= 0)
3501 n->tprint(out);
3505 void vline_node::tprint(troff_output_file *out)
3507 if (n == 0) {
3508 out->down(x);
3509 return;
3511 vunits h = n->size();
3512 int overlaps = n->overlaps_vertically();
3513 vunits y = x;
3514 if (y < V0) {
3515 y = -y;
3516 int i = y / h;
3517 vunits rem = y - i*h;
3518 if (i == 0) {
3519 out->right(n->width());
3520 out->down(-rem);
3522 else {
3523 while (--i > 0) {
3524 n->zero_width_tprint(out);
3525 out->down(-h);
3527 if (overlaps) {
3528 n->zero_width_tprint(out);
3529 out->down(-rem);
3530 n->tprint(out);
3531 out->down(-h);
3533 else {
3534 n->tprint(out);
3535 out->down(-h - rem);
3539 else {
3540 int i = y / h;
3541 vunits rem = y - i*h;
3542 if (i == 0) {
3543 out->down(rem);
3544 out->right(n->width());
3546 else {
3547 out->down(h);
3548 if (overlaps)
3549 n->zero_width_tprint(out);
3550 out->down(rem);
3551 while (--i > 0) {
3552 n->zero_width_tprint(out);
3553 out->down(h);
3555 n->tprint(out);
3560 void zero_width_node::tprint(troff_output_file *out)
3562 if (!n)
3563 return;
3564 if (!n->next) {
3565 n->zero_width_tprint(out);
3566 return;
3568 int hpos = out->get_hpos();
3569 int vpos = out->get_vpos();
3570 node *tem = n;
3571 while (tem) {
3572 tem->tprint(out);
3573 tem = tem->next;
3575 out->moveto(hpos, vpos);
3578 void overstrike_node::tprint(troff_output_file *out)
3580 hunits pos = H0;
3581 for (node *tem = list; tem; tem = tem->next) {
3582 hunits x = (max_width - tem->width())/2;
3583 out->right(x - pos);
3584 pos = x;
3585 tem->zero_width_tprint(out);
3587 out->right(max_width - pos);
3590 void bracket_node::tprint(troff_output_file *out)
3592 if (list == 0)
3593 return;
3594 int npieces = 0;
3595 node *tem;
3596 for (tem = list; tem; tem = tem->next)
3597 ++npieces;
3598 vunits h = list->size();
3599 vunits totalh = h*npieces;
3600 vunits y = (totalh - h)/2;
3601 out->down(y);
3602 for (tem = list; tem; tem = tem->next) {
3603 tem->zero_width_tprint(out);
3604 out->down(-h);
3606 out->right(max_width);
3607 out->down(totalh - y);
3610 void node::tprint(troff_output_file *)
3614 void node::zero_width_tprint(troff_output_file *out)
3616 int hpos = out->get_hpos();
3617 int vpos = out->get_vpos();
3618 tprint(out);
3619 out->moveto(hpos, vpos);
3622 void space_node::tprint(troff_output_file *out)
3624 out->right(n);
3627 void hmotion_node::tprint(troff_output_file *out)
3629 out->right(n);
3632 void vmotion_node::tprint(troff_output_file *out)
3634 out->down(n);
3637 void kern_pair_node::tprint(troff_output_file *out)
3639 n1->tprint(out);
3640 out->right(amount);
3641 n2->tprint(out);
3644 static void tprint_reverse_node_list(troff_output_file *out, node *n)
3646 if (n == 0)
3647 return;
3648 tprint_reverse_node_list(out, n->next);
3649 n->tprint(out);
3652 void dbreak_node::tprint(troff_output_file *out)
3654 tprint_reverse_node_list(out, none);
3657 void composite_node::tprint(troff_output_file *out)
3659 hunits bold_offset;
3660 int is_bold = tf->get_bold(&bold_offset);
3661 hunits track_kern = tf->get_track_kern();
3662 hunits constant_space;
3663 int is_constant_spaced = tf->get_constant_space(&constant_space);
3664 hunits x = H0;
3665 if (is_constant_spaced) {
3666 x = constant_space;
3667 for (node *tem = n; tem; tem = tem->next)
3668 x -= tem->width();
3669 if (is_bold)
3670 x -= bold_offset;
3671 hunits x2 = x/2;
3672 out->right(x2);
3673 x -= x2;
3675 if (is_bold) {
3676 int hpos = out->get_hpos();
3677 int vpos = out->get_vpos();
3678 tprint_reverse_node_list(out, n);
3679 out->moveto(hpos, vpos);
3680 out->right(bold_offset);
3682 tprint_reverse_node_list(out, n);
3683 if (is_constant_spaced)
3684 out->right(x);
3685 else
3686 out->right(track_kern);
3689 node *make_composite_node(charinfo *s, environment *env)
3691 int fontno = env_definite_font(env);
3692 if (fontno < 0) {
3693 error("no current font");
3694 return 0;
3696 assert(fontno < font_table_size && font_table[fontno] != 0);
3697 node *n = charinfo_to_node_list(s, env);
3698 font_size fs = env->get_font_size();
3699 int char_height = env->get_char_height();
3700 int char_slant = env->get_char_slant();
3701 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
3702 fontno);
3703 if (env->is_composite())
3704 tf = tf->get_plain();
3705 return new composite_node(n, s, tf);
3708 node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0)
3710 int fontno = env_definite_font(env);
3711 if (fontno < 0) {
3712 error("no current font");
3713 return 0;
3715 assert(fontno < font_table_size && font_table[fontno] != 0);
3716 int fn = fontno;
3717 int found = font_table[fontno]->contains(s);
3718 if (!found) {
3719 if (s->numbered()) {
3720 if (!no_error_message)
3721 warning(WARN_CHAR, "can't find numbered character %1",
3722 s->get_number());
3723 return 0;
3725 special_font_list *sf = font_table[fontno]->sf;
3726 while (sf != 0 && !found) {
3727 fn = sf->n;
3728 if (font_table[fn])
3729 found = font_table[fn]->contains(s);
3730 sf = sf->next;
3732 if (!found) {
3733 sf = global_special_fonts;
3734 while (sf != 0 && !found) {
3735 fn = sf->n;
3736 if (font_table[fn])
3737 found = font_table[fn]->contains(s);
3738 sf = sf->next;
3741 if (!found
3742 #if 0
3743 && global_special_fonts == 0 && font_table[fontno]->sf == 0
3744 #endif
3746 for (fn = 0; fn < font_table_size; fn++)
3747 if (font_table[fn]
3748 && font_table[fn]->is_special()
3749 && font_table[fn]->contains(s)) {
3750 found = 1;
3751 break;
3754 if (!found) {
3755 if (!no_error_message && s->first_time_not_found()) {
3756 unsigned char input_code = s->get_ascii_code();
3757 if (input_code != 0) {
3758 if (csgraph(input_code))
3759 warning(WARN_CHAR, "can't find character `%1'", input_code);
3760 else
3761 warning(WARN_CHAR, "can't find character with input code %1",
3762 int(input_code));
3764 else
3765 warning(WARN_CHAR, "can't find special character `%1'",
3766 s->nm.contents());
3768 return 0;
3771 font_size fs = env->get_font_size();
3772 int char_height = env->get_char_height();
3773 int char_slant = env->get_char_slant();
3774 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn);
3775 if (env->is_composite())
3776 tf = tf->get_plain();
3777 return new glyph_node(s, tf);
3780 node *make_node(charinfo *ci, environment *env)
3782 switch (ci->get_special_translation()) {
3783 case charinfo::TRANSLATE_SPACE:
3784 return new space_char_hmotion_node(env->get_space_width());
3785 case charinfo::TRANSLATE_DUMMY:
3786 return new dummy_node;
3787 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
3788 error("translation to \\% ignored in this context");
3789 break;
3791 charinfo *tem = ci->get_translation();
3792 if (tem)
3793 ci = tem;
3794 macro *mac = ci->get_macro();
3795 if (mac)
3796 return make_composite_node(ci, env);
3797 else
3798 return make_glyph_node(ci, env);
3801 int character_exists(charinfo *ci, environment *env)
3803 if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
3804 return 1;
3805 charinfo *tem = ci->get_translation();
3806 if (tem)
3807 ci = tem;
3808 if (ci->get_macro())
3809 return 1;
3810 node *nd = make_glyph_node(ci, env, 1);
3811 if (nd) {
3812 delete nd;
3813 return 1;
3815 return 0;
3818 node *node::add_char(charinfo *ci, environment *env, hunits *widthp)
3820 node *res;
3821 switch (ci->get_special_translation()) {
3822 case charinfo::TRANSLATE_SPACE:
3823 res = new space_char_hmotion_node(env->get_space_width(), this);
3824 *widthp += res->width();
3825 return res;
3826 case charinfo::TRANSLATE_DUMMY:
3827 return new dummy_node(this);
3828 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
3829 return add_discretionary_hyphen();
3831 charinfo *tem = ci->get_translation();
3832 if (tem)
3833 ci = tem;
3834 macro *mac = ci->get_macro();
3835 if (mac) {
3836 res = make_composite_node(ci, env);
3837 if (res) {
3838 res->next = this;
3839 *widthp += res->width();
3841 else
3842 return this;
3844 else {
3845 node *gn = make_glyph_node(ci, env);
3846 if (gn == 0)
3847 return this;
3848 else {
3849 hunits old_width = width();
3850 node *p = gn->merge_self(this);
3851 if (p == 0) {
3852 *widthp += gn->width();
3853 gn->next = this;
3854 res = gn;
3856 else {
3857 *widthp += p->width() - old_width;
3858 res = p;
3862 int break_code = 0;
3863 if (ci->can_break_before())
3864 break_code = 1;
3865 if (ci->can_break_after())
3866 break_code |= 2;
3867 if (break_code) {
3868 node *next1 = res->next;
3869 res->next = 0;
3870 res = new break_char_node(res, break_code, next1);
3872 return res;
3876 #ifdef __GNUG__
3877 inline
3878 #endif
3879 int same_node(node *n1, node *n2)
3881 if (n1 != 0) {
3882 if (n2 != 0)
3883 return n1->type() == n2->type() && n1->same(n2);
3884 else
3885 return 0;
3887 else
3888 return n2 == 0;
3891 int same_node_list(node *n1, node *n2)
3893 while (n1 && n2) {
3894 if (n1->type() != n2->type() || !n1->same(n2))
3895 return 0;
3896 n1 = n1->next;
3897 n2 = n2->next;
3899 return !n1 && !n2;
3902 int extra_size_node::same(node *nd)
3904 return n == ((extra_size_node *)nd)->n;
3907 const char *extra_size_node::type()
3909 return "extra_size_node";
3912 int vertical_size_node::same(node *nd)
3914 return n == ((vertical_size_node *)nd)->n;
3917 const char *vertical_size_node::type()
3919 return "vertical_size_node";
3922 int hmotion_node::same(node *nd)
3924 return n == ((hmotion_node *)nd)->n;
3927 const char *hmotion_node::type()
3929 return "hmotion_node";
3932 int space_char_hmotion_node::same(node *nd)
3934 return n == ((space_char_hmotion_node *)nd)->n;
3937 const char *space_char_hmotion_node::type()
3939 return "space_char_hmotion_node";
3942 int vmotion_node::same(node *nd)
3944 return n == ((vmotion_node *)nd)->n;
3947 const char *vmotion_node::type()
3949 return "vmotion_node";
3952 int hline_node::same(node *nd)
3954 return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n);
3957 const char *hline_node::type()
3959 return "hline_node";
3962 int vline_node::same(node *nd)
3964 return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n);
3967 const char *vline_node::type()
3969 return "vline_node";
3972 int dummy_node::same(node * /*nd*/)
3974 return 1;
3977 const char *dummy_node::type()
3979 return "dummy_node";
3982 int transparent_dummy_node::same(node * /*nd*/)
3984 return 1;
3987 const char *transparent_dummy_node::type()
3989 return "transparent_dummy_node";
3992 int transparent_dummy_node::ends_sentence()
3994 return 2;
3997 int zero_width_node::same(node *nd)
3999 return same_node_list(n, ((zero_width_node *)nd)->n);
4002 const char *zero_width_node::type()
4004 return "zero_width_node";
4007 int italic_corrected_node::same(node *nd)
4009 return (x == ((italic_corrected_node *)nd)->x
4010 && same_node(n, ((italic_corrected_node *)nd)->n));
4013 const char *italic_corrected_node::type()
4015 return "italic_corrected_node";
4019 left_italic_corrected_node::left_italic_corrected_node(node *x)
4020 : node(x), n(0)
4024 left_italic_corrected_node::~left_italic_corrected_node()
4026 delete n;
4029 node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
4031 if (n == 0) {
4032 hunits lic = gn->left_italic_correction();
4033 if (!lic.is_zero()) {
4034 x = lic;
4035 n = gn;
4036 return this;
4039 else {
4040 node *nd = n->merge_glyph_node(gn);
4041 if (nd) {
4042 n = nd;
4043 x = n->left_italic_correction();
4044 return this;
4047 return 0;
4050 node *left_italic_corrected_node::copy()
4052 left_italic_corrected_node *nd = new left_italic_corrected_node;
4053 if (n) {
4054 nd->n = n->copy();
4055 nd->x = x;
4057 return nd;
4060 void left_italic_corrected_node::tprint(troff_output_file *out)
4062 if (n) {
4063 out->right(x);
4064 n->tprint(out);
4068 const char *left_italic_corrected_node::type()
4070 return "left_italic_corrected_node";
4073 int left_italic_corrected_node::same(node *nd)
4075 return (x == ((left_italic_corrected_node *)nd)->x
4076 && same_node(n, ((left_italic_corrected_node *)nd)->n));
4079 void left_italic_corrected_node::ascii_print(ascii_output_file *out)
4081 if (n)
4082 n->ascii_print(out);
4085 hunits left_italic_corrected_node::width()
4087 return n ? n->width() + x : H0;
4090 void left_italic_corrected_node::vertical_extent(vunits *min, vunits *max)
4092 if (n)
4093 n->vertical_extent(min, max);
4094 else
4095 node::vertical_extent(min, max);
4098 hunits left_italic_corrected_node::skew()
4100 return n ? n->skew() + x/2 : H0;
4103 hunits left_italic_corrected_node::subscript_correction()
4105 return n ? n->subscript_correction() : H0;
4108 hunits left_italic_corrected_node::italic_correction()
4110 return n ? n->italic_correction() : H0;
4113 int left_italic_corrected_node::ends_sentence()
4115 return n ? n->ends_sentence() : 0;
4118 int left_italic_corrected_node::overlaps_horizontally()
4120 return n ? n->overlaps_horizontally() : 0;
4123 int left_italic_corrected_node::overlaps_vertically()
4125 return n ? n->overlaps_vertically() : 0;
4128 node *left_italic_corrected_node::last_char_node()
4130 return n ? n->last_char_node() : 0;
4133 tfont *left_italic_corrected_node::get_tfont()
4135 return n ? n->get_tfont() : 0;
4138 hyphenation_type left_italic_corrected_node::get_hyphenation_type()
4140 if (n)
4141 return n->get_hyphenation_type();
4142 else
4143 return HYPHEN_MIDDLE;
4146 hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail)
4148 return n ? n->get_hyphen_list(tail) : tail;
4151 node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
4153 if (n) {
4154 nd = new left_italic_corrected_node(nd);
4155 nd = n->add_self(nd, p);
4156 n = 0;
4157 delete this;
4159 return nd;
4162 int left_italic_corrected_node::character_type()
4164 return n ? n->character_type() : 0;
4167 int overstrike_node::same(node *nd)
4169 return same_node_list(list, ((overstrike_node *)nd)->list);
4172 const char *overstrike_node::type()
4174 return "overstrike_node";
4177 int bracket_node::same(node *nd)
4179 return same_node_list(list, ((bracket_node *)nd)->list);
4182 const char *bracket_node::type()
4184 return "bracket_node";
4187 int composite_node::same(node *nd)
4189 return ci == ((composite_node *)nd)->ci
4190 && same_node_list(n, ((composite_node *)nd)->n);
4193 const char *composite_node::type()
4195 return "composite_node";
4198 int glyph_node::same(node *nd)
4200 return ci == ((glyph_node *)nd)->ci && tf == ((glyph_node *)nd)->tf;
4203 const char *glyph_node::type()
4205 return "glyph_node";
4208 int ligature_node::same(node *nd)
4210 return (same_node(n1, ((ligature_node *)nd)->n1)
4211 && same_node(n2, ((ligature_node *)nd)->n2)
4212 && glyph_node::same(nd));
4215 const char *ligature_node::type()
4217 return "ligature_node";
4220 int kern_pair_node::same(node *nd)
4222 return (amount == ((kern_pair_node *)nd)->amount
4223 && same_node(n1, ((kern_pair_node *)nd)->n1)
4224 && same_node(n2, ((kern_pair_node *)nd)->n2));
4227 const char *kern_pair_node::type()
4229 return "kern_pair_node";
4232 int dbreak_node::same(node *nd)
4234 return (same_node_list(none, ((dbreak_node *)nd)->none)
4235 && same_node_list(pre, ((dbreak_node *)nd)->pre)
4236 && same_node_list(post, ((dbreak_node *)nd)->post));
4239 const char *dbreak_node::type()
4241 return "dbreak_node";
4244 int break_char_node::same(node *nd)
4246 return (break_code == ((break_char_node *)nd)->break_code
4247 && same_node(ch, ((break_char_node *)nd)->ch));
4250 const char *break_char_node::type()
4252 return "break_char_node";
4255 int line_start_node::same(node * /*nd*/)
4257 return 1;
4260 const char *line_start_node::type()
4262 return "line_start_node";
4265 int space_node::same(node *nd)
4267 return n == ((space_node *)nd)->n && set == ((space_node *)nd)->set;
4270 const char *space_node::type()
4272 return "space_node";
4275 int word_space_node::same(node *nd)
4277 return (n == ((word_space_node *)nd)->n
4278 && set == ((word_space_node *)nd)->set);
4281 const char *word_space_node::type()
4283 return "word_space_node";
4286 int unbreakable_space_node::same(node *nd)
4288 return (n == ((unbreakable_space_node *)nd)->n
4289 && set == ((unbreakable_space_node *)nd)->set);
4292 const char *unbreakable_space_node::type()
4294 return "unbreakable_space_node";
4297 int diverted_space_node::same(node *nd)
4299 return n == ((diverted_space_node *)nd)->n;
4302 const char *diverted_space_node::type()
4304 return "diverted_space_node";
4307 int diverted_copy_file_node::same(node *nd)
4309 return filename == ((diverted_copy_file_node *)nd)->filename;
4312 const char *diverted_copy_file_node::type()
4314 return "diverted_copy_file_node";
4317 // Grow the font_table so that its size is > n.
4319 static void grow_font_table(int n)
4321 assert(n >= font_table_size);
4322 font_info **old_font_table = font_table;
4323 int old_font_table_size = font_table_size;
4324 font_table_size = font_table_size ? (font_table_size*3)/2 : 10;
4325 if (font_table_size <= n)
4326 font_table_size = n + 10;
4327 font_table = new font_info *[font_table_size];
4328 if (old_font_table_size)
4329 memcpy(font_table, old_font_table,
4330 old_font_table_size*sizeof(font_info *));
4331 a_delete old_font_table;
4332 for (int i = old_font_table_size; i < font_table_size; i++)
4333 font_table[i] = 0;
4336 dictionary font_translation_dictionary(17);
4338 static symbol get_font_translation(symbol nm)
4340 void *p = font_translation_dictionary.lookup(nm);
4341 return p ? symbol((char *)p) : nm;
4344 dictionary font_dictionary(50);
4346 static int mount_font_no_translate(int n, symbol name, symbol external_name)
4348 assert(n >= 0);
4349 // We store the address of this char in font_dictionary to indicate
4350 // that we've previously tried to mount the font and failed.
4351 static char a_char;
4352 font *fm = 0;
4353 void *p = font_dictionary.lookup(external_name);
4354 if (p == 0) {
4355 int not_found;
4356 fm = font::load_font(external_name.contents(), &not_found);
4357 if (!fm) {
4358 if (not_found)
4359 warning(WARN_FONT, "can't find font `%1'", external_name.contents());
4360 font_dictionary.lookup(external_name, &a_char);
4361 return 0;
4363 font_dictionary.lookup(name, fm);
4365 else if (p == &a_char) {
4366 #if 0
4367 error("invalid font `%1'", external_name.contents());
4368 #endif
4369 return 0;
4371 else
4372 fm = (font*)p;
4373 if (n >= font_table_size) {
4374 if (n - font_table_size > 1000) {
4375 error("font position too much larger than first unused position");
4376 return 0;
4378 grow_font_table(n);
4380 else if (font_table[n] != 0)
4381 delete font_table[n];
4382 font_table[n] = new font_info(name, n, external_name, fm);
4383 font_family::invalidate_fontno(n);
4384 return 1;
4387 int mount_font(int n, symbol name, symbol external_name)
4389 assert(n >= 0);
4390 name = get_font_translation(name);
4391 if (external_name.is_null())
4392 external_name = name;
4393 else
4394 external_name = get_font_translation(external_name);
4395 return mount_font_no_translate(n, name, external_name);
4398 void mount_style(int n, symbol name)
4400 assert(n >= 0);
4401 if (n >= font_table_size) {
4402 if (n - font_table_size > 1000) {
4403 error("font position too much larger than first unused position");
4404 return;
4406 grow_font_table(n);
4408 else if (font_table[n] != 0)
4409 delete font_table[n];
4410 font_table[n] = new font_info(get_font_translation(name), n, NULL_SYMBOL, 0);
4411 font_family::invalidate_fontno(n);
4414 /* global functions */
4416 void font_translate()
4418 symbol from = get_name(1);
4419 if (!from.is_null()) {
4420 symbol to = get_name();
4421 if (to.is_null() || from == to)
4422 font_translation_dictionary.remove(from);
4423 else
4424 font_translation_dictionary.lookup(from, (void *)to.contents());
4426 skip_line();
4429 void font_position()
4431 int n;
4432 if (get_integer(&n)) {
4433 if (n < 0)
4434 error("negative font position");
4435 else {
4436 symbol internal_name = get_name(1);
4437 if (!internal_name.is_null()) {
4438 symbol external_name = get_long_name(0);
4439 mount_font(n, internal_name, external_name); // ignore error
4443 skip_line();
4446 font_family::font_family(symbol s)
4447 : map_size(10), nm(s)
4449 map = new int[map_size];
4450 for (int i = 0; i < map_size; i++)
4451 map[i] = -1;
4454 font_family::~font_family()
4456 a_delete map;
4459 int font_family::make_definite(int i)
4461 if (i >= 0) {
4462 if (i < map_size && map[i] >= 0)
4463 return map[i];
4464 else {
4465 if (i < font_table_size && font_table[i] != 0) {
4466 if (i >= map_size) {
4467 int old_map_size = map_size;
4468 int *old_map = map;
4469 map_size *= 3;
4470 map_size /= 2;
4471 if (i >= map_size)
4472 map_size = i + 10;
4473 map = new int[map_size];
4474 memcpy(map, old_map, old_map_size*sizeof(int));
4475 a_delete old_map;
4476 for (int j = old_map_size; j < map_size; j++)
4477 map[j] = -1;
4479 if (font_table[i]->is_style()) {
4480 symbol sty = font_table[i]->get_name();
4481 symbol f = concat(nm, sty);
4482 int n;
4483 // don't use symbol_fontno, because that might return a style
4484 // and because we don't want to translate the name
4485 for (n = 0; n < font_table_size; n++)
4486 if (font_table[n] != 0 && font_table[n]->is_named(f)
4487 && !font_table[n]->is_style())
4488 break;
4489 if (n >= font_table_size) {
4490 n = next_available_font_position();
4491 if (!mount_font_no_translate(n, f, f))
4492 return -1;
4494 return map[i] = n;
4496 else
4497 return map[i] = i;
4499 else
4500 return -1;
4503 else
4504 return -1;
4507 dictionary family_dictionary(5);
4509 font_family *lookup_family(symbol nm)
4511 font_family *f = (font_family *)family_dictionary.lookup(nm);
4512 if (!f) {
4513 f = new font_family(nm);
4514 (void)family_dictionary.lookup(nm, f);
4516 return f;
4519 void font_family::invalidate_fontno(int n)
4521 assert(n >= 0 && n < font_table_size);
4522 dictionary_iterator iter(family_dictionary);
4523 symbol nm;
4524 font_family *fam;
4525 while (iter.get(&nm, (void **)&fam)) {
4526 int map_size = fam->map_size;
4527 if (n < map_size)
4528 fam->map[n] = -1;
4529 for (int i = 0; i < map_size; i++)
4530 if (fam->map[i] == n)
4531 fam->map[i] = -1;
4535 void style()
4537 int n;
4538 if (get_integer(&n)) {
4539 if (n < 0)
4540 error("negative font position");
4541 else {
4542 symbol internal_name = get_name(1);
4543 if (!internal_name.is_null())
4544 mount_style(n, internal_name);
4547 skip_line();
4550 static int get_fontno()
4552 int n;
4553 tok.skip();
4554 if (tok.delimiter()) {
4555 symbol s = get_name(1);
4556 if (!s.is_null()) {
4557 n = symbol_fontno(s);
4558 if (n < 0) {
4559 n = next_available_font_position();
4560 if (!mount_font(n, s))
4561 return -1;
4563 return curenv->get_family()->make_definite(n);
4566 else if (get_integer(&n)) {
4567 if (n < 0 || n >= font_table_size || font_table[n] == 0)
4568 error("bad font number");
4569 else
4570 return curenv->get_family()->make_definite(n);
4572 return -1;
4575 static int underline_fontno = 2;
4577 void underline_font()
4579 int n = get_fontno();
4580 if (n >= 0)
4581 underline_fontno = n;
4582 skip_line();
4585 int get_underline_fontno()
4587 return underline_fontno;
4590 static void read_special_fonts(special_font_list **sp)
4592 special_font_list *s = *sp;
4593 *sp = 0;
4594 while (s != 0) {
4595 special_font_list *tem = s;
4596 s = s->next;
4597 delete tem;
4599 special_font_list **p = sp;
4600 while (has_arg()) {
4601 int i = get_fontno();
4602 if (i >= 0) {
4603 special_font_list *tem = new special_font_list;
4604 tem->n = i;
4605 tem->next = 0;
4606 *p = tem;
4607 p = &(tem->next);
4612 void font_special_request()
4614 int n = get_fontno();
4615 if (n >= 0)
4616 read_special_fonts(&font_table[n]->sf);
4617 skip_line();
4621 void special_request()
4623 read_special_fonts(&global_special_fonts);
4624 skip_line();
4627 int next_available_font_position()
4629 int i;
4630 for (i = 1; i < font_table_size && font_table[i] != 0; i++)
4632 return i;
4635 int symbol_fontno(symbol s)
4637 s = get_font_translation(s);
4638 for (int i = 0; i < font_table_size; i++)
4639 if (font_table[i] != 0 && font_table[i]->is_named(s))
4640 return i;
4641 return -1;
4644 int is_good_fontno(int n)
4646 return n >= 0 && n < font_table_size && font_table[n] != NULL;
4649 int get_bold_fontno(int n)
4651 if (n >= 0 && n < font_table_size && font_table[n] != 0) {
4652 hunits offset;
4653 if (font_table[n]->get_bold(&offset))
4654 return offset.to_units() + 1;
4655 else
4656 return 0;
4658 else
4659 return 0;
4662 hunits env_digit_width(environment *env)
4664 node *n = make_glyph_node(charset_table['0'], env);
4665 if (n) {
4666 hunits x = n->width();
4667 delete n;
4668 return x;
4670 else
4671 return H0;
4674 hunits env_space_width(environment *env)
4676 int fn = env_definite_font(env);
4677 font_size fs = env->get_font_size();
4678 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
4679 return scale(fs.to_units()/3, env->get_space_size(), 12);
4680 else
4681 return font_table[fn]->get_space_width(fs, env->get_space_size());
4684 hunits env_sentence_space_width(environment *env)
4686 int fn = env_definite_font(env);
4687 font_size fs = env->get_font_size();
4688 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
4689 return scale(fs.to_units()/3, env->get_sentence_space_size(), 12);
4690 else
4691 return font_table[fn]->get_space_width(fs, env->get_sentence_space_size());
4694 hunits env_half_narrow_space_width(environment *env)
4696 int fn = env_definite_font(env);
4697 font_size fs = env->get_font_size();
4698 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
4699 return 0;
4700 else
4701 return font_table[fn]->get_half_narrow_space_width(fs);
4704 hunits env_narrow_space_width(environment *env)
4706 int fn = env_definite_font(env);
4707 font_size fs = env->get_font_size();
4708 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
4709 return 0;
4710 else
4711 return font_table[fn]->get_narrow_space_width(fs);
4714 void bold_font()
4716 int n = get_fontno();
4717 if (n >= 0) {
4718 if (has_arg()) {
4719 if (tok.delimiter()) {
4720 int f = get_fontno();
4721 if (f >= 0) {
4722 units offset;
4723 if (has_arg() && get_number(&offset, 'u') && offset >= 1)
4724 font_table[f]->set_conditional_bold(n, hunits(offset - 1));
4725 else
4726 font_table[f]->conditional_unbold(n);
4729 else {
4730 units offset;
4731 if (get_number(&offset, 'u') && offset >= 1)
4732 font_table[n]->set_bold(hunits(offset - 1));
4733 else
4734 font_table[n]->unbold();
4737 else
4738 font_table[n]->unbold();
4740 skip_line();
4743 track_kerning_function::track_kerning_function() : non_zero(0)
4747 track_kerning_function::track_kerning_function(int min_s, hunits min_a,
4748 int max_s, hunits max_a)
4749 : non_zero(1),
4750 min_size(min_s), min_amount(min_a),
4751 max_size(max_s), max_amount(max_a)
4755 int track_kerning_function::operator==(const track_kerning_function &tk)
4757 if (non_zero)
4758 return (tk.non_zero
4759 && min_size == tk.min_size
4760 && min_amount == tk.min_amount
4761 && max_size == tk.max_size
4762 && max_amount == tk.max_amount);
4763 else
4764 return !tk.non_zero;
4767 int track_kerning_function::operator!=(const track_kerning_function &tk)
4769 if (non_zero)
4770 return (!tk.non_zero
4771 || min_size != tk.min_size
4772 || min_amount != tk.min_amount
4773 || max_size != tk.max_size
4774 || max_amount != tk.max_amount);
4775 else
4776 return tk.non_zero;
4779 hunits track_kerning_function::compute(int size)
4781 if (non_zero) {
4782 if (max_size <= min_size)
4783 return min_amount;
4784 else if (size <= min_size)
4785 return min_amount;
4786 else if (size >= max_size)
4787 return max_amount;
4788 else
4789 return (scale(max_amount, size - min_size, max_size - min_size)
4790 + scale(min_amount, max_size - size, max_size - min_size));
4792 else
4793 return H0;
4796 void track_kern()
4798 int n = get_fontno();
4799 if (n >= 0) {
4800 int min_s, max_s;
4801 hunits min_a, max_a;
4802 if (has_arg()
4803 && get_number(&min_s, 'z')
4804 && get_hunits(&min_a, 'p')
4805 && get_number(&max_s, 'z')
4806 && get_hunits(&max_a, 'p')) {
4807 track_kerning_function tk(min_s, min_a, max_s, max_a);
4808 font_table[n]->set_track_kern(tk);
4810 else {
4811 track_kerning_function tk;
4812 font_table[n]->set_track_kern(tk);
4815 skip_line();
4818 void constant_space()
4820 int n = get_fontno();
4821 if (n >= 0) {
4822 int x, y;
4823 if (!has_arg() || !get_integer(&x))
4824 font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
4825 else {
4826 if (!has_arg() || !get_number(&y, 'z'))
4827 font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
4828 else
4829 font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
4830 scale(y*x,
4831 units_per_inch,
4832 36*72*sizescale));
4835 skip_line();
4838 void ligature()
4840 int lig;
4841 if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2)
4842 global_ligature_mode = lig;
4843 else
4844 global_ligature_mode = 1;
4845 skip_line();
4848 void kern_request()
4850 int k;
4851 if (has_arg() && get_integer(&k))
4852 global_kern_mode = k != 0;
4853 else
4854 global_kern_mode = 1;
4855 skip_line();
4858 void set_soft_hyphen_char()
4860 soft_hyphen_char = get_optional_char();
4861 if (!soft_hyphen_char)
4862 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
4863 skip_line();
4866 void init_output()
4868 if (suppress_output_flag)
4869 the_output = new suppress_output_file;
4870 else if (ascii_output_flag)
4871 the_output = new ascii_output_file;
4872 else
4873 the_output = new troff_output_file;
4876 class next_available_font_position_reg : public reg {
4877 public:
4878 const char *get_string();
4881 const char *next_available_font_position_reg::get_string()
4883 return i_to_a(next_available_font_position());
4886 class printing_reg : public reg {
4887 public:
4888 const char *get_string();
4891 const char *printing_reg::get_string()
4893 if (the_output)
4894 return the_output->is_printing() ? "1" : "0";
4895 else
4896 return "0";
4899 void init_node_requests()
4901 init_request("fp", font_position);
4902 init_request("sty", style);
4903 init_request("cs", constant_space);
4904 init_request("bd", bold_font);
4905 init_request("uf", underline_font);
4906 init_request("lg", ligature);
4907 init_request("kern", kern_request);
4908 init_request("tkf", track_kern);
4909 init_request("special", special_request);
4910 init_request("fspecial", font_special_request);
4911 init_request("ftr", font_translate);
4912 init_request("shc", set_soft_hyphen_char);
4913 number_reg_dictionary.define(".fp", new next_available_font_position_reg);
4914 number_reg_dictionary.define(".kern",
4915 new constant_int_reg(&global_kern_mode));
4916 number_reg_dictionary.define(".lg",
4917 new constant_int_reg(&global_ligature_mode));
4918 number_reg_dictionary.define(".P", new printing_reg);
4919 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);