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