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