/* -*- 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 <stdio.h>
#include <oox/drawingml/clrscheme.hxx>
#include <oox/token/namespaces.hxx>
#include <oox/token/tokens.hxx>
#include <oox/token/relationship.hxx>
#include <oox/ole/vbaproject.hxx>
#include "epptooxml.hxx"
#include <oox/export/shapes.hxx>
#include <svx/svdlayer.hxx>
#include <xmloff/autolayout.hxx>
#include <unokywds.hxx>
#include <osl/file.hxx>
 
#include <editeng/fontitem.hxx>
#include <editeng/eeitem.hxx>
#include <drawdoc.hxx>
#include <svl/itempool.hxx>
#include <editeng/section.hxx>
#include <editeng/editeng.hxx>
 
#include <comphelper/sequenceashashmap.hxx>
#include <comphelper/storagehelper.hxx>
#include <comphelper/string.hxx>
#include <comphelper/xmltools.hxx>
#include <sax/fshelper.hxx>
#include <sax/fastattribs.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <tools/UnitConversion.hxx>
#include <tools/urlobj.hxx>
#include <tools/datetime.hxx>
#include <unotools/securityoptions.hxx>
#include <com/sun/star/animations/TransitionType.hpp>
#include <com/sun/star/animations/TransitionSubType.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/drawing/FillStyle.hpp>
#include <com/sun/star/drawing/XDrawPages.hpp>
#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
#include <com/sun/star/drawing/XMasterPageTarget.hpp>
#include <com/sun/star/drawing/XMasterPagesSupplier.hpp>
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/geometry/RealPoint2D.hpp>
#include <com/sun/star/office/XAnnotationEnumeration.hpp>
#include <com/sun/star/office/XAnnotationAccess.hpp>
#include <com/sun/star/presentation/AnimationSpeed.hpp>
#include <com/sun/star/util/DateTime.hpp>
#include <com/sun/star/task/XStatusIndicator.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/presentation/XCustomPresentationSupplier.hpp>
#include <com/sun/star/container/XIndexContainer.hpp>
#include <com/sun/star/container/XNamed.hpp>
#include <com/sun/star/presentation/XPresentationSupplier.hpp>
#include <comphelper/diagnose_ex.hxx>
#include <comphelper/hash.hxx>
#include <vcl/embeddedfontsmanager.hxx>
 
#include <oox/export/utils.hxx>
#include <oox/export/ThemeExport.hxx>
#include <docmodel/theme/Theme.hxx>
#include <ModelTraverser.hxx>
 
#include "pptx-animations.hxx"
#include "../ppt/pptanimations.hxx"
 
#include <i18nlangtag/languagetag.hxx>
#include <svx/sdrmasterpagedescriptor.hxx>
#include <svx/svdpage.hxx>
#include <svx/unoapi.hxx>
#include <svx/svdogrp.hxx>
#include <svx/ColorSets.hxx>
#include <svx/diagram/IDiagramHelper.hxx>
#include <sdmod.hxx>
#include <sdpage.hxx>
#include <unomodel.hxx>
 
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/font/EOTConverter.hxx>
 
#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
#include <com/sun/star/document/XStorageBasedDocument.hpp>
#include <utility>
#include <unordered_map>
#include <unordered_set>
 
#if OSL_DEBUG_LEVEL > 1
#include <com/sun/star/drawing/RectanglePoint.hpp>
#endif
 
using namespace sax_fastparser;
using namespace ::com::sun::star;
using namespace ::com::sun::star::animations;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::drawing;
using namespace ::com::sun::star::geometry;
using namespace ::com::sun::star::presentation;
using namespace ::com::sun::star::office;
using namespace ::com::sun::star::text;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;
using namespace ::ppt;
using ::com::sun::star::beans::XPropertySet;
using ::com::sun::star::beans::XPropertySetInfo;
using ::sax_fastparser::FSHelperPtr;
using namespace oox::drawingml;
using namespace oox::core;
using namespace oox;
 
namespace
{
rtl::Reference<FastAttributeList> presentationNamespaces(oox::core::XmlFilterBase& rFilter)
{
    rtl::Reference<FastAttributeList> pAttributes = FastSerializerHelper::createAttrList();
    pAttributes->add(FSNS(XML_xmlns, XML_a), OUStringToOString(rFilter.getNamespaceURL(OOX_NS(dml)), RTL_TEXTENCODING_UTF8));
    pAttributes->add(FSNS(XML_xmlns, XML_p), OUStringToOString(rFilter.getNamespaceURL(OOX_NS(ppt)), RTL_TEXTENCODING_UTF8));
    pAttributes->add(FSNS(XML_xmlns, XML_r), OUStringToOString(rFilter.getNamespaceURL(OOX_NS(officeRel)), RTL_TEXTENCODING_UTF8));
    pAttributes->add(FSNS(XML_xmlns, XML_p14), OUStringToOString(rFilter.getNamespaceURL(OOX_NS(p14)), RTL_TEXTENCODING_UTF8));
    pAttributes->add(FSNS(XML_xmlns, XML_p15), OUStringToOString(rFilter.getNamespaceURL(OOX_NS(p15)), RTL_TEXTENCODING_UTF8));
    pAttributes->add(FSNS(XML_xmlns, XML_mc), OUStringToOString(rFilter.getNamespaceURL(OOX_NS(mce)), RTL_TEXTENCODING_UTF8));
    return pAttributes;
}
 
rtl::Reference<FastAttributeList> presentationPrNamespaces(oox::core::XmlFilterBase& rFilter)
{
    rtl::Reference<FastAttributeList> pAttributes = FastSerializerHelper::createAttrList();
    pAttributes->add(FSNS(XML_xmlns, XML_a), OUStringToOString(rFilter.getNamespaceURL(OOX_NS(dml)), RTL_TEXTENCODING_UTF8));
    pAttributes->add(FSNS(XML_xmlns, XML_p), OUStringToOString(rFilter.getNamespaceURL(OOX_NS(ppt)), RTL_TEXTENCODING_UTF8));
    pAttributes->add(FSNS(XML_xmlns, XML_r), OUStringToOString(rFilter.getNamespaceURL(OOX_NS(officeRel)), RTL_TEXTENCODING_UTF8));
    return pAttributes;
}
};
 
#if OSL_DEBUG_LEVEL > 1
void dump_pset(Reference< XPropertySet > const& rXPropSet);
#endif
 
namespace oox::core
{
 
class PowerPointShapeExport : public ShapeExport
{
    PowerPointExport&   mrExport;
    PageType            mePageType;
    bool                mbMaster;
public:
    PowerPointShapeExport(FSHelperPtr pFS, ShapeHashMap* pShapeMap, PowerPointExport* pFB);
    void                SetMaster(bool bMaster);
    void                SetPageType(PageType ePageType);
    ShapeExport&        WriteNonVisualProperties(const Reference< XShape >& xShape) override;
    ShapeExport&        WriteTextShape(const Reference< XShape >& xShape) override;
    ShapeExport&        WriteUnknownShape(const Reference< XShape >& xShape) override;
    ShapeExport&        WritePlaceholderShape(const Reference< XShape >& xShape, PlaceholderType ePlaceholder);
    /** Writes a placeholder shape that references the placeholder on the master slide */
    ShapeExport&        WritePlaceholderReferenceShape(PlaceholderType ePlaceholder, sal_Int32 nReferencedPlaceholderIdx, PageType ePageType, const Reference<XPropertySet>& rXPagePropSet);
    ShapeExport&        WritePageShape(const Reference< XShape >& xShape, PageType ePageType, bool bPresObj);
    /** Writes textbody of a placeholder that references the placeholder on the master slide */
    ShapeExport&        WritePlaceholderReferenceTextBody(PlaceholderType ePlaceholder, PageType ePageType, const Reference<XPropertySet>& xPagePropSet);
 
    // helper parts
    bool WritePlaceholder(const Reference< XShape >& xShape, PlaceholderType ePlaceholder, bool bMaster);
};
 
 
namespace
{
void WriteSndAc(const FSHelperPtr& pFS, const OUString& sSoundRelId, const OUString& sSoundName)
{
        pFS->startElementNS(XML_p, XML_sndAc);
        pFS->startElementNS(XML_p, XML_stSnd);
        pFS->singleElementNS(XML_p, XML_snd, FSNS(XML_r, XML_embed),
                             sax_fastparser::UseIf(sSoundRelId, !sSoundRelId.isEmpty()), XML_name,
                             sax_fastparser::UseIf(sSoundName, !sSoundName.isEmpty()));
        pFS->endElement(FSNS(XML_p, XML_stSnd));
        pFS->endElement(FSNS(XML_p, XML_sndAc));
}
 
const char* getPlaceholderTypeName(PlaceholderType ePlaceholder)
{
    switch (ePlaceholder)
    {
        case SlideImage:
            return "sldImg";
        case Notes:
            return "body";
        case Header:
            return "hdr";
        case Footer:
            return "ftr";
        case SlideNumber:
            return "sldNum";
        case DateAndTime:
            return "dt";
        case Outliner:
            return "body";
        case Title:
            return "title";
        case Subtitle:
            return "subTitle";
        default:
            SAL_INFO("sd.eppt", "warning: unhandled placeholder type: " << ePlaceholder);
            return "";
    }
}
}
}
 
namespace {
 
struct PPTXLayoutInfo
{
    int nType;
    const char* sName;
    const char* sType;
};
 
}
 
// the function 'SlidePersist::getLayoutFromValueToken()'
// likely needs to be updated after any changes here
// unsupported PPTX layouts: txAndMedia, twoColTx, twoTxTwoObj,
//  twoObjAndObj, objTx, picTx, secHead, objOnly, objAndTwoObj,
//  mediaAndTx, dgm, cust
const PPTXLayoutInfo aLayoutInfo[] =
{
    { 0, "Title Slide", "title" },
    { 1, "Title and text", "tx" },
    { 2, "Title and chart", "chart" },
    { 3, "Title, text on left, text on right", "twoObj" },
    { 4, "Title, text on left and chart on right", "txAndChart" },
    { 20, "Blank Slide", "blank" },
    { 6, "Title, text on left, clip art on right", "txAndClipArt" },
    { 7, "Title, chart on left and text on right", "chartAndTx" },
    { 8, "Title and table", "tbl" },
    { 9, "Title, clipart on left, text on right", "clipArtAndTx" },
    { 10, "Title, text on left, object on right", "txAndObj" },
    {  1, "Title and object", "obj" },
    { 12, "Title, text on left, two objects on right", "txAndTwoObj" },
    { 13, "Title, object on left, text on right", "objAndTx" },
    { 14, "Title, object on top, text on bottom", "objOverTx" },
    { 15, "Title, two objects on left, text on right", "twoObjAndTx" },
    { 16, "Title, two objects on top, text on bottom", "twoObjOverTx" },
    { 17, "Title, text on top, object on bottom", "txOverObj" },
    { 18, "Title and four objects", "fourObj" },
    { 19, "Title Only", "titleOnly" },
    { 20, "Blank Slide", "blank" },
    { 20, "Blank Slide", "blank" },
    { 20, "Blank Slide", "blank" },
    { 20, "Blank Slide", "blank" },
    { 20, "Blank Slide", "blank" },
    { 20, "Blank Slide", "blank" },
    { 20, "Blank Slide", "blank" },
    { 27, "Vertical title on right, vertical text on top, chart on bottom", "vertTitleAndTxOverChart" },
    { 28, "Vertical title on right, vertical text on left", "vertTitleAndTx" },
    { 29, "Title and vertical text body", "vertTx" },
    { 30, "Title, clip art on left, vertical text on right", "clipArtAndVertTx" },
    { 20, "Blank Slide", "blank" },
    { 32, "Object only", "objOnly" },
    { 20, "Blank Slide", "blank" },
    { 20, "Blank Slide", "blank" }
};
static_assert(std::size(aLayoutInfo) == AUTOLAYOUT_END,
              "aLayoutInfo must have a corresponding item for each AUTOLAYOUT");
 
PowerPointShapeExport::PowerPointShapeExport(FSHelperPtr pFS, ShapeHashMap* pShapeMap,
        PowerPointExport* pFB)
    : ShapeExport(XML_p, std::move(pFS), pShapeMap, pFB)
    , mrExport(*pFB)
    , mePageType(UNDEFINED)
    , mbMaster(false)
{
}
 
void PowerPointShapeExport::SetMaster(bool bMaster)
{
    mbMaster = bMaster;
}
 
void PowerPointShapeExport::SetPageType(PageType ePageType)
{
    mePageType = ePageType;
}
 
ShapeExport& PowerPointShapeExport::WriteNonVisualProperties(const Reference< XShape >&)
{
    GetFS()->singleElementNS(XML_p, XML_nvPr);
 
    return *this;
}
 
ShapeExport& PowerPointShapeExport::WriteTextShape(const Reference< XShape >& xShape)
{
    OUString sShapeType = xShape->getShapeType();
 
    SAL_INFO("sd.eppt", "shape(text) : " << sShapeType.toUtf8());
 
    if (sShapeType == "com.sun.star.drawing.TextShape" || sShapeType == "com.sun.star.drawing.GraphicObjectShape")
    {
        ShapeExport::WriteTextShape(xShape);
    }
    else if (sShapeType == "com.sun.star.presentation.DateTimeShape")
    {
        if (!WritePlaceholder(xShape, DateAndTime, mbMaster))
            ShapeExport::WriteTextShape(xShape);
    }
    else if (sShapeType == "com.sun.star.presentation.FooterShape")
    {
        if (!WritePlaceholder(xShape, Footer, mbMaster))
            ShapeExport::WriteTextShape(xShape);
    }
    else if (sShapeType == "com.sun.star.presentation.HeaderShape")
    {
        if (!WritePlaceholder(xShape, Header, mbMaster))
            ShapeExport::WriteTextShape(xShape);
    }
    else if (sShapeType == "com.sun.star.presentation.NotesShape")
    {
        if (mePageType == NOTICE && mrExport.GetPresObj())
            WritePlaceholderShape(xShape, Notes);
        else
            ShapeExport::WriteTextShape(xShape);
    }
    else if (sShapeType == "com.sun.star.presentation.OutlinerShape")
    {
        if (!WritePlaceholder(xShape, Outliner, mbMaster))
            ShapeExport::WriteTextShape(xShape);
    }
    else if (sShapeType == "com.sun.star.presentation.SlideNumberShape")
    {
        if (!WritePlaceholder(xShape, SlideNumber, mbMaster))
            ShapeExport::WriteTextShape(xShape);
    }
    else if (sShapeType == "com.sun.star.presentation.TitleTextShape")
    {
        if (!WritePlaceholder(xShape, Title, mbMaster))
            ShapeExport::WriteTextShape(xShape);
    }
    else if (sShapeType == "com.sun.star.presentation.SubtitleShape")
    {
        // TODO: handle subtitle shape: see tdf#112557 workaround
        // MSO does not like subtitles on master slides
        if (mePageType != MASTER)
        {
            if (!WritePlaceholder(xShape, Subtitle, mbMaster))
                ShapeExport::WriteTextShape(xShape);
        }
    }
    else
        SAL_WARN("sd.eppt", "PowerPointShapeExport::WriteTextShape: shape of type '" << sShapeType << "' is ignored");
 
    return *this;
}
 
