[Libreoffice-commits] core.git: Branch 'libreoffice-5-1' - vcl/inc vcl/win

Tor Lillqvist tml at collabora.com
Fri Jan 22 00:33:28 PST 2016


 vcl/inc/win/salgdi.h             |   12 
 vcl/win/source/gdi/salgdi.cxx    |    5 
 vcl/win/source/gdi/salgdi3.cxx   |   40 +
 vcl/win/source/gdi/winlayout.cxx |  944 +++++++++++++++++++++++++++++++++++++--
 vcl/win/source/gdi/winlayout.hxx |   45 +
 5 files changed, 1020 insertions(+), 26 deletions(-)

New commits:
commit bf417a38c401868bb246e846d4c697cf840771e1
Author: Tor Lillqvist <tml at collabora.com>
Date:   Thu Jan 7 15:05:58 2016 +0200

    tdf#96420: Re-introduce SimpleWinLayout
    
    Fixes lots of problems with glyph layout. For instance the Cambria
    case in tdf#95648. The UniscribeLayout class turned out to be much
    less correct than one might have hoped.
    
    Use OpenGL glyph caching also for SimpleWinLayout to avoid performance
    decrease.
    
    Corresponding commit in master is 4622689fad7ddff72cd08da9611ccfacdb0aa7bd
    
    Change-Id: Ie3c00304ce58f84c7f54051f4c230b32bfc47d9f
    Reviewed-on: https://gerrit.libreoffice.org/21653
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
    Tested-by: Miklos Vajna <vmiklos at collabora.co.uk>

diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h
index 4ff01fa..3d0046e 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -187,6 +187,7 @@ class WinSalGraphics : public SalGraphics
     friend class ScopedFont;
     friend class OpenGLCompatibleDC;
     friend class WinLayout;
+    friend class SimpleWinLayout;
     friend class UniscribeLayout;
 
 protected:
@@ -214,6 +215,9 @@ private:
     RGNDATA*                mpClipRgnData;      // ClipRegion-Data
     RGNDATA*                mpStdClipRgnData;   // Cache Standard-ClipRegion-Data
     ImplFontAttrCache*      mpFontAttrCache;    // Cache font attributes from files in so/share/fonts
+    bool                    mbFontKernInit;     // FALSE: FontKerns must be queried
+    KERNINGPAIR*            mpFontKernPairs;    // Kerning Pairs of the current Font
+    sal_uIntPtr             mnFontKernPairCount;// Number of Kerning Pairs of the current Font
     int                     mnPenWidth;         // Linienbreite
 
 public:
@@ -330,6 +334,12 @@ protected:
                            const SalBitmap* pAlphaBitmap) override;
     virtual bool       drawAlphaRect( long nX, long nY, long nWidth, long nHeight, sal_uInt8 nTransparency ) override;
 
+private:
+    // local helpers
+
+    // get kernign pairs of the current font
+    sal_uLong               GetKernPairs();
+
 public:
     // public SalGraphics methods, the interface to the independent vcl part
 
