/* -*- 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 <memory>
 
#include <com/sun/star/presentation/XPresentation2.hpp>
 
#include <com/sun/star/drawing/FillStyle.hpp>
#include <com/sun/star/drawing/LineStyle.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
#include <com/sun/star/lang/ServiceNotRegisteredException.hpp>
#include <com/sun/star/lang/Locale.hpp>
#include <com/sun/star/awt/XDevice.hpp>
#include <com/sun/star/document/IndexedPropertyValues.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/util/XTheme.hpp>
#include <com/sun/star/animations/AnimationFill.hpp>
#include <com/sun/star/animations/AnimationRestart.hpp>
#include <com/sun/star/animations/AnimationEndSync.hpp>
#include <com/sun/star/animations/AnimationCalcMode.hpp>
#include <com/sun/star/animations/AnimationAdditiveMode.hpp>
#include <com/sun/star/animations/AnimationNodeType.hpp>
#include <com/sun/star/animations/AnimationTransformType.hpp>
#include <com/sun/star/animations/AnimationColorSpace.hpp>
#include <com/sun/star/animations/Event.hpp>
#include <com/sun/star/animations/EventTrigger.hpp>
#include <com/sun/star/animations/Timing.hpp>
#include <com/sun/star/animations/TransitionType.hpp>
#include <com/sun/star/animations/TransitionSubType.hpp>
#include <com/sun/star/animations/ValuePair.hpp>
#include <com/sun/star/animations/XAnimate.hpp>
#include <com/sun/star/animations/XAnimateMotion.hpp>
#include <com/sun/star/animations/XAnimateColor.hpp>
#include <com/sun/star/animations/XAnimateTransform.hpp>
#include <com/sun/star/animations/XIterateContainer.hpp>
#include <com/sun/star/animations/XTimeContainer.hpp>
#include <com/sun/star/animations/XTransitionFilter.hpp>
#include <com/sun/star/presentation/EffectNodeType.hpp>
#include <com/sun/star/presentation/EffectPresetClass.hpp>
#include <com/sun/star/presentation/ParagraphTarget.hpp>
#include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
#include <com/sun/star/presentation/TextAnimationType.hpp>
 
 
#include <com/sun/star/embed/Aspects.hpp>
 
#include <officecfg/Office/Common.hxx>
#include <officecfg/Office/Impress.hxx>
#include <comphelper/dispatchcommand.hxx>
#include <comphelper/indexedpropertyvalues.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/propertysequence.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/sequence.hxx>
#include <comphelper/servicehelper.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/profilezone.hxx>
 
#include <sal/log.hxx>
#include <editeng/unofield.hxx>
#include <notifydocumentevent.hxx>
#include <tpaction.hxx>
#include <unomodel.hxx>
#include "unopool.hxx"
#include <sfx2/lokhelper.hxx>
#include <sfx2/dispatch.hxx>
#include <vcl/svapp.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
 
#include <editeng/UnoForbiddenCharsTable.hxx>
#include <svx/svdoutl.hxx>
#include <o3tl/any.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
#include <o3tl/test_info.hxx>
#include <o3tl/unit_conversion.hxx>
#include <svx/UnoNamespaceMap.hxx>
#include <svx/svdlayer.hxx>
#include <svx/svdsob.hxx>
#include <svx/svdundo.hxx>
#include <svx/svdomedia.hxx>
#include <svx/unoapi.hxx>
#include <svx/unofill.hxx>
#include <svx/sdrpagewindow.hxx>
#include <svx/sdrpaintwindow.hxx>
#include <editeng/fontitem.hxx>
#include <toolkit/awt/vclxdevice.hxx>
#include <svx/svdpool.hxx>
#include <svx/svdpagv.hxx>
#include <svtools/unoimap.hxx>
#include <svx/unoshape.hxx>
#include <editeng/unonrule.hxx>
#include <editeng/eeitem.hxx>
#include <unotools/datetime.hxx>
#include <sax/tools/converter.hxx>
#include <xmloff/autolayout.hxx>
#include <xmloff/xmltoken.hxx>
#include <rtl/math.hxx>
#include <tools/helpers.hxx>
#include <tools/json_writer.hxx>
 
// Support creation of GraphicStorageHandler and EmbeddedObjectResolver
#include <svx/xmleohlp.hxx>
#include <svx/xmlgrhlp.hxx>
#include <DrawDocShell.hxx>
#include <ViewShellBase.hxx>
#include "UnoDocumentSettings.hxx"
 
#include <Annotation.hxx>
#include <drawdoc.hxx>
#include <sdmod.hxx>
#include <sdresid.hxx>
#include <sdpage.hxx>
 
#include <strings.hrc>
#include <strings.hxx>
#include "unolayer.hxx"
#include <unopage.hxx>
#include "unocpres.hxx"
#include "unoobj.hxx"
#include <stlpool.hxx>
#include "unopback.hxx"
#include <unokywds.hxx>
 
#include <FrameView.hxx>
#include <ClientView.hxx>
#include <DrawViewShell.hxx>
#include <ViewShell.hxx>
#include <Window.hxx>
#include <optsitem.hxx>
 
#include <vcl/pdfextoutdevdata.hxx>
#include <vcl/pdf/PDFNote.hxx>
 
#include <com/sun/star/presentation/AnimationSpeed.hpp>
#include <com/sun/star/presentation/ClickAction.hpp>
#include <svx/sdr/contact/viewobjectcontact.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <svx/sdr/contact/displayinfo.hxx>
 
#include <com/sun/star/office/XAnnotation.hpp>
#include <com/sun/star/office/XAnnotationAccess.hpp>
#include <com/sun/star/office/XAnnotationEnumeration.hpp>
#include <com/sun/star/geometry/RealPoint2D.hpp>
#include <com/sun/star/util/DateTime.hpp>
 
#include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
 
#include <sfx2/lokcomponenthelpers.hxx>
#include <sfx2/LokControlHandler.hxx>
#include <tools/gen.hxx>
#include <tools/debug.hxx>
#include <tools/urlobj.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <tools/UnitConversion.hxx>
#include <svx/ColorSets.hxx>
#include <docmodel/theme/Theme.hxx>
 
#include <frozen/bits/defines.h>
#include <frozen/bits/elsa_std.h>
#include <frozen/unordered_map.h>
 
#include <app.hrc>
 
using namespace ::cppu;
using namespace ::com::sun::star;
using namespace ::sd;
 
const TranslateId aTypeResIds[SdLinkTargetType::Count] =
{
    STR_SD_PAGE,            // SdLinkTargetType::Page
    STR_NOTES_MODE,         // SdLinkTargetType::Notes
    STR_HANDOUT,            // SdLinkTargetType::Handout
    STR_MASTERPAGE_NAME,    // SdLinkTargetType::MasterPage
};
 
TranslateId SdTPAction::GetClickActionSdResId( presentation::ClickAction eCA )
{
    switch( eCA )
    {
        case presentation::ClickAction_NONE:             return STR_CLICK_ACTION_NONE;
        case presentation::ClickAction_PREVPAGE:         return STR_CLICK_ACTION_PREVPAGE;
        case presentation::ClickAction_NEXTPAGE:         return STR_CLICK_ACTION_NEXTPAGE;
        case presentation::ClickAction_FIRSTPAGE:        return STR_CLICK_ACTION_FIRSTPAGE;
        case presentation::ClickAction_LASTPAGE:         return STR_CLICK_ACTION_LASTPAGE;
        case presentation::ClickAction_BOOKMARK:         return STR_CLICK_ACTION_BOOKMARK;
        case presentation::ClickAction_DOCUMENT:         return STR_CLICK_ACTION_DOCUMENT;
        case presentation::ClickAction_PROGRAM:          return STR_CLICK_ACTION_PROGRAM;
        case presentation::ClickAction_MACRO:            return STR_CLICK_ACTION_MACRO;
        case presentation::ClickAction_SOUND:            return STR_CLICK_ACTION_SOUND;
        case presentation::ClickAction_VERB:             return STR_CLICK_ACTION_VERB;
        case presentation::ClickAction_STOPPRESENTATION: return STR_CLICK_ACTION_STOPPRESENTATION;
        default: OSL_FAIL( "No StringResource for ClickAction available!" );
    }
    return {};
}
 
class SdUnoForbiddenCharsTable : public SvxUnoForbiddenCharsTable,
                                 public SfxListener
{
public:
    explicit SdUnoForbiddenCharsTable(SdrModel* pModel);
    virtual ~SdUnoForbiddenCharsTable() override;
 
    // SfxListener
    virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) noexcept override;
protected:
    virtual void onChange() override;
 
private:
    SdrModel*   mpModel;
};
 
namespace {
 
class SlideBackgroundInfo
{
public:
    SlideBackgroundInfo(const uno::Reference<drawing::XDrawPage>& xDrawPage,
                        const uno::Reference<drawing::XDrawPage>& xMasterPage);
    bool slideHasOwnBackground() const { return mbIsCustom; }
    bool hasBackground() const { return bHasBackground; }
    bool isSolidColor() const { return mbIsSolidColor; }
    ::Color getFillColor() const;
    sal_Int32 getFillTransparency() const;
    OString getFillColorAsRGBA() const;
private:
    bool getFillStyleImpl(const uno::Reference<drawing::XDrawPage>& xDrawPage);
private:
    uno::Reference<beans::XPropertySet> mxBackground;
    bool mbIsCustom;
    bool bHasBackground;
    bool mbIsSolidColor;
    drawing::FillStyle maFillStyle;
};
 
SlideBackgroundInfo::SlideBackgroundInfo(
        const uno::Reference<drawing::XDrawPage>& xDrawPage,
        const uno::Reference<drawing::XDrawPage>& xMasterPage)
    : mbIsCustom(false)
    , bHasBackground(false)
    , mbIsSolidColor(false)
    , maFillStyle(drawing::FillStyle_NONE)
{
    mbIsCustom = getFillStyleImpl(xDrawPage);
    bHasBackground = mbIsCustom;
    if (!bHasBackground)
    {
        bHasBackground = getFillStyleImpl(xMasterPage);
    }
    if (bHasBackground)
    {
        if (maFillStyle == drawing::FillStyle_SOLID)
        {
            OUString sGradientName;
            mxBackground->getPropertyValue("FillTransparenceGradientName") >>= sGradientName;
            if (sGradientName.isEmpty())
            {
                mbIsSolidColor = true;
            }
        }
    }
}
 
sal_Int32 SlideBackgroundInfo::getFillTransparency() const
{
    if (!mxBackground.is())
        return 0;
    sal_Int32 nFillTransparency = 0;
    mxBackground->getPropertyValue("FillTransparence") >>= nFillTransparency;
    return nFillTransparency;
}
 
::Color SlideBackgroundInfo::getFillColor() const
{
    if (!mxBackground.is())
        return {};
    if (sal_Int32 nFillColor; mxBackground->getPropertyValue("FillColor") >>= nFillColor)
    {
        return ::Color(ColorTransparency, nFillColor & 0xffffff);
    }
    return {};
}
 
OString SlideBackgroundInfo::getFillColorAsRGBA() const
{
    ::Color aColor = getFillColor();
    OString sColor = aColor.AsRGBHEXString().toUtf8();
    sal_uInt32 nAlpha = std::round((100 - getFillTransparency()) * 255 / 100.0);
    std::stringstream ss;
    ss << std::hex << std::uppercase << std::setfill ('0') << std::setw(2) << nAlpha;
    sColor += ss.str().c_str();
    return sColor;
}
 
bool SlideBackgroundInfo::getFillStyleImpl(const uno::Reference<drawing::XDrawPage>& xDrawPage)
{
    if( xDrawPage.is() )
    {
        uno::Reference< beans::XPropertySet > xPropSet( xDrawPage, uno::UNO_QUERY );
        if( xPropSet.is() )
        {
            uno::Reference< beans::XPropertySet > xBackground;
            if (xPropSet->getPropertySetInfo()->hasPropertyByName("Background"))
                xPropSet->getPropertyValue( "Background" ) >>= xBackground;
            if( xBackground.is() )
            {
                drawing::FillStyle aFillStyle;
                if( xBackground->getPropertyValue( "FillStyle" ) >>= aFillStyle )
                {
                    maFillStyle = aFillStyle;
                    if (aFillStyle != drawing::FillStyle_NONE)
                    {
                        mxBackground = std::move(xBackground);
                        return true;
                    }
                }
            }
        }
    }
    return false;
}
 
using namespace ::css::animations;
using namespace ::css::beans;
using namespace ::css::container;
using namespace ::css::uno;
using namespace ::xmloff::token;
using namespace ::css::presentation;
 
template <typename T, std::size_t N>
constexpr auto mapEnumToString(std::pair<T, std::string_view> const (&items)[N])
{
    return frozen::make_unordered_map<T, std::string_view, N>(items);
}
 
constexpr auto constTransitionTypeToString = mapEnumToString<sal_Int16>({
    { animations::TransitionType::BARWIPE, "BarWipe" }, // Wipe
    { animations::TransitionType::PINWHEELWIPE, "PineWheelWipe" }, // Wheel
    { animations::TransitionType::SLIDEWIPE, "SlideWipe" }, // Cover, Uncover
    { animations::TransitionType::RANDOMBARWIPE, "RandomBarWipe" }, // Bars
    { animations::TransitionType::CHECKERBOARDWIPE, "CheckerBoardWipe" }, // Checkers
    { animations::TransitionType::FOURBOXWIPE, "FourBoxWipe" }, // Shape
    { animations::TransitionType::IRISWIPE, "IrisWipe" }, // Box
    { animations::TransitionType::FANWIPE, "FanWipe" }, // Wedge
    { animations::TransitionType::BLINDSWIPE, "BlindWipe"}, // Venetian
    { animations::TransitionType::FADE, "Fade"},
    { animations::TransitionType::DISSOLVE, "Dissolve"},
    { animations::TransitionType::PUSHWIPE, "PushWipe"}, // Comb
    { animations::TransitionType::ELLIPSEWIPE, "EllipseWipe"}, // Shape
    { animations::TransitionType::BARNDOORWIPE, "BarnDoorWipe"}, // Split
    { animations::TransitionType::WATERFALLWIPE, "WaterfallWipe"}, // Diagonal
    { animations::TransitionType::MISCSHAPEWIPE, "MiscShapeWipe"},
    { animations::TransitionType::ZOOM, "Zoom"}
});
 
constexpr auto constTransitionSubTypeToString = mapEnumToString<sal_Int16>({
    { animations::TransitionSubType::LEFTTORIGHT, "LeftToRight" },
    { animations::TransitionSubType::TOPTOBOTTOM, "TopToBottom" },
    { animations::TransitionSubType::EIGHTBLADE, "8Blade" },
    { animations::TransitionSubType::FOURBLADE, "4Blade" },
    { animations::TransitionSubType::THREEBLADE, "3Blade" },
    { animations::TransitionSubType::TWOBLADEVERTICAL, "2BladeVertical" },
    { animations::TransitionSubType::ONEBLADE, "1Blade" },
    { animations::TransitionSubType::FROMTOPLEFT, "FromTopLeft" },
    { animations::TransitionSubType::FROMTOPRIGHT, "FromTopRight"},
    { animations::TransitionSubType::FROMBOTTOMLEFT, "FromBottomLeft"},
    { animations::TransitionSubType::FROMBOTTOMRIGHT, "FromBottomRight"},
    { animations::TransitionSubType::VERTICAL, "Vertical"},
    { animations::TransitionSubType::HORIZONTAL, "Horizontal"},
    { animations::TransitionSubType::DOWN, "Down"},
    { animations::TransitionSubType::ACROSS, "Across"},
    { animations::TransitionSubType::CORNERSOUT, "CornersOut"},
    { animations::TransitionSubType::DIAMOND, "Diamond"},
    { animations::TransitionSubType::CIRCLE, "Circle"},
    { animations::TransitionSubType::RECTANGLE, "Rectangle"},
    { animations::TransitionSubType::CENTERTOP, "CenterTop"},
    { animations::TransitionSubType::CROSSFADE, "CrossFade"},
    { animations::TransitionSubType::FADEOVERCOLOR, "FadeOverColor"},
    { animations::TransitionSubType::FROMLEFT, "FromLeft"},
    { animations::TransitionSubType::FROMRIGHT, "FromRight"},
    { animations::TransitionSubType::FROMTOP, "FromTop"},
    { animations::TransitionSubType::HORIZONTALLEFT, "HorizontalLeft"},
    { animations::TransitionSubType::HORIZONTALRIGHT, "HorizontalRight"},
    { animations::TransitionSubType::COMBVERTICAL, "CombVertical"},
    { animations::TransitionSubType::COMBHORIZONTAL, "CombHorizontal"},
    { animations::TransitionSubType::TOPLEFT, "TopLeft"},
    { animations::TransitionSubType::TOPRIGHT, "TopRight"},
    { animations::TransitionSubType::BOTTOMRIGHT, "BottomRight"},
    { animations::TransitionSubType::BOTTOMLEFT, "BottomLeft"},
    { animations::TransitionSubType::TOPCENTER, "TopCenter"},
    { animations::TransitionSubType::RIGHTCENTER, "RightCenter"},
    { animations::TransitionSubType::BOTTOMCENTER, "BottomCenter"},
    { animations::TransitionSubType::FANOUTHORIZONTAL, "FanOutHorizontal"},
    { animations::TransitionSubType::CORNERSIN, "CornersIn"},
    { animations::TransitionSubType::HEART, "Heart"},
    { animations::TransitionSubType::ROTATEIN, "RotateIn"}
});
 
constexpr auto constAnimationNodeTypeToString = mapEnumToString<sal_Int16>({
    { AnimationNodeType::ANIMATE, "Animate" },
    { AnimationNodeType::ANIMATECOLOR, "AnimateColor" },
    { AnimationNodeType::ANIMATEMOTION, "Animate" },
    { AnimationNodeType::ANIMATEPHYSICS, "Animate" },
    { AnimationNodeType::ANIMATETRANSFORM, "AnimateTransform" },
    { AnimationNodeType::AUDIO, "Audio" },
    { AnimationNodeType::COMMAND, "Command" },
    { AnimationNodeType::CUSTOM, "Custom" },
    { AnimationNodeType::ITERATE, "Iterate" },
    { AnimationNodeType::PAR, "Par" },
    { AnimationNodeType::SEQ, "Seq" },
    { AnimationNodeType::SET, "Set" },
    { AnimationNodeType::TRANSITIONFILTER, "TransitionFilter" },
});
 
constexpr auto constFillToString = mapEnumToString<sal_Int16>({
    { AnimationFill::DEFAULT, "Default" },
    { AnimationFill::REMOVE, "Remove" },
    { AnimationFill::FREEZE, "Freeze" },
    { AnimationFill::HOLD, "Hold" },
    { AnimationFill::TRANSITION, "Transition" },
    { AnimationFill::AUTO, "Auto" },
});
 
constexpr auto constRestartToString = mapEnumToString<sal_Int16>({
    { AnimationRestart::DEFAULT, "Default" },
    { AnimationRestart::ALWAYS, "Always" },
    { AnimationRestart::WHEN_NOT_ACTIVE, "WhenNotActive" },
    { AnimationRestart::NEVER, "Never" },
});
 
constexpr auto constEndSyncToString = mapEnumToString<sal_Int16>({
    { AnimationEndSync::FIRST, "First" },
    { AnimationEndSync::LAST, "Last" },
    { AnimationEndSync::ALL, "All" },
    { AnimationEndSync::MEDIA, "Media" },
});
 
constexpr auto constCalcModeToString = mapEnumToString<sal_Int16>({
    { AnimationCalcMode::DISCRETE, "Discrete" },
    { AnimationCalcMode::LINEAR, "Linear" },
    { AnimationCalcMode::PACED, "Paced" },
    { AnimationCalcMode::SPLINE, "Spline" },
});
 
constexpr auto constAdditiveModeToString = mapEnumToString<sal_Int16>({
    { AnimationAdditiveMode::BASE, "Base" },
    { AnimationAdditiveMode::SUM, "Sum" },
    { AnimationAdditiveMode::REPLACE, "Replace" },
    { AnimationAdditiveMode::MULTIPLY, "Multiply" },
    { AnimationAdditiveMode::NONE, "None" },
});
 
constexpr auto constEffectPresetClassToString = mapEnumToString<sal_Int16>({
    { EffectPresetClass::CUSTOM, "Custom" },
    { EffectPresetClass::ENTRANCE, "Entrance" },
    { EffectPresetClass::EXIT, "Exit" },
    { EffectPresetClass::EMPHASIS, "Emphasis" },
    { EffectPresetClass::MOTIONPATH, "MotionPath" },
    { EffectPresetClass::OLEACTION, "OleAction" },
    { EffectPresetClass::MEDIACALL, "MediaCall" },
});
 
constexpr auto constEffectNodeTypeToString = mapEnumToString<sal_Int16>({
    { EffectNodeType::DEFAULT, "Default" },
    { EffectNodeType::ON_CLICK, "OnClick" },
    { EffectNodeType::WITH_PREVIOUS, "WithPrevious" },
    { EffectNodeType::AFTER_PREVIOUS, "AfterPrevious" },
    { EffectNodeType::MAIN_SEQUENCE, "MainSequence" },
    { EffectNodeType::TIMING_ROOT, "TimingRoot" },
    { EffectNodeType::INTERACTIVE_SEQUENCE, "InteractiveSequence" },
});
 
constexpr auto constEventTriggerToString = mapEnumToString<sal_Int16>({
    { EventTrigger::BEGIN_EVENT, "BeginEvent" },
    { EventTrigger::END_EVENT, "EndEvent" },
    { EventTrigger::NONE, "None" },
    { EventTrigger::ON_BEGIN, "OnBegin" },
    { EventTrigger::ON_CLICK, "OnClick" },
    { EventTrigger::ON_DBL_CLICK, "OnDblClick" },
    { EventTrigger::ON_END, "OnEnd" },
    { EventTrigger::ON_MOUSE_ENTER, "OnMouseEnter" },
    { EventTrigger::ON_MOUSE_LEAVE, "OnMouseLeave" },
    { EventTrigger::ON_NEXT, "OnNext" },
    { EventTrigger::ON_PREV, "OnPrev" },
    { EventTrigger::ON_STOP_AUDIO, "OnStopAudio" },
    { EventTrigger::REPEAT, "Repeat" },
});
 
constexpr auto constTimingToString = mapEnumToString<Timing>({
    { Timing_INDEFINITE, "indefinite" },
    { Timing_MEDIA, "media" },
});
 
constexpr auto constTransformTypeToString = mapEnumToString<sal_Int16>({
    { AnimationTransformType::TRANSLATE, "Translate" },
    { AnimationTransformType::SCALE, "Scale" },
    { AnimationTransformType::ROTATE, "Rotate" },
    { AnimationTransformType::SKEWX, "SkewX" },
    { AnimationTransformType::SKEWY, "SkewY" },
});
 
constexpr auto constSubItemToString = mapEnumToString<sal_Int16>({
    { ShapeAnimationSubType::AS_WHOLE, "AsWhole" },
    { ShapeAnimationSubType::ONLY_BACKGROUND, "OnlyBackground" },
    { ShapeAnimationSubType::ONLY_TEXT, "OnlyText" },
});
 
constexpr auto constIterateTypeToString = mapEnumToString<sal_Int16>({
    { TextAnimationType::BY_PARAGRAPH, "ByParagraph" },
    { TextAnimationType::BY_WORD, "ByWord" },
    { TextAnimationType::BY_LETTER, "ByLetter" },
});
 
constexpr auto constFillStyleToString = mapEnumToString<drawing::FillStyle>({
    { drawing::FillStyle_NONE, "None" },
    { drawing::FillStyle_SOLID, "Solid" },
    { drawing::FillStyle_BITMAP, "Bitmap" },
    { drawing::FillStyle_GRADIENT, "Gradient" },
    { drawing::FillStyle_HATCH, "Hatch" },
});
 
constexpr auto constLineStyleToString = mapEnumToString<drawing::LineStyle>({
    { drawing::LineStyle_NONE, "None" },
    { drawing::LineStyle_SOLID, "Solid" },
    { drawing::LineStyle_DASH, "Dash" },
});
 
 
constexpr auto constAttributeNameToXMLEnum
    = frozen::make_unordered_map<std::string_view, XMLTokenEnum>({
        { "X", XML_X },
        { "Y", XML_Y },
        { "Width", XML_WIDTH },
        { "Height", XML_HEIGHT },
        { "Rotate", XML_ROTATE },
        { "SkewX", XML_SKEWX },
        { "FillColor", XML_FILL_COLOR },
        { "FillStyle", XML_FILL },
        { "LineColor", XML_STROKE_COLOR },
        { "LineStyle",XML_STROKE  },
        { "CharColor", XML_COLOR },
        { "CharRotation", XML_TEXT_ROTATION_ANGLE },
        { "CharWeight", XML_FONT_WEIGHT },
        { "CharUnderline", XML_TEXT_UNDERLINE },
        { "CharFontName", XML_FONT_FAMILY },
        { "CharHeight", XML_FONT_SIZE },
        { "CharPosture", XML_FONT_STYLE },
        { "Visibility", XML_VISIBILITY },
        { "Opacity", XML_OPACITY },
        { "DimColor", XML_DIM },
});
 
class AnimationsExporter
{
public:
    AnimationsExporter(::tools::JsonWriter& rWriter,
                       const Reference<drawing::XDrawPage>& xDrawPage);
    void exportAnimations();
    [[nodiscard]] bool hasEffects() const { return mbHasEffects; }
 
private:
    void exportNode(const Reference<XAnimationNode>& xNode);
    void exportNodeImpl(const Reference<XAnimationNode>& xNode);
    void exportContainer(const Reference<XTimeContainer>& xContainer);
 
    void exportAnimate(const Reference<XAnimate>& xAnimate);
 
    void convertValue(XMLTokenEnum eAttributeName, OStringBuffer& sTmp, const Any& rValue) const;
    static void convertTarget(OStringBuffer& sTmp, const Any& rTarget);
    static Reference<XInterface> getParagraphTarget( const ParagraphTarget& pTarget );
    void convertTiming(OStringBuffer& sTmp, const Any& rValue) const;
 
private:
    ::tools::JsonWriter& mrWriter;
    Reference<drawing::XDrawPage> mxDrawPage;
    Reference<XPropertySet> mxPageProps;
    Reference<XAnimationNode> mxRootNode;
    bool mbHasEffects;
};
 
AnimationsExporter::AnimationsExporter(::tools::JsonWriter& rWriter,
                                       const Reference<drawing::XDrawPage>& xDrawPage)
    : mrWriter(rWriter)
    , mxDrawPage(xDrawPage)
    , mbHasEffects(false)
{
    if (!mxDrawPage.is())
        return;
 
    try
    {
        mxPageProps = Reference<XPropertySet>(xDrawPage, UNO_QUERY);
        if (!mxPageProps.is())
            return;
 
        Reference<XAnimationNodeSupplier> xAnimNodeSupplier(mxDrawPage, UNO_QUERY);
        if (!xAnimNodeSupplier.is())
            return;
 
        Reference<XAnimationNode> xRootNode = xAnimNodeSupplier->getAnimationNode();
        if (xRootNode.is())
        {
            // first check if there are no animations
            Reference<XEnumerationAccess> xEnumerationAccess(xRootNode, UNO_QUERY_THROW);
            Reference<XEnumeration> xEnumeration(xEnumerationAccess->createEnumeration(),
                                                 css::uno::UNO_SET_THROW);
            if (xEnumeration->hasMoreElements())
            {
                // first child node may be an empty main sequence, check this
                Reference<XAnimationNode> xMainNode(xEnumeration->nextElement(), UNO_QUERY_THROW);
                Reference<XEnumerationAccess> xMainEnumerationAccess(xMainNode, UNO_QUERY_THROW);
                Reference<XEnumeration> xMainEnumeration(
                    xMainEnumerationAccess->createEnumeration(), css::uno::UNO_SET_THROW);
 
                // only export if the main sequence is not empty or if there are additional
                // trigger sequences
                mbHasEffects
                    = xMainEnumeration->hasMoreElements() || xEnumeration->hasMoreElements();
            }
        }
        if (mbHasEffects)
            mxRootNode = std::move(xRootNode);
    }
    catch (const RuntimeException&)
    {
        TOOLS_WARN_EXCEPTION("sd", "unomodel: AnimationsExporter");
    }
}
 
template <typename EnumT, size_t N>
constexpr bool convertEnum(OStringBuffer& rBuffer, EnumT nValue,
                           const frozen::unordered_map<EnumT, std::string_view, N>& rMap)
{
    auto iterator = rMap.find(nValue);
    if (iterator == rMap.end())
        return false;
    rBuffer.append(iterator->second);
    return true;
}
 
void convertDouble(OStringBuffer& rBuffer, double fValue)
{
        ::rtl::math::doubleToStringBuffer(rBuffer, fValue, rtl_math_StringFormat_Automatic,
                                          rtl_math_DecimalPlaces_Max, '.', true);
}
 
void convertBool(OStringBuffer& rBuffer, bool bValue)
{
    rBuffer.append( bValue );
}
 
void convertPath(OStringBuffer& sTmp, const Any& rPath)
{
    OUString aStr;
    rPath >>= aStr;
    sTmp = aStr.toUtf8();
}
 
void convertColor(OStringBuffer& rBuffer, sal_Int32 nColor)
{
    OUStringBuffer aUBuffer;
    ::sax::Converter::convertColor(aUBuffer, nColor);
    rBuffer.append(aUBuffer.makeStringAndClear().toUtf8());
}
 
void convertColor(OStringBuffer& rBuffer, const Any& rValue)
{
    sal_Int32 nColor = 0;
    if (rValue >>= nColor)
    {
        convertColor(rBuffer, nColor);
    }
    else
    {
        Sequence<double> aHSL;
        if ((rValue >>= aHSL) && (aHSL.getLength() == 3))
        {
            rBuffer.append("hsl(" + OString::number(aHSL[0]) + ","
                           + OString::number(aHSL[1] * 100.0) + "%,"
                           + OString::number(aHSL[2] * 100.0) + "%)");
        }
    }
}
 
bool isValidNode(const Reference<XAnimationNode>& xNode)
{
    if (xNode.is())
    {
        sal_Int16 nNodeType = xNode->getType();
        auto iterator = constAnimationNodeTypeToString.find(nNodeType);
        return iterator != constAnimationNodeTypeToString.end();
    }
    return false;
}
 
void AnimationsExporter::exportAnimations()
{
    if (!mxDrawPage.is() || !mxPageProps.is() || !mxRootNode.is() || !hasEffects())
        return;
 
    if (isValidNode(mxRootNode))
    {
        auto aNode = mrWriter.startNode("root");
        exportNodeImpl(mxRootNode);
    }
}
void AnimationsExporter::exportNode(const Reference<XAnimationNode>& xNode)
{
     if (!isValidNode(xNode))
         return;
     auto aStruct = mrWriter.startStruct();
     exportNodeImpl(xNode);
}
 
void AnimationsExporter::exportNodeImpl(const Reference<XAnimationNode>& xNode)
{
    try
    {
        std::string sId = GetInterfaceHash(xNode);
        mrWriter.put("id", sId);
        sal_Int16 nNodeType = xNode->getType();
        auto iterator = constAnimationNodeTypeToString.find(nNodeType);
        assert(iterator != constAnimationNodeTypeToString.end() && "must be previously checked with isValidNode");
        mrWriter.put("nodeName", iterator->second);
 
        // common properties
        OStringBuffer sTmp;
        Any aTemp;
        double fTemp = 0;
        sal_Int16 nTemp;
 
        aTemp = xNode->getBegin();
        if (aTemp.hasValue())
        {
            convertTiming(sTmp, aTemp);
            mrWriter.put("begin", sTmp.makeStringAndClear());
        }
        aTemp = xNode->getDuration();
        if (aTemp.hasValue())
        {
            if (aTemp >>= fTemp)
            {
                convertDouble(sTmp, fTemp);
                sTmp.append('s');
                mrWriter.put("dur", sTmp.makeStringAndClear());
            }
            else
            {
                Timing eTiming;
                if (aTemp >>= eTiming)
                {
                    mrWriter.put("dur", eTiming == Timing_INDEFINITE ? "indefinite" : "media");
                }
            }
        }
        aTemp = xNode->getEnd();
        if (aTemp.hasValue())
        {
            convertTiming(sTmp, aTemp);
            mrWriter.put("end", sTmp.makeStringAndClear());
        }
        nTemp = xNode->getFill();
        if (nTemp != AnimationFill::DEFAULT)
        {
            convertEnum(sTmp, nTemp, constFillToString);
            mrWriter.put("fill", sTmp.makeStringAndClear());
        }
        nTemp = xNode->getFillDefault();
        if (nTemp != AnimationFill::INHERIT)
        {
            convertEnum(sTmp, nTemp, constFillToString);
            mrWriter.put("fillDefault", sTmp.makeStringAndClear());
        }
        nTemp = xNode->getRestart();
        if (nTemp != AnimationRestart::DEFAULT)
        {
            convertEnum(sTmp, nTemp, constRestartToString);
            mrWriter.put("restart", sTmp.makeStringAndClear());
        }
        nTemp = xNode->getRestartDefault();
        if (nTemp != AnimationRestart::INHERIT)
        {
            convertEnum(sTmp, nTemp, constRestartToString);
            mrWriter.put("restartDefault", sTmp.makeStringAndClear());
        }
        fTemp = xNode->getAcceleration();
        if (fTemp != 0.0)
        {
            convertDouble(sTmp, fTemp);
            mrWriter.put("accelerate", sTmp.makeStringAndClear());
        }
        fTemp = xNode->getDecelerate();
        if (fTemp != 0.0)
        {
            convertDouble(sTmp, fTemp);
            mrWriter.put("decelerate", sTmp.makeStringAndClear());
        }
        bool bTemp = xNode->getAutoReverse();
        if (bTemp)
        {
            convertBool(sTmp, bTemp);
            mrWriter.put("autoreverse", sTmp.makeStringAndClear());
        }
        aTemp = xNode->getRepeatCount();
        if (aTemp.hasValue())
        {
            Timing eTiming;
            if ((aTemp >>= eTiming) && (eTiming == Timing_INDEFINITE))
            {
                mrWriter.put("repeatCount", "indefinite");
            }
            else if (aTemp >>= fTemp)
            {
                convertDouble(sTmp, fTemp);
                mrWriter.put("repeatCount", sTmp.makeStringAndClear());
            }
        }
        aTemp = xNode->getRepeatDuration();
        if (aTemp.hasValue())
        {
            Timing eTiming;
            if ((aTemp >>= eTiming) && (eTiming == Timing_INDEFINITE))
            {
                mrWriter.put("repeatDur", "indefinite");
            }
            else if (aTemp >>= fTemp)
            {
                convertDouble(sTmp, fTemp);
                mrWriter.put("repeatDur", sTmp.makeStringAndClear());
            }
        }
        aTemp = xNode->getEndSync();
        if (aTemp.hasValue() && (aTemp >>= nTemp))
        {
            convertEnum(sTmp, nTemp, constEndSyncToString);
            mrWriter.put("endSync", sTmp.makeStringAndClear());
        }
 
        sal_Int16 nContainerNodeType = EffectNodeType::DEFAULT;
        const Sequence<NamedValue> aUserData(xNode->getUserData());
        for (const auto& rValue : aUserData)
        {
            if (IsXMLToken(rValue.Name, XML_NODE_TYPE))
            {
                if ((rValue.Value >>= nContainerNodeType)
                    && (nContainerNodeType != EffectNodeType::DEFAULT))
                {
                    convertEnum(sTmp, nContainerNodeType, constEffectNodeTypeToString);
                    mrWriter.put("nodeType", sTmp.makeStringAndClear());
                }
            }
            else if (IsXMLToken(rValue.Name, XML_PRESET_ID))
            {
                OUString aPresetId;
                if (rValue.Value >>= aPresetId)
                {
                    mrWriter.put("presetId", aPresetId);
                }
            }
            else if (IsXMLToken(rValue.Name, XML_PRESET_SUB_TYPE))
            {
                OUString aPresetSubType;
                if (rValue.Value >>= aPresetSubType)
                {
                    mrWriter.put("presetSubType", aPresetSubType);
                }
            }
            else if (IsXMLToken(rValue.Name, XML_PRESET_CLASS))
            {
                sal_Int16 nEffectPresetClass = sal_uInt16(0);
                if (rValue.Value >>= nEffectPresetClass)
                {
                    convertEnum(sTmp, nEffectPresetClass, constEffectPresetClassToString);
                    mrWriter.put("presetClass", sTmp.makeStringAndClear());
                }
            }
            else if (IsXMLToken(rValue.Name, XML_MASTER_ELEMENT))
            {
                Reference<XInterface> xMaster;
                rValue.Value >>= xMaster;
                if (xMaster.is())
                {
                    const std::string aIdentifier(GetInterfaceHash(xMaster));
                    if (!aIdentifier.empty())
                        mrWriter.put("masterElement", aIdentifier);
                }
            }
            else if (IsXMLToken(rValue.Name, XML_GROUP_ID))
            {
                sal_Int32 nGroupId = 0;
                if (rValue.Value >>= nGroupId)
                    mrWriter.put("groupId", nGroupId);
            }
            else
            {
                OUString aTmp;
                if (rValue.Value >>= aTmp)
                    mrWriter.put(rValue.Name, aTmp);
            }
        }
 
        switch (nNodeType)
        {
            case AnimationNodeType::PAR:
            case AnimationNodeType::SEQ:
            case AnimationNodeType::ITERATE:
            {
                Reference<XTimeContainer> xContainer(xNode, UNO_QUERY_THROW);
                exportContainer(xContainer);
            }
            break;
 
            case AnimationNodeType::ANIMATE:
            case AnimationNodeType::SET:
            case AnimationNodeType::ANIMATEMOTION:
            case AnimationNodeType::ANIMATEPHYSICS:
            case AnimationNodeType::ANIMATECOLOR:
            case AnimationNodeType::ANIMATETRANSFORM:
            case AnimationNodeType::TRANSITIONFILTER:
            {
                Reference<XAnimate> xAnimate(xNode, UNO_QUERY_THROW);
                exportAnimate(xAnimate);
            }
            break;
            case AnimationNodeType::AUDIO:
            {
                SAL_WARN("sd", "AnimationsExporter::exportNode(): Audio Node not supported.");
            }
            break;
            case AnimationNodeType::COMMAND:
            {
                SAL_WARN("sd", "AnimationsExporter::exportNode(): Command Node not supported.");
            }
            break;
            default:
            {
                OSL_FAIL(
                    "sd unomodel: AnimationsExporter::exportNode(), invalid AnimationNodeType!");
            }
        }
    }
    catch (const RuntimeException&)
    {
        TOOLS_WARN_EXCEPTION("sd", "unomodel: AnimationsExporter");
    }
}
 
Reference<XInterface> AnimationsExporter::getParagraphTarget(const ParagraphTarget& pTarget)
{
    try
    {
        Reference<XEnumerationAccess> xParaEnumAccess(pTarget.Shape, UNO_QUERY_THROW);
 
        Reference<XEnumeration> xEnumeration(xParaEnumAccess->createEnumeration(),
                                             css::uno::UNO_SET_THROW);
        sal_Int32 nParagraph = pTarget.Paragraph;
 
        while (xEnumeration->hasMoreElements())
        {
            Reference<XInterface> xRef(xEnumeration->nextElement(), UNO_QUERY);
            if (nParagraph-- == 0)
                return xRef;
        }
    }
    catch (const RuntimeException&)
    {
        TOOLS_WARN_EXCEPTION("sd", "AnimationsExporter::getParagraphTarget");
    }
 
    Reference<XInterface> xRef;
    return xRef;
}
 
void AnimationsExporter::convertTarget(OStringBuffer& sTmp, const Any& rTarget)
{
    if (!rTarget.hasValue())
        return;
 
    Reference<XInterface> xRef;
    if (!(rTarget >>= xRef))
    {
        if (auto pt = o3tl::tryAccess<ParagraphTarget>(rTarget))
        {
            xRef = getParagraphTarget(*pt);
        }
    }
 
    SAL_WARN_IF(!xRef.is(), "sd", "AnimationsExporter::convertTarget(), invalid target type!");
    if (xRef.is())
    {
        const std::string aIdentifier(GetInterfaceHash(xRef));
        if (!aIdentifier.empty())
            sTmp.append(aIdentifier);
    }
}
 
void AnimationsExporter::convertTiming(OStringBuffer& sTmp, const Any& rValue) const
{
    if (!rValue.hasValue())
        return;
 
    if (auto pSequence = o3tl::tryAccess<Sequence<Any>>(rValue))
    {
        const sal_Int32 nLength = pSequence->getLength();
        sal_Int32 nElement;
        const Any* pAny = pSequence->getConstArray();
 
        OStringBuffer sTmp2;
 
        for (nElement = 0; nElement < nLength; nElement++, pAny++)
        {
            if (!sTmp.isEmpty())
                sTmp.append(';');
            convertTiming(sTmp2, *pAny);
            sTmp.append(sTmp2);
            sTmp2.setLength(0);
        }
    }
    else if (auto x = o3tl::tryAccess<double>(rValue))
    {
        sTmp.append(*x);
        sTmp.append('s');
    }
    else if (auto pTiming = o3tl::tryAccess<Timing>(rValue))
    {
        const auto svTiming = (*pTiming == Timing_MEDIA)
                                  ? constTimingToString.at(Timing_MEDIA)
                                  : constTimingToString.at(Timing_INDEFINITE);
        sTmp.append(svTiming);
    }
    else if (auto pEvent = o3tl::tryAccess<Event>(rValue))
    {
        OStringBuffer sTmp2;
 
        if (pEvent->Trigger != EventTrigger::NONE)
        {
            if (pEvent->Source.hasValue())
            {
                convertTarget(sTmp, pEvent->Source);
                sTmp.append('.');
            }
 
            convertEnum(sTmp2, pEvent->Trigger, constEventTriggerToString);
 
            sTmp.append(sTmp2);
            sTmp2.setLength(0);
        }
 
        if (pEvent->Offset.hasValue())
        {
            convertTiming(sTmp2, pEvent->Offset);
 
            if (!sTmp.isEmpty())
                sTmp.append('+');
 
            sTmp.append(sTmp2);
            sTmp2.setLength(0);
        }
    }
    else
    {
        OSL_FAIL("sd.unomodel: AnimationsExporter::convertTiming, invalid value type!");
    }
}
 
void AnimationsExporter::convertValue(XMLTokenEnum eAttributeName, OStringBuffer& sTmp,
                                      const Any& rValue) const
{
    if (!rValue.hasValue())
        return;
 
    if (auto pValuePair = o3tl::tryAccess<ValuePair>(rValue))
    {
        OStringBuffer sTmp2;
        convertValue(eAttributeName, sTmp, pValuePair->First);
        sTmp.append(',');
        convertValue(eAttributeName, sTmp2, pValuePair->Second);
        sTmp.append(sTmp2);
    }
    else if (auto pSequence = o3tl::tryAccess<Sequence<Any>>(rValue))
    {
        const sal_Int32 nLength = pSequence->getLength();
        sal_Int32 nElement;
        const Any* pAny = pSequence->getConstArray();
 
        OStringBuffer sTmp2;
 
        for (nElement = 0; nElement < nLength; nElement++, pAny++)
        {
            if (!sTmp.isEmpty())
                sTmp.append(';');
            convertValue(eAttributeName, sTmp2, *pAny);
            sTmp.append(sTmp2);
            sTmp2.setLength(0);
        }
    }
    else
    {
        switch (eAttributeName)
        {
            case XML_X:
            case XML_Y:
            case XML_WIDTH:
            case XML_HEIGHT:
            case XML_ANIMATETRANSFORM:
            case XML_ANIMATEMOTION:
            case XML_ANIMATEPHYSICS:
            {
                if (auto sValue = o3tl::tryAccess<OUString>(rValue))
                {
                    sTmp.append(sValue->toUtf8());
                }
                else if (auto aValue = o3tl::tryAccess<double>(rValue))
                {
                    sTmp.append(*aValue);
                }
                else
                {
                    OSL_FAIL("sd::AnimationsExporter::convertValue(), invalid value type!");
                }
                return;
            }
            case XML_SKEWX:
            case XML_ROTATE:
            case XML_OPACITY:
            case XML_TRANSITIONFILTER:
                if (auto aValue = o3tl::tryAccess<double>(rValue))
                {
                    sTmp.append(*aValue);
                }
                break;
            case XML_TEXT_ROTATION_ANGLE:
                if (auto aValue = o3tl::tryAccess<sal_Int16>(rValue))
                {
                    // on win and armv7 platforms compiler complains
                    // that append(sal_Int16) is ambiguous
                    sTmp.append(static_cast<sal_Int32>(*aValue));
                }
                break;
            case XML_FILL_COLOR:
            case XML_STROKE_COLOR:
            case XML_DIM:
            case XML_COLOR:
            {
                convertColor(sTmp, rValue);
            }
            break;
            case XML_FILL:
                if (auto aValue = o3tl::tryAccess<drawing::FillStyle>(rValue))
                {
                    convertEnum(sTmp, *aValue, constFillStyleToString);
                }
                break;
            case XML_STROKE:
                if (auto aValue = o3tl::tryAccess<drawing::LineStyle>(rValue))
                {
                    convertEnum(sTmp, *aValue, constLineStyleToString);
                }
                break;
            case XML_FONTSIZE:
                if (auto aValue = o3tl::tryAccess<double>(rValue))
                {
                    double fValue = *aValue * 100;
                    fValue += fValue > 0 ? 0.5 : -0.5;
                    auto nValue = static_cast<sal_Int32>(fValue);
                    sTmp.append(nValue); // percent
                }
                break;
            case XML_FONT_WEIGHT:
            case XML_FONT_STYLE:
            case XML_TEXT_UNDERLINE:
                SAL_WARN("sd", "AnimationsExporter::convertValue(): value type "
                                   << GetXMLToken(eAttributeName) << " not supported");
                break;
            case XML_VISIBILITY:
                if (auto aValue = o3tl::tryAccess<bool>(rValue))
                {
                    OUString sValue = *aValue ? GetXMLToken(XML_VISIBLE) : GetXMLToken(XML_HIDDEN);
                    sTmp.append(sValue.toUtf8());
                }
                break;
            default:
                OSL_FAIL("unomodel: AnimationsExporter::convertValue(), invalid AttributeName!");
        }
    }
}
 
void AnimationsExporter::exportContainer(const Reference<XTimeContainer>& xContainer)
{
    try
    {
        const sal_Int32 nNodeType = xContainer->getType();
 
        if (nNodeType == AnimationNodeType::ITERATE)
        {
            OStringBuffer sTmp;
            Reference<XIterateContainer> xIter(xContainer, UNO_QUERY_THROW);
 
            Any aTemp(xIter->getTarget());
            if (aTemp.hasValue())
            {
                convertTarget(sTmp, aTemp);
                mrWriter.put("targetElement", sTmp.makeStringAndClear());
            }
            sal_Int16 nTemp = xIter->getSubItem();
            if (nTemp)
            {
                convertEnum(sTmp, nTemp, constSubItemToString);
                mrWriter.put("subItem", sTmp.makeStringAndClear());
            }
            nTemp = xIter->getIterateType();
            if (nTemp)
            {
                convertEnum(sTmp, nTemp, constIterateTypeToString);
                mrWriter.put("iterateType", sTmp.makeStringAndClear());
            }
            double fTemp = xIter->getIterateInterval();
            if (fTemp != 0)
            {
                OUStringBuffer buf;
                ::sax::Converter::convertDuration(buf, fTemp / (24 * 60 * 60));
                mrWriter.put("iterateInterval", sTmp.makeStringAndClear());
            }
        }
 
        auto anArray = mrWriter.startArray("children");
 
        Reference<XEnumerationAccess> xEnumerationAccess(xContainer, UNO_QUERY_THROW);
        Reference<XEnumeration> xEnumeration(xEnumerationAccess->createEnumeration(),
                                             css::uno::UNO_SET_THROW);
        while (xEnumeration->hasMoreElements())
        {
            Reference<XAnimationNode> xChildNode(xEnumeration->nextElement(), UNO_QUERY_THROW);
            exportNode(xChildNode);
        }
    }
    catch (const RuntimeException&)
    {
        TOOLS_WARN_EXCEPTION("sd", "unomodel: AnimationsExporter");
    }
}
 
void AnimationsExporter::exportAnimate(const Reference<XAnimate>& xAnimate)
{
    try
    {
        const sal_Int16 nNodeType = xAnimate->getType();
 
        OStringBuffer sTmp;
        sal_Int16 nTemp;
        bool bTemp;
 
        Any aTemp(xAnimate->getTarget());
        if (aTemp.hasValue())
        {
            convertTarget(sTmp, aTemp);
            mrWriter.put("targetElement", sTmp.makeStringAndClear());
        }
        nTemp = xAnimate->getSubItem();
        if (nTemp)
        {
            convertEnum(sTmp, nTemp, constSubItemToString);
            mrWriter.put("subItem", sTmp.makeStringAndClear());
        }
 
        XMLTokenEnum eAttributeName = XML_TOKEN_INVALID;
        if (nNodeType == AnimationNodeType::TRANSITIONFILTER)
        {
            eAttributeName = XML_TRANSITIONFILTER;
        }
        else if (nNodeType == AnimationNodeType::ANIMATETRANSFORM)
        {
            eAttributeName = XML_ANIMATETRANSFORM;
        }
        else if (nNodeType == AnimationNodeType::ANIMATEMOTION)
        {
            eAttributeName = XML_ANIMATEMOTION;
        }
        else if (nNodeType == AnimationNodeType::ANIMATEPHYSICS)
        {
            eAttributeName = XML_ANIMATEPHYSICS;
        }
        else
        {
            OString sTemp(xAnimate->getAttributeName().toUtf8());
            if (!sTemp.isEmpty())
            {
                auto iterator = constAttributeNameToXMLEnum.find(sTemp);
                if (iterator != constAttributeNameToXMLEnum.end())
                {
                    eAttributeName = iterator->second;
                    mrWriter.put("attributeName", sTemp);
                }
                else
                {
                    mrWriter.put("attributeName", "invalid");
                }
            }
        }
 
        Sequence<Any> aValues(xAnimate->getValues());
        if (aValues.hasElements())
        {
            aTemp <<= aValues;
            convertValue(eAttributeName, sTmp, aTemp);
            mrWriter.put("values", sTmp.makeStringAndClear());
        }
        else
        {
            aTemp = xAnimate->getFrom();
            if (aTemp.hasValue())
            {
                convertValue(eAttributeName, sTmp, aTemp);
                mrWriter.put("from", sTmp.makeStringAndClear());
            }
 
            aTemp = xAnimate->getBy();
            if (aTemp.hasValue())
            {
                convertValue(eAttributeName, sTmp, aTemp);
                mrWriter.put("by", sTmp.makeStringAndClear());
            }
 
            aTemp = xAnimate->getTo();
            if (aTemp.hasValue())
            {
                convertValue(eAttributeName, sTmp, aTemp);
                mrWriter.put("to", sTmp.makeStringAndClear());
            }
        }
 
        if (nNodeType != AnimationNodeType::SET)
        {
            const Sequence<double> aKeyTimes(xAnimate->getKeyTimes());
            if (aKeyTimes.hasElements())
            {
                for (const auto& rKeyTime : aKeyTimes)
                {
                    if (!sTmp.isEmpty())
                        sTmp.append(';');
 
                    sTmp.append(rKeyTime);
                }
                mrWriter.put("keyTimes", sTmp.makeStringAndClear());
            }
 
            OUString sTemp(xAnimate->getFormula());
            if (!sTemp.isEmpty())
            {
                mrWriter.put("formula", sTemp);
            }
 
            if ((nNodeType != AnimationNodeType::TRANSITIONFILTER)
                && (nNodeType != AnimationNodeType::AUDIO))
            {
                // calcMode  = "discrete | linear | paced | spline"
                nTemp = xAnimate->getCalcMode();
                if (((nNodeType == AnimationNodeType::ANIMATEMOTION)
                     && (nTemp != AnimationCalcMode::PACED))
                    || ((nNodeType != AnimationNodeType::ANIMATEMOTION)
                        && (nTemp != AnimationCalcMode::LINEAR)))
                {
                    convertEnum(sTmp, nTemp, constCalcModeToString);
                    mrWriter.put("calcMode", sTmp.makeStringAndClear());
                }
 
                bTemp = xAnimate->getAccumulate();
                if (bTemp)
                {
                    mrWriter.put("accumulate", "sum");
                }
 
                nTemp = xAnimate->getAdditive();
                if (nTemp != AnimationAdditiveMode::REPLACE)
                {
                    convertEnum(sTmp, nTemp, constAdditiveModeToString);
                    mrWriter.put("additive", sTmp.makeStringAndClear());
                }
            }
 
            const Sequence<TimeFilterPair> aTimeFilter(xAnimate->getTimeFilter());
            if (aTimeFilter.hasElements())
            {
                for (const auto& rPair : aTimeFilter)
                {
                    if (!sTmp.isEmpty())
                        sTmp.append(';');
 
                    sTmp.append(OString::number(rPair.Time) + ","
                                + OString::number(rPair.Progress));
                }
                mrWriter.put("keySplines", sTmp.makeStringAndClear());
            }
        }
 
        switch (nNodeType)
        {
            case AnimationNodeType::ANIMATEMOTION:
            {
                Reference<XAnimateMotion> xAnimateMotion(xAnimate, UNO_QUERY_THROW);
 
                aTemp = xAnimateMotion->getPath();
                if (aTemp.hasValue())
                {
                    convertPath(sTmp, aTemp);
                    mrWriter.put("path", sTmp.makeStringAndClear());
                }
            }
            break;
            case AnimationNodeType::ANIMATEPHYSICS:
            {
                SAL_WARN(
                    "sd",
                    "unomodel: AnimationsExporter::exportAnimate(): AnimatePhysics not supported");
            }
            break;
            case AnimationNodeType::ANIMATECOLOR:
            {
                Reference<XAnimateColor> xAnimateColor(xAnimate, UNO_QUERY_THROW);
 
                nTemp = xAnimateColor->getColorInterpolation();
                mrWriter.put("colorInterpolation",
                             (nTemp == AnimationColorSpace::RGB) ? "rgb" : "hsl");
 
                bTemp = xAnimateColor->getDirection();
                mrWriter.put("colorInterpolationDirection",
                             bTemp ? "clockwise" : "counterClockwise");
            }
            break;
            case AnimationNodeType::ANIMATETRANSFORM:
            {
                mrWriter.put("attributeName", "transform");
 
                Reference<XAnimateTransform> xTransform(xAnimate, UNO_QUERY_THROW);
                nTemp = xTransform->getTransformType();
                convertEnum(sTmp, nTemp, constTransformTypeToString);
                mrWriter.put("transformType", sTmp.makeStringAndClear());
            }
            break;
            case AnimationNodeType::TRANSITIONFILTER:
            {
                Reference<XTransitionFilter> xTransitionFilter(xAnimate, UNO_QUERY);
 
                sal_Int16 nTransition = xTransitionFilter->getTransition();
                convertEnum(sTmp, nTransition, constTransitionTypeToString);
                mrWriter.put("transitionType", sTmp.makeStringAndClear());
 
                sal_Int16 nSubtype = xTransitionFilter->getSubtype();
                if (nSubtype != TransitionSubType::DEFAULT)
                {
                    convertEnum(sTmp, nSubtype, constTransitionSubTypeToString);
                    mrWriter.put("transitionSubType", sTmp.makeStringAndClear());
                }
 
                bTemp = xTransitionFilter->getMode();
                if (!bTemp)
                    mrWriter.put("transitionMode", "out");
 
                bTemp = xTransitionFilter->getDirection();
                if (!bTemp)
                    mrWriter.put("transitionDirection", "reverse");
 
                if ((nTransition == TransitionType::FADE)
                    && ((nSubtype == TransitionSubType::FADETOCOLOR)
                        || (nSubtype == TransitionSubType::FADEFROMCOLOR)))
                {
                    sal_Int32 nColor = xTransitionFilter->getFadeColor();
                    convertColor(sTmp, nColor);
                    mrWriter.put("transitionFadeColor", sTmp.makeStringAndClear());
                }
            }
            break;
            default:
            {
                SAL_WARN("sd",
                         "unomodel: AnimationsExporter::exportAnimate(): not supported node type: "
                             << nNodeType);
            }
        }
    }
    catch (const Exception&)
    {
        TOOLS_WARN_EXCEPTION("sd", "unomodel: AnimationsExporter");
    }
}
 
} // end anonymous namespace
 
SdUnoForbiddenCharsTable::SdUnoForbiddenCharsTable( SdrModel* pModel )
: SvxUnoForbiddenCharsTable( pModel->GetForbiddenCharsTable() ), mpModel( pModel )
{
    StartListening( *pModel );
}
 
void SdUnoForbiddenCharsTable::onChange()
{
    if( mpModel )
    {
        mpModel->ReformatAllTextObjects();
    }
}
 
SdUnoForbiddenCharsTable::~SdUnoForbiddenCharsTable()
{
    SolarMutexGuard g;
 
    if( mpModel )
        EndListening( *mpModel );
}
 
void SdUnoForbiddenCharsTable::Notify( SfxBroadcaster&, const SfxHint& rHint ) noexcept
{
    if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
        return;
    const SdrHint* pSdrHint = static_cast<const SdrHint*>( &rHint );
    if( SdrHintKind::ModelCleared == pSdrHint->GetKind() )
    {
        mpModel = nullptr;
    }
}
 
const sal_uInt16 WID_MODEL_LANGUAGE           =  1;
const sal_uInt16 WID_MODEL_TABSTOP            =  2;
const sal_uInt16 WID_MODEL_VISAREA            =  3;
const sal_uInt16 WID_MODEL_MAPUNIT            =  4;
const sal_uInt16 WID_MODEL_FORBCHARS          =  5;
const sal_uInt16 WID_MODEL_CONTFOCUS          =  6;
const sal_uInt16 WID_MODEL_DSGNMODE           =  7;
const sal_uInt16 WID_MODEL_BASICLIBS          =  8;
const sal_uInt16 WID_MODEL_RUNTIMEUID         =  9;
const sal_uInt16 WID_MODEL_BUILDID            = 10;
const sal_uInt16 WID_MODEL_HASVALIDSIGNATURES = 11;
const sal_uInt16 WID_MODEL_DIALOGLIBS         = 12;
const sal_uInt16 WID_MODEL_FONTS              = 13;
const sal_uInt16 WID_MODEL_INTEROPGRABBAG     = 14;
const sal_uInt16 WID_MODEL_THEME = 15;
const sal_uInt16 WID_MODEL_ALLOWLINKUPDATE    = 16;
 
static const SvxItemPropertySet* ImplGetDrawModelPropertySet()
{
    // Attention: the first parameter HAS TO BE sorted!!!
    const static SfxItemPropertyMapEntry aDrawModelPropertyMap_Impl[] =
    {
        { u"BuildId"_ustr,                WID_MODEL_BUILDID,            ::cppu::UnoType<OUString>::get(),                      0, 0},
        { sUNO_Prop_CharLocale,           WID_MODEL_LANGUAGE,           ::cppu::UnoType<lang::Locale>::get(),                                  0, 0},
        { sUNO_Prop_TabStop,              WID_MODEL_TABSTOP,            ::cppu::UnoType<sal_Int32>::get(),                                     0, 0},
        { sUNO_Prop_VisibleArea,          WID_MODEL_VISAREA,            ::cppu::UnoType<awt::Rectangle>::get(),                                0, 0},
        { sUNO_Prop_MapUnit,              WID_MODEL_MAPUNIT,            ::cppu::UnoType<sal_Int16>::get(),                                     beans::PropertyAttribute::READONLY, 0},
        { sUNO_Prop_ForbiddenCharacters,  WID_MODEL_FORBCHARS,          cppu::UnoType<i18n::XForbiddenCharacters>::get(), beans::PropertyAttribute::READONLY, 0},
        { sUNO_Prop_AutomContFocus,       WID_MODEL_CONTFOCUS,          cppu::UnoType<bool>::get(),                                                 0, 0},
        { sUNO_Prop_ApplyFrmDsgnMode,     WID_MODEL_DSGNMODE,           cppu::UnoType<bool>::get(),                                                 0, 0},
        { u"BasicLibraries"_ustr,         WID_MODEL_BASICLIBS,          cppu::UnoType<script::XLibraryContainer>::get(),  beans::PropertyAttribute::READONLY, 0},
        { u"DialogLibraries"_ustr,        WID_MODEL_DIALOGLIBS,         cppu::UnoType<script::XLibraryContainer>::get(),  beans::PropertyAttribute::READONLY, 0},
        { sUNO_Prop_RuntimeUID,           WID_MODEL_RUNTIMEUID,         ::cppu::UnoType<OUString>::get(),                      beans::PropertyAttribute::READONLY, 0},
        { sUNO_Prop_HasValidSignatures,   WID_MODEL_HASVALIDSIGNATURES, ::cppu::UnoType<sal_Bool>::get(),                      beans::PropertyAttribute::READONLY, 0},
        { sUNO_Prop_AllowLinkUpdate,      WID_MODEL_ALLOWLINKUPDATE,    ::cppu::UnoType<sal_Bool>::get(),                      beans::PropertyAttribute::READONLY, 0},
        { u"Fonts"_ustr,                  WID_MODEL_FONTS,              cppu::UnoType<uno::Sequence<uno::Any>>::get(),                     beans::PropertyAttribute::READONLY, 0},
        { sUNO_Prop_InteropGrabBag,       WID_MODEL_INTEROPGRABBAG,     cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get(),       0, 0},
        { sUNO_Prop_Theme,                WID_MODEL_THEME,              cppu::UnoType<util::XTheme>::get(),       0, 0},
    };
    static SvxItemPropertySet aDrawModelPropertySet_Impl( aDrawModelPropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() );
    return &aDrawModelPropertySet_Impl;
}
 
// this ctor is used from the DocShell
SdXImpressDocument::SdXImpressDocument(::sd::DrawDocShell* pShell, bool bClipBoard)
:   SfxBaseModel( pShell ),
    mpDocShell( pShell ),
    mpDoc( pShell ? pShell->GetDoc() : nullptr ),
    mbDisposed(false),
    mbImpressDoc( pShell && pShell->GetDoc() && pShell->GetDoc()->GetDocumentType() == DocumentType::Impress ),
    mbClipBoard( bClipBoard ),
    mpPropSet( ImplGetDrawModelPropertySet() ),
    mbPaintTextEdit( true )
{
    if( mpDoc )
    {
        StartListening( *mpDoc );
    }
    else
    {
        OSL_FAIL("DocShell is invalid");
    }
}
 
SdXImpressDocument::SdXImpressDocument(SdDrawDocument* pDoc, bool bClipBoard)
:   SfxBaseModel( nullptr ),
    mpDocShell( nullptr ),
    mpDoc( pDoc ),
    mbDisposed(false),
    mbImpressDoc( pDoc && pDoc->GetDocumentType() == DocumentType::Impress ),
    mbClipBoard( bClipBoard ),
    mpPropSet( ImplGetDrawModelPropertySet() ),
    mbPaintTextEdit( true )
{
    if( mpDoc )
    {
        StartListening( *mpDoc );
    }
    else
    {
        OSL_FAIL("SdDrawDocument is invalid");
    }
}
 
/***********************************************************************
*                                                                      *
***********************************************************************/
SdXImpressDocument::~SdXImpressDocument() noexcept
{
}
 