ShapeExport& PowerPointShapeExport::WriteUnknownShape(const Reference< XShape >& xShape)
{
    OUString sShapeType = xShape->getShapeType();
 
    SAL_INFO("sd.eppt", "shape(unknown): " << sShapeType.toUtf8());
 
    if (sShapeType == "com.sun.star.presentation.PageShape")
    {
        WritePageShape(xShape, mePageType, mrExport.GetPresObj());
    }
    else
        SAL_WARN("sd.eppt", "unknown shape not handled: " << sShapeType.toUtf8());
 
    return *this;
}
 
PowerPointExport::PowerPointExport(const Reference< XComponentContext >& rContext, const uno::Sequence<uno::Any>& rArguments)
    : XmlFilterBase(rContext)
    , mpAuthorIDs( new SvtSecurityMapPersonalInfo )
    , mnLayoutFileIdMax(1)
    , mnSlideIdMax(1 << 8)
    , mnSlideMasterIdMax(1U << 31)
    , mnAnimationNodeIdMax(1)
    , mnThemeIdMax(0)
    , mnDiagramId(1)
    , mbCreateNotes(false)
    , mnPlaceholderIndexMax(1)
{
    comphelper::SequenceAsHashMap aArgumentsMap(rArguments);
    mbPptm = aArgumentsMap.getUnpackedValueOrDefault(u"IsPPTM"_ustr, false);
    mbExportTemplate = aArgumentsMap.getUnpackedValueOrDefault(u"IsTemplate"_ustr, false);
}
 
PowerPointExport::~PowerPointExport()
{
}
 
void PowerPointExport::writeDocumentProperties()
{
    uno::Reference<document::XDocumentProperties> xDocProps = mXModel->getDocumentProperties();
 
    if (xDocProps.is())
    {
        bool bSecurityOptOpenReadOnly = false;
        uno::Reference< beans::XPropertySet > xSettings(mXModel->createInstance(u"com.sun.star.document.Settings"_ustr), uno::UNO_QUERY);
        try
        {
            xSettings->getPropertyValue(u"LoadReadonly"_ustr) >>= bSecurityOptOpenReadOnly;
 
            xSettings->getPropertyValue(u"EmbedFonts"_ustr) >>= mbEmbedFonts;
            xSettings->getPropertyValue(u"EmbedOnlyUsedFonts"_ustr) >>= mbEmbedUsedOnly;
            xSettings->getPropertyValue(u"EmbedLatinScriptFonts"_ustr)  >>= mbEmbedLatinScript;
            xSettings->getPropertyValue(u"EmbedAsianScriptFonts"_ustr)  >>= mbEmbedAsianScript;
            xSettings->getPropertyValue(u"EmbedComplexScriptFonts"_ustr) >>= mbEmbedComplexScript;
        }
        catch( Exception& )
        {
        }
        exportDocumentProperties(xDocProps, bSecurityOptOpenReadOnly);
    }
 
    exportCustomFragments();
}
 
bool PowerPointExport::importDocument() noexcept
{
    return false;
}
 
bool PowerPointExport::exportDocument()
{
    drawingml::DrawingML::ResetMlCounters();
    auto& rGraphicExportCache = drawingml::GraphicExportCache::get();
 
    rGraphicExportCache.push();
 
    maShapeMap.clear();
 
    auto pDoc = dynamic_cast<SdXImpressDocument*>(getModel().get());
    assert(pDoc);
    mXModel = pDoc;
 
    //write document properties
    writeDocumentProperties();
 
    addRelation(oox::getRelationship(Relationship::OFFICEDOCUMENT), u"ppt/presentation.xml");
 
    OUString aMediaType;
    if (mbPptm)
    {
        if (mbExportTemplate)
        {
            aMediaType = "application/vnd.ms-powerpoint.template.macroEnabled.main+xml";
        }
        else
        {
            aMediaType = "application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml";
        }
    }
    else
    {
        if (mbExportTemplate)
        {
            aMediaType = "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml";
        }
        else
        {
            aMediaType = "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml";
        }
    }
 
    mPresentationFS = openFragmentStreamWithSerializer(u"ppt/presentation.xml"_ustr, aMediaType);
 
    addRelation(mPresentationFS->getOutputStream(),
                oox::getRelationship(Relationship::THEME),
                u"theme/theme1.xml");
 
    auto pAttributes = presentationNamespaces(*this);
    if (mbEmbedFonts)
        pAttributes->add(XML_embedTrueTypeFonts, "1");
    mPresentationFS->startElementNS(XML_p, XML_presentation, pAttributes);
 
    mXStatusIndicator = getStatusIndicator();
 
    std::vector< PropertyValue > aProperties;
    PropertyValue aProperty;
    aProperty.Name = "BaseURI";
    aProperty.Value <<= getFileUrl();
    aProperties.push_back(aProperty);
 
    exportPPT(aProperties);
 
    // clamp to minimum values supported by PPTX
    sal_Int64 nDestPageWidth = std::max(sal_Int64(914400), PPTtoEMU(maDestPageSize.Width));
    sal_Int64 nDestPageHeight = std::max(sal_Int64(914400), PPTtoEMU(maDestPageSize.Height));
    mPresentationFS->singleElementNS(XML_p, XML_sldSz,
                                     XML_cx, OString::number(nDestPageWidth),
                                     XML_cy, OString::number(nDestPageHeight));
    // for some reason if added before slides list it will not load the slides (alas with error reports) in mso
    mPresentationFS->singleElementNS(XML_p, XML_notesSz,
                                     XML_cx, OString::number(PPTtoEMU(maNotesPageSize.Width)),
                                     XML_cy, OString::number(PPTtoEMU(maNotesPageSize.Height)));
    WriteEmbeddedFontList();
 
    WriteCustomSlideShow();
 
    WritePresentationProps();
 
    WriteAuthors();
 
    WriteVBA();
 
    WriteModifyVerifier();
 
    mPresentationFS->endElementNS(XML_p, XML_presentation);
    mPresentationFS->endDocument();
    mPresentationFS.reset();
    // Free all FSHelperPtr, to flush data before committing storage
    for (auto& serializer : mpSlidesFSArray)
    {
        if (!serializer)
            continue;
        serializer->endDocument();
    }
    mpSlidesFSArray.clear();
 
    commitStorage();
 
    rGraphicExportCache.pop();
 
    maShapeMap.clear();
    maAuthors.clear();
    maRelId.clear();
 
    return true;
}
 
::oox::ole::VbaProject* PowerPointExport::implCreateVbaProject() const
{
    return new ::oox::ole::VbaProject(getComponentContext(), getModel(), u"Impress");
}
 
namespace
{
struct EmbeddedFont
{
    OUString sFamilyName;
 
    OString aRegularRelID;
    OString aBoldRelID;
    OString aItalicRelID;
    OString aBoldItalicRelID;
};
} // end anonymous namespace
 
namespace
{
 
/** Collects font names of fonts used in the document
 *
 * Uses the model traverser to traverse the document model.
 * Can be parameterized to filter latin, asian or complex scripts.
 */
class FontNameCollector : public sd::ModelTraverseHandler
{
private:
    std::unordered_set<OUString>& mrUsedFontNames;
    bool mbEmbedLatinScript;
    bool mbEmbedAsianScript;
    bool mbEmbedComplexScript;
 
    static const SvxFontItem* getFontItem(const editeng::Section& rSection, sal_uInt16 eFontWhich)
    {
        auto iterator = std::find_if(rSection.maAttributes.begin(), rSection.maAttributes.end(), [eFontWhich] (const SfxPoolItem* pPoolItem) {
            return pPoolItem->Which() == eFontWhich;
        });
 
        if (iterator != rSection.maAttributes.end())
            return static_cast<const SvxFontItem*>(*iterator);
        return nullptr;
    }
 
    void addFontItem(const SvxFontItem* pItem)
    {
        OUString aFontName = pItem->GetFamilyName();
        mrUsedFontNames.insert(aFontName);
    }
 
    void traverseEditEng(SdrTextObj* pTextObject)
    {
        OutlinerParaObject* pOutlinerParagraphObject = pTextObject->GetOutlinerParaObject();
        if (!pOutlinerParagraphObject)
            return;
 
        const EditTextObject& rEditText = pOutlinerParagraphObject->GetTextObject();
        std::vector<editeng::Section> aSections;
        rEditText.GetAllSections(aSections);
 
        {
            SfxStyleSheet* pStyleSheet = pTextObject->getSdrPageFromSdrObject()->GetTextStyleSheetForObject(pTextObject);
            if (pStyleSheet)
            {
                const SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
                if (const SvxFontItem* pItem = rItemSet.GetItemIfSet(EE_CHAR_FONTINFO, false))
                {
                    addFontItem(pItem);
                }
                if (const SvxFontItem* pItem = rItemSet.GetItemIfSet(EE_CHAR_FONTINFO_CJK, false))
                {
                    addFontItem(pItem);
                }
                if (const SvxFontItem* pItem = rItemSet.GetItemIfSet(EE_CHAR_FONTINFO_CTL, false))
                {
                    addFontItem(pItem);
                }
            }
        }
 
        for (editeng::Section const& rSection : aSections)
        {
            if (SvxFontItem const* pFontItem = getFontItem(rSection, EE_CHAR_FONTINFO); pFontItem && mbEmbedLatinScript)
            {
                addFontItem(pFontItem);
            }
            if (SvxFontItem const * pFontItem = getFontItem(rSection, EE_CHAR_FONTINFO_CJK); pFontItem && mbEmbedAsianScript)
            {
                addFontItem(pFontItem);
            }
            if (SvxFontItem const* pFontItem = getFontItem(rSection, EE_CHAR_FONTINFO_CTL); pFontItem && mbEmbedComplexScript)
            {
                addFontItem(pFontItem);
            }
        }
    }
 
public:
    FontNameCollector(std::unordered_set<OUString>& rUsedFontNames, bool bEmbedLatinScript, bool bEmbedAsianScript, bool bEmbedComplexScript)
        : mrUsedFontNames(rUsedFontNames)
        , mbEmbedLatinScript(bEmbedLatinScript)
        , mbEmbedAsianScript(bEmbedAsianScript)
        , mbEmbedComplexScript(bEmbedComplexScript)
    {}
 
protected:
    void handleSdrObject(SdrObject* pObject) override
    {
        SdrTextObj* pTextShape = DynCastSdrTextObj(pObject);
        if (pTextShape)
        {
            auto& rItemSet = pTextShape->GetMergedItemSet();
 
            if (SvxFontItem const* pFontItem = rItemSet.GetItemIfSet(EE_CHAR_FONTINFO, true); pFontItem && mbEmbedLatinScript)
            {
                addFontItem(pFontItem);
            }
            if (SvxFontItem const* pFontItem = rItemSet.GetItemIfSet(EE_CHAR_FONTINFO_CJK, true); pFontItem && mbEmbedAsianScript)
            {
                addFontItem(pFontItem);
            }
            if (SvxFontItem const* pFontItem = rItemSet.GetItemIfSet(EE_CHAR_FONTINFO_CTL, true); pFontItem && mbEmbedComplexScript)
            {
                addFontItem(pFontItem);
            }
 
            traverseEditEng(pTextShape);
        }
    }
};
 
} // end anonymous namespace
 
 
std::unordered_set<OUString> PowerPointExport::getUsedFontList()
{
    std::unordered_set<OUString> aReturnSet;
 
    SdDrawDocument* pDocument = mXModel->GetDoc();
    if (!pDocument)
        return aReturnSet;
 
    auto pCollector = std::make_shared<FontNameCollector>(aReturnSet, mbEmbedLatinScript, mbEmbedAsianScript, mbEmbedComplexScript);
    sd::ModelTraverser aModelTraverser(pDocument, { .mbMasterPages = true });
    aModelTraverser.addNodeHandler(pCollector);
    aModelTraverser.traverse();
 
    return aReturnSet;
}
 
