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