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