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