PPTX export: save SmartArt as diagram instead of group of shapes
preserving SmartArt allows editing it in PowerPoint after saving as pptx file
* moved common parts for docx and pptx export to oox/drawingml
* fixed export tests that expected shapes on output
Change-Id: I3e70a9f4177bebf5e1671232f4cd0ef0e7212626
Reviewed-on: https://gerrit.libreoffice.org/69598
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx
index fbcc25f..b3cdbb3 100644
--- a/include/oox/export/drawingml.hxx
+++ b/include/oox/export/drawingml.hxx
@@ -258,7 +258,12 @@ public:
void WriteShape3DEffects( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet );
void WriteArtisticEffect( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet );
OString WriteWdpPicture( const OUString& rFileId, const css::uno::Sequence< sal_Int8 >& rPictureData );
void WriteDiagram(const css::uno::Reference<css::drawing::XShape>& rXShape, int nDiagramId);
void writeDiagramRels(const css::uno::Sequence<css::uno::Sequence<css::uno::Any>>& xRelSeq,
const css::uno::Reference<css::io::XOutputStream>& xOutStream,
const OUString& sGrabBagProperyName, int nDiagramId);
static bool IsGroupShape( const css::uno::Reference< css::drawing::XShape >& rXShape );
static bool IsDiagram(const css::uno::Reference<css::drawing::XShape>& rXShape);
sal_Int32 getBulletMarginIndentation (const css::uno::Reference< css::beans::XPropertySet >& rXPropSet,sal_Int16 nLevel, const OUString& propName);
static void ResetCounters();
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index b5047b8..1cebeaa 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -25,10 +25,12 @@
#include <oox/core/xmlfilterbase.hxx>
#include <oox/export/drawingml.hxx>
#include <oox/export/utils.hxx>
#include <oox/helper/propertyset.hxx>
#include <oox/drawingml/color.hxx>
#include <drawingml/fillproperties.hxx>
#include <drawingml/textparagraph.hxx>
#include <oox/token/namespaces.hxx>
#include <oox/token/properties.hxx>
#include <oox/token/relationship.hxx>
#include <oox/token/tokens.hxx>
#include <oox/drawingml/drawingmltypes.hxx>
@@ -80,7 +82,13 @@
#include <com/sun/star/text/XTextField.hpp>
#include <com/sun/star/text/XTextRange.hpp>
#include <com/sun/star/style/CaseMap.hpp>
#include <com/sun/star/xml/dom/XNodeList.hpp>
#include <com/sun/star/xml/sax/Writer.hpp>
#include <com/sun/star/xml/sax/XSAXSerializable.hpp>
#include <comphelper/processfactory.hxx>
#include <comphelper/random.hxx>
#include <comphelper/seqstream.hxx>
#include <comphelper/storagehelper.hxx>
#include <comphelper/xmltools.hxx>
#include <o3tl/any.hxx>
@@ -99,6 +107,7 @@
#include <editeng/outlobj.hxx>
#include <editeng/svxenum.hxx>
#include <editeng/unonames.hxx>
#include <editeng/unoprnms.hxx>
#include <editeng/flditem.hxx>
#include <svx/sdtfsitm.hxx>
#include <svx/svdoashp.hxx>
@@ -2268,6 +2277,31 @@ bool DrawingML::IsGroupShape( const Reference< XShape >& rXShape )
return bRet;
}
bool DrawingML::IsDiagram(const Reference<XShape>& rXShape)
{
uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY);
if (!xPropSet.is())
return false;
// if the shape doesn't have the InteropGrabBag property, it's not a diagram
uno::Reference<beans::XPropertySetInfo> xPropSetInfo = xPropSet->getPropertySetInfo();
OUString aName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG;
if (!xPropSetInfo->hasPropertyByName(aName))
return false;
uno::Sequence<beans::PropertyValue> propList;
xPropSet->getPropertyValue(aName) >>= propList;
for (sal_Int32 nProp = 0; nProp < propList.getLength(); ++nProp)
{
// if we find any of the diagram components, it's a diagram
OUString propName = propList[nProp].Name;
if (propName == "OOXData" || propName == "OOXLayout" || propName == "OOXStyle"
|| propName == "OOXColor" || propName == "OOXDrawing")
return true;
}
return false;
}
sal_Int32 DrawingML::getBulletMarginIndentation (const Reference< XPropertySet >& rXPropSet,sal_Int16 nLevel, const OUString& propName)
{
if (nLevel < 0 || !GetProperty(rXPropSet, "NumberingRules"))
@@ -3984,6 +4018,292 @@ OString DrawingML::WriteWdpPicture( const OUString& rFileId, const Sequence< sal
return OUStringToOString( sId, RTL_TEXTENCODING_UTF8 );
}
void DrawingML::WriteDiagram(const css::uno::Reference<css::drawing::XShape>& rXShape, int nDiagramId)
{
uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY);
uno::Reference<xml::dom::XDocument> dataDom;
uno::Reference<xml::dom::XDocument> layoutDom;
uno::Reference<xml::dom::XDocument> styleDom;
uno::Reference<xml::dom::XDocument> colorDom;
uno::Reference<xml::dom::XDocument> drawingDom;
uno::Sequence<uno::Sequence<uno::Any>> xDataRelSeq;
uno::Sequence<uno::Any> diagramDrawing;
// retrieve the doms from the GrabBag
uno::Sequence<beans::PropertyValue> propList;
xPropSet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= propList;
for (sal_Int32 nProp = 0; nProp < propList.getLength(); ++nProp)
{
OUString propName = propList[nProp].Name;
if (propName == "OOXData")
propList[nProp].Value >>= dataDom;
else if (propName == "OOXLayout")
propList[nProp].Value >>= layoutDom;
else if (propName == "OOXStyle")
propList[nProp].Value >>= styleDom;
else if (propName == "OOXColor")
propList[nProp].Value >>= colorDom;
else if (propName == "OOXDrawing")
{
propList[nProp].Value >>= diagramDrawing;
diagramDrawing[0]
>>= drawingDom; // if there is OOXDrawing property then set drawingDom here only.
}
else if (propName == "OOXDiagramDataRels")
propList[nProp].Value >>= xDataRelSeq;
}
// check that we have the 4 mandatory XDocuments
// if not, there was an error importing and we won't output anything
if (!dataDom.is() || !layoutDom.is() || !styleDom.is() || !colorDom.is())
return;
// generate an unique id
sax_fastparser::FastAttributeList* pDocPrAttrList
= sax_fastparser::FastSerializerHelper::createAttrList();
pDocPrAttrList->add(XML_id, OString::number(nDiagramId).getStr());
OUString sName = "Diagram" + OUString::number(nDiagramId);
pDocPrAttrList->add(XML_name, OUStringToOString(sName, RTL_TEXTENCODING_UTF8).getStr());
sax_fastparser::XFastAttributeListRef xDocPrAttrListRef(pDocPrAttrList);
if (GetDocumentType() == DOCUMENT_DOCX)
{
mpFS->singleElementNS(XML_wp, XML_docPr, xDocPrAttrListRef);
mpFS->singleElementNS(XML_wp, XML_cNvGraphicFramePr, FSEND);
mpFS->startElementNS(
XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
OUStringToOString(mpFB->getNamespaceURL(OOX_NS(dml)), RTL_TEXTENCODING_UTF8).getStr(),
FSEND);
}
else
{
mpFS->startElementNS(XML_p, XML_nvGraphicFramePr, FSEND);
mpFS->singleElementNS(XML_p, XML_cNvPr, xDocPrAttrListRef);
mpFS->singleElementNS(XML_p, XML_cNvGraphicFramePr, FSEND);
mpFS->startElementNS(XML_p, XML_nvPr, FSEND);
mpFS->startElementNS(XML_p, XML_extLst, FSEND);
// change tracking extension - required in PPTX
mpFS->startElementNS(XML_p, XML_ext, XML_uri, "{D42A27DB-BD31-4B8C-83A1-F6EECF244321}",
FSEND);
mpFS->singleElementNS(
XML_p14, XML_modId, FSNS(XML_xmlns, XML_p14),
OUStringToOString(mpFB->getNamespaceURL(OOX_NS(p14)), RTL_TEXTENCODING_UTF8).getStr(),
XML_val,
OString::number(comphelper::rng::uniform_uint_distribution(1, SAL_MAX_UINT32)).getStr(),
FSEND);
mpFS->endElementNS(XML_p, XML_ext);
mpFS->endElementNS(XML_p, XML_extLst);
mpFS->endElementNS(XML_p, XML_nvPr);
mpFS->endElementNS(XML_p, XML_nvGraphicFramePr);
awt::Point aPos = rXShape->getPosition();
awt::Size aSize = rXShape->getSize();
WriteTransformation(tools::Rectangle(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height)),
XML_p, false, false, 0, false);
mpFS->startElementNS(XML_a, XML_graphic, FSEND);
}
mpFS->startElementNS(XML_a, XML_graphicData, XML_uri,
"http://schemas.openxmlformats.org/drawingml/2006/diagram", FSEND);
OUString sRelationCompPrefix = OUString::createFromAscii(GetRelationCompPrefix());
// add data relation
OUString dataFileName = "diagrams/data" + OUString::number(nDiagramId) + ".xml";
OString dataRelId = OUStringToOString(
mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::DIAGRAMDATA),
sRelationCompPrefix + dataFileName),
RTL_TEXTENCODING_UTF8);
// add layout relation
OUString layoutFileName = "diagrams/layout" + OUString::number(nDiagramId) + ".xml";
OString layoutRelId
= OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(),
oox::getRelationship(Relationship::DIAGRAMLAYOUT),
sRelationCompPrefix + layoutFileName),
RTL_TEXTENCODING_UTF8);
// add style relation
OUString styleFileName = "diagrams/quickStyle" + OUString::number(nDiagramId) + ".xml";
OString styleRelId
= OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(),
oox::getRelationship(Relationship::DIAGRAMQUICKSTYLE),
sRelationCompPrefix + styleFileName),
RTL_TEXTENCODING_UTF8);
// add color relation
OUString colorFileName = "diagrams/colors" + OUString::number(nDiagramId) + ".xml";
OString colorRelId
= OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(),
oox::getRelationship(Relationship::DIAGRAMCOLORS),
sRelationCompPrefix + colorFileName),
RTL_TEXTENCODING_UTF8);
OUString drawingFileName;
if (drawingDom.is())
{
// add drawing relation
drawingFileName = "diagrams/drawing" + OUString::number(nDiagramId) + ".xml";
OUString drawingRelId = mpFB->addRelation(
mpFS->getOutputStream(), oox::getRelationship(Relationship::DIAGRAMDRAWING),
sRelationCompPrefix + drawingFileName);
// the data dom contains a reference to the drawing relation. We need to update it with the new generated
// relation value before writing the dom to a file
// Get the dsp:damaModelExt node from the dom
uno::Reference<xml::dom::XNodeList> nodeList = dataDom->getElementsByTagNameNS(
"http://schemas.microsoft.com/office/drawing/2008/diagram", "dataModelExt");
// There must be one element only so get it
uno::Reference<xml::dom::XNode> node = nodeList->item(0);
// Get the list of attributes of the node
uno::Reference<xml::dom::XNamedNodeMap> nodeMap = node->getAttributes();
// Get the node with the relId attribute and set its new value
uno::Reference<xml::dom::XNode> relIdNode = nodeMap->getNamedItem("relId");
relIdNode->setNodeValue(drawingRelId);
}
mpFS->singleElementNS(
XML_dgm, XML_relIds, FSNS(XML_xmlns, XML_dgm),
OUStringToOString(mpFB->getNamespaceURL(OOX_NS(dmlDiagram)), RTL_TEXTENCODING_UTF8)
.getStr(),
FSNS(XML_xmlns, XML_r),
OUStringToOString(mpFB->getNamespaceURL(OOX_NS(officeRel)), RTL_TEXTENCODING_UTF8).getStr(),
FSNS(XML_r, XML_dm), dataRelId.getStr(), FSNS(XML_r, XML_lo), layoutRelId.getStr(),
FSNS(XML_r, XML_qs), styleRelId.getStr(), FSNS(XML_r, XML_cs), colorRelId.getStr(), FSEND);
mpFS->endElementNS(XML_a, XML_graphicData);
mpFS->endElementNS(XML_a, XML_graphic);
uno::Reference<xml::sax::XSAXSerializable> serializer;
uno::Reference<xml::sax::XWriter> writer
= xml::sax::Writer::create(comphelper::getProcessComponentContext());
OUString sDir = OUString::createFromAscii(GetComponentDir());
// write data file
serializer.set(dataDom, uno::UNO_QUERY);
uno::Reference<io::XOutputStream> xDataOutputStream = mpFB->openFragmentStream(
sDir + "/" + dataFileName,
"application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml");
writer->setOutputStream(xDataOutputStream);
serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
uno::Sequence<beans::StringPair>());
// write the associated Images and rels for data file
writeDiagramRels(xDataRelSeq, xDataOutputStream, "OOXDiagramDataRels", nDiagramId);
// write layout file
serializer.set(layoutDom, uno::UNO_QUERY);
writer->setOutputStream(mpFB->openFragmentStream(
sDir + "/" + layoutFileName,
"application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml"));
serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
uno::Sequence<beans::StringPair>());
// write style file
serializer.set(styleDom, uno::UNO_QUERY);
writer->setOutputStream(mpFB->openFragmentStream(
sDir + "/" + styleFileName,
"application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml"));
serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
uno::Sequence<beans::StringPair>());
// write color file
serializer.set(colorDom, uno::UNO_QUERY);
writer->setOutputStream(mpFB->openFragmentStream(
sDir + "/" + colorFileName,
"application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml"));
serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
uno::Sequence<beans::StringPair>());
// write drawing file
if (drawingDom.is())
{
serializer.set(drawingDom, uno::UNO_QUERY);
uno::Reference<io::XOutputStream> xDrawingOutputStream = mpFB->openFragmentStream(
sDir + "/" + drawingFileName, "application/vnd.ms-office.drawingml.diagramDrawing+xml");
writer->setOutputStream(xDrawingOutputStream);
serializer->serialize(
uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
uno::Sequence<beans::StringPair>());
// write the associated Images and rels for drawing file
uno::Sequence<uno::Sequence<uno::Any>> xDrawingRelSeq;
diagramDrawing[1] >>= xDrawingRelSeq;
writeDiagramRels(xDrawingRelSeq, xDrawingOutputStream, "OOXDiagramDrawingRels", nDiagramId);
}
}
void DrawingML::writeDiagramRels(const uno::Sequence<uno::Sequence<uno::Any>>& xRelSeq,
const uno::Reference<io::XOutputStream>& xOutStream,
const OUString& sGrabBagProperyName, int nDiagramId)
{
// add image relationships of OOXData, OOXDiagram
OUString sType(oox::getRelationship(Relationship::IMAGE));
uno::Reference<xml::sax::XWriter> xWriter
= xml::sax::Writer::create(comphelper::getProcessComponentContext());
xWriter->setOutputStream(xOutStream);
// retrieve the relationships from Sequence
for (sal_Int32 j = 0; j < xRelSeq.getLength(); j++)
{
// diagramDataRelTuple[0] => RID,
// diagramDataRelTuple[1] => xInputStream
// diagramDataRelTuple[2] => extension
uno::Sequence<uno::Any> diagramDataRelTuple = xRelSeq[j];
OUString sRelId;
OUString sExtension;
diagramDataRelTuple[0] >>= sRelId;
diagramDataRelTuple[2] >>= sExtension;
OUString sContentType;
if (sExtension.equalsIgnoreAsciiCase(".WMF"))
sContentType = "image/x-wmf";
else
sContentType = "image/" + sExtension.copy(1);
sRelId = sRelId.copy(3);
StreamDataSequence dataSeq;
diagramDataRelTuple[1] >>= dataSeq;
uno::Reference<io::XInputStream> dataImagebin(
new ::comphelper::SequenceInputStream(dataSeq));
//nDiagramId is used to make the name unique irrespective of the number of smart arts.
OUString sFragment = "media/" + sGrabBagProperyName + OUString::number(nDiagramId) + "_"
+ OUString::number(j) + sExtension;
PropertySet aProps(xOutStream);
aProps.setAnyProperty(PROP_RelId, uno::makeAny(sRelId.toInt32()));
mpFB->addRelation(xOutStream, sType, "../" + sFragment);
OUString sDir = OUString::createFromAscii(GetComponentDir());
uno::Reference<io::XOutputStream> xBinOutStream
= mpFB->openFragmentStream(sDir + "/" + sFragment, sContentType);
try
{
comphelper::OStorageHelper::CopyInputToOutput(dataImagebin, xBinOutStream);
}
catch (const uno::Exception& rException)
{
SAL_WARN("oox.drawingml", "DrawingML::writeDiagramRels Failed to copy grabbaged Image: "
<< rException);
}
dataImagebin->closeInput();
}
}
}
}
diff --git a/oox/source/token/tokens.txt b/oox/source/token/tokens.txt
index 653050e..3929d8c 100644
--- a/oox/source/token/tokens.txt
+++ b/oox/source/token/tokens.txt
@@ -3431,6 +3431,7 @@ miter
miterlimit
moccasin
mod
modId
modelId
modern
modified
diff --git a/sd/qa/unit/data/pptx/group.pptx b/sd/qa/unit/data/pptx/group.pptx
index 8895fe2a..f0b739e 100644
--- a/sd/qa/unit/data/pptx/group.pptx
+++ b/sd/qa/unit/data/pptx/group.pptx
Binary files differ
diff --git a/sd/qa/unit/data/pptx/smartart-preserve.pptx b/sd/qa/unit/data/pptx/smartart-preserve.pptx
new file mode 100644
index 0000000..6108a8c
--- /dev/null
+++ b/sd/qa/unit/data/pptx/smartart-preserve.pptx
Binary files differ
diff --git a/sd/qa/unit/export-tests-ooxml2.cxx b/sd/qa/unit/export-tests-ooxml2.cxx
index 06330de..9192956 100644
--- a/sd/qa/unit/export-tests-ooxml2.cxx
+++ b/sd/qa/unit/export-tests-ooxml2.cxx
@@ -199,6 +199,7 @@ public:
void testTdf99213();
void testPotxExport();
void testTdf44223();
void testSmartArtPreserve();
CPPUNIT_TEST_SUITE(SdOOXMLExportTest2);
@@ -280,6 +281,7 @@ public:
CPPUNIT_TEST(testTdf99213);
CPPUNIT_TEST(testPotxExport);
CPPUNIT_TEST(testTdf44223);
CPPUNIT_TEST(testSmartArtPreserve);
CPPUNIT_TEST_SUITE_END();
@@ -304,6 +306,7 @@ public:
{ "a14", "http://schemas.microsoft.com/office/drawing/2010/main" },
{ "wps", "http://schemas.microsoft.com/office/word/2010/wordprocessingShape" },
{ "wpg", "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" },
{ "dgm", "http://schemas.openxmlformats.org/drawingml/2006/diagram" },
};
for (size_t i = 0; i < SAL_N_ELEMENTS(namespaces); ++i)
{
@@ -1360,6 +1363,12 @@ void SdOOXMLExportTest2::testTdf104788()
void SdOOXMLExportTest2::testSmartartRotation2()
{
::sd::DrawDocShellRef xDocShRef = loadURL(m_directories.getURLFromSrc("sd/qa/unit/data/pptx/smartart-rotation2.pptx"), PPTX);
// clear SmartArt data to check how group shapes with double-rotated children are exported, not smartart
uno::Reference<beans::XPropertySet> xShape(getShapeFromPage(0, 0, xDocShRef));
uno::Sequence<beans::PropertyValue> aInteropGrabBag;
xShape->setPropertyValue("InteropGrabBag", uno::makeAny(aInteropGrabBag));
utl::TempFile tempFile;
xDocShRef = saveAndReload(xDocShRef.get(), PPTX, &tempFile);
xDocShRef->DoClose();
@@ -1413,10 +1422,10 @@ void SdOOXMLExportTest2::testGroupsPosition()
xDocShRef->DoClose();
xmlDocPtr pXmlDocContent = parseExport(tempFile, "ppt/slides/slide1.xml");
assertXPath(pXmlDocContent, "/p:sld/p:cSld/p:spTree/p:grpSp[1]/p:sp[1]/p:spPr/a:xfrm/a:off", "x", "4040640");
assertXPath(pXmlDocContent, "/p:sld/p:cSld/p:spTree/p:grpSp[1]/p:sp[1]/p:spPr/a:xfrm/a:off", "y", "4273920");
assertXPath(pXmlDocContent, "/p:sld/p:cSld/p:spTree/p:grpSp[1]/p:sp[3]/p:spPr/a:xfrm/a:off", "x", "6796800");
assertXPath(pXmlDocContent, "/p:sld/p:cSld/p:spTree/p:grpSp[1]/p:sp[3]/p:spPr/a:xfrm/a:off", "y", "4273920");
assertXPath(pXmlDocContent, "/p:sld/p:cSld/p:spTree/p:grpSp[1]/p:sp[1]/p:spPr/a:xfrm/a:off", "x", "5004000");
assertXPath(pXmlDocContent, "/p:sld/p:cSld/p:spTree/p:grpSp[1]/p:sp[1]/p:spPr/a:xfrm/a:off", "y", "3310560");
assertXPath(pXmlDocContent, "/p:sld/p:cSld/p:spTree/p:grpSp[1]/p:sp[3]/p:spPr/a:xfrm/a:off", "x", "7760160");
assertXPath(pXmlDocContent, "/p:sld/p:cSld/p:spTree/p:grpSp[1]/p:sp[3]/p:spPr/a:xfrm/a:off", "y", "3310560");
}
void SdOOXMLExportTest2::testGroupsRotatedPosition()
@@ -2105,6 +2114,51 @@ void SdOOXMLExportTest2::testTdf44223()
xDocShRef->DoClose();
}
void SdOOXMLExportTest2::testSmartArtPreserve()
{
::sd::DrawDocShellRef xDocShRef
= loadURL(m_directories.getURLFromSrc("sd/qa/unit/data/pptx/smartart-preserve.pptx"), PPTX);
utl::TempFile tempFile;
xDocShRef = saveAndReload(xDocShRef.get(), PPTX, &tempFile);
xmlDocPtr pXmlDoc = parseExport(tempFile, "ppt/slides/slide1.xml");
assertXPath(pXmlDoc, "//p:sld/p:cSld/p:spTree/p:graphicFrame/p:nvGraphicFramePr/p:cNvPr");
assertXPath(pXmlDoc, "//p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData/dgm:relIds");
assertXPath(pXmlDoc, "//p:sld/p:cSld/p:spTree/p:graphicFrame/p:nvGraphicFramePr/p:nvPr/p:extLst/p:ext",
"uri", "{D42A27DB-BD31-4B8C-83A1-F6EECF244321}");
assertXPath(pXmlDoc, "//p:sld/p:cSld/p:spTree/p:graphicFrame/p:nvGraphicFramePr/p:nvPr/p:extLst/p:ext/p14:modId");
xmlDocPtr pXmlDocRels = parseExport(tempFile, "ppt/slides/_rels/slide1.xml.rels");
assertXPath(pXmlDocRels,
"(/rels:Relationships/rels:Relationship[@Target='../diagrams/layout1.xml'])[1]", "Type",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramLayout");
assertXPath(pXmlDocRels,
"(/rels:Relationships/rels:Relationship[@Target='../diagrams/data1.xml'])[1]", "Type",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramData");
assertXPath(pXmlDocRels,
"(/rels:Relationships/rels:Relationship[@Target='../diagrams/colors1.xml'])[1]", "Type",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramColors");
assertXPath(pXmlDocRels,
"(/rels:Relationships/rels:Relationship[@Target='../diagrams/quickStyle1.xml'])[1]", "Type",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramQuickStyle");
xmlDocPtr pXmlContentType = parseExport(tempFile, "[Content_Types].xml");
assertXPath(pXmlContentType,
"/ContentType:Types/ContentType:Override[@PartName='/ppt/diagrams/layout1.xml']",
"ContentType", "application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml");
assertXPath(pXmlContentType,
"/ContentType:Types/ContentType:Override[@PartName='/ppt/diagrams/data1.xml']",
"ContentType", "application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml");
assertXPath(pXmlContentType,
"/ContentType:Types/ContentType:Override[@PartName='/ppt/diagrams/colors1.xml']",
"ContentType", "application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml");
assertXPath(pXmlContentType,
"/ContentType:Types/ContentType:Override[@PartName='/ppt/diagrams/quickStyle1.xml']",
"ContentType", "application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml");
xDocShRef->DoClose();
}
CPPUNIT_TEST_SUITE_REGISTRATION(SdOOXMLExportTest2);
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sd/source/filter/eppt/epptooxml.hxx b/sd/source/filter/eppt/epptooxml.hxx
index 5791c02..02b96de 100644
--- a/sd/source/filter/eppt/epptooxml.hxx
+++ b/sd/source/filter/eppt/epptooxml.hxx
@@ -50,6 +50,8 @@ enum PlaceholderType
Subtitle
};
class PowerPointShapeExport;
class PowerPointExport final : public XmlFilterBase, public PPTWriterBase
{
friend class PowerPointShapeExport;
@@ -115,6 +117,8 @@ private:
virtual OUString SAL_CALL getImplementationName() override;
static void WriteDiagram(const FSHelperPtr& pFS, PowerPointShapeExport& rDML, const css::uno::Reference<css::drawing::XShape>& rXShape, int nDiagramId);
/// Should we export as .pptm, ie. do we contain macros?
bool mbPptm;
@@ -131,6 +135,8 @@ private:
sal_uInt32 mnSlideMasterIdMax;
sal_uInt32 mnAnimationNodeIdMax;
sal_uInt32 mnDiagramId;
bool mbCreateNotes;
::oox::drawingml::ShapeExport::ShapeHashMap maShapeMap;
diff --git a/sd/source/filter/eppt/pptx-epptooxml.cxx b/sd/source/filter/eppt/pptx-epptooxml.cxx
index 0ac1ca4..cdc452b 100644
--- a/sd/source/filter/eppt/pptx-epptooxml.cxx
+++ b/sd/source/filter/eppt/pptx-epptooxml.cxx
@@ -333,6 +333,7 @@ PowerPointExport::PowerPointExport(const Reference< XComponentContext >& rContex
, mnSlideIdMax(1 << 8)
, mnSlideMasterIdMax(1U << 31)
, mnAnimationNodeIdMax(1)
, mnDiagramId(1)
, mbCreateNotes(false)
{
comphelper::SequenceAsHashMap aArgumentsMap(rArguments);
@@ -1434,7 +1435,10 @@ void PowerPointExport::WriteShapeTree(const FSHelperPtr& pFS, PageType ePageType
if (GetShapeByIndex(GetCurrentGroupIndex(), true))
{
SAL_INFO("sd.eppt", "mType: " << mType);
aDML.WriteShape(mXShape);
if (DrawingML::IsDiagram(mXShape))
WriteDiagram(pFS, aDML, mXShape, mnDiagramId++);
else
aDML.WriteShape(mXShape);
}
}
@@ -2031,6 +2035,14 @@ OUString PowerPointExport::getImplementationName()
return OUString("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, FSEND);
rDML.WriteDiagram(rXShape, nDiagramId);
pFS->endElementNS(XML_p, XML_graphicFrame);
}
// UNO component
extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
css_comp_Impress_oox_PowerPointExport(uno::XComponentContext* rxCtxt,
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index f1c56fa..7e621fa 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -5677,7 +5677,10 @@ void DocxAttributeOutput::OutputFlyFrame_Impl( const ww8::Frame &rFrame, const P
const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject();
if ( pSdrObj )
{
if ( IsDiagram( pSdrObj ) )
uno::Reference<drawing::XShape> xShape(
const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY);
if (xShape.is() && oox::drawingml::DrawingML::IsDiagram(xShape))
{
if ( !m_pPostponedDiagrams )
{
@@ -5786,35 +5789,6 @@ void DocxAttributeOutput::OutputFlyFrame_Impl( const ww8::Frame &rFrame, const P
m_pSerializer->mergeTopMarks(Tag_OutputFlyFrame, sax_fastparser::MergeMarks::POSTPONE);
}
bool DocxAttributeOutput::IsDiagram( const SdrObject* sdrObject )
{
uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(sdrObject)->getUnoShape(), uno::UNO_QUERY );
if ( !xShape.is() )
return false;
uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
if ( !xPropSet.is() )
return false;
// if the shape doesn't have the InteropGrabBag property, it's not a diagram
uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
OUString aName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG;
if ( !xPropSetInfo->hasPropertyByName( aName ) )
return false;
uno::Sequence< beans::PropertyValue > propList;
xPropSet->getPropertyValue( aName ) >>= propList;
for ( sal_Int32 nProp=0; nProp < propList.getLength(); ++nProp )
{
// if we find any of the diagram components, it's a diagram
OUString propName = propList[nProp].Name;
if ( propName == "OOXData" || propName == "OOXLayout" || propName == "OOXStyle" ||
propName == "OOXColor" || propName == "OOXDrawing")
return true;
}
return false;
}
void DocxAttributeOutput::WriteOutliner(const OutlinerParaObject& rParaObj)
{
const EditTextObject& rEditObj = rParaObj.GetTextObject();
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx
index 4ed30cc..2ea64b8 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -400,9 +400,6 @@ private:
void WriteActiveXControl(const SdrObject* pObject, const SwFrameFormat& rFrameFormat, bool bInsideRun);
bool ExportAsActiveXControl(const SdrObject* pObject) const;
/// checks whether the current component is a diagram
static bool IsDiagram (const SdrObject* sdrObject);
void InitTableHelper( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner );
void StartTable( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner );
void StartTableRow( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner );
diff --git a/sw/source/filter/ww8/docxsdrexport.cxx b/sw/source/filter/ww8/docxsdrexport.cxx
index 5f5cfe9..d28c3c9 100644
--- a/sw/source/filter/ww8/docxsdrexport.cxx
+++ b/sw/source/filter/ww8/docxsdrexport.cxx
@@ -1125,290 +1125,19 @@ void DocxSdrExport::writeDMLEffectLst(const SwFrameFormat& rFrameFormat)
m_pImpl->getSerializer()->endElementNS(XML_a, XML_effectLst);
}
void DocxSdrExport::writeDiagramRels(const uno::Sequence<uno::Sequence<uno::Any>>& xRelSeq,
const uno::Reference<io::XOutputStream>& xOutStream,
const OUString& sGrabBagProperyName, int nAnchorId)
{
// add image relationships of OOXData, OOXDiagram
OUString sType(oox::getRelationship(Relationship::IMAGE));
uno::Reference<xml::sax::XWriter> xWriter
= xml::sax::Writer::create(comphelper::getProcessComponentContext());
xWriter->setOutputStream(xOutStream);
// retrieve the relationships from Sequence
for (sal_Int32 j = 0; j < xRelSeq.getLength(); j++)
{
// diagramDataRelTuple[0] => RID,
// diagramDataRelTuple[1] => xInputStream
// diagramDataRelTuple[2] => extension
uno::Sequence<uno::Any> diagramDataRelTuple = xRelSeq[j];
OUString sRelId;
OUString sExtension;
diagramDataRelTuple[0] >>= sRelId;
diagramDataRelTuple[2] >>= sExtension;
OUString sContentType;
if (sExtension.equalsIgnoreAsciiCase(".WMF"))
sContentType = "image/x-wmf";
else
sContentType = "image/" + sExtension.copy(1);
sRelId = sRelId.copy(3);
StreamDataSequence dataSeq;
diagramDataRelTuple[1] >>= dataSeq;
uno::Reference<io::XInputStream> dataImagebin(
new ::comphelper::SequenceInputStream(dataSeq));
OUString sFragment("../media/");
//nAnchorId is used to make the name unique irrespective of the number of smart arts.
sFragment += sGrabBagProperyName + OUString::number(nAnchorId) + "_" + OUString::number(j)
+ sExtension;
PropertySet aProps(xOutStream);
aProps.setAnyProperty(PROP_RelId, uno::makeAny(sRelId.toInt32()));
m_pImpl->getExport().GetFilter().addRelation(xOutStream, sType, sFragment);
sFragment = sFragment.replaceFirst("..", "word");
uno::Reference<io::XOutputStream> xBinOutStream
= m_pImpl->getExport().GetFilter().openFragmentStream(sFragment, sContentType);
try
{
sal_Int32 nBufferSize = 512;
uno::Sequence<sal_Int8> aDataBuffer(nBufferSize);
sal_Int32 nRead;
do
{
nRead = dataImagebin->readBytes(aDataBuffer, nBufferSize);
if (nRead)
{
if (nRead < nBufferSize)
{
nBufferSize = nRead;
aDataBuffer.realloc(nRead);
}
xBinOutStream->writeBytes(aDataBuffer);
}
} while (nRead);
xBinOutStream->flush();
}
catch (const uno::Exception& rException)
{
SAL_WARN("sw.ww8", "DocxSdrExport::writeDiagramRels Failed to copy grabbaged Image: "
<< rException);
}
dataImagebin->closeInput();
}
}
void DocxSdrExport::writeDiagram(const SdrObject* sdrObject, const SwFrameFormat& rFrameFormat,
int nAnchorId)
int nDiagramId)
{
sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObject)->getUnoShape(),
uno::UNO_QUERY);
uno::Reference<beans::XPropertySet> xPropSet(xShape, uno::UNO_QUERY);
uno::Reference<xml::dom::XDocument> dataDom;
uno::Reference<xml::dom::XDocument> layoutDom;
uno::Reference<xml::dom::XDocument> styleDom;
uno::Reference<xml::dom::XDocument> colorDom;
uno::Reference<xml::dom::XDocument> drawingDom;
uno::Sequence<uno::Sequence<uno::Any>> xDataRelSeq;
uno::Sequence<uno::Any> diagramDrawing;
// retrieve the doms from the GrabBag
uno::Sequence<beans::PropertyValue> propList;
xPropSet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= propList;
for (sal_Int32 nProp = 0; nProp < propList.getLength(); ++nProp)
{
OUString propName = propList[nProp].Name;
if (propName == "OOXData")
propList[nProp].Value >>= dataDom;
else if (propName == "OOXLayout")
propList[nProp].Value >>= layoutDom;
else if (propName == "OOXStyle")
propList[nProp].Value >>= styleDom;
else if (propName == "OOXColor")
propList[nProp].Value >>= colorDom;
else if (propName == "OOXDrawing")
{
propList[nProp].Value >>= diagramDrawing;
diagramDrawing[0]
>>= drawingDom; // if there is OOXDrawing property then set drawingDom here only.
}
else if (propName == "OOXDiagramDataRels")
propList[nProp].Value >>= xDataRelSeq;
}
// check that we have the 4 mandatory XDocuments
// if not, there was an error importing and we won't output anything
if (!dataDom.is() || !layoutDom.is() || !styleDom.is() || !colorDom.is())
return;
// write necessary tags to document.xml
Size aSize(sdrObject->GetSnapRect().GetWidth(), sdrObject->GetSnapRect().GetHeight());
startDMLAnchorInline(&rFrameFormat, aSize);
// generate a unique id
sax_fastparser::FastAttributeList* pDocPrAttrList
= sax_fastparser::FastSerializerHelper::createAttrList();
pDocPrAttrList->add(XML_id, OString::number(nAnchorId).getStr());
OUString sName = "Diagram" + OUString::number(nAnchorId);
pDocPrAttrList->add(XML_name, OUStringToOString(sName, RTL_TEXTENCODING_UTF8).getStr());
sax_fastparser::XFastAttributeListRef xDocPrAttrListRef(pDocPrAttrList);
pFS->singleElementNS(XML_wp, XML_docPr, xDocPrAttrListRef);
m_pImpl->getDrawingML()->WriteDiagram(xShape, nDiagramId);
sal_Int32 diagramCount;
diagramCount = nAnchorId;
pFS->singleElementNS(XML_wp, XML_cNvGraphicFramePr, FSEND);
pFS->startElementNS(
XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
OUStringToOString(m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)),
RTL_TEXTENCODING_UTF8)
.getStr(),
FSEND);
pFS->startElementNS(XML_a, XML_graphicData, XML_uri,
"http://schemas.openxmlformats.org/drawingml/2006/diagram", FSEND);
// add data relation
OUString dataFileName = "diagrams/data" + OUString::number(diagramCount) + ".xml";
OString dataRelId = OUStringToOString(
m_pImpl->getExport().GetFilter().addRelation(
pFS->getOutputStream(), oox::getRelationship(Relationship::DIAGRAMDATA), dataFileName),
RTL_TEXTENCODING_UTF8);
// add layout relation
OUString layoutFileName = "diagrams/layout" + OUString::number(diagramCount) + ".xml";
OString layoutRelId
= OUStringToOString(m_pImpl->getExport().GetFilter().addRelation(
pFS->getOutputStream(),
oox::getRelationship(Relationship::DIAGRAMLAYOUT), layoutFileName),
RTL_TEXTENCODING_UTF8);
// add style relation
OUString styleFileName = "diagrams/quickStyle" + OUString::number(diagramCount) + ".xml";
OString styleRelId = OUStringToOString(
m_pImpl->getExport().GetFilter().addRelation(
pFS->getOutputStream(), oox::getRelationship(Relationship::DIAGRAMQUICKSTYLE),
styleFileName),
RTL_TEXTENCODING_UTF8);
// add color relation
OUString colorFileName = "diagrams/colors" + OUString::number(diagramCount) + ".xml";
OString colorRelId
= OUStringToOString(m_pImpl->getExport().GetFilter().addRelation(
pFS->getOutputStream(),
oox::getRelationship(Relationship::DIAGRAMCOLORS), colorFileName),
RTL_TEXTENCODING_UTF8);
OUString drawingFileName;
if (drawingDom.is())
{
// add drawing relation
drawingFileName = "diagrams/drawing" + OUString::number(diagramCount) + ".xml";
OUString drawingRelId = m_pImpl->getExport().GetFilter().addRelation(
pFS->getOutputStream(), oox::getRelationship(Relationship::DIAGRAMDRAWING),
drawingFileName);
// the data dom contains a reference to the drawing relation. We need to update it with the new generated
// relation value before writing the dom to a file
// Get the dsp:damaModelExt node from the dom
uno::Reference<xml::dom::XNodeList> nodeList = dataDom->getElementsByTagNameNS(
"http://schemas.microsoft.com/office/drawing/2008/diagram", "dataModelExt");
// There must be one element only so get it
uno::Reference<xml::dom::XNode> node = nodeList->item(0);
// Get the list of attributes of the node
uno::Reference<xml::dom::XNamedNodeMap> nodeMap = node->getAttributes();
// Get the node with the relId attribute and set its new value
uno::Reference<xml::dom::XNode> relIdNode = nodeMap->getNamedItem("relId");
relIdNode->setNodeValue(drawingRelId);
}
pFS->singleElementNS(
XML_dgm, XML_relIds, FSNS(XML_xmlns, XML_dgm),
OUStringToOString(m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dmlDiagram)),
RTL_TEXTENCODING_UTF8)
.getStr(),
FSNS(XML_xmlns, XML_r),
OUStringToOString(m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(officeRel)),
RTL_TEXTENCODING_UTF8)
.getStr(),
FSNS(XML_r, XML_dm), dataRelId.getStr(), FSNS(XML_r, XML_lo), layoutRelId.getStr(),
FSNS(XML_r, XML_qs), styleRelId.getStr(), FSNS(XML_r, XML_cs), colorRelId.getStr(), FSEND);
pFS->endElementNS(XML_a, XML_graphicData);
pFS->endElementNS(XML_a, XML_graphic);
endDMLAnchorInline(&rFrameFormat);
uno::Reference<xml::sax::XSAXSerializable> serializer;
uno::Reference<xml::sax::XWriter> writer
= xml::sax::Writer::create(comphelper::getProcessComponentContext());
// write data file
serializer.set(dataDom, uno::UNO_QUERY);
uno::Reference<io::XOutputStream> xDataOutputStream
= m_pImpl->getExport().GetFilter().openFragmentStream(
"word/" + dataFileName,
"application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml");
writer->setOutputStream(xDataOutputStream);
serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
uno::Sequence<beans::StringPair>());
// write the associated Images and rels for data file
writeDiagramRels(xDataRelSeq, xDataOutputStream, "OOXDiagramDataRels", nAnchorId);
// write layout file
serializer.set(layoutDom, uno::UNO_QUERY);
writer->setOutputStream(m_pImpl->getExport().GetFilter().openFragmentStream(
"word/" + layoutFileName,
"application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml"));
serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
uno::Sequence<beans::StringPair>());
// write style file
serializer.set(styleDom, uno::UNO_QUERY);
writer->setOutputStream(m_pImpl->getExport().GetFilter().openFragmentStream(
"word/" + styleFileName,
"application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml"));
serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
uno::Sequence<beans::StringPair>());
// write color file
serializer.set(colorDom, uno::UNO_QUERY);
writer->setOutputStream(m_pImpl->getExport().GetFilter().openFragmentStream(
"word/" + colorFileName,
"application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml"));
serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
uno::Sequence<beans::StringPair>());
// write drawing file
if (drawingDom.is())
{
serializer.set(drawingDom, uno::UNO_QUERY);
uno::Reference<io::XOutputStream> xDrawingOutputStream
= m_pImpl->getExport().GetFilter().openFragmentStream(
"word/" + drawingFileName,
"application/vnd.ms-office.drawingml.diagramDrawing+xml");
writer->setOutputStream(xDrawingOutputStream);
serializer->serialize(
uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
uno::Sequence<beans::StringPair>());
// write the associated Images and rels for drawing file
uno::Sequence<uno::Sequence<uno::Any>> xDrawingRelSeq;
diagramDrawing[1] >>= xDrawingRelSeq;
writeDiagramRels(xDrawingRelSeq, xDrawingOutputStream, "OOXDiagramDrawingRels", nAnchorId);
}
}
void DocxSdrExport::writeOnlyTextOfFrame(ww8::Frame const* pParentFrame)
diff --git a/sw/source/filter/ww8/docxsdrexport.hxx b/sw/source/filter/ww8/docxsdrexport.hxx
index 81a8da8..6139787 100644
--- a/sw/source/filter/ww8/docxsdrexport.hxx
+++ b/sw/source/filter/ww8/docxsdrexport.hxx
@@ -95,10 +95,7 @@ public:
/// Write <a:effectLst>, the effect list.
void writeDMLEffectLst(const SwFrameFormat& rFrameFormat);
/// Writes a diagram (smartart).
void writeDiagram(const SdrObject* sdrObject, const SwFrameFormat& rFrameFormat, int nAnchorId);
void writeDiagramRels(const css::uno::Sequence< css::uno::Sequence<css::uno::Any> >& xRelSeq,
const css::uno::Reference<css::io::XOutputStream>& xOutStream, const OUString& sGrabBagProperyName,
int nAnchorId);
void writeDiagram(const SdrObject* sdrObject, const SwFrameFormat& rFrameFormat, int nDiagramId);
/// Writes text frame in DML format.
void writeDMLTextFrame(ww8::Frame const* pParentFrame, int nAnchorId, bool bTextBoxOnly = false);
/// Writes text frame in VML format.