summaryrefslogtreecommitdiffstats
path: root/oox/source/export/preset-definitions-to-shape-types.pl
diff options
context:
space:
mode:
Diffstat (limited to 'oox/source/export/preset-definitions-to-shape-types.pl')
-rw-r--r--oox/source/export/preset-definitions-to-shape-types.pl1237
1 files changed, 1237 insertions, 0 deletions
diff --git a/oox/source/export/preset-definitions-to-shape-types.pl b/oox/source/export/preset-definitions-to-shape-types.pl
new file mode 100644
index 000000000..5760a8609
--- /dev/null
+++ b/oox/source/export/preset-definitions-to-shape-types.pl
@@ -0,0 +1,1237 @@
+#!/usr/bin/env perl -w
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+use strict;
+use warnings;
+
+sub usage() {
+ print STDERR <<EOF;
+Usage: preset-definitions-to-shape-types.pl [ --drawingml-adj-names-data | --vml-shape-types-data ] <shapes> <text>
+
+Converts presetShapeDefinitions.xml and presetTextWarpDefinitions.xml to a
+.cxx that contains VML with the definitions of the shapes. The result is
+written to stdout.
+
+<shapes> presetShapeDefinitions.xml (including the path to it)
+<text> presetTextWarpDefinitions.xml (including the path to it)
+EOF
+ exit 1;
+}
+
+sub show_call_stack
+{
+ my ( $path, $line, $subr );
+ my $max_depth = 30;
+ my $i = 1;
+ print STDERR "--- Begin stack trace ---\n";
+ while ( (my @call_details = (caller($i++))) && ($i<$max_depth) ) {
+ print STDERR "$call_details[1] line $call_details[2] in function $call_details[3]\n";
+ }
+ print STDERR "--- End stack trace ---\n";
+}
+
+my $drawingml_adj_names_data = 0;
+my $vml_shape_types_data = 0;
+my $src_shapes = shift;
+if ($src_shapes eq "--drawingml-adj-names-data") {
+ $drawingml_adj_names_data = 1;
+ $src_shapes = shift;
+} elsif ($src_shapes eq "--vml-shape-types-data") {
+ $vml_shape_types_data = 1;
+ $src_shapes = shift;
+}
+my $src_text = shift;
+
+usage() if ( !defined( $src_shapes ) || !defined( $src_text ) ||
+ $src_shapes eq "-h" || $src_shapes eq "--help" ||
+ !-f $src_shapes || !-f $src_text );
+
+# Global variables
+my @levels = ();
+my $shape_name = "";
+my $state = "";
+my $path = "";
+my $adjust = "";
+my %adj_names;
+my $max_adj_no = 0;
+my @formulas = ();
+my %variables = ();
+my $ignore_this_shape = 0;
+my $handles = "";
+my $textboxrect = "";
+my $last_pos_x = "";
+my $last_pos_y = "";
+my $no_stroke = 0;
+my $no_fill = 0;
+my $path_w = 1;
+my $path_h = 1;
+my @quadratic_bezier = ();
+
+my %result_shapes = ();
+
+my %shapes_ids = (
+ 0 => 'notPrimitive',
+ 1 => 'rectangle',
+ 2 => 'roundRectangle',
+ 3 => 'ellipse',
+ 4 => 'diamond',
+ 5 => 'triangle',
+ 6 => 'rtTriangle',
+ 7 => 'parallelogram',
+ 8 => 'trapezoid',
+ 9 => 'hexagon',
+ 10 => 'octagon',
+ 11 => 'plus',
+ 12 => 'star5',
+ 13 => 'rightArrow',
+ 14 => 'thickArrow', # should not be used
+ 15 => 'homePlate',
+ 16 => 'cube',
+ 17 => 'wedgeRoundRectCallout', # balloon
+ 18 => 'star16', # seal
+ 19 => 'arc',
+ 20 => 'line',
+ 21 => 'plaque',
+ 22 => 'can',
+ 23 => 'donut',
+ 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?
+ 25 => 'textStop', # textOctagon FIXME see 24
+ 26 => 'textTriangle', # textHexagon FIXMME see 24
+ 27 => 'textCanDown', # textCurve FIXMME see 24
+ 28 => 'textWave1', # textWave FIXMME see 24
+ 29 => 'textArchUpPour', # textRing FIXMME see 24
+ 30 => 'textCanDown', # textOnCurve FIXMME see 24
+ 31 => 'textArchUp', # textOnRing FIXMME see 24
+ 32 => 'straightConnector1',
+ 33 => 'bentConnector2',
+ 34 => 'bentConnector3',
+ 35 => 'bentConnector4',
+ 36 => 'bentConnector5',
+ 37 => 'curvedConnector2',
+ 38 => 'curvedConnector3',
+ 39 => 'curvedConnector4',
+ 40 => 'curvedConnector5',
+ 41 => 'callout1',
+ 42 => 'callout2',
+ 43 => 'callout3',
+ 44 => 'accentCallout1',
+ 45 => 'accentCallout2',
+ 46 => 'accentCallout3',
+ 47 => 'borderCallout1',
+ 48 => 'borderCallout2',
+ 49 => 'borderCallout3',
+ 50 => 'accentBorderCallout1',
+ 51 => 'accentBorderCallout2',
+ 52 => 'accentBorderCallout3',
+ 53 => 'ribbon',
+ 54 => 'ribbon2',
+ 55 => 'chevron',
+ 56 => 'pentagon',
+ 57 => 'noSmoking',
+ 58 => 'star8', # seal8
+ 59 => 'star16', # seal16
+ 60 => 'star32', # seal32
+ 61 => 'wedgeRectCallout',
+ 62 => 'wedgeRoundRectCallout', # wedgeRRectCallout
+ 63 => 'wedgeEllipseCallout',
+ 64 => 'wave',
+ 65 => 'foldedCorner',
+ 66 => 'leftArrow',
+ 67 => 'downArrow',
+ 68 => 'upArrow',
+ 69 => 'leftRightArrow',
+ 70 => 'upDownArrow',
+ 71 => 'irregularSeal1',
+ 72 => 'irregularSeal2',
+ 73 => 'lightningBolt',
+ 74 => 'heart',
+ 75 => 'pictureFrame',
+ 76 => 'quadArrow',
+ 77 => 'leftArrowCallout',
+ 78 => 'rightArrowCallout',
+ 79 => 'upArrowCallout',
+ 80 => 'downArrowCallout',
+ 81 => 'leftRightArrowCallout',
+ 82 => 'upDownArrowCallout',
+ 83 => 'quadArrowCallout',
+ 84 => 'bevel',
+ 85 => 'leftBracket',
+ 86 => 'rightBracket',
+ 87 => 'leftBrace',
+ 88 => 'rightBrace',
+ 89 => 'leftUpArrow',
+ 90 => 'bentUpArrow',
+ 91 => 'bentArrow',
+ 92 => 'star24', # seal24
+ 93 => 'stripedRightArrow',
+ 94 => 'notchedRightArrow',
+ 95 => 'blockArc',
+ 96 => 'smileyFace',
+ 97 => 'verticalScroll',
+ 98 => 'horizontalScroll',
+ 99 => 'circularArrow',
+ 100 => 'notchedCircularArrow', # should not be used
+ 101 => 'uturnArrow',
+ 102 => 'curvedRightArrow',
+ 103 => 'curvedLeftArrow',
+ 104 => 'curvedUpArrow',
+ 105 => 'curvedDownArrow',
+ 106 => 'cloudCallout',
+ 107 => 'ellipseRibbon',
+ 108 => 'ellipseRibbon2',
+ 109 => 'flowChartProcess',
+ 110 => 'flowChartDecision',
+ 111 => 'flowChartInputOutput',
+ 112 => 'flowChartPredefinedProcess',
+ 113 => 'flowChartInternalStorage',
+ 114 => 'flowChartDocument',
+ 115 => 'flowChartMultidocument',
+ 116 => 'flowChartTerminator',
+ 117 => 'flowChartPreparation',
+ 118 => 'flowChartManualInput',
+ 119 => 'flowChartManualOperation',
+ 120 => 'flowChartConnector',
+ 121 => 'flowChartPunchedCard',
+ 122 => 'flowChartPunchedTape',
+ 123 => 'flowChartSummingJunction',
+ 124 => 'flowChartOr',
+ 125 => 'flowChartCollate',
+ 126 => 'flowChartSort',
+ 127 => 'flowChartExtract',
+ 128 => 'flowChartMerge',
+ 129 => 'flowChartOfflineStorage',
+ 130 => 'flowChartOnlineStorage',
+ 131 => 'flowChartMagneticTape',
+ 132 => 'flowChartMagneticDisk',
+ 133 => 'flowChartMagneticDrum',
+ 134 => 'flowChartDisplay',
+ 135 => 'flowChartDelay',
+ 136 => 'textPlain', # textPlainText
+ 137 => 'textStop',
+ 138 => 'textTriangle',
+ 139 => 'textTriangleInverted',
+ 140 => 'textChevron',
+ 141 => 'textChevronInverted',
+ 142 => 'textRingInside',
+ 143 => 'textRingOutside',
+ 144 => 'textArchUp', # textArchUpCurve
+ 145 => 'textArchDown', # textArchDownCurve
+ 146 => 'textCircle', # textCircleCurve
+ 147 => 'textButton', # textButtonCurve
+ 148 => 'textArchUpPour',
+ 149 => 'textArchDownPour',
+ 150 => 'textCirclePour',
+ 151 => 'textButtonPour',
+ 152 => 'textCurveUp',
+ 153 => 'textCurveDown',
+ 154 => 'textCascadeUp',
+ 155 => 'textCascadeDown',
+ 156 => 'textWave1',
+ 157 => 'textWave2',
+ 158 => 'textWave3',
+ 159 => 'textWave4',
+ 160 => 'textInflate',
+ 161 => 'textDeflate',
+ 162 => 'textInflateBottom',
+ 163 => 'textDeflateBottom',
+ 164 => 'textInflateTop',
+ 165 => 'textDeflateTop',
+ 166 => 'textDeflateInflate',
+ 167 => 'textDeflateInflateDeflate',
+ 168 => 'textFadeRight',
+ 169 => 'textFadeLeft',
+ 170 => 'textFadeUp',
+ 171 => 'textFadeDown',
+ 172 => 'textSlantUp',
+ 173 => 'textSlantDown',
+ 174 => 'textCanUp',
+ 175 => 'textCanDown',
+ 176 => 'flowChartAlternateProcess',
+ 177 => 'flowChartOffpageConnector',
+ 178 => 'callout1', # callout90
+ 179 => 'accentCallout1', # accentCallout90
+ 180 => 'borderCallout1', # borderCallout90
+ 181 => 'accentBorderCallout1', # accentBorderCallout90
+ 182 => 'leftRightUpArrow',
+ 183 => 'sun',
+ 184 => 'moon',
+ 185 => 'bracketPair',
+ 186 => 'bracePair',
+ 187 => 'star4', # seal4
+ 188 => 'doubleWave',
+ 189 => 'actionButtonBlank',
+ 190 => 'actionButtonHome',
+ 191 => 'actionButtonHelp',
+ 192 => 'actionButtonInformation',
+ 193 => 'actionButtonForwardNext',
+ 194 => 'actionButtonBackPrevious',
+ 195 => 'actionButtonEnd',
+ 196 => 'actionButtonBeginning',
+ 197 => 'actionButtonReturn',
+ 198 => 'actionButtonDocument',
+ 199 => 'actionButtonSound',
+ 200 => 'actionButtonMovie',
+ 201 => 'hostControl',
+ 202 => 'textBox'
+);
+# An error occurred, we have to ignore this shape
+sub error( $ )
+{
+ my ( $msg ) = @_;
+
+ $ignore_this_shape = 1;
+ print STDERR "ERROR (in $shape_name ): $msg\n";
+}
+
+# Setup the %variables map with predefined values
+sub setup_variables()
+{
+ %variables = (
+ 'l' => 0,
+ 't' => 0,
+ 'r' => 21600,
+ 'b' => 21600,
+
+ 'w' => 21600,
+ 'h' => 21600,
+ 'ss' => 21600,
+ 'ls' => 21600,
+
+ 'ssd2' => 10800, # 1/2
+ 'ssd4' => 5400, # 1/4
+ 'ssd6' => 3600, # 1/6
+ 'ssd8' => 2700, # 1/8
+ 'ssd16' => 1350, # 1/16
+ 'ssd32' => 675, # 1/32
+
+ 'hc' => 10800, # horizontal center
+ 'vc' => 10800, # vertical center
+
+ 'wd2' => 10800, # 1/2
+ 'wd3' => 7200, # 1/3
+ 'wd4' => 5400, # 1/4
+ 'wd5' => 4320, # 1/5
+ 'wd6' => 3600, # 1/6
+ 'wd8' => 2700, # 1/8
+ 'wd10' => 2160, # 1/10
+ 'wd12' => 1800, # 1/12
+ 'wd32' => 675, # 1/32
+
+ 'hd2' => 10800, # 1/2
+ 'hd3' => 7200, # 1/3
+ 'hd4' => 5400, # 1/4
+ 'hd5' => 4320, # 1/5
+ 'hd6' => 3600, # 1/6
+ 'hd8' => 2700, # 1/8
+ 'hd10' => 2160, # 1/10
+ 'hd12' => 1800, # 1/12
+ 'hd32' => 675, # 1/32
+
+ '25000' => 5400,
+ '12500' => 2700,
+
+ 'cd4' => 90, # 1/4 of a circle
+ 'cd2' => 180, # 1/2 of a circle
+ '3cd4' => 270, # 3/4 of a circle
+
+ 'cd8' => 45, # 1/8 of a circle
+ '3cd8' => 135, # 3/8 of a circle
+ '5cd8' => 225, # 5/8 of a circle
+ '7cd8' => 315, # 7/8 of a circle
+
+ '-5400000' => -90,
+ '-10800000'=> -180,
+ '-16200000'=> -270,
+ '-21600000'=> -360,
+ '-21599999'=> -360,
+
+ '5400000' => 90,
+ '10800000' => 180,
+ '16200000' => 270,
+ '21600000' => 360,
+ '21599999' => 360
+#
+# '21600000' => 360, # angle conversions
+# '27000000' => 450,
+# '32400000' => 540,
+# '37800000' => 630
+ );
+}
+
+# Convert the (predefined) value to a number
+sub value( $ )
+{
+ my ( $val ) = @_;
+
+ my $result = $variables{$val};
+ return $result if ( defined( $result ) );
+
+ return $val if ( $val =~ /^[0-9-]+$/ );
+
+ error( "Unknown variable '$val'." );
+
+ show_call_stack();
+ return $val;
+}
+
+# Convert the DrawingML formula to a VML one
+my %command_variables = (
+ 'w' => 'width',
+ 'h' => 'height',
+ 'r' => 'width',
+ 'b' => 'height'
+);
+
+# The same as value(), but some of the hardcoded values can have a name
+sub command_value( $ )
+{
+ my ( $value ) = @_;
+
+ return "" if ( $value eq "" );
+
+ return $value if ( $value =~ /^@/ );
+
+ my $command_val = $command_variables{$value};
+ if ( defined( $command_val ) ) {
+ return $command_val;
+ }
+
+ return value( $value );
+}
+
+# Insert the new formula to the list of formulas
+# Creates the name if it's empty...
+sub insert_formula( $$ )
+{
+ my ( $name, $fmla ) = @_;
+
+ my $i = 0;
+ foreach my $f ( @formulas ) {
+ if ( $f eq $fmla ) {
+ if ( $name ne "" ) {
+ $variables{$name} = "@" . $i;
+ }
+ return "@" . $i;
+ }
+ ++$i;
+ }
+
+ if ( $name eq "" ) {
+ $name = "@" . ( $#formulas + 1 );
+ }
+
+ $variables{$name} = "@" . ( $#formulas + 1 );
+ push @formulas, $fmla;
+
+ if ( $#formulas > 127 ) {
+ error( "Reached the maximum amount of formulas, have to ignore the shape '$shape_name'" );
+ }
+
+ return $variables{$name};
+}
+
+# The same as insert_formula(), but converts the params
+sub insert_formula_params( $$$$$ )
+{
+ my ( $name, $command, $p1, $p2, $p3 ) = @_;
+
+ my $result = $command;
+ if ( $p1 ne "" ) {
+ $result .= " " . command_value( $p1 );
+ if ( $p2 ne "" ) {
+ $result .= " " . command_value( $p2 );
+ if ( $p3 ne "" ) {
+ $result .= " " . command_value( $p3 );
+ }
+ }
+ }
+
+ return insert_formula( $name, $result );
+}
+
+# Convert the formula from DrawingML to VML
+sub convert_formula( $$ )
+{
+ my ( $name, $fmla ) = @_;
+
+ if ( $fmla =~ /^([^ ]+)/ ) {
+ my $command = $1;
+
+ # parse the parameters
+ ( my $values = $fmla ) =~ s/^([^ ]+) *//;
+ my $p1 = "";
+ my $p2 = "";
+ my $p3 = "";
+ if ( $values =~ /^([^ ]+)/ ) {
+ $p1 = $1;
+ $values =~ s/^([^ ]+) *//;
+ if ( $values =~ /^([^ ]+)/ ) {
+ $p2 = $1;
+ $values =~ s/^([^ ]+) *//;
+ if ( $values =~ /^([^ ]+)/ ) {
+ $p3 = $1;
+ }
+ }
+ }
+
+ # now convert the formula
+ if ( $command eq "+-" ) {
+ if ( $p1 eq "100000" ) {
+ $p1 = value( 'w' );
+ }
+ insert_formula_params( $name, "sum", $p1, $p2, $p3 );
+ return;
+ }
+ elsif ( $command eq "*/" ) {
+ if ( ( $p2 =~ /^(w|h|ss|hd2|wd2|vc)$/ ) && defined( $variables{$p1} ) ) {
+ # switch it ;-) - presetTextWarpDefinitions.xml has it in other order
+ my $tmp = $p1;
+ $p1 = $p2;
+ $p2 = $tmp;
+ }
+
+ if ( ( $p1 =~ /^(w|h|ss|hd2|wd2|vc)$/ ) && defined( $variables{$p2} ) ) {
+ my $val3 = $p3;
+ if ( $val3 =~ /^[0-9-]+$/ ) {
+ $val3 *= ( value( 'w' ) / value( $p1 ) );
+
+ # Oh yes, I'm too lazy to implement the full GCD here ;-)
+ if ( ( $val3 % 100000 ) == 0 ) {
+ $p1 = 1;
+ $p3 = sprintf( "%.0f", ( $val3 / 100000 ) );
+ }
+ elsif ( $val3 < 100000 ) {
+ $p3 = 1;
+ while ( ( ( $p3 * 100000 ) % $val3 ) != 0 ) {
+ ++$p3
+ }
+ $p1 = ( $p3 * 100000 ) / $val3;
+ }
+ else {
+ error( "Need to count the greatest common divisor." );
+ }
+ }
+ }
+ elsif ( $p3 eq "100000" && $p2 =~ /^[0-9-]+$/ ) {
+ # prevent overflows in some shapes
+ $p2 = sprintf( "%.0f", ( $p2 / 10 ) );
+ $p3 /= 10;
+ }
+ elsif ( $p3 eq "32768" && $p2 =~ /^[0-9-]+$/ ) {
+ # prevent overflows in some shapes
+ $p2 = sprintf( "%.0f", ( $p2 / 8 ) );
+ $p3 /= 8;
+ }
+ elsif ( $p3 eq "50000" ) {
+ $p3 = 10800;
+ }
+ elsif ( $name =~ /^maxAdj/ ) {
+ my $val = value( $p1 );
+ if ( $val =~ /^[0-9-]+$/ ) {
+ $p1 = sprintf( "%.0f", ( value( 'w' ) * $val / 100000 ) );
+ }
+ }
+
+ if ( ( value( $p1 ) eq value( $p3 ) ) || ( value( $p2 ) eq value( $p3 ) ) ) {
+ my $val = value( ( value( $p1 ) eq value( $p3 ) )? $p2: $p1 );
+ if ( $val =~ /^@([0-9]+)$/ ) {
+ insert_formula( $name, $formulas[$1] );
+ }
+ else {
+ insert_formula( $name, "val $val" );
+ }
+ }
+ else {
+ insert_formula_params( $name, "prod", $p1, $p2, $p3 );
+ }
+ return;
+ }
+ elsif ( $command eq "+/" ) {
+ # we have to split this into 2 formulas - 'sum' and 'prod'
+ my $constructed = insert_formula_params( "", "sum", $p1, $p2, "0" );
+ insert_formula_params( $name, "prod", 1, $constructed, $p3); # references the 'sum' formula
+ return;
+ }
+ elsif ( $command eq "?:" ) {
+ insert_formula_params( $name, "if", $p1, $p2, $p3 );
+ return;
+ }
+ elsif ( $command eq "sin" || $command eq "cos" ) {
+ if ( $p2 =~ /^[0-9-]+$/ && ( ( $p2 % 60000 ) == 0 ) ) {
+ $p2 /= 60000;
+ }
+ else {
+ $p2 = insert_formula_params( "", "prod", "1", $p2, "60000" );
+ }
+ # we have to use 'sumangle' even for the case when $p2 is const
+ # and theoretically could be written as such; but Word does not
+ # accept it :-(
+ my $conv = insert_formula_params( "", "sumangle", "0", $p2, "0" );
+
+ $p2 = $conv;
+
+ insert_formula_params( $name, $command, $p1, $p2, "" );
+ return;
+ }
+ elsif ( $command eq "abs" ) {
+ insert_formula_params( $name, $command, $p1, "", "" );
+ return;
+ }
+ elsif ( $command eq "max" || $command eq "min" ) {
+ insert_formula_params( $name, $command, $p1, $p2, "" );
+ return;
+ }
+ elsif ( $command eq "at2" ) {
+ insert_formula_params( $name, "atan2", $p1, $p2, "" );
+ return;
+ }
+ elsif ( $command eq "cat2" ) {
+ insert_formula_params( $name, "cosatan2", $p1, $p2, $p3 );
+ return;
+ }
+ elsif ( $command eq "sat2" ) {
+ insert_formula_params( $name, "sinatan2", $p1, $p2, $p3 );
+ return;
+ }
+ elsif ( $command eq "sqrt" ) {
+ insert_formula_params( $name, "sqrt", $p1, "", "" );
+ return;
+ }
+ elsif ( $command eq "mod" ) {
+ insert_formula_params( $name, "mod", $p1, $p2, $p3 );
+ return;
+ }
+ elsif ( $command eq "val" ) {
+ insert_formula_params( $name, "val", value( $p1 ), "", "" );
+ return;
+ }
+ else {
+ error( "Unknown formula '$name', '$fmla'." );
+ }
+ }
+ else {
+ error( "Cannot convert formula's command '$name', '$fmla'." );
+ }
+}
+
+# There's no exact equivalent of 'arcTo' in VML, we have to do some special casing...
+my %convert_arcTo = (
+ '0' => {
+ '90' => {
+ 'path' => 'qy',
+ 'op' => [ 'sum 0 __last_x__ __wR__', 'sum __hR__ __last_y__ 0' ],
+ },
+ '-90' => {
+ 'path' => 'qy',
+ 'op' => [ 'sum 0 __last_x__ __wR__', 'sum 0 __last_y__ __hR__' ],
+ },
+ },
+ '90' => {
+ '90' => {
+ 'path' => 'qx',
+ 'op' => [ 'sum 0 __last_x__ __wR__', 'sum 0 __last_y__ __hR__' ],
+ },
+ '-90' => {
+ 'path' => 'qx',
+ 'op' => [ 'sum __wR__ __last_x__ 0', 'sum 0 __last_y__ __hR__' ],
+ },
+ },
+ '180' => {
+ '90' => {
+ 'path' => 'qy',
+ 'op' => [ 'sum __wR__ __last_x__ 0', 'sum 0 __last_y__ __hR__' ],
+ },
+ '-90' => {
+ 'path' => 'qy',
+ 'op' => [ 'sum __wR__ __last_x__ 0', 'sum __hR__ __last_y__ 0' ],
+ },
+ },
+ '270' => {
+ '90' => {
+ 'path' => 'qx',
+ 'op' => [ 'sum __wR__ __last_x__ 0', 'sum __hR__ __last_y__ 0' ],
+ },
+ '-90' => {
+ 'path' => 'qx',
+ 'op' => [ 'sum 0 __last_x__ __wR__', 'sum __hR__ __last_y__ 0' ],
+ },
+ },
+);
+
+# Elliptic quadrant
+# FIXME optimize so that we compute the const values when possible
+sub elliptic_quadrant( $$$$ )
+{
+ my ( $wR, $hR, $stAng, $swAng ) = @_;
+
+ if ( defined( $convert_arcTo{$stAng} ) && defined( $convert_arcTo{$stAng}{$swAng} ) ) {
+ my $conv_path = $convert_arcTo{$stAng}{$swAng}{'path'};
+ my $conv_op_ref = $convert_arcTo{$stAng}{$swAng}{'op'};
+
+ $path .= "$conv_path";
+
+ my $pos_x = $last_pos_x;
+ my $pos_y = $last_pos_y;
+ for ( my $i = 0; $i <= $#{$conv_op_ref}; ++$i ) {
+ my $op = $conv_op_ref->[$i];
+
+ $op =~ s/__last_x__/$last_pos_x/g;
+ $op =~ s/__last_y__/$last_pos_y/g;
+ $op =~ s/__wR__/$wR/g;
+ $op =~ s/__hR__/$hR/g;
+
+ my $fmla = insert_formula( "", $op );
+
+ $path .= $fmla;
+
+ # so far it's sufficient just to rotate the positions
+ # FIXME if not ;-)
+ $pos_x = $pos_y;
+ $pos_y = $fmla;
+ }
+ $last_pos_x = $pos_x;
+ $last_pos_y = $pos_y;
+ }
+ else {
+ error( "Unhandled elliptic_quadrant(), input is ($wR, $hR, $stAng, $swAng)." );
+ }
+}
+
+# Convert the quadratic bezier to cubic (exact)
+# No idea why, but the 'qb' did not work for me :-(
+sub quadratic_to_cubic_bezier( $ )
+{
+ my ( $axis ) = @_;
+
+ my $a0 = $quadratic_bezier[0]->{$axis};
+ my $a1 = $quadratic_bezier[1]->{$axis};
+ my $a2 = $quadratic_bezier[2]->{$axis};
+
+ my $b0 = $a0;
+
+ # $b1 = $a0 + 2/3 * ( $a1 - $a0 ), but in VML
+ # FIXME optimize for constants - compute directly
+ my $b1_1 = insert_formula_params( "", "sum", "0", $a1, $a0 );
+ my $b1_2 = insert_formula_params( "", "prod", "2", $b1_1, "3" );
+ my $b1 = insert_formula_params( "", "sum", $a0, $b1_2, "0" );
+
+ # $b2 = $b1 + 1/3 * ( $a2 - $a0 );
+ # FIXME optimize for constants - compute directly
+ my $b2_1 = insert_formula_params( "", "sum", "0", $a2, $a0 );
+ my $b2_2 = insert_formula_params( "", "prod", "1", $b2_1, "3" );
+ my $b2 = insert_formula_params( "", "sum", $b1, $b2_2, "0" );
+
+ my $b3 = $a2;
+
+ return ( $b0, $b1, $b2, $b3 );
+}
+
+# Extend $path by one more point
+sub add_point_to_path( $$ )
+{
+ my ( $x, $y ) = @_;
+
+ if ( $path =~ /[0-9]$/ && $x =~ /^[0-9-]/ ) {
+ $path .= ",";
+ }
+ $path .= $x;
+
+ if ( $path =~ /[0-9]$/ && $y =~ /^[0-9-]/ ) {
+ $path .= ",";
+ }
+ $path .= $y;
+}
+
+# Start of an element
+sub start_element( $% )
+{
+ my ( $element, %attr ) = @_;
+
+ push @levels, $element;
+
+ #print "element: $element\n";
+
+ if ( @levels > 1 && ( $levels[-2] eq "presetShapeDefinitons" ||
+ $levels[-2] eq "presetTextWarpDefinitions" ) ) {
+ $shape_name = $element;
+
+ $state = "";
+ $ignore_this_shape = 0;
+ $path = "";
+ $adjust = "";
+ $max_adj_no = 0;
+ @formulas = ();
+ $handles = "";
+ $textboxrect = "";
+ $last_pos_x = "";
+ $last_pos_y = "";
+ $no_stroke = 0;
+ $no_fill = 0;
+ @quadratic_bezier = ();
+
+ setup_variables();
+
+ if ( $shape_name eq "sun" ) {
+ # hack for this shape
+ $variables{'100000'} = "21600";
+ $variables{'50000'} = "10800";
+ $variables{'25000'} = "5400";
+ $variables{'12500'} = "2700";
+ $variables{'3662'} = "791";
+ }
+
+ my $found = 0;
+ foreach my $name ( values( %shapes_ids ) ) {
+ if ( $name eq $shape_name ) {
+ $found = 1;
+ last;
+ }
+ }
+ if ( !$found ) {
+ error( "Unknown shape '$shape_name'." );
+ }
+ }
+ elsif ( $element eq "pathLst" ) {
+ $state = "path";
+ }
+ elsif ( $element eq "avLst" ) {
+ $state = "adjust";
+ }
+ elsif ( $element eq "gdLst" ) {
+ $state = "formulas";
+ }
+ elsif ( $element eq "ahLst" ) {
+ $state = "handles";
+ }
+ elsif ( $element eq "rect" ) {
+ $textboxrect = value( $attr{'l'} ) . "," . value( $attr{'t'} ) . "," .
+ value( $attr{'r'} ) . "," . value( $attr{'b'} );
+ }
+ elsif ( $state eq "path" ) {
+ if ( $element eq "path" ) {
+ $no_stroke = ( defined( $attr{'stroke'} ) && $attr{'stroke'} eq 'false' );
+ $no_fill = ( defined( $attr{'fill'} ) && $attr{'fill'} eq 'none' );
+ $path_w = $attr{'w'};
+ $path_h = $attr{'h'};
+ }
+ elsif ( $element eq "moveTo" ) {
+ $path .= "m";
+ }
+ elsif ( $element eq "lnTo" ) {
+ $path .= "l";
+ }
+ elsif ( $element eq "cubicBezTo" ) {
+ $path .= "c";
+ }
+ elsif ( $element eq "quadBezTo" ) {
+ my %points = ( 'x' => $last_pos_x, 'y' => $last_pos_y );
+ @quadratic_bezier = ( \%points );
+ }
+ elsif ( $element eq "close" ) {
+ $path .= "x";
+ }
+ elsif ( $element eq "pt" ) {
+ # remember the last position for the arcTo
+ $last_pos_x = value( $attr{'x'} );
+ $last_pos_y = value( $attr{'y'} );
+
+ $last_pos_x *= ( value( 'w' ) / $path_w ) if ( defined( $path_w ) );
+ $last_pos_y *= ( value( 'h' ) / $path_h ) if ( defined( $path_h ) );
+
+ if ( $#quadratic_bezier >= 0 ) {
+ my %points = ( 'x' => $last_pos_x, 'y' => $last_pos_y );
+ push( @quadratic_bezier, \%points );
+ }
+ else {
+ add_point_to_path( $last_pos_x, $last_pos_y );
+ }
+ }
+ elsif ( ( $element eq "arcTo" ) && ( $last_pos_x ne "" ) && ( $last_pos_y ne "" ) ) {
+ # there's no exact equivalent of arcTo in VML, so we have to
+ # compute here a bit...
+ my $stAng = value( $attr{'stAng'} );
+ my $swAng = value( $attr{'swAng'} );
+ my $wR = value( $attr{'wR'} );
+ my $hR = value( $attr{'hR'} );
+
+ $wR *= ( value( 'w' ) / $path_w ) if ( defined( $path_w ) );
+ $hR *= ( value( 'h' ) / $path_h ) if ( defined( $path_h ) );
+
+ if ( ( $stAng =~ /^[0-9-]+$/ ) && ( $swAng =~ /^[0-9-]+$/ ) ) {
+ if ( ( ( $stAng % 90 ) == 0 ) && ( ( $swAng % 90 ) == 0 ) && ( $swAng != 0 ) ) {
+ my $end = $stAng + $swAng;
+ my $step = ( $swAng > 0 )? 90: -90;
+
+ for ( my $cur = $stAng; $cur != $end; $cur += $step ) {
+ elliptic_quadrant( $wR, $hR, ( $cur % 360 ), $step );
+ }
+ }
+ else {
+ error( "Unsupported numeric 'arcTo' ($attr{'wR'}, $attr{'hR'}, $stAng, $swAng)." );
+ }
+ }
+ else {
+ error( "Unsupported 'arcTo' conversion ($attr{'wR'}, $attr{'hR'}, $stAng, $swAng)." );
+ }
+ }
+ else {
+ error( "Unhandled path element '$element'." );
+ }
+ }
+ elsif ( $state eq "adjust" ) {
+ if ( $element eq "gd" ) {
+ my $adj_no = $attr{'name'};
+
+ # Save this adj number for this type for later use.
+ push(@{$adj_names{$shape_name}}, $adj_no);
+
+ my $is_const = 0;
+
+ $adj_no =~ s/^adj//;
+ if ( $adj_no eq "" ) {
+ $max_adj_no = 0;
+ }
+ elsif ( !( $adj_no =~ /^[0-9]*$/ ) ) {
+ ++$max_adj_no;
+ $is_const = 1;
+ }
+ elsif ( $adj_no != $max_adj_no + 1 ) {
+ error( "Wrong order of adj values." );
+ ++$max_adj_no;
+ }
+ else {
+ $max_adj_no = $adj_no;
+ }
+
+ if ( $attr{'fmla'} =~ /^val ([0-9-]*)$/ ) {
+ my $val = sprintf( "%.0f", ( 21600 * $1 ) / 100000 );
+ if ( $is_const ) {
+ $variables{$adj_no} = $val;
+ }
+ elsif ( $adjust eq "" ) {
+ $adjust = $val;
+ }
+ else {
+ $adjust = "$val,$adjust";
+ }
+ }
+ else {
+ error( "Wrong fmla '$attr{'fmla'}'." );
+ }
+ }
+ else {
+ error( "Unhandled adjust element '$element'." );
+ }
+ }
+ elsif ( $state eq "formulas" ) {
+ if ( $element eq "gd" ) {
+ if ( $attr{'fmla'} =~ /^\*\/ (h|w|ss) adj([0-9]+) 100000$/ ) {
+ insert_formula( $attr{'name'}, "val #" . ( $max_adj_no - $2 ) );
+ }
+ elsif ( $attr{'fmla'} =~ /^pin [^ ]+ ([^ ]+) / ) {
+ print STDERR "TODO Map 'pin' to VML as xrange for handles.\n";
+ my $pin_val = $1;
+ if ( $pin_val eq "adj" ) {
+ insert_formula( $attr{'name'}, "val #0" );
+ }
+ elsif ( $pin_val =~ /^adj([0-9]+)/ ) {
+ insert_formula( $attr{'name'}, "val #" . ( $max_adj_no - $1 ) );
+ }
+ else {
+ insert_formula( $attr{'name'}, "val " . value( $pin_val ) );
+ }
+ }
+ elsif ( $attr{'fmla'} =~ /adj/ ) {
+ error( "Non-standard usage of adj in '$attr{'fmla'}'." );
+ }
+ else {
+ convert_formula( $attr{'name'}, $attr{'fmla'} );
+ }
+ }
+ }
+ elsif ( $state eq "handles" ) {
+ if ( $element eq "pos" ) {
+ $handles .= "<v:h position=\"" . value( $attr{'x'} ) . "," . value( $attr{'y'} ) . "\"/>\n";
+ }
+ }
+}
+
+# End of an element
+sub end_element( $ )
+{
+ my ( $element ) = @_;
+
+ pop @levels;
+
+ if ( $element eq $shape_name ) {
+ if ( !$ignore_this_shape ) {
+ # we have all the info, generate the shape now
+ $state = "";
+
+ # shape path
+ my $out = "<v:shapetype id=\"_x0000_t__ID__\" coordsize=\"21600,21600\" o:spt=\"__ID__\" ";
+ if ( $adjust ne "" ) {
+ $out .= "adj=\"$adjust\" ";
+ }
+
+ # optimize it [yes, we need this twice ;-)]
+ $path =~ s/([^0-9-@])0([^0-9-@])/$1$2/g;
+ $path =~ s/([^0-9-@])0([^0-9-@])/$1$2/g;
+
+ $out .= "path=\"$path\">\n";
+
+ # stroke
+ $out .= "<v:stroke joinstyle=\"miter\"/>\n";
+
+ # formulas
+ if ( $#formulas >= 0 )
+ {
+ $out .= "<v:formulas>\n";
+ foreach my $fmla ( @formulas ) {
+ $out .= "<v:f eqn=\"$fmla\"/>\n"
+ }
+ $out .= "</v:formulas>\n";
+ }
+
+ # path
+ if ( $textboxrect ne "" ) { # TODO connectlocs, connectangles
+ $out .= "<v:path gradientshapeok=\"t\" o:connecttype=\"rect\" textboxrect=\"$textboxrect\"/>\n";
+ }
+
+ # handles
+ if ( $handles ne "" ) {
+ $out .= "<v:handles>\n$handles</v:handles>\n";
+ }
+
+ $out .="</v:shapetype>";
+
+ # hooray! :-)
+ $result_shapes{$shape_name} = $out;
+ }
+ else {
+ print STDERR "Shape '$shape_name' ignored; see the above error(s) for the reason.\n";
+ }
+ $shape_name = "";
+ }
+ elsif ( $state eq "path" ) {
+ if ( $element eq "path" ) {
+ $path .= "ns" if ( $no_stroke );
+ $path .= "nf" if ( $no_fill );
+ $path .= "e";
+ }
+ elsif ( $element eq "quadBezTo" ) {
+ # we have to convert the quadratic bezier to cubic
+ if ( $#quadratic_bezier == 2 ) {
+ my @points_x = quadratic_to_cubic_bezier( 'x' );
+ my @points_y = quadratic_to_cubic_bezier( 'y' );
+
+ $path .= "c";
+ # ignore the starting point
+ for ( my $i = 1; $i < 4; ++$i ) {
+ add_point_to_path( $points_x[$i], $points_y[$i] );
+ }
+ }
+ else {
+ error( "Wrong number of points of the quadratic bezier." );
+ }
+ @quadratic_bezier = ();
+ }
+ }
+ elsif ( $element eq "avLst" ) {
+ $state = "";
+ }
+ elsif ( $element eq "gdLst" ) {
+ $state = "";
+ }
+ elsif ( $element eq "ahLst" ) {
+ $state = "";
+ }
+}
+
+# Text inside an element
+sub characters( $ )
+{
+ #my ( $text ) = @_;
+}
+
+#################### A trivial XML parser ####################
+
+# Parse the attributes
+sub parse_start_element( $ )
+{
+ # split the string containing both the elements and attributes
+ my ( $element_tmp ) = @_;
+
+ $element_tmp =~ s/\s*$//;
+ $element_tmp =~ s/^\s*//;
+
+ ( my $element = $element_tmp ) =~ s/\s.*$//;
+ if ( $element_tmp =~ /\s/ ) {
+ $element_tmp =~ s/^[^\s]*\s//;
+ }
+ else {
+ $element_tmp = "";
+ }
+
+ # we have the element, now the attributes
+ my %attr;
+ my $is_key = 1;
+ my $key = "";
+ foreach my $tmp ( split( /"/, $element_tmp ) ) {
+ if ( $is_key ) {
+ $key = $tmp;
+ $key =~ s/^\s*//;
+ $key =~ s/\s*=\s*$//;
+ }
+ else {
+ $attr{$key} = $tmp;
+ }
+ $is_key = !$is_key;
+ }
+
+ if ( $element ne "" ) {
+ start_element( $element, %attr );
+ }
+}
+
+# Parse the file
+sub parse( $ )
+{
+ my ( $file ) = @_;
+
+ my $in_comment = 0;
+ my $line = "";
+ while (<$file>) {
+ # ignore comments
+ s/<\?[^>]*\?>//g;
+ s/<!--[^>]*-->//g;
+ if ( /<!--/ ) {
+ $in_comment = 1;
+ s/<!--.*//;
+ }
+ elsif ( /-->/ && $in_comment ) {
+ $in_comment = 0;
+ s/.*-->//;
+ }
+ elsif ( $in_comment ) {
+ next;
+ }
+ # ignore empty lines
+ chomp;
+ s/^\s*//;
+ s/\s*$//;
+ next if ( $_ eq "" );
+
+ # take care of lines where element continues
+ if ( $line ne "" ) {
+ $line .= " " . $_;
+ }
+ else {
+ $line = $_;
+ }
+ next if ( !/>$/ );
+
+ # the actual parsing
+ my @starts = split( /</, $line );
+ $line = "";
+ foreach my $start ( @starts ) {
+ next if ( $start eq "" );
+
+ my @ends = split( />/, $start );
+ my $element = $ends[0];
+ my $data = $ends[1];
+
+ # start or end element
+ if ( $element =~ /^\/(.*)/ ) {
+ end_element( $1 );
+ }
+ elsif ( $element =~ /^(.*)\/$/ ) {
+ parse_start_element( $1 );
+ ( my $end = $1 ) =~ s/\s.*$//;
+ end_element( $end );
+ }
+ else {
+ parse_start_element( $element );
+ }
+
+ # the data
+ characters( $data ) if ( defined( $data ) && $data ne "" );
+ }
+ }
+}
+
+# Do the real work
+my $file;
+open( $file, "<$src_shapes" ) || die "Cannot open $src_shapes: $!";
+parse( $file );
+close( $file );
+
+open( $file, "<$src_text" ) || die "Cannot open $src_text: $!";
+parse( $file );
+close( $file );
+
+if ( !defined( $result_shapes{'textBox'} ) ) {
+ # tdf#114842 shapetype id of the textbox, must be the same as defined
+ $result_shapes{'textBox'} =
+ "<v:shapetype id=\"_x0000_t__ID__\" coordsize=\"21600,21600\" " .
+ "o:spt=\"__ID__\" path=\"m,l,21600l21600,21600l21600,xe\">\n" .
+ "<v:stroke joinstyle=\"miter\"/>\n" .
+ "<v:path gradientshapeok=\"t\" o:connecttype=\"rect\"/>\n" .
+ "</v:shapetype>";
+}
+
+# Generate the data
+if ($drawingml_adj_names_data eq 1) {
+ foreach my $adj_name (sort(keys %adj_names))
+ {
+ foreach my $adj (@{$adj_names{$adj_name}})
+ {
+ print "$adj_name\t$adj\n";
+ }
+ }
+ exit 0;
+} elsif ($vml_shape_types_data eq 1) {
+ for ( my $i = 0; $i < 203; ++$i ) {
+ if ( $i < 4 ) {
+ print "/* $i - $shapes_ids{$i} - handled separately */\nNULL\n";
+ }
+ else {
+ print "/* $i - $shapes_ids{$i} */\n";
+ my $out = $result_shapes{$shapes_ids{$i}};
+ if ( defined( $out ) ) {
+ # set the id
+ $out =~ s/__ID__/$i/g;
+
+ # output as string
+ $out =~ s/\n//g;
+
+ print "$out\n";
+ }
+ else {
+ print "NULL\n";
+ }
+ }
+ }
+ exit 0;
+}
+
+# should not happen
+exit 1;
+
+# vim:set ft=perl shiftwidth=4 softtabstop=4 expandtab: #