Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/oox/source/export/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 112 kB image not shown  

Quelle  shapes.cxx   Sprache: C

 
/* -*- 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 <config_wasm_strip.h>

#include <sal/config.h>
#include <sal/log.hxx>

#include <filter/msfilter/util.hxx>
#include <o3tl/string_view.hxx>
#include <o3tl/any.hxx>
#include <oox/core/xmlfilterbase.hxx>
#include <oox/export/shapes.hxx>
#include <oox/export/utils.hxx>
#include <oox/token/namespaces.hxx>
#include <oox/token/relationship.hxx>
#include <oox/token/tokens.hxx>

#include <initializer_list>
#include <string_view>

#include <com/sun/star/beans/PropertyValues.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/beans/XPropertyState.hpp>
#include <com/sun/star/container/XChild.hpp>
#include <com/sun/star/document/XExporter.hpp>
#include <com/sun/star/document/XStorageBasedDocument.hpp>
#include <com/sun/star/drawing/CircleKind.hpp>
#include <com/sun/star/drawing/FillStyle.hpp>
#include <com/sun/star/drawing/ConnectorType.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
#include <com/sun/star/embed/Aspects.hpp>
#include <com/sun/star/embed/EmbedStates.hpp>
#include <com/sun/star/embed/XEmbeddedObject.hpp>
#include <com/sun/star/embed/XEmbedPersist.hpp>
#include <com/sun/star/frame/XStorable.hpp>
#include <com/sun/star/graphic/XGraphic.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/io/XOutputStream.hpp>
#include <com/sun/star/text/XSimpleText.hpp>
#include <com/sun/star/text/XText.hpp>
#include <com/sun/star/table/XTable.hpp>
#include <com/sun/star/table/XMergeableCell.hpp>
#include <com/sun/star/chart2/XChartDocument.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/drawing/XDrawPages.hpp>
#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
#include <com/sun/star/presentation/ClickAction.hpp>
#include <com/sun/star/drawing/XGluePointsSupplier.hpp>
#include <com/sun/star/container/XIdentifierAccess.hpp>
#include <com/sun/star/table/BorderLineStyle.hpp>
#include <tools/globname.hxx>
#include <comphelper/classids.hxx>
#include <comphelper/propertysequence.hxx>
#include <comphelper/storagehelper.hxx>
#include <sot/exchange.hxx>
#include <utility>
#include <vcl/graph.hxx>
#include <vcl/outdev.hxx>
#include <filter/msfilter/escherex.hxx>
#include <svtools/embedhlp.hxx>
#include <svx/svdoashp.hxx>
#include <svx/svdoole2.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <oox/export/chartexport.hxx>
#include <oox/mathml/imexport.hxx>
#include <basegfx/numeric/ftools.hxx>
#include <oox/export/DMLPresetShapeExport.hxx>

#include <frozen/bits/defines.h>
#include <frozen/bits/elsa_std.h>
#include <frozen/set.h>
#include <frozen/unordered_map.h>

#include <sax/fastattribs.hxx>

using namespace ::css;
using namespace ::css::beans;
using namespace ::css::uno;
using namespace ::css::drawing;
using namespace ::css::table;
using namespace ::css::container;
using namespace ::css::document;
using namespace ::css::text;

using ::css::io::XOutputStream;
using ::css::chart2::XChartDocument;
using ::css::frame::XModel;

using ::oox::core::XmlFilterBase;
using ::sax_fastparser::FSHelperPtr;


namespace oox {

static void lcl_ConvertProgID(std::u16string_view rProgID,
    OUString & o_rMediaType, OUString & o_rRelationType, OUString & o_rFileExtension)
{
    if (rProgID == u"Excel.Sheet.12")
    {
        o_rMediaType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
        o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
        o_rFileExtension = "xlsx";
    }
    else if (o3tl::starts_with(rProgID, u"Excel.SheetBinaryMacroEnabled.12") )
    {
        o_rMediaType = "application/vnd.ms-excel.sheet.binary.macroEnabled.12";
        o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
        o_rFileExtension = "xlsb";
    }
    else if (o3tl::starts_with(rProgID, u"Excel.SheetMacroEnabled.12"))
    {
        o_rMediaType = "application/vnd.ms-excel.sheet.macroEnabled.12";
        o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
        o_rFileExtension = "xlsm";
    }
    else if (o3tl::starts_with(rProgID, u"Excel.Sheet"))
    {
        o_rMediaType = "application/vnd.ms-excel";
        o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
        o_rFileExtension = "xls";
    }
    else if (rProgID == u"PowerPoint.Show.12")
    {
        o_rMediaType = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
        o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
        o_rFileExtension = "pptx";
    }
    else if (rProgID == u"PowerPoint.ShowMacroEnabled.12")
    {
        o_rMediaType = "application/vnd.ms-powerpoint.presentation.macroEnabled.12";
        o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
        o_rFileExtension = "pptm";
    }
    else if (o3tl::starts_with(rProgID, u"PowerPoint.Show"))
    {
        o_rMediaType = "application/vnd.ms-powerpoint";
        o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
        o_rFileExtension = "ppt";
    }
    else if (o3tl::starts_with(rProgID, u"PowerPoint.Slide.12"))
    {
       o_rMediaType = "application/vnd.openxmlformats-officedocument.presentationml.slide";
       o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
       o_rFileExtension = "sldx";
    }
    else if (rProgID == u"PowerPoint.SlideMacroEnabled.12")
    {
       o_rMediaType = "application/vnd.ms-powerpoint.slide.macroEnabled.12";
       o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
       o_rFileExtension = "sldm";
    }
    else if (rProgID == u"Word.DocumentMacroEnabled.12")
    {
        o_rMediaType = "application/vnd.ms-word.document.macroEnabled.12";
        o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
        o_rFileExtension = "docm";
    }
    else if (rProgID == u"Word.Document.12")
    {
        o_rMediaType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
        o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
        o_rFileExtension = "docx";
    }
    else if (rProgID == u"Word.Document.8")
    {
        o_rMediaType = "application/msword";
        o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
        o_rFileExtension = "doc";
    }
    else if (rProgID == u"Excel.Chart.8")
    {
        o_rMediaType = "application/vnd.ms-excel";
        o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
        o_rFileExtension = "xls";
    }
    else if (rProgID == u"AcroExch.Document.11")
    {
        o_rMediaType = "application/pdf";
        o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
        o_rFileExtension = "pdf";
    }
    else
    {
        o_rMediaType = "application/vnd.openxmlformats-officedocument.oleObject";
        o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
        o_rFileExtension = "bin";
    }
}

static uno::Reference<io::XInputStream> lcl_StoreOwnAsOOXML(
    uno::Reference<uno::XComponentContext> const& xContext,
    uno::Reference<embed::XEmbeddedObject> const& xObj,
    char const*& o_rpProgID,
    OUString & o_rMediaType, OUString & o_rRelationType, OUString & o_rSuffix)
{
    static struct {
        struct {
            sal_uInt32 n1;
            sal_uInt16 n2, n3;
            sal_uInt8 b8, b9, b10, b11, b12, b13, b14, b15;
        } ClassId;
        char const* pFilterName;
        char const* pMediaType;
        char const* pProgID;
        char const* pSuffix;
    } const s_Mapping[] = {
        { {SO3_SW_CLASSID_60}, "MS Word 2007 XML""application/vnd.openxmlformats-officedocument.wordprocessingml.document""Word.Document.12""docx" },
        { {SO3_SC_CLASSID_60}, "Calc MS Excel 2007 XML""application/vnd.openxmlformats-officedocument.spreadsheetml.sheet""Excel.Sheet.12""xlsx" },
        { {SO3_SIMPRESS_CLASSID_60}, "Impress MS PowerPoint 2007 XML""application/vnd.openxmlformats-officedocument.presentationml.presentation""PowerPoint.Show.12""pptx" },
        // FIXME: Draw does not appear to have a MSO format export filter?
//            { {SO3_SDRAW_CLASSID}, "", "", "", "" },
        { {SO3_SCH_CLASSID_60}, "unused""""""" },
        { {SO3_SM_CLASSID_60}, "unused""""""" },
    };

    const char * pFilterName(nullptr);
    SvGlobalName const classId(xObj->getClassID());
    for (auto & i : s_Mapping)
    {
        auto const& rId(i.ClassId);
        SvGlobalName const temp(rId.n1, rId.n2, rId.n3, rId.b8, rId.b9, rId.b10, rId.b11, rId.b12, rId.b13, rId.b14, rId.b15);
        if (temp == classId)
        {
            assert(SvGlobalName(SO3_SCH_CLASSID_60) != classId); // chart should be written elsewhere!
            assert(SvGlobalName(SO3_SM_CLASSID_60) != classId); // formula should be written elsewhere!
            pFilterName = i.pFilterName;
            o_rMediaType = OUString::createFromAscii(i.pMediaType);
            o_rpProgID = i.pProgID;
            o_rSuffix = OUString::createFromAscii(i.pSuffix);
            o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
            break;
        }
    }

    if (!pFilterName)
    {
        SAL_WARN("oox.shape""oox::GetOLEObjectStream: unknown ClassId " << classId.GetHexName());
        return nullptr;
    }

    if (embed::EmbedStates::LOADED == xObj->getCurrentState())
    {
        xObj->changeState(embed::EmbedStates::RUNNING);
    }
    // use a temp stream - while it would work to store directly to a
    // fragment stream, an error during export means we'd have to delete it
    uno::Reference<io::XStream> const xTempStream(
        xContext->getServiceManager()->createInstanceWithContext(
            u"com.sun.star.comp.MemoryStream"_ustr, xContext),
        uno::UNO_QUERY_THROW);
    uno::Sequence<beans::PropertyValue> args( comphelper::InitPropertySequence({
            { "OutputStream", Any(xTempStream->getOutputStream()) },
            { "FilterName", Any(OUString::createFromAscii(pFilterName)) }
        }));
    uno::Reference<frame::XStorable> xStorable(xObj->getComponent(), uno::UNO_QUERY);
    try
    {
        xStorable->storeToURL(u"private:stream"_ustr, args);
    }
    catch (uno::Exception const&)
    {
        TOOLS_WARN_EXCEPTION("oox.shape""oox::GetOLEObjectStream");
        return nullptr;
    }
    xTempStream->getOutputStream()->closeOutput();
    return xTempStream->getInputStream();
}

uno::Reference<io::XInputStream> GetOLEObjectStream(
        uno::Reference<uno::XComponentContext> const& xContext,
        uno::Reference<embed::XEmbeddedObject> const& xObj,
        std::u16string_view i_rProgID,
        OUString & o_rMediaType,
        OUString & o_rRelationType,
        OUString & o_rSuffix,
        const char *& o_rpProgID)
{
    uno::Reference<io::XInputStream> xInStream;
    try
    {
        uno::Reference<document::XStorageBasedDocument> const xParent(
            uno::Reference<container::XChild>(xObj, uno::UNO_QUERY_THROW)->getParent(),
            uno::UNO_QUERY_THROW);
        uno::Reference<embed::XStorage> const xParentStorage(xParent->getDocumentStorage());
        OUString const entryName(
            uno::Reference<embed::XEmbedPersist>(xObj, uno::UNO_QUERY_THROW)->getEntryName());

        if (xParentStorage->isStreamElement(entryName))
        {
            lcl_ConvertProgID(i_rProgID, o_rMediaType, o_rRelationType, o_rSuffix);
            xInStream = xParentStorage->cloneStreamElement(entryName)->getInputStream();
            // TODO: make it possible to take the sMediaType from the stream
        }
        else // the object is ODF - either the whole document is
        {    // ODF, or the OLE was edited so it was converted to ODF
            xInStream = lcl_StoreOwnAsOOXML(xContext, xObj,
                    o_rpProgID, o_rMediaType, o_rRelationType, o_rSuffix);
        }
    }
    catch (uno::Exception const&)
    {
        TOOLS_WARN_EXCEPTION("oox.shape""oox::GetOLEObjectStream");
    }
    return xInStream;
}

// namespace oox

namespace oox::drawingml {

ShapeExport::ShapeExport( sal_Int32 nXmlNamespace, FSHelperPtr pFS, ShapeHashMap* pShapeMap, XmlFilterBase* pFB, DocumentType eDocumentType, DMLTextExport* pTextExport, bool bUserShapes )
    : DrawingML( std::move(pFS), pFB, eDocumentType, pTextExport )
    , m_nEmbeddedObjects(0)
    , mnShapeIdMax( 1 )
    , mbUserShapes( bUserShapes )
    , mnXmlNamespace( nXmlNamespace )
    , maMapModeSrc( MapUnit::Map100thMM )
    , maMapModeDest( MapUnit::MapInch, Point(), Fraction( 1, 576 ), Fraction( 1, 576 ) )
    , mpShapeMap( pShapeMap ? pShapeMap : &maShapeMap )
{
    mpURLTransformer = std::make_shared<URLTransformer>();
}

void ShapeExport::SetURLTranslator(const std::shared_ptr<URLTransformer>& pTransformer)
{
    mpURLTransformer = pTransformer;
}

awt::Size ShapeExport::MapSize( const awt::Size& rSize ) const
{
    Size aRetSize( OutputDevice::LogicToLogic( Size( rSize.Width, rSize.Height ), maMapModeSrc, maMapModeDest ) );

    if ( !aRetSize.Width() )
        aRetSize.AdjustWidth( 1 );
    if ( !aRetSize.Height() )
        aRetSize.AdjustHeight( 1 );
    return awt::Size( aRetSize.Width(), aRetSize.Height() );
}

static bool IsNonEmptySimpleText(const Reference<XInterface>& xIface)
{
    if (Reference<XSimpleText> xText{ xIface, UNO_QUERY })
        return xText->getString().getLength();

    return false;
}

bool ShapeExport::NonEmptyText( const Reference< XInterface >& xIface )
{
    Reference< XPropertySet > xPropSet( xIface, UNO_QUERY );

    if( xPropSet.is() )
    {
        Reference< XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
        if ( xPropSetInfo.is() )
        {
            if ( xPropSetInfo->hasPropertyByName( u"IsEmptyPresentationObject"_ustr ) )
            {
                bool bIsEmptyPresObj = false;
                if ( xPropSet->getPropertyValue( u"IsEmptyPresentationObject"_ustr ) >>= bIsEmptyPresObj )
                {
                    SAL_INFO("oox.shape""empty presentation object " << bIsEmptyPresObj << " , props:");
                    if( bIsEmptyPresObj )
                       return true;
                }
            }

            if ( xPropSetInfo->hasPropertyByName( u"IsPresentationObject"_ustr ) )
            {
                bool bIsPresObj = false;
                if ( xPropSet->getPropertyValue( u"IsPresentationObject"_ustr ) >>= bIsPresObj )
                {
                    SAL_INFO("oox.shape""presentation object " << bIsPresObj << ", props:");
                    if( bIsPresObj )
                       return true;
                }
            }
        }
    }

    return IsNonEmptySimpleText(xIface);
}

static void AddExtLst(FSHelperPtr const& pFS, Reference<XPropertySet> const& xShape)
{
    if (xShape->getPropertySetInfo()->hasPropertyByName(u"Decorative"_ustr)
        && xShape->getPropertyValue(u"Decorative"_ustr).get<bool>())
    {
        pFS->startElementNS(XML_a, XML_extLst);
//            FSNS(XML_xmlns, XML_a), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
        pFS->startElementNS(XML_a, XML_ext,
            // MSO uses this "URI" which is obviously not a URI
            XML_uri, "{C183D7F6-B498-43B3-948B-1728B52AA6E4}");
        pFS->singleElementNS(XML_adec, XML_decorative,
            FSNS(XML_xmlns, XML_adec), "http://schemas.microsoft.com/office/drawing/2017/decorative",
            XML_val, "1");
        pFS->endElementNS(XML_a, XML_ext);
        pFS->endElementNS(XML_a, XML_extLst);
    }
}

ShapeExport& ShapeExport::WritePolyPolygonShape( const Reference< XShape >& xShape, const bool bClosed )
{
    SAL_INFO("oox.shape""write polypolygon shape");

    FSHelperPtr pFS = GetFS();
    pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp));

    awt::Point aPos = xShape->getPosition();
    // Position is relative to group for child elements in Word, but absolute in API.
    if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes && m_xParent.is())
    {
        awt::Point aParentPos = m_xParent->getPosition();
        aPos.X -= aParentPos.X;
        aPos.Y -= aParentPos.Y;
    }
    awt::Size aSize = xShape->getSize();
    tools::Rectangle aRect(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height));

#if OSL_DEBUG_LEVEL > 0
    tools::PolyPolygon aPolyPolygon = EscherPropertyContainer::GetPolyPolygon(xShape);
    awt::Size size = MapSize( awt::Size( aRect.GetWidth(), aRect.GetHeight() ) );
    SAL_INFO("oox.shape""poly count " << aPolyPolygon.Count());
    SAL_INFO("oox.shape""size: " << size.Width << " x " << size.Height);
#endif

    Reference<XPropertySet> const xProps(xShape, UNO_QUERY);
    // non visual shape properties
    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
    {
        pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
        pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
                              XML_id, OString::number(GetNewShapeID(xShape)),
                              XML_name, GetShapeName(xShape));
        AddExtLst(pFS, xProps);
        pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
    }
    pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
    {
        WriteNonVisualProperties( xShape );
        pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
    }

    // visual shape properties
    pFS->startElementNS(mnXmlNamespace, XML_spPr);
    WriteTransformation( xShape, aRect, XML_a );
    WritePolyPolygon(xShape, bClosed);
    if( xProps.is() ) {
        if( bClosed )
            WriteFill(xProps, aSize);
        WriteOutline( xProps );
    }

    pFS->endElementNS( mnXmlNamespace, XML_spPr );

    // write text
    WriteTextBox( xShape, mnXmlNamespace );

    pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp) );

    return *this;
}

ShapeExport& ShapeExport::WriteClosedPolyPolygonShape( const Reference< XShape >&&nbsp;xShape )
{
    return WritePolyPolygonShape( xShape, true );
}

ShapeExport& ShapeExport::WriteOpenPolyPolygonShape( const Reference< XShape >& xShape )
{
    return WritePolyPolygonShape( xShape, false );
}

ShapeExport& ShapeExport::WriteGroupShape(const uno::Reference<drawing::XShape>&&nbsp;xShape)
{
    FSHelperPtr pFS = GetFS();

    sal_Int32 nGroupShapeToken = XML_grpSp;
    if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes)
    {
        if (!m_xParent.is())
            nGroupShapeToken = XML_wgp; // toplevel
        else
            mnXmlNamespace = XML_wpg;
    }

    pFS->startElementNS(mnXmlNamespace, nGroupShapeToken);

    // non visual properties
    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
    {
        pFS->startElementNS(mnXmlNamespace, XML_nvGrpSpPr);
        pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
                XML_id, OString::number(GetNewShapeID(xShape)),
                XML_name, GetShapeName(xShape));
        uno::Reference<beans::XPropertySet> const xShapeProps(xShape, uno::UNO_QUERY_THROW);
        AddExtLst(pFS, xShapeProps);
        pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
        pFS->singleElementNS(mnXmlNamespace, XML_cNvGrpSpPr);
        WriteNonVisualProperties(xShape );
        pFS->endElementNS(mnXmlNamespace, XML_nvGrpSpPr);
    }
    else
        pFS->singleElementNS(mnXmlNamespace, XML_cNvGrpSpPr);

    // visual properties
    pFS->startElementNS(mnXmlNamespace, XML_grpSpPr);
    WriteShapeTransformation(xShape, XML_a, falsefalsetrue);
    pFS->endElementNS(mnXmlNamespace, XML_grpSpPr);

    uno::Reference<drawing::XShapes> xGroupShape(xShape, uno::UNO_QUERY_THROW);
    uno::Reference<drawing::XShape> xParent = m_xParent;
    m_xParent = xShape;
    for (sal_Int32 i = 0; i < xGroupShape->getCount(); ++i)
    {
        uno::Reference<drawing::XShape> xChild(xGroupShape->getByIndex(i), uno::UNO_QUERY_THROW);
        sal_Int32 nSavedNamespace = mnXmlNamespace;

        uno::Reference<lang::XServiceInfo> xServiceInfo(xChild, uno::UNO_QUERY_THROW);
        if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes)
        {
            // tdf#128820: WriteGraphicObjectShapePart calls WriteTextShape for non-empty simple
            // text objects, which needs writing into wps::wsp element, so make sure to use wps
            // namespace for those objects
            if (xServiceInfo->supportsService(u"com.sun.star.drawing.GraphicObjectShape"_ustr)
                && !IsNonEmptySimpleText(xChild))
                mnXmlNamespace = XML_pic;
            else
                mnXmlNamespace = XML_wps;
        }
        WriteShape(xChild);

        mnXmlNamespace = nSavedNamespace;
    }
    m_xParent = std::move(xParent);

    pFS->endElementNS(mnXmlNamespace, nGroupShapeToken);
    return *this;
}
namespace
{

constexpr frozen::set<std::u16string_view, 57> constDenySet(
{
    u"block-arc",
    u"rectangle",
    u"ellipse",
    u"ring",
    u"can",
    u"cube",
    u"paper",
    u"frame",
    u"forbidden",
    u"smiley",
    u"sun",
    u"flower",
    u"bracket-pair",
    u"brace-pair",
    u"quad-bevel",
    u"round-rectangular-callout",
    u"rectangular-callout",
    u"round-callout",
    u"cloud-callout",
    u"line-callout-1",
    u"line-callout-2",
    u"line-callout-3",
    u"paper",
    u"vertical-scroll",
    u"horizontal-scroll",
    u"mso-spt34",
    u"mso-spt75",
    u"mso-spt164",
    u"mso-spt180",
    u"flowchart-process",
    u"flowchart-alternate-process",
    u"flowchart-decision",
    u"flowchart-data",
    u"flowchart-predefined-process",
    u"flowchart-internal-storage",
    u"flowchart-document",
    u"flowchart-multidocument",
    u"flowchart-terminator",
    u"flowchart-preparation",
    u"flowchart-manual-input",
    u"flowchart-manual-operation",
    u"flowchart-connector",
    u"flowchart-off-page-connector",
    u"flowchart-card",
    u"flowchart-punched-tape",
    u"flowchart-summing-junction",
    u"flowchart-or",
    u"flowchart-collate",
    u"flowchart-sort",
    u"flowchart-extract",
    u"flowchart-merge",
    u"flowchart-stored-data",
    u"flowchart-delay",
    u"flowchart-sequential-access",
    u"flowchart-magnetic-disk",
    u"flowchart-direct-access-storage",
    u"flowchart-display"
});

constexpr frozen::set<std::u16string_view, 6> constAllowSet(
{
    u"heart",
    u"puzzle",
    u"col-60da8460",
    u"col-502ad400",
    u"sinusoid",
    u"mso-spt100"
});

// end anonymous namespace

static bool lcl_IsOnDenylist(OUString const & rShapeType)
{
    return constDenySet.find(rShapeType) != constDenySet.end();
}

static bool lcl_IsOnAllowlist(OUString const & rShapeType)
{
    return constAllowSet.find(rShapeType) != constAllowSet.end();
}

static bool lcl_GetHandlePosition( sal_Int32 &nValue, const EnhancedCustomShapeParameter &rParam,&nbsp;const Sequence< EnhancedCustomShapeAdjustmentValue > &rSeq)
{
    bool bAdj = false;
    if ( rParam.Value.getValueTypeClass() == TypeClass_DOUBLE )
    {
        double fValue(0.0);
        if ( rParam.Value >>= fValue )
            nValue = static_cast<sal_Int32>(fValue);
    }
    else
        rParam.Value >>= nValue;

    if ( rParam.Type == EnhancedCustomShapeParameterType::ADJUSTMENT)
    {
        bAdj = true;
        sal_Int32 nIdx = nValue;
        if ( nIdx < rSeq.getLength() )
        {
            if ( rSeq[ nIdx ] .Value.getValueTypeClass() == TypeClass_DOUBLE )
            {
                double fValue(0.0);
                rSeq[ nIdx ].Value >>= fValue;
                nValue = fValue;

            }
            else
            {
                rSeq[ nIdx ].Value >>= nValue;
            }
        }
    }
    return bAdj;
}

static void lcl_AnalyzeHandles( const uno::Sequence<beans::PropertyValues> & rHandles,
        std::vector< std::pair< sal_Int32, sal_Int32> > &rHandlePositionList,
        const Sequence< EnhancedCustomShapeAdjustmentValue > &rSeq)
{
    for ( const Sequence< PropertyValue >& rPropSeq : rHandles )
    {
        static constexpr OUStringLiteral sPosition( u"Position"  );
        bool bPosition = false;
        EnhancedCustomShapeParameterPair aPosition;
        for ( const PropertyValue& rPropVal: rPropSeq )
        {
            if ( rPropVal.Name == sPosition )
            {
                if ( rPropVal.Value >>= aPosition )
                    bPosition = true;
            }
        }
        if ( bPosition )
        {
            sal_Int32 nXPosition = 0;
            sal_Int32 nYPosition = 0;
            // For polar handles, nXPosition is radius and nYPosition is angle
            lcl_GetHandlePosition( nXPosition, aPosition.First , rSeq );
            lcl_GetHandlePosition( nYPosition, aPosition.Second, rSeq );
            rHandlePositionList.emplace_back( nXPosition, nYPosition );
        }
    }
}

static void lcl_AppendAdjustmentValue( std::vector< std::pair< sal_Int32, sal_Int32> > &rAvList, sal_Int32 nAdjIdx, sal_Int32 nValue )
{
    rAvList.emplace_back( nAdjIdx , nValue );
}

static sal_Int32 lcl_NormalizeAngle( sal_Int32 nAngle )
{
    nAngle = nAngle % 360;
    return nAngle < 0 ? ( nAngle + 360 ) : nAngle ;
}

static sal_Int32 lcl_CircleAngle2CustomShapeEllipseAngleOOX(const sal_Int32 nInternAngle, const sal_Int32 nWidth, const sal_Int32 nHeight)
{
    if (nWidth != 0 || nHeight != 0)
    {
        double fAngle = basegfx::deg2rad<100>(nInternAngle); // intern 1/100 deg to rad
        fAngle = atan2(nHeight * sin(fAngle), nWidth * cos(fAngle)); // circle to ellipse
        fAngle = basegfx::rad2deg<60000>(fAngle); // rad to OOXML angle unit
        sal_Int32 nAngle = basegfx::fround(fAngle); // normalize
        nAngle = nAngle % 21600000;
        return nAngle < 0 ? (nAngle + 21600000) : nAngle;
    }
    else // should be handled by caller, dummy value
        return 0;
}

static OUString lcl_GetTarget(const css::uno::Reference<css::frame::XModel>& xModel,
                              std::u16string_view rURL)
{
    Reference<drawing::XDrawPagesSupplier> xDPS(xModel, uno::UNO_QUERY_THROW);
    Reference<drawing::XDrawPages> xDrawPages(xDPS->getDrawPages(), uno::UNO_SET_THROW);
    sal_uInt32 nPageCount = xDrawPages->getCount();
    OUString sTarget;

    for (sal_uInt32 i = 0; i < nPageCount; ++i)
    {
        Reference<XDrawPage> xDrawPage;
        xDrawPages->getByIndex(i) >>= xDrawPage;
        Reference<container::XNamed> xNamed(xDrawPage, UNO_QUERY);
        if (!xNamed)
            continue;
        OUString sSlideName = "#" + xNamed->getName();
        if (rURL == sSlideName)
        {
            sTarget = "slide" + OUString::number(i + 1) + ".xml";
            break;
        }
    }

    return sTarget;
}

ShapeExport& ShapeExport::WriteCustomShape( const Reference< XShape >& xShape )
{
    SAL_INFO("oox.shape""write custom shape");
    Reference< XPropertySet > rXPropSet( xShape, UNO_QUERY );
    // First check, if this is a Fontwork-shape. For DrawingML, such a shape is a
    // TextBox shape with body property prstTxWarp.
    if (IsFontworkShape(rXPropSet))
    {
        ShapeExport::WriteTextShape(xShape); // qualifier to prevent PowerPointShapeExport
        return *this;
    }

    bool bHasGeometrySeq(false);
    Sequence< PropertyValue > aGeometrySeq;
    OUString sShapeType(u"non-primitive"_ustr); // default in ODF
    if (GetProperty(rXPropSet, u"CustomShapeGeometry"_ustr))
    {
        SAL_INFO("oox.shape""got custom shape geometry");
        if (mAny >>= aGeometrySeq)
        {
            bHasGeometrySeq = true;
            SAL_INFO("oox.shape""got custom shape geometry sequence");
            for (const PropertyValue& rProp : aGeometrySeq)
            {
                SAL_INFO("oox.shape""geometry property: " << rProp.Name);
                if (rProp.Name == "Type")
                    rProp.Value >>= sShapeType;
            }
        }
    }

    bool bPredefinedHandlesUsed = true;
    bool bHasHandles = false;

    ShapeFlag nMirrorFlags = ShapeFlag::NONE;
    MSO_SPT eShapeType = EscherPropertyContainer::GetCustomShapeType( xShape, nMirrorFlags, sShapeType );
    assert(dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(xShape)) && "Not a SdrObjCustomShape (!)");
    SdrObjCustomShape& rSdrObjCustomShape(static_cast< SdrObjCustomShape& >(*SdrObject::getSdrObjectFromXShape(xShape)));
    const bool bIsDefaultObject(
        EscherPropertyContainer::IsDefaultObject(
            rSdrObjCustomShape,
            eShapeType));
    OString sPresetShape = msfilter::util::GetOOXMLPresetGeometry(sShapeType);
    SAL_INFO("oox.shape""custom shape type: " << sShapeType << " ==> " << sPresetShape);

    sal_Int32 nAdjustmentValuesIndex = -1;
    awt::Rectangle aViewBox;
    uno::Sequence<beans::PropertyValues> aHandles;

    bool bFlipH = false;
    bool bFlipV = false;

    if (bHasGeometrySeq)
    {
        for (int i = 0; i < aGeometrySeq.getLength(); i++)
        {
                const PropertyValue& rProp = aGeometrySeq[ i ];
                SAL_INFO("oox.shape""geometry property: " << rProp.Name);

                if ( rProp.Name == "MirroredX" )
                    rProp.Value >>= bFlipH;

                if ( rProp.Name == "MirroredY" )
                    rProp.Value >>= bFlipV;
                if ( rProp.Name == "AdjustmentValues" )
                    nAdjustmentValuesIndex = i;
                else if ( rProp.Name == "Handles" )
                {
                    rProp.Value >>= aHandles;
                    if ( aHandles.hasElements() )
                        bHasHandles = true;
                    if( !bIsDefaultObject )
                        bPredefinedHandlesUsed = false;
                    // TODO: update nAdjustmentsWhichNeedsToBeConverted here
                }
                else if ( rProp.Name == "ViewBox" )
                    rProp.Value >>= aViewBox;
        }
    }

    FSHelperPtr pFS = GetFS();
    // non visual shape properties
    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
    {
        // get InteropGrabBag to export attributes stored in the grabbag
        uno::Sequence<beans::PropertyValue> aGrabBagProps;
        rXPropSet->getPropertyValue(u"InteropGrabBag"_ustr) >>= aGrabBagProps;

        bool bUseBackground = false;
        if (GetProperty(rXPropSet, u"FillUseSlideBackground"_ustr))
            mAny >>= bUseBackground;
        if (bUseBackground)
            mpFS->startElementNS(mnXmlNamespace, XML_sp, XML_useBgFill, "1");
        else
        {
            rtl::Reference<sax_fastparser::FastAttributeList> pAttrListSp
                = sax_fastparser::FastSerializerHelper::createAttrList();

            for (auto const& it : aGrabBagProps)
            {
                // export macro attribute of <sp> element
                if (it.Name == u"mso-sp-macro"_ustr)
                {
                    OUString sMacro;
                    it.Value >>= sMacro;

                    if (!sMacro.isEmpty())
                        pAttrListSp->add(XML_macro, sMacro);
                }

                // export textlink attribute of <sp> element
                if (it.Name == u"mso-sp-textlink"_ustr)
                {
                    OUString sTextLink;
                    it.Value >>= sTextLink;

                    if (!sTextLink.isEmpty())
                        pAttrListSp->add(XML_textlink, sTextLink);
                }

                // export fLocksText attribute of <sp> element
                if (it.Name == u"mso-sp-fLocksText"_ustr)
                {
                    bool bFLocksText = true// default="true"
                    it.Value >>= bFLocksText;
                    pAttrListSp->add(XML_fLocksText, ToPsz10(bFLocksText));
                }

                // export fPublished attribute of <sp> element
                if (it.Name == u"mso-sp-fPublished"_ustr)
                {
                    bool bFPublished = false;
                    it.Value >>= bFPublished;
                    pAttrListSp->add(XML_fPublished, ToPsz10(bFPublished));
                }
            }

            // export <sp> element (with a namespace prefix)
            mpFS->startElementNS(mnXmlNamespace, XML_sp, pAttrListSp);
        }

        bool isVisible = true ;
        if( GetProperty(rXPropSet, u"Visible"_ustr))
        {
            mAny >>= isVisible;
        }
        pFS->startElementNS( mnXmlNamespace, XML_nvSpPr );

        // export descr attribute of <cNvPr> element
        OUString sDescr;
        if (GetProperty(rXPropSet, u"Description"_ustr))
            mAny >>= sDescr;

        // export title attribute of <cNvPr> element
        OUString sTitle;
        if (GetProperty(rXPropSet, u"Title"_ustr))
            mAny >>= sTitle;

        // export <cNvPr> element
        pFS->startElementNS(
            mnXmlNamespace, XML_cNvPr, XML_id,
            OString::number(GetShapeID(xShape) == -1 ? GetNewShapeID(xShape) : GetShapeID(xShape)),
            XML_name, GetShapeName(xShape), XML_hidden, sax_fastparser::UseIf("1", !isVisible),
            XML_descr, sax_fastparser::UseIf(sDescr, !sDescr.isEmpty()), XML_title,
            sax_fastparser::UseIf(sTitle, !sTitle.isEmpty()));

        rtl::Reference<sax_fastparser::FastAttributeList> pAttrListHlinkClick
            = sax_fastparser::FastSerializerHelper::createAttrList();

        for (auto const& it : aGrabBagProps)
        {
            // export tooltip attribute of <hlinkClick> element
            if (it.Name == u"mso-hlinkClick-tooltip"_ustr)
            {
                OUString sTooltip;
                it.Value >>= sTooltip;

                if (!sTooltip.isEmpty())
                    pAttrListHlinkClick->add(XML_tooltip, sTooltip);
            }
        }

        if( GetProperty(rXPropSet, u"URL"_ustr) )
        {
            OUString sURL;
            mAny >>= sURL;
            if( !sURL.isEmpty() )
            {
                OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
                        oox::getRelationship(Relationship::HYPERLINK),
                        mpURLTransformer->getTransformedString(sURL),
                        mpURLTransformer->isExternalURL(sURL));

                mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
                // pAttrListHlinkClick->add(FSNS(XML_r, XML_id), sRelId);
            }
        }

        // // export <hlinkClick> element
        // mpFS->singleElementNS(XML_a, XML_hlinkClick, pAttrListHlinkClick);

        OUString sBookmark;
        if (GetProperty(rXPropSet, u"Bookmark"_ustr))
            mAny >>= sBookmark;

        if (GetProperty(rXPropSet, u"OnClick"_ustr))
        {
            OUString sPPAction;
            presentation::ClickAction eClickAction = presentation::ClickAction_NONE;
            mAny >>= eClickAction;
            if (eClickAction != presentation::ClickAction_NONE)
            {
                switch (eClickAction)
                {
                    case presentation::ClickAction_STOPPRESENTATION:
                        sPPAction = "ppaction://hlinkshowjump?jump=endshow";
                        break;
                    case presentation::ClickAction_NEXTPAGE:
                        sPPAction = "ppaction://hlinkshowjump?jump=nextslide";
                        break;
                    case presentation::ClickAction_LASTPAGE:
                        sPPAction = "ppaction://hlinkshowjump?jump=lastslide";
                        break;
                    case presentation::ClickAction_PREVPAGE:
                        sPPAction = "ppaction://hlinkshowjump?jump=previousslide";
                        break;
                    case presentation::ClickAction_FIRSTPAGE:
                        sPPAction = "ppaction://hlinkshowjump?jump=firstslide";
                        break;
                    case presentation::ClickAction_BOOKMARK:
                        sBookmark = "#" + sBookmark;
                        break;
                    default:
                        break;
                }
            }
            if (!sPPAction.isEmpty())
                pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), "", XML_action,
                                     sPPAction);
        }
        if (!sBookmark.isEmpty())
        {
            bool bExtURL = URLTransformer().isExternalURL(sBookmark);
            sBookmark = bExtURL ? sBookmark : lcl_GetTarget(GetFB()->getModel(), sBookmark);

            OUString sRelId
                = mpFB->addRelation(mpFS->getOutputStream(),
                                    bExtURL ? oox::getRelationship(Relationship::HYPERLINK)
                                            : oox::getRelationship(Relationship::SLIDE),
                                    sBookmark, bExtURL);
            if (bExtURL)
                mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
            else
                mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId,
                                      XML_action, "ppaction://hlinksldjump");
        }
        AddExtLst(pFS, rXPropSet);
        pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
        pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
        WriteNonVisualProperties( xShape );
        pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
    }
    else
    {
        pFS->startElementNS(mnXmlNamespace, XML_wsp);
        if (m_xParent.is())
        {
            pFS->startElementNS(mnXmlNamespace, XML_cNvPr, XML_id,
                                OString::number(GetShapeID(xShape) == -1 ? GetNewShapeID(xShape)
                                                                         : GetShapeID(xShape)),
                                XML_name, GetShapeName(xShape));

            if (GetProperty(rXPropSet, u"Hyperlink"_ustr))
            {
                OUString sURL;
                mAny >>= sURL;
                if (!sURL.isEmpty())
                {
                    OUString sRelId = mpFB->addRelation(
                        mpFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK),
                        mpURLTransformer->getTransformedString(sURL),
                        mpURLTransformer->isExternalURL(sURL));

                    mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
                }
            }
            AddExtLst(pFS, rXPropSet);
            pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
        }
        pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
    }

    // visual shape properties
    pFS->startElementNS(mnXmlNamespace, XML_spPr);

    // we export non-primitive shapes to custom geometry
    // we also export non-ooxml shapes which have handles/equations to custom geometry, because
    // we cannot convert ODF equations to DrawingML equations. TODO: see what binary DOC export filter does.
    // but our WritePolyPolygon()/WriteCustomGeometry() functions are incomplete, therefore we use a denylist
    // we use a allowlist for shapes where mapping to MSO preset shape is not optimal
    bool bCustGeom = true;
    bool bOnDenylist = false;
    if( sShapeType == "ooxml-non-primitive" )
        bCustGeom = true;
    else if( sShapeType.startsWith("ooxml") )
        bCustGeom = false;
    else if( lcl_IsOnAllowlist(sShapeType) )
        bCustGeom = true;
    else if( lcl_IsOnDenylist(sShapeType) )
    {
        bCustGeom = false;
        bOnDenylist = true;
    }

    bool bPresetWriteSuccessful = false;
    // Let the custom shapes what has name and preset information in OOXML, to be written
    // as preset ones with parameters. Try that with this converter class.
    if (!sShapeType.startsWith("ooxml") && sShapeType != "non-primitive" && !mbUserShapes
        && xShape->getShapeType() == "com.sun.star.drawing.CustomShape"
        && !lcl_IsOnAllowlist(sShapeType))
    {
        DMLPresetShapeExporter aCustomShapeConverter(this, xShape);
        bPresetWriteSuccessful = aCustomShapeConverter.WriteShape();
    }
    // If preset writing has problems try to write the shape as it done before
    if (bPresetWriteSuccessful)
        ;// Already written do nothing.
    else if (bCustGeom)
    {
        WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
        bool bSuccess = WriteCustomGeometry(xShape, rSdrObjCustomShape);
        // In case of Writer, the parent element is <wps:spPr>, and there the <a:custGeom> element
        // is not optional.
        if (!bSuccess && GetDocumentType() == DOCUMENT_DOCX)
        {
            WriteEmptyCustomGeometry();
        }
    }
    else if (bOnDenylist && bHasHandles && nAdjustmentValuesIndex !=-1 && !sShapeType.startsWith("mso-spt"))
    {
        WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
        Sequence< EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq;
        std::vector< std::pair< sal_Int32, sal_Int32> > aHandlePositionList;
        std::vector< std::pair< sal_Int32, sal_Int32> > aAvList;
        aGeometrySeq[ nAdjustmentValuesIndex ].Value >>= aAdjustmentSeq ;

        lcl_AnalyzeHandles( aHandles, aHandlePositionList, aAdjustmentSeq );

        sal_Int32 nXPosition = 0;
        sal_Int32 nYPosition = 0;
        if ( !aHandlePositionList.empty() )
        {
            nXPosition = aHandlePositionList[0].first ;
            nYPosition = aHandlePositionList[0].second ;
        }
        switch( eShapeType )
        {
            case mso_sptBorderCallout1:
            {
                sal_Int32 adj3 =  double(nYPosition)/aViewBox.Height *100000;
                sal_Int32 adj4 =  double(nXPosition)/aViewBox.Width *100000;
                lcl_AppendAdjustmentValue( aAvList, 1, 18750 );
                lcl_AppendAdjustmentValue( aAvList, 2, -8333 );
                lcl_AppendAdjustmentValue( aAvList, 3, adj3 );
                lcl_AppendAdjustmentValue( aAvList, 4, adj4 );
                break;
            }
            case mso_sptBorderCallout2:
            {
                sal_Int32 adj5 =  double(nYPosition)/aViewBox.Height *100000;
                sal_Int32 adj6 =  double(nXPosition)/aViewBox.Width *100000;
                sal_Int32 adj3 =  18750;
                sal_Int32 adj4 =  -16667;
                lcl_AppendAdjustmentValue( aAvList, 1, 18750 );
                lcl_AppendAdjustmentValue( aAvList, 2, -8333 );
                if ( aHandlePositionList.size() > 1 )
                {
                    nXPosition = aHandlePositionList[1].first ;
                    nYPosition = aHandlePositionList[1].second ;
                    adj3 =  double(nYPosition)/aViewBox.Height *100000;
                    adj4 =  double(nXPosition)/aViewBox.Width *100000;
                }
                lcl_AppendAdjustmentValue( aAvList, 3, adj3 );
                lcl_AppendAdjustmentValue( aAvList, 4, adj4 );
                lcl_AppendAdjustmentValue( aAvList, 5, adj5 );
                lcl_AppendAdjustmentValue( aAvList, 6, adj6 );
                break;
            }
            case mso_sptWedgeRectCallout:
            case mso_sptWedgeRRectCallout:
            case mso_sptWedgeEllipseCallout:
            case mso_sptCloudCallout:
            {
                sal_Int32 adj1 =  (double(nXPosition)/aViewBox.Width -0.5) *100000;
                sal_Int32 adj2 =  (double(nYPosition)/aViewBox.Height -0.5) *100000;
                lcl_AppendAdjustmentValue( aAvList, 1, adj1 );
                lcl_AppendAdjustmentValue( aAvList, 2, adj2 );
                if ( eShapeType == mso_sptWedgeRRectCallout)
                {
                    lcl_AppendAdjustmentValue( aAvList, 3, 16667);
                }

                break;
            }
            case mso_sptFoldedCorner:
            {
                sal_Int32 adj =  double( aViewBox.Width - nXPosition) / std::min( aViewBox.Width,aViewBox.Height ) * 100000;
                lcl_AppendAdjustmentValue( aAvList, 0, adj );
                break;
            }
            case mso_sptDonut:
            case mso_sptSun:
            case mso_sptMoon:
            case mso_sptNoSmoking:
            case mso_sptHorizontalScroll:
            case mso_sptBevel:
            case mso_sptBracketPair:
            {
                sal_Int32 adj =  double( nXPosition )/aViewBox.Width*100000 ;
                lcl_AppendAdjustmentValue( aAvList, 0, adj );
                break;
            }
            case mso_sptCan:
            case mso_sptCube:
            case mso_sptBracePair:
            case mso_sptVerticalScroll:
            {
                sal_Int32 adj =  double( nYPosition )/aViewBox.Height *100000 ;
                lcl_AppendAdjustmentValue( aAvList, 0, adj );
                break;
            }
            case mso_sptSmileyFace:
            {
                sal_Int32 adj =  double( nYPosition )/aViewBox.Height *100000 - 76458.0;
                lcl_AppendAdjustmentValue( aAvList, 0, adj );
                break;
            }
            case mso_sptBlockArc:
            {
                sal_Int32 nRadius = 50000 * ( 1 - double(nXPosition) / 10800);
                sal_Int32 nAngleStart = lcl_NormalizeAngle( nYPosition );
                sal_Int32 nAngleEnd = lcl_NormalizeAngle( 180 - nAngleStart );
                lcl_AppendAdjustmentValue( aAvList, 1, 21600000 / 360 * nAngleStart );
                lcl_AppendAdjustmentValue( aAvList, 2, 21600000 / 360 * nAngleEnd );
                lcl_AppendAdjustmentValue( aAvList, 3, nRadius );
                break;
            }
            // case mso_sptNil:
            // case mso_sptBentConnector3:
            // case mso_sptBorderCallout3:
            default:
            {
                if ( sPresetShape == "frame" )
                {
                    sal_Int32 adj1 =  double( nYPosition )/aViewBox.Height *100000 ;
                    lcl_AppendAdjustmentValue( aAvList, 1, adj1 );
                }
                break;
            }
        }
        WritePresetShape( sPresetShape  , aAvList );
    }
    else // preset geometry
    {
        WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
        if( nAdjustmentValuesIndex != -1 )
        {
            WritePresetShape( sPresetShape, eShapeType, bPredefinedHandlesUsed,
                              aGeometrySeq[ nAdjustmentValuesIndex ] );
        }
        else
            WritePresetShape( sPresetShape );
    }
    if( rXPropSet.is() )
    {
        WriteFill(rXPropSet, xShape->getSize());
        WriteOutline( rXPropSet );
        WriteShapeEffects( rXPropSet );

        bool bHas3DEffectinShape = false;
        uno::Sequence<beans::PropertyValue> grabBag;
        rXPropSet->getPropertyValue(u"InteropGrabBag"_ustr) >>= grabBag;

        for (auto const& it : grabBag)
            if (it.Name == "3DEffectProperties")
                bHas3DEffectinShape = true;

        if( bHas3DEffectinShape)
            Write3DEffects( rXPropSet, /*bIsText=*/false );
    }

    pFS->endElementNS( mnXmlNamespace, XML_spPr );

    pFS->startElementNS(mnXmlNamespace, XML_style);
    WriteShapeStyle( rXPropSet );
    pFS->endElementNS( mnXmlNamespace, XML_style );

    // write text
    WriteTextBox( xShape, mnXmlNamespace );

    pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp) );

    return *this;
}

