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