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