diff options
author | Gülşah Köse <gulsah.kose@collabora.com> | 2021-07-07 00:27:58 +0300 |
---|---|---|
committer | Gülşah Köse <gulsah.kose@collabora.com> | 2021-07-08 23:12:07 +0200 |
commit | 92a407b7f90a98704a238c5ffa3a3491eaf3263a (patch) | |
tree | 24eecc3bbf5e0547167787e71b4573d1af957116 | |
parent | 3175a7684982e7812e8071c595395eb3da3035fc (diff) |
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>
-rw-r--r-- | include/oox/core/contexthandler2.hxx | 19 | ||||
-rw-r--r-- | include/oox/core/fragmenthandler2.hxx | 11 | ||||
-rw-r--r-- | include/oox/drawingml/graphicshapecontext.hxx | 1 | ||||
-rw-r--r-- | include/oox/ole/oleobjecthelper.hxx | 1 | ||||
-rw-r--r-- | oox/source/core/contexthandler2.cxx | 85 | ||||
-rw-r--r-- | oox/source/core/fragmenthandler2.cxx | 70 | ||||
-rw-r--r-- | oox/source/drawingml/graphicshapecontext.cxx | 11 | ||||
-rw-r--r-- | sc/source/filter/oox/worksheetfragment.cxx | 6 | ||||
-rw-r--r-- | sd/qa/unit/data/pptx/tdf143222.pptx | bin | 0 -> 53172 bytes | |||
-rw-r--r-- | sd/qa/unit/export-tests-ooxml3.cxx | 30 |
10 files changed, 148 insertions, 86 deletions
diff --git a/include/oox/core/contexthandler2.hxx b/include/oox/core/contexthandler2.hxx index 4e256089ac8e..3a75aff5706a 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 86d1453f13a1..598426ee681e 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 4813d5fc9aed..ffd579f00bb1 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 d2506f3d4949..5b792f2048b1 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 8ce9784b99ef..1613a3d330cc 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 121f3443731f..3ee410fad854 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 113e5490c51d..3ed00edfd28c 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 20e802d96224..a0e01c915dcf 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 Binary files differnew file mode 100644 index 000000000000..63938d1bdc71 --- /dev/null +++ b/sd/qa/unit/data/pptx/tdf143222.pptx diff --git a/sd/qa/unit/export-tests-ooxml3.cxx b/sd/qa/unit/export-tests-ooxml3.cxx index 6632934e430e..f773c364f395 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(); |