diff options
-rw-r--r-- | include/oox/vml/vmlshape.hxx | 1 | ||||
-rw-r--r-- | include/oox/vml/vmltextbox.hxx | 1 | ||||
-rw-r--r-- | oox/source/vml/vmlshape.cxx | 77 | ||||
-rw-r--r-- | oox/source/vml/vmlshapecontext.cxx | 1 | ||||
-rw-r--r-- | oox/source/vml/vmltextboxcontext.cxx | 2 | ||||
-rw-r--r-- | writerfilter/source/dmapper/DomainMapper_Impl.cxx | 77 |
6 files changed, 151 insertions, 8 deletions
diff --git a/include/oox/vml/vmlshape.hxx b/include/oox/vml/vmlshape.hxx index 6cc180cbdc21..e4b5c9288920 100644 --- a/include/oox/vml/vmlshape.hxx +++ b/include/oox/vml/vmlshape.hxx @@ -58,6 +58,7 @@ const sal_Int32 VML_CLIENTDATA_FORMULA = 4; struct OOX_DLLPUBLIC ShapeTypeModel { OUString maShapeId; ///< Unique identifier of the shape. + OUString maLegacyId; ///< Plaintext identifier of the shape. OUString maShapeName; ///< Name of the shape, if present. OptValue< sal_Int32 > moShapeType; ///< Builtin shape type identifier. diff --git a/include/oox/vml/vmltextbox.hxx b/include/oox/vml/vmltextbox.hxx index 3ff88d48804a..6b14c4c900b3 100644 --- a/include/oox/vml/vmltextbox.hxx +++ b/include/oox/vml/vmltextbox.hxx @@ -95,6 +95,7 @@ public: bool borderDistanceSet; int borderDistanceLeft, borderDistanceTop, borderDistanceRight, borderDistanceBottom; OUString maLayoutFlow; + OUString msNextTextbox; private: typedef ::std::vector< TextPortionModel > PortionVector; diff --git a/oox/source/vml/vmlshape.cxx b/oox/source/vml/vmlshape.cxx index 55b17d99a741..2c2f6eb4a685 100644 --- a/oox/source/vml/vmlshape.cxx +++ b/oox/source/vml/vmlshape.cxx @@ -312,21 +312,64 @@ Reference< XShape > ShapeBase::convertAndInsert( const Reference< XShapes >& rxS if( aShapeProp.hasProperty( PROP_Name ) ) aShapeProp.setProperty( PROP_Name, getShapeName() ); uno::Reference< lang::XServiceInfo > xSInfo( xShape, uno::UNO_QUERY_THROW ); + + OUString sLinkChainName = getTypeModel().maLegacyId; + sal_Int32 id = 0; + sal_Int32 idPos = sLinkChainName.indexOf("_x"); + sal_Int32 seq = 0; + sal_Int32 seqPos = sLinkChainName.indexOf("_s",idPos); + if( idPos >= 0 && idPos < seqPos ) + { + id = sLinkChainName.copy(idPos+2,seqPos-idPos+2).toInt32(); + seq = sLinkChainName.copy(seqPos+2).toInt32(); + } + + OUString s_mso_next_textbox; + if( getTextBox() ) + s_mso_next_textbox = getTextBox()->msNextTextbox; + if( s_mso_next_textbox.startsWith("#") ) + s_mso_next_textbox = s_mso_next_textbox.copy(1); + if (xSInfo->supportsService("com.sun.star.text.TextFrame")) { uno::Sequence<beans::PropertyValue> aGrabBag; uno::Reference<beans::XPropertySet> propertySet (xShape, uno::UNO_QUERY); propertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag; - sal_Int32 length = aGrabBag.getLength(); + sal_Int32 length; + length = aGrabBag.getLength(); aGrabBag.realloc( length+1 ); aGrabBag[length].Name = "VML-Z-ORDER"; aGrabBag[length].Value = uno::makeAny( maTypeModel.maZIndex.toInt32() ); + + if( !s_mso_next_textbox.isEmpty() ) + { + length = aGrabBag.getLength(); + aGrabBag.realloc( length+1 ); + aGrabBag[length].Name = "mso-next-textbox"; + aGrabBag[length].Value = uno::makeAny( s_mso_next_textbox ); + } + + if( !sLinkChainName.isEmpty() ) + { + length = aGrabBag.getLength(); + aGrabBag.realloc( length+4 ); + aGrabBag[length].Name = "TxbxHasLink"; + aGrabBag[length].Value = uno::makeAny( true ); + aGrabBag[length+1].Name = "Txbx-Id"; + aGrabBag[length+1].Value = uno::makeAny( id ); + aGrabBag[length+2].Name = "Txbx-Seq"; + aGrabBag[length+2].Value = uno::makeAny( seq ); + aGrabBag[length+3].Name = "LinkChainName"; + aGrabBag[length+3].Value = uno::makeAny( sLinkChainName ); + } + if(!(maTypeModel.maRotation).isEmpty()) { - aGrabBag.realloc( length+2 ); - aGrabBag[length+1].Name = "mso-rotation-angle"; - aGrabBag[length+1].Value = uno::makeAny(sal_Int32(NormAngle360((maTypeModel.maRotation.toInt32()) * -100))); + length = aGrabBag.getLength(); + aGrabBag.realloc( length+1 ); + aGrabBag[length].Name = "mso-rotation-angle"; + aGrabBag[length].Value = uno::makeAny(sal_Int32(NormAngle360((maTypeModel.maRotation.toInt32()) * -100))); } propertySet->setPropertyValue( "FrameInteropGrabBag", uno::makeAny(aGrabBag) ); sal_Int32 backColorTransparency = 0; @@ -346,10 +389,34 @@ Reference< XShape > ShapeBase::convertAndInsert( const Reference< XShapes >& rxS uno::Sequence<beans::PropertyValue> aGrabBag; uno::Reference<beans::XPropertySet> propertySet (xShape, uno::UNO_QUERY); propertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag; - sal_Int32 length = aGrabBag.getLength(); + sal_Int32 length; + + length = aGrabBag.getLength(); aGrabBag.realloc( length+1 ); aGrabBag[length].Name = "VML-Z-ORDER"; aGrabBag[length].Value = uno::makeAny( maTypeModel.maZIndex.toInt32() ); + + if( !s_mso_next_textbox.isEmpty() ) + { + length = aGrabBag.getLength(); + aGrabBag.realloc( length+1 ); + aGrabBag[length].Name = "mso-next-textbox"; + aGrabBag[length].Value = uno::makeAny( s_mso_next_textbox ); + } + + if( !sLinkChainName.isEmpty() ) + { + length = aGrabBag.getLength(); + aGrabBag.realloc( length+4 ); + aGrabBag[length].Name = "TxbxHasLink"; + aGrabBag[length].Value = uno::makeAny( true ); + aGrabBag[length+1].Name = "Txbx-Id"; + aGrabBag[length+1].Value = uno::makeAny( id ); + aGrabBag[length+2].Name = "Txbx-Seq"; + aGrabBag[length+2].Value = uno::makeAny( seq ); + aGrabBag[length+3].Name = "LinkChainName"; + aGrabBag[length+3].Value = uno::makeAny( sLinkChainName ); + } propertySet->setPropertyValue( "InteropGrabBag", uno::makeAny(aGrabBag) ); } } diff --git a/oox/source/vml/vmlshapecontext.cxx b/oox/source/vml/vmlshapecontext.cxx index 2f69efe3e7f2..4ea10e87bd98 100644 --- a/oox/source/vml/vmlshapecontext.cxx +++ b/oox/source/vml/vmlshapecontext.cxx @@ -269,6 +269,7 @@ ShapeTypeContext::ShapeTypeContext( ContextHandler2Helper& rParent, ShapeType& r // shape identifier and shape name bool bHasOspid = rAttribs.hasAttribute( O_TOKEN( spid ) ); mrTypeModel.maShapeId = rAttribs.getXString( bHasOspid ? O_TOKEN( spid ) : XML_id, OUString() ); + mrTypeModel.maLegacyId = rAttribs.getString( XML_id, OUString() ); OSL_ENSURE( !mrTypeModel.maShapeId.isEmpty(), "ShapeTypeContext::ShapeTypeContext - missing shape identifier" ); // if the o:spid attribute exists, the id attribute contains the user-defined shape name if( bHasOspid ) diff --git a/oox/source/vml/vmltextboxcontext.cxx b/oox/source/vml/vmltextboxcontext.cxx index 8a1d5fb3077c..d1a8b1256d74 100644 --- a/oox/source/vml/vmltextboxcontext.cxx +++ b/oox/source/vml/vmltextboxcontext.cxx @@ -212,6 +212,8 @@ TextBoxContext::TextBoxContext( ContextHandler2Helper& rParent, TextBox& rTextBo rTextBox.mrTypeModel.mbAutoHeight = true; else if (aName == "mso-layout-flow-alt") rTextBox.mrTypeModel.maLayoutFlowAlt = aValue; + else if (aName == "mso-next-textbox") + rTextBox.msNextTextbox = aValue; else SAL_WARN("oox", "unhandled style property: " << aName); } diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index 38a4c075b116..33540579eb7e 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -2457,7 +2457,9 @@ void DomainMapper_Impl::ChainTextFrames() css::uno::Reference< css::drawing::XShape > xShape; sal_Int32 nId; sal_Int32 nSeq; - TextFramesForChaining(): xShape(0), nId(0), nSeq(0) {} + OUString s_mso_next_textbox; + bool bShapeNameSet; + TextFramesForChaining(): xShape(0), nId(0), nSeq(0), bShapeNameSet(false) {} } ; typedef std::map <OUString, TextFramesForChaining> ChainMap; @@ -2472,12 +2474,18 @@ void DomainMapper_Impl::ChainTextFrames() { uno::Reference<text::XTextContent> xTextContent(*iter, uno::UNO_QUERY_THROW); uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY); + uno::Reference<beans::XPropertySetInfo> xPropertySetInfo; + if( xPropertySet.is() ) + xPropertySetInfo = xPropertySet->getPropertySetInfo(); uno::Sequence<beans::PropertyValue> aGrabBag; uno::Reference<lang::XServiceInfo> xServiceInfo(xPropertySet, uno::UNO_QUERY); TextFramesForChaining aChainStruct = TextFramesForChaining(); OUString sShapeName; + OUString sLinkChainName; + //The chaining name and the shape name CAN be different in .docx. + //MUST use LinkDisplayName/ChainName as the shape name for establishing links. if ( xServiceInfo->supportsService("com.sun.star.text.TextFrame") ) { xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag; @@ -2491,8 +2499,68 @@ void DomainMapper_Impl::ChainTextFrames() lcl_getGrabBagValue( aGrabBag, "Txbx-Id") >>= aChainStruct.nId; lcl_getGrabBagValue( aGrabBag, "Txbx-Seq") >>= aChainStruct.nSeq; - aChainStruct.xShape = *iter; - aTextFramesForChainingHelper[sShapeName] = aChainStruct; + lcl_getGrabBagValue( aGrabBag, "LinkChainName") >>= sLinkChainName; + lcl_getGrabBagValue( aGrabBag, "mso-next-textbox") >>= aChainStruct.s_mso_next_textbox; + + //Sometimes the shape names have not been imported. If not, we may have a fallback name. + //Set name later, only if required for linking. + if( sShapeName.isEmpty() ) + aChainStruct.bShapeNameSet = false; + else + { + aChainStruct.bShapeNameSet = true; + sLinkChainName = sShapeName; + } + + if( !sLinkChainName.isEmpty() ) + { + aChainStruct.xShape = *iter; + aTextFramesForChainingHelper[sLinkChainName] = aChainStruct; + } + } + + //if mso-next-textbox tags are provided, create those vml-style links first. Afterwards we will make dml-style id/seq links. + for (ChainMap::iterator msoIter= aTextFramesForChainingHelper.begin(); msoIter != aTextFramesForChainingHelper.end(); ++msoIter) + { + //if no mso-next-textbox, we are done. + //if it points to itself, we are done. + if( !msoIter->second.s_mso_next_textbox.isEmpty() + && !msoIter->second.s_mso_next_textbox.equals(msoIter->first) ) + { + ChainMap::iterator nextFinder=aTextFramesForChainingHelper.find(msoIter->second.s_mso_next_textbox); + if( nextFinder != aTextFramesForChainingHelper.end() ) + { + //if the frames have no name yet, then set them. LinkDisplayName / ChainName are read-only. + if( !msoIter->second.bShapeNameSet ) + { + uno::Reference< container::XNamed > xNamed( msoIter->second.xShape, uno::UNO_QUERY ); + if ( xNamed.is() ) + { + xNamed->setName( msoIter->first ); + msoIter->second.bShapeNameSet = true; + } + } + if( !nextFinder->second.bShapeNameSet ) + { + uno::Reference< container::XNamed > xNamed( nextFinder->second.xShape, uno::UNO_QUERY ); + if ( xNamed.is() ) + { + xNamed->setName( nextFinder->first ); + nextFinder->second.bShapeNameSet = true; + } + } + + uno::Reference<text::XTextContent> xTextContent(msoIter->second.xShape, uno::UNO_QUERY_THROW); + uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY); + + //The reverse chaining happens automatically, so only one direction needs to be set + xPropertySet->setPropertyValue(sChainNextName, uno::makeAny(nextFinder->first)); + + //the last item in an mso-next-textbox chain is indistinguishable from id/seq items. Now that it is handled, remove it. + if( nextFinder->second.s_mso_next_textbox.isEmpty() ) + aTextFramesForChainingHelper.erase(nextFinder->first); + } + } } //TODO: Perhaps allow reverse sequences when mso-layout-flow-alt = "bottom-to-top" @@ -2501,6 +2569,8 @@ void DomainMapper_Impl::ChainTextFrames() //Finally - go through and attach the chains based on matching ID and incremented sequence number (dml-style). for (ChainMap::iterator outer_iter= aTextFramesForChainingHelper.begin(); outer_iter != aTextFramesForChainingHelper.end(); ++outer_iter) { + if( outer_iter->second.s_mso_next_textbox.isEmpty() ) //non-empty ones already handled earlier - so skipping them now. + { for (ChainMap::iterator inner_iter=aTextFramesForChainingHelper.begin(); inner_iter != aTextFramesForChainingHelper.end(); ++inner_iter) { if ( inner_iter->second.nId == outer_iter->second.nId ) @@ -2516,6 +2586,7 @@ void DomainMapper_Impl::ChainTextFrames() } } } + } } m_vTextFramesForChaining.clear(); //clear the vector } |