Line data Source code
1 : /* Copyright 1989-2020 Free Software Foundation, Inc.
2 : 2021-2025 G. Branden Robinson
3 :
4 : Written by James Clark (jjc@jclark.com)
5 :
6 : This file is part of groff, the GNU roff typesetting system.
7 :
8 : groff is free software; you can redistribute it and/or modify it under
9 : the terms of the GNU General Public License as published by the Free
10 : Software Foundation, either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 : for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 :
21 : #ifdef HAVE_CONFIG_H
22 : #include <config.h>
23 : #endif
24 :
25 : #include <errno.h>
26 : #include <stdlib.h> // free(), malloc()
27 : #include <string.h> // strerror()
28 :
29 : #include "troff.h"
30 : #include "dictionary.h"
31 : #include "hvunits.h"
32 : #include "stringclass.h"
33 : #include "mtsm.h"
34 : #include "env.h"
35 : #include "request.h"
36 : #include "node.h"
37 : #include "token.h"
38 : #include "div.h"
39 : #include "reg.h"
40 : #include "font.h"
41 : #include "charinfo.h"
42 : #include "input.h"
43 : #include "geometry.h"
44 : #include "json-encode.h" // json_encode_char()
45 :
46 : #include "posix.h"
47 : #include "nonposix.h"
48 :
49 : #ifdef _POSIX_VERSION
50 :
51 : #include <sys/wait.h>
52 :
53 : #else /* not _POSIX_VERSION */
54 :
55 : /* traditional Unix */
56 :
57 : #define WIFEXITED(s) (((s) & 0377) == 0)
58 : #define WEXITSTATUS(s) (((s) >> 8) & 0377)
59 : #define WTERMSIG(s) ((s) & 0177)
60 : #define WIFSTOPPED(s) (((s) & 0377) == 0177)
61 : #define WSTOPSIG(s) (((s) >> 8) & 0377)
62 : #define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
63 :
64 : #endif /* not _POSIX_VERSION */
65 :
66 : #include <stack>
67 :
68 : static bool is_output_supressed = false;
69 :
70 : // declarations to avoid friend name injections
71 : class tfont;
72 : class tfont_spec;
73 : tfont *make_tfont(tfont_spec &);
74 :
75 :
76 : /*
77 : * how many boundaries of images have been written? Useful for
78 : * debugging grohtml
79 : */
80 :
81 : int image_no = 0;
82 : static int suppression_starting_page_number = 0;
83 : static bool was_any_page_in_output_list = false;
84 :
85 : #define STORE_WIDTH 1
86 :
87 : symbol HYPHEN_SYMBOL("hy");
88 :
89 : // Character used when a hyphen is inserted at a line break.
90 : static charinfo *soft_hyphen_char;
91 :
92 : enum constant_space_type {
93 : CONSTANT_SPACE_NONE,
94 : CONSTANT_SPACE_RELATIVE,
95 : CONSTANT_SPACE_ABSOLUTE
96 : };
97 :
98 : struct special_font_list {
99 : int n;
100 : special_font_list *next;
101 : };
102 :
103 : special_font_list *global_special_fonts;
104 : static int global_ligature_mode = 1; // three-valued Boolean :-|
105 : static bool global_kern_mode = true;
106 : // Font mounting positions are non-negative integers.
107 : const int FONT_NOT_MOUNTED = -1;
108 :
109 : class track_kerning_function {
110 : int non_zero;
111 : units min_size;
112 : hunits min_amount;
113 : units max_size;
114 : hunits max_amount;
115 : public:
116 : track_kerning_function();
117 : track_kerning_function(units, hunits, units, hunits);
118 : int operator==(const track_kerning_function &);
119 : int operator!=(const track_kerning_function &);
120 : hunits compute(int point_size);
121 : };
122 :
123 : struct font_lookup_info {
124 : int position;
125 : int requested_position;
126 : char *requested_name;
127 : font_lookup_info();
128 : };
129 :
130 11015 : font_lookup_info::font_lookup_info() : position(FONT_NOT_MOUNTED),
131 11015 : requested_position(FONT_NOT_MOUNTED), requested_name(0)
132 : {
133 11015 : }
134 :
135 : // embolden fontno when this is the current font
136 :
137 : struct conditional_bold {
138 : conditional_bold *next;
139 : int fontno;
140 : hunits offset;
141 : conditional_bold(int, hunits, conditional_bold * = 0 /* nullptr */);
142 : };
143 :
144 : class font_info {
145 : tfont *last_tfont;
146 : int number;
147 : font_size last_size;
148 : int last_height;
149 : int last_slant;
150 : symbol internal_name;
151 : symbol external_name;
152 : font *fm;
153 : bool has_emboldening;
154 : hunits bold_offset;
155 : track_kerning_function track_kern;
156 : constant_space_type is_constant_spaced;
157 : units constant_space;
158 : int last_ligature_mode;
159 : int last_kern_mode;
160 : conditional_bold *cond_bold_list;
161 : void flush();
162 : public:
163 : special_font_list *sf;
164 : font_info(symbol, int, symbol, font *);
165 : int contains(charinfo *);
166 : void set_bold(hunits);
167 : void unbold();
168 : void set_conditional_bold(int, hunits);
169 : void conditional_unbold(int);
170 : void set_track_kern(track_kerning_function &);
171 : void set_constant_space(constant_space_type, units = 0);
172 : int is_named(symbol);
173 : symbol get_name();
174 : tfont *get_tfont(font_size, int, int, int);
175 : hunits get_space_width(font_size, int);
176 : hunits get_narrow_space_width(font_size);
177 : hunits get_half_narrow_space_width(font_size);
178 : bool is_emboldened(hunits *); // "by how many hunits?" in argument
179 : int is_special();
180 : int is_style();
181 : void set_zoom(int);
182 : int get_zoom();
183 : font *get_font() const;
184 : friend symbol get_font_name(int, environment *);
185 : friend symbol get_style_name(int);
186 : };
187 :
188 : class tfont_spec {
189 : protected:
190 : symbol name;
191 : int input_position;
192 : font *fm;
193 : font_size size;
194 : bool has_emboldening;
195 : bool has_constant_spacing;
196 : int ligature_mode;
197 : int kern_mode;
198 : hunits bold_offset;
199 : hunits track_kern; // add this to the width
200 : hunits constant_space_width;
201 : int height;
202 : int slant;
203 : public:
204 : tfont_spec(symbol, int, font *, font_size, int, int);
205 : tfont_spec plain();
206 : bool operator==(const tfont_spec &);
207 : friend tfont *font_info::get_tfont(font_size fs, int, int, int);
208 : };
209 :
210 : class tfont : public tfont_spec {
211 : static tfont *tfont_list;
212 : tfont *next;
213 : tfont *plain_version;
214 : public:
215 : tfont(tfont_spec &);
216 : int contains(charinfo *);
217 : hunits get_width(charinfo *c);
218 : bool is_emboldened(hunits *); // "by how many hunits?" in argument
219 : bool is_constantly_spaced(hunits *); // "by how many hunits?" in arg
220 : hunits get_track_kern();
221 : tfont *get_plain();
222 : font_size get_size();
223 : int get_zoom();
224 : symbol get_name();
225 : charinfo *get_lig(charinfo *c1, charinfo *c2);
226 : bool is_kerned(charinfo *c1, charinfo *c2, hunits *res);
227 : int get_input_position();
228 : int get_character_type(charinfo *);
229 : int get_height();
230 : int get_slant();
231 : vunits get_char_height(charinfo *);
232 : vunits get_char_depth(charinfo *);
233 : hunits get_char_skew(charinfo *);
234 : hunits get_italic_correction(charinfo *);
235 : hunits get_left_italic_correction(charinfo *);
236 : hunits get_subscript_correction(charinfo *);
237 : friend tfont *make_tfont(tfont_spec &);
238 : };
239 :
240 14598126 : static inline int env_resolve_font(environment *env)
241 : {
242 14598126 : return env->get_family()->resolve(env->get_font());
243 : }
244 :
245 : /* font_info functions */
246 :
247 : static font_info **font_table = 0 /* nullptr */;
248 : static int font_table_size = 0;
249 :
250 17341 : font_info::font_info(symbol nm, int n, symbol enm, font *f)
251 : : last_tfont(0 /* nullptr */), number(n), last_size(0),
252 : internal_name(nm), external_name(enm), fm(f),
253 : has_emboldening(false), is_constant_spaced(CONSTANT_SPACE_NONE),
254 : last_ligature_mode(1), last_kern_mode(1),
255 17341 : cond_bold_list(0 /* nullptr */), sf(0 /* nullptr */)
256 : {
257 17341 : }
258 :
259 12900708 : inline int font_info::contains(charinfo *ci)
260 : {
261 12900708 : return (fm != 0 /* nullptr */) && fm->contains(ci->as_glyph());
262 : }
263 :
264 129302 : inline int font_info::is_special()
265 : {
266 129302 : return (fm != 0 /* nullptr */) && fm->is_special();
267 : }
268 :
269 15417 : inline int font_info::is_style()
270 : {
271 15417 : return (0 /* nullptr */ == fm);
272 : }
273 :
274 0 : void font_info::set_zoom(int zoom)
275 : {
276 0 : assert(fm != 0 /* nullptr */);
277 0 : fm->set_zoom(zoom);
278 0 : }
279 :
280 0 : inline int font_info::get_zoom()
281 : {
282 0 : if (is_style())
283 0 : return 0;
284 0 : return fm->get_zoom();
285 : }
286 :
287 0 : font *font_info::get_font() const
288 : {
289 0 : return fm;
290 : }
291 :
292 19136 : tfont *make_tfont(tfont_spec &spec)
293 : {
294 750446 : for (tfont *p = tfont::tfont_list; p != 0 /* nullptr */; p = p->next)
295 746630 : if (*p == spec)
296 15320 : return p;
297 3816 : return new tfont(spec);
298 : }
299 :
300 0 : int env_get_zoom(environment *env)
301 : {
302 0 : int fontno = env->get_family()->resolve(env->get_font());
303 0 : return font_table[fontno]->get_zoom();
304 : }
305 :
306 : // this is the current_font, fontno is where we found the character,
307 : // presumably a special font
308 :
309 12991235 : tfont *font_info::get_tfont(font_size fs, int height, int slant,
310 : int fontno)
311 : {
312 25982470 : if (0 /* nullptr */ == last_tfont
313 12988933 : || fs != last_size
314 12983334 : || height != last_height
315 12983334 : || slant != last_slant
316 12983334 : || global_ligature_mode != last_ligature_mode
317 12983334 : || global_kern_mode != last_kern_mode
318 25980168 : || fontno != number) {
319 19136 : font_info *f = font_table[fontno];
320 : tfont_spec spec(f->external_name, f->number, f->fm, fs, height,
321 19136 : slant);
322 19136 : for (conditional_bold *p = cond_bold_list;
323 19137 : p != 0 /* nullptr */;
324 1 : p = p->next)
325 2 : if (p->fontno == fontno) {
326 1 : spec.has_emboldening = true;
327 1 : spec.bold_offset = p->offset;
328 1 : break;
329 : }
330 19136 : if (!spec.has_emboldening && has_emboldening) {
331 2 : spec.has_emboldening = true;
332 2 : spec.bold_offset = bold_offset;
333 : }
334 19136 : spec.track_kern = track_kern.compute(fs.to_scaled_points());
335 19136 : spec.ligature_mode = global_ligature_mode;
336 19136 : spec.kern_mode = global_kern_mode;
337 19136 : switch (is_constant_spaced) {
338 19136 : case CONSTANT_SPACE_NONE:
339 19136 : break;
340 0 : case CONSTANT_SPACE_ABSOLUTE:
341 0 : spec.has_constant_spacing = true;
342 0 : spec.constant_space_width = constant_space;
343 0 : break;
344 0 : case CONSTANT_SPACE_RELATIVE:
345 0 : spec.has_constant_spacing = true;
346 : spec.constant_space_width
347 0 : = scale(constant_space * fs.to_scaled_points(),
348 : units_per_inch,
349 0 : 36 * 72 * sizescale);
350 0 : break;
351 0 : default:
352 0 : assert(0 == "unhandled case of constant spacing mode");
353 : }
354 19136 : if (fontno != number)
355 11089 : return make_tfont(spec);
356 : // save font for comparison purposes
357 8047 : last_tfont = make_tfont(spec);
358 : // save font related values not contained in tfont
359 8047 : last_size = fs;
360 8047 : last_height = height;
361 8047 : last_slant = slant;
362 8047 : last_ligature_mode = global_ligature_mode;
363 8047 : last_kern_mode = global_kern_mode;
364 : }
365 12980146 : return last_tfont;
366 : }
367 :
368 1 : bool font_info::is_emboldened(hunits *res)
369 : {
370 1 : if (has_emboldening) {
371 1 : *res = bold_offset;
372 1 : return true;
373 : }
374 : else
375 0 : return false;
376 : }
377 :
378 1 : void font_info::unbold()
379 : {
380 1 : if (has_emboldening) {
381 1 : has_emboldening = false;
382 1 : flush();
383 : }
384 1 : }
385 :
386 3 : void font_info::set_bold(hunits offset)
387 : {
388 3 : if (!has_emboldening || (offset != bold_offset)) {
389 3 : has_emboldening = true;
390 3 : bold_offset = offset;
391 3 : flush();
392 : }
393 3 : }
394 :
395 1 : void font_info::set_conditional_bold(int fontno, hunits offset)
396 : {
397 1 : for (conditional_bold *p = cond_bold_list;
398 1 : p != 0 /* nullptr */;
399 0 : p = p->next)
400 0 : if (p->fontno == fontno) {
401 0 : if (offset != p->offset) {
402 0 : p->offset = offset;
403 0 : flush();
404 : }
405 0 : return;
406 : }
407 1 : cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
408 : }
409 :
410 1 : conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
411 1 : : next(x), fontno(f), offset(h)
412 : {
413 1 : }
414 :
415 1 : void font_info::conditional_unbold(int fontno)
416 : {
417 1 : for (conditional_bold **p = &cond_bold_list;
418 1 : *p != 0 /* nullptr */;
419 0 : p = &(*p)->next)
420 1 : if ((*p)->fontno == fontno) {
421 1 : conditional_bold *tem = *p;
422 1 : *p = (*p)->next;
423 1 : delete tem;
424 1 : flush();
425 1 : return;
426 : }
427 : }
428 :
429 0 : void font_info::set_constant_space(constant_space_type type, units x)
430 : {
431 0 : if (type != is_constant_spaced
432 0 : || (type != CONSTANT_SPACE_NONE && x != constant_space)) {
433 0 : flush();
434 0 : is_constant_spaced = type;
435 0 : constant_space = x;
436 : }
437 0 : }
438 :
439 162 : void font_info::set_track_kern(track_kerning_function &tk)
440 : {
441 162 : if (track_kern != tk) {
442 156 : track_kern = tk;
443 156 : flush();
444 : }
445 162 : }
446 :
447 161 : void font_info::flush()
448 : {
449 161 : last_tfont = 0;
450 161 : }
451 :
452 1600478 : int font_info::is_named(symbol s)
453 : {
454 1600478 : return internal_name == s;
455 : }
456 :
457 26479 : symbol font_info::get_name()
458 : {
459 26479 : return internal_name;
460 : }
461 :
462 1143 : symbol get_font_name(int fontno, environment *env)
463 : {
464 1143 : symbol f = font_table[fontno]->get_name();
465 1143 : if (font_table[fontno]->is_style()) {
466 935 : return concat(env->get_family()->nm, f);
467 : }
468 208 : return f;
469 : }
470 :
471 2658 : symbol get_style_name(int fontno)
472 : {
473 2658 : if (font_table[fontno]->is_style())
474 2463 : return font_table[fontno]->get_name();
475 : else
476 195 : return EMPTY_SYMBOL;
477 : }
478 :
479 1591979 : hunits font_info::get_space_width(font_size fs, int space_sz)
480 : {
481 1591979 : if (is_constant_spaced == CONSTANT_SPACE_NONE)
482 1591979 : return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
483 1591979 : space_sz, 12);
484 0 : else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
485 0 : return constant_space;
486 : else
487 0 : return scale(constant_space*fs.to_scaled_points(),
488 0 : units_per_inch, 36 * 72 * sizescale);
489 : }
490 :
491 5349 : hunits font_info::get_narrow_space_width(font_size fs)
492 : {
493 5349 : charinfo *ci = lookup_charinfo(symbol("|"));
494 5349 : if (fm->contains(ci->as_glyph()))
495 0 : return hunits(fm->get_width(ci->as_glyph(), fs.to_scaled_points()));
496 : else
497 5349 : return hunits(fs.to_units()/6);
498 : }
499 :
500 310 : hunits font_info::get_half_narrow_space_width(font_size fs)
501 : {
502 310 : charinfo *ci = lookup_charinfo(symbol("^"));
503 310 : if (fm->contains(ci->as_glyph()))
504 0 : return hunits(fm->get_width(ci->as_glyph(), fs.to_scaled_points()));
505 : else
506 310 : return hunits(fs.to_units()/12);
507 : }
508 :
509 : /* tfont */
510 :
511 22955 : tfont_spec::tfont_spec(symbol nm, int n, font *f,
512 22955 : font_size s, int h, int sl)
513 : : name(nm), input_position(n), fm(f), size(s),
514 : has_emboldening(false), has_constant_spacing(false), ligature_mode(1),
515 22955 : kern_mode(1), height(h), slant(sl)
516 : {
517 22955 : if (height == size.to_scaled_points())
518 0 : height = 0;
519 22955 : }
520 :
521 753787 : bool tfont_spec::operator==(const tfont_spec &spec)
522 : {
523 1507574 : if (fm == spec.fm
524 251211 : && size == spec.size
525 20854 : && input_position == spec.input_position
526 20854 : && name == spec.name
527 20854 : && height == spec.height
528 20854 : && slant == spec.slant
529 20857 : && (has_emboldening
530 3 : ? (spec.has_emboldening && bold_offset == spec.bold_offset)
531 20851 : : !spec.has_emboldening)
532 20851 : && track_kern == spec.track_kern
533 19560 : && (has_constant_spacing
534 0 : ? (spec.has_constant_spacing
535 0 : && constant_space_width == spec.constant_space_width)
536 19560 : : !spec.has_constant_spacing)
537 19560 : && ligature_mode == spec.ligature_mode
538 1045412 : && kern_mode == spec.kern_mode)
539 19136 : return true;
540 : else
541 734651 : return false;
542 : }
543 :
544 3819 : tfont_spec tfont_spec::plain()
545 : {
546 3819 : return tfont_spec(name, input_position, fm, size, height, slant);
547 : }
548 :
549 15413897 : hunits tfont::get_width(charinfo *c)
550 : {
551 15413897 : if (has_constant_spacing)
552 0 : return constant_space_width;
553 15413897 : else if (has_emboldening)
554 0 : return (hunits(fm->get_width(c->as_glyph(),
555 6 : size.to_scaled_points()))
556 12 : + track_kern + bold_offset);
557 : else
558 15413891 : return (hunits(fm->get_width(c->as_glyph(),
559 15413891 : size.to_scaled_points()))
560 30827782 : + track_kern);
561 : }
562 :
563 2701291 : vunits tfont::get_char_height(charinfo *c)
564 : {
565 2701291 : vunits v = fm->get_height(c->as_glyph(), size.to_scaled_points());
566 2701291 : if (height != 0 && height != size.to_scaled_points())
567 1556 : return scale(v, height, size.to_scaled_points());
568 : else
569 2699735 : return v;
570 : }
571 :
572 2701291 : vunits tfont::get_char_depth(charinfo *c)
573 : {
574 2701291 : vunits v = fm->get_depth(c->as_glyph(), size.to_scaled_points());
575 2701291 : if (height != 0 && height != size.to_scaled_points())
576 1556 : return scale(v, height, size.to_scaled_points());
577 : else
578 2699735 : return v;
579 : }
580 :
581 20778 : hunits tfont::get_char_skew(charinfo *c)
582 : {
583 41556 : return hunits(fm->get_skew(c->as_glyph(), size.to_scaled_points(),
584 20778 : slant));
585 : }
586 :
587 2539271 : hunits tfont::get_italic_correction(charinfo *c)
588 : {
589 5078542 : return hunits(fm->get_italic_correction(c->as_glyph(),
590 2539271 : size.to_scaled_points()));
591 : }
592 :
593 32205 : hunits tfont::get_left_italic_correction(charinfo *c)
594 : {
595 64410 : return hunits(fm->get_left_italic_correction(c->as_glyph(),
596 32205 : size.to_scaled_points()));
597 : }
598 :
599 21035 : hunits tfont::get_subscript_correction(charinfo *c)
600 : {
601 42070 : return hunits(fm->get_subscript_correction(c->as_glyph(),
602 21035 : size.to_scaled_points()));
603 : }
604 :
605 136167 : inline int tfont::get_input_position()
606 : {
607 136167 : return input_position;
608 : }
609 :
610 24563 : inline int tfont::contains(charinfo *ci)
611 : {
612 24563 : return fm->contains(ci->as_glyph());
613 : }
614 :
615 183183 : inline int tfont::get_character_type(charinfo *ci)
616 : {
617 183183 : return fm->get_character_type(ci->as_glyph());
618 : }
619 :
620 118502 : inline bool tfont::is_emboldened(hunits *res)
621 : {
622 118502 : if (has_emboldening) {
623 6 : *res = bold_offset;
624 6 : return true;
625 : }
626 : else
627 118496 : return false;
628 : }
629 :
630 118502 : inline bool tfont::is_constantly_spaced(hunits *res)
631 : {
632 118502 : if (has_constant_spacing) {
633 0 : *res = constant_space_width;
634 0 : return true;
635 : }
636 : else
637 118502 : return false;
638 : }
639 :
640 118263 : inline hunits tfont::get_track_kern()
641 : {
642 118263 : return track_kern;
643 : }
644 :
645 2660259 : inline tfont *tfont::get_plain()
646 : {
647 2660259 : return plain_version;
648 : }
649 :
650 136167 : inline font_size tfont::get_size()
651 : {
652 136167 : return size;
653 : }
654 :
655 136167 : inline int tfont::get_zoom()
656 : {
657 136167 : return fm->get_zoom();
658 : }
659 :
660 136167 : inline symbol tfont::get_name()
661 : {
662 136167 : return name;
663 : }
664 :
665 136167 : inline int tfont::get_height()
666 : {
667 136167 : return height;
668 : }
669 :
670 136167 : inline int tfont::get_slant()
671 : {
672 136167 : return slant;
673 : }
674 :
675 : symbol SYMBOL_ff("ff");
676 : symbol SYMBOL_fi("fi");
677 : symbol SYMBOL_fl("fl");
678 : symbol SYMBOL_Fi("Fi");
679 : symbol SYMBOL_Fl("Fl");
680 :
681 10468377 : charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
682 : {
683 10468377 : if (0 == ligature_mode)
684 0 : return 0 /* nullptr */;
685 10468377 : charinfo *ci = 0 /* nullptr */;
686 10468377 : if (c1->get_ascii_code() == 'f') {
687 427250 : switch (c2->get_ascii_code()) {
688 137782 : case 'f':
689 137782 : if (fm->has_ligature(font::LIG_ff))
690 5 : ci = lookup_charinfo(SYMBOL_ff);
691 137782 : break;
692 14991 : case 'i':
693 14991 : if (fm->has_ligature(font::LIG_fi))
694 10188 : ci = lookup_charinfo(SYMBOL_fi);
695 14991 : break;
696 1032 : case 'l':
697 1032 : if (fm->has_ligature(font::LIG_fl))
698 717 : ci = lookup_charinfo(SYMBOL_fl);
699 1032 : break;
700 : }
701 : }
702 10041127 : else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
703 0 : switch (c2->get_ascii_code()) {
704 0 : case 'i':
705 0 : if (fm->has_ligature(font::LIG_ffi))
706 0 : ci = lookup_charinfo(SYMBOL_Fi);
707 0 : break;
708 0 : case 'l':
709 0 : if (fm->has_ligature(font::LIG_ffl))
710 0 : ci = lookup_charinfo(SYMBOL_Fl);
711 0 : break;
712 : }
713 : }
714 10468377 : if (ci != 0 /* nullptr */ && fm->contains(ci->as_glyph()))
715 10910 : return ci;
716 10457467 : return 0 /* nullptr */;
717 : }
718 :
719 10457467 : inline bool tfont::is_kerned(charinfo *c1, charinfo *c2, hunits *res)
720 : {
721 10457467 : if (0 == kern_mode)
722 9079 : return false;
723 : else {
724 10448388 : int n = fm->get_kern(c1->as_glyph(),
725 : c2->as_glyph(),
726 : size.to_scaled_points());
727 10448388 : if (n) {
728 485400 : *res = hunits(n);
729 485400 : return true;
730 : }
731 : else
732 9962988 : return false;
733 : }
734 : }
735 :
736 : tfont *tfont::tfont_list = 0 /* nullptr */;
737 :
738 3819 : tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
739 : {
740 3819 : next = tfont_list;
741 3819 : tfont_list = this;
742 3819 : tfont_spec plain_spec = plain();
743 : tfont *p;
744 7160 : for (p = tfont_list; p != 0 /* nullptr */; p = p->next)
745 7157 : if (*p == plain_spec) {
746 3816 : plain_version = p;
747 3816 : break;
748 : }
749 3819 : if (0 /* nullptr */ == p)
750 3 : plain_version = new tfont(plain_spec);
751 3819 : }
752 :
753 : /* output_file */
754 :
755 : class real_output_file : public output_file {
756 : bool is_output_piped; // as with `pi` request
757 : bool want_page_printed; // if selected with `troff -o`
758 : bool is_output_on; // as by \O[0], \O[1] escape sequences
759 : virtual void really_transparent_char(unsigned char) = 0;
760 : virtual void really_print_line(hunits x, vunits y, node *n,
761 : vunits before, vunits after,
762 : hunits width) = 0;
763 : virtual void really_begin_page(int pageno, vunits page_length) = 0;
764 : virtual void really_copy_file(hunits x, vunits y,
765 : const char *filename);
766 : virtual void really_put_filename(const char *, int);
767 : virtual void really_on();
768 : virtual void really_off();
769 : public:
770 : FILE *fp;
771 : real_output_file();
772 : ~real_output_file();
773 : void flush();
774 : void transparent_char(unsigned char);
775 : void print_line(hunits x, vunits y, node *n,
776 : vunits before, vunits after, hunits width);
777 : void begin_page(int pageno, vunits page_length);
778 : void put_filename(const char *, int);
779 : void on();
780 : void off();
781 : bool is_on();
782 : bool is_selected_for_printing();
783 : void copy_file(hunits x, vunits y, const char *filename);
784 : };
785 :
786 : class suppress_output_file : public real_output_file {
787 : public:
788 : suppress_output_file();
789 : void really_transparent_char(unsigned char);
790 : void really_print_line(hunits x, vunits y, node *n,
791 : vunits, vunits, hunits width);
792 : void really_begin_page(int pageno, vunits page_length);
793 : };
794 :
795 : class ascii_output_file : public real_output_file {
796 : public:
797 : ascii_output_file();
798 : void really_transparent_char(unsigned char);
799 : void really_print_line(hunits x, vunits y, node *n,
800 : vunits, vunits, hunits width);
801 : void really_begin_page(int pageno, vunits page_length);
802 : void outc(unsigned char c);
803 : void outs(const char *s);
804 : };
805 :
806 1304 : void ascii_output_file::outc(unsigned char c)
807 : {
808 1304 : if (fp != 0 /* nullptr */)
809 1304 : fputc(c, fp);
810 1304 : }
811 :
812 51 : void ascii_output_file::outs(const char *s)
813 : {
814 51 : if (fp != 0 /* nullptr */) {
815 51 : fputc('<', fp);
816 51 : if (s != 0 /* nullptr */)
817 51 : fputs(s, fp);
818 51 : fputc('>', fp);
819 : }
820 51 : }
821 :
822 : struct hvpair;
823 :
824 : class troff_output_file : public real_output_file {
825 : units hpos;
826 : units vpos;
827 : units output_vpos;
828 : units output_hpos;
829 : bool must_update_drawing_position;
830 : int current_size;
831 : int current_slant;
832 : int current_height;
833 : tfont *current_tfont;
834 : color *current_fill_color;
835 : color *current_stroke_color;
836 : int current_font_number;
837 : symbol *font_position;
838 : int nfont_positions;
839 : enum { TBUF_SIZE = 256 };
840 : char tbuf[TBUF_SIZE];
841 : int tbuf_len;
842 : int tbuf_kern;
843 : bool has_page_begun;
844 : int cur_div_level;
845 : string tag_list;
846 : void do_motion();
847 : void put(char c);
848 : void put(unsigned char c);
849 : void put(int i);
850 : void put(unsigned int i);
851 : void put(const char *s);
852 : void set_font(tfont *tf);
853 : void flush_tbuf();
854 : public:
855 : troff_output_file();
856 : ~troff_output_file();
857 : void flush();
858 : void trailer(vunits page_length);
859 : void put_char(charinfo *, tfont *, color *, color *);
860 : void put_char_width(charinfo *, tfont *, color *, color *, hunits,
861 : hunits);
862 : void right(hunits);
863 : void down(vunits);
864 : void moveto(hunits, vunits);
865 : void start_device_extension(tfont * /* tf */,
866 : color * /* gcol */, color * /* fcol */,
867 : bool /* omit_command_prefix */ = false);
868 : void start_device_extension();
869 : void write_device_extension_char(unsigned char c);
870 : void end_device_extension();
871 : void word_marker();
872 : void really_transparent_char(unsigned char c);
873 : void really_print_line(hunits x, vunits y, node *n,
874 : vunits before, vunits after, hunits width);
875 : void really_begin_page(int pageno, vunits page_length);
876 : void really_copy_file(hunits x, vunits y, const char *filename);
877 : void really_put_filename(const char *, int);
878 : void really_on();
879 : void really_off();
880 : void draw(char, hvpair *, int, font_size, color *, color *);
881 : void determine_line_limits (char code, hvpair *point, int npoints);
882 : void check_charinfo(tfont *tf, charinfo *ci);
883 : void stroke_color(color *c);
884 : void fill_color(color *c);
885 424 : int get_hpos() { return hpos; }
886 424 : int get_vpos() { return vpos; }
887 : void add_to_tag_list(string s);
888 : void comment(string s);
889 : friend void space_char_hmotion_node::tprint(troff_output_file *);
890 : friend void unbreakable_space_node::tprint(troff_output_file *);
891 : };
892 :
893 2902837 : static void put_string(const char *s, FILE *fp)
894 : {
895 2902837 : if (fp != 0 /* nullptr */) {
896 12834681 : for (; *s != '\0'; ++s)
897 9931844 : putc(*s, fp);
898 : }
899 2902837 : }
900 :
901 9065783 : inline void troff_output_file::put(char c)
902 : {
903 9065783 : if (fp != 0 /* nullptr */)
904 9065783 : putc(c, fp);
905 9065783 : }
906 :
907 1689087 : inline void troff_output_file::put(unsigned char c)
908 : {
909 1689087 : if (fp != 0 /* nullptr */)
910 1689087 : putc(c, fp);
911 1689087 : }
912 :
913 314911 : inline void troff_output_file::put(const char *s)
914 : {
915 314911 : put_string(s, fp);
916 314911 : }
917 :
918 2344171 : inline void troff_output_file::put(int i)
919 : {
920 2344171 : put_string(i_to_a(i), fp);
921 2344171 : }
922 :
923 243755 : inline void troff_output_file::put(unsigned int i)
924 : {
925 243755 : put_string(ui_to_a(i), fp);
926 243755 : }
927 :
928 66977 : void troff_output_file::start_device_extension(tfont *tf, color *gcol,
929 : color *fcol,
930 : bool omit_command_prefix)
931 : {
932 66977 : flush_tbuf();
933 66977 : set_font(tf);
934 66977 : stroke_color(gcol);
935 66977 : fill_color(fcol);
936 66977 : do_motion();
937 66977 : if (!omit_command_prefix)
938 66977 : put("x X ");
939 66977 : }
940 :
941 116 : void troff_output_file::start_device_extension()
942 : {
943 116 : flush_tbuf();
944 116 : put("x X ");
945 116 : }
946 :
947 1519645 : void troff_output_file::write_device_extension_char(unsigned char c)
948 : {
949 1519645 : put(c);
950 1519645 : if (c == '\n')
951 348 : put('+');
952 1519645 : }
953 :
954 67093 : void troff_output_file::end_device_extension()
955 : {
956 67093 : put('\n');
957 67093 : }
958 :
959 190009 : inline void troff_output_file::moveto(hunits h, vunits v)
960 : {
961 190009 : hpos = h.to_units();
962 190009 : vpos = v.to_units();
963 190009 : }
964 :
965 189584 : void troff_output_file::really_print_line(hunits x, vunits y, node *n,
966 : vunits before, vunits after,
967 : hunits)
968 : {
969 189584 : moveto(x, y);
970 5309955 : while (n != 0 /* nullptr */) {
971 : // Check whether we should push the current troff state and use
972 : // the state at the start of the invocation of this diversion.
973 5120371 : if (n->div_nest_level > cur_div_level && n->push_state) {
974 3535 : state.push_state(n->push_state);
975 3535 : cur_div_level = n->div_nest_level;
976 : }
977 : // Has the current diversion level decreased? Then we must pop the
978 : // troff state.
979 5123906 : while (n->div_nest_level < cur_div_level) {
980 3535 : state.pop_state();
981 3535 : cur_div_level = n->div_nest_level;
982 : }
983 : // Now check whether the state has changed.
984 5266548 : if ((is_on() || n->causes_tprint())
985 5266548 : && (state.changed(n->state) || n->is_tag() || n->is_special)) {
986 79827 : flush_tbuf();
987 79827 : do_motion();
988 79827 : must_update_drawing_position = true;
989 79827 : flush();
990 79827 : state.flush(fp, n->state, tag_list);
991 79827 : tag_list = string("");
992 79827 : flush();
993 : }
994 5120371 : n->tprint(this);
995 5120371 : n = n->next;
996 : }
997 189584 : flush_tbuf();
998 : // Ensure that transparent throughput (.output, \!) has a more
999 : // predictable position.
1000 189584 : do_motion();
1001 189584 : must_update_drawing_position = true;
1002 189584 : hpos = 0;
1003 189584 : put('n');
1004 189584 : put(before.to_units());
1005 189584 : put(' ');
1006 189584 : put(after.to_units());
1007 189584 : put('\n');
1008 189584 : }
1009 :
1010 429091 : inline void troff_output_file::word_marker()
1011 : {
1012 429091 : flush_tbuf();
1013 429091 : if (is_on())
1014 410630 : put('w');
1015 429091 : }
1016 :
1017 996696 : inline void troff_output_file::right(hunits n)
1018 : {
1019 996696 : hpos += n.to_units();
1020 996696 : }
1021 :
1022 102798 : inline void troff_output_file::down(vunits n)
1023 : {
1024 102798 : vpos += n.to_units();
1025 102798 : }
1026 :
1027 1200403 : void troff_output_file::do_motion()
1028 : {
1029 1200403 : if (must_update_drawing_position) {
1030 269412 : put('V');
1031 269412 : put(vpos);
1032 269412 : put('\n');
1033 269412 : put('H');
1034 269412 : put(hpos);
1035 269412 : put('\n');
1036 : }
1037 : else {
1038 930991 : if (hpos != output_hpos) {
1039 600983 : units n = hpos - output_hpos;
1040 600983 : if (n > 0 && n < hpos) {
1041 515804 : put('h');
1042 515804 : put(n);
1043 : }
1044 : else {
1045 85179 : put('H');
1046 85179 : put(hpos);
1047 : }
1048 600983 : put('\n');
1049 : }
1050 930991 : if (vpos != output_vpos) {
1051 61547 : units n = vpos - output_vpos;
1052 61547 : if (n > 0 && n < vpos) {
1053 560 : put('v');
1054 560 : put(n);
1055 : }
1056 : else {
1057 60987 : put('V');
1058 60987 : put(vpos);
1059 : }
1060 61547 : put('\n');
1061 : }
1062 : }
1063 1200403 : output_vpos = vpos;
1064 1200403 : output_hpos = hpos;
1065 1200403 : must_update_drawing_position = false;
1066 1200403 : }
1067 :
1068 2147915 : void troff_output_file::flush_tbuf()
1069 : {
1070 2147915 : if (!is_on()) {
1071 130081 : tbuf_len = 0;
1072 130081 : return;
1073 : }
1074 :
1075 2017834 : if (0 == tbuf_len)
1076 1444324 : return;
1077 573510 : if (0 == tbuf_kern)
1078 572669 : put('t');
1079 : else {
1080 841 : put('u');
1081 841 : put(tbuf_kern);
1082 841 : put(' ');
1083 : }
1084 573510 : check_output_limits(hpos, vpos);
1085 573510 : assert(current_size > 0);
1086 573510 : check_output_limits(hpos, vpos - current_size);
1087 :
1088 3022722 : for (int i = 0; i < tbuf_len; i++)
1089 2449212 : put(tbuf[i]);
1090 573510 : put('\n');
1091 573510 : tbuf_len = 0;
1092 : }
1093 :
1094 2506105 : void troff_output_file::check_charinfo(tfont *tf, charinfo *ci)
1095 : {
1096 2506105 : if (!is_on())
1097 0 : return;
1098 :
1099 2506105 : int height = tf->get_char_height(ci).to_units();
1100 2506105 : int width = tf->get_width(ci).to_units()
1101 2506105 : + tf->get_italic_correction(ci).to_units();
1102 2506105 : int depth = tf->get_char_depth(ci).to_units();
1103 2506105 : check_output_limits(output_hpos, output_vpos - height);
1104 2506105 : check_output_limits(output_hpos + width, output_vpos + depth);
1105 : }
1106 :
1107 2608611 : void troff_output_file::put_char_width(charinfo *ci, tfont *tf,
1108 : color *gcol, color *fcol,
1109 : hunits w, hunits k)
1110 : {
1111 2608611 : int kk = k.to_units();
1112 2608611 : if (!is_on()) {
1113 102506 : flush_tbuf();
1114 102506 : hpos += w.to_units() + kk;
1115 102506 : return;
1116 : }
1117 2506105 : set_font(tf);
1118 2506105 : unsigned char c = ci->get_ascii_code();
1119 2506105 : if (0U == c) {
1120 56893 : stroke_color(gcol);
1121 56893 : fill_color(fcol);
1122 56893 : flush_tbuf();
1123 56893 : do_motion();
1124 56893 : check_charinfo(tf, ci);
1125 56893 : if (ci->is_numbered()) {
1126 21144 : put('N');
1127 21144 : put(ci->get_number());
1128 : }
1129 : else {
1130 35749 : put('C');
1131 35749 : const char *s = ci->nm.contents();
1132 35749 : if (0 == s[1]) {
1133 5042 : put('\\');
1134 5042 : put(s[0]);
1135 : }
1136 : else
1137 30707 : put(s);
1138 : }
1139 56893 : put('\n');
1140 56893 : hpos += w.to_units() + kk;
1141 : }
1142 2449212 : else if (device_has_tcommand) {
1143 2449212 : if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
1144 1875707 : && (!gcol || gcol == current_stroke_color)
1145 1875702 : && (!fcol || fcol == current_fill_color)
1146 1875702 : && kk == tbuf_kern
1147 1875702 : && tbuf_len < TBUF_SIZE) {
1148 1875702 : check_charinfo(tf, ci);
1149 1875702 : tbuf[tbuf_len++] = c;
1150 1875702 : output_hpos += w.to_units() + kk;
1151 1875702 : hpos = output_hpos;
1152 1875702 : return;
1153 : }
1154 573510 : stroke_color(gcol);
1155 573510 : fill_color(fcol);
1156 573510 : flush_tbuf();
1157 573510 : do_motion();
1158 573510 : check_charinfo(tf, ci);
1159 573510 : tbuf[tbuf_len++] = c;
1160 573510 : output_hpos += w.to_units() + kk;
1161 573510 : tbuf_kern = kk;
1162 573510 : hpos = output_hpos;
1163 : }
1164 : else {
1165 : // flush_tbuf();
1166 0 : int n = hpos - output_hpos;
1167 0 : check_charinfo(tf, ci);
1168 : // check_output_limits(output_hpos, output_vpos);
1169 0 : if (vpos == output_vpos
1170 0 : && (!gcol || gcol == current_stroke_color)
1171 0 : && (!fcol || fcol == current_fill_color)
1172 0 : && (n > 0) && (n < 100) && !must_update_drawing_position) {
1173 0 : put(char(n / 10 + '0'));
1174 0 : put(char(n % 10 + '0'));
1175 0 : put(c);
1176 0 : output_hpos = hpos;
1177 : }
1178 : else {
1179 0 : stroke_color(gcol);
1180 0 : fill_color(fcol);
1181 0 : do_motion();
1182 0 : put('c');
1183 0 : put(c);
1184 : }
1185 0 : hpos += w.to_units() + kk;
1186 : }
1187 : }
1188 :
1189 245 : void troff_output_file::put_char(charinfo *ci, tfont *tf,
1190 : color *gcol, color *fcol)
1191 : {
1192 245 : flush_tbuf();
1193 245 : if (!is_on())
1194 0 : return;
1195 245 : set_font(tf);
1196 245 : unsigned char c = ci->get_ascii_code();
1197 245 : if (0U == c) {
1198 20 : stroke_color(gcol);
1199 20 : fill_color(fcol);
1200 20 : flush_tbuf();
1201 20 : do_motion();
1202 20 : if (ci->is_numbered()) {
1203 0 : put('N');
1204 0 : put(ci->get_number());
1205 : }
1206 : else {
1207 20 : put('C');
1208 20 : const char *s = ci->nm.contents();
1209 20 : if (0 == s[1]) {
1210 0 : put('\\');
1211 0 : put(s[0]);
1212 : }
1213 : else
1214 20 : put(s);
1215 : }
1216 20 : put('\n');
1217 : }
1218 : else {
1219 225 : int n = hpos - output_hpos;
1220 225 : if (vpos == output_vpos
1221 116 : && (!gcol || gcol == current_stroke_color)
1222 116 : && (!fcol || fcol == current_fill_color)
1223 116 : && n > 0 && n < 100) {
1224 1 : put(char(n/10 + '0'));
1225 1 : put(char(n%10 + '0'));
1226 1 : put(c);
1227 1 : output_hpos = hpos;
1228 : }
1229 : else {
1230 224 : stroke_color(gcol);
1231 224 : fill_color(fcol);
1232 224 : flush_tbuf();
1233 224 : do_motion();
1234 224 : put('c');
1235 224 : put(c);
1236 : }
1237 : }
1238 : }
1239 :
1240 : // set_font calls 'flush_tbuf' if necessary.
1241 :
1242 2573327 : void troff_output_file::set_font(tfont *tf)
1243 : {
1244 2573327 : if (current_tfont == tf)
1245 2437160 : return;
1246 136167 : flush_tbuf();
1247 136167 : int n = tf->get_input_position();
1248 136167 : symbol nm = tf->get_name();
1249 136167 : if (n >= nfont_positions || font_position[n] != nm) {
1250 4668 : put("x font ");
1251 4668 : put(n);
1252 4668 : put(' ');
1253 4668 : put(nm.contents());
1254 4668 : put('\n');
1255 4668 : if (n >= nfont_positions) {
1256 50 : int old_nfont_positions = nfont_positions;
1257 50 : symbol *old_font_position = font_position;
1258 50 : nfont_positions *= 3;
1259 50 : nfont_positions /= 2;
1260 50 : if (nfont_positions <= n)
1261 40 : nfont_positions = n + 10;
1262 2385 : font_position = new symbol[nfont_positions];
1263 50 : memcpy(font_position, old_font_position,
1264 50 : old_nfont_positions*sizeof(symbol));
1265 50 : delete[] old_font_position;
1266 : }
1267 4668 : font_position[n] = nm;
1268 : }
1269 136167 : if (current_font_number != n) {
1270 135483 : put('f');
1271 135483 : put(n);
1272 135483 : put('\n');
1273 135483 : current_font_number = n;
1274 : }
1275 136167 : int zoom = tf->get_zoom();
1276 : int size;
1277 136167 : if (zoom)
1278 0 : size = scale(tf->get_size().to_scaled_points(),
1279 : zoom, 1000);
1280 : else
1281 136167 : size = tf->get_size().to_scaled_points();
1282 136167 : if (current_size != size) {
1283 4323 : put('s');
1284 4323 : put(size);
1285 4323 : put('\n');
1286 4323 : current_size = size;
1287 : }
1288 136167 : int slant = tf->get_slant();
1289 136167 : if (current_slant != slant) {
1290 0 : put("x Slant ");
1291 0 : put(slant);
1292 0 : put('\n');
1293 0 : current_slant = slant;
1294 : }
1295 136167 : int height = tf->get_height();
1296 136167 : if (current_height != height) {
1297 148 : put("x Height ");
1298 148 : put((0 == height) ? current_size : height);
1299 148 : put('\n');
1300 148 : current_height = height;
1301 : }
1302 136167 : current_tfont = tf;
1303 : }
1304 :
1305 : // fill_color calls 'flush_tbuf' and 'do_motion' if necessary.
1306 :
1307 1933114 : void troff_output_file::fill_color(color *col)
1308 : {
1309 1933114 : if ((0 /* nullptr */ == col) || current_fill_color == col)
1310 1866695 : return;
1311 66419 : current_fill_color = col;
1312 66419 : if (!want_color_output)
1313 0 : return;
1314 66419 : flush_tbuf();
1315 : // In nroff-mode devices (grotty), the fill color is a property of the
1316 : // character cell; our drawing position has to be on the page, lest
1317 : // grotty grouse "output above first line discarded".
1318 66419 : if (in_nroff_mode)
1319 1085 : do_motion();
1320 66419 : put("DF");
1321 : unsigned int components[4];
1322 : color_scheme scheme;
1323 66419 : scheme = col->get_components(components);
1324 66419 : switch (scheme) {
1325 33706 : case DEFAULT:
1326 33706 : put('d');
1327 33706 : break;
1328 32461 : case RGB:
1329 32461 : put("r ");
1330 32461 : put(Red);
1331 32461 : put(' ');
1332 32461 : put(Green);
1333 32461 : put(' ');
1334 32461 : put(Blue);
1335 32461 : break;
1336 0 : case CMY:
1337 0 : put("c ");
1338 0 : put(Cyan);
1339 0 : put(' ');
1340 0 : put(Magenta);
1341 0 : put(' ');
1342 0 : put(Yellow);
1343 0 : break;
1344 0 : case CMYK:
1345 0 : put("k ");
1346 0 : put(Cyan);
1347 0 : put(' ');
1348 0 : put(Magenta);
1349 0 : put(' ');
1350 0 : put(Yellow);
1351 0 : put(' ');
1352 0 : put(Black);
1353 0 : break;
1354 252 : case GRAY:
1355 252 : put("g ");
1356 252 : put(Gray);
1357 252 : break;
1358 : }
1359 66419 : put('\n');
1360 : }
1361 :
1362 : // stroke_color calls 'flush_tbuf' and 'do_motion' if necessary.
1363 :
1364 928679 : void troff_output_file::stroke_color(color *col)
1365 : {
1366 928679 : if (!col || (current_stroke_color == col))
1367 877314 : return;
1368 51365 : current_stroke_color = col;
1369 51365 : if (!want_color_output)
1370 0 : return;
1371 51365 : flush_tbuf();
1372 : // In nroff-mode devices (grotty), the stroke color is a property of
1373 : // the character cell; our drawing position has to be on the page,
1374 : // lest grotty grouse "output above first line discarded".
1375 51365 : if (in_nroff_mode)
1376 990 : do_motion();
1377 51365 : put("m");
1378 : unsigned int components[4];
1379 : color_scheme scheme;
1380 51365 : scheme = col->get_components(components);
1381 51365 : switch (scheme) {
1382 2655 : case DEFAULT:
1383 2655 : put('d');
1384 2655 : break;
1385 48705 : case RGB:
1386 48705 : put("r ");
1387 48705 : put(Red);
1388 48705 : put(' ');
1389 48705 : put(Green);
1390 48705 : put(' ');
1391 48705 : put(Blue);
1392 48705 : break;
1393 0 : case CMY:
1394 0 : put("c ");
1395 0 : put(Cyan);
1396 0 : put(' ');
1397 0 : put(Magenta);
1398 0 : put(' ');
1399 0 : put(Yellow);
1400 0 : break;
1401 0 : case CMYK:
1402 0 : put("k ");
1403 0 : put(Cyan);
1404 0 : put(' ');
1405 0 : put(Magenta);
1406 0 : put(' ');
1407 0 : put(Yellow);
1408 0 : put(' ');
1409 0 : put(Black);
1410 0 : break;
1411 5 : case GRAY:
1412 5 : put("g ");
1413 5 : put(Gray);
1414 5 : break;
1415 : }
1416 51365 : put('\n');
1417 : }
1418 :
1419 50 : void troff_output_file::add_to_tag_list(string s)
1420 : {
1421 50 : if (tag_list == string(""))
1422 50 : tag_list = s;
1423 : else {
1424 0 : tag_list += string("\n");
1425 0 : tag_list += s;
1426 : }
1427 50 : }
1428 :
1429 0 : void troff_output_file::comment(string s)
1430 : {
1431 0 : flush_tbuf();
1432 0 : assert(s.search('\n') == -1); // Don't write a multi-line comment.
1433 0 : put("# ");
1434 0 : string t = s + '\0';
1435 0 : put(t.contents());
1436 0 : put('\n');
1437 0 : }
1438 :
1439 : // determine_line_limits - works out the smallest box which will contain
1440 : // the entity, code, built from the point array.
1441 228693 : void troff_output_file::determine_line_limits(char code, hvpair *point,
1442 : int npoints)
1443 : {
1444 : int i, x, y;
1445 :
1446 228693 : if (!is_on())
1447 0 : return;
1448 :
1449 228693 : switch (code) {
1450 312 : case 'c':
1451 : case 'C':
1452 : // only the h field is used when defining a circle
1453 312 : check_output_limits(output_hpos,
1454 312 : output_vpos - point[0].h.to_units() / 2);
1455 312 : check_output_limits(output_hpos + point[0].h.to_units(),
1456 312 : output_vpos + point[0].h.to_units() / 2);
1457 228682 : break;
1458 34 : case 'E':
1459 : case 'e':
1460 34 : check_output_limits(output_hpos,
1461 34 : output_vpos - point[0].v.to_units() / 2);
1462 34 : check_output_limits(output_hpos + point[0].h.to_units(),
1463 34 : output_vpos + point[0].v.to_units() / 2);
1464 34 : break;
1465 32970 : case 'P':
1466 : case 'p':
1467 32970 : x = output_hpos;
1468 32970 : y = output_vpos;
1469 32970 : check_output_limits(x, y);
1470 131643 : for (i = 0; i < npoints; i++) {
1471 98673 : x += point[i].h.to_units();
1472 98673 : y += point[i].v.to_units();
1473 98673 : check_output_limits(x, y);
1474 : }
1475 32970 : break;
1476 121455 : case 't':
1477 121455 : x = output_hpos;
1478 121455 : y = output_vpos;
1479 242910 : for (i = 0; i < npoints; i++) {
1480 121455 : x += point[i].h.to_units();
1481 121455 : y += point[i].v.to_units();
1482 121455 : check_output_limits(x, y);
1483 : }
1484 121455 : break;
1485 141 : case 'a':
1486 : double c[2];
1487 : int p[4];
1488 : int minx, miny, maxx, maxy;
1489 141 : x = output_hpos;
1490 141 : y = output_vpos;
1491 141 : p[0] = point[0].h.to_units();
1492 141 : p[1] = point[0].v.to_units();
1493 141 : p[2] = point[1].h.to_units();
1494 141 : p[3] = point[1].v.to_units();
1495 141 : if (adjust_arc_center(p, c)) {
1496 141 : check_output_arc_limits(x, y,
1497 : p[0], p[1], p[2], p[3],
1498 : c[0], c[1],
1499 : &minx, &maxx, &miny, &maxy);
1500 141 : check_output_limits(minx, miny);
1501 141 : check_output_limits(maxx, maxy);
1502 141 : break;
1503 : }
1504 : // fall through
1505 : case 'l':
1506 73770 : x = output_hpos;
1507 73770 : y = output_vpos;
1508 73770 : check_output_limits(x, y);
1509 147540 : for (i = 0; i < npoints; i++) {
1510 73770 : x += point[i].h.to_units();
1511 73770 : y += point[i].v.to_units();
1512 73770 : check_output_limits(x, y);
1513 : }
1514 73770 : break;
1515 11 : default:
1516 11 : x = output_hpos;
1517 11 : y = output_vpos;
1518 46 : for (i = 0; i < npoints; i++) {
1519 35 : x += point[i].h.to_units();
1520 35 : y += point[i].v.to_units();
1521 35 : check_output_limits(x, y);
1522 : }
1523 : }
1524 : }
1525 :
1526 231055 : void troff_output_file::draw(char code, hvpair *point, int npoints,
1527 : font_size fsize, color *gcol, color *fcol)
1528 : {
1529 : int i;
1530 231055 : stroke_color(gcol);
1531 231055 : fill_color(fcol);
1532 231055 : flush_tbuf();
1533 231055 : do_motion();
1534 231055 : if (is_on()) {
1535 228693 : int size = fsize.to_scaled_points();
1536 228693 : if (current_size != size) {
1537 259 : put('s');
1538 259 : put(size);
1539 259 : put('\n');
1540 259 : current_size = size;
1541 259 : current_tfont = 0;
1542 : }
1543 228693 : put('D');
1544 228693 : put(code);
1545 228693 : if (code == 'c') {
1546 167 : put(' ');
1547 167 : put(point[0].h.to_units());
1548 : }
1549 : else
1550 522920 : for (i = 0; i < npoints; i++) {
1551 294394 : put(' ');
1552 294394 : put(point[i].h.to_units());
1553 294394 : put(' ');
1554 294394 : put(point[i].v.to_units());
1555 : }
1556 228693 : determine_line_limits(code, point, npoints);
1557 : }
1558 :
1559 528414 : for (i = 0; i < npoints; i++)
1560 297359 : output_hpos += point[i].h.to_units();
1561 231055 : hpos = output_hpos;
1562 231055 : if (code != 'e') {
1563 528334 : for (i = 0; i < npoints; i++)
1564 297319 : output_vpos += point[i].v.to_units();
1565 231015 : vpos = output_vpos;
1566 : }
1567 231055 : if (is_on())
1568 228693 : put('\n');
1569 231055 : }
1570 :
1571 117 : void troff_output_file::really_on()
1572 : {
1573 117 : flush_tbuf();
1574 117 : must_update_drawing_position = true;
1575 117 : do_motion();
1576 117 : }
1577 :
1578 134 : void troff_output_file::really_off()
1579 : {
1580 134 : flush_tbuf();
1581 134 : }
1582 :
1583 618 : void troff_output_file::really_put_filename(const char *filename,
1584 : int po)
1585 : {
1586 618 : flush_tbuf();
1587 618 : put("x F ");
1588 618 : if (po)
1589 0 : put("<");
1590 618 : put(filename);
1591 618 : if (po)
1592 0 : put(">");
1593 618 : put('\n');
1594 618 : }
1595 :
1596 2063 : void troff_output_file::really_begin_page(int pageno,
1597 : vunits page_length)
1598 : {
1599 2063 : flush_tbuf();
1600 2063 : if (has_page_begun) {
1601 869 : if (page_length > V0) {
1602 869 : put('V');
1603 869 : put(page_length.to_units());
1604 869 : put('\n');
1605 : }
1606 : }
1607 : else
1608 1194 : has_page_begun = true;
1609 2063 : current_tfont = 0;
1610 2063 : current_font_number = FONT_NOT_MOUNTED;
1611 2063 : current_size = 0;
1612 : // current_height = 0;
1613 : // current_slant = 0;
1614 2063 : hpos = 0;
1615 2063 : vpos = 0;
1616 2063 : output_hpos = 0;
1617 2063 : output_vpos = 0;
1618 2063 : must_update_drawing_position = true;
1619 53250 : for (int i = 0; i < nfont_positions; i++)
1620 51187 : font_position[i] = NULL_SYMBOL;
1621 2063 : put('p');
1622 2063 : put(pageno);
1623 2063 : put('\n');
1624 2063 : }
1625 :
1626 1 : void troff_output_file::really_copy_file(hunits x, vunits y,
1627 : const char *filename)
1628 : {
1629 1 : moveto(x, y);
1630 1 : flush_tbuf();
1631 1 : do_motion();
1632 1 : errno = 0;
1633 1 : FILE *ifp = include_search_path.open_file_cautiously(filename);
1634 1 : if (0 /* nullptr */ == ifp)
1635 0 : error("cannot open '%1': %2", filename, strerror(errno));
1636 : else {
1637 : int c;
1638 33 : while ((c = getc(ifp)) != EOF)
1639 32 : put(char(c));
1640 1 : fclose(ifp);
1641 : }
1642 1 : must_update_drawing_position = true;
1643 1 : current_size = 0;
1644 1 : current_tfont = 0;
1645 1 : current_font_number = FONT_NOT_MOUNTED;
1646 11 : for (int i = 0; i < nfont_positions; i++)
1647 10 : font_position[i] = NULL_SYMBOL;
1648 1 : }
1649 :
1650 169217 : void troff_output_file::really_transparent_char(unsigned char c)
1651 : {
1652 169217 : put(c);
1653 169217 : }
1654 :
1655 2388 : troff_output_file::~troff_output_file()
1656 : {
1657 1194 : delete[] font_position;
1658 2387 : }
1659 :
1660 1194 : void troff_output_file::trailer(vunits page_length)
1661 : {
1662 1194 : flush_tbuf();
1663 1194 : if (was_any_page_in_output_list) {
1664 1194 : if (page_length > V0) {
1665 1194 : put("x trailer\n");
1666 1194 : put('V');
1667 1194 : put(page_length.to_units());
1668 1194 : put('\n');
1669 : }
1670 : }
1671 : else
1672 0 : warning(WARN_RANGE, "no pages match output page selection list");
1673 1194 : put("x stop\n");
1674 1194 : }
1675 :
1676 1194 : troff_output_file::troff_output_file()
1677 : : current_slant(0), current_height(0), current_fill_color(0),
1678 : current_stroke_color(0), nfont_positions(10), tbuf_len(0),
1679 1194 : has_page_begun(false), cur_div_level(0)
1680 : {
1681 13134 : font_position = new symbol[nfont_positions];
1682 1194 : put("x T ");
1683 1194 : put(device);
1684 1194 : put('\n');
1685 1194 : put("x res ");
1686 1194 : put(units_per_inch);
1687 1194 : put(' ');
1688 1194 : put(hresolution);
1689 1194 : put(' ');
1690 1194 : put(vresolution);
1691 1194 : put('\n');
1692 1194 : put("x init\n");
1693 1194 : }
1694 :
1695 159669 : void troff_output_file::flush()
1696 : {
1697 159669 : flush_tbuf();
1698 159669 : real_output_file::flush();
1699 159669 : }
1700 :
1701 : /* output_file */
1702 :
1703 : output_file *the_output = 0 /* nullptr */;
1704 :
1705 1279 : output_file::output_file()
1706 : {
1707 1279 : is_dying = false;
1708 1279 : }
1709 :
1710 1278 : output_file::~output_file()
1711 : {
1712 1278 : }
1713 :
1714 86 : void output_file::trailer(vunits)
1715 : {
1716 86 : }
1717 :
1718 0 : void output_file::put_filename(const char *, int)
1719 : {
1720 0 : }
1721 :
1722 0 : void output_file::on()
1723 : {
1724 0 : }
1725 :
1726 0 : void output_file::off()
1727 : {
1728 0 : }
1729 :
1730 1279 : real_output_file::real_output_file()
1731 1279 : : want_page_printed(true), is_output_on(true)
1732 : {
1733 1279 : if (pipe_command) {
1734 1 : if ((fp = popen(pipe_command, POPEN_WT)) != 0 /* nullptr */) {
1735 1 : is_output_piped = true;
1736 1 : return;
1737 : }
1738 0 : error("pipe open failed: %1", strerror(errno));
1739 : }
1740 1278 : is_output_piped = false;
1741 1278 : fp = stdout;
1742 : }
1743 :
1744 1278 : real_output_file::~real_output_file()
1745 : {
1746 1279 : if (0 /* nullptr */ == fp)
1747 0 : return;
1748 : // Prevent destructor from recursing; see
1749 : // div.cpp:write_any_trailer_and_exit().
1750 1279 : is_dying = true;
1751 : // To avoid looping, set fp to 0 before calling fatal().
1752 1279 : if (ferror(fp)) {
1753 0 : fp = 0 /* nullptr */;
1754 0 : fatal("error on output file stream");
1755 : }
1756 1279 : else if (fflush(fp) < 0) {
1757 1 : fp = 0 /* nullptr */;
1758 1 : fatal("unable to flush output file: %1", strerror(errno));
1759 : }
1760 1278 : if (is_output_piped) {
1761 1 : int result = pclose(fp);
1762 1 : fp = 0 /* nullptr */;
1763 1 : if (result < 0)
1764 0 : fatal("unable to close pipe: %1", strerror(errno));
1765 1 : if (!WIFEXITED(result))
1766 0 : error("output process '%1' got fatal signal %2",
1767 : pipe_command,
1768 0 : WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
1769 : else {
1770 1 : int exit_status = WEXITSTATUS(result);
1771 1 : if (exit_status != 0)
1772 1 : error("output process '%1' exited with status %2",
1773 2 : pipe_command, exit_status);
1774 : }
1775 : }
1776 : else
1777 1277 : if (fclose(fp) < 0) {
1778 0 : fp = 0 /* nullptr */;
1779 0 : fatal("unable to close output file: %1", strerror(errno));
1780 : }
1781 1278 : }
1782 :
1783 159669 : void real_output_file::flush()
1784 : {
1785 : // To avoid looping, set fp to 0 before calling fatal().
1786 159669 : if (fflush(fp) < 0) {
1787 0 : fp = 0 /* nullptr */;
1788 0 : fatal("unable to flush output file: %1", strerror(errno));
1789 : }
1790 159669 : }
1791 :
1792 0 : bool real_output_file::is_selected_for_printing()
1793 : {
1794 0 : return want_page_printed;
1795 : }
1796 :
1797 2655 : void real_output_file::begin_page(int pageno, vunits page_length)
1798 : {
1799 2655 : want_page_printed = in_output_page_list(pageno);
1800 2655 : if (want_page_printed) {
1801 2655 : was_any_page_in_output_list = true;
1802 2655 : really_begin_page(pageno, page_length);
1803 : }
1804 2655 : }
1805 :
1806 2 : void real_output_file::copy_file(hunits x, vunits y,
1807 : const char *filename)
1808 : {
1809 2 : if (want_page_printed && is_output_on)
1810 2 : really_copy_file(x, y, filename);
1811 2 : check_output_limits(x.to_units(), y.to_units());
1812 2 : }
1813 :
1814 356795 : void real_output_file::transparent_char(unsigned char c)
1815 : {
1816 356795 : if (want_page_printed && is_output_on)
1817 356795 : really_transparent_char(c);
1818 356795 : }
1819 :
1820 214758 : void real_output_file::print_line(hunits x, vunits y, node *n,
1821 : vunits before, vunits after, hunits width)
1822 : {
1823 214758 : if (want_page_printed)
1824 214758 : really_print_line(x, y, n, before, after, width);
1825 214758 : delete_node_list(n);
1826 214758 : }
1827 :
1828 1 : void real_output_file::really_copy_file(hunits, vunits, const char *)
1829 : {
1830 : // do nothing
1831 1 : }
1832 :
1833 618 : void real_output_file::put_filename(const char *filename, int po)
1834 : {
1835 618 : really_put_filename(filename, po);
1836 618 : }
1837 :
1838 0 : void real_output_file::really_put_filename(const char *, int)
1839 : {
1840 0 : }
1841 :
1842 117 : void real_output_file::on()
1843 : {
1844 117 : really_on();
1845 : // XXX: Assertion fails when generating pic.html. Find out why.
1846 : //assert(!is_output_on);
1847 117 : is_output_on = true;
1848 117 : }
1849 :
1850 134 : void real_output_file::off()
1851 : {
1852 134 : really_off();
1853 : // XXX: Assertion fails when generating ms.html. Find out why.
1854 : //assert(is_output_on);
1855 134 : is_output_on = false;
1856 134 : }
1857 :
1858 13511360 : bool real_output_file::is_on()
1859 : {
1860 13511360 : return is_output_on;
1861 : }
1862 :
1863 0 : void real_output_file::really_on()
1864 : {
1865 0 : }
1866 :
1867 0 : void real_output_file::really_off()
1868 : {
1869 0 : }
1870 :
1871 : /* ascii_output_file */
1872 :
1873 32 : void ascii_output_file::really_transparent_char(unsigned char c)
1874 : {
1875 32 : if (fp != 0 /* nullptr */)
1876 32 : putc(c, fp);
1877 32 : }
1878 :
1879 1515 : void ascii_output_file::really_print_line(hunits, vunits, node *n,
1880 : vunits, vunits, hunits)
1881 : {
1882 1515 : while (n != 0 /* nullptr */) {
1883 1438 : n->ascii_print(this);
1884 1438 : n = n->next;
1885 : }
1886 77 : if (fp != 0 /* nullptr */)
1887 77 : fputc('\n', fp);
1888 77 : }
1889 :
1890 28 : void ascii_output_file::really_begin_page(int /* pageno */,
1891 : vunits /* page_length */)
1892 : {
1893 28 : fputs("<beginning of page>\n", fp);
1894 28 : }
1895 :
1896 26 : ascii_output_file::ascii_output_file()
1897 : {
1898 26 : }
1899 :
1900 : /* suppress_output_file */
1901 :
1902 59 : suppress_output_file::suppress_output_file()
1903 : {
1904 59 : }
1905 :
1906 25097 : void suppress_output_file::really_print_line(hunits, vunits, node *,
1907 : vunits, vunits, hunits)
1908 : {
1909 25097 : }
1910 :
1911 564 : void suppress_output_file::really_begin_page(int, vunits)
1912 : {
1913 564 : }
1914 :
1915 187546 : void suppress_output_file::really_transparent_char(unsigned char)
1916 : {
1917 187546 : }
1918 :
1919 : /* glyphs, ligatures, kerns, discretionary breaks */
1920 :
1921 : // abstract
1922 : class charinfo_node : public node {
1923 : protected:
1924 : charinfo *ci;
1925 : public:
1926 : charinfo_node(charinfo *, statem *, int, node * = 0 /* nullptr */);
1927 : virtual int ends_sentence();
1928 : virtual bool overlaps_vertically();
1929 : virtual bool overlaps_horizontally();
1930 : virtual void dump_properties();
1931 : };
1932 :
1933 13555468 : charinfo_node::charinfo_node(charinfo *c, statem *s, int divlevel,
1934 13555468 : node *x)
1935 13555468 : : node(x, s, divlevel), ci(c)
1936 : {
1937 13555468 : }
1938 :
1939 416225 : int charinfo_node::ends_sentence()
1940 : {
1941 416225 : if (ci->ends_sentence())
1942 81582 : return 1;
1943 334643 : else if (ci->is_transparent_to_end_of_sentence())
1944 18453 : return 2;
1945 316190 : return 0;
1946 : }
1947 :
1948 144 : bool charinfo_node::overlaps_horizontally()
1949 : {
1950 144 : return ci->overlaps_horizontally();
1951 : }
1952 :
1953 0 : bool charinfo_node::overlaps_vertically()
1954 : {
1955 0 : return ci->overlaps_vertically();
1956 : }
1957 :
1958 0 : void charinfo_node::dump_properties()
1959 : {
1960 0 : node::dump_properties();
1961 : // GNU troff multiplexes the distinction of ordinary vs. special
1962 : // characters though the special character code zero.
1963 0 : unsigned char c = ci->get_ascii_code();
1964 0 : if (c != 0U) {
1965 0 : fputs(", \"character\": ", stderr);
1966 : // It's not a `string` or `symbol` we can `.json_dump()`, so we have
1967 : // to write the quotation marks ourselves.
1968 0 : fputc('\"', stderr);
1969 0 : json_char jc = json_encode_char(c);
1970 : // Write out its JSON representation by character by character to
1971 : // keep libc string functions from interpreting C escape sequences.
1972 0 : for (size_t i = 0; i < jc.len; i++)
1973 0 : fputc(jc.buf[i], stderr);
1974 0 : fputc('\"', stderr);
1975 : }
1976 : else {
1977 0 : fputs(", \"special character\": ", stderr);
1978 0 : ci->nm.json_dump();
1979 : }
1980 0 : fflush(stderr);
1981 0 : }
1982 :
1983 : // A glyph node corresponds to a glyph supplied by a device font.
1984 :
1985 : class glyph_node : public charinfo_node {
1986 : protected:
1987 : tfont *tf;
1988 : color *gcol;
1989 : color *fcol; /* this is needed for grotty */
1990 : #ifdef STORE_WIDTH
1991 : hunits wid;
1992 : glyph_node(charinfo *, tfont *, color *, color *, hunits,
1993 : statem *, int, node * = 0 /* nullptr */);
1994 : #endif
1995 : public:
1996 : glyph_node(charinfo *, tfont *, color *, color *,
1997 : statem *, int, node * = 0 /* nullptr */);
1998 26935158 : ~glyph_node() {}
1999 : node *copy();
2000 : node *merge_glyph_node(glyph_node *);
2001 : node *merge_self(node *);
2002 : hunits width();
2003 : node *last_char_node();
2004 : units size();
2005 : void vertical_extent(vunits *, vunits *);
2006 : hunits subscript_correction();
2007 : hunits italic_correction();
2008 : hunits left_italic_correction();
2009 : hunits skew();
2010 : hyphenation_type get_hyphenation_type();
2011 : tfont *get_tfont();
2012 : color *get_stroke_color();
2013 : color *get_fill_color();
2014 : void tprint(troff_output_file *);
2015 : void zero_width_tprint(troff_output_file *);
2016 : hyphen_list *get_hyphen_list(hyphen_list *, int *);
2017 : node *add_self(node *, hyphen_list **);
2018 : void ascii_print(ascii_output_file *);
2019 : void asciify(macro *);
2020 : int character_type();
2021 : bool is_same_as(node *);
2022 : const char *type();
2023 : bool causes_tprint();
2024 : bool is_tag();
2025 : };
2026 :
2027 : // Not derived from `container_node`; implements custom double container
2028 : // dumper in dump_node().
2029 : class ligature_node : public glyph_node {
2030 : node *n1;
2031 : node *n2;
2032 : #ifdef STORE_WIDTH
2033 : ligature_node(charinfo *, tfont *, color *, color *, hunits,
2034 : node *, node *, statem *, int,
2035 : node * = 0 /* nullptr */);
2036 : #endif
2037 : public:
2038 : void *operator new(size_t);
2039 : void operator delete(void *);
2040 : ligature_node(charinfo *, tfont *, color *, color *,
2041 : node *, node *, statem *, int,
2042 : node * = 0 /* nullptr */);
2043 : ~ligature_node();
2044 : node *copy();
2045 : node *add_self(node *, hyphen_list **);
2046 : hyphen_list *get_hyphen_list(hyphen_list *, int *);
2047 : void ascii_print(ascii_output_file *);
2048 : void asciify(macro *);
2049 : bool is_same_as(node *);
2050 : const char *type();
2051 : bool causes_tprint();
2052 : bool is_tag();
2053 : void dump_node();
2054 : };
2055 :
2056 : // Not derived from `container_node`; implements custom double container
2057 : // dumper in dump_node().
2058 : class kern_pair_node : public node {
2059 : hunits amount;
2060 : node *n1;
2061 : node *n2;
2062 : public:
2063 : kern_pair_node(hunits, node *, node *, statem *, int,
2064 : node * = 0 /* nullptr */);
2065 : ~kern_pair_node();
2066 : node *copy();
2067 : node *merge_glyph_node(glyph_node *);
2068 : node *add_self(node *, hyphen_list **);
2069 : hyphen_list *get_hyphen_list(hyphen_list *, int *);
2070 : node *add_discretionary_hyphen();
2071 : hunits width();
2072 : node *last_char_node();
2073 : hunits italic_correction();
2074 : hunits subscript_correction();
2075 : void tprint(troff_output_file *);
2076 : hyphenation_type get_hyphenation_type();
2077 : int ends_sentence();
2078 : void ascii_print(ascii_output_file *);
2079 : void asciify(macro *);
2080 : bool is_same_as(node *);
2081 : const char *type();
2082 : bool causes_tprint();
2083 : bool is_tag();
2084 : void vertical_extent(vunits *, vunits *);
2085 : void dump_properties();
2086 : void dump_node();
2087 : };
2088 :
2089 : // Not derived from `container_node`; implements custom triple container
2090 : // dumper in dump_node().
2091 : class dbreak_node : public node {
2092 : node *none;
2093 : node *pre;
2094 : node *post;
2095 : public:
2096 : dbreak_node(node *, node *, statem *, int, node * = 0 /* nullptr */);
2097 : ~dbreak_node();
2098 : node *copy();
2099 : node *merge_glyph_node(glyph_node *);
2100 : node *add_discretionary_hyphen();
2101 : hunits width();
2102 : node *last_char_node();
2103 : hunits italic_correction();
2104 : hunits subscript_correction();
2105 : void tprint(troff_output_file *);
2106 : breakpoint *get_breakpoints(hunits /* width */, int /* ns */,
2107 : breakpoint * /* rest */ = 0 /* nullptr */,
2108 : bool /* is_inner */ = false);
2109 : int nbreaks();
2110 : int ends_sentence();
2111 : void split(int, node **, node **);
2112 : hyphenation_type get_hyphenation_type();
2113 : void ascii_print(ascii_output_file *);
2114 : void asciify(macro *);
2115 : bool is_same_as(node *);
2116 : const char *type();
2117 : bool causes_tprint();
2118 : bool is_tag();
2119 : void dump_node();
2120 : };
2121 :
2122 11988 : void *ligature_node::operator new(size_t n)
2123 : {
2124 11988 : return new char[n];
2125 : }
2126 :
2127 11920 : void ligature_node::operator delete(void *p)
2128 : {
2129 11920 : delete[] (char *)p;
2130 11920 : }
2131 :
2132 12904721 : glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc,
2133 12904721 : statem *s, int divlevel, node *x)
2134 12904721 : : charinfo_node(c, s, divlevel, x), tf(t), gcol(gc), fcol(fc)
2135 : {
2136 : #ifdef STORE_WIDTH
2137 12904721 : wid = tf->get_width(ci);
2138 : #endif
2139 12904721 : }
2140 :
2141 : #ifdef STORE_WIDTH
2142 596304 : glyph_node::glyph_node(charinfo *c, tfont *t,
2143 : color *gc, color *fc, hunits w,
2144 596304 : statem *s, int divlevel, node *x)
2145 596304 : : charinfo_node(c, s, divlevel, x), tf(t), gcol(gc), fcol(fc), wid(w)
2146 : {
2147 596304 : }
2148 : #endif
2149 :
2150 595226 : node *glyph_node::copy()
2151 : {
2152 : #ifdef STORE_WIDTH
2153 595226 : return new glyph_node(ci, tf, gcol, fcol, wid, state, div_nest_level);
2154 : #else
2155 : return new glyph_node(ci, tf, gcol, fcol, state, div_nest_level);
2156 : #endif
2157 : }
2158 :
2159 12850473 : node *glyph_node::merge_self(node *nd)
2160 : {
2161 12850473 : return nd->merge_glyph_node(this);
2162 : }
2163 :
2164 183183 : int glyph_node::character_type()
2165 : {
2166 183183 : return tf->get_character_type(ci);
2167 : }
2168 :
2169 204097 : node *glyph_node::add_self(node *n, hyphen_list **p)
2170 : {
2171 204097 : assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
2172 204097 : next = 0 /* nullptr */;
2173 204097 : node *nn = 0 /* nullptr */;
2174 408173 : if ((0 /* nullptr */ == n) ||
2175 204076 : (0 /* nullptr */ == (nn = n->merge_glyph_node(this)))) {
2176 198356 : next = n;
2177 198356 : nn = this;
2178 : }
2179 204097 : if ((*p)->is_hyphen)
2180 21404 : nn = nn->add_discretionary_hyphen();
2181 204097 : hyphen_list *pp = *p;
2182 204097 : *p = (*p)->next;
2183 204097 : delete pp;
2184 204097 : assert(nn != 0 /* nullptr */);
2185 204097 : return nn;
2186 : }
2187 :
2188 0 : units glyph_node::size()
2189 : {
2190 0 : return tf->get_size().to_units();
2191 : }
2192 :
2193 204097 : hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count)
2194 : {
2195 204097 : (*count)++;
2196 204097 : return new hyphen_list(ci->get_hyphenation_code(), tail);
2197 : }
2198 :
2199 77166 : tfont *node::get_tfont()
2200 : {
2201 77166 : return 0 /* nullptr */;
2202 : }
2203 :
2204 24563 : tfont *glyph_node::get_tfont()
2205 : {
2206 24563 : return tf;
2207 : }
2208 :
2209 64 : color *node::get_stroke_color()
2210 : {
2211 64 : return 0 /* nullptr */;
2212 : }
2213 :
2214 24499 : color *glyph_node::get_stroke_color()
2215 : {
2216 24499 : return gcol;
2217 : }
2218 :
2219 64 : color *node::get_fill_color()
2220 : {
2221 64 : return 0 /* nullptr */;
2222 : }
2223 :
2224 24499 : color *glyph_node::get_fill_color()
2225 : {
2226 24499 : return fcol;
2227 : }
2228 :
2229 2539854 : node *node::merge_glyph_node(glyph_node *)
2230 : {
2231 2539854 : return 0 /* nullptr */;
2232 : }
2233 :
2234 10508480 : node *glyph_node::merge_glyph_node(glyph_node *gn)
2235 : {
2236 10508480 : if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
2237 : charinfo *lig;
2238 10468377 : if ((lig = tf->get_lig(ci, gn->ci)) != 0 /* nullptr */) {
2239 10910 : node *next1 = next;
2240 10910 : next = 0 /* nullptr */;
2241 : return new ligature_node(lig, tf, gcol, fcol, this, gn, state,
2242 496310 : gn->div_nest_level, next1);
2243 : }
2244 10457467 : hunits kern;
2245 10457467 : if (tf->is_kerned(ci, gn->ci, &kern)) {
2246 485400 : node *next1 = next;
2247 485400 : next = 0 /* nullptr */;
2248 : return new kern_pair_node(kern, this, gn, state,
2249 485400 : gn->div_nest_level, next1);
2250 : }
2251 : }
2252 10012170 : return 0 /* nullptr */;
2253 : }
2254 :
2255 : #ifdef STORE_WIDTH
2256 : inline
2257 : #endif
2258 27884326 : hunits glyph_node::width()
2259 : {
2260 : #ifdef STORE_WIDTH
2261 27884326 : return wid;
2262 : #else
2263 : return tf->get_width(ci);
2264 : #endif
2265 : }
2266 :
2267 3401 : node *glyph_node::last_char_node()
2268 : {
2269 3401 : return this;
2270 : }
2271 :
2272 195186 : void glyph_node::vertical_extent(vunits *min, vunits *max)
2273 : {
2274 195186 : *min = -tf->get_char_height(ci);
2275 195186 : *max = tf->get_char_depth(ci);
2276 195186 : }
2277 :
2278 20778 : hunits glyph_node::skew()
2279 : {
2280 20778 : return tf->get_char_skew(ci);
2281 : }
2282 :
2283 21035 : hunits glyph_node::subscript_correction()
2284 : {
2285 21035 : return tf->get_subscript_correction(ci);
2286 : }
2287 :
2288 33166 : hunits glyph_node::italic_correction()
2289 : {
2290 33166 : return tf->get_italic_correction(ci);
2291 : }
2292 :
2293 32205 : hunits glyph_node::left_italic_correction()
2294 : {
2295 32205 : return tf->get_left_italic_correction(ci);
2296 : }
2297 :
2298 194899 : hyphenation_type glyph_node::get_hyphenation_type()
2299 : {
2300 194899 : return HYPHENATION_PERMITTED;
2301 : }
2302 :
2303 1191 : void glyph_node::ascii_print(ascii_output_file *ascii)
2304 : {
2305 1191 : unsigned char c = ci->get_ascii_code();
2306 1191 : if (c != 0U)
2307 1140 : ascii->outc(c);
2308 : else
2309 51 : ascii->outs(ci->nm.contents());
2310 1191 : }
2311 10910 : ligature_node::ligature_node(charinfo *c, tfont *t,
2312 : color *gc, color *fc,
2313 : node *gn1, node *gn2, statem *s,
2314 10910 : int divlevel, node *x)
2315 10910 : : glyph_node(c, t, gc, fc, s, divlevel, x), n1(gn1), n2(gn2)
2316 : {
2317 10910 : }
2318 :
2319 : #ifdef STORE_WIDTH
2320 1078 : ligature_node::ligature_node(charinfo *c, tfont *t,
2321 : color *gc, color *fc, hunits w,
2322 : node *gn1, node *gn2, statem *s,
2323 1078 : int divlevel, node *x)
2324 1078 : : glyph_node(c, t, gc, fc, w, s, divlevel, x), n1(gn1), n2(gn2)
2325 : {
2326 1078 : }
2327 : #endif
2328 :
2329 23840 : ligature_node::~ligature_node()
2330 : {
2331 11920 : delete n1;
2332 11920 : delete n2;
2333 23840 : }
2334 :
2335 1078 : node *ligature_node::copy()
2336 : {
2337 : #ifdef STORE_WIDTH
2338 1078 : return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(),
2339 1078 : n2->copy(), state, div_nest_level);
2340 : #else
2341 : return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy(),
2342 : state, div_nest_level);
2343 : #endif
2344 : }
2345 :
2346 0 : void ligature_node::ascii_print(ascii_output_file *ascii)
2347 : {
2348 0 : n1->ascii_print(ascii);
2349 0 : n2->ascii_print(ascii);
2350 0 : }
2351 :
2352 400 : hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail,
2353 : int *count)
2354 : {
2355 400 : hyphen_list *hl = n2->get_hyphen_list(tail, count);
2356 400 : return n1->get_hyphen_list(hl, count);
2357 : }
2358 :
2359 400 : node *ligature_node::add_self(node *n, hyphen_list **p)
2360 : {
2361 400 : n = n1->add_self(n, p);
2362 400 : n = n2->add_self(n, p);
2363 400 : n1 = n2 = 0 /* nullptr */;
2364 400 : delete this;
2365 400 : return n;
2366 : }
2367 :
2368 0 : void ligature_node::dump_node()
2369 : {
2370 0 : fputc('{', stderr);
2371 : // Flush so that in case something goes wrong with property dumping,
2372 : // we know that we traversed to a new node.
2373 0 : fflush(stderr);
2374 0 : node::dump_properties();
2375 0 : if (n1 != 0 /* nullptr */) {
2376 0 : fputs(", \"n1\": ", stderr);
2377 0 : n1->dump_node();
2378 : }
2379 0 : if (n2 != 0 /* nullptr */) {
2380 0 : fputs(", \"n2\": ", stderr);
2381 0 : n2->dump_node();
2382 : }
2383 0 : fputc('}', stderr);
2384 0 : fflush(stderr);
2385 0 : }
2386 :
2387 499945 : kern_pair_node::kern_pair_node(hunits n, node *first, node *second,
2388 499945 : statem* s, int divlevel, node *x)
2389 499945 : : node(x, s, divlevel), amount(n), n1(first), n2(second)
2390 : {
2391 499945 : }
2392 :
2393 0 : void kern_pair_node::dump_properties()
2394 : {
2395 0 : node::dump_properties();
2396 0 : fprintf(stderr, ", \"amount\": %d", amount.to_units());
2397 0 : fflush(stderr);
2398 0 : }
2399 :
2400 0 : void kern_pair_node::dump_node()
2401 : {
2402 0 : fputc('{', stderr);
2403 : // Flush so that in case something goes wrong with property dumping,
2404 : // we know that we traversed to a new node.
2405 0 : fflush(stderr);
2406 0 : dump_properties();
2407 0 : if (n1 != 0 /* nullptr */) {
2408 0 : fputs(", \"n1\": ", stderr);
2409 0 : n1->dump_node();
2410 : }
2411 0 : if (n2 != 0 /* nullptr */) {
2412 0 : fputs(", \"n2\": ", stderr);
2413 0 : n2->dump_node();
2414 : }
2415 0 : fputc('}', stderr);
2416 0 : fflush(stderr);
2417 0 : }
2418 :
2419 25115 : dbreak_node::dbreak_node(node *n, node *p, statem *s, int divlevel,
2420 25115 : node *x)
2421 25115 : : node(x, s, divlevel), none(n), pre(p), post(0 /* nullptr */)
2422 : {
2423 25115 : }
2424 :
2425 25503 : node *dbreak_node::merge_glyph_node(glyph_node *gn)
2426 : {
2427 25503 : glyph_node *gn2 = static_cast<glyph_node *>(gn->copy());
2428 25503 : node *new_none = none ? none->merge_glyph_node(gn) : 0 /* nullptr */;
2429 25503 : node *new_post = post ? post->merge_glyph_node(gn2) : 0 /* nullptr */;
2430 25503 : if ((0 /* nullptr */ == new_none) && (0 /* nullptr */ == new_post)) {
2431 24681 : delete gn2;
2432 24681 : return 0 /* nullptr */;
2433 : }
2434 822 : if (new_none != 0 /* nullptr */)
2435 822 : none = new_none;
2436 : else {
2437 0 : gn->next = none;
2438 0 : none = gn;
2439 : }
2440 822 : if (new_post != 0 /* nullptr */)
2441 122 : post = new_post;
2442 : else {
2443 700 : gn2->next = post;
2444 700 : post = gn2;
2445 : }
2446 822 : return this;
2447 : }
2448 :
2449 457287 : node *kern_pair_node::merge_glyph_node(glyph_node *gn)
2450 : {
2451 457287 : node *nd = n2->merge_glyph_node(gn);
2452 457287 : if (0 /* nullptr */ == nd)
2453 397399 : return 0 /* nullptr */;
2454 59888 : n2 = nd;
2455 59888 : nd = n2->merge_self(n1);
2456 59888 : if (nd != 0 /* nullptr */) {
2457 0 : nd->next = next;
2458 0 : n1 = n2 = 0 /* nullptr */;
2459 0 : delete this;
2460 0 : return nd;
2461 : }
2462 59888 : return this;
2463 : }
2464 :
2465 5912 : hunits kern_pair_node::italic_correction()
2466 : {
2467 5912 : return n2->italic_correction();
2468 : }
2469 :
2470 328 : hunits kern_pair_node::subscript_correction()
2471 : {
2472 328 : return n2->subscript_correction();
2473 : }
2474 :
2475 5125 : void kern_pair_node::vertical_extent(vunits *min, vunits *max)
2476 : {
2477 5125 : n1->vertical_extent(min, max);
2478 5125 : vunits min2, max2;
2479 5125 : n2->vertical_extent(&min2, &max2);
2480 5125 : if (min2 < *min)
2481 442 : *min = min2;
2482 5125 : if (max2 > *max)
2483 2949 : *max = max2;
2484 5125 : }
2485 :
2486 572 : node *kern_pair_node::add_discretionary_hyphen()
2487 : {
2488 572 : tfont *tf = n1->get_tfont();
2489 572 : if (tf != 0 /* nullptr */) {
2490 572 : if (tf->contains(soft_hyphen_char)) {
2491 572 : color *gcol = n2->get_stroke_color();
2492 572 : color *fcol = n2->get_fill_color();
2493 572 : node *next1 = next;
2494 572 : next = 0 /* nullptr */;
2495 572 : node *n = copy();
2496 : glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2497 572 : state, div_nest_level);
2498 572 : node *nn = n->merge_glyph_node(gn);
2499 572 : if (0 /* nullptr */ == nn) {
2500 572 : gn->next = n;
2501 572 : nn = gn;
2502 : }
2503 572 : return new dbreak_node(this, nn, state, div_nest_level, next1);
2504 : }
2505 : }
2506 0 : return this;
2507 : }
2508 :
2509 998084 : kern_pair_node::~kern_pair_node()
2510 : {
2511 499042 : if (n1 != 0 /* nullptr */)
2512 494326 : delete n1;
2513 499042 : if (n2 != 0 /* nullptr */)
2514 494326 : delete n2;
2515 998084 : }
2516 :
2517 49884 : dbreak_node::~dbreak_node()
2518 : {
2519 24942 : delete_node_list(pre);
2520 24942 : delete_node_list(post);
2521 24942 : delete_node_list(none);
2522 49884 : }
2523 :
2524 14545 : node *kern_pair_node::copy()
2525 : {
2526 14545 : return new kern_pair_node(amount, n1->copy(), n2->copy(), state,
2527 14545 : div_nest_level);
2528 : }
2529 :
2530 5452 : node *copy_node_list(node *n)
2531 : {
2532 5452 : node *p = 0 /* nullptr */;
2533 15964 : while (n != 0 /* nullptr */) {
2534 10512 : node *nn = n->copy();
2535 10512 : nn->next = p;
2536 10512 : p = nn;
2537 10512 : n = n->next;
2538 : }
2539 15964 : while (p != 0 /* nullptr */) {
2540 10512 : node *pp = p->next;
2541 10512 : p->next = n;
2542 10512 : n = p;
2543 10512 : p = pp;
2544 : }
2545 5452 : return n;
2546 : }
2547 :
2548 39504514 : void delete_node_list(node *n)
2549 : {
2550 39504514 : while (n != 0 /* nullptr */) {
2551 27478131 : node *tem = n;
2552 27478131 : n = n->next;
2553 27478131 : delete tem;
2554 : }
2555 12026383 : }
2556 :
2557 0 : void dump_node_list(node *n)
2558 : {
2559 0 : bool need_comma = false;
2560 0 : fputc('[', stderr);
2561 0 : while (n != 0 /* nullptr */) {
2562 0 : if (need_comma)
2563 0 : fputs(", ", stderr);
2564 0 : n->dump_node();
2565 0 : need_comma = true;
2566 0 : n = n->next;
2567 : }
2568 : // !need_comma implies that the list was empty. JSON convention is to
2569 : // put a space between an empty pair of square brackets.
2570 0 : if (!need_comma)
2571 0 : fputc(' ', stderr);
2572 0 : fputc(']', stderr);
2573 0 : fflush(stderr);
2574 0 : }
2575 :
2576 552 : node *dbreak_node::copy()
2577 : {
2578 552 : dbreak_node *p = new dbreak_node(copy_node_list(none),
2579 552 : copy_node_list(pre), state,
2580 552 : div_nest_level);
2581 552 : p->post = copy_node_list(post);
2582 552 : return p;
2583 : }
2584 :
2585 4466 : hyphen_list *node::get_hyphen_list(hyphen_list *tail, int *)
2586 : {
2587 4466 : return tail;
2588 : }
2589 :
2590 4716 : hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail,
2591 : int *count)
2592 : {
2593 4716 : hyphen_list *hl = n2->get_hyphen_list(tail, count);
2594 4716 : return n1->get_hyphen_list(hl, count);
2595 : }
2596 :
2597 : class hyphen_inhibitor_node : public node {
2598 : public:
2599 : hyphen_inhibitor_node(node * = 0 /* nullptr */);
2600 : void asciify(macro *);
2601 : node *copy();
2602 : bool causes_tprint();
2603 : bool is_tag();
2604 : bool is_same_as(node *);
2605 : const char *type();
2606 : hyphenation_type get_hyphenation_type();
2607 : };
2608 :
2609 91067 : hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
2610 : {
2611 91067 : }
2612 :
2613 11336 : node *hyphen_inhibitor_node::copy()
2614 : {
2615 11336 : return new hyphen_inhibitor_node;
2616 : }
2617 :
2618 46 : bool hyphen_inhibitor_node::causes_tprint()
2619 : {
2620 46 : return false;
2621 : }
2622 :
2623 11599 : bool hyphen_inhibitor_node::is_tag()
2624 : {
2625 11599 : return false;
2626 : }
2627 :
2628 890 : bool hyphen_inhibitor_node::is_same_as(node *)
2629 : {
2630 890 : return true;
2631 : }
2632 :
2633 1802 : const char *hyphen_inhibitor_node::type()
2634 : {
2635 1802 : return "hyphenation inhibitor node";
2636 : }
2637 :
2638 1096 : hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
2639 : {
2640 1096 : return HYPHENATION_INHIBITED;
2641 : }
2642 :
2643 : /* add_discretionary_hyphen methods */
2644 :
2645 71 : node *dbreak_node::add_discretionary_hyphen()
2646 : {
2647 71 : if (post)
2648 71 : post = post->add_discretionary_hyphen();
2649 71 : if (none)
2650 71 : none = none->add_discretionary_hyphen();
2651 71 : return this;
2652 : }
2653 :
2654 103722 : node *node::add_discretionary_hyphen()
2655 : {
2656 103722 : tfont *tf = get_tfont();
2657 103722 : if (0 /* nullptr */ == tf)
2658 79731 : return new hyphen_inhibitor_node(this);
2659 23991 : if (tf->contains(soft_hyphen_char)) {
2660 23991 : color *gcol = get_stroke_color();
2661 23991 : color *fcol = get_fill_color();
2662 23991 : node *next1 = next;
2663 23991 : next = 0 /* nullptr */;
2664 23991 : node *n = copy();
2665 : glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2666 23991 : state, div_nest_level);
2667 23991 : node *n1 = n->merge_glyph_node(gn);
2668 23991 : if (0 /* nullptr */ == n1) {
2669 22557 : gn->next = n;
2670 22557 : n1 = gn;
2671 : }
2672 23991 : return new dbreak_node(this, n1, state, div_nest_level, next1);
2673 : }
2674 0 : return this;
2675 : }
2676 :
2677 60066 : node *node::merge_self(node *)
2678 : {
2679 60066 : return 0 /* nullptr */;
2680 : }
2681 :
2682 4466 : node *node::add_self(node *n, hyphen_list ** /*p*/)
2683 : {
2684 4466 : next = n;
2685 4466 : return this;
2686 : }
2687 :
2688 4716 : node *kern_pair_node::add_self(node *n, hyphen_list **p)
2689 : {
2690 4716 : n = n1->add_self(n, p);
2691 4716 : n = n2->add_self(n, p);
2692 4716 : n1 = n2 = 0 /* nullptr */;
2693 4716 : delete this;
2694 4716 : return n;
2695 : }
2696 :
2697 8171552 : hunits node::width()
2698 : {
2699 8171552 : return H0;
2700 : }
2701 :
2702 8 : node *node::last_char_node()
2703 : {
2704 8 : return 0 /* nullptr */;
2705 : }
2706 :
2707 0 : bool node::causes_tprint()
2708 : {
2709 0 : return false;
2710 : }
2711 :
2712 0 : bool node::is_tag()
2713 : {
2714 0 : return false;
2715 : }
2716 :
2717 : // TODO: Figure out what a hyphenation code means in the UTF-8 future,
2718 : // where a "grochar" is a vector of NFD decomposed code points. Can it
2719 : // be a scalar--a 32-bit int?
2720 132199 : unsigned char node::get_break_code()
2721 : {
2722 132199 : return 0U;
2723 : }
2724 :
2725 2000156 : hunits hmotion_node::width()
2726 : {
2727 2000156 : return n;
2728 : }
2729 :
2730 0 : units node::size()
2731 : {
2732 0 : return points_to_units(10);
2733 : }
2734 :
2735 0 : void node::dump_properties()
2736 : {
2737 0 : fprintf(stderr, "\"type\": \"%s\"", type());
2738 0 : fprintf(stderr, ", \"diversion level\": %d", div_nest_level);
2739 0 : fprintf(stderr, ", \"is_special_node\": %s",
2740 0 : is_special ? "true" : "false");
2741 0 : if (push_state) {
2742 0 : fputs(", \"push_state\": ", stderr);
2743 0 : push_state->display_state();
2744 : }
2745 0 : if (state) {
2746 0 : fputs(", \"state\": ", stderr);
2747 0 : state->display_state();
2748 : }
2749 0 : fflush(stderr);
2750 0 : }
2751 :
2752 0 : void node::dump_node()
2753 : {
2754 0 : fputc('{', stderr);
2755 : // Flush so that in case something goes wrong with property dumping,
2756 : // we know that we traversed to a new node.
2757 0 : fflush(stderr);
2758 0 : dump_properties();
2759 0 : fputc('}', stderr);
2760 0 : fflush(stderr);
2761 0 : }
2762 :
2763 34835 : container_node::container_node(node *contents)
2764 34835 : : node(), nodes(contents)
2765 : {
2766 34835 : }
2767 :
2768 138258 : container_node::container_node(node *nxt, node *contents)
2769 138258 : : node(nxt), nodes(contents)
2770 : {
2771 138258 : }
2772 :
2773 : // `left_italic_corrected_node` uses an initially empty container.
2774 7292 : container_node::container_node(node *nxt, statem *s, int divl)
2775 7292 : : node(nxt, s, divl), nodes(0 /* nullptr */)
2776 : {
2777 7292 : }
2778 :
2779 : #if 0
2780 : container_node::container_node(node *nxt, statem *s, node *contents)
2781 : : node(nxt, s), nodes(contents)
2782 : {
2783 : }
2784 : #endif
2785 :
2786 29654 : container_node::container_node(node *nxt, statem *s, int divl,
2787 29654 : node *contents)
2788 29654 : : node(nxt, s, divl), nodes(contents)
2789 : {
2790 29654 : }
2791 :
2792 0 : container_node::container_node(node *nxt, statem *s, int divl,
2793 0 : bool special, node *contents)
2794 0 : : node(nxt, s, divl, special), nodes(contents)
2795 : {
2796 0 : }
2797 :
2798 209415 : container_node::~container_node()
2799 : {
2800 209415 : delete_node_list(nodes);
2801 209415 : }
2802 :
2803 0 : void container_node::dump_node()
2804 : {
2805 0 : fputc('{', stderr);
2806 0 : dump_properties();
2807 0 : fputs(", \"contains\": ", stderr);
2808 0 : dump_node_list(nodes);
2809 0 : fputc('}', stderr);
2810 0 : fflush(stderr);
2811 0 : }
2812 :
2813 0 : void dump_node_list_in_reverse(node *nlist)
2814 : {
2815 : // It's stored in reverse order already; this puts it forward again.
2816 0 : std::stack<node *> reversed_node_list;
2817 0 : node *n = nlist;
2818 :
2819 0 : while (n != 0 /* nullptr */) {
2820 0 : reversed_node_list.push(n);
2821 0 : n = n->next;
2822 : }
2823 0 : fputc('[', stderr);
2824 0 : bool need_comma = false;
2825 0 : while (!reversed_node_list.empty()) {
2826 0 : if (need_comma)
2827 0 : fputs(",\n", stderr);
2828 0 : reversed_node_list.top()->dump_node();
2829 0 : reversed_node_list.pop();
2830 0 : need_comma = true;
2831 : }
2832 : // !need_comma implies that the list was empty. JSON convention is to
2833 : // put a space between an empty pair of square brackets.
2834 0 : if (!need_comma)
2835 0 : fputc(' ', stderr);
2836 0 : fputs("]\n", stderr);
2837 0 : fflush(stderr);
2838 0 : }
2839 :
2840 1018548 : hunits kern_pair_node::width()
2841 : {
2842 2037096 : return n1->width() + n2->width() + amount;
2843 : }
2844 :
2845 0 : node *kern_pair_node::last_char_node()
2846 : {
2847 0 : node *nd = n2->last_char_node();
2848 0 : if (nd != 0 /* nullptr */)
2849 0 : return nd;
2850 0 : return n1->last_char_node();
2851 : }
2852 :
2853 34761 : hunits dbreak_node::width()
2854 : {
2855 34761 : hunits x = H0;
2856 69522 : for (node *n = none; n != 0 /* nullptr */; n = n->next)
2857 34761 : x += n->width();
2858 34761 : return x;
2859 : }
2860 :
2861 0 : node *dbreak_node::last_char_node()
2862 : {
2863 0 : for (node *n = none; n != 0 /* nullptr */; n = n->next) {
2864 0 : node *last_node = n->last_char_node();
2865 0 : if (last_node)
2866 0 : return last_node;
2867 : }
2868 0 : return 0 /* nullptr */;
2869 : }
2870 :
2871 0 : hunits dbreak_node::italic_correction()
2872 : {
2873 0 : return none ? none->italic_correction() : H0;
2874 : }
2875 :
2876 0 : hunits dbreak_node::subscript_correction()
2877 : {
2878 0 : return none ? none->subscript_correction() : H0;
2879 : }
2880 :
2881 : class italic_corrected_node : public container_node {
2882 : hunits x;
2883 : public:
2884 : italic_corrected_node(node *, hunits, statem *, int,
2885 : node * = 0 /* nullptr */);
2886 : node *copy();
2887 : void ascii_print(ascii_output_file *);
2888 : void asciify(macro *);
2889 : hunits width();
2890 : node *last_char_node();
2891 : void vertical_extent(vunits *, vunits *);
2892 : int ends_sentence();
2893 : bool overlaps_horizontally();
2894 : bool overlaps_vertically();
2895 : bool is_same_as(node *);
2896 : hyphenation_type get_hyphenation_type();
2897 : tfont *get_tfont();
2898 : hyphen_list *get_hyphen_list(hyphen_list *, int *);
2899 : int character_type();
2900 : void tprint(troff_output_file *);
2901 : hunits subscript_correction();
2902 : hunits skew();
2903 : node *add_self(node *, hyphen_list **);
2904 : const char *type();
2905 : bool causes_tprint();
2906 : bool is_tag();
2907 : void dump_properties();
2908 : };
2909 :
2910 34465 : node *node::add_italic_correction(hunits *wd)
2911 : {
2912 34465 : hunits ic = italic_correction();
2913 34465 : if (ic.is_zero())
2914 13204 : return this;
2915 : else {
2916 21261 : node *next1 = next;
2917 21261 : next = 0 /* nullptr */;
2918 21261 : *wd += ic;
2919 : return new italic_corrected_node(this, ic, state, div_nest_level,
2920 21261 : next1);
2921 : }
2922 : }
2923 :
2924 24779 : italic_corrected_node::italic_corrected_node(node *nn, hunits xx,
2925 : statem *s, int divlevel,
2926 24779 : node *p)
2927 24779 : : container_node(p, s, divlevel, nn), x(xx)
2928 : {
2929 24779 : assert(nodes != 0 /* nullptr */);
2930 24779 : }
2931 :
2932 0 : void italic_corrected_node::dump_properties()
2933 : {
2934 0 : node::dump_properties();
2935 0 : fprintf(stderr, ", \"hunits\": %d", x.to_units());
2936 0 : fflush(stderr);
2937 0 : }
2938 :
2939 3518 : node *italic_corrected_node::copy()
2940 : {
2941 3518 : return new italic_corrected_node(nodes->copy(), x, state,
2942 3518 : div_nest_level);
2943 : }
2944 :
2945 12916 : hunits italic_corrected_node::width()
2946 : {
2947 25832 : return nodes->width() + x;
2948 : }
2949 :
2950 484 : void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
2951 : {
2952 484 : nodes->vertical_extent(min, max);
2953 484 : }
2954 :
2955 10566 : void italic_corrected_node::tprint(troff_output_file *out)
2956 : {
2957 10566 : nodes->tprint(out);
2958 10566 : out->right(x);
2959 10566 : }
2960 :
2961 294 : hunits italic_corrected_node::skew()
2962 : {
2963 588 : return nodes->skew() - (x / 2);
2964 : }
2965 :
2966 294 : hunits italic_corrected_node::subscript_correction()
2967 : {
2968 588 : return nodes->subscript_correction() - x;
2969 : }
2970 :
2971 10 : void italic_corrected_node::ascii_print(ascii_output_file *out)
2972 : {
2973 10 : nodes->ascii_print(out);
2974 10 : }
2975 :
2976 23698 : int italic_corrected_node::ends_sentence()
2977 : {
2978 23698 : return nodes->ends_sentence();
2979 : }
2980 :
2981 0 : bool italic_corrected_node::overlaps_horizontally()
2982 : {
2983 0 : return nodes->overlaps_horizontally();
2984 : }
2985 :
2986 0 : bool italic_corrected_node::overlaps_vertically()
2987 : {
2988 0 : return nodes->overlaps_vertically();
2989 : }
2990 :
2991 0 : node *italic_corrected_node::last_char_node()
2992 : {
2993 0 : return nodes->last_char_node();
2994 : }
2995 :
2996 0 : tfont *italic_corrected_node::get_tfont()
2997 : {
2998 0 : return nodes->get_tfont();
2999 : }
3000 :
3001 668 : hyphenation_type italic_corrected_node::get_hyphenation_type()
3002 : {
3003 668 : return nodes->get_hyphenation_type();
3004 : }
3005 :
3006 668 : node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
3007 : {
3008 668 : nd = nodes->add_self(nd, p);
3009 668 : hunits not_interested;
3010 668 : nd = nd->add_italic_correction(¬_interested);
3011 668 : nodes = 0 /* nullptr */;
3012 668 : delete this;
3013 668 : return nd;
3014 : }
3015 :
3016 668 : hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail,
3017 : int *count)
3018 : {
3019 668 : return nodes->get_hyphen_list(tail, count);
3020 : }
3021 :
3022 484 : int italic_corrected_node::character_type()
3023 : {
3024 484 : return nodes->character_type();
3025 : }
3026 :
3027 : class break_char_node : public container_node {
3028 : // TODO: Figure out what a hyphenation code means in the UTF-8 future,
3029 : // where a "grochar" is a vector of NFD decomposed code points. Can
3030 : // it be a scalar--a 32-bit int?
3031 : unsigned char break_code;
3032 : unsigned char prev_break_code;
3033 : color *col;
3034 : public:
3035 : break_char_node(node *, int, int, color *, node * = 0 /* nullptr */);
3036 : break_char_node(node *, int, int, color *, statem *, int,
3037 : node * = 0 /* nullptr */);
3038 : node *copy();
3039 : hunits width();
3040 : vunits vertical_width();
3041 : node *last_char_node();
3042 : int character_type();
3043 : int ends_sentence();
3044 : node *add_self(node *, hyphen_list **);
3045 : hyphen_list *get_hyphen_list(hyphen_list *, int *);
3046 : void tprint(troff_output_file *);
3047 : void zero_width_tprint(troff_output_file *);
3048 : void ascii_print(ascii_output_file *);
3049 : void asciify(macro *);
3050 : hyphenation_type get_hyphenation_type();
3051 : bool overlaps_vertically();
3052 : bool overlaps_horizontally();
3053 : units size();
3054 : tfont *get_tfont();
3055 : bool is_same_as(node *);
3056 : const char *type();
3057 : bool causes_tprint();
3058 : bool is_tag();
3059 : unsigned char get_break_code();
3060 : void dump_properties();
3061 : };
3062 :
3063 137765 : break_char_node::break_char_node(node *n, int bc, int pbc, color *c,
3064 137765 : node *x)
3065 137765 : : container_node(x, n), break_code(bc), prev_break_code(pbc), col(c)
3066 : {
3067 137765 : }
3068 :
3069 4006 : break_char_node::break_char_node(node *n, int bc, int pbc, color *c,
3070 4006 : statem *s, int divlevel, node *x)
3071 : : container_node(x, s, divlevel, n), break_code(bc),
3072 4006 : prev_break_code(pbc), col(c)
3073 : {
3074 4006 : }
3075 :
3076 0 : void break_char_node::dump_properties()
3077 : {
3078 0 : node::dump_properties();
3079 0 : fprintf(stderr, ", \"break code before\": %d", break_code);
3080 0 : fprintf(stderr, ", \"break code after\": %d", prev_break_code);
3081 0 : fputs(", \"terminal_color\": ", stderr);
3082 0 : col->nm.json_dump();
3083 0 : fflush(stderr);
3084 0 : }
3085 :
3086 4006 : node *break_char_node::copy()
3087 : {
3088 4006 : return new break_char_node(nodes->copy(), break_code, prev_break_code,
3089 4006 : col, state, div_nest_level);
3090 : }
3091 :
3092 172340 : hunits break_char_node::width()
3093 : {
3094 172340 : return nodes->width();
3095 : }
3096 :
3097 5132 : vunits break_char_node::vertical_width()
3098 : {
3099 5132 : return nodes->vertical_width();
3100 : }
3101 :
3102 0 : node *break_char_node::last_char_node()
3103 : {
3104 0 : return nodes->last_char_node();
3105 : }
3106 :
3107 2199 : int break_char_node::character_type()
3108 : {
3109 2199 : return nodes->character_type();
3110 : }
3111 :
3112 134 : int break_char_node::ends_sentence()
3113 : {
3114 134 : return nodes->ends_sentence();
3115 : }
3116 :
3117 : // Keep these symbol names in sync with the superset used in an
3118 : // anonymous `enum` in "charinfo.h".
3119 : enum break_char_type {
3120 : ALLOWS_BREAK_BEFORE = 0x01,
3121 : ALLOWS_BREAK_AFTER = 0x02,
3122 : IGNORES_SURROUNDING_HYPHENATION_CODES = 0x04,
3123 : PROHIBITS_BREAK_BEFORE = 0x08,
3124 : PROHIBITS_BREAK_AFTER = 0x10,
3125 : IS_INTERWORD_SPACE = 0x20
3126 : };
3127 :
3128 917 : node *break_char_node::add_self(node *n, hyphen_list **p)
3129 : {
3130 917 : bool have_space_node = false;
3131 917 : assert(0U == (*p)->hyphenation_code);
3132 917 : if (break_code & ALLOWS_BREAK_BEFORE) {
3133 0 : if (((*p)->is_breakable)
3134 0 : || (break_code & IGNORES_SURROUNDING_HYPHENATION_CODES)) {
3135 0 : n = new space_node(H0, col, n);
3136 0 : n->freeze_space();
3137 0 : have_space_node = true;
3138 : }
3139 : }
3140 917 : if (!have_space_node) {
3141 917 : if ((prev_break_code & IS_INTERWORD_SPACE)
3142 915 : || (prev_break_code & PROHIBITS_BREAK_AFTER)) {
3143 2 : if (break_code & PROHIBITS_BREAK_BEFORE)
3144 : // stretchable zero-width space not implemented yet
3145 : ;
3146 : else {
3147 : // breakable, stretchable zero-width space not implemented yet
3148 2 : n = new space_node(H0, col, n);
3149 2 : n->freeze_space();
3150 : }
3151 : }
3152 : }
3153 917 : next = n;
3154 917 : n = this;
3155 917 : if (break_code & ALLOWS_BREAK_AFTER) {
3156 913 : if (((*p)->is_breakable)
3157 119 : || (break_code & IGNORES_SURROUNDING_HYPHENATION_CODES)) {
3158 794 : n = new space_node(H0, col, n);
3159 794 : n->freeze_space();
3160 : }
3161 : }
3162 917 : hyphen_list *pp = *p;
3163 917 : *p = (*p)->next;
3164 917 : delete pp;
3165 917 : return n;
3166 : }
3167 :
3168 917 : hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail, int *)
3169 : {
3170 917 : return new hyphen_list(0, tail);
3171 : }
3172 :
3173 917 : hyphenation_type break_char_node::get_hyphenation_type()
3174 : {
3175 917 : return HYPHENATION_PERMITTED;
3176 : }
3177 :
3178 10 : void break_char_node::ascii_print(ascii_output_file *ascii)
3179 : {
3180 10 : nodes->ascii_print(ascii);
3181 10 : }
3182 :
3183 0 : bool break_char_node::overlaps_vertically()
3184 : {
3185 0 : return nodes->overlaps_vertically();
3186 : }
3187 :
3188 0 : bool break_char_node::overlaps_horizontally()
3189 : {
3190 0 : return nodes->overlaps_horizontally();
3191 : }
3192 :
3193 0 : units break_char_node::size()
3194 : {
3195 0 : return nodes->size();
3196 : }
3197 :
3198 6 : tfont *break_char_node::get_tfont()
3199 : {
3200 6 : return nodes->get_tfont();
3201 : }
3202 :
3203 65 : node *extra_size_node::copy()
3204 : {
3205 65 : return new extra_size_node(n, state, div_nest_level);
3206 : }
3207 :
3208 65 : extra_size_node::extra_size_node(vunits i, statem *s, int divlevel)
3209 65 : : node(0 /* nullptr */, s, divlevel), n(i)
3210 : {
3211 65 : }
3212 :
3213 192 : extra_size_node::extra_size_node(vunits i)
3214 192 : : n(i)
3215 : {
3216 192 : }
3217 :
3218 0 : void extra_size_node::dump_properties()
3219 : {
3220 0 : node::dump_properties();
3221 0 : fprintf(stderr, ", \"vunits\": %d", n.to_units());
3222 0 : fflush(stderr);
3223 0 : }
3224 :
3225 3295228 : node *vertical_size_node::copy()
3226 : {
3227 3295228 : return new vertical_size_node(n, state, div_nest_level);
3228 : }
3229 :
3230 3295228 : vertical_size_node::vertical_size_node(vunits i, statem *s,
3231 3295228 : int divlevel)
3232 3295228 : : node(0 /* nullptr */, s, divlevel), n(i)
3233 : {
3234 3295228 : }
3235 :
3236 631832 : vertical_size_node::vertical_size_node(vunits i)
3237 631832 : : n(i)
3238 : {
3239 631832 : }
3240 :
3241 0 : void vertical_size_node::dump_properties()
3242 : {
3243 0 : node::dump_properties();
3244 0 : fprintf(stderr, ", \"vunits\": %d", n.to_units());
3245 0 : fflush(stderr);
3246 0 : }
3247 :
3248 0 : void hmotion_node::dump_properties()
3249 : {
3250 0 : node::dump_properties();
3251 0 : fprintf(stderr, ", \"hunits\": %d", n.to_units());
3252 0 : fprintf(stderr, ", \"was_tab\": %s", was_tab ? "true" : "false");
3253 0 : fprintf(stderr, ", \"unformat\": %s", unformat ? "true" : "false");
3254 0 : fputs(", \"terminal_color\": ", stderr);
3255 0 : col->nm.json_dump();
3256 0 : fflush(stderr);
3257 0 : }
3258 :
3259 1835120 : node *hmotion_node::copy()
3260 : {
3261 5505360 : return new hmotion_node(n, was_tab, unformat, col, state,
3262 1835120 : div_nest_level);
3263 : }
3264 :
3265 415 : node *space_char_hmotion_node::copy()
3266 : {
3267 415 : return new space_char_hmotion_node(n, col, state, div_nest_level);
3268 : }
3269 :
3270 104097 : vmotion_node::vmotion_node(vunits i, color *c)
3271 104097 : : n(i), col(c)
3272 : {
3273 104097 : }
3274 :
3275 220789 : vmotion_node::vmotion_node(vunits i, color *c, statem *s, int divlevel)
3276 220789 : : node(0 /* nullptr */, s, divlevel), n(i), col(c)
3277 : {
3278 220789 : }
3279 :
3280 0 : void vmotion_node::dump_properties()
3281 : {
3282 0 : node::dump_properties();
3283 0 : fprintf(stderr, ", \"vunits\": %d", n.to_units());
3284 0 : fputs(", \"terminal_color\": ", stderr);
3285 0 : col->nm.json_dump();
3286 0 : fflush(stderr);
3287 0 : }
3288 :
3289 220789 : node *vmotion_node::copy()
3290 : {
3291 220789 : return new vmotion_node(n, col, state, div_nest_level);
3292 : }
3293 :
3294 32474 : node *dummy_node::copy()
3295 : {
3296 32474 : return new dummy_node;
3297 : }
3298 :
3299 161958 : node *transparent_dummy_node::copy()
3300 : {
3301 161958 : return new transparent_dummy_node;
3302 : }
3303 :
3304 492 : hline_node::hline_node(hunits i, node *c, node *nxt)
3305 492 : : container_node(nxt, c), x(i)
3306 : {
3307 492 : }
3308 :
3309 614 : hline_node::hline_node(hunits i, node *c, statem *s, int divlevel,
3310 614 : node *nxt)
3311 614 : : container_node(nxt, s, divlevel, c), x(i)
3312 : {
3313 614 : }
3314 :
3315 0 : void hline_node::dump_properties()
3316 : {
3317 0 : node::dump_properties();
3318 0 : fprintf(stderr, ", \"hunits\": %d", x.to_units());
3319 0 : fflush(stderr);
3320 0 : }
3321 :
3322 614 : node *hline_node::copy()
3323 : {
3324 614 : return new hline_node(x, (nodes != 0 /* nullptr */) ? nodes->copy()
3325 : : 0 /* nullptr */,
3326 614 : state, div_nest_level);
3327 : }
3328 :
3329 651 : hunits hline_node::width()
3330 : {
3331 651 : return x < H0 ? H0 : x;
3332 : }
3333 :
3334 1 : vline_node::vline_node(vunits i, node *c, node *nxt)
3335 1 : : container_node(nxt, c), x(i)
3336 : {
3337 1 : }
3338 :
3339 2 : vline_node::vline_node(vunits i, node *c, statem *s,
3340 2 : int divlevel, node *nxt)
3341 2 : : container_node(nxt, s, divlevel, c), x(i)
3342 : {
3343 2 : }
3344 :
3345 0 : void vline_node::dump_properties()
3346 : {
3347 0 : node::dump_properties();
3348 0 : fprintf(stderr, ", \"vunits\": %d", x.to_units());
3349 0 : fflush(stderr);
3350 0 : }
3351 :
3352 2 : node *vline_node::copy()
3353 : {
3354 2 : return new vline_node(x, (nodes != 0 /* nullptr */) ? nodes->copy()
3355 : : 0 /* nullptr */,
3356 2 : state, div_nest_level);
3357 : }
3358 :
3359 3 : hunits vline_node::width()
3360 : {
3361 3 : return (0 /* nullptr */ == nodes) ? H0 : nodes->width();
3362 : }
3363 :
3364 253 : zero_width_node::zero_width_node(node *nd, statem *s, int divlevel)
3365 253 : : container_node(0 /* nullptr */, s, divlevel, nd)
3366 : {
3367 253 : }
3368 :
3369 929 : zero_width_node::zero_width_node(node *nd)
3370 929 : : container_node(nd)
3371 : {
3372 929 : }
3373 :
3374 253 : node *zero_width_node::copy()
3375 : {
3376 253 : return new zero_width_node(copy_node_list(nodes), state,
3377 253 : div_nest_level);
3378 : }
3379 :
3380 58 : int node_list_character_type(node *p)
3381 : {
3382 58 : int t = 0;
3383 379 : for (; p != 0 /* nullptr */; p = p->next)
3384 321 : t |= p->character_type();
3385 58 : return t;
3386 : }
3387 :
3388 58 : int zero_width_node::character_type()
3389 : {
3390 58 : return node_list_character_type(nodes);
3391 : }
3392 :
3393 1378 : void node_list_vertical_extent(node *p, vunits *min, vunits *max)
3394 : {
3395 1378 : *min = V0;
3396 1378 : *max = V0;
3397 1378 : vunits cur_vpos = V0;
3398 1378 : vunits v1, v2;
3399 5089 : for (; p != 0 /* nullptr */; p = p->next) {
3400 3711 : p->vertical_extent(&v1, &v2);
3401 3711 : v1 += cur_vpos;
3402 3711 : if (v1 < *min)
3403 501 : *min = v1;
3404 3711 : v2 += cur_vpos;
3405 3711 : if (v2 > *max)
3406 173 : *max = v2;
3407 3711 : cur_vpos += p->vertical_width();
3408 : }
3409 1378 : }
3410 :
3411 140 : void zero_width_node::vertical_extent(vunits *min, vunits *max)
3412 : {
3413 140 : node_list_vertical_extent(nodes, min, max);
3414 140 : }
3415 :
3416 110 : overstrike_node::overstrike_node()
3417 110 : : container_node(0 /* nullptr */), max_width(H0)
3418 : {
3419 110 : }
3420 :
3421 319 : overstrike_node::overstrike_node(statem *s, int divlevel)
3422 319 : : container_node(0 /* nullptr */, s, divlevel), max_width(H0)
3423 : {
3424 319 : }
3425 :
3426 0 : void overstrike_node::dump_properties()
3427 : {
3428 0 : node::dump_properties();
3429 0 : fprintf(stderr, ", \"max_width\": %d", max_width.to_units());
3430 0 : fflush(stderr);
3431 0 : }
3432 :
3433 319 : node *overstrike_node::copy()
3434 : {
3435 319 : overstrike_node *on = new overstrike_node(state, div_nest_level);
3436 957 : for (node *tem = nodes; tem != 0 /* nullptr */; tem = tem->next)
3437 638 : on->overstrike(tem->copy());
3438 319 : return on;
3439 : }
3440 :
3441 858 : void overstrike_node::overstrike(node *n)
3442 : {
3443 858 : if (0 /* nullptr */ == n)
3444 0 : return;
3445 858 : hunits w = n->width();
3446 858 : if (w > max_width)
3447 432 : max_width = w;
3448 : node **p;
3449 1287 : for (p = &nodes; *p != 0 /* nullptr */; p = &(*p)->next)
3450 : ;
3451 858 : n->next = 0 /* nullptr */;
3452 858 : *p = n;
3453 : }
3454 :
3455 328 : hunits overstrike_node::width()
3456 : {
3457 328 : return max_width;
3458 : }
3459 :
3460 1 : bracket_node::bracket_node()
3461 1 : : container_node(0 /* nullptr */), max_width(H0)
3462 : {
3463 1 : }
3464 :
3465 2 : bracket_node::bracket_node(statem *s, int divlevel)
3466 2 : : container_node(0 /* nullptr */, s, divlevel), max_width(H0)
3467 : {
3468 2 : }
3469 :
3470 0 : void bracket_node::dump_properties()
3471 : {
3472 0 : node::dump_properties();
3473 0 : fprintf(stderr, ", \"max_width\": %d", max_width.to_units());
3474 0 : fflush(stderr);
3475 0 : }
3476 :
3477 2 : node *bracket_node::copy()
3478 : {
3479 2 : bracket_node *on = new bracket_node(state, div_nest_level);
3480 2 : node *last_node = 0 /* nullptr */;
3481 : node *tem;
3482 2 : if (nodes != 0 /* nullptr */)
3483 2 : nodes->last = 0 /* nullptr */;
3484 8 : for (tem = nodes; tem != 0 /* nullptr */; tem = tem->next) {
3485 6 : if (tem->next)
3486 4 : tem->next->last = tem;
3487 6 : last_node = tem;
3488 : }
3489 8 : for (tem = last_node; tem != 0 /* nullptr */; tem = tem->last)
3490 6 : on->bracket(tem->copy());
3491 2 : return on;
3492 : }
3493 :
3494 9 : void bracket_node::bracket(node *n)
3495 : {
3496 9 : if (0 /* nullptr */ == n)
3497 0 : return;
3498 9 : hunits w = n->width();
3499 9 : if (w > max_width)
3500 3 : max_width = w;
3501 9 : n->next = nodes;
3502 9 : nodes = n;
3503 : }
3504 :
3505 3 : hunits bracket_node::width()
3506 : {
3507 3 : return max_width;
3508 : }
3509 :
3510 10518884 : int node::nspaces()
3511 : {
3512 10518884 : return 0;
3513 : }
3514 :
3515 742122 : bool node::did_space_merge(hunits, hunits, hunits)
3516 : {
3517 742122 : return false;
3518 : }
3519 :
3520 :
3521 849980 : space_node::space_node(hunits nn, color *c, node *p)
3522 : : node(p, 0 /* nullptr */, 0), n(nn), set('\0'),
3523 849980 : was_escape_colon(false), col(c)
3524 : {
3525 849980 : }
3526 :
3527 55717 : space_node::space_node(hunits nn, int s, int flag, color *c, statem *st,
3528 55717 : int divlevel, node *p)
3529 55717 : : node(p, st, divlevel), n(nn), set(s), was_escape_colon(flag), col(c)
3530 : {
3531 55717 : }
3532 :
3533 0 : void space_node::dump_properties()
3534 : {
3535 0 : node::dump_properties();
3536 0 : fprintf(stderr, ", \"hunits\": %d", n.to_units());
3537 0 : fprintf(stderr, ", \"undiscardable\": %s", set ? "true" : "false");
3538 0 : fprintf(stderr, ", \"is hyphenless breakpoint\": %s",
3539 0 : was_escape_colon ? "true" : "false");
3540 0 : fputs(", \"terminal_color\": ", stderr);
3541 0 : col->nm.json_dump();
3542 0 : fflush(stderr);
3543 0 : }
3544 :
3545 : #if 0
3546 : space_node::~space_node()
3547 : {
3548 : }
3549 : #endif
3550 :
3551 5892 : node *space_node::copy()
3552 : {
3553 17676 : return new space_node(n, set, was_escape_colon, col, state,
3554 5892 : div_nest_level);
3555 : }
3556 :
3557 80 : bool space_node::causes_tprint()
3558 : {
3559 80 : return false;
3560 : }
3561 :
3562 4955 : bool space_node::is_tag()
3563 : {
3564 4955 : return false;
3565 : }
3566 :
3567 1115240 : int space_node::nspaces()
3568 : {
3569 1115240 : return set ? 0 : 1;
3570 : }
3571 :
3572 102 : bool space_node::did_space_merge(hunits h, hunits, hunits)
3573 : {
3574 102 : n += h;
3575 102 : return true;
3576 : }
3577 :
3578 2511402 : hunits space_node::width()
3579 : {
3580 2511402 : return n;
3581 : }
3582 :
3583 2033873 : void node::spread_space(int*, hunits*)
3584 : {
3585 2033873 : }
3586 :
3587 377601 : void space_node::spread_space(int *n_spaces, hunits *desired_space)
3588 : {
3589 377601 : if (!set) {
3590 375173 : assert(*n_spaces > 0);
3591 375173 : if (*n_spaces == 1) {
3592 27867 : n += *desired_space;
3593 27867 : *desired_space = H0;
3594 : }
3595 : else {
3596 347306 : hunits extra = *desired_space / *n_spaces;
3597 347306 : *desired_space -= extra;
3598 347306 : n += extra;
3599 : }
3600 375173 : *n_spaces -= 1;
3601 375173 : set = true;
3602 : }
3603 377601 : }
3604 :
3605 7735027 : void node::freeze_space()
3606 : {
3607 7735027 : }
3608 :
3609 105973 : void space_node::freeze_space()
3610 : {
3611 105973 : set = true;
3612 105973 : }
3613 :
3614 0 : void node::is_escape_colon()
3615 : {
3616 0 : }
3617 :
3618 49441 : void space_node::is_escape_colon()
3619 : {
3620 49441 : was_escape_colon = true;
3621 49441 : }
3622 :
3623 526272 : diverted_space_node::diverted_space_node(vunits d, statem *s,
3624 526272 : int divlevel, node *p)
3625 526272 : : node(p, s, divlevel), n(d)
3626 : {
3627 526272 : }
3628 :
3629 526275 : diverted_space_node::diverted_space_node(vunits d, node *p)
3630 526275 : : node(p), n(d)
3631 : {
3632 526275 : }
3633 :
3634 0 : void diverted_space_node::dump_properties()
3635 : {
3636 0 : node::dump_properties();
3637 0 : fprintf(stderr, ", \"vunits\": %d", n.to_units());
3638 0 : fflush(stderr);
3639 0 : }
3640 :
3641 526272 : node *diverted_space_node::copy()
3642 : {
3643 526272 : return new diverted_space_node(n, state, div_nest_level);
3644 : }
3645 :
3646 3 : diverted_copy_file_node::diverted_copy_file_node(symbol s, statem *st,
3647 3 : int divlevel, node *p)
3648 3 : : node(p, st, divlevel), filename(s)
3649 : {
3650 3 : }
3651 :
3652 2 : diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
3653 2 : : node(p), filename(s)
3654 : {
3655 2 : }
3656 :
3657 0 : void diverted_copy_file_node::dump_properties()
3658 : {
3659 0 : node::dump_properties();
3660 0 : fputs(", \"filename\": ", stderr);
3661 0 : filename.json_dump();
3662 0 : fflush(stderr);
3663 0 : }
3664 :
3665 3 : node *diverted_copy_file_node::copy()
3666 : {
3667 3 : return new diverted_copy_file_node(filename, state, div_nest_level);
3668 : }
3669 :
3670 18699 : int node::ends_sentence()
3671 : {
3672 18699 : return 0;
3673 : }
3674 :
3675 20031 : int kern_pair_node::ends_sentence()
3676 : {
3677 20031 : switch (n2->ends_sentence()) {
3678 14210 : case 0:
3679 14210 : return 0;
3680 5785 : case 1:
3681 5785 : return 1;
3682 36 : case 2:
3683 36 : break;
3684 0 : default:
3685 0 : assert(0 == "unhandled case of sentence ending status");
3686 : }
3687 36 : return n1->ends_sentence();
3688 : }
3689 :
3690 486309 : int node_list_ends_sentence(node *n)
3691 : {
3692 486309 : for (; n != 0 /* nullptr */; n = n->next)
3693 486245 : switch (n->ends_sentence()) {
3694 335193 : case 0:
3695 335193 : return 0;
3696 81582 : case 1:
3697 81582 : return 1;
3698 69470 : case 2:
3699 69470 : break;
3700 0 : default:
3701 0 : assert(0 == "unhandled case of sentence ending status");
3702 : }
3703 64 : return 2;
3704 : }
3705 :
3706 0 : int dbreak_node::ends_sentence()
3707 : {
3708 0 : return node_list_ends_sentence(none);
3709 : }
3710 :
3711 0 : bool node::overlaps_horizontally()
3712 : {
3713 0 : return false;
3714 : }
3715 :
3716 0 : bool node::overlaps_vertically()
3717 : {
3718 0 : return false;
3719 : }
3720 :
3721 1051971 : bool node::discardable()
3722 : {
3723 1051971 : return false;
3724 : }
3725 :
3726 97741 : bool space_node::discardable()
3727 : {
3728 97741 : return !set;
3729 : }
3730 :
3731 293920 : vunits node::vertical_width()
3732 : {
3733 293920 : return V0;
3734 : }
3735 :
3736 0 : vunits vline_node::vertical_width()
3737 : {
3738 0 : return x;
3739 : }
3740 :
3741 464 : vunits vmotion_node::vertical_width()
3742 : {
3743 464 : return n;
3744 : }
3745 :
3746 45506 : bool node::set_unformat_flag()
3747 : {
3748 45506 : return true;
3749 : }
3750 :
3751 53242 : int node::character_type()
3752 : {
3753 53242 : return 0;
3754 : }
3755 :
3756 4565 : hunits node::subscript_correction()
3757 : {
3758 4565 : return H0;
3759 : }
3760 :
3761 1132 : hunits node::italic_correction()
3762 : {
3763 1132 : return H0;
3764 : }
3765 :
3766 1649 : hunits node::left_italic_correction()
3767 : {
3768 1649 : return H0;
3769 : }
3770 :
3771 4822 : hunits node::skew()
3772 : {
3773 4822 : return H0;
3774 : }
3775 :
3776 : /* vertical_extent methods */
3777 :
3778 52318 : void node::vertical_extent(vunits *min, vunits *max)
3779 : {
3780 52318 : vunits v = vertical_width();
3781 52318 : if (v < V0) {
3782 152 : *min = v;
3783 152 : *max = V0;
3784 : }
3785 : else {
3786 52166 : *max = v;
3787 52166 : *min = V0;
3788 : }
3789 52318 : }
3790 :
3791 0 : void vline_node::vertical_extent(vunits *min, vunits *max)
3792 : {
3793 0 : if (0 /* nullptr */ == nodes)
3794 0 : node::vertical_extent(min, max);
3795 : else {
3796 0 : vunits cmin, cmax;
3797 0 : nodes->vertical_extent(&cmin, &cmax);
3798 0 : vunits h = nodes->size();
3799 0 : if (x < V0) {
3800 0 : if (-x < h) {
3801 0 : *min = x;
3802 0 : *max = V0;
3803 : }
3804 : else {
3805 : // we print the first character and then move up, so
3806 0 : *max = cmax;
3807 : // we print the last character and then move up h
3808 0 : *min = cmin + h;
3809 0 : if (*min > V0)
3810 0 : *min = V0;
3811 0 : *min += x;
3812 : }
3813 : }
3814 : else {
3815 0 : if (x < h) {
3816 0 : *max = x;
3817 0 : *min = V0;
3818 : }
3819 : else {
3820 : // we move down by h and then print the first character, so
3821 0 : *min = cmin + h;
3822 0 : if (*min > V0)
3823 0 : *min = V0;
3824 0 : *max = x + cmax;
3825 : }
3826 : }
3827 : }
3828 0 : }
3829 :
3830 : // `ascii_print()` represents a node for `troff -a`
3831 :
3832 1 : static void ascii_print_node_list(ascii_output_file *ascii, node *n)
3833 : {
3834 1 : node *nn = n;
3835 2 : while(nn != 0 /* nullptr */) {
3836 1 : nn->ascii_print(ascii);
3837 1 : nn = nn->next;
3838 : }
3839 1 : }
3840 :
3841 45 : static void ascii_print_reverse_node_list(ascii_output_file *ascii,
3842 : node *n)
3843 : {
3844 45 : if (0 /* nullptr */ == n)
3845 10 : return;
3846 35 : ascii_print_reverse_node_list(ascii, n->next);
3847 35 : n->ascii_print(ascii);
3848 : }
3849 :
3850 0 : void bracket_node::ascii_print(ascii_output_file *ascii)
3851 : {
3852 0 : ascii_print_reverse_node_list(ascii, nodes);
3853 0 : }
3854 :
3855 3 : void dbreak_node::ascii_print(ascii_output_file *ascii)
3856 : {
3857 3 : ascii_print_reverse_node_list(ascii, none);
3858 3 : }
3859 :
3860 15 : void kern_pair_node::ascii_print(ascii_output_file *ascii)
3861 : {
3862 15 : n1->ascii_print(ascii);
3863 15 : n2->ascii_print(ascii);
3864 15 : }
3865 :
3866 116 : void node::ascii_print(ascii_output_file *)
3867 : {
3868 116 : }
3869 :
3870 0 : void overstrike_node::ascii_print(ascii_output_file *ascii)
3871 : {
3872 0 : ascii_print_node_list(ascii, nodes);
3873 0 : }
3874 :
3875 120 : void space_node::ascii_print(ascii_output_file *ascii)
3876 : {
3877 120 : if (!n.is_zero())
3878 120 : ascii->outc(' ');
3879 120 : }
3880 :
3881 49 : void hmotion_node::ascii_print(ascii_output_file *ascii)
3882 : {
3883 : // this is pretty arbitrary
3884 49 : if (n >= points_to_units(2))
3885 42 : ascii->outc(' ');
3886 49 : }
3887 :
3888 2 : void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
3889 : {
3890 2 : ascii->outc(' ');
3891 2 : }
3892 :
3893 1 : void zero_width_node::ascii_print(ascii_output_file *out)
3894 : {
3895 1 : ascii_print_node_list(out, nodes);
3896 1 : }
3897 :
3898 : // `asciify()` extracts the simple character content of a node; this is
3899 : // a plain text (but not necessarily plain "ASCII") representation
3900 : // suitable for storage in a groff string or embedding in a device
3901 : // extension command escape sequence (as for PDF metadata).
3902 :
3903 114 : void glyph_node::asciify(macro *m)
3904 : {
3905 114 : if (!is_output_supressed) {
3906 109 : unsigned char c = ci->get_asciify_code();
3907 109 : if (c != 0U)
3908 2 : m->append(c);
3909 : else {
3910 107 : c = ci->get_ascii_code();
3911 107 : if (c != 0U)
3912 104 : m->append(c);
3913 : else {
3914 : // Also see input.cpp::charinfo::dump().
3915 3 : int unicode_mapping = ci->get_unicode_mapping();
3916 3 : if (unicode_mapping >= 0) {
3917 : // We must write out an escape sequence. Use the default
3918 : // escape character. TODO: Make `escape_char` global?
3919 : //
3920 : // First, handle the Basic Latin characters that don't map to
3921 : // themselves.
3922 3 : switch (unicode_mapping) {
3923 1 : case 34:
3924 1 : m->append_str("\\[dq]");
3925 1 : break;
3926 0 : case 39:
3927 0 : m->append_str("\\[aq]");
3928 0 : break;
3929 0 : case 45:
3930 0 : m->append_str("\\[-]");
3931 0 : break;
3932 0 : case 92:
3933 0 : m->append_str("\\[rs]");
3934 0 : break;
3935 0 : case 94:
3936 0 : m->append_str("\\[ha]");
3937 0 : break;
3938 0 : case 96:
3939 0 : m->append_str("\\[ga]");
3940 0 : break;
3941 0 : case 126:
3942 0 : m->append_str("\\[ti]");
3943 0 : break;
3944 2 : default:
3945 2 : m->append_str("\\[u");
3946 2 : const size_t buflen = sizeof "10FFFF";
3947 : char hexbuf[buflen];
3948 2 : (void) memset(hexbuf, '\0', buflen);
3949 2 : (void) snprintf(hexbuf, buflen, "%.4X", unicode_mapping);
3950 2 : m->append_str(hexbuf);
3951 2 : m->append(']');
3952 2 : break;
3953 : }
3954 : }
3955 : else {
3956 0 : error("unable to asciify glyph; charinfo data follows");
3957 : // This is garrulous as hell, but by the time we have hold of
3958 : // a glyph's charinfo, it no longer has a "name"--it's already
3959 : // been looked up in the dictionary. (Also, multiple names
3960 : // can refer to the same charinfo datum.) And this racket
3961 : // beats telling the user nothing at all about the glyph: if
3962 : // the character was defined by request (`char` et al.), this
3963 : // dump reports the file name and line number of that request.
3964 0 : ci->dump();
3965 : }
3966 : }
3967 : }
3968 : }
3969 114 : }
3970 :
3971 1 : void kern_pair_node::asciify(macro *m)
3972 : {
3973 1 : if (!is_output_supressed) {
3974 1 : if (n1 != 0 /* nullptr */)
3975 1 : n1->asciify(m);
3976 1 : if (n2 != 0 /* nullptr */)
3977 1 : n2->asciify(m);
3978 : }
3979 1 : }
3980 :
3981 0 : void dbreak_node::asciify(macro *m)
3982 : {
3983 0 : assert(m != 0 /* nullptr */);
3984 0 : if (!is_output_supressed) {
3985 0 : if (m != 0 /* nullptr */)
3986 0 : none->asciify(m);
3987 0 : none = 0 /* nullptr */;
3988 : }
3989 0 : }
3990 :
3991 0 : void ligature_node::asciify(macro *m)
3992 : {
3993 0 : assert(n1 != 0 /* nullptr */);
3994 0 : assert(n2 != 0 /* nullptr */);
3995 0 : if (!is_output_supressed) {
3996 0 : if (n1 != 0 /* nullptr */)
3997 0 : n1->asciify(m);
3998 0 : if (n2 != 0 /* nullptr */)
3999 0 : n2->asciify(m);
4000 : }
4001 0 : }
4002 :
4003 0 : void break_char_node::asciify(macro *m)
4004 : {
4005 0 : assert(nodes != 0 /* nullptr */);
4006 0 : if (!is_output_supressed && (nodes != 0 /* nullptr */))
4007 0 : nodes->asciify(m);
4008 0 : nodes = 0 /* nullptr */;
4009 0 : }
4010 :
4011 0 : void italic_corrected_node::asciify(macro *m)
4012 : {
4013 0 : assert(nodes != 0 /* nullptr */);
4014 0 : if (!is_output_supressed && (nodes != 0 /* nullptr */))
4015 0 : nodes->asciify(m);
4016 0 : nodes = 0 /* nullptr */;
4017 0 : }
4018 :
4019 0 : void left_italic_corrected_node::asciify(macro *m)
4020 : {
4021 0 : assert(nodes != 0 /* nullptr */);
4022 0 : if (!is_output_supressed && (nodes != 0 /* nullptr */))
4023 0 : nodes->asciify(m);
4024 0 : nodes = 0 /* nullptr */;
4025 0 : }
4026 :
4027 3 : void hmotion_node::asciify(macro *)
4028 : {
4029 3 : }
4030 :
4031 415 : space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
4032 : statem *s,
4033 : int divlevel,
4034 415 : node *nxt)
4035 415 : : hmotion_node(i, c, s, divlevel, nxt)
4036 : {
4037 415 : }
4038 :
4039 5777 : space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
4040 5777 : node *nxt)
4041 5777 : : hmotion_node(i, c, 0 /* nullptr */, 0, nxt)
4042 : {
4043 5777 : }
4044 :
4045 1 : void space_char_hmotion_node::asciify(macro *m)
4046 : {
4047 1 : if (!is_output_supressed)
4048 1 : m->append(' ');
4049 1 : }
4050 :
4051 1 : void space_node::asciify(macro *)
4052 : {
4053 1 : }
4054 :
4055 7 : void word_space_node::asciify(macro *m)
4056 : {
4057 7 : if (!is_output_supressed) {
4058 14 : for (width_list *w = orig_width; w != 0 /* nullptr */; w = w->next)
4059 7 : m->append(' ');
4060 : }
4061 7 : }
4062 :
4063 1 : void unbreakable_space_node::asciify(macro *m)
4064 : {
4065 1 : if (!is_output_supressed)
4066 1 : m->append(' ');
4067 1 : }
4068 :
4069 5 : void line_start_node::asciify(macro *)
4070 : {
4071 5 : }
4072 :
4073 10 : void vertical_size_node::asciify(macro *)
4074 : {
4075 10 : }
4076 :
4077 2 : void dummy_node::asciify(macro *)
4078 : {
4079 2 : }
4080 :
4081 9 : void transparent_dummy_node::asciify(macro *)
4082 : {
4083 9 : }
4084 :
4085 1 : void tag_node::asciify(macro *)
4086 : {
4087 1 : }
4088 :
4089 1 : void device_extension_node::asciify(macro *)
4090 : {
4091 1 : }
4092 :
4093 1 : void vmotion_node::asciify(macro *)
4094 : {
4095 1 : }
4096 :
4097 1 : void bracket_node::asciify(macro *)
4098 : {
4099 1 : }
4100 :
4101 1 : void diverted_copy_file_node::asciify(macro *)
4102 : {
4103 1 : }
4104 :
4105 1 : void diverted_space_node::asciify(macro *)
4106 : {
4107 1 : }
4108 :
4109 1 : void draw_node::asciify(macro *)
4110 : {
4111 1 : }
4112 :
4113 1 : void extra_size_node::asciify(macro *)
4114 : {
4115 1 : }
4116 :
4117 3 : void hline_node::asciify(macro *)
4118 : {
4119 3 : }
4120 :
4121 1 : void hyphen_inhibitor_node::asciify(macro *)
4122 : {
4123 1 : }
4124 :
4125 1 : void overstrike_node::asciify(macro *)
4126 : {
4127 1 : }
4128 :
4129 2 : void suppress_node::asciify(macro *)
4130 : {
4131 2 : is_output_supressed = (is_on == 0); // it's a three-valued Boolean :-/
4132 2 : }
4133 :
4134 1 : void vline_node::asciify(macro *)
4135 : {
4136 1 : }
4137 :
4138 : // We probably would asciify zero-width nodes as nothing, but they're
4139 : // used internally to represent some forms of combining character, as
4140 : // with \[u015E] -> S<ac>.
4141 1 : void zero_width_node::asciify(macro *m)
4142 : {
4143 1 : assert(nodes != 0 /* nullptr */);
4144 1 : if (!is_output_supressed) {
4145 1 : node *n = nodes;
4146 9 : while (n != 0 /* nullptr */) {
4147 8 : n->asciify(m);
4148 8 : n = n->next;
4149 : }
4150 1 : nodes = 0 /* nullptr */;
4151 : }
4152 1 : }
4153 :
4154 188905 : breakpoint *node::get_breakpoints(hunits /* width */, int /* nspaces */,
4155 : breakpoint *rest, bool /* is_inner */)
4156 : {
4157 188905 : return rest;
4158 : }
4159 :
4160 13 : int node::nbreaks()
4161 : {
4162 13 : return 0;
4163 : }
4164 :
4165 55358 : breakpoint *space_node::get_breakpoints(hunits wd, int ns,
4166 : breakpoint *rest, bool is_inner)
4167 : {
4168 55358 : if (next && next->discardable())
4169 1 : return rest;
4170 55357 : breakpoint *bp = new breakpoint;
4171 55357 : bp->next = rest;
4172 55357 : bp->width = wd;
4173 55357 : bp->nspaces = ns;
4174 55357 : bp->hyphenated = 0;
4175 55357 : if (is_inner) {
4176 0 : assert(rest != 0);
4177 0 : bp->index = rest->index + 1;
4178 0 : bp->nd = rest->nd;
4179 : }
4180 : else {
4181 55357 : bp->nd = this;
4182 55357 : bp->index = 0;
4183 : }
4184 55357 : return bp;
4185 : }
4186 :
4187 0 : int space_node::nbreaks()
4188 : {
4189 0 : if (next && next->discardable())
4190 0 : return 0;
4191 : else
4192 0 : return 1;
4193 : }
4194 :
4195 37910 : static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
4196 : int ns, breakpoint *rest)
4197 : {
4198 37910 : if (p != 0 /* nullptr */) {
4199 18955 : rest = p->get_breakpoints(*widthp,
4200 : ns,
4201 : node_list_get_breakpoints(p->next, widthp,
4202 : ns, rest),
4203 18955 : true);
4204 18955 : *widthp += p->width();
4205 : }
4206 37910 : return rest;
4207 : }
4208 :
4209 18955 : breakpoint *dbreak_node::get_breakpoints(hunits wd, int ns,
4210 : breakpoint *rest,
4211 : bool is_inner)
4212 : {
4213 18955 : breakpoint *bp = new breakpoint;
4214 18955 : bp->next = rest;
4215 18955 : bp->width = wd;
4216 55688 : for (node *tem = pre; tem != 0 /* nullptr */; tem = tem->next)
4217 36733 : bp->width += tem->width();
4218 18955 : bp->nspaces = ns;
4219 18955 : bp->hyphenated = 1;
4220 18955 : if (is_inner) {
4221 63 : assert(rest != 0);
4222 63 : bp->index = rest->index + 1;
4223 63 : bp->nd = rest->nd;
4224 : }
4225 : else {
4226 18892 : bp->nd = this;
4227 18892 : bp->index = 0;
4228 : }
4229 18955 : return node_list_get_breakpoints(none, &wd, ns, bp);
4230 : }
4231 :
4232 13 : int dbreak_node::nbreaks()
4233 : {
4234 13 : int i = 1;
4235 26 : for (node *tem = none; tem != 0 /* nullptr */; tem = tem->next)
4236 13 : i += tem->nbreaks();
4237 13 : return i;
4238 : }
4239 :
4240 0 : void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
4241 : {
4242 0 : assert(0 == "node::split() unimplemented");
4243 : }
4244 :
4245 24081 : void space_node::split(int where, node **pre, node **post)
4246 : {
4247 24081 : assert(0 == where);
4248 24081 : *pre = next;
4249 24081 : *post = 0 /* nullptr */;
4250 24081 : delete this;
4251 24081 : }
4252 :
4253 26 : static void node_list_split(node *p, int *wherep,
4254 : node **prep, node **postp)
4255 : {
4256 26 : if (0 /* nullptr */ == p)
4257 13 : return;
4258 13 : int nb = p->nbreaks();
4259 13 : node_list_split(p->next, wherep, prep, postp);
4260 13 : if (*wherep < 0) {
4261 0 : p->next = *postp;
4262 0 : *postp = p;
4263 : }
4264 13 : else if (*wherep < nb) {
4265 13 : p->next = *prep;
4266 13 : p->split(*wherep, prep, postp);
4267 : }
4268 : else {
4269 0 : p->next = *prep;
4270 0 : *prep = p;
4271 : }
4272 13 : *wherep -= nb;
4273 : }
4274 :
4275 6796 : void dbreak_node::split(int where, node **prep, node **postp)
4276 : {
4277 6796 : assert(where >= 0);
4278 6796 : if (0 == where) {
4279 6783 : *postp = post;
4280 6783 : post = 0 /* nullptr */;
4281 6783 : if (0 /* nullptr */ == pre)
4282 0 : *prep = next;
4283 : else {
4284 : node *tem;
4285 13154 : for (tem = pre; tem->next != 0 /* nullptr */; tem = tem->next)
4286 : ;
4287 6783 : tem->next = next;
4288 6783 : *prep = pre;
4289 : }
4290 6783 : pre = 0 /* nullptr */;
4291 6783 : delete this;
4292 : }
4293 : else {
4294 13 : *prep = next;
4295 13 : where -= 1;
4296 13 : node_list_split(none, &where, prep, postp);
4297 13 : none = 0 /* nullptr */;
4298 13 : delete this;
4299 : }
4300 6796 : }
4301 :
4302 : // TODO: Make this member function pure virtual to force consideration
4303 : // of this question for each node type.
4304 74 : hyphenation_type node::get_hyphenation_type()
4305 : {
4306 74 : return HYPHENATION_UNNECESSARY;
4307 : }
4308 :
4309 238 : hyphenation_type dbreak_node::get_hyphenation_type()
4310 : {
4311 238 : return HYPHENATION_INHIBITED;
4312 : }
4313 :
4314 4082 : hyphenation_type kern_pair_node::get_hyphenation_type()
4315 : {
4316 4082 : return HYPHENATION_PERMITTED;
4317 : }
4318 :
4319 1959 : hyphenation_type dummy_node::get_hyphenation_type()
4320 : {
4321 1959 : return HYPHENATION_PERMITTED;
4322 : }
4323 :
4324 1726 : hyphenation_type transparent_dummy_node::get_hyphenation_type()
4325 : {
4326 1726 : return HYPHENATION_PERMITTED;
4327 : }
4328 :
4329 90 : hyphenation_type hmotion_node::get_hyphenation_type()
4330 : {
4331 90 : return HYPHENATION_PERMITTED;
4332 : }
4333 :
4334 8 : hyphenation_type space_char_hmotion_node::get_hyphenation_type()
4335 : {
4336 8 : return HYPHENATION_PERMITTED;
4337 : }
4338 :
4339 0 : hyphenation_type overstrike_node::get_hyphenation_type()
4340 : {
4341 0 : return HYPHENATION_PERMITTED;
4342 : }
4343 :
4344 90935 : hyphenation_type space_node::get_hyphenation_type()
4345 : {
4346 90935 : if (was_escape_colon)
4347 322 : return HYPHENATION_PERMITTED;
4348 90613 : return HYPHENATION_UNNECESSARY;
4349 : }
4350 :
4351 363 : hyphenation_type unbreakable_space_node::get_hyphenation_type()
4352 : {
4353 363 : return HYPHENATION_PERMITTED;
4354 : }
4355 :
4356 7788494 : bool node::interpret(macro *)
4357 : {
4358 7788494 : return false;
4359 : }
4360 :
4361 71087 : device_extension_node::device_extension_node(const macro &m, bool b)
4362 : : node(0 /* nullptr */, 0 /* nullptr */, 0, true), mac(m),
4363 71087 : lacks_command_prefix(b)
4364 : {
4365 71087 : font_size fs = curenv->get_font_size();
4366 71087 : int char_height = curenv->get_char_height();
4367 71087 : int char_slant = curenv->get_char_slant();
4368 71087 : int fontno = env_resolve_font(curenv);
4369 71087 : tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
4370 : fontno);
4371 71087 : if (curenv->is_composite())
4372 16 : tf = tf->get_plain();
4373 71087 : gcol = curenv->get_stroke_color();
4374 71087 : fcol = curenv->get_fill_color();
4375 71087 : }
4376 :
4377 133688 : device_extension_node::device_extension_node(const macro &m, tfont *t,
4378 : color *gc, color *fc,
4379 : statem *s, int divlevel,
4380 133688 : bool b)
4381 : : node(0 /* nullptr */, s, divlevel, true), mac(m), tf(t), gcol(gc),
4382 133688 : fcol(fc), lacks_command_prefix(b)
4383 : {
4384 133688 : }
4385 :
4386 0 : void device_extension_node::dump_properties()
4387 : {
4388 0 : node::dump_properties();
4389 0 : fputs(", \"macro\": ", stderr);
4390 0 : mac.json_dump();
4391 0 : fputs(", \"tfont\": ", stderr);
4392 0 : tf->get_name().json_dump();
4393 0 : fputs(", \"stroke_color\": ", stderr);
4394 0 : gcol->nm.json_dump();
4395 0 : fputs(", \"fill_color\": ", stderr);
4396 0 : fcol->nm.json_dump();
4397 0 : fflush(stderr);
4398 0 : }
4399 :
4400 0 : bool device_extension_node::is_same_as(node *n)
4401 : {
4402 0 : return ((mac == static_cast<device_extension_node *>(n)->mac)
4403 0 : && (tf == static_cast<device_extension_node *>(n)->tf)
4404 0 : && (gcol == static_cast<device_extension_node *>(n)->gcol)
4405 0 : && (fcol == static_cast<device_extension_node *>(n)->fcol)
4406 0 : && (lacks_command_prefix
4407 : == static_cast<device_extension_node *>(n)
4408 0 : ->lacks_command_prefix));
4409 : }
4410 :
4411 0 : const char *device_extension_node::type()
4412 : {
4413 0 : return "device extension command node";
4414 : }
4415 :
4416 1618 : int device_extension_node::ends_sentence()
4417 : {
4418 1618 : return 2;
4419 : }
4420 :
4421 0 : bool device_extension_node::causes_tprint()
4422 : {
4423 0 : return false;
4424 : }
4425 :
4426 451 : hyphenation_type device_extension_node::get_hyphenation_type()
4427 : {
4428 451 : return HYPHENATION_PERMITTED;
4429 : }
4430 :
4431 66666 : bool device_extension_node::is_tag()
4432 : {
4433 66666 : return false;
4434 : }
4435 :
4436 133688 : node *device_extension_node::copy()
4437 : {
4438 133688 : return new device_extension_node(mac, tf, gcol, fcol, state,
4439 : div_nest_level,
4440 133688 : lacks_command_prefix);
4441 : }
4442 :
4443 66977 : void device_extension_node::tprint_start(troff_output_file *out)
4444 : {
4445 66977 : out->start_device_extension(tf, gcol, fcol, lacks_command_prefix);
4446 66977 : }
4447 :
4448 1516445 : void device_extension_node::tprint_char(troff_output_file *out,
4449 : unsigned char c)
4450 : {
4451 1516445 : out->write_device_extension_char(c);
4452 1516445 : }
4453 :
4454 66977 : void device_extension_node::tprint_end(troff_output_file *out)
4455 : {
4456 66977 : out->end_device_extension();
4457 66977 : }
4458 :
4459 0 : tfont *device_extension_node::get_tfont()
4460 : {
4461 0 : return tf;
4462 : }
4463 :
4464 : /* suppress_node */
4465 :
4466 370 : suppress_node::suppress_node(int on_or_off, int issue_limits)
4467 370 : : is_on(on_or_off), emit_limits(issue_limits), filename(0), position(0),
4468 370 : image_id(0)
4469 : {
4470 370 : }
4471 :
4472 117 : suppress_node::suppress_node(symbol f, char p, int id)
4473 : : node(0 /* nullptr */, 0 /* nullptr */, 0, true), is_on(2),
4474 117 : emit_limits(false), filename(f), position(p), image_id(id)
4475 : {
4476 117 : }
4477 :
4478 471 : suppress_node::suppress_node(int issue_limits, int on_or_off,
4479 : symbol f, char p, int id,
4480 471 : statem *s, int divlevel)
4481 : : node(0 /* nullptr */, s, divlevel), is_on(on_or_off),
4482 471 : emit_limits(issue_limits), filename(f), position(p), image_id(id)
4483 : {
4484 471 : }
4485 :
4486 0 : void suppress_node::dump_properties()
4487 : {
4488 0 : node::dump_properties();
4489 0 : fprintf(stderr, ", \"is_on\": %d", is_on);
4490 0 : fprintf(stderr, ", \"emit_limits\": %s",
4491 0 : emit_limits ? "true" : "false");
4492 0 : if (filename.contents() != 0 /* nullptr */) {
4493 0 : fputs(", \"filename\": ", stderr);
4494 0 : filename.json_dump();
4495 : }
4496 0 : fputs(", \"position\": \"", stderr);
4497 0 : json_char jc = json_encode_char(position);
4498 : // Write out its JSON representation by character by character to
4499 : // keep libc string functions from interpreting C escape sequences.
4500 0 : for (size_t i = 0; i < jc.len; i++)
4501 0 : fputc(jc.buf[i], stderr);
4502 0 : fputc('\"', stderr);
4503 0 : fprintf(stderr, ", \"image_id\": %d", image_id);
4504 0 : fflush(stderr);
4505 0 : }
4506 :
4507 0 : bool suppress_node::is_same_as(node *n)
4508 : {
4509 0 : return ((is_on == static_cast<suppress_node *>(n)->is_on)
4510 0 : && (emit_limits == static_cast<suppress_node *>(n)->emit_limits)
4511 0 : && (filename == static_cast<suppress_node *>(n)->filename)
4512 0 : && (position == static_cast<suppress_node *>(n)->position)
4513 0 : && (image_id == static_cast<suppress_node *>(n)->image_id));
4514 : }
4515 :
4516 0 : const char *suppress_node::type()
4517 : {
4518 0 : return "suppressed output node";
4519 : }
4520 :
4521 471 : node *suppress_node::copy()
4522 : {
4523 1413 : return new suppress_node(emit_limits, is_on, filename, position,
4524 471 : image_id, state, div_nest_level);
4525 : }
4526 :
4527 : /* tag_node */
4528 :
4529 0 : tag_node::tag_node()
4530 0 : : node(0 /* nullptr */, 0 /* nullptr */, 0, true), delayed(false)
4531 : {
4532 0 : }
4533 :
4534 16214 : tag_node::tag_node(string s, int delay)
4535 16214 : : tag_string(s), delayed(delay)
4536 : {
4537 16214 : is_special = !delay;
4538 16214 : }
4539 :
4540 108 : tag_node::tag_node(string s, statem *st, int divlevel, int delay)
4541 108 : : node(0 /* nullptr */, st, divlevel), tag_string(s), delayed(delay)
4542 : {
4543 108 : is_special = !delay;
4544 108 : }
4545 :
4546 0 : void tag_node::dump_properties()
4547 : {
4548 0 : node::dump_properties();
4549 0 : fputs(", \"string\": ", stderr);
4550 0 : tag_string.json_dump();
4551 0 : fprintf(stderr, ", \"delayed\": %s", delayed ? "true" : "false");
4552 0 : fflush(stderr);
4553 0 : }
4554 :
4555 108 : node *tag_node::copy()
4556 : {
4557 108 : return new tag_node(tag_string, state, div_nest_level, delayed);
4558 : }
4559 :
4560 11208 : void tag_node::tprint(troff_output_file *out)
4561 : {
4562 11208 : if (delayed)
4563 50 : out->add_to_tag_list(tag_string);
4564 : else
4565 11158 : out->state.add_tag(out->fp, tag_string);
4566 11208 : }
4567 :
4568 0 : bool tag_node::is_same_as(node *nd)
4569 : {
4570 0 : return ((tag_string == static_cast<tag_node *>(nd)->tag_string)
4571 0 : && (delayed == static_cast<tag_node *>(nd)->delayed));
4572 : }
4573 :
4574 0 : const char *tag_node::type()
4575 : {
4576 0 : return "tag node";
4577 : }
4578 :
4579 265 : bool tag_node::causes_tprint()
4580 : {
4581 265 : return !delayed;
4582 : }
4583 :
4584 10957 : bool tag_node::is_tag()
4585 : {
4586 10957 : return !delayed;
4587 : }
4588 :
4589 6 : int tag_node::ends_sentence()
4590 : {
4591 6 : return 2;
4592 : }
4593 :
4594 : // Get contents of register `p` as integer.
4595 : // Used only by suppress_node::tprint().
4596 354 : static int get_register(const char *p)
4597 : {
4598 354 : assert(p != 0 /* nullptr */);
4599 354 : reg *r = static_cast<reg *>(register_dictionary.lookup(p));
4600 354 : assert(r != 0 /* nullptr */);
4601 : units value;
4602 354 : assert(r->get_value(&value));
4603 354 : return int(value);
4604 : }
4605 :
4606 : // Get contents of register `p` as string.
4607 : // Used only by suppress_node::tprint().
4608 118 : static const char *get_string(const char *p)
4609 : {
4610 118 : assert(p != 0 /* nullptr */);
4611 118 : reg *r = static_cast<reg *>(register_dictionary.lookup(p));
4612 118 : assert(r != 0 /* nullptr */);
4613 118 : return r->get_string();
4614 : }
4615 :
4616 174 : void suppress_node::put(troff_output_file *out, const char *s)
4617 : {
4618 174 : int i = 0;
4619 3374 : while (s[i] != '\0') {
4620 3200 : out->write_device_extension_char(s[i]);
4621 3200 : i++;
4622 : }
4623 174 : }
4624 :
4625 : /*
4626 : * We need to remember the start of the image and its name (\O5). But
4627 : * we won't always need this information; for instance, \O2 is used to
4628 : * produce a bounding box with no associated image or position thereof.
4629 : */
4630 :
4631 : static char last_position = 'i';
4632 : static const char *image_filename = "";
4633 : static size_t image_filename_len = 0;
4634 : static int subimage_counter = 0;
4635 :
4636 : /*
4637 : * tprint - if (is_on == 2)
4638 : * remember current position (l, r, c, i) and filename
4639 : * else
4640 : * if (emit_limits)
4641 : * if (html)
4642 : * emit image tag
4643 : * else
4644 : * emit postscript bounds for image
4645 : * else
4646 : * if (suppress boolean differs from current state)
4647 : * alter state
4648 : * reset registers
4649 : * record current page
4650 : * set low water mark.
4651 : */
4652 :
4653 485 : void suppress_node::tprint(troff_output_file *out)
4654 : {
4655 485 : int page_number = topdiv->get_page_number();
4656 : // Does the node have an associated position and file name?
4657 485 : if (is_on == 2) {
4658 : // Save them for future bounding box limits.
4659 117 : last_position = position;
4660 117 : image_filename = strsave(filename.contents());
4661 117 : image_filename_len = strlen(image_filename);
4662 : }
4663 : else { // is_on = 0 or 1
4664 : // Now check whether the suppress node requires us to issue limits.
4665 368 : if (emit_limits) {
4666 117 : const size_t namebuflen = 8192;
4667 117 : char name[namebuflen] = { '\0' };
4668 : // Jump through a flaming hoop to avoid a "format nonliteral"
4669 : // warning from blindly using sprintf...and avoid trouble from
4670 : // mischievous image stems.
4671 : //
4672 : // Keep this format string synced with pre-html:makeFileName().
4673 117 : const char format[] = "%d";
4674 117 : const size_t format_len = strlen(format);
4675 117 : const char *percent_position = strstr(image_filename, format);
4676 117 : if (percent_position) {
4677 0 : subimage_counter++;
4678 : assert(sizeof subimage_counter <= 8);
4679 : // A 64-bit signed int produces up to 19 decimal digits.
4680 0 : const size_t ndigits = 19;
4681 : // Reserve enough for that plus null terminator.
4682 : char *subimage_number
4683 0 : = static_cast<char *>(malloc(ndigits + 1));
4684 0 : if (0 == subimage_number)
4685 0 : fatal("memory allocation failure");
4686 : // Replace the %d in the filename with this number.
4687 0 : size_t enough = image_filename_len + ndigits - format_len;
4688 0 : char *new_name = static_cast<char *>(malloc(enough));
4689 0 : if (0 == new_name)
4690 0 : fatal("memory allocation failure");
4691 0 : ptrdiff_t prefix_length = percent_position - image_filename;
4692 0 : strncpy(new_name, image_filename, prefix_length);
4693 0 : sprintf(subimage_number, "%d", subimage_counter);
4694 0 : size_t number_length = strlen(subimage_number);
4695 0 : strcpy(new_name + prefix_length, subimage_number);
4696 : // Skip over the format in the source string.
4697 0 : const char *suffix_src = image_filename + prefix_length
4698 0 : + format_len;
4699 0 : char *suffix_dst = new_name + prefix_length + number_length;
4700 0 : strcpy(suffix_dst, suffix_src);
4701 : // Ensure the new string fits with room for a terminal '\0'.
4702 0 : const size_t len = strlen(new_name);
4703 0 : if (len > (namebuflen - 1))
4704 0 : error("constructed file name in suppressed output escape"
4705 : " sequence is too long (>= %1 bytes); skipping image",
4706 0 : int(namebuflen));
4707 : else
4708 0 : strncpy(name, new_name, (namebuflen - 1));
4709 0 : free(new_name);
4710 0 : free(subimage_number);
4711 : }
4712 : else {
4713 117 : if (image_filename_len > (namebuflen - 1))
4714 0 : error("file name in suppressed output escape sequence is too"
4715 0 : " long (>= %1 bytes); skipping image", int(namebuflen));
4716 : else
4717 117 : strcpy(name, image_filename);
4718 : }
4719 117 : if (is_writing_html) {
4720 58 : switch (last_position) {
4721 57 : case 'c':
4722 57 : out->start_device_extension();
4723 57 : put(out, "devtag:.centered-image");
4724 57 : break;
4725 0 : case 'r':
4726 0 : out->start_device_extension();
4727 0 : put(out, "devtag:.right-image");
4728 0 : break;
4729 1 : case 'l':
4730 1 : out->start_device_extension();
4731 1 : put(out, "devtag:.left-image");
4732 1 : break;
4733 58 : case 'i':
4734 : ;
4735 : default:
4736 : ;
4737 : }
4738 58 : out->end_device_extension();
4739 58 : out->start_device_extension();
4740 58 : put(out, "devtag:.auto-image ");
4741 58 : put(out, name);
4742 58 : out->end_device_extension();
4743 : }
4744 : else {
4745 : // postscript (or other device)
4746 59 : if ((suppression_starting_page_number > 0)
4747 59 : && (page_number != suppression_starting_page_number))
4748 0 : error("suppression limit registers span more than a page;"
4749 0 : " grohtml-info for image %1 will be wrong", image_no);
4750 : //if (topdiv->get_page_number()
4751 : // != suppression_starting_page_number)
4752 : // fprintf(stderr, "end of image and topdiv page = %d and"
4753 : // " suppression_starting_page_number = %d\n",
4754 : // topdiv->get_page_number(),
4755 : // suppression_starting_page_number);
4756 : // `name` will contain a "%d" in which the image_no is placed.
4757 118 : fprintf(stderr,
4758 : "grohtml-info:page %d %d %d %d %d %d %s %d %d"
4759 : " %s:%s\n",
4760 : topdiv->get_page_number(),
4761 : get_register("opminx"), get_register("opminy"),
4762 : get_register("opmaxx"), get_register("opmaxy"),
4763 : // page offset + line length
4764 59 : get_register(".o") + get_register(".l"),
4765 : name, hresolution, vresolution, get_string(".F"),
4766 : get_string(".c"));
4767 59 : fflush(stderr);
4768 : }
4769 : }
4770 : else { // We are not emitting limits.
4771 251 : if (is_on) {
4772 117 : out->on();
4773 117 : reset_output_registers();
4774 : }
4775 : else
4776 134 : out->off();
4777 251 : suppression_starting_page_number = page_number;
4778 : }
4779 : } // is_on
4780 485 : }
4781 :
4782 234 : bool suppress_node::causes_tprint()
4783 : {
4784 234 : return is_on;
4785 : }
4786 :
4787 429 : bool suppress_node::is_tag()
4788 : {
4789 429 : return is_on;
4790 : }
4791 :
4792 1384 : hunits suppress_node::width()
4793 : {
4794 1384 : return H0;
4795 : }
4796 :
4797 : /* composite_node */
4798 :
4799 : // A composite (glyph) node corresponds to a user-defined GNU troff
4800 : // character with a macro definition.
4801 :
4802 : // Not derived from `container_node`; implements custom contained node
4803 : // dumper in dump_node().
4804 : class composite_node : public charinfo_node {
4805 : node *nodes;
4806 : tfont *tf;
4807 : public:
4808 : composite_node(node *, charinfo *, tfont *, statem *, int,
4809 : node * = 0 /* nullptr */);
4810 : ~composite_node();
4811 : node *copy();
4812 : hunits width();
4813 : node *last_char_node();
4814 : units size();
4815 : void tprint(troff_output_file *);
4816 : hyphenation_type get_hyphenation_type();
4817 : void ascii_print(ascii_output_file *);
4818 : void asciify(macro *);
4819 : hyphen_list *get_hyphen_list(hyphen_list *, int *);
4820 : node *add_self(node *, hyphen_list **);
4821 : tfont *get_tfont();
4822 : bool is_same_as(node *);
4823 : const char *type();
4824 : bool causes_tprint();
4825 : bool is_tag();
4826 : void vertical_extent(vunits *, vunits *);
4827 : vunits vertical_width();
4828 : void dump_properties();
4829 : void dump_node();
4830 : };
4831 :
4832 54443 : composite_node::composite_node(node *p, charinfo *c, tfont *t,
4833 54443 : statem *s, int divlevel, node *x)
4834 54443 : : charinfo_node(c, s, divlevel, x), nodes(p), tf(t)
4835 : {
4836 54443 : }
4837 :
4838 108050 : composite_node::~composite_node()
4839 : {
4840 54025 : delete_node_list(nodes);
4841 108050 : }
4842 :
4843 3543 : node *composite_node::copy()
4844 : {
4845 3543 : return new composite_node(copy_node_list(nodes), ci, tf, state,
4846 3543 : div_nest_level);
4847 : }
4848 :
4849 98939 : hunits composite_node::width()
4850 : {
4851 98939 : hunits x;
4852 98939 : if (tf->is_constantly_spaced(&x))
4853 0 : return x;
4854 98939 : x = H0;
4855 301005 : for (node *tem = nodes; tem != 0 /* nullptr */; tem = tem->next)
4856 202066 : x += tem->width();
4857 98939 : hunits offset;
4858 98939 : if (tf->is_emboldened(&offset))
4859 0 : x += offset;
4860 98939 : x += tf->get_track_kern();
4861 98939 : return x;
4862 : }
4863 :
4864 0 : node *composite_node::last_char_node()
4865 : {
4866 0 : return this;
4867 : }
4868 :
4869 1956 : vunits composite_node::vertical_width()
4870 : {
4871 1956 : vunits v = V0;
4872 6677 : for (node *tem = nodes; tem != 0 /* nullptr */; tem = tem->next)
4873 4721 : v += tem->vertical_width();
4874 1956 : return v;
4875 : }
4876 :
4877 0 : units composite_node::size()
4878 : {
4879 0 : return tf->get_size().to_units();
4880 : }
4881 :
4882 312 : hyphenation_type composite_node::get_hyphenation_type()
4883 : {
4884 312 : return HYPHENATION_PERMITTED;
4885 : }
4886 :
4887 5 : void composite_node::asciify(macro *m)
4888 : {
4889 5 : if (!is_output_supressed) {
4890 5 : unsigned char c = ci->get_asciify_code();
4891 5 : if (0U == c)
4892 5 : c = ci->get_ascii_code();
4893 5 : if (c != 0U)
4894 0 : m->append(c);
4895 : else
4896 5 : m->append(this);
4897 : }
4898 5 : }
4899 :
4900 7 : void composite_node::ascii_print(ascii_output_file *ascii)
4901 : {
4902 7 : ascii_print_reverse_node_list(ascii, nodes);
4903 7 : }
4904 :
4905 312 : hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail,
4906 : int *count)
4907 : {
4908 312 : (*count)++;
4909 312 : return new hyphen_list(ci->get_hyphenation_code(), tail);
4910 : }
4911 :
4912 312 : node *composite_node::add_self(node *nn, hyphen_list **p)
4913 : {
4914 312 : assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
4915 312 : next = nn;
4916 312 : nn = this;
4917 312 : if ((*p)->is_hyphen)
4918 0 : nn = nn->add_discretionary_hyphen();
4919 312 : hyphen_list *pp = *p;
4920 312 : *p = (*p)->next;
4921 312 : delete pp;
4922 312 : return nn;
4923 : }
4924 :
4925 0 : tfont *composite_node::get_tfont()
4926 : {
4927 0 : return tf;
4928 : }
4929 :
4930 54494 : node *reverse_node_list(node *n)
4931 : {
4932 54494 : node *r = 0 /* nullptr */;
4933 534250 : while (n != 0 /* nullptr */) {
4934 479756 : node *tem = n;
4935 479756 : n = n->next;
4936 479756 : tem->next = r;
4937 479756 : r = tem;
4938 : }
4939 54494 : return r;
4940 : }
4941 :
4942 1238 : void composite_node::vertical_extent(vunits *minimum, vunits *maximum)
4943 : {
4944 1238 : nodes = reverse_node_list(nodes);
4945 1238 : node_list_vertical_extent(nodes, minimum, maximum);
4946 1238 : nodes = reverse_node_list(nodes);
4947 1238 : }
4948 :
4949 831816 : width_list::width_list(hunits w, hunits s)
4950 831816 : : width(w), sentence_width(s), next(0 /* nullptr */)
4951 : {
4952 831816 : }
4953 :
4954 48427 : width_list::width_list(width_list *w)
4955 48427 : : width(w->width), sentence_width(w->sentence_width), next(0)
4956 : {
4957 48427 : }
4958 :
4959 0 : void width_list::dump()
4960 : {
4961 0 : fputc('[', stderr);
4962 0 : bool need_comma = false;
4963 0 : fprintf(stderr, "{ \"width\": %d", width.to_units());
4964 0 : fprintf(stderr, ", \"sentence_width\": %d }",
4965 : sentence_width.to_units());
4966 0 : fflush(stderr);
4967 0 : width_list *n = this;
4968 0 : while (n->next != 0 /* nullptr */) {
4969 0 : if (need_comma)
4970 0 : fputs(", ", stderr);
4971 0 : need_comma = true;
4972 0 : n = n->next;
4973 : }
4974 0 : fputc(']', stderr);
4975 0 : fflush(stderr);
4976 0 : }
4977 :
4978 793998 : word_space_node::word_space_node(hunits d, color *c, width_list *w,
4979 793998 : node *x)
4980 793998 : : space_node(d, c, x), orig_width(w), unformat(false)
4981 : {
4982 793998 : }
4983 :
4984 49825 : word_space_node::word_space_node(hunits d, int s, color *c,
4985 : width_list *w, bool flag, statem *st,
4986 49825 : int divlevel, node *x)
4987 49825 : : space_node(d, s, 0, c, st, divlevel, x), orig_width(w), unformat(flag)
4988 : {
4989 49825 : }
4990 :
4991 0 : void word_space_node::dump_properties()
4992 : {
4993 0 : space_node::dump_properties();
4994 0 : if (orig_width != 0 /* nullptr */) {
4995 0 : fputs(", \"width_list\": ", stderr);
4996 0 : orig_width->dump();
4997 : }
4998 0 : fprintf(stderr, ", \"unformat\": %s", unformat ? "true" : "false");
4999 0 : fflush(stderr);
5000 0 : }
5001 :
5002 1667116 : word_space_node::~word_space_node()
5003 : {
5004 839570 : width_list *w = orig_width;
5005 1715516 : while (w != 0) {
5006 875946 : width_list *tmp = w;
5007 875946 : w = w->next;
5008 875946 : delete tmp;
5009 : }
5010 1667116 : }
5011 :
5012 46175 : node *word_space_node::copy()
5013 : {
5014 46175 : assert(orig_width != 0);
5015 46175 : width_list *w_old_curr = orig_width;
5016 46175 : width_list *w_new_curr = new width_list(w_old_curr);
5017 46175 : width_list *w_new = w_new_curr;
5018 46175 : w_old_curr = w_old_curr->next;
5019 48427 : while (w_old_curr != 0) {
5020 2252 : w_new_curr->next = new width_list(w_old_curr);
5021 2252 : w_new_curr = w_new_curr->next;
5022 2252 : w_old_curr = w_old_curr->next;
5023 : }
5024 138525 : return new word_space_node(n, set, col, w_new, unformat, state,
5025 46175 : div_nest_level);
5026 : }
5027 :
5028 2451 : bool word_space_node::set_unformat_flag()
5029 : {
5030 2451 : unformat = true;
5031 2451 : return true;
5032 : }
5033 :
5034 423391 : void word_space_node::tprint(troff_output_file *out)
5035 : {
5036 423391 : out->fill_color(col);
5037 423391 : out->word_marker();
5038 423391 : out->right(n);
5039 423391 : }
5040 :
5041 6220 : bool word_space_node::did_space_merge(hunits h, hunits sw, hunits ssw)
5042 : {
5043 6220 : n += h;
5044 6220 : assert(orig_width != 0);
5045 6220 : width_list *w = orig_width;
5046 43796 : for (; w->next != 0 /* nullptr */; w = w->next)
5047 : ;
5048 6220 : w->next = new width_list(sw, ssw);
5049 6220 : return true;
5050 : }
5051 :
5052 8431 : unbreakable_space_node::unbreakable_space_node(hunits d, color *c,
5053 8431 : node *x)
5054 8431 : : word_space_node(d, c, 0, x)
5055 : {
5056 8431 : }
5057 :
5058 3650 : unbreakable_space_node::unbreakable_space_node(hunits d, int s,
5059 : color *c, statem *st,
5060 : int divlevel,
5061 3650 : node *x)
5062 3650 : : word_space_node(d, s, c, 0, 0, st, divlevel, x)
5063 : {
5064 3650 : }
5065 :
5066 3650 : node *unbreakable_space_node::copy()
5067 : {
5068 3650 : return new unbreakable_space_node(n, set, col, state, div_nest_level);
5069 : }
5070 :
5071 120 : bool unbreakable_space_node::causes_tprint()
5072 : {
5073 120 : return false;
5074 : }
5075 :
5076 5579 : bool unbreakable_space_node::is_tag()
5077 : {
5078 5579 : return false;
5079 : }
5080 :
5081 318 : breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
5082 : breakpoint *rest,
5083 : bool /* is_inner */)
5084 : {
5085 318 : return rest;
5086 : }
5087 :
5088 0 : int unbreakable_space_node::nbreaks()
5089 : {
5090 0 : return 0;
5091 : }
5092 :
5093 0 : void unbreakable_space_node::split(int, node **, node **)
5094 : {
5095 0 : assert(0 == "unbreakable_space_node::split() unimplemented");
5096 : }
5097 :
5098 4 : bool unbreakable_space_node::did_space_merge(hunits, hunits, hunits)
5099 : {
5100 4 : return false;
5101 : }
5102 :
5103 3286470 : hvpair::hvpair()
5104 : {
5105 3286470 : }
5106 :
5107 230302 : draw_node::draw_node(char c, hvpair *p, int np, font_size s,
5108 230302 : color *gc, color *fc)
5109 230302 : : npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
5110 : {
5111 526976 : point = new hvpair[npoints];
5112 526976 : for (int i = 0; i < npoints; i++)
5113 296674 : point[i] = p[i];
5114 230302 : }
5115 :
5116 527367 : draw_node::draw_node(char c, hvpair *p, int np, font_size s,
5117 527367 : color *gc, color *fc, statem *st, int divlevel)
5118 : : node(0 /* nullptr */, st, divlevel), npoints(np), sz(s), gcol(gc),
5119 527367 : fcol(fc), code(c)
5120 : {
5121 1214143 : point = new hvpair[npoints];
5122 1214143 : for (int i = 0; i < npoints; i++)
5123 686776 : point[i] = p[i];
5124 527367 : }
5125 :
5126 0 : void draw_node::dump_properties()
5127 : {
5128 0 : node::dump_properties();
5129 0 : fprintf(stderr, ", \"code\": \"%c\"", code);
5130 0 : fprintf(stderr, ", \"npoints\": %d", npoints);
5131 0 : fprintf(stderr, ", \"font_size\": %d", sz.to_units());
5132 0 : fputs(", \"stroke color\": ", stderr);
5133 0 : gcol->nm.json_dump();
5134 0 : fputs(", \"fill color\": ", stderr);
5135 0 : fcol->nm.json_dump();
5136 0 : fprintf(stderr, ", \"point\": \"(%d, %d)\"",
5137 0 : point->h.to_units(), point->v.to_units());
5138 0 : fflush(stderr);
5139 0 : }
5140 :
5141 0 : bool draw_node::is_same_as(node *n)
5142 : {
5143 0 : draw_node *nd = static_cast<draw_node *>(n);
5144 0 : if (code != nd->code || npoints != nd->npoints || sz != nd->sz
5145 0 : || gcol != nd->gcol || fcol != nd->fcol)
5146 0 : return false;
5147 0 : for (int i = 0; i < npoints; i++)
5148 0 : if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
5149 0 : return false;
5150 0 : return true;
5151 : }
5152 :
5153 0 : const char *draw_node::type()
5154 : {
5155 0 : return "drawing command node";
5156 : }
5157 :
5158 2249 : bool draw_node::causes_tprint()
5159 : {
5160 2249 : return false;
5161 : }
5162 :
5163 226478 : bool draw_node::is_tag()
5164 : {
5165 226478 : return false;
5166 : }
5167 :
5168 1515002 : draw_node::~draw_node()
5169 : {
5170 757501 : if (point)
5171 757501 : delete[] point;
5172 1515002 : }
5173 :
5174 758016 : hunits draw_node::width()
5175 : {
5176 758016 : hunits x = H0;
5177 1741803 : for (int i = 0; i < npoints; i++)
5178 983787 : x += point[i].h;
5179 758016 : return x;
5180 : }
5181 :
5182 1063 : vunits draw_node::vertical_width()
5183 : {
5184 1063 : if (code == 'e')
5185 0 : return V0;
5186 1063 : vunits x = V0;
5187 2126 : for (int i = 0; i < npoints; i++)
5188 1063 : x += point[i].v;
5189 1063 : return x;
5190 : }
5191 :
5192 527367 : node *draw_node::copy()
5193 : {
5194 1582101 : return new draw_node(code, point, npoints, sz, gcol, fcol, state,
5195 527367 : div_nest_level);
5196 : }
5197 :
5198 231055 : void draw_node::tprint(troff_output_file *out)
5199 : {
5200 231055 : out->draw(code, point, npoints, sz, gcol, fcol);
5201 231055 : }
5202 :
5203 : /* tprint methods */
5204 :
5205 2608611 : void glyph_node::tprint(troff_output_file *out)
5206 : {
5207 2608611 : tfont *ptf = tf->get_plain();
5208 2608611 : if (ptf == tf)
5209 2605540 : out->put_char_width(ci, ptf, gcol, fcol, width(), H0);
5210 : else {
5211 3071 : hunits offset;
5212 3071 : bool is_emboldened = tf->is_emboldened(&offset);
5213 3071 : hunits w = ptf->get_width(ci);
5214 3071 : hunits k = H0;
5215 3071 : hunits x;
5216 3071 : bool is_constantly_spaced = tf->is_constantly_spaced(&x);
5217 3071 : if (is_constantly_spaced) {
5218 0 : x -= w;
5219 0 : if (is_emboldened)
5220 0 : x -= offset;
5221 0 : hunits x2 = (x / 2);
5222 0 : out->right(x2);
5223 0 : k = x - x2;
5224 : }
5225 : else
5226 3071 : k = tf->get_track_kern();
5227 3071 : if (is_emboldened) {
5228 6 : out->put_char(ci, ptf, gcol, fcol);
5229 6 : out->right(offset);
5230 : }
5231 3071 : out->put_char_width(ci, ptf, gcol, fcol, w, k);
5232 : }
5233 2608611 : }
5234 :
5235 239 : void glyph_node::zero_width_tprint(troff_output_file *out)
5236 : {
5237 239 : tfont *ptf = tf->get_plain();
5238 239 : hunits offset;
5239 239 : bool is_emboldened = tf->is_emboldened(&offset);
5240 239 : hunits x;
5241 239 : bool is_constantly_spaced = tf->is_constantly_spaced(&x);
5242 239 : if (is_constantly_spaced) {
5243 0 : x -= ptf->get_width(ci);
5244 0 : if (is_emboldened)
5245 0 : x -= offset;
5246 0 : x = (x / 2);
5247 0 : out->right(x);
5248 : }
5249 239 : out->put_char(ci, ptf, gcol, fcol);
5250 239 : if (is_emboldened) {
5251 0 : out->right(offset);
5252 0 : out->put_char(ci, ptf, gcol, fcol);
5253 0 : out->right(-offset);
5254 : }
5255 239 : if (is_constantly_spaced)
5256 0 : out->right(-x);
5257 239 : }
5258 :
5259 13894 : void break_char_node::tprint(troff_output_file *t)
5260 : {
5261 13894 : nodes->tprint(t);
5262 13894 : }
5263 :
5264 0 : void break_char_node::zero_width_tprint(troff_output_file *t)
5265 : {
5266 0 : nodes->zero_width_tprint(t);
5267 0 : }
5268 :
5269 310 : void hline_node::tprint(troff_output_file *out)
5270 : {
5271 310 : if (x < H0) {
5272 8 : out->right(x);
5273 8 : x = -x;
5274 : }
5275 310 : if (0 /* nullptr */ == nodes) {
5276 0 : out->right(x);
5277 0 : return;
5278 : }
5279 310 : hunits w = nodes->width();
5280 310 : if (w <= H0) {
5281 0 : error("horizontal line drawing character must have positive width");
5282 0 : out->right(x);
5283 0 : return;
5284 : }
5285 310 : int i = int(x / w);
5286 310 : if (0 == i) {
5287 2 : hunits xx = x - w;
5288 2 : hunits xx2 = (xx / 2);
5289 2 : out->right(xx2);
5290 2 : if (out->is_on())
5291 2 : nodes->tprint(out);
5292 2 : out->right(xx - xx2);
5293 : }
5294 : else {
5295 308 : hunits rem = x - (w * i);
5296 308 : if (rem > H0) {
5297 144 : if (nodes->overlaps_horizontally()) {
5298 31 : if (out->is_on())
5299 31 : nodes->tprint(out);
5300 31 : out->right(rem - w);
5301 : }
5302 : else
5303 113 : out->right(rem);
5304 : }
5305 8494 : while (--i >= 0)
5306 8186 : if (out->is_on())
5307 8180 : nodes->tprint(out);
5308 : }
5309 : }
5310 :
5311 0 : void vline_node::tprint(troff_output_file *out)
5312 : {
5313 0 : if (0 /* nullptr */ == nodes) {
5314 0 : out->down(x);
5315 0 : return;
5316 : }
5317 0 : vunits h = nodes->size();
5318 0 : bool overlaps = nodes->overlaps_vertically();
5319 0 : vunits y = x;
5320 0 : if (y < V0) {
5321 0 : y = -y;
5322 0 : int i = y / h;
5323 0 : vunits rem = y - i*h;
5324 0 : if (0 == i) {
5325 0 : out->right(nodes->width());
5326 0 : out->down(-rem);
5327 : }
5328 : else {
5329 0 : while (--i > 0) {
5330 0 : nodes->zero_width_tprint(out);
5331 0 : out->down(-h);
5332 : }
5333 0 : if (overlaps) {
5334 0 : nodes->zero_width_tprint(out);
5335 0 : out->down(-rem);
5336 0 : if (out->is_on())
5337 0 : nodes->tprint(out);
5338 0 : out->down(-h);
5339 : }
5340 : else {
5341 0 : if (out->is_on())
5342 0 : nodes->tprint(out);
5343 0 : out->down(-h - rem);
5344 : }
5345 : }
5346 : }
5347 : else {
5348 0 : int i = y / h;
5349 0 : vunits rem = y - i*h;
5350 0 : if (0 == i) {
5351 0 : out->down(rem);
5352 0 : out->right(nodes->width());
5353 : }
5354 : else {
5355 0 : out->down(h);
5356 0 : if (overlaps)
5357 0 : nodes->zero_width_tprint(out);
5358 0 : out->down(rem);
5359 0 : while (--i > 0) {
5360 0 : nodes->zero_width_tprint(out);
5361 0 : out->down(h);
5362 : }
5363 0 : if (out->is_on())
5364 0 : nodes->tprint(out);
5365 : }
5366 : }
5367 : }
5368 :
5369 451 : void zero_width_node::tprint(troff_output_file *out)
5370 : {
5371 451 : if (0 /* nullptr */ == nodes)
5372 0 : return;
5373 451 : if (0 /* nullptr */ == nodes->next) {
5374 29 : nodes->zero_width_tprint(out);
5375 29 : return;
5376 : }
5377 422 : int hpos = out->get_hpos();
5378 422 : int vpos = out->get_vpos();
5379 422 : node *tem = nodes;
5380 2658 : while (tem) {
5381 2236 : tem->tprint(out);
5382 2236 : tem = tem->next;
5383 : }
5384 422 : out->moveto(hpos, vpos);
5385 : }
5386 :
5387 106 : void overstrike_node::tprint(troff_output_file *out)
5388 : {
5389 106 : hunits pos = H0;
5390 318 : for (node *tem = nodes; tem != 0 /* nullptr */; tem = tem->next) {
5391 212 : hunits x = (max_width - tem->width()) / 2;
5392 212 : out->right(x - pos);
5393 212 : pos = x;
5394 212 : tem->zero_width_tprint(out);
5395 : }
5396 106 : out->right(max_width - pos);
5397 106 : }
5398 :
5399 0 : void bracket_node::tprint(troff_output_file *out)
5400 : {
5401 0 : if (0 /* nullptr */ == nodes)
5402 0 : return;
5403 0 : int npieces = 0;
5404 : node *tem;
5405 0 : for (tem = nodes; tem != 0 /* nullptr */; tem = tem->next)
5406 0 : ++npieces;
5407 0 : vunits h = nodes->size();
5408 0 : vunits totalh = h*npieces;
5409 0 : vunits y = (totalh - h) / 2;
5410 0 : out->down(y);
5411 0 : for (tem = nodes; tem != 0 /* nullptr */; tem = tem->next) {
5412 0 : tem->zero_width_tprint(out);
5413 0 : out->down(-h);
5414 : }
5415 0 : out->right(max_width);
5416 0 : out->down(totalh - y);
5417 : }
5418 :
5419 1274706 : void node::tprint(troff_output_file *)
5420 : {
5421 1274706 : }
5422 :
5423 2 : void node::zero_width_tprint(troff_output_file *out)
5424 : {
5425 2 : int hpos = out->get_hpos();
5426 2 : int vpos = out->get_vpos();
5427 2 : tprint(out);
5428 2 : out->moveto(hpos, vpos);
5429 2 : }
5430 :
5431 5035 : void space_node::tprint(troff_output_file *out)
5432 : {
5433 5035 : out->fill_color(col);
5434 5035 : out->right(n);
5435 5035 : }
5436 :
5437 463183 : void hmotion_node::tprint(troff_output_file *out)
5438 : {
5439 463183 : out->fill_color(col);
5440 463183 : out->right(n);
5441 463183 : }
5442 :
5443 4328 : void space_char_hmotion_node::tprint(troff_output_file *out)
5444 : {
5445 4328 : out->fill_color(col);
5446 4328 : if (is_writing_html) {
5447 : // we emit the space width as a negative glyph index
5448 0 : out->flush_tbuf();
5449 0 : out->do_motion();
5450 0 : out->put('N');
5451 0 : out->put(-n.to_units());
5452 0 : out->put('\n');
5453 : }
5454 4328 : out->right(n);
5455 4328 : }
5456 :
5457 102798 : void vmotion_node::tprint(troff_output_file *out)
5458 : {
5459 102798 : out->fill_color(col);
5460 102798 : out->down(n);
5461 102798 : }
5462 :
5463 57525 : void kern_pair_node::tprint(troff_output_file *out)
5464 : {
5465 57525 : n1->tprint(out);
5466 57525 : out->right(amount);
5467 57525 : n2->tprint(out);
5468 57525 : }
5469 :
5470 77120 : static void tprint_reverse_node_list(troff_output_file *out, node *n)
5471 : {
5472 77120 : if (0 /* nullptr */ == n)
5473 27586 : return;
5474 49534 : tprint_reverse_node_list(out, n->next);
5475 49534 : n->tprint(out);
5476 : }
5477 :
5478 11333 : void dbreak_node::tprint(troff_output_file *out)
5479 : {
5480 11333 : tprint_reverse_node_list(out, none);
5481 11333 : }
5482 :
5483 16253 : void composite_node::tprint(troff_output_file *out)
5484 : {
5485 16253 : hunits bold_offset;
5486 16253 : bool is_emboldened = tf->is_emboldened(&bold_offset);
5487 16253 : hunits track_kern = tf->get_track_kern();
5488 16253 : hunits constant_space;
5489 16253 : bool is_constantly_spaced = tf->is_constantly_spaced(&constant_space);
5490 16253 : hunits x = H0;
5491 16253 : if (is_constantly_spaced) {
5492 0 : x = constant_space;
5493 0 : for (node *tem = nodes; tem != 0 /* nullptr */; tem = tem->next)
5494 0 : x -= tem->width();
5495 0 : if (is_emboldened)
5496 0 : x -= bold_offset;
5497 0 : hunits x2 = x / 2;
5498 0 : out->right(x2);
5499 0 : x -= x2;
5500 : }
5501 16253 : if (is_emboldened) {
5502 0 : int hpos = out->get_hpos();
5503 0 : int vpos = out->get_vpos();
5504 0 : tprint_reverse_node_list(out, nodes);
5505 0 : out->moveto(hpos, vpos);
5506 0 : out->right(bold_offset);
5507 : }
5508 16253 : tprint_reverse_node_list(out, nodes);
5509 16253 : if (is_constantly_spaced)
5510 0 : out->right(x);
5511 : else
5512 16253 : out->right(track_kern);
5513 16253 : }
5514 :
5515 0 : void composite_node::dump_properties()
5516 : {
5517 0 : node::dump_properties();
5518 : // GNU troff multiplexes the distinction of ordinary vs. special
5519 : // characters though the special character code zero.
5520 0 : unsigned char c = ci->get_ascii_code();
5521 0 : if (c != 0U) {
5522 0 : fputs(", \"character\": ", stderr);
5523 : // It's not a `string` or `symbol` we can `.json_dump()`, so we have
5524 : // to write the quotation marks ourselves.
5525 0 : fputc('\"', stderr);
5526 0 : json_char jc = json_encode_char(c);
5527 : // Write out its JSON representation by character by character to
5528 : // keep libc string functions from interpreting C escape sequences.
5529 0 : for (size_t i = 0; i < jc.len; i++)
5530 0 : fputc(jc.buf[i], stderr);
5531 0 : fputc('\"', stderr);
5532 : }
5533 : else {
5534 0 : fputs(", \"special character\": ", stderr);
5535 0 : ci->nm.json_dump();
5536 : }
5537 0 : fflush(stderr);
5538 0 : }
5539 :
5540 0 : void composite_node::dump_node()
5541 : {
5542 0 : fputc('{', stderr);
5543 0 : dump_properties();
5544 0 : fputs(", \"contents\": ", stderr);
5545 0 : dump_node_list_in_reverse(nodes);
5546 0 : fputc('}', stderr);
5547 0 : fflush(stderr);
5548 0 : }
5549 :
5550 50900 : static node *make_composite_node(charinfo *s, environment *env)
5551 : {
5552 50900 : int fontno = env_resolve_font(env);
5553 50900 : if (fontno < 0) {
5554 0 : error("cannot format composite glyph: no current font");
5555 0 : return 0 /* nullptr */;
5556 : }
5557 50900 : assert((fontno < font_table_size)
5558 : && font_table[fontno] != 0 /* nullptr*/);
5559 50900 : node *n = charinfo_to_node_list(s, env);
5560 50900 : font_size fs = env->get_font_size();
5561 50900 : int char_height = env->get_char_height();
5562 50900 : int char_slant = env->get_char_slant();
5563 50900 : tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
5564 : fontno);
5565 50900 : if (env->is_composite())
5566 36 : tf = tf->get_plain();
5567 50900 : return new composite_node(n, s, tf, 0, 0, 0);
5568 : }
5569 :
5570 12878501 : static node *make_glyph_node(charinfo *s, environment *env,
5571 : bool want_warnings = true)
5572 : {
5573 12878501 : int fontno = env_resolve_font(env);
5574 12878501 : if (fontno < 0) {
5575 0 : error("cannot format glyph: no current font");
5576 0 : return 0 /* nullptr */;
5577 : }
5578 12878501 : assert((fontno < font_table_size)
5579 : && font_table[fontno] != 0 /* nullptr*/);
5580 12878501 : int fn = fontno;
5581 12878501 : bool found = font_table[fontno]->contains(s);
5582 12878501 : if (!found) {
5583 20342 : macro *mac = s->get_macro();
5584 20342 : if ((mac != 0 /* nullptr */) && s->is_fallback())
5585 204 : return make_composite_node(s, env);
5586 20138 : if (s->is_numbered()) {
5587 8222 : if (want_warnings)
5588 0 : warning(WARN_CHAR, "character code %1 not defined in current"
5589 0 : " font", s->get_number());
5590 8222 : return 0 /* nullptr */;
5591 : }
5592 11916 : special_font_list *sf = font_table[fontno]->sf;
5593 11938 : while ((sf != 0 /* nullptr */) && !found) {
5594 22 : fn = sf->n;
5595 22 : if (font_table[fn])
5596 22 : found = font_table[fn]->contains(s);
5597 22 : sf = sf->next;
5598 : }
5599 11916 : if (!found) {
5600 11898 : symbol f = font_table[fontno]->get_name();
5601 11898 : string gl(f.contents());
5602 11898 : gl += ' ';
5603 11898 : gl += s->nm.contents();
5604 11898 : gl += '\0';
5605 11898 : charinfo *ci = lookup_charinfo(symbol(gl.contents()));
5606 11898 : if (ci && ci->get_macro())
5607 0 : return make_composite_node(ci, env);
5608 : }
5609 11916 : if (!found) {
5610 11898 : sf = global_special_fonts;
5611 11906 : while ((sf != 0 /* nullptr */) && !found) {
5612 8 : fn = sf->n;
5613 8 : if (font_table[fn])
5614 8 : found = font_table[fn]->contains(s);
5615 8 : sf = sf->next;
5616 : }
5617 : }
5618 11916 : if (!found)
5619 11897 : if (mac && s->is_special())
5620 214 : return make_composite_node(s, env);
5621 11702 : if (!found) {
5622 144472 : for (fn = 0; fn < font_table_size; fn++)
5623 287718 : if (font_table[fn]
5624 129302 : && font_table[fn]->is_special()
5625 273161 : && font_table[fn]->contains(s)) {
5626 11070 : found = true;
5627 11070 : break;
5628 : }
5629 : }
5630 11702 : if (!found) {
5631 613 : if (want_warnings && s->first_time_not_found()) {
5632 28 : unsigned char input_code = s->get_ascii_code();
5633 28 : if (input_code != 0U) {
5634 0 : if (csgraph(input_code))
5635 0 : warning(WARN_CHAR, "character '%1' not defined",
5636 0 : input_code);
5637 : else
5638 0 : warning(WARN_CHAR, "character with input code %1 not"
5639 0 : " defined", int(input_code));
5640 : }
5641 28 : else if (s->nm.contents()) {
5642 28 : const char *nm = s->nm.contents();
5643 : // If the contents are empty, get_char_for_escape_parameter()
5644 : // should already have thrown an error.
5645 28 : if (nm[0] != '\0') {
5646 28 : const char *backslash = (nm[1] == '\0') ? "\\" : "";
5647 28 : warning(WARN_CHAR, "special character '%1%2' not defined",
5648 56 : backslash, nm);
5649 : }
5650 : }
5651 : }
5652 613 : return 0 /* nullptr */;
5653 : }
5654 : }
5655 12869248 : font_size fs = env->get_font_size();
5656 12869248 : int char_height = env->get_char_height();
5657 12869248 : int char_slant = env->get_char_slant();
5658 12869248 : tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
5659 : fn);
5660 12869248 : if (env->is_composite())
5661 51357 : tf = tf->get_plain();
5662 12869248 : color *gcol = env->get_stroke_color();
5663 12869248 : color *fcol = env->get_fill_color();
5664 : return new glyph_node(s, tf, gcol, fcol, 0 /* nullptr */,
5665 12869248 : 0 /* nullptr */);
5666 : }
5667 :
5668 1007 : node *make_node(charinfo *ci, environment *env)
5669 : {
5670 1007 : switch (ci->get_special_translation()) {
5671 0 : case charinfo::TRANSLATE_SPACE:
5672 0 : return new space_char_hmotion_node(env->get_space_width(),
5673 0 : env->get_fill_color());
5674 0 : case charinfo::TRANSLATE_STRETCHABLE_SPACE:
5675 0 : return new unbreakable_space_node(env->get_space_width(),
5676 0 : env->get_fill_color());
5677 0 : case charinfo::TRANSLATE_DUMMY:
5678 0 : return new dummy_node;
5679 0 : case charinfo::TRANSLATE_HYPHEN_INDICATOR:
5680 0 : error("translation to \\%% ignored in this context");
5681 0 : break;
5682 : }
5683 1007 : charinfo *tem = ci->get_translation();
5684 1007 : if (tem != 0 /* nullptr */)
5685 0 : ci = tem;
5686 1007 : macro *mac = ci->get_macro();
5687 1007 : if (mac && ci->is_normal())
5688 347 : return make_composite_node(ci, env);
5689 : else
5690 660 : return make_glyph_node(ci, env);
5691 : }
5692 :
5693 26117 : bool character_exists(charinfo *ci, environment *env)
5694 : {
5695 26117 : if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
5696 0 : return true;
5697 26117 : charinfo *tem = ci->get_translation();
5698 26117 : if (tem != 0 /* nullptr */)
5699 0 : ci = tem;
5700 26117 : if (ci->get_macro())
5701 262 : return true;
5702 25855 : node *nd = make_glyph_node(ci, env, false /* don't want warnings */);
5703 25855 : if (nd) {
5704 17123 : delete nd;
5705 17123 : return true;
5706 : }
5707 8732 : return false;
5708 : }
5709 :
5710 12900886 : node *node::add_char(charinfo *ci, environment *env,
5711 : hunits *widthp, int *spacep, node **glyph_comp_np)
5712 : {
5713 : node *res;
5714 12900886 : switch (ci->get_special_translation()) {
5715 0 : case charinfo::TRANSLATE_SPACE:
5716 0 : res = new space_char_hmotion_node(env->get_space_width(),
5717 0 : env->get_fill_color(), this);
5718 0 : *widthp += res->width();
5719 0 : return res;
5720 0 : case charinfo::TRANSLATE_STRETCHABLE_SPACE:
5721 0 : res = new unbreakable_space_node(env->get_space_width(),
5722 0 : env->get_fill_color(), this);
5723 0 : res->freeze_space();
5724 0 : *widthp += res->width();
5725 0 : *spacep += res->nspaces();
5726 0 : return res;
5727 0 : case charinfo::TRANSLATE_DUMMY:
5728 0 : return new dummy_node(this);
5729 0 : case charinfo::TRANSLATE_HYPHEN_INDICATOR:
5730 0 : return add_discretionary_hyphen();
5731 : }
5732 12900886 : charinfo *tem = ci->get_translation();
5733 12900886 : if (tem != 0 /* nullptr */)
5734 15896 : ci = tem;
5735 12900886 : macro *mac = ci->get_macro();
5736 12900886 : if (mac && ci->is_normal()) {
5737 50135 : res = make_composite_node(ci, env);
5738 50135 : if (res) {
5739 50135 : res->next = this;
5740 50135 : *widthp += res->width();
5741 50135 : if (glyph_comp_np)
5742 50075 : *glyph_comp_np = res;
5743 : }
5744 : else {
5745 0 : if (glyph_comp_np)
5746 0 : *glyph_comp_np = res;
5747 0 : return this;
5748 : }
5749 : }
5750 : else {
5751 12850751 : node *gn = make_glyph_node(ci, env);
5752 12850751 : if (0 /* nullptr */ == gn)
5753 100 : return this;
5754 : else {
5755 12850651 : hunits old_width = width();
5756 12850651 : node *p = gn->merge_self(this);
5757 12850651 : if (0 /* nullptr */ == p) {
5758 12341003 : *widthp += gn->width();
5759 12341003 : gn->next = this;
5760 12341003 : res = gn;
5761 : }
5762 : else {
5763 509648 : *widthp += p->width() - old_width;
5764 509648 : res = p;
5765 : }
5766 12850651 : if (glyph_comp_np)
5767 12848822 : *glyph_comp_np = res;
5768 : }
5769 : }
5770 12900786 : int break_code = 0;
5771 12900786 : if (ci->allows_break_before())
5772 0 : break_code = ALLOWS_BREAK_BEFORE;
5773 12900786 : if (ci->allows_break_after())
5774 137696 : break_code |= ALLOWS_BREAK_AFTER;
5775 12900786 : if (ci->ignores_surrounding_hyphenation_codes())
5776 0 : break_code |= IGNORES_SURROUNDING_HYPHENATION_CODES;
5777 12900786 : if (ci->prohibits_break_before())
5778 14 : break_code = PROHIBITS_BREAK_BEFORE;
5779 12900786 : if (ci->prohibits_break_after())
5780 0 : break_code |= PROHIBITS_BREAK_AFTER;
5781 12900786 : if (ci->is_interword_space())
5782 57 : break_code |= IS_INTERWORD_SPACE;
5783 12900786 : if (break_code != 0) {
5784 137765 : node *next1 = res->next;
5785 137765 : res->next = 0 /* nullptr */;
5786 275530 : res = new break_char_node(res, break_code, get_break_code(),
5787 137765 : env->get_fill_color(), next1);
5788 : }
5789 12900786 : return res;
5790 : }
5791 :
5792 62913 : static inline int same_node(node *n1, node *n2)
5793 : {
5794 62913 : if (n1 != 0 /* nullptr */) {
5795 62913 : if (n2 != 0 /* nullptr */)
5796 62913 : return n1->type() == n2->type() && n1->is_same_as(n2);
5797 : else
5798 0 : return false;
5799 : }
5800 : else
5801 0 : return 0 /* nullptr */ == n2;
5802 : }
5803 :
5804 2112266 : int same_node_list(node *n1, node *n2)
5805 : {
5806 2112266 : while ((n1 != 0 /* nullptr */) && (n2 != 0 /* nullptr */)) {
5807 1535220 : if (n1->type() != n2->type() || !n1->is_same_as(n2))
5808 802847 : return 0;
5809 732373 : n1 = n1->next;
5810 732373 : n2 = n2->next;
5811 : }
5812 577046 : return !n1 && !n2;
5813 : }
5814 :
5815 0 : bool extra_size_node::is_same_as(node *nd)
5816 : {
5817 0 : return (n == static_cast<extra_size_node *>(nd)->n);
5818 : }
5819 :
5820 0 : const char *extra_size_node::type()
5821 : {
5822 0 : return "extra vertical spacing node";
5823 : }
5824 :
5825 0 : bool extra_size_node::causes_tprint()
5826 : {
5827 0 : return false;
5828 : }
5829 :
5830 133 : bool extra_size_node::is_tag()
5831 : {
5832 133 : return false;
5833 : }
5834 :
5835 0 : bool vertical_size_node::is_same_as(node *nd)
5836 : {
5837 0 : return (n == static_cast<vertical_size_node *>(nd)->n);
5838 : }
5839 :
5840 0 : const char *vertical_size_node::type()
5841 : {
5842 0 : return "vertical spacing node";
5843 : }
5844 :
5845 4042 : bool vertical_size_node::set_unformat_flag()
5846 : {
5847 4042 : return false;
5848 : }
5849 :
5850 7646 : bool vertical_size_node::causes_tprint()
5851 : {
5852 7646 : return false;
5853 : }
5854 :
5855 602762 : bool vertical_size_node::is_tag()
5856 : {
5857 602762 : return false;
5858 : }
5859 :
5860 0 : bool hmotion_node::is_same_as(node *nd)
5861 : {
5862 0 : return ((n == static_cast<hmotion_node *>(nd)->n)
5863 0 : && (col == static_cast<hmotion_node *>(nd)->col));
5864 : }
5865 :
5866 0 : const char *hmotion_node::type()
5867 : {
5868 0 : return "horizontal motion node";
5869 : }
5870 :
5871 293 : bool hmotion_node::set_unformat_flag()
5872 : {
5873 293 : unformat = true;
5874 293 : return true;
5875 : }
5876 :
5877 6268 : bool hmotion_node::causes_tprint()
5878 : {
5879 6268 : return false;
5880 : }
5881 :
5882 455162 : bool hmotion_node::is_tag()
5883 : {
5884 455162 : return false;
5885 : }
5886 :
5887 90 : node *hmotion_node::add_self(node *nd, hyphen_list **p)
5888 : {
5889 90 : next = nd;
5890 90 : hyphen_list *pp = *p;
5891 90 : *p = (*p)->next;
5892 90 : delete pp;
5893 90 : return this;
5894 : }
5895 :
5896 90 : hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail, int *)
5897 : {
5898 90 : return new hyphen_list(0, tail);
5899 : }
5900 :
5901 0 : bool space_char_hmotion_node::is_same_as(node *nd)
5902 : {
5903 0 : return ((n == static_cast<space_char_hmotion_node *>(nd)->n
5904 0 : && col == static_cast<space_char_hmotion_node *>(nd)->col));
5905 : }
5906 :
5907 21 : const char *space_char_hmotion_node::type()
5908 : {
5909 21 : return "space character horizontal motion node";
5910 : }
5911 :
5912 518 : bool space_char_hmotion_node::causes_tprint()
5913 : {
5914 518 : return false;
5915 : }
5916 :
5917 1024 : bool space_char_hmotion_node::is_tag()
5918 : {
5919 1024 : return false;
5920 : }
5921 :
5922 8 : node *space_char_hmotion_node::add_self(node *nd, hyphen_list **p)
5923 : {
5924 8 : next = nd;
5925 8 : hyphen_list *pp = *p;
5926 8 : *p = (*p)->next;
5927 8 : delete pp;
5928 8 : return this;
5929 : }
5930 :
5931 8 : hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail,
5932 : int *)
5933 : {
5934 8 : return new hyphen_list(0, tail);
5935 : }
5936 :
5937 0 : bool vmotion_node::is_same_as(node *nd)
5938 : {
5939 0 : return ((n == static_cast<vmotion_node *>(nd)->n)
5940 0 : && col == static_cast<vmotion_node *>(nd)->col);
5941 : }
5942 :
5943 7 : const char *vmotion_node::type()
5944 : {
5945 7 : return "vertical motion node";
5946 : }
5947 :
5948 2545 : bool vmotion_node::causes_tprint()
5949 : {
5950 2545 : return false;
5951 : }
5952 :
5953 99703 : bool vmotion_node::is_tag()
5954 : {
5955 99703 : return false;
5956 : }
5957 :
5958 0 : bool hline_node::is_same_as(node *nd)
5959 : {
5960 0 : return ((x == static_cast<hline_node *>(nd)->x)
5961 0 : && same_node(nodes, static_cast<hline_node *>(nd)->nodes));
5962 : }
5963 :
5964 0 : const char *hline_node::type()
5965 : {
5966 0 : return "horizontal rule node";
5967 : }
5968 :
5969 1 : bool hline_node::causes_tprint()
5970 : {
5971 1 : return false;
5972 : }
5973 :
5974 303 : bool hline_node::is_tag()
5975 : {
5976 303 : return false;
5977 : }
5978 :
5979 0 : bool vline_node::is_same_as(node *nd)
5980 : {
5981 0 : return ((x == static_cast<vline_node *>(nd)->x)
5982 0 : && same_node(nodes, static_cast<vline_node *>(nd)->nodes));
5983 : }
5984 :
5985 0 : const char *vline_node::type()
5986 : {
5987 0 : return "vertical rule node";
5988 : }
5989 :
5990 0 : bool vline_node::causes_tprint()
5991 : {
5992 0 : return false;
5993 : }
5994 :
5995 0 : bool vline_node::is_tag()
5996 : {
5997 0 : return false;
5998 : }
5999 :
6000 0 : bool dummy_node::is_same_as(node *)
6001 : {
6002 0 : return true;
6003 : }
6004 :
6005 87 : const char *dummy_node::type()
6006 : {
6007 87 : return "dummy node";
6008 : }
6009 :
6010 441 : bool dummy_node::causes_tprint()
6011 : {
6012 441 : return false;
6013 : }
6014 :
6015 51897 : bool dummy_node::is_tag()
6016 : {
6017 51897 : return false;
6018 : }
6019 :
6020 0 : bool transparent_dummy_node::is_same_as(node *)
6021 : {
6022 0 : return true;
6023 : }
6024 :
6025 0 : const char *transparent_dummy_node::type()
6026 : {
6027 0 : return "transparent dummy node";
6028 : }
6029 :
6030 296 : bool transparent_dummy_node::causes_tprint()
6031 : {
6032 296 : return false;
6033 : }
6034 :
6035 106890 : bool transparent_dummy_node::is_tag()
6036 : {
6037 106890 : return false;
6038 : }
6039 :
6040 49217 : int transparent_dummy_node::ends_sentence()
6041 : {
6042 49217 : return 2;
6043 : }
6044 :
6045 0 : bool zero_width_node::is_same_as(node *nd)
6046 : {
6047 0 : return same_node_list(nodes, ((zero_width_node *)nd)->nodes);
6048 : }
6049 :
6050 0 : const char *zero_width_node::type()
6051 : {
6052 0 : return "zero-width output node";
6053 : }
6054 :
6055 7 : bool zero_width_node::causes_tprint()
6056 : {
6057 7 : return false;
6058 : }
6059 :
6060 370 : bool zero_width_node::is_tag()
6061 : {
6062 370 : return false;
6063 : }
6064 :
6065 0 : bool italic_corrected_node::is_same_as(node *nd)
6066 : {
6067 0 : return ((x == static_cast<italic_corrected_node *>(nd)->x)
6068 0 : && same_node(nodes,
6069 0 : static_cast<italic_corrected_node *>(nd)->nodes));
6070 : }
6071 :
6072 0 : const char *italic_corrected_node::type()
6073 : {
6074 0 : return "italic-corrected node";
6075 : }
6076 :
6077 99 : bool italic_corrected_node::causes_tprint()
6078 : {
6079 99 : return false;
6080 : }
6081 :
6082 10369 : bool italic_corrected_node::is_tag()
6083 : {
6084 10369 : return false;
6085 : }
6086 :
6087 33795 : left_italic_corrected_node::left_italic_corrected_node(node *xx)
6088 33795 : : container_node(xx)
6089 : {
6090 33795 : }
6091 :
6092 6971 : left_italic_corrected_node::left_italic_corrected_node(statem *s,
6093 : int divlevel,
6094 6971 : node *xx)
6095 6971 : : container_node(xx, s, divlevel)
6096 : {
6097 6971 : }
6098 :
6099 0 : void left_italic_corrected_node::dump_properties()
6100 : {
6101 0 : node::dump_properties();
6102 0 : fprintf(stderr, ", \"hunits\": %d", x.to_units());
6103 0 : fflush(stderr);
6104 0 : }
6105 :
6106 51376 : node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
6107 : {
6108 51376 : if (0 /* nullptr */ == nodes) {
6109 31600 : hunits lic = gn->left_italic_correction();
6110 31600 : if (!lic.is_zero()) {
6111 20635 : x = lic;
6112 20635 : nodes = gn;
6113 20635 : return this;
6114 : }
6115 : }
6116 : else {
6117 19776 : node *nd = nodes->merge_glyph_node(gn);
6118 19776 : if (nd != 0 /* nullptr */) {
6119 2254 : nodes = nd;
6120 2254 : x = nodes->left_italic_correction();
6121 2254 : return this;
6122 : }
6123 : }
6124 28487 : return 0 /* nullptr */;
6125 : }
6126 :
6127 6346 : node *left_italic_corrected_node::copy()
6128 : {
6129 : left_italic_corrected_node *nd =
6130 6346 : new left_italic_corrected_node(state, div_nest_level);
6131 6346 : if (nodes != 0 /* nullptr */) {
6132 3792 : nd->nodes = nodes->copy();
6133 3792 : nd->x = x;
6134 : }
6135 6346 : return nd;
6136 : }
6137 :
6138 22186 : void left_italic_corrected_node::tprint(troff_output_file *out)
6139 : {
6140 22186 : if (nodes != 0 /* nullptr */) {
6141 10235 : out->right(x);
6142 10235 : nodes->tprint(out);
6143 : }
6144 22186 : }
6145 :
6146 0 : const char *left_italic_corrected_node::type()
6147 : {
6148 0 : return "left italic-corrected node";
6149 : }
6150 :
6151 39 : bool left_italic_corrected_node::causes_tprint()
6152 : {
6153 39 : return false;
6154 : }
6155 :
6156 20673 : bool left_italic_corrected_node::is_tag()
6157 : {
6158 20673 : return false;
6159 : }
6160 :
6161 0 : bool left_italic_corrected_node::is_same_as(node *nd)
6162 : {
6163 0 : return ((x == static_cast<left_italic_corrected_node *>(nd)->x)
6164 0 : && same_node(nodes,
6165 0 : static_cast<left_italic_corrected_node *>(nd)->nodes));
6166 : }
6167 :
6168 10 : void left_italic_corrected_node::ascii_print(ascii_output_file *out)
6169 : {
6170 10 : if (nodes != 0 /* nullptr */)
6171 10 : nodes->ascii_print(out);
6172 10 : }
6173 :
6174 144671 : hunits left_italic_corrected_node::width()
6175 : {
6176 289342 : return (nodes != 0 /* nullptr */) ? (nodes->width() + x) : H0;
6177 : }
6178 :
6179 961 : void left_italic_corrected_node::vertical_extent(vunits *minimum,
6180 : vunits *maximum)
6181 : {
6182 961 : if (nodes != 0 /* nullptr */)
6183 470 : nodes->vertical_extent(minimum, maximum);
6184 : else
6185 491 : node::vertical_extent(minimum, maximum);
6186 961 : }
6187 :
6188 190 : hunits left_italic_corrected_node::skew()
6189 : {
6190 380 : return (nodes != 0 /* nullptr */) ? (nodes->skew() + x / 2) : H0;
6191 : }
6192 :
6193 190 : hunits left_italic_corrected_node::subscript_correction()
6194 : {
6195 190 : return (nodes != 0 /* nullptr */) ? nodes->subscript_correction()
6196 190 : : H0;
6197 : }
6198 :
6199 2834 : hunits left_italic_corrected_node::italic_correction()
6200 : {
6201 2834 : return (nodes != 0 /* nullptr */) ? nodes->italic_correction() : H0;
6202 : }
6203 :
6204 2902 : int left_italic_corrected_node::ends_sentence()
6205 : {
6206 2902 : return (nodes != 0 /* nullptr */) ? nodes->ends_sentence() : 0;
6207 : }
6208 :
6209 0 : bool left_italic_corrected_node::overlaps_horizontally()
6210 : {
6211 0 : return (nodes != 0 /* nullptr */) ? nodes->overlaps_horizontally()
6212 0 : : false;
6213 : }
6214 :
6215 0 : bool left_italic_corrected_node::overlaps_vertically()
6216 : {
6217 0 : return (nodes != 0 /* nullptr */) ? nodes->overlaps_vertically()
6218 0 : : false;
6219 : }
6220 :
6221 0 : node *left_italic_corrected_node::last_char_node()
6222 : {
6223 0 : return (nodes != 0 /* nullptr */) ? nodes->last_char_node()
6224 0 : : 0 /* nullptr */;
6225 : }
6226 :
6227 2583 : tfont *left_italic_corrected_node::get_tfont()
6228 : {
6229 2583 : return (nodes != 0 /* nullptr */) ? nodes->get_tfont()
6230 2583 : : 0 /* nullptr */;
6231 : }
6232 :
6233 1319 : hyphenation_type left_italic_corrected_node::get_hyphenation_type()
6234 : {
6235 1319 : if (nodes != 0 /* nullptr */)
6236 625 : return nodes->get_hyphenation_type();
6237 : else
6238 694 : return HYPHENATION_PERMITTED;
6239 : }
6240 :
6241 1319 : hyphen_list *left_italic_corrected_node::get_hyphen_list(
6242 : hyphen_list *tail, int *count)
6243 : {
6244 1319 : return (nodes != 0 /* nullptr */)
6245 1319 : ? nodes->get_hyphen_list(tail, count) : tail;
6246 : }
6247 :
6248 1319 : node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
6249 : {
6250 1319 : if (nodes != 0 /* nullptr */) {
6251 625 : nd = new left_italic_corrected_node(state, div_nest_level, nd);
6252 625 : nd = nodes->add_self(nd, p);
6253 625 : nodes = 0 /* nullptr */;
6254 625 : delete this;
6255 625 : return nd;
6256 : }
6257 : else {
6258 694 : next = nd;
6259 694 : return this;
6260 : }
6261 : }
6262 :
6263 961 : int left_italic_corrected_node::character_type()
6264 : {
6265 961 : return (nodes != 0 /* nullptr */) ? nodes->character_type() : 0;
6266 : }
6267 :
6268 0 : bool overstrike_node::is_same_as(node *nd)
6269 : {
6270 0 : return same_node_list(nodes, ((overstrike_node *)nd)->nodes);
6271 : }
6272 :
6273 0 : const char *overstrike_node::type()
6274 : {
6275 0 : return "overstricken node";
6276 : }
6277 :
6278 0 : bool overstrike_node::causes_tprint()
6279 : {
6280 0 : return false;
6281 : }
6282 :
6283 105 : bool overstrike_node::is_tag()
6284 : {
6285 105 : return false;
6286 : }
6287 :
6288 0 : node *overstrike_node::add_self(node *more_nodes, hyphen_list **p)
6289 : {
6290 0 : next = more_nodes;
6291 0 : hyphen_list *pp = *p;
6292 0 : *p = (*p)->next;
6293 0 : delete pp;
6294 0 : return this;
6295 : }
6296 :
6297 0 : hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail, int *)
6298 : {
6299 0 : return new hyphen_list(0U, tail);
6300 : }
6301 :
6302 0 : bool bracket_node::is_same_as(node *nd)
6303 : {
6304 0 : return same_node_list(nodes, ((bracket_node *)nd)->nodes);
6305 : }
6306 :
6307 0 : const char *bracket_node::type()
6308 : {
6309 0 : return "bracket-building node";
6310 : }
6311 :
6312 0 : bool bracket_node::causes_tprint()
6313 : {
6314 0 : return false;
6315 : }
6316 :
6317 0 : bool bracket_node::is_tag()
6318 : {
6319 0 : return false;
6320 : }
6321 :
6322 6075 : bool composite_node::is_same_as(node *nd)
6323 : {
6324 6075 : return ((ci == static_cast<composite_node *>(nd)->ci)
6325 6075 : && same_node(nodes,
6326 6075 : static_cast<composite_node *>(nd)->nodes));
6327 : }
6328 :
6329 12203 : const char *composite_node::type()
6330 : {
6331 12203 : return "composite node"; // XXX: composite WHAT? (character macro?)
6332 : }
6333 :
6334 28 : bool composite_node::causes_tprint()
6335 : {
6336 28 : return false;
6337 : }
6338 :
6339 6169 : bool composite_node::is_tag()
6340 : {
6341 6169 : return false;
6342 : }
6343 :
6344 1179015 : bool glyph_node::is_same_as(node *nd)
6345 : {
6346 1179015 : return ((ci == static_cast<glyph_node *>(nd)->ci)
6347 666694 : && (tf == static_cast<glyph_node *>(nd)->tf)
6348 666694 : && (gcol == static_cast<glyph_node *>(nd)->gcol)
6349 1845709 : && (fcol == static_cast<glyph_node *>(nd)->fcol));
6350 : }
6351 :
6352 2435977 : const char *glyph_node::type()
6353 : {
6354 2435977 : return "glyph node";
6355 : }
6356 :
6357 94645 : bool glyph_node::causes_tprint()
6358 : {
6359 94645 : return false;
6360 : }
6361 :
6362 2349134 : bool glyph_node::is_tag()
6363 : {
6364 2349134 : return false;
6365 : }
6366 :
6367 1 : bool ligature_node::is_same_as(node *nd)
6368 : {
6369 1 : return (same_node(n1, ((ligature_node *)nd)->n1)
6370 1 : && same_node(n2, ((ligature_node *)nd)->n2)
6371 2 : && glyph_node::is_same_as(nd));
6372 : }
6373 :
6374 2 : const char *ligature_node::type()
6375 : {
6376 2 : return "ligature node";
6377 : }
6378 :
6379 342 : bool ligature_node::causes_tprint()
6380 : {
6381 342 : return false;
6382 : }
6383 :
6384 4324 : bool ligature_node::is_tag()
6385 : {
6386 4324 : return false;
6387 : }
6388 :
6389 19812 : bool kern_pair_node::is_same_as(node *nd)
6390 : {
6391 19812 : return ((amount == static_cast<kern_pair_node *>(nd)->amount)
6392 19357 : && same_node(n1, static_cast<kern_pair_node *>(nd)->n1)
6393 39169 : && same_node(n2, static_cast<kern_pair_node *>(nd)->n2));
6394 : }
6395 :
6396 109407 : const char *kern_pair_node::type()
6397 : {
6398 109407 : return "kerned character pair node";
6399 : }
6400 :
6401 3121 : bool kern_pair_node::causes_tprint()
6402 : {
6403 3121 : return false;
6404 : }
6405 :
6406 43710 : bool kern_pair_node::is_tag()
6407 : {
6408 43710 : return false;
6409 : }
6410 :
6411 0 : bool dbreak_node::is_same_as(node *nd)
6412 : {
6413 0 : return (same_node_list(none, ((dbreak_node *)nd)->none)
6414 0 : && same_node_list(pre, ((dbreak_node *)nd)->pre)
6415 0 : && same_node_list(post, ((dbreak_node *)nd)->post));
6416 : }
6417 :
6418 0 : const char *dbreak_node::type()
6419 : {
6420 0 : return "discretionary breakpoint node";
6421 : }
6422 :
6423 215 : bool dbreak_node::causes_tprint()
6424 : {
6425 215 : return false;
6426 : }
6427 :
6428 11093 : bool dbreak_node::is_tag()
6429 : {
6430 11093 : return false;
6431 : }
6432 :
6433 0 : void dbreak_node::dump_node()
6434 : {
6435 0 : fputc('{', stderr);
6436 : // Flush so that in case something goes wrong with property dumping,
6437 : // we know that we traversed to a new node.
6438 0 : fflush(stderr);
6439 0 : node::dump_properties();
6440 0 : if (none != 0 /* nullptr */) {
6441 0 : fputs(", \"none\": ", stderr);
6442 0 : none->dump_node();
6443 : }
6444 0 : if (pre != 0 /* nullptr */) {
6445 0 : fputs(", \"pre\": ", stderr);
6446 0 : pre->dump_node();
6447 : }
6448 0 : if (post != 0 /* nullptr */) {
6449 0 : fputs(", \"post\": ", stderr);
6450 0 : post->dump_node();
6451 : }
6452 0 : fputc('}', stderr);
6453 0 : fflush(stderr);
6454 0 : }
6455 :
6456 18560 : bool break_char_node::is_same_as(node *nd)
6457 : {
6458 18560 : return (break_code == static_cast<break_char_node *>(nd)->break_code)
6459 18560 : && (col == static_cast<break_char_node *>(nd)->col)
6460 37120 : && (same_node(nodes,
6461 18560 : static_cast<break_char_node *>(nd)->nodes));
6462 : }
6463 :
6464 39598 : const char *break_char_node::type()
6465 : {
6466 39598 : return "breakpoint node";
6467 : }
6468 :
6469 369 : bool break_char_node::causes_tprint()
6470 : {
6471 369 : return false;
6472 : }
6473 :
6474 9472 : bool break_char_node::is_tag()
6475 : {
6476 9472 : return false;
6477 : }
6478 :
6479 5566 : unsigned char break_char_node::get_break_code()
6480 : {
6481 5566 : return break_code;
6482 : }
6483 :
6484 66688 : bool line_start_node::is_same_as(node *)
6485 : {
6486 66688 : return true;
6487 : }
6488 :
6489 136245 : const char *line_start_node::type()
6490 : {
6491 136245 : return "output line start node";
6492 : }
6493 :
6494 8262 : bool line_start_node::causes_tprint()
6495 : {
6496 8262 : return false;
6497 : }
6498 :
6499 467990 : bool line_start_node::is_tag()
6500 : {
6501 467990 : return false;
6502 : }
6503 :
6504 90 : bool space_node::is_same_as(node *nd)
6505 : {
6506 90 : return ((n == static_cast<space_node *>(nd)->n)
6507 90 : && (set == static_cast<space_node *>(nd)->set)
6508 180 : && (col == static_cast<space_node *>(nd)->col));
6509 : }
6510 :
6511 384 : const char *space_node::type()
6512 : {
6513 384 : return "space node";
6514 : }
6515 :
6516 5 : bool word_space_node::is_same_as(node *nd)
6517 : {
6518 5 : return ((n == static_cast<word_space_node *>(nd)->n)
6519 5 : && (set == static_cast<word_space_node *>(nd)->set)
6520 10 : && (col == static_cast<word_space_node *>(nd)->col));
6521 : }
6522 :
6523 2699 : const char *word_space_node::type()
6524 : {
6525 2699 : return "word space node";
6526 : }
6527 :
6528 18341 : bool word_space_node::causes_tprint()
6529 : {
6530 18341 : return false;
6531 : }
6532 :
6533 404786 : bool word_space_node::is_tag()
6534 : {
6535 404786 : return false;
6536 : }
6537 :
6538 5700 : void unbreakable_space_node::tprint(troff_output_file *out)
6539 : {
6540 5700 : out->fill_color(col);
6541 5700 : out->word_marker();
6542 5700 : if (is_writing_html) {
6543 : // we emit the space width as a negative glyph index
6544 120 : out->flush_tbuf();
6545 120 : out->do_motion();
6546 120 : out->put('N');
6547 120 : out->put(-n.to_units());
6548 120 : out->put('\n');
6549 : }
6550 5700 : out->right(n);
6551 5700 : }
6552 :
6553 1 : bool unbreakable_space_node::is_same_as(node *nd)
6554 : {
6555 1 : return n == ((unbreakable_space_node *)nd)->n
6556 1 : && set == ((unbreakable_space_node *)nd)->set
6557 2 : && col == ((unbreakable_space_node *)nd)->col;
6558 : }
6559 :
6560 2 : const char *unbreakable_space_node::type()
6561 : {
6562 2 : return "unbreakable space node";
6563 : }
6564 :
6565 363 : node *unbreakable_space_node::add_self(node *nd, hyphen_list **p)
6566 : {
6567 363 : next = nd;
6568 363 : hyphen_list *pp = *p;
6569 363 : *p = (*p)->next;
6570 363 : delete pp;
6571 363 : return this;
6572 : }
6573 :
6574 363 : hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail,
6575 : int *)
6576 : {
6577 363 : return new hyphen_list(0, tail);
6578 : }
6579 :
6580 0 : bool diverted_space_node::is_same_as(node *nd)
6581 : {
6582 0 : return n == ((diverted_space_node *)nd)->n;
6583 : }
6584 :
6585 0 : const char *diverted_space_node::type()
6586 : {
6587 0 : return "diverted vertical space node";
6588 : }
6589 :
6590 0 : bool diverted_space_node::causes_tprint()
6591 : {
6592 0 : return false;
6593 : }
6594 :
6595 0 : bool diverted_space_node::is_tag()
6596 : {
6597 0 : return false;
6598 : }
6599 :
6600 0 : bool diverted_copy_file_node::is_same_as(node *nd)
6601 : {
6602 0 : return filename == ((diverted_copy_file_node *)nd)->filename;
6603 : }
6604 :
6605 0 : const char *diverted_copy_file_node::type()
6606 : {
6607 0 : return "diverted file throughput node";
6608 : }
6609 :
6610 0 : bool diverted_copy_file_node::causes_tprint()
6611 : {
6612 0 : return false;
6613 : }
6614 :
6615 0 : bool diverted_copy_file_node::is_tag()
6616 : {
6617 0 : return false;
6618 : }
6619 :
6620 : // Grow the font_table so that its size is > n.
6621 :
6622 2567 : static void grow_font_table(int n)
6623 : {
6624 2567 : assert(n >= font_table_size);
6625 2567 : font_info **old_font_table = font_table;
6626 2567 : int old_font_table_size = font_table_size;
6627 2567 : font_table_size = font_table_size ? ((font_table_size * 3) / 2) : 10;
6628 2567 : if (font_table_size <= n)
6629 0 : font_table_size = n + 10;
6630 2567 : font_table = new font_info *[font_table_size];
6631 2567 : if (old_font_table_size)
6632 1149 : memcpy(font_table, old_font_table,
6633 1149 : old_font_table_size*sizeof(font_info *));
6634 2567 : delete[] old_font_table;
6635 29237 : for (int i = old_font_table_size; i < font_table_size; i++)
6636 26670 : font_table[i] = 0 /* nullptr */;
6637 2567 : }
6638 :
6639 : dictionary font_translation_dictionary(17);
6640 :
6641 272418 : static symbol get_font_translation(symbol nm)
6642 : {
6643 272418 : void *p = font_translation_dictionary.lookup(nm);
6644 272418 : return p ? symbol(static_cast<char *>(p)) : nm;
6645 : }
6646 :
6647 : dictionary font_dictionary(50);
6648 : // We store the address of this font in `font_dictionary` to indicate
6649 : // that we've previously tried to mount the font and failed.
6650 : font nonexistent_font = font("\0");
6651 :
6652 : // Mount font at position `n` with troff identifier `name` and
6653 : // description file name `filename` (these are often identical). If
6654 : // `check_only`, just look up `name` in the existing list of mounted
6655 : // fonts.
6656 21540 : static bool mount_font_no_translate(int n, symbol name, symbol filename,
6657 : bool check_only = false)
6658 : {
6659 21540 : assert(n >= 0);
6660 21540 : font *fm = 0 /* nullptr */;
6661 21540 : void *p = font_dictionary.lookup(filename);
6662 21540 : if (0 /* nullptr */ == p) {
6663 19018 : fm = font::load_font(filename.contents(), check_only);
6664 19018 : if (check_only)
6665 5447 : return fm != 0 /* nullptr */;
6666 13571 : if (0 /* nullptr */ == fm) {
6667 2 : (void) font_dictionary.lookup(filename, &nonexistent_font);
6668 2 : return false;
6669 : }
6670 13569 : (void) font_dictionary.lookup(name, fm);
6671 : }
6672 2522 : else if (&nonexistent_font == p)
6673 1 : return false;
6674 : else
6675 2521 : fm = static_cast<font *>(p);
6676 16090 : if (check_only)
6677 2493 : return true;
6678 13597 : if (n >= font_table_size) {
6679 2205 : if ((n - font_table_size) > 1000) {
6680 0 : error("requested font mounting position %1 too much larger than"
6681 : " first unused position %2", n,
6682 0 : next_available_font_position());
6683 0 : return false;
6684 : }
6685 2205 : grow_font_table(n);
6686 : }
6687 11392 : else if (font_table[n] != 0 /* nullptr */)
6688 0 : delete font_table[n];
6689 13597 : font_table[n] = new font_info(name, n, filename, fm);
6690 13597 : font_family::invalidate_fontno(n);
6691 13597 : return true;
6692 : }
6693 :
6694 0 : void print_font_mounting_position_request()
6695 : {
6696 0 : for (int i = 0; i < font_table_size; i++) {
6697 0 : font_info *fi = font_table[i];
6698 0 : if (0 /* nullptr */ == fi) // No font mounted here.
6699 0 : continue;
6700 0 : errprint("%1\t%2", i, fi->get_name().contents());
6701 0 : font *f = font_table[i]->get_font();
6702 0 : if (f != 0 /* nullptr */) { // It's not an abstract style.
6703 0 : errprint("\t%1", f->get_filename());
6704 0 : if (f->get_internal_name() != 0 /* nullptr */)
6705 0 : errprint("\t%1", f->get_internal_name());
6706 : }
6707 0 : errprint("\n");
6708 0 : fflush(stderr);
6709 : }
6710 0 : skip_line();
6711 0 : }
6712 :
6713 13307 : bool mount_font(int n, symbol name, symbol external_name)
6714 : {
6715 13307 : assert(n >= 0);
6716 13307 : name = get_font_translation(name);
6717 13307 : if (external_name.is_null())
6718 13278 : external_name = name;
6719 : else
6720 29 : external_name = get_font_translation(external_name);
6721 13307 : return mount_font_no_translate(n, name, external_name);
6722 : }
6723 :
6724 : // True for abstract styles and resolved font names.
6725 7940 : bool is_font_name(symbol fam, symbol name)
6726 : {
6727 7940 : if (is_abstract_style(name))
6728 24 : name = concat(fam, name);
6729 7940 : return mount_font_no_translate(0, name, name, true /* check only */);
6730 : }
6731 :
6732 7942 : bool is_abstract_style(symbol s)
6733 : {
6734 7942 : int i = symbol_fontno(s);
6735 7942 : return i < 0 ? 0 : font_table[i]->is_style();
6736 : }
6737 :
6738 3744 : bool mount_style(int n, symbol name)
6739 : {
6740 3744 : assert(n >= 0);
6741 3744 : if (n >= font_table_size) {
6742 362 : if ((n - font_table_size) > 1000) {
6743 0 : error("font position too much larger than first unused position");
6744 0 : return false;
6745 : }
6746 362 : grow_font_table(n);
6747 : }
6748 3382 : else if (font_table[n] != 0 /* nullptr */)
6749 0 : delete font_table[n];
6750 3744 : font_table[n] = new font_info(get_font_translation(name), n,
6751 3744 : NULL_SYMBOL, 0);
6752 3744 : font_family::invalidate_fontno(n);
6753 3744 : return true;
6754 : }
6755 :
6756 : // True for valid (not necessarily used) font mounting positions.
6757 21588 : static bool is_nonnegative_integer(const char *str)
6758 : {
6759 21588 : return strspn(str, "0123456789") == strlen(str);
6760 : }
6761 :
6762 10794 : static void translate_font()
6763 : {
6764 10794 : if (!has_arg()) {
6765 0 : warning(WARN_MISSING, "font translation request expects one or two"
6766 : " font name arguments");
6767 0 : skip_line();
6768 0 : return;
6769 : }
6770 10794 : symbol from = read_identifier(true /* required */);
6771 : // has_arg()+read_identifier() should ensure the assertion succeeds.
6772 10794 : assert(!from.is_null());
6773 10794 : if (is_nonnegative_integer(from.contents())) {
6774 0 : error("cannot translate a font mounting position");
6775 0 : skip_line();
6776 0 : return;
6777 : }
6778 10794 : symbol to = read_identifier();
6779 10794 : if ((!to.is_null()) && is_nonnegative_integer(to.contents())) {
6780 0 : error("cannot translate to a font mounting position");
6781 0 : skip_line();
6782 0 : return;
6783 : }
6784 10794 : if (to.is_null() || from == to)
6785 2871 : font_translation_dictionary.remove(from);
6786 : else
6787 7923 : (void) font_translation_dictionary.lookup(from,
6788 7923 : (void *)to.contents());
6789 10794 : skip_line();
6790 : }
6791 :
6792 0 : static void print_font_translation_request()
6793 : {
6794 0 : dictionary_iterator iter(font_translation_dictionary);
6795 0 : symbol from, to;
6796 : // We must use the nuclear `reinterpret_cast` operator because GNU
6797 : // troff's dictionary types use a pre-STL approach to containers.
6798 0 : while (iter.get(&from, reinterpret_cast<void **>(&to)))
6799 0 : errprint("%1\t%2\n", from.contents(), to.contents());
6800 0 : fflush(stderr);
6801 0 : skip_line();
6802 0 : return;
6803 : }
6804 :
6805 30 : static void mount_font_at_position()
6806 : {
6807 30 : if (!has_arg()) {
6808 0 : warning(WARN_MISSING, "font mounting request expects arguments");
6809 0 : skip_line();
6810 0 : return;
6811 : }
6812 : int n;
6813 30 : if (read_integer(&n)) {
6814 30 : if (n < 0)
6815 0 : error("font mounting position %1 is negative", n);
6816 : else {
6817 30 : symbol internal_name = read_identifier(true /* required */);
6818 30 : if (!internal_name.is_null()) {
6819 30 : symbol filename = read_long_identifier();
6820 30 : if (!mount_font(n, internal_name, filename)) {
6821 2 : string msg;
6822 2 : if (filename != 0 /* nullptr */)
6823 2 : msg += string(" from file '") + filename.contents()
6824 3 : + string("'");
6825 2 : msg += '\0';
6826 2 : error("cannot load font description '%1'%2 for mounting",
6827 4 : internal_name.contents(), msg.contents());
6828 : }
6829 : }
6830 : }
6831 : }
6832 30 : skip_line();
6833 : }
6834 :
6835 1452 : font_family::font_family(symbol s)
6836 1452 : : map_size(10), nm(s)
6837 : {
6838 1452 : map = new int[map_size];
6839 15972 : for (int i = 0; i < map_size; i++)
6840 14520 : map[i] = FONT_NOT_MOUNTED;
6841 1452 : }
6842 :
6843 0 : font_family::~font_family()
6844 : {
6845 0 : delete[] map;
6846 0 : }
6847 :
6848 : // Resolve a requested font mounting position to a mounting position
6849 : // usable by the output driver. (Positions 1 through 4 are typically
6850 : // allocated to styles, and are not usable thus.) A return value of
6851 : // `FONT_NOT_MOUNTED` indicates failure.
6852 14909627 : int font_family::resolve(int mounting_position)
6853 : {
6854 14909627 : assert(mounting_position >= 0);
6855 14909627 : int pos = mounting_position;
6856 14909627 : assert((pos >= 0) || (FONT_NOT_MOUNTED == pos));
6857 14909627 : if (pos < 0)
6858 0 : return FONT_NOT_MOUNTED;
6859 14909627 : if (pos < map_size && map[pos] >= 0)
6860 14900700 : return map[pos];
6861 8927 : if (!((pos < font_table_size)
6862 8927 : && (font_table[pos] != 0 /* nullptr */)))
6863 0 : return FONT_NOT_MOUNTED;
6864 8927 : if (pos >= map_size) {
6865 842 : int old_map_size = map_size;
6866 842 : int *old_map = map;
6867 842 : map_size *= 3;
6868 842 : map_size /= 2;
6869 842 : if (pos >= map_size)
6870 10 : map_size = pos + 10;
6871 842 : map = new int[map_size];
6872 842 : memcpy(map, old_map, old_map_size * sizeof (int));
6873 842 : delete[] old_map;
6874 9130 : for (int j = old_map_size; j < map_size; j++)
6875 8288 : map[j] = FONT_NOT_MOUNTED;
6876 : }
6877 8927 : if (!(font_table[pos]->is_style()))
6878 8444 : return map[pos] = pos;
6879 483 : symbol sty = font_table[pos]->get_name();
6880 483 : symbol f = concat(nm, sty);
6881 : int n;
6882 : // Don't use symbol_fontno, because that might return a style and
6883 : // because we don't want to translate the name.
6884 12776 : for (n = 0; n < font_table_size; n++)
6885 9728 : if ((font_table[n] != 0 /* nullptr */) && font_table[n]->is_named(f)
6886 22211 : && !font_table[n]->is_style())
6887 190 : break;
6888 483 : if (n >= font_table_size) {
6889 293 : n = next_available_font_position();
6890 293 : if (!mount_font_no_translate(n, f, f))
6891 0 : return FONT_NOT_MOUNTED;
6892 : }
6893 483 : return map[pos] = n;
6894 : }
6895 :
6896 : dictionary family_dictionary(5);
6897 :
6898 47326 : font_family *lookup_family(symbol nm)
6899 : {
6900 : font_family *f
6901 47326 : = static_cast<font_family *>(family_dictionary.lookup(nm));
6902 47326 : if (0 /* nullptr */ == f) {
6903 1452 : f = new font_family(nm);
6904 1452 : (void) family_dictionary.lookup(nm, f);
6905 : }
6906 47326 : return f;
6907 : }
6908 :
6909 17341 : void font_family::invalidate_fontno(int n)
6910 : {
6911 17341 : assert(n >= 0 && n < font_table_size);
6912 17341 : dictionary_iterator iter(family_dictionary);
6913 17341 : symbol nam;
6914 : font_family *fam;
6915 26897 : while (iter.get(&nam, (void **)&fam)) {
6916 9556 : int mapsize = fam->map_size;
6917 9556 : if (n < mapsize)
6918 6316 : fam->map[n] = FONT_NOT_MOUNTED;
6919 333650 : for (int i = 0; i < mapsize; i++)
6920 324094 : if (fam->map[i] == n)
6921 0 : fam->map[i] = FONT_NOT_MOUNTED;
6922 : }
6923 17341 : }
6924 :
6925 2632 : static void associate_style_with_font_position()
6926 : {
6927 2632 : if (!has_arg()) {
6928 0 : warning(WARN_MISSING, "abstract style configuration request expects"
6929 : " arguments");
6930 0 : skip_line();
6931 0 : return;
6932 : }
6933 : int n;
6934 2632 : if (read_integer(&n)) {
6935 2632 : if (n < 0)
6936 0 : error("font mounting position %1 is negative", n);
6937 : else {
6938 2632 : if (!has_arg())
6939 0 : warning(WARN_MISSING, "abstract style configuration request"
6940 : " expects a style name as second argument");
6941 : else {
6942 2632 : symbol internal_name = read_identifier(true /* required */);
6943 2632 : if (!internal_name.is_null())
6944 2632 : (void) mount_style(n, internal_name);
6945 : }
6946 : }
6947 : }
6948 2632 : skip_line();
6949 : }
6950 :
6951 0 : static void font_lookup_error(font_lookup_info& finfo,
6952 : const char *msg)
6953 : {
6954 0 : if (finfo.requested_name)
6955 0 : error("cannot select font '%1' %2", finfo.requested_name, msg);
6956 0 : else if (finfo.position == FONT_NOT_MOUNTED)
6957 0 : error("cannot select font %1", msg); // don't report position `-1`
6958 : else
6959 0 : error("cannot select font at position %1 %2",
6960 0 : finfo.requested_position, msg);
6961 0 : }
6962 :
6963 1668389 : bool is_valid_font_mounting_position(int n)
6964 : {
6965 : return ((n >= 0)
6966 1668389 : && (n < font_table_size)
6967 3336778 : && (font_table[n] != 0 /* nullptr */));
6968 : }
6969 :
6970 : // Read the next token and look it up as a font name or position number.
6971 : // Return lookup success. Store, in the supplied struct argument, the
6972 : // requested name or position, and the position actually resolved.
6973 11015 : static bool read_font_identifier(font_lookup_info *finfo)
6974 : {
6975 : int n;
6976 11015 : tok.skip_spaces();
6977 11015 : if (tok.is_usable_as_delimiter()) {
6978 10850 : symbol s = read_identifier(true /* required */);
6979 10850 : finfo->requested_name = const_cast<char *>(s.contents());
6980 10850 : if (!s.is_null()) {
6981 10850 : n = symbol_fontno(s);
6982 10850 : if (n < 0) {
6983 6532 : n = next_available_font_position();
6984 6532 : if (mount_font(n, s))
6985 6532 : finfo->position = n;
6986 : }
6987 10850 : finfo->position = curenv->get_family()->resolve(n);
6988 : }
6989 : }
6990 165 : else if (read_integer(&n)) {
6991 165 : finfo->requested_position = n;
6992 165 : if (is_valid_font_mounting_position(n))
6993 165 : finfo->position = curenv->get_family()->resolve(n);
6994 : }
6995 11015 : return (finfo->position != FONT_NOT_MOUNTED);
6996 : }
6997 :
6998 : static int underline_fontno = 2;
6999 :
7000 0 : static void select_underline_font()
7001 : {
7002 0 : if (!has_arg()) {
7003 0 : warning(WARN_MISSING, "underline font selection request expects an"
7004 : " argument");
7005 0 : skip_line();
7006 0 : return;
7007 : }
7008 0 : font_lookup_info finfo;
7009 0 : if (!read_font_identifier(&finfo))
7010 0 : font_lookup_error(finfo, "to make it the underline font");
7011 : else
7012 0 : underline_fontno = finfo.position;
7013 0 : skip_line();
7014 : }
7015 :
7016 9 : int get_underline_fontno()
7017 : {
7018 9 : return underline_fontno;
7019 : }
7020 :
7021 10492 : static void define_font_specific_character()
7022 : {
7023 10492 : if (!has_arg()) {
7024 0 : warning(WARN_MISSING, "font-specific fallback character definition"
7025 : " request expects arguments");
7026 0 : skip_line();
7027 0 : return;
7028 : }
7029 10492 : font_lookup_info finfo;
7030 10492 : if (!read_font_identifier(&finfo)) {
7031 0 : font_lookup_error(finfo, "to define font-specific fallback"
7032 : " character");
7033 : // Normally we skip the remainder of the line unconditionally at the
7034 : // end of a request-implementing function, but define_character()
7035 : // will eat the rest of it for us.
7036 0 : skip_line();
7037 : }
7038 : else {
7039 10492 : symbol f = font_table[finfo.position]->get_name();
7040 10492 : define_character(CHAR_FONT_SPECIFIC_FALLBACK, f.contents());
7041 : }
7042 : }
7043 :
7044 0 : static void remove_font_specific_character()
7045 : {
7046 0 : if (!has_arg()) {
7047 0 : warning(WARN_MISSING, "font-specific fallback character removal"
7048 : " request expects arguments");
7049 0 : skip_line();
7050 0 : return;
7051 : }
7052 0 : font_lookup_info finfo;
7053 0 : if (!read_font_identifier(&finfo))
7054 0 : font_lookup_error(finfo, "to remove font-specific fallback"
7055 : " character");
7056 : else {
7057 0 : symbol f = font_table[finfo.position]->get_name();
7058 0 : while (!tok.is_newline() && !tok.is_eof()) {
7059 0 : if (!tok.is_space() && !tok.is_tab()) {
7060 0 : charinfo *s = tok.get_charinfo(true /* required */);
7061 0 : if (0 /* nullptr */ == s)
7062 0 : assert(0 == "attempted to use token without charinfo in"
7063 : " font-specific character removal request");
7064 : else {
7065 0 : string gl(f.contents());
7066 0 : gl += ' ';
7067 0 : gl += s->nm.contents();
7068 0 : gl += '\0';
7069 0 : charinfo *ci = lookup_charinfo(symbol(gl.contents()));
7070 : // Expect a null pointer if the font-specific character was
7071 : // already removed or never existed.
7072 0 : if (ci != 0 /* nullptr */) {
7073 0 : macro *m = ci->set_macro(0 /* nullptr */);
7074 0 : if (m != 0 /* nullptr */)
7075 0 : delete m;
7076 : }
7077 : }
7078 : }
7079 0 : tok.next();
7080 : }
7081 : }
7082 0 : skip_line();
7083 : }
7084 :
7085 127 : static void read_special_fonts(special_font_list **sp)
7086 : {
7087 127 : special_font_list *s = *sp;
7088 127 : *sp = 0 /* nullptr */;
7089 141 : while (s != 0 /* nullptr */) {
7090 14 : special_font_list *tem = s;
7091 14 : s = s->next;
7092 14 : delete tem;
7093 : }
7094 127 : special_font_list **p = sp;
7095 370 : while (has_arg()) {
7096 243 : font_lookup_info finfo;
7097 243 : if (!read_font_identifier(&finfo))
7098 0 : font_lookup_error(finfo, "to mark it as special");
7099 : else {
7100 243 : special_font_list *tem = new special_font_list;
7101 243 : tem->n = finfo.position;
7102 243 : tem->next = 0 /* nullptr */;
7103 243 : *p = tem;
7104 243 : p = &(tem->next);
7105 : }
7106 : }
7107 127 : }
7108 :
7109 110 : static void set_font_specific_special_fonts()
7110 : {
7111 110 : if (!has_arg()) {
7112 0 : warning(WARN_MISSING, "font-specific special font selection request"
7113 : " expects at least one argument");
7114 0 : skip_line();
7115 0 : return;
7116 : }
7117 110 : font_lookup_info finfo;
7118 110 : if (!read_font_identifier(&finfo))
7119 0 : font_lookup_error(finfo, "to mark other fonts as special"
7120 : " contingently upon it"); // a mouthful :-/
7121 : else
7122 110 : read_special_fonts(&font_table[finfo.position]->sf);
7123 110 : skip_line();
7124 : }
7125 :
7126 17 : static void set_special_fonts()
7127 : {
7128 17 : read_special_fonts(&global_special_fonts);
7129 17 : skip_line();
7130 17 : }
7131 :
7132 0 : static void zoom_font()
7133 : {
7134 0 : if (!(has_arg())) {
7135 0 : warning(WARN_MISSING, "font zoom factor request expects arguments");
7136 0 : skip_line();
7137 0 : return;
7138 : }
7139 0 : symbol font_name = read_identifier();
7140 : // has_arg()+read_identifier() should ensure the assertion succeeds.
7141 0 : assert(font_name != 0 /* nullptr */);
7142 0 : if (is_nonnegative_integer(font_name.contents())) {
7143 0 : warning(WARN_FONT, "cannot set zoom factor of a font mounting"
7144 : " position");
7145 0 : skip_line();
7146 0 : return;
7147 : }
7148 0 : if (!(has_arg())) {
7149 0 : warning(WARN_MISSING, "font zoom factor request expects zoom factor"
7150 : " argument");
7151 0 : skip_line();
7152 0 : return;
7153 : }
7154 0 : int fpos = next_available_font_position();
7155 0 : if (!(mount_font(fpos, font_name))) {
7156 0 : error("cannot mount font '%1' to set a zoom factor for it",
7157 0 : font_name.contents());
7158 0 : skip_line();
7159 0 : return;
7160 : }
7161 : #if 0
7162 : // This would be a good diagnostic to have, but mount_font() is too
7163 : // formally complex to make it easy. Instead it will fail in the
7164 : // above test on a font named "R", for instance, when that is
7165 : // literally true but might not help users who don't understand that
7166 : // "R", "I", "B", and "BI" are (by default) abstract styles, not fonts
7167 : // in the GNU troff sense. It is a shame that a lot of our validation
7168 : // functions are willing only to handle arguments that they eat from
7169 : // the input stream (i.e., you can't pass them information you
7170 : // obtained elsewhere). That design also forces us to validate
7171 : // request arguments in the order they appear in the input, and seems
7172 : // unnecessarily inflexible to me. --GBR
7173 : if (font_table[fpos]->is_style()) {
7174 : warning(WARN_FONT, "ignoring request to set font zoom factor on an"
7175 : " abstract style");
7176 : skip_line();
7177 : return;
7178 : }
7179 : #endif
7180 0 : int zoom = 0;
7181 0 : read_integer(&zoom);
7182 0 : if (zoom < 0) {
7183 0 : warning(WARN_RANGE, "ignoring negative font zoom factor '%1'",
7184 0 : zoom);
7185 0 : skip_line();
7186 0 : return;
7187 : }
7188 0 : font_table[fpos]->set_zoom(zoom);
7189 0 : skip_line();
7190 : }
7191 :
7192 9486 : int next_available_font_position()
7193 : {
7194 : int i;
7195 385920 : for (i = 1;
7196 385920 : (i < font_table_size) && (font_table[i] != 0 /* nullptr */);
7197 : i++)
7198 : ;
7199 9486 : return i;
7200 : }
7201 :
7202 255338 : int symbol_fontno(symbol s)
7203 : {
7204 255338 : s = get_font_translation(s);
7205 1922118 : for (int i = 0; i < font_table_size; i++)
7206 3820228 : if ((font_table[i] != 0 /* nullptr */)
7207 1910114 : && font_table[i]->is_named(s))
7208 243334 : return i;
7209 12004 : return FONT_NOT_MOUNTED;
7210 : }
7211 :
7212 1 : hunits env_font_emboldening_offset(environment *env, int n)
7213 : {
7214 1 : if (is_valid_font_mounting_position(n)) {
7215 1 : hunits offset;
7216 1 : int fontno = env->get_family()->resolve(env->get_font());
7217 1 : if (font_table[fontno]->is_emboldened(&offset))
7218 1 : return offset.to_units() + 1;
7219 : else
7220 0 : return H0;
7221 : }
7222 : else
7223 0 : return H0;
7224 : }
7225 :
7226 1235 : hunits env_digit_width(environment *env)
7227 : {
7228 1235 : node *n = make_glyph_node(charset_table['0'], env);
7229 1235 : if (n != 0 /* nullptr */) {
7230 1235 : hunits x = n->width();
7231 1235 : delete n;
7232 1235 : return x;
7233 : }
7234 : else
7235 0 : return H0;
7236 : }
7237 :
7238 803676 : hunits env_space_width(environment *env)
7239 : {
7240 803676 : int fn = env_resolve_font(env);
7241 803676 : font_size fs = env->get_font_size();
7242 803676 : if (!is_valid_font_mounting_position(fn))
7243 0 : return scale(fs.to_units() / 3, env->get_space_size(), 12);
7244 : else
7245 803676 : return font_table[fn]->get_space_width(fs, env->get_space_size());
7246 : }
7247 :
7248 788303 : hunits env_sentence_space_width(environment *env)
7249 : {
7250 788303 : int fn = env_resolve_font(env);
7251 788303 : font_size fs = env->get_font_size();
7252 788303 : units sss = env->get_sentence_space_size();
7253 788303 : if (!is_valid_font_mounting_position(fn))
7254 0 : return scale(fs.to_units() / 3, sss, 12);
7255 : else
7256 788303 : return font_table[fn]->get_space_width(fs, sss);
7257 : }
7258 :
7259 310 : hunits env_half_narrow_space_width(environment *env)
7260 : {
7261 310 : int fn = env_resolve_font(env);
7262 310 : font_size fs = env->get_font_size();
7263 310 : if (!is_valid_font_mounting_position(fn))
7264 0 : return H0;
7265 : else
7266 310 : return font_table[fn]->get_half_narrow_space_width(fs);
7267 : }
7268 :
7269 5349 : hunits env_narrow_space_width(environment *env)
7270 : {
7271 5349 : int fn = env_resolve_font(env);
7272 5349 : font_size fs = env->get_font_size();
7273 5349 : if (!is_valid_font_mounting_position(fn))
7274 0 : return H0;
7275 : else
7276 5349 : return font_table[fn]->get_narrow_space_width(fs);
7277 : }
7278 :
7279 : // XXX: We can only conditionally (un)embolden a font specified by name,
7280 : // not position. Does ".bd 1 2" mean "embolden font position 1 by 2
7281 : // units" (really one unit), or "stop conditionally emboldening font 2
7282 : // when font 1 is selected"?
7283 6 : static void embolden_font()
7284 : {
7285 6 : if (!(has_arg())) {
7286 0 : warning(WARN_MISSING, "emboldening request expects arguments");
7287 0 : skip_line();
7288 0 : return;
7289 : }
7290 6 : if (in_nroff_mode) {
7291 0 : skip_line();
7292 0 : return;
7293 : }
7294 : // XXX: Here's where `is_ordinary_character()` would be useful.
7295 : // (TOKEN_CHAR == type) means the same thing, but this file doesn't
7296 : // root around in token::token_type...
7297 6 : if ((!tok.is_any_character())
7298 6 : || tok.is_special_character()
7299 12 : || tok.is_indexed_character()) {
7300 0 : error("emboldening request expects font name or mounting position"
7301 0 : " as first argument, got %1", tok.description());
7302 0 : skip_line();
7303 0 : return;
7304 : }
7305 : // If the 1st argument starts with a non-numeral, we must be prepared
7306 : // to expect a third argument to specify an emboldening amount...
7307 : // .bd S TB 3 \" embolden S when `TB` selected
7308 : // ...or removal of conditional emboldening.
7309 : // .bd S TB \" don't embolden S when `TB` selected
7310 : //
7311 : // XXX: Our approach forecloses the emboldening of fonts whose names
7312 : // _start_ with numerals but _contain_ non-numerals, like '3foo'. If
7313 : // one agrees that Kernighan's grammar for the extended `bd` request
7314 : // of device-independent troff is ambiguous, there may not be a
7315 : // perfect solution. (This approach could be improved by scanning
7316 : // ahead in the input, but is it worth the trouble?)
7317 6 : bool emboldening_may_be_conditional = false;
7318 6 : if (!csdigit(tok.ch()))
7319 3 : emboldening_may_be_conditional = true;
7320 6 : font_lookup_info finfo;
7321 6 : if (!read_font_identifier(&finfo)) {
7322 0 : font_lookup_error(finfo, "for emboldening");
7323 0 : skip_line();
7324 0 : return;
7325 : }
7326 6 : int n = finfo.position;
7327 6 : if (has_arg()) {
7328 : // XXX: Here's another useful `is_ordinary_character()` spot.
7329 5 : if ((!tok.is_any_character())
7330 5 : || tok.is_special_character()
7331 10 : || tok.is_indexed_character()) {
7332 0 : error("emboldening request expects font name or emboldening"
7333 0 : " amount as second argument, got %1", tok.description());
7334 0 : skip_line();
7335 0 : return;
7336 : }
7337 : // If both arguments start with numerals, assume this form.
7338 : // .bd 2 4 \" embolden font position 2 by 3 (yes, 3) units
7339 : // ...otherwise...
7340 5 : if (emboldening_may_be_conditional && !(csdigit(tok.ch()))) {
7341 2 : font_lookup_info finfo2;
7342 2 : if (!read_font_identifier(&finfo2)) {
7343 0 : font_lookup_error(finfo2, "for conditional emboldening");
7344 0 : skip_line();
7345 0 : return;
7346 : }
7347 2 : int f = finfo2.position;
7348 : units offset;
7349 2 : if (has_arg()
7350 1 : && read_measurement(&offset, 'u')
7351 3 : && (offset >= 1))
7352 1 : font_table[f]->set_conditional_bold(n, hunits(offset - 1));
7353 : else
7354 1 : font_table[f]->conditional_unbold(n);
7355 : }
7356 : else {
7357 : // The second argument must be an emboldening amount.
7358 : units offset;
7359 3 : if (read_measurement(&offset, 'u') && (offset >= 1))
7360 3 : font_table[n]->set_bold(hunits(offset - 1));
7361 : else
7362 0 : font_table[n]->unbold();
7363 3 : if (has_arg())
7364 1 : warning(WARN_FONT, "ignoring third argument to font emboldening"
7365 : " request when first interpreted as mounting position"
7366 : " and second as emboldening amount");
7367 : }
7368 : }
7369 : else
7370 1 : font_table[n]->unbold();
7371 6 : skip_line();
7372 : }
7373 :
7374 17341 : track_kerning_function::track_kerning_function() : non_zero(0)
7375 : {
7376 17341 : }
7377 :
7378 162 : track_kerning_function::track_kerning_function(int min_s, hunits min_a,
7379 162 : int max_s, hunits max_a)
7380 : : non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s),
7381 162 : max_amount(max_a)
7382 : {
7383 162 : }
7384 :
7385 0 : int track_kerning_function::operator==(const track_kerning_function &tk)
7386 : {
7387 0 : if (non_zero)
7388 0 : return (tk.non_zero
7389 0 : && min_size == tk.min_size
7390 0 : && min_amount == tk.min_amount
7391 0 : && max_size == tk.max_size
7392 0 : && max_amount == tk.max_amount);
7393 : else
7394 0 : return !tk.non_zero;
7395 : }
7396 :
7397 162 : int track_kerning_function::operator!=(const track_kerning_function &tk)
7398 : {
7399 162 : if (non_zero)
7400 141 : return (!tk.non_zero
7401 141 : || min_size != tk.min_size
7402 141 : || min_amount != tk.min_amount
7403 6 : || max_size != tk.max_size
7404 282 : || max_amount != tk.max_amount);
7405 : else
7406 21 : return tk.non_zero;
7407 : }
7408 :
7409 19136 : hunits track_kerning_function::compute(int size)
7410 : {
7411 19136 : if (non_zero) {
7412 560 : if (max_size <= min_size)
7413 560 : return min_amount;
7414 0 : else if (size <= min_size)
7415 0 : return min_amount;
7416 0 : else if (size >= max_size)
7417 0 : return max_amount;
7418 : else
7419 0 : return (scale(max_amount, size - min_size, max_size - min_size)
7420 0 : + scale(min_amount, max_size - size, max_size - min_size));
7421 : }
7422 : else
7423 18576 : return H0;
7424 : }
7425 :
7426 162 : static void configure_track_kerning()
7427 : {
7428 162 : if (!has_arg()) {
7429 0 : warning(WARN_MISSING, "track kerning request expects arguments");
7430 0 : skip_line();
7431 0 : return;
7432 : }
7433 162 : font_lookup_info finfo;
7434 162 : if (!read_font_identifier(&finfo))
7435 0 : font_lookup_error(finfo, "for track kerning");
7436 : else {
7437 162 : int n = finfo.position, min_s, max_s;
7438 162 : hunits min_a, max_a;
7439 162 : if (has_arg()
7440 162 : && read_measurement(&min_s, 'z')
7441 162 : && read_hunits(&min_a, 'p')
7442 162 : && read_measurement(&max_s, 'z')
7443 324 : && read_hunits(&max_a, 'p')) {
7444 162 : track_kerning_function tk(min_s, min_a, max_s, max_a);
7445 162 : font_table[n]->set_track_kern(tk);
7446 : }
7447 : else {
7448 0 : track_kerning_function tk;
7449 0 : font_table[n]->set_track_kern(tk);
7450 : }
7451 : }
7452 162 : skip_line();
7453 : }
7454 :
7455 0 : static void constantly_space_font()
7456 : {
7457 0 : if (!has_arg()) {
7458 0 : warning(WARN_MISSING, "constant spacing request expects arguments");
7459 0 : skip_line();
7460 0 : return;
7461 : }
7462 0 : font_lookup_info finfo;
7463 0 : if (!read_font_identifier(&finfo))
7464 0 : font_lookup_error(finfo, "for constant spacing");
7465 : else {
7466 0 : int n = finfo.position, x, y;
7467 0 : if (!has_arg() || !read_integer(&x))
7468 0 : font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
7469 : else {
7470 0 : if (!has_arg() || !read_measurement(&y, 'z'))
7471 0 : font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
7472 : else
7473 0 : font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
7474 : scale(y*x,
7475 : units_per_inch,
7476 : 36 * 72 * sizescale));
7477 : }
7478 : }
7479 0 : skip_line();
7480 : }
7481 :
7482 28 : static void set_ligature_mode()
7483 : {
7484 : int lig;
7485 28 : if (has_arg() && read_integer(&lig) && lig >= 0 && lig <= 2)
7486 0 : global_ligature_mode = lig;
7487 : else
7488 28 : global_ligature_mode = 1;
7489 28 : skip_line();
7490 28 : }
7491 :
7492 352 : static void set_kerning_mode()
7493 : {
7494 : int k;
7495 352 : if (has_arg() && read_integer(&k))
7496 162 : global_kern_mode = (k > 0);
7497 : else
7498 190 : global_kern_mode = true;
7499 352 : skip_line();
7500 352 : }
7501 :
7502 : // Set (soft) hyphenation character, used to mark where a discretionary
7503 : // break ("dbreak") has occurred in formatted output, conventionally
7504 : // within a word at a syllable boundary.
7505 : //
7506 : // XXX: The soft hyphen character is global; shouldn't it be
7507 : // environmental?
7508 0 : static void soft_hyphen_character_request()
7509 : {
7510 0 : soft_hyphen_char = read_character();
7511 0 : if (0 /* nullptr */ == soft_hyphen_char)
7512 0 : soft_hyphen_char = lookup_charinfo(HYPHEN_SYMBOL);
7513 0 : skip_line();
7514 0 : }
7515 :
7516 1279 : void init_output()
7517 : {
7518 1279 : if (want_output_suppressed)
7519 59 : the_output = new suppress_output_file;
7520 1220 : else if (want_abstract_output)
7521 26 : the_output = new ascii_output_file;
7522 : else
7523 1194 : the_output = new troff_output_file;
7524 1279 : }
7525 :
7526 : class next_available_font_position_reg : public reg {
7527 : public:
7528 : const char *get_string();
7529 : };
7530 :
7531 2632 : const char *next_available_font_position_reg::get_string()
7532 : {
7533 2632 : return i_to_a(next_available_font_position());
7534 : }
7535 :
7536 : class printing_reg : public reg {
7537 : public:
7538 : const char *get_string();
7539 : };
7540 :
7541 0 : const char *printing_reg::get_string()
7542 : {
7543 0 : if (the_output)
7544 0 : return the_output->is_selected_for_printing() ? "1" : "0";
7545 : else
7546 0 : return "0";
7547 : }
7548 :
7549 1418 : void init_node_requests()
7550 : {
7551 1418 : init_request("bd", embolden_font);
7552 1418 : init_request("cs", constantly_space_font);
7553 1418 : init_request("fp", mount_font_at_position);
7554 1418 : init_request("fschar", define_font_specific_character);
7555 1418 : init_request("fspecial", set_font_specific_special_fonts);
7556 1418 : init_request("fzoom", zoom_font);
7557 1418 : init_request("ftr", translate_font);
7558 1418 : init_request("kern", set_kerning_mode);
7559 1418 : init_request("lg", set_ligature_mode);
7560 1418 : init_request("pfp", print_font_mounting_position_request);
7561 1418 : init_request("pftr", print_font_translation_request);
7562 1418 : init_request("rfschar", remove_font_specific_character);
7563 1418 : init_request("shc", soft_hyphen_character_request);
7564 1418 : init_request("special", set_special_fonts);
7565 1418 : init_request("sty", associate_style_with_font_position);
7566 1418 : init_request("tkf", configure_track_kerning);
7567 1418 : init_request("uf", select_underline_font);
7568 1418 : register_dictionary.define(".fp",
7569 1418 : new next_available_font_position_reg);
7570 1418 : register_dictionary.define(".kern",
7571 1418 : new readonly_boolean_register(&global_kern_mode));
7572 1418 : register_dictionary.define(".lg",
7573 1418 : new readonly_register(&global_ligature_mode));
7574 1418 : register_dictionary.define(".P", new printing_reg);
7575 1418 : soft_hyphen_char = lookup_charinfo(HYPHEN_SYMBOL);
7576 1418 : }
7577 :
7578 : // Local Variables:
7579 : // fill-column: 72
7580 : // mode: C++
7581 : // End:
7582 : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
|