groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / preproc / eqn / box.cpp
CommitLineData
92d0a6a6 1// -*- C++ -*-
4d3e9548 2/* Copyright (C) 1989, 1990, 1991, 1992, 2002, 2004, 2007, 2009
92d0a6a6
JR
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
5
6This file is part of groff.
7
8groff is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
4d3e9548
JL
10Software Foundation, either version 3 of the License, or
11(at your option) any later version.
92d0a6a6
JR
12
13groff is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16for more details.
17
4d3e9548
JL
18You should have received a copy of the GNU General Public License
19along with this program. If not, see <http://www.gnu.org/licenses/>. */
92d0a6a6
JR
20
21#include "eqn.h"
22#include "pbox.h"
23
24const char *current_roman_font;
25
26char *gfont = 0;
27char *grfont = 0;
28char *gbfont = 0;
29int gsize = 0;
30
31int script_size_reduction = -1; // negative means reduce by a percentage
32
33int positive_space = -1;
34int negative_space = -1;
35
36int minimum_size = 5;
37
38int fat_offset = 4;
39int body_height = 85;
40int body_depth = 35;
41
42int over_hang = 0;
43int accent_width = 31;
44int delimiter_factor = 900;
45int delimiter_shortfall = 50;
46
47int null_delimiter_space = 12;
48int script_space = 5;
49int thin_space = 17;
50int medium_space = 22;
51int thick_space = 28;
52
53int num1 = 70;
54int num2 = 40;
55// we don't use num3, because we don't have \atop
56int denom1 = 70;
57int denom2 = 36;
58int axis_height = 26; // in 100ths of an em
59int sup1 = 42;
60int sup2 = 37;
61int sup3 = 28;
62int default_rule_thickness = 4;
63int sub1 = 20;
64int sub2 = 23;
65int sup_drop = 38;
66int sub_drop = 5;
67int x_height = 45;
68int big_op_spacing1 = 11;
69int big_op_spacing2 = 17;
70int big_op_spacing3 = 20;
71int big_op_spacing4 = 60;
72int big_op_spacing5 = 10;
73
74// These are for piles and matrices.
75
76int baseline_sep = 140; // = num1 + denom1
77int shift_down = 26; // = axis_height
78int column_sep = 100; // = em space
79int matrix_side_sep = 17; // = thin space
80
81int nroff = 0; // should we grok ndefine or tdefine?
82
83struct S {
84 const char *name;
85 int *ptr;
86} param_table[] = {
87 { "fat_offset", &fat_offset },
88 { "over_hang", &over_hang },
89 { "accent_width", &accent_width },
90 { "delimiter_factor", &delimiter_factor },
91 { "delimiter_shortfall", &delimiter_shortfall },
92 { "null_delimiter_space", &null_delimiter_space },
93 { "script_space", &script_space },
94 { "thin_space", &thin_space },
95 { "medium_space", &medium_space },
96 { "thick_space", &thick_space },
97 { "num1", &num1 },
98 { "num2", &num2 },
99 { "denom1", &denom1 },
100 { "denom2", &denom2 },
101 { "axis_height", &axis_height },
102 { "sup1", &sup1 },
103 { "sup2", &sup2 },
104 { "sup3", &sup3 },
105 { "default_rule_thickness", &default_rule_thickness },
106 { "sub1", &sub1 },
107 { "sub2", &sub2 },
108 { "sup_drop", &sup_drop },
109 { "sub_drop", &sub_drop },
110 { "x_height", &x_height },
111 { "big_op_spacing1", &big_op_spacing1 },
112 { "big_op_spacing2", &big_op_spacing2 },
113 { "big_op_spacing3", &big_op_spacing3 },
114 { "big_op_spacing4", &big_op_spacing4 },
115 { "big_op_spacing5", &big_op_spacing5 },
116 { "minimum_size", &minimum_size },
117 { "baseline_sep", &baseline_sep },
118 { "shift_down", &shift_down },
119 { "column_sep", &column_sep },
120 { "matrix_side_sep", &matrix_side_sep },
121 { "draw_lines", &draw_flag },
122 { "body_height", &body_height },
123 { "body_depth", &body_depth },
124 { "nroff", &nroff },
125 { 0, 0 }
126};
127
128void set_param(const char *name, int value)
129{
130 for (int i = 0; param_table[i].name != 0; i++)
131 if (strcmp(param_table[i].name, name) == 0) {
132 *param_table[i].ptr = value;
133 return;
134 }
135 error("unrecognised parameter `%1'", name);
136}
137
138int script_style(int style)
139{
140 return style > SCRIPT_STYLE ? style - 2 : style;
141}
142
143int cramped_style(int style)
144{
145 return (style & 1) ? style - 1 : style;
146}
147
148void set_space(int n)
149{
150 if (n < 0)
151 negative_space = -n;
152 else
153 positive_space = n;
154}
155
156// Return 0 if the specified size is bad.
157// The caller is responsible for giving the error message.
158
159int set_gsize(const char *s)
160{
161 const char *p = (*s == '+' || *s == '-') ? s + 1 : s;
162 char *end;
163 long n = strtol(p, &end, 10);
164 if (n <= 0 || *end != '\0' || n > INT_MAX)
165 return 0;
166 if (p > s) {
167 if (!gsize)
168 gsize = 10;
169 if (*s == '+') {
170 if (gsize > INT_MAX - n)
171 return 0;
172 gsize += int(n);
173 }
174 else {
175 if (gsize - n <= 0)
176 return 0;
177 gsize -= int(n);
178 }
179 }
180 else
181 gsize = int(n);
182 return 1;
183}
184
185void set_script_reduction(int n)
186{
187 script_size_reduction = n;
188}
189
190const char *get_gfont()
191{
192 return gfont ? gfont : "I";
193}
194
195const char *get_grfont()
196{
197 return grfont ? grfont : "R";
198}
199
200const char *get_gbfont()
201{
202 return gbfont ? gbfont : "B";
203}
204
205void set_gfont(const char *s)
206{
207 a_delete gfont;
208 gfont = strsave(s);
209}
210
211void set_grfont(const char *s)
212{
213 a_delete grfont;
214 grfont = strsave(s);
215}
216
217void set_gbfont(const char *s)
218{
219 a_delete gbfont;
220 gbfont = strsave(s);
221}
222
223// this must be precisely 2 characters in length
224#define COMPATIBLE_REG "0C"
225
226void start_string()
227{
4d3e9548
JL
228 if (output_format == troff) {
229 printf(".nr " COMPATIBLE_REG " \\n(.C\n");
230 printf(".cp 0\n");
231 printf(".ds " LINE_STRING "\n");
232 }
92d0a6a6
JR
233}
234
235void output_string()
236{
4d3e9548
JL
237 if (output_format == troff)
238 printf("\\*(" LINE_STRING "\n");
239 else if (output_format == mathml && !xhtml)
240 putchar('\n');
92d0a6a6
JR
241}
242
243void restore_compatibility()
244{
4d3e9548
JL
245 if (output_format == troff)
246 printf(".cp \\n(" COMPATIBLE_REG "\n");
92d0a6a6
JR
247}
248
249void do_text(const char *s)
250{
4d3e9548
JL
251 if (output_format == troff) {
252 printf(".eo\n");
253 printf(".as " LINE_STRING " \"%s\n", s);
254 printf(".ec\n");
255 }
256 else if (output_format == mathml) {
257 fputs(s, stdout);
258 if (xhtml && strlen(s) > 0)
259 printf("\n");
260 }
92d0a6a6
JR
261}
262
263void set_minimum_size(int n)
264{
265 minimum_size = n;
266}
267
268void set_script_size()
269{
270 if (minimum_size < 0)
271 minimum_size = 0;
272 if (script_size_reduction >= 0)
273 printf(".ps \\n[.s]-%d>?%d\n", script_size_reduction, minimum_size);
274 else
275 printf(".ps (u;\\n[.ps]*7+5/10>?%d)\n", minimum_size);
276}
277
278int box::next_uid = 0;
279
280box::box() : spacing_type(ORDINARY_TYPE), uid(next_uid++)
281{
282}
283
284box::~box()
285{
286}
287
288void box::top_level()
289{
92d0a6a6 290 box *b = this;
4d3e9548
JL
291 if (output_format == troff) {
292 // debug_print();
293 // putc('\n', stderr);
294 printf(".nr " SAVED_FONT_REG " \\n[.f]\n");
295 printf(".ft\n");
296 printf(".nr " SAVED_PREV_FONT_REG " \\n[.f]\n");
297 printf(".ft %s\n", get_gfont());
298 printf(".nr " SAVED_SIZE_REG " \\n[.ps]\n");
299 if (gsize > 0) {
300 char buf[INT_DIGITS + 1];
301 sprintf(buf, "%d", gsize);
302 b = new size_box(strsave(buf), b);
303 }
304 current_roman_font = get_grfont();
305 // This catches tabs used within \Z (which aren't allowed).
306 b->check_tabs(0);
307 int r = b->compute_metrics(DISPLAY_STYLE);
308 printf(".ft \\n[" SAVED_PREV_FONT_REG "]\n");
309 printf(".ft \\n[" SAVED_FONT_REG "]\n");
310 printf(".nr " MARK_OR_LINEUP_FLAG_REG " %d\n", r);
311 if (r == FOUND_MARK) {
312 printf(".nr " SAVED_MARK_REG " \\n[" MARK_REG "]\n");
313 printf(".nr " MARK_WIDTH_REG " 0\\n[" WIDTH_FORMAT "]\n", b->uid);
314 }
315 else if (r == FOUND_LINEUP)
316 printf(".if r" SAVED_MARK_REG " .as1 " LINE_STRING " \\h'\\n["
317 SAVED_MARK_REG "]u-\\n[" MARK_REG "]u'\n");
318 else
319 assert(r == FOUND_NOTHING);
320 // If we use \R directly, the space will prevent it working in a
321 // macro argument; so we hide it in a string instead.
322 printf(".ds " SAVE_FONT_STRING " "
323 "\\R'" SAVED_INLINE_FONT_REG " \\En[.f]'"
324 "\\fP"
325 "\\R'" SAVED_INLINE_PREV_FONT_REG " \\En[.f]'"
326 "\\R'" SAVED_INLINE_SIZE_REG " \\En[.ps]'"
327 "\\s0"
328 "\\R'" SAVED_INLINE_PREV_SIZE_REG " \\En[.ps]'"
329 "\n"
330 ".ds " RESTORE_FONT_STRING " "
331 "\\f[\\En[" SAVED_INLINE_PREV_FONT_REG "]]"
332 "\\f[\\En[" SAVED_INLINE_FONT_REG "]]"
333 "\\s'\\En[" SAVED_INLINE_PREV_SIZE_REG "]u'"
334 "\\s'\\En[" SAVED_INLINE_SIZE_REG "]u'"
335 "\n");
336 printf(".as1 " LINE_STRING " \\&\\E*[" SAVE_FONT_STRING "]");
337 printf("\\f[%s]", get_gfont());
338 printf("\\s'\\En[" SAVED_SIZE_REG "]u'");
339 current_roman_font = get_grfont();
340 b->output();
341 printf("\\E*[" RESTORE_FONT_STRING "]\n");
342 if (r == FOUND_LINEUP)
343 printf(".if r" SAVED_MARK_REG " .as1 " LINE_STRING " \\h'\\n["
344 MARK_WIDTH_REG "]u-\\n[" SAVED_MARK_REG "]u-(\\n["
345 WIDTH_FORMAT "]u-\\n[" MARK_REG "]u)'\n",
346 b->uid);
347 b->extra_space();
348 if (!inline_flag)
349 printf(".ne \\n[" HEIGHT_FORMAT "]u-%dM>?0+(\\n["
350 DEPTH_FORMAT "]u-%dM>?0)\n",
351 b->uid, body_height, b->uid, body_depth);
92d0a6a6 352 }
4d3e9548
JL
353 else if (output_format == mathml) {
354 if (xhtml)
355 printf(".MATHML ");
356 printf("<math>");
357 b->output();
358 printf("</math>");
92d0a6a6 359 }
92d0a6a6
JR
360 delete b;
361 next_uid = 0;
362}
363
364// gpic defines this register so as to make geqn not produce `\x's
365#define EQN_NO_EXTRA_SPACE_REG "0x"
366
367void box::extra_space()
368{
369 printf(".if !r" EQN_NO_EXTRA_SPACE_REG " "
370 ".nr " EQN_NO_EXTRA_SPACE_REG " 0\n");
371 if (positive_space >= 0 || negative_space >= 0) {
372 if (positive_space > 0)
373 printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
374 ".as1 " LINE_STRING " \\x'-%dM'\n", positive_space);
375 if (negative_space > 0)
376 printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
377 ".as1 " LINE_STRING " \\x'%dM'\n", negative_space);
378 positive_space = negative_space = -1;
379 }
380 else {
381 printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
382 ".if \\n[" HEIGHT_FORMAT "]>%dM .as1 " LINE_STRING
383 " \\x'-(\\n[" HEIGHT_FORMAT
384 "]u-%dM)'\n",
385 uid, body_height, uid, body_height);
386 printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
387 ".if \\n[" DEPTH_FORMAT "]>%dM .as1 " LINE_STRING
388 " \\x'\\n[" DEPTH_FORMAT
389 "]u-%dM'\n",
390 uid, body_depth, uid, body_depth);
391 }
392}
393
394int box::compute_metrics(int)
395{
396 printf(".nr " WIDTH_FORMAT " 0\n", uid);
397 printf(".nr " HEIGHT_FORMAT " 0\n", uid);
398 printf(".nr " DEPTH_FORMAT " 0\n", uid);
399 return FOUND_NOTHING;
400}
401
402void box::compute_subscript_kern()
403{
404 printf(".nr " SUB_KERN_FORMAT " 0\n", uid);
405}
406
407void box::compute_skew()
408{
409 printf(".nr " SKEW_FORMAT " 0\n", uid);
410}
411
412void box::output()
413{
414}
415
416void box::check_tabs(int)
417{
418}
419
420int box::is_char()
421{
422 return 0;
423}
424
425int box::left_is_italic()
426{
427 return 0;
428}
429
430int box::right_is_italic()
431{
432 return 0;
433}
434
435void box::hint(unsigned)
436{
437}
438
439void box::handle_char_type(int, int)
440{
441}
442
443
444box_list::box_list(box *pp)
445{
446 p = new box*[10];
447 for (int i = 0; i < 10; i++)
448 p[i] = 0;
449 maxlen = 10;
450 len = 1;
451 p[0] = pp;
452}
453
454void box_list::append(box *pp)
455{
456 if (len + 1 > maxlen) {
457 box **oldp = p;
458 maxlen *= 2;
459 p = new box*[maxlen];
460 memcpy(p, oldp, sizeof(box*)*len);
461 a_delete oldp;
462 }
463 p[len++] = pp;
464}
465
466box_list::~box_list()
467{
468 for (int i = 0; i < len; i++)
469 delete p[i];
470 a_delete p;
471}
472
473void box_list::list_check_tabs(int level)
474{
475 for (int i = 0; i < len; i++)
476 p[i]->check_tabs(level);
477}
478
479
480pointer_box::pointer_box(box *pp) : p(pp)
481{
482 spacing_type = p->spacing_type;
483}
484
485pointer_box::~pointer_box()
486{
487 delete p;
488}
489
490int pointer_box::compute_metrics(int style)
491{
492 int r = p->compute_metrics(style);
493 printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
494 printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
495 printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
496 return r;
497}
498
499void pointer_box::compute_subscript_kern()
500{
501 p->compute_subscript_kern();
502 printf(".nr " SUB_KERN_FORMAT " \\n[" SUB_KERN_FORMAT "]\n", uid, p->uid);
503}
504
505void pointer_box::compute_skew()
506{
507 p->compute_skew();
508 printf(".nr " SKEW_FORMAT " 0\\n[" SKEW_FORMAT "]\n",
509 uid, p->uid);
510}
511
512void pointer_box::check_tabs(int level)
513{
514 p->check_tabs(level);
515}
516
517int simple_box::compute_metrics(int)
518{
519 printf(".nr " WIDTH_FORMAT " 0\\w" DELIMITER_CHAR, uid);
520 output();
521 printf(DELIMITER_CHAR "\n");
522 printf(".nr " HEIGHT_FORMAT " 0>?\\n[rst]\n", uid);
523 printf(".nr " DEPTH_FORMAT " 0-\\n[rsb]>?0\n", uid);
524 printf(".nr " SUB_KERN_FORMAT " 0-\\n[ssc]>?0\n", uid);
525 printf(".nr " SKEW_FORMAT " 0\\n[skw]\n", uid);
526 return FOUND_NOTHING;
527}
528
529void simple_box::compute_subscript_kern()
530{
531 // do nothing, we already computed it in do_metrics
532}
533
534void simple_box::compute_skew()
535{
536 // do nothing, we already computed it in do_metrics
537}
538
539int box::is_simple()
540{
541 return 0;
542}
543
544int simple_box::is_simple()
545{
546 return 1;
547}
548
549quoted_text_box::quoted_text_box(char *s) : text(s)
550{
551}
552
553quoted_text_box::~quoted_text_box()
554{
555 a_delete text;
556}
557
558void quoted_text_box::output()
559{
4d3e9548
JL
560 if (text) {
561 if (output_format == troff)
562 fputs(text, stdout);
563 else if (output_format == mathml) {
564 fputs("<mtext>", stdout);
565 fputs(text, stdout);
566 fputs("</mtext>", stdout);
567 }
568 }
92d0a6a6
JR
569}
570
571tab_box::tab_box() : disabled(0)
572{
573}
574
575// We treat a tab_box as having width 0 for width computations.
576
577void tab_box::output()
578{
579 if (!disabled)
580 printf("\\t");
581}
582
583void tab_box::check_tabs(int level)
584{
585 if (level > 0) {
586 error("tabs allowed only at outermost level");
587 disabled = 1;
588 }
589}
590
591space_box::space_box()
592{
593 spacing_type = SUPPRESS_TYPE;
594}
595
596void space_box::output()
597{
4d3e9548
JL
598 if (output_format == troff)
599 printf("\\h'%dM'", thick_space);
600 else if (output_format == mathml)
601 // &ThickSpace; doesn't display right under Firefox 1.5.
602 printf("<mtext>&ensp;</mtext>");
92d0a6a6
JR
603}
604
605half_space_box::half_space_box()
606{
607 spacing_type = SUPPRESS_TYPE;
608}
609
610void half_space_box::output()
611{
4d3e9548
JL
612 if (output_format == troff)
613 printf("\\h'%dM'", thin_space);
614 else if (output_format == mathml)
615 printf("<mtext>&ThinSpace;</mtext>");
92d0a6a6
JR
616}
617
618void box_list::list_debug_print(const char *sep)
619{
620 p[0]->debug_print();
621 for (int i = 1; i < len; i++) {
622 fprintf(stderr, "%s", sep);
623 p[i]->debug_print();
624 }
625}
626
627void quoted_text_box::debug_print()
628{
629 fprintf(stderr, "\"%s\"", (text ? text : ""));
630}
631
632void half_space_box::debug_print()
633{
634 fprintf(stderr, "^");
635}
636
637void space_box::debug_print()
638{
639 fprintf(stderr, "~");
640}
641
642void tab_box::debug_print()
643{
644 fprintf(stderr, "<tab>");
645}