include/sfx2/objsh.hxx                                                    |    
4 
 sfx2/source/doc/objcont.cxx                                               |   
10 -
 sw/qa/extras/ooxmlexport/data/tdf167527_title_letters_cut_from_below.docx 
|binary
 sw/qa/extras/ooxmlexport/ooxmlexport22.cxx                                |   
26 ++++
 sw/source/core/text/inftxt.cxx                                            |   
53 +++++++++-
 5 files changed, 84 insertions(+), 9 deletions(-)

New commits:
commit c9ffc264f43074d7ff59db9459b15e105e2a5167
Author:     Bayram Çiçek <bayram.ci...@collabora.com>
AuthorDate: Thu Jul 17 11:19:38 2025 +0300
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Tue Jul 22 18:38:42 2025 +0200

    tdf#167527: adjust field shadings according to line spacing
    
    - we should take line spacing into account for field shadings.
    - otherwise, bottom of some letters(e.g. "Ç", "ş", "g", "p") will be cut 
because of the "field shadings" background layer.
    - also check if line spacing type is "proportional" or "fixed".
    - add a unittest
    
    Signed-off-by: Bayram Çiçek <bayram.ci...@collabora.com>
    Change-Id: I805551469f712ec6147b025d771d996ef7fd0c95
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187986
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Tested-by: Jenkins
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188160
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>

diff --git a/include/sfx2/objsh.hxx b/include/sfx2/objsh.hxx
index 46e9045bc713..112bf64fad26 100644
--- a/include/sfx2/objsh.hxx
+++ b/include/sfx2/objsh.hxx
@@ -464,7 +464,7 @@ public:
 
     Size                        GetFirstPageSize() const;
     bool                        DoClose();
-    std::shared_ptr<GDIMetaFile> GetPreviewMetaFile( bool bFullContent = false 
) const;
+    std::shared_ptr<GDIMetaFile> GetPreviewMetaFile( bool bFullContent = 
false, bool bOutputForScreen = false ) const;
     BitmapEx                    GetPreviewBitmap() const;
     virtual void                CancelTransfers();
 
@@ -714,7 +714,7 @@ public:
     // Destruction of storages and streams
     void InternalCloseAndRemoveFiles();
 
-    SAL_DLLPRIVATE bool CreatePreview_Impl(bool bFullContent, VirtualDevice* 
pDevice, GDIMetaFile* pFile) const;
+    SAL_DLLPRIVATE bool CreatePreview_Impl(bool bFullContent, bool 
bOutputForScreen, VirtualDevice* pDevice, GDIMetaFile* pFile) const;
 
     SAL_DLLPRIVATE static bool IsPackageStorageFormat_Impl(const SfxMedium &);
 
diff --git a/sfx2/source/doc/objcont.cxx b/sfx2/source/doc/objcont.cxx
index 6aa790d22fc4..a1c69f1854e5 100644
--- a/sfx2/source/doc/objcont.cxx
+++ b/sfx2/source/doc/objcont.cxx
@@ -93,12 +93,12 @@ bool operator> (const util::DateTime& i_rLeft, const 
util::DateTime& i_rRight)
 }
 
 std::shared_ptr<GDIMetaFile>
-SfxObjectShell::GetPreviewMetaFile( bool bFullContent ) const
+SfxObjectShell::GetPreviewMetaFile( bool bFullContent, bool bOutputForScreen ) 
const
 {
     auto xFile = std::make_shared<GDIMetaFile>();
     ScopedVclPtrInstance< VirtualDevice > pDevice;
     pDevice->EnableOutput( false );
-    if(!CreatePreview_Impl(bFullContent, pDevice, xFile.get()))
+    if(!CreatePreview_Impl(bFullContent, bOutputForScreen, pDevice, 
xFile.get()))
         return std::shared_ptr<GDIMetaFile>();
     return xFile;
 }
@@ -108,7 +108,7 @@ BitmapEx SfxObjectShell::GetPreviewBitmap() const
     SfxCloseVetoLock lock(this);
     ScopedVclPtrInstance< VirtualDevice > pDevice;
     pDevice->SetAntialiasing(AntialiasingFlags::Enable | 
pDevice->GetAntialiasing());
-    if(!CreatePreview_Impl(/*bFullContent*/false, pDevice, nullptr))
+    if(!CreatePreview_Impl(/*bFullContent*/false, false, pDevice, nullptr))
         return BitmapEx();
     Size size = pDevice->GetOutputSizePixel();
     BitmapEx aBitmap = pDevice->GetBitmapEx( Point(), size);
@@ -120,7 +120,7 @@ BitmapEx SfxObjectShell::GetPreviewBitmap() const
     return aBitmap;
 }
 
