1#! /usr/bin/perl -w 2#************************************************************************* 3# 4# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5# 6# Copyright 2000, 2010 Oracle and/or its affiliates. 7# 8# OpenOffice.org - a multi-platform office productivity suite 9# 10# This file is part of OpenOffice.org. 11# 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. 15# 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). 21# 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. 26# 27#************************************************************************* 28 29use warnings; 30 31sub usage() { 32 print STDERR <<EOF; 33Usage: preset-definitions-to-shape-types.pl <shapes> <text> 34 35Converts presetShapeDefinitions.xml and presetTextWarpDefinitions.xml to a 36.cxx that contains VML with the definitions of the shapes. The result is 37written to stdout. 38 39<shapes> presetShapeDefinitions.xml (including the path to it) 40<text> presetTextWarpDefinitions.xml (including the path to it) 41EOF 42 exit 1; 43} 44 45sub show_call_stack 46{ 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"; 53 } 54 print STDERR "--- End stack trace ---\n"; 55} 56 57$src_shapes = shift; 58$src_text = shift; 59 60usage() if ( !defined( $src_shapes ) || !defined( $src_text ) || 61 $src_shapes eq "-h" || $src_shapes eq "--help" || 62 !-f $src_shapes || !-f $src_text ); 63 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 = (); 83 84%result_shapes = (); 85 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' 290); 291# An error occured, we have to ignore this shape 292sub error( $ ) 293{ 294 my ( $msg ) = @_; 295 296 $ignore_this_shape = 1; 297 print STDERR "ERROR (in $shape_name ): $msg\n"; 298} 299 300# Check that we are in the correct level 301sub is_level( $$ ) 302{ 303 my ( $level, $value ) = @_; 304 305 if ( $level > 0 ) { 306 error( "Error in is_level(), \$level should be <= 0." ); 307 } 308 return ( $#levels + $level > 0 ) && ( $levels[$#levels + $level] eq $value ); 309} 310 311# Setup the %variables map with predefined values 312sub setup_variables() 313{ 314 %variables = ( 315 'l' => 0, 316 't' => 0, 317 'r' => 21600, 318 'b' => 21600, 319 320 'w' => 21600, 321 'h' => 21600, 322 'ss' => 21600, 323 'ls' => 21600, 324 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 331 332 'hc' => 10800, # horizontal center 333 'vc' => 10800, # vertical center 334 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 344 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 354 355 '25000' => 5400, 356 '12500' => 2700, 357 358 'cd4' => 90, # 1/4 of a circle 359 'cd2' => 180, # 1/2 of a circle 360 '3cd4' => 270, # 3/4 of a circle 361 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 366 367 '-5400000' => -90, 368 '-10800000'=> -180, 369 '-16200000'=> -270, 370 '-21600000'=> -360, 371 '-21599999'=> -360, 372 373 '5400000' => 90, 374 '10800000' => 180, 375 '16200000' => 270, 376 '21600000' => 360, 377 '21599999' => 360 378# 379# '21600000' => 360, # angle conversions 380# '27000000' => 450, 381# '32400000' => 540, 382# '37800000' => 630 383 ); 384} 385 386# Convert the (predefiend) value to a number 387sub value( $ ) 388{ 389 my ( $val ) = @_; 390 391 my $result = $variables{$val}; 392 return $result if ( defined( $result ) ); 393 394 return $val if ( $val =~ /^[0-9-]+$/ ); 395 396 error( "Unknown variable '$val'." ); 397 398 show_call_stack(); 399 return $val; 400} 401 402# Convert the DrawingML formula to a VML one 403%command_variables = ( 404 'w' => 'width', 405 'h' => 'height', 406 'r' => 'width', 407 'b' => 'height' 408); 409 410# The same as value(), but some of the hardcoded values can have a name 411sub command_value( $ ) 412{ 413 my ( $value ) = @_; 414 415 return "" if ( $value eq "" ); 416 417 return $value if ( $value =~ /^@/ ); 418 419 my $command_val = $command_variables{$value}; 420 if ( defined( $command_val ) ) { 421 return $command_val; 422 } 423 424 return value( $value ); 425} 426 427# Insert the new formula to the list of formulas 428# Creates the name if it's empty... 429sub insert_formula( $$ ) 430{ 431 my ( $name, $fmla ) = @_; 432 433 my $i = 0; 434 foreach $f ( @formulas ) { 435 if ( $f eq $fmla ) { 436 if ( $name ne "" ) { 437 $variables{$name} = "@" . $i; 438 } 439 return "@" . $i; 440 } 441 ++$i; 442 } 443 444 if ( $name eq "" ) { 445 $name = "@" . ( $#formulas + 1 ); 446 } 447 448 $variables{$name} = "@" . ( $#formulas + 1 ); 449 push @formulas, $fmla; 450 451 if ( $#formulas > 127 ) { 452 error( "Reached the maximum amount of formulas, have to ignore the shape '$shape_name'" ); 453 } 454 455 return $variables{$name}; 456} 457 458# The same as insert_formula(), but converts the params 459sub insert_formula_params( $$$$$ ) 460{ 461 my ( $name, $command, $p1, $p2, $p3 ) = @_; 462 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 ); 470 } 471 } 472 } 473 474 return insert_formula( $name, $result ); 475} 476 477# Convert the formula from DrawingML to VML 478sub convert_formula( $$ ) 479{ 480 my ( $name, $fmla ) = @_; 481 482 if ( $fmla =~ /^([^ ]+)/ ) { 483 my $command = $1; 484 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; 496 } 497 } 498 } 499 500 # now convert the formula 501 if ( $command eq "+-" ) { 502 if ( $p1 eq "100000" ) { 503 $p1 = value( 'w' ); 504 } 505 insert_formula_params( $name, "sum", $p1, $p2, $p3 ); 506 return; 507 } 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; 514 } 515 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 ) ); 520 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 ) ); 525 } 526 elsif ( $val3 < 100000 ) { 527 $p3 = 1; 528 while ( ( ( $p3 * 100000 ) % $val3 ) != 0 ) { 529 ++$p3 530 } 531 $p1 = ( $p3 * 100000 ) / $val3; 532 } 533 else { 534 error( "Need to count the greatest common divisor." ); 535 } 536 } 537 } 538 elsif ( $p3 eq "100000" && $p2 =~ /^[0-9-]+$/ ) { 539 # prevent overflows in some shapes 540 $p2 = sprintf( "%.0f", ( $p2 / 10 ) ); 541 $p3 /= 10; 542 } 543 elsif ( $p3 eq "32768" && $p2 =~ /^[0-9-]+$/ ) { 544 # prevent overflows in some shapes 545 $p2 = sprintf( "%.0f", ( $p2 / 8 ) ); 546 $p3 /= 8; 547 } 548 elsif ( $p3 eq "50000" ) { 549 $p3 = 10800; 550 } 551 elsif ( $name =~ /^maxAdj/ ) { 552 my $val = value( $p1 ); 553 if ( $val =~ /^[0-9-]+$/ ) { 554 $p1 = sprintf( "%.0f", ( value( 'w' ) * $val / 100000 ) ); 555 } 556 } 557 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] ); 562 } 563 else { 564 insert_formula( $name, "val $val" ); 565 } 566 } 567 else { 568 insert_formula_params( $name, "prod", $p1, $p2, $p3 ); 569 } 570 return; 571 } 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; 577 } 578 elsif ( $command eq "?:" ) { 579 insert_formula_params( $name, "if", $p1, $p2, $p3 ); 580 return; 581 } 582 elsif ( $command eq "sin" || $command eq "cos" ) { 583 if ( $p2 =~ /^[0-9-]+$/ && ( ( $p2 % 60000 ) == 0 ) ) { 584 $p2 /= 60000; 585 } 586 else { 587 $p2 = insert_formula_params( "", "prod", "1", $p2, "60000" ); 588 } 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" ); 593 594 $p2 = $conv; 595 596 insert_formula_params( $name, $command, $p1, $p2, "" ); 597 return; 598 } 599 elsif ( $command eq "abs" ) { 600 insert_formula_params( $name, $command, $p1, "", "" ); 601 return; 602 } 603 elsif ( $command eq "max" || $command eq "min" ) { 604 insert_formula_params( $name, $command, $p1, $p2, "" ); 605 return; 606 } 607 elsif ( $command eq "at2" ) { 608 insert_formula_params( $name, "atan2", $p1, $p2, "" ); 609 return; 610 } 611 elsif ( $command eq "cat2" ) { 612 insert_formula_params( $name, "cosatan2", $p1, $p2, $p3 ); 613 return; 614 } 615 elsif ( $command eq "sat2" ) { 616 insert_formula_params( $name, "sinatan2", $p1, $p2, $p3 ); 617 return; 618 } 619 elsif ( $command eq "sqrt" ) { 620 insert_formula_params( $name, "sqrt", $p1, "", "" ); 621 return; 622 } 623 elsif ( $command eq "mod" ) { 624 insert_formula_params( $name, "mod", $p1, $p2, $p3 ); 625 return; 626 } 627 elsif ( $command eq "val" ) { 628 insert_formula_params( $name, "val", value( $p1 ), "", "" ); 629 return; 630 } 631 else { 632 error( "Unknown formula '$name', '$fmla'." ); 633 } 634 } 635 else { 636 error( "Cannot convert formula's command '$name', '$fmla'." ); 637 } 638} 639 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' ], 646 }, 647 '-90' => { 648 'path' => 'qy', 649 'op' => [ 'sum 0 __last_x__ __wR__', 'sum 0 __last_y__ __hR__' ], 650 }, 651 }, 652 '90' => { 653 '90' => { 654 'path' => 'qx', 655 'op' => [ 'sum 0 __last_x__ __wR__', 'sum 0 __last_y__ __hR__' ], 656 }, 657 '-90' => { 658 'path' => 'qx', 659 'op' => [ 'sum __wR__ __last_x__ 0', 'sum 0 __last_y__ __hR__' ], 660 }, 661 }, 662 '180' => { 663 '90' => { 664 'path' => 'qy', 665 'op' => [ 'sum __wR__ __last_x__ 0', 'sum 0 __last_y__ __hR__' ], 666 }, 667 '-90' => { 668 'path' => 'qy', 669 'op' => [ 'sum __wR__ __last_x__ 0', 'sum __hR__ __last_y__ 0' ], 670 }, 671 }, 672 '270' => { 673 '90' => { 674 'path' => 'qx', 675 'op' => [ 'sum __wR__ __last_x__ 0', 'sum __hR__ __last_y__ 0' ], 676 }, 677 '-90' => { 678 'path' => 'qx', 679 'op' => [ 'sum 0 __last_x__ __wR__', 'sum __hR__ __last_y__ 0' ], 680 }, 681 }, 682); 683 684# Elliptic quadrant 685# FIXME optimize so that we compute the const values when possible 686sub elliptic_quadrant( $$$$ ) 687{ 688 my ( $wR, $hR, $stAng, $swAng ) = @_; 689 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'}; 693 694 $path .= "$conv_path"; 695 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]; 700 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; 705 706 my $fmla = insert_formula( "", $op ); 707 708 $path .= $fmla; 709 710 # so far it's sufficient just to rotate the positions 711 # FIXME if not ;-) 712 $pos_x = $pos_y; 713 $pos_y = $fmla; 714 } 715 $last_pos_x = $pos_x; 716 $last_pos_y = $pos_y; 717 } 718 else { 719 error( "Unhandled elliptic_quadrant(), input is ($wR, $hR, $stAng, $swAng)." ); 720 } 721} 722 723# Convert the quadratic bezier to cubic (exact) 724# No idea why, but the 'qb' did not work for me :-( 725sub quadratic_to_cubic_bezier( $ ) 726{ 727 my ( $axis ) = @_; 728 729 my $a0 = $quadratic_bezier[0]->{$axis}; 730 my $a1 = $quadratic_bezier[1]->{$axis}; 731 my $a2 = $quadratic_bezier[2]->{$axis}; 732 733 my $b0 = $a0; 734 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" ); 740 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" ); 746 747 my $b3 = $a2; 748 749 return ( $b0, $b1, $b2, $b3 ); 750} 751 752# Extend $path by one more point 753sub add_point_to_path( $$ ) 754{ 755 my ( $x, $y ) = @_; 756 757 if ( $path =~ /[0-9]$/ && $x =~ /^[0-9-]/ ) { 758 $path .= ","; 759 } 760 $path .= $x; 761 762 if ( $path =~ /[0-9]$/ && $y =~ /^[0-9-]/ ) { 763 $path .= ","; 764 } 765 $path .= $y; 766} 767 768# Start of an element 769sub start_element( $% ) 770{ 771 my ( $element, %attr ) = @_; 772 773 push @levels, $element; 774 775 #print "element: $element\n"; 776 777 if ( is_level( -1, "presetShapeDefinitons" ) || is_level( -1, "presetTextWarpDefinitions" ) ) { 778 $shape_name = $element; 779 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 = (); 793 794 setup_variables(); 795 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"; 803 } 804 805 my $found = 0; 806 foreach my $name ( values( %shapes_ids ) ) { 807 if ( $name eq $shape_name ) { 808 $found = 1; 809 last; 810 } 811 } 812 if ( !$found ) { 813 error( "Unknown shape '$shape_name'." ); 814 } 815 } 816 elsif ( $element eq "pathLst" ) { 817 $state = "path"; 818 } 819 elsif ( $element eq "avLst" ) { 820 $state = "adjust"; 821 } 822 elsif ( $element eq "gdLst" ) { 823 $state = "formulas"; 824 } 825 elsif ( $element eq "ahLst" ) { 826 $state = "handles"; 827 } 828 elsif ( $element eq "rect" ) { 829 $textboxrect = value( $attr{'l'} ) . "," . value( $attr{'t'} ) . "," . 830 value( $attr{'r'} ) . "," . value( $attr{'b'} ); 831 } 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'}; 838 } 839 elsif ( $element eq "moveTo" ) { 840 $path .= "m"; 841 } 842 elsif ( $element eq "lnTo" ) { 843 $path .= "l"; 844 } 845 elsif ( $element eq "cubicBezTo" ) { 846 $path .= "c"; 847 } 848 elsif ( $element eq "quadBezTo" ) { 849 my %points = ( 'x' => $last_pos_x, 'y' => $last_pos_y ); 850 @quadratic_bezier = ( \%points ); 851 } 852 elsif ( $element eq "close" ) { 853 $path .= "x"; 854 } 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'} ); 859 860 $last_pos_x *= ( value( 'w' ) / $path_w ) if ( defined( $path_w ) ); 861 $last_pos_y *= ( value( 'h' ) / $path_h ) if ( defined( $path_h ) ); 862 863 if ( $#quadratic_bezier >= 0 ) { 864 my %points = ( 'x' => $last_pos_x, 'y' => $last_pos_y ); 865 push( @quadratic_bezier, \%points ); 866 } 867 else { 868 add_point_to_path( $last_pos_x, $last_pos_y ); 869 } 870 } 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'} ); 878 879 $wR *= ( value( 'w' ) / $path_w ) if ( defined( $path_w ) ); 880 $hR *= ( value( 'h' ) / $path_h ) if ( defined( $path_h ) ); 881 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; 886 887 for ( my $cur = $stAng; $cur != $end; $cur += $step ) { 888 elliptic_quadrant( $wR, $hR, ( $cur % 360 ), $step ); 889 } 890 } 891 else { 892 error( "Unsupported numeric 'arcTo' ($attr{'wR'}, $attr{'hR'}, $stAng, $swAng)." ); 893 } 894 } 895 else { 896 error( "Unsupported 'arcTo' conversion ($attr{'wR'}, $attr{'hR'}, $stAng, $swAng)." ); 897 } 898 } 899 else { 900 error( "Unhandled path element '$element'." ); 901 } 902 } 903 elsif ( $state eq "adjust" ) { 904 if ( $element eq "gd" ) { 905 my $adj_no = $attr{'name'}; 906 my $is_const = 0; 907 908 $adj_no =~ s/^adj//; 909 if ( $adj_no eq "" ) { 910 $max_adj_no = 0; 911 } 912 elsif ( !( $adj_no =~ /^[0-9]*$/ ) ) { 913 ++$max_adj_no; 914 $is_const = 1; 915 } 916 elsif ( $adj_no != $max_adj_no + 1 ) { 917 error( "Wrong order of adj values." ); 918 ++$max_adj_no; 919 } 920 else { 921 $max_adj_no = $adj_no; 922 } 923 924 if ( $attr{'fmla'} =~ /^val ([0-9-]*)$/ ) { 925 my $val = sprintf( "%.0f", ( 21600 * $1 ) / 100000 ); 926 if ( $is_const ) { 927 $variables{$adj_no} = $val; 928 } 929 elsif ( $adjust eq "" ) { 930 $adjust = $val; 931 } 932 else { 933 $adjust = "$val,$adjust"; 934 } 935 } 936 else { 937 error( "Wrong fmla '$attr{'fmla'}'." ); 938 } 939 } 940 else { 941 error( "Unhandled adjust element '$element'." ); 942 } 943 } 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 ) ); 948 } 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" ); 954 } 955 elsif ( $pin_val =~ /^adj([0-9]+)/ ) { 956 insert_formula( $attr{'name'}, "val #" . ( $max_adj_no - $1 ) ); 957 } 958 else { 959 insert_formula( $attr{'name'}, "val " . value( $pin_val ) ); 960 } 961 } 962 elsif ( $attr{'fmla'} =~ /adj/ ) { 963 error( "Non-standard usage of adj in '$attr{'fmla'}'." ); 964 } 965 else { 966 convert_formula( $attr{'name'}, $attr{'fmla'} ); 967 } 968 } 969 } 970 elsif ( $state eq "handles" ) { 971 if ( $element eq "pos" ) { 972 $handles .= "<v:h position=\"" . value( $attr{'x'} ) . "," . value( $attr{'y'} ) . "\"/>\n"; 973 } 974 } 975} 976 977# End of an element 978sub end_element( $ ) 979{ 980 my ( $element ) = @_; 981 982 pop @levels; 983 984 if ( $element eq $shape_name ) { 985 if ( !$ignore_this_shape ) { 986 # we have all the info, generate the shape now 987 $state = ""; 988 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\" "; 993 } 994 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; 998 999 $out .= "path=\"$path\">\n"; 1000 1001 # stroke 1002 $out .= "<v:stroke joinstyle=\"miter\"/>\n"; 1003 1004 # formulas 1005 if ( $#formulas >= 0 ) 1006 { 1007 $out .= "<v:formulas>\n"; 1008 foreach $fmla ( @formulas ) { 1009 $out .= "<v:f eqn=\"$fmla\"/>\n" 1010 } 1011 $out .= "</v:formulas>\n"; 1012 } 1013 1014 # path 1015 if ( $textboxrect ne "" ) { # TODO connectlocs, connectangles 1016 $out .= "<v:path gradientshapeok=\"t\" o:connecttype=\"rect\" textboxrect=\"$textboxrect\"/>\n"; 1017 } 1018 1019 # handles 1020 if ( $handles ne "" ) { 1021 $out .= "<v:handles>\n$handles</v:handles>\n"; 1022 } 1023 1024 $out .="</v:shapetype>"; 1025 1026 # hooray! :-) 1027 $result_shapes{$shape_name} = $out; 1028 } 1029 else { 1030 print STDERR "Shape '$shape_name' ignored; see the above error(s) for the reason.\n"; 1031 } 1032 $shape_name = ""; 1033 } 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"; 1039 } 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' ); 1045 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] ); 1050 } 1051 } 1052 else { 1053 error( "Wrong number of points of the quadratic bezier." ); 1054 } 1055 @quadratic_bezier = (); 1056 } 1057 } 1058 elsif ( $element eq "avLst" ) { 1059 $state = ""; 1060 } 1061 elsif ( $element eq "gdLst" ) { 1062 $state = ""; 1063 } 1064 elsif ( $element eq "ahLst" ) { 1065 $state = ""; 1066 } 1067} 1068 1069# Text inside an element 1070sub characters( $ ) 1071{ 1072 #my ( $text ) = @_; 1073} 1074 1075#################### A trivial XML parser #################### 1076 1077# Parse the attributes 1078sub parse_start_element( $ ) 1079{ 1080 # split the string containing both the elements and attributes 1081 my ( $element_tmp ) = @_; 1082 1083 $element_tmp =~ s/\s*$//; 1084 $element_tmp =~ s/^\s*//; 1085 1086 ( my $element = $element_tmp ) =~ s/\s.*$//; 1087 if ( $element_tmp =~ /\s/ ) { 1088 $element_tmp =~ s/^[^\s]*\s//; 1089 } 1090 else { 1091 $element_tmp = ""; 1092 } 1093 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*$//; 1103 } 1104 else { 1105 $attr{$key} = $tmp; 1106 } 1107 $is_key = !$is_key; 1108 } 1109 1110 if ( $element ne "" ) { 1111 start_element( $element, %attr ); 1112 } 1113} 1114 1115# Parse the file 1116sub parse( $ ) 1117{ 1118 my ( $file ) = @_; 1119 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/<!--.*//; 1129 } 1130 elsif ( /-->/ && $in_comment ) { 1131 $in_comment = 0; 1132 s/.*-->//; 1133 } 1134 elsif ( $in_comment ) { 1135 next; 1136 } 1137 # ignore empty lines 1138 chomp; 1139 s/^\s*//; 1140 s/\s*$//; 1141 next if ( $_ eq "" ); 1142 1143 # take care of lines where element continues 1144 if ( $line ne "" ) { 1145 $line .= " " . $_; 1146 } 1147 else { 1148 $line = $_; 1149 } 1150 next if ( !/>$/ ); 1151 1152 # the actual parsing 1153 my @starts = split( /</, $line ); 1154 $line = ""; 1155 foreach $start ( @starts ) { 1156 next if ( $start eq "" ); 1157 1158 @ends = split( />/, $start ); 1159 my $element = $ends[0]; 1160 my $data = $ends[1]; 1161 1162 # start or end element 1163 if ( $element =~ /^\/(.*)/ ) { 1164 end_element( $1 ); 1165 } 1166 elsif ( $element =~ /^(.*)\/$/ ) { 1167 parse_start_element( $1 ); 1168 ( my $end = $1 ) =~ s/\s.*$//; 1169 end_element( $end ); 1170 } 1171 else { 1172 parse_start_element( $element ); 1173 } 1174 1175 # the data 1176 characters( $data ) if ( defined( $data ) && $data ne "" ); 1177 } 1178 } 1179} 1180 1181# Do the real work 1182open( IN, "<$src_shapes" ) || die "Cannot open $src_shapes."; 1183parse( IN ); 1184close( IN ); 1185 1186open( IN, "<$src_text" ) || die "Cannot open $src_text."; 1187parse( IN ); 1188close( IN ); 1189 1190if ( !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>"; 1197} 1198 1199# Generate the code 1200print <<EOF; 1201// Shape types generated from 1202// '$src_shapes' 1203// and 1204// '$src_text' 1205// which are part of the OOXML documentation 1206 1207#include <svx/escherex.hxx> 1208 1209const char* pShapeTypes[ ESCHER_ShpInst_COUNT ] = 1210{ 1211EOF 1212 1213for ( $i = 0; $i < 203; ++$i ) { 1214 if ( $i < 4 ) { 1215 print " /* $i - $shapes_ids{$i} - handled separately */\n NULL,\n"; 1216 } 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; 1223 1224 # escape the '"'s 1225 $out =~ s/"/\\"/g; 1226 1227 # output as string 1228 $out =~ s/^/ "/; 1229 $out =~ s/\n/"\n "/g; 1230 $out =~ s/$/"/; 1231 1232 print "$out,\n"; 1233 } 1234 else { 1235 print " NULL,\n"; 1236 } 1237 } 1238} 1239 1240print <<EOF; 1241}; 1242EOF 1243