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