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