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