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