// Writers the list of all embedded fonts and reference to the fonts
void PowerPointExport::WriteEmbeddedFontList()
{
    if (!mbEmbedFonts)
        return;
 
    std::unordered_set<OUString> aUsedFonts;
    if (mbEmbedUsedOnly)
        aUsedFonts = getUsedFontList();
 
    int nextFontId = 1;
 
    std::unordered_set<OUString> aFontFamilyNameSet;
    std::unordered_map<std::string, OString> aFontDeduplicationMap;
 
    std::vector<EmbeddedFont> aEmbeddedFontInfo;
 
    uno::Sequence<uno::Any> aAnySeq;
    if (!(mXModel->getPropertyValue("Fonts") >>= aAnySeq))
        return;
 
    if (aAnySeq.getLength() % 5 != 0)
        return;
 
    int nLen = aAnySeq.getLength() / 5;
    int nSeqIndex = 0;
 
    for (int i = 0; i < nLen; i++)
    {
        OUString sFamilyName;
        OUString sStyleName;
        sal_uInt16 eFamily = FAMILY_DONTKNOW;
        sal_uInt16 ePitch = PITCH_DONTKNOW;
        sal_uInt16 eCharSet = RTL_TEXTENCODING_DONTKNOW;
 
        aAnySeq[nSeqIndex++] >>= sFamilyName;
        aAnySeq[nSeqIndex++] >>= sStyleName;
        aAnySeq[nSeqIndex++] >>= eFamily;
        aAnySeq[nSeqIndex++] >>= ePitch;
        aAnySeq[nSeqIndex++] >>= eCharSet;
 
        if (EmbeddedFontsManager::isCommonFont(sFamilyName))
            continue;
 
        if (mbEmbedUsedOnly && !aUsedFonts.contains(sFamilyName))
            continue;
 
        if (aFontFamilyNameSet.contains(sFamilyName))
            continue;
 
        static std::vector<std::pair<FontItalic, FontWeight>> aFontVariantCombinations =
        {
            { ITALIC_NONE, WEIGHT_NORMAL },
            { ITALIC_NONE, WEIGHT_BOLD },
            { ITALIC_NORMAL, WEIGHT_NORMAL },
            { ITALIC_NORMAL, WEIGHT_BOLD }
        };
 
        EmbeddedFont aInfo;
        aInfo.sFamilyName = sFamilyName;
        SAL_INFO("sd.eppt", "Embedding font: " << sFamilyName);
 
        for (auto [eItalic, eWeight] : aFontVariantCombinations)
        {
            OUString sFontUrl = EmbeddedFontsManager::fontFileUrl(
                                    sFamilyName, FontFamily(eFamily), eItalic, eWeight, FontPitch(ePitch),
                                    EmbeddedFontsManager::FontRights::ViewingAllowed);
 
            if (sFontUrl.isEmpty())
                continue;
 
            osl::File aFile(sFontUrl);
            if (aFile.open(osl_File_OpenFlag_Read ) != osl::File::E_None)
                continue;
 
            std::vector<sal_uInt8> rFontData;
 
            std::array<sal_uInt8, 4096> buffer;
            sal_uInt64 readSize;
 
            comphelper::Hash aHashCalc(comphelper::HashType::SHA256);
 
            OString uRelID;
 
            // Read file
            for(;;)
            {
                sal_Bool eof;
 
                if (aFile.isEndOfFile(&eof) != osl::File::E_None)
                {
                    SAL_WARN("sd.eppt", "Error reading font file " << sFontUrl);
                    break;
                }
                if (eof)
                    break;
 
                if (aFile.read(buffer.data(), 4096, readSize) != osl::File::E_None)
                {
                    SAL_WARN("sd.eppt", "Error reading font file " << sFontUrl);
                    break;
                }
 
                if (readSize == 0)
                    break;
 
                rFontData.insert(rFontData.end(), buffer.data(), buffer.data() + readSize);
                aHashCalc.update(buffer.data(), readSize);
            }
 
            std::string aHash = comphelper::hashToString(aHashCalc.finalize());
            auto iterator = aFontDeduplicationMap.find(aHash);
            if (iterator == aFontDeduplicationMap.end())
            {
                std::vector<sal_uInt8> rEOT;
 
                font::FontDataContainer aContainer(rFontData);
                font::EOTConverter aConverter(aContainer);
                if (!aConverter.convert(rEOT))
                    continue;
 
                OUString sFontType = u"Regular"_ustr;
                if (eItalic == ITALIC_NONE && eWeight == WEIGHT_BOLD)
                    sFontType = u"Bold"_ustr;
                else if (eItalic == ITALIC_NORMAL && eWeight == WEIGHT_NORMAL)
                    sFontType = u"Italic"_ustr;
                else if (eItalic == ITALIC_NORMAL && eWeight == WEIGHT_BOLD)
                    sFontType = u"BoldItalic"_ustr;
 
                OUString sFontFileName = "Font_" + OUString::number(nextFontId) + "_" +
                    sFamilyName.replaceAll(" ", "_") + "_" + sFontType +".fntdata";
 
                OUString sArchivePath = "ppt/fonts/" + sFontFileName;
 
                uno::Reference<css::io::XOutputStream> xOutStream = openFragmentStream(sArchivePath, u"application/x-fontdata"_ustr);
                xOutStream->writeBytes(uno::Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(rEOT.data()), rEOT.size()));
                xOutStream->closeOutput();
 
                OUString sRelID = addRelation(mPresentationFS->getOutputStream(),
                                              oox::getRelationship(Relationship::FONT),
                                              Concat2View("fonts/" + sFontFileName));
 
                ++nextFontId;
 
                uRelID = OUStringToOString(sRelID, RTL_TEXTENCODING_UTF8);
                aFontDeduplicationMap.emplace(aHash, uRelID);
            }
            else
            {
                uRelID = iterator->second;
            }
 
            if (eItalic == ITALIC_NONE && eWeight == WEIGHT_NORMAL)
                aInfo.aRegularRelID = uRelID;
            else if (eItalic == ITALIC_NONE && eWeight == WEIGHT_BOLD)
                aInfo.aBoldRelID = uRelID;
            else if (eItalic == ITALIC_NORMAL && eWeight == WEIGHT_NORMAL)
                aInfo.aItalicRelID = uRelID;
            else if (eItalic == ITALIC_NORMAL && eWeight == WEIGHT_BOLD)
                aInfo.aBoldItalicRelID = uRelID;
        }
 
        aEmbeddedFontInfo.push_back(aInfo);
        aFontFamilyNameSet.insert(sFamilyName);
    }
 
    // if there are fonts to embed and font embedding enabled
    mPresentationFS->startElementNS(XML_p, XML_embeddedFontLst);
    for (auto const& rInfo : aEmbeddedFontInfo)
    {
        mPresentationFS->startElementNS(XML_p, XML_embeddedFont);
 
        mPresentationFS->singleElementNS(XML_p, XML_font,
            //XML_charset, OUString::number(0),
            XML_typeface, rInfo.sFamilyName);
 
        if (!rInfo.aRegularRelID.isEmpty())
            mPresentationFS->singleElementNS(XML_p, XML_regular, FSNS(XML_r, XML_id), rInfo.aRegularRelID);
 
        if (!rInfo.aBoldRelID.isEmpty())
            mPresentationFS->singleElementNS(XML_p, XML_bold, FSNS(XML_r, XML_id), rInfo.aBoldRelID);
 
        if (!rInfo.aItalicRelID.isEmpty())
            mPresentationFS->singleElementNS(XML_p, XML_italic, FSNS(XML_r, XML_id), rInfo.aItalicRelID);
 
        if (!rInfo.aBoldItalicRelID.isEmpty())
            mPresentationFS->singleElementNS(XML_p, XML_boldItalic, FSNS(XML_r, XML_id), rInfo.aBoldItalicRelID);
 
        mPresentationFS->endElementNS(XML_p, XML_embeddedFont);
    }
    mPresentationFS->endElementNS(XML_p, XML_embeddedFontLst);
}
 
void PowerPointExport::WriteCustomSlideShow()
{
    if (!mXModel->getCustomPresentations()->hasElements())
        return;
 
    mPresentationFS->startElementNS(XML_p, XML_custShowLst);
 
    Reference<XDrawPages> xDrawPages(mXModel->getDrawPages(), uno::UNO_SET_THROW);
    Reference<XNameContainer> aXNameCont(mXModel->getCustomPresentations());
    const Sequence<OUString> aNameSeq(aXNameCont->getElementNames());
 
    OUString sRelId;
    sal_uInt32 nCustomShowIndex = 0;
    sal_Int32 nSlideCount = xDrawPages->getCount();
 
    for (OUString const& customShowName : aNameSeq)
    {
        mPresentationFS->startElementNS(XML_p, XML_custShow, XML_name, customShowName, XML_id,
                                        OUString::number(nCustomShowIndex++));
 
        mAny = aXNameCont->getByName(customShowName);
        Reference<XIndexContainer> aXIContainer;
        if (mAny >>= aXIContainer)
        {
            mPresentationFS->startElementNS(XML_p, XML_sldLst);
 
            sal_Int32 nCustomShowSlideCount = aXIContainer->getCount();
            for (sal_Int32 i = 0; i < nCustomShowSlideCount; ++i)
            {
                Reference<XDrawPage> aXCustomShowDrawPage;
                aXIContainer->getByIndex(i) >>= aXCustomShowDrawPage;
                Reference<XNamed> aXName(aXCustomShowDrawPage, UNO_QUERY_THROW);
                OUString sCustomShowSlideName = aXName->getName();
 
                for (sal_Int32 j = 0; j < nSlideCount; ++j)
                {
                    Reference<XDrawPage> xDrawPage;
                    xDrawPages->getByIndex(j) >>= xDrawPage;
                    Reference<XNamed> xNamed(xDrawPage, UNO_QUERY_THROW);
                    OUString sSlideName = xNamed->getName();
 
                    if (sCustomShowSlideName == sSlideName)
                    {
                        sRelId = maRelId[j];
                        break;
                    }
                }
                mPresentationFS->singleElementNS(XML_p, XML_sld, FSNS(XML_r, XML_id), sRelId);
            }
            mPresentationFS->endElementNS(XML_p, XML_sldLst);
        }
        mPresentationFS->endElementNS(XML_p, XML_custShow);
    }
    mPresentationFS->endElementNS(XML_p, XML_custShowLst);
}
 
void PowerPointExport::ImplWriteBackground(const FSHelperPtr& pFS, const Reference< XPropertySet >& rXPropSet)
{
    FillStyle aFillStyle(FillStyle_NONE);
    if (ImplGetPropertyValue(rXPropSet, u"FillStyle"_ustr))
        mAny >>= aFillStyle;
 
    if (aFillStyle == FillStyle_NONE ||
            aFillStyle == FillStyle_HATCH)
        return;
 
    // For BITMAP fill style, check if graphic is valid before writing bgPr
    if (aFillStyle == FillStyle_BITMAP
        && (!ImplGetPropertyValue(rXPropSet, u"FillBitmap"_ustr)
            || !mAny.has<uno::Reference<graphic::XGraphic>>()))
        return;
 
    pFS->startElementNS(XML_p, XML_bg);
    pFS->startElementNS(XML_p, XML_bgPr);
 
    PowerPointShapeExport aDML(pFS, &maShapeMap, this);
    aDML.SetBackgroundDark(mbIsBackgroundDark);
    aDML.WriteFill(rXPropSet, maPageSize);
 
    pFS->endElementNS(XML_p, XML_bgPr);
    pFS->endElementNS(XML_p, XML_bg);
}
 
#define MAIN_GROUP \
     "<p:nvGrpSpPr>\
        <p:cNvPr id=\"1\" name=\"\"/>\
        <p:cNvGrpSpPr/>\
        <p:nvPr/>\
      </p:nvGrpSpPr>\
      <p:grpSpPr>\
        <a:xfrm>\
          <a:off x=\"0\" y=\"0\"/>\
          <a:ext cx=\"0\" cy=\"0\"/>\
          <a:chOff x=\"0\" y=\"0\"/>\
          <a:chExt cx=\"0\" cy=\"0\"/>\
        </a:xfrm>\
      </p:grpSpPr>"
 
const char* PowerPointExport::GetSideDirection(sal_uInt8 nDirection)
{
    const char* pDirection = nullptr;
 
    switch (nDirection)
    {
    case 0:
        pDirection = "l";
        break;
    case 1:
        pDirection = "u";
        break;
    case 2:
        pDirection = "r";
        break;
    case 3:
        pDirection = "d";
        break;
    }
 
    return pDirection;
}
 
const char* PowerPointExport::GetCornerDirection(sal_uInt8 nDirection)
{
    const char* pDirection = nullptr;
 
    switch (nDirection)
    {
    case 4:
        pDirection = "lu";
        break;
    case 5:
        pDirection = "ru";
        break;
    case 6:
        pDirection = "ld";
        break;
    case 7:
        pDirection = "rd";
        break;
    }
 
    return pDirection;
}
 
const char* PowerPointExport::Get8Direction(sal_uInt8 nDirection)
{
    const char* pDirection = GetSideDirection(nDirection);
 
    if (!pDirection)
        pDirection = GetCornerDirection(nDirection);
 
    return pDirection;
}
 
