From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- xmloff/source/draw/ximpcustomshape.cxx | 1421 ++++++++++++++++++++++++++++++++ 1 file changed, 1421 insertions(+) create mode 100644 xmloff/source/draw/ximpcustomshape.cxx (limited to 'xmloff/source/draw/ximpcustomshape.cxx') diff --git a/xmloff/source/draw/ximpcustomshape.cxx b/xmloff/source/draw/ximpcustomshape.cxx new file mode 100644 index 000000000..fbc32e53e --- /dev/null +++ b/xmloff/source/draw/ximpcustomshape.cxx @@ -0,0 +1,1421 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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 . + */ + +#include "ximpcustomshape.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::xmloff::token; +using namespace ::xmloff::EnhancedCustomShapeToken; + + +XMLEnhancedCustomShapeContext::XMLEnhancedCustomShapeContext( SvXMLImport& rImport, + css::uno::Reference< css::drawing::XShape >& rxShape, + std::vector< css::beans::PropertyValue >& rCustomShapeGeometry ) : + SvXMLImportContext( rImport ), + mrUnitConverter( rImport.GetMM100UnitConverter() ), + mrxShape( rxShape ), + mrCustomShapeGeometry( rCustomShapeGeometry ) +{ +} + +const SvXMLEnumMapEntry aXML_GluePointEnumMap[] = +{ + { XML_NONE, 0 }, + { XML_SEGMENTS, 1 }, + { XML_NONE, 2 }, + { XML_RECTANGLE, 3 }, + { XML_TOKEN_INVALID, 0 } +}; +static void GetBool( std::vector< css::beans::PropertyValue >& rDest, + std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp ) +{ + bool bAttrBool; + if (::sax::Converter::convertBool( bAttrBool, rValue )) + { + beans::PropertyValue aProp; + aProp.Name = EASGet( eDestProp ); + aProp.Value <<= bAttrBool; + rDest.push_back( aProp ); + } +} + +static void GetInt32( std::vector< css::beans::PropertyValue >& rDest, + std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp ) +{ + sal_Int32 nAttrNumber; + if (::sax::Converter::convertNumber( nAttrNumber, rValue )) + { + beans::PropertyValue aProp; + aProp.Name = EASGet( eDestProp ); + aProp.Value <<= nAttrNumber; + rDest.push_back( aProp ); + } +} + +static void GetDouble( std::vector< css::beans::PropertyValue >& rDest, + std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp ) +{ + double fAttrDouble; + if (::sax::Converter::convertDouble( fAttrDouble, rValue )) + { + beans::PropertyValue aProp; + aProp.Name = EASGet( eDestProp ); + aProp.Value <<= fAttrDouble; + rDest.push_back( aProp ); + } +} + +static void GetString( std::vector< css::beans::PropertyValue >& rDest, + const OUString& rValue, const EnhancedCustomShapeTokenEnum eDestProp ) +{ + beans::PropertyValue aProp; + aProp.Name = EASGet( eDestProp ); + aProp.Value <<= rValue; + rDest.push_back( aProp ); +} + +template +static void GetEnum( std::vector< css::beans::PropertyValue >& rDest, + std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp, + const SvXMLEnumMapEntry& rMap ) +{ + EnumT eKind; + if( SvXMLUnitConverter::convertEnum( eKind, rValue, &rMap ) ) + { + beans::PropertyValue aProp; + aProp.Name = EASGet( eDestProp ); + aProp.Value <<= static_cast(eKind); + rDest.push_back( aProp ); + } +} + +static void GetDoublePercentage( std::vector< css::beans::PropertyValue >& rDest, + std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp ) +{ + sal_Int16 const eSrcUnit = ::sax::Converter::GetUnitFromString( + rValue, util::MeasureUnit::MM_100TH); + if (util::MeasureUnit::PERCENT != eSrcUnit) + return; + + rtl_math_ConversionStatus eStatus; + double fAttrDouble = rtl_math_stringToDouble(rValue.data(), + rValue.data() + rValue.size(), + '.', ',', &eStatus, nullptr); + if ( eStatus == rtl_math_ConversionStatus_Ok ) + { + beans::PropertyValue aProp; + aProp.Name = EASGet( eDestProp ); + aProp.Value <<= fAttrDouble; + rDest.push_back( aProp ); + } +} + +static void GetB3DVector( std::vector< css::beans::PropertyValue >& rDest, + std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp ) +{ + ::basegfx::B3DVector aB3DVector; + if ( SvXMLUnitConverter::convertB3DVector( aB3DVector, rValue ) ) + { + drawing::Direction3D aDirection3D( aB3DVector.getX(), aB3DVector.getY(), aB3DVector.getZ() ); + beans::PropertyValue aProp; + aProp.Name = EASGet( eDestProp ); + aProp.Value <<= aDirection3D; + rDest.push_back( aProp ); + } +} + +static bool GetEquationName( const OUString& rEquation, const sal_Int32 nStart, OUString& rEquationName ) +{ + sal_Int32 nIndex = nStart; + while( nIndex < rEquation.getLength() ) + { + sal_Unicode nChar = rEquation[ nIndex ]; + if ( + ( ( nChar >= 'a' ) && ( nChar <= 'z' ) ) + || ( ( nChar >= 'A' ) && ( nChar <= 'Z' ) ) + || ( ( nChar >= '0' ) && ( nChar <= '9' ) ) + ) + { + nIndex++; + } + else + break; + } + bool bValid = ( nIndex - nStart ) != 0; + if ( bValid ) + rEquationName = rEquation.copy( nStart, nIndex - nStart ); + return bValid; +} + +static bool GetNextParameter( css::drawing::EnhancedCustomShapeParameter& rParameter, sal_Int32& nIndex, const OUString& rParaString ) +{ + if ( nIndex >= rParaString.getLength() ) + return false; + + bool bValid = true; + bool bNumberRequired = true; + bool bMustBePositiveWholeNumbered = false; + + rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::NORMAL; + if ( rParaString[ nIndex ] == '$' ) + { + rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::ADJUSTMENT; + bMustBePositiveWholeNumbered = true; + nIndex++; + } + else if ( rParaString[ nIndex ] == '?' ) + { + nIndex++; + bNumberRequired = false; + OUString aEquationName; + bValid = GetEquationName( rParaString, nIndex, aEquationName ); + if ( bValid ) + { + rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::EQUATION; + rParameter.Value <<= aEquationName; + nIndex += aEquationName.getLength(); + } + } + else if ( rParaString[ nIndex ] > '9' ) + { + bNumberRequired = false; + if ( rParaString.matchIgnoreAsciiCase( "left", nIndex ) ) + { + rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::LEFT; + nIndex += 4; + } + else if ( rParaString.matchIgnoreAsciiCase( "top", nIndex ) ) + { + rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::TOP; + nIndex += 3; + } + else if ( rParaString.matchIgnoreAsciiCase( "right", nIndex ) ) + { + rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::RIGHT; + nIndex += 5; + } + else if ( rParaString.matchIgnoreAsciiCase( "bottom", nIndex ) ) + { + rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::BOTTOM; + nIndex += 6; + } + else if ( rParaString.matchIgnoreAsciiCase( "xstretch", nIndex ) ) + { + rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::XSTRETCH; + nIndex += 8; + } + else if ( rParaString.matchIgnoreAsciiCase( "ystretch", nIndex ) ) + { + rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::YSTRETCH; + nIndex += 8; + } + else if ( rParaString.matchIgnoreAsciiCase( "hasstroke", nIndex ) ) + { + rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::HASSTROKE; + nIndex += 9; + } + else if ( rParaString.matchIgnoreAsciiCase( "hasfill", nIndex ) ) + { + rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::HASFILL; + nIndex += 7; + } + else if ( rParaString.matchIgnoreAsciiCase( "width", nIndex ) ) + { + rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::WIDTH; + nIndex += 5; + } + else if ( rParaString.matchIgnoreAsciiCase( "height", nIndex ) ) + { + rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::HEIGHT; + nIndex += 6; + } + else if ( rParaString.matchIgnoreAsciiCase( "logwidth", nIndex ) ) + { + rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::LOGWIDTH; + nIndex += 8; + } + else if ( rParaString.matchIgnoreAsciiCase( "logheight", nIndex ) ) + { + rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::LOGHEIGHT; + nIndex += 9; + } + else + bValid = false; + } + if ( bValid ) + { + if ( bNumberRequired ) + { + sal_Int32 nStartIndex = nIndex; + sal_Int32 nEIndex = 0; // index of "E" in double + + bool bE = false; // set if a double is including a "E" statement + bool bENum = false; // there is at least one number after "E" + bool bDot = false; // set if there is a dot included + bool bEnd = false; // set for each value that can not be part of a double/integer + + while( ( nIndex < rParaString.getLength() ) && bValid ) + { + switch( rParaString[ nIndex ] ) + { + case '.' : + { + if ( bMustBePositiveWholeNumbered ) + bValid = false; + else + { + if ( bDot ) + bValid = false; + else + bDot = true; + } + } + break; + case '-' : + { + if ( bMustBePositiveWholeNumbered ) + bValid = false; + else + { + if ( nStartIndex == nIndex ) + bValid = true; + else if ( bE ) + { + if ( nEIndex + 1 == nIndex ) + bValid = true; + else if ( bENum ) + bEnd = true; + else + bValid = false; + } + } + } + break; + + case 'e' : + case 'E' : + { + if ( bMustBePositiveWholeNumbered ) + bEnd = true; + else + { + if ( !bE ) + { + bE = true; + nEIndex = nIndex; + } + else + bEnd = true; + } + } + break; + case '0' : + case '1' : + case '2' : + case '3' : + case '4' : + case '5' : + case '6' : + case '7' : + case '8' : + case '9' : + { + if ( bE && ! bENum ) + bENum = true; + } + break; + default: + bEnd = true; + } + if ( !bEnd ) + nIndex++; + else + break; + } + if ( nIndex == nStartIndex ) + bValid = false; + if ( bValid ) + { + std::u16string_view aNumber( rParaString.subView( nStartIndex, nIndex - nStartIndex ) ); + if ( bE || bDot ) + { + double fAttrDouble; + if (::sax::Converter::convertDouble(fAttrDouble, aNumber)) + rParameter.Value <<= fAttrDouble; + else + bValid = false; + } + else + { + sal_Int32 nValue; + if (::sax::Converter::convertNumber(nValue, aNumber)) + rParameter.Value <<= nValue; + else + bValid = false; + } + } + } + } + if ( bValid ) + { + // skipping white spaces and commas (#i121507#) + const sal_Unicode aSpace(' '); + const sal_Unicode aCommata(','); + + while(nIndex < rParaString.getLength()) + { + const sal_Unicode aCandidate(rParaString[nIndex]); + + if(aSpace == aCandidate || aCommata == aCandidate) + { + nIndex++; + } + else + { + break; + } + } + } + return bValid; +} + +static void GetPosition3D( std::vector< css::beans::PropertyValue >& rDest, // e.g. draw:extrusion-viewpoint + std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp, + const SvXMLUnitConverter& rUnitConverter ) +{ + drawing::Position3D aPosition3D; + if ( rUnitConverter.convertPosition3D( aPosition3D, rValue ) ) + { + beans::PropertyValue aProp; + aProp.Name = EASGet( eDestProp ); + aProp.Value <<= aPosition3D; + rDest.push_back( aProp ); + } +} + +static void GetDoubleSequence( std::vector< css::beans::PropertyValue >& rDest, // e.g. draw:glue-point-leaving-directions + std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp ) +{ + std::vector< double > vDirection; + sal_Int32 nIndex = 0; + do + { + double fAttrDouble; + std::string_view aToken( o3tl::getToken(rValue, 0, ',', nIndex ) ); + if (!::sax::Converter::convertDouble( fAttrDouble, aToken )) + break; + else + vDirection.push_back( fAttrDouble ); + } + while ( nIndex >= 0 ); + + if ( !vDirection.empty() ) + { + beans::PropertyValue aProp; + aProp.Name = EASGet( eDestProp ); + aProp.Value <<= comphelper::containerToSequence(vDirection); + rDest.push_back( aProp ); + } +} + +static void GetSizeSequence( std::vector< css::beans::PropertyValue >& rDest, + std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp ) +{ + std::vector< sal_Int32 > vNum; + sal_Int32 nIndex = 0; + do + { + sal_Int32 n; + std::string_view aToken( o3tl::getToken(rValue, 0, ' ', nIndex ) ); + if (!::sax::Converter::convertNumber( n, aToken )) + break; + else + vNum.push_back( n ); + } + while ( nIndex >= 0 ); + + if ( vNum.empty() ) + return; + + uno::Sequence< awt::Size > aSizeSeq((vNum.size() + 1) / 2); + std::vector< sal_Int32 >::const_iterator aIter = vNum.begin(); + std::vector< sal_Int32 >::const_iterator aEnd = vNum.end(); + awt::Size* pValues = aSizeSeq.getArray(); + + while ( aIter != aEnd ) { + pValues->Width = *aIter++; + if ( aIter != aEnd ) + pValues->Height = *aIter++; + pValues ++; + } + + beans::PropertyValue aProp; + aProp.Name = EASGet( eDestProp ); + aProp.Value <<= aSizeSeq; + rDest.push_back( aProp ); +} + +static void GetEnhancedParameter( std::vector< css::beans::PropertyValue >& rDest, // e.g. draw:handle-position + const OUString& rValue, const EnhancedCustomShapeTokenEnum eDestProp ) +{ + sal_Int32 nIndex = 0; + css::drawing::EnhancedCustomShapeParameter aParameter; + if ( GetNextParameter( aParameter, nIndex, rValue ) ) + { + beans::PropertyValue aProp; + aProp.Name = EASGet( eDestProp ); + aProp.Value <<= aParameter; + rDest.push_back( aProp ); + } +} + +static void GetEnhancedParameterPair( std::vector< css::beans::PropertyValue >& rDest, // e.g. draw:handle-position + const OUString& rValue, const EnhancedCustomShapeTokenEnum eDestProp ) +{ + sal_Int32 nIndex = 0; + css::drawing::EnhancedCustomShapeParameterPair aParameterPair; + if ( GetNextParameter( aParameterPair.First, nIndex, rValue ) + && GetNextParameter( aParameterPair.Second, nIndex, rValue ) ) + { + beans::PropertyValue aProp; + aProp.Name = EASGet( eDestProp ); + aProp.Value <<= aParameterPair; + rDest.push_back( aProp ); + } +} + +static sal_Int32 GetEnhancedParameterPairSequence( std::vector< css::beans::PropertyValue >& rDest, // e.g. draw:glue-points + const OUString& rValue, const EnhancedCustomShapeTokenEnum eDestProp ) +{ + std::vector< css::drawing::EnhancedCustomShapeParameterPair > vParameter; + css::drawing::EnhancedCustomShapeParameterPair aParameter; + + sal_Int32 nIndex = 0; + while ( GetNextParameter( aParameter.First, nIndex, rValue ) + && GetNextParameter( aParameter.Second, nIndex, rValue ) ) + { + vParameter.push_back( aParameter ); + } + if ( !vParameter.empty() ) + { + beans::PropertyValue aProp; + aProp.Name = EASGet( eDestProp ); + aProp.Value <<= comphelper::containerToSequence(vParameter); + rDest.push_back( aProp ); + } + return vParameter.size(); +} + +static void GetEnhancedRectangleSequence( std::vector< css::beans::PropertyValue >& rDest, // e.g. draw:text-areas + const OUString& rValue, const EnhancedCustomShapeTokenEnum eDestProp ) +{ + std::vector< css::drawing::EnhancedCustomShapeTextFrame > vTextFrame; + css::drawing::EnhancedCustomShapeTextFrame aParameter; + + sal_Int32 nIndex = 0; + + while ( GetNextParameter( aParameter.TopLeft.First, nIndex, rValue ) + && GetNextParameter( aParameter.TopLeft.Second, nIndex, rValue ) + && GetNextParameter( aParameter.BottomRight.First, nIndex, rValue ) + && GetNextParameter( aParameter.BottomRight.Second, nIndex, rValue ) ) + { + vTextFrame.push_back( aParameter ); + } + if ( !vTextFrame.empty() ) + { + beans::PropertyValue aProp; + aProp.Name = EASGet( eDestProp ); + aProp.Value <<= comphelper::containerToSequence(vTextFrame); + rDest.push_back( aProp ); + } +} + +static void +GetEnhancedPath(std::vector& rDest, // e.g. draw:enhanced-path + const OUString& rValue, std::u16string_view rType) +{ + std::vector< css::drawing::EnhancedCustomShapeParameterPair > vCoordinates; + std::vector< css::drawing::EnhancedCustomShapeSegment > vSegments; + + sal_Int32 nIndex = 0; + sal_Int32 nParameterCount = 0; + + sal_Int32 nParametersNeeded = 1; + sal_Int16 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::MOVETO; + + bool bValid = true; + + while( bValid && ( nIndex < rValue.getLength() ) ) + { + switch( rValue[ nIndex ] ) + { + case 'M' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::MOVETO; + nParametersNeeded = 1; + nIndex++; + } + break; + case 'L' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::LINETO; + nParametersNeeded = 1; + nIndex++; + } + break; + case 'C' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::CURVETO; + nParametersNeeded = 3; + nIndex++; + } + break; + case 'Z' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH; + nParametersNeeded = 0; + nIndex++; + } + break; + case 'N' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH; + nParametersNeeded = 0; + nIndex++; + } + break; + case 'F' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::NOFILL; + nParametersNeeded = 0; + nIndex++; + } + break; + case 'S' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::NOSTROKE; + nParametersNeeded = 0; + nIndex++; + } + break; + case 'T' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO; + nParametersNeeded = 3; + nIndex++; + } + break; + case 'U' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE; + nParametersNeeded = 3; + nIndex++; + } + break; + case 'A' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ARCTO; + nParametersNeeded = 4; + nIndex++; + } + break; + case 'B' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ARC; + nParametersNeeded = 4; + nIndex++; + } + break; + case 'G' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ARCANGLETO; + nParametersNeeded = 2; + nIndex++; + } + break; + case 'H' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::DARKEN; + nParametersNeeded = 0; + nIndex++; + } + break; + case 'I' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::DARKENLESS; + nParametersNeeded = 0; + nIndex++; + } + break; + case 'J' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::LIGHTEN; + nParametersNeeded = 0; + nIndex++; + } + break; + case 'K' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::LIGHTENLESS; + nParametersNeeded = 0; + nIndex++; + } + break; + case 'W' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO; + nParametersNeeded = 4; + nIndex++; + } + break; + case 'V' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC; + nParametersNeeded = 4; + nIndex++; + } + break; + case 'X' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX; + nParametersNeeded = 1; + nIndex++; + } + break; + case 'Y' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY; + nParametersNeeded = 1; + nIndex++; + } + break; + case 'Q' : + { + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::QUADRATICCURVETO; + nParametersNeeded = 2; + nIndex++; + } + break; + case ' ' : + { + nIndex++; + } + break; + + case '$' : + case '?' : + case '0' : + case '1' : + case '2' : + case '3' : + case '4' : + case '5' : + case '6' : + case '7' : + case '8' : + case '9' : + case '.' : + case '-' : + { + css::drawing::EnhancedCustomShapeParameterPair aPair; + if ( GetNextParameter( aPair.First, nIndex, rValue ) && + GetNextParameter( aPair.Second, nIndex, rValue ) ) + { + vCoordinates.push_back( aPair ); + nParameterCount++; + } + else + bValid = false; + } + break; + default: + nIndex++; + break; + } + if ( !nParameterCount && !nParametersNeeded ) + { + css::drawing::EnhancedCustomShapeSegment aSegment; + aSegment.Command = nLatestSegmentCommand; + aSegment.Count = 0; + vSegments.push_back( aSegment ); + nParametersNeeded = 0x7fffffff; + } + else if ( nParameterCount >= nParametersNeeded ) + { + // Special rule for moveto in ODF 1.2 section 19.145 + // "If a moveto is followed by multiple pairs of coordinates, they are treated as lineto." + if ( nLatestSegmentCommand == css::drawing::EnhancedCustomShapeSegmentCommand::MOVETO ) + { + css::drawing::EnhancedCustomShapeSegment aSegment; + aSegment.Command = css::drawing::EnhancedCustomShapeSegmentCommand::MOVETO; + aSegment.Count = 1; + vSegments.push_back( aSegment ); + nIndex--; + nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::LINETO; + nParametersNeeded = 1; + } + else + { + // General rule in ODF 1.2. section 19.145 + // "If a command is repeated multiple times, all repeated command characters + // except the first one may be omitted." Thus check if the last command is identical, + // if so, we just need to increment the count + if ( !vSegments.empty() && ( vSegments[ vSegments.size() - 1 ].Command == nLatestSegmentCommand ) ) + vSegments[ vSegments.size() -1 ].Count++; + else + { + css::drawing::EnhancedCustomShapeSegment aSegment; + aSegment.Command = nLatestSegmentCommand; + aSegment.Count = 1; + vSegments.push_back( aSegment ); + } + } + nParameterCount = 0; + } + } + + // Corrections for wrong paths in curvedArrow shapes written by older LO versions + if (!vSegments.empty() + && (rType == u"mso-spt102" || rType == u"mso-spt103" || rType == u"mso-spt104" + || rType == u"mso-spt105") + && vSegments[0].Count == 2) + { + vSegments[0].Count = 1; + css::drawing::EnhancedCustomShapeSegment aSegment; + aSegment.Count = 1; + aSegment.Command + = vSegments[0].Command == css::drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC + ? css::drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO + : css::drawing::EnhancedCustomShapeSegmentCommand::ARCTO; + vSegments.insert(vSegments.begin() + 1, aSegment); + } + + // adding the Coordinates property + beans::PropertyValue aProp; + aProp.Name = EASGet( EAS_Coordinates ); + aProp.Value <<= comphelper::containerToSequence(vCoordinates); + rDest.push_back( aProp ); + + // adding the Segments property + aProp.Name = EASGet( EAS_Segments ); + aProp.Value <<= comphelper::containerToSequence(vSegments); + rDest.push_back( aProp ); +} + +static void GetAdjustmentValues( std::vector< css::beans::PropertyValue >& rDest, // draw:adjustments + const OUString& rValue ) +{ + std::vector< css::drawing::EnhancedCustomShapeAdjustmentValue > vAdjustmentValue; + css::drawing::EnhancedCustomShapeParameter aParameter; + sal_Int32 nIndex = 0; + while ( GetNextParameter( aParameter, nIndex, rValue ) ) + { + css::drawing::EnhancedCustomShapeAdjustmentValue aAdj; + if ( aParameter.Type == css::drawing::EnhancedCustomShapeParameterType::NORMAL ) + { + aAdj.Value = aParameter.Value; + aAdj.State = beans::PropertyState_DIRECT_VALUE; + } + else + aAdj.State = beans::PropertyState_DEFAULT_VALUE; // this should not be, but better than setting nothing + + vAdjustmentValue.push_back( aAdj ); + } + + sal_Int32 nAdjustmentValues = vAdjustmentValue.size(); + if ( nAdjustmentValues ) + { + beans::PropertyValue aProp; + aProp.Name = EASGet( EAS_AdjustmentValues ); + aProp.Value <<= comphelper::containerToSequence(vAdjustmentValue); + rDest.push_back( aProp ); + } +} + +void XMLEnhancedCustomShapeContext::startFastElement( + sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + sal_Int32 nAttrNumber; + std::optional oSpecularityValue; // for postpone extrusion-specularity + std::optional oPathValue; // for postpone GetEnhancedPath; + OUString sType("non-primitive"); // default in ODF + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch( EASGet( aIter.getToken() ) ) + { + case EAS_type : + { + sType = aIter.toString(); + GetString( mrCustomShapeGeometry, sType, EAS_Type ); + } + break; + case EAS_mirror_horizontal : + GetBool( mrCustomShapeGeometry, aIter.toView(), EAS_MirroredX ); + break; + case EAS_mirror_vertical : + GetBool( mrCustomShapeGeometry, aIter.toView(), EAS_MirroredY ); + break; + case EAS_viewBox : + { + SdXMLImExViewBox aViewBox( aIter.toString(), GetImport().GetMM100UnitConverter() ); + awt::Rectangle aRect( aViewBox.GetX(), aViewBox.GetY(), aViewBox.GetWidth(), aViewBox.GetHeight() ); + beans::PropertyValue aProp; + aProp.Name = EASGet( EAS_ViewBox ); + aProp.Value <<= aRect; + mrCustomShapeGeometry.push_back( aProp ); + } + break; + case EAS_sub_view_size: + GetSizeSequence( maPath, aIter.toView(), EAS_SubViewSize ); + break; + case EAS_text_rotate_angle : + GetDouble( mrCustomShapeGeometry, aIter.toView(), EAS_TextRotateAngle ); + break; + case EAS_extrusion_allowed : + GetBool( maPath, aIter.toView(), EAS_ExtrusionAllowed ); + break; + case EAS_text_path_allowed : + GetBool( maPath, aIter.toView(), EAS_TextPathAllowed ); + break; + case EAS_concentric_gradient_fill_allowed : + GetBool( maPath, aIter.toView(), EAS_ConcentricGradientFillAllowed ); + break; + case EAS_extrusion : + GetBool( maExtrusion, aIter.toView(), EAS_Extrusion ); + break; + case EAS_extrusion_brightness : + GetDoublePercentage( maExtrusion, aIter.toView(), EAS_Brightness ); + break; + case EAS_extrusion_depth : + { + OUString rValue = aIter.toString(); + sal_Int32 nIndex = 0; + css::drawing::EnhancedCustomShapeParameterPair aParameterPair; + css::drawing::EnhancedCustomShapeParameter& rDepth = aParameterPair.First; + if ( GetNextParameter( rDepth, nIndex, rValue ) ) + { + css::drawing::EnhancedCustomShapeParameter& rFraction = aParameterPair.Second; + // try to catch the unit for the depth + sal_Int16 const eSrcUnit( + ::sax::Converter::GetUnitFromString( + rValue, util::MeasureUnit::MM_100TH)); + + OUStringBuffer aUnitStr; + double fFactor = ::sax::Converter::GetConversionFactor( + aUnitStr, util::MeasureUnit::MM_100TH, eSrcUnit); + if ( ( fFactor != 1.0 ) && ( fFactor != 0.0 ) ) + { + double fDepth(0.0); + if ( rDepth.Value >>= fDepth ) + { + fDepth /= fFactor; + rDepth.Value <<= fDepth; + } + } + if ( rValue.matchIgnoreAsciiCase( aUnitStr, nIndex ) ) + nIndex += aUnitStr.getLength(); + + // skipping white spaces + while( ( nIndex < rValue.getLength() ) && rValue[ nIndex ] == ' ' ) + nIndex++; + + if ( GetNextParameter( rFraction, nIndex, rValue ) ) + { + beans::PropertyValue aProp; + aProp.Name = EASGet( EAS_Depth ); + aProp.Value <<= aParameterPair; + maExtrusion.push_back( aProp ); + } + } + } + break; + case EAS_extrusion_diffusion : + GetDoublePercentage( maExtrusion, aIter.toView(), EAS_Diffusion ); + break; + case EAS_extrusion_number_of_line_segments : + GetInt32( maExtrusion, aIter.toView(), EAS_NumberOfLineSegments ); + break; + case EAS_extrusion_light_face : + GetBool( maExtrusion, aIter.toView(), EAS_LightFace ); + break; + case EAS_extrusion_first_light_harsh : + GetBool( maExtrusion, aIter.toView(), EAS_FirstLightHarsh ); + break; + case EAS_extrusion_second_light_harsh : + GetBool( maExtrusion, aIter.toView(), EAS_SecondLightHarsh ); + break; + case EAS_extrusion_first_light_level : + GetDoublePercentage( maExtrusion, aIter.toView(), EAS_FirstLightLevel ); + break; + case EAS_extrusion_second_light_level : + GetDoublePercentage( maExtrusion, aIter.toView(), EAS_SecondLightLevel ); + break; + case EAS_extrusion_first_light_direction : + GetB3DVector( maExtrusion, aIter.toView(), EAS_FirstLightDirection ); + break; + case EAS_extrusion_second_light_direction : + GetB3DVector( maExtrusion, aIter.toView(), EAS_SecondLightDirection ); + break; + case EAS_extrusion_metal : + GetBool( maExtrusion, aIter.toView(), EAS_Metal ); + break; + case EAS_extrusion_metal_type : + { + OUString rValue = aIter.toString(); + sal_Int16 eMetalType(drawing::EnhancedCustomShapeMetalType::MetalODF); + if (rValue == "loext:MetalMSCompatible") + eMetalType = drawing::EnhancedCustomShapeMetalType::MetalMSCompatible; + beans::PropertyValue aProp; + aProp.Name = EASGet(EAS_MetalType); + aProp.Value <<= eMetalType; + maExtrusion.push_back(aProp); + } + break; + case EAS_shade_mode : + { + drawing::ShadeMode eShadeMode( drawing::ShadeMode_FLAT ); + if( IsXMLToken( aIter, XML_PHONG ) ) + eShadeMode = drawing::ShadeMode_PHONG; + else if ( IsXMLToken( aIter, XML_GOURAUD ) ) + eShadeMode = drawing::ShadeMode_SMOOTH; + else if ( IsXMLToken( aIter, XML_DRAFT ) ) + eShadeMode = drawing::ShadeMode_DRAFT; + + beans::PropertyValue aProp; + aProp.Name = EASGet( EAS_ShadeMode ); + aProp.Value <<= eShadeMode; + maExtrusion.push_back( aProp ); + } + break; + case EAS_extrusion_rotation_angle : + GetEnhancedParameterPair( maExtrusion, aIter.toString(), EAS_RotateAngle ); + break; + case EAS_extrusion_rotation_center : + GetB3DVector( maExtrusion, aIter.toView(), EAS_RotationCenter ); + break; + case EAS_extrusion_shininess : + GetDoublePercentage( maExtrusion, aIter.toView(), EAS_Shininess ); + break; + case EAS_extrusion_skew : + GetEnhancedParameterPair( maExtrusion, aIter.toString(), EAS_Skew ); + break; + case EAS_extrusion_specularity : + if (!oSpecularityValue) + oSpecularityValue = aIter.toView(); + break; + case EAS_extrusion_specularity_loext : + oSpecularityValue = aIter.toView(); + break; + case EAS_projection : + { + drawing::ProjectionMode eProjectionMode( drawing::ProjectionMode_PERSPECTIVE ); + if( IsXMLToken( aIter, XML_PARALLEL ) ) + eProjectionMode = drawing::ProjectionMode_PARALLEL; + + beans::PropertyValue aProp; + aProp.Name = EASGet( EAS_ProjectionMode ); + aProp.Value <<= eProjectionMode; + maExtrusion.push_back( aProp ); + } + break; + case EAS_extrusion_viewpoint : + GetPosition3D( maExtrusion, aIter.toView(), EAS_ViewPoint, mrUnitConverter ); + break; + case EAS_extrusion_origin : + GetEnhancedParameterPair( maExtrusion, aIter.toString(), EAS_Origin ); + break; + case EAS_extrusion_color : + GetBool( maExtrusion, aIter.toView(), EAS_Color ); + break; + case EAS_enhanced_path : + oPathValue = aIter.toString(); + break; + case EAS_path_stretchpoint_x : + { + if (::sax::Converter::convertNumber(nAttrNumber, aIter.toView())) + { + beans::PropertyValue aProp; + aProp.Name = EASGet( EAS_StretchX ); + aProp.Value <<= nAttrNumber; + maPath.push_back( aProp ); + } + } + break; + case EAS_path_stretchpoint_y : + { + if (::sax::Converter::convertNumber(nAttrNumber, aIter.toView())) + { + beans::PropertyValue aProp; + aProp.Name = EASGet( EAS_StretchY ); + aProp.Value <<= nAttrNumber; + maPath.push_back( aProp ); + } + } + break; + case EAS_text_areas : + GetEnhancedRectangleSequence( maPath, aIter.toString(), EAS_TextFrames ); + break; + case EAS_glue_points : + { + sal_Int32 i, nPairs = GetEnhancedParameterPairSequence( maPath, aIter.toString(), EAS_GluePoints ); + GetImport().GetShapeImport()->moveGluePointMapping( mrxShape, nPairs ); + for ( i = 0; i < nPairs; i++ ) + GetImport().GetShapeImport()->addGluePointMapping( mrxShape, i + 4, i + 4 ); + } + break; + case EAS_glue_point_type : + GetEnum( maPath, aIter.toView(), EAS_GluePointType, *aXML_GluePointEnumMap ); + break; + case EAS_glue_point_leaving_directions : + GetDoubleSequence( maPath, aIter.toView(), EAS_GluePointLeavingDirections ); + break; + case EAS_text_path : + GetBool( maTextPath, aIter.toView(), EAS_TextPath ); + break; + case EAS_text_path_mode : + { + css::drawing::EnhancedCustomShapeTextPathMode eTextPathMode( css::drawing::EnhancedCustomShapeTextPathMode_NORMAL ); + if( IsXMLToken( aIter, XML_PATH ) ) + eTextPathMode = css::drawing::EnhancedCustomShapeTextPathMode_PATH; + else if ( IsXMLToken( aIter, XML_SHAPE ) ) + eTextPathMode = css::drawing::EnhancedCustomShapeTextPathMode_SHAPE; + + beans::PropertyValue aProp; + aProp.Name = EASGet( EAS_TextPathMode ); + aProp.Value <<= eTextPathMode; + maTextPath.push_back( aProp ); + } + break; + case EAS_text_path_scale : + { + bool bScaleX = IsXMLToken( aIter, XML_SHAPE ); + beans::PropertyValue aProp; + aProp.Name = EASGet( EAS_ScaleX ); + aProp.Value <<= bScaleX; + maTextPath.push_back( aProp ); + } + break; + case EAS_text_path_same_letter_heights : + GetBool( maTextPath, aIter.toView(), EAS_SameLetterHeights ); + break; + case EAS_modifiers : + GetAdjustmentValues( mrCustomShapeGeometry, aIter.toString() ); + break; + default: + break; + } + } + if (oSpecularityValue) + GetDoublePercentage( maExtrusion, *oSpecularityValue, EAS_Specularity ); + if (oPathValue) + GetEnhancedPath(maPath, *oPathValue, sType); +} + +static void SdXMLCustomShapePropertyMerge( std::vector< css::beans::PropertyValue >& rPropVec, + const std::vector< beans::PropertyValues >& rElement, + const OUString& rElementName ) +{ + if ( !rElement.empty() ) + { + beans::PropertyValue aProp; + aProp.Name = rElementName; + aProp.Value <<= comphelper::containerToSequence(rElement); + rPropVec.push_back( aProp ); + } +} + +static void SdXMLCustomShapePropertyMerge( std::vector< css::beans::PropertyValue >& rPropVec, + const std::vector< OUString >& rElement, + const OUString& rElementName ) +{ + if ( !rElement.empty() ) + { + beans::PropertyValue aProp; + aProp.Name = rElementName; + aProp.Value <<= comphelper::containerToSequence(rElement); + rPropVec.push_back( aProp ); + } +} + +static void SdXMLCustomShapePropertyMerge( std::vector< css::beans::PropertyValue >& rPropVec, + const std::vector< css::beans::PropertyValue >& rElement, + const OUString& rElementName ) +{ + if ( !rElement.empty() ) + { + beans::PropertyValue aProp; + aProp.Name = rElementName; + aProp.Value <<= comphelper::containerToSequence(rElement); + rPropVec.push_back( aProp ); + } +} + +typedef std::unordered_map< OUString, sal_Int32 > EquationHashMap; + +/* if rPara.Type is from type EnhancedCustomShapeParameterType::EQUATION, the name of the equation + will be converted from OUString to index */ +static void CheckAndResolveEquationParameter( css::drawing::EnhancedCustomShapeParameter& rPara, EquationHashMap* pH ) +{ + if ( rPara.Type == css::drawing::EnhancedCustomShapeParameterType::EQUATION ) + { + OUString aEquationName; + if ( rPara.Value >>= aEquationName ) + { + sal_Int32 nIndex = 0; + EquationHashMap::iterator aHashIter( pH->find( aEquationName ) ); + if ( aHashIter != pH->end() ) + nIndex = (*aHashIter).second; + rPara.Value <<= nIndex; + } + } +} + +void XMLEnhancedCustomShapeContext::endFastElement(sal_Int32 ) +{ + // resolve properties that are indexing an Equation + if ( !maEquations.empty() ) + { + // creating hash map containing the name and index of each equation + EquationHashMap aH; + std::vector< OUString >::iterator aEquationNameIter = maEquationNames.begin(); + std::vector< OUString >::iterator aEquationNameEnd = maEquationNames.end(); + while( aEquationNameIter != aEquationNameEnd ) + { + aH[ *aEquationNameIter ] = static_cast( aEquationNameIter - maEquationNames.begin() ); + ++aEquationNameIter; + } + + // resolve equation + for( auto& rEquation : maEquations ) + { + sal_Int32 nIndexOf = 0; + do + { + nIndexOf = rEquation.indexOf( '?', nIndexOf ); + if ( nIndexOf != -1 ) + { + OUString aEquationName; + if ( GetEquationName( rEquation, nIndexOf + 1, aEquationName ) ) + { + // copying first characters inclusive '?' + sal_Int32 nIndex = 0; + EquationHashMap::iterator aHashIter( aH.find( aEquationName ) ); + if ( aHashIter != aH.end() ) + nIndex = (*aHashIter).second; + OUString aNew = rEquation.subView( 0, nIndexOf + 1 ) + + OUString::number( nIndex ) + + rEquation.subView( nIndexOf + aEquationName.getLength() + 1 ); + rEquation = aNew; + } + nIndexOf++; + } + } + while( nIndexOf != -1 ); + } + + // Path + for ( const beans::PropertyValue& rPathItem : maPath ) + { + switch( EASGet( rPathItem.Name ) ) + { + case EAS_Coordinates : + case EAS_GluePoints : + { + uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > const & rSeq = + *o3tl::doAccess >( + rPathItem.Value); + for ( const auto& rElem : rSeq ) + { + CheckAndResolveEquationParameter( const_cast(rElem.First), &aH ); + CheckAndResolveEquationParameter( const_cast(rElem.Second), &aH ); + } + } + break; + case EAS_TextFrames : + { + uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > const & rSeq = + *o3tl::doAccess >( + rPathItem.Value); + for ( const auto& rElem : rSeq ) + { + CheckAndResolveEquationParameter( const_cast(rElem.TopLeft.First), &aH ); + CheckAndResolveEquationParameter( const_cast(rElem.TopLeft.Second), &aH ); + CheckAndResolveEquationParameter( const_cast(rElem.BottomRight.First), &aH ); + CheckAndResolveEquationParameter( const_cast(rElem.BottomRight.Second), &aH ); + } + } + break; + default: + break; + } + } + for ( css::beans::PropertyValues const & aHandle : maHandles ) + { + for ( beans::PropertyValue const & propValue : aHandle ) + { + switch( EASGet( propValue.Name ) ) + { + case EAS_RangeYMinimum : + case EAS_RangeYMaximum : + case EAS_RangeXMinimum : + case EAS_RangeXMaximum : + case EAS_RadiusRangeMinimum : + case EAS_RadiusRangeMaximum : + { + CheckAndResolveEquationParameter( const_cast(*o3tl::doAccess( + propValue.Value)), &aH ); + } + break; + + case EAS_Position : + case EAS_Polar : + { + CheckAndResolveEquationParameter( const_cast((*o3tl::doAccess( + propValue.Value)).First), &aH ); + CheckAndResolveEquationParameter( const_cast((*o3tl::doAccess( + propValue.Value)).Second), &aH ); + } + break; + default: + break; + } + } + } + } + + SdXMLCustomShapePropertyMerge( mrCustomShapeGeometry, maExtrusion, EASGet( EAS_Extrusion ) ); + SdXMLCustomShapePropertyMerge( mrCustomShapeGeometry, maPath, EASGet( EAS_Path ) ); + SdXMLCustomShapePropertyMerge( mrCustomShapeGeometry, maTextPath, EASGet( EAS_TextPath ) ); + SdXMLCustomShapePropertyMerge( mrCustomShapeGeometry, maEquations, EASGet( EAS_Equations ) ); + if ( !maHandles.empty() ) + SdXMLCustomShapePropertyMerge( mrCustomShapeGeometry, maHandles, EASGet( EAS_Handles ) ); +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > XMLEnhancedCustomShapeContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + EnhancedCustomShapeTokenEnum aTokenEnum = EASGet( nElement ); + if ( aTokenEnum == EAS_equation ) + { + OUString aFormula; + OUString aFormulaName; + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + OUString sValue = aIter.toString(); + switch( EASGet( aIter.getToken() ) ) + { + case EAS_formula : + aFormula = sValue; + break; + case EAS_name : + aFormulaName = sValue; + break; + default: + break; + } + } + if ( !aFormulaName.isEmpty() || !aFormula.isEmpty() ) + { + maEquations.push_back( aFormula ); + maEquationNames.push_back( aFormulaName ); + } + } + else if ( aTokenEnum == EAS_handle ) + { + std::vector< css::beans::PropertyValue > aHandle; + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch( EASGet( aIter.getToken() ) ) + { + case EAS_handle_mirror_vertical : + GetBool( aHandle, aIter.toView(), EAS_MirroredY ); + break; + case EAS_handle_mirror_horizontal : + GetBool( aHandle, aIter.toView(), EAS_MirroredX ); + break; + case EAS_handle_switched : + GetBool( aHandle, aIter.toView(), EAS_Switched ); + break; + case EAS_handle_position : + GetEnhancedParameterPair( aHandle, aIter.toString(), EAS_Position ); + break; + case EAS_handle_range_x_minimum : + GetEnhancedParameter( aHandle, aIter.toString(), EAS_RangeXMinimum ); + break; + case EAS_handle_range_x_maximum : + GetEnhancedParameter( aHandle, aIter.toString(), EAS_RangeXMaximum ); + break; + case EAS_handle_range_y_minimum : + GetEnhancedParameter( aHandle, aIter.toString(), EAS_RangeYMinimum ); + break; + case EAS_handle_range_y_maximum : + GetEnhancedParameter( aHandle, aIter.toString(), EAS_RangeYMaximum ); + break; + case EAS_handle_polar : + GetEnhancedParameterPair( aHandle, aIter.toString(), EAS_Polar ); + break; + case EAS_handle_radius_range_minimum : + GetEnhancedParameter( aHandle, aIter.toString(), EAS_RadiusRangeMinimum ); + break; + case EAS_handle_radius_range_maximum : + GetEnhancedParameter( aHandle, aIter.toString(), EAS_RadiusRangeMaximum ); + break; + default: + break; + } + } + maHandles.push_back( comphelper::containerToSequence(aHandle) ); + } + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3