include/oox/vml/vmlshape.hxx             |    2 
 oox/source/vml/vmlshape.cxx              |    4 -
 oox/source/vml/vmlshapecontext.cxx       |  120 +++++++++++++++++++++++--------
 sc/source/filter/oox/drawingfragment.cxx |   51 +++++++++++++
 4 files changed, 148 insertions(+), 29 deletions(-)

New commits:
commit 52a2a51cdea4eb4b24d51c745351049a9536925c
Author:     Johann LORBER <[email protected]>
AuthorDate: Fri Aug 22 11:30:04 2025 +0200
Commit:     Tomaž Vajngerl <[email protected]>
CommitDate: Tue Dec 9 13:35:13 2025 +0100

    tdf#166724 - Checkbox anchor wrongly imported
    
    Form Controls are not being "fully" imported as the MoveWithCell and 
SizeWithCell properties are not set internally in Calc.
    Contrary to their own documentation, Microsoft Excel seems to declare (in 
the VML file) those properties with no value when they are false, and does not 
declare them when they are true.
    Test xlsx documents generated with Excel 2007, 2010, 2013 and 365 have led 
me to this conclusion.
    
    Change-Id: I7ba64983ae8036df8d672d6c94b2ca54d740a064
    Signed-off-by: Johann LORBER <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190166
    Tested-by: Jenkins
    Reviewed-by: Justin Luth <[email protected]>
    Code-Style: Justin Luth <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194707
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>
    (cherry picked from commit 8f47e219f34c3e81de6b155e3be1bfa405f9b37a)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195274
    Tested-by: Miklos Vajna <[email protected]>
    Reviewed-by: Tomaž Vajngerl <[email protected]>

diff --git a/include/oox/vml/vmlshape.hxx b/include/oox/vml/vmlshape.hxx
index 64259c554caf..2d5ba7bcd124 100644
--- a/include/oox/vml/vmlshape.hxx
+++ b/include/oox/vml/vmlshape.hxx
@@ -194,6 +194,8 @@ struct ClientData
     bool                mbMultiLine;        ///< True = textbox allows line 
breaks.
     bool                mbVScroll;          ///< True = textbox has a vertical 
scrollbar.
     bool                mbSecretEdit;       ///< True = textbox is a password 
edit field.
+    bool                mbMoveWithCells;    ///< True = shape moves with the 
cell (anchored to cell).
+    bool                mbSizeWithCells;    ///< True = shape resizes with the 
cell.
 
     explicit            ClientData();
 };
diff --git a/oox/source/vml/vmlshape.cxx b/oox/source/vml/vmlshape.cxx
index 1f5a8f27c59e..3ddcda38825f 100644
--- a/oox/source/vml/vmlshape.cxx
+++ b/oox/source/vml/vmlshape.cxx
@@ -259,7 +259,9 @@ ClientData::ClientData() :
     mbNo3D2( false ),
     mbMultiLine( false ),
     mbVScroll( false ),
-    mbSecretEdit( false )
+    mbSecretEdit( false ),
+    mbMoveWithCells( true ),
+    mbSizeWithCells( true )
 {
 }
 