-bool SfxObjectShell::CreatePreview_Impl( bool bFullContent, VirtualDevice* 
pDevice, GDIMetaFile* pFile) const
+bool SfxObjectShell::CreatePreview_Impl( bool bFullContent, bool 
bOutputForScreen, VirtualDevice* pDevice, GDIMetaFile* pFile) const
 {
     // DoDraw can only be called when no printing is done, otherwise
     // the printer may be turned off
@@ -196,7 +196,7 @@ bool SfxObjectShell::CreatePreview_Impl( bool bFullContent, 
VirtualDevice* pDevi
 
     pDevice->SetDigitLanguage( eLang );
 
-    const_cast<SfxObjectShell*>(this)->DoDraw( pDevice, Point(0,0), aTmpSize, 
JobSetup(), nAspect );
+    const_cast<SfxObjectShell*>(this)->DoDraw( pDevice, Point(0,0), aTmpSize, 
JobSetup(), nAspect, bOutputForScreen );
 
     if(pFile)
         pFile->Stop();
diff --git 
a/sw/qa/extras/ooxmlexport/data/tdf167527_title_letters_cut_from_below.docx 
b/sw/qa/extras/ooxmlexport/data/tdf167527_title_letters_cut_from_below.docx
new file mode 100644
index 000000000000..96dc4f2366a2
Binary files /dev/null and 
b/sw/qa/extras/ooxmlexport/data/tdf167527_title_letters_cut_from_below.docx 
differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx
index 34fe0302cef7..a3f378ce075a 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx
@@ -19,6 +19,7 @@
 #include <pam.hxx>
 #include <unotxdoc.hxx>
 #include <docsh.hxx>
+#include <vcl/gdimtf.hxx>
 
 #include <set>
 
@@ -351,6 +352,31 @@ DECLARE_OOXMLEXPORT_TEST(testFieldMarkFormat, 
"fontsize-field-separator.docx")
     CPPUNIT_ASSERT_EQUAL(12.f, getProperty<float>(xRun, u"CharHeight"_ustr));
 }
 
+CPPUNIT_TEST_FIXTURE(Test, tdf167527_title_letters_cut_from_below)
+{
+    createSwDoc("tdf167527_title_letters_cut_from_below.docx");
+
+    SwDocShell* pDocShell = getSwDocShell();
+    CPPUNIT_ASSERT(pDocShell);
+
+    // bOutputForScreen of true to ensure field backgrounds are rendered
+    std::shared_ptr<GDIMetaFile> xMetaFile = 
pDocShell->GetPreviewMetaFile(false, true);
+
+    MetafileXmlDump dumper;
+    xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
+
+    auto nFieldShadingsBottom = getXPath(pXmlDoc, "(//rect)[2]", 
"bottom").toInt32();
+    auto nFieldShadingsTop = getXPath(pXmlDoc, "(//rect)[2]", "top").toInt32();
+    sal_Int32 nFieldShadingsHeight = nFieldShadingsBottom - nFieldShadingsTop;
+
+    // Without the accompanying fix in place, this test would have failed with:
+    // less equal assertion failed
+    // Expected less or equal than: 700
+    // Actual  : 810
+    // i.e. the field background overlaps the previous row of text
+    CPPUNIT_ASSERT_LESSEQUAL(static_cast<sal_Int32>(700), 
nFieldShadingsHeight);
+}
+
 } // end of anonymous namespace
 CPPUNIT_PLUGIN_IMPLEMENT();
 
diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx
index a00a9d563d7c..497e0c3c49e2 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -785,7 +785,34 @@ void SwTextPaintInfo::CalcRect( const SwLinePortion& rPor,
                                SwRect* pRect, SwRect* pIntersect,
                                const bool bInsideBox ) const
 {
-    Size aSize( rPor.Width(), rPor.Height() );
+    const SwAttrSet& rAttrSet = 
GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet();
+    const SvxLineSpacingItem& rSpace = rAttrSet.GetLineSpacing();
+    tools::Long nPropLineSpace = rSpace.GetPropLineSpace();
+
+    SwTwips nHeight = rPor.Height();
+
+    // we should take line spacing into account.
+    // otherwise, bottom of some letters will be cut because of the "field 
shading" background layer.
+    switch (rSpace.GetInterLineSpaceRule())
+    {
+        case SvxInterLineSpaceRule::Prop: // proportional
+        {
+            if (nPropLineSpace < 100)
+                nHeight = rPor.Height() * nPropLineSpace / 100;
+        }
+        break;
+        case SvxInterLineSpaceRule::Fix: // fixed
+        {
+            if (rSpace.GetInterLineSpace() > 0)
+                nHeight = std::min<SwTwips>(rSpace.GetInterLineSpace(), 
rPor.Height());
+        }
+        break;
+        default:
+            break;
+    }
+
+    Size aSize( rPor.Width(), nHeight);
+
     if( rPor.IsHangingPortion() )
         aSize.setWidth( static_cast<const 
SwHangingPortion&>(rPor).GetInnerWidth() );
     if( rPor.InSpaceGrp() && GetSpaceAdd() )
@@ -820,7 +847,29 @@ void SwTextPaintInfo::CalcRect( const SwLinePortion& rPor,
         if (GetTextFrame()->IsVertLR() && !GetTextFrame()->IsVertLRBT())
             aPoint.setY( Y() - rPor.Height() + rPor.GetAscent() );
         else
-            aPoint.setY( Y() - rPor.GetAscent() );
+        {
+            SwTwips nAscent = rPor.GetAscent();
+
+            switch (rSpace.GetInterLineSpaceRule())
+            {
+                case SvxInterLineSpaceRule::Prop: // proportional
+                {
+                    if (nPropLineSpace < 100)
+                        nAscent = (rPor.GetAscent() * nPropLineSpace / 100);
+                }
+                break;
+                case SvxInterLineSpaceRule::Fix: // fixed
+                {
+                    if (rSpace.GetInterLineSpace() > 0)
+                        nAscent = 
std::min<SwTwips>(rSpace.GetInterLineSpace(), rPor.GetAscent());
+                }
+                break;
+                default:
+                    break;
+            }
+
+            aPoint.setY( Y() - nAscent);
+        }
     }
 
     // Adjust x coordinate if we are inside a bidi portion

Reply via email to