tdf#171275 sw: Only apply DOC/DOCX CJK extra leading when grid disabled

The fix for tdf#129808 added an ascent adjustment for fonts advertising
support for certain CJK code pages. This increased layout accuracy for
many DOC/DOCX files, but caused a layout regression in documents that
use the CJK grid.

This ascent adjustment should not be applied when the CJK grid is used.

Regression since commit e78940b7de
Author: Jonathan Clark <jonathan@libreoffice.org>
Date:   Wed Aug 27 04:18:42 2025 -0600

    tdf#129808 sw: Extend leading for CJK fonts in DOC/DOCX files

Change-Id: I9441f025427623af7d5a9e5825bbef1e73f41227
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/209031
Reviewed-by: Jonathan Clark <jonathan@libreoffice.org>
Tested-by: Jenkins
This commit is contained in:
Jonathan Clark
2026-08-07 15:48:09 +02:00
parent 27fac7442e
commit 8bcfabce3d
8 changed files with 1472 additions and 29 deletions
File diff suppressed because it is too large Load Diff
+19
View File
@@ -2095,6 +2095,25 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter5, testTdf129808CompatFlagUnset)
CPPUNIT_ASSERT_LESS(sal_Int32(1150), nHeight2);
}
CPPUNIT_TEST_FIXTURE(SwLayoutWriter5, testTdf171275GridNoExtraLeading)
{
// tdf#171275: This document contains an embedded font that advertises CP950 coverage. When
// Word-compatible CJK metrics are enabled, the line height should be scaled higher. However,
// this document also uses the document grid, which should disable the extra height.
createSwDoc("tdf171275-grid-no-extra-leading.fodt");
auto pXmlDoc = parseLayoutDump();
auto nHeight1 = getXPath(pXmlDoc, "//txt[1]/SwParaPortion/SwLineLayout", "height").toInt32();
// Without the fix, this would be ~2362
CPPUNIT_ASSERT_GREATER(sal_Int32(1180), nHeight1);
CPPUNIT_ASSERT_LESS(sal_Int32(1182), nHeight1);
auto nHeight2 = getXPath(pXmlDoc, "//txt[2]/SwParaPortion/SwLineLayout", "height").toInt32();
// Without the fix, this would be ~2362
CPPUNIT_ASSERT_GREATER(sal_Int32(1180), nHeight2);
CPPUNIT_ASSERT_LESS(sal_Int32(1182), nHeight2);
}
CPPUNIT_TEST_FIXTURE(SwLayoutWriter5, testTdf168116)
{
// Given a paragraph with a line break immediately followed by a hidden paragraph mark:
+4
View File
@@ -79,6 +79,9 @@ class SwFntObj final : public SwCacheObj
sal_uInt16 m_nZoom;
bool m_bSymbol : 1;
bool m_bPaintBlank : 1;
bool m_bScrHasCJKCodePages : 1 = false;
bool m_bPrtHasCJKCodePages : 1 = false;
bool m_bPrtHasCJKCodePagesInitialized : 1 = false;
static tools::Long s_nPixWidth;
static MapMode *s_pPixMap;
@@ -100,6 +103,7 @@ public:
sal_uInt16 GetFontHeight( const SwViewShell *pSh, const OutputDevice& rOut );
sal_uInt16 GetFontLeading( const SwViewShell *pSh, const OutputDevice& rOut );
sal_uInt16 GetFontHangingBaseline( const SwViewShell *pSh, const OutputDevice& rOut );
bool GetFontHasCJKCodePages(const SwViewShell* pSh, const OutputDevice& rOut);
void GuessLeading( const SwViewShell& rSh, const FontMetric& rMet );
+4
View File
@@ -86,6 +86,7 @@ class SwSubFont final : public SvxFont
void DrawStretchText_( SwDrawTextInfo &rInf );
TextFrameIndex GetModelPositionForViewPoint_( SwDrawTextInfo& rInf );
TextFrameIndex GetCapitalCursorOfst( SwDrawTextInfo& rInf );
bool HasCJKCodePages(SwViewShell const* pSh, const OutputDevice& rOut);
inline void SetColor( const Color& rColor );
inline void SetFillColor( const Color& rColor );
@@ -346,6 +347,9 @@ public:
sal_uInt16 GetHangingBaseline( SwViewShell const *pSh, const OutputDevice& rOut )
{ return m_nActual == SwFontScript::CTL ? m_aSub[m_nActual].GetHangingBaseline( pSh, rOut ) : 0; }
bool HasCJKCodePages( SwViewShell const *pSh, const OutputDevice &rOut )
{ return m_aSub[m_nActual].HasCJKCodePages(pSh, rOut); }
void Invalidate()
{ m_bFontChg = m_bOrgChg = true; }
+7
View File
@@ -270,6 +270,7 @@ public:
sal_uInt16 GetAscent() const;
sal_uInt16 GetHangingBaseline() const;
bool FontHasCJKCodePages() const;
TextFrameIndex GetIdx() const { return m_nIdx; }
void SetIdx(const TextFrameIndex nNew) { m_nIdx = nNew; }
@@ -795,6 +796,12 @@ inline sal_uInt16 SwTextSizeInfo::GetHangingBaseline() const
return const_cast<SwFont*>(GetFont())->GetHangingBaseline( m_pVsh, *GetOut() );
}
inline bool SwTextSizeInfo::FontHasCJKCodePages() const
{
assert(GetOut());
return const_cast<SwFont*>(GetFont())->HasCJKCodePages(m_pVsh, *GetOut());
}
inline SwPositiveSize SwTextSizeInfo::GetTextSize( const OUString &rText ) const
{
return GetTextSize(m_pOut, nullptr, rText, TextFrameIndex(0), TextFrameIndex(rText.getLength()));
+52 -4
View File
@@ -847,6 +847,47 @@ void SwTextFormatter::CalcAdjustLine( SwLineLayout *pCurrent )
void SwTextFormatter::CalcAscent( SwTextFormatInfo &rInf, SwLinePortion *pPor )
{
std::optional<bool> bShouldApplyCJKAdjustment;
auto fnShouldApplyCJKAdjustment = [&]
{
if (!bShouldApplyCJKAdjustment.has_value())
{
bShouldApplyCJKAdjustment = [&]
{
if (!GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
DocumentSettingId::MS_WORD_COMP_GRID_METRICS))
{
return false;
}
// tdf#171275: Do not add the CJK extra leading when grid is enabled
auto const* const pGrid = GetGridItem(m_pFrame->FindPageFrame());
if (pGrid && rInf.SnapToGrid())
{
return false;
}
return rInf.FontHasCJKCodePages();
}();
}
return *bShouldApplyCJKAdjustment;
};
auto fnAdjustHeightAscent = [&](sal_uInt16 nHeight, sal_uInt16 nAscent)
{
if (fnShouldApplyCJKAdjustment())
{
// tdf#129808: For Word layout compatibility, adjust ascent for fonts
// advertising support for certain CJK code pages.
sal_uInt16 nAdjustedHeight = (nHeight * 127) / 100;
sal_uInt16 nAdjustedAscent = nAdjustedHeight - (nHeight - nAscent);
return std::make_tuple(nAdjustedHeight, nAdjustedAscent);
}
return std::make_tuple(nHeight, nAscent);
};
bool bCalc = false;
if ( pPor->InFieldGrp() && static_cast<SwFieldPortion*>(pPor)->GetFont() )
{
@@ -854,8 +895,11 @@ void SwTextFormatter::CalcAscent( SwTextFormatInfo &rInf, SwLinePortion *pPor )
// independent from hard attribute values
SwFont* pFieldFnt = static_cast<SwFieldPortion*>(pPor)->m_pFont.get();
SwFontSave aSave( rInf, pFieldFnt );
pPor->Height( rInf.GetTextHeight() );
pPor->SetAscent( rInf.GetAscent() );
auto [nAdjustedHeight, nAdjustedAscent]
= fnAdjustHeightAscent(rInf.GetTextHeight(), rInf.GetAscent());
pPor->Height(nAdjustedHeight);
pPor->SetAscent(nAdjustedAscent);
bCalc = true;
}
// i#89179
@@ -928,8 +972,12 @@ void SwTextFormatter::CalcAscent( SwTextFormatInfo &rInf, SwLinePortion *pPor )
|| !rInf.GetLast()->InTextGrp() )
{
pPor->SetHangingBaseline( rInf.GetHangingBaseline() );
pPor->SetAscent( rInf.GetAscent() );
pPor->Height(rInf.GetTextHeight());
auto [nAdjustedHeight, nAdjustedAscent]
= fnAdjustHeightAscent(rInf.GetTextHeight(), rInf.GetAscent());
pPor->SetAscent(nAdjustedAscent);
pPor->Height(nAdjustedHeight);
bCalc = true;
}
else
+42 -25
View File
@@ -266,30 +266,49 @@ static void lcl_calcLinePos( const CalcLinePosData &rData,
}
}
// tdf#129808: Oddly, Word scales up ascent and line height for *fonts* that self-report
// coverage for certain specific CJK code pages, even when that font isn't used for CJK
// text. Apply this scale when necessary.
static sal_uInt16 lcl_ApplyCjkHeightAdjustment(sal_uInt16 nBase, const SwViewShell* pSh,
const OutputDevice& rDev)
namespace
{
bool lcl_FontHasCJKCodePages(const OutputDevice& rOut)
{
if (!pSh || !pSh->getIDocumentSettingAccess().get(DocumentSettingId::MS_WORD_COMP_GRID_METRICS))
{
return nBase;
}
vcl::FontCapabilities stCaps;
if (rDev.GetFontCapabilities(stCaps) && stCaps.oCodePageRange.has_value())
if (!rOut.GetFontCapabilities(stCaps) || !stCaps.oCodePageRange.has_value())
{
if (stCaps.oCodePageRange->test(vcl::CodePageCoverage::CP932)
|| stCaps.oCodePageRange->test(vcl::CodePageCoverage::CP936)
|| stCaps.oCodePageRange->test(vcl::CodePageCoverage::CP949)
|| stCaps.oCodePageRange->test(vcl::CodePageCoverage::CP950))
{
return (nBase * 127) / 100;
}
return false;
}
return nBase;
// tdf#129808: Oddly, Word scales up ascent and line height for *fonts* that
// self-report coverage for certain specific CJK code pages, even when that
// font isn't used for CJK text. Only report these code pages.
return stCaps.oCodePageRange->test(vcl::CodePageCoverage::CP932)
|| stCaps.oCodePageRange->test(vcl::CodePageCoverage::CP936)
|| stCaps.oCodePageRange->test(vcl::CodePageCoverage::CP949)
|| stCaps.oCodePageRange->test(vcl::CodePageCoverage::CP950);
}
}
bool SwFntObj::GetFontHasCJKCodePages(const SwViewShell* pSh, const OutputDevice& rOut)
{
const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;
if (pSh)
{
CreateScrFont(*pSh, rOut);
return m_bScrHasCJKCodePages;
}
else
{
if (!m_bPrtHasCJKCodePagesInitialized)
{
CreatePrtFont(rOut);
const vcl::Font aOldFnt(rRefDev.GetFont());
const_cast<OutputDevice&>(rRefDev).SetFont(*m_pPrtFont);
m_bPrtHasCJKCodePages = lcl_FontHasCJKCodePages(rRefDev);
m_bPrtHasCJKCodePagesInitialized = true;
const_cast<OutputDevice&>(rRefDev).SetFont(aOldFnt);
}
return m_bPrtHasCJKCodePages;
}
}
// Returns the Ascent of the Font on the given output device;
@@ -321,8 +340,6 @@ sal_uInt16 SwFntObj::GetFontAscent( const SwViewShell *pSh, const OutputDevice&
nRet = m_nPrtAscent;
}
nRet = lcl_ApplyCjkHeightAdjustment(nRet, pSh, rRefDev);
#if !defined(MACOSX) // #i89844# extleading is below the line for Mac
// TODO: move extleading below the line for all platforms too
nRet += GetFontLeading( pSh, rRefDev );
@@ -344,8 +361,7 @@ sal_uInt16 SwFntObj::GetFontHeight( const SwViewShell* pSh, const OutputDevice&
CreateScrFont( *pSh, rOut );
OSL_ENSURE( USHRT_MAX != m_nScrHeight, "nScrHeight is going berzerk" );
nRet = lcl_ApplyCjkHeightAdjustment(m_nScrHeight, pSh, rRefDev)
+ GetFontLeading(pSh, rRefDev);
nRet = m_nScrHeight + GetFontLeading(pSh, rRefDev);
}
else
{
@@ -368,8 +384,7 @@ sal_uInt16 SwFntObj::GetFontHeight( const SwViewShell* pSh, const OutputDevice&
const_cast<OutputDevice&>(rRefDev).SetFont( aOldFnt );
}
nRet = lcl_ApplyCjkHeightAdjustment(m_nPrtHeight, pSh, rRefDev)
+ GetFontLeading(pSh, rRefDev);
nRet = m_nPrtHeight + GetFontLeading(pSh, rRefDev);
}
OSL_ENSURE( USHRT_MAX != nRet, "GetFontHeight returned USHRT_MAX" );
@@ -521,6 +536,8 @@ void SwFntObj::CreateScrFont( const SwViewShell& rSh, const OutputDevice& rOut )
if ( USHRT_MAX == m_nScrHeight )
m_nScrHeight = o3tl::narrowing<sal_uInt16>(pOut->GetTextHeight());
m_bScrHasCJKCodePages = lcl_FontHasCJKCodePages(*pOut);
// reset original output device font
pOut->SetFont( aOldOutFont );
}
+6
View File
@@ -1013,6 +1013,12 @@ sal_uInt16 SwSubFont::GetHangingBaseline( SwViewShell const *pSh, const OutputDe
return aFntAccess.Get()->GetFontHangingBaseline( pSh, rOut );
}
bool SwSubFont::HasCJKCodePages(SwViewShell const* pSh, const OutputDevice& rOut)
{
SwFntAccess aFntAccess(m_nFontCacheId, m_nFontIndex, this, pSh);
return aFntAccess.Get()->GetFontHasCJKCodePages(pSh, rOut);
}
Size SwSubFont::GetTextSize_( SwDrawTextInfo& rInf )
{
// Robust: the font is supposed to be set already, but better safe than