void PowerPointExport::WriteTransition(const FSHelperPtr& pFS)
{
    FadeEffect eFadeEffect = FadeEffect_NONE;
    if (ImplGetPropertyValue(mXPagePropSet, u"Effect"_ustr))
        mAny >>= eFadeEffect;
 
    sal_Int16 nTransitionType = 0, nTransitionSubtype = 0;
    sal_Int8 nPPTTransitionType = 0;
    sal_uInt8 nDirection = 0;
 
    OUString sSoundUrl;
    OUString sSoundRelId;
    OUString sSoundName;
 
    if (ImplGetPropertyValue(mXPagePropSet, u"TransitionType"_ustr) && (mAny >>= nTransitionType) &&
            ImplGetPropertyValue(mXPagePropSet, u"TransitionSubtype"_ustr) && (mAny >>= nTransitionSubtype))
    {
        // FADEOVERCOLOR with black -> fade, with white -> flash
        sal_Int32 nTransitionFadeColor = 0;
        if( ImplGetPropertyValue(mXPagePropSet, u"TransitionFadeColor"_ustr))
            mAny >>= nTransitionFadeColor;
        nPPTTransitionType = GetTransition(nTransitionType, nTransitionSubtype, eFadeEffect, nTransitionFadeColor, nDirection);
    }
 
    if (!nPPTTransitionType && eFadeEffect != FadeEffect_NONE)
        nPPTTransitionType = GetTransition(eFadeEffect, nDirection);
 
    if (ImplGetPropertyValue(mXPagePropSet, u"Sound"_ustr) && (mAny >>= sSoundUrl))
        embedEffectAudio(pFS, sSoundUrl, sSoundRelId, sSoundName);
 
    bool bOOXmlSpecificTransition = false;
 
    sal_Int32 nTransition = 0;
    const char* pDirection = nullptr;
    const char* pOrientation = nullptr;
    const char* pThruBlk = nullptr;
    const char* pSpokes = nullptr;
 
    char pSpokesTmp[2] = "0";
 
    // p14
    sal_Int32 nTransition14 = 0;
    const char* pDirection14 = nullptr;
    const char* pInverted = nullptr;
    const char* pPattern = nullptr; // diamond or hexagon
 
    //p15
    const char* pPresetTransition = nullptr;
 
    if (!nPPTTransitionType)
    {
        switch (nTransitionType)
        {
        case animations::TransitionType::BARWIPE:
        {
            if (nTransitionSubtype == animations::TransitionSubType::FADEOVERCOLOR)
            {
                nTransition = XML_cut;
                pThruBlk = "true";
                bOOXmlSpecificTransition = true;
            }
            break;
        }
        case animations::TransitionType::MISCSHAPEWIPE:
        {
            switch (nTransitionSubtype)
            {
            case animations::TransitionSubType::TOPTOBOTTOM: // Turn around
                nTransition = XML_fade;
                nTransition14 = XML_flip;
                pDirection14 = "l";
                bOOXmlSpecificTransition = true;
                break;
            case animations::TransitionSubType::BOTTOMRIGHT: // Rochade
                nTransition = XML_fade;
                nTransition14 = XML_switch;
                pDirection14 = "r";
                bOOXmlSpecificTransition = true;
                break;
            case animations::TransitionSubType::VERTICAL: // Vortex
                nTransition = XML_fade;
                nTransition14 = XML_vortex;
                bOOXmlSpecificTransition = true;
                break;
            case animations::TransitionSubType::HORIZONTAL: // Ripple
                nTransition = XML_fade;
                nTransition14 = XML_ripple;
                bOOXmlSpecificTransition = true;
                break;
            case animations::TransitionSubType::LEFTTORIGHT: // Fall
                nTransition = XML_fade;
                pPresetTransition = "fallOver";
                bOOXmlSpecificTransition = true;
                break;
            case animations::TransitionSubType::CORNERSIN: // Inside turning cube
                pInverted = "true";
                [[fallthrough]];
            case animations::TransitionSubType::CORNERSOUT: // Outside turning cube
                nTransition = XML_fade;
                nTransition14 = XML_prism;
                bOOXmlSpecificTransition = true;
                break;
            case animations::TransitionSubType::DIAMOND: // Glitter
                nTransition = XML_fade;
                nTransition14 = XML_glitter;
                pDirection14 = "l";
                pPattern = "hexagon";
                bOOXmlSpecificTransition = true;
                break;
            case animations::TransitionSubType::HEART: // Honeycomb
                nTransition = XML_fade;
                nTransition14 = XML_honeycomb;
                bOOXmlSpecificTransition = true;
                break;
            }
            break;
        }
        }
    }
 
    AnimationSpeed animationSpeed = AnimationSpeed_MEDIUM;
    const char* speed = nullptr;
    sal_Int32 advanceTiming = -1;
    sal_Int32 changeType = 0;
 
    sal_Int32 nTransitionDuration = -1;
    bool isTransitionDurationSet = false;
 
    // try to use TransitionDuration instead of old Speed property
    if (ImplGetPropertyValue(mXPagePropSet, u"TransitionDuration"_ustr))
    {
        double fTransitionDuration = -1.0;
        mAny >>= fTransitionDuration;
        if (fTransitionDuration >= 0)
        {
            nTransitionDuration = fTransitionDuration * 1000.0;
 
            // override values because in MS formats meaning of fast/medium/slow is different
            if (nTransitionDuration <= 500)
            {
                // fast is default
                speed = nullptr;
            }
            else if (nTransitionDuration >= 1000)
            {
                speed = "slow";
            }
            else
            {
                speed = "med";
            }
 
            bool isStandardValue = nTransitionDuration == 500
                || nTransitionDuration == 750
                || nTransitionDuration == 1000;
 
            if(!isStandardValue)
                isTransitionDurationSet = true;
        }
    }
    else if (ImplGetPropertyValue(mXPagePropSet, u"Speed"_ustr))
    {
        mAny >>= animationSpeed;
 
        switch (animationSpeed)
        {
        default:
        case AnimationSpeed_MEDIUM:
            speed = "med";
            break;
        case AnimationSpeed_SLOW:
            speed = "slow";
            break;
        case AnimationSpeed_FAST:
            break;
        }
    }
 
    // check if we resolved what transition to export or time is set
    if (!nPPTTransitionType && !bOOXmlSpecificTransition && !isTransitionDurationSet)
        return;
 
    if (ImplGetPropertyValue(mXPagePropSet, u"Change"_ustr))
        mAny >>= changeType;
 
    // 1 means automatic, 2 half automatic - not sure what it means - at least I don't see it in UI
    if (changeType == 1 && ImplGetPropertyValue(mXPagePropSet, u"Duration"_ustr))
        mAny >>= advanceTiming;
 
    if (!bOOXmlSpecificTransition)
    {
        switch (nPPTTransitionType)
        {
        case PPT_TRANSITION_TYPE_BLINDS:
            nTransition = XML_blinds;
            pDirection = (nDirection == 0) ? "vert" : "horz";
            break;
        case PPT_TRANSITION_TYPE_CHECKER:
            nTransition = XML_checker;
            pDirection = (nDirection == 1) ? "vert" : "horz";
            break;
        case PPT_TRANSITION_TYPE_CIRCLE:
            nTransition = XML_circle;
            break;
        case PPT_TRANSITION_TYPE_COMB:
            nTransition = XML_comb;
            pDirection = (nDirection == 1) ? "vert" : "horz";
            break;
        case PPT_TRANSITION_TYPE_COVER:
            nTransition = XML_cover;
            pDirection = Get8Direction(nDirection);
            break;
        case PPT_TRANSITION_TYPE_DIAMOND:
            nTransition = XML_diamond;
            break;
        case PPT_TRANSITION_TYPE_DISSOLVE:
            nTransition = XML_dissolve;
            break;
        case PPT_TRANSITION_TYPE_FADE:
            nTransition = XML_fade;
            pThruBlk = "true";
            break;
        case PPT_TRANSITION_TYPE_SMOOTHFADE:
            nTransition = XML_fade;
            break;
        case PPT_TRANSITION_TYPE_NEWSFLASH:
            nTransition = XML_newsflash;
            break;
        case PPT_TRANSITION_TYPE_PLUS:
            nTransition = XML_plus;
            break;
        case PPT_TRANSITION_TYPE_PULL:
            nTransition = XML_pull;
            pDirection = Get8Direction(nDirection);
            break;
        case PPT_TRANSITION_TYPE_PUSH:
            nTransition = XML_push;
            pDirection = GetSideDirection(nDirection);
            break;
        case PPT_TRANSITION_TYPE_RANDOM:
            nTransition = XML_random;
            break;
        case PPT_TRANSITION_TYPE_RANDOM_BARS:
            nTransition = XML_randomBar;
            pDirection = (nDirection == 1) ? "vert" : "horz";
            break;
        case PPT_TRANSITION_TYPE_SPLIT:
            nTransition = XML_split;
            pDirection = (nDirection & 1) ? "in" : "out";
            pOrientation = (nDirection < 2) ? "horz" : "vert";
            break;
        case PPT_TRANSITION_TYPE_STRIPS:
            nTransition = XML_strips;
            pDirection = GetCornerDirection(nDirection);
            break;
        case PPT_TRANSITION_TYPE_WEDGE:
            nTransition = XML_wedge;
            break;
        case PPT_TRANSITION_TYPE_WHEEL:
            nTransition = XML_wheel;
            if (nDirection != 4 && nDirection <= 9)
            {
                pSpokesTmp[0] = '0' + nDirection;
                pSpokes = pSpokesTmp;
            }
            break;
        case PPT_TRANSITION_TYPE_WIPE:
            nTransition = XML_wipe;
            pDirection = GetSideDirection(nDirection);
            break;
        case PPT_TRANSITION_TYPE_ZOOM:
            nTransition = XML_zoom;
            pDirection = (nDirection == 1) ? "in" : "out";
            break;
        case PPT_TRANSITION_TYPE_FLASH:
            nTransition14 = XML_flash;
            nTransition = XML_fade;
            bOOXmlSpecificTransition = true;
            break;
        // coverity[dead_error_line] - following conditions exist to avoid compiler warning
        case PPT_TRANSITION_TYPE_NONE:
        default:
            nTransition = 0;
            break;
        }
    }
 
    bool isAdvanceTimingSet = advanceTiming != -1;
    if (nTransition14 || pPresetTransition || isTransitionDurationSet)
    {
        const char* pRequiresNS = (nTransition14 || isTransitionDurationSet) ? "p14" : "p15";
 
        pFS->startElement(FSNS(XML_mc, XML_AlternateContent));
        pFS->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, pRequiresNS);
 
        if(isTransitionDurationSet && isAdvanceTimingSet)
        {
            pFS->startElementNS(XML_p, XML_transition,
                XML_spd, speed,
                XML_advTm, OString::number(advanceTiming * 1000),
                FSNS(XML_p14, XML_dur), OString::number(nTransitionDuration));
        }
        else if(isTransitionDurationSet)
        {
            pFS->startElementNS(XML_p, XML_transition,
                XML_spd, speed,
                FSNS(XML_p14, XML_dur), OString::number(nTransitionDuration));
        }
        else if(isAdvanceTimingSet)
        {
            pFS->startElementNS(XML_p, XML_transition,
                XML_spd, speed,
                XML_advTm, OString::number(advanceTiming * 1000));
        }
        else
        {
            pFS->startElementNS(XML_p, XML_transition, XML_spd, speed);
        }
 
        if (nTransition14)
        {
            pFS->singleElementNS(XML_p14, nTransition14,
                XML_isInverted, pInverted,
                XML_dir, pDirection14,
                XML_pattern, pPattern);
        }
        else if (pPresetTransition)
        {
            pFS->singleElementNS(XML_p15, XML_prstTrans,
                XML_prst, pPresetTransition);
        }
        else if (isTransitionDurationSet && nTransition)
        {
            pFS->singleElementNS(XML_p, nTransition,
                XML_dir, pDirection,
                XML_orient, pOrientation,
                XML_spokes, pSpokes,
                XML_thruBlk, pThruBlk);
        }
 
        if (!sSoundRelId.isEmpty())
            WriteSndAc(pFS, sSoundRelId, sSoundName);
 
        pFS->endElement(FSNS(XML_p, XML_transition));
 
        pFS->endElement(FSNS(XML_mc, XML_Choice));
        pFS->startElement(FSNS(XML_mc, XML_Fallback));
    }
 
    pFS->startElementNS(XML_p, XML_transition,
        XML_spd, speed,
        XML_advTm, sax_fastparser::UseIf(OString::number(advanceTiming * 1000), isAdvanceTimingSet));
 
    if (nTransition)
    {
        pFS->singleElementNS(XML_p, nTransition,
                             XML_dir, pDirection,
                             XML_orient, pOrientation,
                             XML_spokes, pSpokes,
                             XML_thruBlk, pThruBlk);
    }
 
    if (!sSoundRelId.isEmpty())
        WriteSndAc(pFS, sSoundRelId, sSoundName);
 
    pFS->endElementNS(XML_p, XML_transition);
 
    if (nTransition14 || pPresetTransition || isTransitionDurationSet)
    {
        pFS->endElement(FSNS(XML_mc, XML_Fallback));
        pFS->endElement(FSNS(XML_mc, XML_AlternateContent));
    }
}
 
void PowerPointExport::WriteAuthors()
{
    if (maAuthors.empty())
        return;
 
    FSHelperPtr pFS = openFragmentStreamWithSerializer(u"ppt/commentAuthors.xml"_ustr,
                      u"application/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml"_ustr);
    addRelation(mPresentationFS->getOutputStream(),
                oox::getRelationship(Relationship::COMMENTAUTHORS),
                u"commentAuthors.xml");
 
    pFS->startElementNS(XML_p, XML_cmAuthorLst,
                        FSNS(XML_xmlns, XML_p), getNamespaceURL(OOX_NS(ppt)));
 
    for (const AuthorsMap::value_type& i : maAuthors)
    {
        pFS->singleElementNS(XML_p, XML_cmAuthor,
                             XML_id, OString::number(i.second.nId),
                             XML_name, i.first,
                             XML_initials, i.second.sInitials,
                             XML_lastIdx, OString::number(i.second.nLastIndex),
                             XML_clrIdx, OString::number(i.second.nId));
    }
 
    pFS->endElementNS(XML_p, XML_cmAuthorLst);
 
    pFS->endDocument();
}
 
sal_Int32 PowerPointExport::GetAuthorIdAndLastIndex(const OUString& sAuthor,
                                                    const OUString& sInitials,
                                                    sal_Int32& nLastIndex)
{
    if (maAuthors.count(sAuthor) <= 0)
        maAuthors.emplace(sAuthor, AuthorComments(maAuthors.size(), 0, sInitials));
 
    nLastIndex = ++maAuthors[ sAuthor ].nLastIndex;
 
    return maAuthors[ sAuthor ].nId;
}
 
void PowerPointExport::WritePresentationProps()
{
    Reference<beans::XPropertySet> xPresentationProps(mXModel->getPresentation(),
                                                      uno::UNO_QUERY);
    bool bEndlessVal = xPresentationProps->getPropertyValue(u"IsEndless"_ustr).get<bool>();
    bool bChangeManually = xPresentationProps->getPropertyValue(u"IsAutomatic"_ustr).get<bool>();
    OUString sFirstPage = xPresentationProps->getPropertyValue(u"FirstPage"_ustr).get<OUString>();
    OUString sCustomShow = xPresentationProps->getPropertyValue(u"CustomShow"_ustr).get<OUString>();
 
    FSHelperPtr pFS = openFragmentStreamWithSerializer(
        u"ppt/presProps.xml"_ustr,
        u"application/vnd.openxmlformats-officedocument.presentationml.presProps+xml"_ustr);
 
    addRelation(mPresentationFS->getOutputStream(),
                oox::getRelationship(Relationship::PRESPROPS), u"presProps.xml");
 
    pFS->startElementNS(XML_p, XML_presentationPr, presentationPrNamespaces(*this));
 
    pFS->startElementNS(XML_p, XML_showPr, XML_loop, sax_fastparser::UseIf("1", bEndlessVal),
                        XML_useTimings, sax_fastparser::UseIf("0", bChangeManually),
                        XML_showNarration, "1");
 
    Reference<drawing::XDrawPages> xDrawPages(mXModel->getDrawPages(), uno::UNO_SET_THROW);
    if (!sFirstPage.isEmpty())
    {
        sal_Int32 nStartSlide = 1;
        sal_Int32 nEndSlide = xDrawPages->getCount();
        for (sal_Int32 i = 0; i < nEndSlide; i++)
        {
            Reference<drawing::XDrawPage> xDrawPage;
            xDrawPages->getByIndex(i) >>= xDrawPage;
            Reference<container::XNamed> xNamed(xDrawPage, uno::UNO_QUERY_THROW);
            if (xNamed->getName() == sFirstPage)
            {
                nStartSlide = i + 1;
                break;
            }
        }
 
        pFS->singleElementNS(XML_p, XML_sldRg, XML_st, OUString::number(nStartSlide), XML_end,
                             OUString::number(nEndSlide));
    }
 
    if (!sCustomShow.isEmpty())
    {
        css::uno::Reference<css::container::XNameContainer> mxCustShows;
        mxCustShows = mXModel->getCustomPresentations();
        const css::uno::Sequence<OUString> aNameSeq(mxCustShows->getElementNames());
 
        sal_Int32 nCustShowIndex = 0;
        for (sal_Int32 i = 0; i < aNameSeq.getLength(); i++)
        {
            if (aNameSeq[i] == sCustomShow)
            {
                nCustShowIndex = i;
                break;
            }
        }
 
        pFS->singleElementNS(XML_p, XML_custShow, XML_id, OUString::number(nCustShowIndex));
    }
 
    pFS->endElementNS(XML_p, XML_showPr);
 
    pFS->endElementNS(XML_p, XML_presentationPr);
 
    pFS->endDocument();
}
 
bool PowerPointExport::WriteComments(sal_uInt32 nPageNum)
{
    Reference< XAnnotationAccess > xAnnotationAccess(mXDrawPage, uno::UNO_QUERY);
    if (xAnnotationAccess.is())
    {
        Reference< XAnnotationEnumeration > xAnnotationEnumeration(xAnnotationAccess->createAnnotationEnumeration());
 
        if (xAnnotationEnumeration->hasMoreElements())
        {
            bool bRemoveCommentAuthorDates
                = SvtSecurityOptions::IsOptionSet(
                      SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo)
                  && !SvtSecurityOptions::IsOptionSet(
                         SvtSecurityOptions::EOption::DocWarnKeepNoteAuthorDateInfo);
 
            FSHelperPtr pFS = openFragmentStreamWithSerializer(
                              "ppt/comments/comment" + OUString::number(nPageNum + 1) + ".xml",
                              u"application/vnd.openxmlformats-officedocument.presentationml.comments+xml"_ustr);
 
            pFS->startElementNS(XML_p, XML_cmLst,
                                FSNS(XML_xmlns, XML_p), this->getNamespaceURL(OOX_NS(ppt)));
 
            do
            {
                Reference< XAnnotation > xAnnotation(xAnnotationEnumeration->nextElement());
                util::DateTime aDateTime(xAnnotation->getDateTime());
                RealPoint2D aRealPoint2D(xAnnotation->getPosition());
                Reference< XText > xText(xAnnotation->getTextRange());
                sal_Int32 nLastIndex;
                OUString sAuthor(bRemoveCommentAuthorDates
                                     ? "Author"
                                           + OUString::number(GetInfoID(xAnnotation->getAuthor()))
                                     : xAnnotation->getAuthor());
                OUString sInitials(bRemoveCommentAuthorDates
                                       ? "A" + OUString::number(GetInfoID(xAnnotation->getAuthor()))
                                       : xAnnotation->getInitials());
                sal_Int32 nId = GetAuthorIdAndLastIndex(sAuthor, sInitials, nLastIndex);
                char cDateTime[sizeof("-32768-65535-65535T65535:65535:65535.4294967295")];
                    // reserve enough space for hypothetical max length
 
                snprintf(cDateTime, sizeof cDateTime, "%02" SAL_PRIdINT32 "-%02" SAL_PRIuUINT32 "-%02" SAL_PRIuUINT32 "T%02" SAL_PRIuUINT32 ":%02" SAL_PRIuUINT32 ":%02" SAL_PRIuUINT32 ".%09" SAL_PRIuUINT32, sal_Int32(aDateTime.Year), sal_uInt32(aDateTime.Month), sal_uInt32(aDateTime.Day), sal_uInt32(aDateTime.Hours), sal_uInt32(aDateTime.Minutes), sal_uInt32(aDateTime.Seconds), aDateTime.NanoSeconds);
 
                util::DateTime aEmptyDate;
                if (bRemoveCommentAuthorDates || aDateTime == aEmptyDate)
                {
                    pFS->startElementNS(XML_p, XML_cm,
                                        XML_authorId, OString::number(nId),
                                        XML_idx, OString::number(nLastIndex));
                }
                else
                {
                    pFS->startElementNS(XML_p, XML_cm,
                                        XML_authorId, OString::number(nId),
                                        XML_dt, cDateTime,
                                        XML_idx, OString::number(nLastIndex));
                }
 
                pFS->singleElementNS(XML_p, XML_pos,
                                     XML_x, OString::number(std::round(convertMm100ToMasterUnit(aRealPoint2D.X * 100))),
                                     XML_y, OString::number(std::round(convertMm100ToMasterUnit(aRealPoint2D.Y * 100))));
 
                pFS->startElementNS(XML_p, XML_text);
                pFS->writeEscaped(xText->getString());
                pFS->endElementNS(XML_p, XML_text);
 
                pFS->endElementNS(XML_p, XML_cm);
 
            }
            while (xAnnotationEnumeration->hasMoreElements());
 
            pFS->endElementNS(XML_p, XML_cmLst);
 
            pFS->endDocument();
 
            return true;
        }
    }
 
    return false;
}
 
