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 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 101 CTLayout::~CTLayout() 102 { 103 if( mpCTLine ) 104 CFRelease( mpCTLine ); 105 if( mpAttrString ) 106 CFRelease( mpAttrString ); 107 CFRelease( mpStyleDict ); 108 } 109 110 // ----------------------------------------------------------------------- 111 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 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 238 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 // set the text color as fill color (see kCTForegroundColorFromContextAttributeName) 269 CGContextSetFillColor( rAquaGraphics.mrContext, rAquaGraphics.maTextColor.AsArray() ); 270 271 // draw the text 272 CTLineDraw( mpCTLine, rAquaGraphics.mrContext ); 273 274 // request an update of the changed window area 275 if( rAquaGraphics.IsWindowGraphics() ) 276 { 277 const CGRect aInkRect = CTLineGetImageBounds( mpCTLine, rAquaGraphics.mrContext ); 278 const CGRect aRefreshRect = CGContextConvertRectToDeviceSpace( rAquaGraphics.mrContext, aInkRect ); 279 rAquaGraphics.RefreshRect( aRefreshRect ); 280 } 281 282 // restore the original graphic context transformations 283 CGContextRestoreGState( rAquaGraphics.mrContext ); 284 } 285 286 // ----------------------------------------------------------------------- 287 288 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 383 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 397 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 448 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 487 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 515 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 537 bool CTLayout::GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const { return false; } 538 void CTLayout::InitFont() const {} 539 void CTLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ ) {} 540 void CTLayout::DropGlyph( int /*nStart*/ ) {} 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() 545 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 565 SalLayout* CTTextStyle::GetTextLayout( void ) const 566 { 567 return new CTLayout( this); 568 } 569 570 // ======================================================================= 571 572