@@ -451,7 +461,7 @@ void    ImplGetLogFontFromFontSelect( HDC, const FontSelectPattern*,
 #define MAX_64KSALPOINTS    ((((sal_uInt16)0xFFFF)-8)/sizeof(POINTS))
 
 // #102411# Win's GCP mishandles kerning => we need to do it ourselves
-// kerning pairs is sorted by
+// SalGraphicsData::mpFontKernPairs is sorted by
 inline bool ImplCmpKernData( const KERNINGPAIR& a, const KERNINGPAIR& b )
 {
     if( a.wFirst < b.wFirst )
diff --git a/vcl/win/source/gdi/salgdi.cxx b/vcl/win/source/gdi/salgdi.cxx
index 5f29820..42ab90e 100644
--- a/vcl/win/source/gdi/salgdi.cxx
+++ b/vcl/win/source/gdi/salgdi.cxx
@@ -631,6 +631,9 @@ WinSalGraphics::WinSalGraphics(WinSalGraphics::Type eType, bool bScreen, HWND hW
     mhDefPal(0),
     mpStdClipRgnData(NULL),
     mpFontAttrCache(NULL),
+    mpFontKernPairs(NULL),
+    mnFontKernPairCount(0),
+    mbFontKernInit(false),
     mnPenWidth(GSL_PEN_WIDTH)
 {
     if (OpenGLHelper::isVCLOpenGLEnabled() && !mbPrinter)
@@ -660,6 +663,8 @@ WinSalGraphics::~WinSalGraphics()
 
     // delete cache data
     delete [] (BYTE*)mpStdClipRgnData;
+
+    delete [] mpFontKernPairs;
 }
 
 SalGraphicsImpl* WinSalGraphics::GetImpl() const
diff --git a/vcl/win/source/gdi/salgdi3.cxx b/vcl/win/source/gdi/salgdi3.cxx
index 9412799..4de6530 100644
--- a/vcl/win/source/gdi/salgdi3.cxx
+++ b/vcl/win/source/gdi/salgdi3.cxx
@@ -1559,6 +1559,17 @@ sal_uInt16 WinSalGraphics::SetFont( FontSelectPattern* pFont, int nFallbackLevel
     if( mpWinFontData[ nFallbackLevel ] )
         mpWinFontData[ nFallbackLevel ]->UpdateFromHDC( getHDC() );
 
+    if( !nFallbackLevel )
+    {
+        mbFontKernInit = TRUE;
+        if ( mpFontKernPairs )
+        {
+            delete[] mpFontKernPairs;
+            mpFontKernPairs = NULL;
+        }
+        mnFontKernPairCount = 0;
+    }
+
     // some printers have higher internal resolution, so their
     // text output would be different from what we calculated
     // => suggest DrawTextArray to workaround this problem
@@ -1645,6 +1656,35 @@ void WinSalGraphics::GetFontMetric( ImplFontMetricData* pMetric, int nFallbackLe
     pMetric->mnMinKashida = GetMinKashidaWidth();
 }
 
+sal_uLong WinSalGraphics::GetKernPairs()
+{
+    if ( mbFontKernInit )
+    {
+        if( mpFontKernPairs )
+        {
+            delete[] mpFontKernPairs;
+            mpFontKernPairs = NULL;
+        }
+        mnFontKernPairCount = 0;
+
+        KERNINGPAIR* pPairs = NULL;
+        int nCount = ::GetKerningPairsW( getHDC(), 0, NULL );
+        if( nCount )
+        {
+            pPairs = new KERNINGPAIR[ nCount+1 ];
+            mpFontKernPairs = pPairs;
+            mnFontKernPairCount = nCount;
+            ::GetKerningPairsW( getHDC(), nCount, pPairs );
+        }
+
+        mbFontKernInit = FALSE;
+
+        std::sort( mpFontKernPairs, mpFontKernPairs + mnFontKernPairCount, ImplCmpKernData );
+    }
+
+    return mnFontKernPairCount;
+}
+
 const FontCharMapPtr WinSalGraphics::GetFontCharMap() const
 {
     if( !mpWinFontData[0] )
diff --git a/vcl/win/source/gdi/winlayout.cxx b/vcl/win/source/gdi/winlayout.cxx
index ecc7623..a37b198 100644
--- a/vcl/win/source/gdi/winlayout.cxx
+++ b/vcl/win/source/gdi/winlayout.cxx
@@ -47,6 +47,8 @@
 
 #include <unordered_map>
 
+typedef std::unordered_map<int,int> IntMap;
+
 // Graphite headers
 #include <config_graphite.h>
 #if ENABLE_GRAPHITE
@@ -73,6 +75,7 @@ struct OpenGLGlyphCacheChunk
     int mnAscent;
     int mnHeight;
     bool mbVertical;
+    bool mbRealGlyphIndices;
 
     int getExtraSpace() const
     {
@@ -97,6 +100,15 @@ private:
     // TODO: also add HFONT??? Watch out for issues with too many active fonts...
 
 public:
+    bool                    HasKernData() const;
+    void                    SetKernData( int, const KERNINGPAIR* );
+    int                     GetKerning( sal_Unicode, sal_Unicode ) const;
+
+private:
+    KERNINGPAIR*            mpKerningPairs;
+    int                     mnKerningPairs;
+
+public:
     SCRIPT_CACHE&           GetScriptCache() const
                             { return maScriptCache; }
 private:
@@ -104,6 +116,9 @@ private:
     std::vector<OpenGLGlyphCacheChunk> maOpenGLGlyphCache;
 
 public:
+    int                     GetCachedGlyphWidth( int nCharCode ) const;
+    void                    CacheGlyphWidth( int nCharCode, int nCharWidth );
+
     bool                    InitKashidaHandling( HDC );
     int                     GetMinKashidaWidth() const { return mnMinKashidaWidth; }
     int                     GetMinKashidaGlyph() const { return mnMinKashidaGlyph; }
@@ -113,10 +128,11 @@ public:
     GLyphyDemo::demo_font_t*  mpGLyphyFont;
 
     bool                    GlyphIsCached(int nGlyphIndex) const;
-    bool                    AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics);
+    bool                    AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics);
     const OpenGLGlyphCacheChunk&  GetCachedGlyphChunkFor(int nGlyphIndex) const;
 
 private:
+    IntMap                  maWidthMap;
     mutable int             mnMinKashidaWidth;
     mutable int             mnMinKashidaGlyph;
     bool                    mbGLyphySetupCalled;
@@ -192,6 +208,19 @@ inline std::basic_ostream<charT, traits> & operator <<(
     return stream << "}";
 }
 
+inline void ImplWinFontEntry::CacheGlyphWidth( int nCharCode, int nCharWidth )
+{
+    maWidthMap[ nCharCode ] = nCharWidth;
+}
+
+inline int ImplWinFontEntry::GetCachedGlyphWidth( int nCharCode ) const
+{
+    IntMap::const_iterator it = maWidthMap.find( nCharCode );
+    if( it == maWidthMap.end() )
+        return -1;
+    return it->second;
+}
+
 bool ImplWinFontEntry::GlyphIsCached(int nGlyphIndex) const
 {
     if (nGlyphIndex == DROPPED_OUTGLYPH)
@@ -205,7 +234,7 @@ bool ImplWinFontEntry::GlyphIsCached(int nGlyphIndex) const
     return false;
 }
 
-bool ImplWinFontEntry::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics)
+bool ImplWinFontEntry::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics)
 {
     const int DEFAULT_CHUNK_SIZE = 20;
 
@@ -241,6 +270,7 @@ bool ImplWinFontEntry::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayou
     OpenGLGlyphCacheChunk aChunk;
     aChunk.mnFirstGlyph = nGlyphIndex;
     aChunk.mnGlyphCount = nCount;
+    aChunk.mbRealGlyphIndices = bRealGlyphIndices;
 
     std::vector<WORD> aGlyphIndices(nCount);
     for (int i = 0; i < nCount; i++)
@@ -262,21 +292,40 @@ bool ImplWinFontEntry::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayou
 
     SIZE aSize;
 
-    if (!GetTextExtentExPointI(hDC, aGlyphIndices.data(), nCount, 0, NULL, NULL, &aSize))
+    std::vector<ABC> aABC(nCount);
+    if (bRealGlyphIndices)
     {
-        SAL_WARN("vcl.gdi", "GetTextExtentExPointI failed: " << WindowsErrorString(GetLastError()));
-        SelectObject(hDC, hOrigFont);
-        DeleteDC(hDC);
-        return false;
+        if (!GetTextExtentExPointI(hDC, aGlyphIndices.data(), nCount, 0, NULL, NULL, &aSize))
+        {
+            SAL_WARN("vcl.gdi", "GetTextExtentExPointI failed: " << WindowsErrorString(GetLastError()));
+            SelectObject(hDC, hOrigFont);
+            DeleteDC(hDC);
+            return false;
+        }
+        if (!GetCharABCWidthsI(hDC, 0, nCount, aGlyphIndices.data(), aABC.data()))
+        {
+            SAL_WARN("vcl.gdi", "GetCharABCWidthsI failed: " << WindowsErrorString(GetLastError()));
+            SelectObject(hDC, hOrigFont);
+            DeleteDC(hDC);
+            return false;
+        }
     }
-
-    std::vector<ABC> aABC(nCount);
-    if (!GetCharABCWidthsI(hDC, 0, nCount, aGlyphIndices.data(), aABC.data()))
+    else
     {
-        SAL_WARN("vcl.gdi", "GetCharABCWidthsI failed: " << WindowsErrorString(GetLastError()));
-        SelectObject(hDC, hOrigFont);
-        DeleteDC(hDC);
-        return false;
+        if (!GetTextExtentExPointW(hDC, aGlyphIndices.data(), nCount, 0, NULL, NULL, &aSize))
+        {
+            SAL_WARN("vcl.gdi", "GetTextExtentExPoint failed: " << WindowsErrorString(GetLastError()));
+            SelectObject(hDC, hOrigFont);
+            DeleteDC(hDC);
+            return false;
+        }
+        if (!GetCharABCWidthsW(hDC, nGlyphIndex, nGlyphIndex+nCount-1, aABC.data()))
+        {
+            SAL_WARN("vcl.gdi", "GetCharABCWidths failed: " << WindowsErrorString(GetLastError()));
+            SelectObject(hDC, hOrigFont);
+            DeleteDC(hDC);
+            return false;
+        }
     }
 
     std::ostringstream sLine;
@@ -396,7 +445,12 @@ bool ImplWinFontEntry::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayou
     int nX =  nY;
     if (aChunk.mbVertical)
         nX += aDX[0];
-    if (!ExtTextOutW(aDC.getCompatibleHDC(), nX, nY, ETO_GLYPH_INDEX, NULL, aGlyphIndices.data(), nCount, aDX.data()))
+    if (!ExtTextOutW(aDC.getCompatibleHDC(),
+                     nX, nY,
+                     bRealGlyphIndices ? ETO_GLYPH_INDEX : 0,
+                     NULL,
+                     aGlyphIndices.data(), nCount,
+                     aDX.data()))
     {
         SAL_WARN("vcl.gdi", "ExtTextOutW failed: " << WindowsErrorString(GetLastError()));
         SelectFont(aDC.getCompatibleHDC(), hOrigFont);
@@ -443,6 +497,705 @@ bool ImplWinFontEntry::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayou
     return true;
 }
 
+SimpleWinLayout::SimpleWinLayout(HDC hDC, BYTE nCharSet, const ImplWinFontData& rWinFontData,
+        ImplWinFontEntry& rWinFontEntry, bool bUseOpenGL)
+:   WinLayout(hDC, rWinFontData, rWinFontEntry, bUseOpenGL),
+    mnGlyphCount( 0 ),
+    mnCharCount( 0 ),
+    mpOutGlyphs( NULL ),
+    mpGlyphAdvances( NULL ),
+    mpGlyphOrigAdvs( NULL ),
+    mpCharWidths( NULL ),
+    mpChars2Glyphs( NULL ),
+    mpGlyphs2Chars( NULL ),
+    mpGlyphRTLFlags( NULL ),
+    mnWidth( 0 ),
+    mnNotdefWidth( -1 ),
+    mnCharSet( nCharSet )
+{
+}
+
+SimpleWinLayout::~SimpleWinLayout()
+{
+    delete[] mpGlyphRTLFlags;
+    delete[] mpGlyphs2Chars;
+    delete[] mpChars2Glyphs;
+    if( mpCharWidths != mpGlyphAdvances )
+        delete[] mpCharWidths;
+    delete[] mpGlyphOrigAdvs;
+    delete[] mpGlyphAdvances;
+    delete[] mpOutGlyphs;
+}
+
+bool SimpleWinLayout::LayoutText( ImplLayoutArgs& rArgs )
+{
+    // prepare layout
+    // TODO: fix case when recyclying old SimpleWinLayout object
+    mnCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
+
+    // TODO: use a cached value for bDisableAsianKern from upper layers
+    if( rArgs.mnFlags & SalLayoutFlags::KerningAsian )
+    {
+        TEXTMETRICA aTextMetricA;
+        if( GetTextMetricsA( mhDC, &aTextMetricA )
+        && !(aTextMetricA.tmPitchAndFamily & TMPF_FIXED_PITCH) && !(aTextMetricA.tmCharSet == 0x86) )
+            rArgs.mnFlags &= ~SalLayoutFlags::KerningAsian;
+    }
+
+    // layout text
+    int i, j;
+
+    mnGlyphCount = 0;
+    bool bVertical(rArgs.mnFlags & SalLayoutFlags::Vertical);
+
+    // count the number of chars to process if no RTL run
+    rArgs.ResetPos();
+    bool bHasRTL = false;
+    while( rArgs.GetNextRun( &i, &j, &bHasRTL ) && !bHasRTL )
+        mnGlyphCount += j - i;
+
+    // if there are RTL runs we need room to remember individual BiDi flags
+    if( bHasRTL )
+    {
+        mpGlyphRTLFlags = new bool[ mnCharCount ];
+        for( i = 0; i < mnCharCount; ++i )
+            mpGlyphRTLFlags[i] = false;
+    }
+
+    // rewrite the logical string if needed to prepare for the API calls
+    const sal_Unicode* pBidiStr = rArgs.mrStr.pData->buffer + rArgs.mnMinCharPos;
+    if( (mnGlyphCount != mnCharCount) || bVertical )
+    {
+        // we need to rewrite the pBidiStr when any of
+        // - BiDirectional layout
+        // - vertical layout
+        // - partial runs (e.g. with control chars or for glyph fallback)
+        // are involved
+        sal_Unicode* pRewrittenStr = (sal_Unicode*)alloca( mnCharCount * sizeof(sal_Unicode) );
+        pBidiStr = pRewrittenStr;
+
+        // note: glyph to char mapping is relative to first character
+        mpChars2Glyphs = new int[ mnCharCount ];
+        mpGlyphs2Chars = new int[ mnCharCount ];
+        for( i = 0; i < mnCharCount; ++i )
+            mpChars2Glyphs[i] = mpGlyphs2Chars[i] = -1;
+
+        mnGlyphCount = 0;
+        rArgs.ResetPos();
+        bool bIsRTL = false;
+        while( rArgs.GetNextRun( &i, &j, &bIsRTL ) )
+        {
+            do
+            {
+                // get the next leftmost character in this run
+                int nCharPos = bIsRTL ? --j : i++;
+                sal_UCS4 cChar = rArgs.mrStr[ nCharPos ];
+
+                // in the RTL case mirror the character and remember its RTL status
+                if( bIsRTL )
+                {
+                    cChar = GetMirroredChar( cChar );
+                    mpGlyphRTLFlags[ mnGlyphCount ] = true;
+                }
+
+                // rewrite the original string
+                // update the mappings between original and rewritten string
+               // TODO: support surrogates in rewritten strings
+                pRewrittenStr[ mnGlyphCount ] = static_cast<sal_Unicode>(cChar);
+                mpGlyphs2Chars[ mnGlyphCount ] = nCharPos;
+                mpChars2Glyphs[ nCharPos - rArgs.mnMinCharPos ] = mnGlyphCount;
+                ++mnGlyphCount;
+            } while( i < j );
+        }
+    }
+
+    mpOutGlyphs     = new WCHAR[ mnGlyphCount ];
+    mpGlyphAdvances = new int[ mnGlyphCount ];
+
+    if( rArgs.mnFlags & (SalLayoutFlags::KerningPairs | SalLayoutFlags::KerningAsian) )
+        mpGlyphOrigAdvs = new int[ mnGlyphCount ];
+
+    for( i = 0; i < mnGlyphCount; ++i )
+        mpOutGlyphs[i] = pBidiStr[ i ];
+    mnWidth = 0;
+    for( i = 0; i < mnGlyphCount; ++i )
+    {
+        // get the current UCS-4 code point, check for surrogate pairs
+        const WCHAR* pCodes = reinterpret_cast<LPCWSTR>(&pBidiStr[i]);
+        unsigned nCharCode = pCodes[0];
+        bool bSurrogate = ((nCharCode >= 0xD800) && (nCharCode <= 0xDFFF));
+        if( bSurrogate )
+        {
+            // ignore high surrogates, they were already processed with their low surrogates
+            if( nCharCode >= 0xDC00 )
+                continue;
+            // check the second half of the surrogate pair
+            bSurrogate &= (0xDC00 <= pCodes[1]) && (pCodes[1] <= 0xDFFF);
+            // calculate the UTF-32 code of valid surrogate pairs
+            if( bSurrogate )
+                nCharCode = 0x10000 + ((pCodes[0] - 0xD800) << 10) + (pCodes[1] - 0xDC00);
+            else // or fall back to a replacement character
+                nCharCode = '?';
+        }
+
+        // get the advance width for the current UTF-32 code point
+        int nGlyphWidth = mrWinFontEntry.GetCachedGlyphWidth( nCharCode );
+        if( nGlyphWidth == -1 )
+        {
+            ABC aABC;
+            SIZE aExtent;
+            if( GetTextExtentPoint32W( mhDC, &pCodes[0], bSurrogate ? 2 : 1, &aExtent) )
+                nGlyphWidth = aExtent.cx;
+            else if( GetCharABCWidthsW( mhDC, nCharCode, nCharCode, &aABC ) )
+                nGlyphWidth = aABC.abcA + aABC.abcB + aABC.abcC;
+            else if( !GetCharWidth32W( mhDC, nCharCode, nCharCode, &nGlyphWidth )
+                 &&  !GetCharWidthW( mhDC, nCharCode, nCharCode, &nGlyphWidth ) )
+                    nGlyphWidth = 0;
+            mrWinFontEntry.CacheGlyphWidth( nCharCode, nGlyphWidth );
+        }
+        mpGlyphAdvances[ i ] = nGlyphWidth;
+        mnWidth += nGlyphWidth;
+
+        // the second half of surrogate pair gets a zero width
+        if( bSurrogate && ((i+1) < mnGlyphCount) )
+            mpGlyphAdvances[ i+1 ] = 0;
+
+        // check with the font face if glyph fallback is needed
+        if( mrWinFontData.HasChar( nCharCode ) )
+            continue;
+
+        // request glyph fallback at this position in the string
+        bool bRTL = mpGlyphRTLFlags ? mpGlyphRTLFlags[i] : false;
+        int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[i]: i + rArgs.mnMinCharPos;
+        rArgs.NeedFallback( nCharPos, bRTL );
+        if( bSurrogate && ((nCharPos+1) < rArgs.mrStr.getLength()) )
+            rArgs.NeedFallback( nCharPos+1, bRTL );
+
+        // replace the current glyph shape with the NotDef glyph shape
+        if( rArgs.mnFlags & SalLayoutFlags::ForFallback )
+        {
+            // when we already are layouting for glyph fallback
+            // then a new unresolved glyph is not interesting
+            mnNotdefWidth = 0;
+            mpOutGlyphs[i] = DROPPED_OUTGLYPH;
+        }
+        else
+        {
+            if( mnNotdefWidth < 0 )
+            {
+                // get the width of the NotDef glyph
+                SIZE aExtent;
+                WCHAR cNotDef = rArgs.mrStr[ nCharPos ];
+                mnNotdefWidth = 0;
+                if( GetTextExtentPoint32W( mhDC, &cNotDef, 1, &aExtent) )
+                    mnNotdefWidth = aExtent.cx;
+            }
+        }
+        if( bSurrogate && ((i+1) < mnGlyphCount) )
+            mpOutGlyphs[i+1] = DROPPED_OUTGLYPH;
+
+        // adjust the current glyph width to the NotDef glyph width
+        mnWidth += mnNotdefWidth - mpGlyphAdvances[i];
+        mpGlyphAdvances[i] = mnNotdefWidth;
+        if( mpGlyphOrigAdvs )
+            mpGlyphOrigAdvs[i] = mnNotdefWidth;
+    }
+
+    // apply kerning if the layout engine has not yet done it
+    if( rArgs.mnFlags & (SalLayoutFlags::KerningAsian|SalLayoutFlags::KerningPairs) )
+    {
+        for( i = 0; i < mnGlyphCount; ++i )
+            mpGlyphOrigAdvs[i] = mpGlyphAdvances[i];
+
+        // #99658# also apply asian kerning on the substring border
+        int nLen = mnGlyphCount;
+        if( rArgs.mnMinCharPos + nLen < rArgs.mrStr.getLength() )
+            ++nLen;
+        for( i = 1; i < nLen; ++i )
+        {
+            if( rArgs.mnFlags & SalLayoutFlags::KerningPairs )
+            {
+                int nKernAmount = mrWinFontEntry.GetKerning( pBidiStr[i-1], pBidiStr[i] );
+                mpGlyphAdvances[ i-1 ] += nKernAmount;
+                mnWidth += nKernAmount;
+            }
+            else if( rArgs.mnFlags & SalLayoutFlags::KerningAsian )
+
+            if( ( (0x3000 == (0xFF00 & pBidiStr[i-1])) || (0x2010 == (0xFFF0 & pBidiStr[i-1])) || (0xFF00 == (0xFF00 & pBidiStr[i-1])))
+            &&  ( (0x3000 == (0xFF00 & pBidiStr[i])) || (0x2010 == (0xFFF0 & pBidiStr[i])) || (0xFF00 == (0xFF00 & pBidiStr[i])) ) )
+            {
+                long nKernFirst = +CalcAsianKerning( pBidiStr[i-1], true, bVertical );
+                long nKernNext  = -CalcAsianKerning( pBidiStr[i], false, bVertical );
+
+                long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
+                if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
+                {
+                    nDelta = (nDelta * mpGlyphAdvances[i-1] + 2) / 4;
+                    mpGlyphAdvances[i-1] += nDelta;
+                    mnWidth += nDelta;
+                }
+            }
+        }
+    }
+
+    // calculate virtual char widths
+    if( !mpGlyphs2Chars )
+        mpCharWidths = mpGlyphAdvances;
+    else
+    {
+        mpCharWidths = new int[ mnCharCount ];
+        for( i = 0; i < mnCharCount; ++i )
+            mpCharWidths[ i ] = 0;
+        for( i = 0; i < mnGlyphCount; ++i )
+        {
+            int k = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
+            if( k >= 0 )
+                mpCharWidths[ k ] += mpGlyphAdvances[ i ];
+        }
+    }
+
+    // scale layout metrics if needed
+    // TODO: does it make the code more simple if the metric scaling
+    // is moved to the methods that need metric scaling (e.g. FillDXArray())?
+    if( mfFontScale != 1.0 )
+    {
+        mnWidth   = (long)(mnWidth * mfFontScale);
+        mnBaseAdv = (int)(mnBaseAdv * mfFontScale);
+        for( i = 0; i < mnCharCount; ++i )
+            mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale);
+        if( mpGlyphAdvances != mpCharWidths )
+            for( i = 0; i < mnGlyphCount; ++i )
+                mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale);
+        if( mpGlyphOrigAdvs && (mpGlyphOrigAdvs != mpGlyphAdvances) )
+            for( i = 0; i < mnGlyphCount; ++i )
+                mpGlyphOrigAdvs[i] = (int)(mpGlyphOrigAdvs[i] * mfFontScale);
+    }
+
+    return true;
+}
+
+int SimpleWinLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIds, Point& rPos, int& nStart,
+                                    DeviceCoordinate* pGlyphAdvances, int* pCharIndexes,
+                                    const PhysicalFontFace** /*pFallbackFonts*/ ) const
+{
+    // return zero if no more glyph found
+    if( nStart >= mnGlyphCount )
+        return 0;
+
+    // calculate glyph position relative to layout base
+    // TODO: avoid for nStart!=0 case by reusing rPos
+    long nXOffset = mnBaseAdv;
+    for( int i = 0; i < nStart; ++i )
+        nXOffset += mpGlyphAdvances[ i ];
+
+    // calculate absolute position in pixel units
+    Point aRelativePos( nXOffset, 0 );
+    rPos = GetDrawPosition( aRelativePos );
+
+    int nCount = 0;
+    while( nCount < nLen )
+    {
+        // update return values {aGlyphId,nCharPos,nGlyphAdvance}
+        sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ];
+        if( mnLayoutFlags & SalLayoutFlags::Vertical )
+        {
+            const sal_UCS4 cChar = static_cast<sal_UCS4>(aGlyphId & GF_IDXMASK);
+            if( mrWinFontData.HasGSUBstitutions( mhDC )
+            &&  mrWinFontData.IsGSUBstituted( cChar ) )
+                aGlyphId |= GF_GSUB | GF_ROTL;
+            else
+            {
+                aGlyphId |= GetVerticalFlags( cChar );
+                if( (aGlyphId & GF_ROTMASK) == 0 )
+                    aGlyphId |= GF_VERT;
+            }
+        }
+        aGlyphId |= GF_ISCHAR;
+
+        ++nCount;
+        *(pGlyphIds++) = aGlyphId;
+        if( pGlyphAdvances )
+            *(pGlyphAdvances++) = mpGlyphAdvances[ nStart ];
+        if( pCharIndexes )
+        {
+            int nCharPos;
+            if( !mpGlyphs2Chars )
+                nCharPos = nStart + mnMinCharPos;
+            else
+                nCharPos = mpGlyphs2Chars[nStart];
+            *(pCharIndexes++) = nCharPos;
+        }
+
+        // stop at last glyph
+        if( ++nStart >= mnGlyphCount )
+            break;
+
+        // stop when next x-position is unexpected
+        if( !pGlyphAdvances && mpGlyphOrigAdvs )
+            if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] )
+                break;
+    }
+
+    return nCount;
+}
+
+bool SimpleWinLayout::DrawTextImpl(HDC hDC,
+                                   const Rectangle* pRectToErase,
+                                   Point* /* pPos */,
+                                   int* /* pGetNextGlypInfo */) const
+{
+    if (pRectToErase)
+    {
+        RECT aRect = { pRectToErase->Left(), pRectToErase->Top(), pRectToErase->Left()+pRectToErase->GetWidth(), pRectToErase->Top()+pRectToErase->GetHeight() };
+        FillRect(hDC, &aRect, static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));
+    }
+
+    if( mnGlyphCount <= 0 )
+        return false;
+
+    HFONT hOrigFont = DisableFontScaling();
+    Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) );
+
+    // #108267#, break up into glyph portions of a limited size required by Win32 API
+    const unsigned int maxGlyphCount = 8192;
+    UINT numGlyphPortions = mnGlyphCount / maxGlyphCount;
+    UINT remainingGlyphs = mnGlyphCount % maxGlyphCount;
+
+    if( numGlyphPortions )
+    {
+        // #108267#,#109387# break up string into smaller chunks
+        // the output positions will be updated by windows (SetTextAlign)
+        POINT oldPos;
+        UINT oldTa = GetTextAlign(hDC);
+        SetTextAlign(hDC, (oldTa & ~TA_NOUPDATECP) | TA_UPDATECP);
+        MoveToEx(hDC, aPos.X(), aPos.Y(), &oldPos);
+        unsigned int i = 0;
+        for( unsigned int n = 0; n < numGlyphPortions; ++n, i+=maxGlyphCount )
+        {
+            ExtTextOutW(hDC, 0, 0, 0, NULL, mpOutGlyphs+i, maxGlyphCount, mpGlyphAdvances+i);
+        }
+        ExtTextOutW(hDC, 0, 0, 0, NULL, mpOutGlyphs+i, remainingGlyphs, mpGlyphAdvances+i);
+        MoveToEx(hDC, oldPos.x, oldPos.y, (LPPOINT) NULL);
+        SetTextAlign(hDC, oldTa);
+    }
+    else
+        ExtTextOutW(hDC, aPos.X(), aPos.Y(), 0, NULL, mpOutGlyphs, mnGlyphCount, mpGlyphAdvances);
+
+    if( hOrigFont )
+        DeleteFont(SelectFont(hDC, hOrigFont));
+
+    return false;
+}
+
+DeviceCoordinate SimpleWinLayout::FillDXArray( DeviceCoordinate* pDXArray ) const
+{
+    if( !mnWidth )
+    {
+        mnWidth = mnBaseAdv;
+        for( int i = 0; i < mnGlyphCount; ++i )
+            mnWidth += mpGlyphAdvances[ i ];
+    }
+
+    if( pDXArray != NULL )
+    {
+        for( int i = 0; i < mnCharCount; ++i )
+             pDXArray[ i ] = mpCharWidths[ i ];
+    }
+
+    return mnWidth;
+}
+
+sal_Int32 SimpleWinLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor ) const
+// NOTE: the nFactor is used to prevent rounding errors for small nCharExtra values
+{
+    if( mnWidth )
+        if( (mnWidth * nFactor + mnCharCount * nCharExtra) <= nMaxWidth )
+            return -1;
+
+    long nExtraWidth = mnBaseAdv * nFactor;
+    for( int n = 0; n < mnCharCount; ++n )
+    {
+        // skip unused characters
+        if( mpChars2Glyphs && (mpChars2Glyphs[n] < 0) )
+            continue;
+        // add char widths until max
+        nExtraWidth += mpCharWidths[ n ] * nFactor;
+        if( nExtraWidth > nMaxWidth )
+            return (mnMinCharPos + n);
+        nExtraWidth += nCharExtra;
+    }
+
+    return -1;
+}
+
+void SimpleWinLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const
+{
+    long nXPos = mnBaseAdv;
+
+    if( !mpGlyphs2Chars )
+    {
+        for( int i = 0; i < nMaxIdx; i += 2 )
+        {
+            pCaretXArray[ i ] = nXPos;
+            nXPos += mpGlyphAdvances[ i>>1 ];
+            pCaretXArray[ i+1 ] = nXPos;
+        }
+    }
+    else
+    {
+        int  i;
+        for( i = 0; i < nMaxIdx; ++i )
+            pCaretXArray[ i ] = -1;
+
+        // assign glyph positions to character positions
+        for( i = 0; i < mnGlyphCount; ++i )
+        {
+            int nCurrIdx = mpGlyphs2Chars[ i ] - mnMinCharPos;
+            long nXRight = nXPos + mpCharWidths[ nCurrIdx ];
+            nCurrIdx *= 2;
+            if( !(mpGlyphRTLFlags && mpGlyphRTLFlags[i]) )
+            {
+                // normal positions for LTR case
+                pCaretXArray[ nCurrIdx ]   = nXPos;
+                pCaretXArray[ nCurrIdx+1 ] = nXRight;
+            }
+            else
+            {
+                // reverse positions for RTL case
+                pCaretXArray[ nCurrIdx ]   = nXRight;
+                pCaretXArray[ nCurrIdx+1 ] = nXPos;
+            }
+            nXPos += mpGlyphAdvances[ i ];
+        }
+    }
+}
+
+void SimpleWinLayout::Justify( DeviceCoordinate nNewWidth )
+{
+    DeviceCoordinate nOldWidth = mnWidth;
+    mnWidth = nNewWidth;
+
+    if( mnGlyphCount <= 0 )
+        return;
+
+    if( nNewWidth == nOldWidth )
+        return;
+
+    // the rightmost glyph cannot be stretched
+    const int nRight = mnGlyphCount - 1;
+    nOldWidth -= mpGlyphAdvances[ nRight ];
+    nNewWidth -= mpGlyphAdvances[ nRight ];
+
+    // count stretchable glyphs
+    int nStretchable = 0, i;
+    for( i = 0; i < nRight; ++i )
+        if( mpGlyphAdvances[i] >= 0 )
+            ++nStretchable;
+
+    // stretch these glyphs
+    DeviceCoordinate nDiffWidth = nNewWidth - nOldWidth;
+    for( i = 0; (i < nRight) && (nStretchable > 0); ++i )
+    {
+        if( mpGlyphAdvances[i] <= 0 )
+            continue;
+        DeviceCoordinate nDeltaWidth = nDiffWidth / nStretchable;
+        mpGlyphAdvances[i] += nDeltaWidth;
+        --nStretchable;
+        nDiffWidth -= nDeltaWidth;
+    }
+}
+
+void SimpleWinLayout::AdjustLayout( ImplLayoutArgs& rArgs )
+{
+    SalLayout::AdjustLayout( rArgs );
+
+    // adjust positions if requested
+    if( rArgs.mpDXArray )
+        ApplyDXArray( rArgs );
+    else if( rArgs.mnLayoutWidth )
+        Justify( rArgs.mnLayoutWidth );
+    else
+        return;
+
+    // recalculate virtual char widths if they were changed
+    if( mpCharWidths != mpGlyphAdvances )
+    {
+        int i;
+        if( !mpGlyphs2Chars )
+        {
+            // standard LTR case
+            for( i = 0; i < mnGlyphCount; ++i )
+                 mpCharWidths[ i ] = mpGlyphAdvances[ i ];
+        }
+        else
+        {
+            // BiDi or complex case
+            for( i = 0; i < mnCharCount; ++i )
+                mpCharWidths[ i ] = 0;
+            for( i = 0; i < mnGlyphCount; ++i )
+            {
+                int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
+                if( j >= 0 )
+                    mpCharWidths[ j ] += mpGlyphAdvances[ i ];
+            }
+        }
+    }
+}
+
+void SimpleWinLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
+{
+    // try to avoid disturbance of text flow for LSB rounding case;
+    const long* pDXArray = rArgs.mpDXArray;
+
+    int i = 0;
+    long nOldWidth = mnBaseAdv;
+    for(; i < mnCharCount; ++i )
+    {
+        int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
+        if( j >= 0 )
+        {
+            nOldWidth += mpGlyphAdvances[ j ];
+            long nDiff = nOldWidth - pDXArray[ i ];
+
+            // disabled because of #104768#
+            // works great for static text, but problems when typing
+            // if( nDiff>+1 || nDiff<-1 )
+            // only bother with changing anything when something moved
+            if( nDiff != 0 )
+                break;
+        }
+    }
+    if( i >= mnCharCount )
+        return;
+
+    if( !mpGlyphOrigAdvs )
+    {
+        mpGlyphOrigAdvs = new int[ mnGlyphCount ];
+        for( i = 0; i < mnGlyphCount; ++i )
+            mpGlyphOrigAdvs[ i ] = mpGlyphAdvances[ i ];
+    }
+
+    mnWidth = mnBaseAdv;
+    for( i = 0; i < mnCharCount; ++i )
+    {
+        int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
+        if( j >= 0 )
+            mpGlyphAdvances[j] = pDXArray[i] - mnWidth;
+        mnWidth = pDXArray[i];
+    }
+}
+
+void SimpleWinLayout::MoveGlyph( int nStart, long nNewXPos )
+{
+   if( nStart > mnGlyphCount )
+        return;
+
+    // calculate the current x-position of the requested glyph
+    // TODO: cache absolute positions
+    int nXPos = mnBaseAdv;
+    for( int i = 0; i < nStart; ++i )
+        nXPos += mpGlyphAdvances[i];
+
+    // calculate the difference to the current glyph position
+    int nDelta = nNewXPos - nXPos;
+
+    // adjust the width of the layout if it was already cached
+    if( mnWidth )
+        mnWidth += nDelta;
+
+    // depending on whether the requested glyph is leftmost in the layout
+    // adjust either the layout's or the requested glyph's relative position
+    if( nStart > 0 )
+        mpGlyphAdvances[ nStart-1 ] += nDelta;
+    else
+        mnBaseAdv += nDelta;
+}
+
+void SimpleWinLayout::DropGlyph( int nStart )
+{
+    mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH;
+}
+
+void SimpleWinLayout::Simplify( bool /*bIsBase*/ )
+{
+    // return early if no glyph has been dropped
+    int i = mnGlyphCount;
+    while( (--i >= 0) && (mpOutGlyphs[ i ] != DROPPED_OUTGLYPH) );
+    if( i < 0 )
+        return;
+
+    // convert the layout to a sparse layout if it is not already
+    if( !mpGlyphs2Chars )
+    {
+        mpGlyphs2Chars = new int[ mnGlyphCount ];
+        mpCharWidths = new int[ mnCharCount ];
+        // assertion: mnGlyphCount == mnCharCount
+        for( int k = 0; k < mnGlyphCount; ++k )
+        {
+            mpGlyphs2Chars[ k ] = mnMinCharPos + k;
+            mpCharWidths[ k ] = mpGlyphAdvances[ k ];
+        }
+    }
+
+    // remove dropped glyphs that are rightmost in the layout
+    for( i = mnGlyphCount; --i >= 0; )
+    {
+        if( mpOutGlyphs[ i ] != DROPPED_OUTGLYPH )
+            break;
+        if( mnWidth )
+            mnWidth -= mpGlyphAdvances[ i ];
+        int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
+        if( nRelCharPos >= 0 )
+            mpCharWidths[ nRelCharPos ] = 0;
+    }
+    mnGlyphCount = i + 1;
+
+    // keep original glyph widths around
+    if( !mpGlyphOrigAdvs )
+    {
+        mpGlyphOrigAdvs = new int[ mnGlyphCount ];
+        for( int k = 0; k < mnGlyphCount; ++k )
+            mpGlyphOrigAdvs[ k ] = mpGlyphAdvances[ k ];
+    }
+
+    // remove dropped glyphs inside the layout
+    int nNewGC = 0;
+    for( i = 0; i < mnGlyphCount; ++i )
+    {
+        if( mpOutGlyphs[ i ] == DROPPED_OUTGLYPH )
+        {
+            // adjust relative position to last valid glyph
+            int nDroppedWidth = mpGlyphAdvances[ i ];
+            mpGlyphAdvances[ i ] = 0;
+            if( nNewGC > 0 )
+                mpGlyphAdvances[ nNewGC-1 ] += nDroppedWidth;
+            else
+                mnBaseAdv += nDroppedWidth;
+
+            // zero the virtual char width for the char that has a fallback
+            int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
+            if( nRelCharPos >= 0 )
+                mpCharWidths[ nRelCharPos ] = 0;
+        }
+        else
+        {
+            if( nNewGC != i )
+            {
+                // rearrange the glyph array to get rid of the dropped glyph
+                mpOutGlyphs[ nNewGC ]     = mpOutGlyphs[ i ];
+                mpGlyphAdvances[ nNewGC ] = mpGlyphAdvances[ i ];
+                mpGlyphOrigAdvs[ nNewGC ] = mpGlyphOrigAdvs[ i ];
+                mpGlyphs2Chars[ nNewGC ]  = mpGlyphs2Chars[ i ];
+            }
+            ++nNewGC;
+        }
+    }
+
+    mnGlyphCount = nNewGC;
+    if( mnGlyphCount <= 0 )
+        mnWidth = mnBaseAdv = 0;
+}
+
 const OpenGLGlyphCacheChunk& ImplWinFontEntry::GetCachedGlyphChunkFor(int nGlyphIndex) const
 {
     auto i = maOpenGLGlyphCache.cbegin();
@@ -656,6 +1409,74 @@ void WinLayout::DrawText(SalGraphics& rGraphics) const
     }
 }
 