ShapeExport& ShapeExport::WriteEllipseShape( const Reference< XShape >& xShape )
{
    SAL_INFO("oox.shape""write ellipse shape");

    FSHelperPtr pFS = GetFS();

    pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp));

    // TODO: connector ?

    Reference<XPropertySet> const xProps(xShape, UNO_QUERY);
    // non visual shape properties
    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
    {
        pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
        pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
                XML_id, OString::number(GetNewShapeID(xShape)),
                XML_name, GetShapeName(xShape));
        AddExtLst(pFS, xProps);
        pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
        pFS->singleElementNS( mnXmlNamespace, XML_cNvSpPr );
        WriteNonVisualProperties( xShape );
        pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
    }
    else
        pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);

    CircleKind  eCircleKind(CircleKind_FULL);
    if (xProps.is())
        xProps->getPropertyValue(u"CircleKind"_ustr ) >>= eCircleKind;

    // visual shape properties
    pFS->startElementNS( mnXmlNamespace, XML_spPr );
    WriteShapeTransformation( xShape, XML_a );

    if (CircleKind_FULL == eCircleKind)
        WritePresetShape("ellipse"_ostr);
    else
    {
        sal_Int32 nStartAngleIntern(9000);
        sal_Int32 nEndAngleIntern(0);
        if (xProps.is())
        {
           xProps->getPropertyValue(u"CircleStartAngle"_ustr ) >>= nStartAngleIntern;
           xProps->getPropertyValue(u"CircleEndAngle"_ustr) >>= nEndAngleIntern;
        }
        std::vector< std::pair<sal_Int32,sal_Int32>> aAvList;
        awt::Size aSize = xShape->getSize();
        if (aSize.Width != 0 || aSize.Height != 0)
        {
            // Our arc has 90° up, OOXML has 90° down, so mirror it.
            // API angles are 1/100 degree.
            sal_Int32 nStartAngleOOXML(lcl_CircleAngle2CustomShapeEllipseAngleOOX(36000 - nEndAngleIntern, aSize.Width, aSize.Height));
            sal_Int32 nEndAngleOOXML(lcl_CircleAngle2CustomShapeEllipseAngleOOX(36000 - nStartAngleIntern, aSize.Width, aSize.Height));
            lcl_AppendAdjustmentValue( aAvList, 1, nStartAngleOOXML);
            lcl_AppendAdjustmentValue( aAvList, 2, nEndAngleOOXML);
        }
        switch (eCircleKind)
        {
            case CircleKind_ARC :
                WritePresetShape("arc"_ostr, aAvList);
            break;
            case CircleKind_SECTION :
                WritePresetShape("pie"_ostr, aAvList);
            break;
            case CircleKind_CUT :
                WritePresetShape("chord"_ostr, aAvList);
            break;
        default :
            WritePresetShape("ellipse"_ostr);
        }
    }
    if( xProps.is() )
    {
        if (CircleKind_ARC == eCircleKind)
        {
            // An arc in ODF is never filled, even if a fill style other than
            // "none" is set. OOXML arc can be filled, so set fill explicit to
            // NONE, otherwise some hidden or inherited filling is shown.
            FillStyle eFillStyle(FillStyle_NONE);
            uno::Any aNewValue;
            aNewValue <<= eFillStyle;
            xProps->setPropertyValue(u"FillStyle"_ustr, aNewValue);
        }
        WriteFill( xProps );
        WriteOutline( xProps );
    }
    pFS->endElementNS( mnXmlNamespace, XML_spPr );

    // write text
    WriteTextBox( xShape, mnXmlNamespace );

    pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp) );

    return *this;
}

