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:
File diff suppressed because it is too large
Load Diff
@@ -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:
|
||||
|
||||
@@ -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 );
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user