// XInterface
uno::Any SAL_CALL SdXImpressDocument::queryInterface( const uno::Type & rType )
{
    uno::Any aAny;
 
    if (rType == cppu::UnoType<lang::XServiceInfo>::get())
        aAny <<= uno::Reference<lang::XServiceInfo>(this);
    else if (rType == cppu::UnoType<beans::XPropertySet>::get())
        aAny <<= uno::Reference<beans::XPropertySet>(this);
    else if (rType == cppu::UnoType<lang::XMultiServiceFactory>::get())
        aAny <<= uno::Reference<lang::XMultiServiceFactory>(this);
    else if (rType == cppu::UnoType<drawing::XDrawPageDuplicator>::get())
        aAny <<= uno::Reference<drawing::XDrawPageDuplicator>(this);
    else if (rType == cppu::UnoType<drawing::XLayerSupplier>::get())
        aAny <<= uno::Reference<drawing::XLayerSupplier>(this);
    else if (rType == cppu::UnoType<drawing::XMasterPagesSupplier>::get())
        aAny <<= uno::Reference<drawing::XMasterPagesSupplier>(this);
    else if (rType == cppu::UnoType<drawing::XDrawPagesSupplier>::get())
        aAny <<= uno::Reference<drawing::XDrawPagesSupplier>(this);
    else if (rType == cppu::UnoType<presentation::XHandoutMasterSupplier>::get())
        aAny <<= uno::Reference<presentation::XHandoutMasterSupplier>(this);
    else if (rType == cppu::UnoType<document::XLinkTargetSupplier>::get())
        aAny <<= uno::Reference<document::XLinkTargetSupplier>(this);
    else if (rType == cppu::UnoType<style::XStyleFamiliesSupplier>::get())
        aAny <<= uno::Reference<style::XStyleFamiliesSupplier>(this);
    else if (rType == cppu::UnoType<css::ucb::XAnyCompareFactory>::get())
        aAny <<= uno::Reference<css::ucb::XAnyCompareFactory>(this);
    else if (rType == cppu::UnoType<view::XRenderable>::get())
        aAny <<= uno::Reference<view::XRenderable>(this);
    else if (mbImpressDoc && rType == cppu::UnoType<presentation::XPresentationSupplier>::get())
        aAny <<= uno::Reference< presentation::XPresentationSupplier >(this);
    else if (mbImpressDoc && rType == cppu::UnoType<presentation::XCustomPresentationSupplier>::get())
        aAny <<= uno::Reference< presentation::XCustomPresentationSupplier >(this);
    else
        return SfxBaseModel::queryInterface(rType);
 
    return aAny;
}
 
