Kaydet (Commit) 9ab3e929 authored tarafından Justin Luth's avatar Justin Luth Kaydeden (comit) Miklos Vajna

tdf#87348 implement nonsequential and mso-next-textbox textbox chaining

cherry picked from commits 976add10 and
091fe76b

Change-Id: I017049a8f3578ad4c2a1f549be1c683f98c20318
Reviewed-on: https://gerrit.libreoffice.org/16692Reviewed-by: 's avatarMiklos Vajna <vmiklos@collabora.co.uk>
Tested-by: 's avatarMiklos Vajna <vmiklos@collabora.co.uk>
üst 66ed003f
...@@ -58,6 +58,7 @@ const sal_Int32 VML_CLIENTDATA_FORMULA = 4; ...@@ -58,6 +58,7 @@ const sal_Int32 VML_CLIENTDATA_FORMULA = 4;
struct OOX_DLLPUBLIC ShapeTypeModel struct OOX_DLLPUBLIC ShapeTypeModel
{ {
OUString maShapeId; ///< Unique identifier of the shape. OUString maShapeId; ///< Unique identifier of the shape.
OUString maLegacyId; ///< Plaintext identifier of the shape.
OUString maShapeName; ///< Name of the shape, if present. OUString maShapeName; ///< Name of the shape, if present.
OptValue< sal_Int32 > moShapeType; ///< Builtin shape type identifier. OptValue< sal_Int32 > moShapeType; ///< Builtin shape type identifier.
......
...@@ -95,6 +95,7 @@ public: ...@@ -95,6 +95,7 @@ public:
bool borderDistanceSet; bool borderDistanceSet;
int borderDistanceLeft, borderDistanceTop, borderDistanceRight, borderDistanceBottom; int borderDistanceLeft, borderDistanceTop, borderDistanceRight, borderDistanceBottom;
OUString maLayoutFlow; OUString maLayoutFlow;
OUString msNextTextbox;
private: private:
typedef ::std::vector< TextPortionModel > PortionVector; typedef ::std::vector< TextPortionModel > PortionVector;
......
...@@ -312,21 +312,64 @@ Reference< XShape > ShapeBase::convertAndInsert( const Reference< XShapes >& rxS ...@@ -312,21 +312,64 @@ Reference< XShape > ShapeBase::convertAndInsert( const Reference< XShapes >& rxS
if( aShapeProp.hasProperty( PROP_Name ) ) if( aShapeProp.hasProperty( PROP_Name ) )
aShapeProp.setProperty( PROP_Name, getShapeName() ); aShapeProp.setProperty( PROP_Name, getShapeName() );
uno::Reference< lang::XServiceInfo > xSInfo( xShape, uno::UNO_QUERY_THROW ); 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")) if (xSInfo->supportsService("com.sun.star.text.TextFrame"))
{ {
uno::Sequence<beans::PropertyValue> aGrabBag; uno::Sequence<beans::PropertyValue> aGrabBag;
uno::Reference<beans::XPropertySet> propertySet (xShape, uno::UNO_QUERY); uno::Reference<beans::XPropertySet> propertySet (xShape, uno::UNO_QUERY);
propertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag; propertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
sal_Int32 length = aGrabBag.getLength(); sal_Int32 length;
length = aGrabBag.getLength();
aGrabBag.realloc( length+1 ); aGrabBag.realloc( length+1 );
aGrabBag[length].Name = "VML-Z-ORDER"; aGrabBag[length].Name = "VML-Z-ORDER";
aGrabBag[length].Value = uno::makeAny( maTypeModel.maZIndex.toInt32() ); 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()) if(!(maTypeModel.maRotation).isEmpty())
{ {
aGrabBag.realloc( length+2 ); length = aGrabBag.getLength();
aGrabBag[length+1].Name = "mso-rotation-angle"; aGrabBag.realloc( length+1 );
aGrabBag[length+1].Value = uno::makeAny(sal_Int32(NormAngle360((maTypeModel.maRotation.toInt32()) * -100))); aGrabBag[length].Name = "mso-rotation-angle";
aGrabBag[length].Value = uno::makeAny(sal_Int32(NormAngle360((maTypeModel.maRotation.toInt32()) * -100)));
} }
propertySet->setPropertyValue( "FrameInteropGrabBag", uno::makeAny(aGrabBag) ); propertySet->setPropertyValue( "FrameInteropGrabBag", uno::makeAny(aGrabBag) );
sal_Int32 backColorTransparency = 0; sal_Int32 backColorTransparency = 0;
...@@ -346,10 +389,34 @@ Reference< XShape > ShapeBase::convertAndInsert( const Reference< XShapes >& rxS ...@@ -346,10 +389,34 @@ Reference< XShape > ShapeBase::convertAndInsert( const Reference< XShapes >& rxS
uno::Sequence<beans::PropertyValue> aGrabBag; uno::Sequence<beans::PropertyValue> aGrabBag;
uno::Reference<beans::XPropertySet> propertySet (xShape, uno::UNO_QUERY); uno::Reference<beans::XPropertySet> propertySet (xShape, uno::UNO_QUERY);
propertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag; propertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
sal_Int32 length = aGrabBag.getLength(); sal_Int32 length;
length = aGrabBag.getLength();
aGrabBag.realloc( length+1 ); aGrabBag.realloc( length+1 );
aGrabBag[length].Name = "VML-Z-ORDER"; aGrabBag[length].Name = "VML-Z-ORDER";
aGrabBag[length].Value = uno::makeAny( maTypeModel.maZIndex.toInt32() ); 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) ); propertySet->setPropertyValue( "InteropGrabBag", uno::makeAny(aGrabBag) );
} }
} }
......
...@@ -269,6 +269,7 @@ ShapeTypeContext::ShapeTypeContext( ContextHandler2Helper& rParent, ShapeType& r ...@@ -269,6 +269,7 @@ ShapeTypeContext::ShapeTypeContext( ContextHandler2Helper& rParent, ShapeType& r
// shape identifier and shape name // shape identifier and shape name
bool bHasOspid = rAttribs.hasAttribute( O_TOKEN( spid ) ); bool bHasOspid = rAttribs.hasAttribute( O_TOKEN( spid ) );
mrTypeModel.maShapeId = rAttribs.getXString( bHasOspid ? O_TOKEN( spid ) : XML_id, OUString() ); 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" ); 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 the o:spid attribute exists, the id attribute contains the user-defined shape name
if( bHasOspid ) if( bHasOspid )
......
...@@ -212,6 +212,8 @@ TextBoxContext::TextBoxContext( ContextHandler2Helper& rParent, TextBox& rTextBo ...@@ -212,6 +212,8 @@ TextBoxContext::TextBoxContext( ContextHandler2Helper& rParent, TextBox& rTextBo
rTextBox.mrTypeModel.mbAutoHeight = true; rTextBox.mrTypeModel.mbAutoHeight = true;
else if (aName == "mso-layout-flow-alt") else if (aName == "mso-layout-flow-alt")
rTextBox.mrTypeModel.maLayoutFlowAlt = aValue; rTextBox.mrTypeModel.maLayoutFlowAlt = aValue;
else if (aName == "mso-next-textbox")
rTextBox.msNextTextbox = aValue;
else else
SAL_WARN("oox", "unhandled style property: " << aName); SAL_WARN("oox", "unhandled style property: " << aName);
} }
......
...@@ -2449,91 +2449,144 @@ static uno::Any lcl_getGrabBagValue( const uno::Sequence<beans::PropertyValue>& ...@@ -2449,91 +2449,144 @@ static uno::Any lcl_getGrabBagValue( const uno::Sequence<beans::PropertyValue>&
//Link the text frames. //Link the text frames.
void DomainMapper_Impl::ChainTextFrames() 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 ; 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 try
{ {
sal_Int32 nTxbxId1 = 0 ; //holds id for the shape in outer loop ChainMap aTextFramesForChainingHelper;
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
OUString sChainNextName("ChainNextName"); OUString sChainNextName("ChainNextName");
OUString sChainPrevName("ChainPrevName");
for( std::vector<uno::Reference< drawing::XShape > >::iterator outer_itr = m_vTextFramesForChaining.begin(); //learn about ALL of the textboxes and their chaining values first - because frames are processed in no specific order.
outer_itr != m_vTextFramesForChaining.end(); ) std::vector<uno::Reference< drawing::XShape > >::iterator iter;
for( iter = m_vTextFramesForChaining.begin(); iter != m_vTextFramesForChaining.end(); ++iter )
{ {
bool bIsTxbxChained = false ; uno::Reference<text::XTextContent> xTextContent(*iter, uno::UNO_QUERY_THROW);
uno::Reference<text::XTextContent> xTextContent1(*outer_itr, uno::UNO_QUERY_THROW); uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
uno::Reference<beans::XPropertySet> xPropertySet1(xTextContent1, uno::UNO_QUERY); uno::Reference<beans::XPropertySetInfo> xPropertySetInfo;
uno::Sequence<beans::PropertyValue> aGrabBag1; if( xPropertySet.is() )
uno::Reference<lang::XServiceInfo> xServiceInfo1(xPropertySet1, uno::UNO_QUERY); xPropertySetInfo = xPropertySet->getPropertySetInfo();
if (xServiceInfo1->supportsService("com.sun.star.text.TextFrame")) 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; xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
xPropertySet1->getPropertyValue("LinkDisplayName") >>= sName1; xPropertySet->getPropertyValue("LinkDisplayName") >>= sShapeName;
} }
else else
{ {
xPropertySet1->getPropertyValue("InteropGrabBag") >>= aGrabBag1; xPropertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
xPropertySet1->getPropertyValue("ChainName") >>= sName1; xPropertySet->getPropertyValue("ChainName") >>= sShapeName;
} }
lcl_getGrabBagValue( aGrabBag1, "Txbx-Id") >>= nTxbxId1; lcl_getGrabBagValue( aGrabBag, "Txbx-Id") >>= aChainStruct.nId;
lcl_getGrabBagValue( aGrabBag1, "Txbx-Seq") >>= nTxbxSeq1; 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. //Sometimes the shape names have not been imported. If not, we may have a fallback name.
std::vector<uno::Reference< drawing::XShape > >::iterator inner_itr = ( outer_itr + 1 ); //Set name later, only if required for linking.
for( ; inner_itr != m_vTextFramesForChaining.end(); ++inner_itr ) if( sShapeName.isEmpty() )
aChainStruct.bShapeNameSet = false;
else
{ {
uno::Reference<text::XTextContent> xTextContent2(*inner_itr, uno::UNO_QUERY_THROW); aChainStruct.bShapeNameSet = true;
uno::Reference<beans::XPropertySet> xPropertySet2(xTextContent2, uno::UNO_QUERY); sLinkChainName = sShapeName;
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;
}
lcl_getGrabBagValue( aGrabBag2, "Txbx-Id") >>= nTxbxId2; if( !sLinkChainName.isEmpty() )
lcl_getGrabBagValue( aGrabBag2, "Txbx-Seq") >>= nTxbxSeq2; {
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 the frames have no name yet, then set them. LinkDisplayName / ChainName are read-only.
if ( nTxbxSeq1 == ( nTxbxSeq2 + 1 ) ) if( !msoIter->second.bShapeNameSet )
{ {
xPropertySet2->setPropertyValue(sChainNextName, uno::makeAny(sName1)); uno::Reference< container::XNamed > xNamed( msoIter->second.xShape, uno::UNO_QUERY );
xPropertySet1->setPropertyValue(sChainPrevName, uno::makeAny(sName2)); if ( xNamed.is() )
bIsTxbxChained = true ; {
break ; //there cannot be more than one previous/next frames xNamed->setName( msoIter->first );
msoIter->second.bShapeNameSet = true;
}
} }
else if ( nTxbxSeq2 == ( nTxbxSeq1 + 1 ) ) if( !nextFinder->second.bShapeNameSet )
{ {
xPropertySet1->setPropertyValue(sChainNextName, uno::makeAny(sName2)); uno::Reference< container::XNamed > xNamed( nextFinder->second.xShape, uno::UNO_QUERY );
xPropertySet2->setPropertyValue(sChainPrevName, uno::makeAny(sName1)); if ( xNamed.is() )
bIsTxbxChained = true ; {
break ; //there cannot be more than one previous/next frames 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 for (ChainMap::iterator inner_iter=aTextFramesForChainingHelper.begin(); inner_iter != aTextFramesForChainingHelper.end(); ++inner_iter)
//there cannot be more than one previous/next frames {
outer_itr = m_vTextFramesForChaining.erase(outer_itr); 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 m_vTextFramesForChaining.clear(); //clear the vector
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment