From 2f47c9525f293481fdb07761de25cef83e76e2e8 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Wed, 29 Aug 2012 09:25:40 +0100 Subject: n#792778 DOCX import: parse group shapes in oox only Previously textframes inside groupshapes were tried to be imported as TextFrames, but then their addition to a GroupShape failed, so the text simply ended up as a normal paragraph. Fix this by importing members of groupshapes as drawinglayer objects, just like how the WW8 import does. Also fix two testcases, which implicitely tested that the groupshape VML element is ignored on import. (cherry picked from commits 7fe05dc95d0c9a584e07483c04b13c071d55293f and d5c934d150cb6cea5f96cbbee4fb5e8312bf027e) Conflicts: oox/source/vml/vmlshape.cxx oox/source/vml/vmltextbox.cxx Change-Id: I1a9fba8a5fd532203a825e55b1d5996277ea12fa --- oox/inc/oox/vml/vmlshape.hxx | 6 +- oox/inc/oox/vml/vmltextbox.hxx | 11 ++- oox/inc/oox/vml/vmltextboxcontext.hxx | 1 + oox/source/vml/vmlshape.cxx | 81 +++++++++++----------- oox/source/vml/vmlshapecontext.cxx | 11 +-- oox/source/vml/vmltextbox.cxx | 38 ++++++++-- oox/source/vml/vmltextboxcontext.cxx | 45 ++++++++++++ sw/qa/extras/ooxmltok/ooxmltok.cxx | 35 ++++------ sw/qa/extras/swmodeltestbase.hxx | 10 +++ .../source/ooxml/OOXMLFastContextHandler.cxx | 42 ++++++----- 10 files changed, 190 insertions(+), 90 deletions(-) diff --git a/oox/inc/oox/vml/vmlshape.hxx b/oox/inc/oox/vml/vmlshape.hxx index 2bdf16ed9879..68ae1cd50b76 100644 --- a/oox/inc/oox/vml/vmlshape.hxx +++ b/oox/inc/oox/vml/vmlshape.hxx @@ -208,7 +208,7 @@ struct ShapeModel ~ShapeModel(); /** Creates and returns a new shape textbox structure. */ - TextBox& createTextBox(); + TextBox& createTextBox(ShapeTypeModel& rModel); /** Creates and returns a new shape client data structure. */ ClientData& createClientData(); }; @@ -290,6 +290,10 @@ protected: implConvertAndInsert( const ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShapes >& rxShapes, const ::com::sun::star::awt::Rectangle& rShapeRect ) const; + /** Used by both RectangleShape and ComplexShape. */ + com::sun::star::uno::ReferencecreatePictureObject( + const com::sun::star::uno::Reference< com::sun::star::drawing::XShapes >& rxShapes, + const com::sun::star::awt::Rectangle& rShapeRect, rtl::OUString& rGraphicPath ) const; private: ::rtl::OUString maService; /// Name of the UNO shape service. diff --git a/oox/inc/oox/vml/vmltextbox.hxx b/oox/inc/oox/vml/vmltextbox.hxx index ed716de65dfd..3f40136a9fdd 100644 --- a/oox/inc/oox/vml/vmltextbox.hxx +++ b/oox/inc/oox/vml/vmltextbox.hxx @@ -33,10 +33,17 @@ #include #include "oox/helper/helper.hxx" #include "oox/dllapi.h" +#include + +namespace com { namespace sun { namespace star { + namespace drawing { class XShape; } +} } } namespace oox { namespace vml { +class ShapeTypeModel; + // ============================================================================ /** Font settings for a text portion in a textbox. */ @@ -71,7 +78,7 @@ struct TextPortionModel class OOX_DLLPUBLIC TextBox { public: - explicit TextBox(); + explicit TextBox(ShapeTypeModel& rTypeModel); /** Appends a new text portion to the textbox. */ void appendPortion( const TextFontModel& rFont, const ::rtl::OUString& rText ); @@ -82,7 +89,9 @@ public: const TextFontModel* getFirstFont() const; /** Returns the entire text of all text portions. */ ::rtl::OUString getText() const; + void convert(com::sun::star::uno::Reference xShape) const; + ShapeTypeModel& mrTypeModel; /// Text distance from the border (inset attribute of v:textbox), valid only if set. bool borderDistanceSet; int borderDistanceLeft, borderDistanceTop, borderDistanceRight, borderDistanceBottom; diff --git a/oox/inc/oox/vml/vmltextboxcontext.hxx b/oox/inc/oox/vml/vmltextboxcontext.hxx index abe6395ff6d9..9aa0998520c3 100644 --- a/oox/inc/oox/vml/vmltextboxcontext.hxx +++ b/oox/inc/oox/vml/vmltextboxcontext.hxx @@ -50,6 +50,7 @@ public: virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ); virtual void onCharacters( const ::rtl::OUString& rChars ); + virtual void onStartElement(const AttributeList& rAttribs); virtual void onEndElement(); private: diff --git a/oox/source/vml/vmlshape.cxx b/oox/source/vml/vmlshape.cxx index fde972cd907d..e3f2c62b4024 100644 --- a/oox/source/vml/vmlshape.cxx +++ b/oox/source/vml/vmlshape.cxx @@ -236,9 +236,9 @@ ShapeModel::~ShapeModel() { } -TextBox& ShapeModel::createTextBox() +TextBox& ShapeModel::createTextBox(ShapeTypeModel& rModel) { - mxTextBox.reset( new TextBox ); + mxTextBox.reset( new TextBox(rModel) ); return *mxTextBox; } @@ -480,6 +480,9 @@ Reference< XShape > SimpleShape::implConvertAndInsert( const Reference< XShapes PropertySet( xShape ).setAnyProperty(PROP_RelativeHeight, makeAny( nHeight ) ); } } + + if (getTextBox()) + getTextBox()->convert(xShape); } // Import Legacy Fragments (if any) @@ -504,6 +507,37 @@ Reference< XShape > SimpleShape::implConvertAndInsert( const Reference< XShapes return xShape; } +Reference< XShape > SimpleShape::createPictureObject( const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect, OUString& rGraphicPath ) const +{ + Reference< XShape > xShape = mrDrawing.createAndInsertXShape( "com.sun.star.drawing.GraphicObjectShape", rxShapes, rShapeRect ); + if( xShape.is() ) + { + XmlFilterBase& rFilter = mrDrawing.getFilter(); + OUString aGraphicUrl = rFilter.getGraphicHelper().importEmbeddedGraphicObject( rGraphicPath ); + PropertySet aPropSet( xShape ); + if( !aGraphicUrl.isEmpty() ) + { + aPropSet.setProperty( PROP_GraphicURL, aGraphicUrl ); + } + uno::Reference< lang::XServiceInfo > xServiceInfo(rxShapes, uno::UNO_QUERY); + // If the shape has an absolute position, set the properties accordingly, unless we're inside a group shape. + if ( maTypeModel.maPosition == "absolute" && !xServiceInfo->supportsService("com.sun.star.drawing.GroupShape")) + { + aPropSet.setProperty(PROP_HoriOrientPosition, rShapeRect.X); + aPropSet.setProperty(PROP_VertOrientPosition, rShapeRect.Y); + aPropSet.setProperty(PROP_Opaque, sal_False); + } + + lcl_SetAnchorType(aPropSet, maTypeModel); + + if ( maTypeModel.maPositionVerticalRelative == "page" ) + { + aPropSet.setProperty(PROP_VertOrientRelation, text::RelOrientation::PAGE_FRAME); + } + } + return xShape; +} + // ============================================================================ RectangleShape::RectangleShape( Drawing& rDrawing ) : @@ -513,21 +547,11 @@ RectangleShape::RectangleShape( Drawing& rDrawing ) : Reference RectangleShape::implConvertAndInsert(const Reference& rxShapes, const Rectangle& rShapeRect) const { - XmlFilterBase& rFilter = mrDrawing.getFilter(); OUString aGraphicPath = getGraphicPath(); // try to create a picture object if(!aGraphicPath.isEmpty()) - { - Reference xShape = mrDrawing.createAndInsertXShape(CREATE_OUSTRING("com.sun.star.drawing.GraphicObjectShape"), rxShapes, rShapeRect); - if (xShape.is()) - { - OUString aGraphicUrl = rFilter.getGraphicHelper().importEmbeddedGraphicObject(aGraphicPath); - PropertySet aPropSet(xShape); - aPropSet.setProperty(PROP_GraphicURL, aGraphicUrl); - } - return xShape; - } + return SimpleShape::createPictureObject(rxShapes, rShapeRect, aGraphicPath); // default: try to create a rectangle shape Reference xShape = SimpleShape::implConvertAndInsert(rxShapes, rShapeRect); @@ -704,33 +728,7 @@ Reference< XShape > ComplexShape::implConvertAndInsert( const Reference< XShapes // try to create a picture object if( !aGraphicPath.isEmpty() ) - { - Reference< XShape > xShape = mrDrawing.createAndInsertXShape( CREATE_OUSTRING( "com.sun.star.drawing.GraphicObjectShape" ), rxShapes, rShapeRect ); - if( xShape.is() ) - { - OUString aGraphicUrl = rFilter.getGraphicHelper().importEmbeddedGraphicObject( aGraphicPath ); - PropertySet aPropSet( xShape ); - if( !aGraphicUrl.isEmpty() ) - { - aPropSet.setProperty( PROP_GraphicURL, aGraphicUrl ); - } - // If the shape has an absolute position, set the properties accordingly. - if ( maTypeModel.maPosition == "absolute" ) - { - aPropSet.setProperty(PROP_HoriOrientPosition, rShapeRect.X); - aPropSet.setProperty(PROP_VertOrientPosition, rShapeRect.Y); - aPropSet.setProperty(PROP_Opaque, sal_False); - } - - lcl_SetAnchorType(aPropSet, maTypeModel); - - if ( maTypeModel.maPositionVerticalRelative == "page" ) - { - aPropSet.setProperty(PROP_VertOrientRelation, text::RelOrientation::PAGE_FRAME); - } - } - return xShape; - } + return SimpleShape::createPictureObject(rxShapes, rShapeRect, aGraphicPath); // default: try to create a custom shape return CustomShape::implConvertAndInsert( rxShapes, rShapeRect ); @@ -788,6 +786,9 @@ Reference< XShape > GroupShape::implConvertAndInsert( const Reference< XShapes > catch( Exception& ) { } + // Make sure group shapes are inline as well, unless there is an explicit different style. + PropertySet aPropertySet(xGroupShape); + lcl_SetAnchorType(aPropertySet, maTypeModel); return xGroupShape; } diff --git a/oox/source/vml/vmlshapecontext.cxx b/oox/source/vml/vmlshapecontext.cxx index ae00af2de976..4e21ce9b2fa8 100644 --- a/oox/source/vml/vmlshapecontext.cxx +++ b/oox/source/vml/vmlshapecontext.cxx @@ -407,10 +407,13 @@ ContextHandlerRef ShapeContext::onCreateContext( sal_Int32 nElement, const Attri if( isRootElement() ) switch( nElement ) { case VML_TOKEN( textbox ): - // Custom shape in Writer with a textbox are transformed into a frame - dynamic_cast( mrShape ).setService( - "com.sun.star.text.TextFrame"); - return new TextBoxContext( *this, mrShapeModel.createTextBox(), rAttribs, + if (getParentElement() != VML_TOKEN( group )) + { + // Custom shape in Writer with a textbox are transformed into a frame + dynamic_cast( mrShape ).setService( + "com.sun.star.text.TextFrame"); + } + return new TextBoxContext( *this, mrShapeModel.createTextBox(mrShape.getTypeModel()), rAttribs, mrShape.getDrawing().getFilter().getGraphicHelper()); case VMLX_TOKEN( ClientData ): return new ClientDataContext( *this, mrShapeModel.createClientData(), rAttribs ); diff --git a/oox/source/vml/vmltextbox.cxx b/oox/source/vml/vmltextbox.cxx index b4dc7dbdd925..965cdfccda90 100644 --- a/oox/source/vml/vmltextbox.cxx +++ b/oox/source/vml/vmltextbox.cxx @@ -29,6 +29,8 @@ #include "oox/vml/vmltextbox.hxx" #include +#include +#include namespace oox { namespace vml { @@ -37,6 +39,7 @@ namespace vml { using ::rtl::OUString; using ::rtl::OUStringBuffer; +using namespace com::sun::star; // ============================================================================ @@ -52,10 +55,9 @@ TextPortionModel::TextPortionModel( const TextFontModel& rFont, const OUString& { } -// ============================================================================ - -TextBox::TextBox() - : borderDistanceSet( false ) +TextBox::TextBox(ShapeTypeModel& rTypeModel) + : mrTypeModel(rTypeModel), + borderDistanceSet( false ) { } @@ -77,7 +79,33 @@ OUString TextBox::getText() const return aBuffer.makeStringAndClear(); } -// ============================================================================ +void TextBox::convert(uno::Reference xShape) const +{ + uno::Reference xTextAppend(xShape, uno::UNO_QUERY); + for (PortionVector::const_iterator aIt = maPortions.begin(), aEnd = maPortions.end(); aIt != aEnd; ++aIt) + { + beans::PropertyValue aPropertyValue; + std::vector aPropVec; + const TextFontModel& rFont = aIt->maFont; + if (rFont.mobBold.has()) + { + aPropertyValue.Name = "CharWeight"; + aPropertyValue.Value = uno::makeAny(rFont.mobBold.get() ? awt::FontWeight::BOLD : awt::FontWeight::NORMAL); + aPropVec.push_back(aPropertyValue); + } + if (rFont.monSize.has()) + { + aPropertyValue.Name = "CharHeight"; + aPropertyValue.Value = uno::makeAny(double(rFont.monSize.get()) / 2.); + aPropVec.push_back(aPropertyValue); + } + uno::Sequence aPropSeq(aPropVec.size()); + beans::PropertyValue* pValues = aPropSeq.getArray(); + for (std::vector::iterator i = aPropVec.begin(); i != aPropVec.end(); ++i) + *pValues++ = *i; + xTextAppend->appendTextPortion(aIt->maText, aPropSeq); + } +} } // namespace vml } // namespace oox diff --git a/oox/source/vml/vmltextboxcontext.cxx b/oox/source/vml/vmltextboxcontext.cxx index 3f188e35aece..a808f43729f7 100644 --- a/oox/source/vml/vmltextboxcontext.cxx +++ b/oox/source/vml/vmltextboxcontext.cxx @@ -28,6 +28,8 @@ #include "oox/vml/vmlformatting.hxx" #include "oox/vml/vmltextboxcontext.hxx" +#include "oox/vml/vmlshape.hxx" +#include namespace oox { namespace vml { @@ -77,7 +79,22 @@ TextPortionContext::TextPortionContext( ContextHandler2Helper& rParent, OSL_ENSURE( !maFont.mobStrikeout, "TextPortionContext::TextPortionContext - nested elements" ); maFont.mobStrikeout = true; break; + case OOX_TOKEN(dml, blip): + { + OptValue oRelId = rAttribs.getString(R_TOKEN(embed)); + if (oRelId.has()) + mrTextBox.mrTypeModel.moGraphicPath = getFragmentPathFromRelId(oRelId.get()); + } + break; + case VML_TOKEN(imagedata): + { + OptValue oRelId = rAttribs.getString(R_TOKEN(id)); + if (oRelId.has()) + mrTextBox.mrTypeModel.moGraphicPath = getFragmentPathFromRelId(oRelId.get()); + } + break; case XML_span: + case OOX_TOKEN(doc, r): break; default: OSL_ENSURE( false, "TextPortionContext::TextPortionContext - unknown element" ); @@ -87,11 +104,16 @@ TextPortionContext::TextPortionContext( ContextHandler2Helper& rParent, ContextHandlerRef TextPortionContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) { OSL_ENSURE( nElement != XML_font, "TextPortionContext::onCreateContext - nested elements" ); + if (getNamespace(getCurrentElement()) == NMSP_doc) + return this; return new TextPortionContext( *this, mrTextBox, maFont, nElement, rAttribs ); } void TextPortionContext::onCharacters( const OUString& rChars ) { + if (getNamespace(getCurrentElement()) == NMSP_doc && getCurrentElement() != OOX_TOKEN(doc, t)) + return; + switch( getCurrentElement() ) { case XML_span: @@ -103,8 +125,24 @@ void TextPortionContext::onCharacters( const OUString& rChars ) } } +void TextPortionContext::onStartElement(const AttributeList& rAttribs) +{ + switch (getCurrentElement()) + { + case OOX_TOKEN(doc, b): + maFont.mobBold = true; + break; + case OOX_TOKEN(doc, sz): + maFont.monSize = rAttribs.getInteger( OOX_TOKEN(doc, val) ); + break; + } +} + void TextPortionContext::onEndElement() { + if (getNamespace(getCurrentElement()) == NMSP_doc && getCurrentElement() != OOX_TOKEN(doc, t)) + return; + /* A child element without own child elements may contain a single space character, for example: @@ -158,10 +196,17 @@ ContextHandlerRef TextBoxContext::onCreateContext( sal_Int32 nElement, const Att { case VML_TOKEN( textbox ): if( nElement == XML_div ) return this; + else if (nElement == OOX_TOKEN(doc, txbxContent)) return this; break; case XML_div: if( nElement == XML_font ) return new TextPortionContext( *this, mrTextBox, TextFontModel(), nElement, rAttribs ); break; + case OOX_TOKEN(doc, txbxContent): + if (nElement == OOX_TOKEN(doc, p)) return this; + break; + case OOX_TOKEN(doc, p): + if (nElement == OOX_TOKEN(doc, r)) return new TextPortionContext( *this, mrTextBox, TextFontModel(), nElement, rAttribs ); + break; } return 0; } diff --git a/sw/qa/extras/ooxmltok/ooxmltok.cxx b/sw/qa/extras/ooxmltok/ooxmltok.cxx index 6dd79277b92e..e812c063d107 100644 --- a/sw/qa/extras/ooxmltok/ooxmltok.cxx +++ b/sw/qa/extras/ooxmltok/ooxmltok.cxx @@ -228,29 +228,19 @@ void Test::testN751077() load( "n751077.docx" ); /* -enum = ThisComponent.Text.createEnumeration -enum.NextElement -para = enum.NextElement -xray para.String -xray para.PageStyleName +xray ThisComponent.DrawPage(1).getByIndex(0).String +xray ThisComponent.DrawPage(1).getByIndex(0).Anchor.PageStyleName */ - uno::Reference textDocument(mxComponent, uno::UNO_QUERY); - uno::Reference paraEnumAccess(textDocument->getText(), uno::UNO_QUERY); - // list of paragraphs - uno::Reference paraEnum = paraEnumAccess->createEnumeration(); - // go to 1st paragraph - (void) paraEnum->nextElement(); - // get the 2nd paragraph - uno::Reference paragraph(paraEnum->nextElement(), uno::UNO_QUERY); - // text of the paragraph - uno::Reference text(paragraph, uno::UNO_QUERY); - CPPUNIT_ASSERT_EQUAL( OUString( "TEXT1" ), text->getString()); - // we want to test the paragraph is on the first page (it was put onto another page without the fix), + uno::Reference xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference xDrawPageSupplier(xTextDocument, uno::UNO_QUERY); + uno::Reference xDrawPage = xDrawPageSupplier->getDrawPage(); + uno::Reference xShapes(xDrawPage->getByIndex(1), uno::UNO_QUERY); + uno::Reference xShape(xShapes->getByIndex(0), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("TEXT1"), xShape->getString()); + // we want to test the textbox is on the first page (it was put onto another page without the fix), // use a small trick and instead of checking the page layout, check the page style - uno::Reference paragraphProperties(paragraph, uno::UNO_QUERY); - OUString pageStyle; - paragraphProperties->getPropertyValue( "PageStyleName" ) >>= pageStyle; - CPPUNIT_ASSERT_EQUAL( OUString( "First Page" ), pageStyle ); + uno::Reference xTextContent(xShape, uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("First Page"), getProperty(xTextContent->getAnchor(), "PageStyleName")); } void Test::testN705956_1() @@ -266,8 +256,9 @@ xray graphic.Size uno::Reference drawPageSupplier(textDocument, uno::UNO_QUERY); uno::Reference drawPage = drawPageSupplier->getDrawPage(); CPPUNIT_ASSERT_EQUAL( sal_Int32( 1 ), drawPage->getCount()); + uno::Reference shapes(drawPage->getByIndex(0), uno::UNO_QUERY); uno::Reference image; - drawPage->getByIndex(0) >>= image; + shapes->getByIndex(0) >>= image; uno::Reference imageProperties(image, uno::UNO_QUERY); uno::Reference graphic; imageProperties->getPropertyValue( "Graphic" ) >>= graphic; diff --git a/sw/qa/extras/swmodeltestbase.hxx b/sw/qa/extras/swmodeltestbase.hxx index 347d408c9b30..46c54262a700 100644 --- a/sw/qa/extras/swmodeltestbase.hxx +++ b/sw/qa/extras/swmodeltestbase.hxx @@ -26,6 +26,7 @@ */ #include +#include #include #include @@ -74,6 +75,15 @@ protected: return aBuf.getLength(); } + template< typename T > + T getProperty( uno::Reference< uno::XInterface > obj, const rtl::OUString& name ) const + { + uno::Reference< beans::XPropertySet > properties( obj, uno::UNO_QUERY ); + T data = T(); + properties->getPropertyValue( name ) >>= data; + return data; + } + uno::Reference mxComponent; }; diff --git a/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx b/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx index cc02c627220e..46ff0c54bfa7 100644 --- a/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx +++ b/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx @@ -2078,6 +2078,7 @@ OOXMLFastContextHandlerShape::lcl_createFastChildContext { uno::Reference< xml::sax::XFastContextHandler > xContextHandler; + bool bGroupShape = Element == Token_t(NS_vml | OOXML_group); sal_uInt32 nNamespace = Element & 0xffff0000; switch (nNamespace) @@ -2085,26 +2086,33 @@ OOXMLFastContextHandlerShape::lcl_createFastChildContext case NS_wordprocessingml: case NS_vml_wordprocessingDrawing: case NS_office: - xContextHandler.set(OOXMLFactory::getInstance()->createFastChildContextFromStart(this, Element)); - break; + if (!bGroupShape) + xContextHandler.set(OOXMLFactory::getInstance()->createFastChildContextFromStart(this, Element)); + // no break; default: - if (mrShapeContext.is()) + if (!xContextHandler.is()) { - uno::Reference pChildContext = - mrShapeContext->createFastChildContext(Element, Attribs); - - OOXMLFastContextHandlerWrapper * pWrapper = - new OOXMLFastContextHandlerWrapper(this, pChildContext); - - pWrapper->addNamespace(NS_wordprocessingml); - pWrapper->addNamespace(NS_vml_wordprocessingDrawing); - pWrapper->addNamespace(NS_office); - pWrapper->addToken( NS_vml|OOXML_textbox ); - - xContextHandler.set(pWrapper); + if (mrShapeContext.is()) + { + uno::Reference pChildContext = + mrShapeContext->createFastChildContext(Element, Attribs); + + OOXMLFastContextHandlerWrapper * pWrapper = + new OOXMLFastContextHandlerWrapper(this, pChildContext); + + if (!bGroupShape) + { + pWrapper->addNamespace(NS_wordprocessingml); + pWrapper->addNamespace(NS_vml_wordprocessingDrawing); + pWrapper->addNamespace(NS_office); + pWrapper->addToken( NS_vml|OOXML_textbox ); + } + + xContextHandler.set(pWrapper); + } + else + xContextHandler.set(this); } - else - xContextHandler.set(this); break; } -- cgit v1.2.3