[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