[Libreoffice-commits] core.git: sw/qa sw/source writerfilter/source
Regina Henschel (via logerrit)
logerrit at kemper.freedesktop.org
Mon Jul 26 23:34:40 UTC 2021
sw/qa/extras/ooxmlimport/data/tdf143475_rotatedWord2007image.docx |binary
sw/qa/extras/ooxmlimport/data/tdf143475_rotatedWord2007imageInline.docx |binary
sw/qa/extras/ooxmlimport/ooxmlimport2.cxx | 49 +++++++++
sw/source/filter/ww8/docxattributeoutput.cxx | 51 ---------
sw/source/filter/ww8/docxexport.cxx | 52 ++++++++++
sw/source/filter/ww8/docxexport.hxx | 3
sw/source/filter/ww8/docxsdrexport.cxx | 27 +++--
writerfilter/source/dmapper/GraphicImport.cxx | 31 +++++
writerfilter/source/dmapper/GraphicImport.hxx | 1
9 files changed, 159 insertions(+), 55 deletions(-)
New commits:
commit 67f2a99229101757af4f40118f4d3c83ba38648b
Author: Regina Henschel <rb.henschel at t-online.de>
AuthorDate: Sun Jul 25 18:04:53 2021 +0200
Commit: Regina Henschel <rb.henschel at t-online.de>
CommitDate: Tue Jul 27 01:34:02 2021 +0200
tdf#143475 consider Word 2007 rotated image speciality
Usually Word relates effectExtent to a rectangle with swapped width and
height, if the object rotation is between 45deg and 135deg. But Word
2007 (=version 12) makes an exception for images (bug?).
The patch determines the version from compatibility setting and
calculates wrap margins and effectExtent values considering this
special feature of Word 2007.
I have moved the part for getting the Word version from InteropGrabBag
from the local function lcl_getWordCompatibilityMode to DocxExport,
because I need it exactly the same way in docxsdrexport.cxx.
Change-Id: Icc2f3d0710e29207413fb3810d281a0fd7d82002
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/119482
Tested-by: Jenkins
Reviewed-by: Regina Henschel <rb.henschel at t-online.de>
diff --git a/sw/qa/extras/ooxmlimport/data/tdf143475_rotatedWord2007image.docx b/sw/qa/extras/ooxmlimport/data/tdf143475_rotatedWord2007image.docx
new file mode 100644
index 000000000000..48506f4ec53a
Binary files /dev/null and b/sw/qa/extras/ooxmlimport/data/tdf143475_rotatedWord2007image.docx differ
diff --git a/sw/qa/extras/ooxmlimport/data/tdf143475_rotatedWord2007imageInline.docx b/sw/qa/extras/ooxmlimport/data/tdf143475_rotatedWord2007imageInline.docx
new file mode 100644
index 000000000000..42c4894255ca
Binary files /dev/null and b/sw/qa/extras/ooxmlimport/data/tdf143475_rotatedWord2007imageInline.docx differ
diff --git a/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx b/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx
index 257ffb234b6e..86f91c91fcaf 100644
--- a/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx
+++ b/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx
@@ -48,6 +48,55 @@ public:
}
};
+CPPUNIT_TEST_FIXTURE(Test, testTdf143475rotatedWord2007imageInline)
+{
+ // Given a docx document with compatibility to Word version 12 (2007), which has a shape
+ // rotated by 75deg. Similar to testTdf143475rotatedWord2007image but with inline anchored
+ // shape, as in bug report.
+ load(mpTestDocumentPath, "tdf143475_rotatedWord2007imageInline.docx");
+
+ // Word 2007 does not swap width and height for rotated images as done in later versions.
+ // This was not considered and lead to wrong distance to text on import and wrong effectExtent
+ // on export.
+ // Import fails without fix with left: expected 1258 actual -743 ; right expected 1256 actual -743;
+ // top: expected 14 actual 2013; bottom: expected 0 actual 1960;
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(1258), getProperty<sal_Int32>(getShape(1), "LeftMargin"),
+ 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(1256),
+ getProperty<sal_Int32>(getShape(1), "RightMargin"), 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(14), getProperty<sal_Int32>(getShape(1), "TopMargin"),
+ 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(0), getProperty<sal_Int32>(getShape(1), "BottomMargin"),
+ 1);
+
+ // Because LO made the same error on export, which inverts the import error, import-export-cycle
+ // does not fail without the patch. Therefore no export test.
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf143475rotatedWord2007image)
+{
+ // Given a docx document with compatibility to Word version 12 (2007), which has a shape
+ // rotated by 75deg.
+ load(mpTestDocumentPath, "tdf143475_rotatedWord2007image.docx");
+
+ // Word 2007 does not swap width and height for rotated images as done in later versions.
+ // This was not considered and lead to wrong distance to text on import and wrong effectExtent
+ // on export.
+ // Import fails without fix with left: expected 1252 actual -746 ; right expected 1256 actual -743;
+ // top: expected 12 actual 2013; bottom: expected 0 actual 1960;
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(1252), getProperty<sal_Int32>(getShape(1), "LeftMargin"),
+ 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(1256),
+ getProperty<sal_Int32>(getShape(1), "RightMargin"), 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(12), getProperty<sal_Int32>(getShape(1), "TopMargin"),
+ 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(0), getProperty<sal_Int32>(getShape(1), "BottomMargin"),
+ 1);
+
+ // Because LO made the same error on export, which inverts the import error, import-export-cycle
+ // does not fail without the patch. Therefore no export test.
+}
+
CPPUNIT_TEST_FIXTURE(Test, testTdf143219ContourWrapRotate)
{
load(mpTestDocumentPath, "tdf143219_ContourWrap_rotate.docx");
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index 514e42f0da99..bb8334bda259 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -3842,55 +3842,12 @@ OString lcl_padStartToLength(OString const & aString, sal_Int32 nLen, char cFill
//Keep this function in-sync with the one in writerfilter/.../SettingsTable.cxx
//Since this is not import code, "-1" needs to be handled as the mode that LO will save as.
//To identify how your code should handle a "-1", look in DocxExport::WriteSettings().
-sal_Int32 lcl_getWordCompatibilityMode( const SwDoc& rDoc )
+sal_Int32 lcl_getWordCompatibilityMode(const DocxExport& rDocExport)
{
- uno::Reference< beans::XPropertySet > xPropSet( rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW );
- uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
-
- sal_Int32 nWordCompatibilityMode = -1;
- if ( xPropSetInfo->hasPropertyByName( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) )
- {
- uno::Sequence< beans::PropertyValue > propList;
- xPropSet->getPropertyValue( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) >>= propList;
-
- for ( const auto& rProp : std::as_const(propList) )
- {
- if ( rProp.Name == "CompatSettings" )
- {
- css::uno::Sequence< css::beans::PropertyValue > aCurrentCompatSettings;
- rProp.Value >>= aCurrentCompatSettings;
-
- for ( const auto& rCurrentCompatSetting : std::as_const(aCurrentCompatSettings) )
- {
- uno::Sequence< beans::PropertyValue > aCompatSetting;
- rCurrentCompatSetting.Value >>= aCompatSetting;
-
- OUString sName;
- OUString sUri;
- OUString sVal;
-
- for ( const auto& rPropVal : std::as_const(aCompatSetting) )
- {
- if ( rPropVal.Name == "name" ) rPropVal.Value >>= sName;
- if ( rPropVal.Name == "uri" ) rPropVal.Value >>= sUri;
- if ( rPropVal.Name == "val" ) rPropVal.Value >>= sVal;
- }
-
- if ( sName == "compatibilityMode" && sUri == "http://schemas.microsoft.com/office/word" )
- {
- const sal_Int32 nValidMode = sVal.toInt32();
- // if repeated, highest mode wins in MS Word. 11 is the first valid mode.
- if ( nValidMode > 10 && nValidMode > nWordCompatibilityMode )
- nWordCompatibilityMode = nValidMode;
-
- }
- }
- }
- }
- }
+ sal_Int32 nWordCompatibilityMode = rDocExport.getWordCompatibilityModeFromGrabBag();
// TODO: this is duplicated, better store it in DocxExport member?
- if (!rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING))
+ if (!rDocExport.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING))
{
if (nWordCompatibilityMode == -1 || 14 < nWordCompatibilityMode)
{
@@ -4217,7 +4174,7 @@ void DocxAttributeOutput::TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t
// tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables;
// if no compatibilityMode is defined (which now should only happen on a new export to .docx),
// LO uses a higher compatibility than 2010's 14.
- sal_Int32 nMode = lcl_getWordCompatibilityMode( m_rExport.m_rDoc );
+ sal_Int32 nMode = lcl_getWordCompatibilityMode(m_rExport);
const SwFrameFormat* pFrameFormat = pTableTextNodeInfoInner->getTableBox()->GetFrameFormat();
if ((0 < nMode && nMode <= 14) && m_tableReference->m_nTableDepth == 0)
diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx
index 3eefc919eed8..f13a1d2f290d 100644
--- a/sw/source/filter/ww8/docxexport.cxx
+++ b/sw/source/filter/ww8/docxexport.cxx
@@ -1782,6 +1782,58 @@ sal_Int32 DocxExport::WriteOutliner(const OutlinerParaObject& rParaObj, sal_uInt
return nParaId;
}
+//Keep this function in-sync with the one in writerfilter/.../SettingsTable.cxx
+//Since this is not import code, "-1" needs to be handled as the mode that LO will save as.
+//To identify how your code should handle a "-1", look in DocxExport::WriteSettings().
+sal_Int32 DocxExport::getWordCompatibilityModeFromGrabBag() const
+{
+ sal_Int32 nWordCompatibilityMode = -1;
+ uno::Reference< beans::XPropertySet > xPropSet(m_rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW);
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
+ if (xPropSetInfo->hasPropertyByName(UNO_NAME_MISC_OBJ_INTEROPGRABBAG))
+ {
+ uno::Sequence< beans::PropertyValue > propList;
+ xPropSet->getPropertyValue( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) >>= propList;
+
+ for (const auto& rProp : std::as_const(propList))
+ {
+ if (rProp.Name == "CompatSettings")
+ {
+ css::uno::Sequence< css::beans::PropertyValue > aCurrentCompatSettings;
+ rProp.Value >>= aCurrentCompatSettings;
+
+ for (const auto& rCurrentCompatSetting : std::as_const(aCurrentCompatSettings))
+ {
+ uno::Sequence< beans::PropertyValue > aCompatSetting;
+ rCurrentCompatSetting.Value >>= aCompatSetting;
+
+ OUString sName;
+ OUString sUri;
+ OUString sVal;
+
+ for (const auto& rPropVal : std::as_const(aCompatSetting))
+ {
+ if ( rPropVal.Name == "name" ) rPropVal.Value >>= sName;
+ if ( rPropVal.Name == "uri" ) rPropVal.Value >>= sUri;
+ if ( rPropVal.Name == "val" ) rPropVal.Value >>= sVal;
+ }
+
+ if (sName == "compatibilityMode" && sUri == "http://schemas.microsoft.com/office/word")
+ {
+ const sal_Int32 nValidMode = sVal.toInt32();
+ // if repeated, highest mode wins in MS Word. 11 is the first valid mode.
+ if (nValidMode > 10 && nValidMode > nWordCompatibilityMode)
+ nWordCompatibilityMode = nValidMode;
+
+ }
+ }
+ }
+ }
+ }
+
+ return nWordCompatibilityMode;
+}
+
void DocxExport::SetFS( ::sax_fastparser::FSHelperPtr const & pFS )
{
mpFS = pFS;
diff --git a/sw/source/filter/ww8/docxexport.hxx b/sw/source/filter/ww8/docxexport.hxx
index 4bbd2ea9cb0c..91ca7c82d154 100644
--- a/sw/source/filter/ww8/docxexport.hxx
+++ b/sw/source/filter/ww8/docxexport.hxx
@@ -303,6 +303,9 @@ public:
// Get author id to remove personal info
size_t GetInfoID( const OUString sPersonalInfo ) const { return m_pAuthorIDs->GetInfoID(sPersonalInfo); }
+ // needed in docxsdrexport.cxx and docxattributeoutput.cxx
+ sal_Int32 getWordCompatibilityModeFromGrabBag() const;
+
private:
DocxExport( const DocxExport& ) = delete;
diff --git a/sw/source/filter/ww8/docxsdrexport.cxx b/sw/source/filter/ww8/docxsdrexport.cxx
index 5a8fb68c284e..bba707b56235 100644
--- a/sw/source/filter/ww8/docxsdrexport.cxx
+++ b/sw/source/filter/ww8/docxsdrexport.cxx
@@ -13,6 +13,7 @@
#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
#include <editeng/lrspitem.hxx>
#include <editeng/ulspitem.hxx>
+#include <editeng/unoprnms.hxx>
#include <editeng/shaditem.hxx>
#include <editeng/opaqitem.hxx>
#include <editeng/boxitem.hxx>
@@ -141,6 +142,7 @@ bool lcl_IsRotateAngleValid(const SdrObject& rObj)
{
case OBJ_GRUP:
case OBJ_LINE:
+ case OBJ_PLIN:
case OBJ_PATHLINE:
case OBJ_PATHFILL:
return false;
@@ -150,7 +152,8 @@ bool lcl_IsRotateAngleValid(const SdrObject& rObj)
}
void lcl_calculateMSOBaseRectangle(const SdrObject& rObj, double& rfMSOLeft, double& rfMSORight,
- double& rfMSOTop, double& rfMSOBottom)
+ double& rfMSOTop, double& rfMSOBottom,
+ const bool bIsWord2007Image)
{
// Word rotates around shape center, LO around left/top. Thus logic rectangle of LO is not
// directly usable as 'base rectangle'.
@@ -159,10 +162,12 @@ void lcl_calculateMSOBaseRectangle(const SdrObject& rObj, double& rfMSOLeft, dou
double fHalfWidth = rObj.GetLogicRect().getWidth() / 2.0;
double fHalfHeight = rObj.GetLogicRect().getHeight() / 2.0;
- // MSO swaps width and height depending on rotation angle.
+ // MSO swaps width and height depending on rotation angle; exception: Word 2007 (vers 12) never
+ // swaps width and height for images.
double fRotation
= lcl_IsRotateAngleValid(rObj) ? toDegrees(NormAngle36000(rObj.GetRotateAngle())) : 0.0;
- if ((fRotation > 45.0 && fRotation <= 135.0) || (fRotation > 225.0 && fRotation <= 315.0))
+ if (((fRotation > 45.0 && fRotation <= 135.0) || (fRotation > 225.0 && fRotation <= 315.0))
+ && !bIsWord2007Image)
{
rfMSOLeft = fCenterX - fHalfHeight;
rfMSORight = fCenterX + fHalfHeight;
@@ -180,16 +185,16 @@ void lcl_calculateMSOBaseRectangle(const SdrObject& rObj, double& rfMSOLeft, dou
void lcl_calculateRawEffectExtent(sal_Int32& rLeft, sal_Int32& rTop, sal_Int32& rRight,
sal_Int32& rBottom, const SdrObject& rObj,
- const bool bUseBoundRect)
+ const bool bUseBoundRect, const bool bIsWord2007Image)
{
// This method calculates the extent needed, to let Word use the same outer area for the object
// as LO. Word uses as 'base rectangle' the unrotated shape rectangle, maybe having swapped width
- // and height depending on rotation angle.
+ // and height depending on rotation angle and version of Word.
double fMSOLeft;
double fMSORight;
double fMSOTop;
double fMSOBottom;
- lcl_calculateMSOBaseRectangle(rObj, fMSOLeft, fMSORight, fMSOTop, fMSOBottom);
+ lcl_calculateMSOBaseRectangle(rObj, fMSOLeft, fMSORight, fMSOTop, fMSOBottom, bIsWord2007Image);
tools::Rectangle aLORect = bUseBoundRect ? rObj.GetCurrentBoundRect() : rObj.GetSnapRect();
rLeft = fMSOLeft - aLORect.Left();
@@ -618,11 +623,16 @@ void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, cons
}
else // other objects than frames. pObj exists.
{
+ // Word 2007 makes no width-hight-swap for images. Detect this situation.
+ sal_Int32 nMode = m_pImpl->getExport().getWordCompatibilityModeFromGrabBag();
+ bool bIsWord2007Image(nMode > 0 && nMode < 14 && pObj->GetObjIdentifier() == OBJ_GRAF);
+
// Word cannot handle negative EffectExtent although allowed in OOXML, the 'dist' attributes
// may not be negative. Take care of that.
if (isAnchor)
{
- lcl_calculateRawEffectExtent(nLeftExt, nTopExt, nRightExt, nBottomExt, *pObj, true);
+ lcl_calculateRawEffectExtent(nLeftExt, nTopExt, nRightExt, nBottomExt, *pObj, true,
+ bIsWord2007Image);
// We have calculated the effectExtent from boundRect, therefore half stroke width is
// already contained.
// ToDo: The other half of the stroke width needs to be subtracted from padding.
@@ -656,7 +666,8 @@ void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, cons
}
else
{
- lcl_calculateRawEffectExtent(nLeftExt, nTopExt, nRightExt, nBottomExt, *pObj, false);
+ lcl_calculateRawEffectExtent(nLeftExt, nTopExt, nRightExt, nBottomExt, *pObj, false,
+ bIsWord2007Image);
// nDistT,... contain the needed distances from import or set by user. But Word
// ignores Dist attributes of inline shapes. So we move all needed distances to
// effectExtent and force effectExtent to non-negative.
diff --git a/writerfilter/source/dmapper/GraphicImport.cxx b/writerfilter/source/dmapper/GraphicImport.cxx
index 940780ca9705..8ed707c2917f 100644
--- a/writerfilter/source/dmapper/GraphicImport.cxx
+++ b/writerfilter/source/dmapper/GraphicImport.cxx
@@ -552,6 +552,26 @@ static bool lcl_bHasGroupSlantedChild(const SdrObject* pObj)
return false;
}
+void GraphicImport::lcl_correctWord2007EffectExtent(const sal_Int32 nMSOAngle)
+{
+ // Word versions older than 14 do not swap width and height (see lcl_doMSOWidthHeightSwap)
+ // and therefore generate different effectExtent. We correct them here.
+ sal_Int16 nAngleDeg = (nMSOAngle / 60000) % 180;
+ if (nAngleDeg >= 45 && nAngleDeg < 135)
+ {
+ sal_Int32 nDiff = o3tl::convert((m_pImpl->getXSize() - m_pImpl->getYSize()) / 2.0,
+ o3tl::Length::mm100, o3tl::Length::emu);
+ if (m_pImpl->m_oEffectExtentLeft)
+ *m_pImpl->m_oEffectExtentLeft += nDiff;
+ if (m_pImpl->m_oEffectExtentRight)
+ *m_pImpl->m_oEffectExtentRight += nDiff;
+ if (m_pImpl->m_oEffectExtentTop)
+ *m_pImpl->m_oEffectExtentTop -= nDiff;
+ if (m_pImpl->m_oEffectExtentBottom)
+ *m_pImpl->m_oEffectExtentBottom -= nDiff;
+ }
+}
+
static void lcl_doMSOWidthHeightSwap(awt::Point& rLeftTop, awt::Size& rSize,
const sal_Int32 nMSOAngle)
{
@@ -965,6 +985,17 @@ void GraphicImport::lcl_attribute(Id nName, Value& rValue)
// snap rectangle.
// Margin correction
+
+ // tdf#143475: Word 2007 (vers 12) calculates effectExtent for rotated images
+ // based on the unrotated image without width-height-swap. We correct this to
+ // those values, which would be calculated if width-height-swap was used.
+ if (m_pImpl->rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() < 14
+ && xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape")
+ && nOOXAngle != 0)
+ {
+ lcl_correctWord2007EffectExtent(nOOXAngle);
+ }
+
if (m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_INLINE)
{
if (nOOXAngle == 0)
diff --git a/writerfilter/source/dmapper/GraphicImport.hxx b/writerfilter/source/dmapper/GraphicImport.hxx
index bea911eef8bb..7ca4e09ed30d 100644
--- a/writerfilter/source/dmapper/GraphicImport.hxx
+++ b/writerfilter/source/dmapper/GraphicImport.hxx
@@ -126,6 +126,7 @@ public:
void handleWrapTextValue(sal_uInt32 nVal);
void lcl_expandRectangleByEffectExtent(css::awt::Point& rLeftTop, css::awt::Size& rSize);
+ void lcl_correctWord2007EffectExtent(const sal_Int32 nMSOAngle);
};
typedef tools::SvRef<GraphicImport> GraphicImportPtr;
More information about the Libreoffice-commits
mailing list