Rebased ref, commits from common ancestor: commit bf7119d9217ba08c716af4f080623d2d167b989a Author: Khaled Hosny <khaledho...@eglug.org> Date: Sat Oct 15 06:11:26 2016 -0700
Rewrite AquaSalGraphics::DrawSalLayout() Slightly cleaner code and now handles glyph rotation for vertical text. Change-Id: I98cc8fd7df5e73068294e4d7dd6b38a71dcbdcc7 diff --git a/vcl/quartz/salgdi.cxx b/vcl/quartz/salgdi.cxx index 787ddbf..28e7e99 100644 --- a/vcl/quartz/salgdi.cxx +++ b/vcl/quartz/salgdi.cxx @@ -485,37 +485,78 @@ bool AquaSalGraphics::GetGlyphBoundRect( sal_GlyphId aGlyphId, Rectangle& rRect void AquaSalGraphics::DrawSalLayout(const CommonSalLayout& rLayout) { - CGContextRef context = mrContext; - SAL_INFO("vcl.ct", "CGContextSaveGState(" << context << ")"); - CGContextSaveGState(context); - SAL_INFO("vcl.ct", "CGContextScaleCTM(" << context << ",1.0,-1.0)"); - const CoreTextStyle& rCTStyle = rLayout.getFontData(); - - CTFontRef pFont = static_cast<CTFontRef>(CFDictionaryGetValue(rCTStyle.GetStyleDict(), kCTFontAttributeName)); - CGContextScaleCTM(context, 1.0, -1.0); - CGContextSetShouldAntialias(context, !mbNonAntialiasedText); - // rotate the matrix - const CGFloat fRadians = rCTStyle.mfFontRotation; - CGContextRotateCTM(context, +fRadians); - const CGAffineTransform aInvMatrix = CGAffineTransformMakeRotation(-fRadians); - CGContextSetFillColor(context, maTextColor.AsArray()); - - // draw the text + const CoreTextStyle& rStyle = rLayout.getFontData(); + const FontSelectPattern& rFontSelect = rStyle.maFontSelData; + if (rFontSelect.mnHeight == 0) + return; + + CTFontRef pFont = static_cast<CTFontRef>(CFDictionaryGetValue(rStyle.GetStyleDict(), kCTFontAttributeName)); + Point aPos; sal_GlyphId aGlyphId; std::vector<CGGlyph> aGlyphIds; std::vector<CGPoint> aGlyphPos; + std::vector<bool> aGlyphRotation; int nStart = 0; - for (; rLayout.GetNextGlyphs(1, &aGlyphId, aPos, nStart); ) + while (rLayout.GetNextGlyphs(1, &aGlyphId, aPos, nStart)) { + CGAffineTransform aMatrix = CGAffineTransformMakeRotation(-rStyle.mfFontRotation); + bool nGlyphRotation = false; + if ((aGlyphId & GF_ROTMASK) == GF_ROTL) + { + nGlyphRotation = true; + double nYdiff = CTFontGetAscent(pFont) - CTFontGetDescent(pFont); + aMatrix = CGAffineTransformTranslate(aMatrix, 0, -nYdiff); + } + aGlyphIds.push_back(aGlyphId & GF_IDXMASK); - aGlyphPos.push_back(CGPointApplyAffineTransform(CGPointMake(aPos.X(), -1*aPos.Y()), aInvMatrix)); + aGlyphPos.push_back(CGPointApplyAffineTransform(CGPointMake(aPos.X(), -aPos.Y()), aMatrix)); + aGlyphRotation.push_back(nGlyphRotation); + } + + if (aGlyphIds.empty()) + return; + + CGContextSaveGState(mrContext); + + CTFontRef pRotatedFont = nullptr; + if (rStyle.mfFontRotation) + { + CTFontDescriptorRef pDesc = CTFontCopyFontDescriptor(pFont); + CGFloat nSize = CTFontGetSize(pFont); + CGAffineTransform aMatrix = CTFontGetMatrix(pFont); + aMatrix = CGAffineTransformRotate(aMatrix, -rStyle.mfFontRotation); + pRotatedFont = CTFontCreateWithFontDescriptor(pDesc, nSize, &aMatrix); + CFRelease(pDesc); + } + + CGContextScaleCTM(mrContext, 1.0, -1.0); + CGContextRotateCTM(mrContext, rStyle.mfFontRotation); + CGContextSetShouldAntialias(mrContext, !mbNonAntialiasedText); + CGContextSetFillColor(mrContext, maTextColor.AsArray()); + + std::vector<bool>::const_iterator aStart = aGlyphRotation.begin(); + std::vector<bool>::const_iterator aEnd = aGlyphRotation.end(); + std::vector<bool>::const_iterator aI = aStart; + while (aI != aEnd) + { + bool nGlyphRotation = *aI; + std::vector<bool>::const_iterator aNext = std::find(aI + 1, aEnd, !nGlyphRotation); + + size_t nStartIndex = std::distance(aStart, aI); + size_t nLen = std::distance(aI, aNext); + + if (nGlyphRotation && pRotatedFont) + CTFontDrawGlyphs(pRotatedFont, &aGlyphIds[nStartIndex], &aGlyphPos[nStartIndex], nLen, mrContext); + else + CTFontDrawGlyphs(pFont, &aGlyphIds[nStartIndex], &aGlyphPos[nStartIndex], nLen, mrContext); + + aI = aNext; } - CTFontDrawGlyphs(pFont, aGlyphIds.data(), aGlyphPos.data(), nStart, context); - // restore the original graphic context transformations - SAL_INFO("vcl.ct", "CGContextRestoreGState(" << context << ")"); - CGContextRestoreGState(context); + if (pRotatedFont) + CFRelease(pRotatedFont); + CGContextRestoreGState(mrContext); } void AquaSalGraphics::SetFont(FontSelectPattern* pReqFont, int nFallbackLevel) commit 8e0d41fcae628582a01f50d0510d2e0daf3077fc Author: Khaled Hosny <khaledho...@eglug.org> Date: Mon Sep 26 19:09:52 2016 +0200 Support vertical text in CommonSalLayout Change-Id: I52a71c9c21ad75c7cb9c8574e5e7e3b7c1c0c0c3 diff --git a/vcl/source/gdi/CommonSalLayout.cxx b/vcl/source/gdi/CommonSalLayout.cxx index 7aeb251..f61488c 100644 --- a/vcl/source/gdi/CommonSalLayout.cxx +++ b/vcl/source/gdi/CommonSalLayout.cxx @@ -212,12 +212,12 @@ struct HbScriptRun { int32_t mnMin; int32_t mnEnd; - hb_script_t maScript; + UScriptCode maScript; HbScriptRun(int32_t nMin, int32_t nEnd, UScriptCode aScript) : mnMin(nMin) , mnEnd(nEnd) - , maScript(hb_icu_script_to_script(aScript)) + , maScript(aScript) {} }; @@ -307,6 +307,47 @@ void CommonSalLayout::DrawText(SalGraphics& rSalGraphics) const rSalGraphics.DrawSalLayout( *this ); } +/* https://drafts.csswg.org/css-writing-modes-3/#script-orientations */ +static int GetVerticalFlagsForScript(UScriptCode aScript) +{ + int nFlag = GF_NONE; + + switch (aScript) + { + /* ttb 0° */ + case USCRIPT_BOPOMOFO: + case USCRIPT_EGYPTIAN_HIEROGLYPHS: + case USCRIPT_HAN: + case USCRIPT_HANGUL: + case USCRIPT_HIRAGANA: + case USCRIPT_KATAKANA: + case USCRIPT_MEROITIC_CURSIVE: + case USCRIPT_MEROITIC_HIEROGLYPHS: + case USCRIPT_YI: + nFlag = GF_ROTL; + break; +#if 0 + /* ttb 90° */ + case USCRIPT_MONGOLIAN: + case USCRIPT_PHAGS_PA: + nFlag = ??; + break; + /* ttb -90° */ + case USCRIPT_ORKHON: + nFlag = ??; + break; + /* btt -90° */ + case USCRIPT_MONGOLIAN: + nFlag = ??; + break; +#endif + default: + break; + } + + return nFlag; +} + bool CommonSalLayout::LayoutText(ImplLayoutArgs& rArgs) { hb_script_t aHbScript = HB_SCRIPT_INVALID; @@ -371,11 +412,19 @@ bool CommonSalLayout::LayoutText(ImplLayoutArgs& rArgs) int nMinRunPos = aScriptRun.mnMin; int nEndRunPos = aScriptRun.mnEnd; int nRunLen = nEndRunPos - nMinRunPos; - aHbScript = aScriptRun.maScript; + aHbScript = hb_icu_script_to_script(aScriptRun.maScript); + // hb_language_from_string() accept ISO639-3 language tag except for Chinese. LanguageTag &rTag = rArgs.maLanguageTag; OString sLanguage = OUStringToOString(rTag.getBcp47(), RTL_TEXTENCODING_ASCII_US); + bool bVertical = false; + if ((rArgs.mnFlags & SalLayoutFlags::Vertical) && + GetVerticalFlagsForScript(aScriptRun.maScript) == GF_ROTL) + { + bVertical = true; + } + int nHbFlags = HB_BUFFER_FLAGS_DEFAULT; if (nMinRunPos == 0) nHbFlags |= HB_BUFFER_FLAG_BOT; /* Beginning-of-text */ @@ -387,7 +436,10 @@ bool CommonSalLayout::LayoutText(ImplLayoutArgs& rArgs) 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); + if (bVertical) + hb_buffer_set_direction(pHbBuffer, HB_DIRECTION_TTB); + else + hb_buffer_set_direction(pHbBuffer, bRightToLeft ? HB_DIRECTION_RTL: HB_DIRECTION_LTR); hb_buffer_set_script(pHbBuffer, aHbScript); hb_buffer_set_language(pHbBuffer, hb_language_from_string(sLanguage.getStr(), -1)); hb_buffer_set_flags(pHbBuffer, (hb_buffer_flags_t) nHbFlags); @@ -452,17 +504,35 @@ bool CommonSalLayout::LayoutText(ImplLayoutArgs& rArgs) if (bDiacritic) nGlyphFlags |= GlyphItem::IS_DIACRITIC; - int32_t nXOffset = pHbPositions[i].x_offset >> 6; - int32_t nYOffset = pHbPositions[i].y_offset >> 6; - int32_t nXAdvance = pHbPositions[i].x_advance >> 6; - int32_t nYAdvance = pHbPositions[i].y_advance >> 6; + int32_t nAdvance, nXOffset, nYOffset; + if (bVertical) + { + int nVertFlag; +#if 0 /* XXX: does not work as expected for Common script */ + UErrorCode error = U_ZERO_ERROR; + nVertFlag = GetVerticalFlagsForScript(uscript_getScript(aChar, &error)); +#else + nVertFlag = GetVerticalFlags(aChar); + if (nVertFlag == GF_ROTR) + nVertFlag = GF_ROTL; +#endif + nGlyphIndex |= nVertFlag; + nAdvance = -pHbPositions[i].y_advance >> 6; + nXOffset = pHbPositions[i].y_offset >> 6; + nYOffset = -pHbPositions[i].x_offset >> 6; + } + else + { + nAdvance = pHbPositions[i].x_advance >> 6; + nXOffset = pHbPositions[i].x_offset >> 6; + nYOffset = pHbPositions[i].y_offset >> 6; + } Point aNewPos = Point(aCurrPos.X() + nXOffset, -(aCurrPos.Y() + nYOffset)); - const GlyphItem aGI(nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nXAdvance, nXOffset); + const GlyphItem aGI(nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nAdvance, nXOffset); AppendGlyph(aGI); - aCurrPos.X() += nXAdvance; - aCurrPos.Y() += nYAdvance; + aCurrPos.X() += nAdvance; } hb_buffer_destroy(pHbBuffer); diff --git a/vcl/unx/generic/gdi/cairotextrender.cxx b/vcl/unx/generic/gdi/cairotextrender.cxx index 35d086d..9fe2ea5 100644 --- a/vcl/unx/generic/gdi/cairotextrender.cxx +++ b/vcl/unx/generic/gdi/cairotextrender.cxx @@ -323,6 +323,11 @@ void CairoTextRender::DrawServerFontLayout( const GenericSalLayout& rLayout, con { ydiff = font_extents.ascent/nHeight; xdiff = -font_extents.descent/nHeight; + if (SalLayout::UseCommonLayout()) + { + ydiff -= font_extents.descent/nHeight; + xdiff = 0; + } } else if (nGlyphRotation == -1) { commit 77949c07c23450834f49d3ce764432b89d04d90a 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 f9e3e0c..787ddbf 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.Is() || !xFCMap->GetCharCount(), "vcl", "no charmap" ); // get unicode<->glyph encoding commit 844a65ad555cd5e8fc614d364b6c240452385ae1 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 8aaefbf..b865ff9 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 b036307..9b78a72 100644 --- a/vcl/win/gdi/winlayout.cxx +++ b/vcl/win/gdi/winlayout.cxx @@ -3826,7 +3826,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 1c23da6cf5899041fab8a75665619f7987785243 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 d517728..7aeb251 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 cbe0f5181a223499344232126c3c9f75c3dc7c35 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 290ac4f..d517728 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 99e1a150540a91f6e12fd1105ced5c8f221bccab 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..290ac4f 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 = 0; + 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() && nKashidaIndex) + { + 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; + } + } + } }
_______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits