Rebased ref, commits from common ancestor: commit c8e0298987f12f732c686f3ccd2ee4e342d8f89c Author: Khaled Hosny <khaledho...@eglug.org> Date: Fri Oct 14 02:50:27 2016 -0700
Support font fallback on macOS for CommonSalLayout Change-Id: Ifd26b7f14ed77a3aa2a38e5961cac5f9bbb6d796 diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h index f7e5156..6958541 100644 --- a/vcl/inc/quartz/salgdi.h +++ b/vcl/inc/quartz/salgdi.h @@ -102,6 +102,8 @@ public: hb_font_t* GetHbFont() const { return mpHbFont; } void SetHbFont(hb_font_t* pHbFont) const { mpHbFont = pHbFont; } + CFMutableDictionaryRef GetStyleDict( void ) const { return mpStyleDict; } + const CoreTextFontFace* mpFontData; /// <1.0: font is squeezed, >1.0 font is stretched, else 1.0 float mfFontStretch; @@ -113,11 +115,6 @@ private: /// CoreText text style object CFMutableDictionaryRef mpStyleDict; mutable hb_font_t* mpHbFont; - - friend class CTLayout; - friend class AquaSalGraphics; - friend class CommonSalLayout; - CFMutableDictionaryRef GetStyleDict( void ) const { return mpStyleDict; } }; // TODO: move into cross-platform headers @@ -172,8 +169,8 @@ protected: RGBAColor maFillColor; // Device Font settings - const CoreTextFontFace* mpFontData; - CoreTextStyle* mpTextStyle; + const CoreTextFontFace* mpFontData[MAX_FALLBACK]; + CoreTextStyle* mpTextStyle[MAX_FALLBACK]; RGBAColor maTextColor; /// allows text to be rendered without antialiasing bool mbNonAntialiasedText; diff --git a/vcl/quartz/ctlayout.cxx b/vcl/quartz/ctlayout.cxx index 856e066..983f50b 100644 --- a/vcl/quartz/ctlayout.cxx +++ b/vcl/quartz/ctlayout.cxx @@ -28,7 +28,6 @@ #include "quartz/ctfonts.hxx" #include "CTRunData.hxx" #include "quartz/utils.h" -#include "CommonSalLayout.hxx" class CTLayout : public SalLayout @@ -782,10 +781,7 @@ void CTLayout::Simplify( bool /*bIsBase*/ ) {} SalLayout* CoreTextStyle::GetTextLayout() const { - if (SalLayout::UseCommonLayout()) - return new CommonSalLayout(*this); - else - return new CTLayout(this); + return new CTLayout(this); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/quartz/salgdi.cxx b/vcl/quartz/salgdi.cxx index bb99737..caa7e35 100644 --- a/vcl/quartz/salgdi.cxx +++ b/vcl/quartz/salgdi.cxx @@ -42,6 +42,8 @@ #include "impfontcharmap.hxx" #include "impfontmetricdata.hxx" #include "CommonSalLayout.hxx" +#include "outdev.h" +#include "PhysicalFontCollection.hxx" #ifdef MACOSX #include "osx/salframe.h" @@ -55,6 +57,49 @@ using namespace vcl; +class CoreTextGlyphFallbackSubstititution +: public ImplGlyphFallbackFontSubstitution +{ +public: + bool FindFontSubstitute(FontSelectPattern&, OUString&) const override; +}; + +bool CoreTextGlyphFallbackSubstititution::FindFontSubstitute(FontSelectPattern& rPattern, + OUString& rMissingChars) const +{ + bool bFound = false; + CoreTextStyle rStyle(rPattern); + CTFontRef pFont = static_cast<CTFontRef>(CFDictionaryGetValue(rStyle.GetStyleDict(), kCTFontAttributeName)); + CFStringRef pStr = CreateCFString(rMissingChars); + if (pStr) + { + CTFontRef pFallback = CTFontCreateForString(pFont, pStr, CFRangeMake(0, CFStringGetLength(pStr))); + if (pFallback) + { + bFound = true; + + CTFontDescriptorRef pDesc = CTFontCopyFontDescriptor(pFallback); + FontAttributes rAttr = DevFontFromCTFontDescriptor(pDesc, nullptr); + + rPattern.maSearchName = rAttr.GetFamilyName(); + + rPattern.SetWeight(rAttr.GetWeight()); + rPattern.SetItalic(rAttr.GetItalic()); + rPattern.SetPitch(rAttr.GetPitch()); + rPattern.SetWidthType(rAttr.GetWidthType()); + + SalData* pSalData = GetSalData(); + if (pSalData->mpFontList) + rPattern.mpFontData = pSalData->mpFontList->GetFontDataFromId(reinterpret_cast<sal_IntPtr>(pDesc)); + + CFRelease(pFallback); + } + CFRelease(pStr); + } + + return bFound; +} + CoreTextFontFace::CoreTextFontFace( const CoreTextFontFace& rSrc ) : PhysicalFontFace( rSrc ) , mnFontId( rSrc.mnFontId ) @@ -245,8 +290,6 @@ AquaSalGraphics::AquaSalGraphics() , mxClipPath( nullptr ) , maLineColor( COL_WHITE ) , maFillColor( COL_BLACK ) - , mpFontData( nullptr ) - , mpTextStyle( nullptr ) , maTextColor( COL_BLACK ) , mbNonAntialiasedText( false ) , mbPrinter( false ) @@ -258,6 +301,12 @@ AquaSalGraphics::AquaSalGraphics() #endif { SAL_INFO( "vcl.quartz", "AquaSalGraphics::AquaSalGraphics() this=" << this ); + + for (int i = 0; i < MAX_FALLBACK; ++i) + { + mpTextStyle[i] = nullptr; + mpFontData[i] = nullptr; + } } AquaSalGraphics::~AquaSalGraphics() @@ -270,7 +319,8 @@ AquaSalGraphics::~AquaSalGraphics() CGPathRelease( mxClipPath ); } - delete mpTextStyle; + for (int i = 0; i < MAX_FALLBACK; ++i) + delete mpTextStyle[i]; if( mpXorEmulation ) delete mpXorEmulation; @@ -308,9 +358,12 @@ void AquaSalGraphics::SetTextColor( SalColor nSalColor ) // SAL_ DEBUG(std::hex << nSalColor << std::dec << "={" << maTextColor.GetRed() << ", " << maTextColor.GetGreen() << ", " << maTextColor.GetBlue() << ", " << maTextColor.GetAlpha() << "}"); } -void AquaSalGraphics::GetFontMetric( ImplFontMetricDataRef& rxFontMetric, int /*nFallbackLevel*/ ) +void AquaSalGraphics::GetFontMetric(ImplFontMetricDataRef& rxFontMetric, int nFallbackLevel) { - mpTextStyle->GetFontMetric( rxFontMetric ); + if (nFallbackLevel < MAX_FALLBACK && mpTextStyle[nFallbackLevel]) + { + mpTextStyle[nFallbackLevel]->GetFontMetric(rxFontMetric); + } } static bool AddTempDevFont(const OUString& rFontFileURL) @@ -387,6 +440,12 @@ void AquaSalGraphics::GetDevFontList( PhysicalFontCollection* pFontCollection ) // Copy all PhysicalFontFace objects contained in the SystemFontList pSalData->mpFontList->AnnounceFonts( *pFontCollection ); + + if (SalLayout::UseCommonLayout()) + { + static CoreTextGlyphFallbackSubstititution aSubstFallback; + pFontCollection->SetFallbackHook(&aSubstFallback); + } } void AquaSalGraphics::ClearDevFontCache() @@ -404,14 +463,24 @@ bool AquaSalGraphics::AddTempDevFont( PhysicalFontCollection*, bool AquaSalGraphics::GetGlyphOutline( sal_GlyphId aGlyphId, basegfx::B2DPolyPolygon& rPolyPoly ) { - const bool bRC = mpTextStyle->GetGlyphOutline( aGlyphId, rPolyPoly ); - return bRC; + const int nFallbackLevel = aGlyphId >> GF_FONTSHIFT; + if (nFallbackLevel < MAX_FALLBACK && mpTextStyle[nFallbackLevel]) + { + const bool bRC = mpTextStyle[nFallbackLevel]->GetGlyphOutline(aGlyphId, rPolyPoly); + return bRC; + } + return false; } bool AquaSalGraphics::GetGlyphBoundRect( sal_GlyphId aGlyphId, Rectangle& rRect ) { - const bool bRC = mpTextStyle->GetGlyphBoundRect( aGlyphId, rRect ); - return bRC; + const int nFallbackLevel = aGlyphId >> GF_FONTSHIFT; + if (nFallbackLevel < MAX_FALLBACK && mpTextStyle[nFallbackLevel]) + { + const bool bRC = mpTextStyle[nFallbackLevel]->GetGlyphBoundRect(aGlyphId, rRect); + return bRC; + } + return false; } void AquaSalGraphics::DrawSalLayout(const CommonSalLayout& rLayout) @@ -449,60 +518,71 @@ void AquaSalGraphics::DrawSalLayout(const CommonSalLayout& rLayout) CGContextRestoreGState(context); } -void AquaSalGraphics::SetFont( FontSelectPattern* pReqFont, int /*nFallbackLevel*/ ) +void AquaSalGraphics::SetFont(FontSelectPattern* pReqFont, int nFallbackLevel) { // release the text style - delete mpTextStyle; - mpTextStyle = nullptr; + for (int i = nFallbackLevel; i < MAX_FALLBACK; ++i) + { + delete mpTextStyle[i]; + mpTextStyle[i] = nullptr; + } // handle NULL request meaning: release-font-resources request if( !pReqFont ) { - mpFontData = nullptr; + mpFontData[nFallbackLevel] = nullptr; return; } // update the text style - mpFontData = static_cast<const CoreTextFontFace*>( pReqFont->mpFontData ); - mpTextStyle = new CoreTextStyle( *pReqFont ); + mpFontData[nFallbackLevel] = static_cast<const CoreTextFontFace*>(pReqFont->mpFontData); + mpTextStyle[nFallbackLevel] = new CoreTextStyle(*pReqFont); SAL_INFO("vcl.ct", "SetFont" - " to " << mpFontData->GetFamilyName() - << ", " << mpFontData->GetStyleName() - << " fontid=" << mpFontData->GetFontId() + " to " << mpFontData[nFallbackLevel]->GetFamilyName() + << ", " << mpFontData[nFallbackLevel]->GetStyleName() + << " fontid=" << mpFontData[nFallbackLevel]->GetFontId() << " for " << pReqFont->GetFamilyName() << ", " << pReqFont->GetStyleName() << " weight=" << pReqFont->GetWeight() << " slant=" << pReqFont->GetItalic() << " size=" << pReqFont->mnHeight << "x" << pReqFont->mnWidth << " orientation=" << pReqFont->mnOrientation + << " fallback level " << nFallbackLevel ); } -SalLayout* AquaSalGraphics::GetTextLayout( ImplLayoutArgs& /*rArgs*/, int /*nFallbackLevel*/ ) +SalLayout* AquaSalGraphics::GetTextLayout(ImplLayoutArgs& /*rArgs*/, int nFallbackLevel) { - SalLayout* pSalLayout = mpTextStyle->GetTextLayout(); + SalLayout* pSalLayout = nullptr; + if (mpTextStyle[nFallbackLevel]) + { + if (SalLayout::UseCommonLayout()) + pSalLayout = new CommonSalLayout(*mpTextStyle[nFallbackLevel]); + else + pSalLayout = mpTextStyle[nFallbackLevel]->GetTextLayout(); + } return pSalLayout; } const FontCharMapRef AquaSalGraphics::GetFontCharMap() const { - if( !mpFontData ) + if (!mpFontData[0]) { FontCharMapRef xFontCharMap( new FontCharMap() ); return xFontCharMap; } - return mpFontData->GetFontCharMap(); + return mpFontData[0]->GetFontCharMap(); } bool AquaSalGraphics::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const { - if( !mpFontData ) + if (!mpFontData[0]) return false; - return mpFontData->GetFontCapabilities(rFontCapabilities); + return mpFontData[0]->GetFontCapabilities(rFontCapabilities); } // fake a SFNT font directory entry for a font table @@ -772,7 +852,8 @@ void AquaSalGraphics::GetGlyphWidths( const PhysicalFontFace* pFontData, bool bV free( const_cast<TTSimpleGlyphMetrics *>(pGlyphMetrics) ); } - FontCharMapRef xFCMap = mpFontData->GetFontCharMap(); + CoreTextFontFace rCTFontData(*pFontData, pFontData->GetFontId()); + FontCharMapRef xFCMap = rCTFontData.GetFontCharMap(); SAL_WARN_IF( !xFCMap || !xFCMap->GetCharCount(), "vcl", "no charmap" ); // get unicode<->glyph encoding commit 6a9723c2aa90cd455c9f07adef6061caaff04ada Author: Khaled Hosny <khaledho...@eglug.org> Date: Thu Oct 13 22:46:28 2016 +0200 Check SAL_USE_COMMON_LAYOUT envar in one place Makes it easier to flip the switch in the future (or even do something more fancy other than checking envvar). Change-Id: Ie42ca012c167b2108f0fca1ce9ff7beee95f1be7 diff --git a/vcl/inc/sallayout.hxx b/vcl/inc/sallayout.hxx index 1050943..364bd48 100644 --- a/vcl/inc/sallayout.hxx +++ b/vcl/inc/sallayout.hxx @@ -201,6 +201,8 @@ public: virtual std::shared_ptr<vcl::TextLayoutCache> CreateTextLayoutCache(OUString const&) const; + static bool UseCommonLayout(); + protected: // used by layout engines SalLayout(); diff --git a/vcl/quartz/ctlayout.cxx b/vcl/quartz/ctlayout.cxx index f7fe0af..856e066 100644 --- a/vcl/quartz/ctlayout.cxx +++ b/vcl/quartz/ctlayout.cxx @@ -782,7 +782,7 @@ void CTLayout::Simplify( bool /*bIsBase*/ ) {} SalLayout* CoreTextStyle::GetTextLayout() const { - if (getenv("SAL_USE_COMMON_LAYOUT")) + if (SalLayout::UseCommonLayout()) return new CommonSalLayout(*this); else return new CTLayout(this); diff --git a/vcl/source/gdi/sallayout.cxx b/vcl/source/gdi/sallayout.cxx index ea16f4f..86b3ab1d 100644 --- a/vcl/source/gdi/sallayout.cxx +++ b/vcl/source/gdi/sallayout.cxx @@ -768,6 +768,12 @@ bool SalLayout::IsSpacingGlyph( sal_GlyphId nGlyph ) return bRet; } +bool SalLayout::UseCommonLayout() +{ + static bool bUse = getenv("SAL_USE_COMMON_LAYOUT") != nullptr; + return bUse; +} + GenericSalLayout::GenericSalLayout() {} diff --git a/vcl/unx/generic/gdi/cairotextrender.cxx b/vcl/unx/generic/gdi/cairotextrender.cxx index 46b9326..35d086d 100644 --- a/vcl/unx/generic/gdi/cairotextrender.cxx +++ b/vcl/unx/generic/gdi/cairotextrender.cxx @@ -532,7 +532,7 @@ SalLayout* CairoTextRender::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackL if( mpServerFont[ nFallbackLevel ] && !(rArgs.mnFlags & SalLayoutFlags::DisableGlyphProcessing) ) { - if (getenv("SAL_USE_COMMON_LAYOUT")) + if (SalLayout::UseCommonLayout()) { pLayout = new CommonSalLayout(*mpServerFont[nFallbackLevel]); } diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx index de0b4bf..9681986 100644 --- a/vcl/win/gdi/winlayout.cxx +++ b/vcl/win/gdi/winlayout.cxx @@ -3900,7 +3900,7 @@ SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLe const WinFontFace& rFontFace = *mpWinFontData[ nFallbackLevel ]; WinFontInstance& rFontInstance = *mpWinFontEntry[ nFallbackLevel ]; - if (getenv("SAL_USE_COMMON_LAYOUT")) + if (SalLayout::UseCommonLayout()) { return new CommonSalLayout(this, rFontInstance, rFontFace); } commit d5282a6ebfd2c60288a5505f6dd4ecc098fa9a76 Author: Khaled Hosny <khaledho...@eglug.org> Date: Mon Oct 10 01:36:45 2016 +0200 Just call ICU directly and cut the middle layers Change-Id: I7603d03fef8ca227c3e6fe25239281d18801522a diff --git a/vcl/source/gdi/CommonSalLayout.cxx b/vcl/source/gdi/CommonSalLayout.cxx index 47d24ee..feb37eb 100644 --- a/vcl/source/gdi/CommonSalLayout.cxx +++ b/vcl/source/gdi/CommonSalLayout.cxx @@ -25,6 +25,7 @@ #include <i18nlangtag/mslangid.hxx> #include <limits> #include <salgdi.hxx> +#include <unicode/uchar.h> #if defined(_WIN32) struct WinSalGraphicsWithIDFace @@ -116,14 +117,14 @@ static void scaleHbFont(hb_font_t* pHbFont, const FontSelectPattern& aFontSelDat hb_font_set_scale(pHbFont, nXScale, nYScale); } +#if !HB_VERSION_ATLEAST(1, 1, 0) static hb_unicode_funcs_t* getUnicodeFuncs() { static hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_create(hb_icu_get_unicode_funcs()); -#if !HB_VERSION_ATLEAST(1, 1, 0) hb_unicode_funcs_set_decompose_compatibility_func(ufuncs, unicodeDecomposeCompatibility, nullptr, nullptr); -#endif return ufuncs; } +#endif #if defined(_WIN32) CommonSalLayout::CommonSalLayout(WinSalGraphics* WSL, WinFontInstance& rWinFontInstance, const WinFontFace& rWinFontFace) @@ -382,8 +383,8 @@ bool CommonSalLayout::LayoutText(ImplLayoutArgs& rArgs) nHbFlags |= HB_BUFFER_FLAG_EOT; /* End-of-text */ hb_buffer_t *pHbBuffer = hb_buffer_create(); - static hb_unicode_funcs_t* pHbUnicodeFuncs = getUnicodeFuncs(); #if !HB_VERSION_ATLEAST(1, 1, 0) + static hb_unicode_funcs_t* pHbUnicodeFuncs = getUnicodeFuncs(); hb_buffer_set_unicode_funcs(pHbBuffer, pHbUnicodeFuncs); #endif hb_buffer_set_direction(pHbBuffer, bRightToLeft ? HB_DIRECTION_RTL: HB_DIRECTION_LTR); @@ -439,7 +440,7 @@ bool CommonSalLayout::LayoutText(ImplLayoutArgs& rArgs) else { #if HB_VERSION_ATLEAST(0, 9, 42) - if (hb_unicode_general_category (pHbUnicodeFuncs, aChar) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) + if (u_getIntPropertyValue(aChar, UCHAR_GENERAL_CATEGORY) == U_NON_SPACING_MARK) bDiacritic = true; #else // the font lacks GDEF table @@ -561,8 +562,7 @@ void CommonSalLayout::ApplyDXArray(ImplLayoutArgs& rArgs) sal_Int32 indexUtf16 = pGlyphIter->mnCharPos; sal_UCS4 aChar = rArgs.mrStr.iterateCodePoints(&indexUtf16, 0); - static hb_unicode_funcs_t* pHbUnicodeFuncs = getUnicodeFuncs(); - if (hb_unicode_general_category (pHbUnicodeFuncs, aChar) == HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR) + if (u_isUWhiteSpace(aChar)) continue; DeviceCoordinate nGapWidth = pKashida.second; commit 0f2ea62a1b3a26adf96be80064a143826a23cdb1 Author: Khaled Hosny <khaledho...@eglug.org> Date: Mon Oct 10 00:54:00 2016 +0200 Validate Kashida positions in CommonSalLayout Currently checks only for ligatures, but that is a big improvement over al code that didnât do any validation except on Windows. Change-Id: I035248f4ccc23134ea27b40c2dd6197130749f14 diff --git a/vcl/inc/CommonSalLayout.hxx b/vcl/inc/CommonSalLayout.hxx index 3115cee..513d2b9 100644 --- a/vcl/inc/CommonSalLayout.hxx +++ b/vcl/inc/CommonSalLayout.hxx @@ -71,6 +71,8 @@ public: virtual bool GetCharWidths(DeviceCoordinate* pCharWidths) const override; virtual void ApplyDXArray(ImplLayoutArgs&) override; + + virtual bool IsKashidaPosValid(int nCharPos) const override; }; #endif diff --git a/vcl/source/gdi/CommonSalLayout.cxx b/vcl/source/gdi/CommonSalLayout.cxx index 3212994..47d24ee 100644 --- a/vcl/source/gdi/CommonSalLayout.cxx +++ b/vcl/source/gdi/CommonSalLayout.cxx @@ -588,3 +588,30 @@ void CommonSalLayout::ApplyDXArray(ImplLayoutArgs& rArgs) } } } + +bool CommonSalLayout::IsKashidaPosValid(int nCharPos) const +{ + for (auto pIter = m_GlyphItems.begin(); pIter != m_GlyphItems.end(); ++pIter) + { + if (pIter->mnCharPos == nCharPos) + { + // Search backwards for previous glyph belonging to a different + // character. We are looking backwards because we are dealing with + // RTL glyphs, which will be in visual order. + for (auto pPrev = pIter - 1; pPrev != m_GlyphItems.begin(); --pPrev) + { + if (pPrev->mnCharPos != nCharPos) + { + // Check if the found glyph belongs to the next character, + // otherwise the current glyph will be a ligature which is + // invalid kashida position. + if (pPrev->mnCharPos == (nCharPos + 1)) + return true; + break; + } + } + } + } + + return false; +} commit b351497f54cc2f106a48b3c40b41e6ea6b0835d8 Author: Khaled Hosny <khaledho...@eglug.org> Date: Sun Oct 9 23:23:45 2016 +0200 Re-enable Kashida insertion in CommonSalLayout We now do Kashida insertion in ApplyDXArray(), no need for a separate step. This simplifies the code greatly (old code is in GenericSalLayout::KashidaJustify()). Change-Id: Ie31c8969e26f1f293820f1e90f963a5ba1fc9eb1 diff --git a/vcl/source/gdi/CommonSalLayout.cxx b/vcl/source/gdi/CommonSalLayout.cxx index d19506f..3212994 100644 --- a/vcl/source/gdi/CommonSalLayout.cxx +++ b/vcl/source/gdi/CommonSalLayout.cxx @@ -286,27 +286,18 @@ void CommonSalLayout::SetNeedFallback(ImplLayoutArgs& rArgs, sal_Int32 nCharPos, void CommonSalLayout::AdjustLayout(ImplLayoutArgs& rArgs) { - GenericSalLayout::AdjustLayout(rArgs); + SalLayout::AdjustLayout(rArgs); + + if (rArgs.mpDXArray) + ApplyDXArray(rArgs); + else if (rArgs.mnLayoutWidth) + Justify(rArgs.mnLayoutWidth); // apply asian kerning if the glyphs are not already formatted if ((rArgs.mnFlags & SalLayoutFlags::KerningAsian) && !(rArgs.mnFlags & SalLayoutFlags::Vertical)) if ((rArgs.mpDXArray != nullptr) || (rArgs.mnLayoutWidth != 0)) ApplyAsianKerning(rArgs.mrStr); - - if ((rArgs.mnFlags & SalLayoutFlags::KashidaJustification) && rArgs.mpDXArray) - { - hb_codepoint_t nKashidaCodePoint = 0x0640; - hb_codepoint_t nKashidaGlyphIndex; - - if (hb_font_get_glyph(mpHbFont, nKashidaCodePoint, 0, &nKashidaGlyphIndex)) - { - if (nKashidaGlyphIndex) - { - KashidaJustify(nKashidaGlyphIndex, hb_font_get_glyph_h_advance(mpHbFont, nKashidaGlyphIndex) >> 6); - } - } - } } void CommonSalLayout::DrawText(SalGraphics& rSalGraphics) const @@ -522,6 +513,16 @@ void CommonSalLayout::ApplyDXArray(ImplLayoutArgs& rArgs) pNewCharWidths[i] = rArgs.mpDXArray[i] - rArgs.mpDXArray[i - 1]; } + DeviceCoordinate nKashidaWidth = 0; + hb_codepoint_t nKashidaIndex; + if (rArgs.mnFlags & SalLayoutFlags::KashidaJustification) + { + if (hb_font_get_glyph(mpHbFont, 0x0640, 0, &nKashidaIndex)) + nKashidaWidth = hb_font_get_glyph_h_advance(mpHbFont, nKashidaIndex) / 64; + } + + std::map<size_t, DeviceCoordinate> pKashidas; + DeviceCoordinate nDelta = 0; size_t i = 0; while (i < m_GlyphItems.size()) @@ -529,16 +530,61 @@ void CommonSalLayout::ApplyDXArray(ImplLayoutArgs& rArgs) int nCharPos = m_GlyphItems[i].mnCharPos - mnMinCharPos; DeviceCoordinate nDiff = pNewCharWidths[nCharPos] - pOldCharWidths[nCharPos]; - m_GlyphItems[i].maLinearPos.X() += nDelta; + if (nKashidaWidth && nDiff) + pKashidas[i] = nDiff; + size_t j = i; - while (++j < m_GlyphItems.size()) + while (j < m_GlyphItems.size()) { if (m_GlyphItems[j].mnCharPos != m_GlyphItems[i].mnCharPos) break; m_GlyphItems[j].maLinearPos.X() += nDelta; + // For RTL, put all justification space to the left of the glyph. + if (m_GlyphItems[i].IsRTLGlyph()) + m_GlyphItems[j].maLinearPos.X() += nDiff; + ++j; } nDelta += nDiff; i = j; } + + if (!pKashidas.empty()) + { + size_t nInserted = 0; + for (auto const& pKashida : pKashidas) + { + auto pGlyphIter = m_GlyphItems.begin() + nInserted + pKashida.first; + + if (!pGlyphIter->IsRTLGlyph()) + continue; + + sal_Int32 indexUtf16 = pGlyphIter->mnCharPos; + sal_UCS4 aChar = rArgs.mrStr.iterateCodePoints(&indexUtf16, 0); + static hb_unicode_funcs_t* pHbUnicodeFuncs = getUnicodeFuncs(); + if (hb_unicode_general_category (pHbUnicodeFuncs, aChar) == HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR) + continue; + + DeviceCoordinate nGapWidth = pKashida.second; + int nKashidaCount = ceil(nGapWidth / nKashidaWidth); + DeviceCoordinate nInsertedKashidaWidth; + if (nGapWidth < nKashidaWidth) + nInsertedKashidaWidth = nGapWidth; + else + nInsertedKashidaWidth = nGapWidth / nKashidaCount; + + Point aPos(pGlyphIter->maLinearPos.X() - nGapWidth, 0); + int nCharPos = pGlyphIter->mnCharPos; + int nFlags = GlyphItem::IS_IN_CLUSTER | GlyphItem::IS_RTL_GLYPH; + while (nKashidaCount) + { + GlyphItem aKashida(nCharPos, nKashidaIndex, aPos, nFlags, nInsertedKashidaWidth); + pGlyphIter = m_GlyphItems.insert(pGlyphIter, aKashida); + aPos.X() += nInsertedKashidaWidth; + ++pGlyphIter; + ++nInserted; + --nKashidaCount; + } + } + } } commit 730063001e6281a6879908cd3dd9c74d0a657484 Author: Khaled Hosny <khaledho...@eglug.org> Date: Sun Oct 9 19:08:18 2016 +0200 Revert "Use HarfBuzz shape plan for a bit more control" This reverts commit 8b32ead0b988b142cd9878f126d985d946fd4ccc. diff --git a/vcl/source/gdi/CommonSalLayout.cxx b/vcl/source/gdi/CommonSalLayout.cxx index 03e6818..d19506f 100644 --- a/vcl/source/gdi/CommonSalLayout.cxx +++ b/vcl/source/gdi/CommonSalLayout.cxx @@ -317,7 +317,6 @@ void CommonSalLayout::DrawText(SalGraphics& rSalGraphics) const bool CommonSalLayout::LayoutText(ImplLayoutArgs& rArgs) { - hb_face_t* pHbFace = hb_font_get_face(mpHbFont); hb_script_t aHbScript = HB_SCRIPT_INVALID; int nGlyphCapacity = 2 * (rArgs.mnEndCharPos - rArgs.mnMinCharPos); @@ -406,13 +405,7 @@ bool CommonSalLayout::LayoutText(ImplLayoutArgs& rArgs) #if HB_VERSION_ATLEAST(0, 9, 42) hb_buffer_set_cluster_level(pHbBuffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); #endif - const char *pHbShapers[5] = { "coretext_aat", "graphite2", "ot", "fallback", nullptr }; - hb_segment_properties_t aHbProps; - hb_buffer_get_segment_properties(pHbBuffer, &aHbProps); - hb_shape_plan_t *pHbPlan = hb_shape_plan_create_cached(pHbFace, &aHbProps, nullptr, 0, pHbShapers); - assert(hb_shape_plan_execute(pHbPlan, mpHbFont, pHbBuffer, nullptr, 0)); - hb_buffer_set_content_type(pHbBuffer, HB_BUFFER_CONTENT_TYPE_GLYPHS); - SAL_INFO("vcl.harfbuzz", hb_shape_plan_get_shaper(pHbPlan) << " shaper used for " << rArgs); + hb_shape(mpHbFont, pHbBuffer, nullptr, 0); int nRunGlyphCount = hb_buffer_get_length(pHbBuffer); hb_glyph_info_t *pHbGlyphInfos = hb_buffer_get_glyph_infos(pHbBuffer, nullptr); @@ -445,6 +438,7 @@ bool CommonSalLayout::LayoutText(ImplLayoutArgs& rArgs) nGlyphFlags |= GlyphItem::IS_IN_CLUSTER; bool bDiacritic = false; + hb_face_t* pHbFace = hb_font_get_face(mpHbFont); if (hb_ot_layout_has_glyph_classes(pHbFace)) { // the font has GDEF table commit 0a203470270fd76a45733b694af1f97105a7ca1f Author: Khaled Hosny <khaledho...@eglug.org> Date: Thu Oct 6 04:15:41 2016 +0200 Use HarfBuzz shape plan for a bit more control This way we control exactly what shapers we use in what order, and as an extra we can now tell which shaper HarfBuzz ends up using. Change-Id: Idd303b2a557e16ac86ada0c2006d3e2a052ac489 diff --git a/vcl/source/gdi/CommonSalLayout.cxx b/vcl/source/gdi/CommonSalLayout.cxx index d19506f..03e6818 100644 --- a/vcl/source/gdi/CommonSalLayout.cxx +++ b/vcl/source/gdi/CommonSalLayout.cxx @@ -317,6 +317,7 @@ void CommonSalLayout::DrawText(SalGraphics& rSalGraphics) const bool CommonSalLayout::LayoutText(ImplLayoutArgs& rArgs) { + hb_face_t* pHbFace = hb_font_get_face(mpHbFont); hb_script_t aHbScript = HB_SCRIPT_INVALID; int nGlyphCapacity = 2 * (rArgs.mnEndCharPos - rArgs.mnMinCharPos); @@ -405,7 +406,13 @@ bool CommonSalLayout::LayoutText(ImplLayoutArgs& rArgs) #if HB_VERSION_ATLEAST(0, 9, 42) hb_buffer_set_cluster_level(pHbBuffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); #endif - hb_shape(mpHbFont, pHbBuffer, nullptr, 0); + const char *pHbShapers[5] = { "coretext_aat", "graphite2", "ot", "fallback", nullptr }; + hb_segment_properties_t aHbProps; + hb_buffer_get_segment_properties(pHbBuffer, &aHbProps); + hb_shape_plan_t *pHbPlan = hb_shape_plan_create_cached(pHbFace, &aHbProps, nullptr, 0, pHbShapers); + assert(hb_shape_plan_execute(pHbPlan, mpHbFont, pHbBuffer, nullptr, 0)); + hb_buffer_set_content_type(pHbBuffer, HB_BUFFER_CONTENT_TYPE_GLYPHS); + SAL_INFO("vcl.harfbuzz", hb_shape_plan_get_shaper(pHbPlan) << " shaper used for " << rArgs); int nRunGlyphCount = hb_buffer_get_length(pHbBuffer); hb_glyph_info_t *pHbGlyphInfos = hb_buffer_get_glyph_infos(pHbBuffer, nullptr); @@ -438,7 +445,6 @@ bool CommonSalLayout::LayoutText(ImplLayoutArgs& rArgs) nGlyphFlags |= GlyphItem::IS_IN_CLUSTER; bool bDiacritic = false; - hb_face_t* pHbFace = hb_font_get_face(mpHbFont); if (hb_ot_layout_has_glyph_classes(pHbFace)) { // the font has GDEF table
_______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits