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