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 previously 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 little
2498 // NOTE: currently only enabled for monochrome contexts
2499 if( aCGColorSpace == GetSalData()->mxGraySpace )
2500 CGContextSetBlendMode( mxMaskContext, kCGBlendModeDifference );
2501
2502 // initialize 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