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 2a3f53c4aeb915337e9bce5aba88f4a40305780b Author: Johann LORBER <[email protected]> AuthorDate: Fri Aug 22 11:30:04 2025 +0200 Commit: Justin Luth <[email protected]> CommitDate: Tue Nov 25 18:25:40 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]> diff --git a/include/oox/vml/vmlshape.hxx b/include/oox/vml/vmlshape.hxx index 6348b539f689..c960d9d8da46 100644 --- a/include/oox/vml/vmlshape.hxx +++ b/include/oox/vml/vmlshape.hxx @@ -197,6 +197,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 7908dd7527c4..bf4a06c32993 100644 --- a/oox/source/vml/vmlshape.cxx +++ b/oox/source/vml/vmlshape.cxx @@ -260,7 +260,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 85c70347e0e8..5839fa511116 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 --------------------------------------------------------------------