+bool SimpleWinLayout::CacheGlyphs(SalGraphics& rGraphics) const
+{
+    static bool bDoGlyphCaching = (std::getenv("SAL_DISABLE_GLYPH_CACHING") == NULL);
+
+    if (!bDoGlyphCaching)
+        return false;
+
+    for (int i = 0; i < mnGlyphCount; i++)
+    {
+        if (mrWinFontEntry.GlyphIsCached(mpOutGlyphs[i]))
+            continue;
+
+        if (!mrWinFontEntry.AddChunkOfGlyphs(false, mpOutGlyphs[i], *this, rGraphics))
+            return false;
+    }
+
+    return true;
+}
+
+bool SimpleWinLayout::DrawCachedGlyphs(SalGraphics& rGraphics) const
+{
+    WinSalGraphics& rWinGraphics = static_cast<WinSalGraphics&>(rGraphics);
+    HDC hDC = rWinGraphics.getHDC();
+
+    Rectangle aRect;
+    GetBoundRect(rGraphics, aRect);
+
+    COLORREF color = GetTextColor(hDC);
+    SalColor salColor = MAKE_SALCOLOR(GetRValue(color), GetGValue(color), GetBValue(color));
+
+    WinOpenGLSalGraphicsImpl *pImpl = dynamic_cast<WinOpenGLSalGraphicsImpl*>(rWinGraphics.mpImpl.get());
+    if (!pImpl)
+        return false;
+
+    pImpl->PreDraw();
+
+    HFONT hOrigFont = DisableFontScaling();
+    Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) );
+
+    int nAdvance = 0;
+
+    for (int i = 0; i < mnGlyphCount; i++)
+    {
+        if (mpOutGlyphs[i] == DROPPED_OUTGLYPH)
+            continue;
+
+        assert(mrWinFontEntry.GlyphIsCached(mpOutGlyphs[i]));
+
+        const OpenGLGlyphCacheChunk& rChunk = mrWinFontEntry.GetCachedGlyphChunkFor(mpOutGlyphs[i]);
+        const int n = mpOutGlyphs[i] - rChunk.mnFirstGlyph;
+
+        SalTwoRect a2Rects(rChunk.maLocation[n].Left(), rChunk.maLocation[n].Top(),
+                           rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight(),
+                           nAdvance + aPos.X() - rChunk.getExtraOffset(), aPos.Y() - rChunk.mnAscent - rChunk.getExtraOffset(),
+                           rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight()); // ???
+        pImpl->DrawMask(*rChunk.mpTexture, salColor, a2Rects);
+
+        nAdvance += mpGlyphAdvances[i];
+    }
+
+    if( hOrigFont )
+        DeleteFont(SelectFont(hDC, hOrigFont));
+
+    pImpl->PostDraw();
+
+    return true;
+}
+
 struct VisualItem
 {
 public:
@@ -1868,7 +2689,7 @@ bool UniscribeLayout::CacheGlyphs(SalGraphics& rGraphics) const
             if (mrWinFontEntry.GlyphIsCached(mpOutGlyphs[i]))
                 continue;
 
-            if (!mrWinFontEntry.AddChunkOfGlyphs(mpOutGlyphs[i], *this, rGraphics))
+            if (!mrWinFontEntry.AddChunkOfGlyphs(true, mpOutGlyphs[i], *this, rGraphics))
                 return false;
         }
     }
