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 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_vcl.hxx" 26 27 #include "osl/file.hxx" 28 #include "osl/process.h" 29 30 #include "vos/mutex.hxx" 31 32 #include "rtl/bootstrap.h" 33 #include "rtl/strbuf.hxx" 34 35 #include "basegfx/range/b2drectangle.hxx" 36 #include "basegfx/polygon/b2dpolygon.hxx" 37 #include "basegfx/polygon/b2dpolygontools.hxx" 38 #include "basegfx/matrix/b2dhommatrix.hxx" 39 #include "basegfx/matrix/b2dhommatrixtools.hxx" 40 41 #include "vcl/sysdata.hxx" 42 #include "vcl/svapp.hxx" 43 44 #include "aqua/salconst.h" 45 #include "aqua/salgdi.h" 46 #include "aqua/salbmp.h" 47 #include "aqua/salframe.h" 48 #include "aqua/salcolorutils.hxx" 49 #include "aqua/salatsuifontutils.hxx" 50 51 #include "fontsubset.hxx" 52 #include "impfont.hxx" 53 #include "region.h" 54 #include "sallayout.hxx" 55 #include "sft.hxx" 56 57 58 using namespace vcl; 59 60 //typedef unsigned char Boolean; // copied from MacTypes.h, should be properly included 61 typedef std::vector<unsigned char> ByteVector; 62 63 64 // ======================================================================= 65 66 ImplMacFontData::ImplMacFontData( const ImplDevFontAttributes& rDFA, ATSUFontID nFontId ) 67 : ImplFontData( rDFA, 0 ) 68 , mnFontId( nFontId ) 69 , mpCharMap( NULL ) 70 , mbOs2Read( false ) 71 , mbHasOs2Table( false ) 72 , mbCmapEncodingRead( false ) 73 , mbHasCJKSupport( false ) 74 {} 75 76 // ----------------------------------------------------------------------- 77 78 ImplMacFontData::~ImplMacFontData() 79 { 80 if( mpCharMap ) 81 mpCharMap->DeReference(); 82 } 83 84 // ----------------------------------------------------------------------- 85 86 sal_IntPtr ImplMacFontData::GetFontId() const 87 { 88 return (sal_IntPtr)mnFontId; 89 } 90 91 // ----------------------------------------------------------------------- 92 93 ImplFontData* ImplMacFontData::Clone() const 94 { 95 ImplMacFontData* pClone = new ImplMacFontData(*this); 96 if( mpCharMap ) 97 mpCharMap->AddReference(); 98 return pClone; 99 } 100 101 // ----------------------------------------------------------------------- 102 103 ImplFontEntry* ImplMacFontData::CreateFontInstance(ImplFontSelectData& rFSD) const 104 { 105 return new ImplFontEntry(rFSD); 106 } 107 108 // ----------------------------------------------------------------------- 109 110 inline FourCharCode GetTag(const char aTagName[5]) 111 { 112 return (aTagName[0]<<24)+(aTagName[1]<<16)+(aTagName[2]<<8)+(aTagName[3]); 113 } 114 115 static unsigned GetUShort( const unsigned char* p ){return((p[0]<<8)+p[1]);} 116 static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);} 117 118 const ImplFontCharMap* ImplMacFontData::GetImplFontCharMap() const 119 { 120 // return the cached charmap 121 if( mpCharMap ) 122 return mpCharMap; 123 124 // set the default charmap 125 mpCharMap = ImplFontCharMap::GetDefaultMap(); 126 mpCharMap->AddReference(); 127 128 // get the CMAP byte size 129 ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId ); 130 ByteCount nBufSize = 0; 131 OSStatus eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nBufSize ); 132 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::GetImplFontCharMap : ATSFontGetTable1 failed!\n"); 133 if( eStatus != noErr ) 134 return mpCharMap; 135 136 // allocate a buffer for the CMAP raw data 137 ByteVector aBuffer( nBufSize ); 138 139 // get the CMAP raw data 140 ByteCount nRawLength = 0; 141 eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength ); 142 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::GetImplFontCharMap : ATSFontGetTable2 failed!\n"); 143 if( eStatus != noErr ) 144 return mpCharMap; 145 DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::GetImplFontCharMap : ByteCount mismatch!\n"); 146 147 // parse the CMAP 148 CmapResult aCmapResult; 149 if( ParseCMAP( &aBuffer[0], nRawLength, aCmapResult ) ) 150 { 151 // create the matching charmap 152 mpCharMap->DeReference(); 153 mpCharMap = new ImplFontCharMap( aCmapResult ); 154 mpCharMap->AddReference(); 155 } 156 157 return mpCharMap; 158 } 159 160 // ----------------------------------------------------------------------- 161 162 void ImplMacFontData::ReadOs2Table( void ) const 163 { 164 // read this only once per font 165 if( mbOs2Read ) 166 return; 167 mbOs2Read = true; 168 169 // prepare to get the OS/2 table raw data 170 ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId ); 171 ByteCount nBufSize = 0; 172 OSStatus eStatus = ATSFontGetTable( rFont, GetTag("OS/2"), 0, 0, NULL, &nBufSize ); 173 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadOs2Table : ATSFontGetTable1 failed!\n"); 174 if( eStatus != noErr ) 175 return; 176 177 // allocate a buffer for the OS/2 raw data 178 ByteVector aBuffer( nBufSize ); 179 180 // get the OS/2 raw data 181 ByteCount nRawLength = 0; 182 eStatus = ATSFontGetTable( rFont, GetTag("OS/2"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength ); 183 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadOs2Table : ATSFontGetTable2 failed!\n"); 184 if( eStatus != noErr ) 185 return; 186 DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::ReadOs2Table : ByteCount mismatch!\n"); 187 mbHasOs2Table = true; 188 189 // parse the OS/2 raw data 190 // TODO: also analyze panose info, etc. 191 192 // check if the fonts needs the "CJK extra leading" heuristic 193 const unsigned char* pOS2map = &aBuffer[0]; 194 const sal_uInt32 nVersion = GetUShort( pOS2map ); 195 if( nVersion >= 0x0001 ) 196 { 197 sal_uInt32 ulUnicodeRange2 = GetUInt( pOS2map + 46 ); 198 if( ulUnicodeRange2 & 0x2DF00000 ) 199 mbHasCJKSupport = true; 200 } 201 } 202 203 void ImplMacFontData::ReadMacCmapEncoding( void ) const 204 { 205 // read this only once per font 206 if( mbCmapEncodingRead ) 207 return; 208 mbCmapEncodingRead = true; 209 210 ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId ); 211 ByteCount nBufSize = 0; 212 OSStatus eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nBufSize ); 213 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadMacCmapEncoding : ATSFontGetTable1 failed!\n"); 214 if( eStatus != noErr ) 215 return; 216 217 ByteVector aBuffer( nBufSize ); 218 219 ByteCount nRawLength = 0; 220 eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength ); 221 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadMacCmapEncoding : ATSFontGetTable2 failed!\n"); 222 if( eStatus != noErr ) 223 return; 224 DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::ReadMacCmapEncoding : ByteCount mismatch!\n"); 225 226 const unsigned char* pCmap = &aBuffer[0]; 227 228 if (nRawLength < 24 ) 229 return; 230 if( GetUShort( pCmap ) != 0x0000 ) 231 return; 232 233 // check if the fonts needs the "CJK extra leading" heuristic 234 int nSubTables = GetUShort( pCmap + 2 ); 235 236 for( const unsigned char* p = pCmap + 4; --nSubTables >= 0; p += 8 ) 237 { 238 int nPlatform = GetUShort( p ); 239 if( nPlatform == kFontMacintoshPlatform ) { 240 int nEncoding = GetUShort (p + 2 ); 241 if( nEncoding == kFontJapaneseScript || 242 nEncoding == kFontTraditionalChineseScript || 243 nEncoding == kFontKoreanScript || 244 nEncoding == kFontSimpleChineseScript ) 245 { 246 mbHasCJKSupport = true; 247 break; 248 } 249 } 250 } 251 } 252 253 // ----------------------------------------------------------------------- 254 255 bool ImplMacFontData::HasCJKSupport( void ) const 256 { 257 ReadOs2Table(); 258 if( !mbHasOs2Table ) 259 ReadMacCmapEncoding(); 260 261 return mbHasCJKSupport; 262 } 263 264 // ======================================================================= 265 266 AquaSalGraphics::AquaSalGraphics() 267 : mpFrame( NULL ) 268 , mxLayer( NULL ) 269 , mrContext( NULL ) 270 , mpXorEmulation( NULL ) 271 , mnXorMode( 0 ) 272 , mnWidth( 0 ) 273 , mnHeight( 0 ) 274 , mnBitmapDepth( 0 ) 275 , mnRealDPIX( 0 ) 276 , mnRealDPIY( 0 ) 277 , mfFakeDPIScale( 1.0 ) 278 , mxClipPath( NULL ) 279 , maLineColor( COL_WHITE ) 280 , maFillColor( COL_BLACK ) 281 , mpMacFontData( NULL ) 282 , mnATSUIRotation( 0 ) 283 , mfFontScale( 1.0 ) 284 , mfFontStretch( 1.0 ) 285 , mbNonAntialiasedText( false ) 286 , mbPrinter( false ) 287 , mbVirDev( false ) 288 , mbWindow( false ) 289 { 290 // create the style object for font attributes 291 ATSUCreateStyle( &maATSUStyle ); 292 } 293 294 // ----------------------------------------------------------------------- 295 296 AquaSalGraphics::~AquaSalGraphics() 297 { 298 /* 299 if( mnUpdateGraphicsEvent ) 300 { 301 Application::RemoveUserEvent( mnUpdateGraphicsEvent ); 302 } 303 */ 304 CGPathRelease( mxClipPath ); 305 ATSUDisposeStyle( maATSUStyle ); 306 307 if( mpXorEmulation ) 308 delete mpXorEmulation; 309 310 if( mxLayer ) 311 CGLayerRelease( mxLayer ); 312 else if( mrContext && mbWindow ) 313 { 314 // destroy backbuffer bitmap context that we created ourself 315 CGContextRelease( mrContext ); 316 mrContext = NULL; 317 // memory is freed automatically by maOwnContextMemory 318 } 319 } 320 321 bool AquaSalGraphics::supportsOperation( OutDevSupportType eType ) const 322 { 323 bool bRet = false; 324 switch( eType ) 325 { 326 case OutDevSupport_TransparentRect: 327 case OutDevSupport_B2DClip: 328 case OutDevSupport_B2DDraw: 329 bRet = true; 330 break; 331 default: break; 332 } 333 return bRet; 334 } 335 336 // ======================================================================= 337 338 void AquaSalGraphics::updateResolution() 339 { 340 DBG_ASSERT( mbWindow, "updateResolution on inappropriate graphics" ); 341 342 initResolution( (mbWindow && mpFrame) ? mpFrame->mpWindow : nil ); 343 } 344 345 void AquaSalGraphics::initResolution( NSWindow* ) 346 { 347 // #i100617# read DPI only once; there is some kind of weird caching going on 348 // if the main screen changes 349 // FIXME: this is really unfortunate and needs to be investigated 350 351 SalData* pSalData = GetSalData(); 352 if( pSalData->mnDPIX == 0 || pSalData->mnDPIY == 0 ) 353 { 354 NSScreen* pScreen = nil; 355 356 /* #i91301# 357 many woes went into the try to have different resolutions 358 on different screens. The result of these trials is that OOo is not ready 359 for that yet, vcl and applications would need to be adapted. 360 361 Unfortunately this is not possible in the 3.0 timeframe. 362 So let's stay with one resolution for all Windows and VirtualDevices 363 which is the resolution of the main screen 364 365 This of course also means that measurements are exact only on the main screen. 366 For activating different resolutions again just comment out the two lines below. 367 368 if( pWin ) 369 pScreen = [pWin screen]; 370 */ 371 if( pScreen == nil ) 372 { 373 NSArray* pScreens = [NSScreen screens]; 374 if( pScreens ) 375 pScreen = [pScreens objectAtIndex: 0]; 376 } 377 378 mnRealDPIX = mnRealDPIY = 96; 379 if( pScreen ) 380 { 381 NSDictionary* pDev = [pScreen deviceDescription]; 382 if( pDev ) 383 { 384 NSNumber* pVal = [pDev objectForKey: @"NSScreenNumber"]; 385 if( pVal ) 386 { 387 // FIXME: casting a long to CGDirectDisplayID is evil, but 388 // Apple suggest to do it this way 389 const CGDirectDisplayID nDisplayID = (CGDirectDisplayID)[pVal longValue]; 390 const CGSize aSize = CGDisplayScreenSize( nDisplayID ); // => result is in millimeters 391 mnRealDPIX = static_cast<long>((CGDisplayPixelsWide( nDisplayID ) * 25.4) / aSize.width); 392 mnRealDPIY = static_cast<long>((CGDisplayPixelsHigh( nDisplayID ) * 25.4) / aSize.height); 393 } 394 else 395 { 396 DBG_ERROR( "no resolution found in device description" ); 397 } 398 } 399 else 400 { 401 DBG_ERROR( "no device description" ); 402 } 403 } 404 else 405 { 406 DBG_ERROR( "no screen found" ); 407 } 408 409 // #i107076# maintaining size-WYSIWYG-ness causes many problems for 410 // low-DPI, high-DPI or for mis-reporting devices 411 // => it is better to limit the calculation result then 412 static const int nMinDPI = 72; 413 if( (mnRealDPIX < nMinDPI) || (mnRealDPIY < nMinDPI) ) 414 mnRealDPIX = mnRealDPIY = nMinDPI; 415 static const int nMaxDPI = 200; 416 if( (mnRealDPIX > nMaxDPI) || (mnRealDPIY > nMaxDPI) ) 417 mnRealDPIX = mnRealDPIY = nMaxDPI; 418 419 // for OSX any anisotropy reported for the display resolution is best ignored (e.g. TripleHead2Go) 420 mnRealDPIX = mnRealDPIY = (mnRealDPIX + mnRealDPIY + 1) / 2; 421 422 pSalData->mnDPIX = mnRealDPIX; 423 pSalData->mnDPIY = mnRealDPIY; 424 } 425 else 426 { 427 mnRealDPIX = pSalData->mnDPIX; 428 mnRealDPIY = pSalData->mnDPIY; 429 } 430 431 mfFakeDPIScale = 1.0; 432 } 433 434 void AquaSalGraphics::GetResolution( long& rDPIX, long& rDPIY ) 435 { 436 if( !mnRealDPIY ) 437 initResolution( (mbWindow && mpFrame) ? mpFrame->mpWindow : nil ); 438 439 rDPIX = static_cast<long>(mfFakeDPIScale * mnRealDPIX); 440 rDPIY = static_cast<long>(mfFakeDPIScale * mnRealDPIY); 441 } 442 443 void AquaSalGraphics::copyResolution( AquaSalGraphics& rGraphics ) 444 { 445 if( !rGraphics.mnRealDPIY && rGraphics.mbWindow && rGraphics.mpFrame ) 446 rGraphics.initResolution( rGraphics.mpFrame->mpWindow ); 447 448 mnRealDPIX = rGraphics.mnRealDPIX; 449 mnRealDPIY = rGraphics.mnRealDPIY; 450 mfFakeDPIScale = rGraphics.mfFakeDPIScale; 451 } 452 453 // ----------------------------------------------------------------------- 454 455 sal_uInt16 AquaSalGraphics::GetBitCount() 456 { 457 sal_uInt16 nBits = mnBitmapDepth ? mnBitmapDepth : 32;//24; 458 return nBits; 459 } 460 461 // ----------------------------------------------------------------------- 462 463 static const basegfx::B2DPoint aHalfPointOfs ( 0.5, 0.5 ); 464 465 static void AddPolygonToPath( CGMutablePathRef xPath, 466 const ::basegfx::B2DPolygon& rPolygon, bool bClosePath, bool bPixelSnap, bool bLineDraw ) 467 { 468 // short circuit if there is nothing to do 469 const int nPointCount = rPolygon.count(); 470 if( nPointCount <= 0 ) 471 return; 472 473 (void)bPixelSnap; // TODO 474 const CGAffineTransform* pTransform = NULL; 475 476 const bool bHasCurves = rPolygon.areControlPointsUsed(); 477 for( int nPointIdx = 0, nPrevIdx = 0;; nPrevIdx = nPointIdx++ ) 478 { 479 int nClosedIdx = nPointIdx; 480 if( nPointIdx >= nPointCount ) 481 { 482 // prepare to close last curve segment if needed 483 if( bClosePath && (nPointIdx == nPointCount) ) 484 nClosedIdx = 0; 485 else 486 break; 487 } 488 489 ::basegfx::B2DPoint aPoint = rPolygon.getB2DPoint( nClosedIdx ); 490 491 if( bPixelSnap) 492 { 493 // snap device coordinates to full pixels 494 aPoint.setX( basegfx::fround( aPoint.getX() ) ); 495 aPoint.setY( basegfx::fround( aPoint.getY() ) ); 496 } 497 498 if( bLineDraw ) 499 aPoint += aHalfPointOfs; 500 501 if( !nPointIdx ) { // first point => just move there 502 CGPathMoveToPoint( xPath, pTransform, aPoint.getX(), aPoint.getY() ); 503 continue; 504 } 505 506 bool bPendingCurve = false; 507 if( bHasCurves ) 508 { 509 bPendingCurve = rPolygon.isNextControlPointUsed( nPrevIdx ); 510 bPendingCurve |= rPolygon.isPrevControlPointUsed( nClosedIdx ); 511 } 512 513 if( !bPendingCurve ) // line segment 514 CGPathAddLineToPoint( xPath, pTransform, aPoint.getX(), aPoint.getY() ); 515 else // cubic bezier segment 516 { 517 basegfx::B2DPoint aCP1 = rPolygon.getNextControlPoint( nPrevIdx ); 518 basegfx::B2DPoint aCP2 = rPolygon.getPrevControlPoint( nClosedIdx ); 519 if( bLineDraw ) 520 { 521 aCP1 += aHalfPointOfs; 522 aCP2 += aHalfPointOfs; 523 } 524 CGPathAddCurveToPoint( xPath, pTransform, aCP1.getX(), aCP1.getY(), 525 aCP2.getX(), aCP2.getY(), aPoint.getX(), aPoint.getY() ); 526 } 527 } 528 529 if( bClosePath ) 530 CGPathCloseSubpath( xPath ); 531 } 532 533 static void AddPolyPolygonToPath( CGMutablePathRef xPath, 534 const ::basegfx::B2DPolyPolygon& rPolyPoly, bool bPixelSnap, bool bLineDraw ) 535 { 536 // short circuit if there is nothing to do 537 const int nPolyCount = rPolyPoly.count(); 538 if( nPolyCount <= 0 ) 539 return; 540 541 for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx ) 542 { 543 const ::basegfx::B2DPolygon rPolygon = rPolyPoly.getB2DPolygon( nPolyIdx ); 544 AddPolygonToPath( xPath, rPolygon, true, bPixelSnap, bLineDraw ); 545 } 546 } 547 548 // ----------------------------------------------------------------------- 549 550 void AquaSalGraphics::ResetClipRegion() 551 { 552 // release old path and indicate no clipping 553 if( mxClipPath ) 554 { 555 CGPathRelease( mxClipPath ); 556 mxClipPath = NULL; 557 } 558 if( CheckContext() ) 559 SetState(); 560 } 561 562 // ----------------------------------------------------------------------- 563 564 bool AquaSalGraphics::setClipRegion( const Region& i_rClip ) 565 { 566 // release old clip path 567 if( mxClipPath ) 568 { 569 CGPathRelease( mxClipPath ); 570 mxClipPath = NULL; 571 } 572 mxClipPath = CGPathCreateMutable(); 573 574 // set current path, either as polypolgon or sequence of rectangles 575 if( i_rClip.HasPolyPolygon() ) 576 { 577 basegfx::B2DPolyPolygon aClip( const_cast<Region&>(i_rClip).ConvertToB2DPolyPolygon() ); 578 AddPolyPolygonToPath( mxClipPath, aClip, !getAntiAliasB2DDraw(), false ); 579 } 580 else 581 { 582 long nX, nY, nW, nH; 583 ImplRegionInfo aInfo; 584 bool bRegionRect = i_rClip.ImplGetFirstRect(aInfo, nX, nY, nW, nH ); 585 while( bRegionRect ) 586 { 587 if( nW && nH ) 588 { 589 CGRect aRect = {{nX,nY}, {nW,nH}}; 590 CGPathAddRect( mxClipPath, NULL, aRect ); 591 } 592 bRegionRect = i_rClip.ImplGetNextRect( aInfo, nX, nY, nW, nH ); 593 } 594 } 595 // set the current path as clip region 596 if( CheckContext() ) 597 SetState(); 598 return true; 599 } 600 601 // ----------------------------------------------------------------------- 602 603 void AquaSalGraphics::SetLineColor() 604 { 605 maLineColor.SetAlpha( 0.0 ); // transparent 606 if( CheckContext() ) 607 CGContextSetStrokeColor( mrContext, maLineColor.AsArray() ); 608 } 609 610 // ----------------------------------------------------------------------- 611 612 void AquaSalGraphics::SetLineColor( SalColor nSalColor ) 613 { 614 maLineColor = RGBAColor( nSalColor ); 615 if( CheckContext() ) 616 CGContextSetStrokeColor( mrContext, maLineColor.AsArray() ); 617 } 618 619 // ----------------------------------------------------------------------- 620 621 void AquaSalGraphics::SetFillColor() 622 { 623 maFillColor.SetAlpha( 0.0 ); // transparent 624 if( CheckContext() ) 625 CGContextSetFillColor( mrContext, maFillColor.AsArray() ); 626 } 627 628 // ----------------------------------------------------------------------- 629 630 void AquaSalGraphics::SetFillColor( SalColor nSalColor ) 631 { 632 maFillColor = RGBAColor( nSalColor ); 633 if( CheckContext() ) 634 CGContextSetFillColor( mrContext, maFillColor.AsArray() ); 635 } 636 637 // ----------------------------------------------------------------------- 638 639 static SalColor ImplGetROPSalColor( SalROPColor nROPColor ) 640 { 641 SalColor nSalColor; 642 if ( nROPColor == SAL_ROP_0 ) 643 nSalColor = MAKE_SALCOLOR( 0, 0, 0 ); 644 else 645 nSalColor = MAKE_SALCOLOR( 255, 255, 255 ); 646 return nSalColor; 647 } 648 649 void AquaSalGraphics::SetROPLineColor( SalROPColor nROPColor ) 650 { 651 if( ! mbPrinter ) 652 SetLineColor( ImplGetROPSalColor( nROPColor ) ); 653 } 654 655 // ----------------------------------------------------------------------- 656 657 void AquaSalGraphics::SetROPFillColor( SalROPColor nROPColor ) 658 { 659 if( ! mbPrinter ) 660 SetFillColor( ImplGetROPSalColor( nROPColor ) ); 661 } 662 663 // ----------------------------------------------------------------------- 664 665 void AquaSalGraphics::ImplDrawPixel( long nX, long nY, const RGBAColor& rColor ) 666 { 667 if( !CheckContext() ) 668 return; 669 670 // overwrite the fill color 671 CGContextSetFillColor( mrContext, rColor.AsArray() ); 672 // draw 1x1 rect, there is no pixel drawing in Quartz 673 CGRect aDstRect = {{nX,nY,},{1,1}}; 674 CGContextFillRect( mrContext, aDstRect ); 675 RefreshRect( aDstRect ); 676 // reset the fill color 677 CGContextSetFillColor( mrContext, maFillColor.AsArray() ); 678 } 679 680 void AquaSalGraphics::drawPixel( long nX, long nY ) 681 { 682 // draw pixel with current line color 683 ImplDrawPixel( nX, nY, maLineColor ); 684 } 685 686 void AquaSalGraphics::drawPixel( long nX, long nY, SalColor nSalColor ) 687 { 688 const RGBAColor aPixelColor( nSalColor ); 689 ImplDrawPixel( nX, nY, aPixelColor ); 690 } 691 692 // ----------------------------------------------------------------------- 693 694 void AquaSalGraphics::drawLine( long nX1, long nY1, long nX2, long nY2 ) 695 { 696 if( nX1 == nX2 && nY1 == nY2 ) 697 { 698 // #i109453# platform independent code expects at least one pixel to be drawn 699 drawPixel( nX1, nY1 ); 700 return; 701 } 702 703 if( !CheckContext() ) 704 return; 705 706 CGContextBeginPath( mrContext ); 707 CGContextMoveToPoint( mrContext, static_cast<float>(nX1)+0.5, static_cast<float>(nY1)+0.5 ); 708 CGContextAddLineToPoint( mrContext, static_cast<float>(nX2)+0.5, static_cast<float>(nY2)+0.5 ); 709 CGContextDrawPath( mrContext, kCGPathStroke ); 710 711 Rectangle aRefreshRect( nX1, nY1, nX2, nY2 ); 712 } 713 714 // ----------------------------------------------------------------------- 715 716 void AquaSalGraphics::drawRect( long nX, long nY, long nWidth, long nHeight ) 717 { 718 if( !CheckContext() ) 719 return; 720 721 CGRect aRect( CGRectMake(nX, nY, nWidth, nHeight) ); 722 if( IsPenVisible() ) 723 { 724 aRect.origin.x += 0.5; 725 aRect.origin.y += 0.5; 726 aRect.size.width -= 1; 727 aRect.size.height -= 1; 728 } 729 730 if( IsBrushVisible() ) 731 CGContextFillRect( mrContext, aRect ); 732 733 if( IsPenVisible() ) 734 CGContextStrokeRect( mrContext, aRect ); 735 736 RefreshRect( nX, nY, nWidth, nHeight ); 737 } 738 739 // ----------------------------------------------------------------------- 740 741 static void getBoundRect( sal_uLong nPoints, const SalPoint *pPtAry, long &rX, long& rY, long& rWidth, long& rHeight ) 742 { 743 long nX1 = pPtAry->mnX; 744 long nX2 = nX1; 745 long nY1 = pPtAry->mnY; 746 long nY2 = nY1; 747 for( sal_uLong n = 1; n < nPoints; n++ ) 748 { 749 if( pPtAry[n].mnX < nX1 ) 750 nX1 = pPtAry[n].mnX; 751 else if( pPtAry[n].mnX > nX2 ) 752 nX2 = pPtAry[n].mnX; 753 754 if( pPtAry[n].mnY < nY1 ) 755 nY1 = pPtAry[n].mnY; 756 else if( pPtAry[n].mnY > nY2 ) 757 nY2 = pPtAry[n].mnY; 758 } 759 rX = nX1; 760 rY = nY1; 761 rWidth = nX2 - nX1 + 1; 762 rHeight = nY2 - nY1 + 1; 763 } 764 765 static inline void alignLinePoint( const SalPoint* i_pIn, float& o_fX, float& o_fY ) 766 { 767 o_fX = static_cast<float>(i_pIn->mnX ) + 0.5; 768 o_fY = static_cast<float>(i_pIn->mnY ) + 0.5; 769 } 770 771 void AquaSalGraphics::drawPolyLine( sal_uLong nPoints, const SalPoint *pPtAry ) 772 { 773 if( nPoints < 1 ) 774 return; 775 if( !CheckContext() ) 776 return; 777 778 long nX = 0, nY = 0, nWidth = 0, nHeight = 0; 779 getBoundRect( nPoints, pPtAry, nX, nY, nWidth, nHeight ); 780 781 float fX, fY; 782 783 CGContextBeginPath( mrContext ); 784 alignLinePoint( pPtAry, fX, fY ); 785 CGContextMoveToPoint( mrContext, fX, fY ); 786 pPtAry++; 787 for( sal_uLong nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) 788 { 789 alignLinePoint( pPtAry, fX, fY ); 790 CGContextAddLineToPoint( mrContext, fX, fY ); 791 } 792 CGContextDrawPath( mrContext, kCGPathStroke ); 793 794 RefreshRect( nX, nY, nWidth, nHeight ); 795 } 796 797 // ----------------------------------------------------------------------- 798 799 void AquaSalGraphics::drawPolygon( sal_uLong nPoints, const SalPoint *pPtAry ) 800 { 801 if( nPoints <= 1 ) 802 return; 803 if( !CheckContext() ) 804 return; 805 806 long nX = 0, nY = 0, nWidth = 0, nHeight = 0; 807 getBoundRect( nPoints, pPtAry, nX, nY, nWidth, nHeight ); 808 809 CGPathDrawingMode eMode; 810 if( IsBrushVisible() && IsPenVisible() ) 811 eMode = kCGPathEOFillStroke; 812 else if( IsPenVisible() ) 813 eMode = kCGPathStroke; 814 else if( IsBrushVisible() ) 815 eMode = kCGPathEOFill; 816 else 817 return; 818 819 CGContextBeginPath( mrContext ); 820 821 if( IsPenVisible() ) 822 { 823 float fX, fY; 824 alignLinePoint( pPtAry, fX, fY ); 825 CGContextMoveToPoint( mrContext, fX, fY ); 826 pPtAry++; 827 for( sal_uLong nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) 828 { 829 alignLinePoint( pPtAry, fX, fY ); 830 CGContextAddLineToPoint( mrContext, fX, fY ); 831 } 832 } 833 else 834 { 835 CGContextMoveToPoint( mrContext, pPtAry->mnX, pPtAry->mnY ); 836 pPtAry++; 837 for( sal_uLong nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) 838 CGContextAddLineToPoint( mrContext, pPtAry->mnX, pPtAry->mnY ); 839 } 840 841 CGContextDrawPath( mrContext, eMode ); 842 RefreshRect( nX, nY, nWidth, nHeight ); 843 } 844 845 // ----------------------------------------------------------------------- 846 847 void AquaSalGraphics::drawPolyPolygon( sal_uLong nPolyCount, const sal_uLong *pPoints, PCONSTSALPOINT *ppPtAry ) 848 { 849 if( nPolyCount <= 0 ) 850 return; 851 if( !CheckContext() ) 852 return; 853 854 // find bound rect 855 long leftX = 0, topY = 0, maxWidth = 0, maxHeight = 0; 856 getBoundRect( pPoints[0], ppPtAry[0], leftX, topY, maxWidth, maxHeight ); 857 for( sal_uLong n = 1; n < nPolyCount; n++ ) 858 { 859 long nX = leftX, nY = topY, nW = maxWidth, nH = maxHeight; 860 getBoundRect( pPoints[n], ppPtAry[n], nX, nY, nW, nH ); 861 if( nX < leftX ) 862 { 863 maxWidth += leftX - nX; 864 leftX = nX; 865 } 866 if( nY < topY ) 867 { 868 maxHeight += topY - nY; 869 topY = nY; 870 } 871 if( nX + nW > leftX + maxWidth ) 872 maxWidth = nX + nW - leftX; 873 if( nY + nH > topY + maxHeight ) 874 maxHeight = nY + nH - topY; 875 } 876 877 // prepare drawing mode 878 CGPathDrawingMode eMode; 879 if( IsBrushVisible() && IsPenVisible() ) 880 eMode = kCGPathEOFillStroke; 881 else if( IsPenVisible() ) 882 eMode = kCGPathStroke; 883 else if( IsBrushVisible() ) 884 eMode = kCGPathEOFill; 885 else 886 return; 887 888 // convert to CGPath 889 CGContextBeginPath( mrContext ); 890 if( IsPenVisible() ) 891 { 892 for( sal_uLong nPoly = 0; nPoly < nPolyCount; nPoly++ ) 893 { 894 const sal_uLong nPoints = pPoints[nPoly]; 895 if( nPoints > 1 ) 896 { 897 const SalPoint *pPtAry = ppPtAry[nPoly]; 898 float fX, fY; 899 alignLinePoint( pPtAry, fX, fY ); 900 CGContextMoveToPoint( mrContext, fX, fY ); 901 pPtAry++; 902 for( sal_uLong nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) 903 { 904 alignLinePoint( pPtAry, fX, fY ); 905 CGContextAddLineToPoint( mrContext, fX, fY ); 906 } 907 CGContextClosePath(mrContext); 908 } 909 } 910 } 911 else 912 { 913 for( sal_uLong nPoly = 0; nPoly < nPolyCount; nPoly++ ) 914 { 915 const sal_uLong nPoints = pPoints[nPoly]; 916 if( nPoints > 1 ) 917 { 918 const SalPoint *pPtAry = ppPtAry[nPoly]; 919 CGContextMoveToPoint( mrContext, pPtAry->mnX, pPtAry->mnY ); 920 pPtAry++; 921 for( sal_uLong nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) 922 CGContextAddLineToPoint( mrContext, pPtAry->mnX, pPtAry->mnY ); 923 CGContextClosePath(mrContext); 924 } 925 } 926 } 927 928 CGContextDrawPath( mrContext, eMode ); 929 930 RefreshRect( leftX, topY, maxWidth, maxHeight ); 931 } 932 933 // ----------------------------------------------------------------------- 934 935 bool AquaSalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly, 936 double fTransparency ) 937 { 938 // short circuit if there is nothing to do 939 const int nPolyCount = rPolyPoly.count(); 940 if( nPolyCount <= 0 ) 941 return true; 942 943 // ignore invisible polygons 944 if( (fTransparency >= 1.0) || (fTransparency < 0) ) 945 return true; 946 947 // setup poly-polygon path 948 CGMutablePathRef xPath = CGPathCreateMutable(); 949 for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx ) 950 { 951 const ::basegfx::B2DPolygon rPolygon = rPolyPoly.getB2DPolygon( nPolyIdx ); 952 AddPolygonToPath( xPath, rPolygon, true, !getAntiAliasB2DDraw(), IsPenVisible() ); 953 } 954 955 const CGRect aRefreshRect = CGPathGetBoundingBox( xPath ); 956 #ifndef NO_I97317_WORKAROUND 957 // #i97317# workaround for Quartz having problems with drawing small polygons 958 if( ! ((aRefreshRect.size.width <= 0.125) && (aRefreshRect.size.height <= 0.125)) ) 959 #endif 960 { 961 // use the path to prepare the graphics context 962 CGContextSaveGState( mrContext ); 963 CGContextBeginPath( mrContext ); 964 CGContextAddPath( mrContext, xPath ); 965 966 // draw path with antialiased polygon 967 CGContextSetShouldAntialias( mrContext, true ); 968 CGContextSetAlpha( mrContext, 1.0 - fTransparency ); 969 CGContextDrawPath( mrContext, kCGPathEOFillStroke ); 970 CGContextRestoreGState( mrContext ); 971 972 // mark modified rectangle as updated 973 RefreshRect( aRefreshRect ); 974 } 975 976 CGPathRelease( xPath ); 977 978 return true; 979 } 980 981 // ----------------------------------------------------------------------- 982 983 bool AquaSalGraphics::drawPolyLine( const ::basegfx::B2DPolygon& rPolyLine, 984 double fTransparency, 985 const ::basegfx::B2DVector& rLineWidths, 986 basegfx::B2DLineJoin eLineJoin ) 987 { 988 // short circuit if there is nothing to do 989 const int nPointCount = rPolyLine.count(); 990 if( nPointCount <= 0 ) 991 return true; 992 993 // reject requests that cannot be handled yet 994 if( rLineWidths.getX() != rLineWidths.getY() ) 995 return false; 996 997 // #i101491# Aqua does not support B2DLINEJOIN_NONE; return false to use 998 // the fallback (own geometry preparation) 999 // #i104886# linejoin-mode and thus the above only applies to "fat" lines 1000 if( (basegfx::B2DLINEJOIN_NONE == eLineJoin) 1001 && (rLineWidths.getX() > 1.3) ) 1002 return false; 1003 1004 // setup line attributes 1005 CGLineJoin aCGLineJoin = kCGLineJoinMiter; 1006 switch( eLineJoin ) { 1007 case ::basegfx::B2DLINEJOIN_NONE: aCGLineJoin = /*TODO?*/kCGLineJoinMiter; break; 1008 case ::basegfx::B2DLINEJOIN_MIDDLE: aCGLineJoin = /*TODO?*/kCGLineJoinMiter; break; 1009 case ::basegfx::B2DLINEJOIN_BEVEL: aCGLineJoin = kCGLineJoinBevel; break; 1010 case ::basegfx::B2DLINEJOIN_MITER: aCGLineJoin = kCGLineJoinMiter; break; 1011 case ::basegfx::B2DLINEJOIN_ROUND: aCGLineJoin = kCGLineJoinRound; break; 1012 } 1013 1014 // setup poly-polygon path 1015 CGMutablePathRef xPath = CGPathCreateMutable(); 1016 AddPolygonToPath( xPath, rPolyLine, rPolyLine.isClosed(), !getAntiAliasB2DDraw(), true ); 1017 1018 const CGRect aRefreshRect = CGPathGetBoundingBox( xPath ); 1019 #ifndef NO_I97317_WORKAROUND 1020 // #i97317# workaround for Quartz having problems with drawing small polygons 1021 if( ! ((aRefreshRect.size.width <= 0.125) && (aRefreshRect.size.height <= 0.125)) ) 1022 #endif 1023 { 1024 // use the path to prepare the graphics context 1025 CGContextSaveGState( mrContext ); 1026 CGContextAddPath( mrContext, xPath ); 1027 // draw path with antialiased line 1028 CGContextSetShouldAntialias( mrContext, true ); 1029 CGContextSetAlpha( mrContext, 1.0 - fTransparency ); 1030 CGContextSetLineJoin( mrContext, aCGLineJoin ); 1031 CGContextSetLineWidth( mrContext, rLineWidths.getX() ); 1032 CGContextDrawPath( mrContext, kCGPathStroke ); 1033 CGContextRestoreGState( mrContext ); 1034 1035 // mark modified rectangle as updated 1036 RefreshRect( aRefreshRect ); 1037 } 1038 1039 CGPathRelease( xPath ); 1040 1041 return true; 1042 } 1043 1044 // ----------------------------------------------------------------------- 1045 1046 sal_Bool AquaSalGraphics::drawPolyLineBezier( sal_uLong, const SalPoint*, const sal_uInt8* ) 1047 { 1048 return sal_False; 1049 } 1050 1051 // ----------------------------------------------------------------------- 1052 1053 sal_Bool AquaSalGraphics::drawPolygonBezier( sal_uLong, const SalPoint*, const sal_uInt8* ) 1054 { 1055 return sal_False; 1056 } 1057 1058 // ----------------------------------------------------------------------- 1059 1060 sal_Bool AquaSalGraphics::drawPolyPolygonBezier( sal_uLong, const sal_uLong*, 1061 const SalPoint* const*, const sal_uInt8* const* ) 1062 { 1063 return sal_False; 1064 } 1065 1066 // ----------------------------------------------------------------------- 1067 1068 void AquaSalGraphics::copyBits( const SalTwoRect *pPosAry, SalGraphics *pSrcGraphics ) 1069 { 1070 if( !pSrcGraphics ) 1071 pSrcGraphics = this; 1072 1073 //from unix salgdi2.cxx 1074 //[FIXME] find a better way to prevent calc from crashing when width and height are negative 1075 if( pPosAry->mnSrcWidth <= 0 1076 || pPosAry->mnSrcHeight <= 0 1077 || pPosAry->mnDestWidth <= 0 1078 || pPosAry->mnDestHeight <= 0 ) 1079 { 1080 return; 1081 } 1082 1083 // accelerate trivial operations 1084 /*const*/ AquaSalGraphics* pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics); 1085 const bool bSameGraphics = (this == pSrc) || (mbWindow && mpFrame && pSrc->mbWindow && (mpFrame == pSrc->mpFrame)); 1086 if( bSameGraphics 1087 && (pPosAry->mnSrcWidth == pPosAry->mnDestWidth) 1088 && (pPosAry->mnSrcHeight == pPosAry->mnDestHeight)) 1089 { 1090 // short circuit if there is nothing to do 1091 if( (pPosAry->mnSrcX == pPosAry->mnDestX) 1092 && (pPosAry->mnSrcY == pPosAry->mnDestY)) 1093 return; 1094 // use copyArea() if source and destination context are identical 1095 copyArea( pPosAry->mnDestX, pPosAry->mnDestY, pPosAry->mnSrcX, pPosAry->mnSrcY, 1096 pPosAry->mnSrcWidth, pPosAry->mnSrcHeight, 0 ); 1097 return; 1098 } 1099 1100 ApplyXorContext(); 1101 pSrc->ApplyXorContext(); 1102 1103 DBG_ASSERT( pSrc->mxLayer!=NULL, "AquaSalGraphics::copyBits() from non-layered graphics" ); 1104 1105 const CGPoint aDstPoint = { +pPosAry->mnDestX - pPosAry->mnSrcX, pPosAry->mnDestY - pPosAry->mnSrcY }; 1106 if( (pPosAry->mnSrcWidth == pPosAry->mnDestWidth && pPosAry->mnSrcHeight == pPosAry->mnDestHeight) && 1107 (!mnBitmapDepth || (aDstPoint.x + pSrc->mnWidth) <= mnWidth) ) // workaround a Quartz crasher 1108 { 1109 // in XOR mode the drawing context is redirected to the XOR mask 1110 // if source and target are identical then copyBits() paints onto the target context though 1111 CGContextRef xCopyContext = mrContext; 1112 if( mpXorEmulation && mpXorEmulation->IsEnabled() ) 1113 if( pSrcGraphics == this ) 1114 xCopyContext = mpXorEmulation->GetTargetContext(); 1115 1116 CGContextSaveGState( xCopyContext ); 1117 const CGRect aDstRect = { {pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight} }; 1118 CGContextClipToRect( xCopyContext, aDstRect ); 1119 1120 // draw at new destination 1121 // NOTE: flipped drawing gets disabled for this, else the subimage would be drawn upside down 1122 if( pSrc->IsFlipped() ) 1123 { CGContextTranslateCTM( xCopyContext, 0, +mnHeight ); CGContextScaleCTM( xCopyContext, +1, -1 ); } 1124 // TODO: pSrc->size() != this->size() 1125 ::CGContextDrawLayerAtPoint( xCopyContext, aDstPoint, pSrc->mxLayer ); 1126 CGContextRestoreGState( xCopyContext ); 1127 // mark the destination rectangle as updated 1128 RefreshRect( aDstRect ); 1129 } 1130 else 1131 { 1132 SalBitmap* pBitmap = pSrc->getBitmap( pPosAry->mnSrcX, pPosAry->mnSrcY, pPosAry->mnSrcWidth, pPosAry->mnSrcHeight ); 1133 1134 if( pBitmap ) 1135 { 1136 SalTwoRect aPosAry( *pPosAry ); 1137 aPosAry.mnSrcX = 0; 1138 aPosAry.mnSrcY = 0; 1139 drawBitmap( &aPosAry, *pBitmap ); 1140 delete pBitmap; 1141 } 1142 } 1143 } 1144 1145 // ----------------------------------------------------------------------- 1146 1147 void AquaSalGraphics::copyArea( long nDstX, long nDstY,long nSrcX, long nSrcY, long nSrcWidth, long nSrcHeight, sal_uInt16 /*nFlags*/ ) 1148 { 1149 ApplyXorContext(); 1150 1151 #if 0 // TODO: make AquaSalBitmap as fast as the alternative implementation below 1152 SalBitmap* pBitmap = getBitmap( nSrcX, nSrcY, nSrcWidth, nSrcHeight ); 1153 if( pBitmap ) 1154 { 1155 SalTwoRect aPosAry; 1156 aPosAry.mnSrcX = 0; 1157 aPosAry.mnSrcY = 0; 1158 aPosAry.mnSrcWidth = nSrcWidth; 1159 aPosAry.mnSrcHeight = nSrcHeight; 1160 aPosAry.mnDestX = nDstX; 1161 aPosAry.mnDestY = nDstY; 1162 aPosAry.mnDestWidth = nSrcWidth; 1163 aPosAry.mnDestHeight = nSrcHeight; 1164 drawBitmap( &aPosAry, *pBitmap ); 1165 delete pBitmap; 1166 } 1167 #else 1168 DBG_ASSERT( mxLayer!=NULL, "AquaSalGraphics::copyArea() for non-layered graphics" ); 1169 1170 // in XOR mode the drawing context is redirected to the XOR mask 1171 // copyArea() always works on the target context though 1172 CGContextRef xCopyContext = mrContext; 1173 if( mpXorEmulation && mpXorEmulation->IsEnabled() ) 1174 xCopyContext = mpXorEmulation->GetTargetContext(); 1175 1176 // drawing a layer onto its own context causes trouble on OSX => copy it first 1177 // TODO: is it possible to get rid of this unneeded copy more often? 1178 // e.g. on OSX>=10.5 only this situation causes problems: 1179 // mnBitmapDepth && (aDstPoint.x + pSrc->mnWidth) > mnWidth 1180 CGLayerRef xSrcLayer = mxLayer; 1181 // TODO: if( mnBitmapDepth > 0 ) 1182 { 1183 const CGSize aSrcSize = { nSrcWidth, nSrcHeight }; 1184 xSrcLayer = ::CGLayerCreateWithContext( xCopyContext, aSrcSize, NULL ); 1185 const CGContextRef xSrcContext = CGLayerGetContext( xSrcLayer ); 1186 CGPoint aSrcPoint = { -nSrcX, -nSrcY }; 1187 if( IsFlipped() ) 1188 { 1189 ::CGContextTranslateCTM( xSrcContext, 0, +nSrcHeight ); 1190 ::CGContextScaleCTM( xSrcContext, +1, -1 ); 1191 aSrcPoint.y = (nSrcY + nSrcHeight) - mnHeight; 1192 } 1193 ::CGContextDrawLayerAtPoint( xSrcContext, aSrcPoint, mxLayer ); 1194 } 1195 1196 // draw at new destination 1197 const CGPoint aDstPoint = { +nDstX, +nDstY }; 1198 ::CGContextDrawLayerAtPoint( xCopyContext, aDstPoint, xSrcLayer ); 1199 1200 // cleanup 1201 if( xSrcLayer != mxLayer ) 1202 CGLayerRelease( xSrcLayer ); 1203 1204 // mark the destination rectangle as updated 1205 RefreshRect( nDstX, nDstY, nSrcWidth, nSrcHeight ); 1206 #endif 1207 } 1208 1209 // ----------------------------------------------------------------------- 1210 1211 void AquaSalGraphics::drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap ) 1212 { 1213 if( !CheckContext() ) 1214 return; 1215 1216 const AquaSalBitmap& rBitmap = static_cast<const AquaSalBitmap&>(rSalBitmap); 1217 CGImageRef xImage = rBitmap.CreateCroppedImage( (int)pPosAry->mnSrcX, (int)pPosAry->mnSrcY, (int)pPosAry->mnSrcWidth, (int)pPosAry->mnSrcHeight ); 1218 if( !xImage ) 1219 return; 1220 1221 const CGRect aDstRect = {{pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight}}; 1222 CGContextDrawImage( mrContext, aDstRect, xImage ); 1223 CGImageRelease( xImage ); 1224 RefreshRect( aDstRect ); 1225 } 1226 1227 // ----------------------------------------------------------------------- 1228 1229 void AquaSalGraphics::drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap,SalColor ) 1230 { 1231 DBG_ERROR("not implemented for color masking!"); 1232 drawBitmap( pPosAry, rSalBitmap ); 1233 } 1234 1235 // ----------------------------------------------------------------------- 1236 1237 void AquaSalGraphics::drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap, const SalBitmap& rTransparentBitmap ) 1238 { 1239 if( !CheckContext() ) 1240 return; 1241 1242 const AquaSalBitmap& rBitmap = static_cast<const AquaSalBitmap&>(rSalBitmap); 1243 const AquaSalBitmap& rMask = static_cast<const AquaSalBitmap&>(rTransparentBitmap); 1244 CGImageRef xMaskedImage( rBitmap.CreateWithMask( rMask, pPosAry->mnSrcX, pPosAry->mnSrcY, pPosAry->mnSrcWidth, pPosAry->mnSrcHeight ) ); 1245 if( !xMaskedImage ) 1246 return; 1247 1248 const CGRect aDstRect = {{pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight}}; 1249 CGContextDrawImage( mrContext, aDstRect, xMaskedImage ); 1250 CGImageRelease( xMaskedImage ); 1251 RefreshRect( aDstRect ); 1252 } 1253 1254 // ----------------------------------------------------------------------- 1255 1256 void AquaSalGraphics::drawMask( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap, SalColor nMaskColor ) 1257 { 1258 if( !CheckContext() ) 1259 return; 1260 1261 const AquaSalBitmap& rBitmap = static_cast<const AquaSalBitmap&>(rSalBitmap); 1262 CGImageRef xImage = rBitmap.CreateColorMask( pPosAry->mnSrcX, pPosAry->mnSrcY, pPosAry->mnSrcWidth, pPosAry->mnSrcHeight, nMaskColor ); 1263 if( !xImage ) 1264 return; 1265 1266 const CGRect aDstRect = {{pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight}}; 1267 CGContextDrawImage( mrContext, aDstRect, xImage ); 1268 CGImageRelease( xImage ); 1269 RefreshRect( aDstRect ); 1270 } 1271 1272 // ----------------------------------------------------------------------- 1273 1274 SalBitmap* AquaSalGraphics::getBitmap( long nX, long nY, long nDX, long nDY ) 1275 { 1276 DBG_ASSERT( mxLayer, "AquaSalGraphics::getBitmap() with no layer" ); 1277 1278 ApplyXorContext(); 1279 1280 AquaSalBitmap* pBitmap = new AquaSalBitmap; 1281 if( !pBitmap->Create( mxLayer, mnBitmapDepth, nX, nY, nDX, nDY, !mbWindow ) ) 1282 { 1283 delete pBitmap; 1284 pBitmap = NULL; 1285 } 1286 1287 return pBitmap; 1288 } 1289 1290 // ----------------------------------------------------------------------- 1291 1292 SalColor AquaSalGraphics::getPixel( long nX, long nY ) 1293 { 1294 // return default value on printers or when out of bounds 1295 if( !mxLayer 1296 || (nX < 0) || (nX >= mnWidth) 1297 || (nY < 0) || (nY >= mnHeight)) 1298 return COL_BLACK; 1299 1300 // prepare creation of matching a CGBitmapContext 1301 CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace; 1302 CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big; 1303 #if __BIG_ENDIAN__ 1304 struct{ unsigned char b, g, r, a; } aPixel; 1305 #else 1306 struct{ unsigned char a, r, g, b; } aPixel; 1307 #endif 1308 1309 // create a one-pixel bitmap context 1310 // TODO: is it worth to cache it? 1311 CGContextRef xOnePixelContext = ::CGBitmapContextCreate( &aPixel, 1312 1, 1, 8, sizeof(aPixel), aCGColorSpace, aCGBmpInfo ); 1313 1314 // update this graphics layer 1315 ApplyXorContext(); 1316 1317 // copy the requested pixel into the bitmap context 1318 if( IsFlipped() ) 1319 nY = mnHeight - nY; 1320 const CGPoint aCGPoint = {-nX, -nY}; 1321 CGContextDrawLayerAtPoint( xOnePixelContext, aCGPoint, mxLayer ); 1322 CGContextRelease( xOnePixelContext ); 1323 1324 SalColor nSalColor = MAKE_SALCOLOR( aPixel.r, aPixel.g, aPixel.b ); 1325 return nSalColor; 1326 } 1327 1328 // ----------------------------------------------------------------------- 1329 1330 1331 static void DrawPattern50( void*, CGContextRef rContext ) 1332 { 1333 static const CGRect aRects[2] = { { {0,0}, { 2, 2 } }, { { 2, 2 }, { 2, 2 } } }; 1334 CGContextAddRects( rContext, aRects, 2 ); 1335 CGContextFillPath( rContext ); 1336 } 1337 1338 void AquaSalGraphics::Pattern50Fill() 1339 { 1340 static const float aFillCol[4] = { 1,1,1,1 }; 1341 static const CGPatternCallbacks aCallback = { 0, &DrawPattern50, NULL }; 1342 if( ! GetSalData()->mxP50Space ) 1343 GetSalData()->mxP50Space = CGColorSpaceCreatePattern( GetSalData()->mxRGBSpace ); 1344 if( ! GetSalData()->mxP50Pattern ) 1345 GetSalData()->mxP50Pattern = CGPatternCreate( NULL, CGRectMake( 0, 0, 4, 4 ), 1346 CGAffineTransformIdentity, 4, 4, 1347 kCGPatternTilingConstantSpacing, 1348 false, &aCallback ); 1349 1350 CGContextSetFillColorSpace( mrContext, GetSalData()->mxP50Space ); 1351 CGContextSetFillPattern( mrContext, GetSalData()->mxP50Pattern, aFillCol ); 1352 CGContextFillPath( mrContext ); 1353 } 1354 1355 void AquaSalGraphics::invert( long nX, long nY, long nWidth, long nHeight, SalInvert nFlags ) 1356 { 1357 if ( CheckContext() ) 1358 { 1359 CGRect aCGRect = CGRectMake( nX, nY, nWidth, nHeight); 1360 CGContextSaveGState(mrContext); 1361 1362 if ( nFlags & SAL_INVERT_TRACKFRAME ) 1363 { 1364 const float dashLengths[2] = { 4.0, 4.0 }; // for drawing dashed line 1365 CGContextSetBlendMode( mrContext, kCGBlendModeDifference ); 1366 CGContextSetRGBStrokeColor ( mrContext, 1.0, 1.0, 1.0, 1.0 ); 1367 CGContextSetLineDash ( mrContext, 0, dashLengths, 2 ); 1368 CGContextSetLineWidth( mrContext, 2.0); 1369 CGContextStrokeRect ( mrContext, aCGRect ); 1370 } 1371 else if ( nFlags & SAL_INVERT_50 ) 1372 { 1373 //CGContextSetAllowsAntialiasing( mrContext, false ); 1374 CGContextSetBlendMode(mrContext, kCGBlendModeDifference); 1375 CGContextAddRect( mrContext, aCGRect ); 1376 Pattern50Fill(); 1377 } 1378 else // just invert 1379 { 1380 CGContextSetBlendMode(mrContext, kCGBlendModeDifference); 1381 CGContextSetRGBFillColor ( mrContext,1.0, 1.0, 1.0 , 1.0 ); 1382 CGContextFillRect ( mrContext, aCGRect ); 1383 } 1384 CGContextRestoreGState( mrContext); 1385 RefreshRect( aCGRect ); 1386 } 1387 } 1388 1389 // ----------------------------------------------------------------------- 1390 1391 void AquaSalGraphics::invert( sal_uLong nPoints, const SalPoint* pPtAry, SalInvert nSalFlags ) 1392 { 1393 CGPoint* CGpoints ; 1394 if ( CheckContext() ) 1395 { 1396 CGContextSaveGState(mrContext); 1397 CGpoints = makeCGptArray(nPoints,pPtAry); 1398 CGContextAddLines ( mrContext, CGpoints, nPoints ); 1399 if ( nSalFlags & SAL_INVERT_TRACKFRAME ) 1400 { 1401 const float dashLengths[2] = { 4.0, 4.0 }; // for drawing dashed line 1402 CGContextSetBlendMode( mrContext, kCGBlendModeDifference ); 1403 CGContextSetRGBStrokeColor ( mrContext, 1.0, 1.0, 1.0, 1.0 ); 1404 CGContextSetLineDash ( mrContext, 0, dashLengths, 2 ); 1405 CGContextSetLineWidth( mrContext, 2.0); 1406 CGContextStrokePath ( mrContext ); 1407 } 1408 else if ( nSalFlags & SAL_INVERT_50 ) 1409 { 1410 CGContextSetBlendMode(mrContext, kCGBlendModeDifference); 1411 Pattern50Fill(); 1412 } 1413 else // just invert 1414 { 1415 CGContextSetBlendMode( mrContext, kCGBlendModeDifference ); 1416 CGContextSetRGBFillColor( mrContext, 1.0, 1.0, 1.0, 1.0 ); 1417 CGContextFillPath( mrContext ); 1418 } 1419 const CGRect aRefreshRect = CGContextGetClipBoundingBox(mrContext); 1420 CGContextRestoreGState( mrContext); 1421 delete [] CGpoints; 1422 RefreshRect( aRefreshRect ); 1423 } 1424 } 1425 1426 // ----------------------------------------------------------------------- 1427 1428 sal_Bool AquaSalGraphics::drawEPS( long nX, long nY, long nWidth, long nHeight, 1429 void* pEpsData, sal_uLong nByteCount ) 1430 { 1431 // convert the raw data to an NSImageRef 1432 NSData* xNSData = [NSData dataWithBytes:(void*)pEpsData length:(int)nByteCount]; 1433 NSImageRep* xEpsImage = [NSEPSImageRep imageRepWithData: xNSData]; 1434 if( !xEpsImage ) 1435 return false; 1436 1437 // get the target context 1438 if( !CheckContext() ) 1439 return false; 1440 1441 // NOTE: flip drawing, else the nsimage would be drawn upside down 1442 CGContextSaveGState( mrContext ); 1443 // CGContextTranslateCTM( mrContext, 0, +mnHeight ); 1444 CGContextScaleCTM( mrContext, +1, -1 ); 1445 nY = /*mnHeight*/ - (nY + nHeight); 1446 1447 // prepare the target context 1448 NSGraphicsContext* pOrigNSCtx = [NSGraphicsContext currentContext]; 1449 [pOrigNSCtx retain]; 1450 1451 // create new context 1452 NSGraphicsContext* pDrawNSCtx = [NSGraphicsContext graphicsContextWithGraphicsPort: mrContext flipped: IsFlipped()]; 1453 // set it, setCurrentContext also releases the prviously set one 1454 [NSGraphicsContext setCurrentContext: pDrawNSCtx]; 1455 1456 // draw the EPS 1457 const NSRect aDstRect = {{nX,nY},{nWidth,nHeight}}; 1458 const BOOL bOK = [xEpsImage drawInRect: aDstRect]; 1459 1460 // restore the NSGraphicsContext 1461 [NSGraphicsContext setCurrentContext: pOrigNSCtx]; 1462 [pOrigNSCtx release]; // restore the original retain count 1463 1464 CGContextRestoreGState( mrContext ); 1465 // mark the destination rectangle as updated 1466 RefreshRect( aDstRect ); 1467 1468 return bOK; 1469 } 1470 1471 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 1472 bool AquaSalGraphics::drawAlphaBitmap( const SalTwoRect& rTR, 1473 const SalBitmap& rSrcBitmap, const SalBitmap& rAlphaBmp ) 1474 { 1475 // An image mask can't have a depth > 8 bits (should be 1 to 8 bits) 1476 if( rAlphaBmp.GetBitCount() > 8 ) 1477 return false; 1478 1479 // are these two tests really necessary? (see vcl/unx/source/gdi/salgdi2.cxx) 1480 // horizontal/vertical mirroring not implemented yet 1481 if( rTR.mnDestWidth < 0 || rTR.mnDestHeight < 0 ) 1482 return false; 1483 1484 const AquaSalBitmap& rSrcSalBmp = static_cast<const AquaSalBitmap&>(rSrcBitmap); 1485 const AquaSalBitmap& rMaskSalBmp = static_cast<const AquaSalBitmap&>(rAlphaBmp); 1486 1487 CGImageRef xMaskedImage = rSrcSalBmp.CreateWithMask( rMaskSalBmp, rTR.mnSrcX, rTR.mnSrcY, rTR.mnSrcWidth, rTR.mnSrcHeight ); 1488 if( !xMaskedImage ) 1489 return false; 1490 1491 if ( CheckContext() ) 1492 { 1493 const CGRect aDstRect = {{rTR.mnDestX, rTR.mnDestY}, {rTR.mnDestWidth, rTR.mnDestHeight}}; 1494 CGContextDrawImage( mrContext, aDstRect, xMaskedImage ); 1495 RefreshRect( aDstRect ); 1496 } 1497 1498 CGImageRelease(xMaskedImage); 1499 return true; 1500 } 1501 1502 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 1503 bool AquaSalGraphics::drawAlphaRect( long nX, long nY, long nWidth, 1504 long nHeight, sal_uInt8 nTransparency ) 1505 { 1506 if( !CheckContext() ) 1507 return true; 1508 1509 // save the current state 1510 CGContextSaveGState( mrContext ); 1511 CGContextSetAlpha( mrContext, (100-nTransparency) * (1.0/100) ); 1512 1513 CGRect aRect = {{nX,nY},{nWidth-1,nHeight-1}}; 1514 if( IsPenVisible() ) 1515 { 1516 aRect.origin.x += 0.5; 1517 aRect.origin.y += 0.5; 1518 } 1519 1520 CGContextBeginPath( mrContext ); 1521 CGContextAddRect( mrContext, aRect ); 1522 CGContextDrawPath( mrContext, kCGPathFill ); 1523 1524 // restore state 1525 CGContextRestoreGState(mrContext); 1526 RefreshRect( aRect ); 1527 return true; 1528 } 1529 1530 // ----------------------------------------------------------------------- 1531 1532 void AquaSalGraphics::SetTextColor( SalColor nSalColor ) 1533 { 1534 RGBColor color; 1535 color.red = (unsigned short) ( SALCOLOR_RED(nSalColor) * 65535.0 / 255.0 ); 1536 color.green = (unsigned short) ( SALCOLOR_GREEN(nSalColor) * 65535.0 / 255.0 ); 1537 color.blue = (unsigned short) ( SALCOLOR_BLUE(nSalColor) * 65535.0 / 255.0 ); 1538 1539 ATSUAttributeTag aTag = kATSUColorTag; 1540 ByteCount aValueSize = sizeof( color ); 1541 ATSUAttributeValuePtr aValue = &color; 1542 1543 OSStatus err = ATSUSetAttributes( maATSUStyle, 1, &aTag, &aValueSize, &aValue ); 1544 DBG_ASSERT( (err==noErr), "AquaSalGraphics::SetTextColor() : Could not set font attributes!\n"); 1545 if( err != noErr ) 1546 return; 1547 } 1548 1549 // ----------------------------------------------------------------------- 1550 1551 void AquaSalGraphics::GetFontMetric( ImplFontMetricData* pMetric, int nFallbackLevel ) 1552 { 1553 (void)nFallbackLevel; // glyph-fallback on ATSU is done differently -> no fallback level 1554 1555 // get the ATSU font metrics (in point units) 1556 // of the font that has eventually been size-limited 1557 1558 ATSUFontID fontId; 1559 OSStatus err = ATSUGetAttribute( maATSUStyle, kATSUFontTag, sizeof(ATSUFontID), &fontId, 0 ); 1560 DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font id\n"); 1561 1562 ATSFontMetrics aMetrics; 1563 ATSFontRef rFont = FMGetATSFontRefFromFont( fontId ); 1564 err = ATSFontGetHorizontalMetrics ( rFont, kATSOptionFlagsDefault, &aMetrics ); 1565 DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font metrics\n"); 1566 if( err != noErr ) 1567 return; 1568 1569 // all ATS fonts are scalable fonts 1570 pMetric->mbScalableFont = true; 1571 // TODO: check if any kerning is possible 1572 pMetric->mbKernableFont = true; 1573 1574 // convert into VCL font metrics (in unscaled pixel units) 1575 1576 Fixed ptSize; 1577 err = ATSUGetAttribute( maATSUStyle, kATSUSizeTag, sizeof(Fixed), &ptSize, 0); 1578 DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font size\n"); 1579 const double fPointSize = Fix2X( ptSize ); 1580 1581 // convert quartz units to pixel units 1582 // please see the comment in AquaSalGraphics::SetFont() for details 1583 const double fPixelSize = (mfFontScale * mfFakeDPIScale * fPointSize); 1584 pMetric->mnAscent = static_cast<long>(+aMetrics.ascent * fPixelSize + 0.5); 1585 pMetric->mnDescent = static_cast<long>(-aMetrics.descent * fPixelSize + 0.5); 1586 const long nExtDescent = static_cast<long>((-aMetrics.descent + aMetrics.leading) * fPixelSize + 0.5); 1587 pMetric->mnExtLeading = nExtDescent - pMetric->mnDescent; 1588 pMetric->mnIntLeading = 0; 1589 // ATSFontMetrics.avgAdvanceWidth is obsolete, so it is usually set to zero 1590 // since ImplFontMetricData::mnWidth is only used for stretching/squeezing fonts 1591 // setting this width to the pixel height of the fontsize is good enough 1592 // it also makes the calculation of the stretch factor simple 1593 pMetric->mnWidth = static_cast<long>(mfFontStretch * fPixelSize + 0.5); 1594 } 1595 1596 // ----------------------------------------------------------------------- 1597 1598 sal_uLong AquaSalGraphics::GetKernPairs( sal_uLong, ImplKernPairData* ) 1599 { 1600 return 0; 1601 } 1602 1603 // ----------------------------------------------------------------------- 1604 1605 static bool AddTempFontDir( const char* pDir ) 1606 { 1607 FSRef aPathFSRef; 1608 Boolean bIsDirectory = true; 1609 OSStatus eStatus = FSPathMakeRef( reinterpret_cast<const UInt8*>(pDir), &aPathFSRef, &bIsDirectory ); 1610 DBG_ASSERTWARNING( (eStatus==noErr) && bIsDirectory, "vcl AddTempFontDir() with invalid directory name!" ); 1611 if( eStatus != noErr ) 1612 return false; 1613 1614 // TODO: deactivate ATSFontContainerRef when closing app 1615 ATSFontContainerRef aATSFontContainer; 1616 1617 const ATSFontContext eContext = kATSFontContextLocal; // TODO: *Global??? 1618 #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) 1619 eStatus = ::ATSFontActivateFromFileReference( &aPathFSRef, 1620 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, 1621 &aATSFontContainer ); 1622 #else 1623 FSSpec aPathFSSpec; 1624 eStatus = ::FSGetCatalogInfo( &aPathFSRef, kFSCatInfoNone, 1625 NULL, NULL, &aPathFSSpec, NULL ); 1626 if( eStatus != noErr ) 1627 return false; 1628 1629 eStatus = ::ATSFontActivateFromFileSpecification( &aPathFSSpec, 1630 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, 1631 &aATSFontContainer ); 1632 #endif 1633 if( eStatus != noErr ) 1634 return false; 1635 1636 return true; 1637 } 1638 1639 static bool AddLocalTempFontDirs( void ) 1640 { 1641 static bool bFirst = true; 1642 if( !bFirst ) 1643 return false; 1644 bFirst = false; 1645 1646 // add private font files found in brand and base layer 1647 1648 rtl::OUString aBrandStr( RTL_CONSTASCII_USTRINGPARAM( "$BRAND_BASE_DIR" ) ); 1649 rtl_bootstrap_expandMacros( &aBrandStr.pData ); 1650 rtl::OUString aBrandSysPath; 1651 OSL_VERIFY( osl_getSystemPathFromFileURL( aBrandStr.pData, &aBrandSysPath.pData ) == osl_File_E_None ); 1652 1653 rtl::OStringBuffer aBrandFontDir( aBrandSysPath.getLength()*2 ); 1654 aBrandFontDir.append( rtl::OUStringToOString( aBrandSysPath, RTL_TEXTENCODING_UTF8 ) ); 1655 aBrandFontDir.append( "/share/fonts/truetype/" ); 1656 bool bBrandSuccess = AddTempFontDir( aBrandFontDir.getStr() ); 1657 1658 rtl::OUString aBaseStr( RTL_CONSTASCII_USTRINGPARAM( "$OOO_BASE_DIR" ) ); 1659 rtl_bootstrap_expandMacros( &aBaseStr.pData ); 1660 rtl::OUString aBaseSysPath; 1661 OSL_VERIFY( osl_getSystemPathFromFileURL( aBaseStr.pData, &aBaseSysPath.pData ) == osl_File_E_None ); 1662 1663 rtl::OStringBuffer aBaseFontDir( aBaseSysPath.getLength()*2 ); 1664 aBaseFontDir.append( rtl::OUStringToOString( aBaseSysPath, RTL_TEXTENCODING_UTF8 ) ); 1665 aBaseFontDir.append( "/share/fonts/truetype/" ); 1666 bool bBaseSuccess = AddTempFontDir( aBaseFontDir.getStr() ); 1667 1668 return bBrandSuccess && bBaseSuccess; 1669 } 1670 1671 void AquaSalGraphics::GetDevFontList( ImplDevFontList* pFontList ) 1672 { 1673 DBG_ASSERT( pFontList, "AquaSalGraphics::GetDevFontList(NULL) !"); 1674 1675 AddLocalTempFontDirs(); 1676 1677 // The idea is to cache the list of system fonts once it has been generated. 1678 // SalData seems to be a good place for this caching. However we have to 1679 // carefully make the access to the font list thread-safe. If we register 1680 // a font-change event handler to update the font list in case fonts have 1681 // changed on the system we have to lock access to the list. The right 1682 // way to do that is the solar mutex since GetDevFontList is protected 1683 // through it as should be all event handlers 1684 1685 SalData* pSalData = GetSalData(); 1686 if (pSalData->mpFontList == NULL) 1687 pSalData->mpFontList = new SystemFontList(); 1688 1689 // Copy all ImplFontData objects contained in the SystemFontList 1690 pSalData->mpFontList->AnnounceFonts( *pFontList ); 1691 } 1692 1693 // ----------------------------------------------------------------------- 1694 1695 bool AquaSalGraphics::AddTempDevFont( ImplDevFontList*, 1696 const String& rFontFileURL, const String& /*rFontName*/ ) 1697 { 1698 ::rtl::OUString aUSytemPath; 1699 OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSytemPath ) ); 1700 1701 FSRef aNewRef; 1702 Boolean bIsDirectory = true; 1703 ::rtl::OString aCFileName = rtl::OUStringToOString( aUSytemPath, RTL_TEXTENCODING_UTF8 ); 1704 OSStatus eStatus = FSPathMakeRef( (UInt8*)aCFileName.getStr(), &aNewRef, &bIsDirectory ); 1705 DBG_ASSERT( (eStatus==noErr) && !bIsDirectory, "vcl AddTempDevFont() with invalid fontfile name!" ); 1706 if( eStatus != noErr ) 1707 return false; 1708 1709 ATSFontContainerRef oContainer; 1710 1711 const ATSFontContext eContext = kATSFontContextLocal; // TODO: *Global??? 1712 #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) 1713 eStatus = ::ATSFontActivateFromFileReference( &aNewRef, 1714 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, 1715 &oContainer ); 1716 #else 1717 FSSpec aFontFSSpec; 1718 eStatus = ::FSGetCatalogInfo( &aNewRef, kFSCatInfoNone, 1719 NULL, NULL, &aFontFSSpec, NULL ); 1720 if( eStatus != noErr ) 1721 return false; 1722 1723 eStatus = ::ATSFontActivateFromFileSpecification( &aFontFSSpec, 1724 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, 1725 &oContainer ); 1726 #endif 1727 if( eStatus != noErr ) 1728 return false; 1729 1730 // TODO: ATSFontDeactivate( oContainer ) when fonts are no longer needed 1731 // TODO: register new ImplMacFontdata in pFontList 1732 return true; 1733 } 1734 1735 // ----------------------------------------------------------------------- 1736 1737 // callbacks from ATSUGlyphGetCubicPaths() fore GetGlyphOutline() 1738 struct GgoData { basegfx::B2DPolygon maPolygon; basegfx::B2DPolyPolygon* mpPolyPoly; }; 1739 1740 static OSStatus GgoLineToProc( const Float32Point* pPoint, void* pData ) 1741 { 1742 basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon; 1743 const basegfx::B2DPoint aB2DPoint( pPoint->x, pPoint->y ); 1744 rPolygon.append( aB2DPoint ); 1745 return noErr; 1746 } 1747 1748 static OSStatus GgoCurveToProc( const Float32Point* pCP1, const Float32Point* pCP2, 1749 const Float32Point* pPoint, void* pData ) 1750 { 1751 basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon; 1752 const sal_uInt32 nPointCount = rPolygon.count(); 1753 const basegfx::B2DPoint aB2DControlPoint1( pCP1->x, pCP1->y ); 1754 rPolygon.setNextControlPoint( nPointCount-1, aB2DControlPoint1 ); 1755 const basegfx::B2DPoint aB2DEndPoint( pPoint->x, pPoint->y ); 1756 rPolygon.append( aB2DEndPoint ); 1757 const basegfx::B2DPoint aB2DControlPoint2( pCP2->x, pCP2->y ); 1758 rPolygon.setPrevControlPoint( nPointCount, aB2DControlPoint2 ); 1759 return noErr; 1760 } 1761 1762 static OSStatus GgoClosePathProc( void* pData ) 1763 { 1764 GgoData* pGgoData = static_cast<GgoData*>(pData); 1765 basegfx::B2DPolygon& rPolygon = pGgoData->maPolygon; 1766 if( rPolygon.count() > 0 ) 1767 pGgoData->mpPolyPoly->append( rPolygon ); 1768 rPolygon.clear(); 1769 return noErr; 1770 } 1771 1772 static OSStatus GgoMoveToProc( const Float32Point* pPoint, void* pData ) 1773 { 1774 GgoClosePathProc( pData ); 1775 OSStatus eStatus = GgoLineToProc( pPoint, pData ); 1776 return eStatus; 1777 } 1778 1779 sal_Bool AquaSalGraphics::GetGlyphOutline( long nGlyphId, basegfx::B2DPolyPolygon& rPolyPoly ) 1780 { 1781 GgoData aGgoData; 1782 aGgoData.mpPolyPoly = &rPolyPoly; 1783 rPolyPoly.clear(); 1784 1785 ATSUStyle rATSUStyle = maATSUStyle; // TODO: handle glyph fallback when CWS pdffix02 is integrated 1786 OSStatus eGgoStatus = noErr; 1787 OSStatus eStatus = ATSUGlyphGetCubicPaths( rATSUStyle, nGlyphId, 1788 GgoMoveToProc, GgoLineToProc, GgoCurveToProc, GgoClosePathProc, 1789 &aGgoData, &eGgoStatus ); 1790 if( (eStatus != noErr) ) // TODO: why is (eGgoStatus!=noErr) when curves are involved? 1791 return false; 1792 1793 GgoClosePathProc( &aGgoData ); 1794 if( mfFontScale != 1.0 ) { 1795 rPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix(+mfFontScale, +mfFontScale)); 1796 } 1797 return true; 1798 } 1799 1800 // ----------------------------------------------------------------------- 1801 1802 long AquaSalGraphics::GetGraphicsWidth() const 1803 { 1804 long w = 0; 1805 if( mrContext && (mbWindow || mbVirDev) ) 1806 { 1807 w = mnWidth; 1808 } 1809 1810 if( w == 0 ) 1811 { 1812 if( mbWindow && mpFrame ) 1813 w = mpFrame->maGeometry.nWidth; 1814 } 1815 1816 return w; 1817 } 1818 1819 // ----------------------------------------------------------------------- 1820 1821 sal_Bool AquaSalGraphics::GetGlyphBoundRect( long nGlyphId, Rectangle& rRect ) 1822 { 1823 ATSUStyle rATSUStyle = maATSUStyle; // TODO: handle glyph fallback 1824 GlyphID aGlyphId = nGlyphId; 1825 ATSGlyphScreenMetrics aGlyphMetrics; 1826 OSStatus eStatus = ATSUGlyphGetScreenMetrics( rATSUStyle, 1827 1, &aGlyphId, 0, FALSE, !mbNonAntialiasedText, &aGlyphMetrics ); 1828 if( eStatus != noErr ) 1829 return false; 1830 1831 const long nMinX = (long)(+aGlyphMetrics.topLeft.x * mfFontScale - 0.5); 1832 const long nMaxX = (long)(aGlyphMetrics.width * mfFontScale + 0.5) + nMinX; 1833 const long nMinY = (long)(-aGlyphMetrics.topLeft.y * mfFontScale - 0.5); 1834 const long nMaxY = (long)(aGlyphMetrics.height * mfFontScale + 0.5) + nMinY; 1835 rRect = Rectangle( nMinX, nMinY, nMaxX, nMaxY ); 1836 return true; 1837 } 1838 1839 // ----------------------------------------------------------------------- 1840 1841 void AquaSalGraphics::GetDevFontSubstList( OutputDevice* ) 1842 { 1843 // nothing to do since there are no device-specific fonts on Aqua 1844 } 1845 1846 // ----------------------------------------------------------------------- 1847 1848 void AquaSalGraphics::DrawServerFontLayout( const ServerFontLayout& ) 1849 { 1850 } 1851 1852 // ----------------------------------------------------------------------- 1853 1854 sal_uInt16 AquaSalGraphics::SetFont( ImplFontSelectData* pReqFont, int /*nFallbackLevel*/ ) 1855 { 1856 if( !pReqFont ) 1857 { 1858 ATSUClearStyle( maATSUStyle ); 1859 mpMacFontData = NULL; 1860 return 0; 1861 } 1862 1863 // store the requested device font entry 1864 const ImplMacFontData* pMacFont = static_cast<const ImplMacFontData*>( pReqFont->mpFontData ); 1865 mpMacFontData = pMacFont; 1866 1867 // convert pixel units (as seen by upper layers) to typographic point units 1868 double fScaledAtsHeight = pReqFont->mfExactHeight; 1869 // avoid Fixed16.16 overflows by limiting the ATS font size 1870 static const float fMaxAtsHeight = 144.0; 1871 if( fScaledAtsHeight <= fMaxAtsHeight ) 1872 mfFontScale = 1.0; 1873 else 1874 { 1875 mfFontScale = fScaledAtsHeight / fMaxAtsHeight; 1876 fScaledAtsHeight = fMaxAtsHeight; 1877 } 1878 Fixed fFixedSize = FloatToFixed( fScaledAtsHeight ); 1879 // enable bold-emulation if needed 1880 Boolean bFakeBold = FALSE; 1881 if( (pReqFont->GetWeight() >= WEIGHT_BOLD) 1882 && (pMacFont->GetWeight() < WEIGHT_SEMIBOLD) ) 1883 bFakeBold = TRUE; 1884 // enable italic-emulation if needed 1885 Boolean bFakeItalic = FALSE; 1886 if( ((pReqFont->GetSlant() == ITALIC_NORMAL) || (pReqFont->GetSlant() == ITALIC_OBLIQUE)) 1887 && !((pMacFont->GetSlant() == ITALIC_NORMAL) || (pMacFont->GetSlant() == ITALIC_OBLIQUE)) ) 1888 bFakeItalic = TRUE; 1889 1890 // enable/disable antialiased text 1891 mbNonAntialiasedText = pReqFont->mbNonAntialiased; 1892 UInt32 nStyleRenderingOptions = kATSStyleNoOptions; 1893 if( pReqFont->mbNonAntialiased ) 1894 nStyleRenderingOptions |= kATSStyleNoAntiAliasing; 1895 1896 // set horizontal/vertical mode 1897 ATSUVerticalCharacterType aVerticalCharacterType = kATSUStronglyHorizontal; 1898 if( pReqFont->mbVertical ) 1899 aVerticalCharacterType = kATSUStronglyVertical; 1900 1901 // prepare ATS-fontid as type matching to the kATSUFontTag request 1902 ATSUFontID nFontID = static_cast<ATSUFontID>(pMacFont->GetFontId()); 1903 1904 // update ATSU style attributes with requested font parameters 1905 // TODO: no need to set styles which are already defaulted 1906 1907 const ATSUAttributeTag aTag[] = 1908 { 1909 kATSUFontTag, 1910 kATSUSizeTag, 1911 kATSUQDBoldfaceTag, 1912 kATSUQDItalicTag, 1913 kATSUStyleRenderingOptionsTag, 1914 kATSUVerticalCharacterTag 1915 }; 1916 1917 const ByteCount aValueSize[] = 1918 { 1919 sizeof(ATSUFontID), 1920 sizeof(fFixedSize), 1921 sizeof(bFakeBold), 1922 sizeof(bFakeItalic), 1923 sizeof(nStyleRenderingOptions), 1924 sizeof(aVerticalCharacterType) 1925 }; 1926 1927 const ATSUAttributeValuePtr aValue[] = 1928 { 1929 &nFontID, 1930 &fFixedSize, 1931 &bFakeBold, 1932 &bFakeItalic, 1933 &nStyleRenderingOptions, 1934 &aVerticalCharacterType 1935 }; 1936 1937 static const int nTagCount = sizeof(aTag) / sizeof(*aTag); 1938 OSStatus eStatus = ATSUSetAttributes( maATSUStyle, nTagCount, 1939 aTag, aValueSize, aValue ); 1940 // reset ATSUstyle if there was an error 1941 if( eStatus != noErr ) 1942 { 1943 DBG_WARNING( "AquaSalGraphics::SetFont() : Could not set font attributes!\n"); 1944 ATSUClearStyle( maATSUStyle ); 1945 mpMacFontData = NULL; 1946 return 0; 1947 } 1948 1949 // prepare font stretching 1950 const ATSUAttributeTag aMatrixTag = kATSUFontMatrixTag; 1951 if( (pReqFont->mnWidth == 0) || (pReqFont->mnWidth == pReqFont->mnHeight) ) 1952 { 1953 mfFontStretch = 1.0; 1954 ATSUClearAttributes( maATSUStyle, 1, &aMatrixTag ); 1955 } 1956 else 1957 { 1958 mfFontStretch = (float)pReqFont->mnWidth / pReqFont->mnHeight; 1959 CGAffineTransform aMatrix = CGAffineTransformMakeScale( mfFontStretch, 1.0F ); 1960 const ATSUAttributeValuePtr aAttr = &aMatrix; 1961 const ByteCount aMatrixBytes = sizeof(aMatrix); 1962 eStatus = ATSUSetAttributes( maATSUStyle, 1, &aMatrixTag, &aMatrixBytes, &aAttr ); 1963 DBG_ASSERT( (eStatus==noErr), "AquaSalGraphics::SetFont() : Could not set font matrix\n"); 1964 } 1965 1966 // prepare font rotation 1967 mnATSUIRotation = FloatToFixed( pReqFont->mnOrientation / 10.0 ); 1968 1969 #if OSL_DEBUG_LEVEL > 3 1970 fprintf( stderr, "SetFont to (\"%s\", \"%s\", fontid=%d) for (\"%s\" \"%s\" weight=%d, slant=%d size=%dx%d orientation=%d)\n", 1971 ::rtl::OUStringToOString( pMacFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(), 1972 ::rtl::OUStringToOString( pMacFont->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(), 1973 (int)nFontID, 1974 ::rtl::OUStringToOString( pReqFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(), 1975 ::rtl::OUStringToOString( pReqFont->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(), 1976 pReqFont->GetWeight(), 1977 pReqFont->GetSlant(), 1978 pReqFont->mnHeight, 1979 pReqFont->mnWidth, 1980 pReqFont->mnOrientation); 1981 #endif 1982 1983 return 0; 1984 } 1985 1986 // ----------------------------------------------------------------------- 1987 1988 const ImplFontCharMap* AquaSalGraphics::GetImplFontCharMap() const 1989 { 1990 if( !mpMacFontData ) 1991 return ImplFontCharMap::GetDefaultMap(); 1992 1993 return mpMacFontData->GetImplFontCharMap(); 1994 } 1995 1996 // ----------------------------------------------------------------------- 1997 1998 // fake a SFNT font directory entry for a font table 1999 // see http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html#Directory 2000 static void FakeDirEntry( FourCharCode eFCC, ByteCount nOfs, ByteCount nLen, 2001 const unsigned char* /*pData*/, unsigned char*& rpDest ) 2002 { 2003 // write entry tag 2004 rpDest[ 0] = (char)(eFCC >> 24); 2005 rpDest[ 1] = (char)(eFCC >> 16); 2006 rpDest[ 2] = (char)(eFCC >> 8); 2007 rpDest[ 3] = (char)(eFCC >> 0); 2008 // TODO: get entry checksum and write it 2009 // not too important since the subsetter doesn't care currently 2010 // for( pData+nOfs ... pData+nOfs+nLen ) 2011 // write entry offset 2012 rpDest[ 8] = (char)(nOfs >> 24); 2013 rpDest[ 9] = (char)(nOfs >> 16); 2014 rpDest[10] = (char)(nOfs >> 8); 2015 rpDest[11] = (char)(nOfs >> 0); 2016 // write entry length 2017 rpDest[12] = (char)(nLen >> 24); 2018 rpDest[13] = (char)(nLen >> 16); 2019 rpDest[14] = (char)(nLen >> 8); 2020 rpDest[15] = (char)(nLen >> 0); 2021 // advance to next entry 2022 rpDest += 16; 2023 } 2024 2025 static bool GetRawFontData( const ImplFontData* pFontData, 2026 ByteVector& rBuffer, bool* pJustCFF ) 2027 { 2028 const ImplMacFontData* pMacFont = static_cast<const ImplMacFontData*>(pFontData); 2029 const ATSUFontID nFontId = static_cast<ATSUFontID>(pMacFont->GetFontId()); 2030 ATSFontRef rFont = FMGetATSFontRefFromFont( nFontId ); 2031 2032 ByteCount nCffLen = 0; 2033 OSStatus eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, 0, NULL, &nCffLen); 2034 if( pJustCFF != NULL ) 2035 { 2036 *pJustCFF = (eStatus == noErr) && (nCffLen > 0); 2037 if( *pJustCFF ) 2038 { 2039 rBuffer.resize( nCffLen ); 2040 eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, nCffLen, (void*)&rBuffer[0], &nCffLen); 2041 if( (eStatus != noErr) || (nCffLen <= 0) ) 2042 return false; 2043 return true; 2044 } 2045 } 2046 2047 // get font table availability and size in bytes 2048 ByteCount nHeadLen = 0; 2049 eStatus = ATSFontGetTable( rFont, GetTag("head"), 0, 0, NULL, &nHeadLen); 2050 if( (eStatus != noErr) || (nHeadLen <= 0) ) 2051 return false; 2052 ByteCount nMaxpLen = 0; 2053 eStatus = ATSFontGetTable( rFont, GetTag("maxp"), 0, 0, NULL, &nMaxpLen); 2054 if( (eStatus != noErr) || (nMaxpLen <= 0) ) 2055 return false; 2056 ByteCount nCmapLen = 0; 2057 eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nCmapLen); 2058 if( (eStatus != noErr) || (nCmapLen <= 0) ) 2059 return false; 2060 ByteCount nNameLen = 0; 2061 eStatus = ATSFontGetTable( rFont, GetTag("name"), 0, 0, NULL, &nNameLen); 2062 if( (eStatus != noErr) || (nNameLen <= 0) ) 2063 return false; 2064 ByteCount nHheaLen = 0; 2065 eStatus = ATSFontGetTable( rFont, GetTag("hhea"), 0, 0, NULL, &nHheaLen); 2066 if( (eStatus != noErr) || (nHheaLen <= 0) ) 2067 return false; 2068 ByteCount nHmtxLen = 0; 2069 eStatus = ATSFontGetTable( rFont, GetTag("hmtx"), 0, 0, NULL, &nHmtxLen); 2070 if( (eStatus != noErr) || (nHmtxLen <= 0) ) 2071 return false; 2072 2073 // get the glyph outline tables 2074 ByteCount nLocaLen = 0; 2075 ByteCount nGlyfLen = 0; 2076 if( (eStatus != noErr) || (nCffLen <= 0) ) 2077 { 2078 eStatus = ATSFontGetTable( rFont, GetTag("loca"), 0, 0, NULL, &nLocaLen); 2079 if( (eStatus != noErr) || (nLocaLen <= 0) ) 2080 return false; 2081 eStatus = ATSFontGetTable( rFont, GetTag("glyf"), 0, 0, NULL, &nGlyfLen); 2082 if( (eStatus != noErr) || (nGlyfLen <= 0) ) 2083 return false; 2084 } 2085 2086 ByteCount nPrepLen=0, nCvtLen=0, nFpgmLen=0; 2087 if( nGlyfLen ) // TODO: reduce PDF size by making hint subsetting optional 2088 { 2089 eStatus = ATSFontGetTable( rFont, GetTag("prep"), 0, 0, NULL, &nPrepLen); 2090 eStatus = ATSFontGetTable( rFont, GetTag("cvt "), 0, 0, NULL, &nCvtLen); 2091 eStatus = ATSFontGetTable( rFont, GetTag("fpgm"), 0, 0, NULL, &nFpgmLen); 2092 } 2093 2094 // prepare a byte buffer for a fake font 2095 int nTableCount = 7; 2096 nTableCount += (nPrepLen>0) + (nCvtLen>0) + (nFpgmLen>0) + (nGlyfLen>0); 2097 const ByteCount nFdirLen = 12 + 16*nTableCount; 2098 ByteCount nTotalLen = nFdirLen; 2099 nTotalLen += nHeadLen + nMaxpLen + nNameLen + nCmapLen; 2100 if( nGlyfLen ) 2101 nTotalLen += nLocaLen + nGlyfLen; 2102 else 2103 nTotalLen += nCffLen; 2104 nTotalLen += nHheaLen + nHmtxLen; 2105 nTotalLen += nPrepLen + nCvtLen + nFpgmLen; 2106 rBuffer.resize( nTotalLen ); 2107 2108 // fake a SFNT font directory header 2109 if( nTableCount < 16 ) 2110 { 2111 int nLog2 = 0; 2112 while( (nTableCount >> nLog2) > 1 ) ++nLog2; 2113 rBuffer[ 1] = 1; // Win-TTF style scaler 2114 rBuffer[ 5] = nTableCount; // table count 2115 rBuffer[ 7] = nLog2*16; // searchRange 2116 rBuffer[ 9] = nLog2; // entrySelector 2117 rBuffer[11] = (nTableCount-nLog2)*16; // rangeShift 2118 } 2119 2120 // get font table raw data and update the fake directory entries 2121 ByteCount nOfs = nFdirLen; 2122 unsigned char* pFakeEntry = &rBuffer[12]; 2123 eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nCmapLen, (void*)&rBuffer[nOfs], &nCmapLen); 2124 FakeDirEntry( GetTag("cmap"), nOfs, nCmapLen, &rBuffer[0], pFakeEntry ); 2125 nOfs += nCmapLen; 2126 if( nCvtLen ) { 2127 eStatus = ATSFontGetTable( rFont, GetTag("cvt "), 0, nCvtLen, (void*)&rBuffer[nOfs], &nCvtLen); 2128 FakeDirEntry( GetTag("cvt "), nOfs, nCvtLen, &rBuffer[0], pFakeEntry ); 2129 nOfs += nCvtLen; 2130 } 2131 if( nFpgmLen ) { 2132 eStatus = ATSFontGetTable( rFont, GetTag("fpgm"), 0, nFpgmLen, (void*)&rBuffer[nOfs], &nFpgmLen); 2133 FakeDirEntry( GetTag("fpgm"), nOfs, nFpgmLen, &rBuffer[0], pFakeEntry ); 2134 nOfs += nFpgmLen; 2135 } 2136 if( nCffLen ) { 2137 eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, nCffLen, (void*)&rBuffer[nOfs], &nCffLen); 2138 FakeDirEntry( GetTag("CFF "), nOfs, nCffLen, &rBuffer[0], pFakeEntry ); 2139 nOfs += nGlyfLen; 2140 } else { 2141 eStatus = ATSFontGetTable( rFont, GetTag("glyf"), 0, nGlyfLen, (void*)&rBuffer[nOfs], &nGlyfLen); 2142 FakeDirEntry( GetTag("glyf"), nOfs, nGlyfLen, &rBuffer[0], pFakeEntry ); 2143 nOfs += nGlyfLen; 2144 eStatus = ATSFontGetTable( rFont, GetTag("loca"), 0, nLocaLen, (void*)&rBuffer[nOfs], &nLocaLen); 2145 FakeDirEntry( GetTag("loca"), nOfs, nLocaLen, &rBuffer[0], pFakeEntry ); 2146 nOfs += nLocaLen; 2147 } 2148 eStatus = ATSFontGetTable( rFont, GetTag("head"), 0, nHeadLen, (void*)&rBuffer[nOfs], &nHeadLen); 2149 FakeDirEntry( GetTag("head"), nOfs, nHeadLen, &rBuffer[0], pFakeEntry ); 2150 nOfs += nHeadLen; 2151 eStatus = ATSFontGetTable( rFont, GetTag("hhea"), 0, nHheaLen, (void*)&rBuffer[nOfs], &nHheaLen); 2152 FakeDirEntry( GetTag("hhea"), nOfs, nHheaLen, &rBuffer[0], pFakeEntry ); 2153 nOfs += nHheaLen; 2154 eStatus = ATSFontGetTable( rFont, GetTag("hmtx"), 0, nHmtxLen, (void*)&rBuffer[nOfs], &nHmtxLen); 2155 FakeDirEntry( GetTag("hmtx"), nOfs, nHmtxLen, &rBuffer[0], pFakeEntry ); 2156 nOfs += nHmtxLen; 2157 eStatus = ATSFontGetTable( rFont, GetTag("maxp"), 0, nMaxpLen, (void*)&rBuffer[nOfs], &nMaxpLen); 2158 FakeDirEntry( GetTag("maxp"), nOfs, nMaxpLen, &rBuffer[0], pFakeEntry ); 2159 nOfs += nMaxpLen; 2160 eStatus = ATSFontGetTable( rFont, GetTag("name"), 0, nNameLen, (void*)&rBuffer[nOfs], &nNameLen); 2161 FakeDirEntry( GetTag("name"), nOfs, nNameLen, &rBuffer[0], pFakeEntry ); 2162 nOfs += nNameLen; 2163 if( nPrepLen ) { 2164 eStatus = ATSFontGetTable( rFont, GetTag("prep"), 0, nPrepLen, (void*)&rBuffer[nOfs], &nPrepLen); 2165 FakeDirEntry( GetTag("prep"), nOfs, nPrepLen, &rBuffer[0], pFakeEntry ); 2166 nOfs += nPrepLen; 2167 } 2168 2169 DBG_ASSERT( (nOfs==nTotalLen), "AquaSalGraphics::CreateFontSubset (nOfs!=nTotalLen)"); 2170 2171 return sal_True; 2172 } 2173 2174 sal_Bool AquaSalGraphics::CreateFontSubset( const rtl::OUString& rToFile, 2175 const ImplFontData* pFontData, long* pGlyphIDs, sal_uInt8* pEncoding, 2176 sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo ) 2177 { 2178 // TODO: move more of the functionality here into the generic subsetter code 2179 2180 // prepare the requested file name for writing the font-subset file 2181 rtl::OUString aSysPath; 2182 if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) ) 2183 return sal_False; 2184 const rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding(); 2185 const ByteString aToFile( rtl::OUStringToOString( aSysPath, aThreadEncoding ) ); 2186 2187 // get the raw-bytes from the font to be subset 2188 ByteVector aBuffer; 2189 bool bCffOnly = false; 2190 if( !GetRawFontData( pFontData, aBuffer, &bCffOnly ) ) 2191 return sal_False; 2192 2193 // handle CFF-subsetting 2194 if( bCffOnly ) 2195 { 2196 // provide the raw-CFF data to the subsetter 2197 ByteCount nCffLen = aBuffer.size(); 2198 rInfo.LoadFont( FontSubsetInfo::CFF_FONT, &aBuffer[0], nCffLen ); 2199 2200 // NOTE: assuming that all glyphids requested on Aqua are fully translated 2201 2202 // make the subsetter provide the requested subset 2203 FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" ); 2204 bool bRC = rInfo.CreateFontSubset( FontSubsetInfo::TYPE1_PFB, pOutFile, NULL, 2205 pGlyphIDs, pEncoding, nGlyphCount, pGlyphWidths ); 2206 fclose( pOutFile ); 2207 return bRC; 2208 } 2209 2210 // TODO: modernize psprint's horrible fontsubset C-API 2211 // this probably only makes sense after the switch to another SCM 2212 // that can preserve change history after file renames 2213 2214 // prepare data for psprint's font subsetter 2215 TrueTypeFont* pSftFont = NULL; 2216 int nRC = ::OpenTTFontBuffer( (void*)&aBuffer[0], aBuffer.size(), 0, &pSftFont); 2217 if( nRC != SF_OK ) 2218 return sal_False; 2219 2220 // get details about the subsetted font 2221 TTGlobalFontInfo aTTInfo; 2222 ::GetTTGlobalFontInfo( pSftFont, &aTTInfo ); 2223 rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF; 2224 rInfo.m_aPSName = String( aTTInfo.psname, RTL_TEXTENCODING_UTF8 ); 2225 rInfo.m_aFontBBox = Rectangle( Point( aTTInfo.xMin, aTTInfo.yMin ), 2226 Point( aTTInfo.xMax, aTTInfo.yMax ) ); 2227 rInfo.m_nCapHeight = aTTInfo.yMax; // Well ... 2228 rInfo.m_nAscent = aTTInfo.winAscent; 2229 rInfo.m_nDescent = aTTInfo.winDescent; 2230 // mac fonts usually do not have an OS2-table 2231 // => get valid ascent/descent values from other tables 2232 if( !rInfo.m_nAscent ) 2233 rInfo.m_nAscent = +aTTInfo.typoAscender; 2234 if( !rInfo.m_nAscent ) 2235 rInfo.m_nAscent = +aTTInfo.ascender; 2236 if( !rInfo.m_nDescent ) 2237 rInfo.m_nDescent = +aTTInfo.typoDescender; 2238 if( !rInfo.m_nDescent ) 2239 rInfo.m_nDescent = -aTTInfo.descender; 2240 2241 // subset glyphs and get their properties 2242 // take care that subset fonts require the NotDef glyph in pos 0 2243 int nOrigCount = nGlyphCount; 2244 sal_uInt16 aShortIDs[ 256 ]; 2245 sal_uInt8 aTempEncs[ 256 ]; 2246 2247 int nNotDef = -1; 2248 for( int i = 0; i < nGlyphCount; ++i ) 2249 { 2250 aTempEncs[i] = pEncoding[i]; 2251 sal_uInt32 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK; 2252 if( pGlyphIDs[i] & GF_ISCHAR ) 2253 { 2254 bool bVertical = (pGlyphIDs[i] & GF_ROTMASK) != 0; 2255 nGlyphIdx = ::MapChar( pSftFont, static_cast<sal_uInt16>(nGlyphIdx), bVertical ); 2256 if( nGlyphIdx == 0 && pFontData->IsSymbolFont() ) 2257 { 2258 // #i12824# emulate symbol aliasing U+FXXX <-> U+0XXX 2259 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK; 2260 nGlyphIdx = (nGlyphIdx & 0xF000) ? (nGlyphIdx & 0x00FF) : (nGlyphIdx | 0xF000 ); 2261 nGlyphIdx = ::MapChar( pSftFont, static_cast<sal_uInt16>(nGlyphIdx), bVertical ); 2262 } 2263 } 2264 aShortIDs[i] = static_cast<sal_uInt16>( nGlyphIdx ); 2265 if( !nGlyphIdx ) 2266 if( nNotDef < 0 ) 2267 nNotDef = i; // first NotDef glyph found 2268 } 2269 2270 if( nNotDef != 0 ) 2271 { 2272 // add fake NotDef glyph if needed 2273 if( nNotDef < 0 ) 2274 nNotDef = nGlyphCount++; 2275 2276 // NotDef glyph must be in pos 0 => swap glyphids 2277 aShortIDs[ nNotDef ] = aShortIDs[0]; 2278 aTempEncs[ nNotDef ] = aTempEncs[0]; 2279 aShortIDs[0] = 0; 2280 aTempEncs[0] = 0; 2281 } 2282 DBG_ASSERT( nGlyphCount < 257, "too many glyphs for subsetting" ); 2283 2284 // TODO: where to get bVertical? 2285 const bool bVertical = false; 2286 2287 // fill the pGlyphWidths array 2288 // while making sure that the NotDef glyph is at index==0 2289 TTSimpleGlyphMetrics* pGlyphMetrics = 2290 ::GetTTSimpleGlyphMetrics( pSftFont, aShortIDs, nGlyphCount, bVertical ); 2291 if( !pGlyphMetrics ) 2292 return sal_False; 2293 sal_uInt16 nNotDefAdv = pGlyphMetrics[0].adv; 2294 pGlyphMetrics[0].adv = pGlyphMetrics[nNotDef].adv; 2295 pGlyphMetrics[nNotDef].adv = nNotDefAdv; 2296 for( int i = 0; i < nOrigCount; ++i ) 2297 pGlyphWidths[i] = pGlyphMetrics[i].adv; 2298 free( pGlyphMetrics ); 2299 2300 // write subset into destination file 2301 nRC = ::CreateTTFromTTGlyphs( pSftFont, aToFile.GetBuffer(), aShortIDs, 2302 aTempEncs, nGlyphCount, 0, NULL, 0 ); 2303 ::CloseTTFont(pSftFont); 2304 return (nRC == SF_OK); 2305 } 2306 2307 // ----------------------------------------------------------------------- 2308 2309 void AquaSalGraphics::GetGlyphWidths( const ImplFontData* pFontData, bool bVertical, 2310 Int32Vector& rGlyphWidths, Ucs2UIntMap& rUnicodeEnc ) 2311 { 2312 rGlyphWidths.clear(); 2313 rUnicodeEnc.clear(); 2314 2315 if( pFontData->IsSubsettable() ) 2316 { 2317 ByteVector aBuffer; 2318 if( !GetRawFontData( pFontData, aBuffer, NULL ) ) 2319 return; 2320 2321 // TODO: modernize psprint's horrible fontsubset C-API 2322 // this probably only makes sense after the switch to another SCM 2323 // that can preserve change history after file renames 2324 2325 // use the font subsetter to get the widths 2326 TrueTypeFont* pSftFont = NULL; 2327 int nRC = ::OpenTTFontBuffer( (void*)&aBuffer[0], aBuffer.size(), 0, &pSftFont); 2328 if( nRC != SF_OK ) 2329 return; 2330 2331 const int nGlyphCount = ::GetTTGlyphCount( pSftFont ); 2332 if( nGlyphCount > 0 ) 2333 { 2334 // get glyph metrics 2335 rGlyphWidths.resize(nGlyphCount); 2336 std::vector<sal_uInt16> aGlyphIds(nGlyphCount); 2337 for( int i = 0; i < nGlyphCount; i++ ) 2338 aGlyphIds[i] = static_cast<sal_uInt16>(i); 2339 const TTSimpleGlyphMetrics* pGlyphMetrics = ::GetTTSimpleGlyphMetrics( 2340 pSftFont, &aGlyphIds[0], nGlyphCount, bVertical ); 2341 if( pGlyphMetrics ) 2342 { 2343 for( int i = 0; i < nGlyphCount; ++i ) 2344 rGlyphWidths[i] = pGlyphMetrics[i].adv; 2345 free( (void*)pGlyphMetrics ); 2346 } 2347 2348 const ImplFontCharMap* pMap = mpMacFontData->GetImplFontCharMap(); 2349 DBG_ASSERT( pMap && pMap->GetCharCount(), "no charmap" ); 2350 pMap->AddReference(); // TODO: add and use RAII object instead 2351 2352 // get unicode<->glyph encoding 2353 // TODO? avoid sft mapping by using the pMap itself 2354 int nCharCount = pMap->GetCharCount(); 2355 sal_uInt32 nChar = pMap->GetFirstChar(); 2356 for(; --nCharCount >= 0; nChar = pMap->GetNextChar( nChar ) ) 2357 { 2358 if( nChar > 0xFFFF ) // TODO: allow UTF-32 chars 2359 break; 2360 sal_Ucs nUcsChar = static_cast<sal_Ucs>(nChar); 2361 sal_uInt32 nGlyph = ::MapChar( pSftFont, nUcsChar, bVertical ); 2362 if( nGlyph > 0 ) 2363 rUnicodeEnc[ nUcsChar ] = nGlyph; 2364 } 2365 2366 pMap->DeReference(); // TODO: add and use RAII object instead 2367 } 2368 2369 ::CloseTTFont( pSftFont ); 2370 } 2371 else if( pFontData->IsEmbeddable() ) 2372 { 2373 // get individual character widths 2374 #if 0 // FIXME 2375 rWidths.reserve( 224 ); 2376 for( sal_Unicode i = 32; i < 256; ++i ) 2377 { 2378 int nCharWidth = 0; 2379 if( ::GetCharWidth32W( mhDC, i, i, &nCharWidth ) ) 2380 { 2381 rUnicodeEnc[ i ] = rWidths.size(); 2382 rWidths.push_back( nCharWidth ); 2383 } 2384 } 2385 #else 2386 DBG_ERROR("not implemented for non-subsettable fonts!\n"); 2387 #endif 2388 } 2389 } 2390 2391 // ----------------------------------------------------------------------- 2392 2393 const Ucs2SIntMap* AquaSalGraphics::GetFontEncodingVector( 2394 const ImplFontData*, const Ucs2OStrMap** /*ppNonEncoded*/ ) 2395 { 2396 return NULL; 2397 } 2398 2399 // ----------------------------------------------------------------------- 2400 2401 const void* AquaSalGraphics::GetEmbedFontData( const ImplFontData*, 2402 const sal_Ucs* /*pUnicodes*/, 2403 sal_Int32* /*pWidths*/, 2404 FontSubsetInfo&, 2405 long* /*pDataLen*/ ) 2406 { 2407 return NULL; 2408 } 2409 2410 // ----------------------------------------------------------------------- 2411 2412 void AquaSalGraphics::FreeEmbedFontData( const void* pData, long /*nDataLen*/ ) 2413 { 2414 // TODO: implementing this only makes sense when the implementation of 2415 // AquaSalGraphics::GetEmbedFontData() returns non-NULL 2416 (void)pData; 2417 DBG_ASSERT( (pData!=NULL), "AquaSalGraphics::FreeEmbedFontData() is not implemented\n"); 2418 } 2419 2420 // ----------------------------------------------------------------------- 2421 2422 SystemFontData AquaSalGraphics::GetSysFontData( int /* nFallbacklevel */ ) const 2423 { 2424 SystemFontData aSysFontData; 2425 OSStatus err; 2426 aSysFontData.nSize = sizeof( SystemFontData ); 2427 2428 // NOTE: Native ATSU font fallbacks are used, not the VCL fallbacks. 2429 ATSUFontID fontId; 2430 err = ATSUGetAttribute( maATSUStyle, kATSUFontTag, sizeof(fontId), &fontId, 0 ); 2431 if (err) fontId = 0; 2432 aSysFontData.aATSUFontID = (void *) fontId; 2433 2434 Boolean bFbold; 2435 err = ATSUGetAttribute( maATSUStyle, kATSUQDBoldfaceTag, sizeof(bFbold), &bFbold, 0 ); 2436 if (err) bFbold = FALSE; 2437 aSysFontData.bFakeBold = (bool) bFbold; 2438 2439 Boolean bFItalic; 2440 err = ATSUGetAttribute( maATSUStyle, kATSUQDItalicTag, sizeof(bFItalic), &bFItalic, 0 ); 2441 if (err) bFItalic = FALSE; 2442 aSysFontData.bFakeItalic = (bool) bFItalic; 2443 2444 ATSUVerticalCharacterType aVerticalCharacterType; 2445 err = ATSUGetAttribute( maATSUStyle, kATSUVerticalCharacterTag, sizeof(aVerticalCharacterType), &aVerticalCharacterType, 0 ); 2446 if (!err && aVerticalCharacterType == kATSUStronglyVertical) { 2447 aSysFontData.bVerticalCharacterType = true; 2448 } else { 2449 aSysFontData.bVerticalCharacterType = false; 2450 } 2451 2452 aSysFontData.bAntialias = !mbNonAntialiasedText; 2453 2454 return aSysFontData; 2455 } 2456 2457 // ----------------------------------------------------------------------- 2458 2459 SystemGraphicsData AquaSalGraphics::GetGraphicsData() const 2460 { 2461 SystemGraphicsData aRes; 2462 aRes.nSize = sizeof(aRes); 2463 aRes.rCGContext = mrContext; 2464 return aRes; 2465 } 2466 2467 // ----------------------------------------------------------------------- 2468 2469 void AquaSalGraphics::SetXORMode( bool bSet, bool bInvertOnly ) 2470 { 2471 // return early if XOR mode remains unchanged 2472 if( mbPrinter ) 2473 return; 2474 2475 if( ! bSet && mnXorMode == 2 ) 2476 { 2477 CGContextSetBlendMode( mrContext, kCGBlendModeNormal ); 2478 mnXorMode = 0; 2479 return; 2480 } 2481 else if( bSet && bInvertOnly && mnXorMode == 0) 2482 { 2483 CGContextSetBlendMode( mrContext, kCGBlendModeDifference ); 2484 mnXorMode = 2; 2485 return; 2486 } 2487 2488 if( (mpXorEmulation == NULL) && !bSet ) 2489 return; 2490 if( (mpXorEmulation != NULL) && (bSet == mpXorEmulation->IsEnabled()) ) 2491 return; 2492 if( !CheckContext() ) 2493 return; 2494 2495 // prepare XOR emulation 2496 if( !mpXorEmulation ) 2497 { 2498 mpXorEmulation = new XorEmulation(); 2499 mpXorEmulation->SetTarget( mnWidth, mnHeight, mnBitmapDepth, mrContext, mxLayer ); 2500 } 2501 2502 // change the XOR mode 2503 if( bSet ) 2504 { 2505 mpXorEmulation->Enable(); 2506 mrContext = mpXorEmulation->GetMaskContext(); 2507 mnXorMode = 1; 2508 } 2509 else 2510 { 2511 mpXorEmulation->UpdateTarget(); 2512 mpXorEmulation->Disable(); 2513 mrContext = mpXorEmulation->GetTargetContext(); 2514 mnXorMode = 0; 2515 } 2516 } 2517 2518 // ----------------------------------------------------------------------- 2519 2520 // apply the XOR mask to the target context if active and dirty 2521 void AquaSalGraphics::ApplyXorContext() 2522 { 2523 if( !mpXorEmulation ) 2524 return; 2525 if( mpXorEmulation->UpdateTarget() ) 2526 RefreshRect( 0, 0, mnWidth, mnHeight ); // TODO: refresh minimal changerect 2527 } 2528 2529 // ====================================================================== 2530 2531 XorEmulation::XorEmulation() 2532 : mxTargetLayer( NULL ) 2533 , mxTargetContext( NULL ) 2534 , mxMaskContext( NULL ) 2535 , mxTempContext( NULL ) 2536 , mpMaskBuffer( NULL ) 2537 , mpTempBuffer( NULL ) 2538 , mnBufferLongs( 0 ) 2539 , mbIsEnabled( false ) 2540 {} 2541 2542 // ---------------------------------------------------------------------- 2543 2544 XorEmulation::~XorEmulation() 2545 { 2546 Disable(); 2547 SetTarget( 0, 0, 0, NULL, NULL ); 2548 } 2549 2550 // ----------------------------------------------------------------------- 2551 2552 void XorEmulation::SetTarget( int nWidth, int nHeight, int nTargetDepth, 2553 CGContextRef xTargetContext, CGLayerRef xTargetLayer ) 2554 { 2555 // prepare to replace old mask+temp context 2556 if( mxMaskContext ) 2557 { 2558 // cleanup the mask context 2559 CGContextRelease( mxMaskContext ); 2560 delete[] mpMaskBuffer; 2561 mxMaskContext = NULL; 2562 mpMaskBuffer = NULL; 2563 2564 // cleanup the temp context if needed 2565 if( mxTempContext ) 2566 { 2567 CGContextRelease( mxTempContext ); 2568 delete[] mpTempBuffer; 2569 mxTempContext = NULL; 2570 mpTempBuffer = NULL; 2571 } 2572 } 2573 2574 // return early if there is nothing more to do 2575 if( !xTargetContext ) 2576 return; 2577 2578 // retarget drawing operations to the XOR mask 2579 mxTargetLayer = xTargetLayer; 2580 mxTargetContext = xTargetContext; 2581 2582 // prepare creation of matching CGBitmaps 2583 CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace; 2584 CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst; 2585 int nBitDepth = nTargetDepth; 2586 if( !nBitDepth ) 2587 nBitDepth = 32; 2588 int nBytesPerRow = (nBitDepth == 16) ? 2 : 4; 2589 const size_t nBitsPerComponent = (nBitDepth == 16) ? 5 : 8; 2590 if( nBitDepth <= 8 ) 2591 { 2592 aCGColorSpace = GetSalData()->mxGraySpace; 2593 aCGBmpInfo = kCGImageAlphaNone; 2594 nBytesPerRow = 1; 2595 } 2596 nBytesPerRow *= nWidth; 2597 mnBufferLongs = (nHeight * nBytesPerRow + sizeof(sal_uLong)-1) / sizeof(sal_uLong); 2598 2599 // create a XorMask context 2600 mpMaskBuffer = new sal_uLong[ mnBufferLongs ]; 2601 mxMaskContext = ::CGBitmapContextCreate( mpMaskBuffer, 2602 nWidth, nHeight, nBitsPerComponent, nBytesPerRow, 2603 aCGColorSpace, aCGBmpInfo ); 2604 // reset the XOR mask to black 2605 memset( mpMaskBuffer, 0, mnBufferLongs * sizeof(sal_uLong) ); 2606 2607 // a bitmap context will be needed for manual XORing 2608 // create one unless the target context is a bitmap context 2609 if( nTargetDepth ) 2610 mpTempBuffer = (sal_uLong*)CGBitmapContextGetData( mxTargetContext ); 2611 if( !mpTempBuffer ) 2612 { 2613 // create a bitmap context matching to the target context 2614 mpTempBuffer = new sal_uLong[ mnBufferLongs ]; 2615 mxTempContext = ::CGBitmapContextCreate( mpTempBuffer, 2616 nWidth, nHeight, nBitsPerComponent, nBytesPerRow, 2617 aCGColorSpace, aCGBmpInfo ); 2618 } 2619 2620 // initialize XOR mask context for drawing 2621 CGContextSetFillColorSpace( mxMaskContext, aCGColorSpace ); 2622 CGContextSetStrokeColorSpace( mxMaskContext, aCGColorSpace ); 2623 CGContextSetShouldAntialias( mxMaskContext, false ); 2624 2625 // improve the XorMask's XOR emulation a litte 2626 // NOTE: currently only enabled for monochrome contexts 2627 if( aCGColorSpace == GetSalData()->mxGraySpace ) 2628 CGContextSetBlendMode( mxMaskContext, kCGBlendModeDifference ); 2629 2630 // intialize the transformation matrix to the drawing target 2631 const CGAffineTransform aCTM = CGContextGetCTM( xTargetContext ); 2632 CGContextConcatCTM( mxMaskContext, aCTM ); 2633 if( mxTempContext ) 2634 CGContextConcatCTM( mxTempContext, aCTM ); 2635 2636 // initialize the default XorMask graphics state 2637 CGContextSaveGState( mxMaskContext ); 2638 } 2639 2640 // ---------------------------------------------------------------------- 2641 2642 bool XorEmulation::UpdateTarget() 2643 { 2644 if( !IsEnabled() ) 2645 return false; 2646 2647 // update the temp bitmap buffer if needed 2648 if( mxTempContext ) 2649 CGContextDrawLayerAtPoint( mxTempContext, CGPointZero, mxTargetLayer ); 2650 2651 // do a manual XOR with the XorMask 2652 // this approach suffices for simple color manipulations 2653 // and also the complex-clipping-XOR-trick used in metafiles 2654 const sal_uLong* pSrc = mpMaskBuffer; 2655 sal_uLong* pDst = mpTempBuffer; 2656 for( int i = mnBufferLongs; --i >= 0;) 2657 *(pDst++) ^= *(pSrc++); 2658 2659 // write back the XOR results to the target context 2660 if( mxTempContext ) 2661 { 2662 CGImageRef xXorImage = CGBitmapContextCreateImage( mxTempContext ); 2663 const int nWidth = (int)CGImageGetWidth( xXorImage ); 2664 const int nHeight = (int)CGImageGetHeight( xXorImage ); 2665 // TODO: update minimal changerect 2666 const CGRect aFullRect = {{0,0},{nWidth,nHeight}}; 2667 CGContextDrawImage( mxTargetContext, aFullRect, xXorImage ); 2668 CGImageRelease( xXorImage ); 2669 } 2670 2671 // reset the XorMask to black again 2672 // TODO: not needed for last update 2673 memset( mpMaskBuffer, 0, mnBufferLongs * sizeof(sal_uLong) ); 2674 2675 // TODO: return FALSE if target was not changed 2676 return true; 2677 } 2678 2679 // ======================================================================= 2680 2681