ShapeExport& ShapeExport::WriteGraphicObjectShape( const Reference< XShape >& xShape )
{
    WriteGraphicObjectShapePart( xShape );

    return *this;
}

void ShapeExport::WriteGraphicObjectShapePart( const Reference< XShape >& xShape, const Graphic* pGraphic )
{
    SAL_INFO("oox.shape""write graphic object shape");

    if (IsNonEmptySimpleText(xShape))
    {
        SAL_INFO("oox.shape""graphicObject: wrote only text");

        WriteTextShape(xShape);

        return;
    }

    SAL_INFO("oox.shape""graphicObject without text");

    uno::Reference<graphic::XGraphic> xGraphic;
    OUString sMediaURL;

    Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );

    if (pGraphic)
    {
        xGraphic.set(pGraphic->GetXGraphic());
    }
    else if (xShapeProps.is() && xShapeProps->getPropertySetInfo()->hasPropertyByName(u"Graphic"_ustr))
    {
        xShapeProps->getPropertyValue(u"Graphic"_ustr) >>= xGraphic;
    }

    // tdf#155903 Only for PPTX, Microsoft does not support this feature in Word and Excel.
    bool bHasMediaURL = GetDocumentType() == DOCUMENT_PPTX && xShapeProps.is()
                        && xShapeProps->getPropertySetInfo()->hasPropertyByName(u"MediaURL"_ustr)
                        && (xShapeProps->getPropertyValue(u"MediaURL"_ustr) >>= sMediaURL);

    if (!xGraphic.is() && !bHasMediaURL)
    {
        SAL_INFO("oox.shape""no graphic or media URL found");
        return;
    }

    FSHelperPtr pFS = GetFS();
    XmlFilterBase* pFB = GetFB();

    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
        pFS->startElementNS(mnXmlNamespace, XML_pic);
    else
        pFS->startElementNS(mnXmlNamespace, XML_pic,
            FSNS(XML_xmlns, XML_pic), pFB->getNamespaceURL(OOX_NS(dmlPicture)));

    pFS->startElementNS(mnXmlNamespace, XML_nvPicPr);

    presentation::ClickAction eClickAction = presentation::ClickAction_NONE;
    OUString sDescr, sURL, sBookmark, sPPAction;
    bool bHaveDesc;

    if ( ( bHaveDesc = GetProperty( xShapeProps, u"Description"_ustr ) ) )
        mAny >>= sDescr;
    if ( GetProperty( xShapeProps, u"URL"_ustr ) )
        mAny >>= sURL;
    if (GetProperty(xShapeProps, u"Bookmark"_ustr))
        mAny >>= sBookmark;
    if (GetProperty(xShapeProps, u"OnClick"_ustr))
        mAny >>= eClickAction;

    pFS->startElementNS( mnXmlNamespace, XML_cNvPr,
                          XML_id,     OString::number(GetNewShapeID(xShape)),
                          XML_name,   GetShapeName(xShape),
                          XML_descr,  sax_fastparser::UseIf(sDescr, bHaveDesc));

    if (eClickAction != presentation::ClickAction_NONE)
    {
        switch (eClickAction)
        {
            case presentation::ClickAction_STOPPRESENTATION:
                sPPAction = "ppaction://hlinkshowjump?jump=endshow";
                break;
            case presentation::ClickAction_NEXTPAGE:
                sPPAction = "ppaction://hlinkshowjump?jump=nextslide";
                break;
            case presentation::ClickAction_LASTPAGE:
                sPPAction = "ppaction://hlinkshowjump?jump=lastslide";
                break;
            case presentation::ClickAction_PREVPAGE:
                sPPAction = "ppaction://hlinkshowjump?jump=previousslide";
                break;
            case presentation::ClickAction_FIRSTPAGE:
                sPPAction = "ppaction://hlinkshowjump?jump=firstslide";
                break;
            case presentation::ClickAction_BOOKMARK:
                sBookmark = "#" + sBookmark;
                break;
            default:
                break;
        }
    }

    // OOXTODO: //cNvPr children: XML_extLst, XML_hlinkHover
    if (bHasMediaURL || !sPPAction.isEmpty())
        pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), "", XML_action,
                             bHasMediaURL ? u"ppaction://media"_ustr : sPPAction);
    if( !sURL.isEmpty() )
    {
        OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
                oox::getRelationship(Relationship::HYPERLINK),
                mpURLTransformer->getTransformedString(sURL),
                mpURLTransformer->isExternalURL(sURL));

        mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
    }

    if (!sBookmark.isEmpty())
    {
        bool bExtURL = URLTransformer().isExternalURL(sBookmark);
        sBookmark = bExtURL ? sBookmark : lcl_GetTarget(GetFB()->getModel(), sBookmark);

        OUString sRelId = mpFB->addRelation(mpFS->getOutputStream(),
                                            bExtURL ? oox::getRelationship(Relationship::HYPERLINK)
                                                    : oox::getRelationship(Relationship::SLIDE),
                                            sBookmark, bExtURL);

        if (bExtURL)
            mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
        else
            mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId, XML_action,
                                  "ppaction://hlinksldjump");
    }
    AddExtLst(pFS, xShapeProps);
    pFS->endElementNS(mnXmlNamespace, XML_cNvPr);

    pFS->singleElementNS(mnXmlNamespace, XML_cNvPicPr
                         // OOXTODO: XML_preferRelativeSize
                        );
    if (bHasMediaURL)
        WriteMediaNonVisualProperties(xShape);
    else
        WriteNonVisualProperties(xShape);

    pFS->endElementNS( mnXmlNamespace, XML_nvPicPr );

    pFS->startElementNS(mnXmlNamespace, XML_blipFill);

    if (xGraphic.is())
    {
        WriteXGraphicBlip(xShapeProps, xGraphic, mbUserShapes);
    }
    else if (bHasMediaURL)
    {
        Reference<graphic::XGraphic> xFallbackGraphic;
        if (xShapeProps->getPropertySetInfo()->hasPropertyByName(u"FallbackGraphic"_ustr))
            xShapeProps->getPropertyValue(u"FallbackGraphic"_ustr) >>= xFallbackGraphic;

        WriteXGraphicBlip(xShapeProps, xFallbackGraphic, mbUserShapes);
    }

    if (xGraphic.is())
    {
        WriteSrcRectXGraphic(xShapeProps, xGraphic);
    }

    // now we stretch always when we get pGraphic (when changing that
    // behavior, test n#780830 for regression, where the OLE sheet might get tiled
    bool bStretch = false;
    if( !pGraphic && GetProperty( xShapeProps, u"FillBitmapStretch"_ustr ) )
        mAny >>= bStretch;

    if ( pGraphic || bStretch )
        pFS->singleElementNS(XML_a, XML_stretch);

    if (bHasMediaURL)
    {
        // Graphic of media shapes is always stretched.
        pFS->startElementNS(XML_a, XML_stretch);
        pFS->singleElementNS(XML_a, XML_fillRect);
        pFS->endElementNS(XML_a, XML_stretch);
    }

    pFS->endElementNS( mnXmlNamespace, XML_blipFill );

    // visual shape properties
    pFS->startElementNS(mnXmlNamespace, XML_spPr);
    bool bFlipH = false;
    if( xShapeProps->getPropertySetInfo()->hasPropertyByName(u"IsMirrored"_ustr) )
    {
        xShapeProps->getPropertyValue(u"IsMirrored"_ustr) >>= bFlipH;
    }
    WriteShapeTransformation( xShape, XML_a, bFlipH, falsefalsefalsetrue );
    WritePresetShape( "rect"_ostr );
    WriteFill(xShapeProps);
    // graphic object can come with the frame (bnc#654525)
    WriteOutline( xShapeProps );

    WriteShapeEffects( xShapeProps );
    Write3DEffects( xShapeProps, /*bIsText=*/false );

    pFS->endElementNS( mnXmlNamespace, XML_spPr );

    pFS->endElementNS( mnXmlNamespace, XML_pic );
}

