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