merge the formfield patch from ooo-build
[ooovba.git] / vcl / win / source / gdi / winlayout.cxx
blob8650bfe5ebc6c035ce8260c8902168e03ebac3f5
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: winlayout.cxx,v $
10 * $Revision: 1.113.6.9 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
34 #include "tools/svwin.h"
36 #include "salgdi.h"
37 #include "saldata.hxx"
38 // for GetMirroredChar
39 #include "sft.hxx"
41 #include "vcl/sallayout.hxx"
42 #include "vcl/svapp.hxx"
44 #include "rtl/ustring.hxx"
46 #include "osl/module.h"
47 #include "osl/file.h"
50 #include <cstdio>
51 #include <malloc.h>
52 #ifndef __MINGW32__
53 #define alloca _alloca
54 #endif
56 #ifdef GCP_KERN_HACK
57 #include <algorithm>
58 #endif // GCP_KERN_HACK
61 #define USE_UNISCRIBE
62 #ifdef USE_UNISCRIBE
63 #include <Usp10.h>
64 #include <ShLwApi.h>
65 #include <winver.h>
66 #endif // USE_UNISCRIBE
68 #include <hash_map>
69 #include <set>
71 typedef std::hash_map<int,int> IntMap;
72 typedef std::set<int> IntSet;
74 // Graphite headers
75 #ifdef ENABLE_GRAPHITE
76 #include <i18npool/mslangid.hxx>
77 #include <graphite/GrClient.h>
78 #include <graphite/WinFont.h>
79 #include <graphite/Segment.h>
80 #include <vcl/graphite_layout.hxx>
81 #include <vcl/graphite_cache.hxx>
82 #include <vcl/graphite_features.hxx>
83 #endif
85 #define DROPPED_OUTGLYPH 0xFFFF
87 using namespace rtl;
89 // =======================================================================
91 // win32 specific physical font instance
92 class ImplWinFontEntry : public ImplFontEntry
94 public:
95 ImplWinFontEntry( ImplFontSelectData& );
96 ~ImplWinFontEntry();
98 private:
99 // TODO: also add HFONT??? Watch out for issues with too many active fonts...
101 #ifdef GCP_KERN_HACK
102 public:
103 bool HasKernData() const;
104 void SetKernData( int, const KERNINGPAIR* );
105 int GetKerning( sal_Unicode, sal_Unicode ) const;
106 private:
107 KERNINGPAIR* mpKerningPairs;
108 int mnKerningPairs;
109 #endif // GCP_KERN_HACK
111 #ifdef USE_UNISCRIBE
112 public:
113 SCRIPT_CACHE& GetScriptCache() const
114 { return maScriptCache; }
115 private:
116 mutable SCRIPT_CACHE maScriptCache;
117 #endif // USE_UNISCRIBE
119 public:
120 int GetCachedGlyphWidth( int nCharCode ) const;
121 void CacheGlyphWidth( int nCharCode, int nCharWidth );
123 bool InitKashidaHandling( HDC );
124 int GetMinKashidaWidth() const { return mnMinKashidaWidth; }
125 int GetMinKashidaGlyph() const { return mnMinKashidaGlyph; }
127 private:
128 IntMap maWidthMap;
129 mutable int mnMinKashidaWidth;
130 mutable int mnMinKashidaGlyph;
133 // -----------------------------------------------------------------------
135 inline void ImplWinFontEntry::CacheGlyphWidth( int nCharCode, int nCharWidth )
137 maWidthMap[ nCharCode ] = nCharWidth;
140 inline int ImplWinFontEntry::GetCachedGlyphWidth( int nCharCode ) const
142 IntMap::const_iterator it = maWidthMap.find( nCharCode );
143 if( it == maWidthMap.end() )
144 return -1;
145 return it->second;
148 // =======================================================================
150 class WinLayout : public SalLayout
152 public:
153 WinLayout( HDC, const ImplWinFontData&, ImplWinFontEntry& );
154 virtual void InitFont() const;
155 void SetFontScale( float f ) { mfFontScale = f; }
156 float GetFontScale() const { return mfFontScale; }
157 HFONT DisableFontScaling( void) const;
159 #ifdef USE_UNISCRIBE
160 SCRIPT_CACHE& GetScriptCache() const
161 { return mrWinFontEntry.GetScriptCache(); }
162 #endif // USE_UNISCRIBE
164 protected:
165 HDC mhDC; // WIN32 device handle
166 HFONT mhFont; // WIN32 font handle
167 int mnBaseAdv; // x-offset relative to Layout origin
168 float mfFontScale; // allows metrics emulation of huge font sizes
170 const ImplWinFontData& mrWinFontData;
171 ImplWinFontEntry& mrWinFontEntry;
174 // =======================================================================
176 class SimpleWinLayout : public WinLayout
178 public:
179 SimpleWinLayout( HDC, BYTE nCharSet, const ImplWinFontData&, ImplWinFontEntry& );
180 virtual ~SimpleWinLayout();
182 virtual bool LayoutText( ImplLayoutArgs& );
183 virtual void AdjustLayout( ImplLayoutArgs& );
184 virtual void DrawText( SalGraphics& ) const;
186 virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&,
187 sal_Int32* pGlyphAdvances, int* pCharIndexes ) const;
189 virtual long FillDXArray( long* pDXArray ) const;
190 virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
191 virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
193 // for glyph+font+script fallback
194 virtual void MoveGlyph( int nStart, long nNewXPos );
195 virtual void DropGlyph( int nStart );
196 virtual void Simplify( bool bIsBase );
198 protected:
199 void Justify( long nNewWidth );
200 void ApplyDXArray( const ImplLayoutArgs& );
202 private:
203 int mnGlyphCount;
204 int mnCharCount;
205 WCHAR* mpOutGlyphs;
206 int* mpGlyphAdvances; // if possible this is shared with mpGlyphAdvances[]
207 int* mpGlyphOrigAdvs;
208 int* mpCharWidths; // map rel char pos to char width
209 int* mpChars2Glyphs; // map rel char pos to abs glyph pos
210 int* mpGlyphs2Chars; // map abs glyph pos to abs char pos
211 bool* mpGlyphRTLFlags; // BiDi status for glyphs: true=>RTL
212 mutable long mnWidth;
213 bool mbDisableGlyphs;
215 int mnNotdefWidth;
216 BYTE mnCharSet;
219 // =======================================================================
221 WinLayout::WinLayout( HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE )
222 : mhDC( hDC ),
223 mhFont( (HFONT)::GetCurrentObject(hDC,OBJ_FONT) ),
224 mnBaseAdv( 0 ),
225 mfFontScale( 1.0 ),
226 mrWinFontData( rWFD ),
227 mrWinFontEntry( rWFE )
230 // -----------------------------------------------------------------------
232 void WinLayout::InitFont() const
234 ::SelectObject( mhDC, mhFont );
237 // -----------------------------------------------------------------------
239 // Using reasonably sized fonts to emulate huge fonts works around
240 // a lot of problems in printer and display drivers. Huge fonts are
241 // mostly used by high resolution reference devices which are never
242 // painted to anyway. In the rare case that a huge font needs to be
243 // displayed somewhere then the workaround doesn't help anymore.
244 // If the drivers fail silently for huge fonts, so be it...
245 HFONT WinLayout::DisableFontScaling() const
247 if( mfFontScale == 1.0 )
248 return 0;
250 HFONT hHugeFont = 0;
251 if( aSalShlData.mbWNT )
253 LOGFONTW aLogFont;
254 ::GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont);
255 aLogFont.lfHeight = (LONG)(mfFontScale * aLogFont.lfHeight);
256 aLogFont.lfWidth = (LONG)(mfFontScale * aLogFont.lfWidth);
257 hHugeFont = ::CreateFontIndirectW( &aLogFont);
259 else
261 LOGFONTA aLogFont;
262 ::GetObjectA( mhFont, sizeof(LOGFONTA), &aLogFont);
263 aLogFont.lfHeight = (LONG)(mfFontScale * aLogFont.lfHeight);
264 aLogFont.lfWidth = (LONG)(mfFontScale * aLogFont.lfWidth);
265 hHugeFont = ::CreateFontIndirectA( &aLogFont);
268 if( !hHugeFont )
269 return 0;
271 return SelectFont( mhDC, hHugeFont );
274 // =======================================================================
276 SimpleWinLayout::SimpleWinLayout( HDC hDC, BYTE nCharSet,
277 const ImplWinFontData& rWinFontData, ImplWinFontEntry& rWinFontEntry )
278 : WinLayout( hDC, rWinFontData, rWinFontEntry ),
279 mnGlyphCount( 0 ),
280 mnCharCount( 0 ),
281 mpOutGlyphs( NULL ),
282 mpGlyphAdvances( NULL ),
283 mpGlyphOrigAdvs( NULL ),
284 mpCharWidths( NULL ),
285 mpChars2Glyphs( NULL ),
286 mpGlyphs2Chars( NULL ),
287 mpGlyphRTLFlags( NULL ),
288 mnWidth( 0 ),
289 mnNotdefWidth( -1 ),
290 mnCharSet( nCharSet ),
291 mbDisableGlyphs( false )
293 mbDisableGlyphs = true;
296 // -----------------------------------------------------------------------
298 SimpleWinLayout::~SimpleWinLayout()
300 delete[] mpGlyphRTLFlags;
301 delete[] mpGlyphs2Chars;
302 delete[] mpChars2Glyphs;
303 if( mpCharWidths != mpGlyphAdvances )
304 delete[] mpCharWidths;
305 delete[] mpGlyphOrigAdvs;
306 delete[] mpGlyphAdvances;
307 delete[] mpOutGlyphs;
310 // -----------------------------------------------------------------------
312 bool SimpleWinLayout::LayoutText( ImplLayoutArgs& rArgs )
314 // prepare layout
315 // TODO: fix case when recyclying old SimpleWinLayout object
316 mbDisableGlyphs |= ((rArgs.mnFlags & SAL_LAYOUT_DISABLE_GLYPH_PROCESSING) != 0);
317 mnCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
319 if( !mbDisableGlyphs )
321 // Win32 glyph APIs have serious problems with vertical layout
322 // => workaround is to use the unicode methods then
323 if( rArgs.mnFlags & SAL_LAYOUT_VERTICAL )
324 mbDisableGlyphs = true;
325 else
326 // use cached value from font face
327 mbDisableGlyphs = mrWinFontData.IsGlyphApiDisabled();
330 // TODO: use a cached value for bDisableAsianKern from upper layers
331 if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
333 TEXTMETRICA aTextMetricA;
334 if( ::GetTextMetricsA( mhDC, &aTextMetricA )
335 && !(aTextMetricA.tmPitchAndFamily & TMPF_FIXED_PITCH) && !(aTextMetricA.tmCharSet == 0x86) )
336 rArgs.mnFlags &= ~SAL_LAYOUT_KERNING_ASIAN;
339 // layout text
340 int i, j;
342 mnGlyphCount = 0;
343 bool bVertical = (rArgs.mnFlags & SAL_LAYOUT_VERTICAL) != 0;
345 // count the number of chars to process if no RTL run
346 rArgs.ResetPos();
347 bool bHasRTL = false;
348 while( rArgs.GetNextRun( &i, &j, &bHasRTL ) && !bHasRTL )
349 mnGlyphCount += j - i;
351 // if there are RTL runs we need room to remember individual BiDi flags
352 if( bHasRTL )
354 mpGlyphRTLFlags = new bool[ mnCharCount ];
355 for( i = 0; i < mnCharCount; ++i )
356 mpGlyphRTLFlags[i] = false;
359 // rewrite the logical string if needed to prepare for the API calls
360 const sal_Unicode* pBidiStr = rArgs.mpStr + rArgs.mnMinCharPos;
361 if( (mnGlyphCount != mnCharCount) || bVertical )
363 // we need to rewrite the pBidiStr when any of
364 // - BiDirectional layout
365 // - vertical layout
366 // - partial runs (e.g. with control chars or for glyph fallback)
367 // are involved
368 sal_Unicode* pRewrittenStr = (sal_Unicode*)alloca( mnCharCount * sizeof(sal_Unicode) );
369 pBidiStr = pRewrittenStr;
371 // note: glyph to char mapping is relative to first character
372 mpChars2Glyphs = new int[ mnCharCount ];
373 mpGlyphs2Chars = new int[ mnCharCount ];
374 for( i = 0; i < mnCharCount; ++i )
375 mpChars2Glyphs[i] = mpGlyphs2Chars[i] = -1;
377 mnGlyphCount = 0;
378 rArgs.ResetPos();
379 bool bIsRTL = false;
380 while( rArgs.GetNextRun( &i, &j, &bIsRTL ) )
384 // get the next leftmost character in this run
385 int nCharPos = bIsRTL ? --j : i++;
386 sal_UCS4 cChar = rArgs.mpStr[ nCharPos ];
388 // in the RTL case mirror the character and remember its RTL status
389 if( bIsRTL )
391 cChar = ::GetMirroredChar( cChar );
392 mpGlyphRTLFlags[ mnGlyphCount ] = true;
395 // for vertical writing use vertical alternatives
396 if( bVertical )
398 sal_UCS4 cVert = ::GetVerticalChar( cChar );
399 if( cVert )
400 cChar = cVert;
403 // rewrite the original string
404 // update the mappings between original and rewritten string
405 // TODO: support surrogates in rewritten strings
406 pRewrittenStr[ mnGlyphCount ] = static_cast<sal_Unicode>(cChar);
407 mpGlyphs2Chars[ mnGlyphCount ] = nCharPos;
408 mpChars2Glyphs[ nCharPos - rArgs.mnMinCharPos ] = mnGlyphCount;
409 ++mnGlyphCount;
410 } while( i < j );
414 mpOutGlyphs = new WCHAR[ mnGlyphCount ];
415 mpGlyphAdvances = new int[ mnGlyphCount ];
417 if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_PAIRS | SAL_LAYOUT_KERNING_ASIAN) )
418 mpGlyphOrigAdvs = new int[ mnGlyphCount ];
420 #ifndef GCP_KERN_HACK
421 DWORD nGcpOption = 0;
422 // enable kerning if requested
423 if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS )
424 nGcpOption |= GCP_USEKERNING;
425 #endif // GCP_KERN_HACK
427 for( i = 0; i < mnGlyphCount; ++i )
428 mpOutGlyphs[i] = pBidiStr[ i ];
429 mnWidth = 0;
430 for( i = 0; i < mnGlyphCount; ++i )
432 // get the current UCS-4 code point, check for surrogate pairs
433 const WCHAR* pCodes = reinterpret_cast<LPCWSTR>(&pBidiStr[i]);
434 unsigned nCharCode = pCodes[0];
435 bool bSurrogate = ((nCharCode >= 0xD800) && (nCharCode <= 0xDFFF));
436 if( bSurrogate )
438 if( nCharCode >= 0xDC00 ) // this part of a surrogate pair was already processed
439 continue;
440 nCharCode = 0x10000 + ((pCodes[0] - 0xD800) << 10) + (pCodes[1] - 0xDC00);
443 // get the advance width for the current UCS-4 code point
444 int nGlyphWidth = mrWinFontEntry.GetCachedGlyphWidth( nCharCode );
445 if( nGlyphWidth == -1 )
447 ABC aABC;
448 SIZE aExtent;
449 if( ::GetTextExtentPoint32W( mhDC, &pCodes[0], bSurrogate ? 2 : 1, &aExtent) )
450 nGlyphWidth = aExtent.cx;
451 else if( ::GetCharABCWidthsW( mhDC, nCharCode, nCharCode, &aABC ) )
452 nGlyphWidth = aABC.abcA + aABC.abcB + aABC.abcC;
453 else if( !::GetCharWidth32W( mhDC, nCharCode, nCharCode, &nGlyphWidth )
454 && !::GetCharWidthW( mhDC, nCharCode, nCharCode, &nGlyphWidth ) )
455 nGlyphWidth = 0;
456 mrWinFontEntry.CacheGlyphWidth( nCharCode, nGlyphWidth );
458 mpGlyphAdvances[ i ] = nGlyphWidth;
459 mnWidth += nGlyphWidth;
461 // remaining codes of surrogate pair get a zero width
462 if( bSurrogate && ((i+1) < mnGlyphCount) )
463 mpGlyphAdvances[ i+1 ] = 0;
465 // check with the font face if glyph fallback is needed
466 if( mrWinFontData.HasChar( nCharCode ) )
467 continue;
469 // request glyph fallback at this position in the string
470 bool bRTL = mpGlyphRTLFlags ? mpGlyphRTLFlags[i] : false;
471 int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[i]: i + rArgs.mnMinCharPos;
472 rArgs.NeedFallback( nCharPos, bRTL );
473 if( bSurrogate && ((nCharPos+1) < rArgs.mnLength) )
474 rArgs.NeedFallback( nCharPos+1, bRTL );
476 // replace the current glyph shape with the NotDef glyph shape
477 if( rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK )
479 // when we already are layouting for glyph fallback
480 // then a new unresolved glyph is not interesting
481 mnNotdefWidth = 0;
482 mpOutGlyphs[i] = DROPPED_OUTGLYPH;
484 else
486 if( mnNotdefWidth < 0 )
488 // get the width of the NotDef glyph
489 SIZE aExtent;
490 WCHAR cNotDef = rArgs.mpStr[ nCharPos ];
491 mnNotdefWidth = 0;
492 if( ::GetTextExtentPoint32W( mhDC, &cNotDef, 1, &aExtent) )
493 mnNotdefWidth = aExtent.cx;
495 // use a better NotDef glyph
496 if( !mbDisableGlyphs && !bSurrogate )
497 mpOutGlyphs[i] = 0;
499 if( bSurrogate && ((i+1) < mnGlyphCount) )
500 mpOutGlyphs[i+1] = DROPPED_OUTGLYPH;
502 // adjust the current glyph width to the NotDef glyph width
503 mnWidth += mnNotdefWidth - mpGlyphAdvances[i];
504 mpGlyphAdvances[i] = mnNotdefWidth;
505 if( mpGlyphOrigAdvs )
506 mpGlyphOrigAdvs[i] = mnNotdefWidth;
509 #ifdef GCP_KERN_HACK
510 // apply kerning if the layout engine has not yet done it
511 if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_ASIAN|SAL_LAYOUT_KERNING_PAIRS) )
513 #else // GCP_KERN_HACK
514 // apply just asian kerning
515 if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
517 if( !(rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) )
518 #endif // GCP_KERN_HACK
519 for( i = 0; i < mnGlyphCount; ++i )
520 mpGlyphOrigAdvs[i] = mpGlyphAdvances[i];
522 // #99658# also apply asian kerning on the substring border
523 int nLen = mnGlyphCount;
524 if( rArgs.mnMinCharPos + nLen < rArgs.mnLength )
525 ++nLen;
526 for( i = 1; i < nLen; ++i )
528 #ifdef GCP_KERN_HACK
529 if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS )
531 int nKernAmount = mrWinFontEntry.GetKerning( pBidiStr[i-1], pBidiStr[i] );
532 mpGlyphAdvances[ i-1 ] += nKernAmount;
533 mnWidth += nKernAmount;
535 else if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
536 #endif // GCP_KERN_HACK
538 if( ( (0x3000 == (0xFF00 & pBidiStr[i-1])) || (0x2010 == (0xFFF0 & pBidiStr[i-1])) || (0xFF00 == (0xFF00 & pBidiStr[i-1])))
539 && ( (0x3000 == (0xFF00 & pBidiStr[i])) || (0x2010 == (0xFFF0 & pBidiStr[i])) || (0xFF00 == (0xFF00 & pBidiStr[i])) ) )
541 long nKernFirst = +CalcAsianKerning( pBidiStr[i-1], true, bVertical );
542 long nKernNext = -CalcAsianKerning( pBidiStr[i], false, bVertical );
544 long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
545 if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
547 nDelta = (nDelta * mpGlyphAdvances[i-1] + 2) / 4;
548 mpGlyphAdvances[i-1] += nDelta;
549 mnWidth += nDelta;
555 // calculate virtual char widths
556 if( !mpGlyphs2Chars )
557 mpCharWidths = mpGlyphAdvances;
558 else
560 mpCharWidths = new int[ mnCharCount ];
561 for( i = 0; i < mnCharCount; ++i )
562 mpCharWidths[ i ] = 0;
563 for( i = 0; i < mnGlyphCount; ++i )
565 int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
566 if( j >= 0 )
567 mpCharWidths[ j ] += mpGlyphAdvances[ i ];
571 // scale layout metrics if needed
572 // TODO: does it make the code more simple if the metric scaling
573 // is moved to the methods that need metric scaling (e.g. FillDXArray())?
574 if( mfFontScale != 1.0 )
576 mnWidth = (long)(mnWidth * mfFontScale);
577 mnBaseAdv = (int)(mnBaseAdv * mfFontScale);
578 for( i = 0; i < mnCharCount; ++i )
579 mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale);
580 if( mpGlyphAdvances != mpCharWidths )
581 for( i = 0; i < mnGlyphCount; ++i )
582 mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale);
583 if( mpGlyphOrigAdvs && (mpGlyphOrigAdvs != mpGlyphAdvances) )
584 for( i = 0; i < mnGlyphCount; ++i )
585 mpGlyphOrigAdvs[i] = (int)(mpGlyphOrigAdvs[i] * mfFontScale);
588 return true;
591 // -----------------------------------------------------------------------
593 int SimpleWinLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int& nStart,
594 long* pGlyphAdvances, int* pCharIndexes ) const
596 // return zero if no more glyph found
597 if( nStart >= mnGlyphCount )
598 return 0;
600 // calculate glyph position relative to layout base
601 // TODO: avoid for nStart!=0 case by reusing rPos
602 long nXOffset = mnBaseAdv;
603 for( int i = 0; i < nStart; ++i )
604 nXOffset += mpGlyphAdvances[ i ];
606 // calculate absolute position in pixel units
607 Point aRelativePos( nXOffset, 0 );
608 rPos = GetDrawPosition( aRelativePos );
610 int nCount = 0;
611 while( nCount < nLen )
613 // update return values {nGlyphIndex,nCharPos,nGlyphAdvance}
614 sal_GlyphId nGlyphIndex = mpOutGlyphs[ nStart ];
615 if( mbDisableGlyphs )
617 if( mnLayoutFlags & SAL_LAYOUT_VERTICAL )
619 const sal_UCS4 cChar = static_cast<sal_UCS4>(nGlyphIndex & GF_IDXMASK);
620 if( mrWinFontData.HasGSUBstitutions( mhDC )
621 && mrWinFontData.IsGSUBstituted( cChar ) )
622 nGlyphIndex |= GF_GSUB | GF_ROTL;
623 else
625 nGlyphIndex |= GetVerticalFlags( cChar );
626 if( (nGlyphIndex & GF_ROTMASK) == 0 )
627 nGlyphIndex |= GF_VERT;
630 nGlyphIndex |= GF_ISCHAR;
632 ++nCount;
633 *(pGlyphs++) = nGlyphIndex;
634 if( pGlyphAdvances )
635 *(pGlyphAdvances++) = mpGlyphAdvances[ nStart ];
636 if( pCharIndexes )
638 int nCharPos;
639 if( !mpGlyphs2Chars )
640 nCharPos = nStart + mnMinCharPos;
641 else
642 nCharPos = mpGlyphs2Chars[nStart];
643 *(pCharIndexes++) = nCharPos;
646 // stop at last glyph
647 if( ++nStart >= mnGlyphCount )
648 break;
650 // stop when next x-position is unexpected
651 if( !pGlyphAdvances && mpGlyphOrigAdvs )
652 if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] )
653 break;
656 return nCount;
659 // -----------------------------------------------------------------------
661 void SimpleWinLayout::DrawText( SalGraphics& rGraphics ) const
663 if( mnGlyphCount <= 0 )
664 return;
666 WinSalGraphics& rWinGraphics = static_cast<WinSalGraphics&>(rGraphics);
667 HDC aHDC = rWinGraphics.mhDC;
669 HFONT hOrigFont = DisableFontScaling();
671 UINT mnDrawOptions = ETO_GLYPH_INDEX;
672 if( mbDisableGlyphs )
673 mnDrawOptions = 0;
675 Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) );
677 // #108267#, limit the number of glyphs to avoid paint errors
678 UINT limitedGlyphCount = Min( 8192, mnGlyphCount );
679 if( mnDrawOptions || aSalShlData.mbWNT )
681 // #108267#, break up into glyph portions of a limited size required by Win32 API
682 const unsigned int maxGlyphCount = 8192;
683 UINT numGlyphPortions = mnGlyphCount / maxGlyphCount;
684 UINT remainingGlyphs = mnGlyphCount % maxGlyphCount;
686 if( numGlyphPortions )
688 // #108267#,#109387# break up string into smaller chunks
689 // the output positions will be updated by windows (SetTextAlign)
690 unsigned int i,n;
691 POINT oldPos;
692 UINT oldTa = ::GetTextAlign( aHDC );
693 ::SetTextAlign( aHDC, (oldTa & ~TA_NOUPDATECP) | TA_UPDATECP );
694 ::MoveToEx( aHDC, aPos.X(), aPos.Y(), &oldPos );
695 for( i=n=0; n<numGlyphPortions; n++, i+=maxGlyphCount )
696 ::ExtTextOutW( aHDC, 0, 0, mnDrawOptions, NULL,
697 mpOutGlyphs+i, maxGlyphCount, mpGlyphAdvances+i );
698 ::ExtTextOutW( aHDC, 0, 0, mnDrawOptions, NULL,
699 mpOutGlyphs+i, remainingGlyphs, mpGlyphAdvances+i );
700 ::MoveToEx( aHDC, oldPos.x, oldPos.y, (LPPOINT) NULL);
701 ::SetTextAlign( aHDC, oldTa );
703 else
704 ::ExtTextOutW( aHDC, aPos.X(), aPos.Y(), mnDrawOptions, NULL,
705 mpOutGlyphs, mnGlyphCount, mpGlyphAdvances );
707 else
709 // #108267#, On Win9x, we get paint errors when drawing huge strings, even when
710 // split into pieces (see above), seems to be a problem in the internal text clipping
711 // so we just cut off the string
712 if( !mpGlyphOrigAdvs )
713 ::ExtTextOutW( aHDC, aPos.X(), aPos.Y(), 0, NULL,
714 mpOutGlyphs, limitedGlyphCount, NULL );
715 else
717 // workaround for problem in #106259#
718 long nXPos = mnBaseAdv;
719 for( unsigned int i = 0; i < limitedGlyphCount; ++i )
721 ::ExtTextOutW( aHDC, aPos.X(), aPos.Y(), 0, NULL,
722 mpOutGlyphs+i, 1, NULL );
723 nXPos += mpGlyphAdvances[ i ];
724 aPos = GetDrawPosition( Point( nXPos, 0 ) );
729 if( hOrigFont )
730 DeleteFont( SelectFont( aHDC, hOrigFont ) );
733 // -----------------------------------------------------------------------
735 long SimpleWinLayout::FillDXArray( long* pDXArray ) const
737 if( !mnWidth )
739 long mnWidth = mnBaseAdv;
740 for( int i = 0; i < mnGlyphCount; ++i )
741 mnWidth += mpGlyphAdvances[ i ];
744 if( pDXArray != NULL )
746 for( int i = 0; i < mnCharCount; ++i )
747 pDXArray[ i ] = mpCharWidths[ i ];
750 return mnWidth;
753 // -----------------------------------------------------------------------
755 int SimpleWinLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
756 // NOTE: the nFactor is used to prevent rounding errors for small nCharExtra values
758 if( mnWidth )
759 if( (mnWidth * nFactor + mnCharCount * nCharExtra) <= nMaxWidth )
760 return STRING_LEN;
762 long nExtraWidth = mnBaseAdv * nFactor;
763 for( int n = 0; n < mnCharCount; ++n )
765 // skip unused characters
766 if( mpChars2Glyphs && (mpChars2Glyphs[n] < 0) )
767 continue;
768 // add char widths until max
769 nExtraWidth += mpCharWidths[ n ] * nFactor;
770 if( nExtraWidth >= nMaxWidth )
771 return (mnMinCharPos + n);
772 nExtraWidth += nCharExtra;
775 return STRING_LEN;
778 // -----------------------------------------------------------------------
780 void SimpleWinLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const
782 long nXPos = mnBaseAdv;
784 if( !mpGlyphs2Chars )
786 for( int i = 0; i < nMaxIdx; i += 2 )
788 pCaretXArray[ i ] = nXPos;
789 nXPos += mpGlyphAdvances[ i>>1 ];
790 pCaretXArray[ i+1 ] = nXPos;
793 else
795 int i;
796 for( i = 0; i < nMaxIdx; ++i )
797 pCaretXArray[ i ] = -1;
799 // assign glyph positions to character positions
800 for( i = 0; i < mnGlyphCount; ++i )
802 int nCurrIdx = mpGlyphs2Chars[ i ] - mnMinCharPos;
803 long nXRight = nXPos + mpCharWidths[ nCurrIdx ];
804 nCurrIdx *= 2;
805 if( !(mpGlyphRTLFlags && mpGlyphRTLFlags[i]) )
807 // normal positions for LTR case
808 pCaretXArray[ nCurrIdx ] = nXPos;
809 pCaretXArray[ nCurrIdx+1 ] = nXRight;
811 else
813 // reverse positions for RTL case
814 pCaretXArray[ nCurrIdx ] = nXRight;
815 pCaretXArray[ nCurrIdx+1 ] = nXPos;
817 nXPos += mpGlyphAdvances[ i ];
822 // -----------------------------------------------------------------------
824 void SimpleWinLayout::Justify( long nNewWidth )
826 long nOldWidth = mnWidth;
827 mnWidth = nNewWidth;
829 if( mnGlyphCount <= 0 )
830 return;
832 if( nNewWidth == nOldWidth )
833 return;
835 // the rightmost glyph cannot be stretched
836 const int nRight = mnGlyphCount - 1;
837 nOldWidth -= mpGlyphAdvances[ nRight ];
838 nNewWidth -= mpGlyphAdvances[ nRight ];
840 // count stretchable glyphs
841 int nStretchable = 0, i;
842 for( i = 0; i < nRight; ++i )
843 if( mpGlyphAdvances[i] >= 0 )
844 ++nStretchable;
846 // stretch these glyphs
847 int nDiffWidth = nNewWidth - nOldWidth;
848 for( i = 0; (i < nRight) && (nStretchable > 0); ++i )
850 if( mpGlyphAdvances[i] <= 0 )
851 continue;
852 int nDeltaWidth = nDiffWidth / nStretchable;
853 mpGlyphAdvances[i] += nDeltaWidth;
854 --nStretchable;
855 nDiffWidth -= nDeltaWidth;
859 // -----------------------------------------------------------------------
861 void SimpleWinLayout::AdjustLayout( ImplLayoutArgs& rArgs )
863 SalLayout::AdjustLayout( rArgs );
865 // adjust positions if requested
866 if( rArgs.mpDXArray )
867 ApplyDXArray( rArgs );
868 else if( rArgs.mnLayoutWidth )
869 Justify( rArgs.mnLayoutWidth );
870 else
871 return;
873 // recalculate virtual char widths if they were changed
874 if( mpCharWidths != mpGlyphAdvances )
876 int i;
877 if( !mpGlyphs2Chars )
879 // standard LTR case
880 for( i = 0; i < mnGlyphCount; ++i )
881 mpCharWidths[ i ] = mpGlyphAdvances[ i ];
883 else
885 // BiDi or complex case
886 for( i = 0; i < mnCharCount; ++i )
887 mpCharWidths[ i ] = 0;
888 for( i = 0; i < mnGlyphCount; ++i )
890 int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
891 if( j >= 0 )
892 mpCharWidths[ j ] += mpGlyphAdvances[ i ];
898 // -----------------------------------------------------------------------
900 void SimpleWinLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
902 // try to avoid disturbance of text flow for LSB rounding case;
903 const long* pDXArray = rArgs.mpDXArray;
905 int i = 0;
906 long nOldWidth = mnBaseAdv;
907 for(; i < mnCharCount; ++i )
909 int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
910 if( j >= 0 )
912 nOldWidth += mpGlyphAdvances[ j ];
913 int nDiff = nOldWidth - pDXArray[ i ];
915 // disabled because of #104768#
916 // works great for static text, but problems when typing
917 // if( nDiff>+1 || nDiff<-1 )
918 // only bother with changing anything when something moved
919 if( nDiff != 0 )
920 break;
923 if( i >= mnCharCount )
924 return;
926 if( !mpGlyphOrigAdvs )
928 mpGlyphOrigAdvs = new int[ mnGlyphCount ];
929 for( i = 0; i < mnGlyphCount; ++i )
930 mpGlyphOrigAdvs[ i ] = mpGlyphAdvances[ i ];
933 mnWidth = mnBaseAdv;
934 for( i = 0; i < mnCharCount; ++i )
936 int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
937 if( j >= 0 )
938 mpGlyphAdvances[j] = pDXArray[i] - mnWidth;
939 mnWidth = pDXArray[i];
943 // -----------------------------------------------------------------------
945 void SimpleWinLayout::MoveGlyph( int nStart, long nNewXPos )
947 if( nStart > mnGlyphCount )
948 return;
950 // calculate the current x-position of the requested glyph
951 // TODO: cache absolute positions
952 int nXPos = mnBaseAdv;
953 for( int i = 0; i < nStart; ++i )
954 nXPos += mpGlyphAdvances[i];
956 // calculate the difference to the current glyph position
957 int nDelta = nNewXPos - nXPos;
959 // adjust the width of the layout if it was already cached
960 if( mnWidth )
961 mnWidth += nDelta;
963 // depending on whether the requested glyph is leftmost in the layout
964 // adjust either the layout's or the requested glyph's relative position
965 if( nStart > 0 )
966 mpGlyphAdvances[ nStart-1 ] += nDelta;
967 else
968 mnBaseAdv += nDelta;
971 // -----------------------------------------------------------------------
973 void SimpleWinLayout::DropGlyph( int nStart )
975 mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH;
978 // -----------------------------------------------------------------------
980 void SimpleWinLayout::Simplify( bool /*bIsBase*/ )
982 // return early if no glyph has been dropped
983 int i = mnGlyphCount;
984 while( (--i >= 0) && (mpOutGlyphs[ i ] != DROPPED_OUTGLYPH) );
985 if( i < 0 )
986 return;
988 // convert the layout to a sparse layout if it is not already
989 if( !mpGlyphs2Chars )
991 mpGlyphs2Chars = new int[ mnGlyphCount ];
992 mpCharWidths = new int[ mnCharCount ];
993 // assertion: mnGlyphCount == mnCharCount
994 for( int k = 0; k < mnGlyphCount; ++k )
996 mpGlyphs2Chars[ k ] = mnMinCharPos + k;
997 mpCharWidths[ k ] = mpGlyphAdvances[ k ];
1001 // remove dropped glyphs that are rightmost in the layout
1002 for( i = mnGlyphCount; --i >= 0; )
1004 if( mpOutGlyphs[ i ] != DROPPED_OUTGLYPH )
1005 break;
1006 if( mnWidth )
1007 mnWidth -= mpGlyphAdvances[ i ];
1008 int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
1009 if( nRelCharPos >= 0 )
1010 mpCharWidths[ nRelCharPos ] = 0;
1012 mnGlyphCount = i + 1;
1014 // keep original glyph widths around
1015 if( !mpGlyphOrigAdvs )
1017 mpGlyphOrigAdvs = new int[ mnGlyphCount ];
1018 for( int k = 0; k < mnGlyphCount; ++k )
1019 mpGlyphOrigAdvs[ k ] = mpGlyphAdvances[ k ];
1022 // remove dropped glyphs inside the layout
1023 int nNewGC = 0;
1024 for( i = 0; i < mnGlyphCount; ++i )
1026 if( mpOutGlyphs[ i ] == DROPPED_OUTGLYPH )
1028 // adjust relative position to last valid glyph
1029 int nDroppedWidth = mpGlyphAdvances[ i ];
1030 mpGlyphAdvances[ i ] = 0;
1031 if( nNewGC > 0 )
1032 mpGlyphAdvances[ nNewGC-1 ] += nDroppedWidth;
1033 else
1034 mnBaseAdv += nDroppedWidth;
1036 // zero the virtual char width for the char that has a fallback
1037 int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
1038 if( nRelCharPos >= 0 )
1039 mpCharWidths[ nRelCharPos ] = 0;
1041 else
1043 if( nNewGC != i )
1045 // rearrange the glyph array to get rid of the dropped glyph
1046 mpOutGlyphs[ nNewGC ] = mpOutGlyphs[ i ];
1047 mpGlyphAdvances[ nNewGC ] = mpGlyphAdvances[ i ];
1048 mpGlyphOrigAdvs[ nNewGC ] = mpGlyphOrigAdvs[ i ];
1049 mpGlyphs2Chars[ nNewGC ] = mpGlyphs2Chars[ i ];
1051 ++nNewGC;
1055 mnGlyphCount = nNewGC;
1056 if( mnGlyphCount <= 0 )
1057 mnWidth = mnBaseAdv = 0;
1060 // =======================================================================
1062 #ifdef USE_UNISCRIBE
1064 struct VisualItem
1066 public:
1067 SCRIPT_ITEM* mpScriptItem;
1068 int mnMinGlyphPos;
1069 int mnEndGlyphPos;
1070 int mnMinCharPos;
1071 int mnEndCharPos;
1072 //long mnPixelWidth;
1073 int mnXOffset;
1074 ABC maABCWidths;
1075 bool mbHasKashidas;
1077 public:
1078 bool IsEmpty() const { return (mnEndGlyphPos <= 0); }
1079 bool IsRTL() const { return mpScriptItem->a.fRTL; }
1080 bool HasKashidas() const { return mbHasKashidas; }
1083 // -----------------------------------------------------------------------
1085 class UniscribeLayout : public WinLayout
1087 public:
1088 UniscribeLayout( HDC, const ImplWinFontData&, ImplWinFontEntry& );
1090 virtual bool LayoutText( ImplLayoutArgs& );
1091 virtual void AdjustLayout( ImplLayoutArgs& );
1092 virtual void DrawText( SalGraphics& ) const;
1093 virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&,
1094 sal_Int32* pGlyphAdvances, int* pCharPosAry ) const;
1096 virtual long FillDXArray( long* pDXArray ) const;
1097 virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
1098 virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
1099 virtual bool IsKashidaPosValid ( int nCharPos ) const;
1101 // for glyph+font+script fallback
1102 virtual void MoveGlyph( int nStart, long nNewXPos );
1103 virtual void DropGlyph( int nStart );
1104 virtual void Simplify( bool bIsBase );
1105 virtual void DisableGlyphInjection( bool bDisable ) { mbDisableGlyphInjection = bDisable; }
1107 protected:
1108 virtual ~UniscribeLayout();
1110 void Justify( long nNewWidth );
1111 void ApplyDXArray( const ImplLayoutArgs& );
1113 bool GetItemSubrange( const VisualItem&,
1114 int& rMinIndex, int& rEndIndex ) const;
1116 private:
1117 // item specific info
1118 SCRIPT_ITEM* mpScriptItems; // in logical order
1119 VisualItem* mpVisualItems; // in visual order
1120 int mnItemCount; // number of visual items
1122 // string specific info
1123 // everything is in logical order
1124 int mnCharCapacity;
1125 WORD* mpLogClusters; // map from absolute_char_pos to relative_glyph_pos
1126 int* mpCharWidths; // map from absolute_char_pos to char_width
1127 int mnSubStringMin; // char_pos of first char in context
1129 // glyph specific info
1130 // everything is in visual order
1131 int mnGlyphCount;
1132 int mnGlyphCapacity;
1133 int* mpGlyphAdvances; // glyph advance width before justification
1134 int* mpJustifications; // glyph advance width after justification
1135 WORD* mpOutGlyphs; // glyphids in visual order
1136 GOFFSET* mpGlyphOffsets; // glyph offsets to the "naive" layout
1137 SCRIPT_VISATTR* mpVisualAttrs; // glyph visual attributes
1138 mutable int* mpGlyphs2Chars; // map from absolute_glyph_pos to absolute_char_pos
1140 // kashida stuff
1141 void InitKashidaHandling();
1142 void KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos );
1143 bool KashidaWordFix( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos );
1145 int mnMinKashidaWidth;
1146 int mnMinKashidaGlyph;
1147 bool mbDisableGlyphInjection;
1150 // -----------------------------------------------------------------------
1151 // dynamic loading of usp library
1153 static oslModule aUspModule = NULL;
1154 static bool bUspEnabled = true;
1156 static HRESULT ((WINAPI *pScriptIsComplex)( const WCHAR*, int, DWORD ));
1157 static HRESULT ((WINAPI *pScriptItemize)( const WCHAR*, int, int,
1158 const SCRIPT_CONTROL*, const SCRIPT_STATE*, SCRIPT_ITEM*, int* ));
1159 static HRESULT ((WINAPI *pScriptShape)( HDC, SCRIPT_CACHE*, const WCHAR*,
1160 int, int, SCRIPT_ANALYSIS*, WORD*, WORD*, SCRIPT_VISATTR*, int* ));
1161 static HRESULT ((WINAPI *pScriptPlace)( HDC, SCRIPT_CACHE*, const WORD*, int,
1162 const SCRIPT_VISATTR*, SCRIPT_ANALYSIS*, int*, GOFFSET*, ABC* ));
1163 static HRESULT ((WINAPI *pScriptGetLogicalWidths)( const SCRIPT_ANALYSIS*,
1164 int, int, const int*, const WORD*, const SCRIPT_VISATTR*, int* ));
1165 static HRESULT ((WINAPI *pScriptApplyLogicalWidth)( const int*, int, int, const WORD*,
1166 const SCRIPT_VISATTR*, const int*, const SCRIPT_ANALYSIS*, ABC*, int* ));
1167 static HRESULT ((WINAPI *pScriptJustify)( const SCRIPT_VISATTR*,
1168 const int*, int, int, int, int* ));
1169 static HRESULT ((WINAPI *pScriptTextOut)( const HDC, SCRIPT_CACHE*,
1170 int, int, UINT, const RECT*, const SCRIPT_ANALYSIS*, const WCHAR*,
1171 int, const WORD*, int, const int*, const int*, const GOFFSET* ));
1172 static HRESULT ((WINAPI *pScriptGetFontProperties)( HDC, SCRIPT_CACHE*, SCRIPT_FONTPROPERTIES* ));
1173 static HRESULT ((WINAPI *pScriptFreeCache)( SCRIPT_CACHE* ));
1175 static bool bManualCellAlign = true;
1177 // -----------------------------------------------------------------------
1179 static bool InitUSP()
1181 OUString aLibraryName( RTL_CONSTASCII_USTRINGPARAM( "usp10" ) );
1182 aUspModule = osl_loadModule( aLibraryName.pData, SAL_LOADMODULE_DEFAULT );
1183 if( !aUspModule )
1184 return (bUspEnabled = false);
1186 pScriptIsComplex = (HRESULT (WINAPI*)(const WCHAR*,int,DWORD))
1187 osl_getAsciiFunctionSymbol( aUspModule, "ScriptIsComplex" );
1188 bUspEnabled &= (NULL != pScriptIsComplex);
1190 pScriptItemize = (HRESULT (WINAPI*)(const WCHAR*,int,int,
1191 const SCRIPT_CONTROL*,const SCRIPT_STATE*,SCRIPT_ITEM*,int*))
1192 osl_getAsciiFunctionSymbol( aUspModule, "ScriptItemize" );
1193 bUspEnabled &= (NULL != pScriptItemize);
1195 pScriptShape = (HRESULT (WINAPI*)(HDC,SCRIPT_CACHE*,const WCHAR*,
1196 int,int,SCRIPT_ANALYSIS*,WORD*,WORD*,SCRIPT_VISATTR*,int*))
1197 osl_getAsciiFunctionSymbol( aUspModule, "ScriptShape" );
1198 bUspEnabled &= (NULL != pScriptShape);
1200 pScriptPlace = (HRESULT (WINAPI*)(HDC, SCRIPT_CACHE*, const WORD*, int,
1201 const SCRIPT_VISATTR*,SCRIPT_ANALYSIS*,int*,GOFFSET*,ABC*))
1202 osl_getAsciiFunctionSymbol( aUspModule, "ScriptPlace" );
1203 bUspEnabled &= (NULL != pScriptPlace);
1205 pScriptGetLogicalWidths = (HRESULT (WINAPI*)(const SCRIPT_ANALYSIS*,
1206 int,int,const int*,const WORD*,const SCRIPT_VISATTR*,int*))
1207 osl_getAsciiFunctionSymbol( aUspModule, "ScriptGetLogicalWidths" );
1208 bUspEnabled &= (NULL != pScriptGetLogicalWidths);
1210 pScriptApplyLogicalWidth = (HRESULT (WINAPI*)(const int*,int,int,const WORD*,
1211 const SCRIPT_VISATTR*,const int*,const SCRIPT_ANALYSIS*,ABC*,int*))
1212 osl_getAsciiFunctionSymbol( aUspModule, "ScriptApplyLogicalWidth" );
1213 bUspEnabled &= (NULL != pScriptApplyLogicalWidth);
1215 pScriptJustify = (HRESULT (WINAPI*)(const SCRIPT_VISATTR*,const int*,
1216 int,int,int,int*))
1217 osl_getAsciiFunctionSymbol( aUspModule, "ScriptJustify" );
1218 bUspEnabled &= (NULL != pScriptJustify);
1220 pScriptGetFontProperties = (HRESULT (WINAPI*)( HDC,SCRIPT_CACHE*,SCRIPT_FONTPROPERTIES*))
1221 osl_getAsciiFunctionSymbol( aUspModule, "ScriptGetFontProperties" );
1222 bUspEnabled &= (NULL != pScriptGetFontProperties);
1224 pScriptTextOut = (HRESULT (WINAPI*)(const HDC,SCRIPT_CACHE*,
1225 int,int,UINT,const RECT*,const SCRIPT_ANALYSIS*,const WCHAR*,
1226 int,const WORD*,int,const int*,const int*,const GOFFSET*))
1227 osl_getAsciiFunctionSymbol( aUspModule, "ScriptTextOut" );
1228 bUspEnabled &= (NULL != pScriptTextOut);
1230 pScriptFreeCache = (HRESULT (WINAPI*)(SCRIPT_CACHE*))
1231 osl_getAsciiFunctionSymbol( aUspModule, "ScriptFreeCache" );
1232 bUspEnabled &= (NULL != pScriptFreeCache);
1234 if( !bUspEnabled )
1236 osl_unloadModule( aUspModule );
1237 aUspModule = NULL;
1240 // get the DLL version info
1241 int nUspVersion = 0;
1242 // TODO: there must be a simpler way to get the friggin version info from OSL?
1243 rtl_uString* pModuleURL = NULL;
1244 osl_getModuleURLFromAddress( (void*)pScriptIsComplex, &pModuleURL );
1245 rtl_uString* pModuleFileName = NULL;
1246 if( pModuleURL )
1247 osl_getSystemPathFromFileURL( pModuleURL, &pModuleFileName );
1248 const sal_Unicode* pModuleFileCStr = NULL;
1249 if( pModuleFileName )
1250 pModuleFileCStr = rtl_uString_getStr( pModuleFileName );
1251 if( pModuleFileCStr )
1253 DWORD nHandle;
1254 DWORD nBufSize = ::GetFileVersionInfoSizeW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), &nHandle );
1255 char* pBuffer = (char*)alloca( nBufSize );
1256 WIN_BOOL bRC = ::GetFileVersionInfoW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), nHandle, nBufSize, pBuffer );
1257 VS_FIXEDFILEINFO* pFixedFileInfo = NULL;
1258 UINT nFixedFileSize = 0;
1259 if( bRC )
1260 ::VerQueryValueW( pBuffer, const_cast<LPWSTR>(L"\\"), (void**)&pFixedFileInfo, &nFixedFileSize );
1261 if( pFixedFileInfo && pFixedFileInfo->dwSignature == 0xFEEF04BD )
1262 nUspVersion = HIWORD(pFixedFileInfo->dwProductVersionMS) * 10000
1263 + LOWORD(pFixedFileInfo->dwProductVersionMS);
1266 // #i77976# USP>=1.0600 changed the need to manually align glyphs in their cells
1267 if( nUspVersion >= 10600 )
1268 bManualCellAlign = false;
1270 return bUspEnabled;
1273 // -----------------------------------------------------------------------
1275 UniscribeLayout::UniscribeLayout( HDC hDC,
1276 const ImplWinFontData& rWinFontData, ImplWinFontEntry& rWinFontEntry )
1277 : WinLayout( hDC, rWinFontData, rWinFontEntry ),
1278 mnItemCount( 0 ),
1279 mpScriptItems( NULL ),
1280 mpVisualItems( NULL ),
1281 mpLogClusters( NULL ),
1282 mpCharWidths( NULL ),
1283 mnCharCapacity( 0 ),
1284 mnSubStringMin( 0 ),
1285 mnGlyphCapacity( 0 ),
1286 mnGlyphCount( 0 ),
1287 mpOutGlyphs( NULL ),
1288 mpGlyphAdvances( NULL ),
1289 mpJustifications( NULL ),
1290 mpGlyphOffsets( NULL ),
1291 mpVisualAttrs( NULL ),
1292 mpGlyphs2Chars( NULL ),
1293 mnMinKashidaGlyph( 0 ),
1294 mbDisableGlyphInjection( false )
1297 // -----------------------------------------------------------------------
1299 UniscribeLayout::~UniscribeLayout()
1301 delete[] mpScriptItems;
1302 delete[] mpVisualItems;
1303 delete[] mpLogClusters;
1304 delete[] mpCharWidths;
1305 delete[] mpOutGlyphs;
1306 delete[] mpGlyphAdvances;
1307 delete[] mpJustifications;
1308 delete[] mpGlyphOffsets;
1309 delete[] mpVisualAttrs;
1310 delete[] mpGlyphs2Chars;
1313 // -----------------------------------------------------------------------
1315 bool UniscribeLayout::LayoutText( ImplLayoutArgs& rArgs )
1317 // for a base layout only the context glyphs have to be dropped
1318 // => when the whole string is involved there is no extra context
1319 typedef std::vector<int> TIntVector;
1320 TIntVector aDropChars;
1321 if( rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK )
1323 // calculate superfluous context char positions
1324 aDropChars.push_back( 0 );
1325 aDropChars.push_back( rArgs.mnLength );
1326 int nMin, nEnd;
1327 bool bRTL;
1328 for( rArgs.ResetPos(); rArgs.GetNextRun( &nMin, &nEnd, &bRTL ); )
1330 aDropChars.push_back( nMin );
1331 aDropChars.push_back( nEnd );
1333 // prepare aDropChars for binary search which will allow to
1334 // not bother with visual items that will be dropped anyway
1335 std::sort( aDropChars.begin(), aDropChars.end() );
1338 // prepare layout
1339 // TODO: fix case when recyclying old UniscribeLayout object
1340 mnMinCharPos = rArgs.mnMinCharPos;
1341 mnEndCharPos = rArgs.mnEndCharPos;
1343 // determine script items from string
1345 // prepare itemization
1346 // TODO: try to avoid itemization since it costs a lot of performance
1347 SCRIPT_STATE aScriptState = {0,false,false,false,false,false,false,false,false,0,0};
1348 aScriptState.uBidiLevel = (0 != (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL));
1349 aScriptState.fOverrideDirection = (0 != (rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG));
1350 aScriptState.fDigitSubstitute = (0 != (rArgs.mnFlags & SAL_LAYOUT_SUBSTITUTE_DIGITS));
1351 aScriptState.fArabicNumContext = aScriptState.fDigitSubstitute & aScriptState.uBidiLevel;
1352 DWORD nLangId = 0; // TODO: get language from font
1353 SCRIPT_CONTROL aScriptControl = {nLangId,false,false,false,false,false,false,false,false,0};
1354 aScriptControl.fNeutralOverride = aScriptState.fOverrideDirection;
1355 aScriptControl.fContextDigits = (0 != (rArgs.mnFlags & SAL_LAYOUT_SUBSTITUTE_DIGITS));
1356 // determine relevant substring and work only on it
1357 // when Bidi status is unknown we need to look at the whole string though
1358 mnSubStringMin = 0;
1359 int nSubStringEnd = rArgs.mnLength;
1360 if( aScriptState.fOverrideDirection )
1362 // TODO: limit substring to portion limits
1363 mnSubStringMin = rArgs.mnMinCharPos - 8;
1364 if( mnSubStringMin < 0 )
1365 mnSubStringMin = 0;
1366 nSubStringEnd = rArgs.mnEndCharPos + 8;
1367 if( nSubStringEnd > rArgs.mnLength )
1368 nSubStringEnd = rArgs.mnLength;
1372 // now itemize the substring with its context
1373 for( int nItemCapacity = 16;; nItemCapacity *= 8 )
1375 mpScriptItems = new SCRIPT_ITEM[ nItemCapacity ];
1376 HRESULT nRC = (*pScriptItemize)(
1377 reinterpret_cast<LPCWSTR>(rArgs.mpStr + mnSubStringMin), nSubStringEnd - mnSubStringMin,
1378 nItemCapacity - 1, &aScriptControl, &aScriptState,
1379 mpScriptItems, &mnItemCount );
1380 if( !nRC ) // break loop when everything is correctly itemized
1381 break;
1383 // prepare bigger buffers for another itemization round
1384 delete[] mpScriptItems;
1385 mpScriptItems = NULL;
1386 if( nRC != E_OUTOFMEMORY )
1387 return false;
1388 if( nItemCapacity > (nSubStringEnd - mnSubStringMin) )
1389 return false;
1392 // calculate the order of visual items
1393 int nItem, i;
1395 // adjust char positions by substring offset
1396 for( nItem = 0; nItem <= mnItemCount; ++nItem )
1397 mpScriptItems[ nItem ].iCharPos += mnSubStringMin;
1398 // default visual item ordering
1399 mpVisualItems = new VisualItem[ mnItemCount ];
1400 for( nItem = 0; nItem < mnItemCount; ++nItem )
1402 // initialize char specific item info
1403 VisualItem& rVisualItem = mpVisualItems[ nItem ];
1404 SCRIPT_ITEM* pScriptItem = &mpScriptItems[ nItem ];
1405 rVisualItem.mpScriptItem = pScriptItem;
1406 rVisualItem.mnMinCharPos = pScriptItem[0].iCharPos;
1407 rVisualItem.mnEndCharPos = pScriptItem[1].iCharPos;
1410 // reorder visual item order if needed
1411 if( rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG )
1413 // force RTL item ordering if requested
1414 if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL )
1416 VisualItem* pVI0 = &mpVisualItems[ 0 ];
1417 VisualItem* pVI1 = &mpVisualItems[ mnItemCount ];
1418 while( pVI0 < --pVI1 )
1420 VisualItem aVtmp = *pVI0;
1421 *(pVI0++) = *pVI1;
1422 *pVI1 = aVtmp;
1426 else if( mnItemCount > 1 )
1428 // apply bidi algorithm's rule L2 on item level
1429 // TODO: use faster L2 algorithm
1430 int nMaxBidiLevel = 0;
1431 VisualItem* pVI = &mpVisualItems[0];
1432 VisualItem* const pVIend = pVI + mnItemCount;
1433 for(; pVI < pVIend; ++pVI )
1434 if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel )
1435 nMaxBidiLevel = pVI->mpScriptItem->a.s.uBidiLevel;
1437 while( --nMaxBidiLevel >= 0 )
1439 for( pVI = &mpVisualItems[0]; pVI < pVIend; )
1441 // find item range that needs reordering
1442 for(; pVI < pVIend; ++pVI )
1443 if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel )
1444 break;
1445 VisualItem* pVImin = pVI++;
1446 for(; pVI < pVIend; ++pVI )
1447 if( nMaxBidiLevel >= pVI->mpScriptItem->a.s.uBidiLevel )
1448 break;
1449 VisualItem* pVImax = pVI++;
1451 // reverse order of items in this range
1452 while( pVImin < --pVImax )
1454 VisualItem aVtmp = *pVImin;
1455 *(pVImin++) = *pVImax;
1456 *pVImax = aVtmp;
1462 // allocate arrays
1463 // TODO: when reusing object reuse old allocations or delete them
1464 // TODO: use only [nSubStringMin..nSubStringEnd) instead of [0..nSubStringEnd)
1465 mnCharCapacity = nSubStringEnd;
1466 mpLogClusters = new WORD[ mnCharCapacity ];
1467 mpCharWidths = new int[ mnCharCapacity ];
1469 mnGlyphCount = 0;
1470 mnGlyphCapacity = 16 + 4 * (nSubStringEnd - mnSubStringMin); // worst case assumption
1471 mpGlyphAdvances = new int[ mnGlyphCapacity ];
1472 mpOutGlyphs = new WORD[ mnGlyphCapacity ];
1473 mpGlyphOffsets = new GOFFSET[ mnGlyphCapacity ];
1474 mpVisualAttrs = new SCRIPT_VISATTR[ mnGlyphCapacity ];
1476 long nXOffset = 0;
1477 for( int j = mnSubStringMin; j < nSubStringEnd; ++j )
1478 mpCharWidths[j] = 0;
1480 // layout script items
1481 SCRIPT_CACHE& rScriptCache = GetScriptCache();
1482 for( nItem = 0; nItem < mnItemCount; ++nItem )
1484 VisualItem& rVisualItem = mpVisualItems[ nItem ];
1486 // initialize glyph specific item info
1487 rVisualItem.mnMinGlyphPos = mnGlyphCount;
1488 rVisualItem.mnEndGlyphPos = 0;
1489 rVisualItem.mnXOffset = nXOffset;
1490 //rVisualItem.mnPixelWidth = 0;
1492 // shortcut ignorable items
1493 if( (rArgs.mnEndCharPos <= rVisualItem.mnMinCharPos)
1494 || (rArgs.mnMinCharPos >= rVisualItem.mnEndCharPos) )
1496 for( int i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i )
1497 mpLogClusters[i] = sal::static_int_cast<WORD>(~0U);
1498 continue;
1501 // override bidi analysis if requested
1502 if( rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG )
1504 // FIXME: is this intended ?
1505 rVisualItem.mpScriptItem->a.fRTL = (aScriptState.uBidiLevel & 1);
1506 rVisualItem.mpScriptItem->a.s.uBidiLevel = aScriptState.uBidiLevel;
1507 rVisualItem.mpScriptItem->a.s.fOverrideDirection = aScriptState.fOverrideDirection;
1510 // convert the unicodes to glyphs
1511 int nGlyphCount = 0;
1512 int nCharCount = rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos;
1513 HRESULT nRC = (*pScriptShape)( mhDC, &rScriptCache,
1514 reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos),
1515 nCharCount,
1516 mnGlyphCapacity - rVisualItem.mnMinGlyphPos, // problem when >0xFFFF
1517 &rVisualItem.mpScriptItem->a,
1518 mpOutGlyphs + rVisualItem.mnMinGlyphPos,
1519 mpLogClusters + rVisualItem.mnMinCharPos,
1520 mpVisualAttrs + rVisualItem.mnMinGlyphPos,
1521 &nGlyphCount );
1523 // find and handle problems in the unicode to glyph conversion
1524 if( nRC == USP_E_SCRIPT_NOT_IN_FONT )
1526 // the whole visual item needs a fallback, but make sure that the next
1527 // fallback request is limited to the characters in the original request
1528 // => this is handled in ImplLayoutArgs::PrepareFallback()
1529 rArgs.NeedFallback( rVisualItem.mnMinCharPos, rVisualItem.mnEndCharPos,
1530 rVisualItem.IsRTL() );
1532 // don't bother to do a default layout in a fallback level
1533 if( 0 != (rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK) )
1534 continue;
1536 // the primitive layout engine is good enough for the default layout
1537 rVisualItem.mpScriptItem->a.eScript = SCRIPT_UNDEFINED;
1538 nRC = (*pScriptShape)( mhDC, &rScriptCache,
1539 reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos),
1540 nCharCount,
1541 mnGlyphCapacity - rVisualItem.mnMinGlyphPos,
1542 &rVisualItem.mpScriptItem->a,
1543 mpOutGlyphs + rVisualItem.mnMinGlyphPos,
1544 mpLogClusters + rVisualItem.mnMinCharPos,
1545 mpVisualAttrs + rVisualItem.mnMinGlyphPos,
1546 &nGlyphCount );
1548 if( nRC != 0 )
1549 continue;
1551 #if 0 // keep the glyphs for now because they are better than nothing
1552 // mark as NotDef glyphs
1553 for( i = 0; i < nGlyphCount; ++i )
1554 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 0;
1555 #endif
1557 else if( nRC != 0 )
1558 // something undefined happened => give up for this visual item
1559 continue;
1560 else // if( nRC == 0 )
1562 // check if there are any NotDef glyphs
1563 for( i = 0; i < nGlyphCount; ++i )
1564 if( 0 == mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] )
1565 break;
1566 if( i < nGlyphCount )
1568 // clip charpos limits to the layout string without context
1569 int nMinCharPos = rVisualItem.mnMinCharPos;
1570 if( nMinCharPos < rArgs.mnMinCharPos )
1571 nMinCharPos = rArgs.mnMinCharPos;
1572 int nEndCharPos = rVisualItem.mnEndCharPos;
1573 if( nEndCharPos > rArgs.mnEndCharPos )
1574 nEndCharPos = rArgs.mnEndCharPos;
1575 // request fallback for individual NotDef glyphs
1578 // ignore non-NotDef glyphs
1579 if( 0 != mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] )
1580 continue;
1581 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = DROPPED_OUTGLYPH;
1582 // request fallback for the whole cell that resulted in a NotDef glyph
1583 // TODO: optimize algorithm
1584 const bool bRTL = rVisualItem.IsRTL();
1585 if( !bRTL )
1587 // request fallback for the left-to-right cell
1588 for( int c = nMinCharPos; c < nEndCharPos; ++c )
1590 if( mpLogClusters[ c ] == i )
1592 // --> HDU/FME 2005-10-25 #i55716# skip WORDJOINER
1593 if( rArgs.mpStr[ c ] == 0x2060 )
1594 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1;
1595 else
1596 // <--
1597 rArgs.NeedFallback( c, false );
1601 else
1603 // request fallback for the right to left cell
1604 for( int c = nEndCharPos; --c >= nMinCharPos; )
1606 if( mpLogClusters[ c ] == i )
1608 // --> HDU/FME 2005-10-25 #i55716# skip WORDJOINER
1609 if( rArgs.mpStr[ c ] == 0x2060 )
1610 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1;
1611 else
1612 // <--
1613 rArgs.NeedFallback( c, true );
1617 } while( ++i < nGlyphCount );
1621 // now place the glyphs
1622 nRC = (*pScriptPlace)( mhDC, &rScriptCache,
1623 mpOutGlyphs + rVisualItem.mnMinGlyphPos,
1624 nGlyphCount,
1625 mpVisualAttrs + rVisualItem.mnMinGlyphPos,
1626 &rVisualItem.mpScriptItem->a,
1627 mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
1628 mpGlyphOffsets + rVisualItem.mnMinGlyphPos,
1629 &rVisualItem.maABCWidths );
1631 if( nRC != 0 )
1632 continue;
1634 // calculate the logical char widths from the glyph layout
1635 nRC = (*pScriptGetLogicalWidths)(
1636 &rVisualItem.mpScriptItem->a,
1637 nCharCount, nGlyphCount,
1638 mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
1639 mpLogClusters + rVisualItem.mnMinCharPos,
1640 mpVisualAttrs + rVisualItem.mnMinGlyphPos,
1641 mpCharWidths + rVisualItem.mnMinCharPos );
1643 // update the glyph counters
1644 mnGlyphCount += nGlyphCount;
1645 rVisualItem.mnEndGlyphPos = mnGlyphCount;
1647 // update nXOffset
1648 int nEndGlyphPos;
1649 if( GetItemSubrange( rVisualItem, i, nEndGlyphPos ) )
1650 for(; i < nEndGlyphPos; ++i )
1651 nXOffset += mpGlyphAdvances[ i ];
1653 // TODO: shrink glyphpos limits to match charpos/fallback limits
1654 //pVI->mnMinGlyphPos = nMinGlyphPos;
1655 //pVI->mnEndGlyphPos = nEndGlyphPos;
1657 // drop the superfluous context glyphs
1658 TIntVector::const_iterator it = aDropChars.begin();
1659 while( it != aDropChars.end() )
1661 // find matching "drop range"
1662 int nMinDropPos = *(it++); // begin of drop range
1663 if( nMinDropPos >= rVisualItem.mnEndCharPos )
1664 break;
1665 int nEndDropPos = *(it++); // end of drop range
1666 if( nEndDropPos <= rVisualItem.mnMinCharPos )
1667 continue;
1668 // clip "drop range" to visual item's char range
1669 if( nMinDropPos <= rVisualItem.mnMinCharPos )
1671 nMinDropPos = rVisualItem.mnMinCharPos;
1672 // drop the whole visual item if possible
1673 if( nEndDropPos >= rVisualItem.mnEndCharPos )
1675 rVisualItem.mnEndGlyphPos = 0;
1676 break;
1679 if( nEndDropPos > rVisualItem.mnEndCharPos )
1680 nEndDropPos = rVisualItem.mnEndCharPos;
1682 // drop the glyphs which correspond to the charpos range
1683 // drop the corresponding glyphs in the cluster
1684 for( int c = nMinDropPos; c < nEndDropPos; ++c )
1686 int nGlyphPos = mpLogClusters[c] + rVisualItem.mnMinGlyphPos;
1687 // no need to bother when the cluster was already dropped
1688 if( mpOutGlyphs[ nGlyphPos ] != DROPPED_OUTGLYPH )
1690 for(;;)
1692 mpOutGlyphs[ nGlyphPos ] = DROPPED_OUTGLYPH;
1693 // until the end of visual item
1694 if( ++nGlyphPos >= rVisualItem.mnEndGlyphPos )
1695 break;
1696 // until the next cluster start
1697 if( mpVisualAttrs[ nGlyphPos ].fClusterStart )
1698 break;
1705 // scale layout metrics if needed
1706 // TODO: does it make the code more simple if the metric scaling
1707 // is moved to the methods that need metric scaling (e.g. FillDXArray())?
1708 if( mfFontScale != 1.0 )
1710 mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale);
1712 for( i = 0; i < mnItemCount; ++i )
1713 mpVisualItems[i].mnXOffset = (int)((double)mpVisualItems[i].mnXOffset*mfFontScale);
1715 mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale);
1716 for( i = 0; i < mnGlyphCount; ++i )
1718 mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale);
1719 mpGlyphOffsets[i].du = (LONG)(mpGlyphOffsets[i].du * mfFontScale);
1720 mpGlyphOffsets[i].dv = (LONG)(mpGlyphOffsets[i].dv * mfFontScale);
1721 // mpJustifications are still NULL
1724 for( i = mnSubStringMin; i < nSubStringEnd; ++i )
1725 mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale);
1728 return true;
1731 // -----------------------------------------------------------------------
1733 // calculate the range of relevant glyphs for this visual item
1734 bool UniscribeLayout::GetItemSubrange( const VisualItem& rVisualItem,
1735 int& rMinGlyphPos, int& rEndGlyphPos ) const
1737 // return early when nothing of interest in this item
1738 if( rVisualItem.IsEmpty()
1739 || (rVisualItem.mnEndCharPos <= mnMinCharPos)
1740 || (mnEndCharPos <= rVisualItem.mnMinCharPos) )
1741 return false;
1743 // default: subrange is complete range
1744 rMinGlyphPos = rVisualItem.mnMinGlyphPos;
1745 rEndGlyphPos = rVisualItem.mnEndGlyphPos;
1747 // return early when the whole item is of interest
1748 if( (mnMinCharPos <= rVisualItem.mnMinCharPos)
1749 && (rVisualItem.mnEndCharPos <= mnEndCharPos ) )
1750 return true;
1752 // get glyph range from char range by looking at cluster boundries
1753 // TODO: optimize for case that LTR/RTL correspond to monotonous glyph indexes
1754 rMinGlyphPos = rVisualItem.mnEndGlyphPos;
1755 int nMaxGlyphPos = 0;
1757 int i = mnMinCharPos;
1758 if( i < rVisualItem.mnMinCharPos )
1759 i = rVisualItem.mnMinCharPos;
1760 int nCharPosLimit = rVisualItem.mnEndCharPos;
1761 if( nCharPosLimit > mnEndCharPos )
1762 nCharPosLimit = mnEndCharPos;
1763 for(; i < nCharPosLimit; ++i )
1765 int n = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos;
1766 if( rMinGlyphPos > n )
1767 rMinGlyphPos = n;
1768 if( nMaxGlyphPos < n )
1769 nMaxGlyphPos = n;
1771 if (nMaxGlyphPos > rVisualItem.mnEndGlyphPos)
1772 nMaxGlyphPos = rVisualItem.mnEndGlyphPos - 1;
1774 // extend the glyph range to account for all glyphs in referenced clusters
1775 if( !rVisualItem.IsRTL() ) // LTR-item
1777 // extend to rightmost glyph of rightmost referenced cluster
1778 for( i = nMaxGlyphPos; ++i < rVisualItem.mnEndGlyphPos; nMaxGlyphPos = i )
1779 if( mpVisualAttrs[i].fClusterStart )
1780 break;
1782 else // RTL-item
1784 // extend to leftmost glyph of leftmost referenced cluster
1785 for( i = rMinGlyphPos; --i >= rVisualItem.mnMinGlyphPos; rMinGlyphPos = i )
1786 if( mpVisualAttrs[i].fClusterStart )
1787 break;
1789 rEndGlyphPos = nMaxGlyphPos + 1;
1791 return true;
1794 // -----------------------------------------------------------------------
1796 int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
1797 int& nStartx8, sal_Int32* pGlyphAdvances, int* pCharPosAry ) const
1799 // HACK to allow fake-glyph insertion (e.g. for kashidas)
1800 // TODO: use iterator idiom instead of GetNextGlyphs(...)
1801 // TODO: else make sure that the limit for glyph injection is sufficient (currently 256)
1802 int nSubIter = nStartx8 & 0xff;
1803 int nStart = nStartx8 >> 8;
1805 // check the glyph iterator
1806 if( nStart > mnGlyphCount ) // nStart>MAX means no more glyphs
1807 return 0;
1809 // find the visual item for the nStart glyph position
1810 int nItem = 0;
1811 const VisualItem* pVI = mpVisualItems;
1812 if( nStart <= 0 ) // nStart<=0 requests the first visible glyph
1814 // find first visible item
1815 for(; nItem < mnItemCount; ++nItem, ++pVI )
1816 if( !pVI->IsEmpty() )
1817 break;
1818 // it is possible that there are glyphs but no valid visual item
1819 // TODO: get rid of these visual items more early
1820 if( nItem < mnItemCount )
1821 nStart = pVI->mnMinGlyphPos;
1823 else //if( nStart > 0 ) // nStart>0 means absolute glyph pos +1
1825 --nStart;
1827 // find matching item
1828 for(; nItem < mnItemCount; ++nItem, ++pVI )
1829 if( (nStart >= pVI->mnMinGlyphPos)
1830 && (nStart < pVI->mnEndGlyphPos) )
1831 break;
1834 // after the last visual item there are no more glyphs
1835 if( (nItem >= mnItemCount) || (nStart < 0) )
1837 nStartx8 = (mnGlyphCount + 1) << 8;
1838 return 0;
1841 // calculate the first glyph in the next visual item
1842 int nNextItemStart = mnGlyphCount;
1843 while( ++nItem < mnItemCount )
1845 if( mpVisualItems[nItem].IsEmpty() )
1846 continue;
1847 nNextItemStart = mpVisualItems[nItem].mnMinGlyphPos;
1848 break;
1851 // get the range of relevant glyphs in this visual item
1852 int nMinGlyphPos, nEndGlyphPos;
1853 bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos );
1854 DBG_ASSERT( bRC, "USPLayout::GNG GISR() returned false" );
1855 if( !bRC )
1857 nStartx8 = (mnGlyphCount + 1) << 8;
1858 return 0;
1861 // make sure nStart is inside the range of relevant glyphs
1862 if( nStart < nMinGlyphPos )
1863 nStart = nMinGlyphPos;
1865 // calculate the start glyph xoffset relative to layout's base position,
1866 // advance to next visual glyph position by using adjusted glyph widths
1867 // TODO: speed up the calculation for nStart!=0 case by using rPos as a cache
1868 long nXOffset = pVI->mnXOffset;
1869 const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
1870 for( int i = nMinGlyphPos; i < nStart; ++i )
1871 nXOffset += pGlyphWidths[ i ];
1873 // adjust the nXOffset relative to glyph cluster start
1874 int c = mnMinCharPos;
1875 if( !pVI->IsRTL() ) // LTR-case
1877 // LTR case: subtract the remainder of the cell from xoffset
1878 int nTmpIndex = mpLogClusters[c];
1879 while( (--c >= pVI->mnMinCharPos)
1880 && (nTmpIndex == mpLogClusters[c]) )
1881 nXOffset -= mpCharWidths[c];
1883 else // RTL-case
1885 // RTL case: add the remainder of the cell from xoffset
1886 int nTmpIndex = mpLogClusters[ pVI->mnEndCharPos - 1 ];
1887 while( (--c >= pVI->mnMinCharPos)
1888 && (nTmpIndex == mpLogClusters[c]) )
1889 nXOffset += mpCharWidths[c];
1891 // adjust the xoffset if justified glyphs are not positioned at their justified positions yet
1892 if( mpJustifications && !bManualCellAlign )
1893 nXOffset += mpJustifications[ nStart ] - mpGlyphAdvances[ nStart ];
1896 // create mpGlyphs2Chars[] if it is needed later
1897 if( pCharPosAry && !mpGlyphs2Chars )
1899 // create and reset the new array
1900 mpGlyphs2Chars = new int[ mnGlyphCapacity ];
1901 for( int i = 0; i < mnGlyphCount; ++i )
1902 mpGlyphs2Chars[i] = -1;
1903 // calculate the char->glyph mapping
1904 for( nItem = 0; nItem < mnItemCount; ++nItem )
1906 // ignore invisible visual items
1907 const VisualItem& rVI = mpVisualItems[ nItem ];
1908 if( rVI.IsEmpty() )
1909 continue;
1910 // calculate the mapping by using mpLogClusters[]
1911 // mpGlyphs2Chars[] should obey the logical order
1912 // => reversing the loop does this by overwriting higher logicals
1913 for( c = rVI.mnEndCharPos; --c >= rVI.mnMinCharPos; )
1915 int i = mpLogClusters[c] + rVI.mnMinGlyphPos;
1916 mpGlyphs2Chars[i] = c;
1921 // calculate the absolute position of the first result glyph in pixel units
1922 const GOFFSET aGOffset = mpGlyphOffsets[ nStart ];
1923 Point aRelativePos( nXOffset + aGOffset.du, -aGOffset.dv );
1924 rPos = GetDrawPosition( aRelativePos );
1926 // fill the result arrays
1927 int nCount = 0;
1928 while( nCount < nLen )
1930 // prepare return values
1931 sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ];
1932 int nGlyphWidth = pGlyphWidths[ nStart ];
1933 int nCharPos = -1; // no need to determine charpos
1934 if( mpGlyphs2Chars ) // unless explicitly requested+provided
1935 nCharPos = mpGlyphs2Chars[ nStart ];
1937 // inject kashida glyphs if needed
1938 if( !mbDisableGlyphInjection
1939 && mpJustifications
1940 && mnMinKashidaWidth
1941 && mpVisualAttrs[nStart].uJustification >= SCRIPT_JUSTIFY_ARABIC_NORMAL )
1943 // prepare draw position adjustment
1944 int nExtraOfs = (nSubIter++) * mnMinKashidaWidth;
1945 // calculate space available for the injected glyphs
1946 nGlyphWidth = mpGlyphAdvances[ nStart ];
1947 const int nExtraWidth = mpJustifications[ nStart ] - nGlyphWidth;
1948 const int nToFillWidth = nExtraWidth - nExtraOfs;
1949 if( (4*nToFillWidth >= mnMinKashidaWidth) // prevent glyph-injection if there is no room
1950 || ((nSubIter > 1) && (nToFillWidth > 0)) ) // unless they can overlap with others
1952 // handle if there is not sufficient room for a full glyph
1953 if( nToFillWidth < mnMinKashidaWidth )
1955 // overlap it with the previously injected glyph if possible
1956 int nOverlap = mnMinKashidaWidth - nToFillWidth;
1957 // else overlap it with both neighboring glyphs
1958 if( nSubIter <= 1 )
1959 nOverlap /= 2;
1960 nExtraOfs -= nOverlap;
1962 nGlyphWidth = mnMinKashidaWidth;
1963 aGlyphId = mnMinKashidaGlyph;
1964 nCharPos = -1;
1966 else
1968 nExtraOfs += nToFillWidth; // at right of cell
1969 nSubIter = 0; // done with glyph injection
1971 if( !bManualCellAlign )
1972 nExtraOfs -= nExtraWidth; // adjust for right-aligned cells
1974 // adjust the draw position for the injected-glyphs case
1975 if( nExtraOfs )
1977 aRelativePos.X() += nExtraOfs;
1978 rPos = GetDrawPosition( aRelativePos );
1982 // update return values
1983 *(pGlyphs++) = aGlyphId;
1984 if( pGlyphAdvances )
1985 *(pGlyphAdvances++) = nGlyphWidth;
1986 if( pCharPosAry )
1987 *(pCharPosAry++) = nCharPos;
1989 // increment counter of returned glyphs
1990 ++nCount;
1992 // reduce code complexity by returning early in glyph-injection case
1993 if( nSubIter != 0 )
1994 break;
1996 // stop after the last visible glyph in this visual item
1997 if( ++nStart >= nEndGlyphPos )
1999 nStart = nNextItemStart;
2000 break;
2003 // RTL-justified glyph positioning is not easy
2004 // simplify the code by just returning only one glyph at a time
2005 if( mpJustifications && pVI->IsRTL() )
2006 break;
2008 // stop when the x-position of the next glyph is unexpected
2009 if( !pGlyphAdvances )
2010 if( (mpGlyphOffsets && (mpGlyphOffsets[nStart].du != aGOffset.du) )
2011 || (mpJustifications && (mpJustifications[nStart] != mpGlyphAdvances[nStart]) ) )
2012 break;
2014 // stop when the y-position of the next glyph is unexpected
2015 if( mpGlyphOffsets && (mpGlyphOffsets[nStart].dv != aGOffset.dv) )
2016 break;
2019 ++nStart;
2020 nStartx8 = (nStart << 8) + nSubIter;
2021 return nCount;
2024 // -----------------------------------------------------------------------
2026 void UniscribeLayout::MoveGlyph( int nStartx8, long nNewXPos )
2028 DBG_ASSERT( !(nStartx8 & 0xff), "USP::MoveGlyph(): glyph injection not disabled!" );
2029 int nStart = nStartx8 >> 8;
2030 if( nStart > mnGlyphCount )
2031 return;
2033 VisualItem* pVI = mpVisualItems;
2034 int nMinGlyphPos = 0, nEndGlyphPos;
2035 if( nStart == 0 ) // nStart==0 for first visible glyph
2037 for( int i = mnItemCount; --i >= 0; ++pVI )
2038 if( GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ) )
2039 break;
2040 nStart = nMinGlyphPos;
2041 DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::MoveG overflow" );
2043 else //if( nStart > 0 ) // nStart>0 means absolute_glyphpos+1
2045 --nStart;
2046 for( int i = mnItemCount; --i >= 0; ++pVI )
2047 if( (nStart >= pVI->mnMinGlyphPos) && (nStart < pVI->mnEndGlyphPos) )
2048 break;
2049 bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos );
2050 (void)bRC; // avoid var-not-used warning
2051 DBG_ASSERT( bRC, "USPLayout::MoveG GISR() returned false" );
2054 long nDelta = nNewXPos - pVI->mnXOffset;
2055 if( nStart > nMinGlyphPos )
2057 // move the glyph by expanding its left glyph but ignore dropped glyphs
2058 int i, nLastUndropped = nMinGlyphPos - 1;
2059 for( i = nMinGlyphPos; i < nStart; ++i )
2061 if (mpOutGlyphs[i] != DROPPED_OUTGLYPH)
2063 nDelta -= (mpJustifications)? mpJustifications[ i ] : mpGlyphAdvances[ i ];
2064 nLastUndropped = i;
2067 if (nLastUndropped >= nMinGlyphPos)
2069 mpGlyphAdvances[ nLastUndropped ] += nDelta;
2070 if (mpJustifications) mpJustifications[ nLastUndropped ] += nDelta;
2072 else
2074 pVI->mnXOffset += nDelta;
2077 else
2079 // move the visual item by having an offset
2080 pVI->mnXOffset += nDelta;
2084 // -----------------------------------------------------------------------
2086 void UniscribeLayout::DropGlyph( int nStartx8 )
2088 DBG_ASSERT( !(nStartx8 & 0xff), "USP::DropGlyph(): glyph injection not disabled!" );
2089 int nStart = nStartx8 >> 8;
2090 DBG_ASSERT( nStart<=mnGlyphCount, "USPLayout::MoveG nStart overflow" );
2092 if( nStart > 0 ) // nStart>0 means absolute glyph pos + 1
2093 --nStart;
2094 else // nStart<=0 for first visible glyph
2096 VisualItem* pVI = mpVisualItems;
2097 for( int i = mnItemCount, nDummy; --i >= 0; ++pVI )
2098 if( GetItemSubrange( *pVI, nStart, nDummy ) )
2099 break;
2100 DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::DropG overflow" );
2101 int nOffset = 0;
2102 int j = pVI->mnMinGlyphPos;
2103 while (mpOutGlyphs[j] == DROPPED_OUTGLYPH) j++;
2104 if (j == nStart)
2106 pVI->mnXOffset += ((mpJustifications)? mpJustifications[nStart] : mpGlyphAdvances[nStart]);
2110 mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH;
2113 // -----------------------------------------------------------------------
2115 void UniscribeLayout::Simplify( bool /*bIsBase*/ )
2117 static const WCHAR cDroppedGlyph = DROPPED_OUTGLYPH;
2118 int i;
2119 // if there are no dropped glyphs don't bother
2120 for( i = 0; i < mnGlyphCount; ++i )
2121 if( mpOutGlyphs[ i ] == cDroppedGlyph )
2122 break;
2123 if( i >= mnGlyphCount )
2124 return;
2126 // prepare for sparse layout
2127 // => make sure mpGlyphs2Chars[] exists
2128 if( !mpGlyphs2Chars )
2130 mpGlyphs2Chars = new int[ mnGlyphCapacity ];
2131 for( i = 0; i < mnGlyphCount; ++i )
2132 mpGlyphs2Chars[ i ] = -1;
2133 for( int nItem = 0; nItem < mnItemCount; ++nItem )
2135 // skip invisible items
2136 VisualItem& rVI = mpVisualItems[ nItem ];
2137 if( rVI.IsEmpty() )
2138 continue;
2139 for( i = rVI.mnEndCharPos; --i >= rVI.mnMinCharPos; )
2141 int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos;
2142 mpGlyphs2Chars[ j ] = i;
2147 // remove the dropped glyphs
2148 const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
2149 for( int nItem = 0; nItem < mnItemCount; ++nItem )
2151 VisualItem& rVI = mpVisualItems[ nItem ];
2152 if( rVI.IsEmpty() )
2153 continue;
2155 // mark replaced character widths
2156 for( i = rVI.mnMinCharPos; i < rVI.mnEndCharPos; ++i )
2158 int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos;
2159 if( mpOutGlyphs[ j ] == cDroppedGlyph )
2160 mpCharWidths[ i ] = 0;
2163 // handle dropped glyphs at start of visual item
2164 int nMinGlyphPos, nEndGlyphPos, nOrigMinGlyphPos = rVI.mnMinGlyphPos;
2165 GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos );
2166 i = nMinGlyphPos;
2167 while( (mpOutGlyphs[i] == cDroppedGlyph) && (i < nEndGlyphPos) )
2169 //rVI.mnXOffset += pGlyphWidths[ i ];
2170 rVI.mnMinGlyphPos = ++i;
2173 // when all glyphs in item got dropped mark it as empty
2174 if( i >= nEndGlyphPos )
2176 rVI.mnEndGlyphPos = 0;
2177 continue;
2179 // If there are still glyphs in the cluster and mnMinGlyphPos
2180 // has changed then we need to remove the dropped glyphs at start
2181 // to correct logClusters, which is unsigned and relative to the
2182 // item start.
2183 if (rVI.mnMinGlyphPos != nOrigMinGlyphPos)
2185 // drop any glyphs in the visual item outside the range
2186 for (i = nOrigMinGlyphPos; i < nMinGlyphPos; i++)
2187 mpOutGlyphs[ i ] = cDroppedGlyph;
2188 rVI.mnMinGlyphPos = i = nOrigMinGlyphPos;
2191 // handle dropped glyphs in the middle of visual item
2192 for(; i < nEndGlyphPos; ++i )
2193 if( mpOutGlyphs[ i ] == cDroppedGlyph )
2194 break;
2195 int j = i;
2196 while( ++i < nEndGlyphPos )
2198 if( mpOutGlyphs[ i ] == cDroppedGlyph )
2199 continue;
2200 mpOutGlyphs[ j ] = mpOutGlyphs[ i ];
2201 mpGlyphOffsets[ j ] = mpGlyphOffsets[ i ];
2202 mpVisualAttrs[ j ] = mpVisualAttrs[ i ];
2203 mpGlyphAdvances[ j ] = mpGlyphAdvances[ i ];
2204 if( mpJustifications )
2205 mpJustifications[ j ] = mpJustifications[ i ];
2206 const int k = mpGlyphs2Chars[ i ];
2207 mpGlyphs2Chars[ j ] = k;
2208 const int nRelGlyphPos = (j++) - rVI.mnMinGlyphPos;
2209 mpLogClusters[ k ] = static_cast<WORD>(nRelGlyphPos);
2212 rVI.mnEndGlyphPos = j;
2216 // -----------------------------------------------------------------------
2218 void UniscribeLayout::DrawText( SalGraphics& ) const
2220 HFONT hOrigFont = DisableFontScaling();
2222 int nBaseClusterOffset = 0;
2223 int nBaseGlyphPos = -1;
2224 for( int nItem = 0; nItem < mnItemCount; ++nItem )
2226 const VisualItem& rVisualItem = mpVisualItems[ nItem ];
2228 // skip if there is nothing to display
2229 int nMinGlyphPos, nEndGlyphPos;
2230 if( !GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) )
2231 continue;
2233 if( nBaseGlyphPos < 0 )
2235 // adjust draw position relative to cluster start
2236 if( rVisualItem.IsRTL() )
2237 nBaseGlyphPos = nEndGlyphPos - 1;
2238 else
2239 nBaseGlyphPos = nMinGlyphPos;
2241 const int* pGlyphWidths;
2242 if( mpJustifications )
2243 pGlyphWidths = mpJustifications;
2244 else
2245 pGlyphWidths = mpGlyphAdvances;
2247 int i = mnMinCharPos;
2248 while( (--i >= rVisualItem.mnMinCharPos)
2249 && (nBaseGlyphPos == mpLogClusters[i]) )
2250 nBaseClusterOffset += mpCharWidths[i];
2252 if( !rVisualItem.IsRTL() )
2253 nBaseClusterOffset = -nBaseClusterOffset;
2256 // now draw the matching glyphs in this item
2257 Point aRelPos( rVisualItem.mnXOffset + nBaseClusterOffset, 0 );
2258 Point aPos = GetDrawPosition( aRelPos );
2259 SCRIPT_CACHE& rScriptCache = GetScriptCache();
2260 (*pScriptTextOut)( mhDC, &rScriptCache,
2261 aPos.X(), aPos.Y(), 0, NULL,
2262 &rVisualItem.mpScriptItem->a, NULL, 0,
2263 mpOutGlyphs + nMinGlyphPos,
2264 nEndGlyphPos - nMinGlyphPos,
2265 mpGlyphAdvances + nMinGlyphPos,
2266 mpJustifications ? mpJustifications + nMinGlyphPos : NULL,
2267 mpGlyphOffsets + nMinGlyphPos );
2270 if( hOrigFont )
2271 DeleteFont( SelectFont( mhDC, hOrigFont ) );
2274 // -----------------------------------------------------------------------
2276 long UniscribeLayout::FillDXArray( long* pDXArray ) const
2278 // calculate width of the complete layout
2279 long nWidth = mnBaseAdv;
2280 for( int nItem = mnItemCount; --nItem >= 0; )
2282 const VisualItem& rVI = mpVisualItems[ nItem ];
2284 // skip if there is nothing to display
2285 int nMinGlyphPos, nEndGlyphPos;
2286 if( !GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos ) )
2287 continue;
2289 // width = xoffset + width of last item
2290 nWidth = rVI.mnXOffset;
2291 const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
2292 for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2293 nWidth += pGlyphWidths[i];
2294 break;
2297 // copy the virtual char widths into pDXArray[]
2298 if( pDXArray )
2299 for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
2300 pDXArray[ i - mnMinCharPos ] = mpCharWidths[ i ];
2302 return nWidth;
2305 // -----------------------------------------------------------------------
2307 int UniscribeLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
2309 long nWidth = 0;
2310 for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
2312 nWidth += mpCharWidths[ i ] * nFactor;
2314 // check if the nMaxWidth still fits the current sub-layout
2315 if( nWidth >= nMaxWidth )
2317 // go back to cluster start
2318 // we have to find the visual item first since the mpLogClusters[]
2319 // needed to find the cluster start is relative to to the visual item
2320 int nMinGlyphIndex = 0;
2321 for( int nItem = 0; nItem < mnItemCount; ++nItem )
2323 const VisualItem& rVisualItem = mpVisualItems[ nItem ];
2324 nMinGlyphIndex = rVisualItem.mnMinGlyphPos;
2325 if( (i >= rVisualItem.mnMinCharPos)
2326 && (i < rVisualItem.mnEndCharPos) )
2327 break;
2329 // now go back to the matching cluster start
2332 int nGlyphPos = mpLogClusters[i] + nMinGlyphIndex;
2333 if( 0 != mpVisualAttrs[ nGlyphPos ].fClusterStart )
2334 return i;
2335 } while( --i >= mnMinCharPos );
2337 // if the cluster starts before the start of the visual item
2338 // then set the visual breakpoint before this item
2339 return mnMinCharPos;
2342 // the visual break also depends on the nCharExtra between the characters
2343 nWidth += nCharExtra;
2346 // the whole layout did fit inside the nMaxWidth
2347 return STRING_LEN;
2350 // -----------------------------------------------------------------------
2352 void UniscribeLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const
2354 int i;
2355 for( i = 0; i < nMaxIdx; ++i )
2356 pCaretXArray[ i ] = -1;
2357 long* const pGlyphPos = (long*)alloca( (mnGlyphCount+1) * sizeof(long) );
2358 for( i = 0; i <= mnGlyphCount; ++i )
2359 pGlyphPos[ i ] = -1;
2361 long nXPos = 0;
2362 for( int nItem = 0; nItem < mnItemCount; ++nItem )
2364 const VisualItem& rVisualItem = mpVisualItems[ nItem ];
2365 if( rVisualItem.IsEmpty() )
2366 continue;
2368 // get glyph positions
2369 // TODO: handle when rVisualItem's glyph range is only partially used
2370 for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
2372 pGlyphPos[ i ] = nXPos;
2373 nXPos += mpGlyphAdvances[ i ];
2375 // rightmost position of this visualitem
2376 pGlyphPos[ i ] = nXPos;
2378 // convert glyph positions to character positions
2379 i = rVisualItem.mnMinCharPos;
2380 if( i < mnMinCharPos )
2381 i = mnMinCharPos;
2382 for(; (i < rVisualItem.mnEndCharPos) && (i < mnEndCharPos); ++i )
2384 int j = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos;
2385 int nCurrIdx = i * 2;
2386 if( !rVisualItem.IsRTL() )
2388 // normal positions for LTR case
2389 pCaretXArray[ nCurrIdx ] = pGlyphPos[ j ];
2390 pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j+1 ];
2392 else
2394 // reverse positions for RTL case
2395 pCaretXArray[ nCurrIdx ] = pGlyphPos[ j+1 ];
2396 pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j ];
2401 // fixup unknown character positions to neighbor
2402 for( i = 0; i < nMaxIdx; ++i )
2404 if( pCaretXArray[ i ] >= 0 )
2405 nXPos = pCaretXArray[ i ];
2406 else
2407 pCaretXArray[ i ] = nXPos;
2411 // -----------------------------------------------------------------------
2413 void UniscribeLayout::AdjustLayout( ImplLayoutArgs& rArgs )
2415 SalLayout::AdjustLayout( rArgs );
2417 // adjust positions if requested
2418 if( rArgs.mpDXArray )
2419 ApplyDXArray( rArgs );
2420 else if( rArgs.mnLayoutWidth )
2421 Justify( rArgs.mnLayoutWidth );
2424 // -----------------------------------------------------------------------
2426 void UniscribeLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
2428 const long* pDXArray = rArgs.mpDXArray;
2430 // increase char widths in string range to desired values
2431 bool bModified = false;
2432 int nOldWidth = 0;
2433 DBG_ASSERT( mnUnitsPerPixel==1, "UniscribeLayout.mnUnitsPerPixel != 1" );
2434 int i,j;
2435 for( i = mnMinCharPos, j = 0; i < mnEndCharPos; ++i, ++j )
2437 int nNewCharWidth = (pDXArray[j] - nOldWidth);
2438 // TODO: nNewCharWidth *= mnUnitsPerPixel;
2439 if( mpCharWidths[i] != nNewCharWidth )
2441 mpCharWidths[i] = nNewCharWidth;
2442 bModified = true;
2444 nOldWidth = pDXArray[j];
2447 if( !bModified )
2448 return;
2450 // initialize justifications array
2451 mpJustifications = new int[ mnGlyphCapacity ];
2452 for( i = 0; i < mnGlyphCount; ++i )
2453 mpJustifications[ i ] = mpGlyphAdvances[ i ];
2455 // apply new widths to script items
2456 long nXOffset = 0;
2457 for( int nItem = 0; nItem < mnItemCount; ++nItem )
2459 VisualItem& rVisualItem = mpVisualItems[ nItem ];
2461 // set the position of this visual item
2462 rVisualItem.mnXOffset = nXOffset;
2464 // ignore empty visual items
2465 if( rVisualItem.IsEmpty() )
2467 for (i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; i++)
2468 nXOffset += mpCharWidths[i];
2469 continue;
2471 // ignore irrelevant visual items
2472 if( (rVisualItem.mnMinCharPos >= mnEndCharPos)
2473 || (rVisualItem.mnEndCharPos <= mnMinCharPos) )
2474 continue;
2476 // if needed prepare special handling for arabic justification
2477 rVisualItem.mbHasKashidas = false;
2478 if( rVisualItem.IsRTL() )
2480 for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
2481 if ( (1U << mpVisualAttrs[i].uJustification) & 0xFF82 ) // any Arabic justification
2482 { // excluding SCRIPT_JUSTIFY_NONE
2483 // yes
2484 rVisualItem.mbHasKashidas = true;
2485 // so prepare for kashida handling
2486 InitKashidaHandling();
2487 break;
2490 if( rVisualItem.HasKashidas() )
2491 for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
2493 // TODO: check if we still need this hack after correction of kashida placing?
2494 // (i87688): apparently yes, we still need it!
2495 if ( mpVisualAttrs[i].uJustification == SCRIPT_JUSTIFY_NONE )
2496 // usp decided that justification can't be applied here
2497 // but maybe our Kashida algorithm thinks differently.
2498 // To avoid trouble (gaps within words, last character of
2499 // a word gets a Kashida appended) override this.
2501 // I chose SCRIPT_JUSTIFY_ARABIC_KASHIDA to replace SCRIPT_JUSTIFY_NONE
2502 // just because this previous hack (which I haven't understand, sorry) used
2503 // the same value to replace. Don't know if this is really the best
2504 // thing to do, but it seems to fix things
2505 mpVisualAttrs[i].uJustification = SCRIPT_JUSTIFY_ARABIC_KASHIDA;
2509 // convert virtual charwidths to glyph justification values
2510 HRESULT nRC = (*pScriptApplyLogicalWidth)(
2511 mpCharWidths + rVisualItem.mnMinCharPos,
2512 rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos,
2513 rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos,
2514 mpLogClusters + rVisualItem.mnMinCharPos,
2515 mpVisualAttrs + rVisualItem.mnMinGlyphPos,
2516 mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
2517 &rVisualItem.mpScriptItem->a,
2518 &rVisualItem.maABCWidths,
2519 mpJustifications + rVisualItem.mnMinGlyphPos );
2521 if( nRC != 0 )
2523 delete[] mpJustifications;
2524 mpJustifications = NULL;
2525 break;
2528 // to prepare for the next visual item
2529 // update nXOffset to the next items position
2530 // before the mpJustifications[] array gets modified
2531 int nMinGlyphPos, nEndGlyphPos;
2532 if( GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) )
2534 for( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2535 nXOffset += mpJustifications[ i ];
2537 if( rVisualItem.mbHasKashidas )
2538 KashidaItemFix( nMinGlyphPos, nEndGlyphPos );
2541 // workaround needed for older USP versions:
2542 // right align the justification-adjusted glyphs in their cells for RTL-items
2543 // unless the right alignment is done by inserting kashidas
2544 if( bManualCellAlign && rVisualItem.IsRTL() && !rVisualItem.HasKashidas() )
2546 for( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2548 const int nXOffsetAdjust = mpJustifications[i] - mpGlyphAdvances[i];
2549 // #i99862# skip diacritics, we mustn't add extra justification to diacritics
2550 int nIdxAdd = i - 1;
2551 while( (nIdxAdd >= nMinGlyphPos) && !mpGlyphAdvances[nIdxAdd] )
2552 --nIdxAdd;
2553 if( nIdxAdd < nMinGlyphPos )
2554 rVisualItem.mnXOffset += nXOffsetAdjust;
2555 else
2556 mpJustifications[nIdxAdd] += nXOffsetAdjust;
2557 mpJustifications[i] -= nXOffsetAdjust;
2563 // -----------------------------------------------------------------------
2565 void UniscribeLayout::InitKashidaHandling()
2567 if( mnMinKashidaGlyph != 0 ) // already initialized
2568 return;
2570 mrWinFontEntry.InitKashidaHandling( mhDC );
2571 mnMinKashidaWidth = static_cast<int>(mfFontScale * mrWinFontEntry.GetMinKashidaWidth());
2572 mnMinKashidaGlyph = mrWinFontEntry.GetMinKashidaGlyph();
2575 // adjust the kashida placement matching to the WriterEngine
2576 void UniscribeLayout::KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos )
2578 // workaround needed for all known USP versions:
2579 // ApplyLogicalWidth does not match ScriptJustify behaviour
2580 for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2582 // check for vowels
2583 if( (i > nMinGlyphPos && !mpGlyphAdvances[ i-1 ])
2584 && (1U << mpVisualAttrs[i].uJustification) & 0xFF83 ) // all Arabic justifiction types
2585 { // including SCRIPT_JUSTIFY_NONE
2586 // vowel, we do it like ScriptJustify does
2587 // the vowel gets the extra width
2588 long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
2589 mpJustifications [ i ] = mpGlyphAdvances [ i ];
2590 mpJustifications [ i - 1 ] += nSpaceAdded;
2594 // redistribute the widths for kashidas
2595 for( int i = nMinGlyphPos; i < nEndGlyphPos; )
2596 KashidaWordFix ( nMinGlyphPos, nEndGlyphPos, &i );
2599 bool UniscribeLayout::KashidaWordFix ( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos )
2601 // doing pixel work within a word.
2602 // sometimes we have extra pixels and sometimes we miss some pixels to get to mnMinKashidaWidth
2604 // find the next kashida
2605 int nMinPos = *pnCurrentPos;
2606 int nMaxPos = *pnCurrentPos;
2607 for( int i = nMaxPos; i < nEndGlyphPos; ++i )
2609 if( (mpVisualAttrs[ i ].uJustification >= SCRIPT_JUSTIFY_ARABIC_BLANK)
2610 && (mpVisualAttrs[ i ].uJustification < SCRIPT_JUSTIFY_ARABIC_NORMAL) )
2611 break;
2612 nMaxPos = i;
2614 *pnCurrentPos = nMaxPos + 1;
2615 if( nMinPos == nMaxPos )
2616 return false;
2618 // calculate the available space for an extra kashida
2619 long nMaxAdded = 0;
2620 int nKashPos = -1;
2621 for( int i = nMaxPos; i >= nMinPos; --i )
2623 long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
2624 if( nSpaceAdded > nMaxAdded )
2626 nKashPos = i;
2627 nMaxAdded = nSpaceAdded;
2631 // return early if there is no need for an extra kashida
2632 if ( nMaxAdded <= 0 )
2633 return false;
2634 // return early if there is not enough space for an extra kashida
2635 if( 2*nMaxAdded < mnMinKashidaWidth )
2636 return false;
2638 // redistribute the extra spacing to the kashida position
2639 for( int i = nMinPos; i <= nMaxPos; ++i )
2641 if( i == nKashPos )
2642 continue;
2643 // everything else should not have extra spacing
2644 long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
2645 if( nSpaceAdded > 0 )
2647 mpJustifications[ i ] -= nSpaceAdded;
2648 mpJustifications[ nKashPos ] += nSpaceAdded;
2652 // check if we fulfill minimal kashida width
2653 long nSpaceAdded = mpJustifications[ nKashPos ] - mpGlyphAdvances[ nKashPos ];
2654 if( nSpaceAdded < mnMinKashidaWidth )
2656 // ugly: steal some pixels
2657 long nSteal = 1;
2658 if ( nMaxPos - nMinPos > 0 && ((mnMinKashidaWidth - nSpaceAdded) > (nMaxPos - nMinPos)))
2659 nSteal = (mnMinKashidaWidth - nSpaceAdded) / (nMaxPos - nMinPos);
2660 for( int i = nMinPos; i <= nMaxPos; ++i )
2662 if( i == nKashPos )
2663 continue;
2664 nSteal = Min( mnMinKashidaWidth - nSpaceAdded, nSteal );
2665 if ( nSteal > 0 )
2667 mpJustifications [ i ] -= nSteal;
2668 mpJustifications [ nKashPos ] += nSteal;
2669 nSpaceAdded += nSteal;
2671 if( nSpaceAdded >= mnMinKashidaWidth )
2672 return true;
2676 // blank padding
2677 long nSpaceMissing = mnMinKashidaWidth - nSpaceAdded;
2678 if( nSpaceMissing > 0 )
2680 // inner glyph: distribute extra space evenly
2681 if( (nMinPos > nMinGlyphPos) && (nMaxPos < nEndGlyphPos - 1) )
2683 mpJustifications [ nKashPos ] += nSpaceMissing;
2684 long nHalfSpace = nSpaceMissing / 2;
2685 mpJustifications [ nMinPos - 1 ] -= nHalfSpace;
2686 mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing - nHalfSpace;
2688 // rightmost: left glyph gets extra space
2689 else if( nMinPos > nMinGlyphPos )
2691 mpJustifications [ nMinPos - 1 ] -= nSpaceMissing;
2692 mpJustifications [ nKashPos ] += nSpaceMissing;
2694 // leftmost: right glyph gets extra space
2695 else if( nMaxPos < nEndGlyphPos - 1 )
2697 mpJustifications [ nKashPos ] += nSpaceMissing;
2698 mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing;
2700 else
2701 return false;
2704 return true;
2707 // -----------------------------------------------------------------------
2709 void UniscribeLayout::Justify( long nNewWidth )
2711 long nOldWidth = 0;
2712 int i;
2713 for( i = mnMinCharPos; i < mnEndCharPos; ++i )
2714 nOldWidth += mpCharWidths[ i ];
2715 if( nOldWidth <= 0 )
2716 return;
2718 nNewWidth *= mnUnitsPerPixel; // convert into font units
2719 if( nNewWidth == nOldWidth )
2720 return;
2721 // prepare to distribute the extra width evenly among the visual items
2722 const double fStretch = (double)nNewWidth / nOldWidth;
2724 // initialize justifications array
2725 mpJustifications = new int[ mnGlyphCapacity ];
2726 for( i = 0; i < mnGlyphCapacity; ++i )
2727 mpJustifications[ i ] = mpGlyphAdvances[ i ];
2729 // justify stretched script items
2730 long nXOffset = 0;
2731 SCRIPT_CACHE& rScriptCache = GetScriptCache();
2732 for( int nItem = 0; nItem < mnItemCount; ++nItem )
2734 VisualItem& rVisualItem = mpVisualItems[ nItem ];
2735 if( rVisualItem.IsEmpty() )
2736 continue;
2738 if( (rVisualItem.mnMinCharPos < mnEndCharPos)
2739 && (rVisualItem.mnEndCharPos > mnMinCharPos) )
2741 long nItemWidth = 0;
2742 for( i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i )
2743 nItemWidth += mpCharWidths[ i ];
2744 nItemWidth = (int)((fStretch - 1.0) * nItemWidth + 0.5);
2746 HRESULT nRC = (*pScriptJustify) (
2747 mpVisualAttrs + rVisualItem.mnMinGlyphPos,
2748 mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
2749 rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos,
2750 nItemWidth,
2751 mnMinKashidaWidth,
2752 mpJustifications + rVisualItem.mnMinGlyphPos );
2754 rVisualItem.mnXOffset = nXOffset;
2755 nXOffset += nItemWidth;
2760 // -----------------------------------------------------------------------
2762 bool UniscribeLayout::IsKashidaPosValid ( int nCharPos ) const
2764 // we have to find the visual item first since the mpLogClusters[]
2765 // needed to find the cluster start is relative to to the visual item
2766 int nMinGlyphIndex = -1;
2767 for( int nItem = 0; nItem < mnItemCount; ++nItem )
2769 const VisualItem& rVisualItem = mpVisualItems[ nItem ];
2770 if( (nCharPos >= rVisualItem.mnMinCharPos)
2771 && (nCharPos < rVisualItem.mnEndCharPos) )
2773 nMinGlyphIndex = rVisualItem.mnMinGlyphPos;
2774 break;
2777 // Invalid char pos or leftmost glyph in visual item
2778 if ( nMinGlyphIndex == -1 || !mpLogClusters[ nCharPos ] )
2779 return false;
2781 // This test didn't give the expected results
2782 /* if( mpLogClusters[ nCharPos+1 ] == mpLogClusters[ nCharPos ])
2783 // two chars, one glyph
2784 return false;*/
2786 const int nGlyphPos = mpLogClusters[ nCharPos ] + nMinGlyphIndex;
2787 if( nGlyphPos <= 0 )
2788 return true;
2789 // justification is only allowed if the glyph to the left has not SCRIPT_JUSTIFY_NONE
2790 // and not SCRIPT_JUSTIFY_ARABIC_BLANK
2791 // special case: glyph to the left is vowel (no advance width)
2792 if ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK
2793 || ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_NONE
2794 && mpGlyphAdvances [ nGlyphPos-1 ] ))
2795 return false;
2796 return true;
2799 #endif // USE_UNISCRIBE
2801 #ifdef ENABLE_GRAPHITE
2803 class GraphiteLayoutWinImpl : public GraphiteLayout
2805 public:
2806 GraphiteLayoutWinImpl(const gr::Font & font, ImplWinFontEntry & rFont)
2807 throw()
2808 : GraphiteLayout(font), mrFont(rFont) {};
2809 virtual ~GraphiteLayoutWinImpl() throw() {};
2810 virtual sal_GlyphId getKashidaGlyph(int & rWidth);
2811 private:
2812 ImplWinFontEntry & mrFont;
2815 sal_GlyphId GraphiteLayoutWinImpl::getKashidaGlyph(int & rWidth)
2817 rWidth = mrFont.GetMinKashidaWidth();
2818 return mrFont.GetMinKashidaGlyph();
2821 // This class uses the SIL Graphite engine to provide complex text layout services to the VCL
2822 // @author tse
2824 class GraphiteWinLayout : public WinLayout
2826 private:
2827 mutable gr::WinFont mpFont;
2828 grutils::GrFeatureParser * mpFeatures;
2829 mutable GraphiteLayoutWinImpl maImpl;
2830 public:
2831 GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE);
2833 static bool IsGraphiteEnabledFont(HDC hDC) throw();
2835 // used by upper layers
2836 virtual bool LayoutText( ImplLayoutArgs& ); // first step of layout
2837 virtual void AdjustLayout( ImplLayoutArgs& ); // adjusting after fallback etc.
2838 // virtual void InitFont() const;
2839 virtual void DrawText( SalGraphics& ) const;
2841 // methods using string indexing
2842 virtual int GetTextBreak( long nMaxWidth, long nCharExtra=0, int nFactor=1 ) const;
2843 virtual long FillDXArray( long* pDXArray ) const;
2845 virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
2847 // methods using glyph indexing
2848 virtual int GetNextGlyphs(int nLen, sal_GlyphId* pGlyphIdxAry, ::Point & rPos, int&,
2849 long* pGlyphAdvAry = 0, int* pCharPosAry = 0 ) const;
2851 // used by glyph+font+script fallback
2852 virtual void MoveGlyph( int nStart, long nNewXPos );
2853 virtual void DropGlyph( int nStart );
2854 virtual void Simplify( bool bIsBase );
2855 ~GraphiteWinLayout() { delete mpFeatures; mpFeatures = NULL; };
2856 protected:
2857 virtual void ReplaceDC(gr::Segment & segment) const;
2858 virtual void RestoreDC(gr::Segment & segment) const;
2861 bool GraphiteWinLayout::IsGraphiteEnabledFont(HDC hDC) throw()
2863 return gr::WinFont::FontHasGraphiteTables(hDC);
2866 GraphiteWinLayout::GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE) throw()
2867 : WinLayout(hDC, rWFD, rWFE), mpFont(hDC),
2868 maImpl(mpFont, rWFE)
2870 const rtl::OString aLang = MsLangId::convertLanguageToIsoByteString( rWFE.maFontSelData.meLanguage );
2871 rtl::OString name = rtl::OUStringToOString(
2872 rWFE.maFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
2873 sal_Int32 nFeat = name.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + 1;
2874 if (nFeat > 0)
2876 rtl::OString aFeat = name.copy(nFeat, name.getLength() - nFeat);
2877 mpFeatures = new grutils::GrFeatureParser(mpFont, aFeat.getStr(), aLang.getStr());
2879 else
2881 mpFeatures = new grutils::GrFeatureParser(mpFont, aLang.getStr());
2883 maImpl.SetFeatures(mpFeatures);
2886 void GraphiteWinLayout::ReplaceDC(gr::Segment & segment) const
2888 COLORREF color = GetTextColor(mhDC);
2889 dynamic_cast<gr::WinFont&>(segment.getFont()).replaceDC(mhDC);
2890 SetTextColor(mhDC, color);
2893 void GraphiteWinLayout::RestoreDC(gr::Segment & segment) const
2895 dynamic_cast<gr::WinFont&>(segment.getFont()).restoreDC();
2898 bool GraphiteWinLayout::LayoutText( ImplLayoutArgs & args)
2900 HFONT hUnRotatedFont;
2901 if (args.mnOrientation)
2903 // Graphite gets very confused if the font is rotated
2904 LOGFONTW aLogFont;
2905 ::GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont);
2906 aLogFont.lfEscapement = 0;
2907 aLogFont.lfOrientation = 0;
2908 hUnRotatedFont = ::CreateFontIndirectW( &aLogFont);
2909 ::SelectFont(mhDC, hUnRotatedFont);
2911 WinLayout::AdjustLayout(args);
2912 mpFont.replaceDC(mhDC);
2913 maImpl.SetFontScale(WinLayout::mfFontScale);
2914 //bool succeeded = maImpl.LayoutText(args);
2915 #ifdef GRCACHE
2916 GrSegRecord * pSegRecord = NULL;
2917 gr::Segment * pSegment = maImpl.CreateSegment(args, &pSegRecord);
2918 #else
2919 gr::Segment * pSegment = maImpl.CreateSegment(args);
2920 #endif
2921 bool bSucceeded = false;
2922 if (pSegment)
2924 // replace the DC on the font within the segment
2925 ReplaceDC(*pSegment);
2926 // create glyph vectors
2927 #ifdef GRCACHE
2928 bSucceeded = maImpl.LayoutGlyphs(args, pSegment, pSegRecord);
2929 #else
2930 bSucceeded = maImpl.LayoutGlyphs(args, pSegment);
2931 #endif
2932 // restore original DC
2933 RestoreDC(*pSegment);
2934 #ifdef GRCACHE
2935 if (pSegRecord) pSegRecord->unlock();
2936 else delete pSegment;
2937 #else
2938 delete pSegment;
2939 #endif
2941 mpFont.restoreDC();
2942 if (args.mnOrientation)
2944 // restore the rotated font
2945 ::SelectFont(mhDC, mhFont);
2946 ::DeleteObject(hUnRotatedFont);
2948 return bSucceeded;
2951 void GraphiteWinLayout::AdjustLayout(ImplLayoutArgs& rArgs)
2953 WinLayout::AdjustLayout(rArgs);
2954 maImpl.DrawBase() = WinLayout::maDrawBase;
2955 maImpl.DrawOffset() = WinLayout::maDrawOffset;
2956 if ( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) && rArgs.mpDXArray)
2958 mrWinFontEntry.InitKashidaHandling(mhDC);
2960 maImpl.AdjustLayout(rArgs);
2963 void GraphiteWinLayout::DrawText(SalGraphics &sal_graphics) const
2965 HFONT hOrigFont = DisableFontScaling();
2966 HDC aHDC = static_cast<WinSalGraphics&>(sal_graphics).mhDC;
2967 maImpl.DrawBase() = WinLayout::maDrawBase;
2968 maImpl.DrawOffset() = WinLayout::maDrawOffset;
2969 const int MAX_GLYPHS = 2;
2970 sal_GlyphId glyphIntStr[MAX_GLYPHS];
2971 WORD glyphWStr[MAX_GLYPHS];
2972 int glyphIndex = 0;
2973 Point aPos(0,0);
2974 int nGlyphs = 0;
2977 nGlyphs = maImpl.GetNextGlyphs(1, glyphIntStr, aPos, glyphIndex);
2978 if (nGlyphs < 1)
2979 break;
2980 std::copy(glyphIntStr, glyphIntStr + nGlyphs, glyphWStr);
2981 ::ExtTextOutW(aHDC, aPos.X(), aPos.Y(), ETO_GLYPH_INDEX,
2982 NULL, (LPCWSTR)&(glyphWStr), nGlyphs, NULL);
2983 } while (nGlyphs);
2984 if( hOrigFont )
2985 DeleteFont( SelectFont( mhDC, hOrigFont ) );
2988 int GraphiteWinLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
2990 mpFont.replaceDC(mhDC);
2991 int nBreak = maImpl.GetTextBreak(nMaxWidth, nCharExtra, nFactor);
2992 mpFont.restoreDC();
2993 return nBreak;
2996 long GraphiteWinLayout::FillDXArray( long* pDXArray ) const
2998 return maImpl.FillDXArray(pDXArray);
3001 void GraphiteWinLayout::GetCaretPositions( int nArraySize, long* pCaretXArray ) const
3003 maImpl.GetCaretPositions(nArraySize, pCaretXArray);
3006 int GraphiteWinLayout::GetNextGlyphs( int length, sal_GlyphId* glyph_out,
3007 ::Point & pos_out, int &glyph_slot, long * glyph_adv, int *char_index) const
3009 maImpl.DrawBase() = WinLayout::maDrawBase;
3010 maImpl.DrawOffset() = WinLayout::maDrawOffset;
3011 return maImpl.GetNextGlyphs(length, glyph_out, pos_out, glyph_slot, glyph_adv, char_index);
3014 void GraphiteWinLayout::MoveGlyph( int glyph_idx, long new_x_pos )
3016 maImpl.MoveGlyph(glyph_idx, new_x_pos);
3019 void GraphiteWinLayout::DropGlyph( int glyph_idx )
3021 maImpl.DropGlyph(glyph_idx);
3024 void GraphiteWinLayout::Simplify( bool is_base )
3026 maImpl.Simplify(is_base);
3028 #endif // ENABLE_GRAPHITE
3029 // =======================================================================
3031 SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel )
3033 DBG_ASSERT( mpWinFontEntry[nFallbackLevel], "WinSalGraphics mpWinFontEntry==NULL");
3035 WinLayout* pWinLayout = NULL;
3037 const ImplWinFontData& rFontFace = *mpWinFontData[ nFallbackLevel ];
3038 ImplWinFontEntry& rFontInstance = *mpWinFontEntry[ nFallbackLevel ];
3040 #if defined( USE_UNISCRIBE )
3041 if( !(rArgs.mnFlags & SAL_LAYOUT_COMPLEX_DISABLED)
3042 && (aUspModule || (bUspEnabled && InitUSP())) ) // CTL layout engine
3044 #ifdef ENABLE_GRAPHITE
3045 if (rFontFace.SupportsGraphite())
3046 pWinLayout = new GraphiteWinLayout(mhDC, rFontFace, rFontInstance);
3047 else
3048 #endif // ENABLE_GRAPHITE
3049 // script complexity is determined in upper layers
3050 pWinLayout = new UniscribeLayout( mhDC, rFontFace, rFontInstance );
3051 // NOTE: it must be guaranteed that the WinSalGraphics lives longer than
3052 // the created UniscribeLayout, otherwise the data passed into the
3053 // constructor might become invalid too early
3055 else
3056 #endif // USE_UNISCRIBE
3058 #ifdef GCP_KERN_HACK
3059 if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) && !rFontInstance.HasKernData() )
3061 // TODO: directly cache kerning info in the rFontInstance
3062 // TODO: get rid of kerning methods+data in WinSalGraphics object
3063 GetKernPairs( 0, NULL );
3064 rFontInstance.SetKernData( mnFontKernPairCount, mpFontKernPairs );
3066 #endif // GCP_KERN_HACK
3068 BYTE eCharSet = ANSI_CHARSET;
3069 if( mpLogFont )
3070 eCharSet = mpLogFont->lfCharSet;
3071 #ifdef ENABLE_GRAPHITE
3072 if (rFontFace.SupportsGraphite())
3073 pWinLayout = new GraphiteWinLayout(mhDC, rFontFace, rFontInstance);
3074 else
3075 #endif // ENABLE_GRAPHITE
3076 pWinLayout = new SimpleWinLayout( mhDC, eCharSet, rFontFace, rFontInstance );
3079 if( mfFontScale != 1.0 )
3080 pWinLayout->SetFontScale( mfFontScale );
3082 return pWinLayout;
3085 // -----------------------------------------------------------------------
3087 int WinSalGraphics::GetMinKashidaWidth()
3089 if( !mpWinFontEntry[0] )
3090 return 0;
3091 mpWinFontEntry[0]->InitKashidaHandling( mhDC );
3092 int nMinKashida = static_cast<int>(mfFontScale * mpWinFontEntry[0]->GetMinKashidaWidth());
3093 return nMinKashida;
3096 // =======================================================================
3098 ImplWinFontEntry::ImplWinFontEntry( ImplFontSelectData& rFSD )
3099 : ImplFontEntry( rFSD )
3100 , maWidthMap( 512 )
3101 , mpKerningPairs( NULL )
3102 , mnKerningPairs( -1 )
3103 , mnMinKashidaWidth( -1 )
3104 , mnMinKashidaGlyph( -1 )
3106 #ifdef USE_UNISCRIBE
3107 maScriptCache = NULL;
3108 #endif // USE_UNISCRIBE
3111 // -----------------------------------------------------------------------
3113 ImplWinFontEntry::~ImplWinFontEntry()
3115 #ifdef USE_UNISCRIBE
3116 if( maScriptCache != NULL )
3117 (*pScriptFreeCache)( &maScriptCache );
3118 #endif // USE_UNISCRIBE
3119 #ifdef GCP_KERN_HACK
3120 delete[] mpKerningPairs;
3121 #endif // GCP_KERN_HACK
3124 // -----------------------------------------------------------------------
3126 bool ImplWinFontEntry::HasKernData() const
3128 return (mnKerningPairs >= 0);
3131 // -----------------------------------------------------------------------
3133 void ImplWinFontEntry::SetKernData( int nPairCount, const KERNINGPAIR* pPairData )
3135 mnKerningPairs = nPairCount;
3136 mpKerningPairs = new KERNINGPAIR[ mnKerningPairs ];
3137 ::memcpy( mpKerningPairs, (const void*)pPairData, nPairCount*sizeof(KERNINGPAIR) );
3140 // -----------------------------------------------------------------------
3142 int ImplWinFontEntry::GetKerning( sal_Unicode cLeft, sal_Unicode cRight ) const
3144 int nKernAmount = 0;
3145 if( mpKerningPairs )
3147 const KERNINGPAIR aRefPair = { cLeft, cRight, 0 };
3148 const KERNINGPAIR* pFirstPair = mpKerningPairs;
3149 const KERNINGPAIR* pEndPair = mpKerningPairs + mnKerningPairs;
3150 const KERNINGPAIR* pPair = std::lower_bound( pFirstPair,
3151 pEndPair, aRefPair, ImplCmpKernData );
3152 if( (pPair != pEndPair)
3153 && (pPair->wFirst == aRefPair.wFirst)
3154 && (pPair->wSecond == aRefPair.wSecond) )
3155 nKernAmount = pPair->iKernAmount;
3158 return nKernAmount;
3161 // -----------------------------------------------------------------------
3163 bool ImplWinFontEntry::InitKashidaHandling( HDC hDC )
3165 if( mnMinKashidaWidth >= 0 ) // already cached?
3166 return mnMinKashidaWidth;
3168 // initialize the kashida width
3169 mnMinKashidaWidth = 0;
3170 mnMinKashidaGlyph = 0;
3171 #ifdef USE_UNISCRIBE
3172 if (aUspModule || (bUspEnabled && InitUSP()))
3174 SCRIPT_FONTPROPERTIES aFontProperties;
3175 aFontProperties.cBytes = sizeof (aFontProperties);
3176 SCRIPT_CACHE& rScriptCache = GetScriptCache();
3177 HRESULT nRC = (*pScriptGetFontProperties)( hDC, &rScriptCache, &aFontProperties );
3178 if( nRC != 0 )
3179 return false;
3180 mnMinKashidaWidth = aFontProperties.iKashidaWidth;
3181 mnMinKashidaGlyph = aFontProperties.wgKashida;
3183 #endif // USE_UNISCRIBE
3185 return true;
3188 // =======================================================================
3190 ImplFontData* ImplWinFontData::Clone() const
3192 if( mpUnicodeMap )
3193 mpUnicodeMap->AddReference();
3194 ImplFontData* pClone = new ImplWinFontData( *this );
3195 return pClone;
3198 // -----------------------------------------------------------------------
3200 ImplFontEntry* ImplWinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const
3202 ImplFontEntry* pEntry = new ImplWinFontEntry( rFSD );
3203 return pEntry;
3206 // =======================================================================