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