@@ -2875,7 +3696,7 @@ void GraphiteWinLayout::Simplify( bool is_base )
 }
 #endif // ENABLE_GRAPHITE
 
-SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& /*rArgs*/, int nFallbackLevel )
+SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel )
 {
     if (!mpWinFontEntry[nFallbackLevel]) return nullptr;
 
@@ -2890,18 +3711,56 @@ SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& /*rArgs*/, int nFallba
 
     if (!bUspInited)
         InitUSP();
-#if ENABLE_GRAPHITE
-    if (rFontFace.SupportsGraphite())
+
+    if( !(rArgs.mnFlags & SalLayoutFlags::ComplexDisabled) )
     {
-        pWinLayout = new GraphiteWinLayout(getHDC(), rFontFace, rFontInstance, bUseOpenGL);
+#if ENABLE_GRAPHITE
+        if (rFontFace.SupportsGraphite())
+        {
+            pWinLayout = new GraphiteWinLayout(getHDC(), rFontFace, rFontInstance, bUseOpenGL);
+        }
+        else
+#endif // ENABLE_GRAPHITE
+        {
+            // script complexity is determined in upper layers
+            pWinLayout = new UniscribeLayout(getHDC(), rFontFace, rFontInstance, bUseOpenGL);
+            // NOTE: it must be guaranteed that the WinSalGraphics lives longer than
+            // the created UniscribeLayout, otherwise the data passed into the
+            // constructor might become invalid too early
+        }
     }
     else
-#endif // ENABLE_GRAPHITE
     {
-        pWinLayout = new UniscribeLayout(getHDC(), rFontFace, rFontInstance, bUseOpenGL);
-        // NOTE: it must be guaranteed that the WinSalGraphics lives longer than
-        // the created UniscribeLayout, otherwise the data passed into the
-        // constructor might become invalid too early
+#if ENABLE_GRAPHITE
+        if (rFontFace.SupportsGraphite())
+        {
+            pWinLayout = new GraphiteWinLayout(getHDC(), rFontFace, rFontInstance, bUseOpenGL);
+        }
+        else
+#endif // ENABLE_GRAPHITE
+        {
+            static bool bAvoidSimpleWinLayout = (std::getenv("VCL_NO_SIMPLEWINLAYOUT") != NULL);
+
+            if (!bAvoidSimpleWinLayout)
+            {
+                if( (rArgs.mnFlags & SalLayoutFlags::KerningPairs) && !rFontInstance.HasKernData() )
+                {
+                    // TODO: directly cache kerning info in the rFontInstance
+                    // TODO: get rid of kerning methods+data in WinSalGraphics object
+                    GetKernPairs();
+                    rFontInstance.SetKernData( mnFontKernPairCount, mpFontKernPairs );
+                }
+
+                pWinLayout = new SimpleWinLayout(getHDC(), ANSI_CHARSET, rFontFace, rFontInstance, bUseOpenGL);
+            }
+            else
+            {
+                pWinLayout = new UniscribeLayout(getHDC(), rFontFace, rFontInstance, bUseOpenGL);
+                // NOTE: it must be guaranteed that the WinSalGraphics lives longer than
+                // the created UniscribeLayout, otherwise the data passed into the
+                // constructor might become invalid too early
+            }
+        }
     }
 
     if( mfFontScale[nFallbackLevel] != 1.0 )
@@ -2923,6 +3782,9 @@ ImplWinFontEntry::ImplWinFontEntry( FontSelectPattern& rFSD )
 :   ImplFontEntry( rFSD )
 ,    mpGLyphyAtlas( nullptr )
 ,    mpGLyphyFont( nullptr )
+,    mpKerningPairs( NULL )
+,    mnKerningPairs( -1 )
+,    maWidthMap( 512 )
 ,    mnMinKashidaWidth( -1 )
 ,    mnMinKashidaGlyph( -1 )
 {
@@ -2935,6 +3797,38 @@ ImplWinFontEntry::~ImplWinFontEntry()
 {
     if( maScriptCache != NULL )
         ScriptFreeCache( &maScriptCache );
+    delete[] mpKerningPairs;
+}
+
+bool ImplWinFontEntry::HasKernData() const
+{
+    return (mnKerningPairs >= 0);
+}
+
+void ImplWinFontEntry::SetKernData( int nPairCount, const KERNINGPAIR* pPairData )
+{
+    mnKerningPairs = nPairCount;
+    mpKerningPairs = new KERNINGPAIR[ mnKerningPairs ];
+    memcpy( mpKerningPairs, (const void*)pPairData, nPairCount*sizeof(KERNINGPAIR) );
+}
+
+int ImplWinFontEntry::GetKerning( sal_Unicode cLeft, sal_Unicode cRight ) const
+{
+    int nKernAmount = 0;
+    if( mpKerningPairs )
+    {
+        const KERNINGPAIR aRefPair = { cLeft, cRight, 0 };
+        const KERNINGPAIR* pFirstPair = mpKerningPairs;
+        const KERNINGPAIR* pEndPair = mpKerningPairs + mnKerningPairs;
+        const KERNINGPAIR* pPair = std::lower_bound( pFirstPair,
+            pEndPair, aRefPair, ImplCmpKernData );
+        if( (pPair != pEndPair)
+        &&  (pPair->wFirst == aRefPair.wFirst)
+        &&  (pPair->wSecond == aRefPair.wSecond) )
+            nKernAmount = pPair->iKernAmount;
+    }
+
+    return nKernAmount;
 }
 
 bool ImplWinFontEntry::InitKashidaHandling( HDC hDC )
diff --git a/vcl/win/source/gdi/winlayout.hxx b/vcl/win/source/gdi/winlayout.hxx
index ff4e5cc..1d2a776 100644
--- a/vcl/win/source/gdi/winlayout.hxx
+++ b/vcl/win/source/gdi/winlayout.hxx
@@ -68,6 +68,51 @@ public:
     ImplWinFontEntry&   mrWinFontEntry;
 };
 
+class SimpleWinLayout : public WinLayout
+{
+public:
+                    SimpleWinLayout(HDC, BYTE nCharSet, const ImplWinFontData&, ImplWinFontEntry&, bool bUseOpenGL);
+    virtual         ~SimpleWinLayout();
+
+    virtual bool    LayoutText( ImplLayoutArgs& ) override;
+    virtual void    AdjustLayout( ImplLayoutArgs& ) override;
+    virtual bool    DrawTextImpl(HDC hDC, const Rectangle* pRectToErase, Point* pPos, int* pGetNextGlypInfo) const override;
+
+    virtual bool    CacheGlyphs(SalGraphics& rGraphics) const override;
+    virtual bool    DrawCachedGlyphs(SalGraphics& rGraphics) const override;
+    virtual int     GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&,
+                                   DeviceCoordinate* pGlyphAdvances, int* pCharIndexes,
+                                   const PhysicalFontFace** pFallbackFonts = NULL ) const override;
+
+    virtual DeviceCoordinate FillDXArray( DeviceCoordinate* pDXArray ) const override;
+    virtual sal_Int32 GetTextBreak(DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor) const override;
+    virtual void    GetCaretPositions( int nArraySize, long* pCaretXArray ) const override;
+
+    // for glyph+font+script fallback
+    virtual void    MoveGlyph( int nStart, long nNewXPos ) override;
+    virtual void    DropGlyph( int nStart ) override;
+    virtual void    Simplify( bool bIsBase ) override;
+
+protected:
+    void            Justify( DeviceCoordinate nNewWidth );
+    void            ApplyDXArray( const ImplLayoutArgs& );
+
+private:
+    int             mnGlyphCount;
+    int             mnCharCount;
+    WCHAR*          mpOutGlyphs;
+    int*            mpGlyphAdvances;    // if possible this is shared with mpGlyphAdvances[]
+    int*            mpGlyphOrigAdvs;
+    int*            mpCharWidths;       // map rel char pos to char width
+    int*            mpChars2Glyphs;     // map rel char pos to abs glyph pos
+    int*            mpGlyphs2Chars;     // map abs glyph pos to abs char pos
+    bool*           mpGlyphRTLFlags;    // BiDi status for glyphs: true=>RTL
+    mutable long    mnWidth;
+
+    int             mnNotdefWidth;
+    BYTE            mnCharSet;
+};
+
 class UniscribeLayout : public WinLayout
 {
 public:


More information about the Libreoffice-commits mailing list