groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / preproc / eqn / other.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2002, 2007, 2009
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 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 #include "eqn.h"
22 #include "pbox.h"
23
24 class accent_box : public pointer_box {
25 private:
26   box *ab;
27 public:
28   accent_box(box *, box *);
29   ~accent_box();
30   int compute_metrics(int);
31   void output();
32   void debug_print();
33   void check_tabs(int);
34 };
35
36 box *make_accent_box(box *p, box *q)
37 {
38   return new accent_box(p, q);
39 }
40
41 accent_box::accent_box(box *pp, box *qq) : pointer_box(pp), ab(qq)
42 {
43 }
44
45 accent_box::~accent_box()
46 {
47   delete ab;
48 }
49
50 #if 0
51 int accent_box::compute_metrics(int style)
52 {
53   int r = p->compute_metrics(style);
54   p->compute_skew();
55   ab->compute_metrics(style);
56   printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
57   printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
58   printf(".nr " SUP_RAISE_FORMAT " \\n[" HEIGHT_FORMAT "]-%dM>?0\n",
59          uid, p->uid, x_height);
60   printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+\\n["
61          SUP_RAISE_FORMAT "]\n",
62          uid, ab->uid, uid);
63   return r;
64 }
65
66 void accent_box::output()
67 {
68   if (output_format == troff) {
69     printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u+\\n["
70            SKEW_FORMAT "]u'",
71            p->uid, ab->uid, p->uid);
72     printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); 
73     ab->output();
74     printf("\\h'-\\n[" WIDTH_FORMAT "]u'", ab->uid);
75     printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid);
76     printf("\\h'-(\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u+\\n["
77            SKEW_FORMAT "]u)'",
78            p->uid, ab->uid, p->uid);
79     p->output();
80   }
81   else if (output_format == mathml) {
82     printf("<mover accent='true'>");
83     p->output();
84     ab->output();
85     printf("</mover>")
86   }
87 }
88 #endif
89
90 /* This version copes with the possibility of an accent's being wider
91 than its accentee.  LEFT_WIDTH_FORMAT gives the distance from the
92 left edge of the resulting box to the middle of the accentee's box.*/
93
94 int accent_box::compute_metrics(int style)
95 {
96   int r = p->compute_metrics(style);
97   p->compute_skew();
98   ab->compute_metrics(style);
99   printf(".nr " LEFT_WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2"
100          ">?(\\n[" WIDTH_FORMAT "]/2-\\n[" SKEW_FORMAT "])\n",
101          uid, p->uid, ab->uid, p->uid);
102   printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2"
103          ">?(\\n[" WIDTH_FORMAT "]/2+\\n[" SKEW_FORMAT "])"
104          "+\\n[" LEFT_WIDTH_FORMAT "]\n",
105          uid, p->uid, ab->uid, p->uid, uid);
106   printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
107   printf(".nr " SUP_RAISE_FORMAT " \\n[" HEIGHT_FORMAT "]-%dM>?0\n",
108          uid, p->uid, x_height);
109   printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+\\n["
110          SUP_RAISE_FORMAT "]\n",
111          uid, ab->uid, uid);
112   if (r)
113     printf(".nr " MARK_REG " +\\n[" LEFT_WIDTH_FORMAT "]"
114            "-(\\n[" WIDTH_FORMAT "]/2)'\n",
115            uid, p->uid);
116   return r;
117 }
118
119 void accent_box::output()
120 {
121   if (output_format == troff) {
122     printf("\\Z" DELIMITER_CHAR);
123     printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u+\\n[" SKEW_FORMAT "]u"
124            "-(\\n[" WIDTH_FORMAT "]u/2u)'",
125            uid, p->uid, ab->uid);
126     printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); 
127     ab->output();
128     printf(DELIMITER_CHAR);
129     printf("\\Z" DELIMITER_CHAR);
130     printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u-(\\n[" WIDTH_FORMAT "]u/2u)'",
131            uid, p->uid);
132     p->output();
133     printf(DELIMITER_CHAR);
134     printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid);
135   }
136   else if (output_format == mathml) {
137     printf("<mover accent='true'>");
138     p->output();
139     ab->output();
140     printf("</mover>");
141   }
142 }
143
144 void accent_box::check_tabs(int level)
145 {
146   ab->check_tabs(level + 1);
147   p->check_tabs(level + 1);
148 }
149
150 void accent_box::debug_print()
151 {
152   fprintf(stderr, "{ ");
153   p->debug_print();
154   fprintf(stderr, " } accent { ");
155   ab->debug_print();
156   fprintf(stderr, " }");
157 }
158
159 class overline_char_box : public simple_box {
160 public:
161   overline_char_box();
162   void output();
163   void debug_print();
164 };
165
166 overline_char_box::overline_char_box()
167 {
168 }
169
170 void overline_char_box::output()
171 {
172   if (output_format == troff) {
173     printf("\\v'-%dM/2u-%dM'", 7*default_rule_thickness, x_height);
174     printf((draw_flag ? "\\D'l%dM 0'" : "\\l'%dM\\&\\(ru'"),
175            accent_width);
176     printf("\\v'%dM/2u+%dM'", 7*default_rule_thickness, x_height);
177   }
178   else if (output_format == mathml)
179     printf("<mo>&macr;</mo>");
180 }
181
182 void overline_char_box::debug_print()
183 {
184   fprintf(stderr, "<overline char>");
185 }
186
187 class overline_box : public pointer_box {
188 public:
189   overline_box(box *);
190   int compute_metrics(int);
191   void output();
192   void debug_print();
193 };
194
195 box *make_overline_box(box *p)
196 {
197   if (p->is_char())
198     return new accent_box(p, new overline_char_box);
199   else
200     return new overline_box(p);
201 }
202
203 overline_box::overline_box(box *pp) : pointer_box(pp)
204 {
205 }
206
207 int overline_box::compute_metrics(int style)
208 {
209   int r = p->compute_metrics(cramped_style(style));
210   // 9
211   printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+%dM\n",
212          uid, p->uid, default_rule_thickness*5);
213   printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
214   printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
215   return r;
216 }
217
218 void overline_box::output()
219 {
220   if (output_format == troff) {
221     // 9
222     printf("\\Z" DELIMITER_CHAR);
223     printf("\\v'-\\n[" HEIGHT_FORMAT "]u-(%dM/2u)'",
224            p->uid, 7*default_rule_thickness);
225     if (draw_flag)
226       printf("\\D'l\\n[" WIDTH_FORMAT "]u 0'", p->uid);
227     else
228       printf("\\l'\\n[" WIDTH_FORMAT "]u\\&\\(ru'", p->uid);
229     printf(DELIMITER_CHAR);
230     p->output();
231   }
232   else if (output_format == mathml) {
233     printf("<mover accent='false'>");
234     p->output();
235     printf("<mo>&macr;</mo></mover>");
236   }
237 }
238
239 void overline_box::debug_print()
240 {
241   fprintf(stderr, "{ ");
242   p->debug_print();
243   fprintf(stderr, " } bar");
244 }
245
246 class uaccent_box : public pointer_box {
247   box *ab;
248 public:
249   uaccent_box(box *, box *);
250   ~uaccent_box();
251   int compute_metrics(int);
252   void output();
253   void compute_subscript_kern();
254   void check_tabs(int);
255   void debug_print();
256 };
257
258 box *make_uaccent_box(box *p, box *q)
259 {
260   return new uaccent_box(p, q);
261 }
262
263 uaccent_box::uaccent_box(box *pp, box *qq)
264 : pointer_box(pp), ab(qq)
265 {
266 }
267
268 uaccent_box::~uaccent_box()
269 {
270   delete ab;
271 }
272
273 int uaccent_box::compute_metrics(int style)
274 {
275   int r = p->compute_metrics(style);
276   ab->compute_metrics(style);
277   printf(".nr " LEFT_WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2"
278          ">?(\\n[" WIDTH_FORMAT "]/2)\n",
279          uid, p->uid, ab->uid);
280   printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2"
281          ">?(\\n[" WIDTH_FORMAT "]/2)"
282          "+\\n[" LEFT_WIDTH_FORMAT "]\n",
283          uid, p->uid, ab->uid, uid);
284   printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
285   printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]"
286          "+\\n[" DEPTH_FORMAT "]\n",
287          uid, p->uid, ab->uid);
288   if (r)
289     printf(".nr " MARK_REG " +\\n[" LEFT_WIDTH_FORMAT "]"
290            "-(\\n[" WIDTH_FORMAT "]/2)'\n",
291            uid, p->uid);
292   return r;
293 }
294
295 void uaccent_box::output()
296 {
297   if (output_format == troff) {
298     printf("\\Z" DELIMITER_CHAR);
299     printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u-(\\n[" WIDTH_FORMAT "]u/2u)'",
300            uid, ab->uid);
301     printf("\\v'\\n[" DEPTH_FORMAT "]u'", p->uid); 
302     ab->output();
303     printf(DELIMITER_CHAR);
304     printf("\\Z" DELIMITER_CHAR);
305     printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u-(\\n[" WIDTH_FORMAT "]u/2u)'",
306            uid, p->uid);
307     p->output();
308     printf(DELIMITER_CHAR);
309     printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid);
310   }
311   else if (output_format == mathml) {
312     printf("<munder accent='true'>");
313     p->output();
314     ab->output();
315     printf("</munder>");
316   }
317 }
318
319 void uaccent_box::check_tabs(int level)
320 {
321   ab->check_tabs(level + 1);
322   p->check_tabs(level + 1);
323 }
324
325 void uaccent_box::compute_subscript_kern()
326 {
327   box::compute_subscript_kern(); // want 0 subscript kern
328 }
329
330 void uaccent_box::debug_print()
331 {
332   fprintf(stderr, "{ ");
333   p->debug_print();
334   fprintf(stderr, " } uaccent { ");
335   ab->debug_print();
336   fprintf(stderr, " }");
337 }
338
339 class underline_char_box : public simple_box {
340 public:
341   underline_char_box();
342   void output();
343   void debug_print();
344 };
345
346 underline_char_box::underline_char_box()
347 {
348 }
349
350 void underline_char_box::output()
351 {
352   if (output_format == troff) {
353     printf("\\v'%dM/2u'", 7*default_rule_thickness);
354     printf((draw_flag ? "\\D'l%dM 0'" : "\\l'%dM\\&\\(ru'"),
355            accent_width);
356     printf("\\v'-%dM/2u'", 7*default_rule_thickness);
357   }
358   else if (output_format == mathml)
359     printf("<mo>&lowbar;</mo>");
360 }
361
362 void underline_char_box::debug_print()
363 {
364   fprintf(stderr, "<underline char>");
365 }
366
367
368 class underline_box : public pointer_box {
369 public:
370   underline_box(box *);
371   int compute_metrics(int);
372   void output();
373   void compute_subscript_kern();
374   void debug_print();
375 };
376
377 box *make_underline_box(box *p)
378 {
379   if (p->is_char())
380     return new uaccent_box(p, new underline_char_box);
381   else
382     return new underline_box(p);
383 }
384
385 underline_box::underline_box(box *pp) : pointer_box(pp)
386 {
387 }
388
389 int underline_box::compute_metrics(int style)
390 {
391   int r = p->compute_metrics(style);
392   // 10
393   printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]+%dM\n",
394          uid, p->uid, default_rule_thickness*5);
395   printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
396   printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
397   return r;
398 }
399
400 void underline_box::output()
401 {
402   if (output_format == troff) {
403     // 10
404     printf("\\Z" DELIMITER_CHAR);
405     printf("\\v'\\n[" DEPTH_FORMAT "]u+(%dM/2u)'",
406            p->uid, 7*default_rule_thickness);
407     if (draw_flag)
408       printf("\\D'l\\n[" WIDTH_FORMAT "]u 0'", p->uid);
409     else
410       printf("\\l'\\n[" WIDTH_FORMAT "]u\\&\\(ru'", p->uid);
411     printf(DELIMITER_CHAR);
412     p->output();
413   }
414   else if (output_format == mathml) {
415     printf("<munder accent='true'>");
416     p->output();
417     printf("<mo>&macr;</mo></munder>");
418   }
419 }
420
421 // we want an underline box to have 0 subscript kern
422
423 void underline_box::compute_subscript_kern()
424 {
425   box::compute_subscript_kern();
426 }
427
428 void underline_box::debug_print()
429 {
430   fprintf(stderr, "{ ");
431   p->debug_print();
432   fprintf(stderr, " } under");
433 }
434
435 size_box::size_box(char *s, box *pp) : pointer_box(pp), size(s)
436 {
437 }
438
439 int size_box::compute_metrics(int style)
440 {
441   printf(".nr " SIZE_FORMAT " \\n[.ps]\n", uid);
442   printf(".ps %s\n", size);
443   printf(".nr " SMALL_SIZE_FORMAT " \\n[.ps]\n", uid);
444   int r = p->compute_metrics(style);
445   printf(".ps \\n[" SIZE_FORMAT "]u\n", uid);
446   printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
447   printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
448   printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
449   return r;
450 }
451
452 void size_box::output()
453 {
454   if (output_format == troff) {
455     printf("\\s[\\n[" SMALL_SIZE_FORMAT "]u]", uid);
456     p->output();
457     printf("\\s[\\n[" SIZE_FORMAT "]u]", uid);
458   }
459   else if (output_format == mathml) {
460     printf("<mstyle mathsize='%s'>", size);
461     p->output();
462     printf("</mstyle>");
463   }
464 }
465
466 size_box::~size_box()
467 {
468   a_delete size;
469 }
470
471 void size_box::debug_print()
472 {
473   fprintf(stderr, "size %s { ", size);
474   p->debug_print();
475   fprintf(stderr, " }");
476 }
477
478
479 font_box::font_box(char *s, box *pp) : pointer_box(pp), f(s)
480 {
481 }
482
483 font_box::~font_box()
484 {
485   a_delete f;
486 }
487
488 int font_box::compute_metrics(int style)
489 {
490   const char *old_roman_font = current_roman_font;
491   current_roman_font = f;
492   printf(".nr " FONT_FORMAT " \\n[.f]\n", uid);
493   printf(".ft %s\n", f);
494   int r = p->compute_metrics(style);
495   current_roman_font = old_roman_font;
496   printf(".ft \\n[" FONT_FORMAT "]\n", uid);
497   printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
498   printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
499   printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
500   return r;
501 }
502
503 void font_box::output()
504 {
505   if (output_format == troff) {
506     printf("\\f[%s]", f);
507     const char *old_roman_font = current_roman_font;
508     current_roman_font = f;
509     p->output();
510     current_roman_font = old_roman_font;
511     printf("\\f[\\n[" FONT_FORMAT "]]", uid);
512   }
513   else if (output_format == mathml) {
514     const char *mlfont = f;
515     // bold and italic are already in MathML; translate eqn roman here
516     switch (f[0]) {
517     case 'I':
518     case 'i':
519       mlfont = "italic";
520       break;
521     case 'B':
522     case 'b':
523       mlfont = "bold";
524       break;
525     case 'R':
526     case 'r':
527     default:
528       mlfont = "normal";
529       break;
530     }
531     printf("<mstyle mathvariant='%s'>", mlfont);
532     p->output();
533     printf("</mstyle>");
534   }
535 }
536
537 void font_box::debug_print()
538 {
539   fprintf(stderr, "font %s { ", f);
540   p->debug_print();
541   fprintf(stderr, " }");
542 }
543
544 fat_box::fat_box(box *pp) : pointer_box(pp)
545 {
546 }
547
548 int fat_box::compute_metrics(int style)
549 {
550   int r = p->compute_metrics(style);
551   printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]+%dM\n",
552          uid, p->uid, fat_offset);
553   printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
554   printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
555   return r;
556 }
557
558 void fat_box::output()
559 {
560   if (output_format == troff) {
561     p->output();
562     printf("\\h'-\\n[" WIDTH_FORMAT "]u'", p->uid);
563     printf("\\h'%dM'", fat_offset);
564     p->output();
565   }
566   else if (output_format == mathml) {
567     printf("<mstyle mathvariant='double-struck'>");
568     p->output();
569     printf("</mstyle>");
570   }
571 }
572
573
574 void fat_box::debug_print()
575 {
576   fprintf(stderr, "fat { ");
577   p->debug_print();
578   fprintf(stderr, " }");
579 }
580
581
582 vmotion_box::vmotion_box(int i, box *pp) : pointer_box(pp), n(i)
583 {
584 }
585
586 int vmotion_box::compute_metrics(int style)
587 {
588   int r = p->compute_metrics(style);
589   printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
590   if (n > 0) {
591     printf(".nr " HEIGHT_FORMAT " %dM+\\n[" HEIGHT_FORMAT "]\n",
592            uid, n, p->uid);
593     printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
594   }
595   else {
596     printf(".nr " DEPTH_FORMAT " %dM+\\n[" DEPTH_FORMAT "]>?0\n",
597            uid, -n, p->uid);
598     printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n",
599            uid, p->uid);
600   }
601   return r;
602 }
603
604 void vmotion_box::output()
605 {
606   if (output_format == troff) {
607     printf("\\v'%dM'", -n);
608     p->output();
609     printf("\\v'%dM'", n);
610   }
611   else if (output_format == mathml) {
612     printf("<merror>eqn vertical motion cannot be expressed "
613            "in MathML</merror>");
614     p->output();
615   }
616 }
617
618 void vmotion_box::debug_print()
619 {
620   if (n >= 0)
621     fprintf(stderr, "up %d { ", n);
622   else
623     fprintf(stderr, "down %d { ", -n);
624   p->debug_print();
625   fprintf(stderr, " }");
626 }
627
628 hmotion_box::hmotion_box(int i, box *pp) : pointer_box(pp), n(i)
629 {
630 }
631
632 int hmotion_box::compute_metrics(int style)
633 {
634   int r = p->compute_metrics(style);
635   printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]+%dM\n",
636          uid, p->uid, n);
637   printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
638   printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
639   if (r)
640     printf(".nr " MARK_REG " +%dM\n", n);
641   return r;
642 }
643
644 void hmotion_box::output()
645 {
646   if (output_format == troff) {
647     printf("\\h'%dM'", n);
648     p->output();
649   }
650   else if (output_format == mathml) {
651     printf("<merror>eqn horizontal motion cannot be expessed "
652            "in MathML</merror>");
653     p->output();
654   }
655 }
656
657 void hmotion_box::debug_print()
658 {
659   if (n >= 0)
660     fprintf(stderr, "fwd %d { ", n);
661   else
662     fprintf(stderr, "back %d { ", -n);
663   p->debug_print();
664   fprintf(stderr, " }");
665 }
666
667 vcenter_box::vcenter_box(box *pp) : pointer_box(pp)
668 {
669 }
670
671 int vcenter_box::compute_metrics(int style)
672 {
673   int r = p->compute_metrics(style);
674   printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
675   printf(".nr " SUP_RAISE_FORMAT " \\n[" DEPTH_FORMAT "]-\\n["
676          HEIGHT_FORMAT "]/2+%dM\n",
677          uid, p->uid, p->uid, axis_height);
678   printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+\\n["
679          SUP_RAISE_FORMAT "]>?0\n", uid, p->uid, uid);
680   printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]-\\n["
681          SUP_RAISE_FORMAT "]>?0\n", uid, p->uid, uid);
682
683   return r;
684 }
685
686 void vcenter_box::output()
687 {
688   if (output_format == troff)
689     printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid);
690   p->output();
691   if (output_format == troff)
692     printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid);
693 }
694
695 void vcenter_box::debug_print()
696 {
697   fprintf(stderr, "vcenter { ");
698   p->debug_print();
699   fprintf(stderr, " }");
700 }
701