void SAL_CALL SdXImpressDocument::acquire() noexcept
{
    SfxBaseModel::acquire();
}
 
void SAL_CALL SdXImpressDocument::release() noexcept
{
    if (osl_atomic_decrement( &m_refCount ) != 0)
        return;
 
    // restore reference count:
    osl_atomic_increment( &m_refCount );
    if(!mbDisposed)
    {
        try
        {
            dispose();
        }
        catch (const uno::RuntimeException&)
        {
            // don't break throw ()
            TOOLS_WARN_EXCEPTION( "sd", "" );
        }
    }
    SfxBaseModel::release();
}
 
// XUnoTunnel
const css::uno::Sequence< sal_Int8 > & SdXImpressDocument::getUnoTunnelId() noexcept
{
    static const comphelper::UnoIdInit theSdXImpressDocumentUnoTunnelId;
    return theSdXImpressDocumentUnoTunnelId.getSeq();
}
 
sal_Int64 SAL_CALL SdXImpressDocument::getSomething( const css::uno::Sequence< sal_Int8 >& rIdentifier )
{
    if (comphelper::isUnoTunnelId<SdrModel>(rIdentifier))
        return comphelper::getSomething_cast(mpDoc);
 
    return comphelper::getSomethingImpl(rIdentifier, this,
                                        comphelper::FallbackToGetSomethingOf<SfxBaseModel>{});
}
 
// XTypeProvider
uno::Sequence< uno::Type > SAL_CALL SdXImpressDocument::getTypes(  )
{
    ::SolarMutexGuard aGuard;
 
    if( !maTypeSequence.hasElements() )
    {
        uno::Sequence< uno::Type > aTypes( SfxBaseModel::getTypes() );
        aTypes = comphelper::concatSequences(aTypes,
            uno::Sequence {
                cppu::UnoType<beans::XPropertySet>::get(),
                cppu::UnoType<lang::XServiceInfo>::get(),
                cppu::UnoType<lang::XMultiServiceFactory>::get(),
                cppu::UnoType<drawing::XDrawPageDuplicator>::get(),
                cppu::UnoType<drawing::XLayerSupplier>::get(),
                cppu::UnoType<drawing::XMasterPagesSupplier>::get(),
                cppu::UnoType<drawing::XDrawPagesSupplier>::get(),
                cppu::UnoType<document::XLinkTargetSupplier>::get(),
                cppu::UnoType<style::XStyleFamiliesSupplier>::get(),
                cppu::UnoType<css::ucb::XAnyCompareFactory>::get(),
                cppu::UnoType<view::XRenderable>::get() });
        if( mbImpressDoc )
        {
            aTypes = comphelper::concatSequences(aTypes,
                uno::Sequence {
                    cppu::UnoType<presentation::XPresentationSupplier>::get(),
                    cppu::UnoType<presentation::XCustomPresentationSupplier>::get(),
                    cppu::UnoType<presentation::XHandoutMasterSupplier>::get() });
        }
        maTypeSequence = std::move(aTypes);
    }
 
    return maTypeSequence;
}
 
uno::Sequence< sal_Int8 > SAL_CALL SdXImpressDocument::getImplementationId(  )
{
    return css::uno::Sequence<sal_Int8>();
}
 
/***********************************************************************
*                                                                      *
***********************************************************************/
void SdXImpressDocument::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
{
    if( mpDoc )
    {
        if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
        {
            const SdrHint* pSdrHint = static_cast<const SdrHint*>( &rHint );
            if( hasEventListeners() )
            {
                document::EventObject aEvent;
                if( SvxUnoDrawMSFactory::createEvent( mpDoc, pSdrHint, aEvent ) )
                    notifyEvent( aEvent );
            }
 
            if( pSdrHint->GetKind() == SdrHintKind::ModelCleared )
            {
                if( mpDoc )
                    EndListening( *mpDoc );
                mpDoc = nullptr;
                mpDocShell = nullptr;
            }
        }
        else
        {
            // did our SdDrawDocument just died?
            if(rHint.GetId() == SfxHintId::Dying)
            {
                // yes, so we ask for a new one
                if( mpDocShell )
                {
                    SdDrawDocument *pNewDoc = mpDocShell->GetDoc();
 
                    // is there a new one?
                    if( pNewDoc != mpDoc )
                    {
                        mpDoc = pNewDoc;
                        if(mpDoc)
                            StartListening( *mpDoc );
                    }
                }
            }
        }
    }
    SfxBaseModel::Notify( rBC, rHint );
}
 
/******************************************************************************
*                                                                             *
******************************************************************************/
SdPage* SdXImpressDocument::InsertSdPage( sal_uInt16 nPage, bool bDuplicate )
{
    sal_uInt16 nPageCount = mpDoc->GetSdPageCount( PageKind::Standard );
    SdrLayerAdmin& rLayerAdmin = mpDoc->GetLayerAdmin();
    SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background);
    SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects);
 
    rtl::Reference<SdPage> pStandardPage;
 
    if( 0 == nPageCount )
    {
        // this is only used for clipboard where we only have one page
        pStandardPage = mpDoc->AllocSdPage(false);
 
        Size aDefSize(21000, 29700);   // A4 portrait orientation
        pStandardPage->SetSize( aDefSize );
        mpDoc->InsertPage(pStandardPage.get(), 0);
    }
    else
    {
        // here we determine the page after which we should insert
        SdPage* pPreviousStandardPage = mpDoc->GetSdPage( std::min( static_cast<sal_uInt16>(nPageCount - 1), nPage ), PageKind::Standard );
        SdrLayerIDSet aVisibleLayers = pPreviousStandardPage->TRG_GetMasterPageVisibleLayers();
        bool bIsPageBack = aVisibleLayers.IsSet( aBckgrnd );
        bool bIsPageObj = aVisibleLayers.IsSet( aBckgrndObj );
 
        // AutoLayouts must be ready
        mpDoc->StopWorkStartupDelay();
 
        /* First we create a standard page and then a notes page. It is
           guaranteed, that after a standard page the corresponding notes page
           follows. */
 
        sal_uInt16 nStandardPageNum = pPreviousStandardPage->GetPageNum() + 2;
        SdPage* pPreviousNotesPage = static_cast<SdPage*>( mpDoc->GetPage( nStandardPageNum - 1 ) );
        sal_uInt16 nNotesPageNum = nStandardPageNum + 1;
 
        /**************************************************************
        * standard page
        **************************************************************/
        if( bDuplicate )
            pStandardPage = static_cast<SdPage*>( pPreviousStandardPage->CloneSdrPage(*mpDoc).get() );
        else
            pStandardPage = mpDoc->AllocSdPage(false);
 
        pStandardPage->SetSize( pPreviousStandardPage->GetSize() );
        pStandardPage->SetBorder( pPreviousStandardPage->GetLeftBorder(),
                                    pPreviousStandardPage->GetUpperBorder(),
                                    pPreviousStandardPage->GetRightBorder(),
                                    pPreviousStandardPage->GetLowerBorder() );
        pStandardPage->SetOrientation( pPreviousStandardPage->GetOrientation() );
        pStandardPage->SetName(OUString());
 
        // insert page after current page
        mpDoc->InsertPage(pStandardPage.get(), nStandardPageNum);
 
        if( !bDuplicate )
        {
            // use MasterPage of the current page
            pStandardPage->TRG_SetMasterPage(pPreviousStandardPage->TRG_GetMasterPage());
            pStandardPage->SetLayoutName( pPreviousStandardPage->GetLayoutName() );
            pStandardPage->SetAutoLayout(AUTOLAYOUT_NONE, true );
        }
 
        aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background);
        aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects);
        aVisibleLayers.Set(aBckgrnd, bIsPageBack);
        aVisibleLayers.Set(aBckgrndObj, bIsPageObj);
        pStandardPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers);
 
        /**************************************************************
        * notes page
        **************************************************************/
        rtl::Reference<SdPage> pNotesPage;
 
        if( bDuplicate )
            pNotesPage = static_cast<SdPage*>( pPreviousNotesPage->CloneSdrPage(*mpDoc).get() );
        else
            pNotesPage = mpDoc->AllocSdPage(false);
 
        pNotesPage->SetSize( pPreviousNotesPage->GetSize() );
        pNotesPage->SetBorder( pPreviousNotesPage->GetLeftBorder(),
                                pPreviousNotesPage->GetUpperBorder(),
                                pPreviousNotesPage->GetRightBorder(),
                                pPreviousNotesPage->GetLowerBorder() );
        pNotesPage->SetOrientation( pPreviousNotesPage->GetOrientation() );
        pNotesPage->SetName(OUString());
        pNotesPage->SetPageKind(PageKind::Notes);
 
        // insert page after current page
        mpDoc->InsertPage(pNotesPage.get(), nNotesPageNum);
 
        if( !bDuplicate )
        {
            // use MasterPage of the current page
            pNotesPage->TRG_SetMasterPage(pPreviousNotesPage->TRG_GetMasterPage());
            pNotesPage->SetLayoutName( pPreviousNotesPage->GetLayoutName() );
            pNotesPage->SetAutoLayout(AUTOLAYOUT_NOTES, true );
        }
    }
 
    SetModified();
 
    return pStandardPage.get();
}
 
void SdXImpressDocument::SetModified() noexcept
{
    if( mpDoc )
        mpDoc->SetChanged();
}
 
// XModel
void SAL_CALL SdXImpressDocument::lockControllers(  )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    mpDoc->setLock(true);
}
 
void SAL_CALL SdXImpressDocument::unlockControllers(  )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    if( mpDoc->isLocked() )
    {
        mpDoc->setLock(false);
    }
}
 
sal_Bool SAL_CALL SdXImpressDocument::hasControllersLocked(  )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    return mpDoc->isLocked();
}
 
uno::Reference < container::XIndexAccess > SAL_CALL SdXImpressDocument::getViewData()
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    uno::Reference < container::XIndexAccess > xRet( SfxBaseModel::getViewData() );
 
    if( !xRet.is() )
    {
        const std::vector<std::unique_ptr<sd::FrameView>> &rList = mpDoc->GetFrameViewList();
 
        if( !rList.empty() )
        {
            xRet = new comphelper::IndexedPropertyValuesContainer();
 
            uno::Reference < container::XIndexContainer > xCont( xRet, uno::UNO_QUERY );
            DBG_ASSERT( xCont.is(), "SdXImpressDocument::getViewData() failed for OLE object" );
            if( xCont.is() )
            {
                for( sal_uInt32 i = 0, n = rList.size(); i < n; i++ )
                {
                    ::sd::FrameView* pFrameView = rList[ i ].get();
 
                    uno::Sequence< beans::PropertyValue > aSeq;
                    pFrameView->WriteUserDataSequence( aSeq );
                    xCont->insertByIndex( i, uno::Any( aSeq ) );
                }
            }
        }
    }
 
    return xRet;
}
 
void SAL_CALL SdXImpressDocument::setViewData( const uno::Reference < container::XIndexAccess >& xData )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    SfxBaseModel::setViewData( xData );
    if( !(mpDocShell && (mpDocShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED) && xData.is()) )
        return;
 
    const sal_Int32 nCount = xData->getCount();
 
    std::vector<std::unique_ptr<sd::FrameView>> &rViews = mpDoc->GetFrameViewList();
 
    rViews.clear();
 
    uno::Sequence< beans::PropertyValue > aSeq;
    for( sal_Int32 nIndex = 0; nIndex < nCount; nIndex++ )
    {
        if( xData->getByIndex( nIndex ) >>= aSeq )
        {
            std::unique_ptr<::sd::FrameView> pFrameView(new ::sd::FrameView( mpDoc ));
            pFrameView->ReadUserDataSequence( aSeq );
            rViews.push_back( std::move(pFrameView) );
        }
    }
}
 
// XDrawPageDuplicator
uno::Reference< drawing::XDrawPage > SAL_CALL SdXImpressDocument::duplicate( const uno::Reference< drawing::XDrawPage >& xPage )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    // get pPage from xPage and determine the Id (nPos ) afterwards
    SvxDrawPage* pSvxPage = comphelper::getFromUnoTunnel<SvxDrawPage>( xPage );
    if( pSvxPage )
    {
        SdPage* pPage = static_cast<SdPage*>( pSvxPage->GetSdrPage() );
        sal_uInt16 nPos = pPage->GetPageNum();
        nPos = ( nPos - 1 ) / 2;
        pPage = InsertSdPage( nPos, true );
        if( pPage )
        {
            uno::Reference< drawing::XDrawPage > xDrawPage( pPage->getUnoPage(), uno::UNO_QUERY );
            return xDrawPage;
        }
    }
 
    uno::Reference< drawing::XDrawPage > xDrawPage;
    return xDrawPage;
}
 
