diff options
Diffstat (limited to 'sd/source/filter/eppt/pptx-epptooxml.cxx')
-rw-r--r-- | sd/source/filter/eppt/pptx-epptooxml.cxx | 2594 |
1 files changed, 2594 insertions, 0 deletions
diff --git a/sd/source/filter/eppt/pptx-epptooxml.cxx b/sd/source/filter/eppt/pptx-epptooxml.cxx new file mode 100644 index 000000000..6a338a6a6 --- /dev/null +++ b/sd/source/filter/eppt/pptx-epptooxml.cxx @@ -0,0 +1,2594 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <stdio.h> +#include <oox/drawingml/clrscheme.hxx> +#include <oox/token/namespaces.hxx> +#include <oox/token/tokens.hxx> +#include <oox/token/relationship.hxx> +#include <oox/ole/vbaproject.hxx> +#include "epptooxml.hxx" +#include <oox/export/shapes.hxx> + +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/xmltools.hxx> +#include <sax/fshelper.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <tools/UnitConversion.hxx> +#include <tools/datetime.hxx> +#include <com/sun/star/animations/TransitionType.hpp> +#include <com/sun/star/animations/TransitionSubType.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/drawing/XDrawPages.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/geometry/RealPoint2D.hpp> +#include <com/sun/star/office/XAnnotationEnumeration.hpp> +#include <com/sun/star/office/XAnnotationAccess.hpp> +#include <com/sun/star/presentation/AnimationSpeed.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/presentation/XCustomPresentationSupplier.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/presentation/XPresentationSupplier.hpp> +#include <tools/diagnose_ex.h> + +#include <oox/export/utils.hxx> + +#include "pptx-animations.hxx" +#include "../ppt/pptanimations.hxx" + +#include <i18nlangtag/languagetag.hxx> +#include <svx/svdpage.hxx> +#include <svx/unoapi.hxx> +#include <svx/svdogrp.hxx> +#include <sdmod.hxx> +#include <sdpage.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <utility> +#if OSL_DEBUG_LEVEL > 1 +#include <com/sun/star/drawing/RectanglePoint.hpp> +#endif + +// presentation namespaces +#define PNMSS FSNS(XML_xmlns, XML_a), OUStringToOString(this->getNamespaceURL(OOX_NS(dml)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_p), OUStringToOString(this->getNamespaceURL(OOX_NS(ppt)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_r), OUStringToOString(this->getNamespaceURL(OOX_NS(officeRel)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_p14), OUStringToOString(this->getNamespaceURL(OOX_NS(p14)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_p15), OUStringToOString(this->getNamespaceURL(OOX_NS(p15)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_mc), OUStringToOString(this->getNamespaceURL(OOX_NS(mce)), RTL_TEXTENCODING_UTF8).getStr() + +// presentationPr namespace +#define PPRNMSS FSNS(XML_xmlns, XML_a), OUStringToOString(this->getNamespaceURL(OOX_NS(dml)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_r), OUStringToOString(this->getNamespaceURL(OOX_NS(officeRel)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_p), OUStringToOString(this->getNamespaceURL(OOX_NS(ppt)), RTL_TEXTENCODING_UTF8).getStr() + +using namespace ::com::sun::star; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::geometry; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::office; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::ppt; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::beans::XPropertySetInfo; +using ::sax_fastparser::FSHelperPtr; +using namespace oox::drawingml; +using namespace oox::core; + +#if OSL_DEBUG_LEVEL > 1 +void dump_pset(Reference< XPropertySet > const& rXPropSet); +#endif + +namespace oox::core +{ + +class PowerPointShapeExport : public ShapeExport +{ + PowerPointExport& mrExport; + PageType mePageType; + bool mbMaster; +public: + PowerPointShapeExport(FSHelperPtr pFS, ShapeHashMap* pShapeMap, PowerPointExport* pFB); + void SetMaster(bool bMaster); + void SetPageType(PageType ePageType); + ShapeExport& WriteNonVisualProperties(const Reference< XShape >& xShape) override; + ShapeExport& WriteTextShape(const Reference< XShape >& xShape) override; + ShapeExport& WriteUnknownShape(const Reference< XShape >& xShape) override; + ShapeExport& WritePlaceholderShape(const Reference< XShape >& xShape, PlaceholderType ePlaceholder); + /** Writes a placeholder shape that references the placeholder on the master slide */ + ShapeExport& WritePlaceholderReferenceShape(PlaceholderType ePlaceholder, sal_Int32 nReferencedPlaceholderIdx, PageType ePageType, const Reference<XPropertySet>& rXPagePropSet); + ShapeExport& WritePageShape(const Reference< XShape >& xShape, PageType ePageType, bool bPresObj); + /** Writes textbody of a placeholder that references the placeholder on the master slide */ + ShapeExport& WritePlaceholderReferenceTextBody(PlaceholderType ePlaceholder, PageType ePageType, const Reference<XPropertySet> xPagePropSet); + + // helper parts + bool WritePlaceholder(const Reference< XShape >& xShape, PlaceholderType ePlaceholder, bool bMaster); +}; + + +namespace +{ +void WriteSndAc(const FSHelperPtr& pFS, const OUString& sSoundRelId, const OUString& sSoundName) +{ + pFS->startElementNS(XML_p, XML_sndAc); + pFS->startElementNS(XML_p, XML_stSnd); + pFS->singleElementNS(XML_p, XML_snd, FSNS(XML_r, XML_embed), + sax_fastparser::UseIf(sSoundRelId, !sSoundRelId.isEmpty()), XML_name, + sax_fastparser::UseIf(sSoundName, !sSoundName.isEmpty())); + pFS->endElement(FSNS(XML_p, XML_stSnd)); + pFS->endElement(FSNS(XML_p, XML_sndAc)); +} + +const char* getPlaceholderTypeName(PlaceholderType ePlaceholder) +{ + switch (ePlaceholder) + { + case SlideImage: + return "sldImg"; + case Notes: + return "body"; + case Header: + return "hdr"; + case Footer: + return "ftr"; + case SlideNumber: + return "sldNum"; + case DateAndTime: + return "dt"; + case Outliner: + return "body"; + case Title: + return "title"; + case Subtitle: + return "subTitle"; + default: + SAL_INFO("sd.eppt", "warning: unhandled placeholder type: " << ePlaceholder); + return ""; + } +} +} +} + +namespace { + +enum PPTXLayout +{ + LAYOUT_BLANK, + LAYOUT_TITLE_SLIDE, + LAYOUT_TITLE_CONTENT, + LAYOUT_TITLE_2CONTENT, + LAYOUT_TITLE, + LAYOUT_CENTERED_TEXT, + LAYOUT_TITLE_2CONTENT_CONTENT, + LAYOUT_TITLE_CONTENT_2CONTENT, + LAYOUT_TITLE_2CONTENT_OVER_CONTENT, + LAYOUT_TITLE_CONTENT_OVER_CONTENT, + LAYOUT_TITLE_4CONTENT, + LAYOUT_TITLE_6CONTENT, + LAYOUT_SIZE +}; + +struct PPTXLayoutInfo +{ + int nType; + const char* sName; + const char* sType; +}; + +} + +const PPTXLayoutInfo aLayoutInfo[LAYOUT_SIZE] = +{ + { 20, "Blank Slide", "blank" }, + { 0, "Title Slide", "tx" }, + { 1, "Title, Content", "obj" }, + { 3, "Title, 2 Content", "twoObj" }, + { 19, "Title Only", "titleOnly" }, + { 32, "Centered Text", "objOnly" }, // not exactly, but close + { 15, "Title, 2 Content and Content", "twoObjAndObj" }, + { 12, "Title Content and 2 Content", "objAndTwoObj" }, + { 16, "Title, 2 Content over Content", "twoObjOverTx" }, // not exactly, but close + { 14, "Title, Content over Content", "objOverTx" }, // not exactly, but close + { 18, "Title, 4 Content", "fourObj" }, + { 34, "Title, 6 Content", "blank" } // not defined => blank +}; + +int PowerPointExport::GetPPTXLayoutId(int nOffset) +{ + int nId = LAYOUT_BLANK; + + SAL_INFO("sd.eppt", "GetPPTXLayoutId " << nOffset); + + switch (nOffset) + { + case 0: + nId = LAYOUT_TITLE_SLIDE; + break; + case 1: + nId = LAYOUT_TITLE_CONTENT; + break; + case 3: + nId = LAYOUT_TITLE_2CONTENT; + break; + case 19: + nId = LAYOUT_TITLE; + break; + case 15: + nId = LAYOUT_TITLE_2CONTENT_CONTENT; + break; + case 12: + nId = LAYOUT_TITLE_CONTENT_2CONTENT; + break; + case 16: + nId = LAYOUT_TITLE_2CONTENT_OVER_CONTENT; + break; + case 14: + nId = LAYOUT_TITLE_CONTENT_OVER_CONTENT; + break; + case 18: + nId = LAYOUT_TITLE_4CONTENT; + break; + case 32: + nId = LAYOUT_CENTERED_TEXT; + break; + case 34: + nId = LAYOUT_TITLE_6CONTENT; + break; + case 20: + default: + nId = LAYOUT_BLANK; + break; + } + + return nId; +} + +PowerPointShapeExport::PowerPointShapeExport(FSHelperPtr pFS, ShapeHashMap* pShapeMap, + PowerPointExport* pFB) + : ShapeExport(XML_p, std::move(pFS), pShapeMap, pFB) + , mrExport(*pFB) + , mePageType(UNDEFINED) + , mbMaster(false) +{ +} + +void PowerPointShapeExport::SetMaster(bool bMaster) +{ + mbMaster = bMaster; +} + +void PowerPointShapeExport::SetPageType(PageType ePageType) +{ + mePageType = ePageType; +} + +ShapeExport& PowerPointShapeExport::WriteNonVisualProperties(const Reference< XShape >&) +{ + GetFS()->singleElementNS(XML_p, XML_nvPr); + + return *this; +} + +ShapeExport& PowerPointShapeExport::WriteTextShape(const Reference< XShape >& xShape) +{ + OUString sShapeType = xShape->getShapeType(); + + SAL_INFO("sd.eppt", "shape(text) : " << sShapeType.toUtf8()); + + if (sShapeType == "com.sun.star.drawing.TextShape" || sShapeType == "com.sun.star.drawing.GraphicObjectShape") + { + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.DateTimeShape") + { + if (!WritePlaceholder(xShape, DateAndTime, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.FooterShape") + { + if (!WritePlaceholder(xShape, Footer, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.HeaderShape") + { + if (!WritePlaceholder(xShape, Header, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.NotesShape") + { + if (mePageType == NOTICE && mrExport.GetPresObj()) + WritePlaceholderShape(xShape, Notes); + else + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.OutlinerShape") + { + if (!WritePlaceholder(xShape, Outliner, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.SlideNumberShape") + { + if (!WritePlaceholder(xShape, SlideNumber, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.TitleTextShape") + { + if (!WritePlaceholder(xShape, Title, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else + SAL_WARN("sd.eppt", "PowerPointShapeExport::WriteTextShape: shape of type '" << sShapeType << "' is ignored"); + + return *this; +} + +ShapeExport& PowerPointShapeExport::WriteUnknownShape(const Reference< XShape >& xShape) +{ + OUString sShapeType = xShape->getShapeType(); + + SAL_INFO("sd.eppt", "shape(unknown): " << sShapeType.toUtf8()); + + if (sShapeType == "com.sun.star.presentation.PageShape") + { + WritePageShape(xShape, mePageType, mrExport.GetPresObj()); + } + else if (sShapeType == "com.sun.star.presentation.SubtitleShape") + { + if(mePageType != MASTER) + { + if (!WritePlaceholder(xShape, Subtitle, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + } + else + SAL_WARN("sd.eppt", "unknown shape not handled: " << sShapeType.toUtf8()); + + return *this; +} + +PowerPointExport::PowerPointExport(const Reference< XComponentContext >& rContext, const uno::Sequence<uno::Any>& rArguments) + : XmlFilterBase(rContext) + , mnLayoutFileIdMax(1) + , mnSlideIdMax(1 << 8) + , mnSlideMasterIdMax(1U << 31) + , mnAnimationNodeIdMax(1) + , mnDiagramId(1) + , mbCreateNotes(false) + , mnPlaceholderIndexMax(1) +{ + comphelper::SequenceAsHashMap aArgumentsMap(rArguments); + mbPptm = aArgumentsMap.getUnpackedValueOrDefault("IsPPTM", false); + mbExportTemplate = aArgumentsMap.getUnpackedValueOrDefault("IsTemplate", false); +} + +PowerPointExport::~PowerPointExport() +{ +} + +void PowerPointExport::writeDocumentProperties() +{ + uno::Reference<document::XDocumentPropertiesSupplier> xDPS(mXModel, uno::UNO_QUERY); + uno::Reference<document::XDocumentProperties> xDocProps = xDPS->getDocumentProperties(); + + if (xDocProps.is()) + { + bool bSecurityOptOpenReadOnly = false; + uno::Reference< lang::XMultiServiceFactory > xFactory(mXModel, uno::UNO_QUERY); + uno::Reference< beans::XPropertySet > xSettings(xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY); + try + { + xSettings->getPropertyValue("LoadReadonly") >>= bSecurityOptOpenReadOnly; + } + catch( Exception& ) + { + } + exportDocumentProperties(xDocProps, bSecurityOptOpenReadOnly); + } + + exportCustomFragments(); +} + +bool PowerPointExport::importDocument() noexcept +{ + return false; +} + +bool PowerPointExport::exportDocument() +{ + DrawingML::PushExportGraphics(); + maShapeMap.clear(); + + mXModel = getModel(); + + //write document properties + writeDocumentProperties(); + + addRelation(oox::getRelationship(Relationship::OFFICEDOCUMENT), u"ppt/presentation.xml"); + + OUString aMediaType; + if (mbPptm) + { + if (mbExportTemplate) + { + aMediaType = "application/vnd.ms-powerpoint.template.macroEnabled.main+xml"; + } + else + { + aMediaType = "application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml"; + } + } + else + { + if (mbExportTemplate) + { + aMediaType = "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml"; + } + else + { + aMediaType = "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"; + } + } + + mPresentationFS = openFragmentStreamWithSerializer("ppt/presentation.xml", aMediaType); + + addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::THEME), + u"theme/theme1.xml"); + + mPresentationFS->startElementNS(XML_p, XML_presentation, PNMSS); + + mXStatusIndicator = getStatusIndicator(); + + std::vector< PropertyValue > aProperties; + PropertyValue aProperty; + aProperty.Name = "BaseURI"; + aProperty.Value <<= getFileUrl(); + aProperties.push_back(aProperty); + + exportPPT(aProperties); + + mPresentationFS->singleElementNS(XML_p, XML_sldSz, + XML_cx, OString::number(PPTtoEMU(maDestPageSize.Width)), + XML_cy, OString::number(PPTtoEMU(maDestPageSize.Height))); + // for some reason if added before slides list it will not load the slides (alas with error reports) in mso + mPresentationFS->singleElementNS(XML_p, XML_notesSz, + XML_cx, OString::number(PPTtoEMU(maNotesPageSize.Width)), + XML_cy, OString::number(PPTtoEMU(maNotesPageSize.Height))); + + WriteCustomSlideShow(); + + WritePresentationProps(); + + WriteAuthors(); + + WriteVBA(); + + WriteModifyVerifier(); + + mPresentationFS->endElementNS(XML_p, XML_presentation); + mPresentationFS.reset(); + // Free all FSHelperPtr, to flush data before committing storage + mpSlidesFSArray.clear(); + + commitStorage(); + + DrawingML::PopExportGraphics(); + maShapeMap.clear(); + maAuthors.clear(); + maRelId.clear(); + + return true; +} + +::oox::ole::VbaProject* PowerPointExport::implCreateVbaProject() const +{ + return new ::oox::ole::VbaProject(getComponentContext(), getModel(), u"Impress"); +} + +void PowerPointExport::WriteCustomSlideShow() +{ + Reference<XCustomPresentationSupplier> aXCPSup(mXModel, css::uno::UNO_QUERY); + if (!aXCPSup.is() || !aXCPSup->getCustomPresentations()->hasElements()) + return; + + mPresentationFS->startElementNS(XML_p, XML_custShowLst); + + Reference<XDrawPagesSupplier> xDPS(getModel(), uno::UNO_QUERY_THROW); + Reference<XDrawPages> xDrawPages(xDPS->getDrawPages(), uno::UNO_SET_THROW); + Reference<XNameContainer> aXNameCont(aXCPSup->getCustomPresentations()); + const Sequence<OUString> aNameSeq(aXNameCont->getElementNames()); + + OUString sRelId; + sal_uInt32 nCustomShowIndex = 0; + sal_Int32 nSlideCount = xDrawPages->getCount(); + + for (OUString const& customShowName : aNameSeq) + { + mPresentationFS->startElementNS(XML_p, XML_custShow, XML_name, customShowName, XML_id, + OUString::number(nCustomShowIndex++)); + + mAny = aXNameCont->getByName(customShowName); + Reference<XIndexContainer> aXIContainer; + if (mAny >>= aXIContainer) + { + mPresentationFS->startElementNS(XML_p, XML_sldLst); + + sal_Int32 nCustomShowSlideCount = aXIContainer->getCount(); + for (sal_Int32 i = 0; i < nCustomShowSlideCount; ++i) + { + Reference<XDrawPage> aXCustomShowDrawPage; + aXIContainer->getByIndex(i) >>= aXCustomShowDrawPage; + Reference<XNamed> aXName(aXCustomShowDrawPage, UNO_QUERY_THROW); + OUString sCustomShowSlideName = aXName->getName(); + + for (sal_Int32 j = 0; j < nSlideCount; ++j) + { + Reference<XDrawPage> xDrawPage; + xDrawPages->getByIndex(j) >>= xDrawPage; + Reference<XNamed> xNamed(xDrawPage, UNO_QUERY_THROW); + OUString sSlideName = xNamed->getName(); + + if (sCustomShowSlideName == sSlideName) + { + sRelId = maRelId[j]; + break; + } + } + mPresentationFS->singleElementNS(XML_p, XML_sld, FSNS(XML_r, XML_id), sRelId); + } + mPresentationFS->endElementNS(XML_p, XML_sldLst); + } + mPresentationFS->endElementNS(XML_p, XML_custShow); + } + mPresentationFS->endElementNS(XML_p, XML_custShowLst); +} + +void PowerPointExport::ImplWriteBackground(const FSHelperPtr& pFS, const Reference< XPropertySet >& rXPropSet) +{ + FillStyle aFillStyle(FillStyle_NONE); + if (ImplGetPropertyValue(rXPropSet, "FillStyle")) + mAny >>= aFillStyle; + + if (aFillStyle == FillStyle_NONE || + aFillStyle == FillStyle_HATCH) + return; + + pFS->startElementNS(XML_p, XML_bg); + pFS->startElementNS(XML_p, XML_bgPr); + + PowerPointShapeExport aDML(pFS, &maShapeMap, this); + aDML.SetBackgroundDark(mbIsBackgroundDark); + aDML.WriteFill(rXPropSet); + + pFS->endElementNS(XML_p, XML_bgPr); + pFS->endElementNS(XML_p, XML_bg); +} + +#define MAIN_GROUP \ + "<p:nvGrpSpPr>\ + <p:cNvPr id=\"1\" name=\"\"/>\ + <p:cNvGrpSpPr/>\ + <p:nvPr/>\ + </p:nvGrpSpPr>\ + <p:grpSpPr>\ + <a:xfrm>\ + <a:off x=\"0\" y=\"0\"/>\ + <a:ext cx=\"0\" cy=\"0\"/>\ + <a:chOff x=\"0\" y=\"0\"/>\ + <a:chExt cx=\"0\" cy=\"0\"/>\ + </a:xfrm>\ + </p:grpSpPr>" + +const char* PowerPointExport::GetSideDirection(sal_uInt8 nDirection) +{ + const char* pDirection = nullptr; + + switch (nDirection) + { + case 0: + pDirection = "l"; + break; + case 1: + pDirection = "u"; + break; + case 2: + pDirection = "r"; + break; + case 3: + pDirection = "d"; + break; + } + + return pDirection; +} + +const char* PowerPointExport::GetCornerDirection(sal_uInt8 nDirection) +{ + const char* pDirection = nullptr; + + switch (nDirection) + { + case 4: + pDirection = "lu"; + break; + case 5: + pDirection = "ru"; + break; + case 6: + pDirection = "ld"; + break; + case 7: + pDirection = "rd"; + break; + } + + return pDirection; +} + +const char* PowerPointExport::Get8Direction(sal_uInt8 nDirection) +{ + const char* pDirection = GetSideDirection(nDirection); + + if (!pDirection) + pDirection = GetCornerDirection(nDirection); + + return pDirection; +} + +void PowerPointExport::WriteTransition(const FSHelperPtr& pFS) +{ + FadeEffect eFadeEffect = FadeEffect_NONE; + if (ImplGetPropertyValue(mXPagePropSet, "Effect")) + mAny >>= eFadeEffect; + + sal_Int16 nTransitionType = 0, nTransitionSubtype = 0; + sal_Int8 nPPTTransitionType = 0; + sal_uInt8 nDirection = 0; + + OUString sSoundUrl; + OUString sSoundRelId; + OUString sSoundName; + + if (ImplGetPropertyValue(mXPagePropSet, "TransitionType") && (mAny >>= nTransitionType) && + ImplGetPropertyValue(mXPagePropSet, "TransitionSubtype") && (mAny >>= nTransitionSubtype)) + { + // FADEOVERCOLOR with black -> fade, with white -> flash + sal_Int32 nTransitionFadeColor = 0; + if( ImplGetPropertyValue(mXPagePropSet, "TransitionFadeColor")) + mAny >>= nTransitionFadeColor; + nPPTTransitionType = GetTransition(nTransitionType, nTransitionSubtype, eFadeEffect, nTransitionFadeColor, nDirection); + } + + if (!nPPTTransitionType && eFadeEffect != FadeEffect_NONE) + nPPTTransitionType = GetTransition(eFadeEffect, nDirection); + + if (ImplGetPropertyValue(mXPagePropSet, "Sound") && (mAny >>= sSoundUrl)) + embedEffectAudio(pFS, sSoundUrl, sSoundRelId, sSoundName); + + bool bOOXmlSpecificTransition = false; + + sal_Int32 nTransition = 0; + const char* pDirection = nullptr; + const char* pOrientation = nullptr; + const char* pThruBlk = nullptr; + const char* pSpokes = nullptr; + + char pSpokesTmp[2] = "0"; + + // p14 + sal_Int32 nTransition14 = 0; + const char* pDirection14 = nullptr; + const char* pInverted = nullptr; + const char* pPattern = nullptr; // diamond or hexagon + + //p15 + const char* pPresetTransition = nullptr; + + if (!nPPTTransitionType) + { + switch (nTransitionType) + { + case animations::TransitionType::BARWIPE: + { + if (nTransitionSubtype == animations::TransitionSubType::FADEOVERCOLOR) + { + nTransition = XML_cut; + pThruBlk = "true"; + bOOXmlSpecificTransition = true; + } + break; + } + case animations::TransitionType::MISCSHAPEWIPE: + { + switch (nTransitionSubtype) + { + case animations::TransitionSubType::TOPTOBOTTOM: // Turn around + nTransition = XML_fade; + nTransition14 = XML_flip; + pDirection14 = "l"; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::BOTTOMRIGHT: // Rochade + nTransition = XML_fade; + nTransition14 = XML_switch; + pDirection14 = "r"; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::VERTICAL: // Vortex + nTransition = XML_fade; + nTransition14 = XML_vortex; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::HORIZONTAL: // Ripple + nTransition = XML_fade; + nTransition14 = XML_ripple; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::LEFTTORIGHT: // Fall + nTransition = XML_fade; + pPresetTransition = "fallOver"; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::CORNERSIN: // Inside turning cube + pInverted = "true"; + [[fallthrough]]; + case animations::TransitionSubType::CORNERSOUT: // Outside turning cube + nTransition = XML_fade; + nTransition14 = XML_prism; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::DIAMOND: // Glitter + nTransition = XML_fade; + nTransition14 = XML_glitter; + pDirection14 = "l"; + pPattern = "hexagon"; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::HEART: // Honeycomb + nTransition = XML_fade; + nTransition14 = XML_honeycomb; + bOOXmlSpecificTransition = true; + break; + } + break; + } + } + } + + AnimationSpeed animationSpeed = AnimationSpeed_MEDIUM; + const char* speed = nullptr; + sal_Int32 advanceTiming = -1; + sal_Int32 changeType = 0; + + sal_Int32 nTransitionDuration = -1; + bool isTransitionDurationSet = false; + + // try to use TransitionDuration instead of old Speed property + if (ImplGetPropertyValue(mXPagePropSet, "TransitionDuration")) + { + double fTransitionDuration = -1.0; + mAny >>= fTransitionDuration; + if (fTransitionDuration >= 0) + { + nTransitionDuration = fTransitionDuration * 1000.0; + + // override values because in MS formats meaning of fast/medium/slow is different + if (nTransitionDuration <= 500) + { + // fast is default + speed = nullptr; + } + else if (nTransitionDuration >= 1000) + { + speed = "slow"; + } + else + { + speed = "med"; + } + + bool isStandardValue = nTransitionDuration == 500 + || nTransitionDuration == 750 + || nTransitionDuration == 1000; + + if(!isStandardValue) + isTransitionDurationSet = true; + } + } + else if (ImplGetPropertyValue(mXPagePropSet, "Speed")) + { + mAny >>= animationSpeed; + + switch (animationSpeed) + { + default: + case AnimationSpeed_MEDIUM: + speed = "med"; + break; + case AnimationSpeed_SLOW: + speed = "slow"; + break; + case AnimationSpeed_FAST: + break; + } + } + + // check if we resolved what transition to export or time is set + if (!nPPTTransitionType && !bOOXmlSpecificTransition && !isTransitionDurationSet) + return; + + if (ImplGetPropertyValue(mXPagePropSet, "Change")) + mAny >>= changeType; + + // 1 means automatic, 2 half automatic - not sure what it means - at least I don't see it in UI + if (changeType == 1 && ImplGetPropertyValue(mXPagePropSet, "Duration")) + mAny >>= advanceTiming; + + if (!bOOXmlSpecificTransition) + { + switch (nPPTTransitionType) + { + case PPT_TRANSITION_TYPE_BLINDS: + nTransition = XML_blinds; + pDirection = (nDirection == 0) ? "vert" : "horz"; + break; + case PPT_TRANSITION_TYPE_CHECKER: + nTransition = XML_checker; + pDirection = (nDirection == 1) ? "vert" : "horz"; + break; + case PPT_TRANSITION_TYPE_CIRCLE: + nTransition = XML_circle; + break; + case PPT_TRANSITION_TYPE_COMB: + nTransition = XML_comb; + pDirection = (nDirection == 1) ? "vert" : "horz"; + break; + case PPT_TRANSITION_TYPE_COVER: + nTransition = XML_cover; + pDirection = Get8Direction(nDirection); + break; + case PPT_TRANSITION_TYPE_DIAMOND: + nTransition = XML_diamond; + break; + case PPT_TRANSITION_TYPE_DISSOLVE: + nTransition = XML_dissolve; + break; + case PPT_TRANSITION_TYPE_FADE: + nTransition = XML_fade; + pThruBlk = "true"; + break; + case PPT_TRANSITION_TYPE_SMOOTHFADE: + nTransition = XML_fade; + break; + case PPT_TRANSITION_TYPE_NEWSFLASH: + nTransition = XML_newsflash; + break; + case PPT_TRANSITION_TYPE_PLUS: + nTransition = XML_plus; + break; + case PPT_TRANSITION_TYPE_PULL: + nTransition = XML_pull; + pDirection = Get8Direction(nDirection); + break; + case PPT_TRANSITION_TYPE_PUSH: + nTransition = XML_push; + pDirection = GetSideDirection(nDirection); + break; + case PPT_TRANSITION_TYPE_RANDOM: + nTransition = XML_random; + break; + case PPT_TRANSITION_TYPE_RANDOM_BARS: + nTransition = XML_randomBar; + pDirection = (nDirection == 1) ? "vert" : "horz"; + break; + case PPT_TRANSITION_TYPE_SPLIT: + nTransition = XML_split; + pDirection = (nDirection & 1) ? "in" : "out"; + pOrientation = (nDirection < 2) ? "horz" : "vert"; + break; + case PPT_TRANSITION_TYPE_STRIPS: + nTransition = XML_strips; + pDirection = GetCornerDirection(nDirection); + break; + case PPT_TRANSITION_TYPE_WEDGE: + nTransition = XML_wedge; + break; + case PPT_TRANSITION_TYPE_WHEEL: + nTransition = XML_wheel; + if (nDirection != 4 && nDirection <= 9) + { + pSpokesTmp[0] = '0' + nDirection; + pSpokes = pSpokesTmp; + } + break; + case PPT_TRANSITION_TYPE_WIPE: + nTransition = XML_wipe; + pDirection = GetSideDirection(nDirection); + break; + case PPT_TRANSITION_TYPE_ZOOM: + nTransition = XML_zoom; + pDirection = (nDirection == 1) ? "in" : "out"; + break; + case PPT_TRANSITION_TYPE_FLASH: + nTransition14 = XML_flash; + nTransition = XML_fade; + bOOXmlSpecificTransition = true; + break; + // coverity[dead_error_line] - following conditions exist to avoid compiler warning + case PPT_TRANSITION_TYPE_NONE: + default: + nTransition = 0; + break; + } + } + + bool isAdvanceTimingSet = advanceTiming != -1; + if (nTransition14 || pPresetTransition || isTransitionDurationSet) + { + const char* pRequiresNS = (nTransition14 || isTransitionDurationSet) ? "p14" : "p15"; + + pFS->startElement(FSNS(XML_mc, XML_AlternateContent)); + pFS->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, pRequiresNS); + + if(isTransitionDurationSet && isAdvanceTimingSet) + { + pFS->startElementNS(XML_p, XML_transition, + XML_spd, speed, + XML_advTm, OString::number(advanceTiming * 1000), + FSNS(XML_p14, XML_dur), OString::number(nTransitionDuration)); + } + else if(isTransitionDurationSet) + { + pFS->startElementNS(XML_p, XML_transition, + XML_spd, speed, + FSNS(XML_p14, XML_dur), OString::number(nTransitionDuration)); + } + else if(isAdvanceTimingSet) + { + pFS->startElementNS(XML_p, XML_transition, + XML_spd, speed, + XML_advTm, OString::number(advanceTiming * 1000)); + } + else + { + pFS->startElementNS(XML_p, XML_transition, XML_spd, speed); + } + + if (nTransition14) + { + pFS->singleElementNS(XML_p14, nTransition14, + XML_isInverted, pInverted, + XML_dir, pDirection14, + XML_pattern, pPattern); + } + else if (pPresetTransition) + { + pFS->singleElementNS(XML_p15, XML_prstTrans, + XML_prst, pPresetTransition); + } + else if (isTransitionDurationSet && nTransition) + { + pFS->singleElementNS(XML_p, nTransition, + XML_dir, pDirection, + XML_orient, pOrientation, + XML_spokes, pSpokes, + XML_thruBlk, pThruBlk); + } + + if (!sSoundRelId.isEmpty()) + WriteSndAc(pFS, sSoundRelId, sSoundName); + + pFS->endElement(FSNS(XML_p, XML_transition)); + + pFS->endElement(FSNS(XML_mc, XML_Choice)); + pFS->startElement(FSNS(XML_mc, XML_Fallback)); + } + + pFS->startElementNS(XML_p, XML_transition, + XML_spd, speed, + XML_advTm, sax_fastparser::UseIf(OString::number(advanceTiming * 1000), isAdvanceTimingSet)); + + if (nTransition) + { + pFS->singleElementNS(XML_p, nTransition, + XML_dir, pDirection, + XML_orient, pOrientation, + XML_spokes, pSpokes, + XML_thruBlk, pThruBlk); + } + + if (!sSoundRelId.isEmpty()) + WriteSndAc(pFS, sSoundRelId, sSoundName); + + pFS->endElementNS(XML_p, XML_transition); + + if (nTransition14 || pPresetTransition || isTransitionDurationSet) + { + pFS->endElement(FSNS(XML_mc, XML_Fallback)); + pFS->endElement(FSNS(XML_mc, XML_AlternateContent)); + } +} + +static OUString lcl_GetInitials(const OUString& sName) +{ + OUStringBuffer sRet; + + if (!sName.isEmpty()) + { + sRet.append(sName[0]); + sal_Int32 nStart = 0, nOffset; + + while ((nOffset = sName.indexOf(' ', nStart)) != -1) + { + if (nOffset + 1 < sName.getLength()) + sRet.append(sName[ nOffset + 1 ]); + nStart = nOffset + 1; + } + } + + return sRet.makeStringAndClear(); +} + +void PowerPointExport::WriteAuthors() +{ + if (maAuthors.empty()) + return; + + FSHelperPtr pFS = openFragmentStreamWithSerializer("ppt/commentAuthors.xml", + "application/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml"); + addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::COMMENTAUTHORS), + u"commentAuthors.xml"); + + pFS->startElementNS(XML_p, XML_cmAuthorLst, + FSNS(XML_xmlns, XML_p), getNamespaceURL(OOX_NS(ppt))); + + for (const AuthorsMap::value_type& i : maAuthors) + { + pFS->singleElementNS(XML_p, XML_cmAuthor, + XML_id, OString::number(i.second.nId), + XML_name, i.first, + XML_initials, lcl_GetInitials(i.first), + XML_lastIdx, OString::number(i.second.nLastIndex), + XML_clrIdx, OString::number(i.second.nId)); + } + + pFS->endElementNS(XML_p, XML_cmAuthorLst); +} + +sal_Int32 PowerPointExport::GetAuthorIdAndLastIndex(const OUString& sAuthor, sal_Int32& nLastIndex) +{ + if (maAuthors.count(sAuthor) <= 0) + { + struct AuthorComments aAuthorComments; + + aAuthorComments.nId = maAuthors.size(); + aAuthorComments.nLastIndex = 0; + + maAuthors[ sAuthor ] = aAuthorComments; + } + + nLastIndex = ++maAuthors[ sAuthor ].nLastIndex; + + return maAuthors[ sAuthor ].nId; +} + +void PowerPointExport::WritePresentationProps() +{ + Reference<XPresentationSupplier> xPresentationSupplier(mXModel, uno::UNO_QUERY); + if (!xPresentationSupplier.is()) + return; + + Reference<beans::XPropertySet> xPresentationProps(xPresentationSupplier->getPresentation(), + uno::UNO_QUERY); + bool bEndlessVal = xPresentationProps->getPropertyValue("IsEndless").get<bool>(); + bool bChangeManually = xPresentationProps->getPropertyValue("IsAutomatic").get<bool>(); + OUString sFirstPage = xPresentationProps->getPropertyValue("FirstPage").get<OUString>(); + OUString sCustomShow = xPresentationProps->getPropertyValue("CustomShow").get<OUString>(); + + FSHelperPtr pFS = openFragmentStreamWithSerializer( + "ppt/presProps.xml", + "application/vnd.openxmlformats-officedocument.presentationml.presProps+xml"); + + addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::PRESPROPS), u"presProps.xml"); + + pFS->startElementNS(XML_p, XML_presentationPr, PPRNMSS); + + pFS->startElementNS(XML_p, XML_showPr, XML_loop, sax_fastparser::UseIf("1", bEndlessVal), + XML_useTimings, sax_fastparser::UseIf("0", bChangeManually), + XML_showNarration, "1"); + + Reference<drawing::XDrawPagesSupplier> xDPS(mXModel, uno::UNO_QUERY_THROW); + Reference<drawing::XDrawPages> xDrawPages(xDPS->getDrawPages(), uno::UNO_SET_THROW); + if (!sFirstPage.isEmpty()) + { + sal_Int32 nStartSlide = 1; + sal_Int32 nEndSlide = xDrawPages->getCount(); + for (sal_Int32 i = 0; i < nEndSlide; i++) + { + Reference<drawing::XDrawPage> xDrawPage; + xDrawPages->getByIndex(i) >>= xDrawPage; + Reference<container::XNamed> xNamed(xDrawPage, uno::UNO_QUERY_THROW); + if (xNamed->getName() == sFirstPage) + { + nStartSlide = i + 1; + break; + } + } + + pFS->singleElementNS(XML_p, XML_sldRg, XML_st, OUString::number(nStartSlide), XML_end, + OUString::number(nEndSlide)); + } + + if (!sCustomShow.isEmpty()) + { + css::uno::Reference<css::presentation::XCustomPresentationSupplier> + XCustPresentationSupplier(mXModel, css::uno::UNO_QUERY_THROW); + css::uno::Reference<css::container::XNameContainer> mxCustShows; + mxCustShows = XCustPresentationSupplier->getCustomPresentations(); + const css::uno::Sequence<OUString> aNameSeq(mxCustShows->getElementNames()); + + sal_Int32 nCustShowIndex = 0; + for (sal_Int32 i = 0; i < aNameSeq.getLength(); i++) + { + if (aNameSeq[i] == sCustomShow) + { + nCustShowIndex = i; + break; + } + } + + pFS->singleElementNS(XML_p, XML_custShow, XML_id, OUString::number(nCustShowIndex)); + } + + pFS->endElementNS(XML_p, XML_showPr); + + pFS->endElementNS(XML_p, XML_presentationPr); +} + +bool PowerPointExport::WriteComments(sal_uInt32 nPageNum) +{ + Reference< XAnnotationAccess > xAnnotationAccess(mXDrawPage, uno::UNO_QUERY); + if (xAnnotationAccess.is()) + { + Reference< XAnnotationEnumeration > xAnnotationEnumeration(xAnnotationAccess->createAnnotationEnumeration()); + + if (xAnnotationEnumeration->hasMoreElements()) + { + FSHelperPtr pFS = openFragmentStreamWithSerializer( + "ppt/comments/comment" + OUString::number(nPageNum + 1) + ".xml", + "application/vnd.openxmlformats-officedocument.presentationml.comments+xml"); + + pFS->startElementNS(XML_p, XML_cmLst, + FSNS(XML_xmlns, XML_p), this->getNamespaceURL(OOX_NS(ppt))); + + do + { + Reference< XAnnotation > xAnnotation(xAnnotationEnumeration->nextElement()); + util::DateTime aDateTime(xAnnotation->getDateTime()); + RealPoint2D aRealPoint2D(xAnnotation->getPosition()); + Reference< XText > xText(xAnnotation->getTextRange()); + sal_Int32 nLastIndex; + sal_Int32 nId = GetAuthorIdAndLastIndex(xAnnotation->getAuthor(), nLastIndex); + char cDateTime[sizeof("-32768-65535-65535T65535:65535:65535.4294967295")]; + // reserve enough space for hypothetical max length + + snprintf(cDateTime, sizeof cDateTime, "%02" SAL_PRIdINT32 "-%02" SAL_PRIuUINT32 "-%02" SAL_PRIuUINT32 "T%02" SAL_PRIuUINT32 ":%02" SAL_PRIuUINT32 ":%02" SAL_PRIuUINT32 ".%09" SAL_PRIuUINT32, sal_Int32(aDateTime.Year), sal_uInt32(aDateTime.Month), sal_uInt32(aDateTime.Day), sal_uInt32(aDateTime.Hours), sal_uInt32(aDateTime.Minutes), sal_uInt32(aDateTime.Seconds), aDateTime.NanoSeconds); + + pFS->startElementNS(XML_p, XML_cm, + XML_authorId, OString::number(nId), + XML_dt, cDateTime, + XML_idx, OString::number(nLastIndex)); + + pFS->singleElementNS(XML_p, XML_pos, + XML_x, OString::number(std::round(convertMm100ToMasterUnit(aRealPoint2D.X * 100))), + XML_y, OString::number(std::round(convertMm100ToMasterUnit(aRealPoint2D.Y * 100)))); + + pFS->startElementNS(XML_p, XML_text); + pFS->write(xText->getString()); + pFS->endElementNS(XML_p, XML_text); + + pFS->endElementNS(XML_p, XML_cm); + + } + while (xAnnotationEnumeration->hasMoreElements()); + + pFS->endElementNS(XML_p, XML_cmLst); + + return true; + } + } + + return false; +} + +void PowerPointExport::WriteVBA() +{ + if (!mbPptm) + return; + + uno::Reference<document::XStorageBasedDocument> xStorageBasedDocument(getModel(), uno::UNO_QUERY); + if (!xStorageBasedDocument.is()) + return; + + uno::Reference<embed::XStorage> xDocumentStorage = xStorageBasedDocument->getDocumentStorage(); + OUString aMacrosName("_MS_VBA_Macros"); + if (!xDocumentStorage.is() || !xDocumentStorage->hasByName(aMacrosName)) + return; + + const sal_Int32 nOpenMode = embed::ElementModes::READ; + uno::Reference<io::XInputStream> xMacrosStream(xDocumentStorage->openStreamElement(aMacrosName, nOpenMode), uno::UNO_QUERY); + if (!xMacrosStream.is()) + return; + + uno::Reference<io::XOutputStream> xOutputStream = openFragmentStream("ppt/vbaProject.bin", "application/vnd.ms-office.vbaProject"); + comphelper::OStorageHelper::CopyInputToOutput(xMacrosStream, xOutputStream); + + // Write the relationship. + addRelation(mPresentationFS->getOutputStream(), oox::getRelationship(Relationship::VBAPROJECT), u"vbaProject.bin"); +} + +void PowerPointExport::WriteModifyVerifier() +{ + Sequence<PropertyValue> aInfo; + + try + { + Reference<lang::XMultiServiceFactory> xFactory(mXModel, UNO_QUERY); + Reference<XPropertySet> xDocSettings( + xFactory->createInstance("com.sun.star.document.Settings"), UNO_QUERY); + xDocSettings->getPropertyValue("ModifyPasswordInfo") >>= aInfo; + } + catch (const Exception&) + { + } + + if (aInfo.hasElements()) + { + OUString sAlgorithm, sSalt, sHash; + sal_Int32 nCount = 0; + for (auto& prop : aInfo) + { + if (prop.Name == "algorithm-name") + prop.Value >>= sAlgorithm; + else if (prop.Name == "salt") + prop.Value >>= sSalt; + else if (prop.Name == "iteration-count") + prop.Value >>= nCount; + else if (prop.Name == "hash") + prop.Value >>= sHash; + } + if (!sAlgorithm.isEmpty() && !sSalt.isEmpty() && !sHash.isEmpty()) + { + sal_Int32 nAlgorithmSid = 0; + if (sAlgorithm == "MD2") + nAlgorithmSid = 1; + else if (sAlgorithm == "MD4") + nAlgorithmSid = 2; + else if (sAlgorithm == "MD5") + nAlgorithmSid = 3; + else if (sAlgorithm == "SHA-1") + nAlgorithmSid = 4; + else if (sAlgorithm == "MAC") + nAlgorithmSid = 5; + else if (sAlgorithm == "RIPEMD") + nAlgorithmSid = 6; + else if (sAlgorithm == "RIPEMD-160") + nAlgorithmSid = 7; + else if (sAlgorithm == "HMAC") + nAlgorithmSid = 9; + else if (sAlgorithm == "SHA-256") + nAlgorithmSid = 12; + else if (sAlgorithm == "SHA-384") + nAlgorithmSid = 13; + else if (sAlgorithm == "SHA-512") + nAlgorithmSid = 14; + + if (nAlgorithmSid != 0) + mPresentationFS->singleElementNS(XML_p, XML_modifyVerifier, + XML_cryptProviderType, "rsaAES", + XML_cryptAlgorithmClass, "hash", + XML_cryptAlgorithmType, "typeAny", + XML_cryptAlgorithmSid, OString::number(nAlgorithmSid).getStr(), + XML_spinCount, OString::number(nCount).getStr(), + XML_saltData, sSalt.toUtf8().getStr(), + XML_hashData, sHash.toUtf8().getStr()); + } + } +} + +void PowerPointExport::ImplWriteSlide(sal_uInt32 nPageNum, sal_uInt32 nMasterNum, sal_uInt16 /* nMode */, + bool bHasBackground, Reference< XPropertySet > const& aXBackgroundPropSet) +{ + SAL_INFO("sd.eppt", "write slide: " << nPageNum << "\n----------------"); + + // slides list + if (nPageNum == 0) + mPresentationFS->startElementNS(XML_p, XML_sldIdLst); + + // add explicit relation of presentation to this slide + OUString sRelId = addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDE), + OUStringConcatenation("slides/slide" + OUString::number(nPageNum + 1) +".xml")); + + mPresentationFS->singleElementNS(XML_p, XML_sldId, + XML_id, OString::number(GetNewSlideId()), + FSNS(XML_r, XML_id), sRelId); + + maRelId.push_back(sRelId); + + if (nPageNum == mnPages - 1) + mPresentationFS->endElementNS(XML_p, XML_sldIdLst); + + FSHelperPtr pFS = openFragmentStreamWithSerializer( + "ppt/slides/slide" + OUString::number(nPageNum + 1) + ".xml", + "application/vnd.openxmlformats-officedocument.presentationml.slide+xml"); + + if (mpSlidesFSArray.size() < mnPages) + mpSlidesFSArray.resize(mnPages); + mpSlidesFSArray[ nPageNum ] = pFS; + + const char* pShow = nullptr; + const char* pShowMasterShape = nullptr; + + if (ImplGetPropertyValue(mXPagePropSet, "Visible")) + { + bool bShow(false); + if ((mAny >>= bShow) && !bShow) + pShow = "0"; + } + + if (ImplGetPropertyValue(mXPagePropSet, "IsBackgroundObjectsVisible")) + { + bool bShowMasterShape(false); + if ((mAny >>= bShowMasterShape) && !bShowMasterShape) + pShowMasterShape = "0"; + } + + pFS->startElementNS(XML_p, XML_sld, PNMSS, XML_show, pShow, XML_showMasterSp, pShowMasterShape); + + pFS->startElementNS(XML_p, XML_cSld); + + // background + if (bHasBackground) + { + ImplWriteBackground(pFS, aXBackgroundPropSet); + } + + WriteShapeTree(pFS, NORMAL, false); + + pFS->endElementNS(XML_p, XML_cSld); + + WriteTransition(pFS); + WriteAnimations(pFS, mXDrawPage, *this); + + pFS->endElementNS(XML_p, XML_sld); + + // add implicit relation to slide layout + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDELAYOUT), + OUStringConcatenation("../slideLayouts/slideLayout" + + OUString::number(GetLayoutFileId(GetPPTXLayoutId(GetLayoutOffset(mXPagePropSet)), nMasterNum)) + + ".xml")); + + if (WriteComments(nPageNum)) + // add implicit relation to slide comments + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::COMMENTS), + OUStringConcatenation("../comments/comment" + OUString::number(nPageNum + 1) + ".xml")); + + SAL_INFO("sd.eppt", "----------------"); +} + +void PowerPointExport::ImplWriteNotes(sal_uInt32 nPageNum) +{ + if (!mbCreateNotes || !ContainsOtherShapeThanPlaceholders()) + return; + + SAL_INFO("sd.eppt", "write Notes " << nPageNum << "\n----------------"); + + FSHelperPtr pFS = openFragmentStreamWithSerializer( + "ppt/notesSlides/notesSlide" + + OUString::number(nPageNum + 1) + + ".xml", + "application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml"); + + pFS->startElementNS(XML_p, XML_notes, PNMSS); + + pFS->startElementNS(XML_p, XML_cSld); + + WriteShapeTree(pFS, NOTICE, false); + + pFS->endElementNS(XML_p, XML_cSld); + + pFS->endElementNS(XML_p, XML_notes); + + // add implicit relation to slide + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDE), + OUStringConcatenation("../slides/slide" + OUString::number(nPageNum + 1) + ".xml")); + + // add slide implicit relation to notes + if (nPageNum < mpSlidesFSArray.size()) + addRelation(mpSlidesFSArray[ nPageNum ]->getOutputStream(), + oox::getRelationship(Relationship::NOTESSLIDE), + OUStringConcatenation("../notesSlides/notesSlide" + OUString::number(nPageNum + 1) + ".xml")); + + // add implicit relation to notes master + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::NOTESMASTER), + u"../notesMasters/notesMaster1.xml"); + + SAL_INFO("sd.eppt", "-----------------"); +} + +void PowerPointExport::AddLayoutIdAndRelation(const FSHelperPtr& pFS, sal_Int32 nLayoutFileId) +{ + // add implicit relation of slide master to slide layout + OUString sRelId = addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDELAYOUT), + OUStringConcatenation("../slideLayouts/slideLayout" + OUString::number(nLayoutFileId) + ".xml")); + + pFS->singleElementNS(XML_p, XML_sldLayoutId, + XML_id, OString::number(GetNewSlideMasterId()), + FSNS(XML_r, XML_id), sRelId); +} + +void PowerPointExport::ImplWriteSlideMaster(sal_uInt32 nPageNum, Reference< XPropertySet > const& aXBackgroundPropSet) +{ + SAL_INFO("sd.eppt", "write master slide: " << nPageNum << "\n--------------"); + + // slides list + if (nPageNum == 0) + mPresentationFS->startElementNS(XML_p, XML_sldMasterIdLst); + + OUString sRelId = addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDEMASTER), + OUStringConcatenation("slideMasters/slideMaster" + OUString::number(nPageNum + 1) + ".xml")); + + mPresentationFS->singleElementNS(XML_p, XML_sldMasterId, + XML_id, OString::number(GetNewSlideMasterId()), + FSNS(XML_r, XML_id), sRelId); + + if (nPageNum == mnMasterPages - 1) + mPresentationFS->endElementNS(XML_p, XML_sldMasterIdLst); + + FSHelperPtr pFS = + openFragmentStreamWithSerializer("ppt/slideMasters/slideMaster" + + OUString::number(nPageNum + 1) + ".xml", + "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"); + + SdrPage* pMasterPage = SdPage::getImplementation(mXDrawPage); + svx::Theme* pTheme = nullptr; + if (pMasterPage) + { + pTheme = pMasterPage->getSdrPageProperties().GetTheme(); + } + + // write theme per master + WriteTheme(nPageNum, pTheme); + + // add implicit relation to the presentation theme + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::THEME), + OUStringConcatenation("../theme/theme" + OUString::number(nPageNum + 1) + ".xml")); + + pFS->startElementNS(XML_p, XML_sldMaster, PNMSS); + + pFS->startElementNS(XML_p, XML_cSld); + + if (aXBackgroundPropSet) + ImplWriteBackground(pFS, aXBackgroundPropSet); + WriteShapeTree(pFS, MASTER, true); + + pFS->endElementNS(XML_p, XML_cSld); + + // color map - now it uses colors from hardcoded theme, once we eventually generate theme, this might need update + pFS->singleElementNS(XML_p, XML_clrMap, + XML_bg1, "lt1", + XML_bg2, "lt2", + XML_tx1, "dk1", + XML_tx2, "dk2", + XML_accent1, "accent1", + XML_accent2, "accent2", + XML_accent3, "accent3", + XML_accent4, "accent4", + XML_accent5, "accent5", + XML_accent6, "accent6", + XML_hlink, "hlink", + XML_folHlink, "folHlink"); + + // use master's id type as they have same range, mso does that as well + pFS->startElementNS(XML_p, XML_sldLayoutIdLst); + + for (int i = 0; i < LAYOUT_SIZE; i++) + { + sal_Int32 nLayoutFileId = GetLayoutFileId(i, nPageNum); + if (nLayoutFileId > 0) + { + AddLayoutIdAndRelation(pFS, nLayoutFileId); + } + else + { + ImplWritePPTXLayout(i, nPageNum); + AddLayoutIdAndRelation(pFS, GetLayoutFileId(i, nPageNum)); + } + } + + pFS->endElementNS(XML_p, XML_sldLayoutIdLst); + + pFS->endElementNS(XML_p, XML_sldMaster); + + SAL_INFO("sd.eppt", "----------------"); +} + +sal_Int32 PowerPointExport::GetLayoutFileId(sal_Int32 nOffset, sal_uInt32 nMasterNum) +{ + SAL_INFO("sd.eppt", "GetLayoutFileId offset: " << nOffset << " master: " << nMasterNum); + if (mLayoutInfo[ nOffset ].mnFileIdArray.size() <= nMasterNum) + return 0; + + return mLayoutInfo[ nOffset ].mnFileIdArray[ nMasterNum ]; +} + +void PowerPointExport::ImplWritePPTXLayout(sal_Int32 nOffset, sal_uInt32 nMasterNum) +{ + SAL_INFO("sd.eppt", "write layout: " << nOffset); + + Reference< drawing::XDrawPagesSupplier > xDPS(getModel(), uno::UNO_QUERY); + Reference< drawing::XDrawPages > xDrawPages = xDPS->getDrawPages(); + Reference< drawing::XDrawPage > xSlide = xDrawPages->insertNewByIndex(xDrawPages->getCount()); + +#ifdef DEBUG + if (xSlide.is()) + printf("new page created\n"); +#endif + + Reference< beans::XPropertySet > xPropSet(xSlide, uno::UNO_QUERY); + xPropSet->setPropertyValue("Layout", Any(short(aLayoutInfo[ nOffset ].nType))); +#if OSL_DEBUG_LEVEL > 1 + dump_pset(xPropSet); +#endif + mXPagePropSet.set(xSlide, UNO_QUERY); + mXShapes = xSlide; + + if (mLayoutInfo[ nOffset ].mnFileIdArray.size() < mnMasterPages) + { + mLayoutInfo[ nOffset ].mnFileIdArray.resize(mnMasterPages); + } + + if (mLayoutInfo[ nOffset ].mnFileIdArray[ nMasterNum ] != 0) + return; + + FSHelperPtr pFS + = openFragmentStreamWithSerializer("ppt/slideLayouts/slideLayout" + + OUString::number(mnLayoutFileIdMax) + ".xml", + "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"); + + // add implicit relation of slide layout to slide master + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDEMASTER), + OUStringConcatenation("../slideMasters/slideMaster" + OUString::number(nMasterNum + 1) + ".xml")); + + pFS->startElementNS(XML_p, XML_sldLayout, + PNMSS, + XML_type, aLayoutInfo[ nOffset ].sType, + XML_preserve, "1"); + + pFS->startElementNS(XML_p, XML_cSld, + XML_name, aLayoutInfo[ nOffset ].sName); + //pFS->write( MINIMAL_SPTREE ); // TODO: write actual shape tree + WriteShapeTree(pFS, LAYOUT, true); + + pFS->endElementNS(XML_p, XML_cSld); + + pFS->endElementNS(XML_p, XML_sldLayout); + + mLayoutInfo[ nOffset ].mnFileIdArray[ nMasterNum ] = mnLayoutFileIdMax; + + mnLayoutFileIdMax ++; + + xDrawPages->remove(xSlide); +} + +void PowerPointExport::WriteShapeTree(const FSHelperPtr& pFS, PageType ePageType, bool bMaster) +{ + PowerPointShapeExport aDML(pFS, &maShapeMap, this); + aDML.SetMaster(bMaster); + aDML.SetPageType(ePageType); + aDML.SetBackgroundDark(mbIsBackgroundDark); + + pFS->startElementNS(XML_p, XML_spTree); + pFS->write(MAIN_GROUP); + + ResetGroupTable(mXShapes->getCount()); + + while (GetNextGroupEntry()) + { + + sal_uInt32 nGroups = GetGroupsClosed(); + for (sal_uInt32 i = 0; i < nGroups; i++) + { + SAL_INFO("sd.eppt", "leave group"); + } + + if (GetShapeByIndex(GetCurrentGroupIndex(), true)) + { + SAL_INFO("sd.eppt", "mType: " << mType); + const SdrObjGroup* pDiagramCandidate(dynamic_cast<const SdrObjGroup*>(SdrObject::getSdrObjectFromXShape(mXShape))); + const bool bIsDiagram(nullptr != pDiagramCandidate && pDiagramCandidate->isDiagram()); + + if (bIsDiagram) + WriteDiagram(pFS, aDML, mXShape, mnDiagramId++); + else + aDML.WriteShape(mXShape); + } + } + + if ( ePageType == NORMAL || ePageType == LAYOUT ) + WritePlaceholderReferenceShapes(aDML, ePageType); + pFS->endElementNS(XML_p, XML_spTree); +} + +ShapeExport& PowerPointShapeExport::WritePageShape(const Reference< XShape >& xShape, PageType ePageType, bool bPresObj) +{ + if ((ePageType == NOTICE && bPresObj) || ePageType == LAYOUT || ePageType == MASTER) + return WritePlaceholderShape(xShape, SlideImage); + + return WriteTextShape(xShape); +} + +bool PowerPointShapeExport::WritePlaceholder(const Reference< XShape >& xShape, PlaceholderType ePlaceholder, bool bMaster) +{ + SAL_INFO("sd.eppt", "WritePlaceholder " << bMaster << " " << ShapeExport::NonEmptyText(xShape)); + if (!xShape) + return false; + try + { + Reference<XPropertySet> xShapeProps(xShape, UNO_QUERY); + if (xShapeProps->getPropertyValue("IsPresentationObject").get<bool>()) + { + WritePlaceholderShape(xShape, ePlaceholder); + + return true; + } + } + catch (Exception&) + { + return false; + } + return false; +} + +ShapeExport& PowerPointShapeExport::WritePlaceholderShape(const Reference< XShape >& xShape, PlaceholderType ePlaceholder) +{ + Reference<XPropertySet> xProps(xShape, UNO_QUERY); + bool bUseBackground(false); + if (xProps.is() && xProps->getPropertySetInfo()->hasPropertyByName("FillUseSlideBackground")) + xProps->getPropertyValue("FillUseSlideBackground") >>= bUseBackground; + + if (bUseBackground) + mpFS->startElementNS(XML_p, XML_sp, XML_useBgFill, "1"); + else + mpFS->startElementNS(XML_p, XML_sp); + + // non visual shape properties + mpFS->startElementNS(XML_p, XML_nvSpPr); + const OString aPlaceholderID("PlaceHolder " + OString::number(mnShapeIdMax++)); + WriteNonVisualDrawingProperties(xShape, aPlaceholderID.getStr()); + mpFS->startElementNS(XML_p, XML_cNvSpPr); + mpFS->singleElementNS(XML_a, XML_spLocks, XML_noGrp, "1"); + mpFS->endElementNS(XML_p, XML_cNvSpPr); + mpFS->startElementNS(XML_p, XML_nvPr); + + bool bUsePlaceholderIndex + = ePlaceholder == Footer || ePlaceholder == DateAndTime || ePlaceholder == SlideNumber; + const char* pType = getPlaceholderTypeName(ePlaceholder); + + SAL_INFO("sd.eppt", "write placeholder " << pType); + if (bUsePlaceholderIndex) + { + mpFS->singleElementNS( + XML_p, XML_ph, XML_type, pType, XML_idx, + OString::number( + static_cast<PowerPointExport*>(GetFB())->CreateNewPlaceholderIndex(xShape))); + } + else + { + if ((mePageType == PageType::LAYOUT || mePageType == PageType::NORMAL) + && ePlaceholder == Outliner) + mpFS->singleElementNS(XML_p, XML_ph); + else + mpFS->singleElementNS(XML_p, XML_ph, XML_type, pType); + } + mpFS->endElementNS(XML_p, XML_nvPr); + mpFS->endElementNS(XML_p, XML_nvSpPr); + + // visual shape properties + mpFS->startElementNS(XML_p, XML_spPr); + WriteShapeTransformation(xShape, XML_a); + WritePresetShape("rect"); + if (xProps.is()) + { + WriteBlipFill(xProps, "Graphic"); + // Do not forget to export the visible properties. + WriteFill( xProps ); + WriteOutline( xProps ); + WriteShapeEffects( xProps ); + + bool bHas3DEffectinShape = false; + uno::Sequence<beans::PropertyValue> grabBag; + if (xProps->getPropertySetInfo()->hasPropertyByName("InteropGrabBag")) + xProps->getPropertyValue("InteropGrabBag") >>= grabBag; + + for (auto const& it : std::as_const(grabBag)) + if (it.Name == "3DEffectProperties") + bHas3DEffectinShape = true; + + if( bHas3DEffectinShape) + Write3DEffects( xProps, /*bIsText=*/false ); + } + mpFS->endElementNS(XML_p, XML_spPr); + + WriteTextBox(xShape, XML_p, /*bWritePropertiesAsLstStyles=*/bUsePlaceholderIndex); + + mpFS->endElementNS(XML_p, XML_sp); + + return *this; +} + +ShapeExport& PowerPointShapeExport::WritePlaceholderReferenceShape( + PlaceholderType ePlaceholder, sal_Int32 nReferencedPlaceholderIdx, PageType ePageType, + const Reference<XPropertySet>& rXPagePropSet) +{ + mpFS->startElementNS(XML_p, XML_sp); + + // non visual shape properties + mpFS->startElementNS(XML_p, XML_nvSpPr); + const OString aPlaceholderID("PlaceHolder " + OString::number(mnShapeIdMax++)); + GetFS()->singleElementNS(XML_p, XML_cNvPr, XML_id, OString::number(mnShapeIdMax), XML_name, + aPlaceholderID.getStr()); + + mpFS->startElementNS(XML_p, XML_cNvSpPr); + mpFS->singleElementNS(XML_a, XML_spLocks, XML_noGrp, "1"); + mpFS->endElementNS(XML_p, XML_cNvSpPr); + mpFS->startElementNS(XML_p, XML_nvPr); + + const char* pType = getPlaceholderTypeName(ePlaceholder); + mpFS->singleElementNS(XML_p, XML_ph, XML_type, pType, XML_idx, + OString::number(nReferencedPlaceholderIdx)); + mpFS->endElementNS(XML_p, XML_nvPr); + mpFS->endElementNS(XML_p, XML_nvSpPr); + + // visual shape properties + mpFS->startElementNS(XML_p, XML_spPr); + mpFS->endElementNS(XML_p, XML_spPr); + + WritePlaceholderReferenceTextBody(ePlaceholder, ePageType, rXPagePropSet); + + mpFS->endElementNS(XML_p, XML_sp); + + return *this; +} + +ShapeExport& PowerPointShapeExport::WritePlaceholderReferenceTextBody( + PlaceholderType ePlaceholder, PageType ePageType, const Reference<XPropertySet> xPagePropSet) +{ + mpFS->startElementNS(XML_p, XML_txBody); + mpFS->singleElementNS(XML_a, XML_bodyPr); + mpFS->startElementNS(XML_a, XML_p); + + switch (ePlaceholder) + { + case Header: + break; + case Footer: + { + OUString aFooterText; + if (ePageType == LAYOUT) + { + aFooterText = "Footer"; + } + else + { + xPagePropSet->getPropertyValue("FooterText") >>= aFooterText; + } + mpFS->startElementNS(XML_a, XML_r); + mpFS->startElementNS(XML_a, XML_t); + mpFS->writeEscaped(aFooterText); + mpFS->endElementNS(XML_a, XML_t); + mpFS->endElementNS(XML_a, XML_r); + break; + } + case SlideNumber: + { + OUString aSlideNum; + sal_Int32 nSlideNum = 0; + if (ePageType == LAYOUT) + { + aSlideNum = "<#>"; + } + else + { + xPagePropSet->getPropertyValue("Number") >>= nSlideNum; + aSlideNum = OUString::number(nSlideNum); + } + OString aUUID(comphelper::xml::generateGUIDString()); + mpFS->startElementNS(XML_a, XML_fld, XML_id, aUUID.getStr(), XML_type, "slidenum"); + mpFS->startElementNS(XML_a, XML_t); + mpFS->writeEscaped(aSlideNum); + mpFS->endElementNS(XML_a, XML_t); + mpFS->endElementNS(XML_a, XML_fld); + break; + } + case DateAndTime: + { + OUString aDateTimeType = "datetime1"; + bool bIsDateTimeFixed = false; + xPagePropSet->getPropertyValue("IsDateTimeFixed") >>= bIsDateTimeFixed; + + OUString aDateTimeText = "Date"; + const LanguageTag& rLanguageTag = Application::GetSettings().GetLanguageTag(); + + if(ePageType != LAYOUT && !bIsDateTimeFixed) + { + sal_Int32 nDateTimeFormat = 0; + xPagePropSet->getPropertyValue("DateTimeFormat") >>= nDateTimeFormat; + + // 4 LSBs represent the date + SvxDateFormat eDate = static_cast<SvxDateFormat>(nDateTimeFormat & 0x0f); + // the 4 bits after the date bits represent the time + SvxTimeFormat eTime = static_cast<SvxTimeFormat>(nDateTimeFormat >> 4); + aDateTimeType = GetDatetimeTypeFromDateTime(eDate, eTime); + + if (aDateTimeType == "datetime") + aDateTimeType = "datetime1"; + + ::DateTime aDateTime( ::DateTime::SYSTEM ); + + aDateTimeText = SvxDateTimeField::GetFormatted( + aDateTime, aDateTime, eDate, + eTime, *(SD_MOD()->GetNumberFormatter()), + rLanguageTag.getLanguageType()); + } + + if(!bIsDateTimeFixed) + { + OString aUUID(comphelper::xml::generateGUIDString()); + mpFS->startElementNS(XML_a, XML_fld, XML_id, aUUID.getStr(), XML_type, aDateTimeType); + } + else + { + xPagePropSet->getPropertyValue("DateTimeText") >>= aDateTimeText; + mpFS->startElementNS(XML_a, XML_r); + } + + mpFS->startElementNS(XML_a, XML_rPr, XML_lang, rLanguageTag.getBcp47MS()); + mpFS->endElementNS(XML_a, XML_rPr); + + mpFS->startElementNS(XML_a, XML_t); + mpFS->writeEscaped(aDateTimeText); + mpFS->endElementNS(XML_a, XML_t); + + mpFS->endElementNS(XML_a, bIsDateTimeFixed ? XML_r : XML_fld); + break; + } + default: + SAL_INFO("sd.eppt", "warning: no defined textbody for referenced placeholder type: " + << ePlaceholder); + } + mpFS->endElementNS(XML_a, XML_p); + mpFS->endElementNS(XML_p, XML_txBody); + + return *this; +} + +#define SYS_COLOR_SCHEMES " <a:dk1>\ + <a:sysClr val=\"windowText\" lastClr=\"000000\"/>\ + </a:dk1>\ + <a:lt1>\ + <a:sysClr val=\"window\" lastClr=\"FFFFFF\"/>\ + </a:lt1>" + +#define MINIMAL_THEME " <a:fontScheme name=\"Office\">\ + <a:majorFont>\ + <a:latin typeface=\"Arial\"/>\ + <a:ea typeface=\"DejaVu Sans\"/>\ + <a:cs typeface=\"DejaVu Sans\"/>\ + </a:majorFont>\ + <a:minorFont>\ + <a:latin typeface=\"Arial\"/>\ + <a:ea typeface=\"DejaVu Sans\"/>\ + <a:cs typeface=\"DejaVu Sans\"/>\ + </a:minorFont>\ + </a:fontScheme>\ + <a:fmtScheme name=\"Office\">\ + <a:fillStyleLst>\ + <a:solidFill>\ + <a:schemeClr val=\"phClr\"/>\ + </a:solidFill>\ + <a:gradFill rotWithShape=\"1\">\ + <a:gsLst>\ + <a:gs pos=\"0\">\ + <a:schemeClr val=\"phClr\">\ + <a:tint val=\"50000\"/>\ + <a:satMod val=\"300000\"/>\ + </a:schemeClr>\ + </a:gs>\ + <a:gs pos=\"35000\">\ + <a:schemeClr val=\"phClr\">\ + <a:tint val=\"37000\"/>\ + <a:satMod val=\"300000\"/>\ + </a:schemeClr>\ + </a:gs>\ + <a:gs pos=\"100000\">\ + <a:schemeClr val=\"phClr\">\ + <a:tint val=\"15000\"/>\ + <a:satMod val=\"350000\"/>\ + </a:schemeClr>\ + </a:gs>\ + </a:gsLst>\ + <a:lin ang=\"16200000\" scaled=\"1\"/>\ + </a:gradFill>\ + <a:gradFill rotWithShape=\"1\">\ + <a:gsLst>\ + <a:gs pos=\"0\">\ + <a:schemeClr val=\"phClr\">\ + <a:shade val=\"51000\"/>\ + <a:satMod val=\"130000\"/>\ + </a:schemeClr>\ + </a:gs>\ + <a:gs pos=\"80000\">\ + <a:schemeClr val=\"phClr\">\ + <a:shade val=\"93000\"/>\ + <a:satMod val=\"130000\"/>\ + </a:schemeClr>\ + </a:gs>\ + <a:gs pos=\"100000\">\ + <a:schemeClr val=\"phClr\">\ + <a:shade val=\"94000\"/>\ + <a:satMod val=\"135000\"/>\ + </a:schemeClr>\ + </a:gs>\ + </a:gsLst>\ + <a:lin ang=\"16200000\" scaled=\"0\"/>\ + </a:gradFill>\ + </a:fillStyleLst>\ + <a:lnStyleLst>\ + <a:ln w=\"6350\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\">\ + <a:solidFill>\ + <a:schemeClr val=\"phClr\">\ + <a:shade val=\"95000\"/>\ + <a:satMod val=\"105000\"/>\ + </a:schemeClr>\ + </a:solidFill>\ + <a:prstDash val=\"solid\"/>\ + <a:miter/>\ + </a:ln>\ + <a:ln w=\"12700\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\">\ + <a:solidFill>\ + <a:schemeClr val=\"phClr\"/>\ + </a:solidFill>\ + <a:prstDash val=\"solid\"/>\ + <a:miter/>\ + </a:ln>\ + <a:ln w=\"19050\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\">\ + <a:solidFill>\ + <a:schemeClr val=\"phClr\"/>\ + </a:solidFill>\ + <a:prstDash val=\"solid\"/>\ + <a:miter/>\ + </a:ln>\ + </a:lnStyleLst>\ + <a:effectStyleLst>\ + <a:effectStyle>\ + <a:effectLst/>\ + </a:effectStyle>\ + <a:effectStyle>\ + <a:effectLst/>\ + </a:effectStyle>\ + <a:effectStyle>\ + <a:effectLst>\ + <a:outerShdw blurRad=\"40000\" dist=\"23000\" dir=\"5400000\" rotWithShape=\"0\">\ + <a:srgbClr val=\"000000\">\ + <a:alpha val=\"35000\"/>\ + </a:srgbClr>\ + </a:outerShdw>\ + </a:effectLst>\ + <a:scene3d>\ + <a:camera prst=\"orthographicFront\">\ + <a:rot lat=\"0\" lon=\"0\" rev=\"0\"/>\ + </a:camera>\ + <a:lightRig rig=\"threePt\" dir=\"t\">\ + <a:rot lat=\"0\" lon=\"0\" rev=\"1200000\"/>\ + </a:lightRig>\ + </a:scene3d>\ + <a:sp3d>\ + <a:bevelT w=\"63500\" h=\"25400\"/>\ + </a:sp3d>\ + </a:effectStyle>\ + </a:effectStyleLst>\ + <a:bgFillStyleLst>\ + <a:solidFill>\ + <a:schemeClr val=\"phClr\"/>\ + </a:solidFill>\ + <a:gradFill rotWithShape=\"1\">\ + <a:gsLst>\ + <a:gs pos=\"0\">\ + <a:schemeClr val=\"phClr\">\ + <a:tint val=\"40000\"/>\ + <a:satMod val=\"350000\"/>\ + </a:schemeClr>\ + </a:gs>\ + <a:gs pos=\"40000\">\ + <a:schemeClr val=\"phClr\">\ + <a:tint val=\"45000\"/>\ + <a:shade val=\"99000\"/>\ + <a:satMod val=\"350000\"/>\ + </a:schemeClr>\ + </a:gs>\ + <a:gs pos=\"100000\">\ + <a:schemeClr val=\"phClr\">\ + <a:shade val=\"20000\"/>\ + <a:satMod val=\"255000\"/>\ + </a:schemeClr>\ + </a:gs>\ + </a:gsLst>\ + <a:path path=\"circle\">\ + <a:fillToRect l=\"50000\" t=\"-80000\" r=\"50000\" b=\"180000\"/>\ + </a:path>\ + </a:gradFill>\ + <a:gradFill rotWithShape=\"1\">\ + <a:gsLst>\ + <a:gs pos=\"0\">\ + <a:schemeClr val=\"phClr\">\ + <a:tint val=\"80000\"/>\ + <a:satMod val=\"300000\"/>\ + </a:schemeClr>\ + </a:gs>\ + <a:gs pos=\"100000\">\ + <a:schemeClr val=\"phClr\">\ + <a:shade val=\"30000\"/>\ + <a:satMod val=\"200000\"/>\ + </a:schemeClr>\ + </a:gs>\ + </a:gsLst>\ + <a:path path=\"circle\">\ + <a:fillToRect l=\"50000\" t=\"50000\" r=\"50000\" b=\"50000\"/>\ + </a:path>\ + </a:gradFill>\ + </a:bgFillStyleLst>\ + </a:fmtScheme>" + +void PowerPointExport::WriteDefaultColorSchemes(const FSHelperPtr& pFS) +{ + for (int nId = PredefinedClrSchemeId::dk2; nId != PredefinedClrSchemeId::Count; nId++) + { + OUString sName = PredefinedClrNames[static_cast<PredefinedClrSchemeId>(nId)]; + sal_Int32 nColor = 0; + + switch (nId) + { + case PredefinedClrSchemeId::dk2: + nColor = 0x1F497D; + break; + case PredefinedClrSchemeId::lt2: + nColor = 0xEEECE1; + break; + case PredefinedClrSchemeId::accent1: + nColor = 0x4F81BD; + break; + case PredefinedClrSchemeId::accent2: + nColor = 0xC0504D; + break; + case PredefinedClrSchemeId::accent3: + nColor = 0x9BBB59; + break; + case PredefinedClrSchemeId::accent4: + nColor = 0x8064A2; + break; + case PredefinedClrSchemeId::accent5: + nColor = 0x4BACC6; + break; + case PredefinedClrSchemeId::accent6: + nColor = 0xF79646; + break; + case PredefinedClrSchemeId::hlink: + nColor = 0x0000FF; + break; + case PredefinedClrSchemeId::folHlink: + nColor = 0x800080; + break; + } + + OUString sOpenColorScheme = "<a:" + sName + ">"; + pFS->write(sOpenColorScheme); + + pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(nColor)); + + OUString sCloseColorScheme = "</a:" + sName + ">"; + pFS->write(sCloseColorScheme); + } +} + +bool PowerPointExport::WriteColorSets(const FSHelperPtr& pFS, svx::Theme* pTheme) +{ + static std::map<PredefinedClrSchemeId, sal_Int32> aPredefinedClrTokens = + { + { dk1, XML_dk1 }, + { lt1, XML_lt1 }, + { dk2, XML_dk2 }, + { lt2, XML_lt2 }, + { accent1, XML_accent1 }, + { accent2, XML_accent2 }, + { accent3, XML_accent3 }, + { accent4, XML_accent4 }, + { accent5, XML_accent5 }, + { accent6, XML_accent6 }, + { hlink, XML_hlink }, + { folHlink, XML_folHlink } + }; + + if (!pTheme) + { + return false; + } + + svx::ColorSet* pColorSet = pTheme->GetColorSet(); + if (!pColorSet) + { + return false; + } + + for (int nId = PredefinedClrSchemeId::dk1; nId < PredefinedClrSchemeId::Count; nId++) + { + sal_Int32 nToken = aPredefinedClrTokens[static_cast<PredefinedClrSchemeId>(nId)]; + pFS->startElementNS(XML_a, nToken); + pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(static_cast<sal_Int32>(pColorSet->getColor(nId)))); + pFS->endElementNS(XML_a, nToken); + } + + return true; +} + +bool PowerPointExport::WriteColorSchemes(const FSHelperPtr& pFS, const OUString& rThemePath) +{ + try + { + uno::Reference<beans::XPropertySet> xDocProps(getModel(), uno::UNO_QUERY); + if (xDocProps.is()) + { + uno::Reference<beans::XPropertySetInfo> xPropsInfo = xDocProps->getPropertySetInfo(); + + static const OUStringLiteral aGrabBagPropName = u"InteropGrabBag"; + if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(aGrabBagPropName)) + { + comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue(aGrabBagPropName)); + uno::Sequence<beans::PropertyValue> aCurrentTheme; + + aGrabBag.getValue(rThemePath) >>= aCurrentTheme; + + if (!aCurrentTheme.hasElements()) + return false; + + // Order is important + for (int nId = PredefinedClrSchemeId::dk2; nId != PredefinedClrSchemeId::Count; nId++) + { + OUString sName = PredefinedClrNames[static_cast<PredefinedClrSchemeId>(nId)]; + sal_Int32 nColor = 0; + + for (auto aIt = std::cbegin(aCurrentTheme); aIt != std::cend(aCurrentTheme); aIt++) + { + if (aIt->Name == sName) + { + aIt->Value >>= nColor; + break; + } + } + + OUString sOpenColorScheme ="<a:" + sName + ">"; + pFS->write(sOpenColorScheme); + + pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(nColor)); + + OUString sCloseColorScheme = "</a:" + sName + ">"; + pFS->write(sCloseColorScheme); + } + + // TODO: write complete color schemes & only if successful, protection against partial export + return true; + } + } + } + catch (const uno::Exception&) + { + SAL_WARN("writerfilter", "Failed to save documents grab bag"); + } + + return false; +} + +void PowerPointExport::WriteTheme(sal_Int32 nThemeNum, svx::Theme* pTheme) +{ + OUString sThemePath = "ppt/theme/theme" + OUString::number(nThemeNum + 1) + ".xml"; + + FSHelperPtr pFS = openFragmentStreamWithSerializer(sThemePath, + "application/vnd.openxmlformats-officedocument.theme+xml"); + + OUString aThemeName("Office Theme"); + if (pTheme) + { + aThemeName = pTheme->GetName(); + } + pFS->startElementNS(XML_a, XML_theme, + FSNS(XML_xmlns, XML_a), this->getNamespaceURL(OOX_NS(dml)), + XML_name, aThemeName); + + pFS->startElementNS(XML_a, XML_themeElements); + OUString aColorSchemeName("Office"); + if (pTheme) + { + svx::ColorSet* pColorSet = pTheme->GetColorSet(); + if (pColorSet) + { + aColorSchemeName = pColorSet->getName(); + } + } + pFS->startElementNS(XML_a, XML_clrScheme, XML_name, aColorSchemeName); + + if (!WriteColorSets(pFS, pTheme)) + { + pFS->write(SYS_COLOR_SCHEMES); + if (!WriteColorSchemes(pFS, sThemePath)) + { + // if style is not defined, try to use first one + if (!WriteColorSchemes(pFS, "ppt/theme/theme1.xml")) + { + // color schemes are required - use default values + WriteDefaultColorSchemes(pFS); + } + } + } + + pFS->endElementNS(XML_a, XML_clrScheme); + + // export remaining part + pFS->write(MINIMAL_THEME); + + pFS->endElementNS(XML_a, XML_themeElements); + pFS->endElementNS(XML_a, XML_theme); +} + +bool PowerPointExport::ImplCreateDocument() +{ + mbCreateNotes = false; + + for (sal_uInt32 i = 0; i < mnPages; i++) + { + if (!GetPageByIndex(i, NOTICE)) + return false; + + if (ContainsOtherShapeThanPlaceholders()) + { + mbCreateNotes = true; + break; + } + } + + return true; +} + +void PowerPointExport::WriteNotesMaster() +{ + SAL_INFO("sd.eppt", "write Notes master\n---------------"); + + mPresentationFS->startElementNS(XML_p, XML_notesMasterIdLst); + + OUString sRelId = addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::NOTESMASTER), + u"notesMasters/notesMaster1.xml"); + + mPresentationFS->singleElementNS(XML_p, XML_notesMasterId, + FSNS(XML_r, XML_id), sRelId); + + mPresentationFS->endElementNS(XML_p, XML_notesMasterIdLst); + + FSHelperPtr pFS = + openFragmentStreamWithSerializer("ppt/notesMasters/notesMaster1.xml", + "application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml"); + // write theme per master + WriteTheme(mnMasterPages, nullptr); + + // add implicit relation to the presentation theme + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::THEME), + OUStringConcatenation("../theme/theme" + OUString::number(mnMasterPages + 1) + ".xml")); + + pFS->startElementNS(XML_p, XML_notesMaster, PNMSS); + + pFS->startElementNS(XML_p, XML_cSld); + + Reference< XPropertySet > aXBackgroundPropSet; + if (ImplGetPropertyValue(mXPagePropSet, "Background") && + (mAny >>= aXBackgroundPropSet)) + ImplWriteBackground(pFS, aXBackgroundPropSet); + + WriteShapeTree(pFS, NOTICE, true); + + pFS->endElementNS(XML_p, XML_cSld); + + // color map - now it uses colors from hardcoded theme, once we eventually generate theme, this might need update + pFS->singleElementNS(XML_p, XML_clrMap, + XML_bg1, "lt1", + XML_bg2, "lt2", + XML_tx1, "dk1", + XML_tx2, "dk2", + XML_accent1, "accent1", + XML_accent2, "accent2", + XML_accent3, "accent3", + XML_accent4, "accent4", + XML_accent5, "accent5", + XML_accent6, "accent6", + XML_hlink, "hlink", + XML_folHlink, "folHlink"); + + pFS->endElementNS(XML_p, XML_notesMaster); + + SAL_INFO("sd.eppt", "----------------"); +} + +void PowerPointExport::embedEffectAudio(const FSHelperPtr& pFS, const OUString& sUrl, OUString& sRelId, OUString& sName) +{ + comphelper::LifecycleProxy aProxy; + + if (!sUrl.endsWithIgnoreAsciiCase(".wav")) + return; + + uno::Reference<io::XInputStream> xAudioStream; + try + { + if (sUrl.startsWith("vnd.sun.star.Package:")) + { + uno::Reference<document::XStorageBasedDocument> xStorageBasedDocument(getModel(), uno::UNO_QUERY); + if (!xStorageBasedDocument.is()) + return; + + uno::Reference<embed::XStorage> xDocumentStorage = xStorageBasedDocument->getDocumentStorage(); + if (!xDocumentStorage.is()) + return; + + uno::Reference<io::XStream> xStream = comphelper::OStorageHelper::GetStreamAtPackageURL(xDocumentStorage, sUrl, + css::embed::ElementModes::READ, aProxy); + + if (xStream.is()) + xAudioStream = xStream->getInputStream(); + } + else + xAudioStream = comphelper::OStorageHelper::GetInputStreamFromURL(sUrl, getComponentContext()); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("sd", "PowerPointExport::embedEffectAudio"); + } + + if (!xAudioStream.is()) + return; + + int nLastSlash = sUrl.lastIndexOf('/'); + sName = sUrl.copy(nLastSlash >= 0 ? nLastSlash + 1 : 0); + + OUString sPath = "../media/" + sName; + sRelId = addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::AUDIO), sPath); + + uno::Reference<io::XOutputStream> xOutputStream = openFragmentStream(sPath.replaceAt(0, 2, u"/ppt"), + "audio/x-wav"); + + comphelper::OStorageHelper::CopyInputToOutput(xAudioStream, xOutputStream); +} + +sal_Int32 PowerPointExport::GetShapeID(const Reference<XShape>& rXShape) +{ + return ShapeExport::GetShapeID(rXShape, &maShapeMap); +} + +sal_Int32 PowerPointExport::GetNextAnimationNodeID() +{ + return mnAnimationNodeIdMax++; +} + +bool PowerPointExport::ImplCreateMainNotes() +{ + if (mbCreateNotes) + WriteNotesMaster(); + + return true; +} + +OUString PowerPointExport::getImplementationName() +{ + return "com.sun.star.comp.Impress.oox.PowerPointExport"; +} + +void PowerPointExport::WriteDiagram(const FSHelperPtr& pFS, PowerPointShapeExport& rDML, const css::uno::Reference<css::drawing::XShape>& rXShape, int nDiagramId) +{ + SAL_INFO("sd.eppt", "writing Diagram " + OUString::number(nDiagramId)); + pFS->startElementNS(XML_p, XML_graphicFrame); + rDML.WriteDiagram(rXShape, nDiagramId); + pFS->endElementNS(XML_p, XML_graphicFrame); +} + +void PowerPointExport::WritePlaceholderReferenceShapes(PowerPointShapeExport& rDML, PageType ePageType) +{ + bool bCheckProps = ePageType == NORMAL; + Reference<XShape> xShape; + Any aAny; + OUString aText; + if (ePageType == LAYOUT + || (bCheckProps && PropValue::GetPropertyValue(aAny, mXPagePropSet, "IsFooterVisible", true) + && aAny == true && GetPropertyValue(aAny, mXPagePropSet, "FooterText", true) + && (aAny >>= aText) && !aText.isEmpty())) + { + if ((xShape = GetReferencedPlaceholderXShape(Footer, ePageType))) + rDML.WritePlaceholderReferenceShape(Footer, + maPlaceholderShapeToIndexMap.find(xShape)->second, + ePageType, mXPagePropSet); + } + + if (ePageType == LAYOUT + || (bCheckProps + && PropValue::GetPropertyValue(aAny, mXPagePropSet, "IsPageNumberVisible", true) + && aAny == true)) + { + if ((xShape = GetReferencedPlaceholderXShape(SlideNumber, ePageType))) + rDML.WritePlaceholderReferenceShape(SlideNumber, + maPlaceholderShapeToIndexMap.find(xShape)->second, + ePageType, mXPagePropSet); + } + + if (ePageType == LAYOUT + || (bCheckProps + && PropValue::GetPropertyValue(aAny, mXPagePropSet, "IsDateTimeVisible", true) + && aAny == true + && ((GetPropertyValue(aAny, mXPagePropSet, "DateTimeText", true) && (aAny >>= aText) + && !aText.isEmpty()) + || mXPagePropSet->getPropertyValue("IsDateTimeFixed") == false))) + { + if ((xShape = GetReferencedPlaceholderXShape(DateAndTime, ePageType))) + rDML.WritePlaceholderReferenceShape(DateAndTime, + maPlaceholderShapeToIndexMap.find(xShape)->second, + ePageType, mXPagePropSet); + } +} + +sal_Int32 PowerPointExport::CreateNewPlaceholderIndex(const css::uno::Reference<XShape> &rXShape) +{ + maPlaceholderShapeToIndexMap.insert({rXShape, mnPlaceholderIndexMax}); + return mnPlaceholderIndexMax++; +} + +Reference<XShape> PowerPointExport::GetReferencedPlaceholderXShape(const PlaceholderType eType, + PageType ePageType) const +{ + PresObjKind ePresObjKind = PresObjKind::NONE; + switch (eType) + { + case oox::core::None: + break; + case oox::core::SlideImage: + break; + case oox::core::Notes: + break; + case oox::core::Header: + ePresObjKind = PresObjKind::Header; + break; + case oox::core::Footer: + ePresObjKind = PresObjKind::Footer; + break; + case oox::core::SlideNumber: + ePresObjKind = PresObjKind::SlideNumber; + break; + case oox::core::DateAndTime: + ePresObjKind = PresObjKind::DateTime; + break; + case oox::core::Outliner: + break; + case oox::core::Title: + ePresObjKind = PresObjKind::Title; + break; + case oox::core::Subtitle: + break; + } + if (ePresObjKind != PresObjKind::NONE) + { + SdPage* pMasterPage; + if (ePageType == LAYOUT) + { + // since Layout pages do not have drawpages themselves - mXDrawPage is still the master they reference to.. + pMasterPage = SdPage::getImplementation(mXDrawPage); + } + else + { + pMasterPage + = &static_cast<SdPage&>(SdPage::getImplementation(mXDrawPage)->TRG_GetMasterPage()); + } + if (SdrObject* pMasterFooter = pMasterPage->GetPresObj(ePresObjKind)) + return GetXShapeForSdrObject(pMasterFooter); + } + return nullptr; +} + +// UNO component +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +css_comp_Impress_oox_PowerPointExport(uno::XComponentContext* rxCtxt, + uno::Sequence<css::uno::Any> const& rArguments) +{ + return cppu::acquire(new PowerPointExport(rxCtxt, rArguments)); +} + +#if OSL_DEBUG_LEVEL > 1 +void dump_pset(Reference< XPropertySet > const& rXPropSet) +{ + Reference< XPropertySetInfo > info = rXPropSet->getPropertySetInfo(); + Sequence< beans::Property > props = info->getProperties(); + + for (int i=0; i < props.getLength(); i++) + { + OString name = OUStringToOString(props [i].Name, RTL_TEXTENCODING_UTF8); + + Any value = rXPropSet->getPropertyValue(props [i].Name); + + OUString strValue; + sal_Int32 intValue; + bool boolValue; + RectanglePoint pointValue; + + if (value >>= strValue) + SAL_INFO("sd.eppt", name << " = \"" << strValue << "\""); + else if (value >>= intValue) + SAL_INFO("sd.eppt", name << " = " << intValue << "(hex : " << std::hex << intValue << ")"); + else if (value >>= boolValue) + SAL_INFO("sd.eppt", name << " = " << boolValue << " (bool)"); + else if (value >>= pointValue) + SAL_INFO("sd.eppt", name << " = " << static_cast<int>(pointValue) << " (RectanglePoint)"); + else + SAL_INFO("sd.eppt", "??? <unhandled type>"); + } +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |