[Libreoffice-commits] core.git: Branch 'libreoffice-4-4' - include/oox oox/source writerfilter/source

Justin Luth justin_luth at sil.org
Tue Jul 7 00:26:54 PDT 2015


 include/oox/vml/vmlshape.hxx                      |    1 
 include/oox/vml/vmltextbox.hxx                    |    1 
 oox/source/vml/vmlshape.cxx                       |   77 +++++++++
 oox/source/vml/vmlshapecontext.cxx                |    1 
 oox/source/vml/vmltextboxcontext.cxx              |    2 
 writerfilter/source/dmapper/DomainMapper_Impl.cxx |  172 ++++++++++++++--------
 6 files changed, 189 insertions(+), 65 deletions(-)

New commits:
commit f408edabd52f865abb29401dbff995921b394e6c
Author: Justin Luth <justin_luth at sil.org>
Date:   Thu Jul 2 14:00:24 2015 +0300

    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 at collabora.co.uk>
    Tested-by: Miklos Vajna <vmiklos at collabora.co.uk>

diff --git a/include/oox/vml/vmlshape.hxx b/include/oox/vml/vmlshape.hxx
index 6cc180c..e4b5c92 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 3ff88d4..6b14c4c 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 1c826a1..15b37d6 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 71905ba..2353b0a 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 8a1d5fb..d1a8b12 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 2f96375..f043ba1 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
     }


More information about the Libreoffice-commits mailing list