// XDrawPagesSupplier
uno::Reference< drawing::XDrawPages > SAL_CALL SdXImpressDocument::getDrawPages()
{
    ::SolarMutexGuard aGuard;
 
    return getSdDrawPages();
}
 
rtl::Reference< SdDrawPagesAccess > SdXImpressDocument::getSdDrawPages()
{
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    rtl::Reference< SdDrawPagesAccess > xDrawPages( mxDrawPagesAccess );
 
    if( !xDrawPages.is() )
    {
        initializeDocument();
        xDrawPages = new SdDrawPagesAccess(*this);
        mxDrawPagesAccess = xDrawPages.get();
    }
 
    return xDrawPages;
}
 
// XMasterPagesSupplier
uno::Reference< drawing::XDrawPages > SAL_CALL SdXImpressDocument::getMasterPages()
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    rtl::Reference< SdMasterPagesAccess > xMasterPages( mxMasterPagesAccess );
 
    if( !xMasterPages.is() )
    {
        if ( !hasControllersLocked() )
            initializeDocument();
        xMasterPages = new SdMasterPagesAccess(*this);
        mxMasterPagesAccess = xMasterPages.get();
    }
 
    return xMasterPages;
}
 
// XLayerManagerSupplier
uno::Reference< container::XNameAccess > SAL_CALL SdXImpressDocument::getLayerManager(  )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    rtl::Reference< SdLayerManager >  xLayerManager( mxLayerManager );
 
    if( !xLayerManager.is() )
    {
        xLayerManager = new SdLayerManager(*this);
        mxLayerManager = xLayerManager.get();
    }
 
    return xLayerManager;
}
 
// XCustomPresentationSupplier
uno::Reference< container::XNameContainer > SAL_CALL SdXImpressDocument::getCustomPresentations()
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    rtl::Reference< SdXCustomPresentationAccess >  xCustomPres( mxCustomPresentationAccess );
 
    if( !xCustomPres.is() )
    {
        xCustomPres = new SdXCustomPresentationAccess(*this);
        mxCustomPresentationAccess = xCustomPres.get();
    }
 
    return xCustomPres;
}
 
// XPresentationSupplier
uno::Reference< presentation::XPresentation > SAL_CALL SdXImpressDocument::getPresentation()
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    return mpDoc->getPresentation();
}
 
// XHandoutMasterSupplier
uno::Reference< drawing::XDrawPage > SAL_CALL SdXImpressDocument::getHandoutMasterPage()
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    uno::Reference< drawing::XDrawPage > xPage;
 
    initializeDocument();
    SdPage* pPage = mpDoc->GetMasterSdPage(0, PageKind::Handout);
    if (pPage)
        xPage.set(pPage->getUnoPage(), uno::UNO_QUERY);
    return xPage;
}
 
// XMultiServiceFactory ( SvxFmMSFactory )
 
css::uno::Reference<css::uno::XInterface> SdXImpressDocument::create(
    OUString const & aServiceSpecifier, OUString const & referer)
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    if( aServiceSpecifier == "com.sun.star.drawing.DashTable" )
    {
        if( !mxDashTable.is() )
            mxDashTable = SvxUnoDashTable_createInstance( mpDoc );
 
        return mxDashTable;
    }
    if( aServiceSpecifier == "com.sun.star.drawing.GradientTable" )
    {
        if( !mxGradientTable.is() )
            mxGradientTable = SvxUnoGradientTable_createInstance( mpDoc );
 
        return mxGradientTable;
    }
    if( aServiceSpecifier == "com.sun.star.drawing.HatchTable" )
    {
        if( !mxHatchTable.is() )
            mxHatchTable = SvxUnoHatchTable_createInstance( mpDoc );
 
        return mxHatchTable;
    }
    if( aServiceSpecifier == "com.sun.star.drawing.BitmapTable" )
    {
        if( !mxBitmapTable.is() )
            mxBitmapTable = SvxUnoBitmapTable_createInstance( mpDoc );
 
        return mxBitmapTable;
    }
    if( aServiceSpecifier == "com.sun.star.drawing.TransparencyGradientTable" )
    {
        if( !mxTransGradientTable.is() )
            mxTransGradientTable = SvxUnoTransGradientTable_createInstance( mpDoc );
 
        return mxTransGradientTable;
    }
    if( aServiceSpecifier == "com.sun.star.drawing.MarkerTable" )
    {
        if( !mxMarkerTable.is() )
            mxMarkerTable = SvxUnoMarkerTable_createInstance( mpDoc );
 
        return mxMarkerTable;
    }
    if( aServiceSpecifier == "com.sun.star.text.NumberingRules" )
    {
        return uno::Reference< uno::XInterface >( SvxCreateNumRule( mpDoc ), uno::UNO_QUERY );
    }
    if( aServiceSpecifier == "com.sun.star.drawing.Background" )
    {
        return uno::Reference< uno::XInterface >(
            static_cast<uno::XWeak*>(new SdUnoPageBackground( mpDoc )));
    }
 
    if( aServiceSpecifier == "com.sun.star.drawing.Defaults" )
    {
        if( !mxDrawingPool.is() )
            mxDrawingPool = SdUnoCreatePool( mpDoc );
 
        return mxDrawingPool;
 
    }
 
    if ( aServiceSpecifier == sUNO_Service_ImageMapRectangleObject )
    {
        return SvUnoImageMapRectangleObject_createInstance( ImplGetSupportedMacroItems() );
    }
 
    if ( aServiceSpecifier == sUNO_Service_ImageMapCircleObject )
    {
        return SvUnoImageMapCircleObject_createInstance( ImplGetSupportedMacroItems() );
    }
 
    if ( aServiceSpecifier == sUNO_Service_ImageMapPolygonObject )
    {
        return SvUnoImageMapPolygonObject_createInstance( ImplGetSupportedMacroItems() );
    }
 
    if( aServiceSpecifier == "com.sun.star.document.Settings" ||
        ( !mbImpressDoc && ( aServiceSpecifier == "com.sun.star.drawing.DocumentSettings" ) ) ||
        (  mbImpressDoc && ( aServiceSpecifier == "com.sun.star.presentation.DocumentSettings" ) ) )
    {
        return sd::DocumentSettings_createInstance( this );
    }
 
    if( aServiceSpecifier == "com.sun.star.text.TextField.DateTime" ||
        aServiceSpecifier == "com.sun.star.text.textfield.DateTime" )
    {
        return static_cast<cppu::OWeakObject *>(new SvxUnoTextField( text::textfield::Type::DATE ));
    }
 
    if( aServiceSpecifier == "com.sun.star.presentation.TextField.Header" ||
        aServiceSpecifier == "com.sun.star.presentation.textfield.Header" )
    {
        return static_cast<cppu::OWeakObject *>(new SvxUnoTextField( text::textfield::Type::PRESENTATION_HEADER ));
    }
 
    if( aServiceSpecifier == "com.sun.star.presentation.TextField.Footer" ||
        aServiceSpecifier == "com.sun.star.presentation.textfield.Footer" )
    {
        return static_cast<cppu::OWeakObject *>(new SvxUnoTextField( text::textfield::Type::PRESENTATION_FOOTER ));
    }
 
    if( aServiceSpecifier == "com.sun.star.presentation.TextField.DateTime" ||
        aServiceSpecifier == "com.sun.star.presentation.textfield.DateTime" )
    {
        return static_cast<cppu::OWeakObject *>(new SvxUnoTextField( text::textfield::Type::PRESENTATION_DATE_TIME ));
    }
 
    if( aServiceSpecifier == "com.sun.star.text.TextField.PageName" ||
        aServiceSpecifier == "com.sun.star.text.textfield.PageName" )
    {
        return static_cast<cppu::OWeakObject *>(new SvxUnoTextField( text::textfield::Type::PAGE_NAME ));
    }
 
    if (aServiceSpecifier == "com.sun.star.text.TextField.DocInfo.Custom" ||
        aServiceSpecifier == "com.sun.star.text.textfield.DocInfo.Custom")
    {
        return static_cast<cppu::OWeakObject *>(new SvxUnoTextField(text::textfield::Type::DOCINFO_CUSTOM));
    }
 
    if( aServiceSpecifier == "com.sun.star.xml.NamespaceMap" )
    {
        static sal_uInt16 aWhichIds[] = { SDRATTR_XMLATTRIBUTES, EE_CHAR_XMLATTRIBS, EE_PARA_XMLATTRIBS, 0 };
 
        return svx::NamespaceMap_createInstance( aWhichIds, &mpDoc->GetItemPool() );
    }
 
    // Support creation of GraphicStorageHandler and EmbeddedObjectResolver
    if (aServiceSpecifier == "com.sun.star.document.ExportGraphicStorageHandler")
    {
        return static_cast<cppu::OWeakObject *>(new SvXMLGraphicHelper( SvXMLGraphicHelperMode::Write ));
    }
 
    if (aServiceSpecifier == "com.sun.star.document.ImportGraphicStorageHandler")
    {
        return static_cast<cppu::OWeakObject *>(new SvXMLGraphicHelper( SvXMLGraphicHelperMode::Read ));
    }
 
    if( aServiceSpecifier == "com.sun.star.document.ExportEmbeddedObjectResolver" )
    {
        comphelper::IEmbeddedHelper* pPersist = mpDoc->GetPersist();
        if( nullptr == pPersist )
            throw lang::DisposedException();
 
        return static_cast<cppu::OWeakObject *>(new SvXMLEmbeddedObjectHelper( *pPersist, SvXMLEmbeddedObjectHelperMode::Write ));
    }
 
    if( aServiceSpecifier == "com.sun.star.document.ImportEmbeddedObjectResolver" )
    {
        comphelper::IEmbeddedHelper* pPersist = mpDoc->GetPersist();
        if( nullptr == pPersist )
            throw lang::DisposedException();
 
        return static_cast<cppu::OWeakObject *>(new SvXMLEmbeddedObjectHelper( *pPersist, SvXMLEmbeddedObjectHelperMode::Read ));
    }
 
    uno::Reference< uno::XInterface > xRet;
 
    if( aServiceSpecifier.startsWith( "com.sun.star.presentation.") )
    {
        const std::u16string_view aType( aServiceSpecifier.subView(26) );
        rtl::Reference<SvxShape> pShape;
 
        SdrObjKind nType = SdrObjKind::Text;
        // create a shape wrapper
        if( o3tl::starts_with(aType, u"TitleTextShape" ) )
        {
            nType = SdrObjKind::Text;
        }
        else if( o3tl::starts_with(aType, u"OutlinerShape" ) )
        {
            nType = SdrObjKind::Text;
        }
        else if( o3tl::starts_with(aType, u"SubtitleShape" ) )
        {
            nType = SdrObjKind::Text;
        }
        else if( o3tl::starts_with(aType, u"GraphicObjectShape" ) )
        {
            nType = SdrObjKind::Graphic;
        }
        else if( o3tl::starts_with(aType, u"PageShape" ) )
        {
            nType = SdrObjKind::Page;
        }
        else if( o3tl::starts_with(aType, u"OLE2Shape" ) )
        {
            nType = SdrObjKind::OLE2;
        }
        else if( o3tl::starts_with(aType, u"ChartShape" ) )
        {
            nType = SdrObjKind::OLE2;
        }
        else if( o3tl::starts_with(aType, u"CalcShape" ) )
        {
            nType = SdrObjKind::OLE2;
        }
        else if( o3tl::starts_with(aType, u"TableShape" ) )
        {
            nType = SdrObjKind::Table;
        }
        else if( o3tl::starts_with(aType, u"OrgChartShape" ) )
        {
            nType = SdrObjKind::OLE2;
        }
        else if( o3tl::starts_with(aType, u"NotesShape" ) )
        {
            nType = SdrObjKind::Text;
        }
        else if( o3tl::starts_with(aType, u"HandoutShape" ) )
        {
            nType = SdrObjKind::Page;
        }
        else if( o3tl::starts_with(aType, u"FooterShape" ) )
        {
            nType = SdrObjKind::Text;
        }
        else if( o3tl::starts_with(aType, u"HeaderShape" ) )
        {
            nType = SdrObjKind::Text;
        }
        else if( o3tl::starts_with(aType, u"SlideNumberShape" ) )
        {
            nType = SdrObjKind::Text;
        }
        else if( o3tl::starts_with(aType, u"DateTimeShape" ) )
        {
            nType = SdrObjKind::Text;
        }
        else if( o3tl::starts_with(aType, u"MediaShape" ) )
        {
            nType = SdrObjKind::Media;
        }
        else
        {
            throw lang::ServiceNotRegisteredException();
        }
 
        // create the API wrapper
        pShape = CreateSvxShapeByTypeAndInventor( nType, SdrInventor::Default, referer );
 
        // set shape type
        if( pShape && !mbClipBoard )
            pShape->SetShapeType(aServiceSpecifier);
 
        xRet = static_cast<uno::XWeak*>(pShape.get());
    }
    else if ( aServiceSpecifier == "com.sun.star.drawing.TableShape" )
    {
        rtl::Reference<SvxShape> pShape = CreateSvxShapeByTypeAndInventor( SdrObjKind::Table, SdrInventor::Default, referer );
        if( pShape && !mbClipBoard )
            pShape->SetShapeType(aServiceSpecifier);
 
        xRet = static_cast<uno::XWeak*>(pShape.get());
    }
    else
    {
        xRet = SvxFmMSFactory::createInstance( aServiceSpecifier );
    }
 
    uno::Reference< drawing::XShape > xShape( xRet, uno::UNO_QUERY );
    SvxShape* pShape = xShape.is() ? comphelper::getFromUnoTunnel<SvxShape>(xShape) : nullptr;
    if (pShape)
    {
        xRet.clear();
        new SdXShape( pShape, this );
        xRet = xShape;
        xShape.clear();
    }
 
    return xRet;
}
 
uno::Reference< uno::XInterface > SAL_CALL SdXImpressDocument::createInstance( const OUString& aServiceSpecifier )
{
    return create(aServiceSpecifier, u""_ustr);
}
 
css::uno::Reference<css::uno::XInterface>
SdXImpressDocument::createInstanceWithArguments(
    OUString const & ServiceSpecifier,
    css::uno::Sequence<css::uno::Any> const & Arguments)
{
    OUString arg;
    if ((ServiceSpecifier == "com.sun.star.drawing.GraphicObjectShape"
         || ServiceSpecifier == "com.sun.star.drawing.AppletShape"
         || ServiceSpecifier == "com.sun.star.drawing.FrameShape"
         || ServiceSpecifier == "com.sun.star.drawing.OLE2Shape"
         || ServiceSpecifier == "com.sun.star.drawing.MediaShape"
         || ServiceSpecifier == "com.sun.star.drawing.PluginShape"
         || ServiceSpecifier == "com.sun.star.presentation.MediaShape")
        && Arguments.getLength() == 1 && (Arguments[0] >>= arg))
    {
        return create(ServiceSpecifier, arg);
    }
    return SvxFmMSFactory::createInstanceWithArguments(
        ServiceSpecifier, Arguments);
}
 
uno::Sequence< OUString > SAL_CALL SdXImpressDocument::getAvailableServiceNames()
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    const uno::Sequence< OUString > aSNS_ORG( SvxFmMSFactory::getAvailableServiceNames() );
 
    uno::Sequence< OUString > aSNS_Common{ u"com.sun.star.drawing.DashTable"_ustr,
                                           u"com.sun.star.drawing.GradientTable"_ustr,
                                           u"com.sun.star.drawing.HatchTable"_ustr,
                                           u"com.sun.star.drawing.BitmapTable"_ustr,
                                           u"com.sun.star.drawing.TransparencyGradientTable"_ustr,
                                           u"com.sun.star.drawing.MarkerTable"_ustr,
                                           u"com.sun.star.text.NumberingRules"_ustr,
                                           u"com.sun.star.drawing.Background"_ustr,
                                           u"com.sun.star.document.Settings"_ustr,
                                           sUNO_Service_ImageMapRectangleObject,
                                           sUNO_Service_ImageMapCircleObject,
                                           sUNO_Service_ImageMapPolygonObject,
                                           u"com.sun.star.xml.NamespaceMap"_ustr,
 
                                           // Support creation of GraphicStorageHandler and EmbeddedObjectResolver
                                           u"com.sun.star.document.ExportGraphicStorageHandler"_ustr,
                                           u"com.sun.star.document.ImportGraphicStorageHandler"_ustr,
                                           u"com.sun.star.document.ExportEmbeddedObjectResolver"_ustr,
                                           u"com.sun.star.document.ImportEmbeddedObjectResolver"_ustr,
                                           u"com.sun.star.drawing.TableShape"_ustr };
 
    uno::Sequence< OUString > aSNS_Specific;
 
    if(mbImpressDoc)
        aSNS_Specific = { u"com.sun.star.presentation.TitleTextShape"_ustr,
                          u"com.sun.star.presentation.OutlinerShape"_ustr,
                          u"com.sun.star.presentation.SubtitleShape"_ustr,
                          u"com.sun.star.presentation.GraphicObjectShape"_ustr,
                          u"com.sun.star.presentation.ChartShape"_ustr,
                          u"com.sun.star.presentation.PageShape"_ustr,
                          u"com.sun.star.presentation.OLE2Shape"_ustr,
                          u"com.sun.star.presentation.TableShape"_ustr,
                          u"com.sun.star.presentation.OrgChartShape"_ustr,
                          u"com.sun.star.presentation.NotesShape"_ustr,
                          u"com.sun.star.presentation.HandoutShape"_ustr,
                          u"com.sun.star.presentation.DocumentSettings"_ustr,
                          u"com.sun.star.presentation.FooterShape"_ustr,
                          u"com.sun.star.presentation.HeaderShape"_ustr,
                          u"com.sun.star.presentation.SlideNumberShape"_ustr,
                          u"com.sun.star.presentation.DateTimeShape"_ustr,
                          u"com.sun.star.presentation.CalcShape"_ustr,
                          u"com.sun.star.presentation.MediaShape"_ustr };
    else
        aSNS_Specific = { u"com.sun.star.drawing.DocumentSettings"_ustr };
 
    return comphelper::concatSequences( aSNS_ORG, aSNS_Common, aSNS_Specific );
}
 
// lang::XServiceInfo
OUString SAL_CALL SdXImpressDocument::getImplementationName()
{
    return u"SdXImpressDocument"_ustr;
    /* // Matching the .component information:
       return mbImpressDoc
           ? OUString("com.sun.star.comp.Draw.PresentationDocument")
           : OUString("com.sun.star.comp.Draw.DrawingDocument");
    */
}
 
sal_Bool SAL_CALL SdXImpressDocument::supportsService( const OUString& ServiceName )
{
    return cppu::supportsService(this, ServiceName);
}
 
uno::Sequence< OUString > SAL_CALL SdXImpressDocument::getSupportedServiceNames()
{
    ::SolarMutexGuard aGuard;
 
    return { u"com.sun.star.document.OfficeDocument"_ustr,
             u"com.sun.star.drawing.GenericDrawingDocument"_ustr,
             u"com.sun.star.drawing.DrawingDocumentFactory"_ustr,
             mbImpressDoc?u"com.sun.star.presentation.PresentationDocument"_ustr:u"com.sun.star.drawing.DrawingDocument"_ustr };
}
 
// XPropertySet
uno::Reference< beans::XPropertySetInfo > SAL_CALL SdXImpressDocument::getPropertySetInfo(  )
{
    ::SolarMutexGuard aGuard;
    return mpPropSet->getPropertySetInfo();
}
 
void SAL_CALL SdXImpressDocument::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(aPropertyName);
 
    switch( pEntry ? pEntry->nWID : -1 )
    {
        case WID_MODEL_LANGUAGE:
        {
            lang::Locale aLocale;
            if(!(aValue >>= aLocale))
                throw lang::IllegalArgumentException();
 
            mpDoc->SetLanguage( LanguageTag::convertToLanguageType(aLocale), EE_CHAR_LANGUAGE );
            break;
        }
        case WID_MODEL_TABSTOP:
        {
            sal_Int32 nValue = 0;
            if(!(aValue >>= nValue) || nValue < 0 )
                throw lang::IllegalArgumentException();
 
            mpDoc->SetDefaultTabulator(static_cast<sal_uInt16>(nValue));
            break;
        }
        case WID_MODEL_VISAREA:
            {
                SfxObjectShell* pEmbeddedObj = mpDoc->GetDocSh();
                if( !pEmbeddedObj )
                    break;
 
                awt::Rectangle aVisArea;
                if( !(aValue >>= aVisArea) || (aVisArea.Width < 0) || (aVisArea.Height < 0) )
                    throw lang::IllegalArgumentException();
 
                sal_Int32 nRight, nTop;
                if (o3tl::checked_add(aVisArea.X, aVisArea.Width, nRight) || o3tl::checked_add(aVisArea.Y, aVisArea.Height, nTop))
                    throw lang::IllegalArgumentException();
 
                pEmbeddedObj->SetVisArea(::tools::Rectangle(aVisArea.X, aVisArea.Y, nRight, nTop));
            }
            break;
        case WID_MODEL_CONTFOCUS:
            {
                bool bFocus = false;
                if( !(aValue >>= bFocus ) )
                    throw lang::IllegalArgumentException();
                mpDoc->SetAutoControlFocus( bFocus );
            }
            break;
        case WID_MODEL_DSGNMODE:
            {
                bool bMode = false;
                if( !(aValue >>= bMode ) )
                    throw lang::IllegalArgumentException();
                mpDoc->SetOpenInDesignMode( bMode );
            }
            break;
        case WID_MODEL_BUILDID:
            aValue >>= maBuildId;
            return;
        case WID_MODEL_MAPUNIT:
        case WID_MODEL_BASICLIBS:
        case WID_MODEL_RUNTIMEUID: // is read-only
        case WID_MODEL_DIALOGLIBS:
        case WID_MODEL_FONTS:
            throw beans::PropertyVetoException();
        case WID_MODEL_INTEROPGRABBAG:
            setGrabBagItem(aValue);
            break;
        case WID_MODEL_THEME:
            {
                SdrModel& rModel = getSdrModelFromUnoModel();
                std::shared_ptr<model::Theme> pTheme = model::Theme::FromAny(aValue);
                rModel.setTheme(pTheme);
            }
            break;
        default:
            throw beans::UnknownPropertyException( aPropertyName, static_cast<cppu::OWeakObject*>(this));
    }
 
    SetModified();
}
 
uno::Any SAL_CALL SdXImpressDocument::getPropertyValue( const OUString& PropertyName )
{
    ::SolarMutexGuard aGuard;
 
    uno::Any aAny;
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(PropertyName);
 
    switch( pEntry ? pEntry->nWID : -1 )
    {
        case WID_MODEL_LANGUAGE:
        {
            LanguageType eLang = mpDoc->GetLanguage( EE_CHAR_LANGUAGE );
            aAny <<= LanguageTag::convertToLocale( eLang);
            break;
        }
        case WID_MODEL_TABSTOP:
            aAny <<= static_cast<sal_Int32>(mpDoc->GetDefaultTabulator());
            break;
        case WID_MODEL_VISAREA:
            {
                SfxObjectShell* pEmbeddedObj = mpDoc->GetDocSh();
                if( !pEmbeddedObj )
                    break;
 
                const ::tools::Rectangle& aRect = pEmbeddedObj->GetVisArea();
                awt::Rectangle aVisArea( aRect.Left(), aRect.Top(), aRect.getOpenWidth(), aRect.getOpenHeight() );
                aAny <<= aVisArea;
            }
            break;
        case WID_MODEL_MAPUNIT:
            {
                SfxObjectShell* pEmbeddedObj = mpDoc->GetDocSh();
                if( !pEmbeddedObj )
                    break;
 
                sal_Int16 nMeasureUnit = 0;
                SvxMapUnitToMeasureUnit( pEmbeddedObj->GetMapUnit(), nMeasureUnit );
                aAny <<= nMeasureUnit;
        }
        break;
        case WID_MODEL_FORBCHARS:
        {
            aAny <<= getForbiddenCharsTable();
        }
        break;
        case WID_MODEL_CONTFOCUS:
            aAny <<= mpDoc->GetAutoControlFocus();
            break;
        case WID_MODEL_DSGNMODE:
            aAny <<= mpDoc->GetOpenInDesignMode();
            break;
        case WID_MODEL_BASICLIBS:
            aAny <<= mpDocShell->GetBasicContainer();
            break;
        case WID_MODEL_DIALOGLIBS:
            aAny <<= mpDocShell->GetDialogContainer();
            break;
        case WID_MODEL_RUNTIMEUID:
            aAny <<= getRuntimeUID();
            break;
        case WID_MODEL_BUILDID:
            return uno::Any( maBuildId );
        case WID_MODEL_HASVALIDSIGNATURES:
            aAny <<= hasValidSignatures();
            break;
        case WID_MODEL_ALLOWLINKUPDATE:
        {
            comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = mpDocShell->getEmbeddedObjectContainer();
            aAny <<= rEmbeddedObjectContainer.getUserAllowsLinkUpdate();
            break;
        }
        case WID_MODEL_FONTS:
            {
                uno::Sequence<uno::Any> aSeq;
                int nSeqIndex = 0;
 
                sal_uInt16 const aWhichIds[] { EE_CHAR_FONTINFO, EE_CHAR_FONTINFO_CJK,
                                               EE_CHAR_FONTINFO_CTL };
 
                const SfxItemPool& rPool = mpDoc->GetPool();
 
                for(sal_uInt16 nWhichId : aWhichIds)
                {
                    ItemSurrogates aSurrogates;
                    rPool.GetItemSurrogates(aSurrogates, nWhichId);
                    const sal_uInt32 nItems(aSurrogates.size());
 
                    aSeq.realloc( aSeq.getLength() + nItems*5 + 5 );
                    auto pSeq = aSeq.getArray();
 
                    for (const SfxPoolItem* pItem : aSurrogates)
                    {
                        const SvxFontItem *pFont = static_cast<const SvxFontItem *>(pItem);
 
                        pSeq[nSeqIndex++] <<= pFont->GetFamilyName();
                        pSeq[nSeqIndex++] <<= pFont->GetStyleName();
                        pSeq[nSeqIndex++] <<= sal_Int16(pFont->GetFamily());
                        pSeq[nSeqIndex++] <<= sal_Int16(pFont->GetPitch());
                        pSeq[nSeqIndex++] <<= sal_Int16(pFont->GetCharSet());
                    }
 
                    const SvxFontItem& rFont = static_cast<const SvxFontItem&>(rPool.GetUserOrPoolDefaultItem( nWhichId ));
 
                    pSeq[nSeqIndex++] <<= rFont.GetFamilyName();
                    pSeq[nSeqIndex++] <<= rFont.GetStyleName();
                    pSeq[nSeqIndex++] <<= sal_Int16(rFont.GetFamily());
                    pSeq[nSeqIndex++] <<= sal_Int16(rFont.GetPitch());
                    pSeq[nSeqIndex++] <<= sal_Int16(rFont.GetCharSet());
 
                }
 
                aSeq.realloc( nSeqIndex );
                aAny <<= aSeq;
                break;
            }
        case WID_MODEL_INTEROPGRABBAG:
            getGrabBagItem(aAny);
            break;
        case WID_MODEL_THEME:
            {
                SdrModel& rModel = getSdrModelFromUnoModel();
                auto const& pTheme = rModel.getTheme();
                if (pTheme)
                {
                    pTheme->ToAny(aAny);
                }
                else
                {
                    beans::PropertyValues aValues;
                    aAny <<= aValues;
                }
                break;
            }
        default:
            throw beans::UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this));
    }
 
    return aAny;
}
 
