xref: /trunk/main/vcl/source/glyphs/gcach_layout.cxx (revision 86e1cf34)
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 #define ENABLE_ICU_LAYOUT
28 #include <gcach_ftyp.hxx>
29 #include <sallayout.hxx>
30 #include <salgdi.hxx>
31 
32 #include <vcl/svapp.hxx>
33 
34 #include <sal/alloca.h>
35 
36 #if OSL_DEBUG_LEVEL > 1
37 #include <cstdio>
38 #endif
39 #include <rtl/instance.hxx>
40 
41 namespace { struct SimpleLayoutEngine : public rtl::Static< ServerFontLayoutEngine, SimpleLayoutEngine > {}; }
42 
43 // =======================================================================
44 // layout implementation for ServerFont
45 // =======================================================================
46 
ServerFontLayout(ServerFont & rFont)47 ServerFontLayout::ServerFontLayout( ServerFont& rFont )
48 :   mrServerFont( rFont )
49 {}
50 
DrawText(SalGraphics & rSalGraphics) const51 void ServerFontLayout::DrawText( SalGraphics& rSalGraphics ) const
52 {
53     rSalGraphics.DrawServerFontLayout( *this );
54 }
55 
56 // -----------------------------------------------------------------------
57 
LayoutText(ImplLayoutArgs & rArgs)58 bool ServerFontLayout::LayoutText( ImplLayoutArgs& rArgs )
59 {
60     ServerFontLayoutEngine* pLE = NULL;
61     if( !(rArgs.mnFlags & SAL_LAYOUT_COMPLEX_DISABLED) )
62         pLE = mrServerFont.GetLayoutEngine();
63     if( !pLE )
64         pLE = &SimpleLayoutEngine::get();
65 
66     bool bRet = (*pLE)( *this, rArgs );
67     return bRet;
68 }
69 
70 // -----------------------------------------------------------------------
71 
AdjustLayout(ImplLayoutArgs & rArgs)72 void ServerFontLayout::AdjustLayout( ImplLayoutArgs& rArgs )
73 {
74     GenericSalLayout::AdjustLayout( rArgs );
75 
76     // apply asian kerning if the glyphs are not already formatted
77     if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN)
78     && !(rArgs.mnFlags & SAL_LAYOUT_VERTICAL) )
79         if( (rArgs.mpDXArray != NULL) || (rArgs.mnLayoutWidth != 0) )
80             ApplyAsianKerning( rArgs.mpStr, rArgs.mnLength );
81 
82     // insert kashidas where requested by the formatting array
83     if( (rArgs.mnFlags & SAL_LAYOUT_KASHIDA_JUSTIFICATON) && rArgs.mpDXArray )
84     {
85         int nKashidaIndex = mrServerFont.GetGlyphIndex( 0x0640 );
86         if( nKashidaIndex != 0 )
87         {
88             const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nKashidaIndex );
89             KashidaJustify( nKashidaIndex, rGM.GetCharWidth() );
90             // TODO: kashida-GSUB/GPOS
91         }
92     }
93 }
94 
95 // =======================================================================
96 
operator ()(ServerFontLayout & rLayout,ImplLayoutArgs & rArgs)97 bool ServerFontLayoutEngine::operator()( ServerFontLayout& rLayout, ImplLayoutArgs& rArgs )
98 {
99     FreetypeServerFont& rFont = static_cast<FreetypeServerFont&>(rLayout.GetServerFont());
100 
101     Point aNewPos( 0, 0 );
102     sal_GlyphId nOldGlyphId( GF_DROPPED);
103     int nGlyphWidth = 0;
104     GlyphItem aPrevItem;
105     bool bRightToLeft;
106     for( int nCharPos = -1; rArgs.GetNextPos( &nCharPos, &bRightToLeft ); )
107     {
108         sal_UCS4 cChar = rArgs.mpStr[ nCharPos ];
109         if( (cChar >= 0xD800) && (cChar <= 0xDFFF) )
110         {
111             if( cChar >= 0xDC00 ) // this part of a surrogate pair was already processed
112                 continue;
113             cChar = 0x10000 + ((cChar - 0xD800) << 10)
114                   + (rArgs.mpStr[ nCharPos+1 ] - 0xDC00);
115         }
116 
117         if( bRightToLeft )
118             cChar = GetMirroredChar( cChar );
119         sal_GlyphId aGlyphId = rFont.GetGlyphIndex( cChar );
120         // when glyph fallback is needed update LayoutArgs
121         if( !aGlyphId ) {
122             rArgs.NeedFallback( nCharPos, bRightToLeft );
123 	    if( cChar >= 0x10000 ) // handle surrogate pairs
124                 rArgs.NeedFallback( nCharPos+1, bRightToLeft );
125 	}
126 
127         // apply pair kerning to prev glyph if requested
128         if( SAL_LAYOUT_KERNING_PAIRS & rArgs.mnFlags )
129         {
130             int nKernValue = rFont.GetGlyphKernValue( nOldGlyphId, aGlyphId );
131             nGlyphWidth += nKernValue;
132             aPrevItem.mnNewWidth = nGlyphWidth;
133         }
134 
135         // finish previous glyph
136         if( nOldGlyphId != GF_DROPPED )
137             rLayout.AppendGlyph( aPrevItem );
138         aNewPos.X() += nGlyphWidth;
139 
140         // prepare GlyphItem for appending it in next round
141         nOldGlyphId = aGlyphId;
142         const GlyphMetric& rGM = rFont.GetGlyphMetric( aGlyphId );
143         nGlyphWidth = rGM.GetCharWidth();
144         int nGlyphFlags = bRightToLeft ? GlyphItem::IS_RTL_GLYPH : 0;
145         aPrevItem = GlyphItem( nCharPos, aGlyphId, aNewPos, nGlyphFlags, nGlyphWidth );
146     }
147 
148     // append last glyph item if any
149     if( nOldGlyphId != GF_DROPPED )
150         rLayout.AppendGlyph( aPrevItem );
151 
152     return true;
153 }
154 
155 // =======================================================================
156 // bridge to ICU LayoutEngine
157 // =======================================================================
158 
159 #ifdef ENABLE_ICU_LAYOUT
160 
161 #define bool_t signed char
162 
163 // disable warnings in icu layout headers
164 #if defined __SUNPRO_CC
165 #pragma disable_warn
166 #endif
167 
168 #include <layout/LayoutEngine.h>
169 #include <layout/LEFontInstance.h>
170 #include <layout/LEScripts.h>
171 
172 // enable warnings again
173 #if defined __SUNPRO_CC
174 #pragma enable_warn
175 #endif
176 
177 #include <unicode/uscript.h>
178 #include <unicode/ubidi.h>
179 
180 using namespace U_ICU_NAMESPACE;
181 
182 static const LEGlyphID ICU_DELETED_GLYPH = 0xFFFF;
183 static const LEGlyphID ICU_MARKED_GLYPH = 0xFFFE;
184 
185 // -----------------------------------------------------------------------
186 
187 class IcuFontFromServerFont
188 : public LEFontInstance
189 {
190 private:
191     FreetypeServerFont&     mrServerFont;
192 
193 public:
IcuFontFromServerFont(FreetypeServerFont & rFont)194                             IcuFontFromServerFont( FreetypeServerFont& rFont )
195                             : mrServerFont( rFont )
196                             {}
197 
198     virtual const void*     getFontTable(LETag tableTag) const;
199     virtual le_int32        getUnitsPerEM() const;
200     virtual float           getXPixelsPerEm() const;
201     virtual float           getYPixelsPerEm() const;
202     virtual float           getScaleFactorX() const;
203     virtual float           getScaleFactorY() const;
204 
205     using LEFontInstance::mapCharToGlyph;
206     virtual LEGlyphID       mapCharToGlyph( LEUnicode32 ch ) const;
207 
208     virtual le_int32        getAscent() const;
209     virtual le_int32        getDescent() const;
210     virtual le_int32        getLeading() const;
211 
212     virtual void            getGlyphAdvance( LEGlyphID glyph, LEPoint &advance ) const;
213     virtual le_bool         getGlyphPoint( LEGlyphID glyph, le_int32 pointNumber, LEPoint& point ) const;
214 };
215 
216 // -----------------------------------------------------------------------
217 
getFontTable(LETag nICUTableTag) const218 const void* IcuFontFromServerFont::getFontTable( LETag nICUTableTag ) const
219 {
220     char pTagName[5];
221     pTagName[0] = (char)(nICUTableTag >> 24);
222     pTagName[1] = (char)(nICUTableTag >> 16);
223     pTagName[2] = (char)(nICUTableTag >>  8);
224     pTagName[3] = (char)(nICUTableTag);
225     pTagName[4] = 0;
226 
227     sal_uLong nLength;
228     const unsigned char* pBuffer = mrServerFont.GetTable( pTagName, &nLength );
229 #ifdef VERBOSE_DEBUG
230     fprintf(stderr,"IcuGetTable(\"%s\") => %p\n", pTagName, pBuffer);
231     int mnHeight = mrServerFont.GetFontSelData().mnHeight;
232     const char* pName = mrServerFont.GetFontFileName()->getStr();
233     fprintf(stderr,"font( h=%d, \"%s\" )\n", mnHeight, pName );
234 #endif
235     return (const void*)pBuffer;
236 }
237 
238 // -----------------------------------------------------------------------
239 
getUnitsPerEM() const240 le_int32 IcuFontFromServerFont::getUnitsPerEM() const
241 {
242     return mrServerFont.GetEmUnits();
243 }
244 
245 // -----------------------------------------------------------------------
246 
getXPixelsPerEm() const247 float IcuFontFromServerFont::getXPixelsPerEm() const
248 {
249     const ImplFontSelectData& r = mrServerFont.GetFontSelData();
250     float fX = r.mnWidth ? r.mnWidth : r.mnHeight;
251     return fX;
252 }
253 
254 // -----------------------------------------------------------------------
255 
getYPixelsPerEm() const256 float IcuFontFromServerFont::getYPixelsPerEm() const
257 {
258     float fY = mrServerFont.GetFontSelData().mnHeight;
259     return fY;
260 }
261 
262 // -----------------------------------------------------------------------
263 
getScaleFactorX() const264 float IcuFontFromServerFont::getScaleFactorX() const
265 {
266     return 1.0;
267 }
268 
269 // -----------------------------------------------------------------------
270 
getScaleFactorY() const271 float IcuFontFromServerFont::getScaleFactorY() const
272 {
273     return 1.0;
274 }
275 
276 // -----------------------------------------------------------------------
277 
mapCharToGlyph(LEUnicode32 ch) const278 LEGlyphID IcuFontFromServerFont::mapCharToGlyph( LEUnicode32 ch ) const
279 {
280     LEGlyphID nGlyphIndex = mrServerFont.GetRawGlyphIndex( ch );
281     return nGlyphIndex;
282 }
283 
284 // -----------------------------------------------------------------------
285 
getAscent() const286 le_int32 IcuFontFromServerFont::getAscent() const
287 {
288     const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
289     le_int32 nAscent = (+rMetrics.ascender + 32) >> 6;
290     return nAscent;
291 }
292 
293 // -----------------------------------------------------------------------
294 
getDescent() const295 le_int32 IcuFontFromServerFont::getDescent() const
296 {
297     const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
298     le_int32 nDescent = (-rMetrics.descender + 32) >> 6;
299     return nDescent;
300 }
301 
302 // -----------------------------------------------------------------------
303 
getLeading() const304 le_int32 IcuFontFromServerFont::getLeading() const
305 {
306     const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
307     le_int32 nLeading = ((rMetrics.height - rMetrics.ascender + rMetrics.descender) + 32) >> 6;
308     return nLeading;
309 }
310 
311 // -----------------------------------------------------------------------
312 
getGlyphAdvance(LEGlyphID nGlyphIndex,LEPoint & advance) const313 void IcuFontFromServerFont::getGlyphAdvance( LEGlyphID nGlyphIndex,
314     LEPoint &advance ) const
315 {
316     if( (nGlyphIndex == ICU_MARKED_GLYPH)
317     ||  (nGlyphIndex == ICU_DELETED_GLYPH) )
318     {
319         // deleted glyph or mark glyph has not advance
320         advance.fX = 0;
321     }
322     else
323     {
324         const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nGlyphIndex );
325         advance.fX = rGM.GetCharWidth();
326     }
327 
328     advance.fY = 0;
329 }
330 
331 // -----------------------------------------------------------------------
332 
getGlyphPoint(LEGlyphID,le_int32 pointNumber,LEPoint &) const333 le_bool IcuFontFromServerFont::getGlyphPoint( LEGlyphID,
334     le_int32
335 #if OSL_DEBUG_LEVEL > 1
336 pointNumber
337 #endif
338     ,
339     LEPoint& ) const
340 {
341     //TODO: replace dummy implementation
342 #if OSL_DEBUG_LEVEL > 1
343     fprintf(stderr,"getGlyphPoint(%d)\n", pointNumber );
344 #endif
345     return false;
346 }
347 
348 // =======================================================================
349 
350 class IcuLayoutEngine : public ServerFontLayoutEngine
351 {
352 private:
353     IcuFontFromServerFont   maIcuFont;
354 
355     le_int32                meScriptCode;
356     LayoutEngine*           mpIcuLE;
357 
358 public:
359                             IcuLayoutEngine( FreetypeServerFont& );
360     virtual                 ~IcuLayoutEngine();
361 
362     virtual bool            operator()( ServerFontLayout&, ImplLayoutArgs& );
363 };
364 
365 // -----------------------------------------------------------------------
366 
IcuLayoutEngine(FreetypeServerFont & rServerFont)367 IcuLayoutEngine::IcuLayoutEngine( FreetypeServerFont& rServerFont )
368 :   maIcuFont( rServerFont ),
369     meScriptCode( USCRIPT_INVALID_CODE ),
370     mpIcuLE( NULL )
371 {}
372 
373 // -----------------------------------------------------------------------
374 
~IcuLayoutEngine()375 IcuLayoutEngine::~IcuLayoutEngine()
376 {
377     if( mpIcuLE )
378         delete mpIcuLE;
379 }
380 
381 // -----------------------------------------------------------------------
382 
lcl_CharIsJoiner(sal_Unicode cChar)383 static bool lcl_CharIsJoiner(sal_Unicode cChar)
384 {
385 	return ((cChar == 0x200C) || (cChar == 0x200D));
386 }
387 
operator ()(ServerFontLayout & rLayout,ImplLayoutArgs & rArgs)388 bool IcuLayoutEngine::operator()( ServerFontLayout& rLayout, ImplLayoutArgs& rArgs )
389 {
390     LEUnicode* pIcuChars;
391     if( sizeof(LEUnicode) == sizeof(*rArgs.mpStr) )
392         pIcuChars = (LEUnicode*)rArgs.mpStr;
393     else
394     {
395         // this conversion will only be needed when either
396         // ICU's or OOo's unicodes stop being unsigned shorts
397         // TODO: watch out for surrogates!
398         pIcuChars = (LEUnicode*)alloca( rArgs.mnLength * sizeof(LEUnicode) );
399         for( xub_StrLen ic = 0; ic < rArgs.mnLength; ++ic )
400             pIcuChars[ic] = static_cast<LEUnicode>( rArgs.mpStr[ic] );
401     }
402 
403     // allocate temporary arrays, note: round to even
404     int nGlyphCapacity = (3 * (rArgs.mnEndCharPos - rArgs.mnMinCharPos ) | 15) + 1;
405 
406     struct IcuPosition{ float fX, fY; };
407     const int nAllocSize = sizeof(LEGlyphID) + sizeof(le_int32) + sizeof(IcuPosition);
408     LEGlyphID* pIcuGlyphs = (LEGlyphID*)alloca( (nGlyphCapacity * nAllocSize) + sizeof(IcuPosition) );
409     le_int32* pCharIndices = (le_int32*)((char*)pIcuGlyphs + nGlyphCapacity * sizeof(LEGlyphID) );
410     IcuPosition* pGlyphPositions = (IcuPosition*)((char*)pCharIndices + nGlyphCapacity * sizeof(le_int32) );
411 
412     FreetypeServerFont& rFont = reinterpret_cast<FreetypeServerFont&>(rLayout.GetServerFont());
413 
414     UErrorCode rcI18n = U_ZERO_ERROR;
415     LEErrorCode rcIcu = LE_NO_ERROR;
416     Point aNewPos( 0, 0 );
417     for( int nGlyphCount = 0;; )
418     {
419         int nMinRunPos, nEndRunPos;
420         bool bRightToLeft;
421         if( !rArgs.GetNextRun( &nMinRunPos, &nEndRunPos, &bRightToLeft ) )
422             break;
423 
424         // find matching script
425         // TODO: split up bidi run into script runs
426         le_int32 eScriptCode = -1;
427         for( int i = nMinRunPos; i < nEndRunPos; ++i )
428         {
429             eScriptCode = uscript_getScript( pIcuChars[i], &rcI18n );
430             if( (eScriptCode > 0) && (eScriptCode != latnScriptCode) )
431                 break;
432         }
433         if( eScriptCode < 0 )   // TODO: handle errors better
434             eScriptCode = latnScriptCode;
435 
436         // get layout engine matching to this script
437         // no engine change necessary if script is latin
438         if( !mpIcuLE || ((eScriptCode != meScriptCode) && (eScriptCode > USCRIPT_INHERITED)) )
439         {
440             // TODO: cache multiple layout engines when multiple scripts are used
441             delete mpIcuLE;
442             meScriptCode = eScriptCode;
443             le_int32 eLangCode = 0; // TODO: get better value
444             mpIcuLE = LayoutEngine::layoutEngineFactory( &maIcuFont, eScriptCode, eLangCode, rcIcu );
445             if( LE_FAILURE(rcIcu) )
446             {
447                 delete mpIcuLE;
448                 mpIcuLE = NULL;
449             }
450         }
451 
452         // fall back to default layout if needed
453         if( !mpIcuLE )
454             break;
455 
456         // run ICU layout engine
457         // TODO: get enough context, remove extra glyps below
458         int nRawRunGlyphCount = mpIcuLE->layoutChars( pIcuChars,
459             nMinRunPos, nEndRunPos - nMinRunPos, rArgs.mnLength,
460             bRightToLeft, aNewPos.X(), aNewPos.Y(), rcIcu );
461         if( LE_FAILURE(rcIcu) )
462             return false;
463 
464         // import layout info from icu
465         mpIcuLE->getGlyphs( pIcuGlyphs, rcIcu );
466         mpIcuLE->getCharIndices( pCharIndices, rcIcu );
467         mpIcuLE->getGlyphPositions( &pGlyphPositions->fX, rcIcu );
468         mpIcuLE->reset(); // TODO: get rid of this, PROBLEM: crash at exit when removed
469         if( LE_FAILURE(rcIcu) )
470             return false;
471 
472         // layout bidi/script runs and export them to a ServerFontLayout
473         // convert results to GlyphItems
474         int nLastCharPos = -1;
475         int nClusterMinPos = -1;
476         int nClusterMaxPos = -1;
477         bool bClusterStart = true;
478         int nFilteredRunGlyphCount = 0;
479         const IcuPosition* pPos = pGlyphPositions;
480         for( int i = 0; i < nRawRunGlyphCount; ++i, ++pPos )
481         {
482             LEGlyphID nGlyphIndex = pIcuGlyphs[i];
483             // ignore glyphs which were marked or deleted by ICU
484             if( (nGlyphIndex == ICU_MARKED_GLYPH)
485             ||  (nGlyphIndex == ICU_DELETED_GLYPH) )
486                 continue;
487 
488             // adjust the relative char pos
489             int nCharPos = pCharIndices[i];
490             if( nCharPos >= 0 ) {
491                 nCharPos += nMinRunPos;
492                 // ICU seems to return bad pCharIndices
493                 // for some combinations of ICU+font+text
494                 // => better give up now than crash later
495                 if( nCharPos >= nEndRunPos )
496                     continue;
497             }
498 
499             // if needed request glyph fallback by updating LayoutArgs
500             if( !nGlyphIndex )
501             {
502                 if( nCharPos >= 0 )
503                 {
504                     rArgs.NeedFallback( nCharPos, bRightToLeft );
505                     if ( (nCharPos > 0) && lcl_CharIsJoiner(rArgs.mpStr[nCharPos-1]) )
506                         rArgs.NeedFallback( nCharPos-1, bRightToLeft );
507                     else if ( (nCharPos + 1 < nEndRunPos) && lcl_CharIsJoiner(rArgs.mpStr[nCharPos+1]) )
508                         rArgs.NeedFallback( nCharPos+1, bRightToLeft );
509                 }
510 
511                 if( SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags )
512                     continue;
513             }
514 
515 
516             // apply vertical flags, etc.
517 			bool bDiacritic = false;
518             if( nCharPos >= 0 )
519             {
520                 sal_UCS4 aChar = rArgs.mpStr[ nCharPos ];
521 #if 0 // TODO: enable if some unicodes>0xFFFF should need glyph flags!=0
522                 if( (aChar >= 0xD800) && (aChar <= 0xDFFF) )
523                 {
524                     if( cChar >= 0xDC00 ) // this part of a surrogate pair was already processed
525                         continue;
526                     // calculate unicode scalar value of surrogate pair
527                     aChar = 0x10000 + ((aChar - 0xD800) << 10);
528                     sal_UCS4 aLow = rArgs.mpStr[ nCharPos+1 ];
529                     aChar += aLow & 0x03FF;
530                 }
531 #endif
532                 nGlyphIndex = rFont.FixupGlyphIndex( nGlyphIndex, aChar );
533 
534                 // #i99367# HACK: try to detect all diacritics
535 				if( aChar>=0x0300 && aChar<0x2100 )
536 					bDiacritic = IsDiacritic( aChar );
537             }
538 
539             // get glyph position and its metrics
540             aNewPos = Point( (int)(pPos->fX+0.5), (int)(pPos->fY+0.5) );
541             const GlyphMetric& rGM = rFont.GetGlyphMetric( nGlyphIndex );
542             int nGlyphWidth = rGM.GetCharWidth();
543             int nNewWidth = nGlyphWidth;
544             if( nGlyphWidth <= 0 )
545                 bDiacritic |= true;
546             // #i99367# force all diacritics to zero width
547             // TODO: we need mnOrigWidth/mnLogicWidth/mnNewWidth
548             else if( bDiacritic )
549                 nGlyphWidth = nNewWidth = 0;
550             else
551             {
552                 // Hack, find next +ve width glyph and calculate current
553                 // glyph width by subtracting the two positions
554                 const IcuPosition* pNextPos = pPos+1;
555                 for ( int j = i + 1; j <= nRawRunGlyphCount; ++j, ++pNextPos )
556                 {
557                     if ( j == nRawRunGlyphCount )
558                     {
559                         nNewWidth = static_cast<int>(pNextPos->fX - pPos->fX);
560                         break;
561                     }
562 
563                     LEGlyphID nNextGlyphIndex = pIcuGlyphs[j];
564                     if( (nNextGlyphIndex == ICU_MARKED_GLYPH)
565                     ||  (nNextGlyphIndex == ICU_DELETED_GLYPH) )
566                         continue;
567 
568                     const GlyphMetric& rNextGM = rFont.GetGlyphMetric( nNextGlyphIndex );
569                     int nNextGlyphWidth = rNextGM.GetCharWidth();
570                     if ( nNextGlyphWidth > 0 )
571                     {
572                         nNewWidth = static_cast<int>(pNextPos->fX - pPos->fX);
573                         break;
574                     }
575                 }
576             }
577 
578             // heuristic to detect glyph clusters
579 			bool bInCluster = true;
580             if( nLastCharPos == -1 )
581             {
582 				nClusterMinPos = nClusterMaxPos = nCharPos;
583 				bInCluster = false;
584             }
585             else if( !bRightToLeft )
586 			{
587 				// left-to-right case
588 				if( nClusterMinPos > nCharPos )
589 					nClusterMinPos = nCharPos;		// extend cluster
590 				else if( nCharPos <= nClusterMaxPos )
591 					/*NOTHING*/;					// inside cluster
592 				else if( bDiacritic )
593 					nClusterMaxPos = nCharPos;		// add diacritic to cluster
594 				else {
595 					nClusterMinPos = nClusterMaxPos = nCharPos;	// new cluster
596 					bInCluster = false;
597 				}
598 			}
599 			else
600 			{
601 				// right-to-left case
602 				if( nClusterMaxPos < nCharPos )
603 					nClusterMaxPos = nCharPos;		// extend cluster
604 				else if( nCharPos >= nClusterMinPos )
605 					/*NOTHING*/;					// inside cluster
606 				else if( bDiacritic )
607 				{
608 					nClusterMinPos = nCharPos;		// ICU often has [diacritic* baseglyph*]
609 					if( bClusterStart ) {
610 						nClusterMaxPos = nCharPos;
611 						bInCluster = false;
612 					}
613 				}
614 				else
615 				{
616 					nClusterMinPos = nClusterMaxPos = nCharPos;	// new cluster
617 					bInCluster = !bClusterStart;
618 				}
619             }
620 
621             long nGlyphFlags = 0;
622             if( bInCluster )
623                 nGlyphFlags |= GlyphItem::IS_IN_CLUSTER;
624             if( bRightToLeft )
625                 nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
626             if( bDiacritic )
627                 nGlyphFlags |= GlyphItem::IS_DIACRITIC;
628 
629             // add resulting glyph item to layout
630             GlyphItem aGI( nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nGlyphWidth );
631             aGI.mnNewWidth = nNewWidth;
632             rLayout.AppendGlyph( aGI );
633             ++nFilteredRunGlyphCount;
634             nLastCharPos = nCharPos;
635             bClusterStart = !aGI.IsDiacritic(); // TODO: only needed in RTL-codepath
636         }
637         aNewPos = Point( (int)(pPos->fX+0.5), (int)(pPos->fY+0.5) );
638         nGlyphCount += nFilteredRunGlyphCount;
639     }
640 
641     // sort glyphs in visual order
642     // and then in logical order (e.g. diacritics after cluster start)
643     rLayout.SortGlyphItems();
644 
645     // determine need for kashida justification
646     if( (rArgs.mpDXArray || rArgs.mnLayoutWidth)
647     &&  ((meScriptCode == arabScriptCode) || (meScriptCode == syrcScriptCode)) )
648         rArgs.mnFlags |= SAL_LAYOUT_KASHIDA_JUSTIFICATON;
649 
650     return true;
651 }
652 
653 #endif // ENABLE_ICU_LAYOUT
654 
655 // =======================================================================
656 
GetLayoutEngine()657 ServerFontLayoutEngine* FreetypeServerFont::GetLayoutEngine()
658 {
659     // find best layout engine for font, platform, script and language
660 #ifdef ENABLE_ICU_LAYOUT
661     if( !mpLayoutEngine && FT_IS_SFNT( maFaceFT ) )
662         mpLayoutEngine = new IcuLayoutEngine( *this );
663 #endif // ENABLE_ICU_LAYOUT
664 
665     return mpLayoutEngine;
666 }
667 
668 // =======================================================================
669 
670