diff --git a/oox/source/vml/vmlshapecontext.cxx 
b/oox/source/vml/vmlshapecontext.cxx
index daedc527f465..8e769409094c 100644
--- a/oox/source/vml/vmlshapecontext.cxx
+++ b/oox/source/vml/vmlshapecontext.cxx
@@ -196,34 +196,98 @@ void ClientDataContext::onEndElement()
 {
     switch( getCurrentElement() )
     {
-        case VMLX_TOKEN( Anchor ):      mrClientData.maAnchor = maElementText; 
                                         break;
-        case VMLX_TOKEN( FmlaMacro ):   mrClientData.maFmlaMacro = 
maElementText;                                       break;
-        case VMLX_TOKEN( FmlaPict ):    mrClientData.maFmlaPict = 
maElementText;                                        break;
-        case VMLX_TOKEN( FmlaLink ):    mrClientData.maFmlaLink = 
maElementText;                                        break;
-        case VMLX_TOKEN( FmlaRange ):   mrClientData.maFmlaRange = 
maElementText;                                       break;
-        case VMLX_TOKEN( FmlaGroup ):   mrClientData.maFmlaGroup = 
maElementText;                                       break;
-        case VMLX_TOKEN( TextHAlign ):  mrClientData.mnTextHAlign = 
AttributeConversion::decodeToken( maElementText );  break;
-        case VMLX_TOKEN( TextVAlign ):  mrClientData.mnTextVAlign = 
AttributeConversion::decodeToken( maElementText );  break;
-        case VMLX_TOKEN( Column ):      mrClientData.mnCol = 
maElementText.toInt32();                                   break;
-        case VMLX_TOKEN( Row ):         mrClientData.mnRow = 
maElementText.toInt32();                                   break;
-        case VMLX_TOKEN( Checked ):     mrClientData.mnChecked = 
maElementText.toInt32();                               break;
-        case VMLX_TOKEN( DropStyle ):   mrClientData.mnDropStyle = 
AttributeConversion::decodeToken( maElementText );   break;
-        case VMLX_TOKEN( DropLines ):   mrClientData.mnDropLines = 
maElementText.toInt32();                             break;
-        case VMLX_TOKEN( Val ):         mrClientData.mnVal = 
maElementText.toInt32();                                   break;
-        case VMLX_TOKEN( Min ):         mrClientData.mnMin = 
maElementText.toInt32();                                   break;
-        case VMLX_TOKEN( Max ):         mrClientData.mnMax = 
maElementText.toInt32();                                   break;
-        case VMLX_TOKEN( Inc ):         mrClientData.mnInc = 
maElementText.toInt32();                                   break;
-        case VMLX_TOKEN( Page ):        mrClientData.mnPage = 
maElementText.toInt32();                                  break;
-        case VMLX_TOKEN( SelType ):     mrClientData.mnSelType = 
AttributeConversion::decodeToken( maElementText );     break;
-        case VMLX_TOKEN( VTEdit ):      mrClientData.mnVTEdit = 
maElementText.toInt32();                                break;
-        case VMLX_TOKEN( PrintObject ): mrClientData.mbPrintObject = 
lclDecodeVmlxBool( maElementText, true );          break;
-        case VMLX_TOKEN( Visible ):     mrClientData.mbVisible = 
lclDecodeVmlxBool( maElementText, true );              break;
-        case VMLX_TOKEN( DDE ):         mrClientData.mbDde = 
lclDecodeVmlxBool( maElementText, true );                  break;
-        case VMLX_TOKEN( NoThreeD ):    mrClientData.mbNo3D = 
lclDecodeVmlxBool( maElementText, true );                 break;
-        case VMLX_TOKEN( NoThreeD2 ):   mrClientData.mbNo3D2 = 
lclDecodeVmlxBool( maElementText, true );                break;
-        case VMLX_TOKEN( MultiLine ):   mrClientData.mbMultiLine = 
lclDecodeVmlxBool( maElementText, true );            break;
-        case VMLX_TOKEN( VScroll ):     mrClientData.mbVScroll = 
lclDecodeVmlxBool( maElementText, true );              break;
-        case VMLX_TOKEN( SecretEdit ):  mrClientData.mbSecretEdit = 
lclDecodeVmlxBool( maElementText, true );           break;
+        case VMLX_TOKEN(Anchor):
+            mrClientData.maAnchor = maElementText;
+            break;
+        case VMLX_TOKEN(FmlaMacro):
+            mrClientData.maFmlaMacro = maElementText;
+            break;
+        case VMLX_TOKEN(FmlaPict):
+            mrClientData.maFmlaPict = maElementText;
+            break;
+        case VMLX_TOKEN(FmlaLink):
+            mrClientData.maFmlaLink = maElementText;
+            break;
+        case VMLX_TOKEN(FmlaRange):
+            mrClientData.maFmlaRange = maElementText;
+            break;
+        case VMLX_TOKEN(FmlaGroup):
+            mrClientData.maFmlaGroup = maElementText;
+            break;
+        case VMLX_TOKEN(TextHAlign):
+            mrClientData.mnTextHAlign = 
AttributeConversion::decodeToken(maElementText);
+            break;
+        case VMLX_TOKEN(TextVAlign):
+            mrClientData.mnTextVAlign = 
AttributeConversion::decodeToken(maElementText);
+            break;
+        case VMLX_TOKEN(Column):
+            mrClientData.mnCol = maElementText.toInt32();
+            break;
+        case VMLX_TOKEN(Row):
+            mrClientData.mnRow = maElementText.toInt32();
+            break;
+        case VMLX_TOKEN(Checked):
+            mrClientData.mnChecked = maElementText.toInt32();
+            break;
+        case VMLX_TOKEN(DropStyle):
+            mrClientData.mnDropStyle = 
AttributeConversion::decodeToken(maElementText);
+            break;
+        case VMLX_TOKEN(DropLines):
+            mrClientData.mnDropLines = maElementText.toInt32();
+            break;
+        case VMLX_TOKEN(Val):
+            mrClientData.mnVal = maElementText.toInt32();
+            break;
+        case VMLX_TOKEN(Min):
+            mrClientData.mnMin = maElementText.toInt32();
+            break;
+        case VMLX_TOKEN(Max):
+            mrClientData.mnMax = maElementText.toInt32();
+            break;
+        case VMLX_TOKEN(Inc):
+            mrClientData.mnInc = maElementText.toInt32();
+            break;
+        case VMLX_TOKEN(Page):
+            mrClientData.mnPage = maElementText.toInt32();
+            break;
+        case VMLX_TOKEN(SelType):
+            mrClientData.mnSelType = 
AttributeConversion::decodeToken(maElementText);
+            break;
+        case VMLX_TOKEN(VTEdit):
+            mrClientData.mnVTEdit = maElementText.toInt32();
+            break;
+        case VMLX_TOKEN(PrintObject):
+            mrClientData.mbPrintObject = lclDecodeVmlxBool(maElementText, 
true);
+            break;
+        case VMLX_TOKEN(Visible):
+            mrClientData.mbVisible = lclDecodeVmlxBool(maElementText, true);
+            break;
+        case VMLX_TOKEN(DDE):
+            mrClientData.mbDde = lclDecodeVmlxBool(maElementText, true);
+            break;
+        case VMLX_TOKEN(NoThreeD):
+            mrClientData.mbNo3D = lclDecodeVmlxBool(maElementText, true);
+            break;
+        case VMLX_TOKEN(NoThreeD2):
+            mrClientData.mbNo3D2 = lclDecodeVmlxBool(maElementText, true);
+            break;
+        case VMLX_TOKEN(MultiLine):
+            mrClientData.mbMultiLine = lclDecodeVmlxBool(maElementText, true);
+            break;
+        case VMLX_TOKEN(VScroll):
+            mrClientData.mbVScroll = lclDecodeVmlxBool(maElementText, true);
+            break;
+        case VMLX_TOKEN(SecretEdit):
+            mrClientData.mbSecretEdit = lclDecodeVmlxBool(maElementText, true);
+            break;
+        // Oddly, Excel will declare MoveWithCells and SizeWithCells with no 
value if these are false (therefore, bDefaultForEmpty == false)
+        // When MoveWithCells and SizeWithCells are NOT declared they should 
be assumed true.
+        case VMLX_TOKEN(MoveWithCells):
+            mrClientData.mbMoveWithCells = lclDecodeVmlxBool(maElementText, 
false);
+            break;
+        case VMLX_TOKEN(SizeWithCells):
+            mrClientData.mbSizeWithCells = lclDecodeVmlxBool(maElementText, 
false);
+            break;
     }
 }
 