void SAL_CALL SdXImpressDocument::addPropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >&  ) {}
void SAL_CALL SdXImpressDocument::removePropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >&  ) {}
void SAL_CALL SdXImpressDocument::addVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >&  ) {}
void SAL_CALL SdXImpressDocument::removeVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >&  ) {}
 
// XLinkTargetSupplier
uno::Reference< container::XNameAccess > SAL_CALL SdXImpressDocument::getLinks()
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    rtl::Reference< SdDocLinkTargets > xLinks( mxLinks );
    if( !xLinks.is() )
    {
        xLinks = new SdDocLinkTargets( *this );
        mxLinks = xLinks.get();
    }
    return xLinks;
}
 
// XStyleFamiliesSupplier
uno::Reference< container::XNameAccess > SAL_CALL SdXImpressDocument::getStyleFamilies(  )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    uno::Reference< container::XNameAccess > xStyles( static_cast< OWeakObject* >( mpDoc->GetStyleSheetPool() ), css::uno::UNO_QUERY );
    return xStyles;
}
 
// XAnyCompareFactory
uno::Reference< css::ucb::XAnyCompare > SAL_CALL SdXImpressDocument::createAnyCompareByName( const OUString& )
{
    return SvxCreateNumRuleCompare();
}
 
// XRenderable
sal_Int32 SAL_CALL SdXImpressDocument::getRendererCount( const uno::Any& rSelection,
                                                         const uno::Sequence< beans::PropertyValue >&  )
{
    ::SolarMutexGuard aGuard;
    sal_Int32   nRet = 0;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    if (mpDocShell)
    {
        uno::Reference< frame::XModel > xModel;
 
        rSelection >>= xModel;
 
        if( xModel == mpDocShell->GetModel() )
            nRet = mpDoc->GetSdPageCount( PageKind::Standard );
        else
        {
            uno::Reference< drawing::XShapes > xShapes;
 
            rSelection >>= xShapes;
 
            if( xShapes.is() && xShapes->getCount() )
                nRet = 1;
        }
    }
    return nRet;
}
 
uno::Sequence< beans::PropertyValue > SAL_CALL SdXImpressDocument::getRenderer( sal_Int32 , const uno::Any& ,
                                                                                const uno::Sequence< beans::PropertyValue >& rxOptions )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    bool bExportNotesPages = false;
    for( const auto& rOption : rxOptions )
    {
        if ( rOption.Name == "ExportNotesPages" )
            rOption.Value >>= bExportNotesPages;
    }
    uno::Sequence< beans::PropertyValue > aRenderer;
    if (mpDocShell)
    {
        awt::Size aPageSize;
        if ( bExportNotesPages )
        {
            Size aNotesPageSize = mpDoc->GetSdPage( 0, PageKind::Notes )->GetSize();
            aPageSize = awt::Size( aNotesPageSize.Width(), aNotesPageSize.Height() );
        }
        else
        {
            const ::tools::Rectangle aVisArea( mpDocShell->GetVisArea( embed::Aspects::MSOLE_DOCPRINT ) );
            aPageSize = awt::Size( aVisArea.GetWidth(), aVisArea.GetHeight() );
        }
        aRenderer = { comphelper::makePropertyValue(u"PageSize"_ustr, aPageSize) };
    }
    return aRenderer;
}
 
namespace {
 
class ImplRenderPaintProc : public sdr::contact::ViewObjectContactRedirector
{
    const SdrLayerAdmin&    rLayerAdmin;
    SdrPageView*            pSdrPageView;
 
public:
    bool IsVisible  ( const SdrObject* pObj ) const;
    bool IsPrintable( const SdrObject* pObj ) const;
 
    ImplRenderPaintProc(const SdrLayerAdmin& rLA, SdrPageView* pView);
 
    // all default implementations just call the same methods at the original. To do something
    // different, override the method and at least do what the method does.
    virtual void createRedirectedPrimitive2DSequence(
        const sdr::contact::ViewObjectContact& rOriginal,
        const sdr::contact::DisplayInfo& rDisplayInfo,
        drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override;
};
 
}
 
ImplRenderPaintProc::ImplRenderPaintProc(const SdrLayerAdmin& rLA, SdrPageView *const pView)
    : rLayerAdmin(rLA)
    , pSdrPageView(pView)
{
}
 
static sal_Int32 ImplPDFGetBookmarkPage( const OUString& rBookmark, SdDrawDocument const & rDoc )
{
    sal_Int32 nPage = -1;
 
    OUString aBookmark( rBookmark );
 
    if( rBookmark.startsWith("#") )
        aBookmark = rBookmark.copy( 1 );
 
    // is the bookmark a page ?
    bool        bIsMasterPage;
    sal_uInt16  nPgNum = rDoc.GetPageByName( aBookmark, bIsMasterPage );
 
    if ( nPgNum == SDRPAGE_NOTFOUND )
    {
        // is the bookmark an object ?
        SdrObject* pObj = rDoc.GetObj( aBookmark );
        if (pObj)
            nPgNum = pObj->getSdrPageFromSdrObject()->GetPageNum();
    }
    if ( nPgNum != SDRPAGE_NOTFOUND )
        nPage = ( nPgNum - 1 ) / 2;
    return nPage;
}
 
static void ImplPDFExportComments( const uno::Reference< drawing::XDrawPage >& xPage, vcl::PDFExtOutDevData& rPDFExtOutDevData )
{
    try
    {
        uno::Reference< office::XAnnotationAccess > xAnnotationAccess( xPage, uno::UNO_QUERY_THROW );
        uno::Reference< office::XAnnotationEnumeration > xAnnotationEnumeration( xAnnotationAccess->createAnnotationEnumeration() );
 
        while( xAnnotationEnumeration->hasMoreElements() )
        {
            uno::Reference<office::XAnnotation> xAnnotation(xAnnotationEnumeration->nextElement());
 
            geometry::RealPoint2D aRealPoint2D(xAnnotation->getPosition());
            geometry::RealSize2D aRealSize2D(xAnnotation->getSize());
 
            Point aPoint(aRealPoint2D.X * 100.0, aRealPoint2D.Y * 100.0);
            Size aSize(aRealSize2D.Width * 100.0, aRealSize2D.Height * 100.0);
 
            Point aPopupPoint(aPoint.X(), aPoint.Y());
            Size aPopupSize(aSize.Width() * 10.0, aSize.Height() * 10.0);
 
            uno::Reference<text::XText> xText(xAnnotation->getTextRange());
 
            vcl::pdf::PDFNote aNote;
            aNote.maTitle = xAnnotation->getAuthor();
            aNote.maContents = xText->getString();
            aNote.maModificationDate = xAnnotation->getDateTime();
            auto* pAnnotation = dynamic_cast<sd::Annotation*>(xAnnotation.get());
 
            if (pAnnotation && pAnnotation->getCreationInfo().meType != sdr::annotation::AnnotationType::None)
            {
                sdr::annotation::CreationInfo const& rCreation = pAnnotation->getCreationInfo();
                aNote.maPolygons = rCreation.maPolygons;
                aNote.maAnnotationColor = rCreation.maColor;
                aNote.maInteriorColor = rCreation.maFillColor;
                aNote.mfWidth = rCreation.mnWidth;
                switch (rCreation.meType)
                {
                    case sdr::annotation::AnnotationType::Square:
                        aNote.meType = vcl::pdf::PDFAnnotationSubType::Square; break;
                    case sdr::annotation::AnnotationType::Circle:
                        aNote.meType = vcl::pdf::PDFAnnotationSubType::Circle; break;
                    case sdr::annotation::AnnotationType::Polygon:
                        aNote.meType = vcl::pdf::PDFAnnotationSubType::Polygon; break;
                    case sdr::annotation::AnnotationType::Ink:
                        aNote.meType = vcl::pdf::PDFAnnotationSubType::Ink; break;
                    case sdr::annotation::AnnotationType::Highlight:
                        aNote.meType = vcl::pdf::PDFAnnotationSubType::Highlight; break;
                    case sdr::annotation::AnnotationType::Line:
                        aNote.meType = vcl::pdf::PDFAnnotationSubType::Line; break;
                    case sdr::annotation::AnnotationType::FreeText:
                        aNote.meType = vcl::pdf::PDFAnnotationSubType::FreeText; break;
                    default:
                        aNote.meType = vcl::pdf::PDFAnnotationSubType::Text;
                        break;
                }
            }
 
            rPDFExtOutDevData.CreateNote(::tools::Rectangle(aPoint, aSize), aNote,
                                         ::tools::Rectangle(aPopupPoint, aPopupSize));
        }
    }
    catch (const uno::Exception&)
    {
    }
}
 
static void ImplPDFExportShapeInteraction( const uno::Reference< drawing::XShape >& xShape, SdDrawDocument& rDoc, vcl::PDFExtOutDevData& rPDFExtOutDevData )
{
    if ( xShape->getShapeType() == "com.sun.star.drawing.GroupShape" )
    {
        uno::Reference< container::XIndexAccess > xIndexAccess( xShape, uno::UNO_QUERY );
        if ( xIndexAccess.is() )
        {
            sal_Int32 i, nCount = xIndexAccess->getCount();
            for ( i = 0; i < nCount; i++ )
            {
                uno::Reference< drawing::XShape > xSubShape( xIndexAccess->getByIndex( i ), uno::UNO_QUERY );
                if ( xSubShape.is() )
                    ImplPDFExportShapeInteraction( xSubShape, rDoc, rPDFExtOutDevData );
            }
        }
    }
    else
    {
        uno::Reference< beans::XPropertySet > xShapePropSet( xShape, uno::UNO_QUERY );
        if( xShapePropSet.is() )
        {
            Size        aPageSize( rDoc.GetSdPage( 0, PageKind::Standard )->GetSize() );
            Point aPoint( 0, 0 );
            ::tools::Rectangle   aPageRect( aPoint, aPageSize );
 
            awt::Point  aShapePos( xShape->getPosition() );
            awt::Size   aShapeSize( xShape->getSize() );
            ::tools::Rectangle   aLinkRect( Point( aShapePos.X, aShapePos.Y ), Size( aShapeSize.Width, aShapeSize.Height ) );
 
            // Handle linked videos.
            if (xShape->getShapeType() == "com.sun.star.drawing.MediaShape" || xShape->getShapeType() == "com.sun.star.presentation.MediaShape")
            {
                OUString title;
                xShapePropSet->getPropertyValue(u"Title"_ustr) >>= title;
                OUString description;
                xShapePropSet->getPropertyValue(u"Description"_ustr) >>= description;
                OUString const altText(title.isEmpty()
                    ? description
                    : description.isEmpty()
                        ? title
                        : OUString::Concat(title) + OUString::Concat("\n") + OUString::Concat(description));
 
                OUString aMediaURL;
                xShapePropSet->getPropertyValue(u"MediaURL"_ustr) >>= aMediaURL;
                if (!aMediaURL.isEmpty())
                {
                    SdrObject const*const pSdrObj(SdrObject::getSdrObjectFromXShape(xShape));
                    OUString const mimeType(xShapePropSet->getPropertyValue(u"MediaMimeType"_ustr).get<OUString>());
                    sal_Int32 nScreenId = rPDFExtOutDevData.CreateScreen(aLinkRect, altText, mimeType, rPDFExtOutDevData.GetCurrentPageNumber(), pSdrObj);
                    if (aMediaURL.startsWith("vnd.sun.star.Package:"))
                    {
                        OUString aTempFileURL;
                        xShapePropSet->getPropertyValue(u"PrivateTempFileURL"_ustr) >>= aTempFileURL;
                        rPDFExtOutDevData.SetScreenStream(nScreenId, aTempFileURL);
                    }
                    else
                        rPDFExtOutDevData.SetScreenURL(nScreenId, aMediaURL);
                }
            }
 
            presentation::ClickAction eCa;
            uno::Any aAny( xShapePropSet->getPropertyValue( u"OnClick"_ustr ) );
            if ( aAny >>= eCa )
            {
                OUString const actionName(SdResId(SdTPAction::GetClickActionSdResId(eCa)));
                switch ( eCa )
                {
                    case presentation::ClickAction_LASTPAGE :
                    {
                        sal_Int32 nCount = rDoc.GetSdPageCount( PageKind::Standard );
                        sal_Int32 nDestId = rPDFExtOutDevData.CreateDest( aPageRect, nCount - 1, vcl::PDFWriter::DestAreaType::FitRectangle );
                        sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName);
                        rPDFExtOutDevData.SetLinkDest( nLinkId, nDestId );
                    }
                    break;
                    case presentation::ClickAction_FIRSTPAGE :
                    {
                        sal_Int32 nDestId = rPDFExtOutDevData.CreateDest( aPageRect, 0, vcl::PDFWriter::DestAreaType::FitRectangle );
                        sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName);
                        rPDFExtOutDevData.SetLinkDest( nLinkId, nDestId );
                    }
                    break;
                    case presentation::ClickAction_PREVPAGE :
                    {
                        sal_Int32 nDestPage = rPDFExtOutDevData.GetCurrentPageNumber();
                        if ( nDestPage )
                            nDestPage--;
                        sal_Int32 nDestId = rPDFExtOutDevData.CreateDest( aPageRect, nDestPage, vcl::PDFWriter::DestAreaType::FitRectangle );
                        sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName);
                        rPDFExtOutDevData.SetLinkDest( nLinkId, nDestId );
                    }
                    break;
                    case presentation::ClickAction_NEXTPAGE :
                    {
                        sal_Int32 nDestPage = rPDFExtOutDevData.GetCurrentPageNumber() + 1;
                        sal_Int32 nLastPage = rDoc.GetSdPageCount( PageKind::Standard ) - 1;
                        if ( nDestPage > nLastPage )
                            nDestPage = nLastPage;
                        sal_Int32 nDestId = rPDFExtOutDevData.CreateDest( aPageRect, nDestPage, vcl::PDFWriter::DestAreaType::FitRectangle );
                        sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName);
                        rPDFExtOutDevData.SetLinkDest( nLinkId, nDestId );
                    }
                    break;
 
                    case presentation::ClickAction_PROGRAM :
                    case presentation::ClickAction_BOOKMARK :
                    case presentation::ClickAction_DOCUMENT :
                    {
                        OUString aBookmark;
                        xShapePropSet->getPropertyValue( u"Bookmark"_ustr ) >>= aBookmark;
                        if( !aBookmark.isEmpty() )
                        {
                            switch( eCa )
                            {
                                case presentation::ClickAction_DOCUMENT :
                                case presentation::ClickAction_PROGRAM :
                                {
                                    sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName);
                                    rPDFExtOutDevData.SetLinkURL( nLinkId, aBookmark );
                                }
                                break;
                                case presentation::ClickAction_BOOKMARK :
                                {
                                    sal_Int32 nPage = ImplPDFGetBookmarkPage( aBookmark, rDoc );
                                    if ( nPage != -1 )
                                    {
                                        sal_Int32 nDestId = rPDFExtOutDevData.CreateDest( aPageRect, nPage, vcl::PDFWriter::DestAreaType::FitRectangle );
                                        sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName);
                                        rPDFExtOutDevData.SetLinkDest( nLinkId, nDestId );
                                    }
                                }
                                break;
                                default:
                                    break;
                            }
                        }
                    }
                    break;
 
                    case presentation::ClickAction_STOPPRESENTATION :
                    case presentation::ClickAction_SOUND :
                    case presentation::ClickAction_INVISIBLE :
                    case presentation::ClickAction_VERB :
                    case presentation::ClickAction_VANISH :
                    case presentation::ClickAction_MACRO :
                    default :
                    break;
                }
            }
        }
    }
}
 
void ImplRenderPaintProc::createRedirectedPrimitive2DSequence(
    const sdr::contact::ViewObjectContact& rOriginal,
    const sdr::contact::DisplayInfo& rDisplayInfo,
    drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor)
{
    SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject();
    if(!pObject)
    {
        // not an object, maybe a page
        sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(rOriginal, rDisplayInfo, rVisitor);
        return;
    }
    SdrPage* pSdrPage(pObject->getSdrPageFromSdrObject());
    if(!pSdrPage)
        return;
    if(!pSdrPage->checkVisibility(rOriginal, rDisplayInfo, false))
        return;
    if(!IsVisible(pObject) || !IsPrintable(pObject))
        return;
 
    sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(rOriginal, rDisplayInfo, rVisitor);
}
 
bool ImplRenderPaintProc::IsVisible( const SdrObject* pObj ) const
{
    bool bVisible = true;
    SdrLayerID nLayerId = pObj->GetLayer();
    if( pSdrPageView )
    {
        const SdrLayer* pSdrLayer = rLayerAdmin.GetLayerPerID( nLayerId );
        if ( pSdrLayer )
        {
            const OUString& aLayerName = pSdrLayer->GetName();
            bVisible = pSdrPageView->IsLayerVisible( aLayerName );
        }
    }
    return bVisible;
}
bool ImplRenderPaintProc::IsPrintable( const SdrObject* pObj ) const
{
    bool bPrintable = true;
    SdrLayerID nLayerId = pObj->GetLayer();
    if( pSdrPageView )
    {
        const SdrLayer* pSdrLayer = rLayerAdmin.GetLayerPerID( nLayerId );
        if ( pSdrLayer )
        {
            const OUString& aLayerName = pSdrLayer->GetName();
            bPrintable = pSdrPageView->IsLayerPrintable( aLayerName );
        }
    }
    return bPrintable;
 
}
 
namespace
{
    sal_Int16 CalcOutputPageNum(vcl::PDFExtOutDevData const * pPDFExtOutDevData, SdDrawDocument const *pDoc, sal_Int16 nPageNumber)
    {
        //export all pages, simple one to one case
        if (pPDFExtOutDevData && pPDFExtOutDevData->GetIsExportHiddenSlides())
            return nPageNumber-1;
        //check all preceding pages, and only count non-hidden ones
        sal_Int16 nRet = 0;
        for (sal_Int16 i = 0; i < nPageNumber-1; ++i)
        {
           if (!pDoc->GetSdPage(i, PageKind::Standard)->IsExcluded())
                ++nRet;
        }
        return nRet;
    }
}
 
