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