tdf143222 Handle alternate content of graphicData element.
Handle alternate content and make true choice.
According to ooxml spec ole object requires exactly one pic
element. (ECMA-376 Part 1, Annex A, CT_OleObject). In the
current case first choice has not pic element and we should
allow fallback processing.
Change-Id: I30b7de703b8c2f00d6bf286e05eea505ac3627f2
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/118539
Tested-by: Jenkins
Reviewed-by: Gülşah Köse <gulsah.kose@collabora.com>
diff --git a/include/oox/core/contexthandler2.hxx b/include/oox/core/contexthandler2.hxx
index 4e25608..3a75aff 100644
--- a/include/oox/core/contexthandler2.hxx
+++ b/include/oox/core/contexthandler2.hxx
@@ -72,7 +72,7 @@ struct ElementInfo;
class OOX_DLLPUBLIC ContextHandler2Helper
{
public:
explicit ContextHandler2Helper( bool bEnableTrimSpace );
explicit ContextHandler2Helper( bool bEnableTrimSpace, XmlFilterBase& rFilter );
explicit ContextHandler2Helper( const ContextHandler2Helper& rParent );
virtual ~ContextHandler2Helper();
@@ -201,6 +201,21 @@ protected:
/** Must be called from endRecord() in derived classes. */
void implEndRecord( sal_Int32 nRecId );
bool prepareMceContext( sal_Int32 nElement, const AttributeList& rAttribs );
XmlFilterBase& getDocFilter() const { return mrFilter; }
enum class MCE_STATE
{
Started,
FoundChoice
};
MCE_STATE getMCEState() const { return aMceState.back(); }
void setMCEState( MCE_STATE aState ) { aMceState.back() = aState; }
void addMCEState( MCE_STATE aState ) { aMceState.push_back( aState ); }
void removeMCEState() { aMceState.pop_back(); }
bool isMCEStateEmpty() { return aMceState.empty(); }
private:
ContextHandler2Helper& operator=( const ContextHandler2Helper& ) = delete;
@@ -214,9 +229,11 @@ private:
ContextStackRef mxContextStack; ///< Stack of all processed elements.
size_t mnRootStackSize; ///< Stack size on construction time.
std::vector<MCE_STATE> aMceState;
protected:
bool mbEnableTrimSpace; ///< True = trim whitespace in characters().
XmlFilterBase& mrFilter;
};
class OOX_DLLPUBLIC ContextHandler2 : public ContextHandler, public ContextHandler2Helper
diff --git a/include/oox/core/fragmenthandler2.hxx b/include/oox/core/fragmenthandler2.hxx
index 86d1453..598426e 100644
--- a/include/oox/core/fragmenthandler2.hxx
+++ b/include/oox/core/fragmenthandler2.hxx
@@ -47,17 +47,6 @@ class XmlFilterBase;
class OOX_DLLPUBLIC FragmentHandler2 : public FragmentHandler, public ContextHandler2Helper
{
protected:
enum class MCE_STATE
{
Started,
FoundChoice
};
::std::vector<MCE_STATE> aMceState;
bool prepareMceContext( sal_Int32 nElement, const AttributeList& rAttribs );
public:
explicit FragmentHandler2(
XmlFilterBase& rFilter,
diff --git a/include/oox/drawingml/graphicshapecontext.hxx b/include/oox/drawingml/graphicshapecontext.hxx
index 4813d5f..ffd579f 100644
--- a/include/oox/drawingml/graphicshapecontext.hxx
+++ b/include/oox/drawingml/graphicshapecontext.hxx
@@ -62,6 +62,7 @@ public:
OleObjectGraphicDataContext( ::oox::core::ContextHandler2Helper const & rParent, const ShapePtr& pShapePtr );
virtual ~OleObjectGraphicDataContext() override;
virtual ::oox::core::ContextHandlerRef onCreateContext( ::sal_Int32 Element, const ::oox::AttributeList& rAttribs ) override;
virtual void onEndElement() override;
private:
::oox::vml::OleObjectInfo& mrOleObjectInfo;
diff --git a/include/oox/ole/oleobjecthelper.hxx b/include/oox/ole/oleobjecthelper.hxx
index d2506f3..5b792f2 100644
--- a/include/oox/ole/oleobjecthelper.hxx
+++ b/include/oox/ole/oleobjecthelper.hxx
@@ -47,6 +47,7 @@ struct OleObjectInfo
bool mbLinked; ///< True = linked OLE object, false = embedded OLE object.
bool mbShowAsIcon; ///< True = show as icon, false = show contents.
bool mbAutoUpdate;
bool mbHasPicture; ///<Ole object requires a picture element according to spec.>
explicit OleObjectInfo();
};
diff --git a/oox/source/core/contexthandler2.cxx b/oox/source/core/contexthandler2.cxx
index 8ce9784b..1613a3d 100644
--- a/oox/source/core/contexthandler2.cxx
+++ b/oox/source/core/contexthandler2.cxx
@@ -18,16 +18,20 @@
*/
#include <oox/core/contexthandler2.hxx>
#include <oox/core/xmlfilterbase.hxx>
#include <oox/helper/attributelist.hxx>
#include <oox/token/namespaces.hxx>
#include <oox/token/tokens.hxx>
#include <rtl/ustrbuf.hxx>
#include <o3tl/safeint.hxx>
#include <osl/diagnose.h>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
namespace oox::core {
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::xml::sax;
/** Information about a processed element. */
@@ -40,10 +44,11 @@ struct ElementInfo
explicit ElementInfo() : maChars( 0), mnElement( XML_TOKEN_INVALID ), mbTrimSpaces( false ) {}
};
ContextHandler2Helper::ContextHandler2Helper( bool bEnableTrimSpace ) :
ContextHandler2Helper::ContextHandler2Helper( bool bEnableTrimSpace, XmlFilterBase& rFilter ) :
mxContextStack( std::make_shared<ContextStack>() ),
mnRootStackSize( 0 ),
mbEnableTrimSpace( bEnableTrimSpace )
mbEnableTrimSpace( bEnableTrimSpace ),
mrFilter( rFilter )
{
pushElementInfo( XML_ROOT_CONTEXT );
}
@@ -51,7 +56,8 @@ ContextHandler2Helper::ContextHandler2Helper( bool bEnableTrimSpace ) :
ContextHandler2Helper::ContextHandler2Helper( const ContextHandler2Helper& rParent ) :
mxContextStack( rParent.mxContextStack ),
mnRootStackSize( rParent.mxContextStack->size() ),
mbEnableTrimSpace( rParent.mbEnableTrimSpace )
mbEnableTrimSpace( rParent.mbEnableTrimSpace ),
mrFilter(rParent.mrFilter)
{
}
@@ -188,6 +194,13 @@ ContextHandler2::~ContextHandler2()
Reference< XFastContextHandler > SAL_CALL ContextHandler2::createFastChildContext(
sal_Int32 nElement, const Reference< XFastAttributeList >& rxAttribs )
{
if( getNamespace( nElement ) == NMSP_mce ) // TODO for checking 'Ignorable'
{
if( prepareMceContext( nElement, AttributeList( rxAttribs ) ) )
return this;
return nullptr;
}
return implCreateChildContext( nElement, rxAttribs );
}
@@ -207,6 +220,72 @@ void SAL_CALL ContextHandler2::endFastElement( sal_Int32 nElement )
implEndElement( nElement );
}
bool ContextHandler2Helper::prepareMceContext( sal_Int32 nElement, const AttributeList& rAttribs )
{
switch( nElement )
{
case MCE_TOKEN( AlternateContent ):
addMCEState( MCE_STATE::Started );
break;
case MCE_TOKEN( Choice ):
{
if (isMCEStateEmpty() || getMCEState() != MCE_STATE::Started)
return false;
OUString aRequires = rAttribs.getString( XML_Requires, "none" );
// At this point we can't access namespaces as the correct xml filter
// is long gone. For now let's decide depending on a list of supported
// namespaces like we do in writerfilter
std::vector<OUString> aSupportedNS =
{
"a14", // Impress needs this to import math formulas.
"p14",
"p15",
"x12ac",
"v"
};
Reference<XServiceInfo> xModel(getDocFilter().getModel(), UNO_QUERY);
if (xModel.is() && xModel->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
{
// No a14 for Calc documents, it would cause duplicated shapes as-is.
auto it = std::find(aSupportedNS.begin(), aSupportedNS.end(), "a14");
if (it != aSupportedNS.end())
{
aSupportedNS.erase(it);
}
}
if (std::find(aSupportedNS.begin(), aSupportedNS.end(), aRequires) != aSupportedNS.end())
setMCEState( MCE_STATE::FoundChoice ) ;
else
return false;
}
break;
case MCE_TOKEN( Fallback ):
if( !isMCEStateEmpty() && getMCEState() == MCE_STATE::Started )
break;
return false;
default:
{
OUString str = rAttribs.getString( MCE_TOKEN( Ignorable ), OUString() );
if( !str.isEmpty() )
{
// Sequence< css::xml::FastAttribute > attrs = rAttribs.getFastAttributeList()->getFastAttributes();
// printf("MCE: %s\n", OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
// TODO: Check & Get the namespaces in "Ignorable"
// printf("NS: %d : %s\n", attrs.getLength(), OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
}
}
return false;
}
return true;
}
// oox.core.RecordContext interface -------------------------------------------
ContextHandlerRef ContextHandler2::createRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
diff --git a/oox/source/core/fragmenthandler2.cxx b/oox/source/core/fragmenthandler2.cxx
index 121f344..3ee410f 100644
--- a/oox/source/core/fragmenthandler2.cxx
+++ b/oox/source/core/fragmenthandler2.cxx
@@ -34,7 +34,7 @@ using namespace ::com::sun::star::xml::sax;
FragmentHandler2::FragmentHandler2( XmlFilterBase& rFilter, const OUString& rFragmentPath, bool bEnableTrimSpace ) :
FragmentHandler( rFilter, rFragmentPath ),
ContextHandler2Helper( bEnableTrimSpace )
ContextHandler2Helper( bEnableTrimSpace, rFilter )
{
}
@@ -54,72 +54,6 @@ void SAL_CALL FragmentHandler2::endDocument()
finalizeImport();
}
bool FragmentHandler2::prepareMceContext( sal_Int32 nElement, const AttributeList& rAttribs )
{
switch( nElement )
{
case MCE_TOKEN( AlternateContent ):
aMceState.push_back( MCE_STATE::Started );
break;
case MCE_TOKEN( Choice ):
{
if (aMceState.empty() || aMceState.back() != MCE_STATE::Started)
return false;
OUString aRequires = rAttribs.getString( XML_Requires, "none" );
// At this point we can't access namespaces as the correct xml filter
// is long gone. For now let's decide depending on a list of supported
// namespaces like we do in writerfilter
std::vector<OUString> aSupportedNS =
{
"a14", // Impress needs this to import math formulas.
"p14",
"p15",
"x12ac",
"v",
};
uno::Reference<lang::XServiceInfo> xModel(getFilter().getModel(), uno::UNO_QUERY);
if (xModel.is() && xModel->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
{
// No a14 for Calc documents, it would cause duplicated shapes as-is.
auto it = std::find(aSupportedNS.begin(), aSupportedNS.end(), "a14");
if (it != aSupportedNS.end())
{
aSupportedNS.erase(it);
}
}
if (std::find(aSupportedNS.begin(), aSupportedNS.end(), aRequires) != aSupportedNS.end())
aMceState.back() = MCE_STATE::FoundChoice;
else
return false;
}
break;
case MCE_TOKEN( Fallback ):
if( !aMceState.empty() && aMceState.back() == MCE_STATE::Started )
break;
return false;
default:
{
OUString str = rAttribs.getString( MCE_TOKEN( Ignorable ), OUString() );
if( !str.isEmpty() )
{
// Sequence< css::xml::FastAttribute > attrs = rAttribs.getFastAttributeList()->getFastAttributes();
// printf("MCE: %s\n", OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
// TODO: Check & Get the namespaces in "Ignorable"
// printf("NS: %d : %s\n", attrs.getLength(), OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
}
}
return false;
}
return true;
}
// com.sun.star.xml.sax.XFastContextHandler interface -------------------------
Reference< XFastContextHandler > SAL_CALL FragmentHandler2::createFastChildContext(
@@ -151,7 +85,7 @@ void SAL_CALL FragmentHandler2::endFastElement( sal_Int32 nElement )
switch( nElement )
{
case MCE_TOKEN( AlternateContent ):
aMceState.pop_back();
removeMCEState();
break;
}
diff --git a/oox/source/drawingml/graphicshapecontext.cxx b/oox/source/drawingml/graphicshapecontext.cxx
index 113e549..3ed00ed 100644
--- a/oox/source/drawingml/graphicshapecontext.cxx
+++ b/oox/source/drawingml/graphicshapecontext.cxx
@@ -214,6 +214,7 @@ ContextHandlerRef OleObjectGraphicDataContext::onCreateContext( sal_Int32 nEleme
mrOleObjectInfo.maName = rAttribs.getXString( XML_name, OUString() );
mrOleObjectInfo.maProgId = rAttribs.getXString( XML_progId, OUString() );
mrOleObjectInfo.mbShowAsIcon = rAttribs.getBool( XML_showAsIcon, false );
mrOleObjectInfo.mbHasPicture = false; // Initialize as false
return this;
}
break;
@@ -227,6 +228,7 @@ ContextHandlerRef OleObjectGraphicDataContext::onCreateContext( sal_Int32 nEleme
mrOleObjectInfo.mbAutoUpdate = rAttribs.getBool( XML_updateAutomatic, false );
break;
case PPT_TOKEN( pic ):
mrOleObjectInfo.mbHasPicture = true; // Set true if ole object has picture element.
return new GraphicShapeContext( *this, mpMasterShapePtr, mpShapePtr );
}
SAL_WARN("oox", "OleObjectGraphicDataContext::onCreateContext: unhandled element: "
@@ -234,6 +236,15 @@ ContextHandlerRef OleObjectGraphicDataContext::onCreateContext( sal_Int32 nEleme
return nullptr;
}
void OleObjectGraphicDataContext::onEndElement()
{
if( getCurrentElement() == PPT_TOKEN( oleObj ) && !isMCEStateEmpty() )
{
if( getMCEState() == MCE_STATE::FoundChoice && !mrOleObjectInfo.mbHasPicture )
setMCEState( MCE_STATE::Started );
}
}
DiagramGraphicDataContext::DiagramGraphicDataContext( ContextHandler2Helper const & rParent, const ShapePtr& pShapePtr )
: ShapeContext( rParent, ShapePtr(), pShapePtr )
{
diff --git a/sc/source/filter/oox/worksheetfragment.cxx b/sc/source/filter/oox/worksheetfragment.cxx
index 20e802d..a0e01c9 100644
--- a/sc/source/filter/oox/worksheetfragment.cxx
+++ b/sc/source/filter/oox/worksheetfragment.cxx
@@ -466,17 +466,17 @@ ContextHandlerRef WorksheetFragment::onCreateContext( sal_Int32 nElement, const
case XLS_TOKEN( oleObjects ):
if ( getCurrentElement() == XLS_TOKEN( controls ) )
{
if( aMceState.empty() || aMceState.back() == MCE_STATE::Started )
if( isMCEStateEmpty() || getMCEState() == MCE_STATE::Started )
{
if ( getCurrentElement() == XLS_TOKEN( oleObjects ) ) importOleObject( rAttribs );
else
importControl( rAttribs );
}
else if ( !aMceState.empty() && aMceState.back() == MCE_STATE::FoundChoice )
else if ( !isMCEStateEmpty() && getMCEState() == MCE_STATE::FoundChoice )
{
// reset the handling within 'Choice'
// this will force attempted handling in Fallback
aMceState.back() = MCE_STATE::Started;
setMCEState( MCE_STATE::Started );
}
}
break;
diff --git a/sd/qa/unit/data/pptx/tdf143222.pptx b/sd/qa/unit/data/pptx/tdf143222.pptx
new file mode 100644
index 0000000..63938d1
--- /dev/null
+++ b/sd/qa/unit/data/pptx/tdf143222.pptx
Binary files differ
diff --git a/sd/qa/unit/export-tests-ooxml3.cxx b/sd/qa/unit/export-tests-ooxml3.cxx
index 6632934..f773c36 100644
--- a/sd/qa/unit/export-tests-ooxml3.cxx
+++ b/sd/qa/unit/export-tests-ooxml3.cxx
@@ -22,6 +22,7 @@
#include <svx/svdotable.hxx>
#include <svx/xlineit0.hxx>
#include <svx/xlndsit.hxx>
#include <svx/svdoole2.hxx>
#include <rtl/ustring.hxx>
#include <com/sun/star/drawing/XDrawPage.hpp>
@@ -116,6 +117,7 @@ public:
void testTdf125560_textDeflate();
void testTdf125560_textInflateTop();
void testTdf96061_textHighlight();
void testTdf143222_embeddedWorksheet();
void testTdf142235_TestPlaceholderTextAlignment();
CPPUNIT_TEST_SUITE(SdOOXMLExportTest3);
@@ -185,6 +187,7 @@ public:
CPPUNIT_TEST(testTdf125560_textDeflate);
CPPUNIT_TEST(testTdf125560_textInflateTop);
CPPUNIT_TEST(testTdf96061_textHighlight);
CPPUNIT_TEST(testTdf143222_embeddedWorksheet);
CPPUNIT_TEST(testTdf142235_TestPlaceholderTextAlignment);
CPPUNIT_TEST_SUITE_END();
@@ -1743,6 +1746,33 @@ void SdOOXMLExportTest3::testTdf96061_textHighlight()
CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), aColor);
}
void SdOOXMLExportTest3::testTdf143222_embeddedWorksheet()
{
// Check import of embedded worksheet in slide.
::sd::DrawDocShellRef xDocShRef
= loadURL(m_directories.getURLFromSrc(u"sd/qa/unit/data/pptx/tdf143222.pptx"), PPTX);
const SdrPage* pPage = GetPage(1, xDocShRef.get());
const SdrOle2Obj* pOleObj = static_cast<SdrOle2Obj*>(pPage->GetObj(0));
CPPUNIT_ASSERT_MESSAGE("no object", pOleObj != nullptr);
// Without the fix we lost the graphic of ole object.
const Graphic* pGraphic = pOleObj->GetGraphic();
CPPUNIT_ASSERT_MESSAGE("no graphic", pGraphic != nullptr);
// Check export of embedded worksheet in slide.
xDocShRef = saveAndReload(xDocShRef.get(), PPTX);
pPage = GetPage(1, xDocShRef.get());
pOleObj = static_cast<SdrOle2Obj*>(pPage->GetObj(0));
CPPUNIT_ASSERT_MESSAGE("no object after the export", pOleObj != nullptr);
pGraphic = pOleObj->GetGraphic();
CPPUNIT_ASSERT_MESSAGE("no graphic after the export", pGraphic != nullptr);
xDocShRef->DoClose();
}
CPPUNIT_TEST_SUITE_REGISTRATION(SdOOXMLExportTest3);
CPPUNIT_PLUGIN_IMPLEMENT();