void PowerPointExport::WriteVBA()
{
    if (!mbPptm)
        return;
 
    uno::Reference<embed::XStorage> xDocumentStorage = mXModel->getDocumentStorage();
    OUString aMacrosName(u"_MS_VBA_Macros"_ustr);
    if (!xDocumentStorage.is() || !xDocumentStorage->hasByName(aMacrosName))
        return;
 
    const sal_Int32 nOpenMode = embed::ElementModes::READ;
    uno::Reference<io::XInputStream> xMacrosStream(xDocumentStorage->openStreamElement(aMacrosName, nOpenMode), uno::UNO_QUERY);
    if (!xMacrosStream.is())
        return;
 
    uno::Reference<io::XOutputStream> xOutputStream = openFragmentStream(u"ppt/vbaProject.bin"_ustr, u"application/vnd.ms-office.vbaProject"_ustr);
    comphelper::OStorageHelper::CopyInputToOutput(xMacrosStream, xOutputStream);
 
    // Write the relationship.
    addRelation(mPresentationFS->getOutputStream(), oox::getRelationship(Relationship::VBAPROJECT), u"vbaProject.bin");
}
 
void PowerPointExport::WriteModifyVerifier()
{
    Sequence<PropertyValue> aInfo;
 
    try
    {
        Reference<XPropertySet> xDocSettings(
            mXModel->createInstance(u"com.sun.star.document.Settings"_ustr), UNO_QUERY);
        xDocSettings->getPropertyValue(u"ModifyPasswordInfo"_ustr) >>= aInfo;
    }
    catch (const Exception&)
    {
    }
 
    if (aInfo.hasElements())
    {
        OUString sAlgorithm, sSalt, sHash;
        sal_Int32 nCount = 0;
        for (auto& prop : aInfo)
        {
            if (prop.Name == "algorithm-name")
                prop.Value >>= sAlgorithm;
            else if (prop.Name == "salt")
                prop.Value >>= sSalt;
            else if (prop.Name == "iteration-count")
                prop.Value >>= nCount;
            else if (prop.Name == "hash")
                prop.Value >>= sHash;
        }
        if (!sAlgorithm.isEmpty() && !sSalt.isEmpty() && !sHash.isEmpty())
        {
            sal_Int32 nAlgorithmSid = 0;
            if (sAlgorithm == "MD2")
                nAlgorithmSid = 1;
            else if (sAlgorithm == "MD4")
                nAlgorithmSid = 2;
            else if (sAlgorithm == "MD5")
                nAlgorithmSid = 3;
            else if (sAlgorithm == "SHA-1")
                nAlgorithmSid = 4;
            else if (sAlgorithm == "MAC")
                nAlgorithmSid = 5;
            else if (sAlgorithm == "RIPEMD")
                nAlgorithmSid = 6;
            else if (sAlgorithm == "RIPEMD-160")
                nAlgorithmSid = 7;
            else if (sAlgorithm == "HMAC")
                nAlgorithmSid = 9;
            else if (sAlgorithm == "SHA-256")
                nAlgorithmSid = 12;
            else if (sAlgorithm == "SHA-384")
                nAlgorithmSid = 13;
            else if (sAlgorithm == "SHA-512")
                nAlgorithmSid = 14;
 
            if (nAlgorithmSid != 0)
                mPresentationFS->singleElementNS(XML_p, XML_modifyVerifier,
                    XML_cryptProviderType, "rsaAES",
                    XML_cryptAlgorithmClass, "hash",
                    XML_cryptAlgorithmType, "typeAny",
                    XML_cryptAlgorithmSid, OString::number(nAlgorithmSid),
                    XML_spinCount, OString::number(nCount),
                    XML_saltData, sSalt,
                    XML_hashData, sHash);
        }
    }
}
 
void PowerPointExport::ImplWriteSlide(sal_uInt32 nPageNum, sal_uInt32 nMasterNum, sal_uInt16 /* nMode */,
                                      bool bHasBackground, Reference< XPropertySet > const& aXBackgroundPropSet)
{
    SAL_INFO("sd.eppt", "write slide: " << nPageNum << "\n----------------");
 
    // slides list
    if (nPageNum == 0)
        mPresentationFS->startElementNS(XML_p, XML_sldIdLst);
 
    // add explicit relation of presentation to this slide
    OUString sRelId = addRelation(mPresentationFS->getOutputStream(),
                                  oox::getRelationship(Relationship::SLIDE),
                                  Concat2View("slides/slide" + OUString::number(nPageNum + 1) +".xml"));
 
    mPresentationFS->singleElementNS(XML_p, XML_sldId,
                                     XML_id, OString::number(GetNewSlideId()),
                                     FSNS(XML_r, XML_id), sRelId);
 
    maRelId.push_back(sRelId);
 
    if (nPageNum == mnPages - 1)
        mPresentationFS->endElementNS(XML_p, XML_sldIdLst);
 
    FSHelperPtr pFS = openFragmentStreamWithSerializer(
                        "ppt/slides/slide" + OUString::number(nPageNum + 1) + ".xml",
                        u"application/vnd.openxmlformats-officedocument.presentationml.slide+xml"_ustr);
 
    if (mpSlidesFSArray.size() < mnPages)
        mpSlidesFSArray.resize(mnPages);
    mpSlidesFSArray[ nPageNum ] = pFS;
 
    const char* pShow = nullptr;
    const char* pShowMasterShape = nullptr;
 
    if (ImplGetPropertyValue(mXPagePropSet, u"Visible"_ustr))
    {
        bool bShow(false);
        if ((mAny >>= bShow) && !bShow)
            pShow = "0";
    }
 
    if (ImplGetPropertyValue(mXPagePropSet, u"IsBackgroundObjectsVisible"_ustr))
    {
        bool bShowMasterShape(false);
        if ((mAny >>= bShowMasterShape) && !bShowMasterShape)
            pShowMasterShape = "0";
    }
 
    auto pAttributes = presentationNamespaces(*this);
    if (pShow)
        pAttributes->add(XML_show, pShow);
    if (pShowMasterShape)
        pAttributes->add(XML_showMasterSp, pShowMasterShape);
 
    pFS->startElementNS(XML_p, XML_sld, pAttributes);
 
    pFS->startElementNS(XML_p, XML_cSld);
 
    // background
    if (bHasBackground)
    {
        ImplWriteBackground(pFS, aXBackgroundPropSet);
    }
 
    WriteShapeTree(pFS, NORMAL, false);
 
    pFS->endElementNS(XML_p, XML_cSld);
 
    WriteTransition(pFS);
    WriteAnimations(pFS, mXDrawPage, *this);
 
    pFS->endElementNS(XML_p, XML_sld);
 
    // add implicit relation to slide layout
    addRelation(pFS->getOutputStream(),
                oox::getRelationship(Relationship::SLIDELAYOUT),
                Concat2View("../slideLayouts/slideLayout" +
                    OUString::number(GetLayoutFileId(GetLayoutOffset(mXPagePropSet), nMasterNum)) +
                    ".xml"));
 
    if (WriteComments(nPageNum))
        // add implicit relation to slide comments
        addRelation(pFS->getOutputStream(),
                    oox::getRelationship(Relationship::COMMENTS),
                    Concat2View("../comments/comment" + OUString::number(nPageNum + 1) + ".xml"));
 
    SAL_INFO("sd.eppt", "----------------");
}
 
void PowerPointExport::ImplWriteNotes(sal_uInt32 nPageNum)
{
    if (!mbCreateNotes || !ContainsOtherShapeThanPlaceholders())
        return;
 
    SAL_INFO("sd.eppt", "write Notes " << nPageNum << "\n----------------");
 
    FSHelperPtr pFS = openFragmentStreamWithSerializer(
                        "ppt/notesSlides/notesSlide" +
                        OUString::number(nPageNum + 1) +
                        ".xml",
                        u"application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml"_ustr);
 
    pFS->startElementNS(XML_p, XML_notes, presentationNamespaces(*this));
 
    pFS->startElementNS(XML_p, XML_cSld);
 
    WriteShapeTree(pFS, NOTICE, false);
 
    pFS->endElementNS(XML_p, XML_cSld);
 
    pFS->endElementNS(XML_p, XML_notes);
 
    // add implicit relation to slide
    addRelation(pFS->getOutputStream(),
                oox::getRelationship(Relationship::SLIDE),
                Concat2View("../slides/slide" + OUString::number(nPageNum + 1) + ".xml"));
 
    // add slide implicit relation to notes
    if (nPageNum < mpSlidesFSArray.size())
        addRelation(mpSlidesFSArray[ nPageNum ]->getOutputStream(),
                    oox::getRelationship(Relationship::NOTESSLIDE),
                    Concat2View("../notesSlides/notesSlide" + OUString::number(nPageNum + 1) + ".xml"));
 
    // add implicit relation to notes master
    addRelation(pFS->getOutputStream(),
                oox::getRelationship(Relationship::NOTESMASTER),
                u"../notesMasters/notesMaster1.xml");
 
    SAL_INFO("sd.eppt", "-----------------");
 
    pFS->endDocument();
}
 
void PowerPointExport::AddLayoutIdAndRelation(const FSHelperPtr& pFS, sal_Int32 nLayoutFileId)
{
    // add implicit relation of slide master to slide layout
    OUString sRelId = addRelation(pFS->getOutputStream(),
                                  oox::getRelationship(Relationship::SLIDELAYOUT),
                                  Concat2View("../slideLayouts/slideLayout" + OUString::number(nLayoutFileId) + ".xml"));
 
    pFS->singleElementNS(XML_p, XML_sldLayoutId,
                         XML_id, OString::number(GetNewSlideMasterId()),
                         FSNS(XML_r, XML_id), sRelId);
}
 
static bool lcl_ContainsEquivalentObject(const SdrPage* pPage, const SdrObject* pObj)
{
    bool bFound = false;
 
    if (!pPage || !pObj)
        return bFound;
 
    for (size_t nObj = 0; nObj < pPage->GetObjCount(); ++nObj)
    {
        SdrObject* pObjNext = pPage->GetObj(nObj);
        if (pObjNext && pObjNext->GetMergedItemSet().Equals(pObj->GetMergedItemSet(), false))
        {
            bFound = true;
            break;
        }
    }
 
    return bFound;
}
 
static bool lcl_ComparePageObjects(const SdrPage* pMasterPage, SdrPage* pMasterNext)
{
    if (!pMasterPage || !pMasterNext)
        return false;
 
    SdrObject* pObjNext;
    SdrLayerID aLayer = pMasterNext->GetLayerAdmin().GetLayerID(sUNO_LayerName_background_objects);
 
    for (size_t nObj = 0; nObj < pMasterPage->GetObjCount(); ++nObj)
    {
        pObjNext = pMasterPage->GetObj(nObj);
        if (!pObjNext || pObjNext->GetLayer() == aLayer)
            continue;
 
        if (!lcl_ContainsEquivalentObject(pMasterNext, pObjNext))
            return false;
    }
 
    // No differences found
    return true;
}
 
static bool lcl_ComparePageProperties(SdrPage* pMasterPage, SdrPage* pMasterNext)
{
    if (!pMasterPage || !pMasterNext)
        return false;
 
    SdrPageProperties& rProperties = pMasterPage->getSdrPageProperties();
    SdrPageProperties& rNextProperties = pMasterNext->getSdrPageProperties();
 
    return rProperties.GetItemSet().Equals(rNextProperties.GetItemSet(), false)
           && rProperties.getTheme().get() == rNextProperties.getTheme().get();
}
 
void PowerPointExport::FindEquivalentMasterPages()
{
    css::uno::Reference<css::drawing::XDrawPages> xDrawPages(
        mXModel->getMasterPages());
    maMastersLayouts.resize(mnMasterPages);
    maEquivalentMasters.resize(mnMasterPages, SAL_MAX_UINT32);
    for (sal_uInt32 i = 0; i < mnMasterPages; i++)
    {
        css::uno::Reference<css::drawing::XDrawPage> xDrawPage;
        uno::Any aAny(xDrawPages->getByIndex(i));
        aAny >>= xDrawPage;
        if (!xDrawPage.is())
            continue;
 
        maMastersLayouts[i] = std::make_pair(SdPage::getImplementation(xDrawPage), -1);
        uno::Reference<beans::XPropertySet> xPagePropSet(xDrawPage, uno::UNO_QUERY_THROW);
        if (xPagePropSet.is())
        {
            uno::Any aLayout = xPagePropSet->getPropertyValue("SlideLayout");
            if (aLayout.hasValue())
            {
                aLayout >>= maMastersLayouts[i].second;
            }
        }
    }
 
    for (sal_uInt32 i = 0; i < mnMasterPages; i++)
    {
        if (!maMastersLayouts[i].first || maEquivalentMasters[i] != SAL_MAX_UINT32)
            continue;
        for (sal_uInt32 j = i + 1; j < mnMasterPages; j++)
        {
            if (!maMastersLayouts[j].first || maEquivalentMasters[j] != SAL_MAX_UINT32)
                continue;
 
            if (lcl_ComparePageProperties(maMastersLayouts[i].first, maMastersLayouts[j].first)
                && lcl_ComparePageObjects(maMastersLayouts[i].first, maMastersLayouts[j].first))
            {
                // If both masters have the same properties and objects,
                // we assume they are the same and only export the first one
                maEquivalentMasters[j] = i;
                maEquivalentMasters[i] = i;
            }
        }
    }
}
 
sal_uInt32 PowerPointExport::GetEquivalentMasterPage(sal_uInt32 nMasterPage)
{
    if (maEquivalentMasters.size() == 0)
        FindEquivalentMasterPages();
    return maEquivalentMasters[nMasterPage];
}
 
