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