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 23 24 #include "tools/debug.hxx" 25 26 #include "aqua/saldata.hxx" 27 #include "aqua/salgdi.h" 28 #include "aqua/salatsuifontutils.hxx" 29 30 #include "sallayout.hxx" 31 #include "salgdi.hxx" 32 33 #include <math.h> 34 35 // ======================================================================= 36 37 class ATSLayout : public SalLayout 38 { 39 public: 40 ATSLayout( ATSUStyle&, float fFontScale ); 41 virtual ~ATSLayout(); 42 43 virtual bool LayoutText( ImplLayoutArgs& ); 44 virtual void AdjustLayout( ImplLayoutArgs& ); 45 virtual void DrawText( SalGraphics& ) const; 46 47 virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&, 48 sal_Int32* pGlyphAdvances, int* pCharIndexes ) const; 49 50 virtual long GetTextWidth() const; 51 virtual long FillDXArray( long* pDXArray ) const; 52 virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const; 53 virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const; 54 virtual bool GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const; 55 virtual bool GetBoundRect( SalGraphics&, Rectangle& ) const; 56 57 const ImplFontData* GetFallbackFontData( sal_GlyphId ) const; 58 59 virtual void InitFont(); 60 virtual void MoveGlyph( int nStart, long nNewXPos ); 61 virtual void DropGlyph( int nStart ); 62 virtual void Simplify( bool bIsBase ); 63 64 private: 65 ATSUStyle& mrATSUStyle; 66 ATSUTextLayout maATSULayout; 67 int mnCharCount; // ==mnEndCharPos-mnMinCharPos 68 // to prevent ATS overflowing the Fixed16.16 values 69 // ATS font requests get size limited by downscaling huge fonts 70 // in these cases the font scale becomes something bigger than 1.0 71 float mfFontScale; 72 73 private: 74 bool InitGIA( ImplLayoutArgs* pArgs = NULL ) const; 75 bool GetIdealX() const; 76 bool GetDeltaY() const; 77 void InvalidateMeasurements(); 78 79 int Fixed2Vcl( Fixed ) const; // convert ATSU-Fixed units to VCL units 80 int AtsuPix2Vcl( int ) const; // convert ATSU-Pixel units to VCL units 81 Fixed Vcl2Fixed( int ) const; // convert VCL units to ATSU-Fixed units 82 83 // cached details about the resulting layout 84 // mutable members since these details are all lazy initialized 85 mutable int mnGlyphCount; // glyph count 86 mutable Fixed mnCachedWidth; // cached value of resulting typographical width 87 int mnTrailingSpaceWidth; // in Pixels 88 89 mutable ATSGlyphRef* mpGlyphIds; // ATSU glyph ids 90 mutable Fixed* mpCharWidths; // map relative charpos to charwidth 91 mutable int* mpChars2Glyphs; // map relative charpos to absolute glyphpos 92 mutable int* mpGlyphs2Chars; // map absolute glyphpos to absolute charpos 93 mutable bool* mpGlyphRTLFlags; // BiDi status for glyphs: true if RTL 94 mutable Fixed* mpGlyphAdvances; // contains glyph widths for the justified layout 95 mutable Fixed* mpGlyphOrigAdvs; // contains glyph widths for the unjustified layout 96 mutable Fixed* mpDeltaY; // vertical offset from the baseline 97 98 struct SubPortion { int mnMinCharPos, mnEndCharPos; Fixed mnXOffset; }; 99 typedef std::vector<SubPortion> SubPortionVector; 100 mutable SubPortionVector maSubPortions; // Writer&ATSUI layouts can differ quite a bit... 101 102 // storing details about fonts used in glyph-fallback for this layout 103 mutable class FallbackInfo* mpFallbackInfo; 104 105 // x-offset relative to layout origin 106 // currently only used in RTL-layouts 107 mutable Fixed mnBaseAdv; 108 }; 109 110 class FallbackInfo 111 { 112 public: 113 FallbackInfo() : mnMaxLevel(0) {} 114 int AddFallback( ATSUFontID ); 115 const ImplFontData* GetFallbackFontData( int nLevel ) const; 116 117 private: 118 const ImplMacFontData* maFontData[ MAX_FALLBACK ]; 119 ATSUFontID maATSUFontId[ MAX_FALLBACK ]; 120 int mnMaxLevel; 121 }; 122 123 // ======================================================================= 124 125 ATSLayout::ATSLayout( ATSUStyle& rATSUStyle, float fFontScale ) 126 : mrATSUStyle( rATSUStyle ), 127 maATSULayout( NULL ), 128 mnCharCount( 0 ), 129 mfFontScale( fFontScale ), 130 mnGlyphCount( -1 ), 131 mnCachedWidth( 0 ), 132 mnTrailingSpaceWidth( 0 ), 133 mpGlyphIds( NULL ), 134 mpCharWidths( NULL ), 135 mpChars2Glyphs( NULL ), 136 mpGlyphs2Chars( NULL ), 137 mpGlyphRTLFlags( NULL ), 138 mpGlyphAdvances( NULL ), 139 mpGlyphOrigAdvs( NULL ), 140 mpDeltaY( NULL ), 141 mpFallbackInfo( NULL ), 142 mnBaseAdv( 0 ) 143 {} 144 145 // ----------------------------------------------------------------------- 146 147 ATSLayout::~ATSLayout() 148 { 149 if( mpDeltaY ) 150 ATSUDirectReleaseLayoutDataArrayPtr( NULL, 151 kATSUDirectDataBaselineDeltaFixedArray, (void**)&mpDeltaY ); 152 153 if( maATSULayout ) 154 ATSUDisposeTextLayout( maATSULayout ); 155 156 delete[] mpGlyphRTLFlags; 157 delete[] mpGlyphs2Chars; 158 delete[] mpChars2Glyphs; 159 if( mpCharWidths != mpGlyphAdvances ) 160 delete[] mpCharWidths; 161 delete[] mpGlyphIds; 162 delete[] mpGlyphOrigAdvs; 163 delete[] mpGlyphAdvances; 164 165 delete mpFallbackInfo; 166 } 167 168 // ----------------------------------------------------------------------- 169 170 inline int ATSLayout::Fixed2Vcl( Fixed nFixed ) const 171 { 172 float fFloat = mfFontScale * FixedToFloat( nFixed ); 173 return static_cast<int>(fFloat + 0.5); 174 } 175 176 // ----------------------------------------------------------------------- 177 178 inline int ATSLayout::AtsuPix2Vcl( int nAtsuPixel) const 179 { 180 float fVclPixel = mfFontScale * nAtsuPixel; 181 fVclPixel += (fVclPixel>=0) ? +0.5 : -0.5; // prepare rounding to int 182 int nVclPixel = static_cast<int>( fVclPixel); 183 return nVclPixel; 184 } 185 186 // ----------------------------------------------------------------------- 187 188 inline Fixed ATSLayout::Vcl2Fixed( int nPixel ) const 189 { 190 return FloatToFixed( nPixel / mfFontScale ); 191 } 192 193 // ----------------------------------------------------------------------- 194 /** 195 * ATSLayout::LayoutText : Manage text layouting 196 * 197 * @param rArgs: contains array of char to be layouted, starting and ending position of the text to layout 198 * 199 * Typographic layout of text by using the style maATSUStyle 200 * 201 * @return : true if everything is ok 202 **/ 203 bool ATSLayout::LayoutText( ImplLayoutArgs& rArgs ) 204 { 205 if( maATSULayout ) 206 ATSUDisposeTextLayout( maATSULayout ); 207 208 maATSULayout = NULL; 209 210 // Layout text 211 // set up our locals, verify parameters... 212 DBG_ASSERT( (rArgs.mpStr!=NULL), "ATSLayout::LayoutText() with rArgs.mpStr==NULL !!!"); 213 DBG_ASSERT( (mrATSUStyle!=NULL), "ATSLayout::LayoutText() with ATSUStyle==NULL !!!"); 214 215 SalLayout::AdjustLayout( rArgs ); 216 mnCharCount = mnEndCharPos - mnMinCharPos; 217 218 // Workaround a bug in ATSUI with empty string 219 if( mnCharCount<=0 ) 220 return false; 221 222 #if (OSL_DEBUG_LEVEL > 3) 223 Fixed fFontSize = 0; 224 ByteCount nDummy; 225 ATSUGetAttribute( mrATSUStyle, kATSUSizeTag, sizeof(fFontSize), &fFontSize, &nDummy); 226 String aUniName( &rArgs.mpStr[rArgs.mnMinCharPos], mnCharCount ); 227 ByteString aCName( aUniName, RTL_TEXTENCODING_UTF8 ); 228 fprintf( stderr, "ATSLayout( \"%s\" %d..%d of %d) with h=%4.1f\n", 229 aCName.GetBuffer(),rArgs.mnMinCharPos,rArgs.mnEndCharPos,rArgs.mnLength,Fix2X(fFontSize) ); 230 #endif 231 232 // create the ATSUI layout 233 UniCharCount nRunLengths[1] = { mnCharCount }; 234 const int nRunCount = sizeof(nRunLengths)/sizeof(*nRunLengths); 235 OSStatus eStatus = ATSUCreateTextLayoutWithTextPtr( rArgs.mpStr, 236 rArgs.mnMinCharPos, mnCharCount, rArgs.mnLength, 237 nRunCount, &nRunLengths[0], &mrATSUStyle, 238 &maATSULayout); 239 240 DBG_ASSERT( (eStatus==noErr), "ATSUCreateTextLayoutWithTextPtr failed\n"); 241 if( eStatus != noErr ) 242 return false; 243 244 // prepare setting of layout controls 245 static const int nMaxTagCount = 1; 246 ATSUAttributeTag aTagAttrs[ nMaxTagCount ]; 247 ByteCount aTagSizes[ nMaxTagCount ]; 248 ATSUAttributeValuePtr aTagValues[ nMaxTagCount ]; 249 250 // prepare control of "glyph fallback" 251 const SalData* pSalData = GetSalData(); 252 ATSUFontFallbacks aFontFallbacks = pSalData->mpFontList->maFontFallbacks; 253 aTagAttrs[0] = kATSULineFontFallbacksTag; 254 aTagSizes[0] = sizeof( ATSUFontFallbacks ); 255 aTagValues[0] = &aFontFallbacks; 256 257 // set paragraph layout controls 258 ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues ); 259 260 // enable "glyph fallback" 261 ATSUSetTransientFontMatching( maATSULayout, true ); 262 263 // control run-specific layout controls 264 if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG) != 0 ) 265 { 266 // control BiDi defaults 267 BOOL nLineDirTag = kATSULeftToRightBaseDirection; 268 if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) != 0 ) 269 nLineDirTag = kATSURightToLeftBaseDirection; 270 aTagAttrs[0] = kATSULineDirectionTag; 271 aTagSizes[0] = sizeof( nLineDirTag ); 272 aTagValues[0] = &nLineDirTag; 273 // set run-specific layout controls 274 #if 0 // why don't line-controls work as reliably as layout-controls??? 275 ATSUSetLineControls( maATSULayout, rArgs.mnMinCharPos, 1, aTagAttrs, aTagSizes, aTagValues ); 276 #else 277 ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues ); 278 #endif 279 } 280 281 return true; 282 } 283 284 // ----------------------------------------------------------------------- 285 /** 286 * ATSLayout::AdjustLayout : Adjust layout style 287 * 288 * @param rArgs: contains attributes relevant to do a text specific layout 289 * 290 * Adjust text layout by moving glyphs to match the requested logical widths 291 * 292 * @return : none 293 **/ 294 void ATSLayout::AdjustLayout( ImplLayoutArgs& rArgs ) 295 { 296 int nOrigWidth = GetTextWidth(); 297 int nPixelWidth = rArgs.mnLayoutWidth; 298 if( !nPixelWidth && rArgs.mpDXArray ) { 299 // for now we are only interested in the layout width 300 // TODO: use all mpDXArray elements for layouting 301 nPixelWidth = rArgs.mpDXArray[ mnCharCount - 1 ]; 302 303 // workaround for ATSUI not using trailing spaces for justification 304 int i = mnCharCount; 305 while( (--i >= 0) && IsSpacingGlyph( rArgs.mpStr[mnMinCharPos+i]|GF_ISCHAR ) ) {} 306 if( i < 0 ) // nothing to do if the text is all spaces 307 return; 308 // #i91685# trailing letters are left aligned (right aligned for RTL) 309 mnTrailingSpaceWidth = rArgs.mpDXArray[ mnCharCount-1 ]; 310 if( i > 0 ) 311 mnTrailingSpaceWidth -= rArgs.mpDXArray[ i-1 ]; 312 InitGIA(); // ensure valid mpCharWidths[], TODO: use GetIdealX() instead? 313 mnTrailingSpaceWidth -= Fixed2Vcl( mpCharWidths[i] ); 314 // ignore trailing space for calculating the available width 315 nOrigWidth -= mnTrailingSpaceWidth; 316 nPixelWidth -= mnTrailingSpaceWidth; 317 // in RTL-layouts trailing spaces are leftmost 318 // TODO: use BiDi-algorithm to thoroughly check this assumption 319 if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) 320 mnBaseAdv = mnTrailingSpaceWidth; 321 } 322 // return early if there is nothing to do 323 if( !nPixelWidth ) 324 return; 325 326 // HACK: justification requests which change the width by just one pixel were probably 327 // #i86038# introduced by lossy conversions between integer based coordinate system 328 // => ignoring such requests has many more benefits than eventual drawbacks 329 if( (nOrigWidth >= nPixelWidth-1) && (nOrigWidth <= nPixelWidth+1) ) 330 return; 331 332 // changing the layout will make all previous measurements invalid 333 InvalidateMeasurements(); 334 335 ATSUAttributeTag nTags[3]; 336 ATSUAttributeValuePtr nVals[3]; 337 ByteCount nBytes[3]; 338 339 Fixed nFixedWidth = Vcl2Fixed( nPixelWidth ); 340 mnCachedWidth = nFixedWidth; 341 Fract nFractFactor = kATSUFullJustification; 342 ATSLineLayoutOptions nLineLayoutOptions = kATSLineHasNoHangers | kATSLineHasNoOpticalAlignment | kATSLineBreakToNearestCharacter; 343 344 nTags[0] = kATSULineWidthTag; 345 nVals[0] = &nFixedWidth; 346 nBytes[0] = sizeof(Fixed); 347 nTags[1] = kATSULineLayoutOptionsTag; 348 nVals[1] = &nLineLayoutOptions; 349 nBytes[1] = sizeof(ATSLineLayoutOptions); 350 nTags[2] = kATSULineJustificationFactorTag; 351 nVals[2] = &nFractFactor; 352 nBytes[2] = sizeof(Fract); 353 354 OSStatus eStatus = ATSUSetLayoutControls( maATSULayout, 3, nTags, nBytes, nVals ); 355 if( eStatus != noErr ) 356 return; 357 358 // update the measurements of the justified layout to match the justification request 359 if( rArgs.mpDXArray ) 360 InitGIA( &rArgs ); 361 } 362 363 // ----------------------------------------------------------------------- 364 /** 365 * ATSLayout::DrawText : Draw text to screen 366 * 367 * @param rGraphics: device to draw to 368 * 369 * Draw the layouted text to the CGContext 370 * 371 * @return : none 372 **/ 373 void ATSLayout::DrawText( SalGraphics& rGraphics ) const 374 { 375 AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics); 376 377 // short circuit if there is nothing to do 378 if( (mnCharCount <= 0) 379 || !rAquaGraphics.CheckContext() ) 380 return; 381 382 // the view is vertically flipped => flipped glyphs 383 // so apply a temporary transformation that it flips back 384 // also compensate if the font was size limited 385 CGContextSaveGState( rAquaGraphics.mrContext ); 386 CGContextScaleCTM( rAquaGraphics.mrContext, +mfFontScale, -mfFontScale ); 387 CGContextSetShouldAntialias( rAquaGraphics.mrContext, !rAquaGraphics.mbNonAntialiasedText ); 388 389 // prepare ATSUI drawing attributes 390 static const ItemCount nMaxControls = 8; 391 ATSUAttributeTag theTags[ nMaxControls ]; 392 ByteCount theSizes[ nMaxControls]; 393 ATSUAttributeValuePtr theValues[ nMaxControls ]; 394 ItemCount numcontrols = 0; 395 396 // Tell ATSUI to use CoreGraphics 397 theTags[numcontrols] = kATSUCGContextTag; 398 theSizes[numcontrols] = sizeof( CGContextRef ); 399 theValues[numcontrols++] = &rAquaGraphics.mrContext; 400 401 // Rotate if necessary 402 if( rAquaGraphics.mnATSUIRotation != 0 ) 403 { 404 Fixed theAngle = rAquaGraphics.mnATSUIRotation; 405 theTags[numcontrols] = kATSULineRotationTag; 406 theSizes[numcontrols] = sizeof( Fixed ); 407 theValues[numcontrols++] = &theAngle; 408 } 409 410 DBG_ASSERT( (numcontrols <= nMaxControls), "ATSLayout::DrawText() numcontrols overflow" ); 411 OSStatus theErr = ATSUSetLayoutControls (maATSULayout, numcontrols, theTags, theSizes, theValues); 412 DBG_ASSERT( (theErr==noErr), "ATSLayout::DrawText ATSUSetLayoutControls failed!\n" ); 413 414 // Draw the text 415 const Point aPos = GetDrawPosition( Point(mnBaseAdv,0) ); 416 const Fixed nFixedX = Vcl2Fixed( +aPos.X() ); 417 const Fixed nFixedY = Vcl2Fixed( -aPos.Y() ); // adjusted for y-mirroring 418 if( maSubPortions.empty() ) 419 ATSUDrawText( maATSULayout, mnMinCharPos, mnCharCount, nFixedX, nFixedY ); 420 else 421 { 422 // draw the sub-portions and apply individual adjustments 423 SubPortionVector::const_iterator it = maSubPortions.begin(); 424 for(; it != maSubPortions.end(); ++it ) 425 { 426 const SubPortion& rSubPortion = *it; 427 // calculate sub-portion offset for rotated text 428 Fixed nXOfsFixed = 0, nYOfsFixed = 0; 429 if( rAquaGraphics.mnATSUIRotation != 0 ) 430 { 431 const double fRadians = rAquaGraphics.mnATSUIRotation * (M_PI/0xB40000); 432 nXOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * cos( fRadians )); 433 nYOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * sin( fRadians )); 434 } 435 436 // draw sub-portions 437 ATSUDrawText( maATSULayout, 438 rSubPortion.mnMinCharPos, rSubPortion.mnEndCharPos - rSubPortion.mnMinCharPos, 439 nFixedX + nXOfsFixed, nFixedY + nYOfsFixed ); 440 } 441 } 442 443 // request an update of the changed window area 444 if( rAquaGraphics.IsWindowGraphics() ) 445 { 446 Rect drawRect; // rectangle of the changed area 447 theErr = ATSUMeasureTextImage( maATSULayout, 448 mnMinCharPos, mnCharCount, nFixedX, nFixedY, &drawRect ); 449 if( theErr == noErr ) 450 { 451 // FIXME: transformation from baseline to top left 452 // with the simple approach below we invalidate too much 453 short d = drawRect.bottom - drawRect.top; 454 drawRect.top -= d; 455 drawRect.bottom += d; 456 CGRect aRect = CGRectMake( drawRect.left, drawRect.top, 457 drawRect.right - drawRect.left, 458 drawRect.bottom - drawRect.top ); 459 aRect = CGContextConvertRectToDeviceSpace( rAquaGraphics.mrContext, aRect ); 460 rAquaGraphics.RefreshRect( aRect ); 461 } 462 } 463 464 // restore the original graphic context transformations 465 CGContextRestoreGState( rAquaGraphics.mrContext ); 466 } 467 468 // ----------------------------------------------------------------------- 469 /** 470 * ATSLayout::GetNextGlyphs : Get info about next glyphs in the layout 471 * 472 * @param nLen: max number of char 473 * @param pGlyphs: returned array of glyph ids 474 * @param rPos: returned x starting position 475 * @param nStart: index of the first requested glyph 476 * @param pGlyphAdvances: returned array of glyphs advances 477 * @param pCharIndexes: returned array of char indexes 478 * 479 * Returns infos about the next glyphs in the text layout 480 * 481 * @return : number of glyph details that were provided 482 **/ 483 int ATSLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIDs, Point& rPos, int& nStart, 484 sal_Int32* pGlyphAdvances, int* pCharIndexes ) const 485 { 486 if( nStart < 0 ) // first glyph requested? 487 nStart = 0; 488 489 // get glyph measurements 490 InitGIA(); 491 // some measurements are only needed for multi-glyph results 492 if( nLen > 1 ) 493 { 494 GetIdealX(); 495 GetDeltaY(); 496 } 497 498 if( nStart >= mnGlyphCount ) // no glyph left? 499 return 0; 500 501 // calculate glyph position relative to layout base 502 // TODO: avoid for nStart!=0 case by reusing rPos 503 Fixed nXOffset = mnBaseAdv; 504 for( int i = 0; i < nStart; ++i ) 505 nXOffset += mpGlyphAdvances[ i ]; 506 // if sub-portion offsets are involved there is an additional x-offset 507 if( !maSubPortions.empty() ) 508 { 509 // prepare to find the sub-portion 510 int nCharPos = nStart + mnMinCharPos; 511 if( mpGlyphs2Chars ) 512 nCharPos = mpGlyphs2Chars[nStart]; 513 514 // find the matching subportion 515 // TODO: is a non-linear search worth it? 516 SubPortionVector::const_iterator it = maSubPortions.begin(); 517 for(; it != maSubPortions.end(); ++it) { 518 const SubPortion& r = *it; 519 if( nCharPos < r.mnMinCharPos ) 520 continue; 521 if( nCharPos >= r.mnEndCharPos ) 522 continue; 523 // apply the sub-portion xoffset 524 nXOffset += r.mnXOffset; 525 break; 526 } 527 } 528 529 Fixed nYOffset = 0; 530 if( mpDeltaY ) 531 nYOffset = mpDeltaY[ nStart ]; 532 533 // calculate absolute position in pixel units 534 const Point aRelativePos( Fix2Long(static_cast<Fixed>(nXOffset*mfFontScale)), Fix2Long(static_cast<Fixed>(nYOffset*mfFontScale)) ); 535 rPos = GetDrawPosition( aRelativePos ); 536 537 // update return values 538 int nCount = 0; 539 while( nCount < nLen ) 540 { 541 ++nCount; 542 sal_GlyphId nGlyphId = mpGlyphIds[nStart]; 543 544 // check if glyph fallback is needed for this glyph 545 // TODO: use ATSUDirectGetLayoutDataArrayPtrFromTextLayout(kATSUDirectDataStyleIndex) API instead? 546 const int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[nStart] : nStart + mnMinCharPos; 547 ATSUFontID nFallbackFontID = kATSUInvalidFontID; 548 UniCharArrayOffset nChangedOffset = 0; 549 UniCharCount nChangedLength = 0; 550 OSStatus eStatus = ATSUMatchFontsToText( maATSULayout, nCharPos, kATSUToTextEnd, 551 &nFallbackFontID, &nChangedOffset, &nChangedLength ); 552 if( (eStatus == kATSUFontsMatched) && ((int)nChangedOffset == nCharPos) ) 553 { 554 // fallback is needed 555 if( !mpFallbackInfo ) 556 mpFallbackInfo = new FallbackInfo; 557 // register fallback font 558 const int nLevel = mpFallbackInfo->AddFallback( nFallbackFontID ); 559 // update sal_GlyphId with fallback level 560 nGlyphId |= (nLevel << GF_FONTSHIFT); 561 } 562 563 // update resulting glyphid array 564 *(pGlyphIDs++) = nGlyphId; 565 566 // update returned glyph advance array 567 if( pGlyphAdvances ) 568 *(pGlyphAdvances++) = Fixed2Vcl( mpGlyphAdvances[nStart] ); 569 570 // update returned index-into-string array 571 if( pCharIndexes ) 572 { 573 int nCharPos; 574 if( mpGlyphs2Chars ) 575 nCharPos = mpGlyphs2Chars[nStart]; 576 else 577 nCharPos = nStart + mnMinCharPos; 578 *(pCharIndexes++) = nCharPos; 579 } 580 581 // stop at last glyph 582 if( ++nStart >= mnGlyphCount ) 583 break; 584 585 // stop when next the x-position is unexpected 586 if( !maSubPortions.empty() ) 587 break; // TODO: finish the complete sub-portion 588 if( !pGlyphAdvances && mpGlyphOrigAdvs ) 589 if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] ) 590 break; 591 592 // stop when the next y-position is unexpected 593 if( mpDeltaY ) 594 if( mpDeltaY[nStart-1] != mpDeltaY[nStart] ) 595 break; 596 } 597 598 return nCount; 599 } 600 601 // ----------------------------------------------------------------------- 602 /** 603 * ATSLayout::GetTextWidth : Get typographic width of layouted text 604 * 605 * Get typographic bounds of the text 606 * 607 * @return : text width 608 **/ 609 long ATSLayout::GetTextWidth() const 610 { 611 if( mnCharCount <= 0 ) 612 return 0; 613 614 DBG_ASSERT( (maATSULayout!=NULL), "ATSLayout::GetTextWidth() with maATSULayout==NULL !\n"); 615 if( !maATSULayout ) 616 return 0; 617 618 if( !mnCachedWidth ) 619 { 620 // prepare precise measurements on pixel based or reference-device 621 const UInt16 eTypeOfBounds = kATSUseFractionalOrigins; 622 623 // determine number of needed measurement trapezoids 624 ItemCount nMaxBounds = 0; 625 OSStatus err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount, 626 eTypeOfBounds, 0, NULL, &nMaxBounds ); 627 if( (err != noErr) 628 || (nMaxBounds <= 0) ) 629 return 0; 630 631 // get the trapezoids 632 typedef std::vector<ATSTrapezoid> TrapezoidVector; 633 TrapezoidVector aTrapezoidVector( nMaxBounds ); 634 ItemCount nBoundsCount = 0; 635 err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount, 636 eTypeOfBounds, nMaxBounds, &aTrapezoidVector[0], &nBoundsCount ); 637 if( err != noErr ) 638 return 0; 639 640 DBG_ASSERT( (nBoundsCount <= nMaxBounds), "ATSLayout::GetTextWidth() : too many trapezoids !\n"); 641 642 // find the bound extremas 643 Fixed nLeftBound = 0; 644 Fixed nRightBound = 0; 645 for( ItemCount i = 0; i < nBoundsCount; ++i ) 646 { 647 const ATSTrapezoid& rTrap = aTrapezoidVector[i]; 648 if( (i == 0) || (nLeftBound < rTrap.lowerLeft.x) ) 649 nLeftBound = rTrap.lowerLeft.x; 650 if( (i == 0) || (nRightBound > rTrap.lowerRight.x) ) 651 nRightBound = rTrap.lowerRight.x; 652 } 653 654 // measure the bound extremas 655 mnCachedWidth = nRightBound - nLeftBound; 656 // adjust for eliminated trailing space widths 657 } 658 659 int nScaledWidth = Fixed2Vcl( mnCachedWidth ); 660 nScaledWidth += mnTrailingSpaceWidth; 661 return nScaledWidth; 662 } 663 664 // ----------------------------------------------------------------------- 665 /** 666 * ATSLayout::FillDXArray : Get Char widths 667 * 668 * @param pDXArray: array to be filled with x-advances 669 * 670 * Fill the pDXArray with horizontal deltas : CharWidths 671 * 672 * @return : typographical width of the complete text layout 673 **/ 674 long ATSLayout::FillDXArray( long* pDXArray ) const 675 { 676 // short circuit requests which don't need full details 677 if( !pDXArray ) 678 return GetTextWidth(); 679 680 // check assumptions 681 DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::FillDXArray() with nTSW!=0" ); 682 683 // initialize details about the resulting layout 684 InitGIA(); 685 686 // distribute the widths among the string elements 687 int nPixWidth = 0; 688 mnCachedWidth = 0; 689 for( int i = 0; i < mnCharCount; ++i ) 690 { 691 // convert and adjust for accumulated rounding errors 692 mnCachedWidth += mpCharWidths[i]; 693 const int nOldPixWidth = nPixWidth; 694 nPixWidth = Fixed2Vcl( mnCachedWidth ); 695 pDXArray[i] = nPixWidth - nOldPixWidth; 696 } 697 698 return nPixWidth; 699 } 700 701 // ----------------------------------------------------------------------- 702 /** 703 * ATSLayout::GetTextBreak : Find line break depending on width 704 * 705 * @param nMaxWidth : maximal logical text width in subpixel units 706 * @param nCharExtra: expanded/condensed spacing in subpixel units 707 * @param nFactor: number of subpixel units per pixel 708 * 709 * Measure the layouted text to find the typographical line break 710 * the result is needed by the language specific line breaking 711 * 712 * @return : string index corresponding to the suggested line break 713 **/ 714 int ATSLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const 715 { 716 if( !maATSULayout ) 717 return STRING_LEN; 718 719 // the semantics of the legacy use case (nCharExtra!=0) cannot be mapped to ATSUBreakLine() 720 if( nCharExtra != 0 ) 721 { 722 // prepare the measurement by layouting and measuring the un-expanded/un-condensed text 723 if( !InitGIA() ) 724 return STRING_LEN; 725 726 // TODO: use a better way than by testing each the char position 727 ATSUTextMeasurement nATSUSumWidth = 0; 728 const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nMaxWidth / nFactor ); 729 const ATSUTextMeasurement nATSUExtraWidth = Vcl2Fixed( nCharExtra ) / nFactor; 730 for( int i = 0; i < mnCharCount; ++i ) 731 { 732 nATSUSumWidth += mpCharWidths[i]; 733 if( nATSUSumWidth >= nATSUMaxWidth ) 734 return (mnMinCharPos + i); 735 nATSUSumWidth += nATSUExtraWidth; 736 if( nATSUSumWidth >= nATSUMaxWidth ) 737 if( i+1 < mnCharCount ) 738 return (mnMinCharPos + i); 739 } 740 741 return STRING_LEN; 742 } 743 744 // get a quick overview on what could fit 745 const long nPixelWidth = (nMaxWidth - (nCharExtra * mnCharCount)) / nFactor; 746 if( nPixelWidth <= 0 ) 747 return mnMinCharPos; 748 749 // check assumptions 750 DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::GetTextBreak() with nTSW!=0" ); 751 752 // initial measurement of text break position 753 UniCharArrayOffset nBreakPos = mnMinCharPos; 754 const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nPixelWidth ); 755 if( nATSUMaxWidth <= 0xFFFF ) // #i108584# avoid ATSU rejecting the parameter 756 return mnMinCharPos; // or do ATSUMaxWidth=0x10000; 757 OSStatus eStatus = ATSUBreakLine( maATSULayout, mnMinCharPos, 758 nATSUMaxWidth, false, &nBreakPos ); 759 if( (eStatus != noErr) && (eStatus != kATSULineBreakInWord) ) 760 return STRING_LEN; 761 762 // the result from ATSUBreakLine() doesn't match the semantics expected by its 763 // application layer callers from SW+SVX+I18N. Adjust the results to the expectations: 764 765 // ATSU reports that everything fits even when trailing spaces would break the line 766 // #i89789# OOo's application layers expect STRING_LEN if everything fits 767 if( nBreakPos >= static_cast<UniCharArrayOffset>(mnEndCharPos) ) 768 return STRING_LEN; 769 770 // GetTextBreak()'s callers expect it to return the "stupid visual line break". 771 // Returning anything else result.s in subtle problems in the application layers. 772 static const bool bInWord = true; // TODO: add as argument to GetTextBreak() method 773 if( !bInWord ) 774 return nBreakPos; 775 776 // emulate stupid visual line breaking by line breaking for the remaining width 777 ATSUTextMeasurement nLeft, nRight, nDummy; 778 eStatus = ATSUGetUnjustifiedBounds( maATSULayout, mnMinCharPos, nBreakPos-mnMinCharPos, 779 &nLeft, &nRight, &nDummy, &nDummy ); 780 if( eStatus != noErr ) 781 return nBreakPos; 782 const ATSUTextMeasurement nATSURemWidth = nATSUMaxWidth - (nRight - nLeft); 783 if( nATSURemWidth <= 0xFFFF ) // #i108584# avoid ATSU rejecting the parameter 784 return nBreakPos; 785 UniCharArrayOffset nBreakPosInWord = nBreakPos; 786 eStatus = ATSUBreakLine( maATSULayout, nBreakPos, nATSURemWidth, false, &nBreakPosInWord ); 787 return nBreakPosInWord; 788 } 789 790 // ----------------------------------------------------------------------- 791 /** 792 * ATSLayout::GetCaretPositions : Find positions of carets 793 * 794 * @param nMaxIndex position to which we want to find the carets 795 * 796 * Fill the array of positions of carets (for cursors and selections) 797 * 798 * @return : none 799 **/ 800 void ATSLayout::GetCaretPositions( int nMaxIndex, long* pCaretXArray ) const 801 { 802 DBG_ASSERT( ((nMaxIndex>0)&&!(nMaxIndex&1)), 803 "ATSLayout::GetCaretPositions() : invalid number of caret pairs requested"); 804 805 // initialize the caret positions 806 for( int i = 0; i < nMaxIndex; ++i ) 807 pCaretXArray[ i ] = -1; 808 809 for( int n = 0; n <= mnCharCount; ++n ) 810 { 811 // measure the characters cursor position 812 typedef unsigned char Boolean; 813 const Boolean bIsLeading = true; 814 ATSUCaret aCaret0, aCaret1; 815 Boolean bIsSplit; 816 OSStatus eStatus = ATSUOffsetToCursorPosition( maATSULayout, 817 mnMinCharPos + n, bIsLeading, kATSUByCharacter, 818 &aCaret0, &aCaret1, &bIsSplit ); 819 if( eStatus != noErr ) 820 continue; 821 const Fixed nFixedPos = mnBaseAdv + aCaret0.fX; 822 // convert the measurement to pixel units 823 const int nPixelPos = Fixed2Vcl( nFixedPos ); 824 // update previous trailing position 825 if( n > 0 ) 826 pCaretXArray[2*n-1] = nPixelPos; 827 // update current leading position 828 if( 2*n >= nMaxIndex ) 829 break; 830 pCaretXArray[2*n+0] = nPixelPos; 831 } 832 } 833 834 // ----------------------------------------------------------------------- 835 /** 836 * ATSLayout::GetBoundRect : Get rectangle dim containing the layouted text 837 * 838 * @param rVCLRect: rectangle of text image (layout) measures 839 * 840 * Get ink bounds of the text 841 * 842 * @return : measurement valid 843 **/ 844 bool ATSLayout::GetBoundRect( SalGraphics&, Rectangle& rVCLRect ) const 845 { 846 const Point aPos = GetDrawPosition( Point(mnBaseAdv, 0) ); 847 const Fixed nFixedX = Vcl2Fixed( +aPos.X() ); 848 const Fixed nFixedY = Vcl2Fixed( +aPos.Y() ); 849 850 Rect aMacRect; 851 OSStatus eStatus = ATSUMeasureTextImage( maATSULayout, 852 mnMinCharPos, mnCharCount, nFixedX, nFixedY, &aMacRect ); 853 if( eStatus != noErr ) 854 return false; 855 856 // ATSU top-bottom are vertically flipped from a VCL aspect 857 rVCLRect.Left() = AtsuPix2Vcl( aMacRect.left ); 858 rVCLRect.Top() = AtsuPix2Vcl( aMacRect.top ); 859 rVCLRect.Right() = AtsuPix2Vcl( aMacRect.right ); 860 rVCLRect.Bottom() = AtsuPix2Vcl( aMacRect.bottom ); 861 return true; 862 } 863 864 // ----------------------------------------------------------------------- 865 /** 866 * ATSLayout::InitGIA() : get many informations about layouted text 867 * 868 * Fills arrays of information about the gylph layout previously done 869 * in ASTLayout::LayoutText() : glyph advance (width), glyph delta Y (from baseline), 870 * mapping between glyph index and character index, chars widths 871 * 872 * @return : true if everything could be computed, otherwise false 873 **/ 874 bool ATSLayout::InitGIA( ImplLayoutArgs* pArgs ) const 875 { 876 // no need to run InitGIA more than once on the same ATSLayout object 877 if( mnGlyphCount >= 0 ) 878 return true; 879 mnGlyphCount = 0; 880 881 // Workaround a bug in ATSUI with empty string 882 if( mnCharCount <= 0 ) 883 return false; 884 885 // initialize character details 886 mpCharWidths = new Fixed[ mnCharCount ]; 887 mpChars2Glyphs = new int[ mnCharCount ]; 888 for( int n = 0; n < mnCharCount; ++n ) 889 { 890 mpCharWidths[ n ] = 0; 891 mpChars2Glyphs[ n ] = -1; 892 } 893 894 // get details about the glyph layout 895 ItemCount iLayoutDataCount; 896 const ATSLayoutRecord* pALR; 897 OSStatus eStatus = ATSUDirectGetLayoutDataArrayPtrFromTextLayout( 898 maATSULayout, mnMinCharPos, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, 899 (void**)&pALR, &iLayoutDataCount ); 900 DBG_ASSERT( (eStatus==noErr), "ATSLayout::InitGIA() : no ATSLayoutRecords!\n"); 901 if( (eStatus != noErr) 902 || (iLayoutDataCount <= 1) ) 903 return false; 904 905 // initialize glyph details 906 mpGlyphIds = new ATSGlyphRef[ iLayoutDataCount ]; 907 mpGlyphAdvances = new Fixed[ iLayoutDataCount ]; 908 mpGlyphs2Chars = new int[ iLayoutDataCount ]; 909 910 // measure details of the glyph layout 911 Fixed nLeftPos = 0; 912 for( ItemCount i = 0; i < iLayoutDataCount; ++i ) 913 { 914 const ATSLayoutRecord& rALR = pALR[i]; 915 916 // distribute the widths as fairly as possible among the chars 917 const int nRelativeIdx = (rALR.originalOffset / 2); 918 if( i+1 < iLayoutDataCount ) 919 mpCharWidths[ nRelativeIdx ] += pALR[i+1].realPos - rALR.realPos; 920 921 // new glyph is available => finish measurement of old glyph 922 if( mnGlyphCount > 0 ) 923 mpGlyphAdvances[ mnGlyphCount-1 ] = rALR.realPos - nLeftPos; 924 925 // ignore marker or deleted glyphs 926 enum { MARKED_OUTGLYPH=0xFFFE, DROPPED_OUTGLYPH=0xFFFF}; 927 if( rALR.glyphID >= MARKED_OUTGLYPH ) 928 continue; 929 930 DBG_ASSERT( !(rALR.flags & kATSGlyphInfoTerminatorGlyph), 931 "ATSLayout::InitGIA(): terminator glyph not marked as deleted!" ); 932 933 // store details of the visible glyphs 934 nLeftPos = rALR.realPos; 935 mpGlyphIds[ mnGlyphCount ] = rALR.glyphID; 936 937 // map visible glyphs to their counterparts in the UTF16-character array 938 mpGlyphs2Chars[ mnGlyphCount ] = nRelativeIdx + mnMinCharPos; 939 mpChars2Glyphs[ nRelativeIdx ] = mnGlyphCount; 940 941 ++mnGlyphCount; 942 } 943 944 // measure complete width 945 mnCachedWidth = mnBaseAdv; 946 mnCachedWidth += pALR[iLayoutDataCount-1].realPos - pALR[0].realPos; 947 948 #if (OSL_DEBUG_LEVEL > 1) 949 Fixed nWidthSum = mnBaseAdv; 950 for( int n = 0; n < mnCharCount; ++n ) 951 nWidthSum += mpCharWidths[ n ]; 952 DBG_ASSERT( (nWidthSum==mnCachedWidth), 953 "ATSLayout::InitGIA(): measured widths do not match!\n" ); 954 #endif 955 956 // #i91183# we need to split up the portion into sub-portions 957 // if the ATSU-layout differs too much from the requested layout 958 if( pArgs && pArgs->mpDXArray ) 959 { 960 // TODO: non-strong-LTR case cases should be handled too 961 if( (pArgs->mnFlags & TEXT_LAYOUT_BIDI_STRONG) 962 && !(pArgs->mnFlags & TEXT_LAYOUT_BIDI_RTL) ) 963 { 964 Fixed nSumCharWidths = 0; 965 SubPortion aSubPortion = { mnMinCharPos, 0, 0 }; 966 for( int i = 0; i < mnCharCount; ++i ) 967 { 968 // calculate related logical position 969 nSumCharWidths += mpCharWidths[i]; 970 971 // start new sub-portion if needed 972 const Fixed nNextXPos = Vcl2Fixed(pArgs->mpDXArray[i]); 973 const Fixed nNextXOffset = nNextXPos - nSumCharWidths; 974 const Fixed nFixedDiff = aSubPortion.mnXOffset - nNextXOffset; 975 if( (nFixedDiff < -0xC000) || (nFixedDiff > +0xC000) ) { 976 // get to the end of the current sub-portion 977 // prevent splitting up at diacritics etc. 978 int j = i; 979 while( (++j < mnCharCount) && !mpCharWidths[j] ); 980 aSubPortion.mnEndCharPos = mnMinCharPos + j; 981 // emit current sub-portion 982 maSubPortions.push_back( aSubPortion ); 983 // prepare next sub-portion 984 aSubPortion.mnMinCharPos = aSubPortion.mnEndCharPos; 985 aSubPortion.mnXOffset = nNextXOffset; 986 } 987 } 988 989 // emit the remaining sub-portion 990 if( !maSubPortions.empty() ) 991 { 992 aSubPortion.mnEndCharPos = mnEndCharPos; 993 if( aSubPortion.mnEndCharPos != aSubPortion.mnMinCharPos ) 994 maSubPortions.push_back( aSubPortion ); 995 } 996 } 997 998 // override layouted charwidths with requested charwidths 999 for( int n = 0; n < mnCharCount; ++n ) 1000 mpCharWidths[ n ] = pArgs->mpDXArray[ n ]; 1001 } 1002 1003 // release the ATSU layout records 1004 ATSUDirectReleaseLayoutDataArrayPtr(NULL, 1005 kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void**)&pALR ); 1006 1007 return true; 1008 } 1009 1010 // ----------------------------------------------------------------------- 1011 1012 bool ATSLayout::GetIdealX() const 1013 { 1014 // compute the ideal advance widths only once 1015 if( mpGlyphOrigAdvs != NULL ) 1016 return true; 1017 1018 DBG_ASSERT( (mpGlyphIds!=NULL), "GetIdealX() called with mpGlyphIds==NULL !" ); 1019 DBG_ASSERT( (mrATSUStyle!=NULL), "GetIdealX called with mrATSUStyle==NULL !" ); 1020 1021 // TODO: cache ideal metrics per glyph? 1022 std::vector<ATSGlyphIdealMetrics> aIdealMetrics; 1023 aIdealMetrics.resize( mnGlyphCount ); 1024 OSStatus theErr = ATSUGlyphGetIdealMetrics( mrATSUStyle, 1025 mnGlyphCount, &mpGlyphIds[0], sizeof(*mpGlyphIds), &aIdealMetrics[0] ); 1026 DBG_ASSERT( (theErr==noErr), "ATSUGlyphGetIdealMetrics failed!"); 1027 if( theErr != noErr ) 1028 return false; 1029 1030 mpGlyphOrigAdvs = new Fixed[ mnGlyphCount ]; 1031 for( int i = 0;i < mnGlyphCount;++i ) 1032 mpGlyphOrigAdvs[i] = FloatToFixed( aIdealMetrics[i].advance.x ); 1033 1034 return true; 1035 } 1036 1037 // ----------------------------------------------------------------------- 1038 1039 bool ATSLayout::GetDeltaY() const 1040 { 1041 // don't bother to get the same delta-y-array more than once 1042 if( mpDeltaY != NULL ) 1043 return true; 1044 1045 #if 1 1046 if( !maATSULayout ) 1047 return false; 1048 1049 // get and keep the y-deltas in the mpDeltaY member variable 1050 // => release it in the destructor 1051 ItemCount nDeltaCount = 0; 1052 OSStatus theErr = ATSUDirectGetLayoutDataArrayPtrFromTextLayout( 1053 maATSULayout, mnMinCharPos, kATSUDirectDataBaselineDeltaFixedArray, 1054 (void**)&mpDeltaY, &nDeltaCount ); 1055 1056 DBG_ASSERT( (theErr==noErr ), "mpDeltaY - ATSUDirectGetLayoutDataArrayPtrFromTextLayout failed!\n"); 1057 if( theErr != noErr ) 1058 return false; 1059 1060 if( mpDeltaY == NULL ) 1061 return true; 1062 1063 if( nDeltaCount != (ItemCount)mnGlyphCount ) 1064 { 1065 DBG_WARNING( "ATSLayout::GetDeltaY() : wrong deltaY count!" ); 1066 ATSUDirectReleaseLayoutDataArrayPtr( NULL, 1067 kATSUDirectDataBaselineDeltaFixedArray, (void**)&mpDeltaY ); 1068 mpDeltaY = NULL; 1069 return false; 1070 } 1071 #endif 1072 1073 return true; 1074 } 1075 1076 // ----------------------------------------------------------------------- 1077 1078 #define DELETEAZ( X ) { delete[] X; X = NULL; } 1079 1080 void ATSLayout::InvalidateMeasurements() 1081 { 1082 mnGlyphCount = -1; 1083 DELETEAZ( mpGlyphIds ); 1084 DELETEAZ( mpCharWidths ); 1085 DELETEAZ( mpChars2Glyphs ); 1086 DELETEAZ( mpGlyphs2Chars ); 1087 DELETEAZ( mpGlyphRTLFlags ); 1088 DELETEAZ( mpGlyphAdvances ); 1089 DELETEAZ( mpGlyphOrigAdvs ); 1090 DELETEAZ( mpDeltaY ); 1091 } 1092 1093 // ======================================================================= 1094 1095 #if 0 1096 // helper class to convert ATSUI outlines to VCL PolyPolygons 1097 class PolyArgs 1098 { 1099 public: 1100 PolyArgs(); 1101 ~PolyArgs(); 1102 1103 void Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset ); 1104 void AddPoint( const Float32Point&, PolyFlags ); 1105 void ClosePolygon(); 1106 1107 private: 1108 PolyPolygon* mpPolyPoly; 1109 long mnXOffset, mnYOffset; 1110 1111 Point* mpPointAry; 1112 BYTE* mpFlagAry; 1113 USHORT mnMaxPoints; 1114 1115 USHORT mnPointCount; 1116 USHORT mnPolyCount; 1117 bool mbHasOffline; 1118 }; 1119 1120 // ----------------------------------------------------------------------- 1121 1122 PolyArgs::PolyArgs() 1123 : mpPolyPoly(NULL), 1124 mnPointCount(0), 1125 mnPolyCount(0), 1126 mbHasOffline(false) 1127 { 1128 mnMaxPoints = 256; 1129 mpPointAry = new Point[ mnMaxPoints ]; 1130 mpFlagAry = new BYTE [ mnMaxPoints ]; 1131 } 1132 1133 // ----------------------------------------------------------------------- 1134 1135 PolyArgs::~PolyArgs() 1136 { 1137 delete[] mpFlagAry; 1138 delete[] mpPointAry; 1139 } 1140 1141 // ----------------------------------------------------------------------- 1142 1143 void PolyArgs::Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset ) 1144 { 1145 mnXOffset = nXOffset; 1146 mnYOffset = nYOffset; 1147 mpPolyPoly = pPolyPoly; 1148 1149 mpPolyPoly->Clear(); 1150 mnPointCount = 0; 1151 mnPolyCount = 0; 1152 } 1153 1154 // ----------------------------------------------------------------------- 1155 1156 void PolyArgs::AddPoint( const Float32Point& rPoint, PolyFlags eFlags ) 1157 { 1158 if( mnPointCount >= mnMaxPoints ) 1159 { 1160 // resize if needed (TODO: use STL?) 1161 mnMaxPoints *= 4; 1162 Point* mpNewPoints = new Point[ mnMaxPoints ]; 1163 BYTE* mpNewFlags = new BYTE[ mnMaxPoints ]; 1164 for( int i = 0; i < mnPointCount; ++i ) 1165 { 1166 mpNewPoints[ i ] = mpPointAry[ i ]; 1167 mpNewFlags[ i ] = mpFlagAry[ i ]; 1168 } 1169 delete[] mpFlagAry; 1170 delete[] mpPointAry; 1171 mpPointAry = mpNewPoints; 1172 mpFlagAry = mpNewFlags; 1173 } 1174 1175 // convert to pixels and add startpoint offset 1176 int nXPos = Float32ToInt( rPoint.x ); 1177 int nYPos = Float32ToInt( rPoint.y ); 1178 mpPointAry[ mnPointCount ] = Point( nXPos + mnXOffset, nYPos + mnYOffset ); 1179 // set point flags 1180 mpFlagAry[ mnPointCount++ ]= eFlags; 1181 mbHasOffline |= (eFlags != POLY_NORMAL); 1182 } 1183 1184 // ----------------------------------------------------------------------- 1185 1186 void PolyArgs::ClosePolygon() 1187 { 1188 if( !mnPolyCount++ ) 1189 return; 1190 1191 // append finished polygon 1192 Polygon aPoly( mnPointCount, mpPointAry, (mbHasOffline ? mpFlagAry : NULL) ); 1193 mpPolyPoly->Insert( aPoly ); 1194 1195 // prepare for new polygon 1196 mnPointCount = 0; 1197 mbHasOffline = false; 1198 } 1199 #endif 1200 // ======================================================================= 1201 1202 // glyph fallback is supported directly by Aqua 1203 // so methods used only by MultiSalLayout can be dummy implementated 1204 bool ATSLayout::GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const { return false; } 1205 void ATSLayout::InitFont() {} 1206 void ATSLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ ) {} 1207 void ATSLayout::DropGlyph( int /*nStart*/ ) {} 1208 void ATSLayout::Simplify( bool /*bIsBase*/ ) {} 1209 1210 // get the ImplFontData for a glyph fallback font 1211 // for a glyphid that was returned by ATSLayout::GetNextGlyphs() 1212 const ImplFontData* ATSLayout::GetFallbackFontData( sal_GlyphId nGlyphId ) const 1213 { 1214 // check if any fallback fonts were needed 1215 if( !mpFallbackInfo ) 1216 return NULL; 1217 // check if the current glyph needs a fallback font 1218 int nFallbackLevel = (nGlyphId & GF_FONTMASK) >> GF_FONTSHIFT; 1219 if( !nFallbackLevel ) 1220 return NULL; 1221 return mpFallbackInfo->GetFallbackFontData( nFallbackLevel ); 1222 } 1223 1224 // ======================================================================= 1225 1226 int FallbackInfo::AddFallback( ATSUFontID nFontId ) 1227 { 1228 // check if the fallback font is already known 1229 for( int nLevel = 0; nLevel < mnMaxLevel; ++nLevel ) 1230 if( maATSUFontId[ nLevel ] == nFontId ) 1231 return (nLevel + 1); 1232 1233 // append new fallback font if possible 1234 if( mnMaxLevel >= MAX_FALLBACK-1 ) 1235 return 0; 1236 // keep ATSU font id of fallback font 1237 maATSUFontId[ mnMaxLevel ] = nFontId; 1238 // find and cache the corresponding ImplFontData pointer 1239 const SystemFontList* pSFL = GetSalData()->mpFontList; 1240 const ImplMacFontData* pFontData = pSFL->GetFontDataFromId( nFontId ); 1241 maFontData[ mnMaxLevel ] = pFontData; 1242 // increase fallback level by one 1243 return (++mnMaxLevel); 1244 } 1245 1246 // ----------------------------------------------------------------------- 1247 1248 const ImplFontData* FallbackInfo::GetFallbackFontData( int nFallbackLevel ) const 1249 { 1250 const ImplMacFontData* pFallbackFont = maFontData[ nFallbackLevel-1 ]; 1251 return pFallbackFont; 1252 } 1253 1254 // ======================================================================= 1255 1256 SalLayout* AquaSalGraphics::GetTextLayout( ImplLayoutArgs&, int /*nFallbackLevel*/ ) 1257 { 1258 ATSLayout* pATSLayout = new ATSLayout( maATSUStyle, mfFontScale ); 1259 return pATSLayout; 1260 } 1261 1262 // ======================================================================= 1263 1264