void PowerPointExport::ImplWriteSlideMaster(sal_uInt32 nPageNum, Reference< XPropertySet > const& aXBackgroundPropSet)
{
    SAL_INFO("sd.eppt", "write master slide: " << nPageNum << "\n--------------");
 
    if (nPageNum != GetEquivalentMasterPage(nPageNum)
        && GetEquivalentMasterPage(nPageNum) != SAL_MAX_UINT32)
    {
        // It's equivalent to an already written master, write only the layouts files
        OUString aSlideName;
        Reference<XNamed> xNamed(mXDrawPage, UNO_QUERY);
        if (xNamed.is())
            aSlideName = xNamed->getName();
        for (int i = 0; i < AUTOLAYOUT_END; ++i)
        {
            if (mLayoutInfo[i].mnFileIdArray.size() > nPageNum
                && mLayoutInfo[i].mnFileIdArray[nPageNum] > 0)
            {
                ImplWritePPTXLayoutWithContent(i, nPageNum, aSlideName, aXBackgroundPropSet);
            }
        }
 
        // Close the list tag if it was the last one
        if (nPageNum == mnMasterPages - 1)
            mPresentationFS->endElementNS(XML_p, XML_sldMasterIdLst);
 
        return;
    }
 
    // slides list
    if (nPageNum == 0)
        mPresentationFS->startElementNS(XML_p, XML_sldMasterIdLst);
 
    OUString sRelId = addRelation(mPresentationFS->getOutputStream(),
                                  oox::getRelationship(Relationship::SLIDEMASTER),
                                  Concat2View("slideMasters/slideMaster" + OUString::number(nPageNum + 1) + ".xml"));
 
    mPresentationFS->singleElementNS(XML_p, XML_sldMasterId,
                                     XML_id, OString::number(GetNewSlideMasterId()),
                                     FSNS(XML_r, XML_id), sRelId);
 
    if (nPageNum == mnMasterPages - 1)
        mPresentationFS->endElementNS(XML_p, XML_sldMasterIdLst);
 
    FSHelperPtr pFS =
        openFragmentStreamWithSerializer("ppt/slideMasters/slideMaster" +
                                          OUString::number(nPageNum + 1) + ".xml",
                                         u"application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"_ustr);
 
    SdrPage* pMasterPage = SdPage::getImplementation(mXDrawPage);
    model::Theme* pTheme = nullptr;
    if (pMasterPage)
    {
        pTheme = pMasterPage->getSdrPageProperties().getTheme().get();
    }
 
    // write theme per master
    WriteTheme(mnThemeIdMax, pTheme);
 
    // add implicit relation to the presentation theme
    addRelation(pFS->getOutputStream(), oox::getRelationship(Relationship::THEME),
                Concat2View("../theme/theme" + OUString::number(++mnThemeIdMax) + ".xml"));
 
    pFS->startElementNS(XML_p, XML_sldMaster, presentationNamespaces(*this));
 
    pFS->startElementNS(XML_p, XML_cSld);
 
    if (GetEquivalentMasterPage(nPageNum) != nPageNum)
    {
        if (aXBackgroundPropSet)
            ImplWriteBackground(pFS, aXBackgroundPropSet);
        WriteShapeTree(pFS, MASTER, true);
    }
    else
    {
        // Minimal shape tree, the actual one will be written in the layout file.
        pFS->startElementNS(XML_p, XML_spTree);
        pFS->write(MAIN_GROUP);
        pFS->endElementNS(XML_p, XML_spTree);
    }
 
    pFS->endElementNS(XML_p, XML_cSld);
 
    uno::Sequence<beans::PropertyValue> aGrabBag;
    if (mXModel->getPropertySetInfo()->hasPropertyByName(u"InteropGrabBag"_ustr))
        mXModel->getPropertyValue(u"InteropGrabBag"_ustr) >>= aGrabBag;
 
    std::vector<OUString> aClrMap;
    aClrMap.reserve(12);
    uno::Sequence<beans::PropertyValue> aClrMapPropValue;
    if(aGrabBag.hasElements())
    {
        for (const auto& rProp : aGrabBag)
        {
            if (rProp.Name == "OOXColorMap")
            {
                rProp.Value >>= aClrMapPropValue;
                break;
            }
        }
    }
 
    if (aClrMapPropValue.getLength())
    {
        OUString sName;
        sal_Int32 nToken = XML_TOKEN_INVALID;
        for(const auto& item : aClrMapPropValue)
        {
            item.Value >>= nToken;
            switch (nToken)
            {
                case XML_dk1:      sName = u"dk1"_ustr;      break;
                case XML_lt1:      sName = u"lt1"_ustr;      break;
                case XML_dk2:      sName = u"dk2"_ustr;      break;
                case XML_lt2:      sName = u"lt2"_ustr;      break;
                case XML_accent1:  sName = u"accent1"_ustr;  break;
                case XML_accent2:  sName = u"accent2"_ustr;  break;
                case XML_accent3:  sName = u"accent3"_ustr;  break;
                case XML_accent4:  sName = u"accent4"_ustr;  break;
                case XML_accent5:  sName = u"accent5"_ustr;  break;
                case XML_accent6:  sName = u"accent6"_ustr;  break;
                case XML_hlink:    sName = u"hlink"_ustr;    break;
                case XML_folHlink: sName = u"folHlink"_ustr; break;
            }
            aClrMap.push_back(sName);
        }
        assert(aClrMap.size() == 12 && "missing entries for ClrMap");
    }
    else
    {
        // default clrMap to export ".odp" files to ".pptx"
        aClrMap = { u"lt1"_ustr,     u"dk1"_ustr,     u"lt2"_ustr,     u"dk2"_ustr,
                    u"accent1"_ustr, u"accent2"_ustr, u"accent3"_ustr, u"accent4"_ustr,
                    u"accent5"_ustr, u"accent6"_ustr, u"hlink"_ustr,   u"folHlink"_ustr };
    }
 
    pFS->singleElementNS(XML_p, XML_clrMap,
                         XML_bg1, aClrMap[0],
                         XML_tx1, aClrMap[1],
                         XML_bg2, aClrMap[2],
                         XML_tx2, aClrMap[3],
                         XML_accent1, aClrMap[4],
                         XML_accent2, aClrMap[5],
                         XML_accent3, aClrMap[6],
                         XML_accent4, aClrMap[7],
                         XML_accent5, aClrMap[8],
                         XML_accent6, aClrMap[9],
                         XML_hlink, aClrMap[10],
                         XML_folHlink, aClrMap[11]);
 
    // use master's id type as they have same range, mso does that as well
    pFS->startElementNS(XML_p, XML_sldLayoutIdLst);
 
    auto getLayoutsUsedForMaster = [](SdrPage* pMaster) -> std::unordered_set<sal_Int32>
    {
        if (!pMaster)
            return {};
 
        std::unordered_set<sal_Int32> aUsedLayouts{};
        for (const auto* pPageUser : pMaster->GetPageUsers())
        {
            const auto* pMasterPageDescriptor
                = dynamic_cast<const sdr::MasterPageDescriptor*>(pPageUser);
 
            if (!pMasterPageDescriptor)
                continue;
 
            AutoLayout eLayout
                = static_cast<SdPage&>(pMasterPageDescriptor->GetOwnerPage()).GetAutoLayout();
            aUsedLayouts.insert(eLayout);
        }
        return aUsedLayouts;
    };
 
    std::unordered_set<sal_Int32> aLayouts = getLayoutsUsedForMaster(pMasterPage);
 
    css::uno::Reference< css::beans::XPropertySet > xPagePropSet;
    xPagePropSet.set(mXDrawPage, UNO_QUERY);
    if (xPagePropSet.is())
    {
        uno::Any aAny;
        if (GetPropertyValue(aAny, xPagePropSet, u"SlideLayout"_ustr))
            aLayouts.insert(aAny.get<sal_Int32>());
    }
 
    OUString aSlideName;
    Reference< XNamed > xNamed(mXDrawPage, UNO_QUERY);
    if (xNamed.is())
        aSlideName = xNamed->getName();
 
    for (auto nLayout : aLayouts)
    {
        if (GetEquivalentMasterPage(nPageNum) == nPageNum)
            ImplWritePPTXLayoutWithContent(nLayout, nPageNum, aSlideName, aXBackgroundPropSet);
        else
            ImplWritePPTXLayout(nLayout, nPageNum, aSlideName);
        AddLayoutIdAndRelation(pFS, GetLayoutFileId(nLayout, nPageNum));
    }
 
    // Add layouts of other Impress masters that came from a single pptx master with multiple layouts
    for (sal_uInt32 i = 0; i < mnMasterPages; i++)
    {
        if (i != nPageNum && maEquivalentMasters[i] == nPageNum)
        {
            aLayouts = getLayoutsUsedForMaster(maMastersLayouts[i].first);
            if (maMastersLayouts[i].second != -1)
                aLayouts.insert(maMastersLayouts[i].second);
 
            for (auto nLayout : aLayouts)
            {
                // Reserve layout file Id to be written later
                if (mLayoutInfo[nLayout].mnFileIdArray.size() < mnMasterPages)
                    mLayoutInfo[nLayout].mnFileIdArray.resize(mnMasterPages);
                mLayoutInfo[nLayout].mnFileIdArray[i] = mnLayoutFileIdMax;
                mnLayoutFileIdMax++;
 
                AddLayoutIdAndRelation(pFS, GetLayoutFileId(nLayout, i));
            }
        }
    }
 
    pFS->endElementNS(XML_p, XML_sldLayoutIdLst);
 
    pFS->endElementNS(XML_p, XML_sldMaster);
 
    SAL_INFO("sd.eppt", "----------------");
 
    pFS->endDocument();
}
 
sal_Int32 PowerPointExport::GetLayoutFileId(sal_Int32 nOffset, sal_uInt32 nMasterNum)
{
    SAL_INFO("sd.eppt", "GetLayoutFileId offset: " << nOffset << " master: " << nMasterNum);
    if (mLayoutInfo[ nOffset ].mnFileIdArray.size() <= nMasterNum)
        return 0;
 
    return mLayoutInfo[ nOffset ].mnFileIdArray[ nMasterNum ];
}
 
void PowerPointExport::ImplWritePPTXLayout(sal_Int32 nOffset, sal_uInt32 nMasterNum, const OUString& aSlideName)
{
    SAL_INFO("sd.eppt", "write layout: " << nOffset);
 
    Reference< drawing::XDrawPages > xDrawPages = mXModel->getDrawPages();
    Reference< drawing::XDrawPage > xSlide = xDrawPages->insertNewByIndex(xDrawPages->getCount());
 
#if OSL_DEBUG_LEVEL >= 2
    if (xSlide.is())
        printf("new page created\n");
#endif
 
    Reference< beans::XPropertySet > xPropSet(xSlide, uno::UNO_QUERY);
    xPropSet->setPropertyValue(u"Layout"_ustr, Any(short(aLayoutInfo[ nOffset ].nType)));
#if OSL_DEBUG_LEVEL > 1
    dump_pset(xPropSet);
#endif
    mXPagePropSet.set(xSlide, UNO_QUERY);
    mXShapes = xSlide;
 
    if (mLayoutInfo[ nOffset ].mnFileIdArray.size() < mnMasterPages)
    {
        mLayoutInfo[ nOffset ].mnFileIdArray.resize(mnMasterPages);
    }
 
    if (mLayoutInfo[ nOffset ].mnFileIdArray[ nMasterNum ] != 0)
        return;
 
    FSHelperPtr pFS
        = openFragmentStreamWithSerializer("ppt/slideLayouts/slideLayout" +
                                            OUString::number(mnLayoutFileIdMax) + ".xml",
                                           u"application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"_ustr);
 
    // add implicit relation of slide layout to slide master
    addRelation(
        pFS->getOutputStream(), oox::getRelationship(Relationship::SLIDEMASTER),
        Concat2View("../slideMasters/slideMaster" + OUString::number(nMasterNum + 1) + ".xml"));
 
    auto pAttributes = presentationNamespaces(*this);
    pAttributes->add(XML_type, aLayoutInfo[nOffset].sType);
    pAttributes->add(XML_preserve, "1");
 
    pFS->startElementNS(XML_p, XML_sldLayout, pAttributes);
 
    if (!aSlideName.isEmpty())
    {
        pFS->startElementNS(XML_p, XML_cSld,
            XML_name, aSlideName);
    }
    else
    {
        pFS->startElementNS(XML_p, XML_cSld,
            XML_name, aLayoutInfo[nOffset].sName);
    }
    //pFS->write( MINIMAL_SPTREE ); // TODO: write actual shape tree
    WriteShapeTree(pFS, LAYOUT, true);
 
    pFS->endElementNS(XML_p, XML_cSld);
 
    pFS->endElementNS(XML_p, XML_sldLayout);
 
    mLayoutInfo[ nOffset ].mnFileIdArray[ nMasterNum ] = mnLayoutFileIdMax;
 
    mnLayoutFileIdMax ++;
 
    xDrawPages->remove(xSlide);
 
    pFS->endDocument();
}
 
void PowerPointExport::ImplWritePPTXLayoutWithContent(
    sal_Int32 nOffset, sal_uInt32 nMasterNum, const OUString& aSlideName,
    Reference<XPropertySet> const& aXBackgroundPropSet)
{
    SAL_INFO("sd.eppt", "write layout: " << nOffset);
 
    if (mLayoutInfo[nOffset].mnFileIdArray.size() < mnMasterPages)
    {
        mLayoutInfo[nOffset].mnFileIdArray.resize(mnMasterPages);
    }
 
    if (mLayoutInfo[nOffset].mnFileIdArray[nMasterNum] == 0)
    {
        mLayoutInfo[nOffset].mnFileIdArray[nMasterNum] = mnLayoutFileIdMax;
        mnLayoutFileIdMax++;
    }
    sal_Int32 nLayoutFileId = mLayoutInfo[nOffset].mnFileIdArray[nMasterNum];
 
    FSHelperPtr pFS = openFragmentStreamWithSerializer(
        "ppt/slideLayouts/slideLayout" + OUString::number(nLayoutFileId) + ".xml",
        "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml");
 
    // add implicit relation of slide layout to slide master
    addRelation(pFS->getOutputStream(), oox::getRelationship(Relationship::SLIDEMASTER),
                Concat2View("../slideMasters/slideMaster"
                            + OUString::number(GetEquivalentMasterPage(nMasterNum) + 1) + ".xml"));
 
    auto pAttributes = presentationNamespaces(*this);
    pAttributes->add(XML_type, aLayoutInfo[nOffset].sType);
    pAttributes->add(XML_preserve, "1");
 
    pFS->startElementNS(XML_p, XML_sldLayout, pAttributes);
 
    if (!aSlideName.isEmpty())
    {
        pFS->startElementNS(XML_p, XML_cSld, XML_name, aSlideName);
    }
    else
    {
        pFS->startElementNS(XML_p, XML_cSld, XML_name, aLayoutInfo[nOffset].sName);
    }
 
    if (aXBackgroundPropSet)
        ImplWriteBackground(pFS, aXBackgroundPropSet);
 
    WriteShapeTree(pFS, MASTER, true);
 
    pFS->endElementNS(XML_p, XML_cSld);
 
    pFS->endElementNS(XML_p, XML_sldLayout);
 
    pFS->endDocument();
}
 
