/* -*- 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 <sal/config.h>
 
#include <string_view>
 
#include <utility>
#include <xexptran.hxx>
#include <rtl/ustrbuf.hxx>
#include <osl/diagnose.h>
#include <sax/tools/converter.hxx>
#include <xmloff/xmluconv.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/tuple/b2dtuple.hxx>
#include <basegfx/tuple/b3dtuple.hxx>
#include <basegfx/matrix/b3dhommatrix.hxx>
#include <basegfx/numeric/ftools.hxx>
#include <basegfx/matrix/b3dhommatrixtools.hxx>
 
using namespace ::com::sun::star;
 
// parsing help functions for simple chars
static void Imp_SkipSpaces(std::u16string_view rStr, sal_Int32& rPos, const sal_Int32 nLen)
{
    while(rPos < nLen
        && ' ' == rStr[rPos])
        rPos++;
}
 
static void Imp_SkipSpacesAndOpeningBraces(std::u16string_view rStr, sal_Int32& rPos, const sal_Int32 nLen)
{
    while(rPos < nLen
        && (' ' == rStr[rPos] || '(' == rStr[rPos]))
        rPos++;
}
 
static void Imp_SkipSpacesAndCommas(std::u16string_view rStr, sal_Int32& rPos, const sal_Int32 nLen)
{
    while(rPos < nLen
        && (' ' == rStr[rPos] || ',' == rStr[rPos]))
        rPos++;
}
 
static void Imp_SkipSpacesAndClosingBraces(std::u16string_view rStr, sal_Int32& rPos, const sal_Int32 nLen)
{
    while(rPos < nLen
        && (' ' == rStr[rPos] || ')' == rStr[rPos]))
        rPos++;
}
 
// parsing help functions for integer numbers
 
static bool Imp_IsOnUnitChar(std::u16string_view rStr, const sal_Int32 nPos)
{
    sal_Unicode aChar(rStr[nPos]);
 
    return ('a' <= aChar && 'z' >= aChar)
        || ('A' <= aChar && 'Z' >= aChar)
        || '%' == aChar;
}
 
static double Imp_GetDoubleChar(std::u16string_view rStr, sal_Int32& rPos, const sal_Int32 nLen,
    const SvXMLUnitConverter& rConv, double fRetval, bool bLookForUnits = false)
{
    sal_Unicode aChar(rStr[rPos]);
    OUStringBuffer sNumberString(32);
 
    if('+' == aChar || '-' == aChar)
    {
        sNumberString.append(rStr[rPos]);
        ++rPos;
        aChar = rPos >= nLen ? 0 : rStr[rPos];
    }
 
    while(('0' <= aChar && '9' >= aChar)
        || '.' == aChar)
    {
        sNumberString.append(rStr[rPos]);
        ++rPos;
        aChar = rPos >= nLen ? 0 : rStr[rPos];
    }
 
    if('e' == aChar || 'E' == aChar)
    {
        sNumberString.append(rStr[rPos]);
        ++rPos;
        aChar = rPos >= nLen ? 0 : rStr[rPos];
 
        if('+' == aChar || '-' == aChar)
        {
            sNumberString.append(rStr[rPos]);
            ++rPos;
            aChar = rPos >= nLen ? 0 : rStr[rPos];
        }
 
        while('0' <= aChar && '9' >= aChar)
        {
            sNumberString.append(rStr[rPos]);
            ++rPos;
            aChar = rPos >= nLen ? 0 : rStr[rPos];
        }
    }
 
    if(bLookForUnits)
    {
        Imp_SkipSpaces(rStr, rPos, nLen);
        while(rPos < nLen && Imp_IsOnUnitChar(rStr, rPos))
            sNumberString.append(rStr[rPos++]);
    }
 
    if(!sNumberString.isEmpty())
    {
        if(bLookForUnits)
            rConv.convertDouble(fRetval, sNumberString);
        else
        {
            ::sax::Converter::convertDouble(fRetval, sNumberString);
        }
    }
 
    return fRetval;
}
 
static void Imp_PutDoubleChar(OUString& rStr, double fValue)
{
    OUStringBuffer sStringBuffer;
    ::sax::Converter::convertDouble(sStringBuffer, fValue);
    rStr += sStringBuffer;
}
 
static void Imp_PutDoubleChar(OUStringBuffer& rStr, const SvXMLUnitConverter& rConv, double fValue,
    bool bConvertUnits = false)
{
    OUStringBuffer sStringBuffer;
 
    if(bConvertUnits)
        rConv.convertDouble(sStringBuffer, fValue);
    else
    {
        ::sax::Converter::convertDouble(sStringBuffer, fValue);
    }
 
    rStr.append(sStringBuffer);
}
 
// base class of all 2D transform objects
 
struct ImpSdXMLExpTransObj2DBase
{
    sal_uInt16                  mnType;
    explicit ImpSdXMLExpTransObj2DBase(sal_uInt16 nType)
    :   mnType(nType) {}
};
 
// possible object types for 2D
 
#define IMP_SDXMLEXP_TRANSOBJ2D_ROTATE          0x0000
#define IMP_SDXMLEXP_TRANSOBJ2D_SCALE           0x0001
#define IMP_SDXMLEXP_TRANSOBJ2D_TRANSLATE       0x0002
#define IMP_SDXMLEXP_TRANSOBJ2D_SKEWX           0x0003
#define IMP_SDXMLEXP_TRANSOBJ2D_SKEWY           0x0004
#define IMP_SDXMLEXP_TRANSOBJ2D_MATRIX          0x0005
 
// classes of objects, different sizes
 
namespace {
 
struct ImpSdXMLExpTransObj2DRotate : public ImpSdXMLExpTransObj2DBase
{
    double                      mfRotate;
    explicit ImpSdXMLExpTransObj2DRotate(double fVal)
    :   ImpSdXMLExpTransObj2DBase(IMP_SDXMLEXP_TRANSOBJ2D_ROTATE), mfRotate(fVal) {}
};
struct ImpSdXMLExpTransObj2DScale : public ImpSdXMLExpTransObj2DBase
{
    ::basegfx::B2DTuple         maScale;
    explicit ImpSdXMLExpTransObj2DScale(const ::basegfx::B2DTuple& rNew)
    :   ImpSdXMLExpTransObj2DBase(IMP_SDXMLEXP_TRANSOBJ2D_SCALE), maScale(rNew) {}
};
struct ImpSdXMLExpTransObj2DTranslate : public ImpSdXMLExpTransObj2DBase
{
    ::basegfx::B2DTuple         maTranslate;
    explicit ImpSdXMLExpTransObj2DTranslate(const ::basegfx::B2DTuple& rNew)
    :   ImpSdXMLExpTransObj2DBase(IMP_SDXMLEXP_TRANSOBJ2D_TRANSLATE), maTranslate(rNew) {}
};
struct ImpSdXMLExpTransObj2DSkewX : public ImpSdXMLExpTransObj2DBase
{
    double                      mfSkewX;
    explicit ImpSdXMLExpTransObj2DSkewX(double fVal)
    :   ImpSdXMLExpTransObj2DBase(IMP_SDXMLEXP_TRANSOBJ2D_SKEWX), mfSkewX(fVal) {}
};
struct ImpSdXMLExpTransObj2DSkewY : public ImpSdXMLExpTransObj2DBase
{
    double                      mfSkewY;
    explicit ImpSdXMLExpTransObj2DSkewY(double fVal)
    :   ImpSdXMLExpTransObj2DBase(IMP_SDXMLEXP_TRANSOBJ2D_SKEWY), mfSkewY(fVal) {}
};
struct ImpSdXMLExpTransObj2DMatrix : public ImpSdXMLExpTransObj2DBase
{
    ::basegfx::B2DHomMatrix     maMatrix;
    explicit ImpSdXMLExpTransObj2DMatrix(::basegfx::B2DHomMatrix aNew)
    :   ImpSdXMLExpTransObj2DBase(IMP_SDXMLEXP_TRANSOBJ2D_MATRIX), maMatrix(std::move(aNew)) {}
};
 
}
 
// add members
 
void SdXMLImExTransform2D::AddRotate(double fNew)
{
    if(fNew != 0.0)
        maList.push_back(std::make_shared<ImpSdXMLExpTransObj2DRotate>(fNew));
}
 
void SdXMLImExTransform2D::AddTranslate(const ::basegfx::B2DTuple& rNew)
{
    if(!rNew.equalZero())
        maList.push_back(std::make_shared<ImpSdXMLExpTransObj2DTranslate>(rNew));
}
 
void SdXMLImExTransform2D::AddSkewX(double fNew)
{
    if(fNew != 0.0)
        maList.push_back(std::make_shared<ImpSdXMLExpTransObj2DSkewX>(fNew));
}
 
// gen string for export
const OUString& SdXMLImExTransform2D::GetExportString(const SvXMLUnitConverter& rConv)
{
    OUStringBuffer aNewString;
    OUString aClosingBrace(u")"_ustr);
    OUString aEmptySpace(u" "_ustr);
 
    const sal_uInt32 nCount = maList.size();
    for(sal_uInt32 a(0); a < nCount; a++)
    {
        ImpSdXMLExpTransObj2DBase* pObj = maList[a].get();
        switch(pObj->mnType)
        {
            case IMP_SDXMLEXP_TRANSOBJ2D_ROTATE :
            {
                aNewString.append("rotate (");
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj2DRotate*>(pObj)->mfRotate);
                aNewString.append(aClosingBrace);
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ2D_SCALE      :
            {
                aNewString.append("scale (");
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj2DScale*>(pObj)->maScale.getX());
                aNewString.append(aEmptySpace);
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj2DScale*>(pObj)->maScale.getY());
                aNewString.append(aClosingBrace);
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ2D_TRANSLATE  :
            {
                aNewString.append("translate (");
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj2DTranslate*>(pObj)->maTranslate.getX(), true);
                aNewString.append(aEmptySpace);
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj2DTranslate*>(pObj)->maTranslate.getY(), true);
                aNewString.append(aClosingBrace);
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ2D_SKEWX      :
            {
                aNewString.append("skewX (");
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj2DSkewX*>(pObj)->mfSkewX);
                aNewString.append(aClosingBrace);
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ2D_SKEWY      :
            {
                aNewString.append("skewY (");
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj2DSkewY*>(pObj)->mfSkewY);
                aNewString.append(aClosingBrace);
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ2D_MATRIX :
            {
                aNewString.append("matrix (");
 
                // a
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj2DMatrix*>(pObj)->maMatrix.get(0, 0));
                aNewString.append(aEmptySpace);
 
                // b
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj2DMatrix*>(pObj)->maMatrix.get(1, 0));
                aNewString.append(aEmptySpace);
 
                // c
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj2DMatrix*>(pObj)->maMatrix.get(0, 1));
                aNewString.append(aEmptySpace);
 
                // d
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj2DMatrix*>(pObj)->maMatrix.get(1, 1));
                aNewString.append(aEmptySpace);
 
                // e
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj2DMatrix*>(pObj)->maMatrix.get(0, 2), true);
                aNewString.append(aEmptySpace);
 
                // f
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj2DMatrix*>(pObj)->maMatrix.get(1, 2), true);
 
                aNewString.append(aClosingBrace);
                break;
            }
            default :
            {
                OSL_FAIL("SdXMLImExTransform2D: impossible entry!");
                break;
            }
        }
 
        // if not the last entry, add one space to next tag
        if(a + 1 != maList.size())
        {
            aNewString.append(aEmptySpace);
        }
    }
 
    // fill string form OUString
    msString = aNewString.makeStringAndClear();
 
    return msString;
}
 
// sets new string, parses it and generates entries
void SdXMLImExTransform2D::SetString(const OUString& rNew, const SvXMLUnitConverter& rConv)
{
    msString = rNew;
    maList.clear();
 
    if(msString.isEmpty())
        return;
 
    const OUString aStr = msString;
    const sal_Int32 nLen(aStr.getLength());
 
    static constexpr OUStringLiteral aString_rotate( u"rotate" );
    static constexpr OUStringLiteral aString_scale( u"scale" );
    static constexpr OUStringLiteral aString_translate( u"translate" );
    static constexpr OUStringLiteral aString_skewX( u"skewX" );
    static constexpr OUStringLiteral aString_skewY( u"skewY" );
    static constexpr OUStringLiteral aString_matrix( u"matrix" );
 
    sal_Int32 nPos(0);
 
    while(nPos < nLen)
    {
        // skip spaces
        Imp_SkipSpaces(aStr, nPos, nLen);
 
        // look for tag
        if(nPos < nLen)
        {
            if(nPos == aStr.indexOf(aString_rotate, nPos))
            {
                double fValue(0.0);
                nPos += 6;
                Imp_SkipSpacesAndOpeningBraces(aStr, nPos, nLen);
                fValue = Imp_GetDoubleChar(aStr, nPos, nLen, rConv, fValue);
                if(fValue != 0.0)
                    maList.push_back(std::make_shared<ImpSdXMLExpTransObj2DRotate>(fValue));
 
                Imp_SkipSpacesAndClosingBraces(aStr, nPos, nLen);
            }
            else if(nPos == aStr.indexOf(aString_scale, nPos))
            {
                ::basegfx::B2DTuple aValue(1.0, 1.0);
                nPos += 5;
                Imp_SkipSpacesAndOpeningBraces(aStr, nPos, nLen);
                aValue.setX(Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.getX()));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
                aValue.setY(Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.getY()));
 
                if(aValue.getX() != 1.0 || aValue.getY() != 1.0)
                    maList.push_back(std::make_shared<ImpSdXMLExpTransObj2DScale>(aValue));
 
                Imp_SkipSpacesAndClosingBraces(aStr, nPos, nLen);
            }
            else if(nPos == aStr.indexOf(aString_translate, nPos))
            {
                ::basegfx::B2DTuple aValue;
                nPos += 9;
                Imp_SkipSpacesAndOpeningBraces(aStr, nPos, nLen);
                aValue.setX(Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.getX(), true));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
                aValue.setY(Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.getY(), true));
 
                if(!aValue.equalZero())
                    maList.push_back(std::make_shared<ImpSdXMLExpTransObj2DTranslate>(aValue));
 
                Imp_SkipSpacesAndClosingBraces(aStr, nPos, nLen);
            }
            else if(nPos == aStr.indexOf(aString_skewX, nPos))
            {
                double fValue(0.0);
                nPos += 5;
                Imp_SkipSpacesAndOpeningBraces(aStr, nPos, nLen);
                fValue = Imp_GetDoubleChar(aStr, nPos, nLen, rConv, fValue);
                if(fValue != 0.0)
                    maList.push_back(std::make_shared<ImpSdXMLExpTransObj2DSkewX>(fValue));
 
                Imp_SkipSpacesAndClosingBraces(aStr, nPos, nLen);
            }
            else if(nPos == aStr.indexOf(aString_skewY, nPos))
            {
                double fValue(0.0);
                nPos += 5;
                Imp_SkipSpacesAndOpeningBraces(aStr, nPos, nLen);
                fValue = Imp_GetDoubleChar(aStr, nPos, nLen, rConv, fValue);
                if(fValue != 0.0)
                    maList.push_back(std::make_shared<ImpSdXMLExpTransObj2DSkewY>(fValue));
 
                Imp_SkipSpacesAndClosingBraces(aStr, nPos, nLen);
            }
            else if(nPos == aStr.indexOf(aString_matrix, nPos))
            {
                ::basegfx::B2DHomMatrix aValue;
 
                nPos += 6;
                Imp_SkipSpacesAndOpeningBraces(aStr, nPos, nLen);
 
                // a
                aValue.set(0, 0, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(0, 0)));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // b
                aValue.set(1, 0, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(1, 0)));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // c
                aValue.set(0, 1, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(0, 1)));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // d
                aValue.set(1, 1, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(1, 1)));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // e
                aValue.set(0, 2, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(0, 2), true));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // f
                aValue.set(1, 2, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(1, 2), true));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                if(!aValue.isIdentity())
                    maList.push_back(std::make_shared<ImpSdXMLExpTransObj2DMatrix>(aValue));
 
                Imp_SkipSpacesAndClosingBraces(aStr, nPos, nLen);
            }
            else
            {
                nPos++;
            }
        }
    }
}
 
void SdXMLImExTransform2D::GetFullTransform(::basegfx::B2DHomMatrix& rFullTrans)
{
    rFullTrans.identity();
 
    const sal_uInt32 nCount = maList.size();
    for(sal_uInt32 a(0); a < nCount; a++)
    {
        ImpSdXMLExpTransObj2DBase* pObj = maList[a].get();
        switch(pObj->mnType)
        {
            case IMP_SDXMLEXP_TRANSOBJ2D_ROTATE     :
            {
                // #i78696#
                // mfRotate is mathematically wrong oriented since we export/import the angle
                // values mirrored. This error is fixed in the API, but not yet in the FileFormat.
                // For the FileFormat there is a follow-up task (#i78698#) to fix this in the next
                // ODF FileFormat version. For now - to emulate the old behaviour - it is necessary
                // to mirror the value here
                rFullTrans.rotate(static_cast<ImpSdXMLExpTransObj2DRotate*>(pObj)->mfRotate * -1.0);
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ2D_SCALE      :
            {
                const ::basegfx::B2DTuple& rScale = static_cast<ImpSdXMLExpTransObj2DScale*>(pObj)->maScale;
                rFullTrans.scale(rScale.getX(), rScale.getY());
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ2D_TRANSLATE  :
            {
                const ::basegfx::B2DTuple& rTranslate = static_cast<ImpSdXMLExpTransObj2DTranslate*>(pObj)->maTranslate;
                rFullTrans.translate(rTranslate.getX(), rTranslate.getY());
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ2D_SKEWX      :
            {
                // For to get a mathematical correct matrix from already existing documents,
                // mirror the value here. ODF spec is unclear about direction.
                rFullTrans.shearX(-tan(static_cast<ImpSdXMLExpTransObj2DSkewX*>(pObj)->mfSkewX));
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ2D_SKEWY      :
            {
                // LibreOffice does not write skewY, OOo neither. Such files are foreign documents
                // or manually set transformations. OOo had used the value as -tan(value) before
                // errors were introduced, Scribus 1.5.4 uses it as -tan(value) too, MS Office does
                // not shear at all. ODF spec is unclear about direction.
                rFullTrans.shearY(-tan(static_cast<ImpSdXMLExpTransObj2DSkewY*>(pObj)->mfSkewY));
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ2D_MATRIX     :
            {
                rFullTrans *= static_cast<ImpSdXMLExpTransObj2DMatrix*>(pObj)->maMatrix;
                break;
            }
            default :
            {
                OSL_FAIL("SdXMLImExTransform2D: impossible entry!");
                break;
            }
        }
    }
}
 
// base class of all 3D transform objects
 
struct ImpSdXMLExpTransObj3DBase
{
    sal_uInt16                  mnType;
    explicit ImpSdXMLExpTransObj3DBase(sal_uInt16 nType)
    :   mnType(nType) {}
};
 
// possible object types for 3D
 
#define IMP_SDXMLEXP_TRANSOBJ3D_ROTATE_X        0x0000
#define IMP_SDXMLEXP_TRANSOBJ3D_ROTATE_Y        0x0001
#define IMP_SDXMLEXP_TRANSOBJ3D_ROTATE_Z        0x0002
#define IMP_SDXMLEXP_TRANSOBJ3D_SCALE           0x0003
#define IMP_SDXMLEXP_TRANSOBJ3D_TRANSLATE       0x0004
#define IMP_SDXMLEXP_TRANSOBJ3D_MATRIX          0x0005
 
// classes of objects, different sizes
 
namespace {
 
struct ImpSdXMLExpTransObj3DRotateX : public ImpSdXMLExpTransObj3DBase
{
    double                      mfRotateX;
    explicit ImpSdXMLExpTransObj3DRotateX(double fVal)
    :   ImpSdXMLExpTransObj3DBase(IMP_SDXMLEXP_TRANSOBJ3D_ROTATE_X), mfRotateX(fVal) {}
};
struct ImpSdXMLExpTransObj3DRotateY : public ImpSdXMLExpTransObj3DBase
{
    double                      mfRotateY;
    explicit ImpSdXMLExpTransObj3DRotateY(double fVal)
    :   ImpSdXMLExpTransObj3DBase(IMP_SDXMLEXP_TRANSOBJ3D_ROTATE_Y), mfRotateY(fVal) {}
};
struct ImpSdXMLExpTransObj3DRotateZ : public ImpSdXMLExpTransObj3DBase
{
    double                      mfRotateZ;
    explicit ImpSdXMLExpTransObj3DRotateZ(double fVal)
    :   ImpSdXMLExpTransObj3DBase(IMP_SDXMLEXP_TRANSOBJ3D_ROTATE_Z), mfRotateZ(fVal) {}
};
struct ImpSdXMLExpTransObj3DScale : public ImpSdXMLExpTransObj3DBase
{
    ::basegfx::B3DTuple         maScale;
    explicit ImpSdXMLExpTransObj3DScale(const ::basegfx::B3DTuple& rNew)
    :   ImpSdXMLExpTransObj3DBase(IMP_SDXMLEXP_TRANSOBJ3D_SCALE), maScale(rNew) {}
};
struct ImpSdXMLExpTransObj3DTranslate : public ImpSdXMLExpTransObj3DBase
{
    ::basegfx::B3DTuple         maTranslate;
    explicit ImpSdXMLExpTransObj3DTranslate(const ::basegfx::B3DTuple& rNew)
    :   ImpSdXMLExpTransObj3DBase(IMP_SDXMLEXP_TRANSOBJ3D_TRANSLATE), maTranslate(rNew) {}
};
struct ImpSdXMLExpTransObj3DMatrix : public ImpSdXMLExpTransObj3DBase
{
    ::basegfx::B3DHomMatrix     maMatrix;
    explicit ImpSdXMLExpTransObj3DMatrix(::basegfx::B3DHomMatrix aNew)
    :   ImpSdXMLExpTransObj3DBase(IMP_SDXMLEXP_TRANSOBJ3D_MATRIX), maMatrix(std::move(aNew)) {}
};
 
}
 
// add members
 
void SdXMLImExTransform3D::AddMatrix(const ::basegfx::B3DHomMatrix& rNew)
{
    if(!rNew.isIdentity())
        maList.push_back(std::make_shared<ImpSdXMLExpTransObj3DMatrix>(rNew));
}
 
void SdXMLImExTransform3D::AddHomogenMatrix(const drawing::HomogenMatrix& xHomMat)
{
    AddMatrix(basegfx::utils::UnoHomogenMatrixToB3DHomMatrix(xHomMat));
}
 
// gen string for export
const OUString& SdXMLImExTransform3D::GetExportString(const SvXMLUnitConverter& rConv)
{
    OUStringBuffer aNewString;
    OUString aClosingBrace(u")"_ustr);
    OUString aEmptySpace(u" "_ustr);
 
    const sal_uInt32 nCount = maList.size();
    for(sal_uInt32 a(0); a < nCount; a++)
    {
        ImpSdXMLExpTransObj3DBase* pObj = maList[a].get();
        switch(pObj->mnType)
        {
            case IMP_SDXMLEXP_TRANSOBJ3D_ROTATE_X   :
            {
                aNewString.append("rotatex (");
                Imp_PutDoubleChar(aNewString, rConv, basegfx::rad2deg( static_cast<ImpSdXMLExpTransObj3DRotateX*>(pObj)->mfRotateX) );
                aNewString.append(aClosingBrace);
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ3D_ROTATE_Y   :
            {
                aNewString.append("rotatey (");
                Imp_PutDoubleChar(aNewString, rConv, basegfx::rad2deg( static_cast<ImpSdXMLExpTransObj3DRotateY*>(pObj)->mfRotateY) );
                aNewString.append(aClosingBrace);
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ3D_ROTATE_Z   :
            {
                aNewString.append("rotatez (");
                Imp_PutDoubleChar(aNewString, rConv, basegfx::rad2deg( static_cast<ImpSdXMLExpTransObj3DRotateZ*>(pObj)->mfRotateZ) );
                aNewString.append(aClosingBrace);
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ3D_SCALE      :
            {
                aNewString.append("scale (");
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DScale*>(pObj)->maScale.getX());
                aNewString.append(aEmptySpace);
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DScale*>(pObj)->maScale.getY());
                aNewString.append(aEmptySpace);
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DScale*>(pObj)->maScale.getZ());
                aNewString.append(aClosingBrace);
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ3D_TRANSLATE  :
            {
                aNewString.append("translate (");
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DTranslate*>(pObj)->maTranslate.getX(), true);
                aNewString.append(aEmptySpace);
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DTranslate*>(pObj)->maTranslate.getY(), true);
                aNewString.append(aEmptySpace);
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DTranslate*>(pObj)->maTranslate.getZ(), true);
                aNewString.append(aClosingBrace);
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ3D_MATRIX :
            {
                aNewString.append("matrix (");
 
                // a
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DMatrix*>(pObj)->maMatrix.get(0, 0));
                aNewString.append(aEmptySpace);
 
                // b
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DMatrix*>(pObj)->maMatrix.get(1, 0));
                aNewString.append(aEmptySpace);
 
                // c
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DMatrix*>(pObj)->maMatrix.get(2, 0));
                aNewString.append(aEmptySpace);
 
                // d
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DMatrix*>(pObj)->maMatrix.get(0, 1));
                aNewString.append(aEmptySpace);
 
                // e
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DMatrix*>(pObj)->maMatrix.get(1, 1));
                aNewString.append(aEmptySpace);
 
                // f
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DMatrix*>(pObj)->maMatrix.get(2, 1));
                aNewString.append(aEmptySpace);
 
                // g
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DMatrix*>(pObj)->maMatrix.get(0, 2));
                aNewString.append(aEmptySpace);
 
                // h
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DMatrix*>(pObj)->maMatrix.get(1, 2));
                aNewString.append(aEmptySpace);
 
                // i
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DMatrix*>(pObj)->maMatrix.get(2, 2));
                aNewString.append(aEmptySpace);
 
                // j
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DMatrix*>(pObj)->maMatrix.get(0, 3), true);
                aNewString.append(aEmptySpace);
 
                // k
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DMatrix*>(pObj)->maMatrix.get(1, 3), true);
                aNewString.append(aEmptySpace);
 
                // l
                Imp_PutDoubleChar(aNewString, rConv, static_cast<ImpSdXMLExpTransObj3DMatrix*>(pObj)->maMatrix.get(2, 3), true);
 
                aNewString.append(aClosingBrace);
                break;
            }
            default :
            {
                OSL_FAIL("SdXMLImExTransform3D: impossible entry!");
                break;
            }
        }
 
        // if not the last entry, add one space to next tag
        if(a + 1 != maList.size())
        {
            aNewString.append(aEmptySpace);
        }
    }
 
    // fill string form OUString
    msString = aNewString.makeStringAndClear();
 
    return msString;
}
 
// for Import: constructor with string, parses it and generates entries
SdXMLImExTransform3D::SdXMLImExTransform3D(const OUString& rNew, const SvXMLUnitConverter& rConv)
{
    SetString(rNew, rConv);
}
 
// sets new string, parses it and generates entries
void SdXMLImExTransform3D::SetString(const OUString& rNew, const SvXMLUnitConverter& rConv)
{
    msString = rNew;
    maList.clear();
 
    if(msString.isEmpty())
        return;
 
    const OUString aStr = msString;
    const sal_Int32 nLen(aStr.getLength());
 
    static constexpr OUStringLiteral aString_rotatex( u"rotatex" );
    static constexpr OUStringLiteral aString_rotatey( u"rotatey" );
    static constexpr OUStringLiteral aString_rotatez( u"rotatez" );
    static constexpr OUStringLiteral aString_scale( u"scale" );
    static constexpr OUStringLiteral aString_translate( u"translate" );
    static constexpr OUStringLiteral aString_matrix( u"matrix" );
 
    sal_Int32 nPos(0);
 
    while(nPos < nLen)
    {
        // skip spaces
        Imp_SkipSpaces(aStr, nPos, nLen);
 
        // look for tag
        if(nPos < nLen)
        {
            if(nPos == aStr.indexOf(aString_rotatex, nPos))
            {
                double fValue(0.0);
 
                nPos += 7;
                Imp_SkipSpacesAndOpeningBraces(aStr, nPos, nLen);
                fValue = Imp_GetDoubleChar(aStr, nPos, nLen, rConv, fValue);
                if(fValue != 0.0)
                    maList.push_back(std::make_shared<ImpSdXMLExpTransObj3DRotateX>(basegfx::deg2rad(fValue)));
 
                Imp_SkipSpacesAndClosingBraces(aStr, nPos, nLen);
            }
            else if(nPos == aStr.indexOf(aString_rotatey, nPos))
            {
                double fValue(0.0);
 
                nPos += 7;
                Imp_SkipSpacesAndOpeningBraces(aStr, nPos, nLen);
                fValue = Imp_GetDoubleChar(aStr, nPos, nLen, rConv, fValue);
                if(fValue != 0.0)
                    maList.push_back(std::make_shared<ImpSdXMLExpTransObj3DRotateY>(basegfx::deg2rad(fValue)));
 
                Imp_SkipSpacesAndClosingBraces(aStr, nPos, nLen);
            }
            else if(nPos == aStr.indexOf(aString_rotatez, nPos))
            {
                double fValue(0.0);
 
                nPos += 7;
                Imp_SkipSpacesAndOpeningBraces(aStr, nPos, nLen);
                fValue = Imp_GetDoubleChar(aStr, nPos, nLen, rConv, fValue);
                if(fValue != 0.0)
                    maList.push_back(std::make_shared<ImpSdXMLExpTransObj3DRotateZ>(basegfx::deg2rad(fValue)));
 
                Imp_SkipSpacesAndClosingBraces(aStr, nPos, nLen);
            }
            else if(nPos == aStr.indexOf(aString_scale, nPos))
            {
                ::basegfx::B3DTuple aValue(1.0, 1.0, 1.0);
 
                nPos += 5;
                Imp_SkipSpacesAndOpeningBraces(aStr, nPos, nLen);
                aValue.setX(Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.getX()));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
                aValue.setY(Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.getY()));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
                aValue.setZ(Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.getZ()));
 
                if(1.0 != aValue.getX() || 1.0 != aValue.getY() || 1.0 != aValue.getZ())
                    maList.push_back(std::make_shared<ImpSdXMLExpTransObj3DScale>(aValue));
 
                Imp_SkipSpacesAndClosingBraces(aStr, nPos, nLen);
            }
            else if(nPos == aStr.indexOf(aString_translate, nPos))
            {
                ::basegfx::B3DTuple aValue;
 
                nPos += 9;
                Imp_SkipSpacesAndOpeningBraces(aStr, nPos, nLen);
                aValue.setX(Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.getX(), true));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
                aValue.setY(Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.getY(), true));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
                aValue.setZ(Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.getZ(), true));
 
                if(!aValue.equalZero())
                    maList.push_back(std::make_shared<ImpSdXMLExpTransObj3DTranslate>(aValue));
 
                Imp_SkipSpacesAndClosingBraces(aStr, nPos, nLen);
            }
            else if(nPos == aStr.indexOf(aString_matrix, nPos))
            {
                ::basegfx::B3DHomMatrix aValue;
 
                nPos += 6;
                Imp_SkipSpacesAndOpeningBraces(aStr, nPos, nLen);
 
                // a
                aValue.set(0, 0, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(0, 0)));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // b
                aValue.set(1, 0, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(1, 0)));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // c
                aValue.set(2, 0, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(2, 0)));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // d
                aValue.set(0, 1, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(0, 1)));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // e
                aValue.set(1, 1, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(1, 1)));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // f
                aValue.set(2, 1, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(2, 1)));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // g
                aValue.set(0, 2, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(0, 2)));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // h
                aValue.set(1, 2, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(1, 2)));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // i
                aValue.set(2, 2, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(2, 2)));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // j
                aValue.set(0, 3, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(0, 3), true));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // k
                aValue.set(1, 3, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(1, 3), true));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                // l
                aValue.set(2, 3, Imp_GetDoubleChar(aStr, nPos, nLen, rConv, aValue.get(2, 3), true));
                Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
                if(!aValue.isIdentity())
                    maList.push_back(std::make_shared<ImpSdXMLExpTransObj3DMatrix>(aValue));
 
                Imp_SkipSpacesAndClosingBraces(aStr, nPos, nLen);
            }
            else
            {
                nPos++;
            }
        }
    }
}
 
bool SdXMLImExTransform3D::GetFullHomogenTransform(css::drawing::HomogenMatrix& xHomMat)
{
    ::basegfx::B3DHomMatrix aFullTransform;
    GetFullTransform(aFullTransform);
 
    if(!aFullTransform.isIdentity())
    {
        basegfx::utils::B3DHomMatrixToUnoHomogenMatrix(aFullTransform, xHomMat);
        return true;
    }
 
    return false;
}
 
void SdXMLImExTransform3D::GetFullTransform(::basegfx::B3DHomMatrix& rFullTrans)
{
    rFullTrans.identity();
 
    const sal_uInt32 nCount = maList.size();
    for(sal_uInt32 a(0); a < nCount; a++)
    {
        ImpSdXMLExpTransObj3DBase* pObj = maList[a].get();
        switch(pObj->mnType)
        {
            case IMP_SDXMLEXP_TRANSOBJ3D_ROTATE_X   :
            {
                rFullTrans.rotate(static_cast<ImpSdXMLExpTransObj3DRotateX*>(pObj)->mfRotateX, 0.0, 0.0);
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ3D_ROTATE_Y   :
            {
                rFullTrans.rotate(0.0, static_cast<ImpSdXMLExpTransObj3DRotateY*>(pObj)->mfRotateY, 0.0);
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ3D_ROTATE_Z   :
            {
                rFullTrans.rotate(0.0, 0.0, static_cast<ImpSdXMLExpTransObj3DRotateZ*>(pObj)->mfRotateZ);
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ3D_SCALE      :
            {
                const ::basegfx::B3DTuple& rScale = static_cast<ImpSdXMLExpTransObj3DScale*>(pObj)->maScale;
                rFullTrans.scale(rScale.getX(), rScale.getY(), rScale.getZ());
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ3D_TRANSLATE  :
            {
                const ::basegfx::B3DTuple& rTranslate = static_cast<ImpSdXMLExpTransObj3DTranslate*>(pObj)->maTranslate;
                rFullTrans.translate(rTranslate.getX(), rTranslate.getY(), rTranslate.getZ());
                break;
            }
            case IMP_SDXMLEXP_TRANSOBJ3D_MATRIX     :
            {
                rFullTrans *= static_cast<ImpSdXMLExpTransObj3DMatrix*>(pObj)->maMatrix;
                break;
            }
            default :
            {
                OSL_FAIL("SdXMLImExTransform3D: impossible entry!");
                break;
            }
        }
    }
}
 
SdXMLImExViewBox::SdXMLImExViewBox(double fX, double fY, double fW, double fH)
:   mfX( fX ),
    mfY( fY ),
    mfW( fW ),
    mfH( fH )
{
}
 
// #100617# Asked vincent hardy: svg:viewBox values may be double precision.
SdXMLImExViewBox::SdXMLImExViewBox(OUString aNew, const SvXMLUnitConverter& rConv)
:   msString(std::move(aNew)),
    mfX( 0.0 ),
    mfY( 0.0 ),
    mfW( 1000.0 ),
    mfH( 1000.0 )
{
    if(msString.isEmpty())
        return;
 
    const OUString aStr = msString;
    const sal_Int32 nLen(aStr.getLength());
    sal_Int32 nPos(0);
 
    // skip starting spaces
    Imp_SkipSpaces(aStr, nPos, nLen);
 
    // get mX, #100617# be prepared for doubles
    mfX = Imp_GetDoubleChar(aStr, nPos, nLen, rConv, mfX);
 
    // skip spaces and commas
    Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
    // get mY, #100617# be prepared for doubles
    mfY = Imp_GetDoubleChar(aStr, nPos, nLen, rConv, mfY);
 
    // skip spaces and commas
    Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
    // get mW, #100617# be prepared for doubles
    mfW = Imp_GetDoubleChar(aStr, nPos, nLen, rConv, mfW);
 
    // skip spaces and commas
    Imp_SkipSpacesAndCommas(aStr, nPos, nLen);
 
    // get mH, #100617# be prepared for doubles
    mfH = Imp_GetDoubleChar(aStr, nPos, nLen, rConv, mfH);
 
}
 
const OUString& SdXMLImExViewBox::GetExportString()
{
    OUString aNewString;
    OUString aEmptySpace(u" "_ustr);
 
    Imp_PutDoubleChar(aNewString, mfX);
    aNewString += aEmptySpace;
 
    Imp_PutDoubleChar(aNewString, mfY);
    aNewString += aEmptySpace;
 
    Imp_PutDoubleChar(aNewString, mfW);
    aNewString += aEmptySpace;
 
    Imp_PutDoubleChar(aNewString, mfH);
 
    // set new string
    msString = aNewString;
 
    return msString;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.