static void lcl_Rotate(sal_Int32 nAngle, Point center, awt::Point& pt)
{
    sal_Int16 nCos, nSin;
    switch (nAngle)
    {
        case 90:
            nCos = 0;
            nSin = 1;
            break;
        case 180:
            nCos = -1;
            nSin = 0;
            break;
        case 270:
            nCos = 0;
            nSin = -1;
            break;
        default:
            return;
    }
    sal_Int32 x = pt.X - center.X();
    sal_Int32 y = pt.Y - center.Y();
    pt.X = center.X() + x * nCos - y * nSin;
    pt.Y = center.Y() + y * nCos + x * nSin;
}

static void lcl_FlipHFlipV(const tools::Polygon& rPoly, sal_Int32 nAngle, boolrFlipH, bool& rFlipV)
{
    Point aStart = rPoly[0];
    Point aEnd = rPoly[rPoly.GetSize() - 1];

    if (aStart.X() > aEnd.X() && aStart.Y() > aEnd.Y())
    {
        if (nAngle)
        {
            if (nAngle == 90)
                rFlipH = true;
            if (nAngle == 270)
                rFlipV = true;
        }
        else // 0°
        {
            rFlipH = true;
            rFlipV = true;
        }
    }

    if (aStart.X() < aEnd.X() && aStart.Y() < aEnd.Y())
    {
        if (nAngle)
        {
            if (nAngle != 270)
            {
                rFlipH = true;
                rFlipV = true;
            }
            else
                rFlipH = true;
        }
    }

    if (aStart.Y() < aEnd.Y() && aStart.X() > aEnd.X())
    {
        if (nAngle)
        {
            if (nAngle == 180)
                rFlipV = true;
            if (nAngle == 270)
            {
                rFlipV = true;
                rFlipH = true;
            }
        }
        else // 0°
        {
            rFlipH = true;
        }
    }

    if (aStart.Y() > aEnd.Y() && aStart.X() < aEnd.X())
    {
        if (nAngle)
        {
            if (nAngle == 90)
            {
                rFlipH = true;
                rFlipV = true;
            }
            if (nAngle == 180)
                rFlipH = true;
        }
        else // 0°
            rFlipV = true;
    }
}

