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