[Libreoffice-commits] core.git: vcl/inc vcl/win
Khaled Hosny
khaledhosny at eglug.org
Thu Dec 1 08:50:27 UTC 2016
vcl/inc/win/salgdi.h | 39
vcl/inc/win/winlayout.hxx | 161 --
vcl/win/gdi/salfont.cxx | 129 -
vcl/win/gdi/salgdi.cxx | 5
vcl/win/gdi/winlayout.cxx | 3173 +---------------------------------------------
5 files changed, 132 insertions(+), 3375 deletions(-)
New commits:
commit 6c436ba09cb35235ce6f4065cf74c9a6ff14a4bd
Author: Khaled Hosny <khaledhosny at eglug.org>
Date: Thu Dec 1 03:33:30 2016 +0200
Kill old Windows layout engines
Change-Id: I33f8322a6371150698bf926165fb6dddb9d4092c
Reviewed-on: https://gerrit.libreoffice.org/31452
Tested-by: Jenkins <ci at libreoffice.org>
Reviewed-by: Khaled Hosny <khaledhosny at eglug.org>
diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h
index 8471699..d3a7185 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -74,27 +74,16 @@ public:
BYTE GetCharSet() const { return meWinCharSet; }
BYTE GetPitchAndFamily() const { return mnPitchAndFamily; }
- bool SupportsCJK() const { return mbHasCJKSupport; }
- bool SupportsArabic() const { return mbHasArabicSupport; }
FontCharMapRef GetFontCharMap() const;
bool GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const;
- const Ucs2SIntMap* GetEncodingVector() const { return mpEncodingVector; }
- void SetEncodingVector( const Ucs2SIntMap* pNewVec ) const
- {
- if( mpEncodingVector )
- delete mpEncodingVector;
- mpEncodingVector = pNewVec;
- }
+
private:
sal_IntPtr mnId;
// some members that are initalized lazily when the font gets selected into a HDC
- mutable bool mbHasCJKSupport;
- mutable bool mbHasArabicSupport;
mutable bool mbFontCapabilitiesRead;
mutable FontCharMapRef mxUnicodeMap;
- mutable const Ucs2SIntMap* mpEncodingVector;
mutable vcl::FontCapabilities maFontCapabilities;
BYTE meWinCharSet;
@@ -105,14 +94,8 @@ private:
void ReadCmapTable( HDC ) const;
void GetFontCapabilities( HDC hDC ) const;
- void ReadGsubTable( HDC ) const;
-
- mutable std::unordered_set<sal_UCS4> maGsubTable;
- mutable bool mbGsubRead;
mutable hb_font_t* mpHbFont;
public:
- bool HasGSUBstitutions( HDC ) const;
- bool IsGSUBstituted( sal_UCS4 ) const;
hb_font_t* GetHbFont() const { return mpHbFont; }
void SetHbFont( hb_font_t* pHbFont ) const { mpHbFont = pHbFont; }
};
@@ -168,9 +151,6 @@ class WinSalGraphics : public SalGraphics
friend class WinOpenGLSalGraphicsImpl;
friend class ScopedFont;
friend class OpenGLCompatibleDC;
- friend class WinLayout;
- friend class SimpleWinLayout;
- friend class UniscribeLayout;
protected:
std::unique_ptr<SalGraphicsImpl> mpImpl;
@@ -196,9 +176,6 @@ private:
COLORREF mnTextColor; // TextColor
RGNDATA* mpClipRgnData; // ClipRegion-Data
RGNDATA* mpStdClipRgnData; // Cache Standard-ClipRegion-Data
- 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
LogicalFontInstance* GetWinFontEntry(int nFallbackLevel);
@@ -321,9 +298,6 @@ protected:
private:
// local helpers
- // get kernign pairs of the current font
- sal_uLong GetKernPairs();
-
static void DrawTextLayout(const CommonSalLayout&, HDC, bool bUseDWrite);
public:
@@ -427,17 +401,6 @@ 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
-// SalGraphicsData::mpFontKernPairs is sorted by
-inline bool ImplCmpKernData( const KERNINGPAIR& a, const KERNINGPAIR& b )
-{
- if( a.wFirst < b.wFirst )
- return true;
- if( a.wFirst > b.wFirst )
- return false;
- return (a.wSecond < b.wSecond);
-}
-
// called extremely often from just one spot => inline
inline bool WinFontFace::HasChar( sal_uInt32 cChar ) const
{
diff --git a/vcl/inc/win/winlayout.hxx b/vcl/inc/win/winlayout.hxx
index ff57506..d339df3 100644
--- a/vcl/inc/win/winlayout.hxx
+++ b/vcl/inc/win/winlayout.hxx
@@ -35,7 +35,6 @@
typedef std::unordered_map<int,int> IntMap;
class WinFontInstance;
-struct VisualItem;
namespace
{
@@ -53,7 +52,6 @@ struct OpenGLGlyphDrawElement
int mnBaselineOffset;
int mnHeight;
bool mbVertical;
- bool mbRealGlyphIndices;
int getExtraSpace() const
{
@@ -160,24 +158,12 @@ 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:
mutable SCRIPT_CACHE maScriptCache;
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; }
@@ -185,7 +171,7 @@ public:
private:
GlyphCache maGlyphCache;
public:
- bool CacheGlyphToAtlas(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics);
+ bool CacheGlyphToAtlas(HDC hDC, HFONT hFont, int nGlyphIndex, SalGraphics& rGraphics);
GlyphCache& GetGlyphCache()
{
@@ -193,155 +179,10 @@ public:
}
private:
- std::unordered_map<int, int> maWidthMap;
mutable int mnMinKashidaWidth;
mutable int mnMinKashidaGlyph;
};
-class WinLayout : public SalLayout
-{
-public:
- WinLayout(HDC, const WinFontFace&, WinFontInstance&, bool bUseOpenGL);
- virtual ~WinLayout() override;
- virtual void InitFont() const override;
- void SetFontScale( float f ) { mfFontScale = f; }
- HFONT DisableFontScaling() const;
-
- SCRIPT_CACHE& GetScriptCache() const;
-
- /// In the non-OpenGL case, call the DrawTextImpl directly, otherwise make
- /// sure we draw to an interim texture.
- virtual void DrawText(SalGraphics&) const override;
-
- /// Draw to the provided HDC.
- virtual bool DrawTextImpl(HDC hDC, const Rectangle* pRectToErase, Point* pPos, int* pGetNextGlypInfo) const = 0;
-
- virtual bool CacheGlyphs(SalGraphics& rGraphics) const = 0;
- virtual bool DrawCachedGlyphs(SalGraphics& rGraphics) const = 0;
-
- HDC mhDC; // WIN32 device handle
- HFONT mhFont; // WIN32 font handle
- int mnBaseAdv; // x-offset relative to Layout origin
- float mfFontScale; // allows metrics emulation of huge font sizes
- bool mbUseOpenGL; ///< We need to render via OpenGL
-
- const WinFontFace& mrWinFontData;
- WinFontInstance& mrWinFontEntry;
-};
-
-class SimpleWinLayout : public WinLayout
-{
-public:
- SimpleWinLayout(HDC, const WinFontFace&, WinFontInstance&, bool bUseOpenGL);
- virtual ~SimpleWinLayout() override;
-
- 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 = nullptr, int* pCharIndexes = nullptr,
- const PhysicalFontFace** pFallbackFonts = nullptr ) 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;
-};
-
-class UniscribeLayout : public WinLayout
-{
-public:
- UniscribeLayout(HDC, const WinFontFace&, WinFontInstance&, bool bUseOpenGL);
-
- 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 = nullptr, int* pCharPosAry = nullptr,
- const PhysicalFontFace** pFallbackFonts = nullptr ) 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;
- virtual bool IsKashidaPosValid ( int nCharPos ) 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;
- virtual void DisableGlyphInjection( bool bDisable ) override { mbDisableGlyphInjection = bDisable; }
-
-protected:
- virtual ~UniscribeLayout() override;
-
- void Justify( DeviceCoordinate nNewWidth );
- void ApplyDXArray( const ImplLayoutArgs& );
-
- bool GetItemSubrange( const VisualItem&,
- int& rMinIndex, int& rEndIndex ) const;
-
-private:
- // item specific info
- SCRIPT_ITEM* mpScriptItems; // in logical order
- VisualItem* mpVisualItems; // in visual order
- int mnItemCount; // number of visual items
-
- // string specific info
- // everything is in logical order
- OUString msTheString; // Sadly we need it in GetNextGlyphs(), to be able to call GetVerticalFlags()
- int mnCharCapacity;
- WORD* mpLogClusters; // map from absolute_char_pos to relative_glyph_pos
- int* mpCharWidths; // map from absolute_char_pos to char_width
- int mnSubStringMin; // char_pos of first char in context
-
- // glyph specific info
- // everything is in visual order
- int mnGlyphCount;
- int mnGlyphCapacity;
- int* mpGlyphAdvances; // glyph advance width before justification
- int* mpJustifications; // glyph advance width after justification
- WORD* mpOutGlyphs; // glyphids in visual order
- GOFFSET* mpGlyphOffsets; // glyph offsets to the "naive" layout
- SCRIPT_VISATTR* mpVisualAttrs; // glyph visual attributes
- mutable int* mpGlyphs2Chars; // map from absolute_glyph_pos to absolute_char_pos
-
- // kashida stuff
- void InitKashidaHandling();
- void KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos );
- bool KashidaWordFix( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos );
-
- int mnMinKashidaWidth;
- int mnMinKashidaGlyph;
- bool mbDisableGlyphInjection;
-};
-
class TextOutRenderer
{
protected:
diff --git a/vcl/win/gdi/salfont.cxx b/vcl/win/gdi/salfont.cxx
index 5d1e6bc..7147be1 100644
--- a/vcl/win/gdi/salfont.cxx
+++ b/vcl/win/gdi/salfont.cxx
@@ -613,16 +613,12 @@ WinFontFace::WinFontFace( const FontAttributes& rDFS,
int nHeight, BYTE eWinCharSet, BYTE nPitchAndFamily )
: PhysicalFontFace( rDFS ),
mnId( 0 ),
- mbHasCJKSupport( false ),
- mbHasArabicSupport ( false ),
mbFontCapabilitiesRead( false ),
mxUnicodeMap( nullptr ),
- mpEncodingVector( nullptr ),
meWinCharSet( eWinCharSet ),
mnPitchAndFamily( nPitchAndFamily ),
mbAliasSymbolsHigh( false ),
mbAliasSymbolsLow( false ),
- mbGsubRead( false ),
mpHbFont( nullptr )
{
SetBitmapSize( 0, nHeight );
@@ -657,8 +653,6 @@ WinFontFace::~WinFontFace()
if( mxUnicodeMap.Is() )
mxUnicodeMap = nullptr;
- delete mpEncodingVector;
-
if( mpHbFont )
hb_font_destroy( mpHbFont );
}
@@ -681,18 +675,6 @@ void WinFontFace::UpdateFromHDC( HDC hDC ) const
GetFontCapabilities( hDC );
}
-bool WinFontFace::HasGSUBstitutions( HDC hDC ) const
-{
- if( !mbGsubRead )
- ReadGsubTable( hDC );
- return !maGsubTable.empty();
-}
-
-bool WinFontFace::IsGSUBstituted( sal_UCS4 cChar ) const
-{
- return( maGsubTable.find( cChar ) != maGsubTable.end() );
-}
-
FontCharMapRef WinFontFace::GetFontCharMap() const
{
return mxUnicodeMap;
@@ -704,51 +686,6 @@ bool WinFontFace::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities)
return rFontCapabilities.oUnicodeRange || rFontCapabilities.oCodePageRange;
}
-void WinFontFace::ReadGsubTable( HDC hDC ) const
-{
- mbGsubRead = true;
-
- // check the existence of a GSUB table
- const DWORD GsubTag = CalcTag( "GSUB" );
- DWORD nRC = ::GetFontData( hDC, GsubTag, 0, nullptr, 0 );
- if( (nRC == GDI_ERROR) || !nRC )
- return;
-
- // parse the GSUB table through sft
- // TODO: parse it directly
-
- // sft needs the full font file data => get it
- const RawFontData aRawFontData( hDC );
- if( !aRawFontData.get() )
- return;
-
- // open font file
- sal_uInt32 nFaceNum = 0;
- if( !*aRawFontData.get() ) // TTC candidate
- nFaceNum = ~0U; // indicate "TTC font extracts only"
-
- TrueTypeFont* pTTFont = nullptr;
- ::OpenTTFontBuffer( aRawFontData.get(), aRawFontData.size(), nFaceNum, &pTTFont );
- if( !pTTFont )
- return;
-
- // add vertically substituted characters to list
- static const sal_Unicode aGSUBCandidates[] = {
- 0x0020, 0x0080, // ASCII
- 0x2000, 0x2600, // misc
- 0x3000, 0x3100, // CJK punctutation
- 0x3300, 0x3400, // squared words
- 0xFF00, 0xFFF0, // halfwidth|fullwidth forms
- 0 };
-
- for( const sal_Unicode* pPair = aGSUBCandidates; *pPair; pPair += 2 )
- for( sal_Unicode cChar = pPair[0]; cChar < pPair[1]; ++cChar )
- if( ::MapChar( pTTFont, cChar, false ) != ::MapChar( pTTFont, cChar, true ) )
- maGsubTable.insert( cChar ); // insert GSUBbed unicodes
-
- CloseTTFont( pTTFont );
-}
-
void WinFontFace::ReadCmapTable( HDC hDC ) const
{
if( mxUnicodeMap.Is() )
@@ -784,37 +721,15 @@ void WinFontFace::GetFontCapabilities( HDC hDC ) const
mbFontCapabilitiesRead = true;
- // GSUB table
- DWORD nLength;
- const DWORD GsubTag = CalcTag( "GSUB" );
- nLength = ::GetFontData( hDC, GsubTag, 0, nullptr, 0 );
- if( (nLength != GDI_ERROR) && nLength )
- {
- std::vector<unsigned char> aTable( nLength );
- unsigned char* pTable = &aTable[0];
- ::GetFontData( hDC, GsubTag, 0, pTable, nLength );
- vcl::getTTScripts(maFontCapabilities.maGSUBScriptTags, pTable, nLength);
- }
-
// OS/2 table
const DWORD OS2Tag = CalcTag( "OS/2" );
- nLength = ::GetFontData( hDC, OS2Tag, 0, nullptr, 0 );
+ DWORD nLength = ::GetFontData( hDC, OS2Tag, 0, nullptr, 0 );
if( (nLength != GDI_ERROR) && nLength )
{
std::vector<unsigned char> aTable( nLength );
unsigned char* pTable = &aTable[0];
::GetFontData( hDC, OS2Tag, 0, pTable, nLength );
- if (vcl::getTTCoverage(maFontCapabilities.oUnicodeRange, maFontCapabilities.oCodePageRange, pTable, nLength))
- {
- // Check for CJK capabilities of the current font
- // TODO, we have this info already from getTT, decode bits to
- // a readable bitset
- sal_uInt32 ulUnicodeRange1 = GetUInt( pTable + 42 );
- sal_uInt32 ulUnicodeRange2 = GetUInt( pTable + 46 );
-
- mbHasCJKSupport = (ulUnicodeRange2 & 0x2DF00000);
- mbHasArabicSupport = (ulUnicodeRange1 & 0x00002000);
- }
+ vcl::getTTCoverage(maFontCapabilities.oUnicodeRange, maFontCapabilities.oCodePageRange, pTable, nLength);
}
}
@@ -1051,17 +966,6 @@ void WinSalGraphics::SetFont( FontSelectPattern* pFont, int nFallbackLevel )
// now the font is live => update font face
if( mpWinFontData[ nFallbackLevel ] )
mpWinFontData[ nFallbackLevel ]->UpdateFromHDC( getHDC() );
-
- if( !nFallbackLevel )
- {
- mbFontKernInit = TRUE;
- if ( mpFontKernPairs )
- {
- delete[] mpFontKernPairs;
- mpFontKernPairs = nullptr;
- }
- mnFontKernPairCount = 0;
- }
}
void WinSalGraphics::GetFontMetric( ImplFontMetricDataRef& rxFontMetric, int nFallbackLevel )
@@ -1106,35 +1010,6 @@ void WinSalGraphics::GetFontMetric( ImplFontMetricDataRef& rxFontMetric, int nFa
rxFontMetric->SetMinKashida( GetMinKashidaWidth() );
}
-sal_uLong WinSalGraphics::GetKernPairs()
-{
- if ( mbFontKernInit )
- {
- if( mpFontKernPairs )
- {
- delete[] mpFontKernPairs;
- mpFontKernPairs = nullptr;
- }
- mnFontKernPairCount = 0;
-
- KERNINGPAIR* pPairs = nullptr;
- int nCount = ::GetKerningPairsW( getHDC(), 0, nullptr );
- 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 FontCharMapRef WinSalGraphics::GetFontCharMap() const
{
if( !mpWinFontData[0] )
diff --git a/vcl/win/gdi/salgdi.cxx b/vcl/win/gdi/salgdi.cxx
index 2956fed..4c63eea 100644
--- a/vcl/win/gdi/salgdi.cxx
+++ b/vcl/win/gdi/salgdi.cxx
@@ -615,9 +615,6 @@ WinSalGraphics::WinSalGraphics(WinSalGraphics::Type eType, bool bScreen, HWND hW
mhDefFont(nullptr),
mhDefPal(nullptr),
mpStdClipRgnData(nullptr),
- mbFontKernInit(false),
- mpFontKernPairs(nullptr),
- mnFontKernPairCount(0),
mnPenWidth(GSL_PEN_WIDTH)
{
if (OpenGLHelper::isVCLOpenGLEnabled() && !mbPrinter)
@@ -647,8 +644,6 @@ WinSalGraphics::~WinSalGraphics()
// delete cache data
delete [] reinterpret_cast<BYTE*>(mpStdClipRgnData);
-
- delete [] mpFontKernPairs;
}
SalGraphicsImpl* WinSalGraphics::GetImpl() const
diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx
index e2ea865..621c241 100644
--- a/vcl/win/gdi/winlayout.cxx
+++ b/vcl/win/gdi/winlayout.cxx
@@ -37,7 +37,6 @@
#include <cstdio>
#include <cstdlib>
-#include <sal/alloca.h>
#include <rtl/character.hxx>
#include <algorithm>
@@ -45,3010 +44,187 @@
#include <shlwapi.h>
#include <winver.h>
-#include <unordered_map>
-#include <unordered_set>
-
-#define DROPPED_OUTGLYPH 0xFFFF
-
// static initialization
std::unique_ptr<GlobalGlyphCache> GlyphCache::gGlobalGlyphCache(new GlobalGlyphCache);
-inline void WinFontInstance::CacheGlyphWidth( int nCharCode, int nCharWidth )
-{
- maWidthMap[ nCharCode ] = nCharWidth;
-}
-
-inline int WinFontInstance::GetCachedGlyphWidth( int nCharCode ) const
-{
- auto it = maWidthMap.find( nCharCode );
- if( it == maWidthMap.end() )
- return -1;
- return it->second;
-}
-
-bool WinFontInstance::CacheGlyphToAtlas(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics)
+bool WinFontInstance::CacheGlyphToAtlas(HDC hDC, HFONT hFont, int nGlyphIndex, SalGraphics& rGraphics)
{
- if (nGlyphIndex == DROPPED_OUTGLYPH)
- return true;
-
OpenGLGlyphDrawElement aElement;
- aElement.mbRealGlyphIndices = bRealGlyphIndices;
-
- std::vector<uint32_t> aCodePointsOrGlyphIndices(1);
- aCodePointsOrGlyphIndices[0] = nGlyphIndex;
- HDC hDC = CreateCompatibleDC(rLayout.mhDC);
- if (hDC == nullptr)
+ HDC hNewDC = CreateCompatibleDC(hDC);
+ if (hNewDC == nullptr)
{
SAL_WARN("vcl.gdi", "CreateCompatibleDC failed: " << WindowsErrorString(GetLastError()));
return false;
}
- HFONT hOrigFont = static_cast<HFONT>(SelectObject(hDC, rLayout.mhFont));
- if (hOrigFont == nullptr)
- {
- SAL_WARN("vcl.gdi", "SelectObject failed: " << WindowsErrorString(GetLastError()));
- DeleteDC(hDC);
- return false;
- }
-
- // For now we assume DWrite is present and we won't bother with fallback paths.
- D2DWriteTextOutRenderer * pTxt = dynamic_cast<D2DWriteTextOutRenderer *>(&TextOutRenderer::get(true));
- if (!pTxt)
- return false;
-
- if (!pTxt->BindFont(hDC))
- {
- SAL_WARN("vcl.gdi", "Binding of font failed. The font might not be supported by Direct Write.");
- DeleteDC(hDC);
- return false;
- }
-
- // Bail for non-horizontal text.
- {
- wchar_t sFaceName[200];
- int nFaceNameLen = GetTextFaceW(hDC, SAL_N_ELEMENTS(sFaceName), sFaceName);
-
- if (!nFaceNameLen)
- SAL_WARN("vcl.gdi", "GetTextFace failed: " << WindowsErrorString(GetLastError()));
-
- LOGFONTW aLogFont;
- GetObjectW(rLayout.mhFont, sizeof(LOGFONTW), &aLogFont);
-
- SelectObject(hDC, hOrigFont);
- DeleteDC(hDC);
-
- if (sFaceName[0] == '@' || aLogFont.lfOrientation != 0 || aLogFont.lfEscapement != 0)
- {
- pTxt->ReleaseFont();
- return false;
- }
- }
- std::vector<WORD> aGlyphIndices(1);
- // Fetch the ink boxes and calculate the size of the atlas.
- if (!bRealGlyphIndices)
- {
- if (!pTxt->GetFontFace())
- {
- SAL_WARN("vcl.gdi", "Font face is not available.");
- return false;
- }
- if (!SUCCEEDED(pTxt->GetFontFace()->GetGlyphIndices(aCodePointsOrGlyphIndices.data(), aCodePointsOrGlyphIndices.size(), aGlyphIndices.data())))
- {
- pTxt->ReleaseFont();
- return false;
- }
- }
- else
- {
- aGlyphIndices[0] = aCodePointsOrGlyphIndices[0];
- }
-
- Rectangle bounds(0, 0, 0, 0);
- auto aInkBoxes = pTxt->GetGlyphInkBoxes(aGlyphIndices.data(), aGlyphIndices.data() + 1);
- for (auto &box : aInkBoxes)
- bounds.Union(box + Point(bounds.Right(), 0));
-
- // bounds.Top() is the offset from the baseline at (0,0) to the top of the
- // inkbox.
- aElement.mnBaselineOffset = -bounds.Top();
- aElement.mnHeight = bounds.getHeight();
- aElement.mbVertical = false;
-
- // Try hard to avoid overlap as we want to be able to use
- // individual rectangles for each glyph. The ABC widths don't
- // take anti-aliasing into consideration. Let's hope that leaving
- // "extra" space between glyphs will help.
- std::vector<float> aGlyphAdv(1); // offsets between glyphs
- std::vector<DWRITE_GLYPH_OFFSET> aGlyphOffset(1, DWRITE_GLYPH_OFFSET{0.0f, 0.0f});
- std::vector<int> aEnds(1); // end of each glyph box
- float totWidth = 0;
- {
- int overhang = aInkBoxes[0].Left();
- int blackWidth = aInkBoxes[0].getWidth(); // width of non-AA pixels
- aElement.maLeftOverhangs = overhang;
-
- aGlyphAdv[0] = blackWidth + aElement.getExtraSpace();
- aGlyphOffset[0].advanceOffset = -overhang;
-
- totWidth += aGlyphAdv[0];
- aEnds[0] = totWidth;
- }
- // Leave extra space also at top and bottom
- int nBitmapWidth = totWidth;
- int nBitmapHeight = bounds.getHeight() + aElement.getExtraSpace();
-
- UINT nPos = 0;
-
- // FIXME: really I don't get why 'vertical' makes any difference [!] what does it mean !?
- if (aElement.mbVertical)
- {
- aElement.maLocation.Left() = 0;
- aElement.maLocation.Right() = nBitmapWidth;
- aElement.maLocation.Top() = nPos;
- aElement.maLocation.Bottom() = nPos + aGlyphAdv[0] + aElement.maLeftOverhangs;
- }
- else
- {
- aElement.maLocation.Left() = nPos;
- aElement.maLocation.Right() = aEnds[0];
- aElement.maLocation.Top() = 0;
- aElement.maLocation.Bottom() = bounds.getHeight() + aElement.getExtraSpace();
- }
- nPos = aEnds[0];
-
- OpenGLCompatibleDC aDC(rGraphics, 0, 0, nBitmapWidth, nBitmapHeight);
-
- HFONT hNonAntialiasedFont = nullptr;
-
- SetTextColor(aDC.getCompatibleHDC(), RGB(0, 0, 0));
- SetBkColor(aDC.getCompatibleHDC(), RGB(255, 255, 255));
-
- aDC.fill(MAKE_SALCOLOR(0xff, 0xff, 0xff));
-
- pTxt->BindDC(aDC.getCompatibleHDC(), Rectangle(0, 0, nBitmapWidth, nBitmapHeight));
- auto pRT = pTxt->GetRenderTarget();
-
- ID2D1SolidColorBrush* pBrush = nullptr;
- if (!SUCCEEDED(pRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &pBrush)))
- {
- pTxt->ReleaseFont();
- return false;
- }
-
- D2D1_POINT_2F baseline = {
- aElement.getExtraOffset(),
- aElement.getExtraOffset() + aElement.mnBaselineOffset
- };
-
- DWRITE_GLYPH_RUN glyphs = {
- pTxt->GetFontFace(),
- pTxt->GetEmHeight(),
- 1,
- aGlyphIndices.data(),
- aGlyphAdv.data(),
- aGlyphOffset.data(),
- false,
- 0
- };
-
- pRT->BeginDraw();
- pRT->DrawGlyphRun(baseline, &glyphs, pBrush);
- HRESULT hResult = pRT->EndDraw();
-
- pBrush->Release();
-
- switch (hResult)
- {
- case S_OK:
- break;
- case D2DERR_RECREATE_TARGET:
- pTxt->CreateRenderTarget();
- break;
- default:
- SAL_WARN("vcl.gdi", "DrawGlyphRun-EndDraw failed: " << WindowsErrorString(GetLastError()));
- SelectFont(aDC.getCompatibleHDC(), hOrigFont);
- if (hNonAntialiasedFont != nullptr)
- DeleteObject(hNonAntialiasedFont);
- return false;
- }
-
- pTxt->ReleaseFont();
-
- if (!GlyphCache::ReserveTextureSpace(aElement, nBitmapWidth, nBitmapHeight))
- return false;
- if (!aDC.copyToTexture(aElement.maTexture))
- return false;
-
- maGlyphCache.PutDrawElementInCache(aElement, nGlyphIndex);
-
- SelectFont(aDC.getCompatibleHDC(), hOrigFont);
- if (hNonAntialiasedFont != nullptr)
- DeleteObject(hNonAntialiasedFont);
-
- return true;
-}
-
-SimpleWinLayout::SimpleWinLayout(HDC hDC, const WinFontFace& rWinFontData,
- WinFontInstance& rWinFontEntry, bool bUseOpenGL)
-: WinLayout(hDC, rWinFontData, rWinFontEntry, bUseOpenGL),
- mnGlyphCount( 0 ),
- mnCharCount( 0 ),
- mpOutGlyphs( nullptr ),
- mpGlyphAdvances( nullptr ),
- mpGlyphOrigAdvs( nullptr ),
- mpCharWidths( nullptr ),
- mpChars2Glyphs( nullptr ),
- mpGlyphs2Chars( nullptr ),
- mpGlyphRTLFlags( nullptr ),
- mnWidth( 0 ),
- mnNotdefWidth( -1 )
-{
-}
-
-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 = static_cast<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
- {
- // FIXME: Surely this is an error situation that should not happen?
- nCharCode = '?';
- }
- }
-
- int nGlyphWidth = 0;
- // Unicode variance selectors selects glyph of previous base character, do not have width itself.
- if ( (nCharCode >= 0xFE00 && nCharCode <= 0xFE0F) || (nCharCode >= 0xE0100 && nCharCode <= 0xE01EF) )
- {
- mpOutGlyphs[ i ] = DROPPED_OUTGLYPH;
- mpGlyphAdvances[ i ] = 0;
- if ( bSurrogate && ( i+1 ) < mnGlyphCount )
- {
- mpOutGlyphs[ ++i ] = DROPPED_OUTGLYPH;
- mpGlyphAdvances[ i ] = 0;
- }
- continue;
- }
- else
- {
- // get the advance width for the current UTF-32 code point
- 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];
- 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( 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, nullptr, mpOutGlyphs+i, maxGlyphCount, mpGlyphAdvances+i);
- }
- ExtTextOutW(hDC, 0, 0, 0, nullptr, mpOutGlyphs+i, remainingGlyphs, mpGlyphAdvances+i);
- MoveToEx(hDC, oldPos.x, oldPos.y, nullptr);
- SetTextAlign(hDC, oldTa);
- }
- else
- ExtTextOutW(hDC, aPos.X(), aPos.Y(), 0, nullptr, 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 != nullptr )
- {
- 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;
-}
-
-WinLayout::WinLayout(HDC hDC, const WinFontFace& rWFD, WinFontInstance& rWFE, bool bUseOpenGL)
-: mhDC( hDC ),
- mhFont( static_cast<HFONT>(GetCurrentObject(hDC,OBJ_FONT)) ),
- mnBaseAdv( 0 ),
- mfFontScale( 1.0 ),
- mbUseOpenGL(bUseOpenGL),
- mrWinFontData( rWFD ),
- mrWinFontEntry(rWFE)
-{
- assert(mrWinFontEntry.mnRefCount > 0);
- // keep mrWinFontEntry alive
- mrWinFontEntry.mpFontCache->Acquire(&mrWinFontEntry);
-}
-
-WinLayout::~WinLayout()
-{
- mrWinFontEntry.mpFontCache->Release(&mrWinFontEntry);
-}
-
-void WinLayout::InitFont() const
-{
- SelectObject( mhDC, mhFont );
-}
-
-// Using reasonably sized fonts to emulate huge fonts works around
-// a lot of problems in printer and display drivers. Huge fonts are
-// mostly used by high resolution reference devices which are never
-// painted to anyway. In the rare case that a huge font needs to be
-// displayed somewhere then the workaround doesn't help anymore.
-// If the drivers fail silently for huge fonts, so be it...
-HFONT WinLayout::DisableFontScaling() const
-{
- if( mfFontScale == 1.0 )
- return nullptr;
-
- LOGFONTW aLogFont;
- GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont);
- aLogFont.lfHeight = (LONG)(mfFontScale * aLogFont.lfHeight);
- aLogFont.lfWidth = (LONG)(mfFontScale * aLogFont.lfWidth);
- HFONT hHugeFont = CreateFontIndirectW( &aLogFont);
- if( !hHugeFont )
- return nullptr;
-
- return SelectFont( mhDC, hHugeFont );
-}
-
-SCRIPT_CACHE& WinLayout::GetScriptCache() const
-{
- return mrWinFontEntry.GetScriptCache();
-}
-
-void WinLayout::DrawText(SalGraphics& rGraphics) const
-{
- WinSalGraphics& rWinGraphics = static_cast<WinSalGraphics&>(rGraphics);
- HDC hDC = rWinGraphics.getHDC();
-
- if (!mbUseOpenGL)
- {
- // no OpenGL, just classic rendering
- Point aPos(0, 0);
- int nGetNextGlypInfo(0);
- bool bContinue = DrawTextImpl(hDC, nullptr, &aPos, &nGetNextGlypInfo);
- assert(!bContinue);
- }
- else if (CacheGlyphs(rGraphics) &&
- DrawCachedGlyphs(rGraphics))
- {
- // Nothing
- }
- else
- {
- // We have to render the text to a hidden texture, and draw it.
- //
- // Note that Windows GDI does not really support the alpha correctly
- // when drawing - ie. it draws nothing to the alpha channel when
- // rendering the text, even the antialiasing is done as 'real' pixels,
- // not alpha...
- //
- // Luckily, this does not really limit us:
- //
- // To blend properly, we draw the texture, but then use it as an alpha
- // channel for solid color (that will define the text color). This
- // destroys the subpixel antialiasing - turns it into 'classic'
- // antialiasing - but that is the best we can do, because the subpixel
- // antialiasing needs to know what is in the background: When the
- // background is white, or white-ish, it does the subpixel, but when
- // there is a color, it just darkens the color (and does this even
- // when part of the character is on a colored background, and part on
- // white). It has to work this way, the results would look strange
- // otherwise.
- //
- // For the GL rendering to work even with the subpixel antialiasing,
- // we would need to get the current texture from the screen, let GDI
- // draw the text to it (so that it can decide well where to use the
- // subpixel and where not), and draw the result - but in that case we
- // don't need alpha anyway.
- //
- // TODO: check the performance of this 2nd approach at some stage and
- // switch to that if it performs well.
-
- Rectangle aRect;
- GetBoundRect(rGraphics, aRect);
-
- WinOpenGLSalGraphicsImpl *pImpl = dynamic_cast<WinOpenGLSalGraphicsImpl*>(rWinGraphics.mpImpl.get());
-
- if (pImpl)
- {
- pImpl->PreDraw();
-
- Point aPos(0, 0);
- int nGetNextGlypInfo(0);
- while (true)
- {
- OpenGLCompatibleDC aDC(rGraphics, aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight());
-
- // we are making changes to the DC, make sure we got a new one
- assert(aDC.getCompatibleHDC() != hDC);
-
- RECT aWinRect = { aRect.Left(), aRect.Top(), aRect.Left() + aRect.GetWidth(), aRect.Top() + aRect.GetHeight() };
- FillRect(aDC.getCompatibleHDC(), &aWinRect, static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));
-
- // setup the hidden DC with black color and white background, we will
- // use the result of the text drawing later as a mask only
- HFONT hOrigFont = SelectFont(aDC.getCompatibleHDC(), mhFont);
-
- SetTextColor(aDC.getCompatibleHDC(), RGB(0, 0, 0));
- SetBkColor(aDC.getCompatibleHDC(), RGB(255, 255, 255));
-
- UINT nTextAlign = GetTextAlign(hDC);
- SetTextAlign(aDC.getCompatibleHDC(), nTextAlign);
-
- COLORREF color = GetTextColor(hDC);
- SalColor salColor = MAKE_SALCOLOR(GetRValue(color), GetGValue(color), GetBValue(color));
-
- // the actual drawing
- bool bContinue = DrawTextImpl(aDC.getCompatibleHDC(), &aRect, &aPos, &nGetNextGlypInfo);
-
- std::unique_ptr<OpenGLTexture> xTexture(aDC.getTexture());
- if (xTexture)
- pImpl->DrawMask(*xTexture, salColor, aDC.getTwoRect());
-
- SelectFont(aDC.getCompatibleHDC(), hOrigFont);
-
- if (!bContinue)
- break;
- }
- pImpl->PostDraw();
- }
-
- }
-}
-
-bool SimpleWinLayout::CacheGlyphs(SalGraphics& rGraphics) const
-{
- static bool bDoGlyphCaching = (std::getenv("SAL_DISABLE_GLYPH_CACHING") == nullptr);
-
- if (!bDoGlyphCaching)
- return false;
-
- for (int i = 0; i < mnGlyphCount; i++)
- {
- int nCodePoint;
- if (i < mnGlyphCount-1 && rtl::isHighSurrogate(mpOutGlyphs[i]) && rtl::isLowSurrogate(mpOutGlyphs[i+1]))
- {
-#if 1 // Don't remove the #else branch in case somebody wants to
- // continue trying to figure out why sequential non-BMP glyphs
- // get scribbled on top of each others if caching is used.
- return false;
-#else
- nCodePoint = rtl::combineSurrogates(mpOutGlyphs[i], mpOutGlyphs[i+1]);
- i++;
-#endif
- }
- else
- {
- nCodePoint = mpOutGlyphs[i];
- }
-
- if (!mrWinFontEntry.GetGlyphCache().IsGlyphCached(nCodePoint))
- {
- if (!mrWinFontEntry.CacheGlyphToAtlas(false, nCodePoint, *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;
-
- 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;
-
- int nCodePoint;
- if (i < mnGlyphCount-1 && rtl::isHighSurrogate(mpOutGlyphs[i]) && rtl::isLowSurrogate(mpOutGlyphs[i+1]))
- {
- nCodePoint = rtl::combineSurrogates(mpOutGlyphs[i], mpOutGlyphs[i+1]);
- i++;
- }
- else
- {
- nCodePoint = mpOutGlyphs[i];
- }
-
- OpenGLGlyphDrawElement& rElement(mrWinFontEntry.GetGlyphCache().GetDrawElement(nCodePoint));
- OpenGLTexture& rTexture = rElement.maTexture;
-
- if (!rTexture)
- return false;
-
- SalTwoRect a2Rects(0, 0,
- rTexture.GetWidth(), rTexture.GetHeight(),
- nAdvance + aPos.X() - rElement.getExtraOffset() + rElement.maLeftOverhangs,
- aPos.Y() - rElement.mnBaselineOffset - rElement.getExtraOffset(),
- rTexture.GetWidth(), rTexture.GetHeight());
-
- pImpl->DeferredTextDraw(rTexture, salColor, a2Rects);
-
- nAdvance += mpGlyphAdvances[i];
- }
-
- if( hOrigFont )
- DeleteFont(SelectFont(hDC, hOrigFont));
-
- return true;
-}
-
-struct VisualItem
-{
-public:
- SCRIPT_ITEM* mpScriptItem;
- int mnMinGlyphPos;
- int mnEndGlyphPos;
- int mnMinCharPos;
- int mnEndCharPos;
- int mnXOffset;
- ABC maABCWidths;
- bool mbHasKashidas;
-
-public:
- bool IsEmpty() const { return (mnEndGlyphPos <= 0); }
- bool IsRTL() const { return mpScriptItem->a.fRTL; }
- bool HasKashidas() const { return mbHasKashidas; }
-};
-
-static bool bUspInited = false;
-
-static bool bManualCellAlign = true;
-
-static void InitUSP()
-{
-#if _WIN32_WINNT < _WIN32_WINNT_VISTA
- // get the usp10.dll version info
- HMODULE usp10 = GetModuleHandle("usp10.dll");
- void *pScriptIsComplex = reinterpret_cast< void* >( GetProcAddress(usp10, "ScriptIsComplex"));
- int nUspVersion = 0;
- rtl_uString* pModuleURL = NULL;
- osl_getModuleURLFromAddress( pScriptIsComplex, &pModuleURL );
- rtl_uString* pModuleFileName = NULL;
- if( pModuleURL )
- osl_getSystemPathFromFileURL( pModuleURL, &pModuleFileName );
- const sal_Unicode* pModuleFileCStr = NULL;
- if( pModuleFileName )
- pModuleFileCStr = rtl_uString_getStr( pModuleFileName );
- if( pModuleFileCStr )
- {
- DWORD nHandle;
- DWORD nBufSize = GetFileVersionInfoSizeW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), &nHandle );
- char* pBuffer = (char*)alloca( nBufSize );
- BOOL bRC = GetFileVersionInfoW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), nHandle, nBufSize, pBuffer );
- VS_FIXEDFILEINFO* pFixedFileInfo = NULL;
- UINT nFixedFileSize = 0;
- if( bRC )
- VerQueryValueW( pBuffer, const_cast<LPWSTR>(L"\\"), (void**)&pFixedFileInfo, &nFixedFileSize );
- if( pFixedFileInfo && pFixedFileInfo->dwSignature == 0xFEEF04BD )
- nUspVersion = HIWORD(pFixedFileInfo->dwProductVersionMS) * 10000
- + LOWORD(pFixedFileInfo->dwProductVersionMS);
- }
-
- // #i77976# USP>=1.0600 changed the need to manually align glyphs in their cells
- if( nUspVersion >= 10600 )
-#endif
- {
- bManualCellAlign = false;
- }
-
- bUspInited = true;
-}
-
-UniscribeLayout::UniscribeLayout(HDC hDC, const WinFontFace& rWinFontData,
- WinFontInstance& rWinFontEntry, bool bUseOpenGL)
-: WinLayout(hDC, rWinFontData, rWinFontEntry, bUseOpenGL),
- mpScriptItems( nullptr ),
- mpVisualItems( nullptr ),
- mnItemCount( 0 ),
- mnCharCapacity( 0 ),
- mpLogClusters( nullptr ),
- mpCharWidths( nullptr ),
- mnSubStringMin( 0 ),
- mnGlyphCount( 0 ),
- mnGlyphCapacity( 0 ),
- mpGlyphAdvances( nullptr ),
- mpJustifications( nullptr ),
- mpOutGlyphs( nullptr ),
- mpGlyphOffsets( nullptr ),
- mpVisualAttrs( nullptr ),
- mpGlyphs2Chars( nullptr ),
- mnMinKashidaWidth( 0 ),
- mnMinKashidaGlyph( 0 ),
- mbDisableGlyphInjection( false )
-{
-}
-
-UniscribeLayout::~UniscribeLayout()
-{
- delete[] mpScriptItems;
- delete[] mpVisualItems;
- delete[] mpLogClusters;
- delete[] mpCharWidths;
- delete[] mpOutGlyphs;
- delete[] mpGlyphAdvances;
- delete[] mpJustifications;
- delete[] mpGlyphOffsets;
- delete[] mpVisualAttrs;
- delete[] mpGlyphs2Chars;
-}
-
-#if 0 // Don't remove -- useful for temporary SAL_ DEBUG when hacking on this
-
-namespace {
-
-template<typename IntegerType>
-OUString IntegerArrayToString(IntegerType *pWords, int n)
-{
- OUString result = "{";
- for (int i = 0; i < n; ++i)
- {
- if (i > 0)
- result += ",";
- if (i > 0 && i % 10 == 0)
- result += OUString::number(i) + ":";
- result += OUString::number(pWords[i]);
- }
- result += "}";
-
- return result;
-}
-
-OUString GoffsetArrayToString(GOFFSET *pGoffsets, int n)
-{
- OUString result = "{";
- for (int i = 0; i < n; ++i)
- {
- if (i > 0)
- result += ",";
- if (i > 0 && i % 10 == 0)
- result += OUString::number(i) + ":";
- result += "(" + OUString::number(pGoffsets[i].du) + "," + OUString::number(pGoffsets[i].dv) + ")";
- }
- result += "}";
-
- return result;
-}
-
-OUString VisAttrArrayToString(SCRIPT_VISATTR *pVisAttrs, int n)
-{
- static const OUString JUSTIFICATION_NAME[] = {
- "NONE",
- "ARABIC_BLANK",
- "CHARACTER",
- "RESERVED1",
- "BLANK",
- "RESERVED2",
- "RESERVED3",
- "ARABIC_NORMAL",
- "ARABIC_KASHIDA",
- "ARABIC_ALEF",
- "ARABIC_HA",
- "ARABIC_RA",
- "ARABIC_BA",
- "ARABIC_BARA",
- "ARABIC_SEEN",
- "ARABIC_SEEN_M"
- };
-
- OUString result = "{";
- for (int i = 0; i < n; ++i)
- {
- if (i > 0)
- result += ",";
- if (i > 0 && i % 10 == 0)
- result += OUString::number(i) + ":";
- result += OUString("{") + JUSTIFICATION_NAME[pVisAttrs[i].uJustification] + (pVisAttrs[i].fClusterStart ? OUString(",ClusterStart") : OUString()) + (pVisAttrs[i].fDiacritic ? OUString(",Diacritic") : OUString()) + OUString(pVisAttrs[i].fZeroWidth ? OUString(",ZeroWidth") : OUString()) + OUString("}");
- }
- result += "}";
-
- return result;
-}
-
-} // anonymous namespace
-
-#endif // 0
-
-bool UniscribeLayout::LayoutText( ImplLayoutArgs& rArgs )
-{
- msTheString = rArgs.mrStr;
-
- // for a base layout only the context glyphs have to be dropped
- // => when the whole string is involved there is no extra context
- std::vector<int> aDropChars;
- if( rArgs.mnFlags & SalLayoutFlags::ForFallback )
- {
- // calculate superfluous context char positions
- aDropChars.push_back(0);
- aDropChars.push_back(rArgs.mrStr.getLength());
- int nMin, nEnd;
- bool bRTL;
- for( rArgs.ResetPos(); rArgs.GetNextRun( &nMin, &nEnd, &bRTL ); )
- {
- aDropChars.push_back( nMin );
- aDropChars.push_back( nEnd );
- }
- // prepare aDropChars for binary search which will allow to
- // not bother with visual items that will be dropped anyway
- std::sort( aDropChars.begin(), aDropChars.end() );
- }
-
- // prepare layout
- // TODO: fix case when recycling old UniscribeLayout object
- mnMinCharPos = rArgs.mnMinCharPos;
- mnEndCharPos = rArgs.mnEndCharPos;
-
- // determine script items from string
-
- // prepare itemization
- // TODO: try to avoid itemization since it costs a lot of performance
- SCRIPT_STATE aScriptState = {0,WORD(false),WORD(false),WORD(false),WORD(false),WORD(false),WORD(false),WORD(false),WORD(false),0,0};
- aScriptState.uBidiLevel = WORD(bool(rArgs.mnFlags & SalLayoutFlags::BiDiRtl));
- aScriptState.fOverrideDirection = WORD(bool(rArgs.mnFlags & SalLayoutFlags::BiDiStrong));
- aScriptState.fDigitSubstitute = WORD(bool(rArgs.mnFlags & SalLayoutFlags::SubstituteDigits));
- aScriptState.fArabicNumContext = aScriptState.fDigitSubstitute & aScriptState.uBidiLevel;
- DWORD nLangId = 0; // TODO: get language from font
- SCRIPT_CONTROL aScriptControl;
- memset(&aScriptControl, 0, sizeof(aScriptControl));
- aScriptControl.uDefaultLanguage = nLangId;
- aScriptControl.fNeutralOverride = aScriptState.fOverrideDirection;
- aScriptControl.fContextDigits = DWORD(bool(rArgs.mnFlags & SalLayoutFlags::SubstituteDigits));
- aScriptControl.fMergeNeutralItems = DWORD(true);
-
- // determine relevant substring and work only on it
- // when Bidi status is unknown we need to look at the whole string though
- mnSubStringMin = 0;
- const int nLength = rArgs.mrStr.getLength();
- const sal_Unicode *pStr = rArgs.mrStr.getStr();
- int nSubStringEnd = nLength;
- if( aScriptState.fOverrideDirection )
- {
- // TODO: limit substring to portion limits
- mnSubStringMin = rArgs.mnMinCharPos - 8;
- if( mnSubStringMin < 0 )
- mnSubStringMin = 0;
- nSubStringEnd = rArgs.mnEndCharPos + 8;
- if( nSubStringEnd > nLength )
- nSubStringEnd = nLength;
-
- }
- // now itemize the substring with its context
- for( int nItemCapacity = 16;; nItemCapacity *= 8 )
- {
- mpScriptItems = new SCRIPT_ITEM[ nItemCapacity ];
- HRESULT nRC = ScriptItemize(
- reinterpret_cast<LPCWSTR>(pStr + mnSubStringMin), nSubStringEnd - mnSubStringMin,
- nItemCapacity - 1, &aScriptControl, &aScriptState,
- mpScriptItems, &mnItemCount );
- if( !nRC ) // break loop when everything is correctly itemized
- break;
-
- // prepare bigger buffers for another itemization round
- delete[] mpScriptItems;
- mpScriptItems = nullptr;
- if( nRC != E_OUTOFMEMORY )
- return false;
- if( nItemCapacity > (nSubStringEnd - mnSubStringMin) + 16 )
- return false;
- }
-
- // calculate the order of visual items
- int nItem, i;
-
- // adjust char positions by substring offset
- for( nItem = 0; nItem <= mnItemCount; ++nItem )
- mpScriptItems[ nItem ].iCharPos += mnSubStringMin;
- // default visual item ordering
- mpVisualItems = new VisualItem[ mnItemCount ];
- for( nItem = 0; nItem < mnItemCount; ++nItem )
- {
- // initialize char specific item info
- VisualItem& rVisualItem = mpVisualItems[ nItem ];
- SCRIPT_ITEM* pScriptItem = &mpScriptItems[ nItem ];
- rVisualItem.mpScriptItem = pScriptItem;
- rVisualItem.mnMinCharPos = pScriptItem[0].iCharPos;
- rVisualItem.mnEndCharPos = pScriptItem[1].iCharPos;
- }
-
- // reorder visual item order if needed
- if( rArgs.mnFlags & SalLayoutFlags::BiDiStrong )
- {
- // force RTL item ordering if requested
- if( rArgs.mnFlags & SalLayoutFlags::BiDiRtl )
- {
- VisualItem* pVI0 = &mpVisualItems[ 0 ];
- VisualItem* pVI1 = &mpVisualItems[ mnItemCount ];
- while( pVI0 < --pVI1 )
- {
- VisualItem aVtmp = *pVI0;
- *(pVI0++) = *pVI1;
- *pVI1 = aVtmp;
- }
- }
- }
- else if( mnItemCount > 1 )
- {
- // apply bidi algorithm's rule L2 on item level
- // TODO: use faster L2 algorithm
- int nMaxBidiLevel = 0;
- VisualItem* pVI = &mpVisualItems[0];
- VisualItem* const pVIend = pVI + mnItemCount;
- for(; pVI < pVIend; ++pVI )
- if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel )
- nMaxBidiLevel = pVI->mpScriptItem->a.s.uBidiLevel;
-
- while( --nMaxBidiLevel >= 0 )
- {
- for( pVI = &mpVisualItems[0]; pVI < pVIend; )
- {
- // find item range that needs reordering
- for(; pVI < pVIend; ++pVI )
- if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel )
- break;
- VisualItem* pVImin = pVI++;
- for(; pVI < pVIend; ++pVI )
- if( nMaxBidiLevel >= pVI->mpScriptItem->a.s.uBidiLevel )
- break;
- VisualItem* pVImax = pVI++;
-
- // reverse order of items in this range
- while( pVImin < --pVImax )
- {
- VisualItem aVtmp = *pVImin;
- *(pVImin++) = *pVImax;
- *pVImax = aVtmp;
- }
- }
- }
- }
-
- // allocate arrays
- // TODO: when reusing object reuse old allocations or delete them
- // TODO: use only [nSubStringMin..nSubStringEnd) instead of [0..nSubStringEnd)
- mnCharCapacity = nSubStringEnd;
- mpLogClusters = new WORD[ mnCharCapacity ];
- mpCharWidths = new int[ mnCharCapacity ];
-
- mnGlyphCount = 0;
- mnGlyphCapacity = 16 + 4 * (nSubStringEnd - mnSubStringMin); // worst case assumption
- mpGlyphAdvances = new int[ mnGlyphCapacity ];
- mpOutGlyphs = new WORD[ mnGlyphCapacity ];
- mpGlyphOffsets = new GOFFSET[ mnGlyphCapacity ];
- mpVisualAttrs = new SCRIPT_VISATTR[ mnGlyphCapacity ];
-
- long nXOffset = 0;
- for( int j = mnSubStringMin; j < nSubStringEnd; ++j )
- mpCharWidths[j] = 0;
-
- // layout script items
- SCRIPT_CACHE& rScriptCache = GetScriptCache();
- for( nItem = 0; nItem < mnItemCount; ++nItem )
- {
- VisualItem& rVisualItem = mpVisualItems[ nItem ];
-
- // initialize glyph specific item info
- rVisualItem.mnMinGlyphPos = mnGlyphCount;
- rVisualItem.mnEndGlyphPos = 0;
- rVisualItem.mnXOffset = nXOffset;
-
- // shortcut ignorable items
- if( (rArgs.mnEndCharPos <= rVisualItem.mnMinCharPos)
- || (rArgs.mnMinCharPos >= rVisualItem.mnEndCharPos) )
- {
- for( int j = rVisualItem.mnMinCharPos; j < rVisualItem.mnEndCharPos; ++j )
- mpLogClusters[j] = sal::static_int_cast<WORD>(~0U);
- if (rArgs.mnMinCharPos >= rVisualItem.mnEndCharPos)
- { // fdo#47553 adjust "guessed" min (maybe up to -8 off) to
- // actual min so it can be used properly in GetNextGlyphs
- if (mnSubStringMin < rVisualItem.mnEndCharPos)
- mnSubStringMin = rVisualItem.mnEndCharPos;
- }
- continue;
- }
-
- // override bidi analysis if requested
- if( rArgs.mnFlags & SalLayoutFlags::BiDiStrong )
- {
- // FIXME: is this intended ?
- rVisualItem.mpScriptItem->a.fRTL = (aScriptState.uBidiLevel & 1);
- rVisualItem.mpScriptItem->a.s.uBidiLevel = aScriptState.uBidiLevel;
- rVisualItem.mpScriptItem->a.s.fOverrideDirection = aScriptState.fOverrideDirection;
- }
-
- // convert the unicodes to glyphs
- int nGlyphCount = 0;
- int nCharCount = rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos;
- HRESULT nRC = ScriptShape( mhDC, &rScriptCache,
- reinterpret_cast<LPCWSTR>(pStr + rVisualItem.mnMinCharPos),
- nCharCount,
- mnGlyphCapacity - rVisualItem.mnMinGlyphPos, // problem when >0xFFFF
- &rVisualItem.mpScriptItem->a,
- mpOutGlyphs + rVisualItem.mnMinGlyphPos,
- mpLogClusters + rVisualItem.mnMinCharPos,
- mpVisualAttrs + rVisualItem.mnMinGlyphPos,
- &nGlyphCount );
-
- // find and handle problems in the unicode to glyph conversion
- if( nRC == USP_E_SCRIPT_NOT_IN_FONT )
- {
- // the whole visual item needs a fallback, but make sure that the next
- // fallback request is limited to the characters in the original request
- // => this is handled in ImplLayoutArgs::PrepareFallback()
- rArgs.NeedFallback( rVisualItem.mnMinCharPos, rVisualItem.mnEndCharPos,
- rVisualItem.IsRTL() );
-
- // don't bother to do a default layout in a fallback level
- if( rArgs.mnFlags & SalLayoutFlags::ForFallback )
- continue;
-
- // the primitive layout engine is good enough for the default layout
- rVisualItem.mpScriptItem->a.eScript = SCRIPT_UNDEFINED;
- nRC = ScriptShape( mhDC, &rScriptCache,
- reinterpret_cast<LPCWSTR>(pStr + rVisualItem.mnMinCharPos),
- nCharCount,
- mnGlyphCapacity - rVisualItem.mnMinGlyphPos,
- &rVisualItem.mpScriptItem->a,
- mpOutGlyphs + rVisualItem.mnMinGlyphPos,
- mpLogClusters + rVisualItem.mnMinCharPos,
- mpVisualAttrs + rVisualItem.mnMinGlyphPos,
- &nGlyphCount );
-
- if( nRC != 0 )
- continue;
-
- }
- else if( nRC != 0 )
- // something undefined happened => give up for this visual item
- continue;
- else // if( nRC == 0 )
- {
- // check if there are any NotDef glyphs
- for( i = 0; i < nGlyphCount; ++i )
- if( 0 == mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] )
- break;
- if( i < nGlyphCount )
- {
- // clip charpos limits to the layout string without context
- int nMinCharPos = rVisualItem.mnMinCharPos;
- if( nMinCharPos < rArgs.mnMinCharPos )
- nMinCharPos = rArgs.mnMinCharPos;
- int nEndCharPos = rVisualItem.mnEndCharPos;
- if( nEndCharPos > rArgs.mnEndCharPos )
- nEndCharPos = rArgs.mnEndCharPos;
- // request fallback for individual NotDef glyphs
- do
- {
- // ignore non-NotDef glyphs
- if( 0 != mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] )
- continue;
- mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = DROPPED_OUTGLYPH;
- // request fallback for the whole cell that resulted in a NotDef glyph
- // TODO: optimize algorithm
- const bool bRTL = rVisualItem.IsRTL();
- if( !bRTL )
- {
- // request fallback for the left-to-right cell
- for( int c = nMinCharPos; c < nEndCharPos; ++c )
- {
- if( mpLogClusters[ c ] == i )
- {
- // #i55716# skip WORDJOINER
- if( pStr[ c ] == 0x2060 )
- mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1;
- else
- rArgs.NeedFallback( c, false );
- }
- }
- }
- else
- {
- // request fallback for the right to left cell
- for( int c = nEndCharPos; --c >= nMinCharPos; )
- {
- if( mpLogClusters[ c ] == i )
- {
- // #i55716# skip WORDJOINER
- if( pStr[ c ] == 0x2060 )
- mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1;
- else
- rArgs.NeedFallback( c, true );
- }
- }
- }
- } while( ++i < nGlyphCount );
- }
- }
-
- // now place the glyphs
- nRC = ScriptPlace( mhDC, &rScriptCache,
- mpOutGlyphs + rVisualItem.mnMinGlyphPos,
- nGlyphCount,
- mpVisualAttrs + rVisualItem.mnMinGlyphPos,
- &rVisualItem.mpScriptItem->a,
- mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
- mpGlyphOffsets + rVisualItem.mnMinGlyphPos,
- &rVisualItem.maABCWidths );
-
- if( nRC != 0 )
- continue;
-
- // calculate the logical char widths from the glyph layout
- nRC = ScriptGetLogicalWidths(
- &rVisualItem.mpScriptItem->a,
- nCharCount, nGlyphCount,
- mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
- mpLogClusters + rVisualItem.mnMinCharPos,
- mpVisualAttrs + rVisualItem.mnMinGlyphPos,
- mpCharWidths + rVisualItem.mnMinCharPos );
-
- // update the glyph counters
- mnGlyphCount += nGlyphCount;
- rVisualItem.mnEndGlyphPos = mnGlyphCount;
-
- // update nXOffset
- int nEndGlyphPos;
- if( GetItemSubrange( rVisualItem, i, nEndGlyphPos ) )
- for(; i < nEndGlyphPos; ++i )
- nXOffset += mpGlyphAdvances[ i ];
-
- // TODO: shrink glyphpos limits to match charpos/fallback limits
- //pVI->mnMinGlyphPos = nMinGlyphPos;
- //pVI->mnEndGlyphPos = nEndGlyphPos;
-
- // drop the superfluous context glyphs
- auto it = aDropChars.cbegin();
- while( it != aDropChars.cend() )
- {
- // find matching "drop range"
- int nMinDropPos = *(it++); // begin of drop range
- if( nMinDropPos >= rVisualItem.mnEndCharPos )
- break;
- int nEndDropPos = *(it++); // end of drop range
- if( nEndDropPos <= rVisualItem.mnMinCharPos )
- continue;
- // clip "drop range" to visual item's char range
- if( nMinDropPos <= rVisualItem.mnMinCharPos )
- {
- nMinDropPos = rVisualItem.mnMinCharPos;
- // drop the whole visual item if possible
- if( nEndDropPos >= rVisualItem.mnEndCharPos )
- {
- rVisualItem.mnEndGlyphPos = 0;
- break;
- }
- }
- if( nEndDropPos > rVisualItem.mnEndCharPos )
- nEndDropPos = rVisualItem.mnEndCharPos;
-
- // drop the glyphs which correspond to the charpos range
- // drop the corresponding glyphs in the cluster
- for( int c = nMinDropPos; c < nEndDropPos; ++c )
- {
- int nGlyphPos = mpLogClusters[c] + rVisualItem.mnMinGlyphPos;
- // no need to bother when the cluster was already dropped
- if( mpOutGlyphs[ nGlyphPos ] != DROPPED_OUTGLYPH )
- {
- for(;;)
- {
- mpOutGlyphs[ nGlyphPos ] = DROPPED_OUTGLYPH;
- // until the end of visual item
- if( ++nGlyphPos >= rVisualItem.mnEndGlyphPos )
- break;
- // until the next cluster start
- if( mpVisualAttrs[ nGlyphPos ].fClusterStart )
- break;
- }
- }
- }
- }
- }
-
- // 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 )
- {
- mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale);
-
- for( i = 0; i < mnItemCount; ++i )
- mpVisualItems[i].mnXOffset = (int)((double)mpVisualItems[i].mnXOffset*mfFontScale);
-
- mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale);
- for( i = 0; i < mnGlyphCount; ++i )
- {
- mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale);
- mpGlyphOffsets[i].du = (LONG)(mpGlyphOffsets[i].du * mfFontScale);
- mpGlyphOffsets[i].dv = (LONG)(mpGlyphOffsets[i].dv * mfFontScale);
- // mpJustifications are still NULL
- }
-
- for( i = mnSubStringMin; i < nSubStringEnd; ++i )
- mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale);
- }
-
- return true;
-}
-
-// calculate the range of relevant glyphs for this visual item
-bool UniscribeLayout::GetItemSubrange( const VisualItem& rVisualItem,
- int& rMinGlyphPos, int& rEndGlyphPos ) const
-{
- // return early when nothing of interest in this item
- if( rVisualItem.IsEmpty()
- || (rVisualItem.mnEndCharPos <= mnMinCharPos)
- || (mnEndCharPos <= rVisualItem.mnMinCharPos) )
- return false;
-
- // default: subrange is complete range
- rMinGlyphPos = rVisualItem.mnMinGlyphPos;
- rEndGlyphPos = rVisualItem.mnEndGlyphPos;
-
- // return early when the whole item is of interest
- if( (mnMinCharPos <= rVisualItem.mnMinCharPos)
- && (rVisualItem.mnEndCharPos <= mnEndCharPos ) )
- return true;
-
- // get glyph range from char range by looking at cluster boundries
- // TODO: optimize for case that LTR/RTL correspond to monotonous glyph indexes
- rMinGlyphPos = rVisualItem.mnEndGlyphPos;
- int nMaxGlyphPos = 0;
-
- int i = mnMinCharPos;
- if( i < rVisualItem.mnMinCharPos )
- i = rVisualItem.mnMinCharPos;
- int nCharPosLimit = rVisualItem.mnEndCharPos;
- if( nCharPosLimit > mnEndCharPos )
- nCharPosLimit = mnEndCharPos;
- for(; i < nCharPosLimit; ++i )
- {
- int n = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos;
- if( rMinGlyphPos > n )
- rMinGlyphPos = n;
- if( nMaxGlyphPos < n )
- nMaxGlyphPos = n;
- }
- if (nMaxGlyphPos > rVisualItem.mnEndGlyphPos)
- nMaxGlyphPos = rVisualItem.mnEndGlyphPos - 1;
-
- // extend the glyph range to account for all glyphs in referenced clusters
- if( !rVisualItem.IsRTL() ) // LTR-item
- {
- // extend to rightmost glyph of rightmost referenced cluster
- for( i = nMaxGlyphPos; ++i < rVisualItem.mnEndGlyphPos; nMaxGlyphPos = i )
- if( mpVisualAttrs[i].fClusterStart )
- break;
- }
- else // RTL-item
- {
- // extend to leftmost glyph of leftmost referenced cluster
- for( i = rMinGlyphPos; --i >= rVisualItem.mnMinGlyphPos; rMinGlyphPos = i )
- if( mpVisualAttrs[i].fClusterStart )
- break;
- }
- rEndGlyphPos = nMaxGlyphPos + 1;
-
- return true;
-}
-
-int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
- int& nStartx8, DeviceCoordinate* pGlyphAdvances, int* pCharPosAry,
- const PhysicalFontFace** /*pFallbackFonts*/ ) const
-{
- // HACK to allow fake-glyph insertion (e.g. for kashidas)
- // TODO: use iterator idiom instead of GetNextGlyphs(...)
- // TODO: else make sure that the limit for glyph injection is sufficient (currently 256)
- int nSubIter = nStartx8 & 0xff;
- int nStart = nStartx8 >> 8;
-
- // check the glyph iterator
- if( nStart > mnGlyphCount ) // nStart>MAX means no more glyphs
- return 0;
-
- // find the visual item for the nStart glyph position
- int nItem = 0;
- const VisualItem* pVI = mpVisualItems;
- if( nStart <= 0 ) // nStart<=0 requests the first visible glyph
- {
- // find first visible item
- for(; nItem < mnItemCount; ++nItem, ++pVI )
- if( !pVI->IsEmpty() )
- break;
- // it is possible that there are glyphs but no valid visual item
- // TODO: get rid of these visual items more early
- if( nItem < mnItemCount )
- nStart = pVI->mnMinGlyphPos;
- }
- else //if( nStart > 0 ) // nStart>0 means absolute glyph pos +1
- {
- --nStart;
-
- // find matching item
- for(; nItem < mnItemCount; ++nItem, ++pVI )
- if( (nStart >= pVI->mnMinGlyphPos)
- && (nStart < pVI->mnEndGlyphPos) )
- break;
- }
-
- // after the last visual item there are no more glyphs
- if( (nItem >= mnItemCount) || (nStart < 0) )
- {
- nStartx8 = (mnGlyphCount + 1) << 8;
- return 0;
- }
-
- // calculate the first glyph in the next visual item
- int nNextItemStart = mnGlyphCount;
- while( ++nItem < mnItemCount )
- {
- if( mpVisualItems[nItem].IsEmpty() )
- continue;
- nNextItemStart = mpVisualItems[nItem].mnMinGlyphPos;
- break;
- }
-
- // get the range of relevant glyphs in this visual item
- int nMinGlyphPos, nEndGlyphPos;
- bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos );
- SAL_WARN_IF( !bRC, "vcl", "USPLayout::GNG GISR() returned false" );
- if( !bRC )
- {
- nStartx8 = (mnGlyphCount + 1) << 8;
- return 0;
- }
-
- // make sure nStart is inside the range of relevant glyphs
- if( nStart < nMinGlyphPos )
- nStart = nMinGlyphPos;
-
- // calculate the start glyph xoffset relative to layout's base position,
- // advance to next visual glyph position by using adjusted glyph widths
- // TODO: speed up the calculation for nStart!=0 case by using rPos as a cache
- long nXOffset = pVI->mnXOffset;
- const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
- for( int i = nMinGlyphPos; i < nStart; ++i )
- nXOffset += pGlyphWidths[ i ];
-
- // adjust the nXOffset relative to glyph cluster start
- int c = mnMinCharPos;
- if( !pVI->IsRTL() ) // LTR-case
- {
- // LTR case: subtract the remainder of the cell from xoffset
- int nTmpIndex = mpLogClusters[c];
- while( (--c >= pVI->mnMinCharPos)
- && (nTmpIndex == mpLogClusters[c]) )
- nXOffset -= mpCharWidths[c];
- }
- else // RTL-case
- {
- // RTL case: add the remainder of the cell from xoffset
- int nTmpIndex = mpLogClusters[ pVI->mnEndCharPos - 1 ];
- while( (--c >= pVI->mnMinCharPos)
- && (nTmpIndex == mpLogClusters[c]) )
- nXOffset += mpCharWidths[c];
-
- // adjust the xoffset if justified glyphs are not positioned at their justified positions yet
- if( mpJustifications && !bManualCellAlign )
- nXOffset += mpJustifications[ nStart ] - mpGlyphAdvances[ nStart ];
- }
-
- // create mpGlyphs2Chars[] if it is needed later
- if( pCharPosAry && !mpGlyphs2Chars )
- {
- // create and reset the new array
- mpGlyphs2Chars = new int[ mnGlyphCapacity ];
- for( int i = 0; i < mnGlyphCount; ++i )
- mpGlyphs2Chars[i] = -1;
- // calculate the char->glyph mapping
- for( nItem = 0; nItem < mnItemCount; ++nItem )
- {
- // ignore invisible visual items
- const VisualItem& rVI = mpVisualItems[ nItem ];
- if( rVI.IsEmpty() )
- continue;
-
- //Resolves: fdo#33090 Ensure that all glyph slots, even if 0-width
- //or empty due to combining chars etc, map back to a character
- //position so that iterating over glyph slots one at a time for
- //glyph fallback can keep context as to what characters are the
- //inputs that caused a missing glyph in a given font.
-
- //See: fdo#46923/fdo#46896/fdo#46750 for extra complexities
- {
- int dir = 1;
- int out = rVI.mnMinCharPos;
- if (rVI.IsRTL())
- {
- dir = -1;
- out = rVI.mnEndCharPos-1;
- }
- for(c = rVI.mnMinCharPos; c < rVI.mnEndCharPos; ++c)
- {
- int i = out - mnSubStringMin;
- mpGlyphs2Chars[i] = c;
- out += dir;
- }
- }
-
- // calculate the mapping by using mpLogClusters[]
- // mpGlyphs2Chars[] should obey the logical order
- // => reversing the loop does this by overwriting higher logicals
- for( c = rVI.mnEndCharPos; --c >= rVI.mnMinCharPos; )
- {
- int i = mpLogClusters[c] + rVI.mnMinGlyphPos;
- mpGlyphs2Chars[i] = c;
- }
- // use a heuristic to fill the gaps in the glyphs2chars array
- c = !rVI.IsRTL() ? rVI.mnMinCharPos : rVI.mnEndCharPos - 1;
- for( int i = rVI.mnMinGlyphPos; i < rVI.mnEndGlyphPos; ++i ) {
- if( mpGlyphs2Chars[i] == -1 )
- mpGlyphs2Chars[i] = c;
- else
- c = mpGlyphs2Chars[i];
- }
- }
- }
-
- // calculate the absolute position of the first result glyph in pixel units
- const GOFFSET aGOffset = mpGlyphOffsets[ nStart ];
- Point aRelativePos( nXOffset + aGOffset.du, -aGOffset.dv );
- rPos = GetDrawPosition( aRelativePos );
-
- // fill the result arrays
- int nCount = 0;
- while( nCount < nLen )
- {
- // prepare return values
- sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ];
- int nGlyphWidth = pGlyphWidths[ nStart ];
- int nCharPos = -1; // no need to determine charpos
- if( mpGlyphs2Chars ) // unless explicitly requested+provided
- {
- nCharPos = mpGlyphs2Chars[ nStart ];
- }
-
- // inject kashida glyphs if needed
- if( !mbDisableGlyphInjection
- && mpJustifications
- && mnMinKashidaWidth
- && mpVisualAttrs[nStart].uJustification >= SCRIPT_JUSTIFY_ARABIC_NORMAL )
- {
- // prepare draw position adjustment
- int nExtraOfs = (nSubIter++) * mnMinKashidaWidth;
- // calculate space available for the injected glyphs
- nGlyphWidth = mpGlyphAdvances[ nStart ];
- const int nExtraWidth = mpJustifications[ nStart ] - nGlyphWidth;
- const int nToFillWidth = nExtraWidth - nExtraOfs;
- if( (4*nToFillWidth >= mnMinKashidaWidth) // prevent glyph-injection if there is no room
- || ((nSubIter > 1) && (nToFillWidth > 0)) ) // unless they can overlap with others
- {
- // handle if there is not sufficient room for a full glyph
- if( nToFillWidth < mnMinKashidaWidth )
- {
- // overlap it with the previously injected glyph if possible
- int nOverlap = mnMinKashidaWidth - nToFillWidth;
- // else overlap it with both neighboring glyphs
- if( nSubIter <= 1 )
- nOverlap /= 2;
- nExtraOfs -= nOverlap;
- }
- nGlyphWidth = mnMinKashidaWidth;
- aGlyphId = mnMinKashidaGlyph;
- nCharPos = -1;
- }
- else
- {
- nExtraOfs += nToFillWidth; // at right of cell
- nSubIter = 0; // done with glyph injection
- }
- if( !bManualCellAlign )
- nExtraOfs -= nExtraWidth; // adjust for right-aligned cells
-
- // adjust the draw position for the injected-glyphs case
- if( nExtraOfs )
- {
- aRelativePos.X() += nExtraOfs;
- rPos = GetDrawPosition( aRelativePos );
- }
- }
-
- // update return values
- if( (mnLayoutFlags & SalLayoutFlags::Vertical) &&
- nCharPos != -1 )
- aGlyphId |= GetVerticalFlags( msTheString[nCharPos] );
- *(pGlyphs++) = aGlyphId;
- if( pGlyphAdvances )
- *(pGlyphAdvances++) = nGlyphWidth;
- if( pCharPosAry )
- *(pCharPosAry++) = nCharPos;
-
- // increment counter of returned glyphs
- ++nCount;
-
- // reduce code complexity by returning early in glyph-injection case
- if( nSubIter != 0 )
- break;
-
- // stop after the last visible glyph in this visual item
- if( ++nStart >= nEndGlyphPos )
- {
- nStart = nNextItemStart;
- break;
- }
-
- // RTL-justified glyph positioning is not easy
- // simplify the code by just returning only one glyph at a time
- if( mpJustifications && pVI->IsRTL() )
- break;
-
- // stop when the x-position of the next glyph is unexpected
- if( !pGlyphAdvances )
- if( (mpGlyphOffsets && (mpGlyphOffsets[nStart].du != aGOffset.du) )
- || (mpJustifications && (mpJustifications[nStart] != mpGlyphAdvances[nStart]) ) )
- break;
-
- // stop when the y-position of the next glyph is unexpected
- if( mpGlyphOffsets && (mpGlyphOffsets[nStart].dv != aGOffset.dv) )
- break;
- }
-
- ++nStart;
- nStartx8 = (nStart << 8) + nSubIter;
- return nCount;
-}
-
-void UniscribeLayout::MoveGlyph( int nStartx8, long nNewXPos )
-{
- SAL_WARN_IF( (nStartx8 & 0xff), "vcl", "USP::MoveGlyph(): glyph injection not disabled!" );
- int nStart = nStartx8 >> 8;
- if( nStart > mnGlyphCount )
- return;
-
- VisualItem* pVI = mpVisualItems;
- int nMinGlyphPos = 0, nEndGlyphPos;
- if( nStart == 0 ) // nStart==0 for first visible glyph
- {
- for( int i = mnItemCount; --i >= 0; ++pVI )
- if( GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ) )
- break;
- nStart = nMinGlyphPos;
- SAL_WARN_IF( nStart > mnGlyphCount, "vcl", "USPLayout::MoveG overflow" );
- }
- else //if( nStart > 0 ) // nStart>0 means absolute_glyphpos+1
- {
- --nStart;
- for( int i = mnItemCount; --i >= 0; ++pVI )
- if( (nStart >= pVI->mnMinGlyphPos) && (nStart < pVI->mnEndGlyphPos) )
- break;
- bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos );
- (void)bRC; // avoid var-not-used warning
- SAL_WARN_IF( !bRC, "vcl", "USPLayout::MoveG GISR() returned false" );
- }
-
- long nDelta = nNewXPos - pVI->mnXOffset;
- if( nStart > nMinGlyphPos )
- {
- // move the glyph by expanding its left glyph but ignore dropped glyphs
- int i, nLastUndropped = nMinGlyphPos - 1;
- for( i = nMinGlyphPos; i < nStart; ++i )
- {
- if (mpOutGlyphs[i] != DROPPED_OUTGLYPH)
- {
- nDelta -= (mpJustifications)? mpJustifications[ i ] : mpGlyphAdvances[ i ];
- nLastUndropped = i;
- }
- }
... etc. - the rest is truncated
More information about the Libreoffice-commits
mailing list