void PowerPointExport::WriteShapeTree(const FSHelperPtr& pFS, PageType ePageType, bool bMaster)
{
    PowerPointShapeExport aDML(pFS, &maShapeMap, this);
    aDML.SetMaster(bMaster);
    aDML.SetPageType(ePageType);
    aDML.SetBackgroundDark(mbIsBackgroundDark);
 
    pFS->startElementNS(XML_p, XML_spTree);
    pFS->write(MAIN_GROUP);
 
    // MS Office complains if a shape ID is duplicated, and MAIN_GROUP specified p:cNvPr id="1"
    if (GetMaxDocId() < 2)
        SetMaxDocId(2); // this will be the next GetUniqueId()
 
    ResetGroupTable(mXShapes->getCount());
 
    while (GetNextGroupEntry())
    {
 
        sal_uInt32 nGroups = GetGroupsClosed();
        for (sal_uInt32 i = 0; i < nGroups; i++)
        {
            SAL_INFO("sd.eppt", "leave group");
        }
 
        if (GetShapeByIndex(GetCurrentGroupIndex(), true))
        {
            SAL_INFO("sd.eppt", "mType: " << mType);
            const SdrObjGroup* pDiagramCandidate(dynamic_cast<const SdrObjGroup*>(SdrObject::getSdrObjectFromXShape(mXShape)));
            bool bIsDiagram(nullptr != pDiagramCandidate && pDiagramCandidate->isDiagram());
 
            // check if it was modified. For now, since we have no ppt export for Diagram,
            // do not export as Diagram, that would be empty if the GrabBag was deleted
            if (bIsDiagram && pDiagramCandidate->getDiagramHelper()->isModified())
                bIsDiagram = false;
 
            if (bIsDiagram)
                WriteDiagram(pFS, aDML, mXShape, mnDiagramId++);
            else
                aDML.WriteShape(mXShape);
        }
    }
 
    if ( ePageType == NORMAL || ePageType == LAYOUT )
        WritePlaceholderReferenceShapes(aDML, ePageType);
    pFS->endElementNS(XML_p, XML_spTree);
}
 
ShapeExport& PowerPointShapeExport::WritePageShape(const Reference< XShape >& xShape, PageType ePageType, bool bPresObj)
{
    if ((ePageType == NOTICE && bPresObj) || ePageType == LAYOUT || ePageType == MASTER)
        return WritePlaceholderShape(xShape, SlideImage);
 
    return WriteTextShape(xShape);
}
 
bool PowerPointShapeExport::WritePlaceholder(const Reference< XShape >& xShape, PlaceholderType ePlaceholder, bool bMaster)
{
    SAL_INFO("sd.eppt", "WritePlaceholder " << bMaster << " " << ShapeExport::NonEmptyText(xShape));
    if (!xShape)
        return false;
    try
    {
        Reference<XPropertySet> xShapeProps(xShape, UNO_QUERY);
        if (xShapeProps->getPropertyValue(u"IsPresentationObject"_ustr).get<bool>())
        {
            WritePlaceholderShape(xShape, ePlaceholder);
 
            return true;
        }
    }
    catch (Exception&)
    {
        return false;
    }
    return false;
}
 
ShapeExport& PowerPointShapeExport::WritePlaceholderShape(const Reference< XShape >& xShape, PlaceholderType ePlaceholder)
{
    Reference<XPropertySet> xProps(xShape, UNO_QUERY);
    bool bUseBackground(false);
    if (xProps.is() && xProps->getPropertySetInfo()->hasPropertyByName(u"FillUseSlideBackground"_ustr))
        xProps->getPropertyValue(u"FillUseSlideBackground"_ustr) >>= bUseBackground;
 
    if (bUseBackground)
        mpFS->startElementNS(XML_p, XML_sp, XML_useBgFill, "1");
    else
        mpFS->startElementNS(XML_p, XML_sp);
 
    // non visual shape properties
    mpFS->startElementNS(XML_p, XML_nvSpPr);
    const OString aPlaceholderID("PlaceHolder " + OString::number(mnShapeIdMax++));
    WriteNonVisualDrawingProperties(xShape, aPlaceholderID.getStr());
    mpFS->startElementNS(XML_p, XML_cNvSpPr);
    mpFS->singleElementNS(XML_a, XML_spLocks, XML_noGrp, "1");
    mpFS->endElementNS(XML_p, XML_cNvSpPr);
    mpFS->startElementNS(XML_p, XML_nvPr);
 
    bool bUsePlaceholderIndex
        = ePlaceholder == Footer || ePlaceholder == DateAndTime || ePlaceholder == SlideNumber;
    const char* pType = getPlaceholderTypeName(ePlaceholder);
 
    SAL_INFO("sd.eppt", "write placeholder " << pType);
 
    // export Custom Prompt
    bool bUseCustomPrompt(false);
    if (xProps.is() && xProps->getPropertySetInfo()->hasPropertyByName(u"CustomPromptText"_ustr))
    {
        OUString aCustomPromptText;
        xProps->getPropertyValue(u"CustomPromptText"_ustr) >>= aCustomPromptText;
        if (!aCustomPromptText.isEmpty())
            bUseCustomPrompt = true;
    }
 
    if (bUsePlaceholderIndex)
    {
        mpFS->singleElementNS(
            XML_p, XML_ph, XML_type, pType, XML_idx,
            OString::number(
                static_cast<PowerPointExport*>(GetFB())->CreateNewPlaceholderIndex(xShape)),
                    XML_hasCustomPrompt, sax_fastparser::UseIf("1", bUseCustomPrompt));
    }
    else
    {
        if ((mePageType == PageType::LAYOUT || mePageType == PageType::NORMAL)
            && ePlaceholder == Outliner)
            mpFS->singleElementNS(XML_p, XML_ph, XML_hasCustomPrompt, sax_fastparser::UseIf("1", bUseCustomPrompt));
        else
            mpFS->singleElementNS(XML_p, XML_ph, XML_type, pType, XML_hasCustomPrompt, sax_fastparser::UseIf("1", bUseCustomPrompt));
    }
    mpFS->endElementNS(XML_p, XML_nvPr);
    mpFS->endElementNS(XML_p, XML_nvSpPr);
 
    // visual shape properties
    mpFS->startElementNS(XML_p, XML_spPr);
    WriteShapeTransformation(xShape, XML_a);
    WritePresetShape("rect"_ostr);
    if (xProps.is())
    {
        WriteBlipFill(xProps, u"Graphic"_ustr);
        // Do not forget to export the visible properties.
        WriteFill( xProps, xShape->getSize());
        WriteOutline( xProps );
        WriteShapeEffects( xProps );
 
        bool bHas3DEffectinShape = false;
        uno::Sequence<beans::PropertyValue> grabBag;
        if (xProps->getPropertySetInfo()->hasPropertyByName(u"InteropGrabBag"_ustr))
            xProps->getPropertyValue(u"InteropGrabBag"_ustr) >>= grabBag;
 
        for (auto const& it : grabBag)
            if (it.Name == "3DEffectProperties")
                bHas3DEffectinShape = true;
 
        if( bHas3DEffectinShape)
            Write3DEffects( xProps, /*bIsText=*/false );
    }
    mpFS->endElementNS(XML_p, XML_spPr);
 
    bool bWritePropertiesAsLstStyles
        = (mePageType == PageType::MASTER)
          && (ePlaceholder == Title || ePlaceholder == Subtitle || ePlaceholder == Outliner);
 
    WriteTextBox(xShape, XML_p,
                 bUsePlaceholderIndex || bWritePropertiesAsLstStyles);
 
    mpFS->endElementNS(XML_p, XML_sp);
 
    return *this;
}
 
ShapeExport& PowerPointShapeExport::WritePlaceholderReferenceShape(
    PlaceholderType ePlaceholder, sal_Int32 nReferencedPlaceholderIdx, PageType ePageType,
    const Reference<XPropertySet>& rXPagePropSet)
{
    mpFS->startElementNS(XML_p, XML_sp);
 
    // non visual shape properties
    mpFS->startElementNS(XML_p, XML_nvSpPr);
    const OString aPlaceholderID("PlaceHolder " + OString::number(mnShapeIdMax++));
    GetFS()->singleElementNS(XML_p, XML_cNvPr, XML_id, OString::number(mnShapeIdMax), XML_name,
                             aPlaceholderID);
 
    mpFS->startElementNS(XML_p, XML_cNvSpPr);
    mpFS->singleElementNS(XML_a, XML_spLocks, XML_noGrp, "1");
    mpFS->endElementNS(XML_p, XML_cNvSpPr);
    mpFS->startElementNS(XML_p, XML_nvPr);
 
    const char* pType = getPlaceholderTypeName(ePlaceholder);
    mpFS->singleElementNS(XML_p, XML_ph, XML_type, pType, XML_idx,
                          OString::number(nReferencedPlaceholderIdx));
    mpFS->endElementNS(XML_p, XML_nvPr);
    mpFS->endElementNS(XML_p, XML_nvSpPr);
 
    // visual shape properties
    mpFS->startElementNS(XML_p, XML_spPr);
    mpFS->endElementNS(XML_p, XML_spPr);
 
    WritePlaceholderReferenceTextBody(ePlaceholder, ePageType, rXPagePropSet);
 
    mpFS->endElementNS(XML_p, XML_sp);
 
    return *this;
}
 
ShapeExport& PowerPointShapeExport::WritePlaceholderReferenceTextBody(
    PlaceholderType ePlaceholder, PageType ePageType, const Reference<XPropertySet>& xPagePropSet)
{
    mpFS->startElementNS(XML_p, XML_txBody);
    mpFS->singleElementNS(XML_a, XML_bodyPr);
    mpFS->startElementNS(XML_a, XML_p);
 
    switch (ePlaceholder)
    {
        case Header:
            break;
        case Footer:
        {
            OUString aFooterText;
            if (ePageType == LAYOUT)
            {
                aFooterText = "Footer";
            }
            else
            {
                xPagePropSet->getPropertyValue(u"FooterText"_ustr) >>= aFooterText;
            }
            mpFS->startElementNS(XML_a, XML_r);
            mpFS->startElementNS(XML_a, XML_t);
            mpFS->writeEscaped(aFooterText);
            mpFS->endElementNS(XML_a, XML_t);
            mpFS->endElementNS(XML_a, XML_r);
            break;
        }
        case SlideNumber:
        {
            OUString aSlideNum;
            sal_Int32 nSlideNum = 0;
            if (ePageType == LAYOUT)
            {
                aSlideNum = "<#>";
            }
            else
            {
                xPagePropSet->getPropertyValue(u"Number"_ustr) >>= nSlideNum;
                aSlideNum = OUString::number(nSlideNum);
            }
            OString aUUID(comphelper::xml::generateGUIDString());
            mpFS->startElementNS(XML_a, XML_fld, XML_id, aUUID, XML_type, "slidenum");
            mpFS->startElementNS(XML_a, XML_t);
            mpFS->writeEscaped(aSlideNum);
            mpFS->endElementNS(XML_a, XML_t);
            mpFS->endElementNS(XML_a, XML_fld);
            break;
        }
        case DateAndTime:
        {
            OUString aDateTimeType = u"datetime1"_ustr;
            bool bIsDateTimeFixed = false;
            xPagePropSet->getPropertyValue(u"IsDateTimeFixed"_ustr) >>= bIsDateTimeFixed;
 
            OUString aDateTimeText = u"Date"_ustr;
            const LanguageTag& rLanguageTag = Application::GetSettings().GetLanguageTag();
 
            if(ePageType != LAYOUT && !bIsDateTimeFixed)
            {
                sal_Int32 nDateTimeFormat = 0;
                xPagePropSet->getPropertyValue(u"DateTimeFormat"_ustr) >>= nDateTimeFormat;
 
                // 4 LSBs represent the date
                SvxDateFormat eDate = static_cast<SvxDateFormat>(nDateTimeFormat & 0x0f);
                // the 4 bits after the date bits represent the time
                SvxTimeFormat eTime = static_cast<SvxTimeFormat>(nDateTimeFormat >> 4);
                aDateTimeType = GetDatetimeTypeFromDateTime(eDate, eTime);
 
                if (aDateTimeType == "datetime")
                    aDateTimeType = "datetime1";
 
                ::DateTime aDateTime( ::DateTime::SYSTEM );
 
                aDateTimeText = SvxDateTimeField::GetFormatted(
                    aDateTime, aDateTime, eDate,
                    eTime, *(SdModule::get()->GetNumberFormatter()),
                    rLanguageTag.getLanguageType());
            }
 
            if(!bIsDateTimeFixed)
            {
                OString aUUID(comphelper::xml::generateGUIDString());
                mpFS->startElementNS(XML_a, XML_fld, XML_id, aUUID, XML_type, aDateTimeType);
            }
            else
            {
                xPagePropSet->getPropertyValue(u"DateTimeText"_ustr) >>= aDateTimeText;
                mpFS->startElementNS(XML_a, XML_r);
            }
 
            mpFS->startElementNS(XML_a, XML_rPr, XML_lang, rLanguageTag.getBcp47MS());
            mpFS->endElementNS(XML_a, XML_rPr);
 
            mpFS->startElementNS(XML_a, XML_t);
            mpFS->writeEscaped(aDateTimeText);
            mpFS->endElementNS(XML_a, XML_t);
 
            mpFS->endElementNS(XML_a, bIsDateTimeFixed ? XML_r : XML_fld);
            break;
        }
        default:
            SAL_INFO("sd.eppt", "warning: no defined textbody for referenced placeholder type: "
                                    << ePlaceholder);
    }
    mpFS->endElementNS(XML_a, XML_p);
    mpFS->endElementNS(XML_p, XML_txBody);
 
    return *this;
}
 
void PowerPointExport::WriteDefaultColorSchemes(const FSHelperPtr& pFS)
{
    for (int nId = PredefinedClrSchemeId::dk2; nId != PredefinedClrSchemeId::Count; nId++)
    {
        OUString sName(getPredefinedClrNames(static_cast<PredefinedClrSchemeId>(nId)));
        sal_Int32 nColor = 0;
 
        switch (nId)
        {
        case PredefinedClrSchemeId::dk2:
            nColor = 0x1F497D;
            break;
        case PredefinedClrSchemeId::lt2:
            nColor = 0xEEECE1;
            break;
        case PredefinedClrSchemeId::accent1:
            nColor = 0x4F81BD;
            break;
        case PredefinedClrSchemeId::accent2:
            nColor = 0xC0504D;
            break;
        case PredefinedClrSchemeId::accent3:
            nColor = 0x9BBB59;
            break;
        case PredefinedClrSchemeId::accent4:
            nColor = 0x8064A2;
            break;
        case PredefinedClrSchemeId::accent5:
            nColor = 0x4BACC6;
            break;
        case PredefinedClrSchemeId::accent6:
            nColor = 0xF79646;
            break;
        case PredefinedClrSchemeId::hlink:
            nColor = 0x0000FF;
            break;
        case PredefinedClrSchemeId::folHlink:
            nColor = 0x800080;
            break;
        }
 
        OUString sOpenColorScheme = "<a:" + sName + ">";
        pFS->write(sOpenColorScheme);
 
        pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(nColor));
 
        OUString sCloseColorScheme = "</a:" + sName + ">";
        pFS->write(sCloseColorScheme);
    }
}
 
void PowerPointExport::WriteTheme(sal_Int32 nThemeNum, const model::Theme* pTheme)
{
    if (!pTheme)
        return;
    OUString sThemePath = "ppt/theme/theme" + OUString::number(nThemeNum + 1) + ".xml";
 
    oox::ThemeExport aThemeExport(this, DOCUMENT_PPTX);
 
    aThemeExport.write(sThemePath, *pTheme);
}
 