static sal_Int32 lcl_GetAngle(const tools::Polygon& rPoly)
{
    sal_Int32 nAngle;
    Point aStartPoint = rPoly[0];
    Point aEndPoint = rPoly[rPoly.GetSize() - 1];
    if (aStartPoint.X() == rPoly[1].X())
    {
        if ((aStartPoint.X() < aEndPoint.X() && aStartPoint.Y() > aEndPoint.Y())
            || (aStartPoint.X() > aEndPoint.X() && aStartPoint.Y() < aEndPoint.Y()))
        {
            nAngle = 90;
        }
        else
            nAngle = 270;
    }
    else
    {
        if (aStartPoint.X() > rPoly[1].X())
            nAngle = 180;
        else
            nAngle = 0;
    }

    return nAngle;
}

// Adjust value decide the position, where the connector should turn.
static void lcl_GetConnectorAdjustValue(const Reference<XShape>& xShape, const tools::Polygon& rPoly,
                                        ConnectorType eConnectorType,
                                        std::vector<std::pair<sal_Int32, sal_Int32>>& rAvList)
{
    Reference<XPropertySet> xShapeProps(xShape, UNO_QUERY);
    bool bIsOOXMLCurve(false);
    xShapeProps->getPropertyValue(u"EdgeOOXMLCurve"_ustr) >>= bIsOOXMLCurve;
    sal_Int32 nAdjCount = 0;
    if (eConnectorType == ConnectorType_CURVE)
    {
        if (bIsOOXMLCurve)
        {
            nAdjCount = (rPoly.GetSize() - 4) / 3;
        }
        else if (rPoly.GetSize() == 4)
        {
            if ((rPoly[0].X() == rPoly[1].X() && rPoly[2].X() == rPoly[3].X())
                || (rPoly[0].Y() == rPoly[1].Y() && rPoly[2].Y() == rPoly[3].Y()))
            {
                nAdjCount = 1; // curvedConnector3, control vectors parallel
            }
            else
                nAdjCount = 0; // curvedConnector2, control vectors orthogonal
        }
        else if (rPoly.GetSize() > 4)
        {
            if ((rPoly[2].X() == rPoly[3].X() && rPoly[3].X() == rPoly[4].X())
                || (rPoly[2].Y() == rPoly[3].Y() && rPoly[3].Y() == rPoly[4].Y()))
            {
                nAdjCount = 3; // curvedConnector5
            }
            else
                nAdjCount = 2; // curvedConnector4
        }
    }
    else
    {
        switch (rPoly.GetSize())
        {
            case 3:
                nAdjCount = 0; // bentConnector2
                break;
            case 4:
                nAdjCount = 1; // bentConnector3
                break;
            case 5:
                nAdjCount = 2; // bentConnector4
                break;
            case 6:
                nAdjCount = 3; // bentConnector5
                break;
        }
    }

    if (nAdjCount)
    {
        sal_Int32 nAdjustValue;
        Point aStart = rPoly[0];
        Point aEnd = rPoly[rPoly.GetSize() - 1];

        for (sal_Int32 i = 1; i <= nAdjCount; ++i)
        {
            Point aPt = rPoly[i];

            if (aEnd.Y() == aStart.Y())
                aEnd.setY(aStart.Y() + 1);
            if (aEnd.X() == aStart.X())
                aEnd.setX(aStart.X() + 1);

            bool bVertical = rPoly[1].X() - aStart.X() != 0 ? true : false;
            // vertical and horizon alternate
            if (i % 2 == 1)
                bVertical = !bVertical;

            if (eConnectorType == ConnectorType_CURVE)
            {
                if (bIsOOXMLCurve)
                {
                    aPt = rPoly[3 *  i];
                }
                else
                {
                    awt::Size aSize = xShape->getSize();
                    awt::Point aShapePosition = xShape->getPosition();
                    tools::Rectangle aBoundRect = rPoly.GetBoundRect();

                    if (bVertical)
                    {
                        if ((aBoundRect.GetSize().Height() - aSize.Height) == 1)
                            aPt.setY(rPoly[i + 1].Y());
                        else if (aStart.Y() > aPt.Y())
                            aPt.setY(aShapePosition.Y);
                        else
                            aPt.setY(aShapePosition.Y + aSize.Height);
                    }
                    else
                    {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=96 G=95

¤ Dauer der Verarbeitung: 0.24 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.