summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--vcl/inc/win/salgdi.h12
-rw-r--r--vcl/win/gdi/salfont.cxx40
-rw-r--r--vcl/win/gdi/salgdi.cxx5
-rw-r--r--vcl/win/gdi/winlayout.cxx944
-rw-r--r--vcl/win/gdi/winlayout.hxx45
5 files changed, 1020 insertions, 26 deletions
diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h
index d90e429051bb..a889b45dca27 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -186,6 +186,7 @@ class WinSalGraphics : public SalGraphics
friend class ScopedFont;
friend class OpenGLCompatibleDC;
friend class WinLayout;
+ friend class SimpleWinLayout;
friend class UniscribeLayout;
protected:
@@ -213,6 +214,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:
@@ -329,6 +333,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
@@ -448,7 +458,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/gdi/salfont.cxx b/vcl/win/gdi/salfont.cxx
index 1bd142366710..e7dd594793f2 100644
--- a/vcl/win/gdi/salfont.cxx
+++ b/vcl/win/gdi/salfont.cxx
@@ -1470,6 +1470,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
@@ -1556,6 +1567,35 @@ void WinSalGraphics::GetFontMetric( ImplFontMetricDataPtr& rxFontMetric, int nFa
rxFontMetric->SetMinKashida( 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/gdi/salgdi.cxx b/vcl/win/gdi/salgdi.cxx
index 36ff3228305f..6783cc1f9c22 100644
--- a/vcl/win/gdi/salgdi.cxx
+++ b/vcl/win/gdi/salgdi.cxx
@@ -607,6 +607,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)
@@ -636,6 +639,8 @@ WinSalGraphics::~WinSalGraphics()
// delete cache data
delete [] (BYTE*)mpStdClipRgnData;
+
+ delete [] mpFontKernPairs;
}
SalGraphicsImpl* WinSalGraphics::GetImpl() const
diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx
index c41a2b88cdd6..c03b203281b9 100644
--- a/vcl/win/gdi/winlayout.cxx
+++ b/vcl/win/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:
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 WinFontInstance::CacheGlyphWidth( int nCharCode, int nCharWidth )
+{
+ maWidthMap[ nCharCode ] = nCharWidth;
+}
+
+inline int WinFontInstance::GetCachedGlyphWidth( int nCharCode ) const
+{
+ IntMap::const_iterator it = maWidthMap.find( nCharCode );
+ if( it == maWidthMap.end() )
+ return -1;
+ return it->second;
+}
+
bool WinFontInstance::GlyphIsCached(int nGlyphIndex) const
{
if (nGlyphIndex == DROPPED_OUTGLYPH)
@@ -205,7 +234,7 @@ bool WinFontInstance::GlyphIsCached(int nGlyphIndex) const
return false;
}
-bool WinFontInstance::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics)
+bool WinFontInstance::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics)
{
const int DEFAULT_CHUNK_SIZE = 20;
@@ -241,6 +270,7 @@ bool WinFontInstance::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayout
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 WinFontInstance::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayout
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 WinFontInstance::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayout
int nX = nY;
if (aChunk.mbVertical)
nX += aDX[0];
- if (!ExtTextOutW(aDC.getCompatibleHDC(), nX, nY, ETO_GLYPH_INDEX, NULL, reinterpret_cast<wchar_t *>(aGlyphIndices.data()), nCount, aDX.data()))
+ if (!ExtTextOutW(aDC.getCompatibleHDC(),
+ nX, nY,
+ bRealGlyphIndices ? ETO_GLYPH_INDEX : 0,
+ NULL,
+ reinterpret_cast<wchar_t *>(aGlyphIndices.data()), nCount,
+ aDX.data()))
{
SAL_WARN("vcl.gdi", "ExtTextOutW failed: " << WindowsErrorString(GetLastError()));
SelectFont(aDC.getCompatibleHDC(), hOrigFont);
@@ -443,6 +497,705 @@ bool WinFontInstance::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayout
return true;
}
+SimpleWinLayout::SimpleWinLayout(HDC hDC, BYTE nCharSet, const WinFontFace& rWinFontData,
+ WinFontInstance& 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& WinFontInstance::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;
}
}
@@ -2906,7 +3727,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;
@@ -2921,18 +3742,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 )
@@ -2954,6 +3813,9 @@ WinFontInstance::WinFontInstance( FontSelectPattern& rFSD )
: LogicalFontInstance( rFSD )
, mpGLyphyAtlas( nullptr )
, mpGLyphyFont( nullptr )
+, mpKerningPairs( NULL )
+, mnKerningPairs( -1 )
+, maWidthMap( 512 )
, mnMinKashidaWidth( -1 )
, mnMinKashidaGlyph( -1 )
{
@@ -2966,6 +3828,38 @@ WinFontInstance::~WinFontInstance()
{
if( maScriptCache != NULL )
ScriptFreeCache( &maScriptCache );
+ delete[] mpKerningPairs;
+}
+
+bool WinFontInstance::HasKernData() const
+{
+ return (mnKerningPairs >= 0);
+}
+
+void WinFontInstance::SetKernData( int nPairCount, const KERNINGPAIR* pPairData )
+{
+ mnKerningPairs = nPairCount;
+ mpKerningPairs = new KERNINGPAIR[ mnKerningPairs ];
+ memcpy( mpKerningPairs, (const void*)pPairData, nPairCount*sizeof(KERNINGPAIR) );
+}
+
+int WinFontInstance::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 WinFontInstance::InitKashidaHandling( HDC hDC )
diff --git a/vcl/win/gdi/winlayout.hxx b/vcl/win/gdi/winlayout.hxx
index 7a52fe33cc92..0df8397930a6 100644
--- a/vcl/win/gdi/winlayout.hxx
+++ b/vcl/win/gdi/winlayout.hxx
@@ -68,6 +68,51 @@ public:
WinFontInstance& mrWinFontEntry;
};
+class SimpleWinLayout : public WinLayout
+{
+public:
+ SimpleWinLayout(HDC, BYTE nCharSet, const WinFontFace&, WinFontInstance&, 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: