xref: /aoo42x/main/vcl/aqua/source/gdi/salgdi.cxx (revision 9f62ea84)
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( const ::basegfx::B2DPolygon& rPolyLine,
984 	double fTransparency,
985 	const ::basegfx::B2DVector& rLineWidths,
986 	basegfx::B2DLineJoin eLineJoin )
987 {
988 	// short circuit if there is nothing to do
989 	const int nPointCount = rPolyLine.count();
990 	if( nPointCount <= 0 )
991 		return true;
992 
993 	// reject requests that cannot be handled yet
994 	if( rLineWidths.getX() != rLineWidths.getY() )
995 		return false;
996 
997 	// #i101491# Aqua does not support B2DLINEJOIN_NONE; return false to use
998 	// the fallback (own geometry preparation)
999 	// #i104886# linejoin-mode and thus the above only applies to "fat" lines
1000 	if( (basegfx::B2DLINEJOIN_NONE == eLineJoin)
1001 	&& (rLineWidths.getX() > 1.3) )
1002 		return false;
1003 
1004 	// setup line attributes
1005 	CGLineJoin aCGLineJoin = kCGLineJoinMiter;
1006 	switch( eLineJoin ) {
1007 		case ::basegfx::B2DLINEJOIN_NONE:		aCGLineJoin = /*TODO?*/kCGLineJoinMiter; break;
1008 		case ::basegfx::B2DLINEJOIN_MIDDLE:		aCGLineJoin = /*TODO?*/kCGLineJoinMiter; break;
1009 		case ::basegfx::B2DLINEJOIN_BEVEL:		aCGLineJoin = kCGLineJoinBevel; break;
1010 		case ::basegfx::B2DLINEJOIN_MITER:		aCGLineJoin = kCGLineJoinMiter; break;
1011 		case ::basegfx::B2DLINEJOIN_ROUND:		aCGLineJoin = kCGLineJoinRound; break;
1012 	}
1013 
1014 	// setup poly-polygon path
1015 	CGMutablePathRef xPath = CGPathCreateMutable();
1016 	AddPolygonToPath( xPath, rPolyLine, rPolyLine.isClosed(), !getAntiAliasB2DDraw(), true );
1017 
1018 	const CGRect aRefreshRect = CGPathGetBoundingBox( xPath );
1019 #ifndef NO_I97317_WORKAROUND
1020 	// #i97317# workaround for Quartz having problems with drawing small polygons
1021 	if( ! ((aRefreshRect.size.width <= 0.125) && (aRefreshRect.size.height <= 0.125)) )
1022 #endif
1023     {
1024         // use the path to prepare the graphics context
1025         CGContextSaveGState( mrContext );
1026         CGContextAddPath( mrContext, xPath );
1027         // draw path with antialiased line
1028         CGContextSetShouldAntialias( mrContext, true );
1029         CGContextSetAlpha( mrContext, 1.0 - fTransparency );
1030         CGContextSetLineJoin( mrContext, aCGLineJoin );
1031         CGContextSetLineWidth( mrContext, rLineWidths.getX() );
1032         CGContextDrawPath( mrContext, kCGPathStroke );
1033         CGContextRestoreGState( mrContext );
1034 
1035         // mark modified rectangle as updated
1036         RefreshRect( aRefreshRect );
1037     }
1038 
1039 	CGPathRelease( xPath );
1040 
1041 	return true;
1042 }
1043 
1044 // -----------------------------------------------------------------------
1045 
1046 sal_Bool AquaSalGraphics::drawPolyLineBezier( sal_uLong, const SalPoint*, const sal_uInt8* )
1047 {
1048     return sal_False;
1049 }
1050 
1051 // -----------------------------------------------------------------------
1052 
1053 sal_Bool AquaSalGraphics::drawPolygonBezier( sal_uLong, const SalPoint*, const sal_uInt8* )
1054 {
1055     return sal_False;
1056 }
1057 
1058 // -----------------------------------------------------------------------
1059 
1060 sal_Bool AquaSalGraphics::drawPolyPolygonBezier( sal_uLong, const sal_uLong*,
1061                                              const SalPoint* const*, const sal_uInt8* const* )
1062 {
1063     return sal_False;
1064 }
1065 
1066 // -----------------------------------------------------------------------
1067 
1068 void AquaSalGraphics::copyBits( const SalTwoRect *pPosAry, SalGraphics *pSrcGraphics )
1069 {
1070 	if( !pSrcGraphics )
1071 		pSrcGraphics = this;
1072 
1073     //from unix salgdi2.cxx
1074     //[FIXME] find a better way to prevent calc from crashing when width and height are negative
1075     if( pPosAry->mnSrcWidth <= 0
1076         || pPosAry->mnSrcHeight <= 0
1077         || pPosAry->mnDestWidth <= 0
1078         || pPosAry->mnDestHeight <= 0 )
1079     {
1080         return;
1081     }
1082 
1083 	// accelerate trivial operations
1084 	/*const*/ AquaSalGraphics* pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics);
1085 	const bool bSameGraphics = (this == pSrc) || (mbWindow && mpFrame && pSrc->mbWindow && (mpFrame == pSrc->mpFrame));
1086 	if( bSameGraphics
1087 	&&  (pPosAry->mnSrcWidth == pPosAry->mnDestWidth)
1088 	&&  (pPosAry->mnSrcHeight == pPosAry->mnDestHeight))
1089 	{
1090 		// short circuit if there is nothing to do
1091 		if( (pPosAry->mnSrcX == pPosAry->mnDestX)
1092 		&&  (pPosAry->mnSrcY == pPosAry->mnDestY))
1093 			return;
1094 		// use copyArea() if source and destination context are identical
1095 		copyArea( pPosAry->mnDestX, pPosAry->mnDestY, pPosAry->mnSrcX, pPosAry->mnSrcY,
1096 			pPosAry->mnSrcWidth, pPosAry->mnSrcHeight, 0 );
1097 		return;
1098 	}
1099 
1100 	ApplyXorContext();
1101 	pSrc->ApplyXorContext();
1102 
1103 	DBG_ASSERT( pSrc->mxLayer!=NULL, "AquaSalGraphics::copyBits() from non-layered graphics" );
1104 
1105 	const CGPoint aDstPoint = { +pPosAry->mnDestX - pPosAry->mnSrcX, pPosAry->mnDestY - pPosAry->mnSrcY };
1106 	if( (pPosAry->mnSrcWidth == pPosAry->mnDestWidth && pPosAry->mnSrcHeight == pPosAry->mnDestHeight) &&
1107 	    (!mnBitmapDepth || (aDstPoint.x + pSrc->mnWidth) <= mnWidth) ) // workaround a Quartz crasher
1108     {
1109 	    // in XOR mode the drawing context is redirected to the XOR mask
1110 	    // if source and target are identical then copyBits() paints onto the target context though
1111 	    CGContextRef xCopyContext = mrContext;
1112 	    if( mpXorEmulation && mpXorEmulation->IsEnabled() )
1113 		    if( pSrcGraphics == this )
1114 			    xCopyContext = mpXorEmulation->GetTargetContext();
1115 
1116 	    CGContextSaveGState( xCopyContext );
1117 	    const CGRect aDstRect = { {pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight} };
1118 	    CGContextClipToRect( xCopyContext, aDstRect );
1119 
1120 	    // draw at new destination
1121 	    // NOTE: flipped drawing gets disabled for this, else the subimage would be drawn upside down
1122 	    if( pSrc->IsFlipped() )
1123 		    { CGContextTranslateCTM( xCopyContext, 0, +mnHeight ); CGContextScaleCTM( xCopyContext, +1, -1 ); }
1124 	    // TODO: pSrc->size() != this->size()
1125 		    ::CGContextDrawLayerAtPoint( xCopyContext, aDstPoint, pSrc->mxLayer );
1126 	    CGContextRestoreGState( xCopyContext );
1127 	    // mark the destination rectangle as updated
1128    	    RefreshRect( aDstRect );
1129     }
1130     else
1131     {
1132 	    SalBitmap* pBitmap = pSrc->getBitmap( pPosAry->mnSrcX, pPosAry->mnSrcY, pPosAry->mnSrcWidth, pPosAry->mnSrcHeight );
1133 
1134 	    if( pBitmap )
1135 	    {
1136 		    SalTwoRect aPosAry( *pPosAry );
1137 		    aPosAry.mnSrcX = 0;
1138 		    aPosAry.mnSrcY = 0;
1139 		    drawBitmap( &aPosAry, *pBitmap );
1140 		    delete pBitmap;
1141 	    }
1142     }
1143 }
1144 
1145 // -----------------------------------------------------------------------
1146 
1147 void AquaSalGraphics::copyArea( long nDstX, long nDstY,long nSrcX, long nSrcY, long nSrcWidth, long nSrcHeight, sal_uInt16 /*nFlags*/ )
1148 {
1149 	ApplyXorContext();
1150 
1151 #if 0 // TODO: make AquaSalBitmap as fast as the alternative implementation below
1152 	SalBitmap* pBitmap = getBitmap( nSrcX, nSrcY, nSrcWidth, nSrcHeight );
1153 	if( pBitmap )
1154 	{
1155 		SalTwoRect aPosAry;
1156 		aPosAry.mnSrcX = 0;
1157 		aPosAry.mnSrcY = 0;
1158 		aPosAry.mnSrcWidth = nSrcWidth;
1159 		aPosAry.mnSrcHeight = nSrcHeight;
1160 		aPosAry.mnDestX = nDstX;
1161 		aPosAry.mnDestY = nDstY;
1162 		aPosAry.mnDestWidth = nSrcWidth;
1163 		aPosAry.mnDestHeight = nSrcHeight;
1164 		drawBitmap( &aPosAry, *pBitmap );
1165 		delete pBitmap;
1166 	}
1167 #else
1168 	DBG_ASSERT( mxLayer!=NULL, "AquaSalGraphics::copyArea() for non-layered graphics" );
1169 
1170 	// in XOR mode the drawing context is redirected to the XOR mask
1171 	// copyArea() always works on the target context though
1172 	CGContextRef xCopyContext = mrContext;
1173 	if( mpXorEmulation && mpXorEmulation->IsEnabled() )
1174 		xCopyContext = mpXorEmulation->GetTargetContext();
1175 
1176 	// drawing a layer onto its own context causes trouble on OSX => copy it first
1177 	// TODO: is it possible to get rid of this unneeded copy more often?
1178 	//       e.g. on OSX>=10.5 only this situation causes problems:
1179 	//			mnBitmapDepth && (aDstPoint.x + pSrc->mnWidth) > mnWidth
1180 	CGLayerRef xSrcLayer = mxLayer;
1181 	// TODO: if( mnBitmapDepth > 0 )
1182 	{
1183 		const CGSize aSrcSize = { nSrcWidth, nSrcHeight };
1184 		xSrcLayer = ::CGLayerCreateWithContext( xCopyContext, aSrcSize, NULL );
1185 		const CGContextRef xSrcContext = CGLayerGetContext( xSrcLayer );
1186 		CGPoint aSrcPoint = { -nSrcX, -nSrcY };
1187 		if( IsFlipped() )
1188 		{
1189 			::CGContextTranslateCTM( xSrcContext, 0, +nSrcHeight );
1190 			::CGContextScaleCTM( xSrcContext, +1, -1 );
1191 			aSrcPoint.y = (nSrcY + nSrcHeight) - mnHeight;
1192 		}
1193 		::CGContextDrawLayerAtPoint( xSrcContext, aSrcPoint, mxLayer );
1194 	}
1195 
1196 	// draw at new destination
1197 	const CGPoint aDstPoint = { +nDstX, +nDstY };
1198 	::CGContextDrawLayerAtPoint( xCopyContext, aDstPoint, xSrcLayer );
1199 
1200 	// cleanup
1201 	if( xSrcLayer != mxLayer )
1202 		CGLayerRelease( xSrcLayer );
1203 
1204 	// mark the destination rectangle as updated
1205     RefreshRect( nDstX, nDstY, nSrcWidth, nSrcHeight );
1206 #endif
1207 }
1208 
1209 // -----------------------------------------------------------------------
1210 
1211 void AquaSalGraphics::drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap )
1212 {
1213 	if( !CheckContext() )
1214 		return;
1215 
1216 	const AquaSalBitmap& rBitmap = static_cast<const AquaSalBitmap&>(rSalBitmap);
1217 	CGImageRef xImage = rBitmap.CreateCroppedImage( (int)pPosAry->mnSrcX, (int)pPosAry->mnSrcY, (int)pPosAry->mnSrcWidth, (int)pPosAry->mnSrcHeight );
1218 	if( !xImage )
1219 		return;
1220 
1221 	const CGRect aDstRect = {{pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight}};
1222 	CGContextDrawImage( mrContext, aDstRect, xImage );
1223 	CGImageRelease( xImage );
1224 	RefreshRect( aDstRect );
1225 }
1226 
1227 // -----------------------------------------------------------------------
1228 
1229 void AquaSalGraphics::drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap,SalColor )
1230 {
1231 	DBG_ERROR("not implemented for color masking!");
1232 	drawBitmap( pPosAry, rSalBitmap );
1233 }
1234 
1235 // -----------------------------------------------------------------------
1236 
1237 void AquaSalGraphics::drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap, const SalBitmap& rTransparentBitmap )
1238 {
1239 	if( !CheckContext() )
1240 		return;
1241 
1242 	const AquaSalBitmap& rBitmap = static_cast<const AquaSalBitmap&>(rSalBitmap);
1243 	const AquaSalBitmap& rMask = static_cast<const AquaSalBitmap&>(rTransparentBitmap);
1244 	CGImageRef xMaskedImage( rBitmap.CreateWithMask( rMask, pPosAry->mnSrcX, pPosAry->mnSrcY, pPosAry->mnSrcWidth, pPosAry->mnSrcHeight ) );
1245 	if( !xMaskedImage )
1246 		return;
1247 
1248 	const CGRect aDstRect = {{pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight}};
1249 	CGContextDrawImage( mrContext, aDstRect, xMaskedImage );
1250 	CGImageRelease( xMaskedImage );
1251 	RefreshRect( aDstRect );
1252 }
1253 
1254 // -----------------------------------------------------------------------
1255 
1256 void AquaSalGraphics::drawMask( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap, SalColor nMaskColor )
1257 {
1258 	if( !CheckContext() )
1259 		return;
1260 
1261 	const AquaSalBitmap& rBitmap = static_cast<const AquaSalBitmap&>(rSalBitmap);
1262 	CGImageRef xImage = rBitmap.CreateColorMask( pPosAry->mnSrcX, pPosAry->mnSrcY, pPosAry->mnSrcWidth, pPosAry->mnSrcHeight, nMaskColor );
1263 	if( !xImage )
1264 		return;
1265 
1266 	const CGRect aDstRect = {{pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight}};
1267 	CGContextDrawImage( mrContext, aDstRect, xImage );
1268 	CGImageRelease( xImage );
1269 	RefreshRect( aDstRect );
1270 }
1271 
1272 // -----------------------------------------------------------------------
1273 
1274 SalBitmap* AquaSalGraphics::getBitmap( long  nX, long  nY, long  nDX, long  nDY )
1275 {
1276 	DBG_ASSERT( mxLayer, "AquaSalGraphics::getBitmap() with no layer" );
1277 
1278 	ApplyXorContext();
1279 
1280 	AquaSalBitmap* pBitmap = new AquaSalBitmap;
1281 	if( !pBitmap->Create( mxLayer, mnBitmapDepth, nX, nY, nDX, nDY, !mbWindow ) )
1282 	{
1283 		delete pBitmap;
1284 		pBitmap = NULL;
1285 	}
1286 
1287 	return pBitmap;
1288 }
1289 
1290 // -----------------------------------------------------------------------
1291 
1292 SalColor AquaSalGraphics::getPixel( long nX, long nY )
1293 {
1294 	// return default value on printers or when out of bounds
1295 	if( !mxLayer
1296 	|| (nX < 0) || (nX >= mnWidth)
1297 	|| (nY < 0) || (nY >= mnHeight))
1298 		return COL_BLACK;
1299 
1300 	// prepare creation of matching a CGBitmapContext
1301 	CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace;
1302 	CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big;
1303 #if __BIG_ENDIAN__
1304 	struct{ unsigned char b, g, r, a; } aPixel;
1305 #else
1306 	struct{ unsigned char a, r, g, b; } aPixel;
1307 #endif
1308 
1309 	// create a one-pixel bitmap context
1310 	// TODO: is it worth to cache it?
1311 	CGContextRef xOnePixelContext = ::CGBitmapContextCreate( &aPixel,
1312 		1, 1, 8, sizeof(aPixel), aCGColorSpace, aCGBmpInfo );
1313 
1314 	// update this graphics layer
1315 	ApplyXorContext();
1316 
1317 	// copy the requested pixel into the bitmap context
1318 	if( IsFlipped() )
1319 		nY = mnHeight - nY;
1320 	const CGPoint aCGPoint = {-nX, -nY};
1321 	CGContextDrawLayerAtPoint( xOnePixelContext, aCGPoint, mxLayer );
1322 	CGContextRelease( xOnePixelContext );
1323 
1324 	SalColor nSalColor = MAKE_SALCOLOR( aPixel.r, aPixel.g, aPixel.b );
1325 	return nSalColor;
1326 }
1327 
1328 // -----------------------------------------------------------------------
1329 
1330 
1331 static void DrawPattern50( void*, CGContextRef rContext )
1332 {
1333     static const CGRect aRects[2] = { { {0,0}, { 2, 2 } }, { { 2, 2 }, { 2, 2 } } };
1334     CGContextAddRects( rContext, aRects, 2 );
1335     CGContextFillPath( rContext );
1336 }
1337 
1338 void AquaSalGraphics::Pattern50Fill()
1339 {
1340     static const float aFillCol[4] = { 1,1,1,1 };
1341     static const CGPatternCallbacks aCallback = { 0, &DrawPattern50, NULL };
1342     if( ! GetSalData()->mxP50Space )
1343         GetSalData()->mxP50Space = CGColorSpaceCreatePattern( GetSalData()->mxRGBSpace );
1344     if( ! GetSalData()->mxP50Pattern )
1345         GetSalData()->mxP50Pattern = CGPatternCreate( NULL, CGRectMake( 0, 0, 4, 4 ),
1346                                                       CGAffineTransformIdentity, 4, 4,
1347                                                       kCGPatternTilingConstantSpacing,
1348                                                       false, &aCallback );
1349 
1350     CGContextSetFillColorSpace( mrContext, GetSalData()->mxP50Space );
1351     CGContextSetFillPattern( mrContext, GetSalData()->mxP50Pattern, aFillCol );
1352     CGContextFillPath( mrContext );
1353 }
1354 
1355 void AquaSalGraphics::invert( long nX, long nY, long nWidth, long nHeight, SalInvert nFlags )
1356 {
1357     if ( CheckContext() )
1358     {
1359         CGRect aCGRect = CGRectMake( nX, nY, nWidth, nHeight);
1360         CGContextSaveGState(mrContext);
1361 
1362         if ( nFlags & SAL_INVERT_TRACKFRAME )
1363         {
1364             const float dashLengths[2]  = { 4.0, 4.0 };     // for drawing dashed line
1365             CGContextSetBlendMode( mrContext, kCGBlendModeDifference );
1366             CGContextSetRGBStrokeColor ( mrContext, 1.0, 1.0, 1.0, 1.0 );
1367             CGContextSetLineDash ( mrContext, 0, dashLengths, 2 );
1368             CGContextSetLineWidth( mrContext, 2.0);
1369             CGContextStrokeRect ( mrContext, aCGRect );
1370         }
1371         else if ( nFlags & SAL_INVERT_50 )
1372         {
1373             //CGContextSetAllowsAntialiasing( mrContext, false );
1374             CGContextSetBlendMode(mrContext, kCGBlendModeDifference);
1375             CGContextAddRect( mrContext, aCGRect );
1376             Pattern50Fill();
1377 		}
1378         else // just invert
1379         {
1380             CGContextSetBlendMode(mrContext, kCGBlendModeDifference);
1381             CGContextSetRGBFillColor ( mrContext,1.0, 1.0, 1.0 , 1.0 );
1382             CGContextFillRect ( mrContext, aCGRect );
1383         }
1384         CGContextRestoreGState( mrContext);
1385         RefreshRect( aCGRect );
1386     }
1387 }
1388 
1389 // -----------------------------------------------------------------------
1390 
1391 void AquaSalGraphics::invert( sal_uLong nPoints, const SalPoint*  pPtAry, SalInvert nSalFlags )
1392 {
1393     CGPoint* CGpoints ;
1394     if ( CheckContext() )
1395     {
1396         CGContextSaveGState(mrContext);
1397         CGpoints = makeCGptArray(nPoints,pPtAry);
1398         CGContextAddLines ( mrContext, CGpoints, nPoints );
1399         if ( nSalFlags & SAL_INVERT_TRACKFRAME )
1400         {
1401             const float dashLengths[2]  = { 4.0, 4.0 };     // for drawing dashed line
1402             CGContextSetBlendMode( mrContext, kCGBlendModeDifference );
1403             CGContextSetRGBStrokeColor ( mrContext, 1.0, 1.0, 1.0, 1.0 );
1404             CGContextSetLineDash ( mrContext, 0, dashLengths, 2 );
1405             CGContextSetLineWidth( mrContext, 2.0);
1406             CGContextStrokePath ( mrContext );
1407         }
1408         else if ( nSalFlags & SAL_INVERT_50 )
1409         {
1410             CGContextSetBlendMode(mrContext, kCGBlendModeDifference);
1411             Pattern50Fill();
1412         }
1413         else // just invert
1414         {
1415             CGContextSetBlendMode( mrContext, kCGBlendModeDifference );
1416             CGContextSetRGBFillColor( mrContext, 1.0, 1.0, 1.0, 1.0 );
1417             CGContextFillPath( mrContext );
1418         }
1419         const CGRect aRefreshRect = CGContextGetClipBoundingBox(mrContext);
1420         CGContextRestoreGState( mrContext);
1421         delete []  CGpoints;
1422         RefreshRect( aRefreshRect );
1423     }
1424 }
1425 
1426 // -----------------------------------------------------------------------
1427 
1428 sal_Bool AquaSalGraphics::drawEPS( long nX, long nY, long nWidth, long nHeight,
1429 	void* pEpsData, sal_uLong nByteCount )
1430 {
1431 	// convert the raw data to an NSImageRef
1432 	NSData* xNSData = [NSData dataWithBytes:(void*)pEpsData length:(int)nByteCount];
1433 	NSImageRep* xEpsImage = [NSEPSImageRep imageRepWithData: xNSData];
1434 	if( !xEpsImage )
1435 		return false;
1436 
1437 	// get the target context
1438 	if( !CheckContext() )
1439 		return false;
1440 
1441 	// NOTE: flip drawing, else the nsimage would be drawn upside down
1442 	CGContextSaveGState( mrContext );
1443 //	CGContextTranslateCTM( mrContext, 0, +mnHeight );
1444 	CGContextScaleCTM( mrContext, +1, -1 );
1445 	nY = /*mnHeight*/ - (nY + nHeight);
1446 
1447 	// prepare the target context
1448 	NSGraphicsContext* pOrigNSCtx = [NSGraphicsContext currentContext];
1449 	[pOrigNSCtx retain];
1450 
1451 	// create new context
1452 	NSGraphicsContext* pDrawNSCtx = [NSGraphicsContext graphicsContextWithGraphicsPort: mrContext flipped: IsFlipped()];
1453 	// set it, setCurrentContext also releases the prviously set one
1454 	[NSGraphicsContext setCurrentContext: pDrawNSCtx];
1455 
1456 	// draw the EPS
1457 	const NSRect aDstRect = {{nX,nY},{nWidth,nHeight}};
1458 	const BOOL bOK = [xEpsImage drawInRect: aDstRect];
1459 
1460 	// restore the NSGraphicsContext
1461 	[NSGraphicsContext setCurrentContext: pOrigNSCtx];
1462 	[pOrigNSCtx release]; // restore the original retain count
1463 
1464 	CGContextRestoreGState( mrContext );
1465 	// mark the destination rectangle as updated
1466    	RefreshRect( aDstRect );
1467 
1468 	return bOK;
1469 }
1470 
1471 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1472 bool AquaSalGraphics::drawAlphaBitmap( const SalTwoRect& rTR,
1473     const SalBitmap& rSrcBitmap, const SalBitmap& rAlphaBmp )
1474 {
1475     // An image mask can't have a depth > 8 bits (should be 1 to 8 bits)
1476     if( rAlphaBmp.GetBitCount() > 8 )
1477         return false;
1478 
1479     // are these two tests really necessary? (see vcl/unx/source/gdi/salgdi2.cxx)
1480     // horizontal/vertical mirroring not implemented yet
1481     if( rTR.mnDestWidth < 0 || rTR.mnDestHeight < 0 )
1482         return false;
1483 
1484     const AquaSalBitmap& rSrcSalBmp = static_cast<const AquaSalBitmap&>(rSrcBitmap);
1485     const AquaSalBitmap& rMaskSalBmp = static_cast<const AquaSalBitmap&>(rAlphaBmp);
1486 
1487     CGImageRef xMaskedImage = rSrcSalBmp.CreateWithMask( rMaskSalBmp, rTR.mnSrcX, rTR.mnSrcY, rTR.mnSrcWidth, rTR.mnSrcHeight );
1488 	if( !xMaskedImage )
1489 		return false;
1490 
1491     if ( CheckContext() )
1492     {
1493 		const CGRect aDstRect = {{rTR.mnDestX, rTR.mnDestY}, {rTR.mnDestWidth, rTR.mnDestHeight}};
1494 		CGContextDrawImage( mrContext, aDstRect, xMaskedImage );
1495 		RefreshRect( aDstRect );
1496     }
1497 
1498     CGImageRelease(xMaskedImage);
1499     return true;
1500 }
1501 
1502 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1503 bool AquaSalGraphics::drawAlphaRect( long nX, long nY, long nWidth,
1504                                      long nHeight, sal_uInt8 nTransparency )
1505 {
1506     if( !CheckContext() )
1507     	return true;
1508 
1509 	// save the current state
1510 	CGContextSaveGState( mrContext );
1511 	CGContextSetAlpha( mrContext, (100-nTransparency) * (1.0/100) );
1512 
1513 	CGRect aRect = {{nX,nY},{nWidth-1,nHeight-1}};
1514 	if( IsPenVisible() )
1515 	{
1516 		aRect.origin.x += 0.5;
1517 		aRect.origin.y += 0.5;
1518 	}
1519 
1520 	CGContextBeginPath( mrContext );
1521 	CGContextAddRect( mrContext, aRect );
1522 	CGContextDrawPath( mrContext, kCGPathFill );
1523 
1524 	// restore state
1525 	CGContextRestoreGState(mrContext);
1526 	RefreshRect( aRect );
1527     return true;
1528 }
1529 
1530 // -----------------------------------------------------------------------
1531 
1532 void AquaSalGraphics::SetTextColor( SalColor nSalColor )
1533 {
1534     RGBColor color;
1535     color.red     = (unsigned short) ( SALCOLOR_RED(nSalColor)   * 65535.0 / 255.0 );
1536     color.green   = (unsigned short) ( SALCOLOR_GREEN(nSalColor) * 65535.0 / 255.0 );
1537     color.blue    = (unsigned short) ( SALCOLOR_BLUE(nSalColor)  * 65535.0 / 255.0 );
1538 
1539     ATSUAttributeTag aTag = kATSUColorTag;
1540     ByteCount aValueSize = sizeof( color );
1541     ATSUAttributeValuePtr aValue = &color;
1542 
1543     OSStatus err = ATSUSetAttributes( maATSUStyle, 1, &aTag, &aValueSize, &aValue );
1544 	DBG_ASSERT( (err==noErr), "AquaSalGraphics::SetTextColor() : Could not set font attributes!\n");
1545 	if( err != noErr )
1546 		return;
1547 }
1548 
1549 // -----------------------------------------------------------------------
1550 
1551 void AquaSalGraphics::GetFontMetric( ImplFontMetricData* pMetric, int nFallbackLevel )
1552 {
1553 	(void)nFallbackLevel; // glyph-fallback on ATSU is done differently -> no fallback level
1554 
1555 	// get the ATSU font metrics (in point units)
1556 	// of the font that has eventually been size-limited
1557 
1558     ATSUFontID fontId;
1559     OSStatus err = ATSUGetAttribute( maATSUStyle, kATSUFontTag, sizeof(ATSUFontID), &fontId, 0 );
1560     DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font id\n");
1561 
1562     ATSFontMetrics aMetrics;
1563     ATSFontRef rFont = FMGetATSFontRefFromFont( fontId );
1564     err = ATSFontGetHorizontalMetrics ( rFont, kATSOptionFlagsDefault, &aMetrics );
1565 	DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font metrics\n");
1566     if( err != noErr )
1567 		return;
1568 
1569 	// all ATS fonts are scalable fonts
1570 	pMetric->mbScalableFont = true;
1571 	// TODO: check if any kerning is possible
1572 	pMetric->mbKernableFont = true;
1573 
1574 	// convert into VCL font metrics (in unscaled pixel units)
1575 
1576     Fixed ptSize;
1577     err = ATSUGetAttribute( maATSUStyle, kATSUSizeTag, sizeof(Fixed), &ptSize, 0);
1578     DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font size\n");
1579 	const double fPointSize = Fix2X( ptSize );
1580 
1581 	// convert quartz units to pixel units
1582 	// please see the comment in AquaSalGraphics::SetFont() for details
1583     const double fPixelSize = (mfFontScale * mfFakeDPIScale * fPointSize);
1584     pMetric->mnAscent       = static_cast<long>(+aMetrics.ascent  * fPixelSize + 0.5);
1585     pMetric->mnDescent      = static_cast<long>(-aMetrics.descent * fPixelSize + 0.5);
1586     const long nExtDescent  = static_cast<long>((-aMetrics.descent + aMetrics.leading) * fPixelSize + 0.5);
1587     pMetric->mnExtLeading   = nExtDescent - pMetric->mnDescent;
1588     pMetric->mnIntLeading   = 0;
1589     // ATSFontMetrics.avgAdvanceWidth is obsolete, so it is usually set to zero
1590     // since ImplFontMetricData::mnWidth is only used for stretching/squeezing fonts
1591     // setting this width to the pixel height of the fontsize is good enough
1592     // it also makes the calculation of the stretch factor simple
1593     pMetric->mnWidth        = static_cast<long>(mfFontStretch * fPixelSize + 0.5);
1594 }
1595 
1596 // -----------------------------------------------------------------------
1597 
1598 sal_uLong AquaSalGraphics::GetKernPairs( sal_uLong, ImplKernPairData* )
1599 {
1600 	return 0;
1601 }
1602 
1603 // -----------------------------------------------------------------------
1604 
1605 static bool AddTempFontDir( const char* pDir )
1606 {
1607     FSRef aPathFSRef;
1608     Boolean bIsDirectory = true;
1609     OSStatus eStatus = FSPathMakeRef( reinterpret_cast<const UInt8*>(pDir), &aPathFSRef, &bIsDirectory );
1610     DBG_ASSERTWARNING( (eStatus==noErr) && bIsDirectory, "vcl AddTempFontDir() with invalid directory name!" );
1611     if( eStatus != noErr )
1612         return false;
1613 
1614     // TODO: deactivate ATSFontContainerRef when closing app
1615     ATSFontContainerRef aATSFontContainer;
1616 
1617     const ATSFontContext eContext = kATSFontContextLocal; // TODO: *Global???
1618 #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
1619     eStatus = ::ATSFontActivateFromFileReference( &aPathFSRef,
1620         eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault,
1621         &aATSFontContainer );
1622 #else
1623     FSSpec aPathFSSpec;
1624     eStatus = ::FSGetCatalogInfo( &aPathFSRef, kFSCatInfoNone,
1625         NULL, NULL, &aPathFSSpec, NULL );
1626     if( eStatus != noErr )
1627         return false;
1628 
1629     eStatus = ::ATSFontActivateFromFileSpecification( &aPathFSSpec,
1630         eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault,
1631         &aATSFontContainer );
1632 #endif
1633     if( eStatus != noErr )
1634         return false;
1635 
1636     return true;
1637 }
1638 
1639 static bool AddLocalTempFontDirs( void )
1640 {
1641     static bool bFirst = true;
1642     if( !bFirst )
1643         return false;
1644     bFirst = false;
1645 
1646     // add private font files found in brand and base layer
1647 
1648     rtl::OUString aBrandStr( RTL_CONSTASCII_USTRINGPARAM( "$BRAND_BASE_DIR" ) );
1649     rtl_bootstrap_expandMacros( &aBrandStr.pData );
1650     rtl::OUString aBrandSysPath;
1651     OSL_VERIFY( osl_getSystemPathFromFileURL( aBrandStr.pData, &aBrandSysPath.pData ) == osl_File_E_None );
1652 
1653     rtl::OStringBuffer aBrandFontDir( aBrandSysPath.getLength()*2 );
1654     aBrandFontDir.append( rtl::OUStringToOString( aBrandSysPath, RTL_TEXTENCODING_UTF8 ) );
1655     aBrandFontDir.append( "/share/fonts/truetype/" );
1656     bool bBrandSuccess = AddTempFontDir( aBrandFontDir.getStr() );
1657 
1658     rtl::OUString aBaseStr( RTL_CONSTASCII_USTRINGPARAM( "$OOO_BASE_DIR" ) );
1659     rtl_bootstrap_expandMacros( &aBaseStr.pData );
1660     rtl::OUString aBaseSysPath;
1661     OSL_VERIFY( osl_getSystemPathFromFileURL( aBaseStr.pData, &aBaseSysPath.pData ) == osl_File_E_None );
1662 
1663     rtl::OStringBuffer aBaseFontDir( aBaseSysPath.getLength()*2 );
1664     aBaseFontDir.append( rtl::OUStringToOString( aBaseSysPath, RTL_TEXTENCODING_UTF8 ) );
1665     aBaseFontDir.append( "/share/fonts/truetype/" );
1666     bool bBaseSuccess = AddTempFontDir( aBaseFontDir.getStr() );
1667 
1668     return bBrandSuccess && bBaseSuccess;
1669 }
1670 
1671 void AquaSalGraphics::GetDevFontList( ImplDevFontList* pFontList )
1672 {
1673 	DBG_ASSERT( pFontList, "AquaSalGraphics::GetDevFontList(NULL) !");
1674 
1675     AddLocalTempFontDirs();
1676 
1677 	// The idea is to cache the list of system fonts once it has been generated.
1678 	// SalData seems to be a good place for this caching. However we have to
1679 	// carefully make the access to the font list thread-safe. If we register
1680 	// a font-change event handler to update the font list in case fonts have
1681 	// changed on the system we have to lock access to the list. The right
1682     // way to do that is the solar mutex since GetDevFontList is protected
1683     // through it as should be all event handlers
1684 
1685 	SalData* pSalData = GetSalData();
1686 	if (pSalData->mpFontList == NULL)
1687 		pSalData->mpFontList = new SystemFontList();
1688 
1689 	// Copy all ImplFontData objects contained in the SystemFontList
1690 	pSalData->mpFontList->AnnounceFonts( *pFontList );
1691 }
1692 
1693 // -----------------------------------------------------------------------
1694 
1695 bool AquaSalGraphics::AddTempDevFont( ImplDevFontList*,
1696 	const String& rFontFileURL, const String& /*rFontName*/ )
1697 {
1698 	::rtl::OUString aUSytemPath;
1699     OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSytemPath ) );
1700 
1701 	FSRef aNewRef;
1702 	Boolean bIsDirectory = true;
1703 	::rtl::OString aCFileName = rtl::OUStringToOString( aUSytemPath, RTL_TEXTENCODING_UTF8 );
1704 	OSStatus eStatus = FSPathMakeRef( (UInt8*)aCFileName.getStr(), &aNewRef, &bIsDirectory );
1705     DBG_ASSERT( (eStatus==noErr) && !bIsDirectory, "vcl AddTempDevFont() with invalid fontfile name!" );
1706 	if( eStatus != noErr )
1707 		return false;
1708 
1709 	ATSFontContainerRef oContainer;
1710 
1711 	const ATSFontContext eContext = kATSFontContextLocal; // TODO: *Global???
1712 #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
1713 	eStatus = ::ATSFontActivateFromFileReference( &aNewRef,
1714 		eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault,
1715 		&oContainer );
1716 #else
1717 	FSSpec aFontFSSpec;
1718 	eStatus = ::FSGetCatalogInfo( &aNewRef, kFSCatInfoNone,
1719 		NULL, NULL,	&aFontFSSpec, NULL );
1720 	if( eStatus != noErr )
1721 		return false;
1722 
1723 	eStatus = ::ATSFontActivateFromFileSpecification( &aFontFSSpec,
1724 		eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault,
1725 		&oContainer );
1726 #endif
1727 	if( eStatus != noErr )
1728 		return false;
1729 
1730 	// TODO: ATSFontDeactivate( oContainer ) when fonts are no longer needed
1731 	// TODO: register new ImplMacFontdata in pFontList
1732     return true;
1733 }
1734 
1735 // -----------------------------------------------------------------------
1736 
1737 // callbacks from ATSUGlyphGetCubicPaths() fore GetGlyphOutline()
1738 struct GgoData { basegfx::B2DPolygon maPolygon; basegfx::B2DPolyPolygon* mpPolyPoly; };
1739 
1740 static OSStatus GgoLineToProc( const Float32Point* pPoint, void* pData )
1741 {
1742 	basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon;
1743 	const basegfx::B2DPoint aB2DPoint( pPoint->x, pPoint->y );
1744 	rPolygon.append( aB2DPoint );
1745 	return noErr;
1746 }
1747 
1748 static OSStatus GgoCurveToProc( const Float32Point* pCP1, const Float32Point* pCP2,
1749 	const Float32Point* pPoint, void* pData )
1750 {
1751 	basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon;
1752 	const sal_uInt32 nPointCount = rPolygon.count();
1753 	const basegfx::B2DPoint aB2DControlPoint1( pCP1->x, pCP1->y );
1754 	rPolygon.setNextControlPoint( nPointCount-1, aB2DControlPoint1 );
1755 	const basegfx::B2DPoint aB2DEndPoint( pPoint->x, pPoint->y );
1756 	rPolygon.append( aB2DEndPoint );
1757 	const basegfx::B2DPoint aB2DControlPoint2( pCP2->x, pCP2->y );
1758 	rPolygon.setPrevControlPoint( nPointCount, aB2DControlPoint2 );
1759 	return noErr;
1760 }
1761 
1762 static OSStatus GgoClosePathProc( void* pData )
1763 {
1764 	GgoData* pGgoData = static_cast<GgoData*>(pData);
1765 	basegfx::B2DPolygon& rPolygon = pGgoData->maPolygon;
1766 	if( rPolygon.count() > 0 )
1767 		pGgoData->mpPolyPoly->append( rPolygon );
1768 	rPolygon.clear();
1769 	return noErr;
1770 }
1771 
1772 static OSStatus GgoMoveToProc( const Float32Point* pPoint, void* pData )
1773 {
1774 	GgoClosePathProc( pData );
1775 	OSStatus eStatus = GgoLineToProc( pPoint, pData );
1776 	return eStatus;
1777 }
1778 
1779 sal_Bool AquaSalGraphics::GetGlyphOutline( long nGlyphId, basegfx::B2DPolyPolygon& rPolyPoly )
1780 {
1781 	GgoData aGgoData;
1782 	aGgoData.mpPolyPoly = &rPolyPoly;
1783 	rPolyPoly.clear();
1784 
1785 	ATSUStyle rATSUStyle = maATSUStyle;	// TODO: handle glyph fallback when CWS pdffix02 is integrated
1786 	OSStatus eGgoStatus = noErr;
1787 	OSStatus eStatus = ATSUGlyphGetCubicPaths( rATSUStyle, nGlyphId,
1788 		GgoMoveToProc, GgoLineToProc, GgoCurveToProc, GgoClosePathProc,
1789 		&aGgoData, &eGgoStatus );
1790 	if( (eStatus != noErr) ) // TODO: why is (eGgoStatus!=noErr) when curves are involved?
1791 		return false;
1792 
1793 	GgoClosePathProc( &aGgoData );
1794 	if( mfFontScale != 1.0 ) {
1795 		rPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix(+mfFontScale, +mfFontScale));
1796 	}
1797 	return true;
1798 }
1799 
1800 // -----------------------------------------------------------------------
1801 
1802 long AquaSalGraphics::GetGraphicsWidth() const
1803 {
1804     long w = 0;
1805     if( mrContext && (mbWindow || mbVirDev) )
1806     {
1807         w = mnWidth;
1808     }
1809 
1810     if( w == 0 )
1811     {
1812         if( mbWindow && mpFrame )
1813             w = mpFrame->maGeometry.nWidth;
1814     }
1815 
1816     return w;
1817 }
1818 
1819 // -----------------------------------------------------------------------
1820 
1821 sal_Bool AquaSalGraphics::GetGlyphBoundRect( long nGlyphId, Rectangle& rRect )
1822 {
1823 	ATSUStyle rATSUStyle = maATSUStyle;	// TODO: handle glyph fallback
1824 	GlyphID aGlyphId = nGlyphId;
1825 	ATSGlyphScreenMetrics aGlyphMetrics;
1826 	OSStatus eStatus = ATSUGlyphGetScreenMetrics( rATSUStyle,
1827 		1, &aGlyphId, 0, FALSE, !mbNonAntialiasedText, &aGlyphMetrics );
1828 	if( eStatus != noErr )
1829 		return false;
1830 
1831 	const long nMinX = (long)(+aGlyphMetrics.topLeft.x * mfFontScale - 0.5);
1832 	const long nMaxX = (long)(aGlyphMetrics.width * mfFontScale + 0.5) + nMinX;
1833 	const long nMinY = (long)(-aGlyphMetrics.topLeft.y * mfFontScale - 0.5);
1834 	const long nMaxY = (long)(aGlyphMetrics.height * mfFontScale + 0.5) + nMinY;
1835 	rRect = Rectangle( nMinX, nMinY, nMaxX, nMaxY );
1836 	return true;
1837 }
1838 
1839 // -----------------------------------------------------------------------
1840 
1841 void AquaSalGraphics::GetDevFontSubstList( OutputDevice* )
1842 {
1843 	// nothing to do since there are no device-specific fonts on Aqua
1844 }
1845 
1846 // -----------------------------------------------------------------------
1847 
1848 void AquaSalGraphics::DrawServerFontLayout( const ServerFontLayout& )
1849 {
1850 }
1851 
1852 // -----------------------------------------------------------------------
1853 
1854 sal_uInt16 AquaSalGraphics::SetFont( ImplFontSelectData* pReqFont, int /*nFallbackLevel*/ )
1855 {
1856     if( !pReqFont )
1857     {
1858     	ATSUClearStyle( maATSUStyle );
1859 		mpMacFontData = NULL;
1860         return 0;
1861     }
1862 
1863     // store the requested device font entry
1864     const ImplMacFontData* pMacFont = static_cast<const ImplMacFontData*>( pReqFont->mpFontData );
1865     mpMacFontData = pMacFont;
1866 
1867     // convert pixel units (as seen by upper layers) to typographic point units
1868     double fScaledAtsHeight = pReqFont->mfExactHeight;
1869     // avoid Fixed16.16 overflows by limiting the ATS font size
1870     static const float fMaxAtsHeight = 144.0;
1871     if( fScaledAtsHeight <= fMaxAtsHeight )
1872         mfFontScale = 1.0;
1873     else
1874     {
1875         mfFontScale = fScaledAtsHeight / fMaxAtsHeight;
1876         fScaledAtsHeight = fMaxAtsHeight;
1877     }
1878     Fixed fFixedSize = FloatToFixed( fScaledAtsHeight );
1879     // enable bold-emulation if needed
1880     Boolean bFakeBold = FALSE;
1881     if( (pReqFont->GetWeight() >= WEIGHT_BOLD)
1882     &&  (pMacFont->GetWeight() < WEIGHT_SEMIBOLD) )
1883         bFakeBold = TRUE;
1884     // enable italic-emulation if needed
1885     Boolean bFakeItalic = FALSE;
1886     if( ((pReqFont->GetSlant() == ITALIC_NORMAL) || (pReqFont->GetSlant() == ITALIC_OBLIQUE))
1887     && !((pMacFont->GetSlant() == ITALIC_NORMAL) || (pMacFont->GetSlant() == ITALIC_OBLIQUE)) )
1888         bFakeItalic = TRUE;
1889 
1890     // enable/disable antialiased text
1891     mbNonAntialiasedText = pReqFont->mbNonAntialiased;
1892     UInt32 nStyleRenderingOptions = kATSStyleNoOptions;
1893     if( pReqFont->mbNonAntialiased )
1894         nStyleRenderingOptions |= kATSStyleNoAntiAliasing;
1895 
1896 	// set horizontal/vertical mode
1897 	ATSUVerticalCharacterType aVerticalCharacterType = kATSUStronglyHorizontal;
1898 	if( pReqFont->mbVertical )
1899 		aVerticalCharacterType = kATSUStronglyVertical;
1900 
1901 	// prepare ATS-fontid as type matching to the kATSUFontTag request
1902 	ATSUFontID nFontID = static_cast<ATSUFontID>(pMacFont->GetFontId());
1903 
1904     // update ATSU style attributes with requested font parameters
1905 	// TODO: no need to set styles which are already defaulted
1906 
1907     const ATSUAttributeTag aTag[] =
1908     {
1909         kATSUFontTag,
1910         kATSUSizeTag,
1911         kATSUQDBoldfaceTag,
1912         kATSUQDItalicTag,
1913         kATSUStyleRenderingOptionsTag,
1914 		kATSUVerticalCharacterTag
1915     };
1916 
1917     const ByteCount aValueSize[] =
1918     {
1919         sizeof(ATSUFontID),
1920         sizeof(fFixedSize),
1921         sizeof(bFakeBold),
1922         sizeof(bFakeItalic),
1923         sizeof(nStyleRenderingOptions),
1924         sizeof(aVerticalCharacterType)
1925     };
1926 
1927     const ATSUAttributeValuePtr aValue[] =
1928     {
1929         &nFontID,
1930         &fFixedSize,
1931         &bFakeBold,
1932         &bFakeItalic,
1933         &nStyleRenderingOptions,
1934         &aVerticalCharacterType
1935     };
1936 
1937 	static const int nTagCount = sizeof(aTag) / sizeof(*aTag);
1938     OSStatus eStatus = ATSUSetAttributes( maATSUStyle, nTagCount,
1939                              aTag, aValueSize, aValue );
1940     // reset ATSUstyle if there was an error
1941     if( eStatus != noErr )
1942     {
1943         DBG_WARNING( "AquaSalGraphics::SetFont() : Could not set font attributes!\n");
1944         ATSUClearStyle( maATSUStyle );
1945         mpMacFontData = NULL;
1946         return 0;
1947     }
1948 
1949     // prepare font stretching
1950     const ATSUAttributeTag aMatrixTag = kATSUFontMatrixTag;
1951     if( (pReqFont->mnWidth == 0) || (pReqFont->mnWidth == pReqFont->mnHeight) )
1952     {
1953         mfFontStretch = 1.0;
1954     	ATSUClearAttributes( maATSUStyle, 1, &aMatrixTag );
1955     }
1956     else
1957     {
1958         mfFontStretch = (float)pReqFont->mnWidth / pReqFont->mnHeight;
1959         CGAffineTransform aMatrix = CGAffineTransformMakeScale( mfFontStretch, 1.0F );
1960         const ATSUAttributeValuePtr aAttr = &aMatrix;
1961         const ByteCount aMatrixBytes = sizeof(aMatrix);
1962         eStatus = ATSUSetAttributes( maATSUStyle, 1, &aMatrixTag, &aMatrixBytes, &aAttr );
1963         DBG_ASSERT( (eStatus==noErr), "AquaSalGraphics::SetFont() : Could not set font matrix\n");
1964     }
1965 
1966     // prepare font rotation
1967     mnATSUIRotation = FloatToFixed( pReqFont->mnOrientation / 10.0 );
1968 
1969 #if OSL_DEBUG_LEVEL > 3
1970     fprintf( stderr, "SetFont to (\"%s\", \"%s\", fontid=%d) for (\"%s\" \"%s\" weight=%d, slant=%d size=%dx%d orientation=%d)\n",
1971              ::rtl::OUStringToOString( pMacFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(),
1972              ::rtl::OUStringToOString( pMacFont->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(),
1973              (int)nFontID,
1974              ::rtl::OUStringToOString( pReqFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(),
1975              ::rtl::OUStringToOString( pReqFont->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(),
1976              pReqFont->GetWeight(),
1977              pReqFont->GetSlant(),
1978              pReqFont->mnHeight,
1979              pReqFont->mnWidth,
1980              pReqFont->mnOrientation);
1981 #endif
1982 
1983     return 0;
1984 }
1985 
1986 // -----------------------------------------------------------------------
1987 
1988 const ImplFontCharMap* AquaSalGraphics::GetImplFontCharMap() const
1989 {
1990 	if( !mpMacFontData )
1991 		return ImplFontCharMap::GetDefaultMap();
1992 
1993 	return mpMacFontData->GetImplFontCharMap();
1994 }
1995 
1996 // -----------------------------------------------------------------------
1997 
1998 // fake a SFNT font directory entry for a font table
1999 // see http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html#Directory
2000 static void FakeDirEntry( FourCharCode eFCC, ByteCount nOfs, ByteCount nLen,
2001 	const unsigned char* /*pData*/, unsigned char*& rpDest )
2002 {
2003 	// write entry tag
2004 	rpDest[ 0] = (char)(eFCC >> 24);
2005 	rpDest[ 1] = (char)(eFCC >> 16);
2006 	rpDest[ 2] = (char)(eFCC >>  8);
2007 	rpDest[ 3] = (char)(eFCC >>  0);
2008 	// TODO: get entry checksum and write it
2009 	// 		not too important since the subsetter doesn't care currently
2010 	// 		for( pData+nOfs ... pData+nOfs+nLen )
2011 	// write entry offset
2012 	rpDest[ 8] = (char)(nOfs >> 24);
2013 	rpDest[ 9] = (char)(nOfs >> 16);
2014 	rpDest[10] = (char)(nOfs >>  8);
2015 	rpDest[11] = (char)(nOfs >>  0);
2016 	// write entry length
2017 	rpDest[12] = (char)(nLen >> 24);
2018 	rpDest[13] = (char)(nLen >> 16);
2019 	rpDest[14] = (char)(nLen >>  8);
2020 	rpDest[15] = (char)(nLen >>  0);
2021 	// advance to next entry
2022 	rpDest += 16;
2023 }
2024 
2025 static bool GetRawFontData( const ImplFontData* pFontData,
2026 	ByteVector& rBuffer, bool* pJustCFF )
2027 {
2028 	const ImplMacFontData* pMacFont = static_cast<const ImplMacFontData*>(pFontData);
2029 	const ATSUFontID nFontId = static_cast<ATSUFontID>(pMacFont->GetFontId());
2030 	ATSFontRef rFont = FMGetATSFontRefFromFont( nFontId );
2031 
2032 	ByteCount nCffLen = 0;
2033 	OSStatus eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, 0, NULL, &nCffLen);
2034 	if( pJustCFF != NULL )
2035 	{
2036 		*pJustCFF = (eStatus == noErr) && (nCffLen > 0);
2037 		if( *pJustCFF )
2038 		{
2039 			rBuffer.resize( nCffLen );
2040 			eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, nCffLen, (void*)&rBuffer[0], &nCffLen);
2041 			if( (eStatus != noErr) || (nCffLen <= 0) )
2042 				return false;
2043 			return true;
2044 		}
2045 	}
2046 
2047 	// get font table availability and size in bytes
2048 	ByteCount nHeadLen	= 0;
2049 	eStatus = ATSFontGetTable( rFont, GetTag("head"), 0, 0, NULL, &nHeadLen);
2050 	if( (eStatus != noErr) || (nHeadLen <= 0) )
2051 		return false;
2052 	ByteCount nMaxpLen	= 0;
2053     eStatus = ATSFontGetTable( rFont, GetTag("maxp"), 0, 0, NULL, &nMaxpLen);
2054 	if( (eStatus != noErr) || (nMaxpLen <= 0) )
2055 		return false;
2056 	ByteCount nCmapLen	= 0;
2057     eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nCmapLen);
2058 	if( (eStatus != noErr) || (nCmapLen <= 0) )
2059 		return false;
2060 	ByteCount nNameLen	= 0;
2061     eStatus = ATSFontGetTable( rFont, GetTag("name"), 0, 0, NULL, &nNameLen);
2062 	if( (eStatus != noErr) || (nNameLen <= 0) )
2063 		return false;
2064 	ByteCount nHheaLen	= 0;
2065 	eStatus = ATSFontGetTable( rFont, GetTag("hhea"), 0, 0, NULL, &nHheaLen);
2066 	if( (eStatus != noErr) || (nHheaLen <= 0) )
2067 		return false;
2068 	ByteCount nHmtxLen	= 0;
2069 	eStatus = ATSFontGetTable( rFont, GetTag("hmtx"), 0, 0, NULL, &nHmtxLen);
2070 	if( (eStatus != noErr) || (nHmtxLen <= 0) )
2071 		return false;
2072 
2073 	// get the glyph outline tables
2074 	ByteCount nLocaLen	= 0;
2075 	ByteCount nGlyfLen	= 0;
2076 	if( (eStatus != noErr) || (nCffLen <= 0) )
2077 	{
2078 		eStatus = ATSFontGetTable( rFont, GetTag("loca"), 0, 0, NULL, &nLocaLen);
2079 		if( (eStatus != noErr) || (nLocaLen <= 0) )
2080 			return false;
2081 		eStatus = ATSFontGetTable( rFont, GetTag("glyf"), 0, 0, NULL, &nGlyfLen);
2082 		if( (eStatus != noErr) || (nGlyfLen <= 0) )
2083 			return false;
2084 	}
2085 
2086 	ByteCount nPrepLen=0, nCvtLen=0, nFpgmLen=0;
2087 	if( nGlyfLen )	// TODO: reduce PDF size by making hint subsetting optional
2088 	{
2089 		eStatus = ATSFontGetTable( rFont, GetTag("prep"), 0, 0, NULL, &nPrepLen);
2090 		eStatus = ATSFontGetTable( rFont, GetTag("cvt "), 0, 0, NULL, &nCvtLen);
2091 		eStatus = ATSFontGetTable( rFont, GetTag("fpgm"), 0, 0, NULL, &nFpgmLen);
2092 	}
2093 
2094 	// prepare a byte buffer for a fake font
2095 	int nTableCount = 7;
2096 	nTableCount += (nPrepLen>0) + (nCvtLen>0) + (nFpgmLen>0) + (nGlyfLen>0);
2097 	const ByteCount nFdirLen = 12 + 16*nTableCount;
2098 	ByteCount nTotalLen = nFdirLen;
2099 	nTotalLen += nHeadLen + nMaxpLen + nNameLen + nCmapLen;
2100 	if( nGlyfLen )
2101 		nTotalLen += nLocaLen + nGlyfLen;
2102 	else
2103 		nTotalLen += nCffLen;
2104 	nTotalLen += nHheaLen + nHmtxLen;
2105 	nTotalLen += nPrepLen + nCvtLen + nFpgmLen;
2106 	rBuffer.resize( nTotalLen );
2107 
2108 	// fake a SFNT font directory header
2109 	if( nTableCount < 16 )
2110 	{
2111 		int nLog2 = 0;
2112 		while( (nTableCount >> nLog2) > 1 ) ++nLog2;
2113 		rBuffer[ 1] = 1;						// Win-TTF style scaler
2114 		rBuffer[ 5] = nTableCount;				// table count
2115 		rBuffer[ 7] = nLog2*16;					// searchRange
2116 		rBuffer[ 9] = nLog2;					// entrySelector
2117 		rBuffer[11] = (nTableCount-nLog2)*16;	// rangeShift
2118 	}
2119 
2120 	// get font table raw data and update the fake directory entries
2121 	ByteCount nOfs = nFdirLen;
2122 	unsigned char* pFakeEntry = &rBuffer[12];
2123     eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nCmapLen, (void*)&rBuffer[nOfs], &nCmapLen);
2124 	FakeDirEntry( GetTag("cmap"), nOfs, nCmapLen, &rBuffer[0], pFakeEntry );
2125 	nOfs += nCmapLen;
2126 	if( nCvtLen ) {
2127 		eStatus = ATSFontGetTable( rFont, GetTag("cvt "), 0, nCvtLen, (void*)&rBuffer[nOfs], &nCvtLen);
2128 		FakeDirEntry( GetTag("cvt "), nOfs, nCvtLen, &rBuffer[0], pFakeEntry );
2129 		nOfs += nCvtLen;
2130 	}
2131 	if( nFpgmLen ) {
2132 		eStatus = ATSFontGetTable( rFont, GetTag("fpgm"), 0, nFpgmLen, (void*)&rBuffer[nOfs], &nFpgmLen);
2133 		FakeDirEntry( GetTag("fpgm"), nOfs, nFpgmLen, &rBuffer[0], pFakeEntry );
2134 		nOfs += nFpgmLen;
2135 	}
2136 	if( nCffLen ) {
2137 	    eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, nCffLen, (void*)&rBuffer[nOfs], &nCffLen);
2138 		FakeDirEntry( GetTag("CFF "), nOfs, nCffLen, &rBuffer[0], pFakeEntry );
2139 		nOfs += nGlyfLen;
2140 	} else {
2141 	    eStatus = ATSFontGetTable( rFont, GetTag("glyf"), 0, nGlyfLen, (void*)&rBuffer[nOfs], &nGlyfLen);
2142 		FakeDirEntry( GetTag("glyf"), nOfs, nGlyfLen, &rBuffer[0], pFakeEntry );
2143 		nOfs += nGlyfLen;
2144 		eStatus = ATSFontGetTable( rFont, GetTag("loca"), 0, nLocaLen, (void*)&rBuffer[nOfs], &nLocaLen);
2145 		FakeDirEntry( GetTag("loca"), nOfs, nLocaLen, &rBuffer[0], pFakeEntry );
2146 		nOfs += nLocaLen;
2147 	}
2148 	eStatus = ATSFontGetTable( rFont, GetTag("head"), 0, nHeadLen, (void*)&rBuffer[nOfs], &nHeadLen);
2149 	FakeDirEntry( GetTag("head"), nOfs, nHeadLen, &rBuffer[0], pFakeEntry );
2150 	nOfs += nHeadLen;
2151 	eStatus = ATSFontGetTable( rFont, GetTag("hhea"), 0, nHheaLen, (void*)&rBuffer[nOfs], &nHheaLen);
2152 	FakeDirEntry( GetTag("hhea"), nOfs, nHheaLen, &rBuffer[0], pFakeEntry );
2153 	nOfs += nHheaLen;
2154 	eStatus = ATSFontGetTable( rFont, GetTag("hmtx"), 0, nHmtxLen, (void*)&rBuffer[nOfs], &nHmtxLen);
2155 	FakeDirEntry( GetTag("hmtx"), nOfs, nHmtxLen, &rBuffer[0], pFakeEntry );
2156 	nOfs += nHmtxLen;
2157     eStatus = ATSFontGetTable( rFont, GetTag("maxp"), 0, nMaxpLen, (void*)&rBuffer[nOfs], &nMaxpLen);
2158 	FakeDirEntry( GetTag("maxp"), nOfs, nMaxpLen, &rBuffer[0], pFakeEntry );
2159 	nOfs += nMaxpLen;
2160     eStatus = ATSFontGetTable( rFont, GetTag("name"), 0, nNameLen, (void*)&rBuffer[nOfs], &nNameLen);
2161 	FakeDirEntry( GetTag("name"), nOfs, nNameLen, &rBuffer[0], pFakeEntry );
2162 	nOfs += nNameLen;
2163 	if( nPrepLen ) {
2164 		eStatus = ATSFontGetTable( rFont, GetTag("prep"), 0, nPrepLen, (void*)&rBuffer[nOfs], &nPrepLen);
2165 		FakeDirEntry( GetTag("prep"), nOfs, nPrepLen, &rBuffer[0], pFakeEntry );
2166 		nOfs += nPrepLen;
2167 	}
2168 
2169 	DBG_ASSERT( (nOfs==nTotalLen), "AquaSalGraphics::CreateFontSubset (nOfs!=nTotalLen)");
2170 
2171 	return sal_True;
2172 }
2173 
2174 sal_Bool AquaSalGraphics::CreateFontSubset( const rtl::OUString& rToFile,
2175 	const ImplFontData* pFontData, long* pGlyphIDs, sal_uInt8* pEncoding,
2176 	sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo )
2177 {
2178 	// TODO: move more of the functionality here into the generic subsetter code
2179 
2180 	// prepare the requested file name for writing the font-subset file
2181     rtl::OUString aSysPath;
2182     if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) )
2183         return sal_False;
2184     const rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding();
2185     const ByteString aToFile( rtl::OUStringToOString( aSysPath, aThreadEncoding ) );
2186 
2187 	// get the raw-bytes from the font to be subset
2188 	ByteVector aBuffer;
2189 	bool bCffOnly = false;
2190 	if( !GetRawFontData( pFontData, aBuffer, &bCffOnly ) )
2191 		return sal_False;
2192 
2193 	// handle CFF-subsetting
2194 	if( bCffOnly )
2195 	{
2196 		// provide the raw-CFF data to the subsetter
2197 		ByteCount nCffLen = aBuffer.size();
2198 		rInfo.LoadFont( FontSubsetInfo::CFF_FONT, &aBuffer[0], nCffLen );
2199 
2200 		// NOTE: assuming that all glyphids requested on Aqua are fully translated
2201 
2202 		// make the subsetter provide the requested subset
2203 		FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" );
2204 		bool bRC = rInfo.CreateFontSubset( FontSubsetInfo::TYPE1_PFB, pOutFile, NULL,
2205 			pGlyphIDs, pEncoding, nGlyphCount, pGlyphWidths );
2206 		fclose( pOutFile );
2207 		return bRC;
2208 	}
2209 
2210 	// TODO: modernize psprint's horrible fontsubset C-API
2211 	// this probably only makes sense after the switch to another SCM
2212 	// that can preserve change history after file renames
2213 
2214 	// prepare data for psprint's font subsetter
2215 	TrueTypeFont* pSftFont = NULL;
2216 	int nRC = ::OpenTTFontBuffer( (void*)&aBuffer[0], aBuffer.size(), 0, &pSftFont);
2217 	if( nRC != SF_OK )
2218 		return sal_False;
2219 
2220 	// get details about the subsetted font
2221 	TTGlobalFontInfo aTTInfo;
2222 	::GetTTGlobalFontInfo( pSftFont, &aTTInfo );
2223 	rInfo.m_nFontType   = FontSubsetInfo::SFNT_TTF;
2224 	rInfo.m_aPSName     = String( aTTInfo.psname, RTL_TEXTENCODING_UTF8 );
2225 	rInfo.m_aFontBBox	= Rectangle( Point( aTTInfo.xMin, aTTInfo.yMin ),
2226                                     Point( aTTInfo.xMax, aTTInfo.yMax ) );
2227 	rInfo.m_nCapHeight	= aTTInfo.yMax; // Well ...
2228 	rInfo.m_nAscent     = aTTInfo.winAscent;
2229 	rInfo.m_nDescent    = aTTInfo.winDescent;
2230 	// mac fonts usually do not have an OS2-table
2231 	// => get valid ascent/descent values from other tables
2232 	if( !rInfo.m_nAscent )
2233 		rInfo.m_nAscent	= +aTTInfo.typoAscender;
2234 	if( !rInfo.m_nAscent )
2235 		rInfo.m_nAscent	= +aTTInfo.ascender;
2236 	if( !rInfo.m_nDescent )
2237 		rInfo.m_nDescent = +aTTInfo.typoDescender;
2238 	if( !rInfo.m_nDescent )
2239 		rInfo.m_nDescent = -aTTInfo.descender;
2240 
2241     // subset glyphs and get their properties
2242     // take care that subset fonts require the NotDef glyph in pos 0
2243     int nOrigCount = nGlyphCount;
2244     sal_uInt16    aShortIDs[ 256 ];
2245     sal_uInt8 aTempEncs[ 256 ];
2246 
2247     int nNotDef = -1;
2248     for( int i = 0; i < nGlyphCount; ++i )
2249     {
2250         aTempEncs[i] = pEncoding[i];
2251         sal_uInt32 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK;
2252         if( pGlyphIDs[i] & GF_ISCHAR )
2253         {
2254             bool bVertical = (pGlyphIDs[i] & GF_ROTMASK) != 0;
2255             nGlyphIdx = ::MapChar( pSftFont, static_cast<sal_uInt16>(nGlyphIdx), bVertical );
2256             if( nGlyphIdx == 0 && pFontData->IsSymbolFont() )
2257             {
2258                 // #i12824# emulate symbol aliasing U+FXXX <-> U+0XXX
2259                 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK;
2260                 nGlyphIdx = (nGlyphIdx & 0xF000) ? (nGlyphIdx & 0x00FF) : (nGlyphIdx | 0xF000 );
2261                 nGlyphIdx = ::MapChar( pSftFont, static_cast<sal_uInt16>(nGlyphIdx), bVertical );
2262             }
2263         }
2264         aShortIDs[i] = static_cast<sal_uInt16>( nGlyphIdx );
2265         if( !nGlyphIdx )
2266             if( nNotDef < 0 )
2267                 nNotDef = i; // first NotDef glyph found
2268     }
2269 
2270     if( nNotDef != 0 )
2271     {
2272         // add fake NotDef glyph if needed
2273         if( nNotDef < 0 )
2274             nNotDef = nGlyphCount++;
2275 
2276         // NotDef glyph must be in pos 0 => swap glyphids
2277         aShortIDs[ nNotDef ] = aShortIDs[0];
2278         aTempEncs[ nNotDef ] = aTempEncs[0];
2279         aShortIDs[0] = 0;
2280         aTempEncs[0] = 0;
2281     }
2282     DBG_ASSERT( nGlyphCount < 257, "too many glyphs for subsetting" );
2283 
2284 	// TODO: where to get bVertical?
2285 	const bool bVertical = false;
2286 
2287     // fill the pGlyphWidths array
2288 	// while making sure that the NotDef glyph is at index==0
2289     TTSimpleGlyphMetrics* pGlyphMetrics =
2290         ::GetTTSimpleGlyphMetrics( pSftFont, aShortIDs, nGlyphCount, bVertical );
2291     if( !pGlyphMetrics )
2292         return sal_False;
2293     sal_uInt16 nNotDefAdv   	= pGlyphMetrics[0].adv;
2294     pGlyphMetrics[0].adv    	= pGlyphMetrics[nNotDef].adv;
2295     pGlyphMetrics[nNotDef].adv	= nNotDefAdv;
2296     for( int i = 0; i < nOrigCount; ++i )
2297         pGlyphWidths[i] = pGlyphMetrics[i].adv;
2298     free( pGlyphMetrics );
2299 
2300     // write subset into destination file
2301     nRC = ::CreateTTFromTTGlyphs( pSftFont, aToFile.GetBuffer(), aShortIDs,
2302             aTempEncs, nGlyphCount, 0, NULL, 0 );
2303 	::CloseTTFont(pSftFont);
2304     return (nRC == SF_OK);
2305 }
2306 
2307 // -----------------------------------------------------------------------
2308 
2309 void AquaSalGraphics::GetGlyphWidths( const ImplFontData* pFontData, bool bVertical,
2310 	Int32Vector& rGlyphWidths, Ucs2UIntMap& rUnicodeEnc )
2311 {
2312     rGlyphWidths.clear();
2313     rUnicodeEnc.clear();
2314 
2315 	if( pFontData->IsSubsettable() )
2316     {
2317 		ByteVector aBuffer;
2318 		if( !GetRawFontData( pFontData, aBuffer, NULL ) )
2319 			return;
2320 
2321 		// TODO: modernize psprint's horrible fontsubset C-API
2322 		// this probably only makes sense after the switch to another SCM
2323 		// that can preserve change history after file renames
2324 
2325 		// use the font subsetter to get the widths
2326 		TrueTypeFont* pSftFont = NULL;
2327 		int nRC = ::OpenTTFontBuffer( (void*)&aBuffer[0], aBuffer.size(), 0, &pSftFont);
2328 		if( nRC != SF_OK )
2329 			return;
2330 
2331         const int nGlyphCount = ::GetTTGlyphCount( pSftFont );
2332         if( nGlyphCount > 0 )
2333         {
2334 			// get glyph metrics
2335             rGlyphWidths.resize(nGlyphCount);
2336             std::vector<sal_uInt16> aGlyphIds(nGlyphCount);
2337             for( int i = 0; i < nGlyphCount; i++ )
2338                 aGlyphIds[i] = static_cast<sal_uInt16>(i);
2339             const TTSimpleGlyphMetrics* pGlyphMetrics = ::GetTTSimpleGlyphMetrics(
2340 				pSftFont, &aGlyphIds[0], nGlyphCount, bVertical );
2341             if( pGlyphMetrics )
2342             {
2343                 for( int i = 0; i < nGlyphCount; ++i )
2344                     rGlyphWidths[i] = pGlyphMetrics[i].adv;
2345                 free( (void*)pGlyphMetrics );
2346             }
2347 
2348             const ImplFontCharMap* pMap = mpMacFontData->GetImplFontCharMap();
2349             DBG_ASSERT( pMap && pMap->GetCharCount(), "no charmap" );
2350             pMap->AddReference(); // TODO: add and use RAII object instead
2351 
2352 			// get unicode<->glyph encoding
2353 			// TODO? avoid sft mapping by using the pMap itself
2354 			int nCharCount = pMap->GetCharCount();
2355 			sal_uInt32 nChar = pMap->GetFirstChar();
2356 			for(; --nCharCount >= 0; nChar = pMap->GetNextChar( nChar ) )
2357             {
2358 				if( nChar > 0xFFFF ) // TODO: allow UTF-32 chars
2359 					break;
2360 				sal_Ucs nUcsChar = static_cast<sal_Ucs>(nChar);
2361                 sal_uInt32 nGlyph = ::MapChar( pSftFont, nUcsChar, bVertical );
2362                 if( nGlyph > 0 )
2363                     rUnicodeEnc[ nUcsChar ] = nGlyph;
2364             }
2365 
2366             pMap->DeReference(); // TODO: add and use RAII object instead
2367         }
2368 
2369 		::CloseTTFont( pSftFont );
2370     }
2371     else if( pFontData->IsEmbeddable() )
2372     {
2373         // get individual character widths
2374 #if 0 // FIXME
2375         rWidths.reserve( 224 );
2376         for( sal_Unicode i = 32; i < 256; ++i )
2377         {
2378             int nCharWidth = 0;
2379             if( ::GetCharWidth32W( mhDC, i, i, &nCharWidth ) )
2380             {
2381                 rUnicodeEnc[ i ] = rWidths.size();
2382                 rWidths.push_back( nCharWidth );
2383             }
2384         }
2385 #else
2386 		DBG_ERROR("not implemented for non-subsettable fonts!\n");
2387 #endif
2388     }
2389 }
2390 
2391 // -----------------------------------------------------------------------
2392 
2393 const Ucs2SIntMap* AquaSalGraphics::GetFontEncodingVector(
2394 	const ImplFontData*, const Ucs2OStrMap** /*ppNonEncoded*/ )
2395 {
2396     return NULL;
2397 }
2398 
2399 // -----------------------------------------------------------------------
2400 
2401 const void*	AquaSalGraphics::GetEmbedFontData( const ImplFontData*,
2402                               const sal_Ucs* /*pUnicodes*/,
2403                               sal_Int32* /*pWidths*/,
2404                               FontSubsetInfo&,
2405                               long* /*pDataLen*/ )
2406 {
2407     return NULL;
2408 }
2409 
2410 // -----------------------------------------------------------------------
2411 
2412 void AquaSalGraphics::FreeEmbedFontData( const void* pData, long /*nDataLen*/ )
2413 {
2414     // TODO: implementing this only makes sense when the implementation of
2415 	//		AquaSalGraphics::GetEmbedFontData() returns non-NULL
2416 	(void)pData;
2417 	DBG_ASSERT( (pData!=NULL), "AquaSalGraphics::FreeEmbedFontData() is not implemented\n");
2418 }
2419 
2420 // -----------------------------------------------------------------------
2421 
2422 SystemFontData AquaSalGraphics::GetSysFontData( int /* nFallbacklevel */ ) const
2423 {
2424     SystemFontData aSysFontData;
2425     OSStatus err;
2426     aSysFontData.nSize = sizeof( SystemFontData );
2427 
2428     // NOTE: Native ATSU font fallbacks are used, not the VCL fallbacks.
2429     ATSUFontID fontId;
2430     err = ATSUGetAttribute( maATSUStyle, kATSUFontTag, sizeof(fontId), &fontId, 0 );
2431     if (err) fontId = 0;
2432     aSysFontData.aATSUFontID = (void *) fontId;
2433 
2434     Boolean bFbold;
2435     err = ATSUGetAttribute( maATSUStyle, kATSUQDBoldfaceTag, sizeof(bFbold), &bFbold, 0 );
2436     if (err) bFbold = FALSE;
2437     aSysFontData.bFakeBold = (bool) bFbold;
2438 
2439     Boolean bFItalic;
2440     err = ATSUGetAttribute( maATSUStyle, kATSUQDItalicTag, sizeof(bFItalic), &bFItalic, 0 );
2441     if (err) bFItalic = FALSE;
2442     aSysFontData.bFakeItalic = (bool) bFItalic;
2443 
2444     ATSUVerticalCharacterType aVerticalCharacterType;
2445     err = ATSUGetAttribute( maATSUStyle, kATSUVerticalCharacterTag, sizeof(aVerticalCharacterType), &aVerticalCharacterType, 0 );
2446     if (!err && aVerticalCharacterType == kATSUStronglyVertical) {
2447         aSysFontData.bVerticalCharacterType = true;
2448     } else {
2449         aSysFontData.bVerticalCharacterType = false;
2450     }
2451 
2452     aSysFontData.bAntialias = !mbNonAntialiasedText;
2453 
2454     return aSysFontData;
2455 }
2456 
2457 // -----------------------------------------------------------------------
2458 
2459 SystemGraphicsData AquaSalGraphics::GetGraphicsData() const
2460 {
2461     SystemGraphicsData aRes;
2462     aRes.nSize = sizeof(aRes);
2463     aRes.rCGContext = mrContext;
2464     return aRes;
2465 }
2466 
2467 // -----------------------------------------------------------------------
2468 
2469 void AquaSalGraphics::SetXORMode( bool bSet, bool bInvertOnly )
2470 {
2471 	// return early if XOR mode remains unchanged
2472     if( mbPrinter )
2473     	return;
2474 
2475     if( ! bSet && mnXorMode == 2 )
2476     {
2477         CGContextSetBlendMode( mrContext, kCGBlendModeNormal );
2478         mnXorMode = 0;
2479         return;
2480     }
2481     else if( bSet && bInvertOnly && mnXorMode == 0)
2482     {
2483         CGContextSetBlendMode( mrContext, kCGBlendModeDifference );
2484         mnXorMode = 2;
2485         return;
2486     }
2487 
2488 	if( (mpXorEmulation == NULL) && !bSet )
2489 		return;
2490 	if( (mpXorEmulation != NULL) && (bSet == mpXorEmulation->IsEnabled()) )
2491 		return;
2492 	if( !CheckContext() )
2493 	 	return;
2494 
2495 	// prepare XOR emulation
2496 	if( !mpXorEmulation )
2497 	{
2498 		mpXorEmulation = new XorEmulation();
2499 		mpXorEmulation->SetTarget( mnWidth, mnHeight, mnBitmapDepth, mrContext, mxLayer );
2500 	}
2501 
2502 	// change the XOR mode
2503 	if( bSet )
2504 	{
2505 		mpXorEmulation->Enable();
2506 		mrContext = mpXorEmulation->GetMaskContext();
2507         mnXorMode = 1;
2508 	}
2509 	else
2510 	{
2511 		mpXorEmulation->UpdateTarget();
2512 		mpXorEmulation->Disable();
2513 		mrContext = mpXorEmulation->GetTargetContext();
2514         mnXorMode = 0;
2515 	}
2516 }
2517 
2518 // -----------------------------------------------------------------------
2519 
2520 // apply the XOR mask to the target context if active and dirty
2521 void AquaSalGraphics::ApplyXorContext()
2522 {
2523 	if( !mpXorEmulation )
2524 		return;
2525 	if( mpXorEmulation->UpdateTarget() )
2526 		RefreshRect( 0, 0, mnWidth, mnHeight ); // TODO: refresh minimal changerect
2527 }
2528 
2529 // ======================================================================
2530 
2531 XorEmulation::XorEmulation()
2532 :	mxTargetLayer( NULL )
2533 ,	mxTargetContext( NULL )
2534 ,	mxMaskContext( NULL )
2535 ,	mxTempContext( NULL )
2536 ,	mpMaskBuffer( NULL )
2537 ,	mpTempBuffer( NULL )
2538 ,	mnBufferLongs( 0 )
2539 ,	mbIsEnabled( false )
2540 {}
2541 
2542 // ----------------------------------------------------------------------
2543 
2544 XorEmulation::~XorEmulation()
2545 {
2546 	Disable();
2547 	SetTarget( 0, 0, 0, NULL, NULL );
2548 }
2549 
2550 // -----------------------------------------------------------------------
2551 
2552 void XorEmulation::SetTarget( int nWidth, int nHeight, int nTargetDepth,
2553 	CGContextRef xTargetContext, CGLayerRef xTargetLayer )
2554 {
2555 	// prepare to replace old mask+temp context
2556 	if( mxMaskContext )
2557 	{
2558 		// cleanup the mask context
2559 		CGContextRelease( mxMaskContext );
2560 		delete[] mpMaskBuffer;
2561 		mxMaskContext = NULL;
2562 		mpMaskBuffer = NULL;
2563 
2564 		// cleanup the temp context if needed
2565 		if( mxTempContext )
2566 		{
2567 			CGContextRelease( mxTempContext );
2568 			delete[] mpTempBuffer;
2569 			mxTempContext = NULL;
2570 			mpTempBuffer = NULL;
2571 		}
2572 	}
2573 
2574 	// return early if there is nothing more to do
2575 	if( !xTargetContext )
2576 		return;
2577 
2578 	// retarget drawing operations to the XOR mask
2579 	mxTargetLayer = xTargetLayer;
2580 	mxTargetContext = xTargetContext;
2581 
2582 	// prepare creation of matching CGBitmaps
2583 	CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace;
2584 	CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst;
2585 	int nBitDepth = nTargetDepth;
2586 	if( !nBitDepth )
2587 		nBitDepth = 32;
2588 	int nBytesPerRow = (nBitDepth == 16) ? 2 : 4;
2589 	const size_t nBitsPerComponent = (nBitDepth == 16) ? 5 : 8;
2590 	if( nBitDepth <= 8 )
2591 	{
2592 		aCGColorSpace = GetSalData()->mxGraySpace;
2593 		aCGBmpInfo = kCGImageAlphaNone;
2594 		nBytesPerRow = 1;
2595 	}
2596 	nBytesPerRow *= nWidth;
2597 	mnBufferLongs = (nHeight * nBytesPerRow + sizeof(sal_uLong)-1) / sizeof(sal_uLong);
2598 
2599 	// create a XorMask context
2600 	mpMaskBuffer = new sal_uLong[ mnBufferLongs ];
2601 	mxMaskContext = ::CGBitmapContextCreate( mpMaskBuffer,
2602 		nWidth, nHeight, nBitsPerComponent, nBytesPerRow,
2603 		aCGColorSpace, aCGBmpInfo );
2604 	// reset the XOR mask to black
2605 	memset( mpMaskBuffer, 0, mnBufferLongs * sizeof(sal_uLong) );
2606 
2607 	// a bitmap context will be needed for manual XORing
2608 	// create one unless the target context is a bitmap context
2609 	if( nTargetDepth )
2610 		mpTempBuffer = (sal_uLong*)CGBitmapContextGetData( mxTargetContext );
2611 	if( !mpTempBuffer )
2612 	{
2613 		// create a bitmap context matching to the target context
2614 		mpTempBuffer = new sal_uLong[ mnBufferLongs ];
2615 		mxTempContext = ::CGBitmapContextCreate( mpTempBuffer,
2616 			nWidth, nHeight, nBitsPerComponent, nBytesPerRow,
2617 			aCGColorSpace, aCGBmpInfo );
2618 	}
2619 
2620 	// initialize XOR mask context for drawing
2621 	CGContextSetFillColorSpace( mxMaskContext, aCGColorSpace );
2622 	CGContextSetStrokeColorSpace( mxMaskContext, aCGColorSpace );
2623 	CGContextSetShouldAntialias( mxMaskContext, false );
2624 
2625 	// improve the XorMask's XOR emulation a litte
2626 	// NOTE: currently only enabled for monochrome contexts
2627 	if( aCGColorSpace == GetSalData()->mxGraySpace )
2628 		CGContextSetBlendMode( mxMaskContext, kCGBlendModeDifference );
2629 
2630 	// intialize the transformation matrix to the drawing target
2631 	const CGAffineTransform aCTM = CGContextGetCTM( xTargetContext );
2632 	CGContextConcatCTM( mxMaskContext, aCTM );
2633 	if( mxTempContext )
2634 		CGContextConcatCTM( mxTempContext, aCTM );
2635 
2636 	// initialize the default XorMask graphics state
2637 	CGContextSaveGState( mxMaskContext );
2638 }
2639 
2640 // ----------------------------------------------------------------------
2641 
2642 bool XorEmulation::UpdateTarget()
2643 {
2644 	if( !IsEnabled() )
2645 		return false;
2646 
2647 	// update the temp bitmap buffer if needed
2648 	if( mxTempContext )
2649 		CGContextDrawLayerAtPoint( mxTempContext, CGPointZero, mxTargetLayer );
2650 
2651 	// do a manual XOR with the XorMask
2652 	// this approach suffices for simple color manipulations
2653 	// and also the complex-clipping-XOR-trick used in metafiles
2654 	const sal_uLong* pSrc = mpMaskBuffer;
2655 	sal_uLong* pDst = mpTempBuffer;
2656 	for( int i = mnBufferLongs; --i >= 0;)
2657 		*(pDst++) ^= *(pSrc++);
2658 
2659 	// write back the XOR results to the target context
2660 	if( mxTempContext )
2661 	{
2662 		CGImageRef xXorImage = CGBitmapContextCreateImage( mxTempContext );
2663 		const int nWidth  = (int)CGImageGetWidth( xXorImage );
2664 		const int nHeight = (int)CGImageGetHeight( xXorImage );
2665 		// TODO: update minimal changerect
2666 		const CGRect aFullRect = {{0,0},{nWidth,nHeight}};
2667 		CGContextDrawImage( mxTargetContext, aFullRect, xXorImage );
2668 		CGImageRelease( xXorImage );
2669 	}
2670 
2671 	// reset the XorMask to black again
2672 	// TODO: not needed for last update
2673 	memset( mpMaskBuffer, 0, mnBufferLongs * sizeof(sal_uLong) );
2674 
2675 	// TODO: return FALSE if target was not changed
2676 	return true;
2677 }
2678 
2679 // =======================================================================
2680 
2681