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