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