merge the formfield patch from ooo-build
[ooovba.git] / oox / source / export / preset-definitions-to-shape-types.pl
blob1608f8cf90786262f4651a45785e8778d6b7b4a9
1 #! /usr/bin/perl -w
2 #*************************************************************************
4 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 #
6 # Copyright 2008 by Sun Microsystems, Inc.
8 # OpenOffice.org - a multi-platform office productivity suite
10 # This file is part of OpenOffice.org.
12 # OpenOffice.org is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU Lesser General Public License version 3
14 # only, as published by the Free Software Foundation.
16 # OpenOffice.org is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU Lesser General Public License version 3 for more details
20 # (a copy is included in the LICENSE file that accompanied this code).
22 # You should have received a copy of the GNU Lesser General Public License
23 # version 3 along with OpenOffice.org. If not, see
24 # <http://www.openoffice.org/license.html>
25 # for a copy of the LGPLv3 License.
27 #*************************************************************************
29 use warnings;
31 sub usage() {
32 print STDERR <<EOF;
33 Usage: preset-definitions-to-shape-types.pl <shapes> <text>
35 Converts presetShapeDefinitions.xml and presetTextWarpDefinitions.xml to a
36 .cxx that contains VML with the definitions of the shapes. The result is
37 written to stdout.
39 <shapes> presetShapeDefinitions.xml (including the path to it)
40 <text> presetTextWarpDefinitions.xml (including the path to it)
41 EOF
42 exit 1;
45 sub show_call_stack
47 my ( $path, $line, $subr );
48 my $max_depth = 30;
49 my $i = 1;
50 print STDERR "--- Begin stack trace ---\n";
51 while ( (my @call_details = (caller($i++))) && ($i<$max_depth) ) {
52 print STDERR "$call_details[1] line $call_details[2] in function $call_details[3]\n";
54 print STDERR "--- End stack trace ---\n";
57 $src_shapes = shift;
58 $src_text = shift;
60 usage() if ( !defined( $src_shapes ) || !defined( $src_text ) ||
61 $src_shapes eq "-h" || $src_shapes eq "--help" ||
62 !-f $src_shapes || !-f $src_text );
64 # Global variables
65 @levels = ();
66 $shape_name = "";
67 $state = "";
68 $path = "";
69 $adjust = "";
70 $max_adj_no = 0;
71 @formulas = ();
72 %variables = ();
73 $ignore_this_shape = 0;
74 $handles = "";
75 $textboxrect = "";
76 $last_pos_x = "";
77 $last_pos_y = "";
78 $no_stroke = 0;
79 $no_fill = 0;
80 $path_w = 1;
81 $path_h = 1;
82 @quadratic_bezier = ();
84 %result_shapes = ();
86 %shapes_ids = (
87 0 => 'notPrimitive',
88 1 => 'rectangle',
89 2 => 'roundRectangle',
90 3 => 'ellipse',
91 4 => 'diamond',
92 5 => 'triangle',
93 6 => 'rtTriangle',
94 7 => 'parallelogram',
95 8 => 'trapezoid',
96 9 => 'hexagon',
97 10 => 'octagon',
98 11 => 'plus',
99 12 => 'star5',
100 13 => 'rightArrow',
101 14 => 'thickArrow', # should not be used
102 15 => 'homePlate',
103 16 => 'cube',
104 17 => 'wedgeRoundRectCallout', # balloon
105 18 => 'star16', # seal
106 19 => 'arc',
107 20 => 'line',
108 21 => 'plaque',
109 22 => 'can',
110 23 => 'donut',
111 24 => 'textPlain', # textSimple - FIXME MS Office 2007 converts these to textboxes with unstyled text, so is it actually correct to map it to a real style?
112 25 => 'textStop', # textOctagon FIXME see 24
113 26 => 'textTriangle', # textHexagon FIXMME see 24
114 27 => 'textCanDown', # textCurve FIXMME see 24
115 28 => 'textWave1', # textWave FIXMME see 24
116 29 => 'textArchUpPour', # textRing FIXMME see 24
117 30 => 'textCanDown', # textOnCurve FIXMME see 24
118 31 => 'textArchUp', # textOnRing FIXMME see 24
119 32 => 'straightConnector1',
120 33 => 'bentConnector2',
121 34 => 'bentConnector3',
122 35 => 'bentConnector4',
123 36 => 'bentConnector5',
124 37 => 'curvedConnector2',
125 38 => 'curvedConnector3',
126 39 => 'curvedConnector4',
127 40 => 'curvedConnector5',
128 41 => 'callout1',
129 42 => 'callout2',
130 43 => 'callout3',
131 44 => 'accentCallout1',
132 45 => 'accentCallout2',
133 46 => 'accentCallout3',
134 47 => 'borderCallout1',
135 48 => 'borderCallout2',
136 49 => 'borderCallout3',
137 50 => 'accentBorderCallout1',
138 51 => 'accentBorderCallout2',
139 52 => 'accentBorderCallout3',
140 53 => 'ribbon',
141 54 => 'ribbon2',
142 55 => 'chevron',
143 56 => 'pentagon',
144 57 => 'noSmoking',
145 58 => 'star8', # seal8
146 59 => 'star16', # seal16
147 60 => 'star32', # seal32
148 61 => 'wedgeRectCallout',
149 62 => 'wedgeRoundRectCallout', # wedgeRRectCallout
150 63 => 'wedgeEllipseCallout',
151 64 => 'wave',
152 65 => 'foldedCorner',
153 66 => 'leftArrow',
154 67 => 'downArrow',
155 68 => 'upArrow',
156 69 => 'leftRightArrow',
157 70 => 'upDownArrow',
158 71 => 'irregularSeal1',
159 72 => 'irregularSeal2',
160 73 => 'lightningBolt',
161 74 => 'heart',
162 75 => 'frame', # pictureFrame
163 76 => 'quadArrow',
164 77 => 'leftArrowCallout',
165 78 => 'rightArrowCallout',
166 79 => 'upArrowCallout',
167 80 => 'downArrowCallout',
168 81 => 'leftRightArrowCallout',
169 82 => 'upDownArrowCallout',
170 83 => 'quadArrowCallout',
171 84 => 'bevel',
172 85 => 'leftBracket',
173 86 => 'rightBracket',
174 87 => 'leftBrace',
175 88 => 'rightBrace',
176 89 => 'leftUpArrow',
177 90 => 'bentUpArrow',
178 91 => 'bentArrow',
179 92 => 'star24', # seal24
180 93 => 'stripedRightArrow',
181 94 => 'notchedRightArrow',
182 95 => 'blockArc',
183 96 => 'smileyFace',
184 97 => 'verticalScroll',
185 98 => 'horizontalScroll',
186 99 => 'circularArrow',
187 100 => 'notchedCircularArrow', # should not be used
188 101 => 'uturnArrow',
189 102 => 'curvedRightArrow',
190 103 => 'curvedLeftArrow',
191 104 => 'curvedUpArrow',
192 105 => 'curvedDownArrow',
193 106 => 'cloudCallout',
194 107 => 'ellipseRibbon',
195 108 => 'ellipseRibbon2',
196 109 => 'flowChartProcess',
197 110 => 'flowChartDecision',
198 111 => 'flowChartInputOutput',
199 112 => 'flowChartPredefinedProcess',
200 113 => 'flowChartInternalStorage',
201 114 => 'flowChartDocument',
202 115 => 'flowChartMultidocument',
203 116 => 'flowChartTerminator',
204 117 => 'flowChartPreparation',
205 118 => 'flowChartManualInput',
206 119 => 'flowChartManualOperation',
207 120 => 'flowChartConnector',
208 121 => 'flowChartPunchedCard',
209 122 => 'flowChartPunchedTape',
210 123 => 'flowChartSummingJunction',
211 124 => 'flowChartOr',
212 125 => 'flowChartCollate',
213 126 => 'flowChartSort',
214 127 => 'flowChartExtract',
215 128 => 'flowChartMerge',
216 129 => 'flowChartOfflineStorage',
217 130 => 'flowChartOnlineStorage',
218 131 => 'flowChartMagneticTape',
219 132 => 'flowChartMagneticDisk',
220 133 => 'flowChartMagneticDrum',
221 134 => 'flowChartDisplay',
222 135 => 'flowChartDelay',
223 136 => 'textPlain', # textPlainText
224 137 => 'textStop',
225 138 => 'textTriangle',
226 139 => 'textTriangleInverted',
227 140 => 'textChevron',
228 141 => 'textChevronInverted',
229 142 => 'textRingInside',
230 143 => 'textRingOutside',
231 144 => 'textArchUp', # textArchUpCurve
232 145 => 'textArchDown', # textArchDownCurve
233 146 => 'textCircle', # textCircleCurve
234 147 => 'textButton', # textButtonCurve
235 148 => 'textArchUpPour',
236 149 => 'textArchDownPour',
237 150 => 'textCirclePour',
238 151 => 'textButtonPour',
239 152 => 'textCurveUp',
240 153 => 'textCurveDown',
241 154 => 'textCascadeUp',
242 155 => 'textCascadeDown',
243 156 => 'textWave1',
244 157 => 'textWave2',
245 158 => 'textWave3',
246 159 => 'textWave4',
247 160 => 'textInflate',
248 161 => 'textDeflate',
249 162 => 'textInflateBottom',
250 163 => 'textDeflateBottom',
251 164 => 'textInflateTop',
252 165 => 'textDeflateTop',
253 166 => 'textDeflateInflate',
254 167 => 'textDeflateInflateDeflate',
255 168 => 'textFadeRight',
256 169 => 'textFadeLeft',
257 170 => 'textFadeUp',
258 171 => 'textFadeDown',
259 172 => 'textSlantUp',
260 173 => 'textSlantDown',
261 174 => 'textCanUp',
262 175 => 'textCanDown',
263 176 => 'flowChartAlternateProcess',
264 177 => 'flowChartOffpageConnector',
265 178 => 'callout1', # callout90
266 179 => 'accentCallout1', # accentCallout90
267 180 => 'borderCallout1', # borderCallout90
268 181 => 'accentBorderCallout1', # accentBorderCallout90
269 182 => 'leftRightUpArrow',
270 183 => 'sun',
271 184 => 'moon',
272 185 => 'bracketPair',
273 186 => 'bracePair',
274 187 => 'star4', # seal4
275 188 => 'doubleWave',
276 189 => 'actionButtonBlank',
277 190 => 'actionButtonHome',
278 191 => 'actionButtonHelp',
279 192 => 'actionButtonInformation',
280 193 => 'actionButtonForwardNext',
281 194 => 'actionButtonBackPrevious',
282 195 => 'actionButtonEnd',
283 196 => 'actionButtonBeginning',
284 197 => 'actionButtonReturn',
285 198 => 'actionButtonDocument',
286 199 => 'actionButtonSound',
287 200 => 'actionButtonMovie',
288 201 => 'hostControl', # should not be used
289 202 => 'textBox'
291 # An error occured, we have to ignore this shape
292 sub error( $ )
294 my ( $msg ) = @_;
296 $ignore_this_shape = 1;
297 print STDERR "ERROR (in $shape_name ): $msg\n";
300 # Check that we are in the correct level
301 sub is_level( $$ )
303 my ( $level, $value ) = @_;
305 if ( $level > 0 ) {
306 error( "Error in is_level(), \$level should be <= 0." );
308 return ( $#levels + $level > 0 ) && ( $levels[$#levels + $level] eq $value );
311 # Setup the %variables map with predefined values
312 sub setup_variables()
314 %variables = (
315 'l' => 0,
316 't' => 0,
317 'r' => 21600,
318 'b' => 21600,
320 'w' => 21600,
321 'h' => 21600,
322 'ss' => 21600,
323 'ls' => 21600,
325 'ssd2' => 10800, # 1/2
326 'ssd4' => 5400, # 1/4
327 'ssd6' => 3600, # 1/6
328 'ssd8' => 2700, # 1/8
329 'ssd16' => 1350, # 1/16
330 'ssd32' => 675, # 1/32
332 'hc' => 10800, # horizontal center
333 'vc' => 10800, # vertical center
335 'wd2' => 10800, # 1/2
336 'wd3' => 7200, # 1/3
337 'wd4' => 5400, # 1/4
338 'wd5' => 4320, # 1/5
339 'wd6' => 3600, # 1/6
340 'wd8' => 2700, # 1/8
341 'wd10' => 2160, # 1/10
342 'wd12' => 1800, # 1/12
343 'wd32' => 675, # 1/32
345 'hd2' => 10800, # 1/2
346 'hd3' => 7200, # 1/3
347 'hd4' => 5400, # 1/4
348 'hd5' => 4320, # 1/5
349 'hd6' => 3600, # 1/6
350 'hd8' => 2700, # 1/8
351 'hd10' => 2160, # 1/10
352 'hd12' => 1800, # 1/12
353 'hd32' => 675, # 1/32
355 '25000' => 5400,
356 '12500' => 2700,
358 'cd4' => 90, # 1/4 of a circle
359 'cd2' => 180, # 1/2 of a circle
360 '3cd4' => 270, # 3/4 of a circle
362 'cd8' => 45, # 1/8 of a circle
363 '3cd8' => 135, # 3/8 of a circle
364 '5cd8' => 225, # 5/8 of a circle
365 '7cd8' => 315, # 7/8 of a circle
367 '-5400000' => -90,
368 '-10800000'=> -180,
369 '-16200000'=> -270,
370 '-21600000'=> -360,
371 '-21599999'=> -360,
373 '5400000' => 90,
374 '10800000' => 180,
375 '16200000' => 270,
376 '21600000' => 360,
377 '21599999' => 360
379 # '21600000' => 360, # angle conversions
380 # '27000000' => 450,
381 # '32400000' => 540,
382 # '37800000' => 630
386 # Convert the (predefiend) value to a number
387 sub value( $ )
389 my ( $val ) = @_;
391 my $result = $variables{$val};
392 return $result if ( defined( $result ) );
394 return $val if ( $val =~ /^[0-9-]+$/ );
396 error( "Unknown variable '$val'." );
398 show_call_stack();
399 return $val;
402 # Convert the DrawingML formula to a VML one
403 %command_variables = (
404 'w' => 'width',
405 'h' => 'height',
406 'r' => 'width',
407 'b' => 'height'
410 # The same as value(), but some of the hardcoded values can have a name
411 sub command_value( $ )
413 my ( $value ) = @_;
415 return "" if ( $value eq "" );
417 return $value if ( $value =~ /^@/ );
419 my $command_val = $command_variables{$value};
420 if ( defined( $command_val ) ) {
421 return $command_val;
424 return value( $value );
427 # Insert the new formula to the list of formulas
428 # Creates the name if it's empty...
429 sub insert_formula( $$ )
431 my ( $name, $fmla ) = @_;
433 my $i = 0;
434 foreach $f ( @formulas ) {
435 if ( $f eq $fmla ) {
436 if ( $name ne "" ) {
437 $variables{$name} = "@" . $i;
439 return "@" . $i;
441 ++$i;
444 if ( $name eq "" ) {
445 $name = "@" . ( $#formulas + 1 );
448 $variables{$name} = "@" . ( $#formulas + 1 );
449 push @formulas, $fmla;
451 if ( $#formulas > 127 ) {
452 error( "Reached the maximum amount of formulas, have to ignore the shape '$shape_name'" );
455 return $variables{$name};
458 # The same as insert_formula(), but converts the params
459 sub insert_formula_params( $$$$$ )
461 my ( $name, $command, $p1, $p2, $p3 ) = @_;
463 my $result = $command;
464 if ( $p1 ne "" ) {
465 $result .= " " . command_value( $p1 );
466 if ( $p2 ne "" ) {
467 $result .= " " . command_value( $p2 );
468 if ( $p3 ne "" ) {
469 $result .= " " . command_value( $p3 );
474 return insert_formula( $name, $result );
477 # Convert the formula from DrawingML to VML
478 sub convert_formula( $$ )
480 my ( $name, $fmla ) = @_;
482 if ( $fmla =~ /^([^ ]+)/ ) {
483 my $command = $1;
485 # parse the parameters
486 ( my $values = $fmla ) =~ s/^([^ ]+) *//;
487 my $p1 = "", $p2 = "", $p3 = "";
488 if ( $values =~ /^([^ ]+)/ ) {
489 $p1 = $1;
490 $values =~ s/^([^ ]+) *//;
491 if ( $values =~ /^([^ ]+)/ ) {
492 $p2 = $1;
493 $values =~ s/^([^ ]+) *//;
494 if ( $values =~ /^([^ ]+)/ ) {
495 $p3 = $1;
500 # now convert the formula
501 if ( $command eq "+-" ) {
502 if ( $p1 eq "100000" ) {
503 $p1 = value( 'w' );
505 insert_formula_params( $name, "sum", $p1, $p2, $p3 );
506 return;
508 elsif ( $command eq "*/" ) {
509 if ( ( $p2 =~ /^(w|h|ss|hd2|wd2|vc)$/ ) && defined( $variables{$p1} ) ) {
510 # switch it ;-) - presetTextWarpDefinitions.xml has it in other order
511 my $tmp = $p1;
512 $p1 = $p2;
513 $p2 = $tmp;
516 if ( ( $p1 =~ /^(w|h|ss|hd2|wd2|vc)$/ ) && defined( $variables{$p2} ) ) {
517 my $val3 = $p3;
518 if ( $val3 =~ /^[0-9-]+$/ ) {
519 $val3 *= ( value( 'w' ) / value( $p1 ) );
521 # Oh yes, I'm too lazy to implement the full GCD here ;-)
522 if ( ( $val3 % 100000 ) == 0 ) {
523 $p1 = 1;
524 $p3 = sprintf( "%.0f", ( $val3 / 100000 ) );
526 elsif ( $val3 < 100000 ) {
527 $p3 = 1;
528 while ( ( ( $p3 * 100000 ) % $val3 ) != 0 ) {
529 ++$p3
531 $p1 = ( $p3 * 100000 ) / $val3;
533 else {
534 error( "Need to count the greatest common divisor." );
538 elsif ( $p3 eq "100000" && $p2 =~ /^[0-9-]+$/ ) {
539 # prevent overflows in some shapes
540 $p2 = sprintf( "%.0f", ( $p2 / 10 ) );
541 $p3 /= 10;
543 elsif ( $p3 eq "32768" && $p2 =~ /^[0-9-]+$/ ) {
544 # prevent overflows in some shapes
545 $p2 = sprintf( "%.0f", ( $p2 / 8 ) );
546 $p3 /= 8;
548 elsif ( $p3 eq "50000" ) {
549 $p3 = 10800;
551 elsif ( $name =~ /^maxAdj/ ) {
552 my $val = value( $p1 );
553 if ( $val =~ /^[0-9-]+$/ ) {
554 $p1 = sprintf( "%.0f", ( value( 'w' ) * $val / 100000 ) );
558 if ( ( value( $p1 ) eq value( $p3 ) ) || ( value( $p2 ) eq value( $p3 ) ) ) {
559 my $val = value( ( value( $p1 ) eq value( $p3 ) )? $p2: $p1 );
560 if ( $val =~ /^@([0-9]+)$/ ) {
561 insert_formula( $name, $formulas[$1] );
563 else {
564 insert_formula( $name, "val $val" );
567 else {
568 insert_formula_params( $name, "prod", $p1, $p2, $p3 );
570 return;
572 elsif ( $command eq "+/" ) {
573 # we have to split this into 2 formulas - 'sum' and 'prod'
574 my $constructed = insert_formula_params( "", "sum", $p1, $p2, "0" );
575 insert_formula_params( $name, "prod", 1, $constructed, $p3); # references the 'sum' formula
576 return;
578 elsif ( $command eq "?:" ) {
579 insert_formula_params( $name, "if", $p1, $p2, $p3 );
580 return;
582 elsif ( $command eq "sin" || $command eq "cos" ) {
583 if ( $p2 =~ /^[0-9-]+$/ && ( ( $p2 % 60000 ) == 0 ) ) {
584 $p2 /= 60000;
586 else {
587 $p2 = insert_formula_params( "", "prod", "1", $p2, "60000" );
589 # we have to use 'sumangle' even for the case when $p2 is const
590 # and theoretically could be written as such; but Word does not
591 # accept it :-(
592 my $conv = insert_formula_params( "", "sumangle", "0", $p2, "0" );
594 $p2 = $conv;
596 insert_formula_params( $name, $command, $p1, $p2, "" );
597 return;
599 elsif ( $command eq "abs" ) {
600 insert_formula_params( $name, $command, $p1, "", "" );
601 return;
603 elsif ( $command eq "max" || $command eq "min" ) {
604 insert_formula_params( $name, $command, $p1, $p2, "" );
605 return;
607 elsif ( $command eq "at2" ) {
608 insert_formula_params( $name, "atan2", $p1, $p2, "" );
609 return;
611 elsif ( $command eq "cat2" ) {
612 insert_formula_params( $name, "cosatan2", $p1, $p2, $p3 );
613 return;
615 elsif ( $command eq "sat2" ) {
616 insert_formula_params( $name, "sinatan2", $p1, $p2, $p3 );
617 return;
619 elsif ( $command eq "sqrt" ) {
620 insert_formula_params( $name, "sqrt", $p1, "", "" );
621 return;
623 elsif ( $command eq "mod" ) {
624 insert_formula_params( $name, "mod", $p1, $p2, $p3 );
625 return;
627 elsif ( $command eq "val" ) {
628 insert_formula_params( $name, "val", value( $p1 ), "", "" );
629 return;
631 else {
632 error( "Unknown formula '$name', '$fmla'." );
635 else {
636 error( "Cannot convert formula's command '$name', '$fmla'." );
640 # There's no exact equivalent of 'arcTo' in VML, we have to do some special casing...
641 %convert_arcTo = (
642 '0' => {
643 '90' => {
644 'path' => 'qy',
645 'op' => [ 'sum 0 __last_x__ __wR__', 'sum __hR__ __last_y__ 0' ],
647 '-90' => {
648 'path' => 'qy',
649 'op' => [ 'sum 0 __last_x__ __wR__', 'sum 0 __last_y__ __hR__' ],
652 '90' => {
653 '90' => {
654 'path' => 'qx',
655 'op' => [ 'sum 0 __last_x__ __wR__', 'sum 0 __last_y__ __hR__' ],
657 '-90' => {
658 'path' => 'qx',
659 'op' => [ 'sum __wR__ __last_x__ 0', 'sum 0 __last_y__ __hR__' ],
662 '180' => {
663 '90' => {
664 'path' => 'qy',
665 'op' => [ 'sum __wR__ __last_x__ 0', 'sum 0 __last_y__ __hR__' ],
667 '-90' => {
668 'path' => 'qy',
669 'op' => [ 'sum __wR__ __last_x__ 0', 'sum __hR__ __last_y__ 0' ],
672 '270' => {
673 '90' => {
674 'path' => 'qx',
675 'op' => [ 'sum __wR__ __last_x__ 0', 'sum __hR__ __last_y__ 0' ],
677 '-90' => {
678 'path' => 'qx',
679 'op' => [ 'sum 0 __last_x__ __wR__', 'sum __hR__ __last_y__ 0' ],
684 # Elliptic quadrant
685 # FIXME optimize so that we compute the const values when possible
686 sub elliptic_quadrant( $$$$ )
688 my ( $wR, $hR, $stAng, $swAng ) = @_;
690 if ( defined( $convert_arcTo{$stAng} ) && defined( $convert_arcTo{$stAng}{$swAng} ) ) {
691 my $conv_path = $convert_arcTo{$stAng}{$swAng}{'path'};
692 my $conv_op_ref = $convert_arcTo{$stAng}{$swAng}{'op'};
694 $path .= "$conv_path";
696 my $pos_x = $last_pos_x;
697 my $pos_y = $last_pos_y;
698 for ( my $i = 0; $i <= $#{$conv_op_ref}; ++$i ) {
699 my $op = $conv_op_ref->[$i];
701 $op =~ s/__last_x__/$last_pos_x/g;
702 $op =~ s/__last_y__/$last_pos_y/g;
703 $op =~ s/__wR__/$wR/g;
704 $op =~ s/__hR__/$hR/g;
706 my $fmla = insert_formula( "", $op );
708 $path .= $fmla;
710 # so far it's sufficient just to rotate the positions
711 # FIXME if not ;-)
712 $pos_x = $pos_y;
713 $pos_y = $fmla;
715 $last_pos_x = $pos_x;
716 $last_pos_y = $pos_y;
718 else {
719 error( "Unhandled elliptic_quadrant(), input is ($wR, $hR, $stAng, $swAng)." );
723 # Convert the quadratic bezier to cubic (exact)
724 # No idea why, but the 'qb' did not work for me :-(
725 sub quadratic_to_cubic_bezier( $ )
727 my ( $axis ) = @_;
729 my $a0 = $quadratic_bezier[0]->{$axis};
730 my $a1 = $quadratic_bezier[1]->{$axis};
731 my $a2 = $quadratic_bezier[2]->{$axis};
733 my $b0 = $a0;
735 # $b1 = $a0 + 2/3 * ( $a1 - $a0 ), but in VML
736 # FIXME optimize for constants - compute directly
737 my $b1_1 = insert_formula_params( "", "sum", "0", $a1, $a0 );
738 my $b1_2 = insert_formula_params( "", "prod", "2", $b1_1, "3" );
739 my $b1 = insert_formula_params( "", "sum", $a0, $b1_2, "0" );
741 # $b2 = $b1 + 1/3 * ( $a2 - $a0 );
742 # FIXME optimize for constants - compute directly
743 my $b2_1 = insert_formula_params( "", "sum", "0", $a2, $a0 );
744 my $b2_2 = insert_formula_params( "", "prod", "1", $b2_1, "3" );
745 my $b2 = insert_formula_params( "", "sum", $b1, $b2_2, "0" );
747 my $b3 = $a2;
749 return ( $b0, $b1, $b2, $b3 );
752 # Extend $path by one more point
753 sub add_point_to_path( $$ )
755 my ( $x, $y ) = @_;
757 if ( $path =~ /[0-9]$/ && $x =~ /^[0-9-]/ ) {
758 $path .= ",";
760 $path .= $x;
762 if ( $path =~ /[0-9]$/ && $y =~ /^[0-9-]/ ) {
763 $path .= ",";
765 $path .= $y;
768 # Start of an element
769 sub start_element( $% )
771 my ( $element, %attr ) = @_;
773 push @levels, $element;
775 #print "element: $element\n";
777 if ( is_level( -1, "presetShapeDefinitons" ) || is_level( -1, "presetTextWarpDefinitions" ) ) {
778 $shape_name = $element;
780 $state = "";
781 $ignore_this_shape = 0;
782 $path = "";
783 $adjust = "";
784 $max_adj_no = 0;
785 @formulas = ();
786 $handles = "";
787 $textboxrect = "";
788 $last_pos_x = "";
789 $last_pos_y = "";
790 $no_stroke = 0;
791 $no_fill = 0;
792 @quadratic_bezier = ();
794 setup_variables();
796 if ( $shape_name eq "sun" ) {
797 # hack for this shape
798 $variables{'100000'} = "21600";
799 $variables{'50000'} = "10800";
800 $variables{'25000'} = "5400";
801 $variables{'12500'} = "2700";
802 $variables{'3662'} = "791";
805 my $found = 0;
806 foreach my $name ( values( %shapes_ids ) ) {
807 if ( $name eq $shape_name ) {
808 $found = 1;
809 last;
812 if ( !$found ) {
813 error( "Unknown shape '$shape_name'." );
816 elsif ( $element eq "pathLst" ) {
817 $state = "path";
819 elsif ( $element eq "avLst" ) {
820 $state = "adjust";
822 elsif ( $element eq "gdLst" ) {
823 $state = "formulas";
825 elsif ( $element eq "ahLst" ) {
826 $state = "handles";
828 elsif ( $element eq "rect" ) {
829 $textboxrect = value( $attr{'l'} ) . "," . value( $attr{'t'} ) . "," .
830 value( $attr{'r'} ) . "," . value( $attr{'b'} );
832 elsif ( $state eq "path" ) {
833 if ( $element eq "path" ) {
834 $no_stroke = ( defined( $attr{'stroke'} ) && $attr{'stroke'} eq 'false' );
835 $no_fill = ( defined( $attr{'fill'} ) && $attr{'fill'} eq 'none' );
836 $path_w = $attr{'w'};
837 $path_h = $attr{'h'};
839 elsif ( $element eq "moveTo" ) {
840 $path .= "m";
842 elsif ( $element eq "lnTo" ) {
843 $path .= "l";
845 elsif ( $element eq "cubicBezTo" ) {
846 $path .= "c";
848 elsif ( $element eq "quadBezTo" ) {
849 my %points = ( 'x' => $last_pos_x, 'y' => $last_pos_y );
850 @quadratic_bezier = ( \%points );
852 elsif ( $element eq "close" ) {
853 $path .= "x";
855 elsif ( $element eq "pt" ) {
856 # rememeber the last position for the arcTo
857 $last_pos_x = value( $attr{'x'} );
858 $last_pos_y = value( $attr{'y'} );
860 $last_pos_x *= ( value( 'w' ) / $path_w ) if ( defined( $path_w ) );
861 $last_pos_y *= ( value( 'h' ) / $path_h ) if ( defined( $path_h ) );
863 if ( $#quadratic_bezier >= 0 ) {
864 my %points = ( 'x' => $last_pos_x, 'y' => $last_pos_y );
865 push( @quadratic_bezier, \%points );
867 else {
868 add_point_to_path( $last_pos_x, $last_pos_y );
871 elsif ( ( $element eq "arcTo" ) && ( $last_pos_x ne "" ) && ( $last_pos_y ne "" ) ) {
872 # there's no exact equivalent of arcTo in VML, so we have to
873 # compute here a bit...
874 my $stAng = value( $attr{'stAng'} );
875 my $swAng = value( $attr{'swAng'} );
876 my $wR = value( $attr{'wR'} );
877 my $hR = value( $attr{'hR'} );
879 $wR *= ( value( 'w' ) / $path_w ) if ( defined( $path_w ) );
880 $hR *= ( value( 'h' ) / $path_h ) if ( defined( $path_h ) );
882 if ( ( $stAng =~ /^[0-9-]+$/ ) && ( $swAng =~ /^[0-9-]+$/ ) ) {
883 if ( ( ( $stAng % 90 ) == 0 ) && ( ( $swAng % 90 ) == 0 ) && ( $swAng != 0 ) ) {
884 my $end = $stAng + $swAng;
885 my $step = ( $swAng > 0 )? 90: -90;
887 for ( my $cur = $stAng; $cur != $end; $cur += $step ) {
888 elliptic_quadrant( $wR, $hR, ( $cur % 360 ), $step );
891 else {
892 error( "Unsupported numeric 'arcTo' ($attr{'wR'}, $attr{'hR'}, $stAng, $swAng)." );
895 else {
896 error( "Unsupported 'arcTo' conversion ($attr{'wR'}, $attr{'hR'}, $stAng, $swAng)." );
899 else {
900 error( "Unhandled path element '$element'." );
903 elsif ( $state eq "adjust" ) {
904 if ( $element eq "gd" ) {
905 my $adj_no = $attr{'name'};
906 my $is_const = 0;
908 $adj_no =~ s/^adj//;
909 if ( $adj_no eq "" ) {
910 $max_adj_no = 0;
912 elsif ( !( $adj_no =~ /^[0-9]*$/ ) ) {
913 ++$max_adj_no;
914 $is_const = 1;
916 elsif ( $adj_no != $max_adj_no + 1 ) {
917 error( "Wrong order of adj values." );
918 ++$max_adj_no;
920 else {
921 $max_adj_no = $adj_no;
924 if ( $attr{'fmla'} =~ /^val ([0-9-]*)$/ ) {
925 my $val = sprintf( "%.0f", ( 21600 * $1 ) / 100000 );
926 if ( $is_const ) {
927 $variables{$adj_no} = $val;
929 elsif ( $adjust eq "" ) {
930 $adjust = $val;
932 else {
933 $adjust = "$val,$adjust";
936 else {
937 error( "Wrong fmla '$attr{'fmla'}'." );
940 else {
941 error( "Unhandled adjust element '$element'." );
944 elsif ( $state eq "formulas" ) {
945 if ( $element eq "gd" ) {
946 if ( $attr{'fmla'} =~ /^\*\/ (h|w|ss) adj([0-9]+) 100000$/ ) {
947 insert_formula( $attr{'name'}, "val #" . ( $max_adj_no - $2 ) );
949 elsif ( $attr{'fmla'} =~ /^pin [^ ]+ ([^ ]+) / ) {
950 print STDERR "TODO Map 'pin' to VML as xrange for handles.\n";
951 my $pin_val = $1;
952 if ( $pin_val eq "adj" ) {
953 insert_formula( $attr{'name'}, "val #0" );
955 elsif ( $pin_val =~ /^adj([0-9]+)/ ) {
956 insert_formula( $attr{'name'}, "val #" . ( $max_adj_no - $1 ) );
958 else {
959 insert_formula( $attr{'name'}, "val " . value( $pin_val ) );
962 elsif ( $attr{'fmla'} =~ /adj/ ) {
963 error( "Non-standard usage of adj in '$attr{'fmla'}'." );
965 else {
966 convert_formula( $attr{'name'}, $attr{'fmla'} );
970 elsif ( $state eq "handles" ) {
971 if ( $element eq "pos" ) {
972 $handles .= "<v:h position=\"" . value( $attr{'x'} ) . "," . value( $attr{'y'} ) . "\"/>\n";
977 # End of an element
978 sub end_element( $ )
980 my ( $element ) = @_;
982 pop @levels;
984 if ( $element eq $shape_name ) {
985 if ( !$ignore_this_shape ) {
986 # we have all the info, generate the shape now
987 $state = "";
989 # shape path
990 my $out = "<v:shapetype id=\"shapetype___ID__\" coordsize=\"21600,21600\" o:spt=\"__ID__\" ";
991 if ( $adjust ne "" ) {
992 $out .= "adj=\"$adjust\" ";
995 # optimize it [yes, we need this twice ;-)]
996 $path =~ s/([^0-9-@])0([^0-9-@])/$1$2/g;
997 $path =~ s/([^0-9-@])0([^0-9-@])/$1$2/g;
999 $out .= "path=\"$path\">\n";
1001 # stroke
1002 $out .= "<v:stroke joinstyle=\"miter\"/>\n";
1004 # formulas
1005 if ( $#formulas >= 0 )
1007 $out .= "<v:formulas>\n";
1008 foreach $fmla ( @formulas ) {
1009 $out .= "<v:f eqn=\"$fmla\"/>\n"
1011 $out .= "</v:formulas>\n";
1014 # path
1015 if ( $textboxrect ne "" ) { # TODO connectlocs, connectangles
1016 $out .= "<v:path gradientshapeok=\"t\" o:connecttype=\"rect\" textboxrect=\"$textboxrect\"/>\n";
1019 # handles
1020 if ( $handles ne "" ) {
1021 $out .= "<v:handles>\n$handles</v:handles>\n";
1024 $out .="</v:shapetype>";
1026 # hooray! :-)
1027 $result_shapes{$shape_name} = $out;
1029 else {
1030 print STDERR "Shape '$shape_name' ignored; see the above error(s) for the reason.\n";
1032 $shape_name = "";
1034 elsif ( $state eq "path" ) {
1035 if ( $element eq "path" ) {
1036 $path .= "ns" if ( $no_stroke );
1037 $path .= "nf" if ( $no_fill );
1038 $path .= "e";
1040 elsif ( $element eq "quadBezTo" ) {
1041 # we have to convert the quadratic bezier to cubic
1042 if ( $#quadratic_bezier == 2 ) {
1043 my @points_x = quadratic_to_cubic_bezier( 'x' );
1044 my @points_y = quadratic_to_cubic_bezier( 'y' );
1046 $path .= "c";
1047 # ignore the starting point
1048 for ( my $i = 1; $i < 4; ++$i ) {
1049 add_point_to_path( $points_x[$i], $points_y[$i] );
1052 else {
1053 error( "Wrong number of points of the quadratic bezier." );
1055 @quadratic_bezier = ();
1058 elsif ( $element eq "avLst" ) {
1059 $state = "";
1061 elsif ( $element eq "gdLst" ) {
1062 $state = "";
1064 elsif ( $element eq "ahLst" ) {
1065 $state = "";
1069 # Text inside an element
1070 sub characters( $ )
1072 #my ( $text ) = @_;
1075 #################### A trivial XML parser ####################
1077 # Parse the attributes
1078 sub parse_start_element( $ )
1080 # split the string containing both the elements and attributes
1081 my ( $element_tmp ) = @_;
1083 $element_tmp =~ s/\s*$//;
1084 $element_tmp =~ s/^\s*//;
1086 ( my $element = $element_tmp ) =~ s/\s.*$//;
1087 if ( $element_tmp =~ /\s/ ) {
1088 $element_tmp =~ s/^[^\s]*\s//;
1090 else {
1091 $element_tmp = "";
1094 # we have the element, now the attributes
1095 my %attr;
1096 my $is_key = 1;
1097 my $key = "";
1098 foreach my $tmp ( split( /"/, $element_tmp ) ) {
1099 if ( $is_key ) {
1100 $key = $tmp;
1101 $key =~ s/^\s*//;
1102 $key =~ s/\s*=\s*$//;
1104 else {
1105 $attr{$key} = $tmp;
1107 $is_key = !$is_key;
1110 if ( $element ne "" ) {
1111 start_element( $element, %attr );
1115 # Parse the file
1116 sub parse( $ )
1118 my ( $file ) = @_;
1120 my $in_comment = 0;
1121 my $line = "";
1122 while (<$file>) {
1123 # ignore comments
1124 s/<\?[^>]*\?>//g;
1125 s/<!--[^>]*-->//g;
1126 if ( /<!--/ ) {
1127 $in_comment = 1;
1128 s/<!--.*//;
1130 elsif ( /-->/ && $in_comment ) {
1131 $in_comment = 0;
1132 s/.*-->//;
1134 elsif ( $in_comment ) {
1135 next;
1137 # ignore empty lines
1138 chomp;
1139 s/^\s*//;
1140 s/\s*$//;
1141 next if ( $_ eq "" );
1143 # take care of lines where element continues
1144 if ( $line ne "" ) {
1145 $line .= " " . $_;
1147 else {
1148 $line = $_;
1150 next if ( !/>$/ );
1152 # the actual parsing
1153 my @starts = split( /</, $line );
1154 $line = "";
1155 foreach $start ( @starts ) {
1156 next if ( $start eq "" );
1158 @ends = split( />/, $start );
1159 my $element = $ends[0];
1160 my $data = $ends[1];
1162 # start or end element
1163 if ( $element =~ /^\/(.*)/ ) {
1164 end_element( $1 );
1166 elsif ( $element =~ /^(.*)\/$/ ) {
1167 parse_start_element( $1 );
1168 ( my $end = $1 ) =~ s/\s.*$//;
1169 end_element( $end );
1171 else {
1172 parse_start_element( $element );
1175 # the data
1176 characters( $data ) if ( defined( $data ) && $data ne "" );
1181 # Do the real work
1182 open( IN, "<$src_shapes" ) || die "Cannot open $src_shapes.";
1183 parse( IN );
1184 close( IN );
1186 open( IN, "<$src_text" ) || die "Cannot open $src_text.";
1187 parse( IN );
1188 close( IN );
1190 if ( !defined( $result_shapes{'textBox'} ) ) {
1191 $result_shapes{'textBox'} =
1192 "<v:shapetype id=\"shapetype___ID__\" coordsize=\"21600,21600\" " .
1193 "o:spt=\"__ID__\" path=\"m,l,21600l21600,21600l21600,xe\">\n" .
1194 "<v:stroke joinstyle=\"miter\"/>\n" .
1195 "<v:path gradientshapeok=\"t\" o:connecttype=\"rect\"/>\n" .
1196 "</v:shapetype>";
1199 # Generate the code
1200 print <<EOF;
1201 // Shape types generated from
1202 // '$src_shapes'
1203 // and
1204 // '$src_text'
1205 // which are part of the OOXML documentation
1207 #include <svx/escherex.hxx>
1209 const char* pShapeTypes[ ESCHER_ShpInst_COUNT ] =
1213 for ( $i = 0; $i < 203; ++$i ) {
1214 if ( $i < 4 ) {
1215 print " /* $i - $shapes_ids{$i} - handled separately */\n NULL,\n";
1217 else {
1218 print " /* $i - $shapes_ids{$i} */\n";
1219 my $out = $result_shapes{$shapes_ids{$i}};
1220 if ( defined( $out ) ) {
1221 # set the id
1222 $out =~ s/__ID__/$i/g;
1224 # escape the '"'s
1225 $out =~ s/"/\\"/g;
1227 # output as string
1228 $out =~ s/^/ "/;
1229 $out =~ s/\n/"\n "/g;
1230 $out =~ s/$/"/;
1232 print "$out,\n";
1234 else {
1235 print " NULL,\n";
1240 print <<EOF;