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