xref: /aoo41x/main/vcl/win/source/gdi/winlayout.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
30 
31 #include "rtl/ustring.hxx"
32 
33 #include "osl/module.h"
34 #include "osl/file.h"
35 
36 #include "tools/svwin.h"
37 
38 #include "vcl/svapp.hxx"
39 
40 #include "win/salgdi.h"
41 #include "win/saldata.hxx"
42 
43 // for GetMirroredChar
44 #include "sft.hxx"
45 #include "sallayout.hxx"
46 
47 #include <cstdio>
48 #include <malloc.h>
49 #ifndef __MINGW32__
50 #define alloca _alloca
51 #endif
52 
53 #ifdef GCP_KERN_HACK
54     #include <algorithm>
55 #endif // GCP_KERN_HACK
56 
57 
58 #define USE_UNISCRIBE
59 #ifdef USE_UNISCRIBE
60 #include <Usp10.h>
61 #include <ShLwApi.h>
62 #include <winver.h>
63 #endif // USE_UNISCRIBE
64 
65 #include <hash_map>
66 #include <set>
67 
68 typedef std::hash_map<int,int> IntMap;
69 typedef std::set<int> IntSet;
70 
71 // Graphite headers
72 #ifdef ENABLE_GRAPHITE
73 #include <i18npool/mslangid.hxx>
74 #include <graphite/GrClient.h>
75 #include <graphite/WinFont.h>
76 #include <graphite/Segment.h>
77 #include <graphite_layout.hxx>
78 #include <graphite_cache.hxx>
79 #include <graphite_features.hxx>
80 #endif
81 
82 #define DROPPED_OUTGLYPH 0xFFFF
83 
84 using namespace rtl;
85 
86 // =======================================================================
87 
88 // win32 specific physical font instance
89 class ImplWinFontEntry : public ImplFontEntry
90 {
91 public:
92                             ImplWinFontEntry( ImplFontSelectData& );
93                             ~ImplWinFontEntry();
94 
95 private:
96     // TODO: also add HFONT??? Watch out for issues with too many active fonts...
97 
98 #ifdef GCP_KERN_HACK
99 public:
100     bool                    HasKernData() const;
101     void                    SetKernData( int, const KERNINGPAIR* );
102     int                     GetKerning( sal_Unicode, sal_Unicode ) const;
103 private:
104     KERNINGPAIR*            mpKerningPairs;
105     int                     mnKerningPairs;
106 #endif // GCP_KERN_HACK
107 
108 #ifdef USE_UNISCRIBE
109 public:
110     SCRIPT_CACHE&           GetScriptCache() const
111                             { return maScriptCache; }
112 private:
113     mutable SCRIPT_CACHE    maScriptCache;
114 #endif // USE_UNISCRIBE
115 
116 public:
117     int                     GetCachedGlyphWidth( int nCharCode ) const;
118     void                    CacheGlyphWidth( int nCharCode, int nCharWidth );
119 
120 	bool					InitKashidaHandling( HDC );
121 	int						GetMinKashidaWidth() const { return mnMinKashidaWidth; }
122 	int						GetMinKashidaGlyph() const { return mnMinKashidaGlyph; }
123 
124 private:
125     IntMap                  maWidthMap;
126 	mutable int				mnMinKashidaWidth;
127 	mutable int				mnMinKashidaGlyph;
128 };
129 
130 // -----------------------------------------------------------------------
131 
132 inline void ImplWinFontEntry::CacheGlyphWidth( int nCharCode, int nCharWidth )
133 {
134     maWidthMap[ nCharCode ] = nCharWidth;
135 }
136 
137 inline int ImplWinFontEntry::GetCachedGlyphWidth( int nCharCode ) const
138 {
139     IntMap::const_iterator it = maWidthMap.find( nCharCode );
140     if( it == maWidthMap.end() )
141         return -1;
142     return it->second;
143 }
144 
145 // =======================================================================
146 
147 class WinLayout : public SalLayout
148 {
149 public:
150                         WinLayout( HDC, const ImplWinFontData&, ImplWinFontEntry& );
151     virtual void        InitFont() const;
152     void                SetFontScale( float f ) { mfFontScale = f; }
153     float               GetFontScale() const    { return mfFontScale; }
154     HFONT               DisableFontScaling( void) const;
155 
156 #ifdef USE_UNISCRIBE
157     SCRIPT_CACHE&       GetScriptCache() const
158                             { return mrWinFontEntry.GetScriptCache(); }
159 #endif // USE_UNISCRIBE
160 
161 protected:
162     HDC                 mhDC;               // WIN32 device handle
163     HFONT               mhFont;             // WIN32 font handle
164     int                 mnBaseAdv;          // x-offset relative to Layout origin
165     float               mfFontScale;        // allows metrics emulation of huge font sizes
166 
167     const ImplWinFontData& mrWinFontData;
168     ImplWinFontEntry&   mrWinFontEntry;
169 };
170 
171 // =======================================================================
172 
173 class SimpleWinLayout : public WinLayout
174 {
175 public:
176                     SimpleWinLayout( HDC, BYTE nCharSet, const ImplWinFontData&, ImplWinFontEntry& );
177     virtual         ~SimpleWinLayout();
178 
179     virtual bool    LayoutText( ImplLayoutArgs& );
180     virtual void    AdjustLayout( ImplLayoutArgs& );
181     virtual void    DrawText( SalGraphics& ) const;
182 
183     virtual int     GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&,
184                         sal_Int32* pGlyphAdvances, int* pCharIndexes ) const;
185 
186     virtual long    FillDXArray( long* pDXArray ) const;
187     virtual int     GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
188     virtual void    GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
189 
190     // for glyph+font+script fallback
191     virtual void    MoveGlyph( int nStart, long nNewXPos );
192     virtual void    DropGlyph( int nStart );
193     virtual void    Simplify( bool bIsBase );
194 
195 protected:
196     void            Justify( long nNewWidth );
197     void            ApplyDXArray( const ImplLayoutArgs& );
198 
199 private:
200     int             mnGlyphCount;
201     int             mnCharCount;
202     WCHAR*          mpOutGlyphs;
203     int*            mpGlyphAdvances;    // if possible this is shared with mpGlyphAdvances[]
204     int*            mpGlyphOrigAdvs;
205     int*            mpCharWidths;       // map rel char pos to char width
206     int*            mpChars2Glyphs;     // map rel char pos to abs glyph pos
207     int*            mpGlyphs2Chars;     // map abs glyph pos to abs char pos
208     bool*           mpGlyphRTLFlags;    // BiDi status for glyphs: true=>RTL
209     mutable long    mnWidth;
210     bool            mbDisableGlyphs;
211 
212     int             mnNotdefWidth;
213     BYTE            mnCharSet;
214 };
215 
216 // =======================================================================
217 
218 WinLayout::WinLayout( HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE )
219 :   mhDC( hDC ),
220     mhFont( (HFONT)::GetCurrentObject(hDC,OBJ_FONT) ),
221     mnBaseAdv( 0 ),
222     mfFontScale( 1.0 ),
223     mrWinFontData( rWFD ),
224     mrWinFontEntry( rWFE )
225 {}
226 
227 // -----------------------------------------------------------------------
228 
229 void WinLayout::InitFont() const
230 {
231     ::SelectObject( mhDC, mhFont );
232 }
233 
234 // -----------------------------------------------------------------------
235 
236 // Using reasonably sized fonts to emulate huge fonts works around
237 // a lot of problems in printer and display drivers. Huge fonts are
238 // mostly used by high resolution reference devices which are never
239 // painted to anyway. In the rare case that a huge font needs to be
240 // displayed somewhere then the workaround doesn't help anymore.
241 // If the drivers fail silently for huge fonts, so be it...
242 HFONT WinLayout::DisableFontScaling() const
243 {
244     if( mfFontScale == 1.0 )
245         return 0;
246 
247     LOGFONTW aLogFont;
248     ::GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont);
249     aLogFont.lfHeight = (LONG)(mfFontScale * aLogFont.lfHeight);
250     aLogFont.lfWidth  = (LONG)(mfFontScale * aLogFont.lfWidth);
251     HFONT hHugeFont = ::CreateFontIndirectW( &aLogFont);
252     if( !hHugeFont )
253         return 0;
254 
255     return SelectFont( mhDC, hHugeFont );
256 }
257 
258 // =======================================================================
259 
260 SimpleWinLayout::SimpleWinLayout( HDC hDC, BYTE nCharSet,
261     const ImplWinFontData& rWinFontData, ImplWinFontEntry& rWinFontEntry )
262 :   WinLayout( hDC, rWinFontData, rWinFontEntry ),
263     mnGlyphCount( 0 ),
264     mnCharCount( 0 ),
265     mpOutGlyphs( NULL ),
266     mpGlyphAdvances( NULL ),
267     mpGlyphOrigAdvs( NULL ),
268     mpCharWidths( NULL ),
269     mpChars2Glyphs( NULL ),
270     mpGlyphs2Chars( NULL ),
271     mpGlyphRTLFlags( NULL ),
272     mnWidth( 0 ),
273     mnNotdefWidth( -1 ),
274     mnCharSet( nCharSet ),
275     mbDisableGlyphs( false )
276 {
277     mbDisableGlyphs = true;
278 }
279 
280 // -----------------------------------------------------------------------
281 
282 SimpleWinLayout::~SimpleWinLayout()
283 {
284     delete[] mpGlyphRTLFlags;
285     delete[] mpGlyphs2Chars;
286     delete[] mpChars2Glyphs;
287     if( mpCharWidths != mpGlyphAdvances )
288         delete[] mpCharWidths;
289     delete[] mpGlyphOrigAdvs;
290     delete[] mpGlyphAdvances;
291     delete[] mpOutGlyphs;
292 }
293 
294 // -----------------------------------------------------------------------
295 
296 bool SimpleWinLayout::LayoutText( ImplLayoutArgs& rArgs )
297 {
298     // prepare layout
299     // TODO: fix case when recyclying old SimpleWinLayout object
300     mbDisableGlyphs |= ((rArgs.mnFlags & SAL_LAYOUT_DISABLE_GLYPH_PROCESSING) != 0);
301     mnCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
302 
303     if( !mbDisableGlyphs )
304     {
305         // Win32 glyph APIs have serious problems with vertical layout
306         // => workaround is to use the unicode methods then
307         if( rArgs.mnFlags & SAL_LAYOUT_VERTICAL )
308             mbDisableGlyphs = true;
309         else
310             // use cached value from font face
311             mbDisableGlyphs = mrWinFontData.IsGlyphApiDisabled();
312     }
313 
314     // TODO: use a cached value for bDisableAsianKern from upper layers
315     if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
316     {
317         TEXTMETRICA aTextMetricA;
318         if( ::GetTextMetricsA( mhDC, &aTextMetricA )
319         && !(aTextMetricA.tmPitchAndFamily & TMPF_FIXED_PITCH) && !(aTextMetricA.tmCharSet == 0x86) )
320             rArgs.mnFlags &= ~SAL_LAYOUT_KERNING_ASIAN;
321     }
322 
323     // layout text
324     int i, j;
325 
326     mnGlyphCount = 0;
327     bool bVertical = (rArgs.mnFlags & SAL_LAYOUT_VERTICAL) != 0;
328 
329     // count the number of chars to process if no RTL run
330     rArgs.ResetPos();
331     bool bHasRTL = false;
332     while( rArgs.GetNextRun( &i, &j, &bHasRTL ) && !bHasRTL )
333         mnGlyphCount += j - i;
334 
335     // if there are RTL runs we need room to remember individual BiDi flags
336     if( bHasRTL )
337     {
338         mpGlyphRTLFlags = new bool[ mnCharCount ];
339         for( i = 0; i < mnCharCount; ++i )
340             mpGlyphRTLFlags[i] = false;
341     }
342 
343     // rewrite the logical string if needed to prepare for the API calls
344     const sal_Unicode* pBidiStr = rArgs.mpStr + rArgs.mnMinCharPos;
345     if( (mnGlyphCount != mnCharCount) || bVertical )
346     {
347         // we need to rewrite the pBidiStr when any of
348         // - BiDirectional layout
349         // - vertical layout
350         // - partial runs (e.g. with control chars or for glyph fallback)
351         // are involved
352         sal_Unicode* pRewrittenStr = (sal_Unicode*)alloca( mnCharCount * sizeof(sal_Unicode) );
353         pBidiStr = pRewrittenStr;
354 
355         // note: glyph to char mapping is relative to first character
356         mpChars2Glyphs = new int[ mnCharCount ];
357         mpGlyphs2Chars = new int[ mnCharCount ];
358         for( i = 0; i < mnCharCount; ++i )
359             mpChars2Glyphs[i] = mpGlyphs2Chars[i] = -1;
360 
361         mnGlyphCount = 0;
362         rArgs.ResetPos();
363         bool bIsRTL = false;
364         while( rArgs.GetNextRun( &i, &j, &bIsRTL ) )
365         {
366             do
367             {
368                 // get the next leftmost character in this run
369                 int nCharPos = bIsRTL ? --j : i++;
370                 sal_UCS4 cChar = rArgs.mpStr[ nCharPos ];
371 
372                 // in the RTL case mirror the character and remember its RTL status
373                 if( bIsRTL )
374                 {
375                     cChar = ::GetMirroredChar( cChar );
376                     mpGlyphRTLFlags[ mnGlyphCount ] = true;
377                 }
378 
379                 // for vertical writing use vertical alternatives
380                 if( bVertical )
381                 {
382                     sal_UCS4 cVert = ::GetVerticalChar( cChar );
383                     if( cVert )
384                         cChar = cVert;
385                 }
386 
387                 // rewrite the original string
388                 // update the mappings between original and rewritten string
389 	       	// TODO: support surrogates in rewritten strings
390                 pRewrittenStr[ mnGlyphCount ] = static_cast<sal_Unicode>(cChar);
391                 mpGlyphs2Chars[ mnGlyphCount ] = nCharPos;
392                 mpChars2Glyphs[ nCharPos - rArgs.mnMinCharPos ] = mnGlyphCount;
393                 ++mnGlyphCount;
394             } while( i < j );
395         }
396     }
397 
398     mpOutGlyphs     = new WCHAR[ mnGlyphCount ];
399     mpGlyphAdvances = new int[ mnGlyphCount ];
400 
401     if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_PAIRS | SAL_LAYOUT_KERNING_ASIAN) )
402         mpGlyphOrigAdvs = new int[ mnGlyphCount ];
403 
404 #ifndef GCP_KERN_HACK
405     DWORD nGcpOption = 0;
406     // enable kerning if requested
407     if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS )
408         nGcpOption |= GCP_USEKERNING;
409 #endif // GCP_KERN_HACK
410 
411     for( i = 0; i < mnGlyphCount; ++i )
412         mpOutGlyphs[i] = pBidiStr[ i ];
413     mnWidth = 0;
414     for( i = 0; i < mnGlyphCount; ++i )
415     {
416         // get the current UCS-4 code point, check for surrogate pairs
417         const WCHAR* pCodes = reinterpret_cast<LPCWSTR>(&pBidiStr[i]);
418         unsigned nCharCode = pCodes[0];
419         bool bSurrogate = ((nCharCode >= 0xD800) && (nCharCode <= 0xDFFF));
420         if( bSurrogate )
421         {
422             if( nCharCode >= 0xDC00 ) // this part of a surrogate pair was already processed
423                 continue;
424             nCharCode = 0x10000 + ((pCodes[0] - 0xD800) << 10) + (pCodes[1] - 0xDC00);
425 	}
426 
427         // get the advance width for the current UCS-4 code point
428         int nGlyphWidth = mrWinFontEntry.GetCachedGlyphWidth( nCharCode );
429         if( nGlyphWidth == -1 )
430         {
431             ABC aABC;
432             SIZE aExtent;
433             if( ::GetTextExtentPoint32W( mhDC, &pCodes[0], bSurrogate ? 2 : 1, &aExtent) )
434                 nGlyphWidth = aExtent.cx;
435             else if( ::GetCharABCWidthsW( mhDC, nCharCode, nCharCode, &aABC ) )
436                 nGlyphWidth = aABC.abcA + aABC.abcB + aABC.abcC;
437             else if( !::GetCharWidth32W( mhDC, nCharCode, nCharCode, &nGlyphWidth )
438                  &&  !::GetCharWidthW( mhDC, nCharCode, nCharCode, &nGlyphWidth ) )
439                     nGlyphWidth = 0;
440             mrWinFontEntry.CacheGlyphWidth( nCharCode, nGlyphWidth );
441         }
442         mpGlyphAdvances[ i ] = nGlyphWidth;
443         mnWidth += nGlyphWidth;
444 
445         // remaining codes of surrogate pair get a zero width
446         if( bSurrogate && ((i+1) < mnGlyphCount) )
447             mpGlyphAdvances[ i+1 ] = 0;
448 
449         // check with the font face if glyph fallback is needed
450         if( mrWinFontData.HasChar( nCharCode ) )
451             continue;
452 
453         // request glyph fallback at this position in the string
454         bool bRTL = mpGlyphRTLFlags ? mpGlyphRTLFlags[i] : false;
455         int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[i]: i + rArgs.mnMinCharPos;
456         rArgs.NeedFallback( nCharPos, bRTL );
457         if( bSurrogate && ((nCharPos+1) < rArgs.mnLength) )
458             rArgs.NeedFallback( nCharPos+1, bRTL );
459 
460         // replace the current glyph shape with the NotDef glyph shape
461         if( rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK )
462         {
463             // when we already are layouting for glyph fallback
464             // then a new unresolved glyph is not interesting
465             mnNotdefWidth = 0;
466             mpOutGlyphs[i] = DROPPED_OUTGLYPH;
467         }
468         else
469         {
470             if( mnNotdefWidth < 0 )
471             {
472                 // get the width of the NotDef glyph
473                 SIZE aExtent;
474                 WCHAR cNotDef = rArgs.mpStr[ nCharPos ];
475                 mnNotdefWidth = 0;
476                 if( ::GetTextExtentPoint32W( mhDC, &cNotDef, 1, &aExtent) )
477                     mnNotdefWidth = aExtent.cx;
478             }
479             // use a better NotDef glyph
480             if( !mbDisableGlyphs && !bSurrogate )
481                 mpOutGlyphs[i] = 0;
482         }
483         if( bSurrogate && ((i+1) < mnGlyphCount) )
484             mpOutGlyphs[i+1] = DROPPED_OUTGLYPH;
485 
486         // adjust the current glyph width to the NotDef glyph width
487         mnWidth += mnNotdefWidth - mpGlyphAdvances[i];
488         mpGlyphAdvances[i] = mnNotdefWidth;
489         if( mpGlyphOrigAdvs )
490             mpGlyphOrigAdvs[i] = mnNotdefWidth;
491     }
492 
493 #ifdef GCP_KERN_HACK
494     // apply kerning if the layout engine has not yet done it
495     if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_ASIAN|SAL_LAYOUT_KERNING_PAIRS) )
496     {
497 #else // GCP_KERN_HACK
498     // apply just asian kerning
499     if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
500     {
501         if( !(rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) )
502 #endif // GCP_KERN_HACK
503             for( i = 0; i < mnGlyphCount; ++i )
504                 mpGlyphOrigAdvs[i] = mpGlyphAdvances[i];
505 
506         // #99658# also apply asian kerning on the substring border
507         int nLen = mnGlyphCount;
508         if( rArgs.mnMinCharPos + nLen < rArgs.mnLength )
509             ++nLen;
510         for( i = 1; i < nLen; ++i )
511         {
512 #ifdef GCP_KERN_HACK
513             if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS )
514             {
515                 int nKernAmount = mrWinFontEntry.GetKerning( pBidiStr[i-1], pBidiStr[i] );
516                 mpGlyphAdvances[ i-1 ] += nKernAmount;
517                 mnWidth += nKernAmount;
518             }
519             else if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
520 #endif // GCP_KERN_HACK
521 
522             if( ( (0x3000 == (0xFF00 & pBidiStr[i-1])) || (0x2010 == (0xFFF0 & pBidiStr[i-1])) || (0xFF00 == (0xFF00 & pBidiStr[i-1])))
523             &&  ( (0x3000 == (0xFF00 & pBidiStr[i])) || (0x2010 == (0xFFF0 & pBidiStr[i])) || (0xFF00 == (0xFF00 & pBidiStr[i])) ) )
524             {
525                 long nKernFirst = +CalcAsianKerning( pBidiStr[i-1], true, bVertical );
526                 long nKernNext  = -CalcAsianKerning( pBidiStr[i], false, bVertical );
527 
528                 long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
529                 if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
530                 {
531                     nDelta = (nDelta * mpGlyphAdvances[i-1] + 2) / 4;
532                     mpGlyphAdvances[i-1] += nDelta;
533                     mnWidth += nDelta;
534                 }
535             }
536         }
537     }
538 
539     // calculate virtual char widths
540     if( !mpGlyphs2Chars )
541         mpCharWidths = mpGlyphAdvances;
542     else
543     {
544         mpCharWidths = new int[ mnCharCount ];
545         for( i = 0; i < mnCharCount; ++i )
546             mpCharWidths[ i ] = 0;
547         for( i = 0; i < mnGlyphCount; ++i )
548         {
549             int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
550             if( j >= 0 )
551                 mpCharWidths[ j ] += mpGlyphAdvances[ i ];
552         }
553     }
554 
555     // scale layout metrics if needed
556 	// TODO: does it make the code more simple if the metric scaling
557 	// is moved to the methods that need metric scaling (e.g. FillDXArray())?
558     if( mfFontScale != 1.0 )
559     {
560         mnWidth   = (long)(mnWidth * mfFontScale);
561         mnBaseAdv = (int)(mnBaseAdv * mfFontScale);
562         for( i = 0; i < mnCharCount; ++i )
563             mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale);
564         if( mpGlyphAdvances != mpCharWidths )
565             for( i = 0; i < mnGlyphCount; ++i )
566                 mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale);
567         if( mpGlyphOrigAdvs && (mpGlyphOrigAdvs != mpGlyphAdvances) )
568             for( i = 0; i < mnGlyphCount; ++i )
569                 mpGlyphOrigAdvs[i] = (int)(mpGlyphOrigAdvs[i] * mfFontScale);
570     }
571 
572     return true;
573 }
574 
575 // -----------------------------------------------------------------------
576 
577 int SimpleWinLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int& nStart,
578     long* pGlyphAdvances, int* pCharIndexes ) const
579 {
580     // return zero if no more glyph found
581     if( nStart >= mnGlyphCount )
582         return 0;
583 
584     // calculate glyph position relative to layout base
585     // TODO: avoid for nStart!=0 case by reusing rPos
586     long nXOffset = mnBaseAdv;
587     for( int i = 0; i < nStart; ++i )
588         nXOffset += mpGlyphAdvances[ i ];
589 
590     // calculate absolute position in pixel units
591     Point aRelativePos( nXOffset, 0 );
592     rPos = GetDrawPosition( aRelativePos );
593 
594     int nCount = 0;
595     while( nCount < nLen )
596     {
597         // update return values {nGlyphIndex,nCharPos,nGlyphAdvance}
598         sal_GlyphId nGlyphIndex = mpOutGlyphs[ nStart ];
599         if( mbDisableGlyphs )
600         {
601             if( mnLayoutFlags & SAL_LAYOUT_VERTICAL )
602             {
603                 const sal_UCS4 cChar = static_cast<sal_UCS4>(nGlyphIndex & GF_IDXMASK);
604                 if( mrWinFontData.HasGSUBstitutions( mhDC )
605                 &&  mrWinFontData.IsGSUBstituted( cChar ) )
606                     nGlyphIndex |= GF_GSUB | GF_ROTL;
607                 else
608                 {
609                     nGlyphIndex |= GetVerticalFlags( cChar );
610                     if( (nGlyphIndex & GF_ROTMASK) == 0 )
611                         nGlyphIndex |= GF_VERT;
612                 }
613             }
614             nGlyphIndex |= GF_ISCHAR;
615         }
616         ++nCount;
617         *(pGlyphs++) = nGlyphIndex;
618         if( pGlyphAdvances )
619             *(pGlyphAdvances++) = mpGlyphAdvances[ nStart ];
620         if( pCharIndexes )
621         {
622             int nCharPos;
623             if( !mpGlyphs2Chars )
624                 nCharPos = nStart + mnMinCharPos;
625             else
626                 nCharPos = mpGlyphs2Chars[nStart];
627             *(pCharIndexes++) = nCharPos;
628         }
629 
630         // stop at last glyph
631         if( ++nStart >= mnGlyphCount )
632             break;
633 
634         // stop when next x-position is unexpected
635         if( !pGlyphAdvances && mpGlyphOrigAdvs )
636             if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] )
637                 break;
638     }
639 
640     return nCount;
641 }
642 
643 // -----------------------------------------------------------------------
644 
645 void SimpleWinLayout::DrawText( SalGraphics& rGraphics ) const
646 {
647     if( mnGlyphCount <= 0 )
648         return;
649 
650     WinSalGraphics& rWinGraphics = static_cast<WinSalGraphics&>(rGraphics);
651     HDC aHDC = rWinGraphics.mhDC;
652 
653     HFONT hOrigFont = DisableFontScaling();
654 
655     UINT mnDrawOptions = ETO_GLYPH_INDEX;
656     if( mbDisableGlyphs )
657         mnDrawOptions = 0;
658 
659     Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) );
660 
661     // #108267#, break up into glyph portions of a limited size required by Win32 API
662     const unsigned int maxGlyphCount = 8192;
663     UINT numGlyphPortions = mnGlyphCount / maxGlyphCount;
664     UINT remainingGlyphs = mnGlyphCount % maxGlyphCount;
665 
666     if( numGlyphPortions )
667     {
668         // #108267#,#109387# break up string into smaller chunks
669         // the output positions will be updated by windows (SetTextAlign)
670         POINT oldPos;
671         UINT oldTa = ::GetTextAlign( aHDC );
672         ::SetTextAlign( aHDC, (oldTa & ~TA_NOUPDATECP) | TA_UPDATECP );
673         ::MoveToEx( aHDC, aPos.X(), aPos.Y(), &oldPos );
674         unsigned int i = 0;
675         for( unsigned int n = 0; n < numGlyphPortions; ++n, i+=maxGlyphCount )
676             ::ExtTextOutW( aHDC, 0, 0, mnDrawOptions, NULL,
677                 mpOutGlyphs+i, maxGlyphCount, mpGlyphAdvances+i );
678         ::ExtTextOutW( aHDC, 0, 0, mnDrawOptions, NULL,
679             mpOutGlyphs+i, remainingGlyphs, mpGlyphAdvances+i );
680         ::MoveToEx( aHDC, oldPos.x, oldPos.y, (LPPOINT) NULL);
681         ::SetTextAlign( aHDC, oldTa );
682     }
683     else
684         ::ExtTextOutW( aHDC, aPos.X(), aPos.Y(), mnDrawOptions, NULL,
685             mpOutGlyphs, mnGlyphCount, mpGlyphAdvances );
686 
687     if( hOrigFont )
688         DeleteFont( SelectFont( aHDC, hOrigFont ) );
689 }
690 
691 // -----------------------------------------------------------------------
692 
693 long SimpleWinLayout::FillDXArray( long* pDXArray ) const
694 {
695     if( !mnWidth )
696     {
697         long mnWidth = mnBaseAdv;
698         for( int i = 0; i < mnGlyphCount; ++i )
699             mnWidth += mpGlyphAdvances[ i ];
700     }
701 
702     if( pDXArray != NULL )
703     {
704         for( int i = 0; i < mnCharCount; ++i )
705              pDXArray[ i ] = mpCharWidths[ i ];
706     }
707 
708     return mnWidth;
709 }
710 
711 // -----------------------------------------------------------------------
712 
713 int SimpleWinLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
714 // NOTE: the nFactor is used to prevent rounding errors for small nCharExtra values
715 {
716     if( mnWidth )
717         if( (mnWidth * nFactor + mnCharCount * nCharExtra) <= nMaxWidth )
718             return STRING_LEN;
719 
720     long nExtraWidth = mnBaseAdv * nFactor;
721     for( int n = 0; n < mnCharCount; ++n )
722     {
723         // skip unused characters
724         if( mpChars2Glyphs && (mpChars2Glyphs[n] < 0) )
725             continue;
726         // add char widths until max
727         nExtraWidth += mpCharWidths[ n ] * nFactor;
728         if( nExtraWidth >= nMaxWidth )
729             return (mnMinCharPos + n);
730         nExtraWidth += nCharExtra;
731     }
732 
733     return STRING_LEN;
734 }
735 
736 // -----------------------------------------------------------------------
737 
738 void SimpleWinLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const
739 {
740     long nXPos = mnBaseAdv;
741 
742     if( !mpGlyphs2Chars )
743     {
744         for( int i = 0; i < nMaxIdx; i += 2 )
745         {
746             pCaretXArray[ i ] = nXPos;
747             nXPos += mpGlyphAdvances[ i>>1 ];
748             pCaretXArray[ i+1 ] = nXPos;
749         }
750     }
751     else
752     {
753         int  i;
754         for( i = 0; i < nMaxIdx; ++i )
755             pCaretXArray[ i ] = -1;
756 
757         // assign glyph positions to character positions
758         for( i = 0; i < mnGlyphCount; ++i )
759         {
760             int nCurrIdx = mpGlyphs2Chars[ i ] - mnMinCharPos;
761             long nXRight = nXPos + mpCharWidths[ nCurrIdx ];
762             nCurrIdx *= 2;
763             if( !(mpGlyphRTLFlags && mpGlyphRTLFlags[i]) )
764             {
765                 // normal positions for LTR case
766                 pCaretXArray[ nCurrIdx ]   = nXPos;
767                 pCaretXArray[ nCurrIdx+1 ] = nXRight;
768             }
769             else
770             {
771                 // reverse positions for RTL case
772                 pCaretXArray[ nCurrIdx ]   = nXRight;
773                 pCaretXArray[ nCurrIdx+1 ] = nXPos;
774             }
775             nXPos += mpGlyphAdvances[ i ];
776         }
777     }
778 }
779 
780 // -----------------------------------------------------------------------
781 
782 void SimpleWinLayout::Justify( long nNewWidth )
783 {
784     long nOldWidth = mnWidth;
785     mnWidth = nNewWidth;
786 
787     if( mnGlyphCount <= 0 )
788         return;
789 
790     if( nNewWidth == nOldWidth )
791         return;
792 
793     // the rightmost glyph cannot be stretched
794     const int nRight = mnGlyphCount - 1;
795     nOldWidth -= mpGlyphAdvances[ nRight ];
796     nNewWidth -= mpGlyphAdvances[ nRight ];
797 
798     // count stretchable glyphs
799     int nStretchable = 0, i;
800     for( i = 0; i < nRight; ++i )
801         if( mpGlyphAdvances[i] >= 0 )
802             ++nStretchable;
803 
804     // stretch these glyphs
805     int nDiffWidth = nNewWidth - nOldWidth;
806     for( i = 0; (i < nRight) && (nStretchable > 0); ++i )
807     {
808         if( mpGlyphAdvances[i] <= 0 )
809             continue;
810         int nDeltaWidth = nDiffWidth / nStretchable;
811         mpGlyphAdvances[i] += nDeltaWidth;
812         --nStretchable;
813         nDiffWidth -= nDeltaWidth;
814     }
815 }
816 
817 // -----------------------------------------------------------------------
818 
819 void SimpleWinLayout::AdjustLayout( ImplLayoutArgs& rArgs )
820 {
821     SalLayout::AdjustLayout( rArgs );
822 
823     // adjust positions if requested
824     if( rArgs.mpDXArray )
825         ApplyDXArray( rArgs );
826     else if( rArgs.mnLayoutWidth )
827         Justify( rArgs.mnLayoutWidth );
828     else
829         return;
830 
831     // recalculate virtual char widths if they were changed
832     if( mpCharWidths != mpGlyphAdvances )
833     {
834         int i;
835         if( !mpGlyphs2Chars )
836         {
837             // standard LTR case
838             for( i = 0; i < mnGlyphCount; ++i )
839                  mpCharWidths[ i ] = mpGlyphAdvances[ i ];
840         }
841         else
842         {
843             // BiDi or complex case
844             for( i = 0; i < mnCharCount; ++i )
845                 mpCharWidths[ i ] = 0;
846             for( i = 0; i < mnGlyphCount; ++i )
847             {
848                 int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
849                 if( j >= 0 )
850                     mpCharWidths[ j ] += mpGlyphAdvances[ i ];
851             }
852         }
853     }
854 }
855 
856 // -----------------------------------------------------------------------
857 
858 void SimpleWinLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
859 {
860     // try to avoid disturbance of text flow for LSB rounding case;
861     const long* pDXArray = rArgs.mpDXArray;
862 
863     int i = 0;
864     long nOldWidth = mnBaseAdv;
865     for(; i < mnCharCount; ++i )
866     {
867         int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
868         if( j >= 0 )
869         {
870             nOldWidth += mpGlyphAdvances[ j ];
871             int nDiff = nOldWidth - pDXArray[ i ];
872 
873             // disabled because of #104768#
874             // works great for static text, but problems when typing
875             // if( nDiff>+1 || nDiff<-1 )
876             // only bother with changing anything when something moved
877             if( nDiff != 0 )
878                 break;
879         }
880     }
881     if( i >= mnCharCount )
882         return;
883 
884     if( !mpGlyphOrigAdvs )
885     {
886         mpGlyphOrigAdvs = new int[ mnGlyphCount ];
887         for( i = 0; i < mnGlyphCount; ++i )
888             mpGlyphOrigAdvs[ i ] = mpGlyphAdvances[ i ];
889     }
890 
891     mnWidth = mnBaseAdv;
892     for( i = 0; i < mnCharCount; ++i )
893     {
894         int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
895         if( j >= 0 )
896             mpGlyphAdvances[j] = pDXArray[i] - mnWidth;
897         mnWidth = pDXArray[i];
898     }
899 }
900 
901 // -----------------------------------------------------------------------
902 
903 void SimpleWinLayout::MoveGlyph( int nStart, long nNewXPos )
904 {
905    if( nStart > mnGlyphCount )
906         return;
907 
908     // calculate the current x-position of the requested glyph
909     // TODO: cache absolute positions
910     int nXPos = mnBaseAdv;
911     for( int i = 0; i < nStart; ++i )
912         nXPos += mpGlyphAdvances[i];
913 
914     // calculate the difference to the current glyph position
915     int nDelta = nNewXPos - nXPos;
916 
917     // adjust the width of the layout if it was already cached
918     if( mnWidth )
919         mnWidth += nDelta;
920 
921     // depending on whether the requested glyph is leftmost in the layout
922     // adjust either the layout's or the requested glyph's relative position
923     if( nStart > 0 )
924         mpGlyphAdvances[ nStart-1 ] += nDelta;
925     else
926         mnBaseAdv += nDelta;
927 }
928 
929 // -----------------------------------------------------------------------
930 
931 void SimpleWinLayout::DropGlyph( int nStart )
932 {
933     mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH;
934 }
935 
936 // -----------------------------------------------------------------------
937 
938 void SimpleWinLayout::Simplify( bool /*bIsBase*/ )
939 {
940     // return early if no glyph has been dropped
941     int i = mnGlyphCount;
942     while( (--i >= 0) && (mpOutGlyphs[ i ] != DROPPED_OUTGLYPH) );
943     if( i < 0 )
944         return;
945 
946     // convert the layout to a sparse layout if it is not already
947     if( !mpGlyphs2Chars )
948     {
949         mpGlyphs2Chars = new int[ mnGlyphCount ];
950         mpCharWidths = new int[ mnCharCount ];
951         // assertion: mnGlyphCount == mnCharCount
952         for( int k = 0; k < mnGlyphCount; ++k )
953         {
954             mpGlyphs2Chars[ k ] = mnMinCharPos + k;
955             mpCharWidths[ k ] = mpGlyphAdvances[ k ];
956         }
957     }
958 
959     // remove dropped glyphs that are rightmost in the layout
960     for( i = mnGlyphCount; --i >= 0; )
961     {
962         if( mpOutGlyphs[ i ] != DROPPED_OUTGLYPH )
963             break;
964         if( mnWidth )
965             mnWidth -= mpGlyphAdvances[ i ];
966         int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
967         if( nRelCharPos >= 0 )
968             mpCharWidths[ nRelCharPos ] = 0;
969     }
970     mnGlyphCount = i + 1;
971 
972     // keep original glyph widths around
973     if( !mpGlyphOrigAdvs )
974     {
975         mpGlyphOrigAdvs = new int[ mnGlyphCount ];
976         for( int k = 0; k < mnGlyphCount; ++k )
977             mpGlyphOrigAdvs[ k ] = mpGlyphAdvances[ k ];
978     }
979 
980     // remove dropped glyphs inside the layout
981     int nNewGC = 0;
982     for( i = 0; i < mnGlyphCount; ++i )
983     {
984         if( mpOutGlyphs[ i ] == DROPPED_OUTGLYPH )
985         {
986             // adjust relative position to last valid glyph
987             int nDroppedWidth = mpGlyphAdvances[ i ];
988             mpGlyphAdvances[ i ] = 0;
989             if( nNewGC > 0 )
990                 mpGlyphAdvances[ nNewGC-1 ] += nDroppedWidth;
991             else
992                 mnBaseAdv += nDroppedWidth;
993 
994             // zero the virtual char width for the char that has a fallback
995             int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
996             if( nRelCharPos >= 0 )
997                 mpCharWidths[ nRelCharPos ] = 0;
998         }
999         else
1000         {
1001             if( nNewGC != i )
1002             {
1003                 // rearrange the glyph array to get rid of the dropped glyph
1004                 mpOutGlyphs[ nNewGC ]     = mpOutGlyphs[ i ];
1005                 mpGlyphAdvances[ nNewGC ] = mpGlyphAdvances[ i ];
1006                 mpGlyphOrigAdvs[ nNewGC ] = mpGlyphOrigAdvs[ i ];
1007                 mpGlyphs2Chars[ nNewGC ]  = mpGlyphs2Chars[ i ];
1008             }
1009             ++nNewGC;
1010         }
1011     }
1012 
1013     mnGlyphCount = nNewGC;
1014     if( mnGlyphCount <= 0 )
1015         mnWidth = mnBaseAdv = 0;
1016 }
1017 
1018 // =======================================================================
1019 
1020 #ifdef USE_UNISCRIBE
1021 
1022 struct VisualItem
1023 {
1024 public:
1025     SCRIPT_ITEM*    mpScriptItem;
1026     int             mnMinGlyphPos;
1027     int             mnEndGlyphPos;
1028     int             mnMinCharPos;
1029     int             mnEndCharPos;
1030     //long          mnPixelWidth;
1031     int             mnXOffset;
1032     ABC             maABCWidths;
1033     bool            mbHasKashidas;
1034 
1035 public:
1036     bool            IsEmpty() const { return (mnEndGlyphPos <= 0); }
1037     bool            IsRTL() const { return mpScriptItem->a.fRTL; }
1038     bool            HasKashidas() const { return mbHasKashidas; }
1039 };
1040 
1041 // -----------------------------------------------------------------------
1042 
1043 class UniscribeLayout : public WinLayout
1044 {
1045 public:
1046                     UniscribeLayout( HDC, const ImplWinFontData&, ImplWinFontEntry& );
1047 
1048     virtual bool    LayoutText( ImplLayoutArgs& );
1049     virtual void    AdjustLayout( ImplLayoutArgs& );
1050     virtual void    DrawText( SalGraphics& ) const;
1051     virtual int     GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&,
1052                         sal_Int32* pGlyphAdvances, int* pCharPosAry ) const;
1053 
1054     virtual long    FillDXArray( long* pDXArray ) const;
1055     virtual int     GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
1056     virtual void    GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
1057     virtual bool    IsKashidaPosValid ( int nCharPos ) const;
1058 
1059     // for glyph+font+script fallback
1060     virtual void    MoveGlyph( int nStart, long nNewXPos );
1061     virtual void    DropGlyph( int nStart );
1062     virtual void    Simplify( bool bIsBase );
1063     virtual void    DisableGlyphInjection( bool bDisable ) { mbDisableGlyphInjection = bDisable; }
1064 
1065 protected:
1066     virtual         ~UniscribeLayout();
1067 
1068     void            Justify( long nNewWidth );
1069     void            ApplyDXArray( const ImplLayoutArgs& );
1070 
1071     bool            GetItemSubrange( const VisualItem&,
1072                         int& rMinIndex, int& rEndIndex ) const;
1073 
1074 private:
1075     // item specific info
1076     SCRIPT_ITEM*    mpScriptItems;      // in logical order
1077     VisualItem*     mpVisualItems;      // in visual order
1078     int             mnItemCount;        // number of visual items
1079 
1080     // string specific info
1081     // everything is in logical order
1082     int             mnCharCapacity;
1083     WORD*           mpLogClusters;      // map from absolute_char_pos to relative_glyph_pos
1084     int*            mpCharWidths;       // map from absolute_char_pos to char_width
1085     int             mnSubStringMin;     // char_pos of first char in context
1086 
1087     // glyph specific info
1088     // everything is in visual order
1089     int             mnGlyphCount;
1090     int             mnGlyphCapacity;
1091     int*            mpGlyphAdvances;    // glyph advance width before justification
1092     int*            mpJustifications;   // glyph advance width after justification
1093     WORD*           mpOutGlyphs;        // glyphids in visual order
1094     GOFFSET*        mpGlyphOffsets;     // glyph offsets to the "naive" layout
1095     SCRIPT_VISATTR* mpVisualAttrs;      // glyph visual attributes
1096     mutable int*    mpGlyphs2Chars;     // map from absolute_glyph_pos to absolute_char_pos
1097 
1098     // kashida stuff
1099     void InitKashidaHandling();
1100     void KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos );
1101     bool KashidaWordFix( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos );
1102 
1103     int            mnMinKashidaWidth;
1104     int            mnMinKashidaGlyph;
1105     bool           mbDisableGlyphInjection;
1106 };
1107 
1108 // -----------------------------------------------------------------------
1109 // dynamic loading of usp library
1110 
1111 static oslModule aUspModule = NULL;
1112 static bool bUspEnabled = true;
1113 
1114 static HRESULT ((WINAPI *pScriptIsComplex)( const WCHAR*, int, DWORD ));
1115 static HRESULT ((WINAPI *pScriptItemize)( const WCHAR*, int, int,
1116     const SCRIPT_CONTROL*, const SCRIPT_STATE*, SCRIPT_ITEM*, int* ));
1117 static HRESULT ((WINAPI *pScriptShape)( HDC, SCRIPT_CACHE*, const WCHAR*,
1118     int, int, SCRIPT_ANALYSIS*, WORD*, WORD*, SCRIPT_VISATTR*, int* ));
1119 static HRESULT ((WINAPI *pScriptPlace)( HDC, SCRIPT_CACHE*, const WORD*, int,
1120     const SCRIPT_VISATTR*, SCRIPT_ANALYSIS*, int*, GOFFSET*, ABC* ));
1121 static HRESULT ((WINAPI *pScriptGetLogicalWidths)( const SCRIPT_ANALYSIS*,
1122     int, int, const int*, const WORD*, const SCRIPT_VISATTR*, int* ));
1123 static HRESULT ((WINAPI *pScriptApplyLogicalWidth)( const int*, int, int, const WORD*,
1124     const SCRIPT_VISATTR*, const int*, const SCRIPT_ANALYSIS*, ABC*, int* ));
1125 static HRESULT ((WINAPI *pScriptJustify)( const SCRIPT_VISATTR*,
1126     const int*, int, int, int, int* ));
1127 static HRESULT ((WINAPI *pScriptTextOut)( const HDC, SCRIPT_CACHE*,
1128     int, int, UINT, const RECT*, const SCRIPT_ANALYSIS*, const WCHAR*,
1129     int, const WORD*, int, const int*, const int*, const GOFFSET* ));
1130 static HRESULT ((WINAPI *pScriptGetFontProperties)( HDC, SCRIPT_CACHE*, SCRIPT_FONTPROPERTIES* ));
1131 static HRESULT ((WINAPI *pScriptFreeCache)( SCRIPT_CACHE* ));
1132 
1133 static bool bManualCellAlign = true;
1134 
1135 // -----------------------------------------------------------------------
1136 
1137 static bool InitUSP()
1138 {
1139     OUString aLibraryName( RTL_CONSTASCII_USTRINGPARAM( "usp10" ) );
1140     aUspModule = osl_loadModule( aLibraryName.pData, SAL_LOADMODULE_DEFAULT );
1141     if( !aUspModule )
1142         return (bUspEnabled = false);
1143 
1144     pScriptIsComplex = (HRESULT (WINAPI*)(const WCHAR*,int,DWORD))
1145         osl_getAsciiFunctionSymbol( aUspModule, "ScriptIsComplex" );
1146     bUspEnabled &= (NULL != pScriptIsComplex);
1147 
1148     pScriptItemize = (HRESULT (WINAPI*)(const WCHAR*,int,int,
1149         const SCRIPT_CONTROL*,const SCRIPT_STATE*,SCRIPT_ITEM*,int*))
1150         osl_getAsciiFunctionSymbol( aUspModule, "ScriptItemize" );
1151     bUspEnabled &= (NULL != pScriptItemize);
1152 
1153     pScriptShape = (HRESULT (WINAPI*)(HDC,SCRIPT_CACHE*,const WCHAR*,
1154         int,int,SCRIPT_ANALYSIS*,WORD*,WORD*,SCRIPT_VISATTR*,int*))
1155         osl_getAsciiFunctionSymbol( aUspModule, "ScriptShape" );
1156     bUspEnabled &= (NULL != pScriptShape);
1157 
1158     pScriptPlace = (HRESULT (WINAPI*)(HDC, SCRIPT_CACHE*, const WORD*, int,
1159         const SCRIPT_VISATTR*,SCRIPT_ANALYSIS*,int*,GOFFSET*,ABC*))
1160         osl_getAsciiFunctionSymbol( aUspModule, "ScriptPlace" );
1161     bUspEnabled &= (NULL != pScriptPlace);
1162 
1163     pScriptGetLogicalWidths = (HRESULT (WINAPI*)(const SCRIPT_ANALYSIS*,
1164         int,int,const int*,const WORD*,const SCRIPT_VISATTR*,int*))
1165         osl_getAsciiFunctionSymbol( aUspModule, "ScriptGetLogicalWidths" );
1166     bUspEnabled &= (NULL != pScriptGetLogicalWidths);
1167 
1168     pScriptApplyLogicalWidth = (HRESULT (WINAPI*)(const int*,int,int,const WORD*,
1169         const SCRIPT_VISATTR*,const int*,const SCRIPT_ANALYSIS*,ABC*,int*))
1170         osl_getAsciiFunctionSymbol( aUspModule, "ScriptApplyLogicalWidth" );
1171     bUspEnabled &= (NULL != pScriptApplyLogicalWidth);
1172 
1173     pScriptJustify = (HRESULT (WINAPI*)(const SCRIPT_VISATTR*,const int*,
1174         int,int,int,int*))
1175         osl_getAsciiFunctionSymbol( aUspModule, "ScriptJustify" );
1176     bUspEnabled &= (NULL != pScriptJustify);
1177 
1178     pScriptGetFontProperties = (HRESULT (WINAPI*)( HDC,SCRIPT_CACHE*,SCRIPT_FONTPROPERTIES*))
1179         osl_getAsciiFunctionSymbol( aUspModule, "ScriptGetFontProperties" );
1180     bUspEnabled &= (NULL != pScriptGetFontProperties);
1181 
1182     pScriptTextOut = (HRESULT (WINAPI*)(const HDC,SCRIPT_CACHE*,
1183         int,int,UINT,const RECT*,const SCRIPT_ANALYSIS*,const WCHAR*,
1184         int,const WORD*,int,const int*,const int*,const GOFFSET*))
1185         osl_getAsciiFunctionSymbol( aUspModule, "ScriptTextOut" );
1186     bUspEnabled &= (NULL != pScriptTextOut);
1187 
1188     pScriptFreeCache = (HRESULT (WINAPI*)(SCRIPT_CACHE*))
1189         osl_getAsciiFunctionSymbol( aUspModule, "ScriptFreeCache" );
1190     bUspEnabled &= (NULL != pScriptFreeCache);
1191 
1192     if( !bUspEnabled )
1193     {
1194         osl_unloadModule( aUspModule );
1195         aUspModule = NULL;
1196     }
1197 
1198 	// get the DLL version info
1199 	int nUspVersion = 0;
1200 	// TODO: there must be a simpler way to get the friggin version info from OSL?
1201 	rtl_uString* pModuleURL = NULL;
1202 	osl_getModuleURLFromAddress( (void*)pScriptIsComplex, &pModuleURL );
1203 	rtl_uString* pModuleFileName = NULL;
1204 	if( pModuleURL )
1205 		osl_getSystemPathFromFileURL( pModuleURL, &pModuleFileName );
1206 	const sal_Unicode* pModuleFileCStr = NULL;
1207 	if( pModuleFileName )
1208 		pModuleFileCStr = rtl_uString_getStr( pModuleFileName );
1209 	if( pModuleFileCStr )
1210 	{
1211 		DWORD nHandle;
1212 		DWORD nBufSize = ::GetFileVersionInfoSizeW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), &nHandle );
1213 		char* pBuffer = (char*)alloca( nBufSize );
1214 		BOOL bRC = ::GetFileVersionInfoW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), nHandle, nBufSize, pBuffer );
1215 		VS_FIXEDFILEINFO* pFixedFileInfo = NULL;
1216 		UINT nFixedFileSize = 0;
1217 		if( bRC )
1218 			::VerQueryValueW( pBuffer, const_cast<LPWSTR>(L"\\"), (void**)&pFixedFileInfo, &nFixedFileSize );
1219 		if( pFixedFileInfo && pFixedFileInfo->dwSignature == 0xFEEF04BD )
1220 			nUspVersion = HIWORD(pFixedFileInfo->dwProductVersionMS) * 10000
1221 						+ LOWORD(pFixedFileInfo->dwProductVersionMS);
1222 	}
1223 
1224 	// #i77976# USP>=1.0600 changed the need to manually align glyphs in their cells
1225 	if( nUspVersion >= 10600 )
1226 		bManualCellAlign = false;
1227 
1228     return bUspEnabled;
1229 }
1230 
1231 // -----------------------------------------------------------------------
1232 
1233 UniscribeLayout::UniscribeLayout( HDC hDC,
1234     const ImplWinFontData& rWinFontData, ImplWinFontEntry& rWinFontEntry )
1235 :   WinLayout( hDC, rWinFontData, rWinFontEntry ),
1236     mnItemCount( 0 ),
1237     mpScriptItems( NULL ),
1238     mpVisualItems( NULL ),
1239     mpLogClusters( NULL ),
1240     mpCharWidths( NULL ),
1241     mnCharCapacity( 0 ),
1242     mnSubStringMin( 0 ),
1243     mnGlyphCapacity( 0 ),
1244     mnGlyphCount( 0 ),
1245     mpOutGlyphs( NULL ),
1246     mpGlyphAdvances( NULL ),
1247     mpJustifications( NULL ),
1248     mpGlyphOffsets( NULL ),
1249     mpVisualAttrs( NULL ),
1250     mpGlyphs2Chars( NULL ),
1251     mnMinKashidaGlyph( 0 ),
1252     mbDisableGlyphInjection( false )
1253 {}
1254 
1255 // -----------------------------------------------------------------------
1256 
1257 UniscribeLayout::~UniscribeLayout()
1258 {
1259     delete[] mpScriptItems;
1260     delete[] mpVisualItems;
1261     delete[] mpLogClusters;
1262     delete[] mpCharWidths;
1263     delete[] mpOutGlyphs;
1264     delete[] mpGlyphAdvances;
1265     delete[] mpJustifications;
1266     delete[] mpGlyphOffsets;
1267     delete[] mpVisualAttrs;
1268     delete[] mpGlyphs2Chars;
1269 }
1270 
1271 // -----------------------------------------------------------------------
1272 
1273 bool UniscribeLayout::LayoutText( ImplLayoutArgs& rArgs )
1274 {
1275     // for a base layout only the context glyphs have to be dropped
1276     // => when the whole string is involved there is no extra context
1277     typedef std::vector<int> TIntVector;
1278     TIntVector aDropChars;
1279     if( rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK )
1280     {
1281         // calculate superfluous context char positions
1282         aDropChars.push_back( 0 );
1283         aDropChars.push_back( rArgs.mnLength );
1284         int nMin, nEnd;
1285         bool bRTL;
1286         for( rArgs.ResetPos(); rArgs.GetNextRun( &nMin, &nEnd, &bRTL ); )
1287         {
1288             aDropChars.push_back( nMin );
1289             aDropChars.push_back( nEnd );
1290         }
1291         // prepare aDropChars for binary search which will allow to
1292         // not bother with visual items that will be dropped anyway
1293         std::sort( aDropChars.begin(), aDropChars.end() );
1294     }
1295 
1296     // prepare layout
1297     // TODO: fix case when recyclying old UniscribeLayout object
1298     mnMinCharPos = rArgs.mnMinCharPos;
1299     mnEndCharPos = rArgs.mnEndCharPos;
1300 
1301     // determine script items from string
1302 
1303     // prepare itemization
1304     // TODO: try to avoid itemization since it costs a lot of performance
1305     SCRIPT_STATE aScriptState = {0,false,false,false,false,false,false,false,false,0,0};
1306     aScriptState.uBidiLevel         = (0 != (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL));
1307     aScriptState.fOverrideDirection = (0 != (rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG));
1308     aScriptState.fDigitSubstitute   = (0 != (rArgs.mnFlags & SAL_LAYOUT_SUBSTITUTE_DIGITS));
1309     aScriptState.fArabicNumContext  = aScriptState.fDigitSubstitute & aScriptState.uBidiLevel;
1310     DWORD nLangId = 0;  // TODO: get language from font
1311     SCRIPT_CONTROL aScriptControl = {nLangId,false,false,false,false,false,false,false,false,0};
1312     aScriptControl.fNeutralOverride = aScriptState.fOverrideDirection;
1313     aScriptControl.fContextDigits   = (0 != (rArgs.mnFlags & SAL_LAYOUT_SUBSTITUTE_DIGITS));
1314     // determine relevant substring and work only on it
1315     // when Bidi status is unknown we need to look at the whole string though
1316     mnSubStringMin = 0;
1317     int nSubStringEnd = rArgs.mnLength;
1318     if( aScriptState.fOverrideDirection )
1319     {
1320         // TODO: limit substring to portion limits
1321         mnSubStringMin = rArgs.mnMinCharPos - 8;
1322         if( mnSubStringMin < 0 )
1323             mnSubStringMin = 0;
1324         nSubStringEnd = rArgs.mnEndCharPos + 8;
1325         if( nSubStringEnd > rArgs.mnLength )
1326             nSubStringEnd = rArgs.mnLength;
1327 
1328     }
1329 
1330     // now itemize the substring with its context
1331     for( int nItemCapacity = 16;; nItemCapacity *= 8 )
1332     {
1333         mpScriptItems = new SCRIPT_ITEM[ nItemCapacity ];
1334         HRESULT nRC = (*pScriptItemize)(
1335             reinterpret_cast<LPCWSTR>(rArgs.mpStr + mnSubStringMin), nSubStringEnd - mnSubStringMin,
1336             nItemCapacity - 1, &aScriptControl, &aScriptState,
1337             mpScriptItems, &mnItemCount );
1338         if( !nRC )  // break loop when everything is correctly itemized
1339             break;
1340 
1341         // prepare bigger buffers for another itemization round
1342         delete[] mpScriptItems;
1343         mpScriptItems = NULL;
1344         if( nRC != E_OUTOFMEMORY )
1345             return false;
1346         if( nItemCapacity > (nSubStringEnd - mnSubStringMin) + 16 )
1347             return false;
1348     }
1349 
1350     // calculate the order of visual items
1351     int nItem, i;
1352 
1353     // adjust char positions by substring offset
1354     for( nItem = 0; nItem <= mnItemCount; ++nItem )
1355         mpScriptItems[ nItem ].iCharPos += mnSubStringMin;
1356     // default visual item ordering
1357     mpVisualItems = new VisualItem[ mnItemCount ];
1358     for( nItem = 0; nItem < mnItemCount; ++nItem )
1359     {
1360         // initialize char specific item info
1361         VisualItem& rVisualItem = mpVisualItems[ nItem ];
1362         SCRIPT_ITEM* pScriptItem = &mpScriptItems[ nItem ];
1363         rVisualItem.mpScriptItem = pScriptItem;
1364         rVisualItem.mnMinCharPos = pScriptItem[0].iCharPos;
1365         rVisualItem.mnEndCharPos = pScriptItem[1].iCharPos;
1366     }
1367 
1368     // reorder visual item order if needed
1369     if( rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG )
1370     {
1371         // force RTL item ordering if requested
1372         if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL )
1373         {
1374             VisualItem* pVI0 = &mpVisualItems[ 0 ];
1375             VisualItem* pVI1 = &mpVisualItems[ mnItemCount ];
1376             while( pVI0 < --pVI1 )
1377             {
1378                 VisualItem aVtmp = *pVI0;
1379                 *(pVI0++) = *pVI1;
1380                 *pVI1 = aVtmp;
1381             }
1382         }
1383     }
1384     else if( mnItemCount > 1 )
1385     {
1386         // apply bidi algorithm's rule L2 on item level
1387         // TODO: use faster L2 algorithm
1388         int nMaxBidiLevel = 0;
1389         VisualItem* pVI = &mpVisualItems[0];
1390         VisualItem* const pVIend = pVI + mnItemCount;
1391         for(; pVI < pVIend; ++pVI )
1392             if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel )
1393                 nMaxBidiLevel = pVI->mpScriptItem->a.s.uBidiLevel;
1394 
1395         while( --nMaxBidiLevel >= 0 )
1396         {
1397             for( pVI = &mpVisualItems[0]; pVI < pVIend; )
1398             {
1399                 // find item range that needs reordering
1400                 for(; pVI < pVIend; ++pVI )
1401                     if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel )
1402                         break;
1403                 VisualItem* pVImin = pVI++;
1404                 for(; pVI < pVIend; ++pVI )
1405                     if( nMaxBidiLevel >= pVI->mpScriptItem->a.s.uBidiLevel )
1406                         break;
1407                 VisualItem* pVImax = pVI++;
1408 
1409                 // reverse order of items in this range
1410                 while( pVImin < --pVImax )
1411                 {
1412                     VisualItem aVtmp = *pVImin;
1413                     *(pVImin++) = *pVImax;
1414                     *pVImax = aVtmp;
1415                 }
1416             }
1417         }
1418     }
1419 
1420     // allocate arrays
1421     // TODO: when reusing object reuse old allocations or delete them
1422     // TODO: use only [nSubStringMin..nSubStringEnd) instead of [0..nSubStringEnd)
1423     mnCharCapacity  = nSubStringEnd;
1424     mpLogClusters   = new WORD[ mnCharCapacity ];
1425     mpCharWidths    = new int[ mnCharCapacity ];
1426 
1427     mnGlyphCount    = 0;
1428     mnGlyphCapacity = 16 + 4 * (nSubStringEnd - mnSubStringMin); // worst case assumption
1429     mpGlyphAdvances = new int[ mnGlyphCapacity ];
1430     mpOutGlyphs     = new WORD[ mnGlyphCapacity ];
1431     mpGlyphOffsets  = new GOFFSET[ mnGlyphCapacity ];
1432     mpVisualAttrs   = new SCRIPT_VISATTR[ mnGlyphCapacity ];
1433 
1434     long nXOffset = 0;
1435     for( int j = mnSubStringMin; j < nSubStringEnd; ++j )
1436         mpCharWidths[j] = 0;
1437 
1438     // layout script items
1439     SCRIPT_CACHE& rScriptCache = GetScriptCache();
1440     for( nItem = 0; nItem < mnItemCount; ++nItem )
1441     {
1442         VisualItem& rVisualItem = mpVisualItems[ nItem ];
1443 
1444         // initialize glyph specific item info
1445         rVisualItem.mnMinGlyphPos = mnGlyphCount;
1446         rVisualItem.mnEndGlyphPos = 0;
1447         rVisualItem.mnXOffset     = nXOffset;
1448         //rVisualItem.mnPixelWidth  = 0;
1449 
1450         // shortcut ignorable items
1451         if( (rArgs.mnEndCharPos <= rVisualItem.mnMinCharPos)
1452          || (rArgs.mnMinCharPos >= rVisualItem.mnEndCharPos) )
1453         {
1454             for( int i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i )
1455                 mpLogClusters[i] = sal::static_int_cast<WORD>(~0U);
1456             continue;
1457         }
1458 
1459         // override bidi analysis if requested
1460         if( rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG )
1461         {
1462             // FIXME: is this intended ?
1463             rVisualItem.mpScriptItem->a.fRTL                 = (aScriptState.uBidiLevel & 1);
1464             rVisualItem.mpScriptItem->a.s.uBidiLevel         = aScriptState.uBidiLevel;
1465             rVisualItem.mpScriptItem->a.s.fOverrideDirection = aScriptState.fOverrideDirection;
1466         }
1467 
1468         // convert the unicodes to glyphs
1469         int nGlyphCount = 0;
1470         int nCharCount = rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos;
1471         HRESULT nRC = (*pScriptShape)( mhDC, &rScriptCache,
1472             reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos),
1473             nCharCount,
1474             mnGlyphCapacity - rVisualItem.mnMinGlyphPos, // problem when >0xFFFF
1475             &rVisualItem.mpScriptItem->a,
1476             mpOutGlyphs + rVisualItem.mnMinGlyphPos,
1477             mpLogClusters + rVisualItem.mnMinCharPos,
1478             mpVisualAttrs + rVisualItem.mnMinGlyphPos,
1479             &nGlyphCount );
1480 
1481         // find and handle problems in the unicode to glyph conversion
1482         if( nRC == USP_E_SCRIPT_NOT_IN_FONT )
1483         {
1484             // the whole visual item needs a fallback, but make sure that the next
1485             // fallback request is limited to the characters in the original request
1486             // => this is handled in ImplLayoutArgs::PrepareFallback()
1487             rArgs.NeedFallback( rVisualItem.mnMinCharPos, rVisualItem.mnEndCharPos,
1488                 rVisualItem.IsRTL() );
1489 
1490             // don't bother to do a default layout in a fallback level
1491             if( 0 != (rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK) )
1492                 continue;
1493 
1494             // the primitive layout engine is good enough for the default layout
1495             rVisualItem.mpScriptItem->a.eScript = SCRIPT_UNDEFINED;
1496             nRC = (*pScriptShape)( mhDC, &rScriptCache,
1497                 reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos),
1498                 nCharCount,
1499                 mnGlyphCapacity - rVisualItem.mnMinGlyphPos,
1500                 &rVisualItem.mpScriptItem->a,
1501                 mpOutGlyphs + rVisualItem.mnMinGlyphPos,
1502                 mpLogClusters + rVisualItem.mnMinCharPos,
1503                 mpVisualAttrs + rVisualItem.mnMinGlyphPos,
1504                 &nGlyphCount );
1505 
1506             if( nRC != 0 )
1507                 continue;
1508 
1509 #if 0       // keep the glyphs for now because they are better than nothing
1510             // mark as NotDef glyphs
1511             for( i = 0; i < nGlyphCount; ++i )
1512                 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 0;
1513 #endif
1514         }
1515         else if( nRC != 0 )
1516             // something undefined happened => give up for this visual item
1517             continue;
1518         else // if( nRC == 0 )
1519         {
1520             // check if there are any NotDef glyphs
1521             for( i = 0; i < nGlyphCount; ++i )
1522                 if( 0 == mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] )
1523                     break;
1524             if( i < nGlyphCount )
1525             {
1526                 // clip charpos limits to the layout string without context
1527                 int nMinCharPos = rVisualItem.mnMinCharPos;
1528                 if( nMinCharPos < rArgs.mnMinCharPos )
1529                     nMinCharPos = rArgs.mnMinCharPos;
1530                 int nEndCharPos = rVisualItem.mnEndCharPos;
1531                 if( nEndCharPos > rArgs.mnEndCharPos )
1532                     nEndCharPos = rArgs.mnEndCharPos;
1533                 // request fallback for individual NotDef glyphs
1534                 do
1535                 {
1536                     // ignore non-NotDef glyphs
1537                     if( 0 != mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] )
1538                         continue;
1539                     mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = DROPPED_OUTGLYPH;
1540                     // request fallback for the whole cell that resulted in a NotDef glyph
1541                     // TODO: optimize algorithm
1542                     const bool bRTL = rVisualItem.IsRTL();
1543                     if( !bRTL )
1544                     {
1545                         // request fallback for the left-to-right cell
1546                         for( int c = nMinCharPos; c < nEndCharPos; ++c )
1547                         {
1548                             if( mpLogClusters[ c ] == i )
1549                             {
1550                                 // --> HDU/FME 2005-10-25 #i55716# skip WORDJOINER
1551                                 if( rArgs.mpStr[ c ] == 0x2060 )
1552                                     mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1;
1553                                 else
1554                                 // <--
1555                                     rArgs.NeedFallback( c, false );
1556                            }
1557                         }
1558                     }
1559                     else
1560                     {
1561                         // request fallback for the right to left cell
1562                         for( int c = nEndCharPos; --c >= nMinCharPos; )
1563                         {
1564                             if( mpLogClusters[ c ] == i )
1565                             {
1566                                 // --> HDU/FME 2005-10-25 #i55716# skip WORDJOINER
1567                                 if( rArgs.mpStr[ c ] == 0x2060 )
1568                                     mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1;
1569                                 else
1570                                 // <--
1571                                     rArgs.NeedFallback( c, true );
1572                             }
1573                         }
1574                     }
1575                 } while( ++i < nGlyphCount );
1576             }
1577         }
1578 
1579         // now place the glyphs
1580         nRC = (*pScriptPlace)( mhDC, &rScriptCache,
1581             mpOutGlyphs + rVisualItem.mnMinGlyphPos,
1582             nGlyphCount,
1583             mpVisualAttrs + rVisualItem.mnMinGlyphPos,
1584             &rVisualItem.mpScriptItem->a,
1585             mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
1586             mpGlyphOffsets + rVisualItem.mnMinGlyphPos,
1587             &rVisualItem.maABCWidths );
1588 
1589         if( nRC != 0 )
1590             continue;
1591 
1592         // calculate the logical char widths from the glyph layout
1593         nRC = (*pScriptGetLogicalWidths)(
1594             &rVisualItem.mpScriptItem->a,
1595             nCharCount, nGlyphCount,
1596             mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
1597             mpLogClusters + rVisualItem.mnMinCharPos,
1598             mpVisualAttrs + rVisualItem.mnMinGlyphPos,
1599             mpCharWidths + rVisualItem.mnMinCharPos );
1600 
1601         // update the glyph counters
1602         mnGlyphCount += nGlyphCount;
1603         rVisualItem.mnEndGlyphPos = mnGlyphCount;
1604 
1605         // update nXOffset
1606         int nEndGlyphPos;
1607         if( GetItemSubrange( rVisualItem, i, nEndGlyphPos ) )
1608             for(; i < nEndGlyphPos; ++i )
1609                 nXOffset += mpGlyphAdvances[ i ];
1610 
1611         // TODO: shrink glyphpos limits to match charpos/fallback limits
1612         //pVI->mnMinGlyphPos = nMinGlyphPos;
1613         //pVI->mnEndGlyphPos = nEndGlyphPos;
1614 
1615         // drop the superfluous context glyphs
1616         TIntVector::const_iterator it = aDropChars.begin();
1617         while( it != aDropChars.end() )
1618         {
1619             // find matching "drop range"
1620             int nMinDropPos = *(it++); // begin of drop range
1621             if( nMinDropPos >= rVisualItem.mnEndCharPos )
1622                 break;
1623             int nEndDropPos = *(it++); // end of drop range
1624             if( nEndDropPos <= rVisualItem.mnMinCharPos )
1625                 continue;
1626             // clip "drop range" to visual item's char range
1627             if( nMinDropPos <= rVisualItem.mnMinCharPos )
1628             {
1629                 nMinDropPos = rVisualItem.mnMinCharPos;
1630                 // drop the whole visual item if possible
1631                 if( nEndDropPos >= rVisualItem.mnEndCharPos )
1632                 {
1633                     rVisualItem.mnEndGlyphPos = 0;
1634                     break;
1635                 }
1636             }
1637             if( nEndDropPos > rVisualItem.mnEndCharPos )
1638                 nEndDropPos = rVisualItem.mnEndCharPos;
1639 
1640             // drop the glyphs which correspond to the charpos range
1641             // drop the corresponding glyphs in the cluster
1642             for( int c = nMinDropPos; c < nEndDropPos; ++c )
1643             {
1644                 int nGlyphPos = mpLogClusters[c] + rVisualItem.mnMinGlyphPos;
1645                 // no need to bother when the cluster was already dropped
1646                 if( mpOutGlyphs[ nGlyphPos ] != DROPPED_OUTGLYPH )
1647                 {
1648                     for(;;)
1649                     {
1650                         mpOutGlyphs[ nGlyphPos ] = DROPPED_OUTGLYPH;
1651                         // until the end of visual item
1652                         if( ++nGlyphPos >= rVisualItem.mnEndGlyphPos )
1653                             break;
1654                         // until the next cluster start
1655                         if( mpVisualAttrs[ nGlyphPos ].fClusterStart )
1656                             break;
1657                     }
1658                 }
1659             }
1660         }
1661     }
1662 
1663     // scale layout metrics if needed
1664 	// TODO: does it make the code more simple if the metric scaling
1665 	// is moved to the methods that need metric scaling (e.g. FillDXArray())?
1666     if( mfFontScale != 1.0 )
1667     {
1668         mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale);
1669 
1670         for( i = 0; i < mnItemCount; ++i )
1671             mpVisualItems[i].mnXOffset = (int)((double)mpVisualItems[i].mnXOffset*mfFontScale);
1672 
1673         mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale);
1674         for( i = 0; i < mnGlyphCount; ++i )
1675         {
1676             mpGlyphAdvances[i]   = (int)(mpGlyphAdvances[i] * mfFontScale);
1677             mpGlyphOffsets[i].du = (LONG)(mpGlyphOffsets[i].du * mfFontScale);
1678             mpGlyphOffsets[i].dv = (LONG)(mpGlyphOffsets[i].dv * mfFontScale);
1679             // mpJustifications are still NULL
1680         }
1681 
1682         for( i = mnSubStringMin; i < nSubStringEnd; ++i )
1683             mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale);
1684     }
1685 
1686     return true;
1687 }
1688 
1689 // -----------------------------------------------------------------------
1690 
1691 // calculate the range of relevant glyphs for this visual item
1692 bool UniscribeLayout::GetItemSubrange( const VisualItem& rVisualItem,
1693     int& rMinGlyphPos, int& rEndGlyphPos ) const
1694 {
1695     // return early when nothing of interest in this item
1696     if( rVisualItem.IsEmpty()
1697      || (rVisualItem.mnEndCharPos <= mnMinCharPos)
1698      || (mnEndCharPos <= rVisualItem.mnMinCharPos) )
1699         return false;
1700 
1701     // default: subrange is complete range
1702     rMinGlyphPos = rVisualItem.mnMinGlyphPos;
1703     rEndGlyphPos = rVisualItem.mnEndGlyphPos;
1704 
1705     // return early when the whole item is of interest
1706     if( (mnMinCharPos <= rVisualItem.mnMinCharPos)
1707      && (rVisualItem.mnEndCharPos <= mnEndCharPos ) )
1708         return true;
1709 
1710     // get glyph range from char range by looking at cluster boundries
1711     // TODO: optimize for case that LTR/RTL correspond to monotonous glyph indexes
1712     rMinGlyphPos = rVisualItem.mnEndGlyphPos;
1713     int nMaxGlyphPos = 0;
1714 
1715     int i = mnMinCharPos;
1716     if( i < rVisualItem.mnMinCharPos )
1717         i = rVisualItem.mnMinCharPos;
1718     int nCharPosLimit = rVisualItem.mnEndCharPos;
1719     if( nCharPosLimit > mnEndCharPos )
1720         nCharPosLimit = mnEndCharPos;
1721     for(; i < nCharPosLimit; ++i )
1722     {
1723         int n = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos;
1724         if( rMinGlyphPos > n )
1725             rMinGlyphPos = n;
1726         if( nMaxGlyphPos < n )
1727             nMaxGlyphPos = n;
1728     }
1729 	if (nMaxGlyphPos > rVisualItem.mnEndGlyphPos)
1730 		nMaxGlyphPos = rVisualItem.mnEndGlyphPos - 1;
1731 
1732     // extend the glyph range to account for all glyphs in referenced clusters
1733     if( !rVisualItem.IsRTL() ) // LTR-item
1734     {
1735         // extend to rightmost glyph of rightmost referenced cluster
1736         for( i = nMaxGlyphPos; ++i < rVisualItem.mnEndGlyphPos; nMaxGlyphPos = i )
1737             if( mpVisualAttrs[i].fClusterStart )
1738                 break;
1739     }
1740     else // RTL-item
1741     {
1742         // extend to leftmost glyph of leftmost referenced cluster
1743         for( i = rMinGlyphPos; --i >= rVisualItem.mnMinGlyphPos; rMinGlyphPos = i )
1744             if( mpVisualAttrs[i].fClusterStart )
1745                 break;
1746     }
1747     rEndGlyphPos = nMaxGlyphPos + 1;
1748 
1749     return true;
1750 }
1751 
1752 // -----------------------------------------------------------------------
1753 
1754 int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
1755     int& nStartx8, sal_Int32* pGlyphAdvances, int* pCharPosAry ) const
1756 {
1757     // HACK to allow fake-glyph insertion (e.g. for kashidas)
1758     // TODO: use iterator idiom instead of GetNextGlyphs(...)
1759     // TODO: else make sure that the limit for glyph injection is sufficient (currently 256)
1760     int nSubIter = nStartx8 & 0xff;
1761     int nStart = nStartx8 >> 8;
1762 
1763     // check the glyph iterator
1764     if( nStart > mnGlyphCount )       // nStart>MAX means no more glyphs
1765         return 0;
1766 
1767     // find the visual item for the nStart glyph position
1768     int nItem = 0;
1769     const VisualItem* pVI = mpVisualItems;
1770     if( nStart <= 0 )                 // nStart<=0 requests the first visible glyph
1771     {
1772         // find first visible item
1773         for(; nItem < mnItemCount; ++nItem, ++pVI )
1774             if( !pVI->IsEmpty() )
1775                 break;
1776         // it is possible that there are glyphs but no valid visual item
1777         // TODO: get rid of these visual items more early
1778         if( nItem < mnItemCount )
1779             nStart = pVI->mnMinGlyphPos;
1780     }
1781     else //if( nStart > 0 )           // nStart>0 means absolute glyph pos +1
1782     {
1783         --nStart;
1784 
1785         // find matching item
1786         for(; nItem < mnItemCount; ++nItem, ++pVI )
1787             if( (nStart >= pVI->mnMinGlyphPos)
1788             &&  (nStart < pVI->mnEndGlyphPos) )
1789                 break;
1790     }
1791 
1792     // after the last visual item there are no more glyphs
1793     if( (nItem >= mnItemCount) || (nStart < 0) )
1794     {
1795         nStartx8 = (mnGlyphCount + 1) << 8;
1796         return 0;
1797     }
1798 
1799     // calculate the first glyph in the next visual item
1800     int nNextItemStart = mnGlyphCount;
1801     while( ++nItem < mnItemCount )
1802     {
1803         if( mpVisualItems[nItem].IsEmpty() )
1804             continue;
1805         nNextItemStart = mpVisualItems[nItem].mnMinGlyphPos;
1806         break;
1807     }
1808 
1809     // get the range of relevant glyphs in this visual item
1810     int nMinGlyphPos, nEndGlyphPos;
1811     bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos );
1812     DBG_ASSERT( bRC, "USPLayout::GNG GISR() returned false" );
1813     if( !bRC )
1814     {
1815         nStartx8 = (mnGlyphCount + 1) << 8;
1816         return 0;
1817     }
1818 
1819     // make sure nStart is inside the range of relevant glyphs
1820     if( nStart < nMinGlyphPos )
1821         nStart = nMinGlyphPos;
1822 
1823     // calculate the start glyph xoffset relative to layout's base position,
1824     // advance to next visual glyph position by using adjusted glyph widths
1825     // TODO: speed up the calculation for nStart!=0 case by using rPos as a cache
1826     long nXOffset = pVI->mnXOffset;
1827     const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
1828     for( int i = nMinGlyphPos; i < nStart; ++i )
1829         nXOffset += pGlyphWidths[ i ];
1830 
1831     // adjust the nXOffset relative to glyph cluster start
1832     int c = mnMinCharPos;
1833     if( !pVI->IsRTL() ) // LTR-case
1834     {
1835         // LTR case: subtract the remainder of the cell from xoffset
1836         int nTmpIndex = mpLogClusters[c];
1837         while( (--c >= pVI->mnMinCharPos)
1838             && (nTmpIndex == mpLogClusters[c]) )
1839             nXOffset -= mpCharWidths[c];
1840     }
1841     else // RTL-case
1842     {
1843         // RTL case: add the remainder of the cell from xoffset
1844         int nTmpIndex = mpLogClusters[ pVI->mnEndCharPos - 1 ];
1845         while( (--c >= pVI->mnMinCharPos)
1846             && (nTmpIndex == mpLogClusters[c]) )
1847             nXOffset += mpCharWidths[c];
1848 
1849         // adjust the xoffset if justified glyphs are not positioned at their justified positions yet
1850 		if( mpJustifications && !bManualCellAlign )
1851            nXOffset += mpJustifications[ nStart ] - mpGlyphAdvances[ nStart ];
1852     }
1853 
1854     // create mpGlyphs2Chars[] if it is needed later
1855     if( pCharPosAry && !mpGlyphs2Chars )
1856     {
1857         // create and reset the new array
1858         mpGlyphs2Chars = new int[ mnGlyphCapacity ];
1859         for( int i = 0; i < mnGlyphCount; ++i )
1860             mpGlyphs2Chars[i] = -1;
1861         // calculate the char->glyph mapping
1862         for( nItem = 0; nItem < mnItemCount; ++nItem )
1863         {
1864             // ignore invisible visual items
1865             const VisualItem& rVI = mpVisualItems[ nItem ];
1866             if( rVI.IsEmpty() )
1867                 continue;
1868             // calculate the mapping by using mpLogClusters[]
1869             // mpGlyphs2Chars[] should obey the logical order
1870             // => reversing the loop does this by overwriting higher logicals
1871             for( c = rVI.mnEndCharPos; --c >= rVI.mnMinCharPos; )
1872             {
1873                 int i = mpLogClusters[c] + rVI.mnMinGlyphPos;
1874                 mpGlyphs2Chars[i] = c;
1875             }
1876         }
1877     }
1878 
1879     // calculate the absolute position of the first result glyph in pixel units
1880     const GOFFSET aGOffset = mpGlyphOffsets[ nStart ];
1881     Point aRelativePos( nXOffset + aGOffset.du, -aGOffset.dv );
1882     rPos = GetDrawPosition( aRelativePos );
1883 
1884 	// fill the result arrays
1885     int nCount = 0;
1886     while( nCount < nLen )
1887     {
1888         // prepare return values
1889         sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ];
1890         int nGlyphWidth = pGlyphWidths[ nStart ];
1891         int nCharPos = -1;    // no need to determine charpos
1892         if( mpGlyphs2Chars )  // unless explicitly requested+provided
1893             nCharPos = mpGlyphs2Chars[ nStart ];
1894 
1895         // inject kashida glyphs if needed
1896         if( !mbDisableGlyphInjection
1897         && mpJustifications
1898         && mnMinKashidaWidth
1899         && mpVisualAttrs[nStart].uJustification >= SCRIPT_JUSTIFY_ARABIC_NORMAL )
1900         {
1901 			// prepare draw position adjustment
1902 			int nExtraOfs = (nSubIter++) * mnMinKashidaWidth;
1903         	// calculate space available for the injected glyphs
1904    	        nGlyphWidth = mpGlyphAdvances[ nStart ];
1905 	        const int nExtraWidth = mpJustifications[ nStart ] - nGlyphWidth;
1906 			const int nToFillWidth = nExtraWidth - nExtraOfs;
1907 	        if( (4*nToFillWidth >= mnMinKashidaWidth)    // prevent glyph-injection if there is no room
1908 	        ||  ((nSubIter > 1) && (nToFillWidth > 0)) ) // unless they can overlap with others
1909 	        {
1910 	        	// handle if there is not sufficient room for a full glyph
1911 	        	if( nToFillWidth < mnMinKashidaWidth )
1912 	        	{
1913 	        		// overlap it with the previously injected glyph if possible
1914 	        		int nOverlap = mnMinKashidaWidth - nToFillWidth;
1915 	        		// else overlap it with both neighboring glyphs
1916 					if( nSubIter <= 1 )
1917 						nOverlap /= 2;
1918 					nExtraOfs -= nOverlap;
1919 	        	}
1920 	        	nGlyphWidth = mnMinKashidaWidth;
1921 	        	aGlyphId = mnMinKashidaGlyph;
1922 				nCharPos = -1;
1923 	        }
1924 	        else
1925 	        {
1926 	        	nExtraOfs += nToFillWidth;	// at right of cell
1927 	        	nSubIter = 0;				// done with glyph injection
1928 	        }
1929             if( !bManualCellAlign )
1930                 nExtraOfs -= nExtraWidth;	// adjust for right-aligned cells
1931 
1932 			// adjust the draw position for the injected-glyphs case
1933 			if( nExtraOfs )
1934 			{
1935 				aRelativePos.X() += nExtraOfs;
1936 				rPos = GetDrawPosition( aRelativePos );
1937 			}
1938         }
1939 
1940         // update return values
1941         *(pGlyphs++) = aGlyphId;
1942         if( pGlyphAdvances )
1943             *(pGlyphAdvances++) = nGlyphWidth;
1944         if( pCharPosAry )
1945             *(pCharPosAry++) = nCharPos;
1946 
1947         // increment counter of returned glyphs
1948         ++nCount;
1949 
1950         // reduce code complexity by returning early in glyph-injection case
1951        	if( nSubIter != 0 )
1952        		break;
1953 
1954         // stop after the last visible glyph in this visual item
1955         if( ++nStart >= nEndGlyphPos )
1956         {
1957             nStart = nNextItemStart;
1958             break;
1959         }
1960 
1961         // RTL-justified glyph positioning is not easy
1962         // simplify the code by just returning only one glyph at a time
1963         if( mpJustifications && pVI->IsRTL() )
1964             break;
1965 
1966         // stop when the x-position of the next glyph is unexpected
1967         if( !pGlyphAdvances  )
1968             if( (mpGlyphOffsets && (mpGlyphOffsets[nStart].du != aGOffset.du) )
1969              || (mpJustifications && (mpJustifications[nStart] != mpGlyphAdvances[nStart]) ) )
1970                 break;
1971 
1972         // stop when the y-position of the next glyph is unexpected
1973         if( mpGlyphOffsets && (mpGlyphOffsets[nStart].dv != aGOffset.dv) )
1974             break;
1975     }
1976 
1977     ++nStart;
1978     nStartx8 = (nStart << 8) + nSubIter;
1979     return nCount;
1980 }
1981 
1982 // -----------------------------------------------------------------------
1983 
1984 void UniscribeLayout::MoveGlyph( int nStartx8, long nNewXPos )
1985 {
1986     DBG_ASSERT( !(nStartx8 & 0xff), "USP::MoveGlyph(): glyph injection not disabled!" );
1987     int nStart = nStartx8 >> 8;
1988     if( nStart > mnGlyphCount )
1989         return;
1990 
1991     VisualItem* pVI = mpVisualItems;
1992     int nMinGlyphPos = 0, nEndGlyphPos;
1993     if( nStart == 0 )               // nStart==0 for first visible glyph
1994     {
1995         for( int i = mnItemCount; --i >= 0; ++pVI )
1996             if( GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ) )
1997                 break;
1998         nStart = nMinGlyphPos;
1999         DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::MoveG overflow" );
2000     }
2001     else //if( nStart > 0 )         // nStart>0 means absolute_glyphpos+1
2002     {
2003         --nStart;
2004         for( int i = mnItemCount; --i >= 0; ++pVI )
2005             if( (nStart >= pVI->mnMinGlyphPos) && (nStart < pVI->mnEndGlyphPos) )
2006                 break;
2007         bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos );
2008 	(void)bRC; // avoid var-not-used warning
2009         DBG_ASSERT( bRC, "USPLayout::MoveG GISR() returned false" );
2010     }
2011 
2012     long nDelta = nNewXPos - pVI->mnXOffset;
2013     if( nStart > nMinGlyphPos )
2014     {
2015         // move the glyph by expanding its left glyph but ignore dropped glyphs
2016         int i, nLastUndropped = nMinGlyphPos - 1;
2017         for( i = nMinGlyphPos; i < nStart; ++i )
2018 		{
2019 			if (mpOutGlyphs[i] != DROPPED_OUTGLYPH)
2020 			{
2021 	            nDelta -= (mpJustifications)? mpJustifications[ i ] : mpGlyphAdvances[ i ];
2022 				nLastUndropped = i;
2023 			}
2024 		}
2025 		if (nLastUndropped >= nMinGlyphPos)
2026 		{
2027 			mpGlyphAdvances[ nLastUndropped ] += nDelta;
2028 			if (mpJustifications) mpJustifications[ nLastUndropped ] += nDelta;
2029 		}
2030 		else
2031 		{
2032 			pVI->mnXOffset += nDelta;
2033 		}
2034     }
2035     else
2036     {
2037         // move the visual item by having an offset
2038         pVI->mnXOffset += nDelta;
2039     }
2040     // move subsequent items - this often isn't necessary because subsequent
2041     // moves will correct subsequent items. However, if there is a contiguous
2042     // range not involving fallback which spans items, this will be needed
2043     while (++pVI - mpVisualItems < mnItemCount)
2044     {
2045         pVI->mnXOffset += nDelta;
2046     }
2047 }
2048 
2049 // -----------------------------------------------------------------------
2050 
2051 void UniscribeLayout::DropGlyph( int nStartx8 )
2052 {
2053     DBG_ASSERT( !(nStartx8 & 0xff), "USP::DropGlyph(): glyph injection not disabled!" );
2054     int nStart = nStartx8 >> 8;
2055     DBG_ASSERT( nStart<=mnGlyphCount, "USPLayout::MoveG nStart overflow" );
2056 
2057     if( nStart > 0 )        // nStart>0 means absolute glyph pos + 1
2058         --nStart;
2059     else                    // nStart<=0 for first visible glyph
2060     {
2061         VisualItem* pVI = mpVisualItems;
2062         for( int i = mnItemCount, nDummy; --i >= 0; ++pVI )
2063             if( GetItemSubrange( *pVI, nStart, nDummy ) )
2064                 break;
2065         DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::DropG overflow" );
2066 		int nOffset = 0;
2067 		int j = pVI->mnMinGlyphPos;
2068 		while (mpOutGlyphs[j] == DROPPED_OUTGLYPH) j++;
2069 		if (j == nStart)
2070 		{
2071 			pVI->mnXOffset += ((mpJustifications)? mpJustifications[nStart] : mpGlyphAdvances[nStart]);
2072 		}
2073     }
2074 
2075     mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH;
2076 }
2077 
2078 // -----------------------------------------------------------------------
2079 
2080 void UniscribeLayout::Simplify( bool /*bIsBase*/ )
2081 {
2082     static const WCHAR cDroppedGlyph = DROPPED_OUTGLYPH;
2083     int i;
2084     // if there are no dropped glyphs don't bother
2085     for( i = 0; i < mnGlyphCount; ++i )
2086         if( mpOutGlyphs[ i ] == cDroppedGlyph )
2087             break;
2088     if( i >= mnGlyphCount )
2089         return;
2090 
2091     // prepare for sparse layout
2092     // => make sure mpGlyphs2Chars[] exists
2093     if( !mpGlyphs2Chars )
2094     {
2095         mpGlyphs2Chars = new int[ mnGlyphCapacity ];
2096         for( i = 0; i < mnGlyphCount; ++i )
2097             mpGlyphs2Chars[ i ] = -1;
2098         for( int nItem = 0; nItem < mnItemCount; ++nItem )
2099         {
2100             // skip invisible items
2101             VisualItem& rVI = mpVisualItems[ nItem ];
2102             if( rVI.IsEmpty() )
2103                 continue;
2104             for( i = rVI.mnEndCharPos; --i >= rVI.mnMinCharPos; )
2105             {
2106                 int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos;
2107                 mpGlyphs2Chars[ j ] = i;
2108             }
2109         }
2110     }
2111 
2112     // remove the dropped glyphs
2113     const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
2114     for( int nItem = 0; nItem < mnItemCount; ++nItem )
2115     {
2116         VisualItem& rVI = mpVisualItems[ nItem ];
2117         if( rVI.IsEmpty() )
2118             continue;
2119 
2120         // mark replaced character widths
2121         for( i = rVI.mnMinCharPos; i < rVI.mnEndCharPos; ++i )
2122         {
2123             int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos;
2124             if( mpOutGlyphs[ j ] == cDroppedGlyph )
2125                 mpCharWidths[ i ] = 0;
2126         }
2127 
2128         // handle dropped glyphs at start of visual item
2129         int nMinGlyphPos, nEndGlyphPos, nOrigMinGlyphPos = rVI.mnMinGlyphPos;
2130         GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos );
2131         i = nMinGlyphPos;
2132         while( (mpOutGlyphs[i] == cDroppedGlyph) && (i < nEndGlyphPos) )
2133         {
2134             //rVI.mnXOffset += pGlyphWidths[ i ];
2135             rVI.mnMinGlyphPos = ++i;
2136         }
2137 
2138         // when all glyphs in item got dropped mark it as empty
2139         if( i >= nEndGlyphPos )
2140         {
2141             rVI.mnEndGlyphPos = 0;
2142             continue;
2143         }
2144 		// If there are still glyphs in the cluster and mnMinGlyphPos
2145 		// has changed then we need to remove the dropped glyphs at start
2146 		// to correct logClusters, which is unsigned and relative to the
2147 		// item start.
2148 		if (rVI.mnMinGlyphPos != nOrigMinGlyphPos)
2149 		{
2150 			// drop any glyphs in the visual item outside the range
2151 			for (i = nOrigMinGlyphPos; i < nMinGlyphPos; i++)
2152 				mpOutGlyphs[ i ] = cDroppedGlyph;
2153 			rVI.mnMinGlyphPos = i = nOrigMinGlyphPos;
2154 		}
2155 
2156         // handle dropped glyphs in the middle of visual item
2157         for(; i < nEndGlyphPos; ++i )
2158             if( mpOutGlyphs[ i ] == cDroppedGlyph )
2159                 break;
2160         int j = i;
2161         while( ++i < nEndGlyphPos )
2162         {
2163             if( mpOutGlyphs[ i ] == cDroppedGlyph )
2164                 continue;
2165             mpOutGlyphs[ j ]      = mpOutGlyphs[ i ];
2166             mpGlyphOffsets[ j ]   = mpGlyphOffsets[ i ];
2167             mpVisualAttrs[ j ]    = mpVisualAttrs[ i ];
2168             mpGlyphAdvances[ j ]  = mpGlyphAdvances[ i ];
2169             if( mpJustifications )
2170                 mpJustifications[ j ] = mpJustifications[ i ];
2171             const int k = mpGlyphs2Chars[ i ];
2172             mpGlyphs2Chars[ j ]   = k;
2173             const int nRelGlyphPos = (j++) - rVI.mnMinGlyphPos;
2174             if( k < 0) // extra glyphs are already mapped
2175                 continue;
2176             mpLogClusters[ k ] = static_cast<WORD>(nRelGlyphPos);
2177         }
2178 
2179         rVI.mnEndGlyphPos = j;
2180     }
2181 }
2182 
2183 // -----------------------------------------------------------------------
2184 
2185 void UniscribeLayout::DrawText( SalGraphics& ) const
2186 {
2187     HFONT hOrigFont = DisableFontScaling();
2188 
2189     int nBaseClusterOffset = 0;
2190     int nBaseGlyphPos = -1;
2191     for( int nItem = 0; nItem < mnItemCount; ++nItem )
2192     {
2193         const VisualItem& rVisualItem = mpVisualItems[ nItem ];
2194 
2195         // skip if there is nothing to display
2196         int nMinGlyphPos, nEndGlyphPos;
2197         if( !GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) )
2198             continue;
2199 
2200         if( nBaseGlyphPos < 0 )
2201         {
2202             // adjust draw position relative to cluster start
2203             if( rVisualItem.IsRTL() )
2204                 nBaseGlyphPos = nEndGlyphPos - 1;
2205             else
2206                 nBaseGlyphPos = nMinGlyphPos;
2207 
2208             const int* pGlyphWidths;
2209             if( mpJustifications )
2210                 pGlyphWidths = mpJustifications;
2211             else
2212                 pGlyphWidths = mpGlyphAdvances;
2213 
2214             int i = mnMinCharPos;
2215             while( (--i >= rVisualItem.mnMinCharPos)
2216                 && (nBaseGlyphPos == mpLogClusters[i]) )
2217                  nBaseClusterOffset += mpCharWidths[i];
2218 
2219             if( !rVisualItem.IsRTL() )
2220                 nBaseClusterOffset = -nBaseClusterOffset;
2221         }
2222 
2223         // now draw the matching glyphs in this item
2224         Point aRelPos( rVisualItem.mnXOffset + nBaseClusterOffset, 0 );
2225         Point aPos = GetDrawPosition( aRelPos );
2226         SCRIPT_CACHE& rScriptCache = GetScriptCache();
2227         (*pScriptTextOut)( mhDC, &rScriptCache,
2228             aPos.X(), aPos.Y(), 0, NULL,
2229             &rVisualItem.mpScriptItem->a, NULL, 0,
2230             mpOutGlyphs + nMinGlyphPos,
2231             nEndGlyphPos - nMinGlyphPos,
2232             mpGlyphAdvances + nMinGlyphPos,
2233             mpJustifications ? mpJustifications + nMinGlyphPos : NULL,
2234             mpGlyphOffsets + nMinGlyphPos );
2235     }
2236 
2237     if( hOrigFont )
2238         DeleteFont( SelectFont( mhDC, hOrigFont ) );
2239 }
2240 
2241 // -----------------------------------------------------------------------
2242 
2243 long UniscribeLayout::FillDXArray( long* pDXArray ) const
2244 {
2245     // calculate width of the complete layout
2246     long nWidth = mnBaseAdv;
2247     for( int nItem = mnItemCount; --nItem >= 0; )
2248     {
2249         const VisualItem& rVI = mpVisualItems[ nItem ];
2250 
2251         // skip if there is nothing to display
2252         int nMinGlyphPos, nEndGlyphPos;
2253         if( !GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos ) )
2254             continue;
2255 
2256         // width = xoffset + width of last item
2257         nWidth = rVI.mnXOffset;
2258         const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
2259         for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2260             nWidth += pGlyphWidths[i];
2261         break;
2262     }
2263 
2264     // copy the virtual char widths into pDXArray[]
2265     if( pDXArray )
2266         for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
2267             pDXArray[ i - mnMinCharPos ] = mpCharWidths[ i ];
2268 
2269     return nWidth;
2270 }
2271 
2272 // -----------------------------------------------------------------------
2273 
2274 int UniscribeLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
2275 {
2276     long nWidth = 0;
2277     for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
2278     {
2279         nWidth += mpCharWidths[ i ] * nFactor;
2280 
2281         // check if the nMaxWidth still fits the current sub-layout
2282         if( nWidth >= nMaxWidth )
2283         {
2284             // go back to cluster start
2285             // we have to find the visual item first since the mpLogClusters[]
2286             // needed to find the cluster start is relative to to the visual item
2287             int nMinGlyphIndex = 0;
2288             for( int nItem = 0; nItem < mnItemCount; ++nItem )
2289             {
2290                 const VisualItem& rVisualItem = mpVisualItems[ nItem ];
2291                 nMinGlyphIndex = rVisualItem.mnMinGlyphPos;
2292                 if( (i >= rVisualItem.mnMinCharPos)
2293                 &&  (i < rVisualItem.mnEndCharPos) )
2294                     break;
2295             }
2296             // now go back to the matching cluster start
2297             do
2298             {
2299                 int nGlyphPos = mpLogClusters[i] + nMinGlyphIndex;
2300                 if( 0 != mpVisualAttrs[ nGlyphPos ].fClusterStart )
2301                     return i;
2302             } while( --i >= mnMinCharPos );
2303 
2304             // if the cluster starts before the start of the visual item
2305             // then set the visual breakpoint before this item
2306             return mnMinCharPos;
2307         }
2308 
2309         // the visual break also depends on the nCharExtra between the characters
2310         nWidth += nCharExtra;
2311     }
2312 
2313     // the whole layout did fit inside the nMaxWidth
2314     return STRING_LEN;
2315 }
2316 
2317 // -----------------------------------------------------------------------
2318 
2319 void UniscribeLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const
2320 {
2321     int i;
2322     for( i = 0; i < nMaxIdx; ++i )
2323         pCaretXArray[ i ] = -1;
2324     long* const pGlyphPos = (long*)alloca( (mnGlyphCount+1) * sizeof(long) );
2325     for( i = 0; i <= mnGlyphCount; ++i )
2326         pGlyphPos[ i ] = -1;
2327 
2328     long nXPos = 0;
2329     for( int nItem = 0; nItem < mnItemCount; ++nItem )
2330     {
2331         const VisualItem& rVisualItem = mpVisualItems[ nItem ];
2332         if( rVisualItem.IsEmpty() )
2333             continue;
2334 
2335         if (mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK)
2336         {
2337             nXPos = rVisualItem.mnXOffset;
2338         }
2339         // get glyph positions
2340         // TODO: handle when rVisualItem's glyph range is only partially used
2341         for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
2342         {
2343             pGlyphPos[ i ] = nXPos;
2344             nXPos += mpGlyphAdvances[ i ];
2345         }
2346         // rightmost position of this visualitem
2347         pGlyphPos[ i ] = nXPos;
2348 
2349         // convert glyph positions to character positions
2350         i = rVisualItem.mnMinCharPos;
2351         if( i < mnMinCharPos )
2352             i = mnMinCharPos;
2353         for(; (i < rVisualItem.mnEndCharPos) && (i < mnEndCharPos); ++i )
2354         {
2355             int j = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos;
2356             int nCurrIdx = i * 2;
2357             if( !rVisualItem.IsRTL() )
2358             {
2359                 // normal positions for LTR case
2360                 pCaretXArray[ nCurrIdx ]   = pGlyphPos[ j ];
2361                 pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j+1 ];
2362             }
2363             else
2364             {
2365                 // reverse positions for RTL case
2366                 pCaretXArray[ nCurrIdx ]   = pGlyphPos[ j+1 ];
2367                 pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j ];
2368             }
2369         }
2370     }
2371 
2372     if (!(mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK))
2373     {
2374         nXPos = 0;
2375         // fixup unknown character positions to neighbor
2376         for( i = 0; i < nMaxIdx; ++i )
2377         {
2378             if( pCaretXArray[ i ] >= 0 )
2379                 nXPos = pCaretXArray[ i ];
2380             else
2381                 pCaretXArray[ i ] = nXPos;
2382         }
2383     }
2384 }
2385 
2386 // -----------------------------------------------------------------------
2387 
2388 void UniscribeLayout::AdjustLayout( ImplLayoutArgs& rArgs )
2389 {
2390     SalLayout::AdjustLayout( rArgs );
2391 
2392     // adjust positions if requested
2393     if( rArgs.mpDXArray )
2394         ApplyDXArray( rArgs );
2395     else if( rArgs.mnLayoutWidth )
2396         Justify( rArgs.mnLayoutWidth );
2397 }
2398 
2399 // -----------------------------------------------------------------------
2400 
2401 void UniscribeLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
2402 {
2403     const long* pDXArray = rArgs.mpDXArray;
2404 
2405     // increase char widths in string range to desired values
2406     bool bModified = false;
2407     int nOldWidth = 0;
2408     DBG_ASSERT( mnUnitsPerPixel==1, "UniscribeLayout.mnUnitsPerPixel != 1" );
2409     int i,j;
2410     for( i = mnMinCharPos, j = 0; i < mnEndCharPos; ++i, ++j )
2411     {
2412         int nNewCharWidth = (pDXArray[j] - nOldWidth);
2413         // TODO: nNewCharWidth *= mnUnitsPerPixel;
2414         if( mpCharWidths[i] != nNewCharWidth )
2415         {
2416             mpCharWidths[i] = nNewCharWidth;
2417             bModified = true;
2418         }
2419         nOldWidth = pDXArray[j];
2420     }
2421 
2422     if( !bModified )
2423         return;
2424 
2425     // initialize justifications array
2426     mpJustifications = new int[ mnGlyphCapacity ];
2427     for( i = 0; i < mnGlyphCount; ++i )
2428         mpJustifications[ i ] = mpGlyphAdvances[ i ];
2429 
2430     // apply new widths to script items
2431     long nXOffset = 0;
2432     for( int nItem = 0; nItem < mnItemCount; ++nItem )
2433     {
2434         VisualItem& rVisualItem = mpVisualItems[ nItem ];
2435 
2436         // set the position of this visual item
2437         rVisualItem.mnXOffset = nXOffset;
2438 
2439         // ignore empty visual items
2440         if( rVisualItem.IsEmpty() )
2441         {
2442             for (i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; i++)
2443               nXOffset += mpCharWidths[i];
2444             continue;
2445         }
2446         // ignore irrelevant visual items
2447         if( (rVisualItem.mnMinCharPos >= mnEndCharPos)
2448          || (rVisualItem.mnEndCharPos <= mnMinCharPos) )
2449             continue;
2450 
2451 		// if needed prepare special handling for arabic justification
2452 		rVisualItem.mbHasKashidas = false;
2453 		if( rVisualItem.IsRTL() )
2454         {
2455 			for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
2456                 if ( (1U << mpVisualAttrs[i].uJustification) & 0xFF82 )  //  any Arabic justification
2457                 {                                                        //  excluding SCRIPT_JUSTIFY_NONE
2458                     // yes
2459                     rVisualItem.mbHasKashidas = true;
2460                     // so prepare for kashida handling
2461                     InitKashidaHandling();
2462 					break;
2463 				}
2464 
2465 			if( rVisualItem.HasKashidas() )
2466 				for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
2467 				{
2468                     // TODO: check if we still need this hack after correction of kashida placing?
2469                     // (i87688): apparently yes, we still need it!
2470                     if ( mpVisualAttrs[i].uJustification == SCRIPT_JUSTIFY_NONE )
2471                         // usp decided that justification can't be applied here
2472 						// but maybe our Kashida algorithm thinks differently.
2473 						// To avoid trouble (gaps within words, last character of
2474 						// a word gets a Kashida appended) override this.
2475 
2476 						// I chose SCRIPT_JUSTIFY_ARABIC_KASHIDA to replace SCRIPT_JUSTIFY_NONE
2477 						// just because this previous hack (which I haven't understand, sorry) used
2478 						// the same value to replace. Don't know if this is really the best
2479 						// thing to do, but it seems to fix things
2480 						mpVisualAttrs[i].uJustification = SCRIPT_JUSTIFY_ARABIC_KASHIDA;
2481 				}
2482         }
2483 
2484         // convert virtual charwidths to glyph justification values
2485         HRESULT nRC = (*pScriptApplyLogicalWidth)(
2486             mpCharWidths + rVisualItem.mnMinCharPos,
2487             rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos,
2488             rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos,
2489             mpLogClusters + rVisualItem.mnMinCharPos,
2490             mpVisualAttrs + rVisualItem.mnMinGlyphPos,
2491             mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
2492             &rVisualItem.mpScriptItem->a,
2493             &rVisualItem.maABCWidths,
2494             mpJustifications + rVisualItem.mnMinGlyphPos );
2495 
2496         if( nRC != 0 )
2497         {
2498             delete[] mpJustifications;
2499             mpJustifications = NULL;
2500             break;
2501         }
2502 
2503         // to prepare for the next visual item
2504         // update nXOffset to the next items position
2505         // before the mpJustifications[] array gets modified
2506         int nMinGlyphPos, nEndGlyphPos;
2507         if( GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) )
2508         {
2509             for( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2510                 nXOffset += mpJustifications[ i ];
2511 
2512             if( rVisualItem.mbHasKashidas )
2513 				KashidaItemFix( nMinGlyphPos, nEndGlyphPos );
2514         }
2515 
2516 		// workaround needed for older USP versions:
2517         // right align the justification-adjusted glyphs in their cells for RTL-items
2518 		// unless the right alignment is done by inserting kashidas
2519         if( bManualCellAlign && rVisualItem.IsRTL() && !rVisualItem.HasKashidas() )
2520         {
2521             for( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2522             {
2523                 const int nXOffsetAdjust = mpJustifications[i] - mpGlyphAdvances[i];
2524 				// #i99862# skip diacritics, we mustn't add extra justification to diacritics
2525 				int nIdxAdd = i - 1;
2526 				while( (nIdxAdd >= nMinGlyphPos) && !mpGlyphAdvances[nIdxAdd] )
2527 					--nIdxAdd;
2528                 if( nIdxAdd < nMinGlyphPos )
2529                     rVisualItem.mnXOffset += nXOffsetAdjust;
2530                 else
2531                     mpJustifications[nIdxAdd] += nXOffsetAdjust;
2532                 mpJustifications[i] -= nXOffsetAdjust;
2533             }
2534         }
2535     }
2536 }
2537 
2538 // -----------------------------------------------------------------------
2539 
2540 void UniscribeLayout::InitKashidaHandling()
2541 {
2542 	if( mnMinKashidaGlyph != 0 )	// already initialized
2543 		return;
2544 
2545 	mrWinFontEntry.InitKashidaHandling( mhDC );
2546 	mnMinKashidaWidth = static_cast<int>(mfFontScale * mrWinFontEntry.GetMinKashidaWidth());
2547 	mnMinKashidaGlyph = mrWinFontEntry.GetMinKashidaGlyph();
2548 }
2549 
2550 // adjust the kashida placement matching to the WriterEngine
2551 void UniscribeLayout::KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos )
2552 {
2553 	// workaround needed for all known USP versions:
2554 	// ApplyLogicalWidth does not match ScriptJustify behaviour
2555 	for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2556 	{
2557 		// check for vowels
2558 		if( (i > nMinGlyphPos && !mpGlyphAdvances[ i-1 ])
2559 		&&  (1U << mpVisualAttrs[i].uJustification) & 0xFF83 )	// all Arabic justifiction types
2560 		{														// including SCRIPT_JUSTIFY_NONE
2561 			// vowel, we do it like ScriptJustify does
2562 			// the vowel gets the extra width
2563 			long nSpaceAdded =  mpJustifications[ i ] - mpGlyphAdvances[ i ];
2564 			mpJustifications [ i ] = mpGlyphAdvances [ i ];
2565 			mpJustifications [ i - 1 ] += nSpaceAdded;
2566 		}
2567 	}
2568 
2569 	// redistribute the widths for kashidas
2570 	for( int i = nMinGlyphPos; i < nEndGlyphPos; )
2571 		KashidaWordFix ( nMinGlyphPos, nEndGlyphPos, &i );
2572 }
2573 
2574 bool UniscribeLayout::KashidaWordFix ( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos )
2575 {
2576 	// doing pixel work within a word.
2577 	// sometimes we have extra pixels and sometimes we miss some pixels to get to mnMinKashidaWidth
2578 
2579 	// find the next kashida
2580 	int nMinPos = *pnCurrentPos;
2581 	int nMaxPos = *pnCurrentPos;
2582 	for( int i = nMaxPos; i < nEndGlyphPos; ++i )
2583 	{
2584 		if( (mpVisualAttrs[ i ].uJustification >= SCRIPT_JUSTIFY_ARABIC_BLANK)
2585 		&&  (mpVisualAttrs[ i ].uJustification < SCRIPT_JUSTIFY_ARABIC_NORMAL) )
2586 			break;
2587 		nMaxPos = i;
2588 	}
2589 	*pnCurrentPos = nMaxPos + 1;
2590 	if( nMinPos == nMaxPos )
2591 		return false;
2592 
2593 	// calculate the available space for an extra kashida
2594 	long nMaxAdded = 0;
2595 	int nKashPos = -1;
2596 	for( int i = nMaxPos; i >= nMinPos; --i )
2597 	{
2598 		long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
2599 		if( nSpaceAdded > nMaxAdded )
2600 		{
2601 			nKashPos = i;
2602 			nMaxAdded = nSpaceAdded;
2603 		}
2604 	}
2605 
2606 	// return early if there is no need for an extra kashida
2607 	if ( nMaxAdded <= 0 )
2608 		return false;
2609 	// return early if there is not enough space for an extra kashida
2610 	if( 2*nMaxAdded < mnMinKashidaWidth )
2611 		return false;
2612 
2613 	// redistribute the extra spacing to the kashida position
2614 	for( int i = nMinPos; i <= nMaxPos; ++i )
2615 	{
2616 		if( i == nKashPos )
2617 			continue;
2618 		// everything else should not have extra spacing
2619 		long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
2620 		if( nSpaceAdded > 0 )
2621 		{
2622 			mpJustifications[ i ] -= nSpaceAdded;
2623 			mpJustifications[ nKashPos ] += nSpaceAdded;
2624 		}
2625 	}
2626 
2627 	// check if we fulfill minimal kashida width
2628 	long nSpaceAdded = mpJustifications[ nKashPos ] - mpGlyphAdvances[ nKashPos ];
2629 	if( nSpaceAdded < mnMinKashidaWidth )
2630 	{
2631 		// ugly: steal some pixels
2632 		long nSteal = 1;
2633 		if ( nMaxPos - nMinPos > 0 && ((mnMinKashidaWidth - nSpaceAdded) > (nMaxPos - nMinPos)))
2634 			nSteal = (mnMinKashidaWidth - nSpaceAdded) / (nMaxPos - nMinPos);
2635 		for( int i = nMinPos; i <= nMaxPos; ++i )
2636 		{
2637 			if( i == nKashPos )
2638 				continue;
2639 			nSteal = Min( mnMinKashidaWidth - nSpaceAdded, nSteal );
2640 			if ( nSteal > 0 )
2641 			{
2642 				mpJustifications [ i ] -= nSteal;
2643 				mpJustifications [ nKashPos ] += nSteal;
2644 				nSpaceAdded += nSteal;
2645 			}
2646 			if( nSpaceAdded >= mnMinKashidaWidth )
2647 				return true;
2648 		}
2649 	}
2650 
2651 	// blank padding
2652 	long nSpaceMissing = mnMinKashidaWidth - nSpaceAdded;
2653 	if( nSpaceMissing > 0 )
2654 	{
2655 		// inner glyph: distribute extra space evenly
2656 		if( (nMinPos > nMinGlyphPos) && (nMaxPos < nEndGlyphPos - 1) )
2657 		{
2658 			mpJustifications [ nKashPos ] += nSpaceMissing;
2659 			long nHalfSpace = nSpaceMissing / 2;
2660 			mpJustifications [ nMinPos - 1 ] -= nHalfSpace;
2661 			mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing - nHalfSpace;
2662 		}
2663 		// rightmost: left glyph gets extra space
2664 		else if( nMinPos > nMinGlyphPos )
2665 		{
2666 			mpJustifications [ nMinPos - 1 ] -= nSpaceMissing;
2667 			mpJustifications [ nKashPos ] += nSpaceMissing;
2668 		}
2669 		// leftmost: right glyph gets extra space
2670 		else if( nMaxPos < nEndGlyphPos - 1 )
2671 		{
2672 			mpJustifications [ nKashPos ] += nSpaceMissing;
2673 			mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing;
2674 		}
2675 		else
2676 			return false;
2677 	}
2678 
2679 	return true;
2680 }
2681 
2682 // -----------------------------------------------------------------------
2683 
2684 void UniscribeLayout::Justify( long nNewWidth )
2685 {
2686     long nOldWidth = 0;
2687     int i;
2688     for( i = mnMinCharPos; i < mnEndCharPos; ++i )
2689         nOldWidth += mpCharWidths[ i ];
2690 	if( nOldWidth <= 0 )
2691 		return;
2692 
2693     nNewWidth *= mnUnitsPerPixel;	// convert into font units
2694     if( nNewWidth == nOldWidth )
2695         return;
2696     // prepare to distribute the extra width evenly among the visual items
2697     const double fStretch = (double)nNewWidth / nOldWidth;
2698 
2699     // initialize justifications array
2700     mpJustifications = new int[ mnGlyphCapacity ];
2701     for( i = 0; i < mnGlyphCapacity; ++i )
2702         mpJustifications[ i ] = mpGlyphAdvances[ i ];
2703 
2704     // justify stretched script items
2705     long nXOffset = 0;
2706     SCRIPT_CACHE& rScriptCache = GetScriptCache();
2707     for( int nItem = 0; nItem < mnItemCount; ++nItem )
2708     {
2709         VisualItem& rVisualItem = mpVisualItems[ nItem ];
2710         if( rVisualItem.IsEmpty() )
2711             continue;
2712 
2713         if( (rVisualItem.mnMinCharPos < mnEndCharPos)
2714          && (rVisualItem.mnEndCharPos > mnMinCharPos) )
2715         {
2716             long nItemWidth = 0;
2717             for( i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i )
2718                 nItemWidth += mpCharWidths[ i ];
2719             nItemWidth = (int)((fStretch - 1.0) * nItemWidth + 0.5);
2720 
2721             HRESULT nRC = (*pScriptJustify) (
2722                 mpVisualAttrs + rVisualItem.mnMinGlyphPos,
2723                 mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
2724                 rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos,
2725                 nItemWidth,
2726                 mnMinKashidaWidth,
2727                 mpJustifications + rVisualItem.mnMinGlyphPos );
2728 
2729             rVisualItem.mnXOffset = nXOffset;
2730             nXOffset += nItemWidth;
2731         }
2732     }
2733 }
2734 
2735 // -----------------------------------------------------------------------
2736 
2737 bool UniscribeLayout::IsKashidaPosValid ( int nCharPos ) const
2738 {
2739 	// we have to find the visual item first since the mpLogClusters[]
2740     // needed to find the cluster start is relative to to the visual item
2741     int nMinGlyphIndex = -1;
2742     for( int nItem = 0; nItem < mnItemCount; ++nItem )
2743     {
2744         const VisualItem& rVisualItem = mpVisualItems[ nItem ];
2745         if( (nCharPos >= rVisualItem.mnMinCharPos)
2746         &&  (nCharPos < rVisualItem.mnEndCharPos) )
2747 		{
2748 			nMinGlyphIndex = rVisualItem.mnMinGlyphPos;
2749             break;
2750 		}
2751     }
2752 	// Invalid char pos or leftmost glyph in visual item
2753     if ( nMinGlyphIndex == -1 || !mpLogClusters[ nCharPos ] )
2754 		return false;
2755 
2756 //	This test didn't give the expected results
2757 /*	if( mpLogClusters[ nCharPos+1 ] == mpLogClusters[ nCharPos ])
2758 	// two chars, one glyph
2759 		return false;*/
2760 
2761 	const int nGlyphPos = mpLogClusters[ nCharPos ] + nMinGlyphIndex;
2762 	if( nGlyphPos <= 0 )
2763 		return true;
2764 	// justification is only allowed if the glyph to the left has not SCRIPT_JUSTIFY_NONE
2765 	// and not SCRIPT_JUSTIFY_ARABIC_BLANK
2766 	// special case: glyph to the left is vowel (no advance width)
2767 	if ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK
2768 		|| ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_NONE
2769 			&& mpGlyphAdvances [ nGlyphPos-1 ] ))
2770 		return false;
2771 	return true;
2772 }
2773 
2774 #endif // USE_UNISCRIBE
2775 
2776 #ifdef ENABLE_GRAPHITE
2777 
2778 class GraphiteLayoutWinImpl : public GraphiteLayout
2779 {
2780 public:
2781     GraphiteLayoutWinImpl(const gr::Font & font, ImplWinFontEntry & rFont)
2782         throw()
2783     : GraphiteLayout(font), mrFont(rFont) {};
2784     virtual ~GraphiteLayoutWinImpl() throw() {};
2785     virtual sal_GlyphId getKashidaGlyph(int & rWidth);
2786 private:
2787     ImplWinFontEntry & mrFont;
2788 };
2789 
2790 sal_GlyphId GraphiteLayoutWinImpl::getKashidaGlyph(int & rWidth)
2791 {
2792     rWidth = mrFont.GetMinKashidaWidth();
2793     return mrFont.GetMinKashidaGlyph();
2794 }
2795 
2796 // This class uses the SIL Graphite engine to provide complex text layout services to the VCL
2797 // @author tse
2798 //
2799 class GraphiteWinLayout : public WinLayout
2800 {
2801 private:
2802     mutable GraphiteWinFont mpFont;
2803     grutils::GrFeatureParser * mpFeatures;
2804     mutable GraphiteLayoutWinImpl maImpl;
2805 public:
2806     GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE);
2807 
2808     static bool IsGraphiteEnabledFont(HDC hDC) throw();
2809 
2810     // used by upper layers
2811     virtual bool  LayoutText( ImplLayoutArgs& );    // first step of layout
2812     virtual void  AdjustLayout( ImplLayoutArgs& );  // adjusting after fallback etc.
2813     //  virtual void  InitFont() const;
2814     virtual void  DrawText( SalGraphics& ) const;
2815 
2816     // methods using string indexing
2817     virtual int   GetTextBreak( long nMaxWidth, long nCharExtra=0, int nFactor=1 ) const;
2818     virtual long  FillDXArray( long* pDXArray ) const;
2819 
2820     virtual void  GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
2821 
2822     // methods using glyph indexing
2823     virtual int   GetNextGlyphs(int nLen, sal_GlyphId* pGlyphIdxAry, ::Point & rPos, int&,
2824                       long* pGlyphAdvAry = 0, int* pCharPosAry = 0 ) const;
2825 
2826     // used by glyph+font+script fallback
2827     virtual void    MoveGlyph( int nStart, long nNewXPos );
2828     virtual void    DropGlyph( int nStart );
2829     virtual void    Simplify( bool bIsBase );
2830     ~GraphiteWinLayout() { delete mpFeatures; mpFeatures = NULL; };
2831 protected:
2832     virtual void    ReplaceDC(gr::Segment & segment) const;
2833     virtual void    RestoreDC(gr::Segment & segment) const;
2834 };
2835 
2836 bool GraphiteWinLayout::IsGraphiteEnabledFont(HDC hDC) throw()
2837 {
2838   return gr::WinFont::FontHasGraphiteTables(hDC);
2839 }
2840 
2841 GraphiteWinLayout::GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE) throw()
2842   : WinLayout(hDC, rWFD, rWFE), mpFont(hDC),
2843     maImpl(mpFont, rWFE)
2844 {
2845     const rtl::OString aLang = MsLangId::convertLanguageToIsoByteString( rWFE.maFontSelData.meLanguage );
2846     rtl::OString name = rtl::OUStringToOString(
2847         rWFE.maFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
2848     sal_Int32 nFeat = name.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + 1;
2849     if (nFeat > 0)
2850     {
2851         rtl::OString aFeat = name.copy(nFeat, name.getLength() - nFeat);
2852         mpFeatures = new grutils::GrFeatureParser(mpFont, aFeat.getStr(), aLang.getStr());
2853     }
2854     else
2855     {
2856         mpFeatures = new grutils::GrFeatureParser(mpFont, aLang.getStr());
2857     }
2858     maImpl.SetFeatures(mpFeatures);
2859 }
2860 
2861 void GraphiteWinLayout::ReplaceDC(gr::Segment & segment) const
2862 {
2863     COLORREF color = GetTextColor(mhDC);
2864     dynamic_cast<gr::WinFont&>(segment.getFont()).replaceDC(mhDC);
2865     SetTextColor(mhDC, color);
2866 }
2867 
2868 void GraphiteWinLayout::RestoreDC(gr::Segment & segment) const
2869 {
2870     dynamic_cast<gr::WinFont&>(segment.getFont()).restoreDC();
2871 }
2872 
2873 bool GraphiteWinLayout::LayoutText( ImplLayoutArgs & args)
2874 {
2875     if (args.mnMinCharPos >= args.mnEndCharPos)
2876     {
2877         maImpl.clear();
2878         return true;
2879     }
2880     HFONT hUnRotatedFont;
2881     if (args.mnOrientation)
2882     {
2883         // Graphite gets very confused if the font is rotated
2884         LOGFONTW aLogFont;
2885         ::GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont);
2886         aLogFont.lfEscapement = 0;
2887         aLogFont.lfOrientation = 0;
2888         hUnRotatedFont = ::CreateFontIndirectW( &aLogFont);
2889         ::SelectFont(mhDC, hUnRotatedFont);
2890     }
2891     WinLayout::AdjustLayout(args);
2892     mpFont.replaceDC(mhDC);
2893     maImpl.SetFontScale(WinLayout::mfFontScale);
2894     //bool succeeded = maImpl.LayoutText(args);
2895 #ifdef GRCACHE
2896     GrSegRecord * pSegRecord = NULL;
2897     gr::Segment * pSegment = maImpl.CreateSegment(args, &pSegRecord);
2898 #else
2899     gr::Segment * pSegment = maImpl.CreateSegment(args);
2900 #endif
2901     bool bSucceeded = false;
2902     if (pSegment)
2903     {
2904         // replace the DC on the font within the segment
2905         ReplaceDC(*pSegment);
2906         // create glyph vectors
2907 #ifdef GRCACHE
2908         bSucceeded = maImpl.LayoutGlyphs(args, pSegment, pSegRecord);
2909 #else
2910         bSucceeded = maImpl.LayoutGlyphs(args, pSegment);
2911 #endif
2912         // restore original DC
2913         RestoreDC(*pSegment);
2914 #ifdef GRCACHE
2915         if (pSegRecord) pSegRecord->unlock();
2916         else delete pSegment;
2917 #else
2918         delete pSegment;
2919 #endif
2920     }
2921     mpFont.restoreDC();
2922     if (args.mnOrientation)
2923     {
2924         // restore the rotated font
2925         ::SelectFont(mhDC, mhFont);
2926         ::DeleteObject(hUnRotatedFont);
2927     }
2928     return bSucceeded;
2929 }
2930 
2931 void  GraphiteWinLayout::AdjustLayout(ImplLayoutArgs& rArgs)
2932 {
2933     WinLayout::AdjustLayout(rArgs);
2934     maImpl.DrawBase() = WinLayout::maDrawBase;
2935     maImpl.DrawOffset() = WinLayout::maDrawOffset;
2936     if ( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) && rArgs.mpDXArray)
2937     {
2938         mrWinFontEntry.InitKashidaHandling(mhDC);
2939     }
2940     maImpl.AdjustLayout(rArgs);
2941 }
2942 
2943 void GraphiteWinLayout::DrawText(SalGraphics &sal_graphics) const
2944 {
2945     HFONT hOrigFont = DisableFontScaling();
2946     HDC aHDC = static_cast<WinSalGraphics&>(sal_graphics).mhDC;
2947     maImpl.DrawBase() = WinLayout::maDrawBase;
2948     maImpl.DrawOffset() = WinLayout::maDrawOffset;
2949     const int MAX_GLYPHS = 2;
2950     sal_GlyphId glyphIntStr[MAX_GLYPHS];
2951     WORD glyphWStr[MAX_GLYPHS];
2952     int glyphIndex = 0;
2953     Point aPos(0,0);
2954     int nGlyphs = 0;
2955     do
2956     {
2957         nGlyphs = maImpl.GetNextGlyphs(1, glyphIntStr, aPos, glyphIndex);
2958         if (nGlyphs < 1)
2959           break;
2960         std::copy(glyphIntStr, glyphIntStr + nGlyphs, glyphWStr);
2961         ::ExtTextOutW(aHDC, aPos.X(), aPos.Y(), ETO_GLYPH_INDEX,
2962 		              NULL, (LPCWSTR)&(glyphWStr), nGlyphs, NULL);
2963     } while (nGlyphs);
2964     if( hOrigFont )
2965           DeleteFont( SelectFont( mhDC, hOrigFont ) );
2966 }
2967 
2968 int GraphiteWinLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
2969 {
2970     mpFont.replaceDC(mhDC);
2971     int nBreak = maImpl.GetTextBreak(nMaxWidth, nCharExtra, nFactor);
2972     mpFont.restoreDC();
2973     return nBreak;
2974 }
2975 
2976 long  GraphiteWinLayout::FillDXArray( long* pDXArray ) const
2977 {
2978     return maImpl.FillDXArray(pDXArray);
2979 }
2980 
2981 void GraphiteWinLayout::GetCaretPositions( int nArraySize, long* pCaretXArray ) const
2982 {
2983 	maImpl.GetCaretPositions(nArraySize, pCaretXArray);
2984 }
2985 
2986 int GraphiteWinLayout::GetNextGlyphs( int length, sal_GlyphId* glyph_out,
2987         ::Point & pos_out, int &glyph_slot, long * glyph_adv, int *char_index) const
2988 {
2989     maImpl.DrawBase() = WinLayout::maDrawBase;
2990     maImpl.DrawOffset() = WinLayout::maDrawOffset;
2991     return maImpl.GetNextGlyphs(length, glyph_out, pos_out, glyph_slot, glyph_adv, char_index);
2992 }
2993 
2994 void GraphiteWinLayout::MoveGlyph( int glyph_idx, long new_x_pos )
2995 {
2996 	maImpl.MoveGlyph(glyph_idx, new_x_pos);
2997 }
2998 
2999 void GraphiteWinLayout::DropGlyph( int glyph_idx )
3000 {
3001 	maImpl.DropGlyph(glyph_idx);
3002 }
3003 
3004 void GraphiteWinLayout::Simplify( bool is_base )
3005 {
3006 	maImpl.Simplify(is_base);
3007 }
3008 #endif // ENABLE_GRAPHITE
3009 // =======================================================================
3010 
3011 SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel )
3012 {
3013     DBG_ASSERT( mpWinFontEntry[nFallbackLevel], "WinSalGraphics mpWinFontEntry==NULL");
3014 
3015 	WinLayout* pWinLayout = NULL;
3016 
3017     const ImplWinFontData& rFontFace = *mpWinFontData[ nFallbackLevel ];
3018     ImplWinFontEntry& rFontInstance = *mpWinFontEntry[ nFallbackLevel ];
3019 
3020 #if defined( USE_UNISCRIBE )
3021     if( !(rArgs.mnFlags & SAL_LAYOUT_COMPLEX_DISABLED)
3022     &&   (aUspModule || (bUspEnabled && InitUSP())) )   // CTL layout engine
3023     {
3024 #ifdef ENABLE_GRAPHITE
3025         if (rFontFace.SupportsGraphite())
3026             pWinLayout = new GraphiteWinLayout(mhDC, rFontFace, rFontInstance);
3027         else
3028 #endif // ENABLE_GRAPHITE
3029         // script complexity is determined in upper layers
3030         pWinLayout = new UniscribeLayout( mhDC, rFontFace, rFontInstance );
3031         // NOTE: it must be guaranteed that the WinSalGraphics lives longer than
3032         // the created UniscribeLayout, otherwise the data passed into the
3033         // constructor might become invalid too early
3034     }
3035     else
3036 #endif // USE_UNISCRIBE
3037     {
3038 #ifdef GCP_KERN_HACK
3039         if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) && !rFontInstance.HasKernData() )
3040         {
3041             // TODO: directly cache kerning info in the rFontInstance
3042             // TODO: get rid of kerning methods+data in WinSalGraphics object
3043             GetKernPairs( 0, NULL );
3044             rFontInstance.SetKernData( mnFontKernPairCount, mpFontKernPairs );
3045         }
3046 #endif // GCP_KERN_HACK
3047 
3048         BYTE eCharSet = ANSI_CHARSET;
3049         if( mpLogFont )
3050             eCharSet = mpLogFont->lfCharSet;
3051 #ifdef ENABLE_GRAPHITE
3052         if (rFontFace.SupportsGraphite())
3053             pWinLayout = new GraphiteWinLayout(mhDC, rFontFace, rFontInstance);
3054         else
3055 #endif // ENABLE_GRAPHITE
3056             pWinLayout = new SimpleWinLayout( mhDC, eCharSet, rFontFace, rFontInstance );
3057     }
3058 
3059     if( mfFontScale != 1.0 )
3060         pWinLayout->SetFontScale( mfFontScale );
3061 
3062     return pWinLayout;
3063 }
3064 
3065 // -----------------------------------------------------------------------
3066 
3067 int	WinSalGraphics::GetMinKashidaWidth()
3068 {
3069 	if( !mpWinFontEntry[0] )
3070 		return 0;
3071 	mpWinFontEntry[0]->InitKashidaHandling( mhDC );
3072 	int nMinKashida = static_cast<int>(mfFontScale * mpWinFontEntry[0]->GetMinKashidaWidth());
3073 	return nMinKashida;
3074 }
3075 
3076 // =======================================================================
3077 
3078 ImplWinFontEntry::ImplWinFontEntry( ImplFontSelectData& rFSD )
3079 :   ImplFontEntry( rFSD )
3080 ,   maWidthMap( 512 )
3081 ,   mpKerningPairs( NULL )
3082 ,   mnKerningPairs( -1 )
3083 ,	mnMinKashidaWidth( -1 )
3084 ,	mnMinKashidaGlyph( -1 )
3085 {
3086 #ifdef USE_UNISCRIBE
3087     maScriptCache = NULL;
3088 #endif // USE_UNISCRIBE
3089 }
3090 
3091 // -----------------------------------------------------------------------
3092 
3093 ImplWinFontEntry::~ImplWinFontEntry()
3094 {
3095 #ifdef USE_UNISCRIBE
3096     if( maScriptCache != NULL )
3097         (*pScriptFreeCache)( &maScriptCache );
3098 #endif // USE_UNISCRIBE
3099 #ifdef GCP_KERN_HACK
3100     delete[] mpKerningPairs;
3101 #endif // GCP_KERN_HACK
3102 }
3103 
3104 // -----------------------------------------------------------------------
3105 
3106 bool ImplWinFontEntry::HasKernData() const
3107 {
3108     return (mnKerningPairs >= 0);
3109 }
3110 
3111 // -----------------------------------------------------------------------
3112 
3113 void ImplWinFontEntry::SetKernData( int nPairCount, const KERNINGPAIR* pPairData )
3114 {
3115     mnKerningPairs = nPairCount;
3116     mpKerningPairs = new KERNINGPAIR[ mnKerningPairs ];
3117     ::memcpy( mpKerningPairs, (const void*)pPairData, nPairCount*sizeof(KERNINGPAIR) );
3118 }
3119 
3120 // -----------------------------------------------------------------------
3121 
3122 int ImplWinFontEntry::GetKerning( sal_Unicode cLeft, sal_Unicode cRight ) const
3123 {
3124     int nKernAmount = 0;
3125     if( mpKerningPairs )
3126     {
3127         const KERNINGPAIR aRefPair = { cLeft, cRight, 0 };
3128         const KERNINGPAIR* pFirstPair = mpKerningPairs;
3129         const KERNINGPAIR* pEndPair = mpKerningPairs + mnKerningPairs;
3130         const KERNINGPAIR* pPair = std::lower_bound( pFirstPair,
3131             pEndPair, aRefPair, ImplCmpKernData );
3132         if( (pPair != pEndPair)
3133         &&  (pPair->wFirst == aRefPair.wFirst)
3134         &&  (pPair->wSecond == aRefPair.wSecond) )
3135             nKernAmount = pPair->iKernAmount;
3136     }
3137 
3138     return nKernAmount;
3139 }
3140 
3141 // -----------------------------------------------------------------------
3142 
3143 bool ImplWinFontEntry::InitKashidaHandling( HDC hDC )
3144 {
3145 	if( mnMinKashidaWidth >= 0 )	// already cached?
3146 		return mnMinKashidaWidth;
3147 
3148 	// initialize the kashida width
3149 	mnMinKashidaWidth = 0;
3150 	mnMinKashidaGlyph = 0;
3151 #ifdef USE_UNISCRIBE
3152 	if (aUspModule || (bUspEnabled && InitUSP()))
3153 	{
3154 		SCRIPT_FONTPROPERTIES aFontProperties;
3155 		aFontProperties.cBytes = sizeof (aFontProperties);
3156 		SCRIPT_CACHE& rScriptCache = GetScriptCache();
3157 		HRESULT nRC = (*pScriptGetFontProperties)( hDC, &rScriptCache, &aFontProperties );
3158 		if( nRC != 0 )
3159 			return false;
3160 		mnMinKashidaWidth = aFontProperties.iKashidaWidth;
3161 		mnMinKashidaGlyph = aFontProperties.wgKashida;
3162     }
3163 #endif // USE_UNISCRIBE
3164 
3165 	return true;
3166 }
3167 
3168 // =======================================================================
3169 
3170 ImplFontData* ImplWinFontData::Clone() const
3171 {
3172     if( mpUnicodeMap )
3173         mpUnicodeMap->AddReference();
3174     ImplFontData* pClone = new ImplWinFontData( *this );
3175     return pClone;
3176 }
3177 
3178 // -----------------------------------------------------------------------
3179 
3180 ImplFontEntry* ImplWinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const
3181 {
3182     ImplFontEntry* pEntry = new ImplWinFontEntry( rFSD );
3183     return pEntry;
3184 }
3185 
3186 // =======================================================================
3187