groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / roff / troff / node.cpp
CommitLineData
92d0a6a6 1// -*- C++ -*-
4d3e9548
JL
2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2008, 2009
92d0a6a6
JR
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
6
7This file is part of groff.
8
9groff is free software; you can redistribute it and/or modify it under
10the terms of the GNU General Public License as published by the Free
4d3e9548
JL
11Software Foundation, either version 3 of the License, or
12(at your option) any later version.
92d0a6a6
JR
13
14groff is distributed in the hope that it will be useful, but WITHOUT ANY
15WARRANTY; without even the implied warranty of MERCHANTABILITY or
16FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17for more details.
18
4d3e9548
JL
19You should have received a copy of the GNU General Public License
20along with this program. If not, see <http://www.gnu.org/licenses/>. */
465b256c
JR
21
22extern int debug_state;
92d0a6a6
JR
23
24#include "troff.h"
25
26#ifdef HAVE_UNISTD_H
27#include <unistd.h>
28#endif
29
30#include "dictionary.h"
31#include "hvunits.h"
465b256c
JR
32#include "stringclass.h"
33#include "mtsm.h"
92d0a6a6
JR
34#include "env.h"
35#include "request.h"
36#include "node.h"
37#include "token.h"
465b256c
JR
38#include "div.h"
39#include "reg.h"
92d0a6a6 40#include "font.h"
4d3e9548 41#include "charinfo.h"
92d0a6a6 42#include "input.h"
92d0a6a6 43#include "geometry.h"
92d0a6a6
JR
44
45#include "nonposix.h"
46
47#ifdef _POSIX_VERSION
48
49#include <sys/wait.h>
50
51#else /* not _POSIX_VERSION */
52
53/* traditional Unix */
54
55#define WIFEXITED(s) (((s) & 0377) == 0)
56#define WEXITSTATUS(s) (((s) >> 8) & 0377)
57#define WTERMSIG(s) ((s) & 0177)
58#define WIFSTOPPED(s) (((s) & 0377) == 0177)
59#define WSTOPSIG(s) (((s) >> 8) & 0377)
60#define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
61
62#endif /* not _POSIX_VERSION */
63
465b256c
JR
64// declarations to avoid friend name injections
65class tfont;
66class tfont_spec;
67tfont *make_tfont(tfont_spec &);
68
69
92d0a6a6
JR
70/*
71 * how many boundaries of images have been written? Useful for
72 * debugging grohtml
73 */
74
75int image_no = 0;
76static int suppress_start_page = 0;
77
78#define STORE_WIDTH 1
79
80symbol HYPHEN_SYMBOL("hy");
81
82// Character used when a hyphen is inserted at a line break.
83static charinfo *soft_hyphen_char;
84
85enum constant_space_type {
86 CONSTANT_SPACE_NONE,
87 CONSTANT_SPACE_RELATIVE,
88 CONSTANT_SPACE_ABSOLUTE
89 };
90
91struct special_font_list {
92 int n;
93 special_font_list *next;
94};
95
96special_font_list *global_special_fonts;
97static int global_ligature_mode = 1;
98static int global_kern_mode = 1;
99
100class track_kerning_function {
101 int non_zero;
102 units min_size;
103 hunits min_amount;
104 units max_size;
105 hunits max_amount;
106public:
107 track_kerning_function();
108 track_kerning_function(units, hunits, units, hunits);
109 int operator==(const track_kerning_function &);
110 int operator!=(const track_kerning_function &);
111 hunits compute(int point_size);
112};
113
114// embolden fontno when this is the current font
115
116struct conditional_bold {
117 conditional_bold *next;
118 int fontno;
119 hunits offset;
120 conditional_bold(int, hunits, conditional_bold * = 0);
121};
122
92d0a6a6
JR
123class font_info {
124 tfont *last_tfont;
125 int number;
126 font_size last_size;
127 int last_height;
128 int last_slant;
129 symbol internal_name;
130 symbol external_name;
131 font *fm;
132 char is_bold;
133 hunits bold_offset;
134 track_kerning_function track_kern;
135 constant_space_type is_constant_spaced;
136 units constant_space;
137 int last_ligature_mode;
138 int last_kern_mode;
139 conditional_bold *cond_bold_list;
140 void flush();
141public:
142 special_font_list *sf;
465b256c 143 font_info(symbol, int, symbol, font *);
92d0a6a6
JR
144 int contains(charinfo *);
145 void set_bold(hunits);
146 void unbold();
147 void set_conditional_bold(int, hunits);
148 void conditional_unbold(int);
149 void set_track_kern(track_kerning_function &);
150 void set_constant_space(constant_space_type, units = 0);
151 int is_named(symbol);
152 symbol get_name();
153 tfont *get_tfont(font_size, int, int, int);
154 hunits get_space_width(font_size, int);
155 hunits get_narrow_space_width(font_size);
156 hunits get_half_narrow_space_width(font_size);
157 int get_bold(hunits *);
158 int is_special();
159 int is_style();
4d3e9548
JL
160 void set_zoom(int);
161 int get_zoom();
92d0a6a6 162 friend symbol get_font_name(int, environment *);
465b256c 163 friend symbol get_style_name(int);
92d0a6a6
JR
164};
165
166class tfont_spec {
167protected:
168 symbol name;
169 int input_position;
170 font *fm;
171 font_size size;
172 char is_bold;
173 char is_constant_spaced;
174 int ligature_mode;
175 int kern_mode;
176 hunits bold_offset;
177 hunits track_kern; // add this to the width
178 hunits constant_space_width;
179 int height;
180 int slant;
181public:
465b256c 182 tfont_spec(symbol, int, font *, font_size, int, int);
92d0a6a6
JR
183 tfont_spec(const tfont_spec &spec) { *this = spec; }
184 tfont_spec plain();
185 int operator==(const tfont_spec &);
186 friend tfont *font_info::get_tfont(font_size fs, int, int, int);
187};
188
189class tfont : public tfont_spec {
190 static tfont *tfont_list;
191 tfont *next;
192 tfont *plain_version;
193public:
194 tfont(tfont_spec &);
195 int contains(charinfo *);
196 hunits get_width(charinfo *c);
197 int get_bold(hunits *);
198 int get_constant_space(hunits *);
199 hunits get_track_kern();
200 tfont *get_plain();
201 font_size get_size();
4d3e9548 202 int get_zoom();
92d0a6a6
JR
203 symbol get_name();
204 charinfo *get_lig(charinfo *c1, charinfo *c2);
205 int get_kern(charinfo *c1, charinfo *c2, hunits *res);
206 int get_input_position();
207 int get_character_type(charinfo *);
208 int get_height();
209 int get_slant();
210 vunits get_char_height(charinfo *);
211 vunits get_char_depth(charinfo *);
212 hunits get_char_skew(charinfo *);
213 hunits get_italic_correction(charinfo *);
214 hunits get_left_italic_correction(charinfo *);
215 hunits get_subscript_correction(charinfo *);
216 friend tfont *make_tfont(tfont_spec &);
217};
218
219inline int env_definite_font(environment *env)
220{
221 return env->get_family()->make_definite(env->get_font());
222}
223
224/* font_info functions */
225
226static font_info **font_table = 0;
227static int font_table_size = 0;
228
229font_info::font_info(symbol nm, int n, symbol enm, font *f)
230: last_tfont(0), number(n), last_size(0),
231 internal_name(nm), external_name(enm), fm(f),
232 is_bold(0), is_constant_spaced(CONSTANT_SPACE_NONE), last_ligature_mode(1),
233 last_kern_mode(1), cond_bold_list(0), sf(0)
234{
235}
236
237inline int font_info::contains(charinfo *ci)
238{
4d3e9548 239 return fm != 0 && fm->contains(ci->as_glyph());
92d0a6a6
JR
240}
241
242inline int font_info::is_special()
243{
244 return fm != 0 && fm->is_special();
245}
246
247inline int font_info::is_style()
248{
249 return fm == 0;
250}
251
4d3e9548
JL
252void font_info::set_zoom(int zoom)
253{
254 assert(fm != 0);
255 fm->set_zoom(zoom);
256}
257
258inline int font_info::get_zoom()
259{
260 if (is_style())
261 return 0;
262 return fm->get_zoom();
263}
264
92d0a6a6
JR
265tfont *make_tfont(tfont_spec &spec)
266{
267 for (tfont *p = tfont::tfont_list; p; p = p->next)
268 if (*p == spec)
269 return p;
270 return new tfont(spec);
271}
272
4d3e9548
JL
273int env_get_zoom(environment *env)
274{
275 int fontno = env->get_family()->make_definite(env->get_font());
276 return font_table[fontno]->get_zoom();
277}
278
92d0a6a6
JR
279// this is the current_font, fontno is where we found the character,
280// presumably a special font
281
282tfont *font_info::get_tfont(font_size fs, int height, int slant, int fontno)
283{
284 if (last_tfont == 0 || fs != last_size
285 || height != last_height || slant != last_slant
286 || global_ligature_mode != last_ligature_mode
287 || global_kern_mode != last_kern_mode
288 || fontno != number) {
289 font_info *f = font_table[fontno];
290 tfont_spec spec(f->external_name, f->number, f->fm, fs, height, slant);
291 for (conditional_bold *p = cond_bold_list; p; p = p->next)
292 if (p->fontno == fontno) {
293 spec.is_bold = 1;
294 spec.bold_offset = p->offset;
295 break;
296 }
297 if (!spec.is_bold && is_bold) {
298 spec.is_bold = 1;
299 spec.bold_offset = bold_offset;
300 }
301 spec.track_kern = track_kern.compute(fs.to_scaled_points());
302 spec.ligature_mode = global_ligature_mode;
303 spec.kern_mode = global_kern_mode;
304 switch (is_constant_spaced) {
305 case CONSTANT_SPACE_NONE:
306 break;
307 case CONSTANT_SPACE_ABSOLUTE:
308 spec.is_constant_spaced = 1;
309 spec.constant_space_width = constant_space;
310 break;
311 case CONSTANT_SPACE_RELATIVE:
312 spec.is_constant_spaced = 1;
313 spec.constant_space_width
314 = scale(constant_space*fs.to_scaled_points(),
315 units_per_inch,
316 36*72*sizescale);
317 break;
318 default:
319 assert(0);
320 }
321 if (fontno != number)
322 return make_tfont(spec);
4d3e9548 323 // save font for comparison purposes
92d0a6a6 324 last_tfont = make_tfont(spec);
4d3e9548 325 // save font related values not contained in tfont
92d0a6a6
JR
326 last_size = fs;
327 last_height = height;
328 last_slant = slant;
329 last_ligature_mode = global_ligature_mode;
330 last_kern_mode = global_kern_mode;
331 }
332 return last_tfont;
333}
334
335int font_info::get_bold(hunits *res)
336{
337 if (is_bold) {
338 *res = bold_offset;
339 return 1;
340 }
341 else
342 return 0;
343}
344
345void font_info::unbold()
346{
347 if (is_bold) {
348 is_bold = 0;
349 flush();
350 }
351}
352
353void font_info::set_bold(hunits offset)
354{
355 if (!is_bold || offset != bold_offset) {
356 is_bold = 1;
357 bold_offset = offset;
358 flush();
359 }
360}
361
362void font_info::set_conditional_bold(int fontno, hunits offset)
363{
364 for (conditional_bold *p = cond_bold_list; p; p = p->next)
365 if (p->fontno == fontno) {
366 if (offset != p->offset) {
367 p->offset = offset;
368 flush();
369 }
370 return;
371 }
372 cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
373}
374
375conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
376: next(x), fontno(f), offset(h)
377{
378}
379
380void font_info::conditional_unbold(int fontno)
381{
382 for (conditional_bold **p = &cond_bold_list; *p; p = &(*p)->next)
383 if ((*p)->fontno == fontno) {
384 conditional_bold *tem = *p;
385 *p = (*p)->next;
386 delete tem;
387 flush();
388 return;
389 }
390}
391
392void font_info::set_constant_space(constant_space_type type, units x)
393{
394 if (type != is_constant_spaced
395 || (type != CONSTANT_SPACE_NONE && x != constant_space)) {
396 flush();
397 is_constant_spaced = type;
398 constant_space = x;
399 }
400}
401
402void font_info::set_track_kern(track_kerning_function &tk)
403{
404 if (track_kern != tk) {
405 track_kern = tk;
406 flush();
407 }
408}
409
410void font_info::flush()
411{
412 last_tfont = 0;
413}
414
415int font_info::is_named(symbol s)
416{
417 return internal_name == s;
418}
419
420symbol font_info::get_name()
421{
422 return internal_name;
423}
424
425symbol get_font_name(int fontno, environment *env)
426{
427 symbol f = font_table[fontno]->get_name();
428 if (font_table[fontno]->is_style()) {
429 return concat(env->get_family()->nm, f);
430 }
431 return f;
432}
433
465b256c
JR
434symbol get_style_name(int fontno)
435{
436 if (font_table[fontno]->is_style())
437 return font_table[fontno]->get_name();
438 else
439 return EMPTY_SYMBOL;
440}
441
92d0a6a6
JR
442hunits font_info::get_space_width(font_size fs, int space_sz)
443{
444 if (is_constant_spaced == CONSTANT_SPACE_NONE)
445 return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
446 space_sz, 12);
447 else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
448 return constant_space;
449 else
450 return scale(constant_space*fs.to_scaled_points(),
451 units_per_inch, 36*72*sizescale);
452}
453
454hunits font_info::get_narrow_space_width(font_size fs)
455{
456 charinfo *ci = get_charinfo(symbol("|"));
4d3e9548
JL
457 if (fm->contains(ci->as_glyph()))
458 return hunits(fm->get_width(ci->as_glyph(), fs.to_scaled_points()));
92d0a6a6
JR
459 else
460 return hunits(fs.to_units()/6);
461}
462
463hunits font_info::get_half_narrow_space_width(font_size fs)
464{
465 charinfo *ci = get_charinfo(symbol("^"));
4d3e9548
JL
466 if (fm->contains(ci->as_glyph()))
467 return hunits(fm->get_width(ci->as_glyph(), fs.to_scaled_points()));
92d0a6a6
JR
468 else
469 return hunits(fs.to_units()/12);
470}
471
472/* tfont */
473
474tfont_spec::tfont_spec(symbol nm, int n, font *f,
475 font_size s, int h, int sl)
476: name(nm), input_position(n), fm(f), size(s),
477 is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1),
478 height(h), slant(sl)
479{
480 if (height == size.to_scaled_points())
481 height = 0;
482}
483
484int tfont_spec::operator==(const tfont_spec &spec)
485{
486 if (fm == spec.fm
487 && size == spec.size
488 && input_position == spec.input_position
489 && name == spec.name
490 && height == spec.height
491 && slant == spec.slant
492 && (is_bold
493 ? (spec.is_bold && bold_offset == spec.bold_offset)
494 : !spec.is_bold)
495 && track_kern == spec.track_kern
496 && (is_constant_spaced
497 ? (spec.is_constant_spaced
498 && constant_space_width == spec.constant_space_width)
499 : !spec.is_constant_spaced)
500 && ligature_mode == spec.ligature_mode
501 && kern_mode == spec.kern_mode)
502 return 1;
503 else
504 return 0;
505}
506
507tfont_spec tfont_spec::plain()
508{
509 return tfont_spec(name, input_position, fm, size, height, slant);
510}
511
512hunits tfont::get_width(charinfo *c)
513{
514 if (is_constant_spaced)
515 return constant_space_width;
516 else if (is_bold)
4d3e9548 517 return (hunits(fm->get_width(c->as_glyph(), size.to_scaled_points()))
92d0a6a6
JR
518 + track_kern + bold_offset);
519 else
4d3e9548 520 return (hunits(fm->get_width(c->as_glyph(), size.to_scaled_points()))
92d0a6a6
JR
521 + track_kern);
522}
523
524vunits tfont::get_char_height(charinfo *c)
525{
4d3e9548 526 vunits v = fm->get_height(c->as_glyph(), size.to_scaled_points());
92d0a6a6
JR
527 if (height != 0 && height != size.to_scaled_points())
528 return scale(v, height, size.to_scaled_points());
529 else
530 return v;
531}
532
533vunits tfont::get_char_depth(charinfo *c)
534{
4d3e9548 535 vunits v = fm->get_depth(c->as_glyph(), size.to_scaled_points());
92d0a6a6
JR
536 if (height != 0 && height != size.to_scaled_points())
537 return scale(v, height, size.to_scaled_points());
538 else
539 return v;
540}
541
542hunits tfont::get_char_skew(charinfo *c)
543{
4d3e9548 544 return hunits(fm->get_skew(c->as_glyph(), size.to_scaled_points(), slant));
92d0a6a6
JR
545}
546
547hunits tfont::get_italic_correction(charinfo *c)
548{
4d3e9548 549 return hunits(fm->get_italic_correction(c->as_glyph(), size.to_scaled_points()));
92d0a6a6
JR
550}
551
552hunits tfont::get_left_italic_correction(charinfo *c)
553{
4d3e9548 554 return hunits(fm->get_left_italic_correction(c->as_glyph(),
92d0a6a6
JR
555 size.to_scaled_points()));
556}
557
558hunits tfont::get_subscript_correction(charinfo *c)
559{
4d3e9548 560 return hunits(fm->get_subscript_correction(c->as_glyph(),
92d0a6a6
JR
561 size.to_scaled_points()));
562}
563
564inline int tfont::get_input_position()
565{
566 return input_position;
567}
568
569inline int tfont::contains(charinfo *ci)
570{
4d3e9548 571 return fm->contains(ci->as_glyph());
92d0a6a6
JR
572}
573
574inline int tfont::get_character_type(charinfo *ci)
575{
4d3e9548 576 return fm->get_character_type(ci->as_glyph());
92d0a6a6
JR
577}
578
579inline int tfont::get_bold(hunits *res)
580{
581 if (is_bold) {
582 *res = bold_offset;
583 return 1;
584 }
585 else
586 return 0;
587}
588
589inline int tfont::get_constant_space(hunits *res)
590{
591 if (is_constant_spaced) {
592 *res = constant_space_width;
593 return 1;
594 }
595 else
596 return 0;
597}
598
599inline hunits tfont::get_track_kern()
600{
601 return track_kern;
602}
603
604inline tfont *tfont::get_plain()
605{
606 return plain_version;
607}
608
609inline font_size tfont::get_size()
610{
611 return size;
612}
613
4d3e9548
JL
614inline int tfont::get_zoom()
615{
616 return fm->get_zoom();
617}
618
92d0a6a6
JR
619inline symbol tfont::get_name()
620{
621 return name;
622}
623
624inline int tfont::get_height()
625{
626 return height;
627}
628
629inline int tfont::get_slant()
630{
631 return slant;
632}
633
634symbol SYMBOL_ff("ff");
635symbol SYMBOL_fi("fi");
636symbol SYMBOL_fl("fl");
637symbol SYMBOL_Fi("Fi");
638symbol SYMBOL_Fl("Fl");
639
640charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
641{
642 if (ligature_mode == 0)
643 return 0;
644 charinfo *ci = 0;
645 if (c1->get_ascii_code() == 'f') {
646 switch (c2->get_ascii_code()) {
647 case 'f':
648 if (fm->has_ligature(font::LIG_ff))
649 ci = get_charinfo(SYMBOL_ff);
650 break;
651 case 'i':
652 if (fm->has_ligature(font::LIG_fi))
653 ci = get_charinfo(SYMBOL_fi);
654 break;
655 case 'l':
656 if (fm->has_ligature(font::LIG_fl))
657 ci = get_charinfo(SYMBOL_fl);
658 break;
659 }
660 }
661 else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
662 switch (c2->get_ascii_code()) {
663 case 'i':
664 if (fm->has_ligature(font::LIG_ffi))
665 ci = get_charinfo(SYMBOL_Fi);
666 break;
667 case 'l':
668 if (fm->has_ligature(font::LIG_ffl))
669 ci = get_charinfo(SYMBOL_Fl);
670 break;
671 }
672 }
4d3e9548 673 if (ci != 0 && fm->contains(ci->as_glyph()))
92d0a6a6
JR
674 return ci;
675 return 0;
676}
677
678inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res)
679{
680 if (kern_mode == 0)
681 return 0;
682 else {
4d3e9548
JL
683 int n = fm->get_kern(c1->as_glyph(),
684 c2->as_glyph(),
92d0a6a6
JR
685 size.to_scaled_points());
686 if (n) {
687 *res = hunits(n);
688 return 1;
689 }
690 else
691 return 0;
692 }
693}
694
695tfont *tfont::tfont_list = 0;
696
697tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
698{
699 next = tfont_list;
700 tfont_list = this;
701 tfont_spec plain_spec = plain();
702 tfont *p;
703 for (p = tfont_list; p; p = p->next)
704 if (*p == plain_spec) {
705 plain_version = p;
706 break;
707 }
708 if (!p)
709 plain_version = new tfont(plain_spec);
710}
711
712/* output_file */
713
714class real_output_file : public output_file {
715#ifndef POPEN_MISSING
716 int piped;
717#endif
4d3e9548
JL
718 int printing; // decision via optional page list
719 int output_on; // \O[0] or \O[1] escape calls
92d0a6a6
JR
720 virtual void really_transparent_char(unsigned char) = 0;
721 virtual void really_print_line(hunits x, vunits y, node *n,
722 vunits before, vunits after, hunits width) = 0;
723 virtual void really_begin_page(int pageno, vunits page_length) = 0;
724 virtual void really_copy_file(hunits x, vunits y, const char *filename);
4d3e9548 725 virtual void really_put_filename(const char *, int);
92d0a6a6
JR
726 virtual void really_on();
727 virtual void really_off();
92d0a6a6 728public:
465b256c 729 FILE *fp;
92d0a6a6
JR
730 real_output_file();
731 ~real_output_file();
732 void flush();
733 void transparent_char(unsigned char);
734 void print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
735 void begin_page(int pageno, vunits page_length);
4d3e9548 736 void put_filename(const char *, int);
92d0a6a6
JR
737 void on();
738 void off();
739 int is_on();
740 int is_printing();
741 void copy_file(hunits x, vunits y, const char *filename);
742};
743
744class suppress_output_file : public real_output_file {
745public:
746 suppress_output_file();
747 void really_transparent_char(unsigned char);
748 void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
749 void really_begin_page(int pageno, vunits page_length);
750};
751
752class ascii_output_file : public real_output_file {
753public:
754 ascii_output_file();
755 void really_transparent_char(unsigned char);
756 void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
757 void really_begin_page(int pageno, vunits page_length);
758 void outc(unsigned char c);
759 void outs(const char *s);
760};
761
762void ascii_output_file::outc(unsigned char c)
763{
764 fputc(c, fp);
765}
766
767void ascii_output_file::outs(const char *s)
768{
769 fputc('<', fp);
770 if (s)
771 fputs(s, fp);
772 fputc('>', fp);
773}
774
775struct hvpair;
776
777class troff_output_file : public real_output_file {
778 units hpos;
779 units vpos;
780 units output_vpos;
781 units output_hpos;
782 int force_motion;
783 int current_size;
784 int current_slant;
785 int current_height;
786 tfont *current_tfont;
787 color *current_fill_color;
788 color *current_glyph_color;
789 int current_font_number;
790 symbol *font_position;
791 int nfont_positions;
792 enum { TBUF_SIZE = 256 };
793 char tbuf[TBUF_SIZE];
794 int tbuf_len;
795 int tbuf_kern;
796 int begun_page;
465b256c
JR
797 int cur_div_level;
798 string tag_list;
92d0a6a6
JR
799 void do_motion();
800 void put(char c);
801 void put(unsigned char c);
802 void put(int i);
803 void put(unsigned int i);
804 void put(const char *s);
805 void set_font(tfont *tf);
806 void flush_tbuf();
807public:
808 troff_output_file();
809 ~troff_output_file();
810 void trailer(vunits page_length);
811 void put_char(charinfo *, tfont *, color *, color *);
812 void put_char_width(charinfo *, tfont *, color *, color *, hunits, hunits);
813 void right(hunits);
814 void down(vunits);
815 void moveto(hunits, vunits);
816 void start_special(tfont *, color *, color *, int = 0);
817 void start_special();
818 void special_char(unsigned char c);
819 void end_special();
820 void word_marker();
821 void really_transparent_char(unsigned char c);
822 void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
823 void really_begin_page(int pageno, vunits page_length);
824 void really_copy_file(hunits x, vunits y, const char *filename);
4d3e9548 825 void really_put_filename(const char *, int);
92d0a6a6
JR
826 void really_on();
827 void really_off();
828 void draw(char, hvpair *, int, font_size, color *, color *);
829 void determine_line_limits (char code, hvpair *point, int npoints);
830 void check_charinfo(tfont *tf, charinfo *ci);
831 void glyph_color(color *c);
832 void fill_color(color *c);
833 int get_hpos() { return hpos; }
834 int get_vpos() { return vpos; }
465b256c 835 void add_to_tag_list(string s);
92d0a6a6
JR
836 friend void space_char_hmotion_node::tprint(troff_output_file *);
837 friend void unbreakable_space_node::tprint(troff_output_file *);
838};
839
840static void put_string(const char *s, FILE *fp)
841{
842 for (; *s != '\0'; ++s)
843 putc(*s, fp);
844}
845
846inline void troff_output_file::put(char c)
847{
848 putc(c, fp);
849}
850
851inline void troff_output_file::put(unsigned char c)
852{
853 putc(c, fp);
854}
855
856inline void troff_output_file::put(const char *s)
857{
858 put_string(s, fp);
859}
860
861inline void troff_output_file::put(int i)
862{
863 put_string(i_to_a(i), fp);
864}
865
866inline void troff_output_file::put(unsigned int i)
867{
868 put_string(ui_to_a(i), fp);
869}
870
871void troff_output_file::start_special(tfont *tf, color *gcol, color *fcol,
872 int no_init_string)
873{
874 set_font(tf);
875 glyph_color(gcol);
876 fill_color(fcol);
877 flush_tbuf();
878 do_motion();
879 if (!no_init_string)
880 put("x X ");
881}
882
883void troff_output_file::start_special()
884{
885 flush_tbuf();
886 do_motion();
887 put("x X ");
888}
889
890void troff_output_file::special_char(unsigned char c)
891{
892 put(c);
893 if (c == '\n')
894 put('+');
895}
896
897void troff_output_file::end_special()
898{
899 put('\n');
900}
901
902inline void troff_output_file::moveto(hunits h, vunits v)
903{
904 hpos = h.to_units();
905 vpos = v.to_units();
906}
907
908void troff_output_file::really_print_line(hunits x, vunits y, node *n,
909 vunits before, vunits after, hunits)
910{
911 moveto(x, y);
912 while (n != 0) {
465b256c
JR
913 // Check whether we should push the current troff state and use
914 // the state at the start of the invocation of this diversion.
915 if (n->div_nest_level > cur_div_level && n->push_state) {
916 state.push_state(n->push_state);
917 cur_div_level = n->div_nest_level;
918 }
919 // Has the current diversion level decreased? Then we must pop the
920 // troff state.
921 while (n->div_nest_level < cur_div_level) {
922 state.pop_state();
923 cur_div_level = n->div_nest_level;
924 }
925 // Now check whether the state has changed.
926 if ((is_on() || n->force_tprint())
927 && (state.changed(n->state) || n->is_tag() || n->is_special)) {
928 flush_tbuf();
929 do_motion();
930 force_motion = 1;
931 flush();
932 state.flush(fp, n->state, tag_list);
933 tag_list = string("");
934 flush();
935 }
92d0a6a6
JR
936 n->tprint(this);
937 n = n->next;
938 }
939 flush_tbuf();
940 // This ensures that transparent throughput will have a more predictable
941 // position.
942 do_motion();
943 force_motion = 1;
944 hpos = 0;
945 put('n');
946 put(before.to_units());
947 put(' ');
948 put(after.to_units());
949 put('\n');
950}
951
952inline void troff_output_file::word_marker()
953{
954 flush_tbuf();
955 if (is_on())
956 put('w');
957}
958
959inline void troff_output_file::right(hunits n)
960{
961 hpos += n.to_units();
962}
963
964inline void troff_output_file::down(vunits n)
965{
966 vpos += n.to_units();
967}
968
969void troff_output_file::do_motion()
970{
971 if (force_motion) {
972 put('V');
973 put(vpos);
974 put('\n');
975 put('H');
976 put(hpos);
977 put('\n');
978 }
979 else {
980 if (hpos != output_hpos) {
981 units n = hpos - output_hpos;
982 if (n > 0 && n < hpos) {
983 put('h');
984 put(n);
985 }
986 else {
987 put('H');
988 put(hpos);
989 }
990 put('\n');
991 }
992 if (vpos != output_vpos) {
993 units n = vpos - output_vpos;
994 if (n > 0 && n < vpos) {
995 put('v');
996 put(n);
997 }
998 else {
999 put('V');
1000 put(vpos);
1001 }
1002 put('\n');
1003 }
1004 }
1005 output_vpos = vpos;
1006 output_hpos = hpos;
1007 force_motion = 0;
1008}
1009
1010void troff_output_file::flush_tbuf()
1011{
1012 if (!is_on()) {
1013 tbuf_len = 0;
1014 return;
1015 }
1016
1017 if (tbuf_len == 0)
1018 return;
1019 if (tbuf_kern == 0)
1020 put('t');
1021 else {
1022 put('u');
1023 put(tbuf_kern);
1024 put(' ');
1025 }
1026 check_output_limits(hpos, vpos);
1027 check_output_limits(hpos, vpos - current_size);
1028
1029 for (int i = 0; i < tbuf_len; i++)
1030 put(tbuf[i]);
1031 put('\n');
1032 tbuf_len = 0;
1033}
1034
1035void troff_output_file::check_charinfo(tfont *tf, charinfo *ci)
1036{
1037 if (!is_on())
1038 return;
1039
1040 int height = tf->get_char_height(ci).to_units();
1041 int width = tf->get_width(ci).to_units()
1042 + tf->get_italic_correction(ci).to_units();
1043 int depth = tf->get_char_depth(ci).to_units();
1044 check_output_limits(output_hpos, output_vpos - height);
1045 check_output_limits(output_hpos + width, output_vpos + depth);
1046}
1047
1048void troff_output_file::put_char_width(charinfo *ci, tfont *tf,
1049 color *gcol, color *fcol,
1050 hunits w, hunits k)
1051{
1052 int kk = k.to_units();
1053 if (!is_on()) {
1054 flush_tbuf();
1055 hpos += w.to_units() + kk;
1056 return;
1057 }
1058 set_font(tf);
1059 unsigned char c = ci->get_ascii_code();
1060 if (c == '\0') {
1061 glyph_color(gcol);
1062 fill_color(fcol);
1063 flush_tbuf();
1064 do_motion();
1065 check_charinfo(tf, ci);
1066 if (ci->numbered()) {
1067 put('N');
1068 put(ci->get_number());
1069 }
1070 else {
1071 put('C');
1072 const char *s = ci->nm.contents();
1073 if (s[1] == 0) {
1074 put('\\');
1075 put(s[0]);
1076 }
1077 else
1078 put(s);
1079 }
1080 put('\n');
1081 hpos += w.to_units() + kk;
1082 }
1083 else if (tcommand_flag) {
1084 if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
1085 && (!gcol || gcol == current_glyph_color)
1086 && (!fcol || fcol == current_fill_color)
1087 && kk == tbuf_kern
1088 && tbuf_len < TBUF_SIZE) {
1089 check_charinfo(tf, ci);
1090 tbuf[tbuf_len++] = c;
1091 output_hpos += w.to_units() + kk;
1092 hpos = output_hpos;
1093 return;
1094 }
1095 glyph_color(gcol);
1096 fill_color(fcol);
1097 flush_tbuf();
1098 do_motion();
1099 check_charinfo(tf, ci);
1100 tbuf[tbuf_len++] = c;
1101 output_hpos += w.to_units() + kk;
1102 tbuf_kern = kk;
1103 hpos = output_hpos;
1104 }
1105 else {
1106 // flush_tbuf();
1107 int n = hpos - output_hpos;
1108 check_charinfo(tf, ci);
1109 // check_output_limits(output_hpos, output_vpos);
1110 if (vpos == output_vpos
1111 && (!gcol || gcol == current_glyph_color)
1112 && (!fcol || fcol == current_fill_color)
1113 && n > 0 && n < 100 && !force_motion) {
1114 put(char(n/10 + '0'));
1115 put(char(n%10 + '0'));
1116 put(c);
1117 output_hpos = hpos;
1118 }
1119 else {
1120 glyph_color(gcol);
1121 fill_color(fcol);
1122 do_motion();
1123 put('c');
1124 put(c);
1125 }
1126 hpos += w.to_units() + kk;
1127 }
1128}
1129
1130void troff_output_file::put_char(charinfo *ci, tfont *tf,
1131 color *gcol, color *fcol)
1132{
1133 flush_tbuf();
1134 if (!is_on())
1135 return;
1136 set_font(tf);
1137 unsigned char c = ci->get_ascii_code();
1138 if (c == '\0') {
1139 glyph_color(gcol);
1140 fill_color(fcol);
1141 flush_tbuf();
1142 do_motion();
1143 if (ci->numbered()) {
1144 put('N');
1145 put(ci->get_number());
1146 }
1147 else {
1148 put('C');
1149 const char *s = ci->nm.contents();
1150 if (s[1] == 0) {
1151 put('\\');
1152 put(s[0]);
1153 }
1154 else
1155 put(s);
1156 }
1157 put('\n');
1158 }
1159 else {
1160 int n = hpos - output_hpos;
1161 if (vpos == output_vpos
1162 && (!gcol || gcol == current_glyph_color)
1163 && (!fcol || fcol == current_fill_color)
1164 && n > 0 && n < 100) {
1165 put(char(n/10 + '0'));
1166 put(char(n%10 + '0'));
1167 put(c);
1168 output_hpos = hpos;
1169 }
1170 else {
1171 glyph_color(gcol);
1172 fill_color(fcol);
1173 flush_tbuf();
1174 do_motion();
1175 put('c');
1176 put(c);
1177 }
1178 }
1179}
1180
1181// set_font calls `flush_tbuf' if necessary.
1182
1183void troff_output_file::set_font(tfont *tf)
1184{
1185 if (current_tfont == tf)
1186 return;
1187 flush_tbuf();
1188 int n = tf->get_input_position();
1189 symbol nm = tf->get_name();
1190 if (n >= nfont_positions || font_position[n] != nm) {
1191 put("x font ");
1192 put(n);
1193 put(' ');
1194 put(nm.contents());
1195 put('\n');
1196 if (n >= nfont_positions) {
1197 int old_nfont_positions = nfont_positions;
1198 symbol *old_font_position = font_position;
1199 nfont_positions *= 3;
1200 nfont_positions /= 2;
1201 if (nfont_positions <= n)
1202 nfont_positions = n + 10;
1203 font_position = new symbol[nfont_positions];
1204 memcpy(font_position, old_font_position,
1205 old_nfont_positions*sizeof(symbol));
1206 a_delete old_font_position;
1207 }
1208 font_position[n] = nm;
1209 }
1210 if (current_font_number != n) {
1211 put('f');
1212 put(n);
1213 put('\n');
1214 current_font_number = n;
1215 }
4d3e9548
JL
1216 int zoom = tf->get_zoom();
1217 int size;
1218 if (zoom)
1219 size = scale(tf->get_size().to_scaled_points(),
1220 zoom, 1000);
1221 else
1222 size = tf->get_size().to_scaled_points();
92d0a6a6
JR
1223 if (current_size != size) {
1224 put('s');
1225 put(size);
1226 put('\n');
1227 current_size = size;
1228 }
1229 int slant = tf->get_slant();
1230 if (current_slant != slant) {
1231 put("x Slant ");
1232 put(slant);
1233 put('\n');
1234 current_slant = slant;
1235 }
1236 int height = tf->get_height();
1237 if (current_height != height) {
1238 put("x Height ");
1239 put(height == 0 ? current_size : height);
1240 put('\n');
1241 current_height = height;
1242 }
1243 current_tfont = tf;
1244}
1245
1246// fill_color calls `flush_tbuf' and `do_motion' if necessary.
1247
1248void troff_output_file::fill_color(color *col)
1249{
1250 if (!col || current_fill_color == col)
1251 return;
1252 current_fill_color = col;
1253 if (!color_flag)
1254 return;
1255 flush_tbuf();
1256 do_motion();
1257 put("DF");
1258 unsigned int components[4];
1259 color_scheme cs;
1260 cs = col->get_components(components);
1261 switch (cs) {
1262 case DEFAULT:
1263 put('d');
1264 break;
1265 case RGB:
1266 put("r ");
1267 put(Red);
1268 put(' ');
1269 put(Green);
1270 put(' ');
1271 put(Blue);
1272 break;
1273 case CMY:
1274 put("c ");
1275 put(Cyan);
1276 put(' ');
1277 put(Magenta);
1278 put(' ');
1279 put(Yellow);
1280 break;
1281 case CMYK:
1282 put("k ");
1283 put(Cyan);
1284 put(' ');
1285 put(Magenta);
1286 put(' ');
1287 put(Yellow);
1288 put(' ');
1289 put(Black);
1290 break;
1291 case GRAY:
1292 put("g ");
1293 put(Gray);
1294 break;
1295 }
1296 put('\n');
1297}
1298
1299// glyph_color calls `flush_tbuf' and `do_motion' if necessary.
1300
1301void troff_output_file::glyph_color(color *col)
1302{
1303 if (!col || current_glyph_color == col)
1304 return;
1305 current_glyph_color = col;
1306 if (!color_flag)
1307 return;
1308 flush_tbuf();
1309 // grotty doesn't like a color command if the vertical position is zero.
1310 do_motion();
1311 put("m");
1312 unsigned int components[4];
1313 color_scheme cs;
1314 cs = col->get_components(components);
1315 switch (cs) {
1316 case DEFAULT:
1317 put('d');
1318 break;
1319 case RGB:
1320 put("r ");
1321 put(Red);
1322 put(' ');
1323 put(Green);
1324 put(' ');
1325 put(Blue);
1326 break;
1327 case CMY:
1328 put("c ");
1329 put(Cyan);
1330 put(' ');
1331 put(Magenta);
1332 put(' ');
1333 put(Yellow);
1334 break;
1335 case CMYK:
1336 put("k ");
1337 put(Cyan);
1338 put(' ');
1339 put(Magenta);
1340 put(' ');
1341 put(Yellow);
1342 put(' ');
1343 put(Black);
1344 break;
1345 case GRAY:
1346 put("g ");
1347 put(Gray);
1348 break;
1349 }
1350 put('\n');
1351}
1352
465b256c
JR
1353void troff_output_file::add_to_tag_list(string s)
1354{
1355 if (tag_list == string(""))
1356 tag_list = s;
1357 else {
1358 tag_list += string("\n");
1359 tag_list += s;
1360 }
1361}
1362
92d0a6a6
JR
1363// determine_line_limits - works out the smallest box which will contain
1364// the entity, code, built from the point array.
1365void troff_output_file::determine_line_limits(char code, hvpair *point,
1366 int npoints)
1367{
1368 int i, x, y;
1369
1370 if (!is_on())
1371 return;
1372
1373 switch (code) {
1374 case 'c':
1375 case 'C':
1376 // only the h field is used when defining a circle
1377 check_output_limits(output_hpos,
1378 output_vpos - point[0].h.to_units()/2);
1379 check_output_limits(output_hpos + point[0].h.to_units(),
1380 output_vpos + point[0].h.to_units()/2);
1381 break;
1382 case 'E':
1383 case 'e':
1384 check_output_limits(output_hpos,
1385 output_vpos - point[0].v.to_units()/2);
1386 check_output_limits(output_hpos + point[0].h.to_units(),
1387 output_vpos + point[0].v.to_units()/2);
1388 break;
1389 case 'P':
1390 case 'p':
1391 x = output_hpos;
1392 y = output_vpos;
1393 check_output_limits(x, y);
1394 for (i = 0; i < npoints; i++) {
1395 x += point[i].h.to_units();
1396 y += point[i].v.to_units();
1397 check_output_limits(x, y);
1398 }
1399 break;
1400 case 't':
1401 x = output_hpos;
1402 y = output_vpos;
1403 for (i = 0; i < npoints; i++) {
1404 x += point[i].h.to_units();
1405 y += point[i].v.to_units();
1406 check_output_limits(x, y);
1407 }
1408 break;
1409 case 'a':
1410 double c[2];
1411 int p[4];
1412 int minx, miny, maxx, maxy;
1413 x = output_hpos;
1414 y = output_vpos;
1415 p[0] = point[0].h.to_units();
1416 p[1] = point[0].v.to_units();
1417 p[2] = point[1].h.to_units();
1418 p[3] = point[1].v.to_units();
1419 if (adjust_arc_center(p, c)) {
1420 check_output_arc_limits(x, y,
1421 p[0], p[1], p[2], p[3],
1422 c[0], c[1],
1423 &minx, &maxx, &miny, &maxy);
1424 check_output_limits(minx, miny);
1425 check_output_limits(maxx, maxy);
1426 break;
1427 }
1428 // fall through
1429 case 'l':
1430 x = output_hpos;
1431 y = output_vpos;
1432 check_output_limits(x, y);
1433 for (i = 0; i < npoints; i++) {
1434 x += point[i].h.to_units();
1435 y += point[i].v.to_units();
1436 check_output_limits(x, y);
1437 }
1438 break;
1439 default:
1440 x = output_hpos;
1441 y = output_vpos;
1442 for (i = 0; i < npoints; i++) {
1443 x += point[i].h.to_units();
1444 y += point[i].v.to_units();
1445 check_output_limits(x, y);
1446 }
1447 }
1448}
1449
1450void troff_output_file::draw(char code, hvpair *point, int npoints,
1451 font_size fsize, color *gcol, color *fcol)
1452{
1453 int i;
1454 glyph_color(gcol);
1455 fill_color(fcol);
1456 flush_tbuf();
1457 do_motion();
1458 if (is_on()) {
1459 int size = fsize.to_scaled_points();
1460 if (current_size != size) {
1461 put('s');
1462 put(size);
1463 put('\n');
1464 current_size = size;
1465 current_tfont = 0;
1466 }
1467 put('D');
1468 put(code);
1469 if (code == 'c') {
1470 put(' ');
1471 put(point[0].h.to_units());
1472 }
1473 else
1474 for (i = 0; i < npoints; i++) {
1475 put(' ');
1476 put(point[i].h.to_units());
1477 put(' ');
1478 put(point[i].v.to_units());
1479 }
1480 determine_line_limits(code, point, npoints);
1481 }
1482
1483 for (i = 0; i < npoints; i++)
1484 output_hpos += point[i].h.to_units();
1485 hpos = output_hpos;
1486 if (code != 'e') {
1487 for (i = 0; i < npoints; i++)
1488 output_vpos += point[i].v.to_units();
1489 vpos = output_vpos;
1490 }
1491 if (is_on())
1492 put('\n');
1493}
1494
1495void troff_output_file::really_on()
1496{
1497 flush_tbuf();
1498 force_motion = 1;
1499 do_motion();
1500}
1501
1502void troff_output_file::really_off()
1503{
1504 flush_tbuf();
1505}
1506
4d3e9548 1507void troff_output_file::really_put_filename(const char *filename, int po)
92d0a6a6
JR
1508{
1509 flush_tbuf();
1510 put("F ");
4d3e9548
JL
1511 if (po)
1512 put("<");
92d0a6a6 1513 put(filename);
4d3e9548
JL
1514 if (po)
1515 put(">");
92d0a6a6
JR
1516 put('\n');
1517}
1518
1519void troff_output_file::really_begin_page(int pageno, vunits page_length)
1520{
1521 flush_tbuf();
1522 if (begun_page) {
1523 if (page_length > V0) {
1524 put('V');
1525 put(page_length.to_units());
1526 put('\n');
1527 }
1528 }
1529 else
1530 begun_page = 1;
1531 current_tfont = 0;
1532 current_font_number = -1;
1533 current_size = 0;
1534 // current_height = 0;
1535 // current_slant = 0;
1536 hpos = 0;
1537 vpos = 0;
1538 output_hpos = 0;
1539 output_vpos = 0;
1540 force_motion = 1;
1541 for (int i = 0; i < nfont_positions; i++)
1542 font_position[i] = NULL_SYMBOL;
1543 put('p');
1544 put(pageno);
1545 put('\n');
1546}
1547
1548void troff_output_file::really_copy_file(hunits x, vunits y,
1549 const char *filename)
1550{
1551 moveto(x, y);
1552 flush_tbuf();
1553 do_motion();
1554 errno = 0;
1555 FILE *ifp = include_search_path.open_file_cautious(filename);
1556 if (ifp == 0)
1557 error("can't open `%1': %2", filename, strerror(errno));
1558 else {
1559 int c;
1560 while ((c = getc(ifp)) != EOF)
1561 put(char(c));
1562 fclose(ifp);
1563 }
1564 force_motion = 1;
1565 current_size = 0;
1566 current_tfont = 0;
1567 current_font_number = -1;
1568 for (int i = 0; i < nfont_positions; i++)
1569 font_position[i] = NULL_SYMBOL;
1570}
1571
1572void troff_output_file::really_transparent_char(unsigned char c)
1573{
1574 put(c);
1575}
1576
1577troff_output_file::~troff_output_file()
1578{
1579 a_delete font_position;
1580}
1581
1582void troff_output_file::trailer(vunits page_length)
1583{
1584 flush_tbuf();
1585 if (page_length > V0) {
1586 put("x trailer\n");
1587 put('V');
1588 put(page_length.to_units());
1589 put('\n');
1590 }
1591 put("x stop\n");
1592}
1593
1594troff_output_file::troff_output_file()
1595: current_slant(0), current_height(0), current_fill_color(0),
465b256c
JR
1596 current_glyph_color(0), nfont_positions(10), tbuf_len(0), begun_page(0),
1597 cur_div_level(0)
92d0a6a6
JR
1598{
1599 font_position = new symbol[nfont_positions];
1600 put("x T ");
1601 put(device);
1602 put('\n');
1603 put("x res ");
1604 put(units_per_inch);
1605 put(' ');
1606 put(hresolution);
1607 put(' ');
1608 put(vresolution);
1609 put('\n');
1610 put("x init\n");
1611}
1612
1613/* output_file */
1614
1615output_file *the_output = 0;
1616
1617output_file::output_file()
1618{
1619}
1620
1621output_file::~output_file()
1622{
1623}
1624
1625void output_file::trailer(vunits)
1626{
1627}
1628
4d3e9548 1629void output_file::put_filename(const char *, int)
92d0a6a6
JR
1630{
1631}
1632
1633void output_file::on()
1634{
1635}
1636
1637void output_file::off()
1638{
1639}
1640
1641real_output_file::real_output_file()
1642: printing(0), output_on(1)
1643{
1644#ifndef POPEN_MISSING
1645 if (pipe_command) {
1646 if ((fp = popen(pipe_command, POPEN_WT)) != 0) {
1647 piped = 1;
1648 return;
1649 }
1650 error("pipe open failed: %1", strerror(errno));
1651 }
1652 piped = 0;
1653#endif /* not POPEN_MISSING */
1654 fp = stdout;
1655}
1656
1657real_output_file::~real_output_file()
1658{
1659 if (!fp)
1660 return;
1661 // To avoid looping, set fp to 0 before calling fatal().
1662 if (ferror(fp) || fflush(fp) < 0) {
1663 fp = 0;
1664 fatal("error writing output file");
1665 }
1666#ifndef POPEN_MISSING
1667 if (piped) {
1668 int result = pclose(fp);
1669 fp = 0;
1670 if (result < 0)
1671 fatal("pclose failed");
1672 if (!WIFEXITED(result))
1673 error("output process `%1' got fatal signal %2",
1674 pipe_command,
1675 WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
1676 else {
1677 int exit_status = WEXITSTATUS(result);
1678 if (exit_status != 0)
1679 error("output process `%1' exited with status %2",
1680 pipe_command, exit_status);
1681 }
1682 }
1683 else
1684#endif /* not POPEN MISSING */
1685 if (fclose(fp) < 0) {
1686 fp = 0;
1687 fatal("error closing output file");
1688 }
1689}
1690
1691void real_output_file::flush()
1692{
1693 if (fflush(fp) < 0)
1694 fatal("error writing output file");
1695}
1696
1697int real_output_file::is_printing()
1698{
1699 return printing;
1700}
1701
1702void real_output_file::begin_page(int pageno, vunits page_length)
1703{
1704 printing = in_output_page_list(pageno);
1705 if (printing)
1706 really_begin_page(pageno, page_length);
1707}
1708
1709void real_output_file::copy_file(hunits x, vunits y, const char *filename)
1710{
1711 if (printing && output_on)
1712 really_copy_file(x, y, filename);
1713 check_output_limits(x.to_units(), y.to_units());
1714}
1715
1716void real_output_file::transparent_char(unsigned char c)
1717{
1718 if (printing && output_on)
1719 really_transparent_char(c);
1720}
1721
1722void real_output_file::print_line(hunits x, vunits y, node *n,
1723 vunits before, vunits after, hunits width)
1724{
1725 if (printing)
1726 really_print_line(x, y, n, before, after, width);
1727 delete_node_list(n);
1728}
1729
1730void real_output_file::really_copy_file(hunits, vunits, const char *)
1731{
1732 // do nothing
1733}
1734
4d3e9548 1735void real_output_file::put_filename(const char *filename, int po)
92d0a6a6 1736{
4d3e9548 1737 really_put_filename(filename, po);
92d0a6a6
JR
1738}
1739
4d3e9548 1740void real_output_file::really_put_filename(const char *, int)
92d0a6a6
JR
1741{
1742}
1743
1744void real_output_file::on()
1745{
1746 really_on();
1747 if (output_on == 0)
1748 output_on = 1;
1749}
1750
1751void real_output_file::off()
1752{
1753 really_off();
1754 output_on = 0;
1755}
1756
1757int real_output_file::is_on()
1758{
1759 return output_on;
1760}
1761
1762void real_output_file::really_on()
1763{
1764}
1765
1766void real_output_file::really_off()
1767{
1768}
1769
1770/* ascii_output_file */
1771
1772void ascii_output_file::really_transparent_char(unsigned char c)
1773{
1774 putc(c, fp);
1775}
1776
1777void ascii_output_file::really_print_line(hunits, vunits, node *n,
1778 vunits, vunits, hunits)
1779{
1780 while (n != 0) {
1781 n->ascii_print(this);
1782 n = n->next;
1783 }
1784 fputc('\n', fp);
1785}
1786
1787void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/)
1788{
1789 fputs("<beginning of page>\n", fp);
1790}
1791
1792ascii_output_file::ascii_output_file()
1793{
1794}
1795
1796/* suppress_output_file */
1797
1798suppress_output_file::suppress_output_file()
1799{
1800}
1801
1802void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits, hunits)
1803{
1804}
1805
1806void suppress_output_file::really_begin_page(int, vunits)
1807{
1808}
1809
1810void suppress_output_file::really_transparent_char(unsigned char)
1811{
1812}
1813
1814/* glyphs, ligatures, kerns, discretionary breaks */
1815
1816class charinfo_node : public node {
1817protected:
1818 charinfo *ci;
1819public:
465b256c 1820 charinfo_node(charinfo *, statem *, int, node * = 0);
92d0a6a6
JR
1821 int ends_sentence();
1822 int overlaps_vertically();
1823 int overlaps_horizontally();
1824};
1825
465b256c
JR
1826charinfo_node::charinfo_node(charinfo *c, statem *s, int pop, node *x)
1827: node(x, s, pop), ci(c)
92d0a6a6
JR
1828{
1829}
1830
1831int charinfo_node::ends_sentence()
1832{
1833 if (ci->ends_sentence())
1834 return 1;
1835 else if (ci->transparent())
1836 return 2;
1837 else
1838 return 0;
1839}
1840
1841int charinfo_node::overlaps_horizontally()
1842{
1843 return ci->overlaps_horizontally();
1844}
1845
1846int charinfo_node::overlaps_vertically()
1847{
1848 return ci->overlaps_vertically();
1849}
1850
1851class glyph_node : public charinfo_node {
1852 static glyph_node *free_list;
1853protected:
1854 tfont *tf;
1855 color *gcol;
1856 color *fcol; /* this is needed for grotty */
1857#ifdef STORE_WIDTH
1858 hunits wid;
465b256c
JR
1859 glyph_node(charinfo *, tfont *, color *, color *, hunits,
1860 statem *, int, node * = 0);
92d0a6a6
JR
1861#endif
1862public:
1863 void *operator new(size_t);
1864 void operator delete(void *);
465b256c
JR
1865 glyph_node(charinfo *, tfont *, color *, color *,
1866 statem *, int, node * = 0);
92d0a6a6
JR
1867 ~glyph_node() {}
1868 node *copy();
1869 node *merge_glyph_node(glyph_node *);
1870 node *merge_self(node *);
1871 hunits width();
1872 node *last_char_node();
1873 units size();
1874 void vertical_extent(vunits *, vunits *);
1875 hunits subscript_correction();
1876 hunits italic_correction();
1877 hunits left_italic_correction();
1878 hunits skew();
1879 hyphenation_type get_hyphenation_type();
1880 tfont *get_tfont();
1881 color *get_glyph_color();
1882 color *get_fill_color();
1883 void tprint(troff_output_file *);
1884 void zero_width_tprint(troff_output_file *);
1885 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1886 node *add_self(node *, hyphen_list **);
1887 void ascii_print(ascii_output_file *);
1888 void asciify(macro *);
1889 int character_type();
1890 int same(node *);
1891 const char *type();
1892 int force_tprint();
465b256c
JR
1893 int is_tag();
1894 void debug_node();
92d0a6a6
JR
1895};
1896
1897glyph_node *glyph_node::free_list = 0;
1898
1899class ligature_node : public glyph_node {
1900 node *n1;
1901 node *n2;
1902#ifdef STORE_WIDTH
1903 ligature_node(charinfo *, tfont *, color *, color *, hunits,
465b256c 1904 node *, node *, statem *, int, node * = 0);
92d0a6a6
JR
1905#endif
1906public:
1907 void *operator new(size_t);
1908 void operator delete(void *);
1909 ligature_node(charinfo *, tfont *, color *, color *,
465b256c 1910 node *, node *, statem *, int, node * = 0);
92d0a6a6
JR
1911 ~ligature_node();
1912 node *copy();
1913 node *add_self(node *, hyphen_list **);
1914 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1915 void ascii_print(ascii_output_file *);
1916 void asciify(macro *);
1917 int same(node *);
1918 const char *type();
1919 int force_tprint();
465b256c 1920 int is_tag();
92d0a6a6
JR
1921};
1922
1923class kern_pair_node : public node {
1924 hunits amount;
1925 node *n1;
1926 node *n2;
1927public:
465b256c 1928 kern_pair_node(hunits, node *, node *, statem *, int, node * = 0);
92d0a6a6
JR
1929 ~kern_pair_node();
1930 node *copy();
1931 node *merge_glyph_node(glyph_node *);
1932 node *add_self(node *, hyphen_list **);
1933 hyphen_list *get_hyphen_list(hyphen_list *, int *);
1934 node *add_discretionary_hyphen();
1935 hunits width();
1936 node *last_char_node();
1937 hunits italic_correction();
1938 hunits subscript_correction();
1939 void tprint(troff_output_file *);
1940 hyphenation_type get_hyphenation_type();
1941 int ends_sentence();
1942 void ascii_print(ascii_output_file *);
1943 void asciify(macro *);
1944 int same(node *);
1945 const char *type();
1946 int force_tprint();
465b256c 1947 int is_tag();
92d0a6a6
JR
1948 void vertical_extent(vunits *, vunits *);
1949};
1950
1951class dbreak_node : public node {
1952 node *none;
1953 node *pre;
1954 node *post;
1955public:
465b256c 1956 dbreak_node(node *, node *, statem *, int, node * = 0);
92d0a6a6
JR
1957 ~dbreak_node();
1958 node *copy();
1959 node *merge_glyph_node(glyph_node *);
1960 node *add_discretionary_hyphen();
1961 hunits width();
1962 node *last_char_node();
1963 hunits italic_correction();
1964 hunits subscript_correction();
1965 void tprint(troff_output_file *);
1966 breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
1967 int is_inner = 0);
1968 int nbreaks();
1969 int ends_sentence();
1970 void split(int, node **, node **);
1971 hyphenation_type get_hyphenation_type();
1972 void ascii_print(ascii_output_file *);
1973 void asciify(macro *);
1974 int same(node *);
1975 const char *type();
1976 int force_tprint();
465b256c 1977 int is_tag();
92d0a6a6
JR
1978};
1979
1980void *glyph_node::operator new(size_t n)
1981{
1982 assert(n == sizeof(glyph_node));
1983 if (!free_list) {
1984 const int BLOCK = 1024;
1985 free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
1986 for (int i = 0; i < BLOCK - 1; i++)
1987 free_list[i].next = free_list + i + 1;
1988 free_list[BLOCK-1].next = 0;
1989 }
1990 glyph_node *p = free_list;
1991 free_list = (glyph_node *)(free_list->next);
1992 p->next = 0;
1993 return p;
1994}
1995
1996void *ligature_node::operator new(size_t n)
1997{
1998 return new char[n];
1999}
2000
2001void glyph_node::operator delete(void *p)
2002{
2003 if (p) {
2004 ((glyph_node *)p)->next = free_list;
2005 free_list = (glyph_node *)p;
2006 }
2007}
2008
2009void ligature_node::operator delete(void *p)
2010{
2011 delete[] (char *)p;
2012}
2013
465b256c
JR
2014glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc,
2015 statem *s, int pop, node *x)
2016: charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc)
92d0a6a6
JR
2017{
2018#ifdef STORE_WIDTH
2019 wid = tf->get_width(ci);
2020#endif
2021}
2022
2023#ifdef STORE_WIDTH
2024glyph_node::glyph_node(charinfo *c, tfont *t,
465b256c
JR
2025 color *gc, color *fc, hunits w,
2026 statem *s, int pop, node *x)
2027: charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc), wid(w)
92d0a6a6
JR
2028{
2029}
2030#endif
2031
2032node *glyph_node::copy()
2033{
2034#ifdef STORE_WIDTH
465b256c 2035 return new glyph_node(ci, tf, gcol, fcol, wid, state, div_nest_level);
92d0a6a6 2036#else
465b256c 2037 return new glyph_node(ci, tf, gcol, fcol, state, div_nest_level);
92d0a6a6
JR
2038#endif
2039}
2040
2041node *glyph_node::merge_self(node *nd)
2042{
2043 return nd->merge_glyph_node(this);
2044}
2045
2046int glyph_node::character_type()
2047{
2048 return tf->get_character_type(ci);
2049}
2050
2051node *glyph_node::add_self(node *n, hyphen_list **p)
2052{
2053 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
2054 next = 0;
2055 node *nn;
2056 if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
2057 next = n;
2058 nn = this;
2059 }
2060 if ((*p)->hyphen)
2061 nn = nn->add_discretionary_hyphen();
2062 hyphen_list *pp = *p;
2063 *p = (*p)->next;
2064 delete pp;
2065 return nn;
2066}
2067
2068units glyph_node::size()
2069{
2070 return tf->get_size().to_units();
2071}
2072
2073hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count)
2074{
2075 (*count)++;
2076 return new hyphen_list(ci->get_hyphenation_code(), tail);
2077}
2078
2079tfont *node::get_tfont()
2080{
2081 return 0;
2082}
2083
2084tfont *glyph_node::get_tfont()
2085{
2086 return tf;
2087}
2088
2089color *node::get_glyph_color()
2090{
2091 return 0;
2092}
2093
2094color *glyph_node::get_glyph_color()
2095{
2096 return gcol;
2097}
2098
2099color *node::get_fill_color()
2100{
2101 return 0;
2102}
2103
2104color *glyph_node::get_fill_color()
2105{
2106 return fcol;
2107}
2108
2109node *node::merge_glyph_node(glyph_node *)
2110{
2111 return 0;
2112}
2113
2114node *glyph_node::merge_glyph_node(glyph_node *gn)
2115{
2116 if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
2117 charinfo *lig;
2118 if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
2119 node *next1 = next;
2120 next = 0;
465b256c
JR
2121 return new ligature_node(lig, tf, gcol, fcol, this, gn, state,
2122 gn->div_nest_level, next1);
92d0a6a6
JR
2123 }
2124 hunits kern;
2125 if (tf->get_kern(ci, gn->ci, &kern)) {
2126 node *next1 = next;
2127 next = 0;
465b256c
JR
2128 return new kern_pair_node(kern, this, gn, state,
2129 gn->div_nest_level, next1);
92d0a6a6
JR
2130 }
2131 }
2132 return 0;
2133}
2134
2135#ifdef STORE_WIDTH
2136inline
2137#endif
2138hunits glyph_node::width()
2139{
2140#ifdef STORE_WIDTH
2141 return wid;
2142#else
2143 return tf->get_width(ci);
2144#endif
2145}
2146
2147node *glyph_node::last_char_node()
2148{
2149 return this;
2150}
2151
2152void glyph_node::vertical_extent(vunits *min, vunits *max)
2153{
2154 *min = -tf->get_char_height(ci);
2155 *max = tf->get_char_depth(ci);
2156}
2157
2158hunits glyph_node::skew()
2159{
2160 return tf->get_char_skew(ci);
2161}
2162
2163hunits glyph_node::subscript_correction()
2164{
2165 return tf->get_subscript_correction(ci);
2166}
2167
2168hunits glyph_node::italic_correction()
2169{
2170 return tf->get_italic_correction(ci);
2171}
2172
2173hunits glyph_node::left_italic_correction()
2174{
2175 return tf->get_left_italic_correction(ci);
2176}
2177
2178hyphenation_type glyph_node::get_hyphenation_type()
2179{
2180 return HYPHEN_MIDDLE;
2181}
2182
2183void glyph_node::ascii_print(ascii_output_file *ascii)
2184{
2185 unsigned char c = ci->get_ascii_code();
2186 if (c != 0)
2187 ascii->outc(c);
2188 else
2189 ascii->outs(ci->nm.contents());
2190}
2191
465b256c
JR
2192void glyph_node::debug_node()
2193{
2194 unsigned char c = ci->get_ascii_code();
2195 fprintf(stderr, "{ %s [", type());
2196 if (c)
2197 fprintf(stderr, "%c", c);
2198 else
2199 fprintf(stderr, ci->nm.contents());
2200 if (push_state)
2201 fprintf(stderr, " <push_state>");
2202 if (state)
2203 state->display_state();
2204 fprintf(stderr, " nest level %d", div_nest_level);
2205 fprintf(stderr, "]}\n");
2206 fflush(stderr);
2207}
2208
92d0a6a6 2209ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
465b256c
JR
2210 node *gn1, node *gn2, statem *s,
2211 int pop, node *x)
2212: glyph_node(c, t, gc, fc, s, pop, x), n1(gn1), n2(gn2)
92d0a6a6
JR
2213{
2214}
2215
2216#ifdef STORE_WIDTH
2217ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
465b256c
JR
2218 hunits w, node *gn1, node *gn2, statem *s,
2219 int pop, node *x)
2220: glyph_node(c, t, gc, fc, w, s, pop, x), n1(gn1), n2(gn2)
92d0a6a6
JR
2221{
2222}
2223#endif
2224
2225ligature_node::~ligature_node()
2226{
2227 delete n1;
2228 delete n2;
2229}
2230
2231node *ligature_node::copy()
2232{
2233#ifdef STORE_WIDTH
465b256c
JR
2234 return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy(),
2235 state, div_nest_level);
92d0a6a6 2236#else
465b256c
JR
2237 return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy(),
2238 state, div_nest_level);
92d0a6a6
JR
2239#endif
2240}
2241
2242void ligature_node::ascii_print(ascii_output_file *ascii)
2243{
2244 n1->ascii_print(ascii);
2245 n2->ascii_print(ascii);
2246}
2247
2248hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail, int *count)
2249{
2250 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2251 return n1->get_hyphen_list(hl, count);
2252}
2253
2254node *ligature_node::add_self(node *n, hyphen_list **p)
2255{
2256 n = n1->add_self(n, p);
2257 n = n2->add_self(n, p);
2258 n1 = n2 = 0;
2259 delete this;
2260 return n;
2261}
2262
465b256c
JR
2263kern_pair_node::kern_pair_node(hunits n, node *first, node *second,
2264 statem* s, int pop, node *x)
2265: node(x, s, pop), amount(n), n1(first), n2(second)
92d0a6a6
JR
2266{
2267}
2268
465b256c
JR
2269dbreak_node::dbreak_node(node *n, node *p, statem *s, int pop, node *x)
2270: node(x, s, pop), none(n), pre(p), post(0)
92d0a6a6
JR
2271{
2272}
2273
2274node *dbreak_node::merge_glyph_node(glyph_node *gn)
2275{
2276 glyph_node *gn2 = (glyph_node *)gn->copy();
2277 node *new_none = none ? none->merge_glyph_node(gn) : 0;
2278 node *new_post = post ? post->merge_glyph_node(gn2) : 0;
2279 if (new_none == 0 && new_post == 0) {
2280 delete gn2;
2281 return 0;
2282 }
2283 if (new_none != 0)
2284 none = new_none;
2285 else {
2286 gn->next = none;
2287 none = gn;
2288 }
2289 if (new_post != 0)
2290 post = new_post;
2291 else {
2292 gn2->next = post;
2293 post = gn2;
2294 }
2295 return this;
2296}
2297
2298node *kern_pair_node::merge_glyph_node(glyph_node *gn)
2299{
2300 node *nd = n2->merge_glyph_node(gn);
2301 if (nd == 0)
2302 return 0;
2303 n2 = nd;
2304 nd = n2->merge_self(n1);
2305 if (nd) {
2306 nd->next = next;
2307 n1 = 0;
2308 n2 = 0;
2309 delete this;
2310 return nd;
2311 }
2312 return this;
2313}
2314
2315hunits kern_pair_node::italic_correction()
2316{
2317 return n2->italic_correction();
2318}
2319
2320hunits kern_pair_node::subscript_correction()
2321{
2322 return n2->subscript_correction();
2323}
2324
2325void kern_pair_node::vertical_extent(vunits *min, vunits *max)
2326{
2327 n1->vertical_extent(min, max);
2328 vunits min2, max2;
2329 n2->vertical_extent(&min2, &max2);
2330 if (min2 < *min)
2331 *min = min2;
2332 if (max2 > *max)
2333 *max = max2;
2334}
2335
2336node *kern_pair_node::add_discretionary_hyphen()
2337{
2338 tfont *tf = n2->get_tfont();
2339 if (tf) {
2340 if (tf->contains(soft_hyphen_char)) {
2341 color *gcol = n2->get_glyph_color();
2342 color *fcol = n2->get_fill_color();
2343 node *next1 = next;
2344 next = 0;
2345 node *n = copy();
465b256c
JR
2346 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2347 state, div_nest_level);
92d0a6a6
JR
2348 node *nn = n->merge_glyph_node(gn);
2349 if (nn == 0) {
2350 gn->next = n;
2351 nn = gn;
2352 }
465b256c 2353 return new dbreak_node(this, nn, state, div_nest_level, next1);
92d0a6a6
JR
2354 }
2355 }
2356 return this;
2357}
2358
2359kern_pair_node::~kern_pair_node()
2360{
2361 if (n1 != 0)
2362 delete n1;
2363 if (n2 != 0)
2364 delete n2;
2365}
2366
2367dbreak_node::~dbreak_node()
2368{
2369 delete_node_list(pre);
2370 delete_node_list(post);
2371 delete_node_list(none);
2372}
2373
2374node *kern_pair_node::copy()
2375{
465b256c
JR
2376 return new kern_pair_node(amount, n1->copy(), n2->copy(), state,
2377 div_nest_level);
92d0a6a6
JR
2378}
2379
2380node *copy_node_list(node *n)
2381{
2382 node *p = 0;
2383 while (n != 0) {
2384 node *nn = n->copy();
2385 nn->next = p;
2386 p = nn;
2387 n = n->next;
2388 }
2389 while (p != 0) {
2390 node *pp = p->next;
2391 p->next = n;
2392 n = p;
2393 p = pp;
2394 }
2395 return n;
2396}
2397
2398void delete_node_list(node *n)
2399{
2400 while (n != 0) {
2401 node *tem = n;
2402 n = n->next;
2403 delete tem;
2404 }
2405}
2406
2407node *dbreak_node::copy()
2408{
465b256c
JR
2409 dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre),
2410 state, div_nest_level);
92d0a6a6
JR
2411 p->post = copy_node_list(post);
2412 return p;
2413}
2414
2415hyphen_list *node::get_hyphen_list(hyphen_list *tail, int *)
2416{
2417 return tail;
2418}
2419
2420hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail, int *count)
2421{
2422 hyphen_list *hl = n2->get_hyphen_list(tail, count);
2423 return n1->get_hyphen_list(hl, count);
2424}
2425
2426class hyphen_inhibitor_node : public node {
2427public:
465b256c 2428 hyphen_inhibitor_node(node * = 0);
92d0a6a6
JR
2429 node *copy();
2430 int same(node *);
2431 const char *type();
2432 int force_tprint();
465b256c 2433 int is_tag();
92d0a6a6
JR
2434 hyphenation_type get_hyphenation_type();
2435};
2436
2437hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
2438{
2439}
2440
2441node *hyphen_inhibitor_node::copy()
2442{
2443 return new hyphen_inhibitor_node;
2444}
2445
2446int hyphen_inhibitor_node::same(node *)
2447{
2448 return 1;
2449}
2450
2451const char *hyphen_inhibitor_node::type()
2452{
2453 return "hyphen_inhibitor_node";
2454}
2455
2456int hyphen_inhibitor_node::force_tprint()
2457{
2458 return 0;
2459}
2460
465b256c
JR
2461int hyphen_inhibitor_node::is_tag()
2462{
2463 return 0;
2464}
2465
92d0a6a6
JR
2466hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
2467{
2468 return HYPHEN_INHIBIT;
2469}
2470
2471/* add_discretionary_hyphen methods */
2472
2473node *dbreak_node::add_discretionary_hyphen()
2474{
2475 if (post)
2476 post = post->add_discretionary_hyphen();
2477 if (none)
2478 none = none->add_discretionary_hyphen();
2479 return this;
2480}
2481
2482node *node::add_discretionary_hyphen()
2483{
2484 tfont *tf = get_tfont();
2485 if (!tf)
2486 return new hyphen_inhibitor_node(this);
2487 if (tf->contains(soft_hyphen_char)) {
2488 color *gcol = get_glyph_color();
2489 color *fcol = get_fill_color();
2490 node *next1 = next;
2491 next = 0;
2492 node *n = copy();
465b256c
JR
2493 glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2494 state, div_nest_level);
92d0a6a6
JR
2495 node *n1 = n->merge_glyph_node(gn);
2496 if (n1 == 0) {
2497 gn->next = n;
2498 n1 = gn;
2499 }
465b256c 2500 return new dbreak_node(this, n1, state, div_nest_level, next1);
92d0a6a6
JR
2501 }
2502 return this;
2503}
2504
2505node *node::merge_self(node *)
2506{
2507 return 0;
2508}
2509
2510node *node::add_self(node *n, hyphen_list ** /*p*/)
2511{
2512 next = n;
2513 return this;
2514}
2515
2516node *kern_pair_node::add_self(node *n, hyphen_list **p)
2517{
2518 n = n1->add_self(n, p);
2519 n = n2->add_self(n, p);
2520 n1 = n2 = 0;
2521 delete this;
2522 return n;
2523}
2524
2525hunits node::width()
2526{
2527 return H0;
2528}
2529
2530node *node::last_char_node()
2531{
2532 return 0;
2533}
2534
2535int node::force_tprint()
2536{
2537 return 0;
2538}
2539
465b256c
JR
2540int node::is_tag()
2541{
2542 return 0;
2543}
2544
92d0a6a6
JR
2545hunits hmotion_node::width()
2546{
2547 return n;
2548}
2549
2550units node::size()
2551{
2552 return points_to_units(10);
2553}
2554
465b256c
JR
2555void node::debug_node()
2556{
2557 fprintf(stderr, "{ %s ", type());
2558 if (push_state)
2559 fprintf(stderr, " <push_state>");
2560 if (state)
2561 fprintf(stderr, " <state>");
2562 fprintf(stderr, " nest level %d", div_nest_level);
2563 fprintf(stderr, " }\n");
2564 fflush(stderr);
2565}
2566
2567void node::debug_node_list()
2568{
2569 node *n = next;
2570
2571 debug_node();
2572 while (n != 0) {
2573 n->debug_node();
2574 n = n->next;
2575 }
2576}
2577
92d0a6a6
JR
2578hunits kern_pair_node::width()
2579{
2580 return n1->width() + n2->width() + amount;
2581}
2582
2583node *kern_pair_node::last_char_node()
2584{
2585 node *nd = n2->last_char_node();
2586 if (nd)
2587 return nd;
2588 return n1->last_char_node();
2589}
2590
2591hunits dbreak_node::width()
2592{
2593 hunits x = H0;
2594 for (node *n = none; n != 0; n = n->next)
2595 x += n->width();
2596 return x;
2597}
2598
2599node *dbreak_node::last_char_node()
2600{
2601 for (node *n = none; n; n = n->next) {
2602 node *last_node = n->last_char_node();
2603 if (last_node)
2604 return last_node;
2605 }
2606 return 0;
2607}
2608
2609hunits dbreak_node::italic_correction()
2610{
2611 return none ? none->italic_correction() : H0;
2612}
2613
2614hunits dbreak_node::subscript_correction()
2615{
2616 return none ? none->subscript_correction() : H0;
2617}
2618
2619class italic_corrected_node : public node {
2620 node *n;
2621 hunits x;
2622public:
465b256c 2623 italic_corrected_node(node *, hunits, statem *, int, node * = 0);
92d0a6a6
JR
2624 ~italic_corrected_node();
2625 node *copy();
2626 void ascii_print(ascii_output_file *);
2627 void asciify(macro *);
2628 hunits width();
2629 node *last_char_node();
2630 void vertical_extent(vunits *, vunits *);
2631 int ends_sentence();
2632 int overlaps_horizontally();
2633 int overlaps_vertically();
2634 int same(node *);
2635 hyphenation_type get_hyphenation_type();
2636 tfont *get_tfont();
2637 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2638 int character_type();
2639 void tprint(troff_output_file *);
2640 hunits subscript_correction();
2641 hunits skew();
2642 node *add_self(node *, hyphen_list **);
2643 const char *type();
2644 int force_tprint();
465b256c 2645 int is_tag();
92d0a6a6
JR
2646};
2647
2648node *node::add_italic_correction(hunits *wd)
2649{
2650 hunits ic = italic_correction();
2651 if (ic.is_zero())
2652 return this;
2653 else {
2654 node *next1 = next;
2655 next = 0;
2656 *wd += ic;
465b256c 2657 return new italic_corrected_node(this, ic, state, div_nest_level, next1);
92d0a6a6
JR
2658 }
2659}
2660
465b256c
JR
2661italic_corrected_node::italic_corrected_node(node *nn, hunits xx, statem *s,
2662 int pop, node *p)
2663: node(p, s, pop), n(nn), x(xx)
92d0a6a6
JR
2664{
2665 assert(n != 0);
2666}
2667
2668italic_corrected_node::~italic_corrected_node()
2669{
2670 delete n;
2671}
2672
2673node *italic_corrected_node::copy()
2674{
465b256c 2675 return new italic_corrected_node(n->copy(), x, state, div_nest_level);
92d0a6a6
JR
2676}
2677
2678hunits italic_corrected_node::width()
2679{
2680 return n->width() + x;
2681}
2682
2683void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
2684{
2685 n->vertical_extent(min, max);
2686}
2687
2688void italic_corrected_node::tprint(troff_output_file *out)
2689{
2690 n->tprint(out);
2691 out->right(x);
2692}
2693
2694hunits italic_corrected_node::skew()
2695{
2696 return n->skew() - x/2;
2697}
2698
2699hunits italic_corrected_node::subscript_correction()
2700{
2701 return n->subscript_correction() - x;
2702}
2703
2704void italic_corrected_node::ascii_print(ascii_output_file *out)
2705{
2706 n->ascii_print(out);
2707}
2708
2709int italic_corrected_node::ends_sentence()
2710{
2711 return n->ends_sentence();
2712}
2713
2714int italic_corrected_node::overlaps_horizontally()
2715{
2716 return n->overlaps_horizontally();
2717}
2718
2719int italic_corrected_node::overlaps_vertically()
2720{
2721 return n->overlaps_vertically();
2722}
2723
2724node *italic_corrected_node::last_char_node()
2725{
2726 return n->last_char_node();
2727}
2728
2729tfont *italic_corrected_node::get_tfont()
2730{
2731 return n->get_tfont();
2732}
2733
2734hyphenation_type italic_corrected_node::get_hyphenation_type()
2735{
2736 return n->get_hyphenation_type();
2737}
2738
2739node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
2740{
2741 nd = n->add_self(nd, p);
2742 hunits not_interested;
2743 nd = nd->add_italic_correction(&not_interested);
2744 n = 0;
2745 delete this;
2746 return nd;
2747}
2748
2749hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail,
2750 int *count)
2751{
2752 return n->get_hyphen_list(tail, count);
2753}
2754
2755int italic_corrected_node::character_type()
2756{
2757 return n->character_type();
2758}
2759
2760class break_char_node : public node {
2761 node *ch;
2762 char break_code;
2763 color *col;
2764public:
2765 break_char_node(node *, int, color *, node * = 0);
465b256c 2766 break_char_node(node *, int, color *, statem *, int, node * = 0);
92d0a6a6
JR
2767 ~break_char_node();
2768 node *copy();
2769 hunits width();
2770 vunits vertical_width();
2771 node *last_char_node();
2772 int character_type();
2773 int ends_sentence();
2774 node *add_self(node *, hyphen_list **);
2775 hyphen_list *get_hyphen_list(hyphen_list *, int *);
2776 void tprint(troff_output_file *);
2777 void zero_width_tprint(troff_output_file *);
2778 void ascii_print(ascii_output_file *);
2779 void asciify(macro *);
2780 hyphenation_type get_hyphenation_type();
2781 int overlaps_vertically();
2782 int overlaps_horizontally();
2783 units size();
2784 tfont *get_tfont();
2785 int same(node *);
2786 const char *type();
2787 int force_tprint();
465b256c 2788 int is_tag();
92d0a6a6
JR
2789};
2790
2791break_char_node::break_char_node(node *n, int bc, color *c, node *x)
2792: node(x), ch(n), break_code(bc), col(c)
2793{
2794}
2795
465b256c
JR
2796break_char_node::break_char_node(node *n, int bc, color *c, statem *s,
2797 int pop, node *x)
2798: node(x, s, pop), ch(n), break_code(bc), col(c)
2799{
2800}
2801
92d0a6a6
JR
2802break_char_node::~break_char_node()
2803{
2804 delete ch;
2805}
2806
2807node *break_char_node::copy()
2808{
465b256c
JR
2809 return new break_char_node(ch->copy(), break_code, col, state,
2810 div_nest_level);
92d0a6a6
JR
2811}
2812
2813hunits break_char_node::width()
2814{
2815 return ch->width();
2816}
2817
2818vunits break_char_node::vertical_width()
2819{
2820 return ch->vertical_width();
2821}
2822
2823node *break_char_node::last_char_node()
2824{
2825 return ch->last_char_node();
2826}
2827
2828int break_char_node::character_type()
2829{
2830 return ch->character_type();
2831}
2832
2833int break_char_node::ends_sentence()
2834{
2835 return ch->ends_sentence();
2836}
2837
2838node *break_char_node::add_self(node *n, hyphen_list **p)
2839{
2840 assert((*p)->hyphenation_code == 0);
4d3e9548
JL
2841 if (break_code & 1) {
2842 if ((*p)->breakable || break_code & 4) {
2843 n = new space_node(H0, col, n);
2844 n->freeze_space();
2845 }
92d0a6a6
JR
2846 }
2847 next = n;
2848 n = this;
4d3e9548
JL
2849 if (break_code & 2) {
2850 if ((*p)->breakable || break_code & 4) {
2851 n = new space_node(H0, col, n);
2852 n->freeze_space();
2853 }
92d0a6a6
JR
2854 }
2855 hyphen_list *pp = *p;
2856 *p = (*p)->next;
2857 delete pp;
2858 return n;
2859}
2860
2861hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail, int *)
2862{
2863 return new hyphen_list(0, tail);
2864}
2865
2866hyphenation_type break_char_node::get_hyphenation_type()
2867{
2868 return HYPHEN_MIDDLE;
2869}
2870
2871void break_char_node::ascii_print(ascii_output_file *ascii)
2872{
2873 ch->ascii_print(ascii);
2874}
2875
2876int break_char_node::overlaps_vertically()
2877{
2878 return ch->overlaps_vertically();
2879}
2880
2881int break_char_node::overlaps_horizontally()
2882{
2883 return ch->overlaps_horizontally();
2884}
2885
2886units break_char_node::size()
2887{
2888 return ch->size();
2889}
2890
2891tfont *break_char_node::get_tfont()
2892{
2893 return ch->get_tfont();
2894}
2895
2896node *extra_size_node::copy()
2897{
465b256c
JR
2898 return new extra_size_node(n, state, div_nest_level);
2899}
2900
2901extra_size_node::extra_size_node(vunits i, statem *s, int pop)
2902: node(0, s, pop), n(i)
2903{
2904}
2905
2906extra_size_node::extra_size_node(vunits i)
2907: n(i)
2908{
92d0a6a6
JR
2909}
2910
2911node *vertical_size_node::copy()
2912{
465b256c
JR
2913 return new vertical_size_node(n, state, div_nest_level);
2914}
2915
2916vertical_size_node::vertical_size_node(vunits i, statem *s, int pop)
2917: node(0, s, pop), n(i)
2918{
2919}
2920
2921vertical_size_node::vertical_size_node(vunits i)
2922: n(i)
2923{
92d0a6a6
JR
2924}
2925
2926node *hmotion_node::copy()
2927{
465b256c 2928 return new hmotion_node(n, was_tab, unformat, col, state, div_nest_level);
92d0a6a6
JR
2929}
2930
2931node *space_char_hmotion_node::copy()
2932{
465b256c
JR
2933 return new space_char_hmotion_node(n, col, state, div_nest_level);
2934}
2935
2936vmotion_node::vmotion_node(vunits i, color *c)
2937: n(i), col(c)
2938{
2939}
2940
2941vmotion_node::vmotion_node(vunits i, color *c, statem *s, int pop)
2942: node(0, s, pop), n(i), col(c)
2943{
92d0a6a6
JR
2944}
2945
2946node *vmotion_node::copy()
2947{
465b256c 2948 return new vmotion_node(n, col, state, div_nest_level);
92d0a6a6
JR
2949}
2950
2951node *dummy_node::copy()
2952{
2953 return new dummy_node;
2954}
2955
2956node *transparent_dummy_node::copy()
2957{
2958 return new transparent_dummy_node;
2959}
2960
2961hline_node::~hline_node()
2962{
2963 if (n)
2964 delete n;
2965}
2966
465b256c
JR
2967hline_node::hline_node(hunits i, node *c, node *nxt)
2968: node(nxt), x(i), n(c)
2969{
2970}
2971
2972hline_node::hline_node(hunits i, node *c, statem *s, int pop, node *nxt)
2973: node(nxt, s, pop), x(i), n(c)
2974{
2975}
2976
92d0a6a6
JR
2977node *hline_node::copy()
2978{
465b256c 2979 return new hline_node(x, n ? n->copy() : 0, state, div_nest_level);
92d0a6a6
JR
2980}
2981
2982hunits hline_node::width()
2983{
2984 return x < H0 ? H0 : x;
2985}
2986
465b256c
JR
2987vline_node::vline_node(vunits i, node *c, node *nxt)
2988: node(nxt), x(i), n(c)
2989{
2990}
2991
2992vline_node::vline_node(vunits i, node *c, statem *s, int pop, node *nxt)
2993: node(nxt, s, pop), x(i), n(c)
2994{
2995}
2996
92d0a6a6
JR
2997vline_node::~vline_node()
2998{
2999 if (n)
3000 delete n;
3001}
3002
3003node *vline_node::copy()
3004{
465b256c 3005 return new vline_node(x, n ? n->copy() : 0, state, div_nest_level);
92d0a6a6
JR
3006}
3007
3008hunits vline_node::width()
3009{
3010 return n == 0 ? H0 : n->width();
3011}
3012
465b256c
JR
3013zero_width_node::zero_width_node(node *nd, statem *s, int pop)
3014: node(0, s, pop), n(nd)
3015{
3016}
3017
3018zero_width_node::zero_width_node(node *nd)
3019: n(nd)
92d0a6a6
JR
3020{
3021}
3022
3023zero_width_node::~zero_width_node()
3024{
3025 delete_node_list(n);
3026}
3027
3028node *zero_width_node::copy()
3029{
465b256c 3030 return new zero_width_node(copy_node_list(n), state, div_nest_level);
92d0a6a6
JR
3031}
3032
3033int node_list_character_type(node *p)
3034{
3035 int t = 0;
3036 for (; p; p = p->next)
3037 t |= p->character_type();
3038 return t;
3039}
3040
3041int zero_width_node::character_type()
3042{
3043 return node_list_character_type(n);
3044}
3045
3046void node_list_vertical_extent(node *p, vunits *min, vunits *max)
3047{
3048 *min = V0;
3049 *max = V0;
3050 vunits cur_vpos = V0;
3051 vunits v1, v2;
3052 for (; p; p = p->next) {
3053 p->vertical_extent(&v1, &v2);
3054 v1 += cur_vpos;
3055 if (v1 < *min)
3056 *min = v1;
3057 v2 += cur_vpos;
3058 if (v2 > *max)
3059 *max = v2;
3060 cur_vpos += p->vertical_width();
3061 }
3062}
3063
3064void zero_width_node::vertical_extent(vunits *min, vunits *max)
3065{
3066 node_list_vertical_extent(n, min, max);
3067}
3068
465b256c
JR
3069overstrike_node::overstrike_node()
3070: list(0), max_width(H0)
3071{
3072}
3073
3074overstrike_node::overstrike_node(statem *s, int pop)
3075: node(0, s, pop), list(0), max_width(H0)
92d0a6a6
JR
3076{
3077}
3078
3079overstrike_node::~overstrike_node()
3080{
3081 delete_node_list(list);
3082}
3083
3084node *overstrike_node::copy()
3085{
465b256c 3086 overstrike_node *on = new overstrike_node(state, div_nest_level);
92d0a6a6
JR
3087 for (node *tem = list; tem; tem = tem->next)
3088 on->overstrike(tem->copy());
3089 return on;
3090}
3091
3092void overstrike_node::overstrike(node *n)
3093{
3094 if (n == 0)
3095 return;
3096 hunits w = n->width();
3097 if (w > max_width)
3098 max_width = w;
3099 node **p;
3100 for (p = &list; *p; p = &(*p)->next)
3101 ;
3102 n->next = 0;
3103 *p = n;
3104}
3105
3106hunits overstrike_node::width()
3107{
3108 return max_width;
3109}
3110
465b256c
JR
3111bracket_node::bracket_node()
3112: list(0), max_width(H0)
3113{
3114}
3115
3116bracket_node::bracket_node(statem *s, int pop)
3117: node(0, s, pop), list(0), max_width(H0)
92d0a6a6
JR
3118{
3119}
3120
3121bracket_node::~bracket_node()
3122{
3123 delete_node_list(list);
3124}
3125
3126node *bracket_node::copy()
3127{
465b256c 3128 bracket_node *on = new bracket_node(state, div_nest_level);
92d0a6a6
JR
3129 node *last_node = 0;
3130 node *tem;
3131 if (list)
3132 list->last = 0;
3133 for (tem = list; tem; tem = tem->next) {
3134 if (tem->next)
3135 tem->next->last = tem;
3136 last_node = tem;
3137 }
3138 for (tem = last_node; tem; tem = tem->last)
3139 on->bracket(tem->copy());
3140 return on;
3141}
3142
3143void bracket_node::bracket(node *n)
3144{
3145 if (n == 0)
3146 return;
3147 hunits w = n->width();
3148 if (w > max_width)
3149 max_width = w;
3150 n->next = list;
3151 list = n;
3152}
3153
3154hunits bracket_node::width()
3155{
3156 return max_width;
3157}
3158
3159int node::nspaces()
3160{
3161 return 0;
3162}
3163
3164int node::merge_space(hunits, hunits, hunits)
3165{
3166 return 0;
3167}
3168
3169#if 0
3170space_node *space_node::free_list = 0;
3171
3172void *space_node::operator new(size_t n)
3173{
3174 assert(n == sizeof(space_node));
3175 if (!free_list) {
3176 free_list = (space_node *)new char[sizeof(space_node)*BLOCK];
3177 for (int i = 0; i < BLOCK - 1; i++)
3178 free_list[i].next = free_list + i + 1;
3179 free_list[BLOCK-1].next = 0;
3180 }
3181 space_node *p = free_list;
3182 free_list = (space_node *)(free_list->next);
3183 p->next = 0;
3184 return p;
3185}
3186
3187inline void space_node::operator delete(void *p)
3188{
3189 if (p) {
3190 ((space_node *)p)->next = free_list;
3191 free_list = (space_node *)p;
3192 }
3193}
3194#endif
3195
3196space_node::space_node(hunits nn, color *c, node *p)
465b256c 3197: node(p, 0, 0), n(nn), set(0), was_escape_colon(0), col(c)
92d0a6a6
JR
3198{
3199}
3200
465b256c
JR
3201space_node::space_node(hunits nn, color *c, statem *s, int pop, node *p)
3202: node(p, s, pop), n(nn), set(0), was_escape_colon(0), col(c)
3203{
3204}
3205
3206space_node::space_node(hunits nn, int s, int flag, color *c, statem *st,
3207 int pop, node *p)
3208: node(p, st, pop), n(nn), set(s), was_escape_colon(flag), col(c)
92d0a6a6
JR
3209{
3210}
3211
3212#if 0
3213space_node::~space_node()
3214{
3215}
3216#endif
3217
3218node *space_node::copy()
3219{
465b256c 3220 return new space_node(n, set, was_escape_colon, col, state, div_nest_level);
92d0a6a6
JR
3221}
3222
3223int space_node::force_tprint()
3224{
3225 return 0;
3226}
3227
465b256c
JR
3228int space_node::is_tag()
3229{
3230 return 0;
3231}
3232
92d0a6a6
JR
3233int space_node::nspaces()
3234{
3235 return set ? 0 : 1;
3236}
3237
3238int space_node::merge_space(hunits h, hunits, hunits)
3239{
3240 n += h;
3241 return 1;
3242}
3243
3244hunits space_node::width()
3245{
3246 return n;
3247}
3248
3249void node::spread_space(int*, hunits*)
3250{
3251}
3252
3253void space_node::spread_space(int *n_spaces, hunits *desired_space)
3254{
3255 if (!set) {
3256 assert(*n_spaces > 0);
3257 if (*n_spaces == 1) {
3258 n += *desired_space;
3259 *desired_space = H0;
3260 }
3261 else {
3262 hunits extra = *desired_space / *n_spaces;
3263 *desired_space -= extra;
3264 n += extra;
3265 }
3266 *n_spaces -= 1;
3267 set = 1;
3268 }
3269}
3270
3271void node::freeze_space()
3272{
3273}
3274
3275void space_node::freeze_space()
3276{
3277 set = 1;
3278}
3279
3280void node::is_escape_colon()
3281{
3282}
3283
3284void space_node::is_escape_colon()
3285{
3286 was_escape_colon = 1;
3287}
3288
465b256c
JR
3289diverted_space_node::diverted_space_node(vunits d, statem *s, int pop,
3290 node *p)
3291: node(p, s, pop), n(d)
3292{
3293}
3294
92d0a6a6
JR
3295diverted_space_node::diverted_space_node(vunits d, node *p)
3296: node(p), n(d)
3297{
3298}
3299
3300node *diverted_space_node::copy()
3301{
465b256c
JR
3302 return new diverted_space_node(n, state, div_nest_level);
3303}
3304
3305diverted_copy_file_node::diverted_copy_file_node(symbol s, statem *st,
3306 int pop, node *p)
3307: node(p, st, pop), filename(s)
3308{
92d0a6a6
JR
3309}
3310
3311diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
3312: node(p), filename(s)
3313{
3314}
3315
3316node *diverted_copy_file_node::copy()
3317{
465b256c 3318 return new diverted_copy_file_node(filename, state, div_nest_level);
92d0a6a6
JR
3319}
3320
3321int node::ends_sentence()
3322{
3323 return 0;
3324}
3325
3326int kern_pair_node::ends_sentence()
3327{
3328 switch (n2->ends_sentence()) {
3329 case 0:
3330 return 0;
3331 case 1:
3332 return 1;
3333 case 2:
3334 break;
3335 default:
3336 assert(0);
3337 }
3338 return n1->ends_sentence();
3339}
3340
3341int node_list_ends_sentence(node *n)
3342{
3343 for (; n != 0; n = n->next)
3344 switch (n->ends_sentence()) {
3345 case 0:
3346 return 0;
3347 case 1:
3348 return 1;
3349 case 2:
3350 break;
3351 default:
3352 assert(0);
3353 }
3354 return 2;
3355}
3356
3357int dbreak_node::ends_sentence()
3358{
3359 return node_list_ends_sentence(none);
3360}
3361
3362int node::overlaps_horizontally()
3363{
3364 return 0;
3365}
3366
3367int node::overlaps_vertically()
3368{
3369 return 0;
3370}
3371
3372int node::discardable()
3373{
3374 return 0;
3375}
3376
3377int space_node::discardable()
3378{
3379 return set ? 0 : 1;
3380}
3381
3382vunits node::vertical_width()
3383{
3384 return V0;
3385}
3386
3387vunits vline_node::vertical_width()
3388{
3389 return x;
3390}
3391
3392vunits vmotion_node::vertical_width()
3393{
3394 return n;
3395}
3396
3397int node::set_unformat_flag()
3398{
3399 return 1;
3400}
3401
3402int node::character_type()
3403{
3404 return 0;
3405}
3406
3407hunits node::subscript_correction()
3408{
3409 return H0;
3410}
3411
3412hunits node::italic_correction()
3413{
3414 return H0;
3415}
3416
3417hunits node::left_italic_correction()
3418{
3419 return H0;
3420}
3421
3422hunits node::skew()
3423{
3424 return H0;
3425}
3426
3427/* vertical_extent methods */
3428
3429void node::vertical_extent(vunits *min, vunits *max)
3430{
3431 vunits v = vertical_width();
3432 if (v < V0) {
3433 *min = v;
3434 *max = V0;
3435 }
3436 else {
3437 *max = v;
3438 *min = V0;
3439 }
3440}
3441
3442void vline_node::vertical_extent(vunits *min, vunits *max)
3443{
3444 if (n == 0)
3445 node::vertical_extent(min, max);
3446 else {
3447 vunits cmin, cmax;
3448 n->vertical_extent(&cmin, &cmax);
3449 vunits h = n->size();
3450 if (x < V0) {
3451 if (-x < h) {
3452 *min = x;
3453 *max = V0;
3454 }
3455 else {
3456 // we print the first character and then move up, so
3457 *max = cmax;
3458 // we print the last character and then move up h
3459 *min = cmin + h;
3460 if (*min > V0)
3461 *min = V0;
3462 *min += x;
3463 }
3464 }
3465 else {
3466 if (x < h) {
3467 *max = x;
3468 *min = V0;
3469 }
3470 else {
3471 // we move down by h and then print the first character, so
3472 *min = cmin + h;
3473 if (*min > V0)
3474 *min = V0;
3475 *max = x + cmax;
3476 }
3477 }
3478 }
3479}
3480
3481/* ascii_print methods */
3482
3483static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n)
3484{
3485 if (n == 0)
3486 return;
3487 ascii_print_reverse_node_list(ascii, n->next);
3488 n->ascii_print(ascii);
3489}
3490
3491void dbreak_node::ascii_print(ascii_output_file *ascii)
3492{
3493 ascii_print_reverse_node_list(ascii, none);
3494}
3495
3496void kern_pair_node::ascii_print(ascii_output_file *ascii)
3497{
3498 n1->ascii_print(ascii);
3499 n2->ascii_print(ascii);
3500}
3501
3502void node::ascii_print(ascii_output_file *)
3503{
3504}
3505
3506void space_node::ascii_print(ascii_output_file *ascii)
3507{
3508 if (!n.is_zero())
3509 ascii->outc(' ');
3510}
3511
3512void hmotion_node::ascii_print(ascii_output_file *ascii)
3513{
3514 // this is pretty arbitrary
3515 if (n >= points_to_units(2))
3516 ascii->outc(' ');
3517}
3518
3519void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
3520{
3521 ascii->outc(' ');
3522}
3523
3524/* asciify methods */
3525
3526void node::asciify(macro *m)
3527{
3528 m->append(this);
3529}
3530
3531void glyph_node::asciify(macro *m)
3532{
3533 unsigned char c = ci->get_asciify_code();
3534 if (c == 0)
3535 c = ci->get_ascii_code();
3536 if (c != 0) {
3537 m->append(c);
3538 delete this;
3539 }
3540 else
3541 m->append(this);
3542}
3543
3544void kern_pair_node::asciify(macro *m)
3545{
3546 n1->asciify(m);
3547 n2->asciify(m);
3548 n1 = n2 = 0;
3549 delete this;
3550}
3551
3552static void asciify_reverse_node_list(macro *m, node *n)
3553{
3554 if (n == 0)
3555 return;
3556 asciify_reverse_node_list(m, n->next);
3557 n->asciify(m);
3558}
3559
3560void dbreak_node::asciify(macro *m)
3561{
3562 asciify_reverse_node_list(m, none);
3563 none = 0;
3564 delete this;
3565}
3566
3567void ligature_node::asciify(macro *m)
3568{
3569 n1->asciify(m);
3570 n2->asciify(m);
3571 n1 = n2 = 0;
3572 delete this;
3573}
3574
3575void break_char_node::asciify(macro *m)
3576{
3577 ch->asciify(m);
3578 ch = 0;
3579 delete this;
3580}
3581
3582void italic_corrected_node::asciify(macro *m)
3583{
3584 n->asciify(m);
3585 n = 0;
3586 delete this;
3587}
3588
3589void left_italic_corrected_node::asciify(macro *m)
3590{
3591 if (n) {
3592 n->asciify(m);
3593 n = 0;
3594 }
3595 delete this;
3596}
3597
3598void hmotion_node::asciify(macro *m)
3599{
3600 if (was_tab) {
3601 m->append('\t');
3602 delete this;
3603 }
3604 else
3605 m->append(this);
3606}
3607
3608space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
465b256c 3609 statem *s, int pop,
92d0a6a6 3610 node *nxt)
465b256c
JR
3611: hmotion_node(i, c, s, pop, nxt)
3612{
3613}
3614
3615space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3616 node *nxt)
3617: hmotion_node(i, c, 0, 0, nxt)
92d0a6a6
JR
3618{
3619}
3620
3621void space_char_hmotion_node::asciify(macro *m)
3622{
3623 m->append(ESCAPE_SPACE);
3624 delete this;
3625}
3626
3627void space_node::asciify(macro *m)
3628{
3629 if (was_escape_colon) {
3630 m->append(ESCAPE_COLON);
3631 delete this;
3632 }
3633 else
3634 m->append(this);
3635}
3636
3637void word_space_node::asciify(macro *m)
3638{
3639 for (width_list *w = orig_width; w; w = w->next)
3640 m->append(' ');
3641 delete this;
3642}
3643
3644void unbreakable_space_node::asciify(macro *m)
3645{
3646 m->append(ESCAPE_TILDE);
3647 delete this;
3648}
3649
3650void line_start_node::asciify(macro *)
3651{
3652 delete this;
3653}
3654
3655void vertical_size_node::asciify(macro *)
3656{
3657 delete this;
3658}
3659
3660breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/,
3661 breakpoint *rest, int /*is_inner*/)
3662{
3663 return rest;
3664}
3665
3666int node::nbreaks()
3667{
3668 return 0;
3669}
3670
3671breakpoint *space_node::get_breakpoints(hunits wd, int ns,
3672 breakpoint *rest, int is_inner)
3673{
3674 if (next && next->discardable())
3675 return rest;
3676 breakpoint *bp = new breakpoint;
3677 bp->next = rest;
3678 bp->width = wd;
3679 bp->nspaces = ns;
3680 bp->hyphenated = 0;
3681 if (is_inner) {
3682 assert(rest != 0);
3683 bp->index = rest->index + 1;
3684 bp->nd = rest->nd;
3685 }
3686 else {
3687 bp->nd = this;
3688 bp->index = 0;
3689 }
3690 return bp;
3691}
3692
3693int space_node::nbreaks()
3694{
3695 if (next && next->discardable())
3696 return 0;
3697 else
3698 return 1;
3699}
3700
3701static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
3702 int ns, breakpoint *rest)
3703{
3704 if (p != 0) {
3705 rest = p->get_breakpoints(*widthp,
3706 ns,
3707 node_list_get_breakpoints(p->next, widthp, ns,
3708 rest),
3709 1);
3710 *widthp += p->width();
3711 }
3712 return rest;
3713}
3714
3715breakpoint *dbreak_node::get_breakpoints(hunits wd, int ns,
3716 breakpoint *rest, int is_inner)
3717{
3718 breakpoint *bp = new breakpoint;
3719 bp->next = rest;
3720 bp->width = wd;
3721 for (node *tem = pre; tem != 0; tem = tem->next)
3722 bp->width += tem->width();
3723 bp->nspaces = ns;
3724 bp->hyphenated = 1;
3725 if (is_inner) {
3726 assert(rest != 0);
3727 bp->index = rest->index + 1;
3728 bp->nd = rest->nd;
3729 }
3730 else {
3731 bp->nd = this;
3732 bp->index = 0;
3733 }
3734 return node_list_get_breakpoints(none, &wd, ns, bp);
3735}
3736
3737int dbreak_node::nbreaks()
3738{
3739 int i = 1;
3740 for (node *tem = none; tem != 0; tem = tem->next)
3741 i += tem->nbreaks();
3742 return i;
3743}
3744
3745void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
3746{
3747 assert(0);
3748}
3749
3750void space_node::split(int where, node **pre, node **post)
3751{
3752 assert(where == 0);
3753 *pre = next;
3754 *post = 0;
3755 delete this;
3756}
3757
3758static void node_list_split(node *p, int *wherep, node **prep, node **postp)
3759{
3760 if (p == 0)
3761 return;
3762 int nb = p->nbreaks();
3763 node_list_split(p->next, wherep, prep, postp);
3764 if (*wherep < 0) {
3765 p->next = *postp;
3766 *postp = p;
3767 }
3768 else if (*wherep < nb) {
3769 p->next = *prep;
3770 p->split(*wherep, prep, postp);
3771 }
3772 else {
3773 p->next = *prep;
3774 *prep = p;
3775 }
3776 *wherep -= nb;
3777}
3778
3779void dbreak_node::split(int where, node **prep, node **postp)
3780{
3781 assert(where >= 0);
3782 if (where == 0) {
3783 *postp = post;
3784 post = 0;
3785 if (pre == 0)
3786 *prep = next;
3787 else {
3788 node *tem;
3789 for (tem = pre; tem->next != 0; tem = tem->next)
3790 ;
3791 tem->next = next;
3792 *prep = pre;
3793 }
3794 pre = 0;
3795 delete this;
3796 }
3797 else {
3798 *prep = next;
3799 where -= 1;
3800 node_list_split(none, &where, prep, postp);
3801 none = 0;
3802 delete this;
3803 }
3804}
3805
3806hyphenation_type node::get_hyphenation_type()
3807{
3808 return HYPHEN_BOUNDARY;
3809}
3810
3811hyphenation_type dbreak_node::get_hyphenation_type()
3812{
3813 return HYPHEN_INHIBIT;
3814}
3815
3816hyphenation_type kern_pair_node::get_hyphenation_type()
3817{
3818 return HYPHEN_MIDDLE;
3819}
3820
3821hyphenation_type dummy_node::get_hyphenation_type()
3822{
3823 return HYPHEN_MIDDLE;
3824}
3825
3826hyphenation_type transparent_dummy_node::get_hyphenation_type()
3827{
3828 return HYPHEN_MIDDLE;
3829}
3830
3831hyphenation_type hmotion_node::get_hyphenation_type()
3832{
3833 return HYPHEN_MIDDLE;
3834}
3835
3836hyphenation_type space_char_hmotion_node::get_hyphenation_type()
3837{
3838 return HYPHEN_MIDDLE;
3839}
3840
3841hyphenation_type overstrike_node::get_hyphenation_type()
3842{
3843 return HYPHEN_MIDDLE;
3844}
3845
3846hyphenation_type space_node::get_hyphenation_type()
3847{
3848 if (was_escape_colon)
3849 return HYPHEN_MIDDLE;
3850 return HYPHEN_BOUNDARY;
3851}
3852
3853hyphenation_type unbreakable_space_node::get_hyphenation_type()
3854{
3855 return HYPHEN_MIDDLE;
3856}
3857
3858int node::interpret(macro *)
3859{
3860 return 0;
3861}
3862
3863special_node::special_node(const macro &m, int n)
3864: mac(m), no_init_string(n)
3865{
3866 font_size fs = curenv->get_font_size();
3867 int char_height = curenv->get_char_height();
3868 int char_slant = curenv->get_char_slant();
3869 int fontno = env_definite_font(curenv);
3870 tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fontno);
3871 if (curenv->is_composite())
3872 tf = tf->get_plain();
3873 gcol = curenv->get_glyph_color();
3874 fcol = curenv->get_fill_color();
465b256c 3875 is_special = 1;
92d0a6a6
JR
3876}
3877
3878special_node::special_node(const macro &m, tfont *t,
465b256c
JR
3879 color *gc, color *fc,
3880 statem *s, int pop,
3881 int n)
3882: node(0, s, pop), mac(m), tf(t), gcol(gc), fcol(fc), no_init_string(n)
92d0a6a6 3883{
465b256c 3884 is_special = 1;
92d0a6a6
JR
3885}
3886
3887int special_node::same(node *n)
3888{
3889 return mac == ((special_node *)n)->mac
3890 && tf == ((special_node *)n)->tf
3891 && gcol == ((special_node *)n)->gcol
3892 && fcol == ((special_node *)n)->fcol
3893 && no_init_string == ((special_node *)n)->no_init_string;
3894}
3895
3896const char *special_node::type()
3897{
3898 return "special_node";
3899}
3900
3901int special_node::ends_sentence()
3902{
3903 return 2;
3904}
3905
3906int special_node::force_tprint()
3907{
3908 return 0;
3909}
3910
465b256c
JR
3911int special_node::is_tag()
3912{
3913 return 0;
3914}
3915
92d0a6a6
JR
3916node *special_node::copy()
3917{
465b256c
JR
3918 return new special_node(mac, tf, gcol, fcol, state, div_nest_level,
3919 no_init_string);
92d0a6a6
JR
3920}
3921
3922void special_node::tprint_start(troff_output_file *out)
3923{
3924 out->start_special(tf, gcol, fcol, no_init_string);
3925}
3926
3927void special_node::tprint_char(troff_output_file *out, unsigned char c)
3928{
3929 out->special_char(c);
3930}
3931
3932void special_node::tprint_end(troff_output_file *out)
3933{
3934 out->end_special();
3935}
3936
3937tfont *special_node::get_tfont()
3938{
3939 return tf;
3940}
3941
3942/* suppress_node */
3943
3944suppress_node::suppress_node(int on_or_off, int issue_limits)
465b256c
JR
3945: is_on(on_or_off), emit_limits(issue_limits), filename(0), position(0),
3946 image_id(0)
92d0a6a6
JR
3947{
3948}
3949
3950suppress_node::suppress_node(symbol f, char p, int id)
3951: is_on(2), emit_limits(0), filename(f), position(p), image_id(id)
3952{
465b256c 3953 is_special = 1;
92d0a6a6
JR
3954}
3955
3956suppress_node::suppress_node(int issue_limits, int on_or_off,
465b256c
JR
3957 symbol f, char p, int id,
3958 statem *s, int pop)
3959: node(0, s, pop), is_on(on_or_off), emit_limits(issue_limits), filename(f),
3960 position(p), image_id(id)
92d0a6a6
JR
3961{
3962}
3963
3964int suppress_node::same(node *n)
3965{
3966 return ((is_on == ((suppress_node *)n)->is_on)
3967 && (emit_limits == ((suppress_node *)n)->emit_limits)
3968 && (filename == ((suppress_node *)n)->filename)
3969 && (position == ((suppress_node *)n)->position)
3970 && (image_id == ((suppress_node *)n)->image_id));
3971}
3972
3973const char *suppress_node::type()
3974{
3975 return "suppress_node";
3976}
3977
3978node *suppress_node::copy()
3979{
465b256c
JR
3980 return new suppress_node(emit_limits, is_on, filename, position, image_id,
3981 state, div_nest_level);
3982}
3983
3984/* tag_node */
3985
3986tag_node::tag_node()
3987: delayed(0)
3988{
3989 is_special = 1;
3990}
3991
3992tag_node::tag_node(string s, int delay)
3993: tag_string(s), delayed(delay)
3994{
3995 is_special = !delay;
3996}
3997
3998tag_node::tag_node(string s, statem *st, int pop, int delay)
3999: node(0, st, pop), tag_string(s), delayed(delay)
4000{
4001 is_special = !delay;
4002}
4003
4004node *tag_node::copy()
4005{
4006 return new tag_node(tag_string, state, div_nest_level, delayed);
4007}
4008
4009void tag_node::tprint(troff_output_file *out)
4010{
4011 if (delayed)
4012 out->add_to_tag_list(tag_string);
4013 else
4014 out->state.add_tag(out->fp, tag_string);
4015}
4016
4017int tag_node::same(node *nd)
4018{
4019 return tag_string == ((tag_node *)nd)->tag_string
4020 && delayed == ((tag_node *)nd)->delayed;
4021}
4022
4023const char *tag_node::type()
4024{
4025 return "tag_node";
4026}
4027
4028int tag_node::force_tprint()
4029{
4030 return !delayed;
4031}
4032
4033int tag_node::is_tag()
4034{
4035 return !delayed;
4036}
4037
4038int tag_node::ends_sentence()
4039{
4040 return 2;
92d0a6a6
JR
4041}
4042
4043int get_reg_int(const char *p)
4044{
4045 reg *r = (reg *)number_reg_dictionary.lookup(p);
4046 units prev_value;
4047 if (r && (r->get_value(&prev_value)))
4048 return (int)prev_value;
4049 else
4050 warning(WARN_REG, "number register `%1' not defined", p);
4051 return 0;
4052}
4053
4054const char *get_reg_str(const char *p)
4055{
4056 reg *r = (reg *)number_reg_dictionary.lookup(p);
4057 if (r)
4058 return r->get_string();
4059 else
4060 warning(WARN_REG, "register `%1' not defined", p);
4061 return 0;
4062}
4063
4064void suppress_node::put(troff_output_file *out, const char *s)
4065{
4066 int i = 0;
4067 while (s[i] != (char)0) {
4068 out->special_char(s[i]);
4069 i++;
4070 }
4071}
4072
4073/*
4074 * We need to remember the start of the image and its name.
4075 */
4076
4077static char last_position = 0;
4078static const char *last_image_filename = 0;
4079static int last_image_id = 0;
4080
4081inline int min(int a, int b)
4082{
4083 return a < b ? a : b;
4084}
4085
4086/*
4087 * tprint - if (is_on == 2)
4088 * remember current position (l, r, c, i) and filename
4089 * else
4090 * if (emit_limits)
4091 * if (html)
4092 * emit image tag
4093 * else
4094 * emit postscript bounds for image
4095 * else
4096 * if (suppress boolean differs from current state)
4097 * alter state
4098 * reset registers
4099 * record current page
4100 * set low water mark.
4101 */
4102
4103void suppress_node::tprint(troff_output_file *out)
4104{
4105 int current_page = topdiv->get_page_number();
4106 // firstly check to see whether this suppress node contains
4107 // an image filename & position.
4108 if (is_on == 2) {
4109 // remember position and filename
4110 last_position = position;
4111 char *tem = (char *)last_image_filename;
4112 last_image_filename = strsave(filename.contents());
4113 if (tem)
4114 a_delete tem;
4115 last_image_id = image_id;
4116 // printf("start of image and page = %d\n", current_page);
4117 }
4118 else {
4119 // now check whether the suppress node requires us to issue limits.
4120 if (emit_limits) {
4121 char name[8192];
4122 // remember that the filename will contain a %d in which the
4123 // last_image_id is placed
4124 if (last_image_filename == (char *) 0)
4125 *name = '\0';
4126 else
4127 sprintf(name, last_image_filename, last_image_id);
4128 if (is_html) {
4129 switch (last_position) {
4130 case 'c':
4131 out->start_special();
465b256c 4132 put(out, "devtag:.centered-image");
92d0a6a6
JR
4133 break;
4134 case 'r':
4135 out->start_special();
465b256c 4136 put(out, "devtag:.right-image");
92d0a6a6
JR
4137 break;
4138 case 'l':
4139 out->start_special();
465b256c 4140 put(out, "devtag:.left-image");
92d0a6a6
JR
4141 break;
4142 case 'i':
4143 ;
4144 default:
4145 ;
4146 }
4147 out->end_special();
4148 out->start_special();
465b256c 4149 put(out, "devtag:.auto-image ");
92d0a6a6
JR
4150 put(out, name);
4151 out->end_special();
4152 }
4153 else {
4154 // postscript (or other device)
4155 if (suppress_start_page > 0 && current_page != suppress_start_page)
4156 error("suppression limit registers span more than one page;\n"
4d3e9548 4157 "image description %1 will be wrong", image_no);
92d0a6a6
JR
4158 // if (topdiv->get_page_number() != suppress_start_page)
4159 // fprintf(stderr, "end of image and topdiv page = %d and suppress_start_page = %d\n",
4160 // topdiv->get_page_number(), suppress_start_page);
4161
4162 // remember that the filename will contain a %d in which the
4163 // image_no is placed
4164 fprintf(stderr,
4165 "grohtml-info:page %d %d %d %d %d %d %s %d %d %s\n",
4166 topdiv->get_page_number(),
4167 get_reg_int("opminx"), get_reg_int("opminy"),
4168 get_reg_int("opmaxx"), get_reg_int("opmaxy"),
4169 // page offset + line length
4170 get_reg_int(".o") + get_reg_int(".l"),
4171 name, hresolution, vresolution, get_reg_str(".F"));
4172 fflush(stderr);
4173 }
4174 }
4175 else {
4176 if (is_on) {
4177 out->on();
4178 // lastly we reset the output registers
4179 reset_output_registers();
4180 }
4181 else
4182 out->off();
4183 suppress_start_page = current_page;
4184 }
4185 }
4186}
4187
4188int suppress_node::force_tprint()
4189{
4190 return is_on;
4191}
4192
465b256c
JR
4193int suppress_node::is_tag()
4194{
4195 return is_on;
4196}
4197
92d0a6a6
JR
4198hunits suppress_node::width()
4199{
4200 return H0;
4201}
4202
4203/* composite_node */
4204
4205class composite_node : public charinfo_node {
4206 node *n;
4207 tfont *tf;
4208public:
465b256c 4209 composite_node(node *, charinfo *, tfont *, statem *, int, node * = 0);
92d0a6a6
JR
4210 ~composite_node();
4211 node *copy();
4212 hunits width();
4213 node *last_char_node();
4214 units size();
4215 void tprint(troff_output_file *);
4216 hyphenation_type get_hyphenation_type();
4217 void ascii_print(ascii_output_file *);
4218 void asciify(macro *);
4219 hyphen_list *get_hyphen_list(hyphen_list *, int *);
4220 node *add_self(node *, hyphen_list **);
4221 tfont *get_tfont();
4222 int same(node *);
4223 const char *type();
4224 int force_tprint();
465b256c 4225 int is_tag();
92d0a6a6
JR
4226 void vertical_extent(vunits *, vunits *);
4227 vunits vertical_width();
4228};
4229
465b256c
JR
4230composite_node::composite_node(node *p, charinfo *c, tfont *t, statem *s,
4231 int pop, node *x)
4232: charinfo_node(c, s, pop, x), n(p), tf(t)
92d0a6a6
JR
4233{
4234}
4235
4236composite_node::~composite_node()
4237{
4238 delete_node_list(n);
4239}
4240
4241node *composite_node::copy()
4242{
465b256c 4243 return new composite_node(copy_node_list(n), ci, tf, state, div_nest_level);
92d0a6a6
JR
4244}
4245
4246hunits composite_node::width()
4247{
4248 hunits x;
4249 if (tf->get_constant_space(&x))
4250 return x;
4251 x = H0;
4252 for (node *tem = n; tem; tem = tem->next)
4253 x += tem->width();
4254 hunits offset;
4255 if (tf->get_bold(&offset))
4256 x += offset;
4257 x += tf->get_track_kern();
4258 return x;
4259}
4260
4261node *composite_node::last_char_node()
4262{
4263 return this;
4264}
4265
4266vunits composite_node::vertical_width()
4267{
4268 vunits v = V0;
4269 for (node *tem = n; tem; tem = tem->next)
4270 v += tem->vertical_width();
4271 return v;
4272}
4273
4274units composite_node::size()
4275{
4276 return tf->get_size().to_units();
4277}
4278
4279hyphenation_type composite_node::get_hyphenation_type()
4280{
4281 return HYPHEN_MIDDLE;
4282}
4283
4284void composite_node::asciify(macro *m)
4285{
4286 unsigned char c = ci->get_asciify_code();
4287 if (c == 0)
4288 c = ci->get_ascii_code();
4289 if (c != 0) {
4290 m->append(c);
4291 delete this;
4292 }
4293 else
4294 m->append(this);
4295}
4296
4297void composite_node::ascii_print(ascii_output_file *ascii)
4298{
4299 unsigned char c = ci->get_ascii_code();
4300 if (c != 0)
4301 ascii->outc(c);
4302 else
4303 ascii->outs(ci->nm.contents());
4304
4305}
4306
4307hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail, int *count)
4308{
4309 (*count)++;
4310 return new hyphen_list(ci->get_hyphenation_code(), tail);
4311}
4312
4313node *composite_node::add_self(node *nn, hyphen_list **p)
4314{
4315 assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
4316 next = nn;
4317 nn = this;
4318 if ((*p)->hyphen)
4319 nn = nn->add_discretionary_hyphen();
4320 hyphen_list *pp = *p;
4321 *p = (*p)->next;
4322 delete pp;
4323 return nn;
4324}
4325
4326tfont *composite_node::get_tfont()
4327{
4328 return tf;
4329}
4330
4331node *reverse_node_list(node *n)
4332{
4333 node *r = 0;
4334 while (n) {
4335 node *tem = n;
4336 n = n->next;
4337 tem->next = r;
4338 r = tem;
4339 }
4340 return r;
4341}
4342
4343void composite_node::vertical_extent(vunits *minimum, vunits *maximum)
4344{
4345 n = reverse_node_list(n);
4346 node_list_vertical_extent(n, minimum, maximum);
4347 n = reverse_node_list(n);
4348}
4349
4350width_list::width_list(hunits w, hunits s)
4351: width(w), sentence_width(s), next(0)
4352{
4353}
4354
4355width_list::width_list(width_list *w)
4356: width(w->width), sentence_width(w->sentence_width), next(0)
4357{
4358}
4359
4360word_space_node::word_space_node(hunits d, color *c, width_list *w, node *x)
4361: space_node(d, c, x), orig_width(w), unformat(0)
4362{
4363}
4364
4365word_space_node::word_space_node(hunits d, int s, color *c, width_list *w,
465b256c
JR
4366 int flag, statem *st, int pop, node *x)
4367: space_node(d, s, 0, c, st, pop, x), orig_width(w), unformat(flag)
92d0a6a6
JR
4368{
4369}
4370
4371word_space_node::~word_space_node()
4372{
4373 width_list *w = orig_width;
4374 while (w != 0) {
4375 width_list *tmp = w;
4376 w = w->next;
4377 delete tmp;
4378 }
4379}
4380
4381node *word_space_node::copy()
4382{
4383 assert(orig_width != 0);
4384 width_list *w_old_curr = orig_width;
4385 width_list *w_new_curr = new width_list(w_old_curr);
4386 width_list *w_new = w_new_curr;
4387 w_old_curr = w_old_curr->next;
4388 while (w_old_curr != 0) {
4389 w_new_curr->next = new width_list(w_old_curr);
4390 w_new_curr = w_new_curr->next;
4391 w_old_curr = w_old_curr->next;
4392 }
465b256c
JR
4393 return new word_space_node(n, set, col, w_new, unformat, state,
4394 div_nest_level);
92d0a6a6
JR
4395}
4396
4397int word_space_node::set_unformat_flag()
4398{
4399 unformat = 1;
4400 return 1;
4401}
4402
4403void word_space_node::tprint(troff_output_file *out)
4404{
4405 out->fill_color(col);
4406 out->word_marker();
4407 out->right(n);
4408}
4409
4410int word_space_node::merge_space(hunits h, hunits sw, hunits ssw)
4411{
4412 n += h;
4413 assert(orig_width != 0);
4414 width_list *w = orig_width;
4415 for (; w->next; w = w->next)
4416 ;
4417 w->next = new width_list(sw, ssw);
4418 return 1;
4419}
4420
4421unbreakable_space_node::unbreakable_space_node(hunits d, color *c, node *x)
4422: word_space_node(d, c, 0, x)
4423{
4424}
4425
4426unbreakable_space_node::unbreakable_space_node(hunits d, int s,
465b256c
JR
4427 color *c, statem *st, int pop,
4428 node *x)
4429: word_space_node(d, s, c, 0, 0, st, pop, x)
92d0a6a6
JR
4430{
4431}
4432
4433node *unbreakable_space_node::copy()
4434{
465b256c 4435 return new unbreakable_space_node(n, set, col, state, div_nest_level);
92d0a6a6
JR
4436}
4437
4438int unbreakable_space_node::force_tprint()
4439{
4440 return 0;
4441}
4442
465b256c
JR
4443int unbreakable_space_node::is_tag()
4444{
4445 return 0;
4446}
4447
92d0a6a6
JR
4448breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
4449 breakpoint *rest, int)
4450{
4451 return rest;
4452}
4453
4454int unbreakable_space_node::nbreaks()
4455{
4456 return 0;
4457}
4458
4459void unbreakable_space_node::split(int, node **, node **)
4460{
4461 assert(0);
4462}
4463
4464int unbreakable_space_node::merge_space(hunits, hunits, hunits)
4465{
4466 return 0;
4467}
4468
4469hvpair::hvpair()
4470{
4471}
4472
4473draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4474 color *gc, color *fc)
4475: npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4476{
4477 point = new hvpair[npoints];
4478 for (int i = 0; i < npoints; i++)
4479 point[i] = p[i];
4480}
4481
465b256c
JR
4482draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4483 color *gc, color *fc, statem *st, int pop)
4484: node(0, st, pop), npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4485{
4486 point = new hvpair[npoints];
4487 for (int i = 0; i < npoints; i++)
4488 point[i] = p[i];
4489}
4490
92d0a6a6
JR
4491int draw_node::same(node *n)
4492{
4493 draw_node *nd = (draw_node *)n;
4494 if (code != nd->code || npoints != nd->npoints || sz != nd->sz
4495 || gcol != nd->gcol || fcol != nd->fcol)
4496 return 0;
4497 for (int i = 0; i < npoints; i++)
4498 if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
4499 return 0;
4500 return 1;
4501}
4502
4503const char *draw_node::type()
4504{
4505 return "draw_node";
4506}
4507
4508int draw_node::force_tprint()
4509{
4510 return 0;
4511}
4512
465b256c
JR
4513int draw_node::is_tag()
4514{
4515 return 0;
4516}
4517
92d0a6a6
JR
4518draw_node::~draw_node()
4519{
4520 if (point)
4521 a_delete point;
4522}
4523
4524hunits draw_node::width()
4525{
4526 hunits x = H0;
4527 for (int i = 0; i < npoints; i++)
4528 x += point[i].h;
4529 return x;
4530}
4531
4532vunits draw_node::vertical_width()
4533{
4534 if (code == 'e')
4535 return V0;
4536 vunits x = V0;
4537 for (int i = 0; i < npoints; i++)
4538 x += point[i].v;
4539 return x;
4540}
4541
4542node *draw_node::copy()
4543{
465b256c
JR
4544 return new draw_node(code, point, npoints, sz, gcol, fcol, state,
4545 div_nest_level);
92d0a6a6
JR
4546}
4547
4548void draw_node::tprint(troff_output_file *out)
4549{
4550 out->draw(code, point, npoints, sz, gcol, fcol);
4551}
4552
4553/* tprint methods */
4554
4555void glyph_node::tprint(troff_output_file *out)
4556{
4557 tfont *ptf = tf->get_plain();
4558 if (ptf == tf)
4559 out->put_char_width(ci, ptf, gcol, fcol, width(), H0);
4560 else {
4561 hunits offset;
4562 int bold = tf->get_bold(&offset);
4563 hunits w = ptf->get_width(ci);
4564 hunits k = H0;
4565 hunits x;
4566 int cs = tf->get_constant_space(&x);
4567 if (cs) {
4568 x -= w;
4569 if (bold)
4570 x -= offset;
4571 hunits x2 = x/2;
4572 out->right(x2);
4573 k = x - x2;
4574 }
4575 else
4576 k = tf->get_track_kern();
4577 if (bold) {
4578 out->put_char(ci, ptf, gcol, fcol);
4579 out->right(offset);
4580 }
4581 out->put_char_width(ci, ptf, gcol, fcol, w, k);
4582 }
4583}
4584
4585void glyph_node::zero_width_tprint(troff_output_file *out)
4586{
4587 tfont *ptf = tf->get_plain();
4588 hunits offset;
4589 int bold = tf->get_bold(&offset);
4590 hunits x;
4591 int cs = tf->get_constant_space(&x);
4592 if (cs) {
4593 x -= ptf->get_width(ci);
4594 if (bold)
4595 x -= offset;
4596 x = x/2;
4597 out->right(x);
4598 }
4599 out->put_char(ci, ptf, gcol, fcol);
4600 if (bold) {
4601 out->right(offset);
4602 out->put_char(ci, ptf, gcol, fcol);
4603 out->right(-offset);
4604 }
4605 if (cs)
4606 out->right(-x);
4607}
4608
4609void break_char_node::tprint(troff_output_file *t)
4610{
4611 ch->tprint(t);
4612}
4613
4614void break_char_node::zero_width_tprint(troff_output_file *t)
4615{
4616 ch->zero_width_tprint(t);
4617}
4618
4619void hline_node::tprint(troff_output_file *out)
4620{
4621 if (x < H0) {
4622 out->right(x);
4623 x = -x;
4624 }
4625 if (n == 0) {
4626 out->right(x);
4627 return;
4628 }
4629 hunits w = n->width();
4630 if (w <= H0) {
4631 error("horizontal line drawing character must have positive width");
4632 out->right(x);
4633 return;
4634 }
4635 int i = int(x/w);
4636 if (i == 0) {
4637 hunits xx = x - w;
4638 hunits xx2 = xx/2;
4639 out->right(xx2);
4640 if (out->is_on())
4641 n->tprint(out);
4642 out->right(xx - xx2);
4643 }
4644 else {
4645 hunits rem = x - w*i;
4646 if (rem > H0)
4647 if (n->overlaps_horizontally()) {
4648 if (out->is_on())
4649 n->tprint(out);
4650 out->right(rem - w);
4651 }
4652 else
4653 out->right(rem);
4654 while (--i >= 0)
4655 if (out->is_on())
4656 n->tprint(out);
4657 }
4658}
4659
4660void vline_node::tprint(troff_output_file *out)
4661{
4662 if (n == 0) {
4663 out->down(x);
4664 return;
4665 }
4666 vunits h = n->size();
4667 int overlaps = n->overlaps_vertically();
4668 vunits y = x;
4669 if (y < V0) {
4670 y = -y;
4671 int i = y / h;
4672 vunits rem = y - i*h;
4673 if (i == 0) {
4674 out->right(n->width());
4675 out->down(-rem);
4676 }
4677 else {
4678 while (--i > 0) {
4679 n->zero_width_tprint(out);
4680 out->down(-h);
4681 }
4682 if (overlaps) {
4683 n->zero_width_tprint(out);
4684 out->down(-rem);
4685 if (out->is_on())
4686 n->tprint(out);
4687 out->down(-h);
4688 }
4689 else {
4690 if (out->is_on())
4691 n->tprint(out);
4692 out->down(-h - rem);
4693 }
4694 }
4695 }
4696 else {
4697 int i = y / h;
4698 vunits rem = y - i*h;
4699 if (i == 0) {
4700 out->down(rem);
4701 out->right(n->width());
4702 }
4703 else {
4704 out->down(h);
4705 if (overlaps)
4706 n->zero_width_tprint(out);
4707 out->down(rem);
4708 while (--i > 0) {
4709 n->zero_width_tprint(out);
4710 out->down(h);
4711 }
4712 if (out->is_on())
4713 n->tprint(out);
4714 }
4715 }
4716}
4717
4718void zero_width_node::tprint(troff_output_file *out)
4719{
4720 if (!n)
4721 return;
4722 if (!n->next) {
4723 n->zero_width_tprint(out);
4724 return;
4725 }
4726 int hpos = out->get_hpos();
4727 int vpos = out->get_vpos();
4728 node *tem = n;
4729 while (tem) {
4730 tem->tprint(out);
4731 tem = tem->next;
4732 }
4733 out->moveto(hpos, vpos);
4734}
4735
4736void overstrike_node::tprint(troff_output_file *out)
4737{
4738 hunits pos = H0;
4739 for (node *tem = list; tem; tem = tem->next) {
4740 hunits x = (max_width - tem->width())/2;
4741 out->right(x - pos);
4742 pos = x;
4743 tem->zero_width_tprint(out);
4744 }
4745 out->right(max_width - pos);
4746}
4747
4748void bracket_node::tprint(troff_output_file *out)
4749{
4750 if (list == 0)
4751 return;
4752 int npieces = 0;
4753 node *tem;
4754 for (tem = list; tem; tem = tem->next)
4755 ++npieces;
4756 vunits h = list->size();
4757 vunits totalh = h*npieces;
4758 vunits y = (totalh - h)/2;
4759 out->down(y);
4760 for (tem = list; tem; tem = tem->next) {
4761 tem->zero_width_tprint(out);
4762 out->down(-h);
4763 }
4764 out->right(max_width);
4765 out->down(totalh - y);
4766}
4767
4768void node::tprint(troff_output_file *)
4769{
4770}
4771
4772void node::zero_width_tprint(troff_output_file *out)
4773{
4774 int hpos = out->get_hpos();
4775 int vpos = out->get_vpos();
4776 tprint(out);
4777 out->moveto(hpos, vpos);
4778}
4779
4780void space_node::tprint(troff_output_file *out)
4781{
4782 out->fill_color(col);
4783 out->right(n);
4784}
4785
4786void hmotion_node::tprint(troff_output_file *out)
4787{
4788 out->fill_color(col);
4789 out->right(n);
4790}
4791
4792void space_char_hmotion_node::tprint(troff_output_file *out)
4793{
4794 out->fill_color(col);
4795 if (is_html) {
4796 // we emit the space width as a negative glyph index
4797 out->flush_tbuf();
4798 out->do_motion();
4799 out->put('N');
4800 out->put(-n.to_units());
4801 out->put('\n');
4802 }
4803 out->right(n);
4804}
4805
4806void vmotion_node::tprint(troff_output_file *out)
4807{
4808 out->fill_color(col);
4809 out->down(n);
4810}
4811
4812void kern_pair_node::tprint(troff_output_file *out)
4813{
4814 n1->tprint(out);
4815 out->right(amount);
4816 n2->tprint(out);
4817}
4818
4819static void tprint_reverse_node_list(troff_output_file *out, node *n)
4820{
4821 if (n == 0)
4822 return;
4823 tprint_reverse_node_list(out, n->next);
4824 n->tprint(out);
4825}
4826
4827void dbreak_node::tprint(troff_output_file *out)
4828{
4829 tprint_reverse_node_list(out, none);
4830}
4831
4832void composite_node::tprint(troff_output_file *out)
4833{
4834 hunits bold_offset;
4835 int is_bold = tf->get_bold(&bold_offset);
4836 hunits track_kern = tf->get_track_kern();
4837 hunits constant_space;
4838 int is_constant_spaced = tf->get_constant_space(&constant_space);
4839 hunits x = H0;
4840 if (is_constant_spaced) {
4841 x = constant_space;
4842 for (node *tem = n; tem; tem = tem->next)
4843 x -= tem->width();
4844 if (is_bold)
4845 x -= bold_offset;
4846 hunits x2 = x/2;
4847 out->right(x2);
4848 x -= x2;
4849 }
4850 if (is_bold) {
4851 int hpos = out->get_hpos();
4852 int vpos = out->get_vpos();
4853 tprint_reverse_node_list(out, n);
4854 out->moveto(hpos, vpos);
4855 out->right(bold_offset);
4856 }
4857 tprint_reverse_node_list(out, n);
4858 if (is_constant_spaced)
4859 out->right(x);
4860 else
4861 out->right(track_kern);
4862}
4863
4864node *make_composite_node(charinfo *s, environment *env)
4865{
4866 int fontno = env_definite_font(env);
4867 if (fontno < 0) {
4868 error("no current font");
4869 return 0;
4870 }
4871 assert(fontno < font_table_size && font_table[fontno] != 0);
4872 node *n = charinfo_to_node_list(s, env);
4873 font_size fs = env->get_font_size();
4874 int char_height = env->get_char_height();
4875 int char_slant = env->get_char_slant();
4876 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
4877 fontno);
4878 if (env->is_composite())
4879 tf = tf->get_plain();
465b256c 4880 return new composite_node(n, s, tf, 0, 0, 0);
92d0a6a6
JR
4881}
4882
4883node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0)
4884{
4885 int fontno = env_definite_font(env);
4886 if (fontno < 0) {
4887 error("no current font");
4888 return 0;
4889 }
4890 assert(fontno < font_table_size && font_table[fontno] != 0);
4891 int fn = fontno;
4892 int found = font_table[fontno]->contains(s);
4893 if (!found) {
4894 macro *mac = s->get_macro();
4895 if (mac && s->is_fallback())
4896 return make_composite_node(s, env);
4897 if (s->numbered()) {
4898 if (!no_error_message)
4899 warning(WARN_CHAR, "can't find numbered character %1",
4900 s->get_number());
4901 return 0;
4902 }
4903 special_font_list *sf = font_table[fontno]->sf;
4904 while (sf != 0 && !found) {
4905 fn = sf->n;
4906 if (font_table[fn])
4907 found = font_table[fn]->contains(s);
4908 sf = sf->next;
4909 }
4910 if (!found) {
4911 symbol f = font_table[fontno]->get_name();
4912 string gl(f.contents());
4913 gl += ' ';
4914 gl += s->nm.contents();
4915 gl += '\0';
4916 charinfo *ci = get_charinfo(symbol(gl.contents()));
4917 if (ci && ci->get_macro())
4918 return make_composite_node(ci, env);
4919 }
4920 if (!found) {
4921 sf = global_special_fonts;
4922 while (sf != 0 && !found) {
4923 fn = sf->n;
4924 if (font_table[fn])
4925 found = font_table[fn]->contains(s);
4926 sf = sf->next;
4927 }
4928 }
4929 if (!found)
4930 if (mac && s->is_special())
4931 return make_composite_node(s, env);
4932 if (!found) {
4933 for (fn = 0; fn < font_table_size; fn++)
4934 if (font_table[fn]
4935 && font_table[fn]->is_special()
4936 && font_table[fn]->contains(s)) {
4937 found = 1;
4938 break;
4939 }
4940 }
4941 if (!found) {
4942 if (!no_error_message && s->first_time_not_found()) {
4943 unsigned char input_code = s->get_ascii_code();
4944 if (input_code != 0) {
4945 if (csgraph(input_code))
4946 warning(WARN_CHAR, "can't find character `%1'", input_code);
4947 else
4948 warning(WARN_CHAR, "can't find character with input code %1",
4949 int(input_code));
4950 }
4d3e9548
JL
4951 else if (s->nm.contents()) {
4952 const char *nm = s->nm.contents();
4953 const char *backslash = (nm[1] == 0) ? "\\" : "";
4954 warning(WARN_CHAR, "can't find special character `%1%2'",
4955 backslash, nm);
4956 }
92d0a6a6
JR
4957 }
4958 return 0;
4959 }
4960 }
4961 font_size fs = env->get_font_size();
4962 int char_height = env->get_char_height();
4963 int char_slant = env->get_char_slant();
4964 tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn);
4965 if (env->is_composite())
4966 tf = tf->get_plain();
4967 color *gcol = env->get_glyph_color();
4968 color *fcol = env->get_fill_color();
465b256c 4969 return new glyph_node(s, tf, gcol, fcol, 0, 0);
92d0a6a6
JR
4970}
4971
4972node *make_node(charinfo *ci, environment *env)
4973{
4974 switch (ci->get_special_translation()) {
4975 case charinfo::TRANSLATE_SPACE:
4976 return new space_char_hmotion_node(env->get_space_width(),
4977 env->get_fill_color());
4978 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4979 return new unbreakable_space_node(env->get_space_width(),
4980 env->get_fill_color());
4981 case charinfo::TRANSLATE_DUMMY:
4982 return new dummy_node;
4983 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4984 error("translation to \\% ignored in this context");
4985 break;
4986 }
4987 charinfo *tem = ci->get_translation();
4988 if (tem)
4989 ci = tem;
4990 macro *mac = ci->get_macro();
4991 if (mac && ci->is_normal())
4992 return make_composite_node(ci, env);
4993 else
4994 return make_glyph_node(ci, env);
4995}
4996
4997int character_exists(charinfo *ci, environment *env)
4998{
4999 if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
5000 return 1;
5001 charinfo *tem = ci->get_translation();
5002 if (tem)
5003 ci = tem;
5004 if (ci->get_macro())
5005 return 1;
5006 node *nd = make_glyph_node(ci, env, 1);
5007 if (nd) {
5008 delete nd;
5009 return 1;
5010 }
5011 return 0;
5012}
5013
5014node *node::add_char(charinfo *ci, environment *env,
465b256c 5015 hunits *widthp, int *spacep, node **glyph_comp_np)
92d0a6a6
JR
5016{
5017 node *res;
5018 switch (ci->get_special_translation()) {
5019 case charinfo::TRANSLATE_SPACE:
5020 res = new space_char_hmotion_node(env->get_space_width(),
5021 env->get_fill_color(), this);
5022 *widthp += res->width();
5023 return res;
5024 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
5025 res = new unbreakable_space_node(env->get_space_width(),
5026 env->get_fill_color(), this);
5027 res->freeze_space();
5028 *widthp += res->width();
5029 *spacep += res->nspaces();
5030 return res;
5031 case charinfo::TRANSLATE_DUMMY:
5032 return new dummy_node(this);
5033 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
5034 return add_discretionary_hyphen();
5035 }
5036 charinfo *tem = ci->get_translation();
5037 if (tem)
5038 ci = tem;
5039 macro *mac = ci->get_macro();
5040 if (mac && ci->is_normal()) {
5041 res = make_composite_node(ci, env);
5042 if (res) {
5043 res->next = this;
5044 *widthp += res->width();
465b256c
JR
5045 if (glyph_comp_np)
5046 *glyph_comp_np = res;
92d0a6a6 5047 }
465b256c
JR
5048 else {
5049 if (glyph_comp_np)
5050 *glyph_comp_np = res;
92d0a6a6 5051 return this;
465b256c 5052 }
92d0a6a6
JR
5053 }
5054 else {
5055 node *gn = make_glyph_node(ci, env);
5056 if (gn == 0)
5057 return this;
5058 else {
5059 hunits old_width = width();
5060 node *p = gn->merge_self(this);
5061 if (p == 0) {
5062 *widthp += gn->width();
5063 gn->next = this;
5064 res = gn;
5065 }
5066 else {
5067 *widthp += p->width() - old_width;
5068 res = p;
5069 }
465b256c
JR
5070 if (glyph_comp_np)
5071 *glyph_comp_np = res;
92d0a6a6
JR
5072 }
5073 }
5074 int break_code = 0;
5075 if (ci->can_break_before())
5076 break_code = 1;
5077 if (ci->can_break_after())
5078 break_code |= 2;
4d3e9548
JL
5079 if (ci->ignore_hcodes())
5080 break_code |= 4;
92d0a6a6
JR
5081 if (break_code) {
5082 node *next1 = res->next;
5083 res->next = 0;
5084 res = new break_char_node(res, break_code, env->get_fill_color(), next1);
5085 }
5086 return res;
5087}
5088
5089#ifdef __GNUG__
5090inline
5091#endif
5092int same_node(node *n1, node *n2)
5093{
5094 if (n1 != 0) {
5095 if (n2 != 0)
5096 return n1->type() == n2->type() && n1->same(n2);
5097 else
5098 return 0;
5099 }
5100 else
5101 return n2 == 0;
5102}
5103
5104int same_node_list(node *n1, node *n2)
5105{
5106 while (n1 && n2) {
5107 if (n1->type() != n2->type() || !n1->same(n2))
5108 return 0;
5109 n1 = n1->next;
5110 n2 = n2->next;
5111 }
5112 return !n1 && !n2;
5113}
5114
5115int extra_size_node::same(node *nd)
5116{
5117 return n == ((extra_size_node *)nd)->n;
5118}
5119
5120const char *extra_size_node::type()
5121{
5122 return "extra_size_node";
5123}
5124
5125int extra_size_node::force_tprint()
5126{
5127 return 0;
5128}
5129
465b256c
JR
5130int extra_size_node::is_tag()
5131{
5132 return 0;
5133}
5134
92d0a6a6
JR
5135int vertical_size_node::same(node *nd)
5136{
5137 return n == ((vertical_size_node *)nd)->n;
5138}
5139
5140const char *vertical_size_node::type()
5141{
5142 return "vertical_size_node";
5143}
5144
5145int vertical_size_node::set_unformat_flag()
5146{
5147 return 0;
5148}
5149
5150int vertical_size_node::force_tprint()
5151{
5152 return 0;
5153}
5154
465b256c
JR
5155int vertical_size_node::is_tag()
5156{
5157 return 0;
5158}
5159
92d0a6a6
JR
5160int hmotion_node::same(node *nd)
5161{
5162 return n == ((hmotion_node *)nd)->n
5163 && col == ((hmotion_node *)nd)->col;
5164}
5165
5166const char *hmotion_node::type()
5167{
5168 return "hmotion_node";
5169}
5170
5171int hmotion_node::set_unformat_flag()
5172{
5173 unformat = 1;
5174 return 1;
5175}
5176
5177int hmotion_node::force_tprint()
5178{
5179 return 0;
5180}
5181
465b256c
JR
5182int hmotion_node::is_tag()
5183{
5184 return 0;
5185}
5186
92d0a6a6
JR
5187node *hmotion_node::add_self(node *nd, hyphen_list **p)
5188{
5189 next = nd;
5190 hyphen_list *pp = *p;
5191 *p = (*p)->next;
5192 delete pp;
5193 return this;
5194}
5195
5196hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail, int *)
5197{
5198 return new hyphen_list(0, tail);
5199}
5200
5201int space_char_hmotion_node::same(node *nd)
5202{
5203 return n == ((space_char_hmotion_node *)nd)->n
5204 && col == ((space_char_hmotion_node *)nd)->col;
5205}
5206
5207const char *space_char_hmotion_node::type()
5208{
5209 return "space_char_hmotion_node";
5210}
5211
5212int space_char_hmotion_node::force_tprint()
5213{
5214 return 0;
5215}
5216
465b256c
JR
5217int space_char_hmotion_node::is_tag()
5218{
5219 return 0;
5220}
5221
92d0a6a6
JR
5222node *space_char_hmotion_node::add_self(node *nd, hyphen_list **p)
5223{
5224 next = nd;
5225 hyphen_list *pp = *p;
5226 *p = (*p)->next;
5227 delete pp;
5228 return this;
5229}
5230
5231hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail,
5232 int *)
5233{
5234 return new hyphen_list(0, tail);
5235}
5236
5237int vmotion_node::same(node *nd)
5238{
5239 return n == ((vmotion_node *)nd)->n
5240 && col == ((vmotion_node *)nd)->col;
5241}
5242
5243const char *vmotion_node::type()
5244{
5245 return "vmotion_node";
5246}
5247
5248int vmotion_node::force_tprint()
5249{
5250 return 0;
5251}
5252
465b256c
JR
5253int vmotion_node::is_tag()
5254{
5255 return 0;
5256}
5257
92d0a6a6
JR
5258int hline_node::same(node *nd)
5259{
5260 return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n);
5261}
5262
5263const char *hline_node::type()
5264{
5265 return "hline_node";
5266}
5267
5268int hline_node::force_tprint()
5269{
5270 return 0;
5271}
5272
465b256c
JR
5273int hline_node::is_tag()
5274{
5275 return 0;
5276}
5277
92d0a6a6
JR
5278int vline_node::same(node *nd)
5279{
5280 return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n);
5281}
5282
5283const char *vline_node::type()
5284{
5285 return "vline_node";
5286}
5287
5288int vline_node::force_tprint()
5289{
5290 return 0;
5291}
5292
465b256c
JR
5293int vline_node::is_tag()
5294{
5295 return 0;
5296}
5297
92d0a6a6
JR
5298int dummy_node::same(node * /*nd*/)
5299{
5300 return 1;
5301}
5302
5303const char *dummy_node::type()
5304{
5305 return "dummy_node";
5306}
5307
5308int dummy_node::force_tprint()
5309{
5310 return 0;
5311}
5312
465b256c
JR
5313int dummy_node::is_tag()
5314{
5315 return 0;
5316}
5317
92d0a6a6
JR
5318int transparent_dummy_node::same(node * /*nd*/)
5319{
5320 return 1;
5321}
5322
5323const char *transparent_dummy_node::type()
5324{
5325 return "transparent_dummy_node";
5326}
5327
5328int transparent_dummy_node::force_tprint()
5329{
5330 return 0;
5331}
5332
465b256c
JR
5333int transparent_dummy_node::is_tag()
5334{
5335 return 0;
5336}
5337
92d0a6a6
JR
5338int transparent_dummy_node::ends_sentence()
5339{
5340 return 2;
5341}
5342
5343int zero_width_node::same(node *nd)
5344{
5345 return same_node_list(n, ((zero_width_node *)nd)->n);
5346}
5347
5348const char *zero_width_node::type()
5349{
5350 return "zero_width_node";
5351}
5352
5353int zero_width_node::force_tprint()
5354{
5355 return 0;
5356}
5357
465b256c
JR
5358int zero_width_node::is_tag()
5359{
5360 return 0;
5361}
5362
92d0a6a6
JR
5363int italic_corrected_node::same(node *nd)
5364{
5365 return (x == ((italic_corrected_node *)nd)->x
5366 && same_node(n, ((italic_corrected_node *)nd)->n));
5367}
5368
5369const char *italic_corrected_node::type()
5370{
5371 return "italic_corrected_node";
5372}
5373
5374int italic_corrected_node::force_tprint()
5375{
5376 return 0;
5377}
5378
465b256c
JR
5379int italic_corrected_node::is_tag()
5380{
5381 return 0;
5382}
5383
92d0a6a6
JR
5384left_italic_corrected_node::left_italic_corrected_node(node *xx)
5385: node(xx), n(0)
5386{
5387}
5388
465b256c
JR
5389left_italic_corrected_node::left_italic_corrected_node(statem *s, int pop,
5390 node *xx)
5391: node(xx, s, pop), n(0)
5392{
5393}
5394
92d0a6a6
JR
5395left_italic_corrected_node::~left_italic_corrected_node()
5396{
5397 delete n;
5398}
5399
5400node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
5401{
5402 if (n == 0) {
5403 hunits lic = gn->left_italic_correction();
5404 if (!lic.is_zero()) {
5405 x = lic;
5406 n = gn;
5407 return this;
5408 }
5409 }
5410 else {
5411 node *nd = n->merge_glyph_node(gn);
5412 if (nd) {
5413 n = nd;
5414 x = n->left_italic_correction();
5415 return this;
5416 }
5417 }
5418 return 0;
5419}
5420
5421node *left_italic_corrected_node::copy()
5422{
465b256c
JR
5423 left_italic_corrected_node *nd =
5424 new left_italic_corrected_node(state, div_nest_level);
92d0a6a6
JR
5425 if (n) {
5426 nd->n = n->copy();
5427 nd->x = x;
5428 }
5429 return nd;
5430}
5431
5432void left_italic_corrected_node::tprint(troff_output_file *out)
5433{
5434 if (n) {
5435 out->right(x);
5436 n->tprint(out);
5437 }
5438}
5439
5440const char *left_italic_corrected_node::type()
5441{
5442 return "left_italic_corrected_node";
5443}
5444
5445int left_italic_corrected_node::force_tprint()
5446{
5447 return 0;
5448}
5449
465b256c
JR
5450int left_italic_corrected_node::is_tag()
5451{
5452 return 0;
5453}
5454
92d0a6a6
JR
5455int left_italic_corrected_node::same(node *nd)
5456{
5457 return (x == ((left_italic_corrected_node *)nd)->x
5458 && same_node(n, ((left_italic_corrected_node *)nd)->n));
5459}
5460
5461void left_italic_corrected_node::ascii_print(ascii_output_file *out)
5462{
5463 if (n)
5464 n->ascii_print(out);
5465}
5466
5467hunits left_italic_corrected_node::width()
5468{
5469 return n ? n->width() + x : H0;
5470}
5471
5472void left_italic_corrected_node::vertical_extent(vunits *minimum,
5473 vunits *maximum)
5474{
5475 if (n)
5476 n->vertical_extent(minimum, maximum);
5477 else
5478 node::vertical_extent(minimum, maximum);
5479}
5480
5481hunits left_italic_corrected_node::skew()
5482{
5483 return n ? n->skew() + x/2 : H0;
5484}
5485
5486hunits left_italic_corrected_node::subscript_correction()
5487{
5488 return n ? n->subscript_correction() : H0;
5489}
5490
5491hunits left_italic_corrected_node::italic_correction()
5492{
5493 return n ? n->italic_correction() : H0;
5494}
5495
5496int left_italic_corrected_node::ends_sentence()
5497{
5498 return n ? n->ends_sentence() : 0;
5499}
5500
5501int left_italic_corrected_node::overlaps_horizontally()
5502{
5503 return n ? n->overlaps_horizontally() : 0;
5504}
5505
5506int left_italic_corrected_node::overlaps_vertically()
5507{
5508 return n ? n->overlaps_vertically() : 0;
5509}
5510
5511node *left_italic_corrected_node::last_char_node()
5512{
5513 return n ? n->last_char_node() : 0;
5514}
5515
5516tfont *left_italic_corrected_node::get_tfont()
5517{
5518 return n ? n->get_tfont() : 0;
5519}
5520
5521hyphenation_type left_italic_corrected_node::get_hyphenation_type()
5522{
5523 if (n)
5524 return n->get_hyphenation_type();
5525 else
5526 return HYPHEN_MIDDLE;
5527}
5528
5529hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail,
5530 int *count)
5531{
5532 return n ? n->get_hyphen_list(tail, count) : tail;
5533}
5534
5535node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
5536{
5537 if (n) {
465b256c 5538 nd = new left_italic_corrected_node(state, div_nest_level, nd);
92d0a6a6
JR
5539 nd = n->add_self(nd, p);
5540 n = 0;
5541 delete this;
4d3e9548
JL
5542 return nd;
5543 }
5544 else {
5545 next = nd;
5546 return this;
92d0a6a6 5547 }
92d0a6a6
JR
5548}
5549
5550int left_italic_corrected_node::character_type()
5551{
5552 return n ? n->character_type() : 0;
5553}
5554
5555int overstrike_node::same(node *nd)
5556{
5557 return same_node_list(list, ((overstrike_node *)nd)->list);
5558}
5559
5560const char *overstrike_node::type()
5561{
5562 return "overstrike_node";
5563}
5564
5565int overstrike_node::force_tprint()
5566{
5567 return 0;
5568}
5569
465b256c
JR
5570int overstrike_node::is_tag()
5571{
5572 return 0;
5573}
5574
92d0a6a6
JR
5575node *overstrike_node::add_self(node *n, hyphen_list **p)
5576{
5577 next = n;
5578 hyphen_list *pp = *p;
5579 *p = (*p)->next;
5580 delete pp;
5581 return this;
5582}
5583
5584hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail, int *)
5585{
5586 return new hyphen_list(0, tail);
5587}
5588
5589int bracket_node::same(node *nd)
5590{
5591 return same_node_list(list, ((bracket_node *)nd)->list);
5592}
5593
5594const char *bracket_node::type()
5595{
5596 return "bracket_node";
5597}
5598
5599int bracket_node::force_tprint()
5600{
5601 return 0;
5602}
5603
465b256c
JR
5604int bracket_node::is_tag()
5605{
5606 return 0;
5607}
5608
92d0a6a6
JR
5609int composite_node::same(node *nd)
5610{
5611 return ci == ((composite_node *)nd)->ci
5612 && same_node_list(n, ((composite_node *)nd)->n);
5613}
5614
5615const char *composite_node::type()
5616{
5617 return "composite_node";
5618}
5619
5620int composite_node::force_tprint()
5621{
5622 return 0;
5623}
5624
465b256c
JR
5625int composite_node::is_tag()
5626{
5627 return 0;
5628}
5629
92d0a6a6
JR
5630int glyph_node::same(node *nd)
5631{
5632 return ci == ((glyph_node *)nd)->ci
5633 && tf == ((glyph_node *)nd)->tf
5634 && gcol == ((glyph_node *)nd)->gcol
5635 && fcol == ((glyph_node *)nd)->fcol;
5636}
5637
5638const char *glyph_node::type()
5639{
5640 return "glyph_node";
5641}
5642
5643int glyph_node::force_tprint()
5644{
5645 return 0;
5646}
5647
465b256c
JR
5648int glyph_node::is_tag()
5649{
5650 return 0;
5651}
5652
92d0a6a6
JR
5653int ligature_node::same(node *nd)
5654{
5655 return (same_node(n1, ((ligature_node *)nd)->n1)
5656 && same_node(n2, ((ligature_node *)nd)->n2)
5657 && glyph_node::same(nd));
5658}
5659
5660const char *ligature_node::type()
5661{
5662 return "ligature_node";
5663}
5664
5665int ligature_node::force_tprint()
5666{
5667 return 0;
5668}
5669
465b256c
JR
5670int ligature_node::is_tag()
5671{
5672 return 0;
5673}
5674
92d0a6a6
JR
5675int kern_pair_node::same(node *nd)
5676{
5677 return (amount == ((kern_pair_node *)nd)->amount
5678 && same_node(n1, ((kern_pair_node *)nd)->n1)
5679 && same_node(n2, ((kern_pair_node *)nd)->n2));
5680}
5681
5682const char *kern_pair_node::type()
5683{
5684 return "kern_pair_node";
5685}
5686
5687int kern_pair_node::force_tprint()
5688{
5689 return 0;
5690}
5691
465b256c
JR
5692int kern_pair_node::is_tag()
5693{
5694 return 0;
5695}
5696
92d0a6a6
JR
5697int dbreak_node::same(node *nd)
5698{
5699 return (same_node_list(none, ((dbreak_node *)nd)->none)
5700 && same_node_list(pre, ((dbreak_node *)nd)->pre)
5701 && same_node_list(post, ((dbreak_node *)nd)->post));
5702}
5703
5704const char *dbreak_node::type()
5705{
5706 return "dbreak_node";
5707}
5708
5709int dbreak_node::force_tprint()
5710{
5711 return 0;
5712}
5713
465b256c
JR
5714int dbreak_node::is_tag()
5715{
5716 return 0;
5717}
5718
92d0a6a6
JR
5719int break_char_node::same(node *nd)
5720{
5721 return break_code == ((break_char_node *)nd)->break_code
5722 && col == ((break_char_node *)nd)->col
5723 && same_node(ch, ((break_char_node *)nd)->ch);
5724}
5725
5726const char *break_char_node::type()
5727{
5728 return "break_char_node";
5729}
5730
5731int break_char_node::force_tprint()
5732{
5733 return 0;
5734}
5735
465b256c
JR
5736int break_char_node::is_tag()
5737{
5738 return 0;
5739}
5740
92d0a6a6
JR
5741int line_start_node::same(node * /*nd*/)
5742{
5743 return 1;
5744}
5745
5746const char *line_start_node::type()
5747{
5748 return "line_start_node";
5749}
5750
5751int line_start_node::force_tprint()
5752{
5753 return 0;
5754}
5755
465b256c
JR
5756int line_start_node::is_tag()
5757{
5758 return 0;
5759}
5760
92d0a6a6
JR
5761int space_node::same(node *nd)
5762{
5763 return n == ((space_node *)nd)->n
5764 && set == ((space_node *)nd)->set
5765 && col == ((space_node *)nd)->col;
5766}
5767
5768const char *space_node::type()
5769{
5770 return "space_node";
5771}
5772
5773int word_space_node::same(node *nd)
5774{
5775 return n == ((word_space_node *)nd)->n
5776 && set == ((word_space_node *)nd)->set
5777 && col == ((word_space_node *)nd)->col;
5778}
5779
5780const char *word_space_node::type()
5781{
5782 return "word_space_node";
5783}
5784
5785int word_space_node::force_tprint()
5786{
5787 return 0;
5788}
5789
465b256c
JR
5790int word_space_node::is_tag()
5791{
5792 return 0;
5793}
5794
92d0a6a6
JR
5795void unbreakable_space_node::tprint(troff_output_file *out)
5796{
5797 out->fill_color(col);
5798 if (is_html) {
5799 // we emit the space width as a negative glyph index
5800 out->flush_tbuf();
5801 out->do_motion();
5802 out->put('N');
5803 out->put(-n.to_units());
5804 out->put('\n');
5805 }
5806 out->right(n);
5807}
5808
5809int unbreakable_space_node::same(node *nd)
5810{
5811 return n == ((unbreakable_space_node *)nd)->n
5812 && set == ((unbreakable_space_node *)nd)->set
5813 && col == ((unbreakable_space_node *)nd)->col;
5814}
5815
5816const char *unbreakable_space_node::type()
5817{
5818 return "unbreakable_space_node";
5819}
5820
5821node *unbreakable_space_node::add_self(node *nd, hyphen_list **p)
5822{
5823 next = nd;
5824 hyphen_list *pp = *p;
5825 *p = (*p)->next;
5826 delete pp;
5827 return this;
5828}
5829
5830hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail, int *)
5831{
5832 return new hyphen_list(0, tail);
5833}
5834
5835int diverted_space_node::same(node *nd)
5836{
5837 return n == ((diverted_space_node *)nd)->n;
5838}
5839
5840const char *diverted_space_node::type()
5841{
5842 return "diverted_space_node";
5843}
5844
5845int diverted_space_node::force_tprint()
5846{
5847 return 0;
5848}
5849
465b256c
JR
5850int diverted_space_node::is_tag()
5851{
5852 return 0;
5853}
5854
92d0a6a6
JR
5855int diverted_copy_file_node::same(node *nd)
5856{
5857 return filename == ((diverted_copy_file_node *)nd)->filename;
5858}
5859
5860const char *diverted_copy_file_node::type()
5861{
5862 return "diverted_copy_file_node";
5863}
5864
5865int diverted_copy_file_node::force_tprint()
5866{
5867 return 0;
5868}
5869
465b256c
JR
5870int diverted_copy_file_node::is_tag()
5871{
5872 return 0;
5873}
5874
92d0a6a6
JR
5875// Grow the font_table so that its size is > n.
5876
5877static void grow_font_table(int n)
5878{
5879 assert(n >= font_table_size);
5880 font_info **old_font_table = font_table;
5881 int old_font_table_size = font_table_size;
5882 font_table_size = font_table_size ? (font_table_size*3)/2 : 10;
5883 if (font_table_size <= n)
5884 font_table_size = n + 10;
5885 font_table = new font_info *[font_table_size];
5886 if (old_font_table_size)
5887 memcpy(font_table, old_font_table,
5888 old_font_table_size*sizeof(font_info *));
5889 a_delete old_font_table;
5890 for (int i = old_font_table_size; i < font_table_size; i++)
5891 font_table[i] = 0;
5892}
5893
5894dictionary font_translation_dictionary(17);
5895
5896static symbol get_font_translation(symbol nm)
5897{
5898 void *p = font_translation_dictionary.lookup(nm);
5899 return p ? symbol((char *)p) : nm;
5900}
5901
5902dictionary font_dictionary(50);
5903
465b256c
JR
5904static int mount_font_no_translate(int n, symbol name, symbol external_name,
5905 int check_only = 0)
92d0a6a6
JR
5906{
5907 assert(n >= 0);
5908 // We store the address of this char in font_dictionary to indicate
5909 // that we've previously tried to mount the font and failed.
5910 static char a_char;
5911 font *fm = 0;
5912 void *p = font_dictionary.lookup(external_name);
5913 if (p == 0) {
5914 int not_found;
465b256c
JR
5915 fm = font::load_font(external_name.contents(), &not_found, check_only);
5916 if (check_only)
5917 return fm != 0;
92d0a6a6
JR
5918 if (!fm) {
5919 if (not_found)
5920 warning(WARN_FONT, "can't find font `%1'", external_name.contents());
5921 (void)font_dictionary.lookup(external_name, &a_char);
5922 return 0;
5923 }
5924 (void)font_dictionary.lookup(name, fm);
5925 }
5926 else if (p == &a_char) {
5927#if 0
5928 error("invalid font `%1'", external_name.contents());
5929#endif
5930 return 0;
5931 }
5932 else
5933 fm = (font*)p;
465b256c
JR
5934 if (check_only)
5935 return 1;
92d0a6a6
JR
5936 if (n >= font_table_size) {
5937 if (n - font_table_size > 1000) {
5938 error("font position too much larger than first unused position");
5939 return 0;
5940 }
5941 grow_font_table(n);
5942 }
5943 else if (font_table[n] != 0)
5944 delete font_table[n];
5945 font_table[n] = new font_info(name, n, external_name, fm);
5946 font_family::invalidate_fontno(n);
5947 return 1;
5948}
5949
5950int mount_font(int n, symbol name, symbol external_name)
5951{
5952 assert(n >= 0);
5953 name = get_font_translation(name);
5954 if (external_name.is_null())
5955 external_name = name;
5956 else
5957 external_name = get_font_translation(external_name);
5958 return mount_font_no_translate(n, name, external_name);
5959}
5960
465b256c
JR
5961int check_font(symbol fam, symbol name)
5962{
5963 if (check_style(name))
5964 name = concat(fam, name);
5965 return mount_font_no_translate(0, name, name, 1);
5966}
5967
5968int check_style(symbol s)
5969{
5970 int i = symbol_fontno(s);
5971 return i < 0 ? 0 : font_table[i]->is_style();
5972}
5973
92d0a6a6
JR
5974void mount_style(int n, symbol name)
5975{
5976 assert(n >= 0);
5977 if (n >= font_table_size) {
5978 if (n - font_table_size > 1000) {
5979 error("font position too much larger than first unused position");
5980 return;
5981 }
5982 grow_font_table(n);
5983 }
5984 else if (font_table[n] != 0)
5985 delete font_table[n];
5986 font_table[n] = new font_info(get_font_translation(name), n, NULL_SYMBOL, 0);
5987 font_family::invalidate_fontno(n);
5988}
5989
5990/* global functions */
5991
5992void font_translate()
5993{
5994 symbol from = get_name(1);
5995 if (!from.is_null()) {
5996 symbol to = get_name();
5997 if (to.is_null() || from == to)
5998 font_translation_dictionary.remove(from);
5999 else
6000 (void)font_translation_dictionary.lookup(from, (void *)to.contents());
6001 }
6002 skip_line();
6003}
6004
6005void font_position()
6006{
6007 int n;
6008 if (get_integer(&n)) {
6009 if (n < 0)
6010 error("negative font position");
6011 else {
6012 symbol internal_name = get_name(1);
6013 if (!internal_name.is_null()) {
6014 symbol external_name = get_long_name();
6015 mount_font(n, internal_name, external_name); // ignore error
6016 }
6017 }
6018 }
6019 skip_line();
6020}
6021
6022font_family::font_family(symbol s)
6023: map_size(10), nm(s)
6024{
6025 map = new int[map_size];
6026 for (int i = 0; i < map_size; i++)
6027 map[i] = -1;
6028}
6029
6030font_family::~font_family()
6031{
6032 a_delete map;
6033}
6034
6035int font_family::make_definite(int i)
6036{
6037 if (i >= 0) {
6038 if (i < map_size && map[i] >= 0)
6039 return map[i];
6040 else {
6041 if (i < font_table_size && font_table[i] != 0) {
6042 if (i >= map_size) {
6043 int old_map_size = map_size;
6044 int *old_map = map;
6045 map_size *= 3;
6046 map_size /= 2;
6047 if (i >= map_size)
6048 map_size = i + 10;
6049 map = new int[map_size];
6050 memcpy(map, old_map, old_map_size*sizeof(int));
6051 a_delete old_map;
6052 for (int j = old_map_size; j < map_size; j++)
6053 map[j] = -1;
6054 }
6055 if (font_table[i]->is_style()) {
6056 symbol sty = font_table[i]->get_name();
6057 symbol f = concat(nm, sty);
6058 int n;
6059 // don't use symbol_fontno, because that might return a style
6060 // and because we don't want to translate the name
6061 for (n = 0; n < font_table_size; n++)
6062 if (font_table[n] != 0 && font_table[n]->is_named(f)
6063 && !font_table[n]->is_style())
6064 break;
6065 if (n >= font_table_size) {
6066 n = next_available_font_position();
6067 if (!mount_font_no_translate(n, f, f))
6068 return -1;
6069 }
6070 return map[i] = n;
6071 }
6072 else
6073 return map[i] = i;
6074 }
6075 else
6076 return -1;
6077 }
6078 }
6079 else
6080 return -1;
6081}
6082
6083dictionary family_dictionary(5);
6084
6085font_family *lookup_family(symbol nm)
6086{
6087 font_family *f = (font_family *)family_dictionary.lookup(nm);
6088 if (!f) {
6089 f = new font_family(nm);
6090 (void)family_dictionary.lookup(nm, f);
6091 }
6092 return f;
6093}
6094
6095void font_family::invalidate_fontno(int n)
6096{
6097 assert(n >= 0 && n < font_table_size);
6098 dictionary_iterator iter(family_dictionary);
6099 symbol nam;
6100 font_family *fam;
6101 while (iter.get(&nam, (void **)&fam)) {
6102 int mapsize = fam->map_size;
6103 if (n < mapsize)
6104 fam->map[n] = -1;
6105 for (int i = 0; i < mapsize; i++)
6106 if (fam->map[i] == n)
6107 fam->map[i] = -1;
6108 }
6109}
6110
6111void style()
6112{
6113 int n;
6114 if (get_integer(&n)) {
6115 if (n < 0)
6116 error("negative font position");
6117 else {
6118 symbol internal_name = get_name(1);
6119 if (!internal_name.is_null())
6120 mount_style(n, internal_name);
6121 }
6122 }
6123 skip_line();
6124}
6125
6126static int get_fontno()
6127{
6128 int n;
6129 tok.skip();
6130 if (tok.delimiter()) {
6131 symbol s = get_name(1);
6132 if (!s.is_null()) {
6133 n = symbol_fontno(s);
6134 if (n < 0) {
6135 n = next_available_font_position();
6136 if (!mount_font(n, s))
6137 return -1;
6138 }
6139 return curenv->get_family()->make_definite(n);
6140 }
6141 }
6142 else if (get_integer(&n)) {
6143 if (n < 0 || n >= font_table_size || font_table[n] == 0)
6144 error("bad font number");
6145 else
6146 return curenv->get_family()->make_definite(n);
6147 }
6148 return -1;
6149}
6150
6151static int underline_fontno = 2;
6152
6153void underline_font()
6154{
6155 int n = get_fontno();
6156 if (n >= 0)
6157 underline_fontno = n;
6158 skip_line();
6159}
6160
6161int get_underline_fontno()
6162{
6163 return underline_fontno;
6164}
6165
6166void define_font_special_character()
6167{
6168 int n = get_fontno();
6169 if (n < 0) {
6170 skip_line();
6171 return;
6172 }
6173 symbol f = font_table[n]->get_name();
6174 do_define_character(CHAR_FONT_SPECIAL, f.contents());
6175}
6176
6177void remove_font_special_character()
6178{
6179 int n = get_fontno();
6180 if (n < 0) {
6181 skip_line();
6182 return;
6183 }
6184 symbol f = font_table[n]->get_name();
6185 while (!tok.newline() && !tok.eof()) {
6186 if (!tok.space() && !tok.tab()) {
6187 charinfo *s = tok.get_char(1);
6188 string gl(f.contents());
6189 gl += ' ';
6190 gl += s->nm.contents();
6191 gl += '\0';
6192 charinfo *ci = get_charinfo(symbol(gl.contents()));
6193 if (!ci)
6194 break;
6195 macro *m = ci->set_macro(0);
6196 if (m)
6197 delete m;
6198 }
6199 tok.next();
6200 }
6201 skip_line();
6202}
6203
6204static void read_special_fonts(special_font_list **sp)
6205{
6206 special_font_list *s = *sp;
6207 *sp = 0;
6208 while (s != 0) {
6209 special_font_list *tem = s;
6210 s = s->next;
6211 delete tem;
6212 }
6213 special_font_list **p = sp;
6214 while (has_arg()) {
6215 int i = get_fontno();
6216 if (i >= 0) {
6217 special_font_list *tem = new special_font_list;
6218 tem->n = i;
6219 tem->next = 0;
6220 *p = tem;
6221 p = &(tem->next);
6222 }
6223 }
6224}
6225
6226void font_special_request()
6227{
6228 int n = get_fontno();
6229 if (n >= 0)
6230 read_special_fonts(&font_table[n]->sf);
6231 skip_line();
6232}
6233
6234void special_request()
6235{
6236 read_special_fonts(&global_special_fonts);
6237 skip_line();
6238}
6239
4d3e9548
JL
6240void font_zoom_request()
6241{
6242 int n = get_fontno();
6243 if (n >= 0) {
6244 if (font_table[n]->is_style())
6245 warning(WARN_FONT, "can't set zoom factor for a style");
6246 else {
6247 int zoom;
6248 if (has_arg() && get_integer(&zoom)) {
6249 if (zoom < 0)
6250 warning(WARN_FONT, "can't use negative zoom factor");
6251 else
6252 font_table[n]->set_zoom(zoom);
6253 }
6254 else
6255 font_table[n]->set_zoom(0);
6256 }
6257 }
6258 skip_line();
6259}
6260
92d0a6a6
JR
6261int next_available_font_position()
6262{
6263 int i;
6264 for (i = 1; i < font_table_size && font_table[i] != 0; i++)
6265 ;
6266 return i;
6267}
6268
6269int symbol_fontno(symbol s)
6270{
6271 s = get_font_translation(s);
6272 for (int i = 0; i < font_table_size; i++)
6273 if (font_table[i] != 0 && font_table[i]->is_named(s))
6274 return i;
6275 return -1;
6276}
6277
6278int is_good_fontno(int n)
6279{
6280 return n >= 0 && n < font_table_size && font_table[n] != 0;
6281}
6282
6283int get_bold_fontno(int n)
6284{
6285 if (n >= 0 && n < font_table_size && font_table[n] != 0) {
6286 hunits offset;
6287 if (font_table[n]->get_bold(&offset))
6288 return offset.to_units() + 1;
6289 else
6290 return 0;
6291 }
6292 else
6293 return 0;
6294}
6295
6296hunits env_digit_width(environment *env)
6297{
6298 node *n = make_glyph_node(charset_table['0'], env);
6299 if (n) {
6300 hunits x = n->width();
6301 delete n;
6302 return x;
6303 }
6304 else
6305 return H0;
6306}
6307
6308hunits env_space_width(environment *env)
6309{
6310 int fn = env_definite_font(env);
6311 font_size fs = env->get_font_size();
6312 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6313 return scale(fs.to_units()/3, env->get_space_size(), 12);
6314 else
6315 return font_table[fn]->get_space_width(fs, env->get_space_size());
6316}
6317
6318hunits env_sentence_space_width(environment *env)
6319{
6320 int fn = env_definite_font(env);
6321 font_size fs = env->get_font_size();
6322 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6323 return scale(fs.to_units()/3, env->get_sentence_space_size(), 12);
6324 else
6325 return font_table[fn]->get_space_width(fs, env->get_sentence_space_size());
6326}
6327
6328hunits env_half_narrow_space_width(environment *env)
6329{
6330 int fn = env_definite_font(env);
6331 font_size fs = env->get_font_size();
6332 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6333 return 0;
6334 else
6335 return font_table[fn]->get_half_narrow_space_width(fs);
6336}
6337
6338hunits env_narrow_space_width(environment *env)
6339{
6340 int fn = env_definite_font(env);
6341 font_size fs = env->get_font_size();
6342 if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6343 return 0;
6344 else
6345 return font_table[fn]->get_narrow_space_width(fs);
6346}
6347
6348void bold_font()
6349{
6350 int n = get_fontno();
6351 if (n >= 0) {
6352 if (has_arg()) {
6353 if (tok.delimiter()) {
6354 int f = get_fontno();
6355 if (f >= 0) {
6356 units offset;
6357 if (has_arg() && get_number(&offset, 'u') && offset >= 1)
6358 font_table[f]->set_conditional_bold(n, hunits(offset - 1));
6359 else
6360 font_table[f]->conditional_unbold(n);
6361 }
6362 }
6363 else {
6364 units offset;
6365 if (get_number(&offset, 'u') && offset >= 1)
6366 font_table[n]->set_bold(hunits(offset - 1));
6367 else
6368 font_table[n]->unbold();
6369 }
6370 }
6371 else
6372 font_table[n]->unbold();
6373 }
6374 skip_line();
6375}
6376
6377track_kerning_function::track_kerning_function() : non_zero(0)
6378{
6379}
6380
6381track_kerning_function::track_kerning_function(int min_s, hunits min_a,
6382 int max_s, hunits max_a)
6383: non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s),
6384 max_amount(max_a)
6385{
6386}
6387
6388int track_kerning_function::operator==(const track_kerning_function &tk)
6389{
6390 if (non_zero)
6391 return (tk.non_zero
6392 && min_size == tk.min_size
6393 && min_amount == tk.min_amount
6394 && max_size == tk.max_size
6395 && max_amount == tk.max_amount);
6396 else
6397 return !tk.non_zero;
6398}
6399
6400int track_kerning_function::operator!=(const track_kerning_function &tk)
6401{
6402 if (non_zero)
6403 return (!tk.non_zero
6404 || min_size != tk.min_size
6405 || min_amount != tk.min_amount
6406 || max_size != tk.max_size
6407 || max_amount != tk.max_amount);
6408 else
6409 return tk.non_zero;
6410}
6411
6412hunits track_kerning_function::compute(int size)
6413{
6414 if (non_zero) {
6415 if (max_size <= min_size)
6416 return min_amount;
6417 else if (size <= min_size)
6418 return min_amount;
6419 else if (size >= max_size)
6420 return max_amount;
6421 else
6422 return (scale(max_amount, size - min_size, max_size - min_size)
6423 + scale(min_amount, max_size - size, max_size - min_size));
6424 }
6425 else
6426 return H0;
6427}
6428
6429void track_kern()
6430{
6431 int n = get_fontno();
6432 if (n >= 0) {
6433 int min_s, max_s;
6434 hunits min_a, max_a;
6435 if (has_arg()
6436 && get_number(&min_s, 'z')
6437 && get_hunits(&min_a, 'p')
6438 && get_number(&max_s, 'z')
6439 && get_hunits(&max_a, 'p')) {
6440 track_kerning_function tk(min_s, min_a, max_s, max_a);
6441 font_table[n]->set_track_kern(tk);
6442 }
6443 else {
6444 track_kerning_function tk;
6445 font_table[n]->set_track_kern(tk);
6446 }
6447 }
6448 skip_line();
6449}
6450
6451void constant_space()
6452{
6453 int n = get_fontno();
6454 if (n >= 0) {
6455 int x, y;
6456 if (!has_arg() || !get_integer(&x))
6457 font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
6458 else {
6459 if (!has_arg() || !get_number(&y, 'z'))
6460 font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
6461 else
6462 font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
6463 scale(y*x,
6464 units_per_inch,
6465 36*72*sizescale));
6466 }
6467 }
6468 skip_line();
6469}
6470
6471void ligature()
6472{
6473 int lig;
6474 if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2)
6475 global_ligature_mode = lig;
6476 else
6477 global_ligature_mode = 1;
6478 skip_line();
6479}
6480
6481void kern_request()
6482{
6483 int k;
6484 if (has_arg() && get_integer(&k))
6485 global_kern_mode = k != 0;
6486 else
6487 global_kern_mode = 1;
6488 skip_line();
6489}
6490
6491void set_soft_hyphen_char()
6492{
6493 soft_hyphen_char = get_optional_char();
6494 if (!soft_hyphen_char)
6495 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
6496 skip_line();
6497}
6498
6499void init_output()
6500{
6501 if (suppress_output_flag)
6502 the_output = new suppress_output_file;
6503 else if (ascii_output_flag)
6504 the_output = new ascii_output_file;
6505 else
6506 the_output = new troff_output_file;
6507}
6508
6509class next_available_font_position_reg : public reg {
6510public:
6511 const char *get_string();
6512};
6513
6514const char *next_available_font_position_reg::get_string()
6515{
6516 return i_to_a(next_available_font_position());
6517}
6518
6519class printing_reg : public reg {
6520public:
6521 const char *get_string();
6522};
6523
6524const char *printing_reg::get_string()
6525{
6526 if (the_output)
6527 return the_output->is_printing() ? "1" : "0";
6528 else
6529 return "0";
6530}
6531
6532void init_node_requests()
6533{
6534 init_request("bd", bold_font);
6535 init_request("cs", constant_space);
6536 init_request("fp", font_position);
6537 init_request("fschar", define_font_special_character);
6538 init_request("fspecial", font_special_request);
4d3e9548 6539 init_request("fzoom", font_zoom_request);
92d0a6a6
JR
6540 init_request("ftr", font_translate);
6541 init_request("kern", kern_request);
6542 init_request("lg", ligature);
6543 init_request("rfschar", remove_font_special_character);
6544 init_request("shc", set_soft_hyphen_char);
6545 init_request("special", special_request);
6546 init_request("sty", style);
6547 init_request("tkf", track_kern);
6548 init_request("uf", underline_font);
6549 number_reg_dictionary.define(".fp", new next_available_font_position_reg);
6550 number_reg_dictionary.define(".kern",
6551 new constant_int_reg(&global_kern_mode));
6552 number_reg_dictionary.define(".lg",
6553 new constant_int_reg(&global_ligature_mode));
6554 number_reg_dictionary.define(".P", new printing_reg);
6555 soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
6556}