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 aced0ab0ee7541643f4eae8c1bf7ee80f82fabc3
Author:     Bayram Çiçek <bayram.ci...@collabora.com>
AuthorDate: Thu Jul 17 11:19:38 2025 +0300
Commit:     Bayram Çiçek <bayram.ci...@collabora.com>
CommitDate: Tue Jul 22 13:21:56 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

diff --git a/include/sfx2/objsh.hxx b/include/sfx2/objsh.hxx
index 8a7875640762..f4f865bf991e 100644
--- a/include/sfx2/objsh.hxx
+++ b/include/sfx2/objsh.hxx
@@ -463,7 +463,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();
 
@@ -713,7 +713,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 e9a32dc0a22d..291c4e435d9c 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(DeviceFormat::WITH_ALPHA);
     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 20c65ae8a637..76d9d0af8a30 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx
@@ -20,6 +20,7 @@
 #include <unotxdoc.hxx>
 #include <docsh.hxx>
 #include <IDocumentSettingAccess.hxx>
+#include <vcl/gdimtf.hxx>
 
 #include <set>
 
@@ -428,6 +429,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 7c85cc95a8a8..e85e1160cf39 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -794,7 +794,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() )
@@ -829,7 +856,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