[Libreoffice-commits] core.git: Branch 'feature/commonsallayout' - 7 commits - vcl/inc vcl/quartz vcl/source vcl/unx vcl/win

Khaled Hosny khaledhosny at eglug.org
Sat Oct 15 23:50:35 UTC 2016


Rebased ref, commits from common ancestor:
commit bf7119d9217ba08c716af4f080623d2d167b989a
Author: Khaled Hosny <khaledhosny at 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 <khaledhosny at 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 <khaledhosny at 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 <khaledhosny at 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 <khaledhosny at 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 <khaledhosny at 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 <khaledhosny at 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;
+            }
+        }
+    }
 }


More information about the Libreoffice-commits mailing list