/* -*- 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 <animations/animationnodehelper.hxx>
#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 <SlideshowLayerRenderer.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 <SlideSorter.hxx>
#include <SlideSorterViewShell.hxx>
#include <controller/SlideSorterController.hxx>
#include <controller/SlsPageSelector.hxx>
#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_STOPPRESEN
TATION;
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, "AnimateMotion" },
{ 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();
void exportTriggers() const ;
[[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 ;
void convertTiming(OStringBuffer& sTmp, const Any& rValue);
void appendTrigger(const css::uno::Any& rTarget, const OString& rTriggerHash);
void exportTriggersImpl(const uno::Reference<drawing::XShapes>& xShapes) const ;
private :
::tools::JsonWriter& mrWriter;
Reference<drawing::XDrawPage> mxDrawPage;
Reference<XPropertySet> mxPageProps;
Reference<XAnimationNode> mxRootNode;
bool mbHasEffects;
std::unordered_map<SdrObject*, OString> maEventTriggerSet;
};
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 ;
}
SdrObject* getObjectForShape(uno::Reference<drawing::XShape> const & xShape)
{
if (!xShape.is())
return nullptr;
SvxShape* pShape = comphelper::getFromUnoTunnel<SvxShape>(xShape);
if (pShape)
return pShape->GetSdrObject();
return nullptr;
}
SdrObject* getTargetObject(const uno::Any& aTargetAny)
{
SdrObject* pObject = nullptr;
uno::Reference<drawing::XShape> xShape;
if ((aTargetAny >>= xShape) && xShape.is())
{
pObject = getObjectForShape(xShape);
}
else // if target is not a shape - could be paragraph target containing a shape
{
presentation::ParagraphTarget aParagraphTarget;
if ((aTargetAny >>= aParagraphTarget) && aParagraphTarget.Shape.is())
{
pObject = getObjectForShape(aParagraphTarget.Shape);
}
}
return pObject;
}
bool isNodeTargetInShapeGroup(const Reference<XAnimationNode>& xNode)
{
Reference<XAnimate> xAnimate(xNode, UNO_QUERY);
if (xAnimate.is())
{
SdrObject* pObject = getTargetObject(xAnimate->getTarget());
if (pObject)
return pObject->getParentSdrObjectFromSdrObject() != nullptr;
}
return false ;
}
bool isNodeTargetAGroup(const Reference<XAnimationNode>& xNode)
{
Reference<XAnimate> xAnimate(xNode, UNO_QUERY);
if (xAnimate.is())
{
SdrObject* pObject = getTargetObject(xAnimate->getTarget());
if (pObject)
return pObject->getChildrenOfSdrObject() != nullptr;
}
return false ;
}
bool isEffectValidForTarget(const Reference<XAnimationNode>& xNode)
{
const Sequence<NamedValue> aUserData(xNode->getUserData());
for (const auto & rValue : aUserData)
{
if (!IsXMLToken(rValue.Name, XML_PRESET_ID))
continue ;
OUString aPresetId;
if (rValue.Value >>= aPresetId)
{
if (constNonValidEffectsForGroupSet.find(aPresetId.toUtf8())
!= constNonValidEffectsForGroupSet.end())
{
// it's in the list, so we need to check if the effect target is a group or not
Reference<XTimeContainer> xContainer(xNode, UNO_QUERY);
if (xContainer.is())
{
Reference<XEnumerationAccess> xEnumerationAccess(xContainer, UNO_QUERY);
Reference<XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration();
// target is the same for all children, check the first one
if (xEnumeration.is() && xEnumeration->hasMoreElements())
{
Reference<XAnimationNode> xChildNode(xEnumeration->nextElement(),
UNO_QUERY);
if (isNodeTargetAGroup(xChildNode))
return false ;
}
}
}
}
// preset id found and checked, we can exit
break ;
}
return true ;
}
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)
{
// afaics, when a shape is part of a group any applied effect is ignored
// moreover, some kind of effect, like the ones based on color animations,
// is ignored when applied to a group
if (!isValidNode(xNode) || isNodeTargetInShapeGroup(xNode) || !isEffectValidForTarget(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" );
}
}
void AnimationsExporter::convertTiming(OStringBuffer& sTmp, const Any& rValue)
{
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())
{
OStringBuffer aTriggerBuffer;
// hash must not start with a digit or on client it is parsed as a time in seconds
aTriggerBuffer.append("id" );
anim::convertTarget(aTriggerBuffer, pEvent->Source);
OString sTriggerHash(aTriggerBuffer.makeStringAndClear());
sTmp.append(sTriggerHash);
sTmp.append('.' );
appendTrigger(pEvent->Source, sTriggerHash);
}
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::appendTrigger(const css::uno::Any& rTarget, const OString& rTriggerHash)
{
css::uno::Reference<css::uno::XInterface> xRef;
rTarget >>= xRef;
uno::Reference<drawing::XShape> xShape(xRef, uno::UNO_QUERY);
if (!xShape.is())
{
if (auto xParagraphTarget = o3tl::tryAccess<css::presentation::ParagraphTarget>(rTarget))
{
xShape = xParagraphTarget->Shape;
}
}
if (xShape.is())
{
auto * pObject = SdrObject::getSdrObjectFromXShape(xShape);
maEventTriggerSet[pObject] = rTriggerHash;
}
}
void AnimationsExporter::exportTriggersImpl(const uno::Reference<drawing::XShapes>& xShapes) const
{
if (!xShapes.is())
return ;
sal_Int32 nCount = xShapes->getCount();
for (sal_Int32 i = 0; i < nCount; ++i)
{
auto xObject = xShapes->getByIndex(i);
uno::Reference<drawing::XShape> xShape(xObject, uno::UNO_QUERY);
if (!xShape.is())
continue ;
auto * pObject = SdrObject::getSdrObjectFromXShape(xShape);
if (maEventTriggerSet.find(pObject) == maEventTriggerSet.end())
continue ;
{
auto aShape = mrWriter.startStruct();
mrWriter.put("hash" , maEventTriggerSet.at(pObject));
{
auto const & rRectangle = pObject->GetLogicRect();
auto aRectangle
= o3tl::convert(rRectangle, o3tl::Length::mm100, o3tl::Length::twip);
auto aRect = mrWriter.startNode("bounds" );
mrWriter.put("x" , aRectangle.Left());
mrWriter.put("y" , aRectangle.Top());
mrWriter.put("width" , aRectangle.GetWidth());
mrWriter.put("height" , aRectangle.GetHeight());
}
}
}
}
void AnimationsExporter::exportTriggers() const
{
uno::Reference<drawing::XShapes> const xShapes(mxDrawPage, uno::UNO_QUERY_THROW);
if (!xShapes.is())
return ;
auto aTriggerList = mrWriter.startArray("triggers" );
exportTriggersImpl(xShapes);
}
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())
{
anim::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())
{
anim::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" );
}
}
void GetDocStructureSlides(::tools::JsonWriter& rJsonWriter, const SdXImpressDocument* pDoc,
const std::map<OUString, OUString>& rArguments)
{
auto it = rArguments.find(u"filter" _ustr);
if (it != rArguments.end())
{
// If filter is present but we are filtering not to slide information
if (!it->second.equals(u"slides" _ustr))
return ;
}
sal_uInt16 nPageCount = pDoc->GetDoc()->GetSdPageCount(PageKind::Standard);
sal_uInt16 nMasterPageCount = pDoc->GetDoc()->GetMasterSdPageCount(PageKind::Standard);
rJsonWriter.put("SlideCount" , nPageCount);
rJsonWriter.put("MasterSlideCount" , nMasterPageCount);
// write data of every master slide
if (nMasterPageCount > 0)
{
auto aMasterPagesNode = rJsonWriter.startNode("MasterSlides" );
for (int nMPId = 0; nMPId < nMasterPageCount; nMPId++)
{
auto aMasterPageNode = rJsonWriter.startNode("MasterSlide " + std::to_string(nMPId));
const OUString& aMName
= pDoc->GetDoc()->GetMasterSdPage(nMPId, PageKind::Standard)->GetName();
rJsonWriter.put("Name" , aMName);
}
}
// write data of every slide
if (nPageCount > 0)
{
auto aPagesNode = rJsonWriter.startNode("Slides" );
for (int nPId = 0; nPId < nPageCount; nPId++)
{
auto aPageNode = rJsonWriter.startNode("Slide " + std::to_string(nPId));
SdPage* pPageStandard = pDoc->GetDoc()->GetSdPage(nPId, PageKind::Standard);
// Slide Name
rJsonWriter.put("SlideName" , pPageStandard->GetName());
// MasterSlide Name
const FmFormPage* pMasterPage
= dynamic_cast <const FmFormPage*>(&pPageStandard->TRG_GetMasterPage());
if (pMasterPage)
{
rJsonWriter.put("MasterSlideName" , pMasterPage->GetName());
}
// Layout id, and name.
AutoLayout nLayout = pPageStandard->GetAutoLayout();
rJsonWriter.put("LayoutId" , static_cast <int >(nLayout));
rJsonWriter.put("LayoutName" , SdPage::autoLayoutToString(nLayout));
// Every Objects in the page
int nObjCount = pPageStandard->GetObjCount();
rJsonWriter.put("ObjectCount" , nObjCount);
if (nObjCount > 0)
{
auto aObjectsNode = rJsonWriter.startNode("Objects" );
for (int nOId = 0; nOId < nObjCount; nOId++)
{
auto aObjectNode = rJsonWriter.startNode("Objects " + std::to_string(nOId));
SdrObject* pSdrObj = pPageStandard->GetObj(nOId);
SdrTextObj* pSdrTxtObj = DynCastSdrTextObj(pSdrObj);
if (pSdrTxtObj && pSdrTxtObj->HasText())
{
sal_Int32 nTextCount = pSdrTxtObj->getTextCount();
rJsonWriter.put("TextCount" , nTextCount);
if (nTextCount > 0)
{
auto aTextsNode = rJsonWriter.startNode("Texts" );
for (int nTId = 0; nTId < nTextCount; nTId++)
{
auto aTextNode
= rJsonWriter.startNode("Text " + std::to_string(nTId));
SdrText* pSdrTxt = pSdrTxtObj->getText(nTId);
OutlinerParaObject* pOutlinerParaObject
= pSdrTxt->GetOutlinerParaObject();
sal_Int32 nParaCount
= pOutlinerParaObject->GetTextObject().GetParagraphCount();
rJsonWriter.put("ParaCount" , nParaCount);
auto aParasNode = rJsonWriter.startArray("Paragraphs" );
for (int nParaId = 0; nParaId < nParaCount; nParaId++)
{
OUString aParaStr(
pOutlinerParaObject->GetTextObject().GetText(nParaId));
rJsonWriter.putSimpleValue(aParaStr);
}
}
}
}
}
}
}
}
}
} // 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},
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=96 H=96 G=95
¤ Dauer der Verarbeitung: 0.21 Sekunden
¤
*© Formatika GbR, Deutschland