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