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