bool PowerPointExport::ImplCreateDocument()
{
    mbCreateNotes = false;
 
    for (sal_uInt32 i = 0; i < mnPages; i++)
    {
        if (!GetPageByIndex(i, NOTICE))
            return false;
 
        if (ContainsOtherShapeThanPlaceholders())
        {
            mbCreateNotes = true;
            break;
        }
    }
 
    return true;
}
 
void PowerPointExport::WriteNotesMaster()
{
    SAL_INFO("sd.eppt", "write Notes master\n---------------");
 
    mPresentationFS->startElementNS(XML_p, XML_notesMasterIdLst);
 
    OUString sRelId = addRelation(mPresentationFS->getOutputStream(),
                                  oox::getRelationship(Relationship::NOTESMASTER),
                                  u"notesMasters/notesMaster1.xml");
 
    mPresentationFS->singleElementNS(XML_p, XML_notesMasterId,
                                     FSNS(XML_r, XML_id), sRelId);
 
    mPresentationFS->endElementNS(XML_p, XML_notesMasterIdLst);
 
    FSHelperPtr pFS =
        openFragmentStreamWithSerializer(u"ppt/notesMasters/notesMaster1.xml"_ustr,
                                         u"application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml"_ustr);
    // write theme per master
 
    // TODO: Need to implement theme support for note master, so the
    // note master has his own theme associated.
 
    // For now just use the default theme
    auto const* pDefaultColorSet = svx::ColorSets::get().getColorSet(u"LibreOffice");
    if (pDefaultColorSet)
    {
        auto pTheme = std::make_shared<model::Theme>("Office Theme");
        pTheme->setColorSet(std::make_shared<model::ColorSet>(*pDefaultColorSet));
 
        WriteTheme(mnMasterPages, pTheme.get());
 
        // add implicit relation to the presentation theme
        addRelation(pFS->getOutputStream(),
                    oox::getRelationship(Relationship::THEME),
                    Concat2View("../theme/theme" + OUString::number(mnMasterPages + 1) + ".xml"));
    }
 
    pFS->startElementNS(XML_p, XML_notesMaster, presentationNamespaces(*this));
 
    pFS->startElementNS(XML_p, XML_cSld);
 
    Reference< XPropertySet > aXBackgroundPropSet;
    if (ImplGetPropertyValue(mXPagePropSet, u"Background"_ustr) &&
            (mAny >>= aXBackgroundPropSet))
        ImplWriteBackground(pFS, aXBackgroundPropSet);
 
    WriteShapeTree(pFS, NOTICE, true);
 
    pFS->endElementNS(XML_p, XML_cSld);
 
    // color map - now it uses colors from hardcoded theme, once we eventually generate theme, this might need update
    pFS->singleElementNS(XML_p, XML_clrMap,
                         XML_bg1, "lt1",
                         XML_bg2, "lt2",
                         XML_tx1, "dk1",
                         XML_tx2, "dk2",
                         XML_accent1, "accent1",
                         XML_accent2, "accent2",
                         XML_accent3, "accent3",
                         XML_accent4, "accent4",
                         XML_accent5, "accent5",
                         XML_accent6, "accent6",
                         XML_hlink, "hlink",
                         XML_folHlink, "folHlink");
 
    pFS->endElementNS(XML_p, XML_notesMaster);
 
    SAL_INFO("sd.eppt", "----------------");
 
    pFS->endDocument();
}
 
void PowerPointExport::embedEffectAudio(const FSHelperPtr& pFS, const OUString& sUrl, OUString& sRelId, OUString& sName)
{
    comphelper::LifecycleProxy aProxy;
 
    if (!sUrl.endsWithIgnoreAsciiCase(".wav"))
        return;
 
    uno::Reference<io::XInputStream> xAudioStream;
    try
    {
        if (sUrl.startsWith("vnd.sun.star.Package:"))
        {
            uno::Reference<embed::XStorage> xDocumentStorage = mXModel->getDocumentStorage();
            if (!xDocumentStorage.is())
                return;
 
            uno::Reference<io::XStream> xStream = comphelper::OStorageHelper::GetStreamAtPackageURL(xDocumentStorage, sUrl,
                                                        css::embed::ElementModes::READ, aProxy);
 
            if (xStream.is())
                xAudioStream = xStream->getInputStream();
        }
        else
            xAudioStream = comphelper::OStorageHelper::GetInputStreamFromURL(sUrl, getComponentContext());
    }
    catch (const Exception&)
    {
        TOOLS_WARN_EXCEPTION("sd", "PowerPointExport::embedEffectAudio");
    }
 
    if (!xAudioStream.is())
        return;
 
    sName = INetURLObject::decode(sUrl, INetURLObject::DecodeMechanism::ToIUri, RTL_TEXTENCODING_UTF8);
    int nLastSlash = sName.lastIndexOf('/');
    sName = sName.copy(nLastSlash >= 0 ? nLastSlash + 1 : 0);
 
    // MS PowerPoint reports a corrupt file if the media name contains non-ascii characters
    OUString sAsciiName;
    if (comphelper::string::isValidAsciiFilename(sName))
        sAsciiName = sName;
    else
    {
        // create an ASCII name - using a hash to try and keep it unique and yet non-random
        comphelper::Hash aHash(comphelper::HashType::MD5);
        sal_Int32 nBytesToRead = std::clamp<sal_Int32>(xAudioStream->available(), 0, 32000);
        uno::Sequence<sal_Int8> aTempBuf(nBytesToRead);
        if ((nBytesToRead = xAudioStream->readBytes(aTempBuf, nBytesToRead)))
            aHash.update(aTempBuf.getConstArray(), nBytesToRead);
        else // safety fallback: use the name to create a hash: should never happen
        {
            const OString sUtf(OUStringToOString(sName, RTL_TEXTENCODING_UTF8));
            aHash.update(sUtf.getStr(), sUtf.getLength());
        }
        sAsciiName
            = "audio_" + OUString::fromUtf8(comphelper::hashToString(aHash.finalize())) + ".wav";
    }
 
    OUString sPath = "../media/" + sAsciiName;
    sRelId = addRelation(pFS->getOutputStream(),
                        oox::getRelationship(Relationship::AUDIO), sPath);
 
    uno::Reference<io::XOutputStream> xOutputStream = openFragmentStream(sPath.replaceAt(0, 2, u"/ppt"),
            u"audio/x-wav"_ustr);
 
    comphelper::OStorageHelper::CopyInputToOutput(xAudioStream, xOutputStream);
}
 
sal_Int32 PowerPointExport::GetShapeID(const Reference<XShape>& rXShape)
{
    return ShapeExport::GetShapeID(rXShape, &maShapeMap);
}
 
sal_Int32 PowerPointExport::GetNextAnimationNodeID()
{
    return mnAnimationNodeIdMax++;
}
 
bool PowerPointExport::ImplCreateMainNotes()
{
    if (mbCreateNotes)
        WriteNotesMaster();
 
    return true;
}
 
OUString PowerPointExport::getImplementationName()
{
    return u"com.sun.star.comp.Impress.oox.PowerPointExport"_ustr;
}
 
void PowerPointExport::WriteDiagram(const FSHelperPtr& pFS, PowerPointShapeExport& rDML,
                                    const css::uno::Reference<css::drawing::XShape>& rXShape,
                                    sal_Int32 nDiagramId)
{
    sal_Int32 nShapeId = rDML.GetNewShapeID(rXShape);
    SAL_INFO("sd.eppt", "writing Diagram " + OUString::number(nDiagramId) + " with Shape Id "
                            + OUString::number(nShapeId));
    pFS->startElementNS(XML_p, XML_graphicFrame);
    rDML.WriteDiagram(rXShape, nDiagramId, nShapeId);
    pFS->endElementNS(XML_p, XML_graphicFrame);
}
 
void PowerPointExport::WritePlaceholderReferenceShapes(PowerPointShapeExport& rDML, PageType ePageType)
{
    bool bCheckProps = ePageType == NORMAL;
    Reference<XShape> xShape;
    Any aAny;
    OUString aText;
    if (ePageType == LAYOUT
        || (bCheckProps && PropValue::GetPropertyValue(aAny, mXPagePropSet, u"IsFooterVisible"_ustr, true)
            && aAny == true && GetPropertyValue(aAny, mXPagePropSet, u"FooterText"_ustr, true)
            && (aAny >>= aText) && !aText.isEmpty()))
    {
        if ((xShape = GetReferencedPlaceholderXShape(Footer, ePageType)))
        {
            const auto iter = maPlaceholderShapeToIndexMap.find(xShape);
            assert(iter != maPlaceholderShapeToIndexMap.end());
            rDML.WritePlaceholderReferenceShape(Footer,
                                                iter->second,
                                                ePageType, mXPagePropSet);
        }
    }
 
    if (ePageType == LAYOUT
        || (bCheckProps
            && PropValue::GetPropertyValue(aAny, mXPagePropSet, u"IsPageNumberVisible"_ustr, true)
            && aAny == true))
    {
        if ((xShape = GetReferencedPlaceholderXShape(SlideNumber, ePageType)))
        {
            const auto iter = maPlaceholderShapeToIndexMap.find(xShape);
            assert(iter != maPlaceholderShapeToIndexMap.end());
            rDML.WritePlaceholderReferenceShape(SlideNumber,
                                                iter->second,
                                                ePageType, mXPagePropSet);
        }
    }
 
    if (ePageType == LAYOUT
        || (bCheckProps
            && PropValue::GetPropertyValue(aAny, mXPagePropSet, u"IsDateTimeVisible"_ustr, true)
            && aAny == true
            && ((GetPropertyValue(aAny, mXPagePropSet, u"DateTimeText"_ustr, true) && (aAny >>= aText)
                 && !aText.isEmpty())
                || mXPagePropSet->getPropertyValue(u"IsDateTimeFixed"_ustr) == false)))
    {
        if ((xShape = GetReferencedPlaceholderXShape(DateAndTime, ePageType)))
        {
            const auto iter = maPlaceholderShapeToIndexMap.find(xShape);
            assert(iter != maPlaceholderShapeToIndexMap.end());
            rDML.WritePlaceholderReferenceShape(DateAndTime,
                                                iter->second,
                                                ePageType, mXPagePropSet);
        }
    }
}
 
sal_Int32 PowerPointExport::CreateNewPlaceholderIndex(const css::uno::Reference<XShape> &rXShape)
{
    maPlaceholderShapeToIndexMap.insert({rXShape, mnPlaceholderIndexMax});
    return mnPlaceholderIndexMax++;
}
 
Reference<XShape> PowerPointExport::GetReferencedPlaceholderXShape(const PlaceholderType eType,
                                                        PageType ePageType) const
{
    PresObjKind ePresObjKind = PresObjKind::NONE;
    switch (eType)
    {
        case oox::core::None:
            break;
        case oox::core::SlideImage:
            break;
        case oox::core::Notes:
            break;
        case oox::core::Header:
            ePresObjKind = PresObjKind::Header;
            break;
        case oox::core::Footer:
            ePresObjKind = PresObjKind::Footer;
            break;
        case oox::core::SlideNumber:
            ePresObjKind = PresObjKind::SlideNumber;
            break;
        case oox::core::DateAndTime:
            ePresObjKind = PresObjKind::DateTime;
            break;
        case oox::core::Outliner:
            break;
        case oox::core::Title:
            ePresObjKind = PresObjKind::Title;
            break;
        case oox::core::Subtitle:
            break;
    }
    if (ePresObjKind != PresObjKind::NONE)
    {
        SdrPage* pPage;
        if (ePageType == LAYOUT)
        {
            // since Layout pages do not have drawpages themselves - mXDrawPage is still the master they reference to..
            pPage = SdPage::getImplementation(mXDrawPage);
        }
        else
        {
            pPage = &SdPage::getImplementation(mXDrawPage)->TRG_GetMasterPage();
        }
        SdPage* pMasterPage = dynamic_cast<SdPage*>(pPage);
        if (SdrObject* pMasterFooter
            = (pMasterPage ? pMasterPage->GetPresObj(ePresObjKind) : nullptr))
            return GetXShapeForSdrObject(pMasterFooter);
    }
    return nullptr;
}
 
// UNO component
extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
css_comp_Impress_oox_PowerPointExport(uno::XComponentContext* rxCtxt,
                                      uno::Sequence<css::uno::Any> const& rArguments)
{
    return cppu::acquire(new PowerPointExport(rxCtxt, rArguments));
}
 
#if OSL_DEBUG_LEVEL > 1
void dump_pset(Reference< XPropertySet > const& rXPropSet)
{
    Reference< XPropertySetInfo > info = rXPropSet->getPropertySetInfo();
    Sequence< beans::Property > props = info->getProperties();
 
    for (int i=0; i < props.getLength(); i++)
    {
        OString name = OUStringToOString(props [i].Name, RTL_TEXTENCODING_UTF8);
 
        Any value = rXPropSet->getPropertyValue(props [i].Name);
 
        OUString strValue;
        sal_Int32 intValue;
        bool boolValue;
        RectanglePoint pointValue;
 
        if (value >>= strValue)
            SAL_INFO("sd.eppt", name << " = \"" << strValue << "\"");
        else if (value >>= intValue)
            SAL_INFO("sd.eppt", name << " = " << intValue << "(hex : " << std::hex << intValue << ")");
        else if (value >>= boolValue)
            SAL_INFO("sd.eppt", name << " = " << boolValue << "           (bool)");
        else if (value >>= pointValue)
            SAL_INFO("sd.eppt", name << " = " << static_cast<int>(pointValue) << "    (RectanglePoint)");
        else
            SAL_INFO("sd.eppt", "???          <unhandled type>");
    }
}
#endif
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

V547 Expression 'advanceTiming != - 1' is always false.

V522 There might be dereferencing of a potential null pointer 'pDiagramCandidate'.

V547 Expression '!bOOXmlSpecificTransition' is always true.

V547 Expression 'isAdvanceTimingSet' is always false.

V547 Expression 'pPresetTransition' is always false.

V547 Expression 'bUseBackground' is always false.

V547 Expression '!bIsDateTimeFixed' is always true.

V547 Expression 'bIsDateTimeFixed' is always false.

V560 A part of conditional expression is always true: aFillStyle == FillStyle_NONE.

V560 A part of conditional expression is always false: aFillStyle == FillStyle_HATCH.

V560 A part of conditional expression is always true: !bOOXmlSpecificTransition.

V560 A part of conditional expression is always false: changeType == 1.

V560 A part of conditional expression is always false: pPresetTransition.

V560 A part of conditional expression is always false: pPresetTransition.

V560 A part of conditional expression is always true: !bIsDateTimeFixed.

V785 Constant expression in switch statement.

V785 Constant expression in switch statement.

V785 Constant expression in switch statement.

V1019 Compound assignment expression 'mAny >>= nTransitionType' is used inside condition.

V1019 Compound assignment expression 'mAny >>= nTransitionSubtype' is used inside condition.

V1019 Compound assignment expression 'mAny >>= sSoundUrl' is used inside condition.

V1019 Compound assignment expression 'mAny >>= aXBackgroundPropSet' is used inside condition.

V1019 Compound assignment expression 'aAny >>= aText' is used inside condition.

V1019 Compound assignment expression 'aAny >>= aText' is used inside condition.