diff --git a/sc/source/filter/oox/drawingfragment.cxx 
b/sc/source/filter/oox/drawingfragment.cxx
index 6b58204ddc1d..eb9c20451560 100644
--- a/sc/source/filter/oox/drawingfragment.cxx
+++ b/sc/source/filter/oox/drawingfragment.cxx
@@ -23,6 +23,7 @@
 #include <comphelper/propertyvalue.hxx>
 
 #include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
 #include <com/sun/star/container/XIndexContainer.hpp>
 #include <com/sun/star/container/XNameReplace.hpp>
 #include <com/sun/star/document/XEventsSupplier.hpp>
@@ -31,6 +32,8 @@
 #include <com/sun/star/drawing/XShapes.hpp>
 #include <com/sun/star/script/ScriptEventDescriptor.hpp>
 #include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
 #include <rtl/strbuf.hxx>
 #include <svx/svdobj.hxx>
 #include <drwlayer.hxx>
@@ -752,6 +755,54 @@ void VmlDrawing::notifyXShapeInserted( const Reference< 
XShape >& rxShape,
     catch( Exception& )
     {
     }
+
+    // Anchor to cell holding top left corner of the shape if true
+    if (pClientData->mbMoveWithCells)
+    {
+        Reference< css::sheet::XSpreadsheet > xSheet;
+        Reference< css::sheet::XSpreadsheetDocument > xSSDoc( 
this->getFilter().getModel(), UNO_QUERY );
+        if ( xSSDoc.is() )
+        {
+            Reference< XIndexAccess > xSheets( xSSDoc->getSheets(), UNO_QUERY 
);
+            if ( xSheets.is() )
+            {
+                Any aSheetAny = xSheets->getByIndex( getSheetIndex() );
+                aSheetAny >>= xSheet;
+            }
+        }
+        if ( xSheet.is() )
+        {
+            sal_Int32 nCol = -1;
+            sal_Int32 nRow = -1;
+
+            if ( pClientData->mnCol >= 0 && pClientData->mnRow >= 0 )
+            {
+                nCol = pClientData->mnCol;
+                nRow = pClientData->mnRow;
+            }
+            else if ( !pClientData->maAnchor.isEmpty() )
+            {
+                // maAnchor is "LeftColumn, LeftOffset, TopRow, TopOffset, 
RightColumn, RightOffset, BottomRow, BottomOffset". Extract tokens 0 and 2.
+                sal_Int32 nIndex = 0;
+                nCol = o3tl::toInt32(o3tl::getToken(pClientData->maAnchor, 0, 
',', nIndex));
+                // skip LeftOffset
+                o3tl::getToken(pClientData->maAnchor, 0, ',', nIndex);
+                nRow = o3tl::toInt32(o3tl::getToken(pClientData->maAnchor, 0, 
',', nIndex));
+            }
+
+            if ( nCol >= 0 && nRow >= 0 )
+            {
+                Reference< css::table::XCell > xCell = 
xSheet->getCellByPosition( nCol, nRow );
+                if ( xCell.is() )
+                {
+                    Reference< XPropertySet > aPropertySet( rxShape, 
UNO_QUERY_THROW );
+                    aPropertySet->setPropertyValue( "Anchor", Any( xCell ) );
+                    if (pClientData->mbSizeWithCells)
+                        aPropertySet->setPropertyValue( "ResizeWithCell", Any( 
true ) );
+                }
+            }
+        }
+    }
 }
 
 // private --------------------------------------------------------------------

Reply via email to