void SAL_CALL SdXImpressDocument::render( sal_Int32 nRenderer, const uno::Any& rSelection,
                                          const uno::Sequence< beans::PropertyValue >& rxOptions )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpDoc )
        throw lang::DisposedException();
 
    if (!mpDocShell)
        return;
 
    uno::Reference< awt::XDevice >  xRenderDevice;
    const sal_Int32                 nPageNumber = nRenderer + 1;
    PageKind                        ePageKind = PageKind::Standard;
    bool                        bExportNotesPages = false;
 
    for( const auto& rOption : rxOptions )
    {
        if ( rOption.Name == "RenderDevice" )
            rOption.Value >>= xRenderDevice;
        else if ( rOption.Name == "ExportNotesPages" )
        {
            rOption.Value >>= bExportNotesPages;
            if ( bExportNotesPages )
                ePageKind = PageKind::Notes;
        }
    }
 
    if( !(xRenderDevice.is() && nPageNumber && ( nPageNumber <= mpDoc->GetSdPageCount( ePageKind ) )) )
        return;
 
    VCLXDevice* pDevice = dynamic_cast<VCLXDevice*>( xRenderDevice.get() );
    VclPtr< OutputDevice> pOut = pDevice ? pDevice->GetOutputDevice() : VclPtr< OutputDevice >();
 
    if( !pOut )
        return;
 
    vcl::PDFExtOutDevData* pPDFExtOutDevData = dynamic_cast<vcl::PDFExtOutDevData* >( pOut->GetExtOutDevData() );
 
    if ( mpDoc->GetSdPage(static_cast<sal_Int16>(nPageNumber)-1, PageKind::Standard)->IsExcluded() &&
        !(pPDFExtOutDevData && pPDFExtOutDevData->GetIsExportHiddenSlides()) )
        return;
 
    if (pPDFExtOutDevData)
    {
        css::lang::Locale const docLocale(Application::GetSettings().GetLanguageTag().getLocale());
        pPDFExtOutDevData->SetDocumentLocale(docLocale);
    }
 
    ::sd::ClientView aView( mpDocShell, pOut );
    ::tools::Rectangle aVisArea( Point(), mpDoc->GetSdPage( static_cast<sal_uInt16>(nPageNumber) - 1, ePageKind )->GetSize() );
    vcl::Region                       aRegion( aVisArea );
 
    ::sd::ViewShell* pOldViewSh = mpDocShell->GetViewShell();
    ::sd::View* pOldSdView = pOldViewSh ? pOldViewSh->GetView() : nullptr;
 
    if  ( pOldSdView )
        pOldSdView->SdrEndTextEdit();
 
    aView.SetHlplVisible( false );
    aView.SetGridVisible( false );
    aView.SetBordVisible( false );
    aView.SetPageVisible( false );
    aView.SetGlueVisible( false );
 
    pOut->SetMapMode(MapMode(MapUnit::Map100thMM));
    pOut->IntersectClipRegion( aVisArea );
 
    uno::Reference< frame::XModel > xModel;
    rSelection >>= xModel;
 
    if( xModel == mpDocShell->GetModel() )
    {
        aView.ShowSdrPage( mpDoc->GetSdPage( static_cast<sal_uInt16>(nPageNumber) - 1, ePageKind ));
        SdrPageView* pPV = aView.GetSdrPageView();
 
        if( pOldSdView )
        {
            SdrPageView* pOldPV = pOldSdView->GetSdrPageView();
            if( pPV && pOldPV )
            {
                pPV->SetVisibleLayers( pOldPV->GetVisibleLayers() );
                pPV->SetPrintableLayers( pOldPV->GetPrintableLayers() );
            }
        }
 
        ImplRenderPaintProc aImplRenderPaintProc( mpDoc->GetLayerAdmin(),
            pPV);
 
        // background color for outliner :o
        SdPage* pPage = pPV ? static_cast<SdPage*>(pPV->GetPage()) : nullptr;
        if( pPage )
        {
            SdrOutliner& rOutl = mpDoc->GetDrawOutliner();
            bool bScreenDisplay(true);
 
            // #i75566# printing; suppress AutoColor BackgroundColor generation
            // for visibility reasons by giving GetPageBackgroundColor()
            // the needed hint
            // #i75566# PDF export; suppress AutoColor BackgroundColor generation (see printing)
            if (pOut && ((OUTDEV_PRINTER == pOut->GetOutDevType())
                    || (OUTDEV_PDF == pOut->GetOutDevType())))
                bScreenDisplay = false;
 
            // #i75566# Name change GetBackgroundColor -> GetPageBackgroundColor and
            // hint value if screen display. Only then the AutoColor mechanisms shall be applied
            rOutl.SetBackgroundColor( pPage->GetPageBackgroundColor( pPV, bScreenDisplay ) );
        }
 
        // produce link annots for media shapes before painting them
        if ( pPDFExtOutDevData && pPage )
        {
            try
            {
                uno::Any aAny;
                uno::Reference< drawing::XDrawPage > xPage( uno::Reference< drawing::XDrawPage >::query( pPage->getUnoPage() ) );
                if ( xPage.is() )
                {
                    if ( pPDFExtOutDevData->GetIsExportNotes() )
                        ImplPDFExportComments( xPage, *pPDFExtOutDevData );
                    uno::Reference< beans::XPropertySet > xPagePropSet( xPage, uno::UNO_QUERY );
                    if( xPagePropSet.is() )
                    {
                        // exporting object interactions to pdf
 
                        // if necessary, the master page interactions will be exported first
                        bool bIsBackgroundObjectsVisible = false;   // #i39428# IsBackgroundObjectsVisible not available for Draw
                        if ( mbImpressDoc && xPagePropSet->getPropertySetInfo()->hasPropertyByName( u"IsBackgroundObjectsVisible"_ustr ) )
                            xPagePropSet->getPropertyValue( u"IsBackgroundObjectsVisible"_ustr ) >>= bIsBackgroundObjectsVisible;
                        if ( bIsBackgroundObjectsVisible && !pPDFExtOutDevData->GetIsExportNotesPages() )
                        {
                            uno::Reference< drawing::XMasterPageTarget > xMasterPageTarget( xPage, uno::UNO_QUERY );
                            if ( xMasterPageTarget.is() )
                            {
                                uno::Reference< drawing::XDrawPage > xMasterPage = xMasterPageTarget->getMasterPage();
                                if ( xMasterPage.is() )
                                {
                                    sal_Int32 i, nCount = xMasterPage->getCount();
                                    for ( i = 0; i < nCount; i++ )
                                    {
                                        aAny = xMasterPage->getByIndex( i );
                                        uno::Reference< drawing::XShape > xShape;
                                        if ( aAny >>= xShape )
                                            ImplPDFExportShapeInteraction( xShape, *mpDoc, *pPDFExtOutDevData );
                                    }
                                }
                            }
                        }
 
                        // exporting slide page object interactions
                        sal_Int32 i, nCount = xPage->getCount();
                        for ( i = 0; i < nCount; i++ )
                        {
                            aAny = xPage->getByIndex( i );
                            uno::Reference< drawing::XShape > xShape;
                            if ( aAny >>= xShape )
                                ImplPDFExportShapeInteraction( xShape, *mpDoc, *pPDFExtOutDevData );
                        }
 
                        // exporting transition effects to pdf
                        if ( mbImpressDoc && !pPDFExtOutDevData->GetIsExportNotesPages() && pPDFExtOutDevData->GetIsExportTransitionEffects() )
                        {
                            static constexpr OUString sEffect( u"Effect"_ustr );
                            static constexpr OUString sSpeed ( u"Speed"_ustr );
                            sal_Int32 nTime = 800;
                            presentation::AnimationSpeed aAs;
                            if ( xPagePropSet->getPropertySetInfo( )->hasPropertyByName( sSpeed ) )
                            {
                                aAny = xPagePropSet->getPropertyValue( sSpeed );
                                if ( aAny >>= aAs )
                                {
                                    switch( aAs )
                                    {
                                        case presentation::AnimationSpeed_SLOW : nTime = 1500; break;
                                        case presentation::AnimationSpeed_FAST : nTime = 300; break;
                                        default:
                                        case presentation::AnimationSpeed_MEDIUM : nTime = 800;
                                    }
                                }
                            }
                            presentation::FadeEffect eFe;
                            vcl::PDFWriter::PageTransition eType = vcl::PDFWriter::PageTransition::Regular;
                            if ( xPagePropSet->getPropertySetInfo( )->hasPropertyByName( sEffect ) )
                            {
                                aAny = xPagePropSet->getPropertyValue( sEffect );
                                if ( aAny >>= eFe )
                                {
                                    switch( eFe )
                                    {
                                        case presentation::FadeEffect_HORIZONTAL_LINES :
                                        case presentation::FadeEffect_HORIZONTAL_CHECKERBOARD :
                                        case presentation::FadeEffect_HORIZONTAL_STRIPES : eType = vcl::PDFWriter::PageTransition::BlindsHorizontal; break;
 
                                        case presentation::FadeEffect_VERTICAL_LINES :
                                        case presentation::FadeEffect_VERTICAL_CHECKERBOARD :
                                        case presentation::FadeEffect_VERTICAL_STRIPES : eType = vcl::PDFWriter::PageTransition::BlindsVertical; break;
 
                                        case presentation::FadeEffect_UNCOVER_TO_RIGHT :
                                        case presentation::FadeEffect_UNCOVER_TO_UPPERRIGHT :
                                        case presentation::FadeEffect_ROLL_FROM_LEFT :
                                        case presentation::FadeEffect_FADE_FROM_UPPERLEFT :
                                        case presentation::FadeEffect_MOVE_FROM_UPPERLEFT :
                                        case presentation::FadeEffect_FADE_FROM_LEFT :
                                        case presentation::FadeEffect_MOVE_FROM_LEFT : eType = vcl::PDFWriter::PageTransition::WipeLeftToRight; break;
 
                                        case presentation::FadeEffect_UNCOVER_TO_BOTTOM :
                                        case presentation::FadeEffect_UNCOVER_TO_LOWERRIGHT :
                                        case presentation::FadeEffect_ROLL_FROM_TOP :
                                        case presentation::FadeEffect_FADE_FROM_UPPERRIGHT :
                                        case presentation::FadeEffect_MOVE_FROM_UPPERRIGHT :
                                        case presentation::FadeEffect_FADE_FROM_TOP :
                                        case presentation::FadeEffect_MOVE_FROM_TOP : eType = vcl::PDFWriter::PageTransition::WipeTopToBottom; break;
 
                                        case presentation::FadeEffect_UNCOVER_TO_LEFT :
                                        case presentation::FadeEffect_UNCOVER_TO_LOWERLEFT :
                                        case presentation::FadeEffect_ROLL_FROM_RIGHT :
 
                                        case presentation::FadeEffect_FADE_FROM_LOWERRIGHT :
                                        case presentation::FadeEffect_MOVE_FROM_LOWERRIGHT :
                                        case presentation::FadeEffect_FADE_FROM_RIGHT :
                                        case presentation::FadeEffect_MOVE_FROM_RIGHT : eType = vcl::PDFWriter::PageTransition::WipeRightToLeft; break;
 
                                        case presentation::FadeEffect_UNCOVER_TO_TOP :
                                        case presentation::FadeEffect_UNCOVER_TO_UPPERLEFT :
                                        case presentation::FadeEffect_ROLL_FROM_BOTTOM :
                                        case presentation::FadeEffect_FADE_FROM_LOWERLEFT :
                                        case presentation::FadeEffect_MOVE_FROM_LOWERLEFT :
                                        case presentation::FadeEffect_FADE_FROM_BOTTOM :
                                        case presentation::FadeEffect_MOVE_FROM_BOTTOM : eType = vcl::PDFWriter::PageTransition::WipeBottomToTop; break;
 
                                        case presentation::FadeEffect_OPEN_VERTICAL : eType = vcl::PDFWriter::PageTransition::SplitHorizontalInward; break;
                                        case presentation::FadeEffect_CLOSE_HORIZONTAL : eType = vcl::PDFWriter::PageTransition::SplitHorizontalOutward; break;
 
                                        case presentation::FadeEffect_OPEN_HORIZONTAL : eType = vcl::PDFWriter::PageTransition::SplitVerticalInward; break;
                                        case presentation::FadeEffect_CLOSE_VERTICAL : eType = vcl::PDFWriter::PageTransition::SplitVerticalOutward; break;
 
                                        case presentation::FadeEffect_FADE_TO_CENTER : eType = vcl::PDFWriter::PageTransition::BoxInward; break;
                                        case presentation::FadeEffect_FADE_FROM_CENTER : eType = vcl::PDFWriter::PageTransition::BoxOutward; break;
 
                                        case presentation::FadeEffect_NONE : eType = vcl::PDFWriter::PageTransition::Regular; break;
 
                                        case presentation::FadeEffect_RANDOM :
                                        case presentation::FadeEffect_DISSOLVE :
                                        default: eType = vcl::PDFWriter::PageTransition::Dissolve; break;
                                    }
                                }
                            }
 
                            if ( xPagePropSet->getPropertySetInfo( )->hasPropertyByName( sEffect ) ||
                                xPagePropSet->getPropertySetInfo( )->hasPropertyByName( sSpeed ) )
                            {
                                pPDFExtOutDevData->SetPageTransition( eType, nTime );
                            }
                        }
                    }
                }
            }
            catch (const uno::Exception&)
            {
            }
        }
 
        aView.SdrPaintView::CompleteRedraw(pOut, aRegion, &aImplRenderPaintProc);
 
        if (pPDFExtOutDevData && pPage)
        {
            try
            {
                Size        aPageSize( mpDoc->GetSdPage( 0, PageKind::Standard )->GetSize() );
                Point aPoint( 0, 0 );
                ::tools::Rectangle   aPageRect( aPoint, aPageSize );
 
                // resolving links found in this page by the method ImpEditEngine::Paint
                std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFExtOutDevData->GetBookmarks();
                for ( const auto& rBookmark : rBookmarks )
                {
                    sal_Int32 nPage = ImplPDFGetBookmarkPage( rBookmark.aBookmark, *mpDoc );
                    if ( nPage != -1 )
                    {
                        if ( rBookmark.nLinkId != -1 )
                            pPDFExtOutDevData->SetLinkDest( rBookmark.nLinkId, pPDFExtOutDevData->CreateDest( aPageRect, nPage, vcl::PDFWriter::DestAreaType::FitRectangle ) );
                        else
                            pPDFExtOutDevData->DescribeRegisteredDest( rBookmark.nDestId, aPageRect, nPage, vcl::PDFWriter::DestAreaType::FitRectangle );
                    }
                    else
                        pPDFExtOutDevData->SetLinkURL( rBookmark.nLinkId, rBookmark.aBookmark );
                }
                rBookmarks.clear();
                //---> #i56629, #i40318
                //get the page name, will be used as outline element in PDF bookmark pane
                OUString aPageName = mpDoc->GetSdPage( static_cast<sal_uInt16>(nPageNumber) - 1 , PageKind::Standard )->GetName();
                if( !aPageName.isEmpty() )
                {
                    // Destination PageNum
                    const sal_Int32 nDestPageNum = CalcOutputPageNum(pPDFExtOutDevData, mpDoc, nPageNumber);
 
                    // insert the bookmark to this page into the NamedDestinations
                    if( pPDFExtOutDevData->GetIsExportNamedDestinations() )
                        pPDFExtOutDevData->CreateNamedDest(aPageName, aPageRect, nDestPageNum);
 
                    // add the name to the outline, (almost) same code as in sc/source/ui/unoobj/docuno.cxx
                    // issue #i40318.
 
                    if( pPDFExtOutDevData->GetIsExportBookmarks() )
                    {
                        // Destination Export
                        const sal_Int32 nDestId =
                            pPDFExtOutDevData->CreateDest(aPageRect , nDestPageNum);
 
                        // Create a new outline item:
                        pPDFExtOutDevData->CreateOutlineItem( -1 , aPageName, nDestId );
                    }
                }
                //<--- #i56629, #i40318
            }
            catch (const uno::Exception&)
            {
            }
 
        }
    }
    else
    {
        uno::Reference< drawing::XShapes > xShapes;
        rSelection >>= xShapes;
 
        if( xShapes.is() && xShapes->getCount() )
        {
            SdrPageView* pPV = nullptr;
 
            ImplRenderPaintProc  aImplRenderPaintProc( mpDoc->GetLayerAdmin(),
                            pOldSdView ? pOldSdView->GetSdrPageView() : nullptr);
 
            for( sal_uInt32 i = 0, nCount = xShapes->getCount(); i < nCount; i++ )
            {
                uno::Reference< drawing::XShape > xShape;
                xShapes->getByIndex( i ) >>= xShape;
 
                if( xShape.is() )
                {
                    SdrObject* pObj = SdrObject::getSdrObjectFromXShape( xShape );
                    if( pObj && pObj->getSdrPageFromSdrObject()
                        && aImplRenderPaintProc.IsVisible( pObj )
                            && aImplRenderPaintProc.IsPrintable( pObj ) )
                    {
                        if( !pPV )
                            pPV = aView.ShowSdrPage( pObj->getSdrPageFromSdrObject() );
 
                        if( pPV )
                            aView.MarkObj( pObj, pPV );
                    }
                }
            }
            aView.DrawMarkedObj(*pOut);
        }
    }
}
 
DrawViewShell* SdXImpressDocument::GetViewShell()
{
    DrawViewShell* pViewSh = dynamic_cast<DrawViewShell*>(mpDocShell->GetViewShell());
    if (!pViewSh)
    {
        SAL_WARN("sd", "DrawViewShell not available!");
        return nullptr;
    }
    return pViewSh;
}
 
void SdXImpressDocument::paintTile( VirtualDevice& rDevice,
                            int nOutputWidth, int nOutputHeight,
                            int nTilePosX, int nTilePosY,
                            ::tools::Long nTileWidth, ::tools::Long nTileHeight )
{
    DrawViewShell* pViewSh = GetViewShell();
    if (!pViewSh)
        return;
 
    // we need to skip tile invalidation for controls on rendering
    comphelper::LibreOfficeKit::setTiledPainting(true);
 
    // Setup drawing layer to work properly. Since we use a custom VirtualDevice
    // for the drawing, SdrPaintView::BeginCompleteRedraw() will call FindPaintWindow()
    // unsuccessfully and use a temporary window that doesn't keep state. So patch
    // the existing SdrPageWindow to use a temporary, and this way the state will be kept.
    // Well, at least that's how I understand it based on Writer's RenderContextGuard,
    // as the drawing layer classes lack documentation.
    SdrPageWindow* patchedPageWindow = nullptr;
    SdrPaintWindow* previousPaintWindow = nullptr;
    std::unique_ptr<SdrPaintWindow> temporaryPaintWindow;
    if(SdrView* pDrawView = pViewSh->GetDrawView())
    {
        if(SdrPageView* pSdrPageView = pDrawView->GetSdrPageView())
        {
            pSdrPageView->SetApplicationDocumentColor(pViewSh->GetViewOptions().mnDocBackgroundColor);
            patchedPageWindow = pSdrPageView->FindPageWindow(*getDocWindow()->GetOutDev());
            temporaryPaintWindow.reset(new SdrPaintWindow(*pDrawView, rDevice));
            if (patchedPageWindow)
                previousPaintWindow = patchedPageWindow->patchPaintWindow(*temporaryPaintWindow);
        }
    }
 
    // Scaling. Must convert from pixels to twips. We know
    // that VirtualDevices use a DPI of 96.
    // We specifically calculate these scales first as we're still
    // in TWIPs, and might as well minimize the number of conversions.
    const Fraction scale = conversionFract(o3tl::Length::px, o3tl::Length::twip);
    Fraction scaleX = Fraction(nOutputWidth, nTileWidth) * scale;
    Fraction scaleY = Fraction(nOutputHeight, nTileHeight) * scale;
 
    // svx seems to be the only component that works natively in
    // 100th mm rather than TWIP. It makes most sense just to
    // convert here and in getDocumentSize, and leave the tiled
    // rendering API working in TWIPs.
    ::tools::Long nTileWidthHMM = convertTwipToMm100( nTileWidth );
    ::tools::Long nTileHeightHMM = convertTwipToMm100( nTileHeight );
    int nTilePosXHMM = convertTwipToMm100( nTilePosX );
    int nTilePosYHMM = convertTwipToMm100( nTilePosY );
 
    MapMode aMapMode = rDevice.GetMapMode();
    aMapMode.SetMapUnit( MapUnit::Map100thMM );
    aMapMode.SetOrigin( Point( -nTilePosXHMM,
                               -nTilePosYHMM) );
    aMapMode.SetScaleX( scaleX );
    aMapMode.SetScaleY( scaleY );
 
    rDevice.SetMapMode( aMapMode );
 
    rDevice.SetOutputSizePixel( Size(nOutputWidth, nOutputHeight) );
 
    Point aPoint(nTilePosXHMM, nTilePosYHMM);
    Size aSize(nTileWidthHMM, nTileHeightHMM);
    ::tools::Rectangle aRect(aPoint, aSize);
 
    SdrView* pView = pViewSh->GetDrawView();
    if (comphelper::LibreOfficeKit::isActive())
        pView->SetPaintTextEdit(mbPaintTextEdit);
 
    pViewSh->GetView()->CompleteRedraw(&rDevice, vcl::Region(aRect));
 
    if (comphelper::LibreOfficeKit::isActive())
        pView->SetPaintTextEdit(true);
 
    LokChartHelper::PaintAllChartsOnTile(rDevice, nOutputWidth, nOutputHeight,
                                         nTilePosX, nTilePosY, nTileWidth, nTileHeight);
    LokStarMathHelper::PaintAllInPlaceOnTile(rDevice, nOutputWidth, nOutputHeight, nTilePosX,
                                             nTilePosY, nTileWidth, nTileHeight);
 
    if(patchedPageWindow != nullptr)
        patchedPageWindow->unpatchPaintWindow(previousPaintWindow);
 
    // Draw Form controls
    SdrView* pDrawView = pViewSh->GetDrawView();
    SdrPageView* pPageView = pDrawView->GetSdrPageView();
    if (pPageView != nullptr)
    {
        SdrPage* pPage = pPageView->GetPage();
        ::sd::Window* pActiveWin = pViewSh->GetActiveWindow();
        ::tools::Rectangle aTileRect(Point(nTilePosX, nTilePosY), Size(nTileWidth, nTileHeight));
        Size aOutputSize(nOutputWidth, nOutputHeight);
        LokControlHandler::paintControlTile(pPage, pDrawView, *pActiveWin, rDevice, aOutputSize, aTileRect);
    }
 
    comphelper::LibreOfficeKit::setTiledPainting(false);
}
 
OString SdXImpressDocument::getViewRenderState(SfxViewShell* pViewShell)
{
    OStringBuffer aState;
    DrawViewShell* pView = nullptr;
 
    if (ViewShellBase* pShellBase = dynamic_cast<ViewShellBase*>(pViewShell))
        pView = dynamic_cast<DrawViewShell*>(pShellBase->GetMainViewShell().get());
    else
        pView = GetViewShell();
 
    if (pView)
    {
        const SdViewOptions& pVOpt = pView->GetViewOptions();
        if (mpDoc->GetOnlineSpell())
            aState.append('S');
        if (pVOpt.mnDocBackgroundColor == svtools::ColorConfig::GetDefaultColor(svtools::DOCCOLOR, 1))
            aState.append('D');
        aState.append(';');
 
        OString aThemeName = OUStringToOString(pVOpt.msColorSchemeName, RTL_TEXTENCODING_UTF8);
        aState.append(aThemeName);
    }
    return aState.makeStringAndClear();
}
 
void SdXImpressDocument::selectPart(int nPart, int nSelect)
{
    DrawViewShell* pViewSh = GetViewShell();
    if (!pViewSh)
        return;
 
    pViewSh->SelectPage(nPart, nSelect);
}
 
void SdXImpressDocument::moveSelectedParts(int nPosition, bool bDuplicate)
{
    // Duplicating is currently unsupported.
    if (!bDuplicate)
        mpDoc->MovePages(nPosition);
}
 
OUString SdXImpressDocument::getPartInfo(int nPart)
{
    DrawViewShell* pViewSh = GetViewShell();
    if (!pViewSh)
        return OUString();
 
    const SdPage* pSdPage = mpDoc->GetSdPage(nPart, pViewSh->GetPageKind());
    const bool bIsVisible = pSdPage && !pSdPage->IsExcluded();
    const bool bIsSelected = pViewSh->IsSelected(nPart);
    const sal_Int16 nMasterPageCount= pViewSh->GetDoc()->GetMasterSdPageCount(pViewSh->GetPageKind());
 
    OUString aPartInfo = "{ \"visible\": \"" +
        OUString::number(static_cast<unsigned int>(bIsVisible)) +
        "\", \"selected\": \"" +
        OUString::number(static_cast<unsigned int>(bIsSelected)) +
        "\", \"masterPageCount\": \"" +
        OUString::number(nMasterPageCount) +
        "\", \"mode\": \"" +
        OUString::number(getEditMode()) +
        "\" }";
 
    return aPartInfo;
}
 
void SdXImpressDocument::setPart( int nPart, bool bAllowChangeFocus )
{
    DrawViewShell* pViewSh = GetViewShell();
    if (!pViewSh)
        return;
 
    pViewSh->SwitchPage( nPart, bAllowChangeFocus );
}
 
int SdXImpressDocument::getParts()
{
    if (!mpDoc)
        return 0;
 
    if (isMasterViewMode())
        return mpDoc->GetMasterSdPageCount(PageKind::Standard);
 
    return mpDoc->GetSdPageCount(PageKind::Standard);
}
 
int SdXImpressDocument::getPart()
{
    DrawViewShell* pViewSh = GetViewShell();
    if (!pViewSh)
        return 0;
 
    return pViewSh->GetViewShellBase().getPart();
}
 
OUString SdXImpressDocument::getPartName(int nPart)
{
    SdPage* pPage;
    if (isMasterViewMode())
        pPage = mpDoc->GetMasterSdPage(nPart, PageKind::Standard);
    else
        pPage = mpDoc->GetSdPage(nPart, PageKind::Standard);
 
    if (!pPage)
    {
        SAL_WARN("sd", "DrawViewShell not available!");
        return OUString();
    }
 
    return pPage->GetName();
}
 
OUString SdXImpressDocument::getPartHash(int nPart)
{
    SdPage* pPage;
    if (isMasterViewMode())
        pPage = mpDoc->GetMasterSdPage(nPart, PageKind::Standard);
    else
        pPage = mpDoc->GetSdPage(nPart, PageKind::Standard);
 
    if (!pPage)
    {
        SAL_WARN("sd", "DrawViewShell not available!");
        return OUString();
    }
 
    uno::Reference<drawing::XDrawPage> xDrawPage(pPage->getUnoPage(), uno::UNO_QUERY);
    return OUString::fromUtf8(GetInterfaceHash(xDrawPage));
}
 
bool SdXImpressDocument::isMasterViewMode()
{
    DrawViewShell* pViewSh = GetViewShell();
    if (!pViewSh)
        return false;
 
    if (pViewSh->GetDispatcher())
    {
        SfxPoolItemHolder aResult;
        pViewSh->GetDispatcher()->QueryState(SID_SLIDE_MASTER_MODE, aResult);
        const SfxBoolItem* isMasterViewMode(static_cast<const SfxBoolItem*>(aResult.getItem()));
        if (isMasterViewMode && isMasterViewMode->GetValue())
            return true;
    }
    return false;
}
 
VclPtr<vcl::Window> SdXImpressDocument::getDocWindow()
{
    SolarMutexGuard aGuard;
    DrawViewShell* pViewShell = GetViewShell();
    if (!pViewShell)
        return {};
 
    if (VclPtr<vcl::Window> pWindow = SfxLokHelper::getInPlaceDocWindow(pViewShell->GetViewShell()))
        return pWindow;
 
    return pViewShell->GetActiveWindow();
}
 
void SdXImpressDocument::setPartMode( int nPartMode )
{
    DrawViewShell* pViewSh = GetViewShell();
    if (!pViewSh)
        return;
 
    PageKind aPageKind( PageKind::Standard );
    switch ( nPartMode )
    {
    case LOK_PARTMODE_SLIDES:
        break;
    case LOK_PARTMODE_NOTES:
        aPageKind = PageKind::Notes;
        break;
    }
    pViewSh->SetPageKind( aPageKind );
}
 
int SdXImpressDocument::getEditMode()
{
    DrawViewShell* pViewSh = GetViewShell();
    if (!pViewSh)
        return 0;
 
    return pViewSh->GetViewShellBase().getEditMode();
}
 
void SdXImpressDocument::setEditMode(int nMode)
{
    SolarMutexGuard aGuard;
 
    DrawViewShell* pViewSh = GetViewShell();
    if (!pViewSh)
        return;
 
    pViewSh->GetViewShellBase().setEditMode(nMode);
}
 
Size SdXImpressDocument::getDocumentSize()
{
    DrawViewShell* pViewSh = GetViewShell();
    if (!pViewSh)
        return Size();
 
    SdrView *pSdrView = pViewSh->GetView();
    if (!pSdrView)
        return Size();
 
    SdrPageView* pCurPageView = pSdrView->GetSdrPageView();
    if (!pCurPageView)
        return Size();
 
    Size aSize = pCurPageView->GetPageRect().GetSize();
    // Convert the size in 100th mm to TWIP
    // See paintTile above for further info.
    return o3tl::convert(aSize, o3tl::Length::mm100, o3tl::Length::twip);
}
 
void SdXImpressDocument::getPostIts(::tools::JsonWriter& rJsonWriter)
{
    auto commentsNode = rJsonWriter.startNode("comments");
    if (!mpDoc)
        return;
    // Return annotations on master pages too ?
    const sal_uInt16 nMaxPages = mpDoc->GetPageCount();
    for (sal_uInt16 nPage = 0; nPage < nMaxPages; ++nPage)
    {
        SdrPage* pPage = mpDoc->GetPage(nPage);
 
        for (auto const& xAnnotation : pPage->getAnnotations())
        {
            sal_uInt32 nID = xAnnotation->GetId();
            OString nodeName = "comment" + OString::number(nID);
            auto commentNode = rJsonWriter.startNode(nodeName);
            rJsonWriter.put("id", nID);
            rJsonWriter.put("author", xAnnotation->getAuthor());
            rJsonWriter.put("dateTime", utl::toISO8601(xAnnotation->getDateTime()));
            uno::Reference<text::XText> xText(xAnnotation->getTextRange());
            rJsonWriter.put("text", xText->getString());
            rJsonWriter.put("parthash", pPage->GetUniqueID());
            geometry::RealPoint2D const aPoint = xAnnotation->getPosition();
            geometry::RealSize2D const aSize = xAnnotation->getSize();
            ::tools::Rectangle aRectangle(Point(aPoint.X * 100.0, aPoint.Y * 100.0), Size(aSize.Width * 100.0, aSize.Height * 100.0));
            aRectangle = o3tl::toTwips(aRectangle, o3tl::Length::mm100);
            OString sRectangle = aRectangle.toString();
            rJsonWriter.put("rectangle", sRectangle.getStr());
        }
    }
}
 
