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