editeng/source/items/svxfont.cxx | 28 ++++++++++++------------ include/editeng/svxfont.hxx | 6 ----- include/vcl/font.hxx | 3 ++ include/vcl/rendercontext/SalLayoutFlags.hxx | 3 +- vcl/inc/impfont.hxx | 1 vcl/source/font/font.cxx | 31 ++++++++++++++++++++++++++- vcl/source/gdi/CommonSalLayout.cxx | 6 ++++- vcl/source/outdev/text.cxx | 3 ++ vcl/source/text/ImplLayoutArgs.cxx | 1 9 files changed, 59 insertions(+), 23 deletions(-)
New commits: commit b9f0caad5d9e628f82d5148dfc7d2436d32817e2 Author: Khaled Hosny <kha...@aliftype.com> AuthorDate: Tue Aug 23 04:13:28 2022 +0200 Commit: خالد حسني <kha...@aliftype.com> CommitDate: Tue Aug 23 09:24:52 2022 +0200 tdf#66819: Disable ligatures with character spacing When character spacing is not zero, optional ligatures should be disabled (e.g. this what CSS requires and what browsers implement: https://drafts.csswg.org/css-text/#letter-spacing-property). This disables both “liga” and “clig” OpenType features because they are optional features and are enabled by default, it does not disable “rlig” because it is for orthographically-required ligatures, nor “dlig” or “hlig” because they are disabled by default. The character spacing values (confusingly called kerning in the source code) are moved from SvxFont to vcl::Font so it can’t be accessed in OutputDevice and pass the relevant flag to SalLayout. Change-Id: Ieacc875df3896ad7a63ae8d116f4c6ff7265b9a5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/138711 Tested-by: Jenkins Reviewed-by: خالد حسني <kha...@aliftype.com> diff --git a/editeng/source/items/svxfont.cxx b/editeng/source/items/svxfont.cxx index ac360873824a..868b830c63cc 100644 --- a/editeng/source/items/svxfont.cxx +++ b/editeng/source/items/svxfont.cxx @@ -42,7 +42,7 @@ static tools::Long GetTextArray( const OutputDevice* pOut, const OUString& rStr, SvxFont::SvxFont() { - nKern = nEsc = 0; + nEsc = 0; nPropr = 100; eCaseMap = SvxCaseMap::NotMapped; SetLanguage(LANGUAGE_SYSTEM); @@ -51,7 +51,7 @@ SvxFont::SvxFont() SvxFont::SvxFont( const vcl::Font &rFont ) : Font( rFont ) { - nKern = nEsc = 0; + nEsc = 0; nPropr = 100; eCaseMap = SvxCaseMap::NotMapped; SetLanguage(LANGUAGE_SYSTEM); @@ -60,7 +60,6 @@ SvxFont::SvxFont( const vcl::Font &rFont ) SvxFont::SvxFont( const SvxFont &rFont ) : Font( rFont ) { - nKern = rFont.GetFixKerning(); nEsc = rFont.GetEscapement(); nPropr = rFont.GetPropr(); eCaseMap = rFont.GetCaseMap(); @@ -409,7 +408,7 @@ vcl::Font SvxFont::ChgPhysFont(OutputDevice& rOut) const Size SvxFont::GetPhysTxtSize( const OutputDevice *pOut, const OUString &rTxt, const sal_Int32 nIdx, const sal_Int32 nLen ) const { - if ( !IsCaseMap() && !IsKern() ) + if ( !IsCaseMap() && !IsFixKerning() ) return Size( pOut->GetTextWidth( rTxt, nIdx, nLen ), pOut->GetTextHeight() ); @@ -439,8 +438,9 @@ Size SvxFont::GetPhysTxtSize( const OutputDevice *pOut, const OUString &rTxt, aTxtSize.setWidth(nWidth); } - if( IsKern() && ( nLen > 1 ) ) + if( IsFixKerning() && ( nLen > 1 ) ) { + auto nKern = GetFixKerning(); std::vector<sal_Int32> aDXArray(nLen); GetTextArray(pOut, rTxt, &aDXArray, nIdx, nLen); tools::Long nOldValue = aDXArray[0]; @@ -461,7 +461,7 @@ Size SvxFont::GetPhysTxtSize( const OutputDevice *pOut, const OUString &rTxt, Size SvxFont::GetPhysTxtSize( const OutputDevice *pOut ) { - if ( !IsCaseMap() && !IsKern() ) + if ( !IsCaseMap() && !IsFixKerning() ) return Size( pOut->GetTextWidth( "" ), pOut->GetTextHeight() ); Size aTxtSize; @@ -477,14 +477,14 @@ Size SvxFont::GetPhysTxtSize( const OutputDevice *pOut ) Size SvxFont::QuickGetTextSize( const OutputDevice *pOut, const OUString &rTxt, const sal_Int32 nIdx, const sal_Int32 nLen, std::vector<sal_Int32>* pDXArray ) const { - if ( !IsCaseMap() && !IsKern() ) + if ( !IsCaseMap() && !IsFixKerning() ) return Size( GetTextArray( pOut, rTxt, pDXArray, nIdx, nLen ), pOut->GetTextHeight() ); std::vector<sal_Int32> aDXArray; // We always need pDXArray to count the number of kern spaces - if (!pDXArray && IsKern() && nLen > 1) + if (!pDXArray && IsFixKerning() && nLen > 1) { pDXArray = &aDXArray; aDXArray.reserve(nLen); @@ -498,8 +498,9 @@ Size SvxFont::QuickGetTextSize( const OutputDevice *pOut, const OUString &rTxt, aTxtSize.setWidth( GetTextArray( pOut, CalcCaseMap( rTxt ), pDXArray, nIdx, nLen ) ); - if( IsKern() && ( nLen > 1 ) ) + if( IsFixKerning() && ( nLen > 1 ) ) { + auto nKern = GetFixKerning(); tools::Long nOldValue = (*pDXArray)[0]; tools::Long nSpaceSum = nKern; (*pDXArray)[0] += nSpaceSum; @@ -560,7 +561,7 @@ void SvxFont::QuickDrawText( OutputDevice *pOut, { // Font has to be selected in OutputDevice... - if ( !IsCaseMap() && !IsCapital() && !IsKern() && !IsEsc() ) + if ( !IsCaseMap() && !IsCapital() && !IsFixKerning() && !IsEsc() ) { DrawTextArray( pOut, rPos, rTxt, pDXArray, pKashidaArray, nIdx, nLen ); return; @@ -587,7 +588,7 @@ void SvxFont::QuickDrawText( OutputDevice *pOut, } else { - if ( IsKern() && pDXArray.empty() ) + if ( IsFixKerning() && pDXArray.empty() ) { Size aSize = GetPhysTxtSize( pOut, rTxt, nIdx, nLen ); @@ -687,7 +688,6 @@ SvxFont& SvxFont::operator=( const SvxFont& rFont ) eCaseMap = rFont.eCaseMap; nEsc = rFont.nEsc; nPropr = rFont.nPropr; - nKern = rFont.nKern; return *this; } @@ -744,7 +744,7 @@ Size SvxFont::GetCapitalSize( const OutputDevice *pOut, const OUString &rTxt, const sal_Int32 nIdx, const sal_Int32 nLen) const { // Start: - SvxDoGetCapitalSize aDo( const_cast<SvxFont *>(this), pOut, rTxt, nIdx, nLen, nKern ); + SvxDoGetCapitalSize aDo( const_cast<SvxFont *>(this), pOut, rTxt, nIdx, nLen, GetFixKerning() ); DoOnCapitals( aDo ); Size aTxtSize( aDo.GetSize() ); @@ -856,7 +856,7 @@ void SvxFont::DrawCapital( OutputDevice *pOut, const Point &rPos, const OUString &rTxt, const sal_Int32 nIdx, const sal_Int32 nLen ) const { - SvxDoDrawCapital aDo( const_cast<SvxFont *>(this),pOut,rTxt,nIdx,nLen,rPos,nKern ); + SvxDoDrawCapital aDo( const_cast<SvxFont *>(this),pOut,rTxt,nIdx,nLen,rPos,GetFixKerning() ); DoOnCapitals( aDo ); } diff --git a/include/editeng/svxfont.hxx b/include/editeng/svxfont.hxx index ac4ec390128a..f4810ba25640 100644 --- a/include/editeng/svxfont.hxx +++ b/include/editeng/svxfont.hxx @@ -41,7 +41,6 @@ class EDITENG_DLLPUBLIC SvxFont : public vcl::Font SvxCaseMap eCaseMap; // Text Markup short nEsc; // Degree of Superscript/Subscript sal_uInt8 nPropr; // Degree of reduction of the font height - short nKern; // Kerning in Pt public: SvxFont(); @@ -59,17 +58,12 @@ public: void SetProprRel( const sal_uInt8 nNewPropr ) { SetPropr( static_cast<sal_uInt8>( static_cast<tools::Long>(nNewPropr) * static_cast<tools::Long>(nPropr) / 100 ) ); } - // Kerning - short GetFixKerning() const { return nKern; } - void SetFixKerning( const short nNewKern ) { nKern = nNewKern; } - SvxCaseMap GetCaseMap() const { return eCaseMap; } void SetCaseMap( const SvxCaseMap eNew ) { eCaseMap = eNew; } // Is-Methods: bool IsCaseMap() const { return SvxCaseMap::NotMapped != eCaseMap; } bool IsCapital() const { return SvxCaseMap::SmallCaps == eCaseMap; } - bool IsKern() const { return 0 != nKern; } bool IsEsc() const { return 0 != nEsc; } // Consider Upper case, Lower case letters etc. diff --git a/include/vcl/font.hxx b/include/vcl/font.hxx index b371990ba11c..222f7d962774 100644 --- a/include/vcl/font.hxx +++ b/include/vcl/font.hxx @@ -133,6 +133,9 @@ public: void SetKerning( FontKerning nKerning ); FontKerning GetKerning() const; bool IsKerning() const; + void SetFixKerning(const short nSpacing); + short GetFixKerning() const; + bool IsFixKerning() const; void SetOutline( bool bOutline ); bool IsOutline() const; diff --git a/include/vcl/rendercontext/SalLayoutFlags.hxx b/include/vcl/rendercontext/SalLayoutFlags.hxx index e8c5901d8528..576a4abd8fbd 100644 --- a/include/vcl/rendercontext/SalLayoutFlags.hxx +++ b/include/vcl/rendercontext/SalLayoutFlags.hxx @@ -30,12 +30,13 @@ enum class SalLayoutFlags DisableKerning = 0x0010, KerningAsian = 0x0020, Vertical = 0x0040, + DisableLigatures = 0x0200, ForFallback = 0x2000, GlyphItemsOnly = 0x4000, }; namespace o3tl { -template <> struct typed_flags<SalLayoutFlags> : is_typed_flags<SalLayoutFlags, 0x6077> +template <> struct typed_flags<SalLayoutFlags> : is_typed_flags<SalLayoutFlags, 0x6277> { }; } diff --git a/vcl/inc/impfont.hxx b/vcl/inc/impfont.hxx index a63e2a27d1c2..450e227b34ad 100644 --- a/vcl/inc/impfont.hxx +++ b/vcl/inc/impfont.hxx @@ -119,6 +119,7 @@ private: FontRelief meRelief; FontEmphasisMark meEmphasisMark; FontKerning meKerning; + short mnSpacing; Size maAverageFontSize; rtl_TextEncoding meCharSet; diff --git a/vcl/source/font/font.cxx b/vcl/source/font/font.cxx index 941fe3d6fc0a..90cdba4163db 100644 --- a/vcl/source/font/font.cxx +++ b/vcl/source/font/font.cxx @@ -247,6 +247,22 @@ bool Font::IsKerning() const return mpImplFont->meKerning != FontKerning::NONE; } +void Font::SetFixKerning( short nSpacing ) +{ + if (const_cast<const ImplType&>(mpImplFont)->mnSpacing != nSpacing) + mpImplFont->mnSpacing = nSpacing; +} + +short Font::GetFixKerning() const +{ + return mpImplFont->mnSpacing; +} + +bool Font::IsFixKerning() const +{ + return mpImplFont->mnSpacing != 0; +} + void Font::SetWeight( FontWeight eWeight ) { if (const_cast<const ImplType&>(mpImplFont)->GetWeightNoAsk() != eWeight) @@ -539,6 +555,12 @@ SvStream& ReadImplFont( SvStream& rIStm, ImplFont& rImplFont, tools::Long& rnNor rnNormedFontScaling = nNormedFontScaling; } + if( aCompat.GetVersion() >= 5 ) + { + rIStm.ReadInt16( nTmps16 ); + rImplFont.mnSpacing = nTmps16; + } + // Relief // CJKContextLanguage @@ -548,7 +570,8 @@ SvStream& ReadImplFont( SvStream& rIStm, ImplFont& rImplFont, tools::Long& rnNor SvStream& WriteImplFont( SvStream& rOStm, const ImplFont& rImplFont, tools::Long nNormedFontScaling ) { // tdf#127471 increase to version 4 - VersionCompatWrite aCompat( rOStm, 4 ); + // tdf#66819 increase to version 5 + VersionCompatWrite aCompat( rOStm, 5 ); TypeSerializer aSerializer(rOStm); rOStm.WriteUniOrByteString( rImplFont.GetFamilyName(), rOStm.GetStreamCharSet() ); @@ -584,6 +607,8 @@ SvStream& WriteImplFont( SvStream& rOStm, const ImplFont& rImplFont, tools::Long // new in version 4, NormedFontScaling rOStm.WriteInt32(nNormedFontScaling); + // new in version 5 + rOStm.WriteInt16( rImplFont.mnSpacing ); return rOStm; } @@ -961,6 +986,7 @@ ImplFont::ImplFont() : meRelief( FontRelief::NONE ), meEmphasisMark( FontEmphasisMark::NONE ), meKerning( FontKerning::FontSpecific ), + mnSpacing( 0 ), meCharSet( RTL_TEXTENCODING_DONTKNOW ), maLanguageTag( LANGUAGE_DONTKNOW ), maCJKLanguageTag( LANGUAGE_DONTKNOW ), @@ -993,6 +1019,7 @@ ImplFont::ImplFont( const ImplFont& rImplFont ) : meRelief( rImplFont.meRelief ), meEmphasisMark( rImplFont.meEmphasisMark ), meKerning( rImplFont.meKerning ), + mnSpacing( rImplFont.mnSpacing ), maAverageFontSize( rImplFont.maAverageFontSize ), meCharSet( rImplFont.meCharSet ), maLanguageTag( rImplFont.maLanguageTag ), @@ -1056,6 +1083,7 @@ bool ImplFont::EqualIgnoreColor( const ImplFont& rOther ) const || (mbOutline != rOther.mbOutline) || (mbShadow != rOther.mbShadow) || (meKerning != rOther.meKerning) + || (mnSpacing != rOther.mnSpacing) || (mbTransparent != rOther.mbTransparent) ) return false; @@ -1100,6 +1128,7 @@ size_t ImplFont::GetHashValueIgnoreColor() const o3tl::hash_combine( hash, mbOutline ); o3tl::hash_combine( hash, mbShadow ); o3tl::hash_combine( hash, meKerning ); + o3tl::hash_combine( hash, mnSpacing ); o3tl::hash_combine( hash, mbTransparent ); return hash; diff --git a/vcl/source/gdi/CommonSalLayout.cxx b/vcl/source/gdi/CommonSalLayout.cxx index 73b7e58ae43d..81f495afe981 100644 --- a/vcl/source/gdi/CommonSalLayout.cxx +++ b/vcl/source/gdi/CommonSalLayout.cxx @@ -318,10 +318,14 @@ bool GenericSalLayout::LayoutText(vcl::text::ImplLayoutArgs& rArgs, const SalLay maFeatures.push_back({ HB_TAG('k','e','r','n'), 0, 0, static_cast<unsigned int>(-1) }); } - if (rFontSelData.GetPitch() == PITCH_FIXED) + if (rArgs.mnFlags & SalLayoutFlags::DisableLigatures) { SAL_INFO("vcl.harfbuzz", "Disabling ligatures for font: " << rFontSelData.maTargetName); + + // Both of these are optional ligatures, enabled by default but not for + // orthographically-required ligatures. maFeatures.push_back({ HB_TAG('l','i','g','a'), 0, 0, static_cast<unsigned int>(-1) }); + maFeatures.push_back({ HB_TAG('c','l','i','g'), 0, 0, static_cast<unsigned int>(-1) }); } ParseFeatures(rFontSelData.maTargetName); diff --git a/vcl/source/outdev/text.cxx b/vcl/source/outdev/text.cxx index d2c8855a2543..823362c6c071 100644 --- a/vcl/source/outdev/text.cxx +++ b/vcl/source/outdev/text.cxx @@ -1177,6 +1177,9 @@ vcl::text::ImplLayoutArgs OutputDevice::ImplPrepareLayoutArgs( OUString& rStr, nLayoutFlags |= SalLayoutFlags::KerningAsian; if( maFont.IsVertical() ) nLayoutFlags |= SalLayoutFlags::Vertical; + if( maFont.IsFixKerning() || + ( mpFontInstance && mpFontInstance->GetFontSelectPattern().GetPitch() == PITCH_FIXED ) ) + nLayoutFlags |= SalLayoutFlags::DisableLigatures; if( meTextLanguage ) //TODO: (mnTextLayoutMode & vcl::text::ComplexTextLayoutFlags::SubstituteDigits) { diff --git a/vcl/source/text/ImplLayoutArgs.cxx b/vcl/source/text/ImplLayoutArgs.cxx index 943de7d2eb75..e837ca411014 100644 --- a/vcl/source/text/ImplLayoutArgs.cxx +++ b/vcl/source/text/ImplLayoutArgs.cxx @@ -269,6 +269,7 @@ std::ostream& operator<<(std::ostream& s, vcl::text::ImplLayoutArgs const& rArgs TEST(DisableKerning); TEST(KerningAsian); TEST(Vertical); + TEST(DisableLigatures); TEST(ForFallback); #undef TEST s << "}";