summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Luth <justin_luth@sil.org>2015-07-02 14:00:24 +0300
committerMiklos Vajna <vmiklos@collabora.co.uk>2015-07-07 07:26:20 +0000
commitf408edabd52f865abb29401dbff995921b394e6c (patch)
treeeab83b6bfa42cb4e44603859f52571632df697f7
parent21b15296d9fb2a5c47c890cb7423aaa49b22131b (diff)
tdf#87348 implement nonsequential and mso-next-textbox textbox chaining
Change-Id: I017049a8f3578ad4c2a1f549be1c683f98c20318 Reviewed-on: https://gerrit.libreoffice.org/16691 Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk> Tested-by: Miklos Vajna <vmiklos@collabora.co.uk>
-rw-r--r--include/oox/vml/vmlshape.hxx1
-rw-r--r--include/oox/vml/vmltextbox.hxx1
-rw-r--r--oox/source/vml/vmlshape.cxx77
-rw-r--r--oox/source/vml/vmlshapecontext.cxx1
-rw-r--r--oox/source/vml/vmltextboxcontext.cxx2
-rw-r--r--writerfilter/source/dmapper/DomainMapper_Impl.cxx172
6 files changed, 189 insertions, 65 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 1c826a1ee971..15b37d6c5e84 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) );
}
@@ -337,10 +380,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 71905bad08df..2353b0aa2d08 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 2f9637508055..f043ba13c777 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -2408,92 +2408,144 @@ static uno::Any lcl_getGrabBagValue( const uno::Sequence<beans::PropertyValue>&
//Link the text frames.
void DomainMapper_Impl::ChainTextFrames()
{
- if( 0 == m_vTextFramesForChaining.size() )
+ //can't link textboxes if there are not even two of them...
+ if( 2 > m_vTextFramesForChaining.size() )
return ;
+ struct TextFramesForChaining {
+ css::uno::Reference< css::drawing::XShape > xShape;
+ sal_Int32 nId;
+ sal_Int32 nSeq;
+ OUString s_mso_next_textbox;
+ bool bShapeNameSet;
+ TextFramesForChaining(): xShape(0), nId(0), nSeq(0), bShapeNameSet(false) {}
+ } ;
+ typedef std::map <OUString, TextFramesForChaining> ChainMap;
+
try
{
- bool bIsTxbxChained = false ;
- sal_Int32 nTxbxId1 = 0 ; //holds id for the shape in outer loop
- sal_Int32 nTxbxId2 = 0 ; //holds id for the shape in inner loop
- sal_Int32 nTxbxSeq1 = 0 ; //holds seq number for the shape in outer loop
- sal_Int32 nTxbxSeq2 = 0 ; //holds seq number for the shape in inner loop
- OUString sName1 ; //holds the text box Name for the shape in outer loop
- OUString sName2 ; //holds the text box Name for the shape in outer loop
+ ChainMap aTextFramesForChainingHelper;
OUString sChainNextName("ChainNextName");
- OUString sChainPrevName("ChainPrevName");
- for( std::vector<uno::Reference< drawing::XShape > >::iterator outer_itr = m_vTextFramesForChaining.begin();
- outer_itr != m_vTextFramesForChaining.end(); )
+ //learn about ALL of the textboxes and their chaining values first - because frames are processed in no specific order.
+ std::vector<uno::Reference< drawing::XShape > >::iterator iter;
+ for( iter = m_vTextFramesForChaining.begin(); iter != m_vTextFramesForChaining.end(); ++iter )
{
- bIsTxbxChained = false ;
- uno::Reference<text::XTextContent> xTextContent1(*outer_itr, uno::UNO_QUERY_THROW);
- uno::Reference<beans::XPropertySet> xPropertySet1(xTextContent1, uno::UNO_QUERY);
- uno::Sequence<beans::PropertyValue> aGrabBag1;
- uno::Reference<lang::XServiceInfo> xServiceInfo1(xPropertySet1, uno::UNO_QUERY);
- if (xServiceInfo1->supportsService("com.sun.star.text.TextFrame"))
+ 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") )
{
- xPropertySet1->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag1;
- xPropertySet1->getPropertyValue("LinkDisplayName") >>= sName1;
+ xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
+ xPropertySet->getPropertyValue("LinkDisplayName") >>= sShapeName;
}
else
{
- xPropertySet1->getPropertyValue("InteropGrabBag") >>= aGrabBag1;
- xPropertySet1->getPropertyValue("ChainName") >>= sName1;
+ xPropertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
+ xPropertySet->getPropertyValue("ChainName") >>= sShapeName;
}
- lcl_getGrabBagValue( aGrabBag1, "Txbx-Id") >>= nTxbxId1;
- lcl_getGrabBagValue( aGrabBag1, "Txbx-Seq") >>= nTxbxSeq1;
+ lcl_getGrabBagValue( aGrabBag, "Txbx-Id") >>= aChainStruct.nId;
+ lcl_getGrabBagValue( aGrabBag, "Txbx-Seq") >>= aChainStruct.nSeq;
+ lcl_getGrabBagValue( aGrabBag, "LinkChainName") >>= sLinkChainName;
+ lcl_getGrabBagValue( aGrabBag, "mso-next-textbox") >>= aChainStruct.s_mso_next_textbox;
- //Check which text box in the document links/(is a link) to this one.
- std::vector<uno::Reference< drawing::XShape > >::iterator inner_itr = ( outer_itr + 1 );
- for( ; inner_itr != m_vTextFramesForChaining.end(); ++inner_itr )
+ //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
{
- uno::Reference<text::XTextContent> xTextContent2(*inner_itr, uno::UNO_QUERY_THROW);
- uno::Reference<beans::XPropertySet> xPropertySet2(xTextContent2, uno::UNO_QUERY);
- uno::Sequence<beans::PropertyValue> aGrabBag2;
- uno::Reference<lang::XServiceInfo> xServiceInfo2(xPropertySet1, uno::UNO_QUERY);
- if (xServiceInfo2->supportsService("com.sun.star.text.TextFrame"))
- {
- xPropertySet2->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag2;
- xPropertySet2->getPropertyValue("LinkDisplayName") >>= sName2;
- }
- else
- {
- xPropertySet2->getPropertyValue("InteropGrabBag") >>= aGrabBag2;
- xPropertySet2->getPropertyValue("ChainName") >>= sName2;
- }
+ aChainStruct.bShapeNameSet = true;
+ sLinkChainName = sShapeName;
+ }
- lcl_getGrabBagValue( aGrabBag2, "Txbx-Id") >>= nTxbxId2;
- lcl_getGrabBagValue( aGrabBag2, "Txbx-Seq") >>= nTxbxSeq2;
+ if( !sLinkChainName.isEmpty() )
+ {
+ aChainStruct.xShape = *iter;
+ aTextFramesForChainingHelper[sLinkChainName] = aChainStruct;
+ }
+ }
- if ( nTxbxId1 == nTxbxId2 )
+ //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() )
{
- //who connects whom ??
- if ( nTxbxSeq1 == ( nTxbxSeq2 + 1 ) )
+ //if the frames have no name yet, then set them. LinkDisplayName / ChainName are read-only.
+ if( !msoIter->second.bShapeNameSet )
{
- xPropertySet2->setPropertyValue(sChainNextName, uno::makeAny(sName1));
- xPropertySet1->setPropertyValue(sChainPrevName, uno::makeAny(sName2));
- bIsTxbxChained = true ;
- break ; //there cannot be more than one previous/next frames
+ uno::Reference< container::XNamed > xNamed( msoIter->second.xShape, uno::UNO_QUERY );
+ if ( xNamed.is() )
+ {
+ xNamed->setName( msoIter->first );
+ msoIter->second.bShapeNameSet = true;
+ }
}
- else if ( nTxbxSeq2 == ( nTxbxSeq1 + 1 ) )
+ if( !nextFinder->second.bShapeNameSet )
{
- xPropertySet1->setPropertyValue(sChainNextName, uno::makeAny(sName2));
- xPropertySet2->setPropertyValue(sChainPrevName, uno::makeAny(sName1));
- bIsTxbxChained = true ;
- break ; //there cannot be more than one previous/next frames
+ 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);
}
}
- if( bIsTxbxChained )
+ }
+
+ //TODO: Perhaps allow reverse sequences when mso-layout-flow-alt = "bottom-to-top"
+ const sal_Int32 nDirection = 1;
+
+ //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.
{
- //This txt box is no longer needed for chaining since
- //there cannot be more than one previous/next frames
- outer_itr = m_vTextFramesForChaining.erase(outer_itr);
+ for (ChainMap::iterator inner_iter=aTextFramesForChainingHelper.begin(); inner_iter != aTextFramesForChainingHelper.end(); ++inner_iter)
+ {
+ if ( inner_iter->second.nId == outer_iter->second.nId )
+ {
+ if ( inner_iter->second.nSeq == ( outer_iter->second.nSeq + nDirection ) )
+ {
+ uno::Reference<text::XTextContent> xTextContent(outer_iter->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(inner_iter->first));
+ break ; //there cannot be more than one next frame
+ }
+ }
+ }
}
- else
- ++outer_itr ;
}
m_vTextFramesForChaining.clear(); //clear the vector
}