xref: /aoo41x/main/vcl/aqua/source/gdi/ctlayout.cxx (revision 250ab64b)
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 #include "ctfonts.hxx"
23 
24 // =======================================================================
25 
26 class CTLayout
27 :	public SalLayout
28 {
29 public:
30 	explicit        CTLayout( const CTTextStyle* );
31 	virtual         ~CTLayout( void );
32 
33 	virtual bool	LayoutText( ImplLayoutArgs& );
34 	virtual void	AdjustLayout( ImplLayoutArgs& );
35 	virtual void	DrawText( SalGraphics& ) const;
36 
37 	virtual int     GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int&,
38 						sal_Int32* pGlyphAdvances, int* pCharIndexes ) const;
39 
40 	virtual long    GetTextWidth() const;
41 	virtual long    FillDXArray( sal_Int32* pDXArray ) const;
42 	virtual int     GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
43 	virtual void    GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const;
44 	virtual bool    GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const;
45 	virtual bool    GetBoundRect( SalGraphics&, Rectangle& ) const;
46 
47 	const ImplFontData* GetFallbackFontData( sal_GlyphId ) const;
48 
49 	virtual void	InitFont( void) const;
50 	virtual void	MoveGlyph( int nStart, long nNewXPos );
51 	virtual void	DropGlyph( int nStart );
52 	virtual void	Simplify( bool bIsBase );
53 
54 private:
55 	const CTTextStyle* const	mpTextStyle;
56 
57 	// CoreText specific objects
58 	CFAttributedStringRef mpAttrString;
59 	CTLineRef mpCTLine;
60 
61 	int mnCharCount;		// ==mnEndCharPos-mnMinCharPos
62 	int mnTrailingSpaceCount;
63 	double mfTrailingSpaceWidth;	// preserves the width of stripped-off trailing space
64 
65 	// to prevent overflows
66 	// font requests get size limited by downscaling huge fonts
67 	// in these cases the font scale becomes something bigger than 1.0
68 	float mfFontScale; // TODO: does CoreText have a font size limit?
69 
70 	// cached details about the resulting layout
71 	// mutable members since these details are all lazy initialized
72 	mutable double	mfCachedWidth;			// cached value of resulting typographical width
73 
74 	// x-offset relative to layout origin
75 	// currently only used in RTL-layouts
76 	mutable long	mnBaseAdv;
77 };
78 
79 // =======================================================================
80 
81 CTLayout::CTLayout( const CTTextStyle* pTextStyle )
82 :	mpTextStyle( pTextStyle )
83 ,	mpAttrString( NULL )
84 ,	mpCTLine( NULL )
85 ,	mnCharCount( 0 )
86 ,	mnTrailingSpaceCount( 0 )
87 ,	mfTrailingSpaceWidth( 0.0 )
88 ,	mfFontScale( pTextStyle->mfFontScale )
89 ,	mfCachedWidth( -1 )
90 ,	mnBaseAdv( 0 )
91 {
92 	CFRetain( mpTextStyle->GetStyleDict() );
93 }
94 
95 // -----------------------------------------------------------------------
96 
97 CTLayout::~CTLayout()
98 {
99 	if( mpCTLine )
100 		CFRelease( mpCTLine );
101 	if( mpAttrString )
102 		CFRelease( mpAttrString );
103 	CFRelease( mpTextStyle->GetStyleDict() );
104 }
105 
106 // -----------------------------------------------------------------------
107 
108 bool CTLayout::LayoutText( ImplLayoutArgs& rArgs )
109 {
110 	if( mpAttrString )
111 		CFRelease( mpAttrString );
112 	mpAttrString = NULL;
113 	if( mpCTLine )
114 		CFRelease( mpCTLine );
115 	mpCTLine = NULL;
116 
117 	SalLayout::AdjustLayout( rArgs );
118 	mnCharCount = mnEndCharPos - mnMinCharPos;
119 
120 	// short circuit if there is nothing to do
121 	if( mnCharCount <= 0 )
122 		return false;
123 
124 	// prepare the string to be layouted by CoreText
125 	CFStringRef aCFText = CFStringCreateWithCharactersNoCopy( NULL, rArgs.mpStr + mnMinCharPos, mnCharCount, kCFAllocatorNull );
126 	// #i124375# force soft-hyphen visibility to meet the expectations of Writer+EditEngine
127 	if( CFStringFind( aCFText, (CFStringRef)@"\u00AD", 0).length > 0 )
128 	{
129 		NSString* pDashStr = [(NSString*)aCFText stringByReplacingOccurrencesOfString: @"\u00AD" withString: @"-"];
130 		aCFText = CFStringCreateCopy( NULL, (CFStringRef)pDashStr );
131 	}
132 
133 	// create the CoreText line layout using the requested text style
134 	mpAttrString = CFAttributedStringCreate( NULL, aCFText, mpTextStyle->GetStyleDict() );
135 	mpCTLine = CTLineCreateWithAttributedString( mpAttrString );
136 	CFRelease( aCFText);
137 
138 	// get info about trailing whitespace to prepare for text justification in AdjustLayout()
139 	mnTrailingSpaceCount = 0;
140 	for( int i = mnEndCharPos; --i >= mnMinCharPos; ++mnTrailingSpaceCount )
141 		if( !IsSpacingGlyph( rArgs.mpStr[i] | GF_ISCHAR )
142 		&&  (rArgs.mpStr[i] != 0x00A0) )
143 			break;
144 	return true;
145 }
146 
147 // -----------------------------------------------------------------------
148 
149 void CTLayout::AdjustLayout( ImplLayoutArgs& rArgs )
150 {
151 	if( !mpCTLine)
152 		return;
153 
154 	int nPixelWidth = rArgs.mnLayoutWidth;
155 	if( rArgs.mpDXArray )
156 	{
157 		// for now we are only interested in the layout width
158 		// TODO: use all mpDXArray elements for layouting
159 		nPixelWidth = rArgs.mpDXArray[ mnCharCount-1 ];
160 	}
161 	else if( !nPixelWidth ) // short-circuit if there is nothing to adjust
162 		return;
163 
164 	// short-circuit when justifying an all-whitespace string
165 	if( mnTrailingSpaceCount >= mnCharCount)
166 	{
167 		mfCachedWidth = nPixelWidth / mfFontScale;
168 		return;
169 	}
170 
171 	// return early if there is nothing to do
172 	if( nPixelWidth <= 0 )
173 		return;
174 
175 	// HACK: justification requests which change the width by just one pixel are probably
176 	// #i86038# introduced by lossy conversions between integer based coordinate system
177 	const int nOrigWidth = GetTextWidth();
178 	if( (nOrigWidth >= nPixelWidth-1) && (nOrigWidth <= nPixelWidth+1) )
179 		return;
180 
181 	// if the text to be justified has whitespace in it then
182 	// - Writer goes crazy with its HalfSpace magic
183 	// - CoreText handles spaces specially (in particular at the text end)
184 	if( mnTrailingSpaceCount ) {
185 		int nTrailingSpaceWidth = 0;
186 		if( rArgs.mpDXArray) {
187 			const int nFullPixWidth = nPixelWidth;
188 			nPixelWidth = rArgs.mpDXArray[ mnCharCount-1-mnTrailingSpaceCount ];
189 			nTrailingSpaceWidth = nFullPixWidth - nPixelWidth;
190 			mfTrailingSpaceWidth = nTrailingSpaceWidth;
191 		} else {
192 			if( mfTrailingSpaceWidth <= 0.0 )
193 				mfTrailingSpaceWidth = CTLineGetTrailingWhitespaceWidth( mpCTLine );
194 			nTrailingSpaceWidth = rint( mfTrailingSpaceWidth );
195 			nPixelWidth -= nTrailingSpaceWidth;
196 		}
197 		if( nPixelWidth <= 0 )
198 			return;
199 
200 		// recreate the CoreText line layout without trailing spaces
201 		CFRelease( mpCTLine );
202 		CFStringRef aCFText = CFStringCreateWithCharactersNoCopy( NULL, rArgs.mpStr + mnMinCharPos,
203 			mnCharCount - mnTrailingSpaceCount, kCFAllocatorNull );
204 		CFAttributedStringRef pAttrStr = CFAttributedStringCreate( NULL, aCFText, mpTextStyle->GetStyleDict() );
205 		mpCTLine = CTLineCreateWithAttributedString( pAttrStr );
206 		CFRelease( aCFText);
207 		CFRelease( pAttrStr );
208 
209 		// in RTL-layouts trailing spaces are leftmost
210 		// TODO: use BiDi-algorithm to thoroughly check this assumption
211 		if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL)
212 			mnBaseAdv = nTrailingSpaceWidth;
213 	}
214 
215 	const double fAdjustedWidth = nPixelWidth / mfFontScale;
216 	CTLineRef pNewCTLine = CTLineCreateJustifiedLine( mpCTLine, 1.0, fAdjustedWidth );
217 	if( !pNewCTLine ) { // CTLineCreateJustifiedLine can and does fail
218 		// handle failure by keeping the unjustified layout
219 		// TODO: a better solution such as
220 		// - forcing glyph overlap
221 		// - changing the font size
222 		// - changing the CTM matrix
223 		return;
224 	}
225 	CFRelease( mpCTLine );
226 	mpCTLine = pNewCTLine;
227 	mfCachedWidth = fAdjustedWidth + mfTrailingSpaceWidth;
228 }
229 
230 // -----------------------------------------------------------------------
231 
232 void CTLayout::DrawText( SalGraphics& rGraphics ) const
233 {
234 	AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics);
235 
236 	// short circuit if there is nothing to do
237 	if( (mnCharCount <= 0)
238 	||  !rAquaGraphics.CheckContext() )
239 		return;
240 
241 	// the view is vertically flipped => flipped glyphs
242 	// so apply a temporary transformation that it flips back
243 	// also compensate if the font was size limited
244 	CGContextSaveGState( rAquaGraphics.mrContext );
245 	CGContextScaleCTM( rAquaGraphics.mrContext, +mfFontScale, -mfFontScale );
246 	CGContextSetShouldAntialias( rAquaGraphics.mrContext, !rAquaGraphics.mbNonAntialiasedText );
247 
248 	// Draw the text
249 	const Point aVclPos = GetDrawPosition( Point(mnBaseAdv,0) );
250 	CGPoint aTextPos = { +aVclPos.X()/mfFontScale, -aVclPos.Y()/mfFontScale };
251 
252 	if( mpTextStyle->mfFontRotation != 0.0 )
253 	{
254 		const CGFloat fRadians = mpTextStyle->mfFontRotation;
255 		CGContextRotateCTM( rAquaGraphics.mrContext, +fRadians );
256 
257 		const CGAffineTransform aInvMatrix = CGAffineTransformMakeRotation( -fRadians );
258 		aTextPos = CGPointApplyAffineTransform( aTextPos, aInvMatrix );
259 	}
260 
261 	CGContextSetTextPosition( rAquaGraphics.mrContext, aTextPos.x, aTextPos.y );
262 	CTLineDraw( mpCTLine, rAquaGraphics.mrContext );
263 
264 	// request an update of the changed window area
265 	if( rAquaGraphics.IsWindowGraphics() )
266 	{
267 		const CGRect aInkRect = CTLineGetImageBounds( mpCTLine, rAquaGraphics.mrContext );
268 		const CGRect aRefreshRect = CGContextConvertRectToDeviceSpace( rAquaGraphics.mrContext, aInkRect );
269 		rAquaGraphics.RefreshRect( aRefreshRect );
270 	}
271 
272 	// restore the original graphic context transformations
273 	CGContextRestoreGState( rAquaGraphics.mrContext );
274 }
275 
276 // -----------------------------------------------------------------------
277 
278 int CTLayout::GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int& nStart,
279 	sal_Int32* pGlyphAdvances, int* pCharIndexes ) const
280 {
281 	if( !mpCTLine )
282 		return 0;
283 
284 	if( nStart < 0 ) // first glyph requested?
285 		nStart = 0;
286 	nLen = 1; // TODO: handle nLen>1 below
287 
288 	// prepare to iterate over the glyph runs
289 	int nCount = 0;
290 	int nSubIndex = nStart;
291 
292 	const DynCoreTextSyms& rCT = DynCoreTextSyms::get();
293 	typedef std::vector<CGGlyph> CGGlyphVector;
294 	typedef std::vector<CGPoint> CGPointVector;
295 	typedef std::vector<CGSize>  CGSizeVector;
296 	typedef std::vector<CFIndex> CFIndexVector;
297 	CGGlyphVector aCGGlyphVec;
298 	CGPointVector aCGPointVec;
299 	CGSizeVector  aCGSizeVec;
300 	CFIndexVector aCFIndexVec;
301 
302 	// TODO: iterate over cached layout
303 	CFArrayRef aGlyphRuns = rCT.LineGetGlyphRuns( mpCTLine );
304 	const int nRunCount = CFArrayGetCount( aGlyphRuns );
305 	for( int nRunIndex = 0; nRunIndex < nRunCount; ++nRunIndex ) {
306 		CTRunRef pGlyphRun = (CTRunRef)CFArrayGetValueAtIndex( aGlyphRuns, nRunIndex );
307 		const CFIndex nGlyphsInRun = rCT.RunGetGlyphCount( pGlyphRun );
308 		// skip to the first glyph run of interest
309 		if( nSubIndex >= nGlyphsInRun ) {
310 			nSubIndex -= nGlyphsInRun;
311 			continue;
312 		}
313 		const CFRange aFullRange = CFRangeMake( 0, nGlyphsInRun );
314 
315 		// get glyph run details
316 		const CGGlyph* pCGGlyphIdx = rCT.RunGetGlyphsPtr( pGlyphRun );
317 		if( !pCGGlyphIdx ) {
318 			aCGGlyphVec.reserve( nGlyphsInRun );
319 			CTRunGetGlyphs( pGlyphRun, aFullRange, &aCGGlyphVec[0] );
320 			pCGGlyphIdx = &aCGGlyphVec[0];
321 		}
322 		const CGPoint* pCGGlyphPos = rCT.RunGetPositionsPtr( pGlyphRun );
323 		if( !pCGGlyphPos ) {
324 			aCGPointVec.reserve( nGlyphsInRun );
325 			CTRunGetPositions( pGlyphRun, aFullRange, &aCGPointVec[0] );
326 			pCGGlyphPos = &aCGPointVec[0];
327 		}
328 
329 		const CGSize* pCGGlyphAdvs = NULL;
330 		if( pGlyphAdvances) {
331 			pCGGlyphAdvs = rCT.RunGetAdvancesPtr( pGlyphRun );
332 			if( !pCGGlyphAdvs) {
333 				aCGSizeVec.reserve( nGlyphsInRun );
334 				CTRunGetAdvances( pGlyphRun, aFullRange, &aCGSizeVec[0] );
335 				pCGGlyphAdvs = &aCGSizeVec[0];
336 			}
337 		}
338 
339 		const CFIndex* pCGGlyphStrIdx = NULL;
340 		if( pCharIndexes) {
341 			pCGGlyphStrIdx = rCT.RunGetStringIndicesPtr( pGlyphRun );
342 			if( !pCGGlyphStrIdx) {
343 				aCFIndexVec.reserve( nGlyphsInRun );
344 				CTRunGetStringIndices( pGlyphRun, aFullRange, &aCFIndexVec[0] );
345 				pCGGlyphStrIdx = &aCFIndexVec[0];
346 			}
347 		}
348 
349 		// get the details for each interesting glyph
350 		// TODO: handle nLen>1
351 		for(; (--nLen >= 0) && (nSubIndex < nGlyphsInRun); ++nSubIndex, ++nStart )
352 		{
353 			// convert glyph details for VCL
354 			*(pOutGlyphIds++) = pCGGlyphIdx[ nSubIndex ];
355 			if( pGlyphAdvances )
356 				*(pGlyphAdvances++) = pCGGlyphAdvs[ nSubIndex ].width;
357 			if( pCharIndexes )
358 				*(pCharIndexes++) = pCGGlyphStrIdx[ nSubIndex] + mnMinCharPos;
359 			if( !nCount++ ) {
360 				const CGPoint& rCurPos = pCGGlyphPos[ nSubIndex ];
361 				rPos = GetDrawPosition( Point( mfFontScale * rCurPos.x, mfFontScale * rCurPos.y) );
362 			}
363 		}
364 		nSubIndex = 0; // prepare for the next glyph run
365 		break; // TODO: handle nLen>1
366 	}
367 
368 	return nCount;
369 }
370 
371 // -----------------------------------------------------------------------
372 
373 long CTLayout::GetTextWidth() const
374 {
375 	if( (mnCharCount <= 0) || !mpCTLine )
376 		return 0;
377 
378 	if( mfCachedWidth < 0.0 )
379 		mfCachedWidth = CTLineGetTypographicBounds( mpCTLine, NULL, NULL, NULL );
380 
381 	const long nScaledWidth = lrint( mfFontScale * mfCachedWidth );
382 	return nScaledWidth;
383 }
384 
385 // -----------------------------------------------------------------------
386 
387 long CTLayout::FillDXArray( sal_Int32* pDXArray ) const
388 {
389 	// short circuit requests which don't need full details
390 	if( !pDXArray )
391 		return GetTextWidth();
392 
393 	long nPixWidth = GetTextWidth();
394 	if( pDXArray ) {
395 		// prepare the sub-pixel accurate logical-width array
396 		::std::vector<float> aWidthVector( mnCharCount );
397 		if( mnTrailingSpaceCount && (mfTrailingSpaceWidth > 0.0) ) {
398 			const double fOneWidth = mfTrailingSpaceWidth / mnTrailingSpaceCount;
399 			for( int i = 1; i <= mnTrailingSpaceCount; ++i)
400 				aWidthVector[ mnCharCount - i ] = fOneWidth;
401 		}
402 		// measure advances in each glyph run
403 		CFArrayRef aGlyphRuns = CTLineGetGlyphRuns( mpCTLine );
404 		const int nRunCount = CFArrayGetCount( aGlyphRuns );
405 		typedef std::vector<CGSize> CGSizeVector;
406 		CGSizeVector aSizeVec;
407 		typedef std::vector<CFIndex> CFIndexVector;
408 		CFIndexVector aIndexVec;
409 		for( int nRunIndex = 0; nRunIndex < nRunCount; ++nRunIndex ) {
410 			CTRunRef pGlyphRun = (CTRunRef)CFArrayGetValueAtIndex( aGlyphRuns, nRunIndex );
411 			const CFIndex nGlyphCount = CTRunGetGlyphCount( pGlyphRun );
412 			const CFRange aFullRange = CFRangeMake( 0, nGlyphCount );
413 			aSizeVec.resize( nGlyphCount );
414 			aIndexVec.resize( nGlyphCount );
415 			CTRunGetAdvances( pGlyphRun, aFullRange, &aSizeVec[0] );
416 			CTRunGetStringIndices( pGlyphRun, aFullRange, &aIndexVec[0] );
417 			for( int i = 0; i != nGlyphCount; ++i ) {
418 				const int nRelIdx = aIndexVec[i];
419 				aWidthVector[nRelIdx] += aSizeVec[i].width;
420  			}
421  		}
422 
423 		// convert the sub-pixel accurate array into classic pDXArray integers
424 		float fWidthSum = 0.0;
425 		sal_Int32 nOldDX = 0;
426 		for( int i = 0; i < mnCharCount; ++i) {
427 			const sal_Int32 nNewDX = rint( fWidthSum += aWidthVector[i]);
428 			pDXArray[i] = nNewDX - nOldDX;
429 			nOldDX = nNewDX;
430 		}
431 	}
432 
433 	return nPixWidth;
434 }
435 
436 // -----------------------------------------------------------------------
437 
438 int CTLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
439 {
440 	if( !mpCTLine )
441 		return STRING_LEN;
442 
443 	CTTypesetterRef aCTTypeSetter = CTTypesetterCreateWithAttributedString( mpAttrString );
444 	const double fCTMaxWidth = (double)nMaxWidth / (nFactor * mfFontScale);
445 	CFIndex nIndex = CTTypesetterSuggestClusterBreak( aCTTypeSetter, 0, fCTMaxWidth );
446 	if( nIndex >= mnCharCount )
447 		return STRING_LEN;
448 
449 	nIndex += mnMinCharPos;
450 	return (int)nIndex;
451 }
452 
453 // -----------------------------------------------------------------------
454 
455 void CTLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
456 {
457 	DBG_ASSERT( ((nMaxIndex>0)&&!(nMaxIndex&1)),
458 		"CTLayout::GetCaretPositions() : invalid number of caret pairs requested");
459 
460 	// initialize the caret positions
461 	for( int i = 0; i < nMaxIndex; ++i )
462 		pCaretXArray[ i ] = -1;
463 
464 	const DynCoreTextSyms& rCT = DynCoreTextSyms::get();
465 	for( int n = 0; n <= mnCharCount; ++n )
466 	{
467 		// measure the characters cursor position
468 		CGFloat fPos2 = -1;
469 		const CGFloat fPos1 = rCT.LineGetOffsetForStringIndex( mpCTLine, n, &fPos2 );
470 		(void)fPos2; // TODO: split cursor at line direction change
471 		// update previous trailing position
472 		if( n > 0 )
473 			pCaretXArray[ 2*n-1 ] = lrint( fPos1 * mfFontScale );
474 		// update current leading position
475 		if( 2*n >= nMaxIndex )
476 			break;
477 		pCaretXArray[ 2*n+0 ] = lrint( fPos1 * mfFontScale );
478 	}
479 }
480 
481 // -----------------------------------------------------------------------
482 
483 bool CTLayout::GetBoundRect( SalGraphics& rGraphics, Rectangle& rVCLRect ) const
484 {
485 	AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics);
486 	CGRect aMacRect = CTLineGetImageBounds( mpCTLine, rAquaGraphics.mrContext );
487 	CGPoint aMacPos = CGContextGetTextPosition( rAquaGraphics.mrContext );
488 	aMacRect.origin.x -= aMacPos.x;
489 	aMacRect.origin.y -= aMacPos.y;
490 
491 	const Point aPos = GetDrawPosition( Point(mnBaseAdv, 0) );
492 
493 	// CoreText top-bottom are vertically flipped from a VCL aspect
494 	rVCLRect.Left()   = aPos.X() + mfFontScale * aMacRect.origin.x;
495 	rVCLRect.Right()  = aPos.X() + mfFontScale * (aMacRect.origin.x + aMacRect.size.width);
496 	rVCLRect.Bottom() = aPos.Y() - mfFontScale * aMacRect.origin.y;
497 	rVCLRect.Top()    = aPos.Y() - mfFontScale * (aMacRect.origin.y + aMacRect.size.height);
498 	return true;
499 }
500 
501 // =======================================================================
502 
503 // glyph fallback is supported directly by Aqua
504 // so methods used only by MultiSalLayout can be dummy implementated
505 bool CTLayout::GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const { return false; }
506 void CTLayout::InitFont() const {}
507 void CTLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ ) {}
508 void CTLayout::DropGlyph( int /*nStart*/ ) {}
509 void CTLayout::Simplify( bool /*bIsBase*/ ) {}
510 
511 // get the ImplFontData for a glyph fallback font
512 // for a glyphid that was returned by CTLayout::GetNextGlyphs()
513 const ImplFontData* CTLayout::GetFallbackFontData( sal_GlyphId /*aGlyphId*/ ) const
514 {
515 #if 0
516 	// check if any fallback fonts were needed
517 	if( !mpFallbackInfo )
518 		return NULL;
519 	// check if the current glyph needs a fallback font
520 	int nFallbackLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
521 	if( !nFallbackLevel )
522 		return NULL;
523 	pFallbackFont = mpFallbackInfo->GetFallbackFontData( nFallbackLevel );
524 #else
525 	// let CoreText's font cascading handle glyph fallback
526 	const ImplFontData* pFallbackFont = NULL;
527 #endif
528 	return pFallbackFont;
529 }
530 
531 // =======================================================================
532 
533 SalLayout* CTTextStyle::GetTextLayout( void ) const
534 {
535 	return new CTLayout( this);
536 }
537 
538 // =======================================================================
539 
540