xref: /aoo41x/main/vcl/aqua/source/gdi/atslayout.cxx (revision cdf0e10c)
1 /*************************************************************************
2 *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26 ************************************************************************/
27 
28 #include "tools/debug.hxx"
29 
30 #include "aqua/saldata.hxx"
31 #include "aqua/salgdi.h"
32 #include "aqua/salatsuifontutils.hxx"
33 
34 #include "sallayout.hxx"
35 #include "salgdi.hxx"
36 
37 #include <math.h>
38 
39 // =======================================================================
40 
41 class ATSLayout : public SalLayout
42 {
43 public:
44                     ATSLayout( ATSUStyle&, float fFontScale );
45 	virtual         ~ATSLayout();
46 
47 	virtual bool	LayoutText( ImplLayoutArgs& );
48 	virtual void	AdjustLayout( ImplLayoutArgs& );
49 	virtual void	DrawText( SalGraphics& ) const;
50 
51 	virtual int		GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&,
52 						sal_Int32* pGlyphAdvances, int* pCharIndexes ) const;
53 
54 	virtual long    GetTextWidth() const;
55 	virtual long    FillDXArray( long* pDXArray ) const;
56 	virtual int     GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
57 	virtual void    GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
58 	virtual bool    GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const;
59 	virtual bool    GetBoundRect( SalGraphics&, Rectangle& ) const;
60 
61 	const ImplFontData* GetFallbackFontData( sal_GlyphId ) const;
62 
63 	virtual void    InitFont();
64 	virtual void    MoveGlyph( int nStart, long nNewXPos );
65 	virtual void    DropGlyph( int nStart );
66 	virtual void    Simplify( bool bIsBase );
67 
68 private:
69 	ATSUStyle&			mrATSUStyle;
70 	ATSUTextLayout		maATSULayout;
71 	int					mnCharCount;		// ==mnEndCharPos-mnMinCharPos
72 	// to prevent ATS overflowing the Fixed16.16 values
73 	// ATS font requests get size limited by downscaling huge fonts
74 	// in these cases the font scale becomes something bigger than 1.0
75 	float				mfFontScale;
76 
77 private:
78 	bool	InitGIA( ImplLayoutArgs* pArgs = NULL ) const;
79 	bool	GetIdealX() const;
80 	bool	GetDeltaY() const;
81 	void	InvalidateMeasurements();
82 
83 	int	Fixed2Vcl( Fixed ) const;       // convert ATSU-Fixed units to VCL units
84 	int	AtsuPix2Vcl( int ) const;       // convert ATSU-Pixel units to VCL units
85 	Fixed	Vcl2Fixed( int ) const;     // convert VCL units to ATSU-Fixed units
86 
87     // cached details about the resulting layout
88     // mutable members since these details are all lazy initialized
89     mutable int			mnGlyphCount;			// glyph count
90     mutable Fixed		mnCachedWidth;			// cached value of resulting typographical width
91     int					mnTrailingSpaceWidth;   // in Pixels
92 
93 	mutable ATSGlyphRef*	mpGlyphIds;			// ATSU glyph ids
94     mutable Fixed*			mpCharWidths;       // map relative charpos to charwidth
95     mutable int*			mpChars2Glyphs;     // map relative charpos to absolute glyphpos
96     mutable int*			mpGlyphs2Chars;     // map absolute glyphpos to absolute charpos
97     mutable bool*			mpGlyphRTLFlags;    // BiDi status for glyphs: true if RTL
98     mutable Fixed*			mpGlyphAdvances;	// contains glyph widths for the justified layout
99     mutable Fixed*			mpGlyphOrigAdvs;	// contains glyph widths for the unjustified layout
100     mutable Fixed*			mpDeltaY;			// vertical offset from the baseline
101 
102 	struct SubPortion { int mnMinCharPos, mnEndCharPos; Fixed mnXOffset; };
103 	typedef std::vector<SubPortion> SubPortionVector;
104 	mutable SubPortionVector	maSubPortions;		// Writer&ATSUI layouts can differ quite a bit...
105 
106     // storing details about fonts used in glyph-fallback for this layout
107     mutable class FallbackInfo*	mpFallbackInfo;
108 
109     // x-offset relative to layout origin
110     // currently only used in RTL-layouts
111     mutable Fixed			mnBaseAdv;
112 };
113 
114 class FallbackInfo
115 {
116 public:
117         FallbackInfo() : mnMaxLevel(0) {}
118     int AddFallback( ATSUFontID );
119     const ImplFontData* GetFallbackFontData( int nLevel ) const;
120 
121 private:
122     const ImplMacFontData* maFontData[ MAX_FALLBACK ];
123     ATSUFontID             maATSUFontId[ MAX_FALLBACK ];
124     int                    mnMaxLevel;
125 };
126 
127 // =======================================================================
128 
129 ATSLayout::ATSLayout( ATSUStyle& rATSUStyle, float fFontScale )
130 :   mrATSUStyle( rATSUStyle ),
131 	maATSULayout( NULL ),
132 	mnCharCount( 0 ),
133 	mfFontScale( fFontScale ),
134 	mnGlyphCount( -1 ),
135 	mnCachedWidth( 0 ),
136 	mnTrailingSpaceWidth( 0 ),
137 	mpGlyphIds( NULL ),
138 	mpCharWidths( NULL ),
139 	mpChars2Glyphs( NULL ),
140 	mpGlyphs2Chars( NULL ),
141 	mpGlyphRTLFlags( NULL ),
142 	mpGlyphAdvances( NULL ),
143 	mpGlyphOrigAdvs( NULL ),
144 	mpDeltaY( NULL ),
145 	mpFallbackInfo( NULL ),
146 	mnBaseAdv( 0 )
147 {}
148 
149 // -----------------------------------------------------------------------
150 
151 ATSLayout::~ATSLayout()
152 {
153     if( mpDeltaY )
154         ATSUDirectReleaseLayoutDataArrayPtr( NULL,
155             kATSUDirectDataBaselineDeltaFixedArray, (void**)&mpDeltaY );
156 
157     if( maATSULayout )
158         ATSUDisposeTextLayout( maATSULayout );
159 
160     delete[] mpGlyphRTLFlags;
161     delete[] mpGlyphs2Chars;
162     delete[] mpChars2Glyphs;
163     if( mpCharWidths != mpGlyphAdvances )
164         delete[] mpCharWidths;
165     delete[] mpGlyphIds;
166     delete[] mpGlyphOrigAdvs;
167     delete[] mpGlyphAdvances;
168 
169     delete mpFallbackInfo;
170 }
171 
172 // -----------------------------------------------------------------------
173 
174 inline int ATSLayout::Fixed2Vcl( Fixed nFixed ) const
175 {
176 	float fFloat = mfFontScale * FixedToFloat( nFixed );
177 	return static_cast<int>(fFloat + 0.5);
178 }
179 
180 // -----------------------------------------------------------------------
181 
182 inline int ATSLayout::AtsuPix2Vcl( int nAtsuPixel) const
183 {
184 	float fVclPixel = mfFontScale * nAtsuPixel;
185 	fVclPixel += (fVclPixel>=0) ? +0.5 : -0.5;	// prepare rounding to int
186 	int nVclPixel = static_cast<int>( fVclPixel);
187 	return nVclPixel;
188 }
189 
190 // -----------------------------------------------------------------------
191 
192 inline Fixed ATSLayout::Vcl2Fixed( int nPixel ) const
193 {
194 	return FloatToFixed( nPixel / mfFontScale );
195 }
196 
197 // -----------------------------------------------------------------------
198 /**
199  * ATSLayout::LayoutText : Manage text layouting
200  *
201  * @param rArgs: contains array of char to be layouted, starting and ending position of the text to layout
202  *
203  * Typographic layout of text by using the style maATSUStyle
204  *
205  * @return : true if everything is ok
206 **/
207 bool ATSLayout::LayoutText( ImplLayoutArgs& rArgs )
208 {
209 	if( maATSULayout )
210 		ATSUDisposeTextLayout( maATSULayout );
211 
212     maATSULayout = NULL;
213 
214 	// Layout text
215 	// set up our locals, verify parameters...
216     DBG_ASSERT( (rArgs.mpStr!=NULL), "ATSLayout::LayoutText() with rArgs.mpStr==NULL !!!");
217     DBG_ASSERT( (mrATSUStyle!=NULL), "ATSLayout::LayoutText() with ATSUStyle==NULL !!!");
218 
219 	SalLayout::AdjustLayout( rArgs );
220 	mnCharCount = mnEndCharPos - mnMinCharPos;
221 
222 	// Workaround a bug in ATSUI with empty string
223 	if( mnCharCount<=0 )
224 		return false;
225 
226 #if (OSL_DEBUG_LEVEL > 3)
227 	Fixed fFontSize = 0;
228 	ByteCount nDummy;
229 	ATSUGetAttribute( mrATSUStyle, kATSUSizeTag, sizeof(fFontSize), &fFontSize, &nDummy);
230 	String aUniName( &rArgs.mpStr[rArgs.mnMinCharPos], mnCharCount );
231 	ByteString aCName( aUniName, RTL_TEXTENCODING_UTF8 );
232 	fprintf( stderr, "ATSLayout( \"%s\" %d..%d of %d) with h=%4.1f\n",
233     	aCName.GetBuffer(),rArgs.mnMinCharPos,rArgs.mnEndCharPos,rArgs.mnLength,Fix2X(fFontSize) );
234 #endif
235 
236 	// create the ATSUI layout
237 	UniCharCount nRunLengths[1] = { mnCharCount };
238 	const int nRunCount = sizeof(nRunLengths)/sizeof(*nRunLengths);
239 	OSStatus eStatus = ATSUCreateTextLayoutWithTextPtr( rArgs.mpStr,
240         rArgs.mnMinCharPos, mnCharCount, rArgs.mnLength,
241         nRunCount, &nRunLengths[0], &mrATSUStyle,
242 		&maATSULayout);
243 
244 	DBG_ASSERT( (eStatus==noErr), "ATSUCreateTextLayoutWithTextPtr failed\n");
245 	if( eStatus != noErr )
246 		return false;
247 
248 	// prepare setting of layout controls
249 	static const int nMaxTagCount = 1;
250 	ATSUAttributeTag aTagAttrs[ nMaxTagCount ];
251 	ByteCount aTagSizes[ nMaxTagCount ];
252 	ATSUAttributeValuePtr aTagValues[ nMaxTagCount ];
253 
254 	// prepare control of "glyph fallback"
255 	const SalData* pSalData = GetSalData();
256 	ATSUFontFallbacks aFontFallbacks = pSalData->mpFontList->maFontFallbacks;
257 	aTagAttrs[0]  = kATSULineFontFallbacksTag;
258 	aTagSizes[0]  = sizeof( ATSUFontFallbacks );
259 	aTagValues[0] = &aFontFallbacks;
260 
261 	// set paragraph layout controls
262 	ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues );
263 
264 	// enable "glyph fallback"
265 	ATSUSetTransientFontMatching( maATSULayout, true );
266 
267 	// control run-specific layout controls
268 	if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG) != 0 )
269 	{
270 		// control BiDi defaults
271 		BOOL nLineDirTag = kATSULeftToRightBaseDirection;
272 		if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) != 0 )
273 			nLineDirTag = kATSURightToLeftBaseDirection;
274 		aTagAttrs[0] = kATSULineDirectionTag;
275 		aTagSizes[0] = sizeof( nLineDirTag );
276 		aTagValues[0] = &nLineDirTag;
277 		// set run-specific layout controls
278 #if 0 // why don't line-controls work as reliably as layout-controls???
279 		ATSUSetLineControls( maATSULayout, rArgs.mnMinCharPos, 1, aTagAttrs, aTagSizes, aTagValues );
280 #else
281 		ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues );
282 #endif
283 	}
284 
285 	return true;
286 }
287 
288 // -----------------------------------------------------------------------
289 /**
290  * ATSLayout::AdjustLayout : Adjust layout style
291  *
292  * @param rArgs: contains attributes relevant to do a text specific layout
293  *
294  * Adjust text layout by moving glyphs to match the requested logical widths
295  *
296  * @return : none
297 **/
298 void ATSLayout::AdjustLayout( ImplLayoutArgs& rArgs )
299 {
300 	int nOrigWidth = GetTextWidth();
301 	int nPixelWidth = rArgs.mnLayoutWidth;
302 	if( !nPixelWidth && rArgs.mpDXArray ) {
303 		// for now we are only interested in the layout width
304 		// TODO: use all mpDXArray elements for layouting
305 		nPixelWidth = rArgs.mpDXArray[ mnCharCount - 1 ];
306 
307 		// workaround for ATSUI not using trailing spaces for justification
308 		int i = mnCharCount;
309 		while( (--i >= 0) && IsSpacingGlyph( rArgs.mpStr[mnMinCharPos+i]|GF_ISCHAR ) ) {}
310 		if( i < 0 ) // nothing to do if the text is all spaces
311 			return;
312 		// #i91685# trailing letters are left aligned (right aligned for RTL)
313 		mnTrailingSpaceWidth = rArgs.mpDXArray[ mnCharCount-1 ];
314 		if( i > 0 )
315 			mnTrailingSpaceWidth -= rArgs.mpDXArray[ i-1 ];
316         InitGIA(); // ensure valid mpCharWidths[], TODO: use GetIdealX() instead?
317 		mnTrailingSpaceWidth -= Fixed2Vcl( mpCharWidths[i] );
318 		// ignore trailing space for calculating the available width
319 		nOrigWidth -= mnTrailingSpaceWidth;
320 		nPixelWidth -= mnTrailingSpaceWidth;
321 		// in RTL-layouts trailing spaces are leftmost
322 		// TODO: use BiDi-algorithm to thoroughly check this assumption
323 		if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL)
324 			mnBaseAdv = mnTrailingSpaceWidth;
325 	}
326 	// return early if there is nothing to do
327 	if( !nPixelWidth )
328 		return;
329 
330 	// HACK: justification requests which change the width by just one pixel were probably
331 	// #i86038# introduced by lossy conversions between integer based coordinate system
332 	// => ignoring such requests has many more benefits than eventual drawbacks
333 	if( (nOrigWidth >= nPixelWidth-1) && (nOrigWidth <= nPixelWidth+1) )
334 		return;
335 
336 	// changing the layout will make all previous measurements invalid
337 	InvalidateMeasurements();
338 
339 	ATSUAttributeTag nTags[3];
340 	ATSUAttributeValuePtr nVals[3];
341 	ByteCount nBytes[3];
342 
343 	Fixed nFixedWidth = Vcl2Fixed( nPixelWidth );
344 	mnCachedWidth = nFixedWidth;
345 	Fract nFractFactor = kATSUFullJustification;
346 	ATSLineLayoutOptions nLineLayoutOptions = kATSLineHasNoHangers | kATSLineHasNoOpticalAlignment | kATSLineBreakToNearestCharacter;
347 
348 	nTags[0] = kATSULineWidthTag;
349 	nVals[0] = &nFixedWidth;
350 	nBytes[0] = sizeof(Fixed);
351 	nTags[1] = kATSULineLayoutOptionsTag;
352 	nVals[1] = &nLineLayoutOptions;
353 	nBytes[1] = sizeof(ATSLineLayoutOptions);
354 	nTags[2] = kATSULineJustificationFactorTag;
355 	nVals[2] = &nFractFactor;
356 	nBytes[2] = sizeof(Fract);
357 
358 	OSStatus eStatus = ATSUSetLayoutControls( maATSULayout, 3, nTags, nBytes, nVals );
359 	if( eStatus != noErr )
360 		return;
361 
362 	// update the measurements of the justified layout to match the justification request
363 	if( rArgs.mpDXArray )
364 		InitGIA( &rArgs );
365 }
366 
367 // -----------------------------------------------------------------------
368 /**
369  * ATSLayout::DrawText : Draw text to screen
370  *
371  * @param rGraphics: device to draw to
372  *
373  * Draw the layouted text to the CGContext
374  *
375  * @return : none
376 **/
377 void ATSLayout::DrawText( SalGraphics& rGraphics ) const
378 {
379 	AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics);
380 
381 	// short circuit if there is nothing to do
382 	if( (mnCharCount <= 0)
383 	||  !rAquaGraphics.CheckContext() )
384 		return;
385 
386 	// the view is vertically flipped => flipped glyphs
387     // so apply a temporary transformation that it flips back
388 	// also compensate if the font was size limited
389     CGContextSaveGState( rAquaGraphics.mrContext );
390     CGContextScaleCTM( rAquaGraphics.mrContext, +mfFontScale, -mfFontScale );
391     CGContextSetShouldAntialias( rAquaGraphics.mrContext, !rAquaGraphics.mbNonAntialiasedText );
392 
393 	// prepare ATSUI drawing attributes
394 	static const ItemCount nMaxControls = 8;
395 	ATSUAttributeTag theTags[ nMaxControls ];
396 	ByteCount theSizes[ nMaxControls];
397 	ATSUAttributeValuePtr theValues[ nMaxControls ];
398 	ItemCount numcontrols = 0;
399 
400 	// Tell ATSUI to use CoreGraphics
401     theTags[numcontrols] = kATSUCGContextTag;
402     theSizes[numcontrols] = sizeof( CGContextRef );
403     theValues[numcontrols++] = &rAquaGraphics.mrContext;
404 
405 	// Rotate if necessary
406 	if( rAquaGraphics.mnATSUIRotation != 0 )
407 	{
408 		Fixed theAngle = rAquaGraphics.mnATSUIRotation;
409 		theTags[numcontrols] = kATSULineRotationTag;
410 		theSizes[numcontrols] = sizeof( Fixed );
411 		theValues[numcontrols++] = &theAngle;
412 	}
413 
414 	DBG_ASSERT( (numcontrols <= nMaxControls), "ATSLayout::DrawText() numcontrols overflow" );
415 	OSStatus theErr = ATSUSetLayoutControls (maATSULayout, numcontrols, theTags, theSizes, theValues);
416 	DBG_ASSERT( (theErr==noErr), "ATSLayout::DrawText ATSUSetLayoutControls failed!\n" );
417 
418 	// Draw the text
419 	const Point aPos = GetDrawPosition( Point(mnBaseAdv,0) );
420 	const Fixed nFixedX = Vcl2Fixed( +aPos.X() );
421 	const Fixed nFixedY = Vcl2Fixed( -aPos.Y() ); // adjusted for y-mirroring
422 	if( maSubPortions.empty() )
423 		ATSUDrawText( maATSULayout, mnMinCharPos, mnCharCount, nFixedX, nFixedY );
424 	else
425 	{
426 		// draw the sub-portions and apply individual adjustments
427 		SubPortionVector::const_iterator it = maSubPortions.begin();
428 		for(; it != maSubPortions.end(); ++it )
429 		{
430 			const SubPortion& rSubPortion = *it;
431 			// calculate sub-portion offset for rotated text
432 			Fixed nXOfsFixed = 0, nYOfsFixed = 0;
433 			if( rAquaGraphics.mnATSUIRotation != 0 )
434 			{
435 				const double fRadians = rAquaGraphics.mnATSUIRotation * (M_PI/0xB40000);
436 				nXOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * cos( fRadians ));
437 				nYOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * sin( fRadians ));
438 			}
439 
440 			// draw sub-portions
441 			ATSUDrawText( maATSULayout,
442 				rSubPortion.mnMinCharPos, rSubPortion.mnEndCharPos - rSubPortion.mnMinCharPos,
443 				nFixedX + nXOfsFixed, nFixedY + nYOfsFixed );
444 		}
445 	}
446 
447 	// request an update of the changed window area
448 	if( rAquaGraphics.IsWindowGraphics() )
449 	{
450 		Rect drawRect; // rectangle of the changed area
451 		theErr = ATSUMeasureTextImage( maATSULayout,
452 			mnMinCharPos, mnCharCount, nFixedX, nFixedY, &drawRect );
453 		if( theErr == noErr )
454         {
455             // FIXME: transformation from baseline to top left
456             // with the simple approach below we invalidate too much
457             short d = drawRect.bottom - drawRect.top;
458             drawRect.top -= d;
459             drawRect.bottom += d;
460             CGRect aRect = CGRectMake( drawRect.left, drawRect.top,
461                                        drawRect.right - drawRect.left,
462                                        drawRect.bottom - drawRect.top );
463             aRect = CGContextConvertRectToDeviceSpace( rAquaGraphics.mrContext, aRect );
464 			rAquaGraphics.RefreshRect( aRect );
465         }
466 	}
467 
468     // restore the original graphic context transformations
469 	CGContextRestoreGState( rAquaGraphics.mrContext );
470 }
471 
472 // -----------------------------------------------------------------------
473 /**
474  * ATSLayout::GetNextGlyphs : Get info about next glyphs in the layout
475  *
476  * @param nLen: max number of char
477  * @param pGlyphs: returned array of glyph ids
478  * @param rPos: returned x starting position
479  * @param nStart: index of the first requested glyph
480  * @param pGlyphAdvances: returned array of glyphs advances
481  * @param pCharIndexes: returned array of char indexes
482  *
483  * Returns infos about the next glyphs in the text layout
484  *
485  * @return : number of glyph details that were provided
486 **/
487 int ATSLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIDs, Point& rPos, int& nStart,
488     sal_Int32* pGlyphAdvances, int* pCharIndexes ) const
489 {
490 	if( nStart < 0 )                // first glyph requested?
491 		nStart = 0;
492 
493 	// get glyph measurements
494 	InitGIA();
495 	// some measurements are only needed for multi-glyph results
496 	if( nLen > 1 )
497 	{
498 		GetIdealX();
499 		GetDeltaY();
500 	}
501 
502 	if( nStart >= mnGlyphCount )    // no glyph left?
503 		return 0;
504 
505 	// calculate glyph position relative to layout base
506     // TODO: avoid for nStart!=0 case by reusing rPos
507     Fixed nXOffset = mnBaseAdv;
508     for( int i = 0; i < nStart; ++i )
509         nXOffset += mpGlyphAdvances[ i ];
510     // if sub-portion offsets are involved there is an additional x-offset
511     if( !maSubPortions.empty() )
512     {
513     	// prepare to find the sub-portion
514         int nCharPos = nStart + mnMinCharPos;
515         if( mpGlyphs2Chars )
516             nCharPos = mpGlyphs2Chars[nStart];
517 
518     	// find the matching subportion
519     	// TODO: is a non-linear search worth it?
520     	SubPortionVector::const_iterator it = maSubPortions.begin();
521         for(; it != maSubPortions.end(); ++it) {
522             const SubPortion& r = *it;
523             if( nCharPos < r.mnMinCharPos )
524                 continue;
525             if( nCharPos >= r.mnEndCharPos )
526                 continue;
527             // apply the sub-portion xoffset
528             nXOffset += r.mnXOffset;
529             break;
530     	}
531     }
532 
533 	Fixed nYOffset = 0;
534 	if( mpDeltaY )
535 		nYOffset = mpDeltaY[ nStart ];
536 
537     // calculate absolute position in pixel units
538     const Point aRelativePos( Fix2Long(static_cast<Fixed>(nXOffset*mfFontScale)), Fix2Long(static_cast<Fixed>(nYOffset*mfFontScale)) );
539     rPos = GetDrawPosition( aRelativePos );
540 
541 	// update return values
542 	int nCount = 0;
543     while( nCount < nLen )
544     {
545         ++nCount;
546         sal_GlyphId nGlyphId = mpGlyphIds[nStart];
547 
548        	// check if glyph fallback is needed for this glyph
549         // TODO: use ATSUDirectGetLayoutDataArrayPtrFromTextLayout(kATSUDirectDataStyleIndex) API instead?
550         const int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[nStart] : nStart + mnMinCharPos;
551         ATSUFontID nFallbackFontID = kATSUInvalidFontID;
552         UniCharArrayOffset nChangedOffset = 0;
553         UniCharCount nChangedLength = 0;
554         OSStatus eStatus = ATSUMatchFontsToText( maATSULayout, nCharPos, kATSUToTextEnd,
555                       &nFallbackFontID, &nChangedOffset, &nChangedLength );
556         if( (eStatus == kATSUFontsMatched) && ((int)nChangedOffset == nCharPos) )
557         {
558             // fallback is needed
559             if( !mpFallbackInfo )
560                 mpFallbackInfo = new FallbackInfo;
561             // register fallback font
562             const int nLevel = mpFallbackInfo->AddFallback( nFallbackFontID );
563        	    // update sal_GlyphId with fallback level
564             nGlyphId |= (nLevel << GF_FONTSHIFT);
565         }
566 
567         // update resulting glyphid array
568         *(pGlyphIDs++) = nGlyphId;
569 
570         // update returned glyph advance array
571         if( pGlyphAdvances )
572             *(pGlyphAdvances++) = Fixed2Vcl( mpGlyphAdvances[nStart] );
573 
574         // update returned index-into-string array
575         if( pCharIndexes )
576         {
577             int nCharPos;
578             if( mpGlyphs2Chars )
579                 nCharPos = mpGlyphs2Chars[nStart];
580             else
581                 nCharPos = nStart + mnMinCharPos;
582             *(pCharIndexes++) = nCharPos;
583         }
584 
585         // stop at last glyph
586         if( ++nStart >= mnGlyphCount )
587             break;
588 
589         // stop when next the x-position is unexpected
590         if( !maSubPortions.empty() )
591             break;	 // TODO: finish the complete sub-portion
592         if( !pGlyphAdvances && mpGlyphOrigAdvs )
593             if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] )
594                 break;
595 
596         // stop when the next y-position is unexpected
597         if( mpDeltaY )
598             if( mpDeltaY[nStart-1] != mpDeltaY[nStart] )
599                 break;
600     }
601 
602     return nCount;
603 }
604 
605 // -----------------------------------------------------------------------
606 /**
607  * ATSLayout::GetTextWidth : Get typographic width of layouted text
608  *
609  * Get typographic bounds of the text
610  *
611  * @return : text width
612 **/
613 long ATSLayout::GetTextWidth() const
614 {
615 	if( mnCharCount <= 0 )
616 		return 0;
617 
618 	DBG_ASSERT( (maATSULayout!=NULL), "ATSLayout::GetTextWidth() with maATSULayout==NULL !\n");
619 	if( !maATSULayout )
620 		return 0;
621 
622 	if( !mnCachedWidth )
623 	{
624 		// prepare precise measurements on pixel based or reference-device
625 		const UInt16 eTypeOfBounds = kATSUseFractionalOrigins;
626 
627 		// determine number of needed measurement trapezoids
628 		ItemCount nMaxBounds = 0;
629 		OSStatus err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount,
630 			eTypeOfBounds, 0, NULL, &nMaxBounds );
631 		if( (err != noErr)
632 		||  (nMaxBounds <= 0) )
633 			return 0;
634 
635 		// get the trapezoids
636 		typedef std::vector<ATSTrapezoid> TrapezoidVector;
637 		TrapezoidVector aTrapezoidVector( nMaxBounds );
638 		ItemCount nBoundsCount = 0;
639 		err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount,
640 			eTypeOfBounds, nMaxBounds, &aTrapezoidVector[0], &nBoundsCount );
641 		if( err != noErr )
642 			return 0;
643 
644 		DBG_ASSERT( (nBoundsCount <= nMaxBounds), "ATSLayout::GetTextWidth() : too many trapezoids !\n");
645 
646 		// find the bound extremas
647 		Fixed nLeftBound = 0;
648 		Fixed nRightBound = 0;
649 		for( ItemCount i = 0; i < nBoundsCount; ++i )
650 		{
651 			const ATSTrapezoid& rTrap = aTrapezoidVector[i];
652 			if( (i == 0) || (nLeftBound < rTrap.lowerLeft.x) )
653 				nLeftBound = rTrap.lowerLeft.x;
654 			if( (i == 0) || (nRightBound > rTrap.lowerRight.x) )
655 				nRightBound = rTrap.lowerRight.x;
656 		}
657 
658 		// measure the bound extremas
659 		mnCachedWidth = nRightBound - nLeftBound;
660 		// adjust for eliminated trailing space widths
661 	}
662 
663     int nScaledWidth = Fixed2Vcl( mnCachedWidth );
664 	nScaledWidth += mnTrailingSpaceWidth;
665     return nScaledWidth;
666 }
667 
668 // -----------------------------------------------------------------------
669 /**
670  * ATSLayout::FillDXArray : Get Char widths
671  *
672  * @param pDXArray: array to be filled with x-advances
673  *
674  * Fill the pDXArray with horizontal deltas : CharWidths
675  *
676  * @return : typographical width of the complete text layout
677 **/
678 long ATSLayout::FillDXArray( long* pDXArray ) const
679 {
680     // short circuit requests which don't need full details
681     if( !pDXArray )
682         return GetTextWidth();
683 
684 	// check assumptions
685 	DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::FillDXArray() with nTSW!=0" );
686 
687 	// initialize details about the resulting layout
688     InitGIA();
689 
690     // distribute the widths among the string elements
691     int nPixWidth = 0;
692     mnCachedWidth = 0;
693     for( int i = 0; i < mnCharCount; ++i )
694     {
695         // convert and adjust for accumulated rounding errors
696         mnCachedWidth += mpCharWidths[i];
697         const int nOldPixWidth = nPixWidth;
698         nPixWidth = Fixed2Vcl( mnCachedWidth );
699         pDXArray[i] = nPixWidth - nOldPixWidth;
700     }
701 
702     return nPixWidth;
703 }
704 
705 // -----------------------------------------------------------------------
706 /**
707  * ATSLayout::GetTextBreak : Find line break depending on width
708  *
709  * @param nMaxWidth : maximal logical text width in subpixel units
710  * @param nCharExtra: expanded/condensed spacing in subpixel units
711  * @param nFactor:    number of subpixel units per pixel
712  *
713  * Measure the layouted text to find the typographical line break
714  * the result is needed by the language specific line breaking
715  *
716  * @return : string index corresponding to the suggested line break
717 **/
718 int ATSLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
719 {
720 	if( !maATSULayout )
721 		return STRING_LEN;
722 
723 	// the semantics of the legacy use case (nCharExtra!=0) cannot be mapped to ATSUBreakLine()
724 	if( nCharExtra != 0 )
725 	{
726 		// prepare the measurement by layouting and measuring the un-expanded/un-condensed text
727 		if( !InitGIA() )
728 			return STRING_LEN;
729 
730 		// TODO: use a better way than by testing each the char position
731 		ATSUTextMeasurement nATSUSumWidth = 0;
732 		const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nMaxWidth / nFactor );
733 		const ATSUTextMeasurement nATSUExtraWidth = Vcl2Fixed( nCharExtra ) / nFactor;
734 		for( int i = 0; i < mnCharCount; ++i )
735 		{
736 			nATSUSumWidth += mpCharWidths[i];
737 			if( nATSUSumWidth >= nATSUMaxWidth )
738 				return (mnMinCharPos + i);
739 			nATSUSumWidth += nATSUExtraWidth;
740 			if( nATSUSumWidth >= nATSUMaxWidth )
741 				if( i+1 < mnCharCount )
742 					return (mnMinCharPos + i);
743 		}
744 
745 		return STRING_LEN;
746 	}
747 
748 	// get a quick overview on what could fit
749     const long nPixelWidth = (nMaxWidth - (nCharExtra * mnCharCount)) / nFactor;
750     if( nPixelWidth <= 0 )
751         return mnMinCharPos;
752 
753 	// check assumptions
754 	DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::GetTextBreak() with nTSW!=0" );
755 
756 	// initial measurement of text break position
757 	UniCharArrayOffset nBreakPos = mnMinCharPos;
758 	const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nPixelWidth );
759 	if( nATSUMaxWidth <= 0xFFFF ) // #i108584# avoid ATSU rejecting the parameter
760 		return mnMinCharPos;      //           or do ATSUMaxWidth=0x10000;
761 	OSStatus eStatus = ATSUBreakLine( maATSULayout, mnMinCharPos,
762 		nATSUMaxWidth, false, &nBreakPos );
763 	if( (eStatus != noErr) && (eStatus != kATSULineBreakInWord) )
764 		return STRING_LEN;
765 
766 	// the result from ATSUBreakLine() doesn't match the semantics expected by its
767 	// application layer callers from SW+SVX+I18N. Adjust the results to the expectations:
768 
769 	// ATSU reports that everything fits even when trailing spaces would break the line
770 	// #i89789# OOo's application layers expect STRING_LEN if everything fits
771 	if( nBreakPos >= static_cast<UniCharArrayOffset>(mnEndCharPos) )
772 		return STRING_LEN;
773 
774 	// GetTextBreak()'s callers expect it to return the "stupid visual line break".
775 	// Returning anything else result.s in subtle problems in the application layers.
776 	static const bool bInWord = true; // TODO: add as argument to GetTextBreak() method
777 	if( !bInWord )
778 		return nBreakPos;
779 
780 	// emulate stupid visual line breaking by line breaking for the remaining width
781 	ATSUTextMeasurement nLeft, nRight, nDummy;
782 	eStatus = ATSUGetUnjustifiedBounds( maATSULayout, mnMinCharPos, nBreakPos-mnMinCharPos,
783 		&nLeft, &nRight, &nDummy, &nDummy );
784 	if( eStatus != noErr )
785 		return nBreakPos;
786 	const ATSUTextMeasurement nATSURemWidth = nATSUMaxWidth - (nRight - nLeft);
787 	if( nATSURemWidth <= 0xFFFF ) // #i108584# avoid ATSU rejecting the parameter
788 		return nBreakPos;
789 	UniCharArrayOffset nBreakPosInWord = nBreakPos;
790 	eStatus = ATSUBreakLine( maATSULayout, nBreakPos, nATSURemWidth, false, &nBreakPosInWord );
791 	return nBreakPosInWord;
792 }
793 
794 // -----------------------------------------------------------------------
795 /**
796  * ATSLayout::GetCaretPositions : Find positions of carets
797  *
798  * @param nMaxIndex position to which we want to find the carets
799  *
800  * Fill the array of positions of carets (for cursors and selections)
801  *
802  * @return : none
803 **/
804 void ATSLayout::GetCaretPositions( int nMaxIndex, long* pCaretXArray ) const
805 {
806 	DBG_ASSERT( ((nMaxIndex>0)&&!(nMaxIndex&1)),
807 		"ATSLayout::GetCaretPositions() : invalid number of caret pairs requested");
808 
809 	// initialize the caret positions
810     for( int i = 0; i < nMaxIndex; ++i )
811         pCaretXArray[ i ] = -1;
812 
813 	for( int n = 0; n <= mnCharCount; ++n )
814 	{
815 		// measure the characters cursor position
816 		typedef unsigned char Boolean;
817 		const Boolean bIsLeading = true;
818 		ATSUCaret aCaret0, aCaret1;
819 		Boolean bIsSplit;
820 		OSStatus eStatus = ATSUOffsetToCursorPosition( maATSULayout,
821 			mnMinCharPos + n, bIsLeading, kATSUByCharacter,
822 			&aCaret0, &aCaret1, &bIsSplit );
823 		if( eStatus != noErr )
824 			continue;
825 		const Fixed nFixedPos = mnBaseAdv + aCaret0.fX;
826 		// convert the measurement to pixel units
827 		const int nPixelPos = Fixed2Vcl( nFixedPos );
828 		// update previous trailing position
829 		if( n > 0 )
830 			pCaretXArray[2*n-1] = nPixelPos;
831 		// update current leading position
832 		if( 2*n >= nMaxIndex )
833 			break;
834 		pCaretXArray[2*n+0] = nPixelPos;
835 	}
836 }
837 
838 // -----------------------------------------------------------------------
839 /**
840  * ATSLayout::GetBoundRect : Get rectangle dim containing the layouted text
841  *
842  * @param rVCLRect: rectangle of text image (layout) measures
843  *
844  * Get ink bounds of the text
845  *
846  * @return : measurement valid
847 **/
848 bool ATSLayout::GetBoundRect( SalGraphics&, Rectangle& rVCLRect ) const
849 {
850 	const Point aPos = GetDrawPosition( Point(mnBaseAdv, 0) );
851 	const Fixed nFixedX = Vcl2Fixed( +aPos.X() );
852 	const Fixed nFixedY = Vcl2Fixed( +aPos.Y() );
853 
854 	Rect aMacRect;
855 	OSStatus eStatus = ATSUMeasureTextImage( maATSULayout,
856 		mnMinCharPos, mnCharCount, nFixedX, nFixedY, &aMacRect );
857 	if( eStatus != noErr )
858 	    return false;
859 
860 	// ATSU top-bottom are vertically flipped from a VCL aspect
861 	rVCLRect.Left()   = AtsuPix2Vcl( aMacRect.left );
862 	rVCLRect.Top()    = AtsuPix2Vcl( aMacRect.top );
863 	rVCLRect.Right()  = AtsuPix2Vcl( aMacRect.right );
864 	rVCLRect.Bottom() = AtsuPix2Vcl( aMacRect.bottom );
865 	return true;
866 }
867 
868 // -----------------------------------------------------------------------
869 /**
870  * ATSLayout::InitGIA() : get many informations about layouted text
871  *
872  * Fills arrays of information about the gylph layout previously done
873  *	in ASTLayout::LayoutText() : glyph advance (width), glyph delta Y (from baseline),
874  *  mapping between glyph index and character index, chars widths
875  *
876  * @return : true if everything could be computed, otherwise false
877 **/
878 bool ATSLayout::InitGIA( ImplLayoutArgs* pArgs ) const
879 {
880 	// no need to run InitGIA more than once on the same ATSLayout object
881 	if( mnGlyphCount >= 0 )
882 		return true;
883 	mnGlyphCount = 0;
884 
885 	// Workaround a bug in ATSUI with empty string
886 	if( mnCharCount <=  0 )
887 		return false;
888 
889 	// initialize character details
890 	mpCharWidths	= new Fixed[ mnCharCount ];
891 	mpChars2Glyphs	= new int[ mnCharCount ];
892 	for( int n = 0; n < mnCharCount; ++n )
893 	{
894 		mpCharWidths[ n ] = 0;
895 		mpChars2Glyphs[ n ] = -1;
896 	}
897 
898 	// get details about the glyph layout
899 	ItemCount iLayoutDataCount;
900 	const ATSLayoutRecord* pALR;
901 	OSStatus eStatus = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(
902 		maATSULayout, mnMinCharPos, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
903 		(void**)&pALR, &iLayoutDataCount );
904 	DBG_ASSERT( (eStatus==noErr), "ATSLayout::InitGIA() : no ATSLayoutRecords!\n");
905 	if( (eStatus != noErr)
906 	|| (iLayoutDataCount <= 1) )
907 		return false;
908 
909 	// initialize glyph details
910 	mpGlyphIds      = new ATSGlyphRef[ iLayoutDataCount ];
911 	mpGlyphAdvances	= new Fixed[ iLayoutDataCount ];
912 	mpGlyphs2Chars	= new int[ iLayoutDataCount ];
913 
914 	// measure details of the glyph layout
915 	Fixed nLeftPos = 0;
916 	for( ItemCount i = 0; i < iLayoutDataCount; ++i )
917 	{
918 		const ATSLayoutRecord& rALR = pALR[i];
919 
920 		// distribute the widths as fairly as possible among the chars
921 		const int nRelativeIdx = (rALR.originalOffset / 2);
922 		if( i+1 < iLayoutDataCount )
923 			mpCharWidths[ nRelativeIdx ] += pALR[i+1].realPos - rALR.realPos;
924 
925 		// new glyph is available => finish measurement of old glyph
926 		if( mnGlyphCount > 0 )
927 			mpGlyphAdvances[ mnGlyphCount-1 ] = rALR.realPos - nLeftPos;
928 
929 		// ignore marker or deleted glyphs
930 		enum { MARKED_OUTGLYPH=0xFFFE, DROPPED_OUTGLYPH=0xFFFF};
931 		if( rALR.glyphID >= MARKED_OUTGLYPH )
932 			continue;
933 
934 		DBG_ASSERT( !(rALR.flags & kATSGlyphInfoTerminatorGlyph),
935 			"ATSLayout::InitGIA(): terminator glyph not marked as deleted!" );
936 
937 		// store details of the visible glyphs
938 		nLeftPos = rALR.realPos;
939 		mpGlyphIds[ mnGlyphCount ] = rALR.glyphID;
940 
941 		// map visible glyphs to their counterparts in the UTF16-character array
942 		mpGlyphs2Chars[ mnGlyphCount ] = nRelativeIdx + mnMinCharPos;
943 		mpChars2Glyphs[ nRelativeIdx ] = mnGlyphCount;
944 
945 		++mnGlyphCount;
946 	}
947 
948 	// measure complete width
949 	mnCachedWidth = mnBaseAdv;
950 	mnCachedWidth += pALR[iLayoutDataCount-1].realPos - pALR[0].realPos;
951 
952 #if (OSL_DEBUG_LEVEL > 1)
953 	Fixed nWidthSum = mnBaseAdv;
954 	for( int n = 0; n < mnCharCount; ++n )
955 		nWidthSum += mpCharWidths[ n ];
956 	DBG_ASSERT( (nWidthSum==mnCachedWidth),
957 		"ATSLayout::InitGIA(): measured widths do not match!\n" );
958 #endif
959 
960 	// #i91183# we need to split up the portion into sub-portions
961 	// if the ATSU-layout differs too much from the requested layout
962 	if( pArgs && pArgs->mpDXArray )
963 	{
964 		// TODO: non-strong-LTR case cases should be handled too
965 		if( (pArgs->mnFlags & TEXT_LAYOUT_BIDI_STRONG)
966 		&& !(pArgs->mnFlags & TEXT_LAYOUT_BIDI_RTL) )
967 		{
968 			Fixed nSumCharWidths = 0;
969 			SubPortion aSubPortion = { mnMinCharPos, 0, 0 };
970 			for( int i = 0; i < mnCharCount; ++i )
971 			{
972 				// calculate related logical position
973 				nSumCharWidths += mpCharWidths[i];
974 
975 				// start new sub-portion if needed
976 				const Fixed nNextXPos = Vcl2Fixed(pArgs->mpDXArray[i]);
977 				const Fixed nNextXOffset = nNextXPos - nSumCharWidths;
978 				const Fixed nFixedDiff = aSubPortion.mnXOffset - nNextXOffset;
979 				if( (nFixedDiff < -0xC000) || (nFixedDiff > +0xC000) ) {
980 					// get to the end of the current sub-portion
981 					// prevent splitting up at diacritics etc.
982 					int j = i;
983 					while( (++j < mnCharCount) && !mpCharWidths[j] );
984 					aSubPortion.mnEndCharPos = mnMinCharPos + j;
985 					// emit current sub-portion
986 					maSubPortions.push_back( aSubPortion );
987 					// prepare next sub-portion
988 					aSubPortion.mnMinCharPos = aSubPortion.mnEndCharPos;
989 					aSubPortion.mnXOffset = nNextXOffset;
990 				}
991 			}
992 
993 			// emit the remaining sub-portion
994 			if( !maSubPortions.empty() )
995 			{
996 				aSubPortion.mnEndCharPos = mnEndCharPos;
997 				if( aSubPortion.mnEndCharPos != aSubPortion.mnMinCharPos )
998 					maSubPortions.push_back( aSubPortion );
999 			}
1000 		}
1001 
1002 		// override layouted charwidths with requested charwidths
1003 		for( int n = 0; n < mnCharCount; ++n )
1004 			mpCharWidths[ n ] = pArgs->mpDXArray[ n ];
1005 	}
1006 
1007 	// release the ATSU layout records
1008 	ATSUDirectReleaseLayoutDataArrayPtr(NULL,
1009 		kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void**)&pALR );
1010 
1011 	return true;
1012 }
1013 
1014 // -----------------------------------------------------------------------
1015 
1016 bool ATSLayout::GetIdealX() const
1017 {
1018 	// compute the ideal advance widths only once
1019 	if( mpGlyphOrigAdvs != NULL )
1020 		return true;
1021 
1022 	DBG_ASSERT( (mpGlyphIds!=NULL), "GetIdealX() called with mpGlyphIds==NULL !" );
1023 	DBG_ASSERT( (mrATSUStyle!=NULL), "GetIdealX called with mrATSUStyle==NULL !" );
1024 
1025 	// TODO: cache ideal metrics per glyph?
1026 	std::vector<ATSGlyphIdealMetrics> aIdealMetrics;
1027 	aIdealMetrics.resize( mnGlyphCount );
1028 	OSStatus theErr = ATSUGlyphGetIdealMetrics( mrATSUStyle,
1029 		mnGlyphCount, &mpGlyphIds[0], sizeof(*mpGlyphIds), &aIdealMetrics[0] );
1030 	DBG_ASSERT( (theErr==noErr), "ATSUGlyphGetIdealMetrics failed!");
1031 	if( theErr != noErr )
1032 		return false;
1033 
1034 	mpGlyphOrigAdvs = new Fixed[ mnGlyphCount ];
1035 	for( int i = 0;i < mnGlyphCount;++i )
1036 		mpGlyphOrigAdvs[i] = FloatToFixed( aIdealMetrics[i].advance.x );
1037 
1038 	return true;
1039 }
1040 
1041 // -----------------------------------------------------------------------
1042 
1043 bool ATSLayout::GetDeltaY() const
1044 {
1045 	// don't bother to get the same delta-y-array more than once
1046 	if( mpDeltaY != NULL )
1047 		return true;
1048 
1049 #if 1
1050     if( !maATSULayout )
1051 		return false;
1052 
1053 	// get and keep the y-deltas in the mpDeltaY member variable
1054 	// => release it in the destructor
1055 	ItemCount nDeltaCount = 0;
1056 	OSStatus theErr = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(
1057 		maATSULayout, mnMinCharPos, kATSUDirectDataBaselineDeltaFixedArray,
1058 		(void**)&mpDeltaY, &nDeltaCount );
1059 
1060 	DBG_ASSERT( (theErr==noErr ), "mpDeltaY - ATSUDirectGetLayoutDataArrayPtrFromTextLayout failed!\n");
1061 	if( theErr != noErr )
1062 		return false;
1063 
1064 	if( mpDeltaY == NULL )
1065 		return true;
1066 
1067 	if( nDeltaCount != (ItemCount)mnGlyphCount )
1068 	{
1069 		DBG_WARNING( "ATSLayout::GetDeltaY() : wrong deltaY count!" );
1070 		ATSUDirectReleaseLayoutDataArrayPtr( NULL,
1071 			kATSUDirectDataBaselineDeltaFixedArray,	(void**)&mpDeltaY );
1072 		mpDeltaY = NULL;
1073 		return false;
1074 	}
1075 #endif
1076 
1077 	return true;
1078 }
1079 
1080 // -----------------------------------------------------------------------
1081 
1082 #define DELETEAZ( X ) { delete[] X; X = NULL; }
1083 
1084 void ATSLayout::InvalidateMeasurements()
1085 {
1086 	mnGlyphCount = -1;
1087 	DELETEAZ( mpGlyphIds );
1088 	DELETEAZ( mpCharWidths );
1089 	DELETEAZ( mpChars2Glyphs );
1090 	DELETEAZ( mpGlyphs2Chars );
1091 	DELETEAZ( mpGlyphRTLFlags );
1092 	DELETEAZ( mpGlyphAdvances );
1093 	DELETEAZ( mpGlyphOrigAdvs );
1094 	DELETEAZ( mpDeltaY );
1095 }
1096 
1097 // =======================================================================
1098 
1099 #if 0
1100 // helper class to convert ATSUI outlines to VCL PolyPolygons
1101 class PolyArgs
1102 {
1103 public:
1104 				PolyArgs();
1105 				~PolyArgs();
1106 
1107 	void		Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset );
1108 	void		AddPoint( const Float32Point&, PolyFlags );
1109 	void		ClosePolygon();
1110 
1111 private:
1112 	PolyPolygon* mpPolyPoly;
1113 	long		mnXOffset, mnYOffset;
1114 
1115 	Point*		mpPointAry;
1116 	BYTE*		mpFlagAry;
1117 	USHORT		mnMaxPoints;
1118 
1119 	USHORT		mnPointCount;
1120 	USHORT		mnPolyCount;
1121 	bool		mbHasOffline;
1122 };
1123 
1124 // -----------------------------------------------------------------------
1125 
1126 PolyArgs::PolyArgs()
1127 :	mpPolyPoly(NULL),
1128 	mnPointCount(0),
1129 	mnPolyCount(0),
1130 	mbHasOffline(false)
1131 {
1132 	mnMaxPoints = 256;
1133 	mpPointAry	= new Point[ mnMaxPoints ];
1134 	mpFlagAry	= new BYTE [ mnMaxPoints ];
1135 }
1136 
1137 // -----------------------------------------------------------------------
1138 
1139 PolyArgs::~PolyArgs()
1140 {
1141 	delete[] mpFlagAry;
1142 	delete[] mpPointAry;
1143 }
1144 
1145 // -----------------------------------------------------------------------
1146 
1147 void PolyArgs::Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset )
1148 {
1149 	mnXOffset = nXOffset;
1150 	mnYOffset = nYOffset;
1151 	mpPolyPoly = pPolyPoly;
1152 
1153 	mpPolyPoly->Clear();
1154 	mnPointCount = 0;
1155 	mnPolyCount  = 0;
1156 }
1157 
1158 // -----------------------------------------------------------------------
1159 
1160 void PolyArgs::AddPoint( const Float32Point& rPoint, PolyFlags eFlags )
1161 {
1162 	if( mnPointCount >= mnMaxPoints )
1163 	{
1164 		// resize if needed (TODO: use STL?)
1165 		mnMaxPoints *= 4;
1166 		Point* mpNewPoints = new Point[ mnMaxPoints ];
1167 		BYTE* mpNewFlags = new BYTE[ mnMaxPoints ];
1168 		for( int i = 0; i < mnPointCount; ++i )
1169 		{
1170 			mpNewPoints[ i ] = mpPointAry[ i ];
1171 			mpNewFlags[ i ] = mpFlagAry[ i ];
1172 		}
1173 		delete[] mpFlagAry;
1174 		delete[] mpPointAry;
1175 		mpPointAry = mpNewPoints;
1176 		mpFlagAry = mpNewFlags;
1177 	}
1178 
1179 	// convert to pixels and add startpoint offset
1180 	int nXPos = Float32ToInt( rPoint.x );
1181 	int nYPos = Float32ToInt( rPoint.y );
1182 	mpPointAry[ mnPointCount ] = Point( nXPos + mnXOffset, nYPos + mnYOffset );
1183 	// set point flags
1184 	mpFlagAry[ mnPointCount++ ]= eFlags;
1185 	mbHasOffline |= (eFlags != POLY_NORMAL);
1186 }
1187 
1188 // -----------------------------------------------------------------------
1189 
1190 void PolyArgs::ClosePolygon()
1191 {
1192 	if( !mnPolyCount++ )
1193 		 return;
1194 
1195 	// append finished polygon
1196 	Polygon aPoly( mnPointCount, mpPointAry, (mbHasOffline ? mpFlagAry : NULL) );
1197 	mpPolyPoly->Insert( aPoly );
1198 
1199 	// prepare for new polygon
1200 	mnPointCount = 0;
1201 	mbHasOffline = false;
1202 }
1203 #endif
1204 // =======================================================================
1205 
1206 // glyph fallback is supported directly by Aqua
1207 // so methods used only by MultiSalLayout can be dummy implementated
1208 bool ATSLayout::GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const { return false; }
1209 void ATSLayout::InitFont() {}
1210 void ATSLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ ) {}
1211 void ATSLayout::DropGlyph( int /*nStart*/ ) {}
1212 void ATSLayout::Simplify( bool /*bIsBase*/ ) {}
1213 
1214 // get the ImplFontData for a glyph fallback font
1215 // for a glyphid that was returned by ATSLayout::GetNextGlyphs()
1216 const ImplFontData* ATSLayout::GetFallbackFontData( sal_GlyphId nGlyphId ) const
1217 {
1218     // check if any fallback fonts were needed
1219     if( !mpFallbackInfo )
1220         return NULL;
1221     // check if the current glyph needs a fallback font
1222     int nFallbackLevel = (nGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
1223     if( !nFallbackLevel )
1224         return NULL;
1225 	return mpFallbackInfo->GetFallbackFontData( nFallbackLevel );
1226 }
1227 
1228 // =======================================================================
1229 
1230 int FallbackInfo::AddFallback( ATSUFontID nFontId )
1231 {
1232     // check if the fallback font is already known
1233     for( int nLevel = 0; nLevel < mnMaxLevel; ++nLevel )
1234         if( maATSUFontId[ nLevel ] == nFontId )
1235             return (nLevel + 1);
1236 
1237 	// append new fallback font if possible
1238     if( mnMaxLevel >= MAX_FALLBACK-1 )
1239         return 0;
1240     // keep ATSU font id of fallback font
1241     maATSUFontId[ mnMaxLevel ] = nFontId;
1242     // find and cache the corresponding ImplFontData pointer
1243     const SystemFontList* pSFL = GetSalData()->mpFontList;
1244     const ImplMacFontData* pFontData = pSFL->GetFontDataFromId( nFontId );
1245     maFontData[ mnMaxLevel ] = pFontData;
1246     // increase fallback level by one
1247     return (++mnMaxLevel);
1248 }
1249 
1250 // -----------------------------------------------------------------------
1251 
1252 const ImplFontData* FallbackInfo::GetFallbackFontData( int nFallbackLevel ) const
1253 {
1254     const ImplMacFontData* pFallbackFont = maFontData[ nFallbackLevel-1 ];
1255     return pFallbackFont;
1256 }
1257 
1258 // =======================================================================
1259 
1260 SalLayout* AquaSalGraphics::GetTextLayout( ImplLayoutArgs&, int /*nFallbackLevel*/ )
1261 {
1262     ATSLayout* pATSLayout = new ATSLayout( maATSUStyle, mfFontScale );
1263     return pATSLayout;
1264 }
1265 
1266 // =======================================================================
1267 
1268