void SdXImpressDocument::initializeForTiledRendering(const css::uno::Sequence<css::beans::PropertyValue>& rArguments)
{
    SolarMutexGuard aGuard;
 
    OUString sThemeName;
    OUString sBackgroundThemeName;
 
    if (DrawViewShell* pViewShell = GetViewShell())
    {
        DrawView* pDrawView = pViewShell->GetDrawView();
        for (const beans::PropertyValue& rValue : rArguments)
        {
            if (rValue.Name == ".uno:ShowBorderShadow" && rValue.Value.has<bool>())
                pDrawView->SetPageShadowVisible(rValue.Value.get<bool>());
            else if (rValue.Name == ".uno:Author" && rValue.Value.has<OUString>())
                pDrawView->SetAuthor(rValue.Value.get<OUString>());
            else if (rValue.Name == ".uno:SpellOnline" && rValue.Value.has<bool>())
                mpDoc->SetOnlineSpell(rValue.Value.get<bool>());
            else if (rValue.Name == ".uno:ChangeTheme" && rValue.Value.has<OUString>())
                sThemeName = rValue.Value.get<OUString>();
            else if (rValue.Name == ".uno:InvertBackground" && rValue.Value.has<OUString>())
                sBackgroundThemeName = rValue.Value.get<OUString>();
        }
 
        // Disable comments if requested
        SdOptions* pOptions = SD_MOD()->GetSdOptions(mpDoc->GetDocumentType());
        pOptions->SetShowComments(comphelper::LibreOfficeKit::isTiledAnnotations());
 
        pViewShell->SetRuler(false);
        pViewShell->SetScrollBarsVisible(false);
 
        if (sd::Window* pWindow = pViewShell->GetActiveWindow())
        {
            // get the full page size in pixels
            pWindow->EnableMapMode();
            Size aSize(pWindow->LogicToPixel(pDrawView->GetSdrPageView()->GetPage()->GetSize()));
            // Disable map mode, so that it's possible to send mouse event
            // coordinates in logic units
            pWindow->EnableMapMode(false);
 
            // arrange UI elements again with new view size
            pViewShell->GetParentWindow()->SetSizePixel(aSize);
            pViewShell->Resize();
        }
 
        // Forces all images to be swapped in synchronously, this
        // ensures that images are available when paintTile is called
        // (whereas with async loading images start being loaded after
        //  we have painted the tile, resulting in an invalidate, followed
        //  by the tile being rerendered - which is wasteful and ugly).
        pDrawView->SetSwapAsynchron(false);
    }
 
    // when the "This document may contain formatting or content that cannot
    // be saved..." dialog appears, it is auto-cancelled with tiled rendering,
    // causing 'Save' being disabled; so let's always save to the original
    // format
    auto xChanges = comphelper::ConfigurationChanges::create();
    officecfg::Office::Common::Save::Document::WarnAlienFormat::set(false, xChanges);
 
    if (!o3tl::IsRunningUnitTest() || !comphelper::LibreOfficeKit::isActive())
        officecfg::Office::Impress::MultiPaneGUI::SlideSorterBar::Visible::ImpressView::set(true,xChanges);
    xChanges->commit();
 
    // if we know what theme the user wants, then we can dispatch that now early
    if (!sThemeName.isEmpty())
    {
        css::uno::Sequence<css::beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
        {
            { "NewTheme", uno::Any(sThemeName) }
        }));
        comphelper::dispatchCommand(u".uno:ChangeTheme"_ustr, aPropertyValues);
    }
    if (!sBackgroundThemeName.isEmpty())
    {
        css::uno::Sequence<css::beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
        {
            { "NewTheme", uno::Any(sBackgroundThemeName) }
        }));
        comphelper::dispatchCommand(".uno:InvertBackground", aPropertyValues);
    }
}
 
void SdXImpressDocument::postKeyEvent(int nType, int nCharCode, int nKeyCode)
{
    SolarMutexGuard aGuard;
    SfxLokHelper::postKeyEventAsync(getDocWindow(), nType, nCharCode, nKeyCode);
}
 
void SdXImpressDocument::postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons, int nModifier)
{
    SolarMutexGuard aGuard;
 
    DrawViewShell* pViewShell = GetViewShell();
    if (!pViewShell)
        return;
 
    constexpr double fScale = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::px);
 
    if (SfxLokHelper::testInPlaceComponentMouseEventHit(
            pViewShell->GetViewShell(), nType, nX, nY, nCount, nButtons, nModifier, fScale, fScale))
        return;
 
    // try to forward mouse event to control
    const Point aPointTwip(nX, nY);
    const Point aPointHMM = o3tl::convert(aPointTwip, o3tl::Length::twip, o3tl::Length::mm100);
    SdrView* pDrawView = pViewShell->GetDrawView();
    SdrPageView* pPageView = pDrawView->GetSdrPageView();
    SdrPage* pPage = pPageView->GetPage();
    ::sd::Window* pActiveWin = pViewShell->GetActiveWindow();
    if (!pActiveWin)
    {
        return;
    }
 
    if (LokControlHandler::postMouseEvent(pPage, pDrawView, *pActiveWin, nType, aPointHMM, nCount, nButtons, nModifier))
            return;
 
    LokMouseEventData aMouseEventData(nType, aPointHMM, nCount, MouseEventModifiers::SIMPLECLICK,
                                      nButtons, nModifier);
    SfxLokHelper::postMouseEventAsync(pViewShell->GetActiveWindow(), aMouseEventData);
}
 
void SdXImpressDocument::setTextSelection(int nType, int nX, int nY)
{
    SolarMutexGuard aGuard;
 
    DrawViewShell* pViewShell = GetViewShell();
    if (!pViewShell)
        return;
 
    LokChartHelper aChartHelper(pViewShell->GetViewShell());
    if (aChartHelper.setTextSelection(nType, nX, nY))
        return;
 
    Point aPoint(convertTwipToMm100(nX), convertTwipToMm100(nY));
    switch (nType)
    {
    case LOK_SETTEXTSELECTION_START:
        pViewShell->SetCursorMm100Position(aPoint, /*bPoint=*/false, /*bClearMark=*/false);
        break;
    case LOK_SETTEXTSELECTION_END:
        pViewShell->SetCursorMm100Position(aPoint, /*bPoint=*/true, /*bClearMark=*/false);
        break;
    case LOK_SETTEXTSELECTION_RESET:
        pViewShell->SetCursorMm100Position(aPoint, /*bPoint=*/true, /*bClearMark=*/true);
        break;
    default:
        assert(false);
        break;
    }
}
 
uno::Reference<datatransfer::XTransferable> SdXImpressDocument::getSelection()
{
    SolarMutexGuard aGuard;
 
    DrawViewShell* pViewShell = GetViewShell();
    if (!pViewShell)
        return uno::Reference<datatransfer::XTransferable>();
 
    return pViewShell->GetSelectionTransferable();
}
 
void SdXImpressDocument::setGraphicSelection(int nType, int nX, int nY)
{
    SolarMutexGuard aGuard;
 
    DrawViewShell* pViewShell = GetViewShell();
    if (!pViewShell)
        return;
 
    constexpr double fScale = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::px);
 
    LokChartHelper aChartHelper(pViewShell->GetViewShell());
    if (aChartHelper.setGraphicSelection(nType, nX, nY, fScale, fScale))
        return;
 
    Point aPoint(convertTwipToMm100(nX), convertTwipToMm100(nY));
    switch (nType)
    {
    case LOK_SETGRAPHICSELECTION_START:
        pViewShell->SetGraphicMm100Position(/*bStart=*/true, aPoint);
        break;
    case LOK_SETGRAPHICSELECTION_END:
        pViewShell->SetGraphicMm100Position(/*bStart=*/false, aPoint);
        break;
    default:
        assert(false);
        break;
    }
}
 
void SdXImpressDocument::resetSelection()
{
    SolarMutexGuard aGuard;
 
    DrawViewShell* pViewShell = GetViewShell();
    if (!pViewShell)
        return;
 
    SdrView* pSdrView = pViewShell->GetView();
    if (!pSdrView)
        return;
 
    if (pSdrView->IsTextEdit())
    {
        // Reset the editeng selection.
        pSdrView->UnmarkAll();
        // Finish editing.
        pSdrView->SdrEndTextEdit();
    }
    // Reset graphic selection.
    pSdrView->UnmarkAll();
}
 
void SdXImpressDocument::setClientVisibleArea(const ::tools::Rectangle& rRectangle)
{
    SolarMutexGuard aGuard;
 
    DrawViewShell* pViewShell = GetViewShell();
    if (!pViewShell)
        return;
 
    pViewShell->GetViewShellBase().setLOKVisibleArea(rRectangle);
}
 
void SdXImpressDocument::setClipboard(const uno::Reference<datatransfer::clipboard::XClipboard>& xClipboard)
{
    SolarMutexGuard aGuard;
 
    DrawViewShell* pViewShell = GetViewShell();
    if (!pViewShell)
        return;
 
    pViewShell->GetActiveWindow()->SetClipboard(xClipboard);
}
 
bool SdXImpressDocument::isMimeTypeSupported()
{
    SolarMutexGuard aGuard;
    DrawViewShell* pViewShell = GetViewShell();
    if (!pViewShell)
        return false;
 
    TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromSystemClipboard(pViewShell->GetActiveWindow()));
    return EditEngine::HasValidData(aDataHelper.GetTransferable());
}
 
PointerStyle SdXImpressDocument::getPointer()
{
    SolarMutexGuard aGuard;
    DrawViewShell* pViewShell = GetViewShell();
    if (!pViewShell)
        return PointerStyle::Arrow;
 
    Window* pWindow = pViewShell->GetActiveWindow();
    if (!pWindow)
        return PointerStyle::Arrow;
 
    return pWindow->GetPointer();
}
 
uno::Reference< i18n::XForbiddenCharacters > SdXImpressDocument::getForbiddenCharsTable()
{
    rtl::Reference<SdUnoForbiddenCharsTable> xRef = mxForbiddenCharacters.get();
    if( !xRef )
    {
        xRef = new SdUnoForbiddenCharsTable( mpDoc );
        mxForbiddenCharacters = xRef.get();
    }
    return xRef;
}
 
void SdXImpressDocument::initializeDocument()
{
    if( mbClipBoard )
        return;
 
    switch( mpDoc->GetPageCount() )
    {
    case 1:
    {
        // nasty hack to detect clipboard document
        mbClipBoard = true;
        break;
    }
    case 0:
    {
        mpDoc->CreateFirstPages();
        mpDoc->StopWorkStartupDelay();
        break;
    }
    }
}
 
OString SdXImpressDocument::getPresentationInfo() const
{
    ::tools::JsonWriter aJsonWriter;
 
    try
    {
        rtl::Reference<SdDrawPagesAccess> xDrawPages = const_cast<SdXImpressDocument*>(this)->getSdDrawPages();
        // size in twips
        Size aDocSize = const_cast<SdXImpressDocument*>(this)->getDocumentSize();
        aJsonWriter.put("docWidth", aDocSize.getWidth());
        aJsonWriter.put("docHeight", aDocSize.getHeight());
 
        sd::PresentationSettings const& rSettings = mpDoc->getPresentationSettings();
 
        const bool bIsEndless = rSettings.mbEndless;
        aJsonWriter.put("isEndless", bIsEndless);
 
        if (bIsEndless) {
            const sal_Int32 nPauseTimeout = rSettings.mnPauseTimeout;
            aJsonWriter.put("loopAndRepeatDuration", nPauseTimeout);
        }
 
        auto aSlideList = aJsonWriter.startArray("slides");
        sal_Int32 nSlideCount = xDrawPages->getCount();
        for (sal_Int32 i = 0; i < nSlideCount; ++i)
        {
            SdGenericDrawPage* pSlide(xDrawPages->getDrawPageByIndex(i));
            bool bIsVisible = true; // default visible
            pSlide->getPropertyValue("Visible") >>= bIsVisible;
            if (bIsVisible)
            {
                SdrPage* pPage = pSlide->GetSdrPage();
 
                auto aSlideNode = aJsonWriter.startStruct();
                std::string sSlideHash = GetInterfaceHash(cppu::getXWeak(pSlide));
                aJsonWriter.put("hash", sSlideHash);
                aJsonWriter.put("index", i);
 
                bool bIsDrawPageEmpty = pSlide->getCount() == 0;
                aJsonWriter.put("empty", bIsDrawPageEmpty);
 
                // Notes
                SdPage* pNotesPage = mpDoc->GetSdPage((pPage->GetPageNum() - 1) >> 1, PageKind::Notes);
                if (pNotesPage)
                {
                    SdrObject* pNotes = pNotesPage->GetPresObj(PresObjKind::Notes);
                    if (pNotes)
                    {
                        OUStringBuffer strNotes;
                        OutlinerParaObject* pPara = pNotes->GetOutlinerParaObject();
                        if (pPara)
                        {
                            const EditTextObject& rText = pPara->GetTextObject();
                            for (sal_Int32 nNote = 0; nNote < rText.GetParagraphCount(); nNote++)
                            {
                                strNotes.append(rText.GetText(nNote));
                            }
                            aJsonWriter.put("notes", strNotes.makeStringAndClear());
                        }
                    }
                }
 
                SdMasterPage* pMasterPage = nullptr;
                SdDrawPage* pMasterPageTarget(dynamic_cast<SdDrawPage*>(pSlide));
                if (pMasterPageTarget)
                {
                    pMasterPage = pMasterPageTarget->getSdMasterPage();
                    if (pMasterPage)
                    {
                        std::string sMPHash = GetInterfaceHash(cppu::getXWeak(pMasterPage));
                        aJsonWriter.put("masterPage", sMPHash);
 
                        bool bBackgroundObjectsVisibility = true; // default visible
                        pSlide->getPropertyValue("IsBackgroundObjectsVisible") >>= bBackgroundObjectsVisibility;
                        aJsonWriter.put("masterPageObjectsVisibility", bBackgroundObjectsVisibility);
                    }
                }
 
                bool bBackgroundVisibility = true; // default visible
                pSlide->getPropertyValue("IsBackgroundVisible")  >>= bBackgroundVisibility;
                if (bBackgroundVisibility)
                {
                    SlideBackgroundInfo aSlideBackgroundInfo(pSlide, static_cast<SvxDrawPage*>(pMasterPage));
                    if (aSlideBackgroundInfo.hasBackground())
                    {
                        auto aBackgroundNode = aJsonWriter.startNode("background");
                        aJsonWriter.put("isCustom", aSlideBackgroundInfo.slideHasOwnBackground());
                        if (aSlideBackgroundInfo.isSolidColor())
                        {
                            aJsonWriter.put("fillColor", aSlideBackgroundInfo.getFillColorAsRGBA());
                        }
                    }
                }
 
                {
                    auto aVideoList = aJsonWriter.startArray("videos");
                    SdrObjListIter aIterator(pPage, SdrIterMode::DeepWithGroups);
                    while (aIterator.IsMore())
                    {
                        auto* pObject = aIterator.Next();
                        if (pObject->GetObjIdentifier() == SdrObjKind::Media)
                        {
                            auto aVideosNode = aJsonWriter.startStruct();
                            auto* pMediaObject = static_cast<SdrMediaObj*>(pObject);
                            auto const& rRectangle = pMediaObject->GetLogicRect();
                            auto aRectangle = o3tl::convert(rRectangle, o3tl::Length::mm100, o3tl::Length::twip);
                            aJsonWriter.put("id", reinterpret_cast<sal_uInt64>(pMediaObject));
                            aJsonWriter.put("url", pMediaObject->getTempURL());
                            aJsonWriter.put("x", aRectangle.Left());
                            aJsonWriter.put("y", aRectangle.Top());
                            aJsonWriter.put("width", aRectangle.GetWidth());
                            aJsonWriter.put("height", aRectangle.GetHeight());
                        }
                    }
                }
 
                sal_Int32 nTransitionType = 0;
                pSlide->getPropertyValue("TransitionType") >>= nTransitionType;
 
                if (nTransitionType != 0)
                {
                    auto iterator = constTransitionTypeToString.find(nTransitionType);
 
                    if (iterator != constTransitionTypeToString.end())
                    {
                        aJsonWriter.put("transitionType", iterator->second);
 
                        sal_Int32 nTransitionSubtype = 0;
                        pSlide->getPropertyValue("TransitionSubtype") >>= nTransitionSubtype;
 
                        auto iteratorSubType = constTransitionSubTypeToString.find(nTransitionSubtype);
                        if (iteratorSubType != constTransitionSubTypeToString.end())
                        {
                            aJsonWriter.put("transitionSubtype", iteratorSubType->second);
                        }
                        else
                        {
                            SAL_WARN("sd", "Transition sub-type unknown: " << nTransitionSubtype);
                        }
 
                        bool nTransitionDirection = false;
                        pSlide->getPropertyValue("TransitionDirection") >>= nTransitionDirection;
                        aJsonWriter.put("transitionDirection", nTransitionDirection);
 
                        // fade color
                        if ((nTransitionType == TransitionType::FADE)
                                && ((nTransitionSubtype == TransitionSubType::FADETOCOLOR)
                                    || (nTransitionSubtype == TransitionSubType::FADEFROMCOLOR)
                                    || (nTransitionSubtype == TransitionSubType::FADEOVERCOLOR)))
                        {
                            sal_Int32 nFadeColor = 0;
                            pSlide->getPropertyValue("TransitionFadeColor") >>= nFadeColor;
                            OUStringBuffer sTmpBuf;
                            ::sax::Converter::convertColor(sTmpBuf, nFadeColor);
                            aJsonWriter.put("transitionFadeColor", sTmpBuf.makeStringAndClear());
                        }
                    }
 
                    double nTransitionDuration(0.0);
                    if( pSlide->getPropertySetInfo()->hasPropertyByName( "TransitionDuration" ) &&
                        (pSlide->getPropertyValue( "TransitionDuration" ) >>= nTransitionDuration ) && nTransitionDuration != 0.0 )
                    {
                        // convert transitionDuration time to ms
                        aJsonWriter.put("transitionDuration", nTransitionDuration * 1000);
                    }
 
                    sal_Int32 nChange(0);
                    if( pSlide->getPropertySetInfo()->hasPropertyByName( "Change" ) &&
                        (pSlide->getPropertyValue( "Change" ) >>= nChange ) && nChange == 1 )
                    {
                        double fSlideDuration(0);
                        if( pSlide->getPropertySetInfo()->hasPropertyByName( "HighResDuration" ) &&
                            (pSlide->getPropertyValue( "HighResDuration" ) >>= fSlideDuration) )
                        {
                            // convert slide duration time to ms
                            aJsonWriter.put("nextSlideDuration", fSlideDuration * 1000);
                        }
                    }
                }
 
                AnimationsExporter aAnimationExporter(aJsonWriter, pSlide);
                if (aAnimationExporter.hasEffects())
                {
                    auto aAnimationsNode = aJsonWriter.startNode("animations");
                    aAnimationExporter.exportAnimations();
                }
            }
        }
    }
    catch (uno::Exception& )
    {
        TOOLS_WARN_EXCEPTION("sd", "SdXImpressDocument::getSlideShowInfo ... maybe some property can't be retrieved");
    }
    return aJsonWriter.finishAndGetAsOString();
}
 
bool SdXImpressDocument::createSlideRenderer(
    sal_Int32 nSlideNumber, sal_Int32& nViewWidth, sal_Int32& nViewHeight,
    bool bRenderBackground, bool bRenderMasterPage)
{
    DrawViewShell* pViewSh = GetViewShell();
    if (!pViewSh)
        return false;
 
    uno::Reference<presentation::XSlideShow> xSlideShow = pViewSh->getXSlideShowInstance();
    if (!xSlideShow.is())
        return false;
 
    bool bSuccess = false;
    try
    {
        rtl::Reference<SdXImpressDocument> xDrawPages(mpDoc->getUnoModel());
        uno::Reference<container::XIndexAccess> xSlides(xDrawPages->getDrawPages(), uno::UNO_QUERY_THROW);
        uno::Reference<drawing::XDrawPage> xSlide(xSlides->getByIndex(nSlideNumber), uno::UNO_QUERY_THROW);
        uno::Reference<animations::XAnimationNodeSupplier> xAnimNodeSupplier(xSlide, uno::UNO_QUERY_THROW);
        uno::Reference<animations::XAnimationNode> xAnimNode = xAnimNodeSupplier->getAnimationNode();
 
        bSuccess = xSlideShow->createLOKSlideRenderer(nViewWidth, nViewHeight,
                                                 bRenderMasterPage, bRenderBackground,
                                                 xSlide, xDrawPages, xAnimNode);
    }
    catch (uno::Exception&)
    {
        TOOLS_WARN_EXCEPTION( "sd", "SdXImpressDocument::createLOKSlideRenderer: failed" );
    }
    return bSuccess;
}
 
void SdXImpressDocument::postSlideshowCleanup()
{
    DrawViewShell* pViewSh = GetViewShell();
    if (!pViewSh)
        return;
 
    pViewSh->destroyXSlideShowInstance();
}
 
bool SdXImpressDocument::renderNextSlideLayer(unsigned char* pBuffer, bool& bIsBitmapLayer, OUString& rJsonMsg)
{
    DrawViewShell* pViewSh = GetViewShell();
    if (!pViewSh)
        return true;
 
    uno::Reference<presentation::XSlideShow> xSlideShow = pViewSh->getXSlideShowInstance();
    if (!xSlideShow.is())
        return true;
 
    auto nBufferPointer = sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(pBuffer));
    sal_Bool bBitmapLayer = false;
    bool bDone = xSlideShow->renderNextLOKSlideLayer(nBufferPointer, bBitmapLayer, rJsonMsg);
    bIsBitmapLayer = bBitmapLayer;
 
    return bDone;
}
 
SdrModel& SdXImpressDocument::getSdrModelFromUnoModel() const
{
    OSL_ENSURE(GetDoc(), "No SdrModel in draw/Impress, should not happen");
    return *GetDoc(); // TTTT should be reference
}
 
void SAL_CALL SdXImpressDocument::dispose()
{
    if( mbDisposed )
        return;
 
    ::SolarMutexGuard aGuard;
 
    if( mpDoc )
    {
        EndListening( *mpDoc );
        mpDoc = nullptr;
    }
 
    // Call the base class dispose() before setting the mbDisposed flag
    // to true.  The reason for this is that if close() has not yet been
    // called this is done in SfxBaseModel::dispose().  At the end of
    // that dispose() is called again.  It is important to forward this
    // second dispose() to the base class, too.
    // As a consequence the following code has to be able to be run twice.
    SfxBaseModel::dispose();
    mbDisposed = true;
 
    rtl::Reference< SdDocLinkTargets > xLinks( mxLinks );
    if( xLinks.is() )
    {
        xLinks->dispose();
        xLinks = nullptr;
    }
 
    rtl::Reference< SdDrawPagesAccess > xDrawPagesAccess( mxDrawPagesAccess );
    if( xDrawPagesAccess.is() )
    {
        xDrawPagesAccess->dispose();
        xDrawPagesAccess = nullptr;
    }
 
    rtl::Reference< SdMasterPagesAccess > xMasterPagesAccess( mxMasterPagesAccess );
    if( xDrawPagesAccess.is() )
    {
        xMasterPagesAccess->dispose();
        xMasterPagesAccess = nullptr;
    }
 
    rtl::Reference< SdLayerManager > xLayerManager( mxLayerManager );
    if( xLayerManager.is() )
    {
        xLayerManager->dispose();
        xLayerManager = nullptr;
    }
 
    mxDashTable = nullptr;
    mxGradientTable = nullptr;
    mxHatchTable = nullptr;
    mxBitmapTable = nullptr;
    mxTransGradientTable = nullptr;
    mxMarkerTable = nullptr;
    mxDrawingPool = nullptr;
}
 
 
SdDrawPagesAccess::SdDrawPagesAccess( SdXImpressDocument& rMyModel )  noexcept
:   mpModel( &rMyModel)
{
}
 
SdDrawPagesAccess::~SdDrawPagesAccess() noexcept
{
}
 
// XIndexAccess
sal_Int32 SAL_CALL SdDrawPagesAccess::getCount()
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpModel )
        throw lang::DisposedException();
 
    return mpModel->mpDoc->GetSdPageCount( PageKind::Standard );
}
 
uno::Any SAL_CALL SdDrawPagesAccess::getByIndex( sal_Int32 Index )
{
    uno::Reference< drawing::XDrawPage > xDrawPage( getDrawPageByIndex(Index) );
    return uno::Any(xDrawPage);
}
 
SdGenericDrawPage* SdDrawPagesAccess::getDrawPageByIndex( sal_Int32 Index )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpModel )
        throw lang::DisposedException();
 
    if( (Index < 0) || (Index >= mpModel->mpDoc->GetSdPageCount( PageKind::Standard ) ) )
        throw lang::IndexOutOfBoundsException();
 
    SdPage* pPage = mpModel->mpDoc->GetSdPage( static_cast<sal_uInt16>(Index), PageKind::Standard );
    if( pPage )
        return dynamic_cast<SdGenericDrawPage*>( pPage->getUnoPage().get() );
 
    return nullptr;
}
 
// XNameAccess
uno::Any SAL_CALL SdDrawPagesAccess::getByName( const OUString& aName )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpModel )
        throw lang::DisposedException();
 
    if( !aName.isEmpty() )
    {
        const sal_uInt16 nCount = mpModel->mpDoc->GetSdPageCount( PageKind::Standard );
        sal_uInt16 nPage;
        for( nPage = 0; nPage < nCount; nPage++ )
        {
            SdPage* pPage = mpModel->mpDoc->GetSdPage( nPage, PageKind::Standard );
            if(nullptr == pPage)
                continue;
 
            if( aName == SdDrawPage::getPageApiName( pPage ) )
            {
                uno::Any aAny;
                uno::Reference< drawing::XDrawPage >  xDrawPage( pPage->getUnoPage(), uno::UNO_QUERY );
                aAny <<= xDrawPage;
                return aAny;
            }
        }
    }
 
    throw container::NoSuchElementException();
}
 
uno::Sequence< OUString > SAL_CALL SdDrawPagesAccess::getElementNames()
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpModel )
        throw lang::DisposedException();
 
    const sal_uInt16 nCount = mpModel->mpDoc->GetSdPageCount( PageKind::Standard );
    uno::Sequence< OUString > aNames( nCount );
    OUString* pNames = aNames.getArray();
 
    sal_uInt16 nPage;
    for( nPage = 0; nPage < nCount; nPage++ )
    {
        SdPage* pPage = mpModel->mpDoc->GetSdPage( nPage, PageKind::Standard );
        *pNames++ = SdDrawPage::getPageApiName( pPage );
    }
 
    return aNames;
}
 
sal_Bool SAL_CALL SdDrawPagesAccess::hasByName( const OUString& aName )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpModel )
        throw lang::DisposedException();
 
    const sal_uInt16 nCount = mpModel->mpDoc->GetSdPageCount( PageKind::Standard );
    sal_uInt16 nPage;
    for( nPage = 0; nPage < nCount; nPage++ )
    {
        SdPage* pPage = mpModel->mpDoc->GetSdPage( nPage, PageKind::Standard );
        if(nullptr == pPage)
            continue;
 
        if( aName == SdDrawPage::getPageApiName( pPage ) )
            return true;
    }
 
    return false;
}
 
// XElementAccess
uno::Type SAL_CALL SdDrawPagesAccess::getElementType()
{
    return cppu::UnoType<drawing::XDrawPage>::get();
}
 
sal_Bool SAL_CALL SdDrawPagesAccess::hasElements()
{
    return getCount() > 0;
}
 
// XDrawPages
 
/**
 * Creates a new page with model at the specified position.
 * @returns corresponding SdDrawPage
 */
