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