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