uno::Reference< drawing::XDrawPage > SAL_CALL SdDrawPagesAccess::insertNewByIndex( sal_Int32 nIndex )
{
    ::SolarMutexGuard aGuard;
    comphelper::ProfileZone aZone("insertNewByIndex");
 
    if( nullptr == mpModel )
        throw lang::DisposedException();
 
    if( mpModel->mpDoc )
    {
        SdPage* pPage = mpModel->InsertSdPage( static_cast<sal_uInt16>(nIndex), false );
        if( pPage )
        {
            uno::Reference< drawing::XDrawPage > xDrawPage( pPage->getUnoPage(), uno::UNO_QUERY );
            return xDrawPage;
        }
    }
    uno::Reference< drawing::XDrawPage > xDrawPage;
    return xDrawPage;
}
 
/**
 * Removes the specified SdDrawPage from the model and the internal list. It
 * only works, if there is at least one *normal* page in the model after
 * removing this page.
 */
void SAL_CALL SdDrawPagesAccess::remove( const uno::Reference< drawing::XDrawPage >& xPage )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpModel || mpModel->mpDoc == nullptr )
        throw lang::DisposedException();
 
    SdDrawDocument& rDoc = *mpModel->mpDoc;
 
    sal_uInt16 nPageCount = rDoc.GetSdPageCount( PageKind::Standard );
    if( nPageCount > 1 )
    {
        // get pPage from xPage and determine the Id (nPos ) afterwards
        SdDrawPage* pSvxPage = comphelper::getFromUnoTunnel<SdDrawPage>( xPage );
        if( pSvxPage )
        {
            SdPage* pPage = static_cast<SdPage*>(pSvxPage->GetSdrPage());
            if(pPage && ( pPage->GetPageKind() == PageKind::Standard ) )
            {
                sal_uInt16 nPage = pPage->GetPageNum();
 
                SdPage* pNotesPage = static_cast< SdPage* >( rDoc.GetPage( nPage+1 ) );
 
                bool bUndo = rDoc.IsUndoEnabled();
                if( bUndo )
                {
                    // Add undo actions and delete the pages.  The order of adding
                    // the undo actions is important.
                    rDoc.BegUndo( SdResId( STR_UNDO_DELETEPAGES ) );
                    rDoc.AddUndo(rDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pNotesPage));
                    rDoc.AddUndo(rDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage));
                }
 
                rDoc.RemovePage( nPage ); // the page
                rDoc.RemovePage( nPage ); // the notes page
 
                if( bUndo )
                {
                    rDoc.EndUndo();
                }
            }
        }
    }
 
    mpModel->SetModified();
}
 
// XServiceInfo
 
OUString SAL_CALL SdDrawPagesAccess::getImplementationName(  )
{
    return u"SdDrawPagesAccess"_ustr;
}
 
sal_Bool SAL_CALL SdDrawPagesAccess::supportsService( const OUString& ServiceName )
{
    return cppu::supportsService(this, ServiceName);
}
 
uno::Sequence< OUString > SAL_CALL SdDrawPagesAccess::getSupportedServiceNames(  )
{
    return { u"com.sun.star.drawing.DrawPages"_ustr };
}
 
// XComponent
void SAL_CALL SdDrawPagesAccess::dispose(  )
{
    mpModel = nullptr;
}
 
void SAL_CALL SdDrawPagesAccess::addEventListener( const uno::Reference< lang::XEventListener >&  )
{
    OSL_FAIL( "not implemented!" );
}
 
void SAL_CALL SdDrawPagesAccess::removeEventListener( const uno::Reference< lang::XEventListener >&  )
{
    OSL_FAIL( "not implemented!" );
}
 
 
SdMasterPagesAccess::SdMasterPagesAccess( SdXImpressDocument& rMyModel ) noexcept
:   mpModel(&rMyModel)
{
}
 
SdMasterPagesAccess::~SdMasterPagesAccess() noexcept
{
}
 
// XComponent
void SAL_CALL SdMasterPagesAccess::dispose(  )
{
    mpModel = nullptr;
}
 
void SAL_CALL SdMasterPagesAccess::addEventListener( const uno::Reference< lang::XEventListener >&  )
{
    OSL_FAIL( "not implemented!" );
}
 
void SAL_CALL SdMasterPagesAccess::removeEventListener( const uno::Reference< lang::XEventListener >&  )
{
    OSL_FAIL( "not implemented!" );
}
 
// XIndexAccess
sal_Int32 SAL_CALL SdMasterPagesAccess::getCount()
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpModel->mpDoc )
        throw lang::DisposedException();
 
    return mpModel->mpDoc->GetMasterSdPageCount(PageKind::Standard);
}
 
/**
 * Provides a drawing::XDrawPage interface for accessing the Masterpage at the
 * specified position in the model.
 */
uno::Any SAL_CALL SdMasterPagesAccess::getByIndex( sal_Int32 Index )
{
    ::SolarMutexGuard aGuard;
    comphelper::ProfileZone aZone("SdMasterPagesAccess::getByIndex");
 
    if( nullptr == mpModel )
        throw lang::DisposedException();
 
    uno::Any aAny;
 
    if( (Index < 0) || (Index >= mpModel->mpDoc->GetMasterSdPageCount( PageKind::Standard ) ) )
        throw lang::IndexOutOfBoundsException();
 
    SdPage* pPage = mpModel->mpDoc->GetMasterSdPage( static_cast<sal_uInt16>(Index), PageKind::Standard );
    if( pPage )
    {
        uno::Reference< drawing::XDrawPage >  xDrawPage( pPage->getUnoPage(), uno::UNO_QUERY );
        aAny <<= xDrawPage;
    }
 
    return aAny;
}
 
// XElementAccess
uno::Type SAL_CALL SdMasterPagesAccess::getElementType()
{
    return cppu::UnoType<drawing::XDrawPage>::get();
}
 
sal_Bool SAL_CALL SdMasterPagesAccess::hasElements()
{
    return getCount() > 0;
}
 
// XDrawPages
uno::Reference< drawing::XDrawPage > SAL_CALL SdMasterPagesAccess::insertNewByIndex( sal_Int32 nInsertPos )
{
    return insertNewImpl(nInsertPos, std::nullopt);
}
 
// XDrawPages2
uno::Reference< drawing::XDrawPage > SAL_CALL SdMasterPagesAccess::insertNamedNewByIndex( sal_Int32 nInsertPos, const OUString& sName )
{
    return insertNewImpl(nInsertPos, sName);
}
 
uno::Reference< drawing::XDrawPage > SdMasterPagesAccess::insertNewImpl( sal_Int32 nInsertPos, std::optional<OUString> oPageName )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpModel )
        throw lang::DisposedException();
 
    uno::Reference< drawing::XDrawPage > xDrawPage;
 
    SdDrawDocument* pDoc = mpModel->mpDoc;
    if( pDoc )
    {
        // calculate internal index and check for range errors
        const sal_Int32 nMPageCount = pDoc->GetMasterPageCount();
        nInsertPos = nInsertPos * 2 + 1;
        if( nInsertPos < 0 || nInsertPos > nMPageCount )
            nInsertPos = nMPageCount;
 
        // now generate a unique name for the new masterpage
        OUString aPrefix;
        if (oPageName)
            aPrefix = *oPageName;
        else
        {
            const OUString aStdPrefix( SdResId(STR_LAYOUT_DEFAULT_NAME) );
            aPrefix = aStdPrefix;
 
            bool bUnique = true;
 
            std::vector<OUString> aPageNames;
            for (sal_Int32 nMaster = 1; nMaster < nMPageCount; ++nMaster)
            {
                const SdPage* pPage = static_cast<const SdPage*>(pDoc->GetMasterPage(static_cast<sal_uInt16>(nMaster)));
                if (!pPage)
                    continue;
                aPageNames.push_back(pPage->GetName());
                if (aPageNames.back() == aPrefix)
                    bUnique = false;
            }
 
            sal_Int32 i = 0;
            while (!bUnique)
            {
                aPrefix = aStdPrefix + " " + OUString::number(++i);
                bUnique = std::find(aPageNames.begin(), aPageNames.end(), aPrefix) == aPageNames.end();
            }
        }
        OUString aLayoutName = aPrefix + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE;
 
        // create styles
        static_cast<SdStyleSheetPool*>(pDoc->GetStyleSheetPool())->CreateLayoutStyleSheets( aPrefix );
 
        // get the first page for initial size and border settings
        SdPage* pPage = mpModel->mpDoc->GetSdPage( sal_uInt16(0), PageKind::Standard );
        SdPage* pRefNotesPage = mpModel->mpDoc->GetSdPage( sal_uInt16(0), PageKind::Notes);
 
        // create and insert new draw masterpage
        rtl::Reference<SdPage> pMPage = mpModel->mpDoc->AllocSdPage(true);
        pMPage->SetSize( pPage->GetSize() );
        pMPage->SetBorder( pPage->GetLeftBorder(),
                           pPage->GetUpperBorder(),
                           pPage->GetRightBorder(),
                           pPage->GetLowerBorder() );
        if (oPageName)
            // no need to update the page URLs on a brand new page
            pMPage->SetName(*oPageName, /*bUpdatePageRelativeURLs*/false);
        pMPage->SetLayoutName( aLayoutName );
        pDoc->InsertMasterPage(pMPage.get(),  static_cast<sal_uInt16>(nInsertPos));
 
        {
            // ensure default MasterPage fill
            pMPage->EnsureMasterPageDefaultBackground();
        }
 
        xDrawPage.set( pMPage->getUnoPage(), uno::UNO_QUERY );
 
        // create and insert new notes masterpage
        rtl::Reference<SdPage> pMNotesPage = mpModel->mpDoc->AllocSdPage(true);
        pMNotesPage->SetSize( pRefNotesPage->GetSize() );
        pMNotesPage->SetPageKind(PageKind::Notes);
        pMNotesPage->SetBorder( pRefNotesPage->GetLeftBorder(),
                                pRefNotesPage->GetUpperBorder(),
                                pRefNotesPage->GetRightBorder(),
                                pRefNotesPage->GetLowerBorder() );
        pMNotesPage->SetLayoutName( aLayoutName );
        pDoc->InsertMasterPage(pMNotesPage.get(),  static_cast<sal_uInt16>(nInsertPos) + 1);
        pMNotesPage->SetAutoLayout(AUTOLAYOUT_NOTES, true, true);
        mpModel->SetModified();
    }
 
    return xDrawPage;
}
 
/**
 * Removes the specified SdMasterPage from the model and the internal list. It
 * only works, if there is no *normal* page using this page as MasterPage in
 * the model.
 */
void SAL_CALL SdMasterPagesAccess::remove( const uno::Reference< drawing::XDrawPage >& xPage )
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpModel || mpModel->mpDoc == nullptr )
        throw lang::DisposedException();
 
    SdMasterPage* pSdPage = comphelper::getFromUnoTunnel<SdMasterPage>( xPage );
    if(pSdPage == nullptr)
        return;
 
    SdPage* pPage = dynamic_cast< SdPage* > (pSdPage->GetSdrPage());
 
    DBG_ASSERT( pPage && pPage->IsMasterPage(), "SdMasterPage is not masterpage?");
 
    if( !pPage || !pPage->IsMasterPage() || (mpModel->mpDoc->GetMasterPageUserCount(pPage) > 0))
        return; //Todo: this should be excepted
 
    // only standard pages can be removed directly
    if( pPage->GetPageKind() != PageKind::Standard )
        return;
 
    sal_uInt16 nPage = pPage->GetPageNum();
 
    SdDrawDocument& rDoc = *mpModel->mpDoc;
 
    SdPage* pNotesPage = static_cast< SdPage* >( rDoc.GetMasterPage( nPage+1 ) );
 
    bool bUndo = rDoc.IsUndoEnabled();
    if( bUndo )
    {
        // Add undo actions and delete the pages.  The order of adding
        // the undo actions is important.
        rDoc.BegUndo( SdResId( STR_UNDO_DELETEPAGES ) );
        rDoc.AddUndo(rDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pNotesPage));
        rDoc.AddUndo(rDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage));
    }
 
    // remove both pages
    rDoc.RemoveMasterPage( nPage );
    rDoc.RemoveMasterPage( nPage );
 
    if( bUndo )
    {
        rDoc.EndUndo();
    }
}
 
// XServiceInfo
 
OUString SAL_CALL SdMasterPagesAccess::getImplementationName(  )
{
    return u"SdMasterPagesAccess"_ustr;
}
 
sal_Bool SAL_CALL SdMasterPagesAccess::supportsService( const OUString& ServiceName )
{
    return cppu::supportsService(this, ServiceName);
}
 
uno::Sequence< OUString > SAL_CALL SdMasterPagesAccess::getSupportedServiceNames(  )
{
    return { u"com.sun.star.drawing.MasterPages"_ustr };
}
 
SdDocLinkTargets::SdDocLinkTargets(SdXImpressDocument& rMyModel)
    : mpModel(&rMyModel)
{
    for (sal_uInt16 i=0; i < SdLinkTargetType::Count; i++)
        aNames[i] = SdResId(aTypeResIds[i]);
}
 
SdDocLinkTargets::~SdDocLinkTargets() noexcept
{
}
 
// XComponent
void SAL_CALL SdDocLinkTargets::dispose(  )
{
    mpModel = nullptr;
}
 
void SAL_CALL SdDocLinkTargets::addEventListener( const uno::Reference< lang::XEventListener >&  )
{
    OSL_FAIL( "not implemented!" );
}
 
void SAL_CALL SdDocLinkTargets::removeEventListener( const uno::Reference< lang::XEventListener >&  )
{
    OSL_FAIL( "not implemented!" );
}
 
// XNameAccess
uno::Any SAL_CALL SdDocLinkTargets::getByName( const OUString& aName )
{
    if (mpModel)
    {
        for (sal_uInt16 i=0; i < SdLinkTargetType::Count; i++)
            if ( aNames[i] == aName )
                return uno::Any(uno::Reference< beans::XPropertySet >(new SdDocLinkTargetType( mpModel, i )));
    }
 
    throw container::NoSuchElementException();
}
 
uno::Sequence< OUString > SAL_CALL SdDocLinkTargets::getElementNames()
{
    uno::Sequence<OUString> aRet(SdLinkTargetType::Count);
    OUString* pArray = aRet.getArray();
    for (sal_uInt16 i=0; i < SdLinkTargetType::Count; i++)
        pArray[i] = aNames[i];
    return aRet;
}
 
sal_Bool SAL_CALL SdDocLinkTargets::hasByName( const OUString& aName )
{
    for (const auto & i : aNames)
        if ( i == aName )
            return true;
    return false;
}
 
// container::XElementAccess
uno::Type SAL_CALL SdDocLinkTargets::getElementType()
{
    return cppu::UnoType<beans::XPropertySet>::get();
}
 
sal_Bool SAL_CALL SdDocLinkTargets::hasElements()
{
    return true;
}
 
SdPage* SdDocLinkTarget::FindPage( std::u16string_view rName ) const
{
    SdDrawDocument* pDoc = mpModel->GetDoc();
    if( pDoc == nullptr )
        return nullptr;
 
    const sal_uInt16 nMaxPages = pDoc->GetPageCount();
    const sal_uInt16 nMaxMasterPages = pDoc->GetMasterPageCount();
 
    sal_uInt16 nPage;
    SdPage* pPage;
 
    const bool bDraw = pDoc->GetDocumentType() == DocumentType::Draw;
 
    // standard pages
    for( nPage = 0; nPage < nMaxPages; nPage++ )
    {
        pPage = static_cast<SdPage*>(pDoc->GetPage( nPage ));
        if( (pPage->GetName() == rName) && (!bDraw || (pPage->GetPageKind() == PageKind::Standard)) )
            return pPage;
    }
 
    // master pages
    for( nPage = 0; nPage < nMaxMasterPages; nPage++ )
    {
        pPage = static_cast<SdPage*>(pDoc->GetMasterPage( nPage ));
        if( (pPage->GetName() == rName) && (!bDraw || (pPage->GetPageKind() == PageKind::Standard)) )
            return pPage;
    }
 
    return nullptr;
}
 
// XServiceInfo
OUString SAL_CALL SdDocLinkTargets::getImplementationName()
{
    return u"SdDocLinkTargets"_ustr;
}
 
sal_Bool SAL_CALL SdDocLinkTargets::supportsService( const OUString& ServiceName )
{
    return cppu::supportsService( this, ServiceName );
}
 
uno::Sequence< OUString > SAL_CALL SdDocLinkTargets::getSupportedServiceNames()
{
    return { u"com.sun.star.document.LinkTargets"_ustr };
}
 
SdDocLinkTargetType::SdDocLinkTargetType(SdXImpressDocument* pModel, sal_uInt16 nT)
    : mpModel(pModel)
    , mnType(nT)
{
    maName = SdResId(aTypeResIds[nT]);
}
 
// beans::XPropertySet
 
uno::Reference< beans::XPropertySetInfo > SAL_CALL SdDocLinkTargetType::getPropertySetInfo()
{
    static uno::Reference< beans::XPropertySetInfo > aRef;//(new SfxItemPropertySetInfo( lcl_GetLinkTargetMap() ));
    return aRef;
}
 
void SAL_CALL SdDocLinkTargetType::setPropertyValue(const OUString& /* aPropertyName */,
            const uno::Any& /* aValue */)
{
    //  everything is read-only
}
 
uno::Any SAL_CALL SdDocLinkTargetType::getPropertyValue(const OUString& PropertyName)
{
    uno::Any aRet;
    if ( PropertyName == "LinkDisplayName" )
        aRet <<= maName;
 
    return aRet;
}
 
void SAL_CALL SdDocLinkTargetType::addPropertyChangeListener( const OUString&,
                            const uno::Reference<beans::XPropertyChangeListener>&)
{ OSL_FAIL("not implemented"); }
 
void SAL_CALL SdDocLinkTargetType::removePropertyChangeListener( const OUString&,
                        const uno::Reference<beans::XPropertyChangeListener>&)
{ OSL_FAIL("not implemented"); }
 
void SAL_CALL SdDocLinkTargetType::addVetoableChangeListener( const OUString&,
                        const uno::Reference<beans::XVetoableChangeListener>&)
{ OSL_FAIL("not implemented"); }
 
void SAL_CALL SdDocLinkTargetType::removeVetoableChangeListener( const OUString&,
                        const uno::Reference<beans::XVetoableChangeListener>&)
{ OSL_FAIL("not implemented"); }
 
// document::XLinkTargetSupplier
 
uno::Reference< container::XNameAccess > SAL_CALL SdDocLinkTargetType::getLinks()
{
    return new SdDocLinkTarget( mpModel, mnType );
}
 
// XServiceInfo
OUString SAL_CALL SdDocLinkTargetType::getImplementationName()
{
    return u"SdDocLinkTargetType"_ustr;
}
 
sal_Bool SAL_CALL SdDocLinkTargetType::supportsService( const OUString& ServiceName )
{
    return cppu::supportsService( this, ServiceName );
}
 
uno::Sequence< OUString > SAL_CALL SdDocLinkTargetType::getSupportedServiceNames()
{
    return { u"com.sun.star.document.LinkTargetSupplier"_ustr };
}
 
SdDocLinkTarget::SdDocLinkTarget( SdXImpressDocument* pModel, sal_uInt16 nT )
    : mpModel(pModel)
    , mnType(nT)
{
}
 
// container::XNameAccess
 
uno::Any SAL_CALL SdDocLinkTarget::getByName(const OUString& aName)
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpModel )
        throw lang::DisposedException();
 
    SdPage* pPage = FindPage( aName );
 
    if( pPage == nullptr )
        throw container::NoSuchElementException();
 
    uno::Any aAny;
 
    uno::Reference< beans::XPropertySet > xProps( pPage->getUnoPage(), uno::UNO_QUERY );
    if( xProps.is() )
        aAny <<= xProps;
 
    return aAny;
}
 
uno::Sequence<OUString> SAL_CALL SdDocLinkTarget::getElementNames()
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpModel )
        throw lang::DisposedException();
 
    SdDrawDocument* pDoc = mpModel->GetDoc();
    if( pDoc == nullptr )
    {
        return { };
    }
 
    if( pDoc->GetDocumentType() == DocumentType::Draw )
    {
        const sal_uInt16 nMaxPages = pDoc->GetSdPageCount( PageKind::Standard );
        const sal_uInt16 nMaxMasterPages = pDoc->GetMasterSdPageCount( PageKind::Standard );
 
        uno::Sequence< OUString > aSeq( mnType == SdLinkTargetType::Page ? nMaxPages : nMaxMasterPages );
        OUString* pStr = aSeq.getArray();
 
        sal_uInt16 nPage;
        if (mnType == SdLinkTargetType::Page)
        {
            // standard pages
            for( nPage = 0; nPage < nMaxPages; nPage++ )
                *pStr++ = pDoc->GetSdPage( nPage, PageKind::Standard )->GetName();
        }
        else
        {
            // master pages
            for( nPage = 0; nPage < nMaxMasterPages; nPage++ )
                *pStr++ = pDoc->GetMasterSdPage( nPage, PageKind::Standard )->GetName();
        }
        return aSeq;
    }
    else
    {
        PageKind eKind;
        switch (mnType)
        {
            case SdLinkTargetType::Notes:
                eKind = PageKind::Notes;
                break;
            case SdLinkTargetType::Handout:
                eKind = PageKind::Handout;
                break;
            default:
                eKind = PageKind::Standard;
                break;
        }
        const sal_uInt16 nMaxPages = pDoc->GetSdPageCount( eKind );
        const sal_uInt16 nMaxMasterPages = pDoc->GetMasterPageCount();
 
        uno::Sequence< OUString > aSeq( mnType == SdLinkTargetType::MasterPage ? nMaxMasterPages : nMaxPages );
        OUString* pStr = aSeq.getArray();
 
        sal_uInt16 nPage;
        switch (mnType)
        {
            case SdLinkTargetType::Page:
            {
                for( nPage = 0; nPage < nMaxPages; nPage++ )
                    *pStr++ = pDoc->GetSdPage( nPage, PageKind::Standard )->GetName();
                break;
            }
            case SdLinkTargetType::Notes:
            {
                for( nPage = 0; nPage < nMaxPages; nPage++ )
                    *pStr++ = pDoc->GetSdPage( nPage, PageKind::Notes )->GetName();
                break;
            }
            case SdLinkTargetType::Handout:
            {
                for( nPage = 0; nPage < nMaxPages; nPage++ )
                    *pStr++ = pDoc->GetSdPage( nPage, PageKind::Handout )->GetName();
                break;
            }
            case SdLinkTargetType::MasterPage:
            {
                for( nPage = 0; nPage < nMaxMasterPages; nPage++ )
                    *pStr++ = static_cast<SdPage*>(pDoc->GetMasterPage( nPage ))->GetName();
                break;
            }
        }
        return aSeq;
    }
}
 
sal_Bool SAL_CALL SdDocLinkTarget::hasByName(const OUString& aName)
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpModel )
        throw lang::DisposedException();
 
    return FindPage( aName ) != nullptr;
}
 
// container::XElementAccess
 
uno::Type SAL_CALL SdDocLinkTarget::getElementType()
{
    return cppu::UnoType<beans::XPropertySet>::get();
}
 
sal_Bool SAL_CALL SdDocLinkTarget::hasElements()
{
    ::SolarMutexGuard aGuard;
 
    if( nullptr == mpModel )
        throw lang::DisposedException();
 
    return mpModel->GetDoc() != nullptr;
}
 
// XServiceInfo
OUString SAL_CALL SdDocLinkTarget::getImplementationName()
{
    return u"SdDocLinkTarget"_ustr;
}
 
sal_Bool SAL_CALL SdDocLinkTarget::supportsService( const OUString& ServiceName )
{
    return cppu::supportsService( this, ServiceName );
}
 
uno::Sequence< OUString > SAL_CALL SdDocLinkTarget::getSupportedServiceNames()
{
    return { u"com.sun.star.document.LinkTargets"_ustr };
}
 
rtl::Reference< SdXImpressDocument > SdXImpressDocument::GetModel( SdDrawDocument const & rDocument )
{
    rtl::Reference< SdXImpressDocument > xRet;
    ::sd::DrawDocShell* pDocShell(rDocument.GetDocSh());
    if( pDocShell )
    {
        uno::Reference<frame::XModel> xModel(pDocShell->GetModel());
 
        xRet.set( dynamic_cast< SdXImpressDocument* >( xModel.get() ) );
    }
 
    return xRet;
}
 
void NotifyDocumentEvent(SdDrawDocument const & rDocument, const OUString& rEventName)
{
    rtl::Reference<SdXImpressDocument> xModel(SdXImpressDocument::GetModel(rDocument));
 
    if (xModel.is())
    {
        uno::Reference<uno::XInterface> xSource(static_cast<uno::XWeak*>(xModel.get()));
        NotifyDocumentEvent(rDocument, rEventName, xSource);
    }
}
 
void NotifyDocumentEvent(SdDrawDocument const & rDocument, const OUString& rEventName, const uno::Reference<uno::XInterface>& xSource)
{
    rtl::Reference<SdXImpressDocument> xModel(SdXImpressDocument::GetModel(rDocument));
 
    if (xModel.is())
    {
        css::document::EventObject aEvent(xSource, rEventName);
        xModel->notifyEvent(aEvent);
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

V547 Expression 'nTransitionType != 0' is always false.

V614 Uninitialized variable 'nFillColor' used.

V614 Uninitialized variable 'aFillStyle' used.

V547 Expression 'bExportNotesPages' is always false.

V547 Expression 'bExportNotesPages' is always false.

V547 Expression 'bIsVisible' is always true.

V547 Expression 'bBackgroundVisibility' is always true.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

V1019 Compound assignment expression 'aTemp >>= nTemp' is used inside condition.

V1019 Compound assignment expression 'Arguments[0] >>= arg' is used inside condition.

V1048 The 'nType' variable was assigned the same value.

V1048 The 'nType' variable was assigned the same value.

V1048 The 'nType' variable was assigned the same value.

V1048 The 'nType' variable was assigned the same value.

V1048 The 'nType' variable was assigned the same value.

V1048 The 'nType' variable was assigned the same value.

V1048 The 'nType' variable was assigned the same value.

V1048 The 'nType' variable was assigned the same value.

V1048 The 'aNote.meType' variable was assigned the same value.

V1048 The 'nTime' variable was assigned the same value.