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