1/* 2 CairoContext.m 3 4 Copyright (C) 2003 Free Software Foundation, Inc. 5 6 August 31, 2003 7 Written by Banlu Kemiyatorn <object at gmail dot com> 8 9 This file is part of GNUstep. 10 11 This library is free software; you can redistribute it and/or 12 modify it under the terms of the GNU Lesser General Public 13 License as published by the Free Software Foundation; either 14 version 2 of the License, or (at your option) any later version. 15 16 This library is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 Lesser General Public License for more details. 20 21 You should have received a copy of the GNU Lesser General Public 22 License along with this library; see the file COPYING.LIB. 23 If not, see <http://www.gnu.org/licenses/> or write to the 24 Free Software Foundation, 51 Franklin Street, Fifth Floor, 25 Boston, MA 02110-1301, USA. 26*/ 27 28#include <AppKit/NSBitmapImageRep.h> 29#include <AppKit/NSGraphics.h> 30#include <AppKit/NSPrintInfo.h> 31#include <AppKit/NSPrintOperation.h> 32 33#include "cairo/CairoContext.h" 34#include "cairo/CairoSurface.h" 35#include "cairo/CairoPSSurface.h" 36#include "cairo/CairoPDFSurface.h" 37#include "cairo/CairoFontInfo.h" 38#include "cairo/CairoFontEnumerator.h" 39#include "config.h" 40 41#define CGSTATE ((CairoGState *)gstate) 42 43#if BUILD_SERVER == SERVER_x11 44# include "cairo/CairoGState.h" 45# include "x11/XGServer.h" 46# define _CAIRO_GSTATE_CLASSNAME CairoGState 47# ifdef USE_GLITZ 48# define _CAIRO_SURFACE_CLASSNAME XGCairoGlitzSurface 49# include "cairo/XGCairoGlitzSurface.h" 50# else 51//# define _CAIRO_SURFACE_CLASSNAME XGCairoSurface 52//# include "cairo/XGCairoSurface.h" 53//# define _CAIRO_SURFACE_CLASSNAME XGCairoXImageSurface 54//# include "cairo/XGCairoXImageSurface.h" 55# define _CAIRO_SURFACE_CLASSNAME XGCairoModernSurface 56# include "cairo/XGCairoModernSurface.h" 57# endif /* USE_GLITZ */ 58# include "x11/XGServerWindow.h" 59# include "x11/XWindowBuffer.h" 60#elif BUILD_SERVER == SERVER_win32 61# include "cairo/Win32CairoGState.h" 62# include <windows.h> 63# include "win32/WIN32Server.h" 64# include "win32/WIN32Geometry.h" 65# define _CAIRO_GSTATE_CLASSNAME Win32CairoGState 66# ifdef USE_GLITZ 67# define _CAIRO_SURFACE_CLASSNAME Win32CairoGlitzSurface 68# include "cairo/Win32CairoGlitzSurface.h" 69# else 70# define _CAIRO_SURFACE_CLASSNAME Win32CairoSurface 71# include "cairo/Win32CairoSurface.h" 72# endif /* USE_GLITZ */ 73#else 74# error Invalid server for Cairo backend : non implemented 75#endif /* BUILD_SERVER */ 76 77@implementation CairoContext 78 79+ (void) initializeBackend 80{ 81 [NSGraphicsContext setDefaultContextClass: self]; 82 83 [GSFontEnumerator setDefaultClass: [CairoFontEnumerator class]]; 84 [GSFontInfo setDefaultClass: [CairoFontInfo class]]; 85} 86 87+ (Class) GStateClass 88{ 89 return [_CAIRO_GSTATE_CLASSNAME class]; 90} 91 92+ (BOOL) handlesPS 93{ 94 return YES; 95} 96 97- (BOOL) supportsDrawGState 98{ 99 return YES; 100} 101 102- (BOOL) isDrawingToScreen 103{ 104 CairoSurface *surface = nil; 105 [CGSTATE GSCurrentSurface: &surface : NULL : NULL]; 106 return [surface isDrawingToScreen]; 107} 108 109- (void) flushGraphics 110{ 111 // FIXME: Why is this here? When is it called? 112 // Comments added 07/20/2013 to address the above question after 113 // further debugging cairo/MSwindows non-retained backing store type: 114 // This is called from NSWindow/flushWindow for the non-retained backing 115 // store case. This code originally seems to have been added due to the 116 // non-retained backing store type NOT showing up on XWindows/Cairo 117 // combination and added this XFlush call to handle this case. 118 119 // For MSWindows a similar problem seems to occur. However, the only 120 // equivalent to XFlush on MSWindows is GdiFlush, which doesn't cause 121 // the window to re-display properly. So, on MSWindows, the non-retained 122 // cairo backend surface is currently excluded from the build and we 123 // handle it as a buffered window expose event for now. 124 // FIXME: Anyone know how this can be handled/fixed correctly... 125 126#if BUILD_SERVER == SERVER_x11 127 XFlush([(XGServer *)server xDisplay]); 128#elif BUILD_SERVER == SERVER_win32 129 CairoSurface *surface = nil; 130 131 [CGSTATE GSCurrentSurface: &surface : NULL : NULL]; 132 if ((surface != nil) && ([surface surface] != NULL)) 133 { 134 // Non-retained backing store types currently unsupported on MSWindows... 135 // Currently handling such windows as buffered and invoking the handle 136 // expose event processing... 137 RECT rect; 138 HWND hwnd = surface->gsDevice; 139 GetClientRect(hwnd, &rect); 140 NSRect frame = MSWindowRectToGS((WIN32Server*)GSCurrentServer(), hwnd, rect); 141 [[self class] handleExposeRect: frame forDriver: surface]; 142 } 143#endif 144} 145 146 147/* Private backend methods */ 148+ (void) handleExposeRect: (NSRect)rect forDriver: (void *)driver 149{ 150#if BUILD_SERVER == SERVER_x11 151 if ([(id)driver isKindOfClass: [XWindowBuffer class]]) 152 { 153 // For XGCairoXImageSurface 154 [(XWindowBuffer *)driver _exposeRect: rect]; 155 } 156 else 157#endif 158 if ([(id)driver isKindOfClass: [CairoSurface class]]) 159 { 160 // For XGCairoModernSurface 161 [(CairoSurface *)driver handleExposeRect: rect]; 162 } 163} 164 165#if BUILD_SERVER == SERVER_x11 166 167#ifdef XSHM 168 169+ (void) _gotShmCompletion: (Drawable)d 170{ 171 [XWindowBuffer _gotShmCompletion: d]; 172} 173 174- (void) gotShmCompletion: (Drawable)d 175{ 176 [XWindowBuffer _gotShmCompletion: d]; 177} 178 179#endif // XSHM 180 181#endif // BUILD_SERVER = SERVER_x11 182 183@end 184 185@implementation CairoContext (Ops) 186 187- (BOOL) isCompatibleBitmap: (NSBitmapImageRep*)bitmap 188{ 189 NSString *colorSpaceName; 190 191 if ([bitmap bitmapFormat] != 0) 192 { 193 return NO; 194 } 195 196 if ([bitmap isPlanar]) 197 { 198 return NO; 199 } 200 201 if ([bitmap bitsPerSample] != 8) 202 { 203 return NO; 204 } 205 206 colorSpaceName = [bitmap colorSpaceName]; 207 if (![colorSpaceName isEqualToString: NSDeviceRGBColorSpace] && 208 ![colorSpaceName isEqualToString: NSCalibratedRGBColorSpace]) 209 { 210 return NO; 211 } 212 else 213 { 214 return YES; 215 } 216} 217 218- (void) GSCurrentDevice: (void **)device : (int *)x : (int *)y 219{ 220 CairoSurface *surface; 221 222 [CGSTATE GSCurrentSurface: &surface : x : y]; 223 if (device) 224 { 225 *device = surface->gsDevice; 226 } 227} 228 229- (void) GSSetDevice: (void *)device : (int)x : (int)y 230{ 231 CairoSurface *surface; 232 233 surface = [[_CAIRO_SURFACE_CLASSNAME alloc] initWithDevice: device]; 234 235 [CGSTATE GSSetSurface: surface : x : y]; 236 [surface release]; 237} 238 239- (void) beginPrologueBBox: (NSRect)boundingBox 240 creationDate: (NSString*)dateCreated 241 createdBy: (NSString*)anApplication 242 fonts: (NSString*)fontNames 243 forWhom: (NSString*)user 244 pages: (int)numPages 245 title: (NSString*)aTitle 246{ 247 CairoSurface *surface; 248 NSSize size; 249 NSString *contextType; 250 251 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 252 NSPrintInfo *printInfo = [printOp printInfo]; 253 254 if (printInfo != nil) 255 { 256 size = [printInfo paperSize]; 257 258 // FIXME: This is confusing.. 259 // When an 8.5x11 page is set to landscape, 260 // NSPrintInfo also swaps the paperSize to be 11x8.5, 261 // but gui also adds a 90 degree rotation as if it will 262 // be drawing on a 8.5x11 page. So, swap 11x8.5 back to 263 // 8.5x11 here. 264 if ([printInfo orientation] == NSLandscapeOrientation) 265 { 266 size = NSMakeSize(size.height, size.width); 267 } 268 } 269 else 270 { 271 [NSException raise: NSInternalInconsistencyException 272 format: @"current print operation printInfo is nil in %@", 273 NSStringFromSelector(_cmd)]; 274 return; 275 } 276 277 contextType = [context_info objectForKey: 278 NSGraphicsContextRepresentationFormatAttributeName]; 279 280 if (contextType) 281 { 282 if ([contextType isEqual: NSGraphicsContextPSFormat]) 283 { 284 surface = [[CairoPSSurface alloc] initWithDevice: context_info]; 285 [surface setSize: size]; 286 // This strange setting is needed because of the way GUI handles offset. 287 [CGSTATE GSSetSurface: surface : 0.0 : size.height]; 288 RELEASE(surface); 289 } 290 else if ([contextType isEqual: NSGraphicsContextPDFFormat]) 291 { 292 surface = [[CairoPDFSurface alloc] initWithDevice: context_info]; 293 [surface setSize: size]; 294 // This strange setting is needed because of the way GUI handles offset. 295 [CGSTATE GSSetSurface: surface : 0.0 : size.height]; 296 RELEASE(surface); 297 } 298 } 299} 300 301- (void) showPage 302{ 303 [CGSTATE showPage]; 304} 305 306@end 307 308#undef _CAIRO_SURFACE_CLASSNAME 309#undef _CAIRO_GSTATE_CLASSNAME 310 311