Navigation

BasicDrawing

A python version of the BasicDrawing example in the book
“Programming with Quartz”. This shows how to use most of the core API’s in
Quartz/CoreGraphics.

Sources

AppDrawing.py

importQuartzimportCocoaimportUtilitiesimportUIHandlingimportImagesimportImageMaskingimportBitmapContextimportEPSPrintingimportCoordinateSystemimportPathDrawingimportColorAndGStateimportQuartzTextDrawingimportPatternDrawingimportShadowsAndTransparencyLayersimportShadingsimportDrawingBasics# DefineskCatPDF="Kitty.pdf"kPDFForBlendMode="blendmode.pdf"kOurJPEG="Poot.jpg"kQTImage="ptlobos.tif"kOurSubstituteJPG="LyingOnDeckNoProfile.JPG"kOurEPS="imageturkey.eps"RAW_IMAGE_WIDTH=400RAW_IMAGE_HEIGHT=300kRawColorImage="image-400x300x24.raw"kOtherColorImage="otherimage-400x300x24.raw"MASKING_IMAGE_WIDTH=400MASKING_IMAGE_HEIGHT=259kMaskingImage="400x259x8.bw.raw"noOffScreen=0bitmapOffScreen=1layerOffScreen=2# Cache info for GetURL_mainBundle=None_urlMap={}defGetURL(name):""" Returns the CFURLRef for an embeded resource, or None of that cannot be found. """global_mainBundleif_mainBundleisNone:_mainBundle=Utilities.getAppBundle()mainBundle=_mainBundleifmainBundleisnotNone:ifnamein_urlMap:return_urlMap[name]url=Cocoa.CFBundleCopyResourceURL(mainBundle,name,None,None)_urlMap[name]=urlelse:print("Can't get the app bundle!")returnifurlisNone:print("Couldn't get URL for %r"%(name,))returnurl## Helper functions for drawing#defcallPDFDrawProc(context,proc,pdfFile):ourPDFurl=GetURL(pdfFile)ifourPDFurl:proc(context,ourPDFurl)defdoDrawJPEGFile(context):ourJPEGurl=GetURL(kOurJPEG)ifourJPEGurlisnotNone:Images.drawJPEGImage(context,ourJPEGurl)defdoRawImageFileWithURL(context):url=GetURL(kRawColorImage)ifurlisnotNone:Images.drawImageFromURL(context,url,RAW_IMAGE_WIDTH,RAW_IMAGE_HEIGHT,8,True);# 8 bits per component, isColor = TruedefdoRawImageFileWithCallbacks(context):url=GetURL(kRawColorImage)ifurlisnotNone:Images.doImageWithCallbacksCreatedFromURL(context,url,RAW_IMAGE_WIDTH,RAW_IMAGE_HEIGHT,8,True);# 8 bits per component, isColor = TruedefdoDrawImageWithCGImageSource(context):url=GetURL(kOurJPEG)ifurlisnotNone:Images.drawImageWithCGImageDataSource(context,url)defdoIncrementalImage(context):url=GetURL(kOurJPEG)ifurlisnotNone:Images.doIncrementalImageWithURL(context,url)defdoQTImage(context):url=GetURL(kQTImage)ifurlisnotNone:Images.drawQTImageWithQuartz(context,url)defdoJPEGDocumentWithMultipleProfiles(context):url=GetURL(kOurSubstituteJPG)ifurlisnotNone:Images.drawJPEGDocumentWithMultipleProfiles(context,url)defdoMaskImageWithMask(context):theImageToMaskURL=GetURL(kOtherColorImage)theMaskingImageURL=GetURL(kMaskingImage)iftheImageToMaskURLisnotNoneandtheMaskingImageURLisnotNone:ImageMasking.doMaskImageWithMaskFromURL(context,theImageToMaskURL,RAW_IMAGE_WIDTH,RAW_IMAGE_HEIGHT,8,theMaskingImageURL,MASKING_IMAGE_WIDTH,MASKING_IMAGE_HEIGHT)defdoMaskImageWithGrayImage(context):theImageToMaskURL=GetURL(kOtherColorImage)theMaskingImageURL=GetURL(kMaskingImage)iftheImageToMaskURLisnotNoneandtheMaskingImageURLisnotNone:ImageMasking.doMaskImageWithGrayImageFromURL(context,theImageToMaskURL,RAW_IMAGE_WIDTH,RAW_IMAGE_HEIGHT,8,theMaskingImageURL,MASKING_IMAGE_WIDTH,MASKING_IMAGE_HEIGHT)defdoImageMaskedWithColor(context):url=GetURL(kOtherColorImage)ifurlisnotNone:ImageMasking.doMaskImageWithColorFromURL(context,url,RAW_IMAGE_WIDTH,RAW_IMAGE_HEIGHT,True)defexportImageMaskedWithImage(context):theImageToMaskURL=GetURL(kOtherColorImage)theMaskingImageURL=GetURL(kMaskingImage)iftheImageToMaskURLisnotNoneandtheMaskingImageURLisnotNone:ImageMasking.exportImageWithMaskFromURLWithDestination(context,theImageToMaskURL,RAW_IMAGE_WIDTH,RAW_IMAGE_HEIGHT,8,theMaskingImageURL,MASKING_IMAGE_WIDTH,MASKING_IMAGE_HEIGHT)defdoClipMask(context):theMaskingImageURL=GetURL(kMaskingImage)iftheMaskingImageURLisnotNone:ImageMasking.drawWithClippingMask(context,theMaskingImageURL,MASKING_IMAGE_WIDTH,MASKING_IMAGE_HEIGHT)deftilePDFDocument(context,offscreenType):url=GetURL(kCatPDF)ifurlisnotNone:ifoffscreenType==noOffScreen:BitmapContext.TilePDFNoBuffer(context,url)elifoffscreenType==bitmapOffScreen:BitmapContext.TilePDFWithOffscreenBitmap(context,url)else:BitmapContext.TilePDFWithCGLayer(context,url)defdoCompatibleEPSDrawing(context):ourEPSurl=GetURL(kOurEPS)ifourEPSurlisnotNone:EPSPrinting.drawEPSDataImage(context,ourEPSurl)defDispatchDrawing(context,drawingType):""" Drawing dispatcher """ifdrawingType==UIHandling.kHICommandSimpleRect:DrawingBasics.doSimpleRect(context)elifdrawingType==UIHandling.kHICommandStrokedRect:DrawingBasics.doStrokedRect(context)elifdrawingType==UIHandling.kHICommandStrokedAndFilledRect:DrawingBasics.doStrokedAndFilledRect(context)elifdrawingType==UIHandling.kHICommandPathRects:DrawingBasics.doPathRects(context)elifdrawingType==UIHandling.kHICommandAlphaRects:DrawingBasics.doAlphaRects(context)elifdrawingType==UIHandling.kHICommandDashed:DrawingBasics.doDashedLines(context)elifdrawingType==UIHandling.kHICommandSimpleClip:DrawingBasics.doClippedCircle(context)elifdrawingType==UIHandling.kHICommandPDFDoc:callPDFDrawProc(context,DrawingBasics.doPDFDocument,kCatPDF)elifdrawingType==UIHandling.kHICommandRotatedEllipses:CoordinateSystem.doRotatedEllipses(context)elifdrawingType==UIHandling.kHICommandDrawSkewCoordinates:CoordinateSystem.drawSkewedCoordinateSystem(context)elifdrawingType==UIHandling.kHICommandBezierEgg:PathDrawing.doEgg(context)elifdrawingType==UIHandling.kHICommandRoundedRects:PathDrawing.doRoundedRects(context)elifdrawingType==UIHandling.kHICommandStrokeWithCTM:PathDrawing.doStrokeWithCTM(context)elifdrawingType==UIHandling.kHICommandRotatedEllipsesWithCGPath:PathDrawing.doRotatedEllipsesWithCGPath(context)elifdrawingType==UIHandling.kHICommandPixelAligned:PathDrawing.doPixelAlignedFillAndStroke(context)elifdrawingType==UIHandling.kHICommandDeviceFillAndStrokeColor:ColorAndGState.doColorSpaceFillAndStroke(context)elifdrawingType==UIHandling.kHICommandCLUTDrawGraphics:ColorAndGState.doIndexedColorDrawGraphics(context)elifdrawingType==UIHandling.kHICommandDrawWithGlobalAlpha:ColorAndGState.drawWithGlobalAlpha(context)elifdrawingType==UIHandling.kHICommandDrawWithBlendMode:callPDFDrawProc(context,ColorAndGState.drawWithColorBlendMode,kPDFForBlendMode)elifdrawingType==UIHandling.kHICommandDrawWithColorRefs:ColorAndGState.drawWithColorRefs(context)elifdrawingType==UIHandling.kHICommandFunctionsHaveOwnGSave:ColorAndGState.doClippedEllipse(context)elifdrawingType==UIHandling.kHICommandDrawJPEGImage:doDrawJPEGFile(context)elifdrawingType==UIHandling.kHICommandColorImageFromFile:doRawImageFileWithURL(context)elifdrawingType==UIHandling.kHICommandColorImageFromData:Images.doColorRampImage(context)elifdrawingType==UIHandling.kHICommandColorImageFromCallbacks:doRawImageFileWithCallbacks(context)elifdrawingType==UIHandling.kHICommandGrayRamp:Images.doGrayRamp(context)elifdrawingType==UIHandling.kHICommandDrawWithCGImageSource:doDrawImageWithCGImageSource(context)elifdrawingType==UIHandling.kHICommandDrawWithCGImageSourceIncremental:doIncrementalImage(context)elifdrawingType==UIHandling.kHICommandDrawWithQuickTime:doQTImage(context)elifdrawingType==UIHandling.kHICommandSubstituteImageProfile:doJPEGDocumentWithMultipleProfiles(context)elifdrawingType==UIHandling.kHICommandDoSubImage:Images.doColorRampSubImage(context)elifdrawingType==UIHandling.kHICommandExportWithQuickTime:Images.exportColorRampImageWithQT(context)elifdrawingType==UIHandling.kHICommandMaskTurkeyImage:ImageMasking.doOneBitMaskImages(context)elifdrawingType==UIHandling.kHICommandImageMaskedWithMask:doMaskImageWithMask(context)elifdrawingType==UIHandling.kHICommandImageMaskedWithGrayImage:doMaskImageWithGrayImage(context)elifdrawingType==UIHandling.kHICommandMaskImageWithColor:doImageMaskedWithColor(context)elifdrawingType==UIHandling.kHICommandClipToMask:doClipMask(context)elifdrawingType==UIHandling.kHICommandExportWithCGImageDestination:exportImageMaskedWithImage(context)elifdrawingType==UIHandling.kHICommandSimpleCGLayer:BitmapContext.doSimpleCGLayer(context)elifdrawingType==UIHandling.kHICommandAlphaOnlyContext:BitmapContext.doAlphaOnlyContext(context)elifdrawingType==UIHandling.kHICommandDrawNoOffScreenImage:tilePDFDocument(context,noOffScreen)elifdrawingType==UIHandling.kHICommandDrawOffScreenImage:tilePDFDocument(context,bitmapOffScreen)elifdrawingType==UIHandling.kHICommandDrawWithLayer:tilePDFDocument(context,layerOffScreen)elifdrawingType==UIHandling.kHICommandQuartzRomanText:QuartzTextDrawing.drawQuartzRomanText(context)elifdrawingType==UIHandling.kHICommandQuartzTextModes:QuartzTextDrawing.drawQuartzTextWithTextModes(context)elifdrawingType==UIHandling.kHICommandQuartzTextMatrix:QuartzTextDrawing.drawQuartzTextWithTextMatrix(context)elifdrawingType==UIHandling.kHICommandSimplePattern:PatternDrawing.doRedBlackCheckerboard(context)elifdrawingType==UIHandling.kHICommandPatternPhase:PatternDrawing.doPatternPhase(context)elifdrawingType==UIHandling.kHICommandPatternMatrix:PatternDrawing.doPatternMatrix(context)elifdrawingType==UIHandling.kHICommandUncoloredPattern:PatternDrawing.doStencilPattern(context)elifdrawingType==UIHandling.kHICommandDrawWithPDFPattern:callPDFDrawProc(context,PatternDrawing.drawWithPDFPattern,kCatPDF)elifdrawingType==UIHandling.kHICommandSimpleShadow:ShadowsAndTransparencyLayers.drawSimpleShadow(context)elifdrawingType==UIHandling.kHICommandShadowScaling:ShadowsAndTransparencyLayers.doShadowScaling(context)elifdrawingType==UIHandling.kHICommandShadowProblems:ShadowsAndTransparencyLayers.showComplexShadowIssues(context)elifdrawingType==UIHandling.kHICommandComplexShadow:ShadowsAndTransparencyLayers.showComplexShadow(context)elifdrawingType==UIHandling.kHICommandMultipleShapeComposite:ShadowsAndTransparencyLayers.doLayerCompositing(context)elifdrawingType==UIHandling.kHICommandFillAndStrokeWithShadow:ShadowsAndTransparencyLayers.drawFillAndStrokeWithShadow(context)elifdrawingType==UIHandling.kHICommandPDFDocumentShadow:callPDFDrawProc(context,ShadowsAndTransparencyLayers.shadowPDFDocument,kCatPDF)elifdrawingType==UIHandling.kHICommandSimpleAxialShading:Shadings.doSimpleAxialShading(context)elifdrawingType==UIHandling.kHICommandExampleAxialShadings:Shadings.doExampleAxialShading(context)elifdrawingType==UIHandling.kHICommandSimpleRadialShading:Shadings.doSimpleRadialShading(context)elifdrawingType==UIHandling.kHICommandExampleRadialShadings:Shadings.doExampleRadialShadings(context)elifdrawingType==UIHandling.kHICommandEllipseShading:Shadings.doEllipseShading(context)elifdrawingType==UIHandling.kHICommandDoCompatibleEPS:doCompatibleEPSDrawing(context)

BitmapContext.py

importsysimportobjcimportQuartzimportLaunchServicesimportUtilitiesimportDrawingBasicsimportAppDrawing#import Carbon.QTBEST_BYTE_ALIGNMENT=16defCOMPUTE_BEST_BYTES_PER_ROW(bpr):return((int(bpr)+BEST_BYTE_ALIGNMENT-1)&~(BEST_BYTE_ALIGNMENT-1))# We need to ensure that the raster data stays alive until we clean up# the context, store it here._rasterDataForContext={}defcreateRGBBitmapContext(width,height,wantDisplayColorSpace,needsTransparentBitmap):# This routine allocates data for a pixel array that contains width*height# pixels where each pixel is 4 bytes. The format is 8-bit ARGB or XRGB, depending on# whether needsTransparentBitmap is true. In order to get the recommended# pixel alignment, the bytesPerRow is rounded up to the nearest multiple# of BEST_BYTE_ALIGNMENT bytes.# Minimum bytes per row is 4 bytes per sample * number of samples.bytesPerRow=width*4;# Round to nearest multiple of BEST_BYTE_ALIGNMENT.bytesPerRow=COMPUTE_BEST_BYTES_PER_ROW(bytesPerRow);# Allocate the data for the raster. The total amount of data is bytesPerRow# times the number of rows. The function 'calloc' is used so that the# memory is initialized to 0.try:rasterData=objc.allocateBuffer(int(bytesPerRow*height))exceptMemoryError:returnNone# The wantDisplayColorSpace argument passed to the function determines# whether or not to use the display color space or the generic calibrated# RGB color space. The needsTransparentBitmap argument determines whether# create a context that records alpha or not.ifwantDisplayColorSpace:cs=Utilities.getTheDisplayColorSpace()else:cs=Utilities.getTheCalibratedRGBColorSpace()ifneedsTransparentBitmap:transparency=Quartz.kCGImageAlphaPremultipliedFirstelse:transparency=Quartz.kCGImageAlphaPremultipliedFirstcontext=Quartz.CGBitmapContextCreate(rasterData,width,height,8,bytesPerRow,cs,transparency)ifcontextisNone:returnNone_rasterDataForContext[context]=rasterData# Either clear the rect or paint with opaque white, depending on# the needs of the caller.ifneedsTransparentBitmap:# Clear the context bits so they are transparent.Quartz.CGContextClearRect(context,Quartz.CGRectMake(0,0,width,height))else:# Since the drawing destination is opaque, first paint# the context bits to white.Quartz.CGContextSaveGState(context)Quartz.CGContextSetFillColorWithColor(context,Utilities.getRGBOpaqueWhiteColor())Quartz.CGContextFillRect(context,Quartz.CGRectMake(0,0,width,height))Quartz.CGContextRestoreGState(context)returncontextdefmyCGContextGetBitmapInfo(c):ifhasattr(Quartz,'CGBitmapContextGetBitmapInfo'):returnQuartz.CGBitmapContextGetBitmapInfo(c)else:returnQuartz.CGBitmapContextGetAlphaInfo(c)# createImageFromBitmapContext creates a CGImageRef# from a bitmap context. Calling this routine# transfers 'ownership' of the raster data# in the bitmap context, to the image. If the# image can't be created, this routine frees# the memory associated with the raster.defcreateImageFromBitmapContext(c):rasterData=_rasterDataForContext[c]# We own the data, hence remove from the mappingdel_rasterDataForContext[c]imageDataSize=Quartz.CGBitmapContextGetBytesPerRow(c)*Quartz.CGBitmapContextGetHeight(c)ifrasterDataisNone:fprintf(stderr,"Context is not a bitmap context!")# Create the data provider from the image datadataProvider=Quartz.CGDataProviderCreateWithData(None,rasterData,imageDataSize,None)ifdataProviderisNone:print("Couldn't create data provider!")returnNone# Now create the image. The parameters for the image closely match# the parameters of the bitmap context. This code uses a NULL# decode array and shouldInterpolate is true.image=Quartz.CGImageCreate(Quartz.CGBitmapContextGetWidth(c),Quartz.CGBitmapContextGetHeight(c),Quartz.CGBitmapContextGetBitsPerComponent(c),Quartz.CGBitmapContextGetBitsPerPixel(c),Quartz.CGBitmapContextGetBytesPerRow(c),Quartz.CGBitmapContextGetColorSpace(c),myCGContextGetBitmapInfo(c),dataProvider,None,True,Quartz.kCGRenderingIntentDefault)ifimageisNone:print("Couldn't create image!")returnNonereturnimagedefexportCGImageToFileWithQT(image,url,outputFormat,dpi):""" Export an image using QuickTime API's. This function can't possibly work, as the relevant APIs aren't available through MacPython. The code below is mostly there in case someone fixes the MacPython QuickTime bindings. """returnifoutputFormat.lower()==LaunchServices.kUTTypeTIFF.lower():imageExportType=LaunchServices.kQTFileTypeTIFF;elifoutputFormat.lower()==LaunchServices.kUTTypePNG.lower():imageExportType=LaunchServices.kQTFileTypePNG;elifoutputFormat.lower()==LaunchServices.kUTTypeJPEG.lower():imageExportType=LaunchServices.kQTFileTypeJPEG;else:print("Requested image export format %@s unsupported"%(outputFormat,))returnresult,dataRef,dataRefType=QTNewDataReferenceFromCFURL(url,0,None,None)ifresult==0:result,graphicsExporter=OpenADefaultComponent(GraphicsExporterComponentType,imageExportType,graphicsExporter)ifresult==0:result=GraphicsExportSetInputCGImage(graphicsExporter,image)ifresult==0:result=GraphicsExportSetResolution(graphicsExporter,FloatToFixed(dpi),FloatToFixed(dpi));ifresult==0:result=GraphicsExportSetOutputDataReference(graphicsExporter,dataRef,dataRefType);ifresult==0:result,sizeWritten=GraphicsExportDoExport(graphicsExporter,None)CloseComponent(graphicsExporter);ifdataRef:DisposeHandle(dataRef);ifresult:print("QT export got bad result = %d!"%(result,))defexportCGImageToFileWithDestination(image,url,outputFormat,dpi):# Create an image destination at the supplied URL that# corresponds to the output image format. The destination will# only contain 1 image.imageDestination=Quartz.CGImageDestinationCreateWithURL(url,outputFormat,1,None)ifimageDestinationisNone:print("Couldn't create image destination!")return# Create an options dictionary with the X&Y resolution of the imageoptions={Quartz.kCGImagePropertyDPIWidth:dpi,Quartz.kCGImagePropertyDPIHeight:dpi,}# Add the image with the options dictionary to the destination.Quartz.CGImageDestinationAddImage(imageDestination,image,options);# When all the images are added to the destination, finalize it.Quartz.CGImageDestinationFinalize(imageDestination);defMakeImageDocument(url,imageType,exportInfo):# First make a bitmap context for a US Letter size# raster at the requested resolution.dpi=exportInfo.dpi;width=int(8.5*dpi)height=int(11*dpi)# For JPEG output type the bitmap should not be transparent. If other types are added that# do not support transparency, this code should be updated to check for those types as well.needTransparentBitmap=(imageType.lower()!=LaunchServices.kUTTypeJPEG.lower())# Create an RGB Bitmap context using the generic calibrated RGB color space# instead of the display color space.useDisplayColorSpace=False;c=createRGBBitmapContext(width,height,useDisplayColorSpace,needTransparentBitmap)ifcisNone:print("Couldn't make destination bitmap context")returnmemFullErr;# Scale the coordinate system based on the resolution in dots per inch.Quartz.CGContextScaleCTM(c,dpi/72,dpi/72);# Set the font smoothing parameter to false since it's better to# draw any text without special LCD text rendering when creating# rendered data for export.ifhasattr(Quartz,'CGContextSetShouldSmoothFonts'):Quartz.CGContextSetShouldSmoothFonts(c,False)# Set the scaling factor for shadows. This is a hack so that# drawing code that needs to know the scaling factor can# obtain it. Better would be that DispatchDrawing and the code# it calls would take this scaling factor as a parameter.Utilities.setScalingFactor(dpi/72)# Draw into that raster...AppDrawing.DispatchDrawing(c,exportInfo.command)# Set the scaling factor back to 1.0.Utilities.setScalingFactor(1.0)# Create an image from the raster data. Calling# createImageFromBitmapContext gives up ownership# of the raster data used by the context.image=createImageFromBitmapContext(c);# Release the context now that the image is created.delcifimageisNone:# Users of this code should update this to be an error code they find useful.returnmemFullErr# Now export the image.ifexportInfo.useQTForExport:exportCGImageToFileWithQT(image,url,imageType,exportInfo.dpi)else:exportCGImageToFileWithDestination(image,url,imageType,exportInfo.dpi)defMakeTIFFDocument(url,exportInfo):returnMakeImageDocument(url,LaunchServices.kUTTypeTIFF,exportInfo)defMakePNGDocument(url,exportInfo):returnMakeImageDocument(url,LaunchServices.kUTTypePNG,exportInfo)defMakeJPEGDocument(url,exportInfo):returnMakeImageDocument(url,LaunchServices.kUTTypeJPEG,exportInfo)defcreateCGLayerForDrawing(c):rect=Quartz.CGRectMake(0,0,50,50)# Make the layer the size of the rectangle that# this code draws into the layer.layerSize=rect.size# Create the layer to draw into.layer=Quartz.CGLayerCreateWithContext(c,layerSize,None);iflayerisNone:returnNone# Get the context corresponding to the layer.layerContext=Quartz.CGLayerGetContext(layer)iflayerContextisNone:returnNone#$ Set the fill color to opaque black.Quartz.CGContextSetFillColorWithColor(layerContext,Utilities.getRGBOpaqueBlackColor())# Draw the content into the layer.Quartz.CGContextFillRect(layerContext,rect)# Now the layer has the contents needed.returnlayerdefdoSimpleCGLayer(context):# Create the layer.layer=createCGLayerForDrawing(context)iflayerisNone:print("Couldn't create layer!")return# Get the size of the layer created.s=Quartz.CGLayerGetSize(layer)# Clip to a rect that corresponds to# a grid of 8x8 layer objects.Quartz.CGContextClipToRect(context,Quartz.CGRectMake(0,0,8*s.width,8*s.height))# Paint 8 rows of layer objects.forjinrange(8):Quartz.CGContextSaveGState(context)# Paint 4 columns of layer objects, moving# across the drawing canvas by skipping a# square on the grid each time across.foriinrange(4):# Draw the layer at the current origin.Quartz.CGContextDrawLayerAtPoint(context,Quartz.CGPointZero,layer);# Translate across two layer widths.Quartz.CGContextTranslateCTM(context,2*s.width,0);Quartz.CGContextRestoreGState(context)# Translate to the left one layer width on# even loop counts and to the right one# layer width on odd loop counts. Each# time through the outer loop, translate up# one layer height.ifj%2:Quartz.CGContextTranslateCTM(context,s.width,s.height)else:Quartz.CGContextTranslateCTM(context,-s.width,s.height)defcreateAlphaOnlyContext(width,height):# This routine allocates data for a pixel array that contains# width*height pixels, each pixel is 1 byte. The format is# 8 bits per pixel, where the data is the alpha value of the pixel.# Minimum bytes per row is 1 byte per sample * number of samples.bytesPerRow=width;# Round to nearest multiple of BEST_BYTE_ALIGNMENT.bytesPerRow=COMPUTE_BEST_BYTES_PER_ROW(bytesPerRow);# Allocate the data for the raster. The total amount of data is bytesPerRow#// times the number of rows. The function 'calloc' is used so that the#// memory is initialized to 0.try:rasterData=objc.allocateBuffer(bytesPerRow*height);exceptMemoryError:returnNone# This type of context is only available in Panther and later, otherwise# this fails and returns a NULL context. The color space for an alpha#// only context is NULL and the BitmapInfo value is kCGImageAlphaOnly.context=Quartz.CGBitmapContextCreate(rasterData,width,height,8,bytesPerRow,None,Quartz.kCGImageAlphaOnly);ifcontextisNone:print("Couldn't create the context!")returnNone_rasterDataForContext[context]=rasterData# Clear the context bits so they are initially transparent.Quartz.CGContextClearRect(context,Quartz.CGRectMake(0,0,width,height))returncontext;# createMaskFromAlphaOnlyContext creates a CGImageRef# from an alpha-only bitmap context. Calling this routine# transfers 'ownership' of the raster data in the bitmap# context, to the image. If the image can't be created, this# routine frees the memory associated with the raster.defcreateMaskFromAlphaOnlyContext(alphaContext):rasterData=_rasterDataForContext[alphaContext]# We own the data, hence remove from the mappingdel_rasterDataForContext[alphaContext]imageDataSize=Quartz.CGBitmapContextGetBytesPerRow(alphaContext)*Quartz.CGBitmapContextGetHeight(alphaContext)invertDecode=[1.0,0.0]# Create the data provider from the image data.dataProvider=Quartz.CGDataProviderCreateWithData(None,rasterData,imageDataSize,None)ifdataProviderisNone:print("Couldn't create data provider!")returnNonemask=Quartz.CGImageMaskCreate(Quartz.CGBitmapContextGetWidth(alphaContext),Quartz.CGBitmapContextGetHeight(alphaContext),Quartz.CGBitmapContextGetBitsPerComponent(alphaContext),Quartz.CGBitmapContextGetBitsPerPixel(alphaContext),Quartz.CGBitmapContextGetBytesPerRow(alphaContext),dataProvider,# The decode is an inverted decode since a mask has the opposite# sense than alpha, i.e. 0 in a mask paints 100% and 1 in a mask# paints nothing.invertDecode,True)ifmaskisNone:print("Couldn't create image mask!")returnNonereturnmaskdefdoAlphaOnlyContext(context):# This code is going to capture the alpha coverage# of the drawing done by the doAlphaRects routine.# The value passed here as the width and height is# the size of the bounding rectangle of that drawing.width=520height=400alphaContext=createAlphaOnlyContext(width,height);ifcontextisNone:print("Couldn't create the alpha-only context!")return# Draw the content to the alpha-only context, capturing# the alpha coverage. The doAlphaRects routine paints# a series of translucent red rectangles.DrawingBasics.doAlphaRects(alphaContext)# Finished drawing to the context and now the raster contains# the alpha data captured from the drawing. Create# the mask from the data in the context.mask=createMaskFromAlphaOnlyContext(alphaContext);# This code is now finshed with the context so it can# release it.delalphaContextifmaskisNone:return# Set the fill color space.Quartz.CGContextSetFillColorSpace(context,Utilities.getTheCalibratedRGBColorSpace());opaqueBlue=(0.11,0.208,0.451,1.0)# Set the painting color to opaque blue.Quartz.CGContextSetFillColor(context,opaqueBlue);# Draw the mask, painting the mask with blue. This colorizes# the image to blue and it is as if we painted the# alpha rects with blue instead of red.Quartz.CGContextDrawImage(context,Quartz.CGRectMake(0,0,width,height),mask);_pdfDoc=None_pdfURL=None_width=0_height=0defgetThePDFDoc(url):""" This function caches a CGPDFDocumentRef for the most recently requested PDF document. """global_pdfDoc,_pdfURL,_width,_heightifurlisNone:returnNone,0,0# See whether to update the cached PDF document.if_pdfDocisNoneorurl!=_pdfURL:# Release any cached document or URL._pdfDoc=Quartz.CGPDFDocumentCreateWithURL(url);if_pdfDocisnotNone:pdfMediaRect=Quartz.CGPDFDocumentGetMediaBox(_pdfDoc,1)_width=pdfMediaRect.size.width;_height=pdfMediaRect.size.height;# Keep the URL of the PDF file being cached._pdfURL=urlelse:_pdfURL=Noneif_pdfDocisnotNone:return_pdfDoc,_width,_heightelse:returnNone,0,0# Defining this scales the content down by 1/3.DOSCALING=TruedefTilePDFNoBuffer(context,url):# The amount of area to tile should really be based on the# window/document. Here it is hard coded to a US Letter# size document. This may draw too many or too few tiles# for the area actually being filled.fillwidth=612.0fillheight=792.0extraOffset=6.0pdfDoc,tileX,tileY=getThePDFDoc(url)ifpdfDocisNone:print("Couldn't get the PDF document!")returnifDOSCALING:# Make the tiles 1/3 the size of the PDF document.tileX/=3tileY/=3extraOffset/=3# Space the tiles by the tile width and height# plus extraOffset units in each dimension.tileOffsetX=extraOffset+tileX;tileOffsetY=extraOffset+tileY;# Tile the PDF document.forhinrange(0,int(fillheight),int(tileOffsetY)):forwinrange(0,int(fillwidth),int(tileOffsetX)):Quartz.CGContextDrawPDFDocument(context,Quartz.CGRectMake(w,h,tileX,tileY),pdfDoc,1);defTilePDFWithOffscreenBitmap(context,url):# Again this should really be computed based on# the area intended to be tiled.fillwidth=612.0fillheight=792.0extraOffset=6.0pdfDoc,tileX,tileY=getThePDFDoc(url)ifpdfDocisNone:print("Couldn't get the PDF document")returnifDOSCALING:# Make the tiles 1/3 the size of the PDF document.tileX/=3tileY/=3extraOffset/=3# Space the tiles by the tile width and height# plus extraOffset units in each dimension.tileOffsetX=extraOffset+tileX;tileOffsetY=extraOffset+tileY;# Since the bitmap context is for use with the display# and should capture alpha, these are the values# to pass to createRGBBitmapContext.useDisplayColorSpace=True;needTransparentBitmap=True;bitmapContext=createRGBBitmapContext(tileX,tileY,useDisplayColorSpace,needTransparentBitmap);ifbitmapContextisNone:print("Couldn't create bitmap context!")return# Draw the PDF document one time into the bitmap context.Quartz.CGContextDrawPDFDocument(bitmapContext,Quartz.CGRectMake(0,0,tileX,tileY),pdfDoc,1);# Create an image from the raster data. Calling# createImageFromBitmapContext gives up ownership# of the raster data used by the context.image=createImageFromBitmapContext(bitmapContext);# Release the context now that the image is created.delbitmapContextifimageisNone:return# Now tile the image.forhinrange(0,int(fillheight),int(tileOffsetY)):forwinrange(0,int(fillwidth),int(tileOffsetX)):Quartz.CGContextDrawImage(context,Quartz.CGRectMake(w,h,tileX,tileY),image)defcreateLayerWithImageForContext(c,url):layerSize=Quartz.CGSize()pdfDoc,layerSize.width,layerSize.height=getThePDFDoc(url)ifpdfDocisNone:returnNoneifDOSCALING:# Make the layer 1/3 the size of the PDF document.layerSize.width/=3layerSize.height/=3# Create the layer to draw into.layer=Quartz.CGLayerCreateWithContext(c,layerSize,None)iflayerisNone:returnNULL# Get the context corresponding to the layer. Note# that this is a 'Get' function so the code must# not release the context.layerContext=Quartz.CGLayerGetContext(layer)iflayerContextisNone:returnNone# Draw the PDF document into the layer.Quartz.CGContextDrawPDFDocument(layerContext,Quartz.CGRectMake(0,0,layerSize.width,layerSize.height),pdfDoc,1);# Now the layer has the contents needed.returnlayerdefTilePDFWithCGLayer(context,url):# Again this should really be computed based on# the area intended to be tiled.fillwidth=612.0fillheight=792.0layer=createLayerWithImageForContext(context,url)iflayerisNone:print("Couldn't create the layer!")return# Compute the tile size and offset.s=Quartz.CGLayerGetSize(layer);tileX=s.widthtileY=s.heightifDOSCALING:# Space the tiles by the tile width and height# plus an extra 2 units in each dimension.tileOffsetX=2.0+tileX;tileOffsetY=2.0+tileY;else:# Add 6 units to the offset in each direction# if there is no scaling of the source PDF document.tileOffsetX=6.+tileX;tileOffsetY=6.+tileY;# Now draw the contents of the layer to the context.# The layer is drawn at its true size (the size of# the tile) with its origin located at the corner# of each tile.forhinrange(0,int(fillheight),int(tileOffsetY)):forwinrange(0,int(fillwidth),int(tileOffsetX)):Quartz.CGContextDrawLayerAtPoint(context,Quartz.CGPointMake(w,h),layer)

ColorAndGState.py

importsysimportQuartzimportUtilitiesimportarraydefdoColorSpaceFillAndStroke(context):theColorSpace=Utilities.getTheCalibratedRGBColorSpace()opaqueRed=(0.663,0.0,0.031,1.0)# red,green,blue,alphaaBlue=(0.482,0.62,0.871,1.0)# red,green,blue,alpha# Set the fill color space to be the generic calibrated RGB color space.Quartz.CGContextSetFillColorSpace(context,theColorSpace)# Set the fill color to opaque red. The number of elements in the# array passed to this function must be the number of color# components in the current fill color space plus 1 for alpha.Quartz.CGContextSetFillColor(context,opaqueRed)# Set the stroke color space to be the generic calibrated RGB color space.Quartz.CGContextSetStrokeColorSpace(context,theColorSpace)# Set the stroke color to opaque blue. The number of elements# in the array passed to this function must be the number of color# components in the current stroke color space plus 1 for alpha.Quartz.CGContextSetStrokeColor(context,aBlue)Quartz.CGContextSetLineWidth(context,8.0)# Rectangle 1.Quartz.CGContextBeginPath(context)Quartz.CGContextAddRect(context,Quartz.CGRectMake(20.0,20.0,100.0,100.0))Quartz.CGContextDrawPath(context,Quartz.kCGPathFillStroke)# Continue to use the stroke colorspace already set# but change the stroke alpha value to a semitransparent blue.aBlue=list(aBlue)aBlue[3]=0.5Quartz.CGContextSetStrokeColor(context,aBlue)# Rectangle 2.Quartz.CGContextBeginPath(context)Quartz.CGContextAddRect(context,Quartz.CGRectMake(140.0,20.0,100.0,100.0))Quartz.CGContextDrawPath(context,Quartz.kCGPathFillStroke)# Don't release the color space since this routine# didn't create it._opaqueRedColor=None_opaqueBlueColor=None_transparentBlueColor=NonedefdrawWithColorRefs(context):global_opaqueRedColorglobal_opaqueBlueColorglobal_transparentBlueColor# Initialize the CGColorRefs if necessaryif_opaqueRedColorisNone:# Initialize the color array to an opaque red# in the generic calibrated RGB color space.color=(0.663,0.0,0.031,1.0)theColorSpace=Utilities.getTheCalibratedRGBColorSpace()# Create a CGColorRef for opaque red._opaqueRedColor=Quartz.CGColorCreate(theColorSpace,color)# Make the color array correspond to an opaque blue color.color=(0.482,0.62,0.87,1.0)# Create another Quartz.CGColorRef for opaque blue._opaqueBlueColor=Quartz.CGColorCreate(theColorSpace,color)# Create a new CGColorRef from the opaqueBlue CGColorRef# but with a different alpha value._transparentBlueColor=Quartz.CGColorCreateCopyWithAlpha(_opaqueBlueColor,0.5)if_opaqueRedColorisNoneor_opaqueBlueColorisNoneor_transparentBlueColorisNone:print("Couldn't create one of the CGColorRefs!!!")return# Set the fill color to the opaque red CGColor object.Quartz.CGContextSetFillColorWithColor(context,_opaqueRedColor)# Set the stroke color to the opaque blue CGColor object.Quartz.CGContextSetStrokeColorWithColor(context,_opaqueBlueColor)Quartz.CGContextSetLineWidth(context,8.0)# Draw the first rectangle.Quartz.CGContextBeginPath(context)Quartz.CGContextAddRect(context,Quartz.CGRectMake(20.0,20.0,100.0,100.0))Quartz.CGContextDrawPath(context,Quartz.kCGPathFillStroke)# Set the stroke color to be that of the transparent blue# CGColor object.Quartz.CGContextSetStrokeColorWithColor(context,_transparentBlueColor)# Draw a second rectangle to the right of the first one.Quartz.CGContextBeginPath(context)Quartz.CGContextAddRect(context,Quartz.CGRectMake(140.0,20.0,100.0,100.0))Quartz.CGContextDrawPath(context,Quartz.kCGPathFillStroke)defdoIndexedColorDrawGraphics(context):theBaseRGBSpace=Utilities.getTheCalibratedRGBColorSpace()lookupTable=array.array('B',(0,)*6)opaqueRed=(0,1)# index, alphaaBlue=(1,1)# index, alpha# Set the first 3 values in the lookup table to a red of# 169/255 = 0.663, no green, and blue = 8/255 = 0.031. This makes# the first entry in the lookup table a shade of red.lookupTable[0]=169;lookupTable[1]=0;lookupTable[2]=8# Set the second 3 values in the lookup table to a red value# of 123/255 = 0.482, a green value of 158/255 = 0.62, and# a blue value of 222/255 = 0.871. This makes the second entry# in the lookup table a shade of blue.lookupTable[3]=123;lookupTable[4]=158;lookupTable[5]=222# Create the indexed color space with this color lookup table,# using the RGB color space as the base color space and a 2 element# color lookup table to characterize the indexed color space.theIndexedSpace=Quartz.CGColorSpaceCreateIndexed(theBaseRGBSpace,1,lookupTable)iftheIndexedSpaceisnotNone:Quartz.CGContextSetStrokeColorSpace(context,theIndexedSpace)Quartz.CGContextSetFillColorSpace(context,theIndexedSpace)# Set the stroke color to an opaque blue.Quartz.CGContextSetStrokeColor(context,aBlue)# Set the fill color to an opaque red.Quartz.CGContextSetFillColor(context,opaqueRed)Quartz.CGContextSetLineWidth(context,8.0)# Draw the first rectangle.Quartz.CGContextBeginPath(context)Quartz.CGContextAddRect(context,Quartz.CGRectMake(20.0,20.0,100.0,100.0))Quartz.CGContextDrawPath(context,Quartz.kCGPathFillStroke)# Continue to use the stroke colorspace already set# but change the stroke alpha value to a semitransparent value# while leaving the index value unchanged.aBlue=list(aBlue)aBlue[1]=0.5Quartz.CGContextSetStrokeColor(context,aBlue)# Draw another rectangle to the right of the first one.Quartz.CGContextBeginPath(context)Quartz.CGContextAddRect(context,Quartz.CGRectMake(140.0,20.0,100.0,100.0))Quartz.CGContextDrawPath(context,Quartz.kCGPathFillStroke)else:print("Couldn't make the indexed color space!")defdrawWithGlobalAlpha(context):rect=Quartz.CGRectMake(40.0,210.0,100.0,100.0)color=[1.0,0.0,0.0,1.0]# opaque red# Set the fill color space to that returned by getTheCalibratedRGBColorSpace.Quartz.CGContextSetFillColorSpace(context,Utilities.getTheCalibratedRGBColorSpace())Quartz.CGContextSetFillColor(context,color)foriinrange(2):Quartz.CGContextSaveGState(context)# Paint the leftmost rect on this row with 100% opaque red.Quartz.CGContextFillRect(context,rect)Quartz.CGContextTranslateCTM(context,rect.size.width+70.0,0.0)# Set the alpha value of this rgba color to 0.5.color[3]=0.5# Use the new color as the fill color in the graphics state.Quartz.CGContextSetFillColor(context,color)# Paint the center rect on this row with 50% opaque red.Quartz.CGContextFillRect(context,rect)Quartz.CGContextTranslateCTM(context,rect.size.width+70.0,0.0)# Set the alpha value of this rgba color to 0.25.color[3]=0.25# Use the new color as the fill color in the graphics state.Quartz.CGContextSetFillColor(context,color)# Paint the rightmost rect on this row with 25% opaque red.Quartz.CGContextFillRect(context,rect)Quartz.CGContextRestoreGState(context)# After restoring the graphics state, the fill color is set to# that prior to calling CGContextSaveGState, that is, opaque# red. The coordinate system is also restored.# Now set the context global alpha value to 50% opaque.Quartz.CGContextSetAlpha(context,0.5)# Translate down for a second row of rectangles.Quartz.CGContextTranslateCTM(context,0.0,-(rect.size.height+70.0))# Reset the alpha value of the color array to fully opaque.color[3]=1.0defdrawWithColorBlendMode(context,url):# A pleasant green color.green=[0.584,0.871,0.318,1.0]# Create a CGPDFDocument object from the URL.pdfDoc=Quartz.CGPDFDocumentCreateWithURL(url)ifpdfDocisNone:print("Couldn't create CGPDFDocument from URL!")return# Obtain the media box for page 1 of the PDF document.pdfRect=Quartz.CGPDFDocumentGetMediaBox(pdfDoc,1)# Set the origin of the rectangle to (0,0).pdfRect.origin.x=pdfRect.origin.y=0# Graphic 1, the left portion of the figure.Quartz.CGContextTranslateCTM(context,20,10+Quartz.CGRectGetHeight(pdfRect)/2)# Draw the PDF document.Quartz.CGContextDrawPDFDocument(context,pdfRect,pdfDoc,1)# Set the fill color space to that returned by getTheCalibratedRGBColorSpace.Quartz.CGContextSetFillColorSpace(context,Utilities.getTheCalibratedRGBColorSpace())# Set the fill color to green.Quartz.CGContextSetFillColor(context,green)# Graphic 2, the top-right portion of the figure.Quartz.CGContextTranslateCTM(context,Quartz.CGRectGetWidth(pdfRect)+10,Quartz.CGRectGetHeight(pdfRect)/2+10)# Draw the PDF document again.Quartz.CGContextDrawPDFDocument(context,pdfRect,pdfDoc,1)# Make a fill rectangle that is the same size as the PDF document# but inset each side by 80 units in x and 20 units in y.insetRect=Quartz.CGRectInset(pdfRect,80,20)# Fill the rectangle with green. Because the fill color is opaque and# the blend mode is Normal, this obscures the drawing underneath.Quartz.CGContextFillRect(context,insetRect)# Graphic 3, the bottom-right portion of the figure.Quartz.CGContextTranslateCTM(context,0,-(10+Quartz.CGRectGetHeight(pdfRect)))# Draw the PDF document again.Quartz.CGContextDrawPDFDocument(context,pdfRect,pdfDoc,1)# Set the blend mode to kCGBlendModeColor which will# colorize the destination with subsequent drawing.Quartz.CGContextSetBlendMode(context,Quartz.kCGBlendModeColor)# Draw the rectangle on top of the PDF document. The portion of the# background that is covered by the rectangle is colorized# with the fill color.Quartz.CGContextFillRect(context,insetRect)defcreateEllipsePath(context,center,ellipseSize):Quartz.CGContextSaveGState(context)# Translate the coordinate origin to the center point.Quartz.CGContextTranslateCTM(context,center.x,center.y)# Scale the coordinate system to half the width and height# of the ellipse.Quartz.CGContextScaleCTM(context,ellipseSize.width/2,ellipseSize.height/2)Quartz.CGContextBeginPath(context)# Add a circular arc to the path, centered at the origin and# with a radius of 1.0. This radius, together with the# scaling above for the width and height, produces an ellipse# of the correct size.Quartz.CGContextAddArc(context,0.0,0.0,1.0,0.0,Utilities.DEGREES_TO_RADIANS(360.0),0.0)# Close the path so that this path is suitable for stroking,# should that be desired.Quartz.CGContextClosePath(context)Quartz.CGContextRestoreGState(context)_opaqueBrownColor=None_opaqueOrangeColor=NonedefdoClippedEllipse(context):global_opaqueBrownColor,_opaqueOrangeColortheCenterPoint=Quartz.CGPoint(120.0,120.0)theEllipseSize=Quartz.CGSize(100.0,200.0)dash=[2.0]# Initialize the CGColorRefs if necessary.if_opaqueBrownColorisNone:# The initial value of the color array is an# opaque brown in an RGB color space.color=[0.325,0.208,0.157,1.0]theColorSpace=Utilities.getTheCalibratedRGBColorSpace()# Create a CGColorRef for opaque brown._opaqueBrownColor=Quartz.CGColorCreate(theColorSpace,color)# Make the color array correspond to an opaque orange.color=[0.965,0.584,0.059,1.0]# Create another CGColorRef for opaque orange._opaqueOrangeColor=Quartz.CGColorCreate(theColorSpace,color)# Draw two ellipses centered about the same point, one# rotated 45 degrees from the other.Quartz.CGContextSaveGState(context)# Ellipse 1createEllipsePath(context,theCenterPoint,theEllipseSize)Quartz.CGContextSetFillColorWithColor(context,_opaqueBrownColor)Quartz.CGContextFillPath(context)# Translate and rotate about the center point of the ellipse.Quartz.CGContextTranslateCTM(context,theCenterPoint.x,theCenterPoint.y)# Rotate by 45 degrees.Quartz.CGContextRotateCTM(context,Utilities.DEGREES_TO_RADIANS(45))# Ellipse 2# CGPointZero is a pre-defined Quartz point corresponding to# the coordinate (0,0).createEllipsePath(context,Quartz.CGPointZero,theEllipseSize)Quartz.CGContextSetFillColorWithColor(context,_opaqueOrangeColor)Quartz.CGContextFillPath(context)Quartz.CGContextRestoreGState(context)Quartz.CGContextTranslateCTM(context,170.0,0.0)# Now use the first ellipse as a clipping area prior to# painting the second ellipse.Quartz.CGContextSaveGState(context)# Ellipse 3createEllipsePath(context,theCenterPoint,theEllipseSize)Quartz.CGContextSetStrokeColorWithColor(context,_opaqueBrownColor)Quartz.CGContextSetLineDash(context,0,dash,1)# Stroke the path with a dash.Quartz.CGContextStrokePath(context)# Ellipse 4createEllipsePath(context,theCenterPoint,theEllipseSize)# Clip to the elliptical path.Quartz.CGContextClip(context)Quartz.CGContextTranslateCTM(context,theCenterPoint.x,theCenterPoint.y)# Rotate by 45 degrees.Quartz.CGContextRotateCTM(context,Utilities.DEGREES_TO_RADIANS(45))# Ellipse 5createEllipsePath(context,Quartz.CGPointZero,theEllipseSize)Quartz.CGContextSetFillColorWithColor(context,_opaqueOrangeColor)Quartz.CGContextFillPath(context)Quartz.CGContextRestoreGState(context)

CoordinateSystem.py

importQuartzimportmathimportUtilitiesdefdoRotatedEllipses(context):totreps=144tint=1.0tintIncrement=1.0/totreps# Create a new transform consisting of a 45 degrees rotation.theTransform=Quartz.CGAffineTransformMakeRotation(math.pi/4)# Apply a scale to the transform just created.theTransform=Quartz.CGAffineTransformScale(theTransform,1,2)# Place the first ellipse at a good location.Quartz.CGContextTranslateCTM(context,100.0,100.0)foriinrange(totreps):# Make a snapshot the coordinate system.Quartz.CGContextSaveGState(context)# Set up the coordinate system for the rotated ellipse.Quartz.CGContextConcatCTM(context,theTransform)Quartz.CGContextBeginPath(context)Quartz.CGContextAddArc(context,0.0,0.0,45.0,0.0,2*math.pi,0);# Set the fill color for this instance of the ellipse.Quartz.CGContextSetRGBFillColor(context,tint,0.0,0.0,1.0)Quartz.CGContextDrawPath(context,Quartz.kCGPathFill)# Restore the coordinate system to that of the snapshot.Quartz.CGContextRestoreGState(context)# Compute the next tint color.tint-=tintIncrement# Move over by 1 unit in x for the next ellipse.Quartz.CGContextTranslateCTM(context,1.0,0.0)defdrawSkewedCoordinateSystem(context):# alpha is 22.5 degrees and beta is 15 degrees.alpha=math.pi/8beta=math.pi/12# Create a rectangle that is 72 units on a side# with its origin at (0,0).r=Quartz.CGRectMake(0,0,72,72)Quartz.CGContextTranslateCTM(context,144,144)# Draw the coordinate axes untransformed.Utilities.drawCoordinateAxes(context)# Fill the rectangle.Quartz.CGContextFillRect(context,r)# Create an affine transform that skews the coordinate system,# skewing the x-axis by alpha radians and the y-axis by beta radians.skew=Quartz.CGAffineTransformMake(1,math.tan(alpha),math.tan(beta),1,0,0)# Apply that transform to the context coordinate system.Quartz.CGContextConcatCTM(context,skew)# Set the fill and stroke color to a dark blue.Quartz.CGContextSetRGBStrokeColor(context,0.11,0.208,0.451,1)Quartz.CGContextSetRGBFillColor(context,0.11,0.208,0.451,1)# Draw the coordinate axes again, now transformed.Utilities.drawCoordinateAxes(context)# Set the fill color again but with a partially transparent alpha.Quartz.CGContextSetRGBFillColor(context,0.11,0.208,0.451,0.7)# Fill the rectangle in the transformed coordinate system.Quartz.CGContextFillRect(context,r)

DataProvidersAndConsumers.py

importos,sysimportQuartzimportCocoaimportobjcdefcreateDataProviderFromPathName(path):# Create a CFURL for the supplied file system path.url=Cocoa.CFURLCreateWithFileSystemPath(None,path,Cocoa.kCFURLPOSIXPathStyle,False)ifurlisNone:print("Couldn't create url!")returnNone# Create a Quartz data provider for the URL.dataProvider=Quartz.CGDataProviderCreateWithURL(url)ifdataProviderisNone:print("Couldn't create data provider!")returnNonereturndataProviderdefcreateRGBRampDataProvider():width=256height=256imageDataSize=width*height*3dataP=objc.allocateBuffer(imageDataSize)# Build an image that is RGB 24 bits per sample. This is a ramp# where the red component value increases in red from left to# right and the green component increases from top to bottom.#idx=0forginrange(height):forrinrange(width):dataP[idx]=chr(r)ifsys.version_info[0]==2elserdataP[idx+1]=chr(g)ifsys.version_info[0]==2elsegdataP[idx+2]='\0'ifsys.version_info[0]==2else0idx+=3# Once this data provider is created, the data associated# with dataP MUST be available until Quartz calls the data# releaser function 'rgbReleaseRampData'.dataProvider=Quartz.CGDataProviderCreateWithData(None,dataP,imageDataSize,None)returndataProviderclassMyImageDataInfo(object):fp=NonetotalBytesRead=0skippedBytes=0numRewinds=0defgetBytesSequentialAccessDP(data,buffer,count):buf=data.fp.read(count)buffer[:len(buf)]=bufdata.totalBytesRead+=len(buf)returnlen(buf),bufferdefskipBytesSequentialAccessDP(data,count):try:data.fp.seek(count,os.SEEK_CUR)data.skippedBytes+=countexceptIOErrorasmsg:print("Couldn't seek %d bytes because of %s"%(count,msg))defrewindSequentialAccessDP(data):# Rewind the beginning of the data.data.fp.seek(0,0)data.numRewinds+=1defreleaseSequentialAccessDP(data):ifdataisnotNone:print("read %d bytes, skipped %d bytes, rewind called %d times"%(data.totalBytesRead,data.skippedBytes,data.numRewinds))data.fp.close()defcreateSequentialAccessDPForURL(url):success,pathString=Cocoa.CFURLGetFileSystemRepresentation(url,True,None,1024)pathString=pathString.rstrip(b'\0')ifnotsuccess:print("Couldn't get the path name C string!")returnNonefp=open(pathString,"rb")iffpisNone:print("Couldn't open path to file %s!"%(pathString,))returnNoneimageDataInfoP=MyImageDataInfo()imageDataInfoP.fp=fpprovider=Quartz.CGDataProviderCreate(imageDataInfoP,(getBytesSequentialAccessDP,skipBytesSequentialAccessDP,rewindSequentialAccessDP,releaseSequentialAccessDP))ifproviderisNone:print("Couldn't create data provider!")# Release the info data and cleanup.releaseSequentialAccessDP(imageDataInfoP)returnNonereturnproviderdefgetBytesGrayRampDirectAccess(info,buffer,offset,count):# This computes a linear gray ramp that is 256 samples wide and# 1 sample high. The ith byte in the image is the sample# value i. This produces a gray ramp that goes from 0 (black) to# FF (white).idx=0# This data provider provides 256 bytes total. If Quartz# requests more data than is available, only return# the available data.if(offset+count)>256:count=256-offsetforiinrange(offset,offset+count):buffer[idx]=chr(i)ifsys.version_info[0]==2elseiidx+=1returncount,bufferdefcreateGrayRampDirectAccessDP():provider=Quartz.CGDataProviderCreateDirectAccess(None,256,(None,None,getBytesGrayRampDirectAccess,None))ifproviderisNone:print("Couldn't create data provider!")returnNonereturnprovider# This only builds on Tiger and later.defmyCGDataProviderCreateWithCFData(data):# If the CFData object passed in is None, this code returns# a None data provider.ifdataisNone:returnNone# Test to see if the Quartz version is available and if so, use it.#XXX: force the replacment code to be used#if hasattr(Quartz, 'CGDataProviderCreateWithCFData'):# return CGDataProviderCreateWithCFData(data)dataSize=Cocoa.CFDataGetLength(data)provider=Quartz.CGDataProviderCreateWithData(data,buffer(data),dataSize,None)returnproviderdefcreateDataConsumerFromPathName(path):# Create a CFURL for the supplied file system path.url=Cocoa.CFURLCreateWithFileSystemPath(None,path,Cocoa.kCFURLPOSIXPathStyle,False)ifurlisNone:print("Couldn't create url!")returnNone# Create a Quartz data provider for the URL.dataConsumer=Quartz.CGDataConsumerCreateWithURL(url)ifdataConsumerisNone:print("Couldn't create data consumer!")returnNonereturndataConsumerdefmyCFDataConsumerPutBytes(data,buffer,count):# Append 'count' bytes from 'buffer' to the CFData# object 'data'.Cocoa.CFDataAppendBytes(data,buffer,count)returncount# This only builds on Tiger and later.defmyCGDataConsumerCreateWithCFData(data):# If the CFData object passed in is None, this code returns# a None data consumer.ifdataisNone:returnNone# Test to see if the Quartz version is available.# XXX: force the replacement code to be used:#if hasattr(Quartz, 'CGDataConsumerCreateWithCFData'):# return CGDataConsumerCreateWithCFData(data)consumer=Quartz.CGDataConsumerCreate(data,(myCFDataConsumerPutBytes,None))returnconsumer

DrawingBasics.py

importmath,sysimportQuartzdefdoSimpleRect(context):# Set the fill color to opaque red.Quartz.CGContextSetRGBFillColor(context,1.0,0.0,0.0,1.0)# Set up the rectangle for drawing.ourRect=Quartz.CGRectMake(20.0,20.0,130.0,100.0)# Draw the filled rectangle.Quartz.CGContextFillRect(context,ourRect)defdoStrokedRect(context):# Set the stroke color to a light opaque blue.Quartz.CGContextSetRGBStrokeColor(context,0.482,0.62,0.871,1.0)# Set up the rectangle for drawing.ourRect=Quartz.CGRectMake(20.0,20.0,130.0,100.0)# Draw the stroked rectangle with a line width of 3.Quartz.CGContextStrokeRectWithWidth(context,ourRect,3.0)defdoStrokedAndFilledRect(context):# Define a rectangle to use for drawing.ourRect=Quartz.CGRectMake(20.0,220.0,130.0,100.0)# ***** Rectangle 1 *****# Set the fill color to a light opaque blue.Quartz.CGContextSetRGBFillColor(context,0.482,0.62,0.871,1.0)# Set the stroke color to an opaque green.Quartz.CGContextSetRGBStrokeColor(context,0.404,0.808,0.239,1.0)# Fill the rect.Quartz.CGContextFillRect(context,ourRect)# ***** Rectangle 2 *****# Move the rectangle's origin to the right by 200 units.ourRect.origin.x+=200.0# Stroke the rectangle with a line width of 10.Quartz.CGContextStrokeRectWithWidth(context,ourRect,10.0)# ***** Rectangle 3 *****# Move the rectangle's origin to the left by 200 units# and down by 200 units.ourRect.origin.x-=200.0ourRect.origin.y-=200.0# Fill then stroke the rect with a line width of 10.Quartz.CGContextFillRect(context,ourRect)Quartz.CGContextStrokeRectWithWidth(context,ourRect,10.0)# ***** Rectangle 4 *****# Move the rectangle's origin to the right by 200 units.ourRect.origin.x+=200.0# Stroke then fill the rect.Quartz.CGContextStrokeRectWithWidth(context,ourRect,10.0)Quartz.CGContextFillRect(context,ourRect)defcreateRectPath(context,rect):# Create a path using the coordinates of the rect passed in.Quartz.CGContextBeginPath(context)Quartz.CGContextMoveToPoint(context,rect.origin.x,rect.origin.y)# ***** Segment 1 *****Quartz.CGContextAddLineToPoint(context,rect.origin.x+rect.size.width,rect.origin.y)# ***** Segment 2 *****Quartz.CGContextAddLineToPoint(context,rect.origin.x+rect.size.width,rect.origin.y+rect.size.height)# ***** Segment 3 *****Quartz.CGContextAddLineToPoint(context,rect.origin.x,rect.origin.y+rect.size.height)# ***** Segment 4 is created by closing the path *****Quartz.CGContextClosePath(context)defdoPathRects(context):# Define a rectangle to use for drawing.ourRect=Quartz.CGRectMake(20.0,20.0,130.0,100.0)# ***** Rectangle 1 *****# Create the rect path.createRectPath(context,ourRect)# Set the fill color to a light opaque blue.Quartz.CGContextSetRGBFillColor(context,0.482,0.62,0.871,1.0)# Fill the path.Quartz.CGContextDrawPath(context,Quartz.kCGPathFill)# Clears the path.# ***** Rectangle 2 *****# Translate the coordinate system 200 units to the right.Quartz.CGContextTranslateCTM(context,200.0,0.0)# Set the stroke color to an opaque green.Quartz.CGContextSetRGBStrokeColor(context,0.404,0.808,0.239,1.0)createRectPath(context,ourRect)# Set the line width to 10 units.Quartz.CGContextSetLineWidth(context,10.0)# Stroke the path.Quartz.CGContextDrawPath(context,Quartz.kCGPathStroke)# Clears the path.# ***** Rectangle 3 *****# Translate the coordinate system# 200 units to the left and 200 units down.Quartz.CGContextTranslateCTM(context,-200.0,-200.0)createRectPath(context,ourRect)#Quartz.CGContextSetLineWidth(context, 10.0) # This is redundant.# Fill, then stroke the path.Quartz.CGContextDrawPath(context,Quartz.kCGPathFillStroke)# Clears the path.# ***** Rectangle 4 *****# Translate the coordinate system 200 units to the right.Quartz.CGContextTranslateCTM(context,200.0,0.0)createRectPath(context,ourRect)# Stroke the path.Quartz.CGContextDrawPath(context,Quartz.kCGPathStroke)# Clears the path.# Create the path again.createRectPath(context,ourRect)# Fill the path.Quartz.CGContextDrawPath(context,Quartz.kCGPathFill)# Clears the path.defdoAlphaRects(context):# ***** Part 1 *****ourRect=Quartz.CGRectMake(0.0,0.0,130.0,100.0)numRects=6rotateAngle=2*math.pi/numRectstintAdjust=1.0/numRects# ***** Part 2 *****Quartz.CGContextTranslateCTM(context,2*ourRect.size.width,2*ourRect.size.height)# ***** Part 3 *****tint=1.0foriinrange(numRects):Quartz.CGContextSetRGBFillColor(context,tint,0.0,0.0,tint)Quartz.CGContextFillRect(context,ourRect)# These transformations are cummulative.Quartz.CGContextRotateCTM(context,rotateAngle)tint-=tintAdjustdefdrawStrokedLine(context,start,end):Quartz.CGContextBeginPath(context)Quartz.CGContextMoveToPoint(context,start.x,start.y)Quartz.CGContextAddLineToPoint(context,end.x,end.y)Quartz.CGContextDrawPath(context,Quartz.kCGPathStroke)defdoDashedLines(context):lengths=(12.0,6.0,5.0,6.0,5.0,6.0)start=Quartz.CGPoint(20.0,270.0)end=Quartz.CGPoint(300.0,270.0)# ***** Line 1 solid line *****Quartz.CGContextSetLineWidth(context,5.0)drawStrokedLine(context,start,end)# ***** Line 2 long dashes *****Quartz.CGContextTranslateCTM(context,0.0,-50.0)Quartz.CGContextSetLineDash(context,0.0,lengths,2)drawStrokedLine(context,start,end)# ***** Line 3 long short pattern *****Quartz.CGContextTranslateCTM(context,0.0,-50.0)Quartz.CGContextSetLineDash(context,0.0,lengths,4)drawStrokedLine(context,start,end)# ***** Line 4 long short short pattern *****Quartz.CGContextTranslateCTM(context,0.0,-50.0)Quartz.CGContextSetLineDash(context,0.0,lengths,6)drawStrokedLine(context,start,end)# ***** Line 5 short short long pattern *****Quartz.CGContextTranslateCTM(context,0.0,-50.0)Quartz.CGContextSetLineDash(context,lengths[0]+lengths[1],lengths,6)drawStrokedLine(context,start,end)# ***** Line 6 solid line *****Quartz.CGContextTranslateCTM(context,0.0,-50.0)# Reset dash to solid line.Quartz.CGContextSetLineDash(context,0,None,0)drawStrokedLine(context,start,end)defdoClippedCircle(context):circleCenter=Quartz.CGPoint(150.0,150.0)circleRadius=100.0startingAngle=0.0endingAngle=2*math.piourRect=Quartz.CGRectMake(65.0,65.0,170.0,170.0)# ***** Filled Circle *****Quartz.CGContextSetRGBFillColor(context,0.663,0.,0.031,1.0)Quartz.CGContextBeginPath(context)# Construct the circle path counterclockwise.Quartz.CGContextAddArc(context,circleCenter.x,circleCenter.y,circleRadius,startingAngle,endingAngle,0)Quartz.CGContextDrawPath(context,Quartz.kCGPathFill)# ***** Stroked Square *****Quartz.CGContextStrokeRect(context,ourRect)# Translate so that the next drawing doesn't overlap what# has already been drawn.Quartz.CGContextTranslateCTM(context,ourRect.size.width+circleRadius+5.0,0)# Create a rectangular path and clip to that path.Quartz.CGContextBeginPath(context)Quartz.CGContextAddRect(context,ourRect)Quartz.CGContextClip(context)# ***** Clipped Circle *****Quartz.CGContextBeginPath(context)# Construct the circle path counterclockwise.Quartz.CGContextAddArc(context,circleCenter.x,circleCenter.y,circleRadius,startingAngle,endingAngle,0)Quartz.CGContextDrawPath(context,Quartz.kCGPathFill)defdoPDFDocument(context,url):pdfDoc=Quartz.CGPDFDocumentCreateWithURL(url)ifpdfDocisnotNone:Quartz.CGContextScaleCTM(context,.5,.5)# The media box is the bounding box of the PDF document.pdfRect=Quartz.CGPDFDocumentGetMediaBox(pdfDoc,1)# page 1# Set the destination rect origin to the Quartz origin.pdfRect.origin.x=pdfRect.origin.y=0.# Draw page 1 of the PDF document.Quartz.CGContextDrawPDFDocument(context,pdfRect,pdfDoc,1)Quartz.CGContextTranslateCTM(context,pdfRect.size.width*1.2,0)# Scale non-uniformly making the y coordinate scale 1.5 times# the x coordinate scale.Quartz.CGContextScaleCTM(context,1,1.5)Quartz.CGContextDrawPDFDocument(context,pdfRect,pdfDoc,1)Quartz.CGContextTranslateCTM(context,pdfRect.size.width*1.2,pdfRect.size.height)# Flip the y coordinate axis horizontally about the x axis.Quartz.CGContextScaleCTM(context,1,-1)Quartz.CGContextDrawPDFDocument(context,pdfRect,pdfDoc,1)else:print("Can't create PDF document for URL!")

EPSPrinting.py

importCocoaimportQuartzimportobjcimportUtilitiesimportBitmapContextimportsys# We're using a function that isn't made available through a wrapper, just# load it manually:ifnothasattr(Quartz,'PMCGImageCreateWithEPSDataProvider'):functions=[('PMCGImageCreateWithEPSDataProvider',b'@@@'),]importAppKitd={}objc.loadBundleFunctions(AppKit.__bundle__,d,functions)if'PMCGImageCreateWithEPSDataProvider'ind:PMCGImageCreateWithEPSDataProvider=d['PMCGImageCreateWithEPSDataProvider']else:print("PMCGImageCreateWithEPSDataProvider doesn't exist")defgetEPSBBox(epspath):try:fp=open(epspath,'rU')exceptIOErrorasmsg:returnQuartz.CGRectZerotry:# This is a VERY poor man's EPS DSC parser, here just so that# this sample code can handle simple EPS files. It is# simple but very inefficient. In addition it does not ensure# that the DSC comments are at the beginning of a line,# nor does it handle (atend) style comments at all.# It will simply find the first occurance of a# %%BoundingBox comment and if it is of the typical# form, it will obtain the bounding box data.#forlninfp:ifln.startswith("%%BoundingBox:"):fields=ln.split()[1:]iflen(fields)>=4:llx=int(fields[0])lly=int(fields[1])urx=int(fields[2])ury=int(fields[3])returnQuartz.CGRectMake(llx,lly,urx-llx,ury-lly)finally:fp.close()returnQuartz.CGRectZerodefcreateEPSPreviewImage(url):# The CGImage used as the preview needs to have the# same width and height as the EPS data it will# be associated with. This sample code doesn't attempt# to use any preview image associated with the EPS# data but instead simply draws a box of an appropriate# size. Your code would most likely create an image# that reflects a PICT or TIFF preview present in the# EPS data.result,path=Cocoa.CFURLGetFileSystemRepresentation(url,True,None,1024)ifnotresult:print("Couldn't get the path for EPS file!")returnNonepath=path.rstrip(b'\0')epsRect=getEPSBBox(path)# Check whether the EPS bounding box is empty.ifepsRect==Quartz.CGRectZero:print("Couldn't find BoundingBox comment!")returnNonewantDisplayColorSpace=FalseneedsTransparentBitmap=True# Create a bitmap context to draw to in order to# create the preview image. Use the routine# createRGBBitmapContext from the earlier chapter.bitmapContext=BitmapContext.createRGBBitmapContext(epsRect.size.width,epsRect.size.height,wantDisplayColorSpace,needsTransparentBitmap)ifbitmapContextisNone:print("Couldn't create bitmap context")returnNoneepsRect.origin.x=epsRect.origin.y=0# Draw the contents of the preview. The preview consists# of two lines and a stroke around the bounding box. One# of the two lines is drawn from the lower-left corner to# the upper-right corner of the bounding box and the other# line is from the lower-right corner to the upper-left# corner of the bounding box.Quartz.CGContextBeginPath(bitmapContext)Quartz.CGContextMoveToPoint(bitmapContext,0,0)Quartz.CGContextAddLineToPoint(bitmapContext,epsRect.size.width,epsRect.size.height)Quartz.CGContextMoveToPoint(bitmapContext,epsRect.size.width,0)Quartz.CGContextAddLineToPoint(bitmapContext,0,epsRect.size.height)Quartz.CGContextStrokePath(bitmapContext)# Stroke the bounding rectangle, inset so that the stroke is# completely contained in the EPS bounding rect.Quartz.CGContextStrokeRect(bitmapContext,Quartz.CGRectInset(epsRect,0.5,0.5))# Now create an image from the bitmap raster data. This image# has a data provider that releases the image raster data when# the image is released. Use the createImageFromBitmapContext# from Chapter 12. Calling createImageFromBitmapContext# gives up ownership of the raster data used by the context.epsPreviewImage=BitmapContext.createImageFromBitmapContext(bitmapContext)ifepsPreviewImageisNone:print("Couldn't create preview image!")returnNonereturnepsPreviewImage# This technique of handling EPS data is available in# Mac OS X v10.1 and later and is one alternative method# of supporting EPS data during printing as compared to# converting EPS data to PDF data using CGPSConverter which# is only available in Panther and later.defcreateCGEPSImage(url):previewImage=createEPSPreviewImage(url)ifpreviewImageisNone:print("Couldn't create EPS preview!")returnNone# It is important that the data provider supplying the# EPS data conform to the Quartz guidelines for data providers# and is able to provide the data until the data releaser function# is called. If you have a custom data provider, you need# to follow these guidelines since your data provider# is not necessarily called before you release the image# that uses the provider.epsDataProvider=Quartz.CGDataProviderCreateWithURL(url)ifepsDataProviderisNone:print("Couldn't create EPS data provider!")returnNone# Create the hybrid CGImage that contains the preview image# and the EPS data. Note that the data provider isn't# called during image creation but at some later point in time.epsImage=PMCGImageCreateWithEPSDataProvider(epsDataProvider,previewImage)# The preview image and data provider are no longer needed# because Quartz retains them and this code doesn't# require them further.delpreviewImagedelepsDataProviderifepsImageisNone:print("Couldn't create EPS hybrid image!")returnNonereturnepsImagedefdrawEPSDataImage(context,url):# Create the a CGImage that has EPS data associated with it.epsDataImage=createCGEPSImage(url)ifepsDataImageisNone:return# Create a destination rectangle at the location# to draw the EPS document. The size of the rect is scaled# down to 1/2 the size of the EPS graphic.destinationRect=Quartz.CGRectMake(100,100,Quartz.CGImageGetWidth(epsDataImage),Quartz.CGImageGetHeight(epsDataImage))# Draw the image to the destination. When the EPS# data associated with the image is sent to a PostScript# printer, the EPS bounding box is mapped to this# destination rectangle, translated and scaled as necessary.Quartz.CGContextDrawImage(context,destinationRect,epsDataImage)# Draw the image a second time. This time the image is# rotated by 45 degrees and scaled by an additional scaling factor# of 0.5 in the x dimension. The center point of this image coincides# with the center point of the earlier drawing.Quartz.CGContextTranslateCTM(context,destinationRect.origin.x+destinationRect.size.width/2,destinationRect.origin.y+destinationRect.size.height/2)Quartz.CGContextRotateCTM(context,Utilities.DEGREES_TO_RADIANS(45))Quartz.CGContextScaleCTM(context,0.5,1)Quartz.CGContextTranslateCTM(context,-(destinationRect.origin.x+destinationRect.size.width/2),-(destinationRect.origin.y+destinationRect.size.height/2))Quartz.CGContextDrawImage(context,destinationRect,epsDataImage)

FrameworkTextDrawing.py

importQuartzimportCocoaimportUtilitiesimportQuartzTextDrawingimportsysimportobjcfromobjcimportsuperdefgetTextString():# These unicode values are the characters: Q, u, a, r, t, z,# eighthnote, floral heart, black chess queen, and two CJK characters.# Note: Create an NSString, because we'll use NSString-specific API's, otherwise# we could just have used a python unicode objectreturnCocoa.NSString.stringWithString_('\u0051\u0075\u0061\u0072\u0074\u007A\u266A\u2766\u265B\u3042\u304E')doPointDrawing=1defdrawNSStringWithAttributes():textString=getTextString()ifdoPointDrawing:context=Cocoa.NSGraphicsContext.currentContext().graphicsPort()# Text Line 1. Draw with default attributes.p=Cocoa.NSMakePoint(20.0,400.0)# Draw text with default text attributes. The point supplied is# not the text baseline but rather the lower-left corner of the box# which bounds the text.textString.drawAtPoint_withAttributes_(p,None)ifdoPointDrawing:Utilities.drawPoint(context,p)# Text Line 2. Draw with a specific font and color.# Position the text 50 units below the previous text.p.y-=50# Set attributes to use when drawing the string.stringAttributes={# Use the font with the PostScript name "Times-Roman" at 40 point.Cocoa.NSFontAttributeName:Cocoa.NSFont.fontWithName_size_("Times-Roman",40),# Set the color attribute to an opaque red.Cocoa.NSForegroundColorAttributeName:Cocoa.NSColor.colorWithCalibratedRed_green_blue_alpha_(0.663,0,0.031,1.0)}# Draw the text.textString.drawAtPoint_withAttributes_(p,stringAttributes)ifdoPointDrawing:Utilities.drawPoint(context,p)# Text Line 3. Draw stroked text.# Position the text 50 units below the previous text.p.y-=50# Panther and later support stroke attributes. A positive value# of the stroke width attribute produces text that is stroked rather# than filled.stringAttributes[Cocoa.NSStrokeWidthAttributeName]=3.0textString.drawAtPoint_withAttributes_(p,stringAttributes)ifdoPointDrawing:Utilities.drawPoint(context,p)# Text Line 4. Draw with fill and stroke.p.y-=50# Panther and later support stroke attributes. A negative value# of the stroke width attribute results in text that is both filled# and stroked.stringAttributes[Cocoa.NSStrokeWidthAttributeName]=-3.0# Set the stroke color attribute to black.stringAttributes[Cocoa.NSStrokeColorAttributeName]=Cocoa.NSColor.colorWithCalibratedRed_green_blue_alpha_(0,0,0,1.0)textString.drawAtPoint_withAttributes_(p,stringAttributes)ifdoPointDrawing:Utilities.drawPoint(context,p)# Text Line 5. Draw at baseline.# Tiger and later support the drawWithRect method which allows# string text drawing from a point on the text baseline.p.y-=50rect=Cocoa.NSRect(origin=p,size=Cocoa.NSSize(0,0),)textString.drawWithRect_options_attributes_(rect,Cocoa.NSStringDrawingDisableScreenFontSubstitution,stringAttributes)ifdoPointDrawing:Utilities.drawPoint(context,p)_myLayout=None_textStorage=None_myTextRange=NonedefdrawWithNSLayout():global_myLayout,_textStorage,_myTextRangeif_myLayoutisNone:# Initialize the text storage with the string to draw._textStorage=Cocoa.NSTextStorage.alloc().initWithString_(getTextString())# Initialize the layout manager to use with the text storage._myLayout=Cocoa.NSLayoutManager.alloc().init()# Allocate and initialize a text container object.textContainer=Cocoa.NSTextContainer.alloc().init()# Add the text container to the layout._myLayout.addTextContainer_(textContainer)# Release the text container since the layout retains it and# this code no longer needs it.deltextContainer# Add the layout to the text storage._textStorage.addLayoutManager_(_myLayout)# Set attributes to use when drawing the string.stringAttributes={# Use the font with the PostScript name "Times-Roman" at 40 point.Cocoa.NSFontAttributeName:Cocoa.NSFont.fontWithName_size_("Times-Roman",40),# Set the text color attribute to an opaque red.Cocoa.NSForegroundColorAttributeName:Cocoa.NSColor.colorWithCalibratedRed_green_blue_alpha_(0.663,0,0.031,1.0),}# Create the range of text for the entire length of text# in the textStorage object._myTextRange=Cocoa.NSMakeRange(0,_textStorage.length())# Set the attributes on the entire range of text._textStorage.setAttributes_range_(stringAttributes,_myTextRange)# Set the point for drawing the layout.p=Cocoa.NSMakePoint(20.0,400.0)# Draw the text range at the point._myLayout.drawGlyphsForGlyphRange_atPoint_(_myTextRange,p)ifdoPointDrawing:context=Cocoa.NSGraphicsContext.currentContext().graphicsPort()Utilities.drawPoint(context,p)# The interface to the NSLayoutManager subclass.classMyNSLayoutManager(Cocoa.NSLayoutManager):# The extra instance variables for this subclass._textMode=objc.ivar()_fColor=objc.ivar()_sColor=objc.ivar()_yStartPosition=objc.ivar()_lineWidth=objc.ivar()_clippingDrawProc=objc.ivar()_clippingInfo=objc.ivar()# Public methods to set the special attributes# of the MyNSLayoutManager instance.defsetTextMode_(self,textMode):self._textMode=textModedefsetFillColor_(self,color):self._fColor=colordefsetStrokeColor_(self,color):self._sColor=colordefsetTextLineWidth_(self,width):self._lineWidth=widthdefsetClippingDrawProc_withInfo_(self,clippingDrawProc,info):self._clippingDrawProc=clippingDrawProcself._clippingInfo=infodefinit(self):self=super(MyNSLayoutManager,self).init()ifselfisNone:returnNone# Initialize the custom instance variables.self._textMode=Quartz.kCGTextFillself._fColor=Noneself._sColor=Noneself._yStartPosition=0self._lineWidth=1self._clippingDrawProc=Noneself._clippingInfo=Nonereturnself# This code overrides this method to record the y coordinate# to use as the True baseline for the text drawing.defdrawGlyphsForGlyphRange_atPoint_(self,glyphsToShow,origin):self._yStartPosition=origin.ysuper(MyNSLayoutManager,self).drawGlyphsForGlyphRange_atPoint_(glyphsToShow,origin)# This is the rendering method of NSLayoutManager that the# code overrides to perform its custom rendering.defshowPackedGlyphs_length_glyphRange_atPoint_font_color_printAdjustment_(self,glyphs,glyphLen,glyphRange,point,font,color,printingAdjustment):# Obtain the destination drawing context.context=Cocoa.NSGraphicsContext.currentContext().graphicsPort()# Adjust start position y value based on the adjusted y coordinate.# This ensures the text baseline is at the starting position# passed to drawGlyphsForGlyphRange. This technique won't work# for super, subscripts, or underlines but that's OK for this example.point.y=_yStartPosition# The Quartz graphics state should be preserved by showPackedGlyphs.Quartz.CGContextSaveGState(context)# Set the desired text drawing mode.Quartz.CGContextSetTextDrawingMode(context,self._textMode)# Set the fill color if needed.if(self._textMode==Quartz.kCGTextFillor_self.textMode==Quartz.kCGTextFillStrokeorself._textMode==Quartz.kCGTextFillClipor_textMode==Quartz.kCGTextFillStrokeClip):ifself._fColorisnotNone:Quartz.CGContextSetFillColorWithColor(context,self._fColor)# Set the line width and the stroke color if needed.if(self._textMode==Quartz.kCGTextStrokeorself._textMode==Quartz.kCGTextFillStrokeorself._textMode==Quartz.kCGTextStrokeCliporself._textMode==Quartz.kCGTextFillStrokeClip):Quartz.CGContextSetLineWidth(context,self._lineWidth)ifself._sColorisnotNone:Quartz.CGContextSetStrokeColorWithColor(context,self._sColor)# Now draw the text. Check whether to adjust for printing widths# and if needed adjust extra character spacing accordingly.ifprintingAdjustment.width!=0.0:# If printingAdjustment width is non-zero then the text# needs to be adjusted. printingAdjustment is the per character# adjustment required for this piece of text. Because# the Quartz text character spacing set is transformed by# the text matrix, this code needs to factor out that effect# prior to setting it. Cocoa sets the text matrix to account# for the point size of the font so we factor that out of the# per character width supplied here.charAdjust=printingAdjustment.width/font.pointSize()Quartz.CGContextSetCharacterSpacing(context,charAdjust)else:Quartz.CGContextSetCharacterSpacing(context,0.0)# Draw the glyphs. The total number of glyphs is the length# of the glyphs string passed to showPackedGlyphs, divided by 2# since there are two bytes per glyph.Quartz.CGContextShowGlyphsAtPoint(context,point.x,point.y,glyphs,glyphLen/2)# If the text drawing mode requires clipping and there is# a custom clipping proc, call it. This allows drawing through# clipped text before the graphics state is restored.if(self._textMode==Quartz.kCGTextCliporself._textMode==Quartz.kCGTextFillCliporself._textMode==Quartz.kCGTextStrokeCliporself._textMode==Quartz.kCGTextFillStrokeClip)andself._clippingDrawProcisnotNone:self._clippingDrawProc(context,point.x,point.y,self._clippingInfo)Quartz.CGContextRestoreGState(context)defMyClipProc(c,x,y,info):Quartz.CGContextTranslateCTM(c,x,y)Quartz.CGContextSetStrokeColorWithColor(c,Utilities.getRGBOpaqueBlackColor())# Draw a grid of lines through the clip.QuartzTextDrawing.drawGridLines(c);_myLayout2=None_textStorage2=None_myTextRange2=NonedefdrawWithCustomNSLayout():global_myLayout2,_textStorage2,_myTextRange2if_myLayout2isNone:textContainer=Cocoa.NSTextContainer.alloc().init()_textStorage2=Cocoa.NSTextStorage.alloc().initWithString_(getTextString())# Create an instance of the MyNSLayoutManager subclass of NSLayoutManager._myLayout2=MyNSLayoutManager.alloc().init()_myLayout2.addTextContainer_(textContainer)# The layout retains the text container so this code can release it.deltextContainer_textStorage2.addLayoutManager_(_myLayout2)# Set attributes to use when drawing the string.stringAttributes={# Use the font with the PostScript name "Times-Roman" at 40 point.Cocoa.NSFontAttributeName:Cocoa.NSFont.fontWithName_size_("Times-Roman",40),}# Create the range._myTextRange2=Cocoa.NSMakeRange(0,_textStorage2.length())# Set the attributes on the entire range of text._textStorage2.setAttributes_range_(stringAttributes,_myTextRange2)p=Cocoa.NSMakePoint(20.0,400.0)# Set the custom attributes of the layout subclass so that# the text will be filled with black._myLayout2.setTextMode_(Quartz.kCGTextFill)_myLayout2.setFillColor_(Utilities.getRGBOpaqueBlackColor())# Draw text line 1._myLayout2.drawGlyphsForGlyphRange_atPoint_(_myTextRange2,p)ifdoPointDrawing:context=Cocoa.NSGraphicsContext.currentContext().graphicsPort()Utilities.drawPoint(context,p)# Set the custom attributes of the layout subclass so that# the text will be stroked with black._myLayout2.setTextMode_(Quartz.kCGTextStroke)_myLayout2.setStrokeColor_(Utilities.getRGBOpaqueBlackColor())_myLayout2.setTextLineWidth_(2)# Draw text line 2.p.y-=50;_myLayout2.drawGlyphsForGlyphRange_atPoint_(_myTextRange2,p)ifdoPointDrawing:Utilities.drawPoint(context,p)p.y-=50;# Set the custom attributes of the layout subclass so that# the text will be filled and stroked and the fill color# will be red. Since the stroke color hasn't changed it# will be stroked with black._myLayout2.setTextMode_(Quartz.kCGTextFillStroke)_myLayout2.setFillColor_(Utilities.getRGBOpaqueRedColor())# Draw text line 3._myLayout2.drawGlyphsForGlyphRange_atPoint_(_myTextRange2,p)ifdoPointDrawing:Utilities.drawPoint(context,p)p.y-=50;# Set the custom attributes of the layout subclass so that# the text will be filled, stroked, then clipped._myLayout2.setTextMode_(Quartz.kCGTextFillStrokeClip)# Set the clipping proc to MyClipProc which requires# no info data._myLayout2.setClippingDrawProc_withInfo_(MyClipProc,None)# Draw text line 4._myLayout2.drawGlyphsForGlyphRange_atPoint_(_myTextRange2,p)ifdoPointDrawing:Utilities.drawPoint(context,p)# Set the clipping proc to None for future drawing._myLayout2.setClippingDrawProc_withInfo_(None,None)

FrameworkUtilities.py

importCocoaimportAppDrawingdefmyCreatePDFDataFromPasteBoard():# Obtain the pasteboard to examine.pboard=Cocoa.NSPasteboard.generalPasteboard()# Scan the types of data on the pasteboard and return the first type available that is# listed in the array supplied. Here the array of requested types contains only one type,# that of PDF data. If your application could handle more types, you would list them in# the creation of this array.type=pboard.availableTypeFromArray_([Cocoa.NSPDFPboardType])# If the string is non-nil, there was data of one of our requested types# on the pasteboard that can be obtained.iftypeisnotNone:# Test that the type is the PDF data type. This code is not strictly necessary# for this example since we only said we could handle PDF data, but is appropriate# if you can handle more types than just PDF.iftype==Cocoa.NSPDFPboardType:# Get the PDF data from the pasteboard.pdfData=pboard.dataForType_(type)ifpdfDataisnotNone:returnpdfDataelse:Cocoa.NSLog("Couldn't get PDF data from pasteboard!")else:Cocoa.NSLog("Pasteboard doesn't contain PDF data!")returnNonedefaddPDFDataToPasteBoard(command):pdfData=cfDataCreatePDFDocumentFromCommand(command)ifpdfDataisnotNone:pboard=Cocoa.NSPasteboard.generalPasteboard()pboard.declareTypes_owner_(Cocoa.NSPDFPboardType,None)pboard.setData_forType_(pdfData,Cocoa.NSPDFPboardType)

ImageMasking.py

importCocoaimportQuartzimportImagesimportDataProvidersAndConsumersimportUtilitiesimportsysdefexportImageWithMaskFromURLWithDestination(context,imageURL,imagewidth,imageheight,bitsPerComponent,theMaskingImageURL,maskwidth,maskheight):imageBitsPerPixel=bitsPerComponent*3bytesPerRow=((imagewidth*imageBitsPerPixel)+7)/8shouldInterpolate=TrueimageDataProvider=Quartz.CGDataProviderCreateWithURL(imageURL)ifimageDataProviderisNone:print("Couldn't create Image Data provider!")returncolorspace=Utilities.getTheCalibratedRGBColorSpace()image=Quartz.CGImageCreate(imagewidth,imageheight,bitsPerComponent,imageBitsPerPixel,bytesPerRow,colorspace,Quartz.kCGImageAlphaNone,imageDataProvider,None,shouldInterpolate,Quartz.kCGRenderingIntentDefault)delimageDataProviderifimageisNone:print("Couldn't create CGImageRef for this data!")returnimageRect=Quartz.CGRectMake(0.0,imageheight,imagewidth,imageheight)# Draw the image.Quartz.CGContextDrawImage(context,imageRect,image)# Now the mask.maskDataProvider=Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)ifmaskDataProviderisNone:print("Couldn't create Image Data provider!")returnmask=Quartz.CGImageMaskCreate(maskwidth,maskheight,bitsPerComponent,bitsPerComponent,maskwidth,maskDataProvider,None,shouldInterpolate)delmaskDataProviderifmaskisNone:print("Couldn't create CGImageRef for mask data!")return# Draw the mask below the image.maskRect=Quartz.CGRectMake(0.0,0.0,maskwidth,maskheight)Quartz.CGContextDrawImage(context,maskRect,mask)# Create a new CGImage object, the image, masked with mask.imageMaskedWithImage=Quartz.CGImageCreateWithMask(image,mask)# Once the new image is created, we can release the image# and the mask which make it up. Quartz retains what it needs# for the new masked image.delimagedelmaskifimageMaskedWithImageisNone:print("Couldn't create image masked with mask!")returnimageRect=Quartz.CGRectMake(imagewidth,imageheight/2,imagewidth,imageheight)# Draw the masked image to the right of the image and its mask.Quartz.CGContextDrawImage(context,imageRect,imageMaskedWithImage)# Of course this is a total hack.outPath=b"/tmp/imageout.png"exportURL=Cocoa.CFURLCreateFromFileSystemRepresentation(None,outPath,len(outPath),False)ifexportURLisnotNone:Images.exportCGImageToPNGFileWithDestination(imageMaskedWithImage,exportURL)ifsys.version_info[0]==2:defmake_bytes(values):return''.join(map(chr,values))else:defmake_bytes(values):returnbytes(values)_data=make_bytes((0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF8,0x00,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0xF8,0xE7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x40,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x00,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFC,0x00,0x00,0x00,0x00,0x00,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xF8,0x00,0x00,0x00,0x00,0x00,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xF8,0x00,0x00,0x00,0x00,0x00,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x01,0xC0,0x00,0x00,0x7F,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x0F,0xF8,0x00,0x00,0x7F,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x0F,0xFF,0xF8,0x00,0x00,0x7F,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x1F,0xFF,0xFC,0x00,0x00,0x7F,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x7F,0xFF,0xFC,0x00,0x00,0x7F,0xFF,0xFF,0xF8,0x00,0x00,0x00,0xFF,0xFF,0xFC,0x00,0x00,0xFF,0xFF,0xFF,0xF8,0x00,0x00,0x03,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x03,0xFF,0xFF,0xFF,0x00,0x00,0x7F,0xFF,0xFF,0xF8,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x7F,0xFF,0xFF,0xF8,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x1F,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0xE0,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0xFF,0xE0,0x00,0x00,0x7F,0xFF,0xFF,0xFF,0x80,0x00,0x1F,0xFF,0xFF,0xE0,0x00,0x00,0x7F,0xFF,0xFF,0xFF,0x80,0x00,0x1F,0xFF,0xFF,0xE0,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0xC0,0x00,0x1F,0xFF,0xFF,0xF8,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0xE0,0x00,0x1F,0xFF,0xFF,0xF8,0x00,0x00,0x7F,0xFF,0xFF,0xFF,0xC0,0x00,0x1F,0xFF,0xFF,0xFC,0x00,0x00,0x7F,0xFF,0xFF,0xFE,0x40,0x00,0x0F,0xFF,0xFF,0xFC,0x00,0x00,0xFF,0xFF,0xFF,0xFE,0x00,0x00,0x1F,0xFF,0xFF,0xFC,0x00,0x00,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x1F,0xFF,0xFF,0xFC,0x00,0x01,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x3F,0xFF,0xFF,0xFC,0x00,0x05,0xEF,0xFF,0xFE,0x00,0x00,0x00,0x3F,0xFF,0xFF,0xF0,0x00,0x3F,0x00,0x03,0xFC,0x00,0x00,0x00,0x3F,0xFF,0xFF,0xE0,0x00,0x7C,0x00,0x00,0x78,0x1F,0x00,0x00,0x7F,0xFF,0xFF,0xC0,0x00,0x38,0x00,0x00,0x78,0x3C,0x00,0x01,0xFF,0xFF,0xFF,0xC0,0x00,0x78,0x00,0x00,0x70,0x18,0x00,0x01,0xFF,0xFF,0xFF,0xF0,0x00,0x78,0x1F,0x00,0x30,0x00,0x00,0x01,0xFF,0xFF,0xFF,0xFE,0x00,0x7C,0x3F,0x00,0x18,0x00,0x00,0x01,0xFF,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x03,0xFF,0xFF,0xFF,0xFE,0x00,0x00,0x80,0x00,0x3C,0x00,0x0C,0x03,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x3C,0x20,0x1C,0x03,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x04,0x00,0x3C,0x00,0x3C,0x03,0xFF,0xFF,0xFF,0xFF,0x00,0x70,0xBF,0x86,0x3C,0x1F,0xFC,0x0B,0xFF,0xFF,0xFF,0xFF,0xA0,0x11,0xF0,0x0E,0x3C,0x1F,0xFE,0x8B,0xFF,0xFF,0xFF,0xFF,0xA0,0x19,0xF0,0x0C,0x3C,0x0F,0xFF,0x0B,0xFF,0xFF,0xFF,0xFF,0xB0,0x1D,0xFE,0x1C,0x7E,0x0F,0xFF,0x03,0xFF,0xFF,0xFF,0xFF,0xB8,0x1C,0xFF,0x3C,0xFE,0x03,0xFE,0x03,0xFF,0xFF,0xFF,0xFF,0xFC,0x1E,0x7F,0xF8,0xDE,0x00,0x7C,0x03,0xFF,0xFF,0xFF,0xFF,0xFE,0x1E,0x7F,0xF1,0xDF,0x30,0x03,0x83,0xFF,0xFF,0xFF,0xFF,0xFE,0x1F,0x3F,0xE3,0x9F,0x10,0x3F,0x83,0xFF,0xFF,0xFF,0xFF,0xFE,0x0F,0xFF,0x83,0xDF,0x80,0x1F,0x83,0xFF,0xFF,0xFF,0xFF,0xFE,0x03,0xFC,0x03,0xDF,0x81,0x8F,0x83,0xFF,0xFF,0xFF,0xFF,0xFE,0x07,0xFE,0x1F,0x8F,0x00,0x07,0x83,0xFF,0xFF,0xFF,0xFF,0xFF,0x07,0xFE,0x3C,0x06,0x00,0x01,0x83,0xFF,0xFF,0xFF,0xFF,0xFF,0x03,0xFC,0x7C,0x00,0x00,0x01,0x83,0xFF,0xFF,0xFF,0xFF,0xFF,0x01,0xF8,0x7F,0x00,0x00,0x01,0x83,0xFF,0xFF,0xFF,0xFF,0xFF,0x01,0xF8,0xFF,0xE0,0x30,0x01,0x83,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0xF1,0xEF,0xF9,0xE0,0x03,0x83,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0xF1,0xFF,0xFF,0x80,0x0F,0x83,0xFF,0xFF,0xFF,0xFF,0xFC,0x03,0xE2,0xFF,0xFE,0x00,0x1F,0x87,0xFF,0xFF,0xFF,0xFF,0xFF,0x83,0xF0,0x00,0x00,0x1C,0x3F,0x87,0xFF,0xFF,0xFF,0xFF,0xFF,0xC3,0xF0,0x00,0x01,0xF8,0x0F,0x87,0xFF,0xFF,0xFF,0xFF,0xFF,0xC3,0xF0,0x03,0xFF,0xF0,0x5F,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xC1,0xFF,0xC7,0xFF,0xE0,0x7F,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xE1,0xFF,0xF1,0xFF,0x80,0x2F,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xE1,0xFF,0xF8,0x0F,0xC0,0x06,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF4,0xFF,0xFE,0x0F,0xF8,0x44,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF4,0xFF,0xFF,0xFF,0xF8,0x64,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF9,0xFF,0xFF,0xFF,0x3C,0xE4,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0x9F,0xFF,0xFC,0x1F,0xC0,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0x1F,0xFF,0xFC,0x03,0xC0,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x01,0xFF,0xFF,0xFF,0xC0,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x00,0xFF,0xFF,0xFF,0x00,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x00,0xFF,0xFF,0xFF,0x00,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x80,0x7F,0xFF,0xFF,0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x80,0x1F,0xFF,0xFF,0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xC0,0x0F,0xFF,0xFF,0x00,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0x07,0xFF,0xFF,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x03,0xFF,0xFF,0x00,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x70,0x01,0xFF,0xFC,0x00,0x17,0xFF,0xFF,0xFF,0xFF,0xFF,0xFC,0x78,0x00,0x7F,0xF0,0x00,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xFC,0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0x03,0xFE,0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0xFF,0xFE,0x1F,0xFF,0x80,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0xFF,0xFC,0x7F,0x7F,0xC0,0x00,0x00,0x00,0x07,0xFF,0xFF))defgetMaskData1():return_data_data2=make_bytes((0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFC,0x00,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0x00,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFC,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF8,0x00,0x00,0x00,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFC,0x00,0x00,0x00,0x00,0x00,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF8,0x00,0x00,0x00,0x00,0x00,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF8,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x01,0x80,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x03,0xC0,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x0B,0xE0,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x07,0xF0,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x1F,0xF4,0x83,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x3F,0xE4,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x3F,0xE4,0x43,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x00,0x00,0x00,0x3F,0xE4,0x4B,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x00,0x00,0x02,0xFF,0xE4,0x5B,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x02,0xFF,0xE0,0x5B,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x07,0xC1,0xFF,0xE0,0x59,0xFF,0xFF,0xFF,0xFF,0xFF,0x18,0x00,0x7F,0xF0,0xFE,0x00,0x79,0xFF,0xFF,0xFF,0xFF,0xFE,0x18,0x00,0x78,0x0F,0xFE,0x04,0xE1,0xFF,0xFF,0xFF,0xFF,0xFE,0x18,0x00,0xB0,0x47,0xFF,0xFF,0xE1,0xFF,0xFF,0xFF,0xFF,0xFE,0x10,0x00,0xC4,0x69,0xFF,0xFF,0xC3,0xFF,0xFF,0xFF,0xFF,0xFE,0x10,0x01,0xFF,0xE1,0xFC,0x07,0xC1,0xFF,0xFF,0xFF,0xFF,0xFC,0x00,0x01,0xFF,0xF8,0x78,0x01,0xC5,0xFF,0xFF,0xFF,0xFF,0xFC,0x04,0x01,0xFF,0xF0,0x78,0x01,0xC5,0xFF,0xFF,0xFF,0xFF,0xFC,0x0C,0x00,0xFF,0xF8,0x7E,0x3F,0xC1,0xFF,0xFF,0xFF,0xFF,0xFC,0x0C,0x00,0x7F,0xF0,0x18,0xFF,0xC3,0xFF,0xFF,0xFF,0xFF,0xFC,0x07,0x00,0x7F,0xF4,0x1F,0xFF,0xE3,0xFF,0xFF,0xFF,0xFF,0xFC,0x23,0x00,0x7F,0xE0,0x3E,0xFF,0xE3,0xFF,0xFF,0xFF,0xFF,0xF8,0x11,0x00,0x7F,0xEC,0x5F,0xBF,0xE3,0xFF,0xFF,0xFF,0xFF,0xF8,0x01,0x00,0x3F,0xCE,0x7E,0x3F,0xE3,0xFF,0xFF,0xFF,0xFF,0xF8,0x08,0x00,0x7F,0x80,0x2E,0x3F,0xE3,0xFF,0xFF,0xFF,0xFF,0xF8,0x06,0x00,0x7F,0x00,0x6E,0x3F,0xEB,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x7E,0x0D,0xFE,0xFF,0xEB,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x3C,0x00,0xFE,0x3F,0xEB,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x50,0x00,0xFE,0x3F,0xCB,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x01,0x98,0xFF,0x3F,0xCB,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0xC7,0xE1,0xFF,0x3F,0x8B,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x40,0xFF,0xFF,0xBF,0x8B,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0xE0,0x1F,0xFF,0xDF,0x0B,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0xE8,0x63,0xFF,0xDF,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x02,0xFC,0xF9,0xFF,0xEF,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x03,0xFE,0x7F,0xF8,0x1E,0x0F,0xFF,0xFF,0xFF,0xFF,0xE0,0x00,0x03,0x7E,0x0F,0xF9,0xBE,0x05,0xFF,0xFF,0xFF,0xFF,0xE0,0x00,0x01,0x7F,0xC1,0xF3,0xFC,0x05,0xFF,0xFF,0xFF,0xFF,0xE0,0x00,0x01,0x3D,0xF8,0x0F,0x7C,0x05,0xFF,0xFF,0xFF,0xFF,0xE0,0x00,0x01,0xBC,0x7F,0xFF,0xF8,0x02,0xFF,0xFF,0xFF,0xFF,0xE0,0x00,0x00,0xBE,0xFF,0xFF,0xF8,0x02,0xFF,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x1D,0xFF,0xFF,0xF0,0x00,0xFF,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x0F,0xF7,0xFF,0xE0,0x00,0x7F,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x0F,0xF3,0xFF,0xE0,0x00,0x7F,0xFF,0xFF,0xFF,0x80,0x00,0x00,0x03,0xF3,0xFF,0xC0,0x00,0x7F,0xFF,0xFF,0xFF,0x80,0x00,0x00,0x01,0xF7,0xFF,0x80,0x00,0x3F,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x3F,0xFF,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x1F,0xFF,0x00,0x20,0x3F,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x1F,0xFF,0x00,0x10,0x3F,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x1F,0xFF,0x00,0x10,0x3F,0xFF,0xFF,0xFC,0x00,0x00,0x02,0x00,0x0F,0xFF,0x00,0x00,0x1F,0xFF,0xFF,0xFC,0x00,0x00,0x04,0x00,0x1F,0xFE,0x00,0x00,0x1F,0xFF,0xFF,0xF8,0x00,0x00,0x07,0x81,0x7F,0xFE,0x00,0x00,0x1F,0xFF,0xFF,0xF8,0x00,0x00,0x03,0xDF,0xFF,0xFE,0x00,0x00,0x1F,0xFF,0xFF,0xF8,0x00,0x00,0x07,0xFF,0xFF,0xFE,0x00,0x00,0x1F,0xFF,0xFF,0xF0,0x00,0x00,0x07,0xFF,0xFF,0xFE,0x00,0x00,0x1F,0xFF,0xFF,0xF0,0x00,0x40,0x07,0xFF,0xFF,0xFE,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0xC0,0x03,0xFF,0xFF,0xFE,0x00,0x00,0x0F,0xFF,0xFF,0xE0,0x00,0xE0,0x07,0xFF,0xFF,0xFE,0x81,0x00,0x07,0xFF,0xFF,0xC0,0x01,0xE0,0x07,0xFF,0xFF,0xFE,0x01,0x00,0x07,0xFF,0xFF,0x80,0x0F,0xF0,0x03,0xFF,0xFF,0xFE,0x83,0x80,0x03,0xFF,0xFF,0x00,0x1F,0xF0,0x13,0xFF,0xFF,0xFE,0x03,0xE0,0x01,0xFF,0xFC,0x03,0x3F,0xF0,0x21,0xFF,0xFF,0xFE,0x03,0xFC,0x00,0x3F,0xF0,0x3F,0x3F,0xF8,0x3B,0xFF,0xFF,0xFE,0x03,0xFE,0xC0,0x0F,0xE3,0xFB,0x7F,0xF8,0x3B,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0x07,0x9F,0xFB,0x7F,0xFC,0x79,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFC,0x39,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFE,0x3F,0xFF,0xFF,0xFE,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFE,0x1F,0xFF,0xFF,0xFE,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFE,0x1F,0x7F,0xFF,0xFE,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF,0x1F,0xFE,0xFF,0xFC,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x1F,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0x87,0xFF,0xFF,0xFE,0x1F,0xFF,0xEF,0xFF,0xFF,0xFF,0xBF,0xFF,0x82,0xFF,0xFF,0xFC,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF,0xFF,0x83,0xFF,0xFF,0xFC,0x3F,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC1,0xFF,0xFF,0xF8,0x7F,0xFF,0xBF,0xFF,0xFF,0xFF,0xBF,0xFF,0xE0,0xFF,0xFF,0xF0,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF))defgetMaskData2():return_data2defdoOneBitMaskImages(context):bitsPerComponent=1bitsPerPixel=1width=96height=96bytesPerRow=12imageDataSize=bytesPerRow*heightshouldInterpolate=TruelightBlue=[0.482,0.62,0.871,1.0]black=[0.0,0.0,0.0,1.0]darkRed=[0.663,0.,0.031,1.0]darkGreen=[0.404,0.808,0.239,1.0]darkBlue=[0.11,0.208,0.451,1.0]purple=[0.69,0.486,0.722,1.0]darkOrange=[0.965,0.584,0.059,1.0]# A decode array contains two elements for each component. In this# case, an image mask has one component so the array consists of# two values. When using this decode array, a sample value of 0# is mapped into the value 1, and the maximum sample value is# mapped into the value 0. This inverts the sense of the mask data.decode=(1,0)# Create a Quartz data provider for the image data. Because this# data is static data, we don't need to release it so the data# release function is None.data=getMaskData1()dataProvider=Quartz.CGDataProviderCreateWithData(None,data,imageDataSize,None)ifdataProviderisNone:print("Couldn't create Mask1 Data provider!")return# Create a mask from the data.mask1=Quartz.CGImageMaskCreate(width,height,bitsPerComponent,bitsPerPixel,bytesPerRow,dataProvider,None,shouldInterpolate)# Create the same mask but with a decode array that# inverts the sense of the mask.invertedmask1=Quartz.CGImageMaskCreate(width,height,bitsPerComponent,bitsPerPixel,bytesPerRow,dataProvider,decode,shouldInterpolate)# Release the data provider now that this code no longer needs it.deldataProviderifmask1isNoneorinvertedmask1isNone:ifmask1isNone:print("Couldn't create CGImageRef for the mask data 1!")ifinvertedmask1isNone:print("Couldn't create CGImageRef for the inverted mask data 1!")return# Get the pointer to the data for the second mask.data=getMaskData2()dataProvider=Quartz.CGDataProviderCreateWithData(None,data,imageDataSize,None)ifdataProviderisNone:print("Couldn't create Mask2 Data provider!")returnmask2=Quartz.CGImageMaskCreate(width,height,bitsPerComponent,bitsPerPixel,bytesPerRow,dataProvider,None,shouldInterpolate)# Create the same mask but with a decode array that# inverts the sense of the mask.invertedmask2=Quartz.CGImageMaskCreate(width,height,bitsPerComponent,bitsPerPixel,bytesPerRow,dataProvider,decode,shouldInterpolate)# Release the data provider now that this code no longer needs it.deldataProviderifmask2isNoneorinvertedmask2isNone:ifmask2isNone:print("Couldn't create CGImageRef for the mask data 2!")ifinvertedmask2isNone:print("Couldn't create CGImageRef for the inverted mask data 2!")returnQuartz.CGContextScaleCTM(context,1.5,1.5)colorSpace=Utilities.getTheCalibratedRGBColorSpace()Quartz.CGContextSetFillColorSpace(context,colorSpace);# Set the fill color to a light blue.Quartz.CGContextSetFillColor(context,lightBlue)# Paint part of the background.backRect=Quartz.CGRectMake(width/2,height/2,width*3,height)Quartz.CGContextFillRect(context,backRect)imageRect=Quartz.CGRectMake(0.,height,width,height)Quartz.CGContextSaveGState(context)# Set the fill color to opaque black.Quartz.CGContextSetFillColor(context,black)# Mask 1.Quartz.CGContextDrawImage(context,imageRect,mask1)Quartz.CGContextTranslateCTM(context,width,0)# Set the fill color to opaque red.Quartz.CGContextSetFillColor(context,darkRed);# Mask 2.Quartz.CGContextDrawImage(context,imageRect,mask2)Quartz.CGContextTranslateCTM(context,width,0)# Set the fill color to dark orange.Quartz.CGContextSetFillColor(context,darkOrange)# Mask 3.Quartz.CGContextDrawImage(context,imageRect,mask1)Quartz.CGContextTranslateCTM(context,width,0)# Make the orange 50% transparent.darkOrange[3]=0.5Quartz.CGContextSetFillColor(context,darkOrange)# Mask 4.Quartz.CGContextDrawImage(context,imageRect,mask2)Quartz.CGContextRestoreGState(context)# Translate down the page. The cast is necessary# since height is typed as size_t which is unsigned.Quartz.CGContextTranslateCTM(context,0,-height)# Set the fill color to an opaque green.Quartz.CGContextSetFillColor(context,darkGreen)# Mask 5.Quartz.CGContextDrawImage(context,imageRect,invertedmask2)Quartz.CGContextTranslateCTM(context,width,0)# Set the fill color to a dark blue.Quartz.CGContextSetFillColor(context,darkBlue)# Mask 6.Quartz.CGContextDrawImage(context,imageRect,invertedmask1)Quartz.CGContextTranslateCTM(context,width,0)# Set the fill color to purple.Quartz.CGContextSetFillColor(context,purple)# Mask 7.Quartz.CGContextDrawImage(context,imageRect,invertedmask2)Quartz.CGContextTranslateCTM(context,width,0)# Make the purple 50% transparent.purple[3]=0.5Quartz.CGContextSetFillColor(context,purple)# Mask 8.Quartz.CGContextDrawImage(context,imageRect,invertedmask1)defdoMaskImageWithMaskFromURL(context,imageURL,imagewidth,imageheight,bitsPerComponent,theMaskingImageURL,maskwidth,maskheight):imageBitsPerPixel=bitsPerComponent*3bytesPerRow=((imagewidth*imageBitsPerPixel)+7)/8shouldInterpolate=TrueimageDataProvider=Quartz.CGDataProviderCreateWithURL(imageURL)ifimageDataProviderisNone:print("Couldn't create Image Data provider!")returncolorspace=Utilities.getTheCalibratedRGBColorSpace()image=Quartz.CGImageCreate(imagewidth,imageheight,bitsPerComponent,imageBitsPerPixel,bytesPerRow,colorspace,Quartz.kCGImageAlphaNone,imageDataProvider,None,shouldInterpolate,Quartz.kCGRenderingIntentDefault)delimageDataProviderifimageisNone:print("Couldn't create CGImageRef for this data!")returnimageRect=Quartz.CGRectMake(0.0,imageheight,imagewidth,imageheight)# Draw the image.Quartz.CGContextDrawImage(context,imageRect,image)# Now the mask.maskDataProvider=Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)ifmaskDataProviderisNone:print("Couldn't create Image Data provider!")returnmask=Quartz.CGImageMaskCreate(maskwidth,maskheight,bitsPerComponent,bitsPerComponent,maskwidth,maskDataProvider,None,shouldInterpolate)delmaskDataProviderifmaskisNone:print("Couldn't create CGImageRef for mask data!")return# Draw the mask below the image. The current fill color (black)# is painted through the mask.maskRect=Quartz.CGRectMake(0.0,0.0,maskwidth,maskheight)Quartz.CGContextDrawImage(context,maskRect,mask)# Create a new CGImage object, the image, masked with mask.imageMaskedWithImage=Quartz.CGImageCreateWithMask(image,mask)# Once the new image is created, the code can release the image# and the mask which make it up. Quartz retains what it needs# for the new masked image 'imageMaskedWithImage'.delimagedelmaskifimageMaskedWithImageisNone:print("Couldn't create image masked with mask!")returnimageRect=Quartz.CGRectMake(imagewidth+10,imageheight/2,imagewidth,imageheight)# Draw the masked image to the right of the image and its mask.Quartz.CGContextDrawImage(context,imageRect,imageMaskedWithImage)defdoMaskImageWithGrayImageFromURL(context,imageURL,imagewidth,imageheight,bitsPerComponent,theMaskingImageURL,maskwidth,maskheight):imageBitsPerPixel=bitsPerComponent*3bytesPerRow=((imagewidth*imageBitsPerPixel)+7)/8shouldInterpolate=TrueimageDataProvider=Quartz.CGDataProviderCreateWithURL(imageURL)ifimageDataProviderisNone:print("Couldn't create Image Data provider!")returncolorspace=Utilities.getTheCalibratedRGBColorSpace()image=Quartz.CGImageCreate(imagewidth,imageheight,bitsPerComponent,imageBitsPerPixel,bytesPerRow,colorspace,Quartz.kCGImageAlphaNone,imageDataProvider,None,shouldInterpolate,Quartz.kCGRenderingIntentDefault)delimageDataProviderifimageisNone:print("Couldn't create CGImageRef for this data!")returnimageRect=Quartz.CGRectMake(0.,imageheight,imagewidth,imageheight)# Draw the image.Quartz.CGContextDrawImage(context,imageRect,image)# Now the mask.maskDataProvider=Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)ifmaskDataProviderisNone:print("Couldn't create Image Data provider!")return# The color space for the image MUST be DeviceGray for it to# be used as a masking image with CGImageCreateWithMask.deviceGraySpace=Quartz.CGColorSpaceCreateDeviceGray();mask=Quartz.CGImageCreate(maskwidth,maskheight,bitsPerComponent,bitsPerComponent,maskwidth,deviceGraySpace,Quartz.kCGImageAlphaNone,maskDataProvider,None,shouldInterpolate,Quartz.kCGRenderingIntentDefault)# Release the color space since it is no longer needed.deldeviceGraySpacedelmaskDataProviderifmaskisNone:print("Couldn't create CGImageRef for gray image data!")return# Draw the mask below the image. The current fill color (black)# is painted through the mask.maskRect=Quartz.CGRectMake(0.,0.,maskwidth,maskheight)Quartz.CGContextDrawImage(context,maskRect,mask)# Create a new CGImage object, the image, masked with mask.imageMaskedWithImage=Quartz.CGImageCreateWithMask(image,mask)# Once the new image is created, the code can release the image# and the mask which make it up. Quartz retains what it needs# for the new masked image 'imageMaskedWithImage'.delimagedelmaskifimageMaskedWithImageisNone:print("Couldn't create image masked with mask!")returnimageRect=Quartz.CGRectMake(imagewidth+10,imageheight/2,imagewidth,imageheight)# Draw the masked image to the right of the image and its mask.Quartz.CGContextDrawImage(context,imageRect,imageMaskedWithImage)# Be sure and release the masked image.delimageMaskedWithImagedefdoMaskImageWithColorFromURL(context,url,width,height,isColor):# This routine treats color images as RGB.bitsPerComponent=8ifisColor:bitsPerPixel=bitsPerComponent*3else:bitsPerPixel=bitsPerComponentbytesPerRow=((width*bitsPerPixel)+7)/8shouldInterpolate=True# This is a range of dark gray to black colors for an 8 bit per component# image in a gray or RGB color space. The entries are image sample# values of 0-0x1F for the first color component, 0-0x1F for the# second color component, and so on. For image sample values where# all components fall within the ranges in maskingColors, the sample# value is masked and therefore unpainted.maskingColors=(0x00,0x1F,0x00,0x1F,0x00,0x1F)backColor=(1.,0.,0.,1.)# Opaque red.# Create a Quartz data provider from the supplied URL.dataProvider=Quartz.CGDataProviderCreateWithURL(url)ifdataProviderisNone:print("Couldn't create Image data provider!")return# Create an image of the specified width, height and bits per pixel# from the URL.ifisColor:colorspace=Utilities.getTheCalibratedRGBColorSpace()else:colorspace=Utilities.getTheCalibratedGrayColorSpace()image=Quartz.CGImageCreate(width,height,bitsPerComponent,bitsPerPixel,bytesPerRow,colorspace,Quartz.kCGImageAlphaNone,dataProvider,None,shouldInterpolate,Quartz.kCGRenderingIntentDefault)deldataProviderifimageisNone:print("Couldn't create CGImageRef for this data!")returnimageRect=Quartz.CGRectMake(10.,10.,width,height)#Quartz.CGContextScaleCTM(context, 0.33, 0.33)# Set the color space and the color, then# paint a red rectangle behind the image.Quartz.CGContextSetFillColorSpace(context,colorspace)Quartz.CGContextSetFillColor(context,backColor)Quartz.CGContextFillRect(context,imageRect)# Draw the image into the rectangle.Quartz.CGContextDrawImage(context,imageRect,image)# Create a new image from the original one, masking out a range# of the blackest sample values.imageMaskedWithColor=Quartz.CGImageCreateWithMaskingColors(image,maskingColors)# Release the original image; it is no longer needed.delimageifimageMaskedWithColorisNone:print("Couldn't create CGImageRef for masking color!")return# Paint the rectangle behind the next image with red.imageRect=Quartz.CGRectMake(30.+width,10.,width,height)Quartz.CGContextFillRect(context,imageRect)# Draw the image. Image sample values in the range of# the masking color are unpainted, allowing the background# to show through.Quartz.CGContextDrawImage(context,imageRect,imageMaskedWithColor)if1:# Set to 1 for code in the book.defdrawWithClippingMask(context,theMaskingImageURL,imagewidth,imageheight):# An array of CGColor objects.colors=(Utilities.getRGBOpaqueDarkGreenColor(),Utilities.getRGBOpaqueDarkBlueColor(),Utilities.getRGBOpaqueBlueColor(),Utilities.getRGBOpaqueRedColor())imageBitsPerComponent=8bytesPerRow=imagewidthshouldInterpolate=Truedecode=(1,0)# Create the data.dataProvider=Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)ifdataProviderisNone:print("Couldn't create Image data provider!")returncs=Quartz.CGColorSpaceCreateDeviceGray()image=Quartz.CGImageCreate(imagewidth,imageheight,imageBitsPerComponent,imageBitsPerComponent,bytesPerRow,cs,Quartz.kCGImageAlphaNone,dataProvider,decode,shouldInterpolate,Quartz.kCGRenderingIntentDefault)delcsdeldataProviderifimageisNone:print("Couldn't create Image!")returnimageRect=Quartz.CGRectMake(0,0,imagewidth*2/3,imageheight*2/3)# Position for drawing the image at the left side of the figure.Quartz.CGContextTranslateCTM(context,50,50)# Draw the image.Quartz.CGContextDrawImage(context,imageRect,image)# Position to the right of the image just painted.Quartz.CGContextTranslateCTM(context,Quartz.CGRectGetWidth(imageRect)+25,0)# Clip to the image.Quartz.CGContextClipToMask(context,imageRect,image)# Release the image since this code no longer needs it.delimage# Make a rect that has a width and height 1/3 that of the image.rect=Quartz.CGRectMake(0,0,Quartz.CGRectGetWidth(imageRect)/3,Quartz.CGRectGetHeight(imageRect)/3)Quartz.CGContextTranslateCTM(context,0,2*Quartz.CGRectGetHeight(rect))# Draw a 3 x 3 grid of rectangles, setting the color for each rectangle# by cycling through the array of CGColor objects in the 'colors' array.forjinrange(3):Quartz.CGContextSaveGState(context)foriinrange(3):# Draw a row of rectangles.# Set the fill color using one of the CGColor objects in the# colors array.Quartz.CGContextSetFillColorWithColor(context,colors[(i+j)%4])Quartz.CGContextFillRect(context,rect)Quartz.CGContextTranslateCTM(context,Quartz.CGRectGetWidth(rect),0)Quartz.CGContextRestoreGState(context)# Position to draw the next row.Quartz.CGContextTranslateCTM(context,0,-Quartz.CGRectGetHeight(rect))else:# This code works just fine to screen but when drawing to a PDF# or printing context the masked drawing is completely masked out# due to a bug in Quartz prior to Tiger 10.4.3.defdrawWithClippingMask(context,theMaskingImageURL,maskwidth,maskheight):# An array of Quartz.CGColor objects.colors=(Utilities.getRGBOpaqueDarkGreenColor(),Utilities.getRGBOpaqueDarkBlueColor(),Utilities.getRGBOpaqueBlueColor(),Utilities.getRGBOpaqueRedColor())maskBitsPerComponent=8bytesPerRow=((maskwidth*maskBitsPerComponent)+7)/8shouldInterpolate=TruemaskDataProvider=Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)ifmaskDataProviderisNone:print("Couldn't create Image Mask provider!")returnmask=Quartz.CGImageMaskCreate(maskwidth,maskheight,maskBitsPerComponent,maskBitsPerComponent,maskwidth,maskDataProvider,None,shouldInterpolate)delmaskDataProviderifmaskisNone:print("Couldn't create Image Mask!")returnmaskRect=Quartz.CGRectMake(0,0,maskwidth/3,maskheight/3)# Position for drawing the mask at the left side of the figure.Quartz.CGContextTranslateCTM(context,50,50)# Set the context fill color to a Quartz.CGColor object that is black.Quartz.CGContextSetFillColorWithColor(context,getRGBOpaqueBlackColor())# Draw the mask. It is painted with with the black fill color.Quartz.CGContextDrawImage(context,maskRect,mask)# Position to the right of the mask just painted.Quartz.CGContextTranslateCTM(context,Quartz.CGRectGetWidth(maskRect)+25,0)# Clip to the mask.Quartz.CGContextClipToMask(context,maskRect,mask)# Release the mask since this code no longer needs it.delmask# Make a rect that has a width and height 1/3 that of the image mask.rect=Quartz.CGRectMake(0,0,Quartz.CGRectGetWidth(maskRect)/3,Quartz.CGRectGetHeight(maskRect)/3)Quartz.CGContextTranslateCTM(context,0,2*Quartz.CGRectGetHeight(rect))# Draw a 3 x 3 grid of rectangles, setting the color for each rectangle# by cycling through the array of CGColor objects in the 'colors' array.forjinrange(3):Quartz.CGContextSaveGState(context)foriinrange(3):# Draw a row of rectangles.# Set the fill color using one of the CGColor objects in the# colors array.Quartz.CGContextSetFillColorWithColor(context,colors[(i+j)%4])Quartz.CGContextFillRect(context,rect)Quartz.CGContextTranslateCTM(context,Quartz.CGRectGetWidth(rect),0)Quartz.CGContextRestoreGState(context)# Position to draw the next row.Quartz.CGContextTranslateCTM(context,0,-Quartz.CGRectGetHeight(rect))

Images.py

importsysimportDataProvidersAndConsumersimportUtilitiesimportCocoaimportQuartzfromLaunchServicesimport*# kUTType* constantsdefdrawJPEGImage(context,url):# Create a Quartz data provider for the supplied URL.jpgProvider=Quartz.CGDataProviderCreateWithURL(url)ifjpgProviderisNone:print("Couldn't create JPEG Data provider!")return# Create the CGImageRef for the JPEG image from the data provider.jpgImage=Quartz.CGImageCreateWithJPEGDataProvider(jpgProvider,None,True,Quartz.kCGRenderingIntentDefault)# CGImageCreateWithJPEGDataProvider retains the data provider.# Since this code created the data provider and this code no# longer needs it, it must release it.deljpgProviderifjpgImageisNone:print("Couldn't create CGImageRef for JPEG data!")return# Make a rectangle that has its origin at (0,0) and# has a width and height that is 1/4 the native width# and height of the image.jpgRect=Quartz.CGRectMake(0.0,0.0,Quartz.CGImageGetWidth(jpgImage)/4,Quartz.CGImageGetHeight(jpgImage)/4)# Draw the image into the rectangle.# This is Image 1.Quartz.CGContextDrawImage(context,jpgRect,jpgImage)Quartz.CGContextSaveGState(context)# Translate to the top-right corner of the image just drawn.Quartz.CGContextTranslateCTM(context,jpgRect.size.width,jpgRect.size.height)# Rotate by -90 degrees.Quartz.CGContextRotateCTM(context,Utilities.DEGREES_TO_RADIANS(-90))# Translate in -x by the width of the drawing.Quartz.CGContextTranslateCTM(context,-jpgRect.size.width,0)# Draw the image into the same rectangle as before.# This is Image 2.Quartz.CGContextDrawImage(context,jpgRect,jpgImage)Quartz.CGContextRestoreGState(context)Quartz.CGContextSaveGState(context)# Translate so that the next drawing of the image appears# below and to the right of the image just drawn.Quartz.CGContextTranslateCTM(context,jpgRect.size.width+jpgRect.size.height,jpgRect.size.height)# Scale the y axis by a negative value and flip the image.Quartz.CGContextScaleCTM(context,0.75,-1.0)# This is Image 3.Quartz.CGContextDrawImage(context,jpgRect,jpgImage)Quartz.CGContextRestoreGState(context)# Adjust the position of the rectangle so that its origin is# to the right and above where Image 3 was drawn. Adjust the# size of the rectangle so that it is 1/4 the image width# and 1/6 the image height.jpgRect=Quartz.CGRectMake(1.75*jpgRect.size.width+jpgRect.size.height,jpgRect.size.height,Quartz.CGImageGetWidth(jpgImage)/4,Quartz.CGImageGetHeight(jpgImage)/6)# This is Image 4.Quartz.CGContextDrawImage(context,jpgRect,jpgImage)defdrawImageFromURL(context,url,width,height,bitsPerComponent,isRGB):# This routine treats color images as RGBifisRGB:bitsPerPixel=bitsPerComponent*3else:bitsPerPixel=bitsPerComponentbytesPerRow=(width*bitsPerPixel+7)/8shouldInterpolate=True# Create a Quartz data provider from the supplied URL.dataProvider=Quartz.CGDataProviderCreateWithURL(url)ifdataProviderisNone:print("Couldn't create Image data provider!")return# Get a Quartz color space object appropriate for the image type.ifisRGB:colorspace=Utilities.getTheCalibratedRGBColorSpace()else:colorspace=Utilities.getTheCalibratedGrayColorSpace()# Create an image of the width, height, and bitsPerComponent with# no alpha data, the default decode array, with interpolation,# and the default rendering intent for images. This code is# intended for Gray images of the format GGGGG... or RGB images# of the format RGBRGBRGB... .image=Quartz.CGImageCreate(width,height,bitsPerComponent,bitsPerPixel,bytesPerRow,colorspace,Quartz.kCGImageAlphaNone,dataProvider,None,shouldInterpolate,Quartz.kCGRenderingIntentDefault)# Quartz retains the data provider with the image and since this# code does not create any more images with the data provider, it# can release it.deldataProviderifimageisNone:print("Couldn't create CGImageRef for this data!")return# Create a rectangle into which the code will draw the image.imageRect=Quartz.CGRectMake(0.0,0.0,width,height)# Draw the image into the rectangle.Quartz.CGContextDrawImage(context,imageRect,image)defdoColorRampImage(context):width=256height=256bitsPerComponent=8bitsPerPixel=24bytesPerRow=width*3shouldInterpolate=TrueimageDataProvider=DataProvidersAndConsumers.createRGBRampDataProvider()ifimageDataProviderisNone:print("Couldn't create Image Data provider!")returncolorspace=Utilities.getTheCalibratedRGBColorSpace()image=Quartz.CGImageCreate(width,height,bitsPerComponent,bitsPerPixel,bytesPerRow,colorspace,Quartz.kCGImageAlphaNone,imageDataProvider,None,shouldInterpolate,Quartz.kCGRenderingIntentDefault)# No longer need the data provider.delimageDataProviderifimageisNone:print("Couldn't create CGImageRef for this data!")returnimageRect=Quartz.CGRectMake(0.0,0.0,width,height)# Draw the image.Quartz.CGContextDrawImage(context,imageRect,image)defdoImageWithCallbacksCreatedFromURL(context,url,width,height,bitsPerComponent,isRGB):ifisRGB:bitsPerPixel=bitsPerComponent*3else:bitsPerPixel=bitsPerComponentbytesPerRow=((width*bitsPerPixel)+7)/8shouldInterpolate=TruedataProvider=DataProvidersAndConsumers.createSequentialAccessDPForURL(url)ifdataProviderisNone:print("Couldn't create Image Data provider!")return# Create a Quartz color space object appropriate for the image type.# These user written functions create the color space object# and that reference must be released by this code.ifisRGB:colorspace=Utilities.getTheCalibratedRGBColorSpace()else:colorspace=Utilities.getTheCalibratedGrayColorSpace()image=Quartz.CGImageCreate(width,height,bitsPerComponent,bitsPerPixel,bytesPerRow,colorspace,Quartz.kCGImageAlphaNone,dataProvider,None,shouldInterpolate,Quartz.kCGRenderingIntentDefault)deldataProviderifimageisNone:print("Couldn't create CGImageRef for this data!")returnimageRect=Quartz.CGRectMake(0.0,0.0,width,height)# Draw the image into the rectangle.Quartz.CGContextDrawImage(context,imageRect,image)defdoGrayRamp(context):width=256height=1bitsPerComponent=8bitsPerPixel=8bytesPerRow=widthshouldInterpolate=TruedataProvider=DataProvidersAndConsumers.createGrayRampDirectAccessDP()ifdataProviderisNone:print("Couldn't create Gray Ramp provider!")returncolorspace=Utilities.getTheCalibratedGrayColorSpace()image=Quartz.CGImageCreate(width,height,bitsPerComponent,bitsPerPixel,bytesPerRow,colorspace,Quartz.kCGImageAlphaNone,dataProvider,None,shouldInterpolate,Quartz.kCGRenderingIntentDefault)deldataProviderifimageisNone:print("Couldn't create CGImageRef for image data!")returnimageRect=Quartz.CGRectMake(0.0,0.0,256,256)# Drawing the image that is 256 samples wide and# 1 scanline high into a rectangle that is 256 x 256 units# on a side causes Quartz to stretch the image to fill# the destination rectangle.Quartz.CGContextDrawImage(context,imageRect,image)# This routine examines the CGImageSource at index 0 to# determine if the first image is a floating point image and# if it is, it returns an options dictionary suitable for# passing to CGImageSourceCreateImageAtIndex in order to create# a CGImageRef that contains full dynamic range floating point data.defcreateFloatingPointImageOptions(imageSource):# Allow the image to be a floating point image.# Without this, Quartz would return integer pixel data, even for# floating point images. Typically you don't need floating point data# but in some special cases you might want it.options={Quartz.kCGImageSourceShouldAllowFloat:True}isFloat=False# Obtain the properties for the first image# in the image source. This is a 'Copy' function# so the code owns a reference to the# dictionary returned.properties=Quartz.CGImageSourceCopyPropertiesAtIndex(imageSource,0,options)ifpropertiesisnotNone:# Get the value for the kCGImagePropertyIsFloat if it exists# and if the value is a CFBoolean then get the corresponding# Boolean result.ifQuartz.kCGImagePropertyIsFloatinproperties:isFloat=bool(properties[Quartz.kCGImagePropertyIsFloat])ifnotisFloat:returnNonereturnoptionsdefmyCreateImageUsingImageSource(url):# Set to zero, indicating the property was unavailable.xdpi=ydpi=0# Create the image source from the URL.imageSource=Quartz.CGImageSourceCreateWithURL(url,None)ifimageSourceisNone:print("Couldn't create image source from URL!")return(None,xdpi,ydpi)ifFalse:options=createFloatingPointImageOptions(imageSource)ifoptionsisnotNone:print("image IS a floating point image")else:print("image IS NOT a floating point image")else:options=None# Obtain the properties dictionary for the first image# in the image source. This is a copy function so this# code owns the reference returned and must# must release it.properties=Quartz.CGImageSourceCopyPropertiesAtIndex(imageSource,0,options)ifpropertiesisnotNone:# Check for the x and y resolution of the image.xdpi=properties[Quartz.kCGImagePropertyDPIWidth]ydpi=properties[Quartz.kCGImagePropertyDPIHeight]# Create a CGImageRef from the first image in the CGImageSource.image=Quartz.CGImageSourceCreateImageAtIndex(imageSource,0,options)# Release the CGImageSource object since it is no longer needed# and this code created it. This code uses CFRelease since a# CGImageSource object is a CoreFoundation object.delimageSourcedeloptionsifimageisNone:print("Couldn't create image from image source!")returnNonereturn(image,xdpi,ydpi)defmyCreateThumbnailFromImageSource(url):maxThumbSize=160# Create the image source from the URL.imageSource=Quartz.CGImageSourceCreateWithURL(url,None)ifimageSourceisNone:print("Couldn't create image source from URL!")returnNoneoptions={# Specify 160 pixels as the maximum width and height of# the thumbnail for Quartz to create.Quartz.kCGImageSourceThumbnailMaxPixelSize:maxThumbSize,# Request that Quartz create a thumbnail image if# thumbnail data isn't present in the file.Quartz.kCGImageSourceCreateThumbnailFromImageIfAbsent:True,}# Create the thumbnail image for the first image in the# image source, that at index 0, using the options# dictionary that the code just created.thumb=Quartz.CGImageSourceCreateThumbnailAtIndex(imageSource,0,options)# Release the options dictionary.deloptions# Release the image source the code created.delimageSourceifthumbisNone:print("Couldn't create thumbnail from image source!")returnNonereturnthumbdefimageHasFloatingPointSamples(image):ifhasattr(Quartz,'CGImageGetBitmapInfo'):return(Quartz.kCGBitmapFloatComponents&Quartz.CGImageGetBitmapInfo(image))!=0returnFalsedefdrawImageWithCGImageDataSource(context,url):# This code would be better if it created the image source# once and used the same image source to create the image and its# thumbnail, but the point here is to simply test the routines# myCreateImageUsingImageSource and myCreateThumbnailFromImageSource.image,xdpi,ydpi=myCreateImageUsingImageSource(url)ifimageisNone:print("myCreateImageFromImageSource didn't create a CGImage!")returnprint("xdpi = %2.f, ydpi = %2.f"%(xdpi,ydpi))imageRect=Quartz.CGRectMake(0.0,0.0,Quartz.CGImageGetWidth(image)/3,Quartz.CGImageGetHeight(image)/3)Quartz.CGContextDrawImage(context,imageRect,image)if0:isFloatingImage=imageHasFloatingPointSamples(image)ifisFloatingImage:print("First image IS a floating point image")else:print("First image IS NOT a floating point image")delimageimage=myCreateThumbnailFromImageSource(url)ifimageisNone:print("myCreateThumbnailFromImageSource didn't create a CGImage!")returnimageRect=Quartz.CGRectMake(400.0,0.0,Quartz.CGImageGetWidth(image),Quartz.CGImageGetHeight(image))Quartz.CGContextDrawImage(context,imageRect,image)delimageclassMyIncrementalData(object):data=NonedataSize=0repCount=0chunkSize=0# This is a dummy data accumulation routine used to demonstrate incremental# loading of an image.defmyCreateAccumulatedDataSoFar(myDataP):myDataP.repCount+=1sizeToReturn=myDataP.chunkSize*myDataP.repCountifsizeToReturn>myDataP.dataSize:sizeToReturn=myDataP.dataSizedone=(sizeToReturn==myDataP.dataSize)data=Cocoa.CFDataCreate(None,myDataP.data,sizeToReturn)returndata,donedefMyDrawIncrementalImage(context,image,fullHeight):# Obtain the width and height of the image that has been# accumulated so far.print("MyDrawIncrementalImage",context,image,fullHeight)width=Quartz.CGImageGetWidth(image)height=Quartz.CGImageGetHeight(image)# Adjust the location of the imageRect so that the origin is# such that the full image would be located at 0,0 and the partial# image top-left corner does not move as the image is filled in.# This is only needed for views where the y axis points up the# drawing canvas.imageRect=Quartz.CGRectMake(0,fullHeight-height,width,height)Quartz.CGContextDrawImage(context,imageRect,image)defmyDrawFirstImageIncrementally(context,myDataP):height=-1# Create an incremental image source.imageSource=Quartz.CGImageSourceCreateIncremental(None)ifimageSourceisNone:print("Couldn't create incremental imagesource!")return# Loop, gathering the necessary data to find the True# height of the image.while1:# Fetch the data. The CFData object returned by# myCreateAccumulatedDataSoFar is used to update the# image source. When the data is complete, the code# passes True in the 'done' parameter passed to# CGImageSourceUpdateData. Once the data is passed# to CGImageSourceUpdateData, the code can release# its reference to the data.# Accumulate the data.data,done=myCreateAccumulatedDataSoFar(myDataP)Quartz.CGImageSourceUpdateData(imageSource,data,done)# Release the data since Quartz retains it and this code# no longer needs it.deldataifheight<0:print("height < 0",height)# Determine the height of the full image. This is needed in order# to adjust the location of the drawing of the partial image in# a context where the y axis has the default Quartz orientation# pointing up the drawing canvas.properties=Quartz.CGImageSourceCopyPropertiesAtIndex(imageSource,0,None)ifpropertiesisnotNone:ifQuartz.kCGImagePropertyPixelHeightinproperties:height=properties[Quartz.kCGImagePropertyPixelHeight]delproperties# Once the height is obtained, go ahead and see if Quartz# has enough data to create a CGImage object.print("height",height)ifheight>0:# Now create the CGImageRef from the image source for the# first image.image=Quartz.CGImageSourceCreateImageAtIndex(imageSource,0,None)ifimageisnotNone:# Draw the image using the height of the full image# to adjust the location where the image is drawn.MyDrawIncrementalImage(context,image,height)# Release the partial image once you've drawn it.delimage# Potentially you would want to flush the context so# that drawing to a window would appear, even inside# this loop. Of course this flush should really be# done on a timer so that the flush only occurs at# most every 60th of a second. See Chapter 17 regarding# timing your usage of CGContextFlush.Quartz.CGContextFlush(context)# Obtain the status for the image source for the first image.status=Quartz.CGImageSourceGetStatusAtIndex(imageSource,0)ifdone:# or status == Quartz.kCGImageStatusComplete:print(done,status,status==Quartz.kCGImageStatusComplete)breakdefcreateMyIncrementalDataFromURL(url,myDataP):myDataP.data=NonemyDataP.dataSize=0myDataP.repCount=0success,pathString=CFURLGetFileSystemRepresentation(url,True,None,1024)pathString=pathString.rstrip(b'\0')ifsuccessandlen(pathString):fp=open(pathString,'rb')myDataP.data=fp.read()fp.close()myDataP.dataSize=len(myDataP.data)ifmyDataP.dataSize>0:myDataP.chunkSize=myDataP.dataSize/10# 10 chunksdefdoIncrementalImageWithURL(context,url):myData=MyIncrementalData()createMyIncrementalDataFromURL(url,myData)ifmyData.dataisNone:print("couldn't read data from URL!")myDrawFirstImageIncrementally(context,myData)delmyData# This code requires QuickTime.framework.# from Carbon import QtdefcreateCGImageWithQuickTimeFromURL(url):""" Note: this function doesn't actually worked because the APIs used in here aren't properly wrapped (yet). """returnNoneimageRef=Noneerr=noErrresult,dataRef,dataRefType=QTNewDataReferenceFromCFURL(url,0,None,None)ifdataRefisnotNone:err,gi=GetGraphicsImporterForDataRefWithFlags(dataRef,dataRefType,None,0)ifnoterrandgi:# Tell the graphics importer that it shouldn't perform# gamma correction and it should create an image in# the original source color space rather than matching it to# a generic calibrated color space.result=GraphicsImportSetFlags(gi,(kGraphicsImporterDontDoGammaCorrection+kGraphicsImporterDontUseColorMatching))ifresult==0:result,imageRef=GraphicsImportCreateCGImage(gi,None,0)ifresult!=0:print("got a bad result = %d!"%(result,))DisposeHandle(dataRef)CloseComponent(gi)returnimageRefdefdrawQTImageWithQuartz(context,url):image=createCGImageWithQuickTimeFromURL(url)ifimageisNone:print("createCGImageWithQuickTimeFromURL didn't create a CGImage!")returnimageRect=Quartz.CGRectMake(0.0,0.0,Quartz.CGImageGetWidth(image),Quartz.CGImageGetHeight(image))Quartz.CGContextDrawImage(context,imageRect,image)defdrawJPEGDocumentWithMultipleProfiles(context,url):isDeviceRGBImage=False# Create a Quartz data provider for the supplied URL.jpgProvider=Quartz.CGDataProviderCreateWithURL(url)ifjpgProviderisNone:print("Couldn't create JPEG Data provider!")return# Create the Quartz.CGImageRef for the JPEG image from the data provider.jpgImage=Quartz.CGImageCreateWithJPEGDataProvider(jpgProvider,None,True,Quartz.kCGRenderingIntentDefault)deljpgProviderifjpgImageisNone:print("Couldn't create CGImageRef for JPEG data!")return# Get the color space characterizing the image. This is a# function with 'Get' semantics so the code doesn't own a reference# to the color space returned and must not release it.originalColorSpace=Quartz.CGImageGetColorSpace(jpgImage)iforiginalColorSpaceisNone:print("image is a masking image, not an image with color!")returnifQuartz.CGColorSpaceGetNumberOfComponents(originalColorSpace)!=3:print("This example only works with 3 component JPEG images")return# Determine if the original color space is DeviceRGB. If that is# not the case then bail.comparisonColorSpace=Quartz.CGColorSpaceCreateDeviceRGB()# Note that this comparison of color spaces works only on# Jaguar and later where a CGColorSpaceRef is a# CoreFoundation object. Otherwise this will crash!## NOTE: 20140109: Disabled the color space comparison because that's not valid# on recent enough OSX versions.#isDeviceRGBImage = (comparisonColorSpace == originalColorSpace)# This code created 'comparisonColorSpace' so it must release it.#del comparisonColorSpace#if not isDeviceRGBImage:# print("The color space for the JPEG image is not DeviceRGB!", comparisonColorSpace, originalColorSpace)# #return# Might need to adjust this based on the size of the original image.Quartz.CGContextScaleCTM(context,0.5,0.5)imageRect=Quartz.CGRectMake(0.0,Quartz.CGImageGetHeight(jpgImage)/2,Quartz.CGImageGetWidth(jpgImage),Quartz.CGImageGetHeight(jpgImage))# Draw the original image to the left of the other two.Quartz.CGContextDrawImage(context,imageRect,jpgImage)# Recharacterize the original image with the generic Calibrated RGB# color space.updatedImage1=Quartz.CGImageCreateCopyWithColorSpace(jpgImage,Utilities.getTheCalibratedRGBColorSpace())# Release the original image since this code is done with it.deljpgImageifupdatedImage1isNone:print("There is no updated image to draw!")return# Draw the image characterized by the Generic profile# to the right of the other image.imageRect=Quartz.CGRectOffset(imageRect,Quartz.CGRectGetWidth(imageRect)+10,0)Quartz.CGContextDrawImage(context,imageRect,updatedImage1)# Recharacterize the image but now with a color space# created with the sRGB profile.updatedImage2=Quartz.CGImageCreateCopyWithColorSpace(updatedImage1,Utilities.getTheSRGBColorSpace())# Release updatedImage1 since this code is done with it.delupdatedImage1ifupdatedImage2isNone:print("There is no second updated image to draw!")return# Draw the image characterized by the sRGB profile to the right of# the image characterized by the generic RGB profile.imageRect=Quartz.CGRectOffset(imageRect,Quartz.CGRectGetWidth(imageRect)+10,0)Quartz.CGContextDrawImage(context,imageRect,updatedImage2)defcreateRedGreenRampImageData(width,height,size):try:dataP=objc.allocateBuffer(size)exceptMemoryError:returnNoneidx=0# Build an image that is RGB 24 bits per sample. This is a ramp# where the red component value increases in red from left to# right and the green component increases from top to bottom.forginrange(height):forrinrange(width):dataP[idx+0]=rdataP[idx+1]=gdataP[idx+2]=0idx+=3returndataPdefcreateRGBRampSubDataProvider(subRect):bytesPerSample=3width=256height=256bytesPerRow=width*bytesPerSamplestartOffsetX=subRect.origin.xstartOffsetY=subRect.origin.yimageDataSize=bytesPerRow*height# The first image sample is at# (startOffsetY*bytesPerRow + startOffsetX*bytesPerSample)# bytes into the RGB ramp data.firstByteOffset=startOffsetY*bytesPerRow+startOffsetX*bytesPerSample# The actual size of the image data provided is the full image size# minus the amount skipped at the beginning. This is more than the# total amount of data that is needed for the subimage but it is# valid and easy to calculate.totalBytesProvided=imageDataSize-firstByteOffset# Create the full color ramp.dataP=createRedGreenRampImageData(width,height,imageDataSize)ifdataPisNone:print("Couldn't create image data!")returnNone# Use the pointer to the first byte as the info parameter since# that is the pointer to the block to free when done.dataProvider=Quartz.CGDataProviderCreateWithData(dataP,buffer(dataP,firstByteOffset),totalBytesProvided,None)ifdataProviderisNone:returnNonereturndataProviderdefdoColorRampSubImage(context):# Start 4 scanlines from the top and 16 pixels from the left edge,# skip the last 40 scanlines of the image and the right# most 64 pixels.insetLeft=16insetTop=4insetRight=64insetBottom=40fullImageWidth=256fullImageHeight=256subImageWidth=fullImageWidth-insetLeft-insetRightsubImageHeight=fullImageHeight-insetTop-insetBottombitsPerComponent=8bitsPerPixel=24bytesPerRow=fullImageWidth*3shouldInterpolate=TrueimageSubRect=Quartz.CGRectMake(insetLeft,insetTop,subImageWidth,subImageHeight)colorspace=Utilities.getTheCalibratedRGBColorSpace()ifhasattr(Quartz,'CGImageCreateWithImageInRect'):imageDataProvider=DataProvidersAndConsumers.createRGBRampDataProvider()ifimageDataProviderisNone:print("Couldn't create Image Data provider!")returnfullImage=Quartz.CGImageCreate(fullImageWidth,fullImageHeight,bitsPerComponent,bitsPerPixel,bytesPerRow,colorspace,Quartz.kCGImageAlphaNone,imageDataProvider,None,shouldInterpolate,Quartz.kCGRenderingIntentDefault)iffullImageisnotNone:image=Quartz.CGImageCreateWithImageInRect(fullImage,imageSubRect)# release the full image since it is no longer required.delfullImage# If the image hasn't been created yet, this code uses the# customized data provider to do so.ifimageisNone:imageDataProvider=createRGBRampSubDataProvider(imageSubRect)ifimageDataProviderisNone:print("Couldn't create Image Data provider!")return# By supplying bytesPerRow, the extra data at the end of# each scanline and the beginning of the next is properly skipped.image=Quartz.CGImageCreate(subImageWidth,subImageHeight,bitsPerComponent,bitsPerPixel,bytesPerRow,colorspace,Quartz.kCGImageAlphaNone,imageDataProvider,None,shouldInterpolate,Quartz.kCGRenderingIntentDefault)# This code no longer needs the data provider.delimageDataProviderifimageisNone:print("Couldn't create CGImageRef for this data!")return# Draw the subimage.rect=Quartz.CGRectMake(0,0,subImageWidth,subImageHeight)Quartz.CGContextDrawImage(context,rect,image)defexportCGImageToPNGFileWithDestination(image,url):resolution=144.# Create an image destination at the supplied URL that# corresponds to the PNG image format.imageDestination=Quartz.CGImageDestinationCreateWithURL(url,kUTTypePNG,1,None)ifimageDestinationisNone:print("couldn't create image destination!")return# Set the keys to be the x and y resolution of the image.options={Quartz.kCGImagePropertyDPIWidth:resolution,Quartz.kCGImagePropertyDPIHeight:resolution,}# Add the image with the options dictionary to the destination.Quartz.CGImageDestinationAddImage(imageDestination,image,options)# Release the options dictionary this code created.deloptions# When all the images are added to the destination, finalize it.Quartz.CGImageDestinationFinalize(imageDestination)# Release the destination when done with it.delimageDestination# This code requires QuickTime.framework# include <QuickTime/QuickTime.h>defexportCGImageToJPEGFile(imageRef,url):# This doesn't actually work due to lame Python Quicktime bindings...returnresult,dataRef,dataRefType=QTNewDataReferenceFromCFURL(url,0,None,None)ifresult==0:result,graphicsExporter=OpenADefaultComponent(GraphicsExporterComponentType,kQTFileTypeJPEG)ifresult==0:result=GraphicsExportSetInputCGImage(graphicsExporter,imageRef)ifresult==0:result=GraphicsExportSetOutputDataReference(graphicsExporter,dataRef,dataRefType)ifresult==0:result,sizeWritten=GraphicsExportDoExport(graphicsExporter,None)CloseComponent(graphicsExporter)ifdataRefisnotNone:DisposeHandle(dataRef)ifresult!=0:print("Exporting QT image got bad result = %d!"%(result,))defexportColorRampImageWithQT(context):width=256height=256bitsPerComponent=8bitsPerPixel=24bytesPerRow=width*3shouldInterpolate=TrueimageDataProvider=DataProvidersAndConsumers.createRGBRampDataProvider()ifimageDataProviderisNone:print("Couldn't create Image Data provider!")returncolorspace=Utilities.getTheCalibratedRGBColorSpace()image=Quartz.CGImageCreate(width,height,bitsPerComponent,bitsPerPixel,bytesPerRow,colorspace,Quartz.kCGImageAlphaNone,imageDataProvider,None,shouldInterpolate,Quartz.kCGRenderingIntentDefault)delimageDataProviderifimageisNone:print("Couldn't create CGImageRef for this data!")returnrect=Quartz.CGRectMake(0.0,0.0,width,height)Quartz.CGContextDrawImage(context,rect,image)# Of course this is a total hack.outPath=b"/tmp/imageout.jpg"exportURL=CFURLCreateFromFileSystemRepresentation(None,outPath,len(outPath),False)ifexportURL:exportCGImageToJPEGFile(image,exportURL)

MyAppController.py

importCocoaimportobjcimportPDFHandlingimportBitmapContextimportUtilities# Initial defaults_dpi=144_useQT=FalsedefgetURLToExport(suffix):savePanel=Cocoa.NSSavePanel.savePanel()initialFileName="BasicDrawing.%s"%(suffix,)ifsavePanel.runModalForDirectory_file_(None,initialFileName)==Cocoa.NSFileHandlingPanelOKButton:returnsavePanel.URL()returnNoneclassMyAppController(Cocoa.NSObject):theView=objc.IBOutlet()currentDPIMenuItem=objc.IBOutlet()currentExportStyleMenuItem=objc.IBOutlet()@objc.IBActiondefprint_(self,sender):self.theView.print_(sender)defupdateDPIMenu_(self,sender):ifself.currentDPIMenuItemisnotsender:# Uncheck the previous item.ifself.currentDPIMenuItemisnotNone:self.currentDPIMenuItem.setState_(Cocoa.NSOffState)# Update to the current item.self.currentDPIMenuItem=sender# Check new menu item.self.currentDPIMenuItem.setState_(Cocoa.NSOnState)defupdateExportStyleMenu_(self,sender):ifself.currentExportStyleMenuItemisnotsender:# Uncheck the previous item.ifself.currentExportStyleMenuItemisnotNone:self.currentExportStyleMenuItem.setState_(Cocoa.NSOffState)# Update to the current item.self.currentExportStyleMenuItem=sender# Check new menu item.self.currentExportStyleMenuItem.setState_(Cocoa.NSOnState)@objc.IBActiondefsetExportResolution_(self,sender):global_dpi_dpi=sender.tag()self.updateDPIMenu_(sender)@objc.IBActiondefsetUseQT_(self,sender):global_useQT_useQT=Trueself.updateExportStyleMenu_(sender)@objc.IBActiondefsetUseCGImageSource_(self,sender):global_useQT_useQT=Falseself.updateExportStyleMenu_(sender)defsetupExportInfo_(self,exportInfoP):# Use the printable version of the current command. This produces# the best results for exporting.exportInfoP.command=self.theView.currentPrintableCommand()exportInfoP.fileType=' '# unusedexportInfoP.useQTForExport=_useQTexportInfoP.dpi=_dpi@objc.IBActiondefexportAsPDF_(self,sender):url=getURLToExport("pdf")ifurlisnotNone:exportInfo=Utilities.ExportInfo()self.setupExportInfo_(exportInfo)PDFHandling.MakePDFDocument(url,exportInfo)@objc.IBActiondefexportAsPNG_(self,sender):url=getURLToExport("png")ifurlisnotNone:exportInfo=Utilities.ExportInfo()self.setupExportInfo_(exportInfo)BitmapContext.MakePNGDocument(url,exportInfo)@objc.IBActiondefexportAsTIFF_(self,sender):url=getURLToExport("tif")ifurlisnotNone:exportInfo=Utilities.ExportInfo()self.setupExportInfo_(exportInfo)BitmapContext.MakeTIFFDocument(url,exportInfo)@objc.IBActiondefexportAsJPEG_(self,sender):url=getURLToExport("jpg")ifurlisnotNone:exportInfo=Utilities.ExportInfo()self.setupExportInfo_(exportInfo)BitmapContext.MakeJPEGDocument(url,exportInfo)defvalidateMenuItem_(self,menuItem):ifmenuItem.tag==_dpi:currentDPIMenuItem=menuItemmenuItem.setState_(True)elifmenuItem.action()=='setUseQT:':if_useQT:self.currentDPIMenuItem=menuItemmenuItem.setState_(True)else:menuItem.setState_(False)elifmenuItem.action()=='setUseCGImageSource:':if_useQT:currentDPIMenuItem=menuItemmenuItem.setState_(True)else:menuItem.setState_(False)returnTrue

MyView.py

importobjcfromobjcimportsuperimportCocoaimportUIHandlingimportAppDrawingimportFrameworkTextDrawing# XXX: Why are these global?_drawingCommand=UIHandling.kHICommandSimpleRect_pdfDocument=NoneclassMyView(Cocoa.NSView):currentMenuItem=objc.IBOutlet()definitWithFrame_(self,frameRect):self=super(MyView,self).initWithFrame_(frameRect)ifselfisNone:returnNoneglobal_pdfDocument_pdfDocument=NonereturnselfifFalse:defisFlipped(self):returnTruedefdrawRect_(self,rect):context=Cocoa.NSGraphicsContext.currentContext().graphicsPort()if_pdfDocumentisNone:if_drawingCommandin(UIHandling.kHICommandDrawNSString,UIHandling.kHICommandDrawNSLayoutMgr,UIHandling.kHICommandDrawCustomNSLayoutMgr):if_drawingCommand==UIHandling.kHICommandDrawNSString:FrameworkTextDrawing.drawNSStringWithAttributes()elif_drawingCommand==UIHandling.kHICommandDrawNSLayoutMgr:FrameworkTextDrawing.drawWithNSLayout()else:FrameworkTextDrawing.drawWithCustomNSLayout()else:AppDrawing.DispatchDrawing(context,_drawingCommand)else:mediaRect=CGPDFDocumentGetMediaBox(_pdfDocument,1)mediaRect.origin.x=mediaRect.origin.y=0CGContextDrawPDFDocument(context,mediaRect,_pdfDocument,1)@objc.IBActiondefsetDrawCommand_(self,sender):global_drawingCommand,_pdfDocumentnewCommand=sender.tag()if_drawingCommand!=newCommand:_drawingCommand=newCommand# The view needs to be redisplayed since there is a new drawing command.self.setNeedsDisplay_(True)# Disable previous menu item.ifself.currentMenuItemisnotNone:self.currentMenuItem.setState_(Cocoa.NSOffState)# Update the current item.self.currentMenuItem=sender# Enable new menu item.self.currentMenuItem.setState_(Cocoa.NSOnState)# If we were showing a pasted document, let's get rid of it.if_pdfDocument:_pdfDocument=NonedefcurrentPrintableCommand(self):# The best representation for printing or exporting# when the current command caches using a bitmap context# or a layer is to not do any caching.if_drawingCommandin(UIHandling.kHICommandDrawOffScreenImage,UIHandling.kHICommandDrawWithLayer):returnUIHandling.kHICommandDrawNoOffScreenImagereturn_drawingCommanddefprint_(self,sender):global_drawingCommandsavedDrawingCommand=_drawingCommand# Set the drawing command to be one that is printable._drawingCommand=self.currentPrintableCommand()# Do the printing operation on the view.Cocoa.NSPrintOperation.printOperationWithView_(self).runOperation()# Restore that before the printing operation._drawingCommand=savedDrawingCommanddefacceptsFirstResponder(self):returnTrue@objc.IBActiondefcopy_(self,sender):addPDFDataToPasteBoard(_drawingCommand)@objc.IBActiondefpaste_(self,sender):global_pdfDocumentnewPDFDocument=createNewPDFRefFromPasteBoard()ifnewPDFDocumentisnotNone:_pdfDocument=newPDFDocument# The view needs to be redisplayed since there is# a new PDF document.self.setNeedsDisplay_(True)# Return the number of pages available for printing. For this# application it is always 1.defknowsPageRange_(self,aRange):returnTrue,Cocoa.NSRange(1,1)# Return the drawing rectangle for a particular page number.# For this application it is always the page width and height.defrectForPage_(self,page):pi=Cocoa.NSPrintOperation.currentOperation().printInfo()# Calculate the page height in points.paperSize=pi.paperSize()returnCocoa.NSMakeRect(0,0,paperSize.width,paperSize.height)defvalidateMenuItem_(self,menuItem):ifmenuItem.tag()==_drawingCommand:self.currentMenuItem=menuItemmenuItem.setState_(True)else:menuItem.setState_(False)returnTrue

PDFHandling.py

importAppDrawingimportFrameworkUtilitiesimportUtilitiesimportDataProvidersAndConsumersimportCocoaimportQuartzimportsysdefcreateNewPDFRefFromPasteBoard():# Create a reference to the PDF data on the pasteboard.# The implementation of myCreatePDFDataFromPasteBoard depends# on the application framework you are using for your application.## myCreatePDFDataFromPasteBoard creates a reference that is owned# by the calling application.pasteBoardData=FrameworkUtilities.myCreatePDFDataFromPasteBoard()ifpasteBoardDataisNone:print("There is no PDF data on pasteboard!")returnNone# Create a data provider from the pasteboard data.dataProvider=myCGDataProviderCreateWithCFData(pasteBoardData)# Release the pasteboard data since the data provider retains# it and this code owns a reference but no longer requires it.delpasteBoardDataifdataProviderisNone:print("Couldn't create data provider.")returnNonepasteBoardPDFDocument=Quartz.CGPDFDocumentCreateWithProvider(dataProvider)# Release the data provider now that the code is done with it.deldataProviderifpasteBoardPDFDocumentisNone:print("Couldn't create PDF document from pasteboard data provider.")returnNonereturnpasteBoardPDFDocument_pdfDoc=NonedefgetPasteBoardPDFDoc(reset):global_pdfDocifreset:# Release any existing document._pdfDoc=createNewPDFRefFromPasteBoard()else:# If there isn't already one, create it fresh.if_pdfDocisNone:_pdfDoc=createNewPDFRefFromPasteBoard()return_pdfDocdefdrawPasteBoardPDF(context):pdfDoc=getPasteBoardPDFDoc(False)# Obtain the existing one.ifpdfDocisNone:print("Quartz couldn't create CGPDFDocumentRef from pasteboard.")return# The media box is the bounding box of the PDF document.pdfRect=Quartz.CGPDFDocumentGetMediaBox(pdfDoc,1);# page 1# Make the destination rect origin at the Quartz origin.pdfRect.origin.x=pdfRect.origin.y=0.;Quartz.CGContextDrawPDFDocument(context,pdfRect,pdfDoc,1);# page 1defcfDataCreatePDFDocumentFromCommand(command):# Media rect for the drawing. In a real application this# should be the bounding rectangle of the graphics# that will be the PDF content.mediaRect=Quartz.CGRectMake(0,0,612,792)# Create a dictionary to hold the optional information describing the PDF data.dict={}# Add the creator and title information to the PDF content.dict[Quartz.kCGPDFContextTitle]="Pasted From Sample Quartz Application"dict[Quartz.kCGPDFContextCreator]="Sample Quartz Application"# Create a mutable CFData object with unlimited capacity.data=Cocoa.CFDataCreateMutable(None,0)ifdataisNone:print("Couldn't make CFData!")returnNone# Create the data consumer to capture the PDF data.consumer=DataProvidersAndConsumers.myCGDataConsumerCreateWithCFData(data)ifconsumerisNone:print("Couldn't create data consumer!")returnNonepdfContext,mediaRect=Quartz.CGPDFContextCreate(consumer,None,dict)delconsumerdeldictifpdfContextisNone:print("Couldn't create pdf context!")returnNonemediaRect=Quartz.CGContextBeginPage(pdfContext)if1:Quartz.CGContextSaveGState(pdfContext)if1:Quartz.CGContextClipToRect(pdfContext,mediaRect)AppDrawing.DispatchDrawing(pdfContext,command)Quartz.CGContextRestoreGState(pdfContext)Quartz.CGContextEndPage(pdfContext)returndatadefMakePDFDocument(url,exportInfo):# Use this as the media box for the document.# In a real application this should be the bounding# rectangle of the graphics that will be the PDF content.mediaRect=Quartz.CGRectMake(0,0,612,792)info={# Add the title information for this document.Quartz.kCGPDFContextTitle:"BasicDrawing Sample Graphics",# Add the author information for this document. This is typically# the user creating the document.Quartz.kCGPDFContextAuthor:"David Gelphman and Bunny Laden",# The creator is the application creating the document.Quartz.kCGPDFContextCreator:"BasicDrawing Application",}if0:# Before using the kCGPDFContextCropBox key, check to ensure that it# is available.ifhasattr(Quartz,'kCFPDFContextCropBox'):# Prepare the crop box entry. Use this rectangle as the crop box for# this example.# XXX:fixme: need to encode as CFData!!!info[Quartz.kCGPDFContextCropBox]=Quartz.CGRectMake(100,100,200,200)ifurlisnotNone:pdfContext=Quartz.CGPDFContextCreateWithURL(url,mediaRect,info)ifpdfContextisnotNone:Quartz.CGContextBeginPage(pdfContext,mediaRect)if1:Quartz.CGContextSaveGState(pdfContext)if1:Quartz.CGContextClipToRect(pdfContext,mediaRect)AppDrawing.DispatchDrawing(pdfContext,exportInfo.command)Quartz.CGContextRestoreGState(pdfContext)Quartz.CGContextEndPage(pdfContext)delpdfContextelse:print("Can't create PDF document!")

PathDrawing.py

importQuartzimportmathdefdoEgg(context):p0=Quartz.CGPoint(0,0)p1=Quartz.CGPoint(0,200)c1=Quartz.CGPoint(140,5)c2=Quartz.CGPoint(80,198)Quartz.CGContextTranslateCTM(context,100,5)Quartz.CGContextBeginPath(context)Quartz.CGContextMoveToPoint(context,p0.x,p0.y)# Create the Bezier path segment for the right side of the egg.Quartz.CGContextAddCurveToPoint(context,c1.x,c1.y,c2.x,c2.y,p1.x,p1.y)# Create the Bezier path segment for the left side of the egg.Quartz.CGContextAddCurveToPoint(context,-c2.x,c2.y,-c1.x,c1.y,p0.x,p0.y)Quartz.CGContextClosePath(context)Quartz.CGContextSetLineWidth(context,2)Quartz.CGContextDrawPath(context,Quartz.kCGPathStroke)defaddRoundedRectToPath(context,rect,ovalWidth,ovalHeight):# If either ovalWidth or ovalHeight is 0, draw a regular rectangle.ifovalWidth==0orovalHeight==0:Quartz.CGContextAddRect(context,rect)else:Quartz.CGContextSaveGState(context)if1:# Translate to lower-left corner of rectangle.Quartz.CGContextTranslateCTM(context,Quartz.CGRectGetMinX(rect),Quartz.CGRectGetMinY(rect))# Scale by the oval width and height so that# each rounded corner is 0.5 units in radius.Quartz.CGContextScaleCTM(context,ovalWidth,ovalHeight)# Unscale the rectangle width by the amount of the X scaling.fw=Quartz.CGRectGetWidth(rect)/ovalWidth# Unscale the rectangle height by the amount of the Y scaling.fh=Quartz.CGRectGetHeight(rect)/ovalHeight# Start at the right edge of the rect, at the midpoint in Y.Quartz.CGContextMoveToPoint(context,fw,fh/2)# Segment 1Quartz.CGContextAddArcToPoint(context,fw,fh,fw/2,fh,0.5)# Segment 2Quartz.CGContextAddArcToPoint(context,0,fh,0,fh/2,0.5)# Segment 3Quartz.CGContextAddArcToPoint(context,0,0,fw/2,0,0.5)# Segment 4Quartz.CGContextAddArcToPoint(context,fw,0,fw,fh/2,0.5)# Closing the path adds the last segment.Quartz.CGContextClosePath(context)Quartz.CGContextRestoreGState(context)defdoRoundedRects(context):rect=Quartz.CGRectMake(10,10,210,150)ovalWidth=100ovalHeight=100Quartz.CGContextSetLineWidth(context,2.)Quartz.CGContextBeginPath(context)addRoundedRectToPath(context,rect,ovalWidth,ovalHeight)Quartz.CGContextSetRGBStrokeColor(context,1,0,0,1)Quartz.CGContextDrawPath(context,Quartz.kCGPathStroke)defdoStrokeWithCTM(context):Quartz.CGContextTranslateCTM(context,150.,180.)Quartz.CGContextSetLineWidth(context,10)# Draw ellipse 1 with a uniform stroke.Quartz.CGContextSaveGState(context)if1:# Scale the CTM so the circular arc will be elliptical.Quartz.CGContextScaleCTM(context,2,1)Quartz.CGContextBeginPath(context)# Create an arc that is a circle.Quartz.CGContextAddArc(context,0.,0.,45.,0.,2*math.pi,0)# Restore the context parameters prior to stroking the path.# CGContextRestoreGState does not affect the path in the context.Quartz.CGContextRestoreGState(context)Quartz.CGContextStrokePath(context)# *** was 0, -120Quartz.CGContextTranslateCTM(context,220.,0.)# Draw ellipse 2 with non-uniform stroke.Quartz.CGContextSaveGState(context)if1:# Scale the CTM so the circular arc will be elliptical.Quartz.CGContextScaleCTM(context,2,1)Quartz.CGContextBeginPath(context)# Create an arc that is a circle.Quartz.CGContextAddArc(context,0.,0.,45.,0.,2*math.pi,0)# Stroke the path with the scaled coordinate system in effect.Quartz.CGContextStrokePath(context)Quartz.CGContextRestoreGState(context)defdoRotatedEllipsesWithCGPath(context):totreps=144tint=1.0tintIncrement=1.0/totreps# Create a new transform consisting of a 45 degree rotation.theTransform=Quartz.CGAffineTransformMakeRotation(math.pi/4)# Apply a scaling transformation to the transform just created.theTransform=Quartz.CGAffineTransformScale(theTransform,1,2)# Create a mutable CGPath object.path=Quartz.CGPathCreateMutable()ifpathisNone:print("Couldn't create path!")return# Add a circular arc to the CGPath object, transformed# by an affine transform.Quartz.CGPathAddArc(path,theTransform,0.,0.,45.,0.,2*math.pi,False);# Close the CGPath object.Quartz.CGPathCloseSubpath(path)# Place the first ellipse at a good location.Quartz.CGContextTranslateCTM(context,100,100)foriinrange(totreps):Quartz.CGContextBeginPath(context)# Add the CGPath object to the current path in the context.Quartz.CGContextAddPath(context,path)# Set the fill color for this instance of the ellipse.Quartz.CGContextSetRGBFillColor(context,tint,0.,0.,1.)# Filling the path implicitly closes it.Quartz.CGContextFillPath(context)# Compute the next tint color.tint-=tintIncrement# Move over for the next ellipse.Quartz.CGContextTranslateCTM(context,1,0.)defalignPointToUserSpace(context,p):# Compute the coordinates of the point in device space.p=Quartz.CGContextConvertPointToDeviceSpace(context,p)# Ensure that coordinates are at exactly the corner# of a device pixel.p.x=math.floor(p.x)p.y=math.floor(p.y)# Convert the device aligned coordinate back to user space.returnQuartz.CGContextConvertPointToUserSpace(context,p)defalignSizeToUserSpace(context,s):# Compute the size in device space.s=Quartz.CGContextConvertSizeToDeviceSpace(context,s)# Ensure that size is an integer multiple of device pixels.s.width=math.floor(s.width)s.height=math.floor(s.height)# Convert back to user space.returnQuartz.CGContextConvertSizeToUserSpace(context,s)defalignRectToUserSpace(context,r):# Compute the coordinates of the rectangle in device space.r=Quartz.CGContextConvertRectToDeviceSpace(context,r)# Ensure that the x and y coordinates are at a pixel corner.r.origin.x=math.floor(r.origin.x)r.origin.y=math.floor(r.origin.y)# Ensure that the width and height are an integer number of# device pixels. Note that this produces a width and height# that is less than or equal to the original width. Another# approach is to use ceil to ensure that the new rectangle# encloses the original one.r.size.width=math.floor(r.size.width)r.size.height=math.floor(r.size.height)# Convert back to user space.returnQuartz.CGContextConvertRectToUserSpace(context,r)defdoPixelAlignedFillAndStroke(context):p1=Quartz.CGPointMake(16.7,17.8)p2=Quartz.CGPointMake(116.7,17.8)r=Quartz.CGRectMake(16.7,20.8,100.6,100.6)Quartz.CGContextSetLineWidth(context,2)Quartz.CGContextSetRGBFillColor(context,1.,0.,0.,1.)Quartz.CGContextSetRGBStrokeColor(context,1.,0.,0.,1.)# Unaligned drawing.Quartz.CGContextBeginPath(context)Quartz.CGContextMoveToPoint(context,p1.x,p1.y)Quartz.CGContextAddLineToPoint(context,p2.x,p2.y)Quartz.CGContextStrokePath(context)Quartz.CGContextFillRect(context,r)# Translate to the right before drawing along# aligned coordinates.Quartz.CGContextTranslateCTM(context,106,0)# Aligned drawing.# Compute the length of the line in user space.s=Quartz.CGSizeMake(p2.x-p1.x,p2.y-p1.y)Quartz.CGContextBeginPath(context)# Align the starting point to a device# pixel boundary.p1=alignPointToUserSpace(context,p1)# Establish the starting point of the line.Quartz.CGContextMoveToPoint(context,p1.x,p1.y)# Compute the line length as an integer# number of device pixels.s=alignSizeToUserSpace(context,s)Quartz.CGContextAddLineToPoint(context,p1.x+s.width,p1.y+s.height)Quartz.CGContextStrokePath(context)# Compute a rect that is aligned to device# space with a width that is an integer# number of device pixels.r=alignRectToUserSpace(context,r)Quartz.CGContextFillRect(context,r)

PatternDrawing.py

importQuartzimportUtilitiesimportsysdefscalePatternPhase(phase):# Adjust the pattern phase if scaling to export as bits. This is equivalent to scaling base# space by the scaling factor.patternScaling=Utilities.getScalingFactor()ifpatternScaling!=1.0:phase=Quartz.CGSizeApplyAffineTransform(phase,Quartz.CGAffineTransformMakeScale(patternScaling,patternScaling))returnphasedefscalePatternMatrix(patternTransform):# Scale the pattern by the scaling factor when exporting to bits. This is equivalent to# scaling base space by the scaling factor.patternScaling=Utilities.getScalingFactor()ifpatternScaling!=1.0:patternTransform=Quartz.CGAffineTransformConcat(patternTransform,Quartz.CGAffineTransformMakeScale(patternScaling,patternScaling))returnpatternTransformdefmyDrawRedBlackCheckerBoardPattern(info,patternCellContext):# This pattern proc draws a red and a black rectangle# patch representing the minimum cell needed to paint a# checkerboard with that pattern.## Each 'cell' of the checkerboard is 2 units on a side.## This code uses Quartz.CGColorRefs which are available in Panther# and later only. Patterns are available in all versions of# Mac OS X but this code uses Quartz.CGColorRefs for convenience# and efficiency.# Paint a black checkerboard box.Quartz.CGContextSetFillColorWithColor(patternCellContext,Utilities.getRGBOpaqueBlackColor())# This is a 1x1 unit rect whose origin is at 0,0 in pattern space.Quartz.CGContextFillRect(patternCellContext,Quartz.CGRectMake(0.0,0.0,1.0,1.0))# This is a 1x1 unit rect whose origin is at 1,1 in pattern space.Quartz.CGContextFillRect(patternCellContext,Quartz.CGRectMake(1.0,1.0,1.0,1.0))# Paint a red checkerboard box.Quartz.CGContextSetFillColorWithColor(patternCellContext,Utilities.getRGBOpaqueRedColor())# This is a 1x1 unit rect whose origin is at 1,0 in pattern space,# that is, immediately to the right of first black checkerboard box.Quartz.CGContextFillRect(patternCellContext,Quartz.CGRectMake(1.0,0.0,1.0,1.0))# This is a 1x1 unit rect whose origin is at 0,1 in pattern space,# that is, immediately above the first black checkerboard box.Quartz.CGContextFillRect(patternCellContext,Quartz.CGRectMake(0.0,1.0,1.0,1.0))defcreateRedBlackCheckerBoardPattern(patternTransform):pattern=Quartz.CGPatternCreate(None,# The pattern cell origin is at (0,0) with a# width of 2 units and a height of 2 units.Quartz.CGRectMake(0,0,2,2),# Use the pattern transform supplied to this routine.scalePatternMatrix(patternTransform),# In pattern space the xStep is 2 units to the next cell in x# and the yStep is 2 units to the next row of cells in y.2,2,# This value is a good choice for this type of pattern and it# avoids seams between tiles.Quartz.kCGPatternTilingConstantSpacingMinimalDistortion,# This pattern has intrinsic color.True,myDrawRedBlackCheckerBoardPattern,)returnpatterndefdoRedBlackCheckerboard(context):dash=[4]pattern=createRedBlackCheckerBoardPattern(Quartz.CGAffineTransformMakeScale(20,20))ifpatternisNone:print("Couldn't create pattern!")return# Create the pattern color space. Since the pattern# itself has intrinsic color, the 'baseColorSpace' parameter# to Quartz.CGColorSpaceCreatePattern must be None.patternColorSpace=Quartz.CGColorSpaceCreatePattern(None)Quartz.CGContextSetFillColorSpace(context,patternColorSpace)# The pattern has intrinsic color so the color components array# passed to CGContextSetFillPattern is just the alpha value used# to composite the pattern cell.# Paint the pattern with alpha = 1.color=[1.0]# Set the fill color to the checkerboard pattern.Quartz.CGContextSetFillPattern(context,pattern,color)# Fill a 100x100 unit rect at (20,20).Quartz.CGContextFillRect(context,Quartz.CGRectMake(20,20,100,100))# Save the graphics state before changing the stroke color.Quartz.CGContextSaveGState(context)if1:# Set the stroke color space and color to the pattern.Quartz.CGContextSetStrokeColorSpace(context,patternColorSpace)Quartz.CGContextSetStrokePattern(context,pattern,color)# Stroke an ellipse with the pattern.Quartz.CGContextSetLineWidth(context,8)Quartz.CGContextBeginPath(context)Utilities.myCGContextAddEllipseInRect(context,Quartz.CGRectMake(120,20,50,100))Quartz.CGContextStrokePath(context)# Restore to the graphics state without the# pattern stroke color.Quartz.CGContextRestoreGState(context)# Now draw text.Quartz.CGContextSetTextMatrix(context,Quartz.CGAffineTransformIdentity)# Choose the font with the PostScript name "Times-Roman",# size 80 points, with the encoding MacRoman encoding.Quartz.CGContextSelectFont(context,b"Times-Roman",80,Quartz.kCGEncodingMacRoman)# Using the fill text drawing mode.Quartz.CGContextSetTextDrawingMode(context,Quartz.kCGTextFill)# Draw text with the pattern.Quartz.CGContextShowTextAtPoint(context,20,120,b"Text",4)# Rectangle 1, filled.Quartz.CGContextFillRect(context,Quartz.CGRectMake(200,20,90,90))# Rectangle 2, filled and stroked with a dash.Quartz.CGContextSetLineWidth(context,2)Quartz.CGContextSetLineDash(context,0,dash,1)Quartz.CGContextBeginPath(context)Quartz.CGContextAddRect(context,Quartz.CGRectMake(200,70,90,90))Quartz.CGContextDrawPath(context,Quartz.kCGPathFillStroke)defdoPatternMatrix(context):basePatternMatrix=Quartz.CGAffineTransformMakeScale(20,20)pattern=createRedBlackCheckerBoardPattern(basePatternMatrix)ifpatternisNone:print("Couldn't create pattern!")return# Create the pattern color space. Since the pattern# itself has intrinsic color, the 'baseColorSpace' parameter# to Quartz.CGColorSpaceCreatePattern must be None.patternColorSpace=Quartz.CGColorSpaceCreatePattern(None)Quartz.CGContextSetFillColorSpace(context,patternColorSpace)delpatternColorSpaceQuartz.CGContextTranslateCTM(context,40,40)Quartz.CGContextSetPatternPhase(context,scalePatternPhase(Quartz.CGSize(40,40)))# The pattern has intrinsic color so the color components array# passed to Quartz.CGContextSetFillPattern is the alpha value used# to composite the pattern cell.# Paint the pattern first with alpha = 1.color=[1]Quartz.CGContextSetFillPattern(context,pattern,color)# Rectangle 1.Quartz.CGContextFillRect(context,Quartz.CGRectMake(0,0,100,100))Quartz.CGContextSaveGState(context)if1:# Rectangle 2.# Paint the pattern with 65% alpha.color=[0.65]Quartz.CGContextSetFillPattern(context,pattern,color)# Rotate 45 degrees about the point (150, 50).Quartz.CGContextTranslateCTM(context,150.0,50.0)Quartz.CGContextRotateCTM(context,Utilities.DEGREES_TO_RADIANS(45.0))Quartz.CGContextTranslateCTM(context,-50.0,-50.0)# Rectangle 2. Patterns do not translate, scale or# rotate with the CTM. You can see that the pattern# tile of this filled rectangle is that of Rectangle# 1.Quartz.CGContextFillRect(context,Quartz.CGRectMake(0,0,100,100))# Release the pattern.delpatternQuartz.CGContextRestoreGState(context)Quartz.CGContextSaveGState(context)if1:# Rectangle 3. The pattern is rotated with the object.# Rotate 45 degrees about the point 250, 50.t=Quartz.CGAffineTransformMakeTranslation(250.0,50.0)t=Quartz.CGAffineTransformRotate(t,Utilities.DEGREES_TO_RADIANS(45.0))# Translate back to -50, -50.t=Quartz.CGAffineTransformTranslate(t,-50.0,-50.0)Quartz.CGContextConcatCTM(context,t)# Make a new pattern that is equivalent to# the old pattern but transformed to current user# space. The order of transformations is crucial.# This ordering is equivalent to using the same pattern# matrix as before but transforming base space by t.patTransform=Quartz.CGAffineTransformConcat(basePatternMatrix,t)pattern=createRedBlackCheckerBoardPattern(patTransform)color=[1]Quartz.CGContextSetFillPattern(context,pattern,color)# Release the pattern.delpatternQuartz.CGContextFillRect(context,Quartz.CGRectMake(0,0,100,100))Quartz.CGContextRestoreGState(context)Quartz.CGContextSaveGState(context)if1:# Rectangle 4. The pattern is scaled with the object.# Translate and scale.t=Quartz.CGAffineTransformMakeTranslation(320,0)t=Quartz.CGAffineTransformScale(t,2,2)Quartz.CGContextConcatCTM(context,t)# Make a new pattern that is equivalent to# the old pattern but transformed to current user# space. The order of transformations is crucial.# This ordering is equivalent to using the same pattern# matrix as before but transforming base space by t.patTransform=Quartz.CGAffineTransformConcat(basePatternMatrix,t)pattern=createRedBlackCheckerBoardPattern(patTransform)color=[1]Quartz.CGContextSetFillPattern(context,pattern,color)# Release the pattern.delpatternQuartz.CGContextFillRect(context,Quartz.CGRectMake(0,0,100,100))Quartz.CGContextRestoreGState(context)defdoPatternPhase(context):pattern=createRedBlackCheckerBoardPattern(Quartz.CGAffineTransformMakeScale(20,20))ifpatternisNone:print("Couldn't create pattern!")return# Create the pattern color space for a colored pattern.patternColorSpace=Quartz.CGColorSpaceCreatePattern(None)Quartz.CGContextSetFillColorSpace(context,patternColorSpace)# Paint the pattern with alpha = 1.color=(1,)Quartz.CGContextSetFillPattern(context,pattern,color)# Rectangle 1Quartz.CGContextFillRect(context,Quartz.CGRectMake(20,150,100,100))# Rectangle 2Quartz.CGContextFillRect(context,Quartz.CGRectMake(130,150,100,100))# Rectangle 3# Set the pattern phase so that the pattern origin# is at the lower-left of the shape.Quartz.CGContextSetPatternPhase(context,scalePatternPhase(Quartz.CGSizeMake(20,20)))Quartz.CGContextFillRect(context,Quartz.CGRectMake(20,20,100,100))# Rectangle 4# Set the pattern phase so that the pattern origin# is at the lower-left corner of the shape.Quartz.CGContextSetPatternPhase(context,scalePatternPhase(Quartz.CGSizeMake(130,20)))Quartz.CGContextTranslateCTM(context,130,20)Quartz.CGContextFillRect(context,Quartz.CGRectMake(0,0,100,100))defdrawRotatedRect(c,p):r=Quartz.CGRectMake(0,0,1,1)Quartz.CGContextSaveGState(c)if1:Quartz.CGContextTranslateCTM(c,p.x,p.y)Quartz.CGContextRotateCTM(c,Utilities.DEGREES_TO_RADIANS(45))Quartz.CGContextTranslateCTM(c,-r.size.width/2,-r.size.height/2)Quartz.CGContextFillRect(c,r)Quartz.CGContextRestoreGState(c)defmyStencilPatternProc(info,patternCellContext):drawRotatedRect(patternCellContext,Quartz.CGPointMake(1,1))drawRotatedRect(patternCellContext,Quartz.CGPointMake(1.75,1))defcreateStencilPattern(patternTransform):pattern=Quartz.CGPatternCreate(None,# The pattern cell origin is at (0,0) with a# width of 2.5 units and a height of 2 units. This# pattern cell has transparent areas since# the pattern proc only marks a portion of the cell.Quartz.CGRectMake(0,0,2.5,2),# Use the pattern transform supplied to this routine.scalePatternMatrix(patternTransform),# Use the width and height of the pattern cell for# the xStep and yStep.2.5,2,# This value is a good choice for this type of pattern and it# avoids seams between tiles.Quartz.kCGPatternTilingConstantSpacingMinimalDistortion,# This pattern does not have intrinsic color.False,# Must be False for a stencil pattern.myStencilPatternProc,)returnpatterndefdoStencilPattern(context):pattern=createStencilPattern(Quartz.CGAffineTransformMakeScale(20,20))ifpatternisNone:print("Couldn't create pattern!")return# Create the pattern color space. This pattern is a stencil# pattern so when the code sets the pattern it also sets the# color it will paint the pattern with. In order to# set the pattern color space in this case we also have# to say what underlying color space should be used when# the pattern proc is called.baseColorSpace=Utilities.getTheCalibratedRGBColorSpace()patternColorSpace=Quartz.CGColorSpaceCreatePattern(baseColorSpace)Quartz.CGContextSetFillColorSpace(context,patternColorSpace)# This code is finished with the pattern color space and can release# it because Quartz retains it while it is the current color space.delpatternColorSpace# The pattern has no intrinsic color so the color components array# passed to CGContextSetFillPattern contains the colors to paint# the pattern with in the baseColorSpace. In the case here,# first paint the pattern with opaque blue.color=(0.11,0.208,0.451,1.0)Quartz.CGContextSetFillPattern(context,pattern,color)# Rectangle 1.Quartz.CGContextSetPatternPhase(context,scalePatternPhase(Quartz.CGSizeMake(20,160)))Quartz.CGContextBeginPath(context)Quartz.CGContextAddRect(context,Quartz.CGRectMake(20,160,105,80))Quartz.CGContextDrawPath(context,Quartz.kCGPathFillStroke)# Rectangle 2.# Set the pattern color so the stencil pattern# is painted in a yellow shade.color=(1.0,0.816,0.0,1.0)Quartz.CGContextSetFillPattern(context,pattern,color)# Set the pattern phase to the origin of the next object.Quartz.CGContextSetPatternPhase(context,scalePatternPhase(Quartz.CGSizeMake(140,160)))Quartz.CGContextBeginPath(context)Quartz.CGContextAddRect(context,Quartz.CGRectMake(140,160,105,80))Quartz.CGContextDrawPath(context,Quartz.kCGPathFillStroke)Quartz.CGContextSaveGState(context)if1:Quartz.CGContextSetFillColorWithColor(context,Utilities.getRGBOpaqueBlueColor())# Fill color is now blue. Paint two blue rectangles# that will be underneath the drawing which follows.Quartz.CGContextFillRect(context,Quartz.CGRectMake(20,40,105,80))Quartz.CGContextFillRect(context,Quartz.CGRectMake(140,40,105,80))Quartz.CGContextRestoreGState(context)# The fill color is again the stencil pattern with# the underlying fill color an opaque yellow.# Rectangle 3.# This paints over the blue rect just painted at 20,40# and the blue underneath is visible where the pattern has# transparent areas.Quartz.CGContextSetPatternPhase(context,scalePatternPhase(Quartz.CGSizeMake(20,40)))Quartz.CGContextFillRect(context,Quartz.CGRectMake(20,40,105,80))# Rectangle 4.# Change the alpha value of the underlying color used# to paint the stencil pattern.color=list(color)color[3]=0.75Quartz.CGContextSetFillPattern(context,pattern,color)Quartz.CGContextSetPatternPhase(context,scalePatternPhase(Quartz.CGSizeMake(140,40)))Quartz.CGContextFillRect(context,Quartz.CGRectMake(140,40,105,80))classMyPDFPatternInfo(object):rect=NonepdfDoc=NonedefmyDrawPDFPattern(info,patternCellContext):# This pattern proc draws the first page of a PDF document to# a destination rect.Quartz.CGContextSaveGState(patternCellContext)Quartz.CGContextClipToRect(patternCellContext,info.rect)Quartz.CGContextDrawPDFDocument(patternCellContext,info.rect,info.pdfDoc,1)Quartz.CGContextRestoreGState(patternCellContext)# Versions of Tiger prior to 10.4.3 have a bug such that use of an xStep that# doesn't match the width of pattern bounding box or a yStep that doesn't match the# height of the pattern bounding box produces incorrect results when drawn# to a bit-based context. Setting TIGERSTEPWORKAROUND works around this bug.TIGERSTEPWORKAROUND=1SCALEPATTERN=1OPTIMIZEDPERF=0defcreatePDFPatternPattern(additionalTransformP,url):patternInfoP=MyPDFPatternInfo()patternInfoP.pdfDoc=Quartz.CGPDFDocumentCreateWithURL(url)ifpatternInfoP.pdfDocisNone:print("Couldn't create PDF document reference!")returnpatternInfoP.rect=Quartz.CGPDFDocumentGetMediaBox(patternInfoP.pdfDoc,1)# Set the origin of the media rect for the PDF document to (0,0).patternInfoP.rect.origin=Quartz.CGPointZeroifadditionalTransformPisnotNone:patternTransform=additionalTransformPelse:patternTransform=Quartz.CGAffineTransformIdentity# To emulate the example from the bitmap context drawing chapter,# the tile offset in each dimension is the tile size in that# dimension, plus 6 units.ifSCALEPATTERN:tileOffsetX=6.+patternInfoP.rect.size.widthtileOffsetY=6.+patternInfoP.rect.size.heightelse:tileOffsetX=2.+patternInfoP.rect.size.widthtileOffsetY=2.+patternInfoP.rect.size.height# Tiger versions 10.4.0 - 10.4.2 have a bug such that the bounds# width and height is incorrectly used as the xstep,ystep.# To workaround this bug, we can make the bounds rect incorporate# the xstep,ystep since xstep,ystep are larger than the bounds.ifOPTIMIZEDPERForTIGERSTEPWORKAROUND:patternRect=Quartz.CGRectMake(0,0,tileOffsetX,tileOffsetY)else:patternRect=patternInfoP.rectifOPTIMIZEDPERF:# Produces best performance if bbox == xstep/ystepspacing=Quartz.kCGPatternTilingConstantSpacingelse:spacing=Quartz.kCGPatternTilingConstantSpacingMinimalDistortionpattern=Quartz.CGPatternCreate(patternInfoP,# The pattern cell size is the size# of the media rect of the PDF document.patternRect,scalePatternMatrix(patternTransform),tileOffsetX,tileOffsetY,# This value is a good choice for this type of pattern and# it avoids seams between tiles.spacing,# This pattern has intrinsic color.True,myDrawPDFPattern,)# If the pattern can't be created then release the# pattern resources and info parameter.ifpatternisNone:patternInfoP=NonereturnpatterndefdrawWithPDFPattern(context,url):ifSCALEPATTERN:patternMatrix=Quartz.CGAffineTransformMakeScale(1.0/3,1.0/3)else:patternMatrix=Quartz.CGAffineTransformMakeScale(1,1)# Scale the PDF pattern down to 1/3 its original size.pdfPattern=createPDFPatternPattern(patternMatrix,url)ifpdfPatternisNone:print("Couldn't create pattern!")return# Create the pattern color space. Since the pattern# itself has intrinsic color, the 'baseColorSpace' parameter# to CGColorSpaceCreatePattern must be None.patternColorSpace=Quartz.CGColorSpaceCreatePattern(None)Quartz.CGContextSetFillColorSpace(context,patternColorSpace)# Quartz retains the color space so this code# can now release it since it no longer needs it.delpatternColorSpace# Paint the pattern with an alpha of 1.color=(1,)Quartz.CGContextSetFillPattern(context,pdfPattern,color)# Quartz retains the pattern so this code# can now release it since it no longer needs it.delpdfPattern# Fill a US Letter size rect with the pattern.Quartz.CGContextFillRect(context,Quartz.CGRectMake(0,0,612,792))

QuartzTextDrawing.py

importQuartzimportUtilitiesimportsysdefdrawQuartzRomanText(context):text=b"Quartz"textlen=len(text)fontSize=60opaqueBlack=[0.0,0.0,0.0,1.0]opaqueRed=[0.663,0.0,0.031,1.0]# Set the fill color space. This sets the# fill painting color to opaque black.Quartz.CGContextSetFillColorSpace(context,Utilities.getTheCalibratedRGBColorSpace())# The Cocoa framework calls the draw method with an undefined# value of the text matrix. It's best to set it to what is needed by# this code: the identity transform.Quartz.CGContextSetTextMatrix(context,Quartz.CGAffineTransformIdentity)# Set the font with the PostScript name "Times-Roman", at# fontSize points, with the MacRoman encoding.Quartz.CGContextSelectFont(context,b"Times-Roman",fontSize,Quartz.kCGEncodingMacRoman)# The default text drawing mode is fill. Draw the text at (70, 400).Quartz.CGContextShowTextAtPoint(context,70,400,text,textlen)# Set the fill color to red.Quartz.CGContextSetFillColor(context,opaqueRed)# Draw the next piece of text where the previous one left off.Quartz.CGContextShowText(context,text,textlen)foriinrange(3):# Get the current text pen position.p=Quartz.CGContextGetTextPosition(context)# Translate to the current text pen position.Quartz.CGContextTranslateCTM(context,p.x,p.y)# Rotate clockwise by 90 degrees for the next# piece of text.Quartz.CGContextRotateCTM(context,Utilities.DEGREES_TO_RADIANS(-90))# Draw the next piece of text in blac at the origin.Quartz.CGContextSetFillColor(context,opaqueBlack)Quartz.CGContextShowTextAtPoint(context,0,0,text,textlen)# Draw the next piece of text where the previous piece# left off and paint it with red.Quartz.CGContextSetFillColor(context,opaqueRed)Quartz.CGContextShowText(context,text,textlen)defmyCGContextStrokeLineSegments(context,s,count):# CGContextStrokeLineSegments is available only on Tiger and later# so if it isn't available, use an emulation of# CGContextStrokeLineSegments. It is better to use the# built-in CGContextStrokeLineSegments since it has significant# performance optimizations on some hardware.ifhasattr(Quartz,'CGContextStrokeLineSegments'):Quartz.CGContextStrokeLineSegments(context,s,count)else:Quartz.CGContextBeginPath(context)forkinrange(0,count,2):Quartz.CGContextMoveToPoint(context,s[k].x,s[k].y)Quartz.CGContextAddLineToPoint(context,s[k+1].x,s[k+1].y)Quartz.CGContextStrokePath(context)_gridLines=[]defdrawGridLines(context):numlines=60ifnot_gridLines:stepsize=4.0val=0foriinrange(0,2*numlines,2):_gridLines.append(Quartz.CGPointMake(val,-60))_gridLines.append(Quartz.CGPointMake(val,200))val+=stepsizeval=-20foriinrange(2*numlines,4*numlines,2):_gridLines.append(Quartz.CGPointMake(0,val))_gridLines.append(Quartz.CGPointMake(400,val))val+=stepsizemyCGContextStrokeLineSegments(context,_gridLines,len(_gridLines))defdrawQuartzTextWithTextModes(context):fillText=b"Fill "strokeText=b"Stroke "fillAndStrokeText=b"FillStroke "invisibleText=b"Invisible "clipText=b"ClipText "fillStrokeClipText=b"FillStrokeClip "fontSize=40.0extraLeading=5.0dash=(1,1)opaqueRed=(1.0,0.0,0.0,1.0)# Set the fill and stroke color space. This sets the# fill and stroke painting color to opaque black.Quartz.CGContextSetFillColorSpace(context,Utilities.getTheCalibratedRGBColorSpace())Quartz.CGContextSetStrokeColorSpace(context,Utilities.getTheCalibratedRGBColorSpace())# The Cocoa framework calls the draw method with an undefined# value of the text matrix. It's best to set it to what is needed by# this code: the identity transform.Quartz.CGContextSetTextMatrix(context,Quartz.CGAffineTransformIdentity)# Set the font with the PostScript name "Times-Roman", at# fontSize points, with the MacRoman encoding.Quartz.CGContextSelectFont(context,b"Times-Roman",fontSize,Quartz.kCGEncodingMacRoman)# ---- Text Line 1 ----# Default text drawing mode is fill. Draw the text at (10, 400).Quartz.CGContextShowTextAtPoint(context,10,400,fillText,len(fillText))# Set the fill color to red.Quartz.CGContextSetFillColor(context,opaqueRed)Quartz.CGContextSetTextPosition(context,180,400)Quartz.CGContextShowText(context,fillText,len(fillText))# Translate down for the next line of text.Quartz.CGContextTranslateCTM(context,0,-(fontSize+extraLeading))# ---- Text Line 2 ----# Now stroke the text by setting the text drawing mode# to kCGTextStroke. When stroking text, Quartz uses the stroke# color in the graphics state.Quartz.CGContextSetTextDrawingMode(context,Quartz.kCGTextStroke)Quartz.CGContextShowTextAtPoint(context,10,400,strokeText,len(strokeText))# When stroking text, the line width and other gstate parameters# that affect stroking affect text stroking as well.Quartz.CGContextSetLineWidth(context,2)Quartz.CGContextSetLineDash(context,0,dash,2)Quartz.CGContextSetTextPosition(context,180,400)Quartz.CGContextShowText(context,strokeText,len(strokeText))# Reset the line dash and line width to their defaults.Quartz.CGContextSetLineDash(context,0,None,0)Quartz.CGContextSetLineWidth(context,1)# Translate down for the next line of text.Quartz.CGContextTranslateCTM(context,0,-(fontSize+extraLeading))# ---- Text Line 3 ----# Set the text drawing mode so that text is both filled and# stroked. This produces text that is filled with the fill# color and stroked with the stroke color.Quartz.CGContextSetTextDrawingMode(context,Quartz.kCGTextFillStroke)Quartz.CGContextShowTextAtPoint(context,10,400,fillAndStrokeText,len(fillAndStrokeText))# Now draw again with a thicker stroke width.Quartz.CGContextSetLineWidth(context,2)Quartz.CGContextSetTextPosition(context,180,400)Quartz.CGContextShowText(context,fillAndStrokeText,len(fillAndStrokeText))Quartz.CGContextSetLineWidth(context,1)Quartz.CGContextTranslateCTM(context,0,-(fontSize+extraLeading))# ---- Text Line 4 ----# Set the text drawing mode to invisible so that the next piece of# text does not appear. Quartz updates the text position as# if it had been drawn.Quartz.CGContextSetTextDrawingMode(context,Quartz.kCGTextInvisible)Quartz.CGContextShowTextAtPoint(context,10,400,invisibleText,len(invisibleText))Quartz.CGContextSetTextDrawingMode(context,Quartz.kCGTextFill)Quartz.CGContextSetTextPosition(context,180,400)Quartz.CGContextShowText(context,fillText,len(fillText))Quartz.CGContextTranslateCTM(context,0,-(fontSize+extraLeading))# ---- Text Line 5 ----Quartz.CGContextSaveGState(context)if1:# Use the text as a clipping path.Quartz.CGContextSetTextDrawingMode(context,Quartz.kCGTextClip)Quartz.CGContextShowTextAtPoint(context,10,400,clipText,len(clipText))# Position and draw a grid of lines.Quartz.CGContextTranslateCTM(context,10,400)drawGridLines(context)Quartz.CGContextRestoreGState(context)Quartz.CGContextSaveGState(context)if1:# The current text position is that after the last piece# of text has been drawn. Since CGContextSaveGState/# CGContextRestoreGState do not affect the text position or# the text matrix, the text position is that after the last# text was "drawn", that drawn with the kCGTextClip mode# above. This is where the next text drawn will go if it# isn't explicitly positioned.nextTextPosition=Quartz.CGContextGetTextPosition(context)# Draw so that the text is filled, stroked, and then used# the clip subsequent drawing.Quartz.CGContextSetTextDrawingMode(context,Quartz.kCGTextFillStrokeClip)# Explicitly set the text position.Quartz.CGContextSetTextPosition(context,180,400)nextTextPosition=Quartz.CGContextGetTextPosition(context)Quartz.CGContextShowText(context,fillStrokeClipText,len(fillStrokeClipText))# Adjust the location of the grid lines so that they overlap the# text just drawn.Quartz.CGContextTranslateCTM(context,nextTextPosition.x,nextTextPosition.y)# Draw the grid lines clipped by the text.drawGridLines(context)Quartz.CGContextRestoreGState(context)# showFlippedTextAtPoint is a cover routine for Quartz.CGContextShowText# that is useful for drawing text in a coordinate system where the y axis# is flipped relative to the default Quartz coordinate system.## This code assumes that the text matrix is only used to# flip the text, not to perform scaling or any other# possible use of the text matrix.## This function preserves the a, b, c, and d components of# the text matrix across its execution but updates the# tx, ty components (the text position) to reflect the# text just drawn. If all the text you draw is flipped, it# isn't necessary to continually set the text matrix. Instead# you could simply call CGContextSetTextMatrix once with# the flipped matrix each time your drawing# code is called.defshowFlippedTextAtPoint(c,x,y,text,textLen):t=Quartz.CGAffineTransform(1.0,0.0,0.0,-1.0,0.0,0.0)# Get the existing text matrix.s=Quartz.CGContextGetTextMatrix(c)# Set the text matrix to the one that flips in y.Quartz.CGContextSetTextMatrix(c,t)# Draw the text at the point.Quartz.CGContextShowTextAtPoint(c,x,y,text,textLen)# Get the updated text position.p=Quartz.CGContextGetTextPosition(c)# Update the saved text matrix to reflect the updated# text position.s.tx=p.x;s.ty=p.y# Reset to the text matrix in effect when this# routine was called but with the text position updated.Quartz.CGContextSetTextMatrix(c,s)defdrawQuartzTextWithTextMatrix(context):fontSize=60.0extraLeading=10.0text=b"Quartz "textlen=len(text)# The Cocoa framework calls the draw method with an undefined# value of the text matrix. It's best to set it to what is needed by# this code. Initially that is the identity transform.Quartz.CGContextSetTextMatrix(context,Quartz.CGAffineTransformIdentity)# Set the font with the PostScript name "Times-Roman", at# fontSize points, with the MacRoman encoding.Quartz.CGContextSelectFont(context,b"Times-Roman",fontSize,Quartz.kCGEncodingMacRoman)# ---- Text Line 1 ----# Draw the text at (10, 600).Quartz.CGContextShowTextAtPoint(context,10,600,text,textlen)# Get the current text position. The text pen is at the trailing# point from the text just drawn.textPosition=Quartz.CGContextGetTextPosition(context)# Set the text matrix to one that flips text in y and sets# the text position to the user space coordinate (0,0).t=Quartz.CGAffineTransformMake(1,0,0,-1,0,0)Quartz.CGContextSetTextMatrix(context,t)# Set the text position to the point where the previous text ended.Quartz.CGContextSetTextPosition(context,textPosition.x,textPosition.y)# Draw the text at the current text position. It will be drawn# flipped in y, relative to the text drawn previously.Quartz.CGContextShowText(context,text,textlen)# ---- Text Line 2 ----# Translate down for the next piece of text.Quartz.CGContextTranslateCTM(context,0,-(3*fontSize+extraLeading))Quartz.CGContextSaveGState(context)if1:# Change the text matrix to {1, 0, 0, 3, 0, 0}, which# scales text by a factor of 1 in x and 3 in y.# This scaling doesn't affect any drawing other than text# drawing since only text drawing is transformed by# the text matrix.t=Quartz.CGAffineTransformMake(1,0,0,3,0,0)Quartz.CGContextSetTextMatrix(context,t)# This text is scaled relative to the previous text# because of the text matrix scaling.Quartz.CGContextShowTextAtPoint(context,10,600,text,textlen)# This restores the graphics state to what it was at the time# of the last Quartz.CGContextSaveGState, but since the text matrix# isn't part of the Quartz graphics state, it isn't affected.Quartz.CGContextRestoreGState(context)# The text matrix isn't affected by Quartz.CGContextSaveGState and# Quartz.CGContextRestoreGState. You can see this by observing that# the next text piece appears immediately after the first piece# and with the same text scaling as that text drawn with the# text matrix established before we did CGContextRestoreGState.Quartz.CGContextShowText(context,text,textlen)# ---- Text Line 3 ----# Translate down for the next piece of text.Quartz.CGContextTranslateCTM(context,0,-(fontSize+extraLeading))# Reset the text matrix to the identity matrix.Quartz.CGContextSetTextMatrix(context,Quartz.CGAffineTransformIdentity)# Now draw text in a flipped coordinate system.Quartz.CGContextSaveGState(context)if1:# Flip the coordinate system to mimic a coordinate system with the origin# at the top-left corner of a window. The new origin is at 600 units in# +y from the old origin and the y axis now increases with positive y# going down the window.Quartz.CGContextConcatCTM(context,Quartz.CGAffineTransformMake(1,0,0,-1,0,600))# This text will be flipped along with the CTM.Quartz.CGContextShowTextAtPoint(context,10,10,text,textlen)# Obtain the user space coordinates of the current text position.textPosition=Quartz.CGContextGetTextPosition(context)# Draw text at that point but flipped in y.showFlippedTextAtPoint(context,textPosition.x,textPosition.y,text,textlen)Quartz.CGContextRestoreGState(context)

Shadings.py

importQuartzimportUtilitiesimportsysdefRedBlackRedRampEvaluate(info,input,output):# The domain of this function is 0 - 1. For an input value of 0# this function returns the color to paint at the start point# of the shading. For an input value of 1 this function returns# the color to paint at the end point of the shading. This# is a 1 in, 4 out function where the output values correspond# to an r,g,b,a color.## For an RGB color space as the shading color space, this# function evaluates to produce a blend from pure, opaque# red at the start point to a pure opaque black at the# midpoint, and back to pure opaque red at the end point.return(# The red component evaluates to 1 for an input value of 0# (the start point of the shading). It smoothly reduces# to zero at the midpoint of the shading (input value 0.5)# and increases up to 1 at the endpoint of the shading (input# value 1.0).abs(1.0-input[0]*2),# The green and blue components are always 0.0,0,# The alpha component is 1 for the entire shading.1,)defcreateFunctionForRGB(evaluationFunction):# This is a 1 in, 4 out function for drawing shadings# in a 3 component (plus alpha) color space. Shadings# parameterize the endpoints such that the starting point# represents the function input value 0 and the ending point# represents the function input value 1.domain=(0,1)# The range is the range for the output colors. For an rgb# color space the values range from 0-1 for the r,g,b, and a# components.range=(# The red component, min and max.0,1,# The green component, min and max.0,1,# The blue component, min and max.0,1,# The alpha component, min and max.0,1)# Dimension of domain is 1 and dimension of range is 4.function=Quartz.CGFunctionCreate(None,1,domain,4,range,evaluationFunction)iffunctionisNone:print("Couldn't create the CGFunction!")returnNonereturnfunctiondefdoSimpleAxialShading(context):# This shading paints colors in the calibrated Generic RGB# color space so it needs a function that evaluates 1 in to 4 out.axialFunction=createFunctionForRGB(RedBlackRedRampEvaluate)ifaxialFunctionisNone:return# Start the shading at the point (20,20) and# end it at (420,20). The axis of the shading# is a line from (20,20) to (420,20).startPoint=Quartz.CGPoint(x=20,y=20)endPoint=Quartz.CGPoint(x=420,y=20)# Don't extend this shading.extendStart=extendEnd=Falseshading=Quartz.CGShadingCreateAxial(Utilities.getTheCalibratedRGBColorSpace(),startPoint,endPoint,axialFunction,extendStart,extendEnd)# The shading retains the function and this code# is done with the function so it should release it.delaxialFunctionifshadingisNone:print("Couldn't create the shading!")return# Draw the shading. This paints the shading to# the destination context, clipped by the# current clipping area.Quartz.CGContextDrawShading(context,shading)defRedGreenRampEvaluate(info,input,output):# The domain of this function is 0 - 1. For an input value of 0# this function returns the color to paint at the start point# of the shading. For an input value of 1 this function returns# the color to paint at the end point of the shading. This# is a 1 in, 4 out function where the output values correspond# to an r,g,b,a color.## For an RGB color space as the shading color space, this# function evaluates to produce a blend from pure, opaque# red at the start point to a pure opaque green at the end point.return(# The red component starts at 1 and reduces to zero as the input# goes from 0 (the start point of the shading) and increases# to 1 (the end point of the shading).1.0-input[0],# The green component starts at 0 for an input of 0# (the start point of the shading) and increases to 1# for an input value of 1 (the end point of the shading).input[0],# The blue component is always 0.0,# The alpha component is always 1, the shading is always opaque.1,)defdoExampleAxialShading(context):rect=Quartz.CGRectMake(0,0,240,240)# This shading paints colors in the calibrated Generic RGB# color space so it needs a function that evaluates 1 in to 4 out.redGreenFunction=createFunctionForRGB(RedGreenRampEvaluate)ifredGreenFunctionisNone:return# Start the shading at the point (20,20) and# end it at (220,220). The axis of the shading# is a diagonal line from (20,20) to (220,220).startPoint=Quartz.CGPoint(x=20,y=20)endPoint=Quartz.CGPoint(x=220,y=220)# Don't extend this shading.extendStart=extendEnd=Falseshading=Quartz.CGShadingCreateAxial(Utilities.getTheCalibratedRGBColorSpace(),startPoint,endPoint,redGreenFunction,extendStart,extendEnd)ifshadingisNone:print("Couldn't create the shading!")return# Position for the first portion of the drawing.Quartz.CGContextTranslateCTM(context,40,260)# Stroke a black rectangle that will frame the shading.Quartz.CGContextSetLineWidth(context,2)Quartz.CGContextSetStrokeColorWithColor(context,Utilities.getRGBOpaqueBlackColor())Quartz.CGContextStrokeRect(context,rect)Quartz.CGContextSaveGState(context)if1:# Clip to the rectangle that was just stroked.Quartz.CGContextClipToRect(context,rect)# Draw the shading. This paints the shading to# the destination context, clipped to rect.Quartz.CGContextDrawShading(context,shading)# Release the shading once the code is finished with it.delshading# Restore the graphics state so that the rectangular# clip is no longer present.Quartz.CGContextRestoreGState(context)# Prepare for the next shading.Quartz.CGContextTranslateCTM(context,0,-250)# Extend this shading.extendStart=extendEnd=Trueshading=Quartz.CGShadingCreateAxial(Utilities.getTheCalibratedRGBColorSpace(),startPoint,endPoint,redGreenFunction,extendStart,extendEnd)# The shading retains the function and this code# is done with the function so it should release it.delredGreenFunctionifshadingisNone:print("Couldn't create the shading!")return# Stroke with the current stroke color.Quartz.CGContextStrokeRect(context,rect)Quartz.CGContextSaveGState(context)if1:Quartz.CGContextClipToRect(context,rect)# Draw the shading. This paints the shading to# the destination context, clipped to rect.Quartz.CGContextDrawShading(context,shading)Quartz.CGContextRestoreGState(context)# Now paint some text with a shading.Quartz.CGContextSaveGState(context)if1:Quartz.CGContextTranslateCTM(context,260,0)Quartz.CGContextSetTextMatrix(context,Quartz.CGAffineTransformIdentity)# Set the font with the PostScript name "Times-Roman", at# 80 points, with the MacRoman encoding.Quartz.CGContextSelectFont(context,b"Times-Roman",80,Quartz.kCGEncodingMacRoman)# Rotate so that the text characters are rotated# relative to the page.Quartz.CGContextRotateCTM(context,Utilities.DEGREES_TO_RADIANS(45))# Set the text drawing mode to clip so that# the characters in the string are intersected with# the clipping area.Quartz.CGContextSetTextDrawingMode(context,Quartz.kCGTextClip)Quartz.CGContextShowTextAtPoint(context,30,0,b"Shading",7)# At this point nothing has been painted; the# glyphs in the word "Shading" have been intersected# with the previous clipping area to create a new# clipping area.# Rotate the coordinate system back so that the# shading is not rotated relative to the page.Quartz.CGContextRotateCTM(context,Utilities.DEGREES_TO_RADIANS(-45))# Draw the shading, painting the shading# to the destination context, clipped by the glyphs.Quartz.CGContextDrawShading(context,shading)Quartz.CGContextRestoreGState(context)# Release the shading once the code is finished with it.delshadingclassMyStartEndColor(object):def__init__(self):self.startColor=[0.0]*3self.endColor=[0.0]*3defStartColorEndColorEvaluate(info,input,output):# The domain of this function is 0 - 1. For an input value of 0# this function returns the color to paint at the start point# of the shading. For an input value of 1 this function returns# the color to paint at the end point of the shading. This# is a 1 in, 4 out function where the output values correspond# to an r,g,b,a color.## This function evaluates to produce a blend from startColor to endColor.## Note that the returned results are clipped to the range# by Quartz so this function doesn't worry about values# that are outside the range 0-1.# Weight the starting and ending color components depending# on what position in the blend the input value specifies.return((info.startColor[0]*(1-input[0])+info.endColor[0]*input[0]),(info.startColor[1]*(1-input[0])+info.endColor[1]*input[0]),(info.startColor[2]*(1-input[0])+info.endColor[2]*input[0]),# The alpha component is always 1, the shading is always opaque.1,)defcreateFunctionWithStartEndColorRamp(startColor,endColor):# Use a pointer to a MyStartEndColor as a way of# parameterizing the color ramp this function produces.startEndColorP=MyStartEndColor()# Set up start and end colors in the info structure.startEndColorP.startColor[0]=startColor[0]startEndColorP.startColor[1]=startColor[1]startEndColorP.startColor[2]=startColor[2]startEndColorP.endColor[0]=endColor[0]startEndColorP.endColor[1]=endColor[1]startEndColorP.endColor[2]=endColor[2]# This is a 1 in, 4 out function for drawing shadings# in a 3 component (plus alpha) color space. Shadings# parameterize the endpoints such that the starting point# represents the function input value 0 and the ending point# represents the function input value 1.domain=(0,1)# The range is the range for the output colors. For an rgb# color space the values range from 0-1 for the r,g,b, and a# components.range=(# The red component, min and max.0,1,# The green component, min and max.0,1,# The blue component, min and max.0,1,# The alpha component, min and max.0,1,)# Pass startEndColorP as the info parameter.function=Quartz.CGFunctionCreate(startEndColorP,1,domain,4,range,StartColorEndColorEvaluate)iffunctionisNone:print("Couldn't create the CGFunction!")returnNonereturnfunctiondefdoSimpleRadialShading(context):startColor=[0.663,0.0,0.031]# Red.endColor=[1.0,0.8,0.4]# Light yellow.# This function describes a color ramp where the starting color# is red and the ending color is blue.redYellowFunction=createFunctionWithStartEndColorRamp(startColor,endColor)ifredYellowFunctionisNone:returnQuartz.CGContextTranslateCTM(context,120,120)# Circles whose origin is the same.circleACenter=Quartz.CGPoint(x=0,y=0)circleBCenter=circleACenter# The starting circle is inside the ending circle.circleARadius=50circleBRadius=100# Don't extend the shading.extendStart=extendEnd=Falseshading=Quartz.CGShadingCreateRadial(Utilities.getTheCalibratedRGBColorSpace(),circleACenter,circleARadius,circleBCenter,circleBRadius,redYellowFunction,extendStart,extendEnd)delredYellowFunctionifshadingisNone:print("Couldn't create the shading!")returnQuartz.CGContextDrawShading(context,shading)defdoExampleRadialShadings(context):magenta=[1,0,1]# Pure magenta.magenta30=[0.3,0,0.3]# 30% magenta.black=[0,0,0]red=[1,0,0]green=[0,1,0]blue=[0,0,1]redgreen=[0.66,1,0.04]# A red-green shade.Quartz.CGContextTranslateCTM(context,120,550)# This function describes a color ramp where the starting color# is a full magenta, the ending color is 30% magenta.magentaFunction=createFunctionWithStartEndColorRamp(magenta,magenta30)ifmagentaFunctionisNone:print("Couldn't create the magenta function!")return# Shading 1. Circle A is completely inside circle B but with# different origins. Circle A has radius 0 which produces# a point source.# The center of circle A is offset from the origin.circleACenter=Quartz.CGPoint(x=30,y=40)# The center of circle B is at the origin.circleBCenter=Quartz.CGPoint(x=0,y=0)# A radius of zero produces a point source.circleARadius=0circleBRadius=100# Don't extend the shading.extendStart=extendEnd=Falseshading=Quartz.CGShadingCreateRadial(Utilities.getTheCalibratedRGBColorSpace(),circleACenter,circleARadius,circleBCenter,circleBRadius,magentaFunction,extendStart,extendEnd)# Finished with the magenta function so release it.delmagentaFunctionifshadingisNone:print("Couldn't create the shading!")returnQuartz.CGContextDrawShading(context,shading)# Finished with the shading so release it.delshading# Shading 2. Circle A is completely inside# circle B but with different origins.# The starting color is red and the ending color is green.redGreenFunction=createFunctionWithStartEndColorRamp(red,green)ifredGreenFunctionisNone:print("Couldn't create the red-Green function!")returncircleACenter.x=55circleACenter.y=70circleBCenter.x=20circleBCenter.y=0circleARadius=10# The outer circle is outside the clipping path so the# color at the edge of the shape is not# that at the radius of the outer circle.circleBRadius=200# Extend the end point of this shading.extendStart=FalseextendEnd=Trueshading=Quartz.CGShadingCreateRadial(Utilities.getTheCalibratedRGBColorSpace(),circleACenter,circleARadius,circleBCenter,circleBRadius,redGreenFunction,extendStart,extendEnd)# Finished with this function so release it.delredGreenFunctionifshadingisNone:print("Couldn't create the shading!")return# Set a clipping area to bound the extend. This code# sets a clipping area that corresponds to a circular# wedge. The starting circle is inside the clipping# area and the ending circle is outside.Quartz.CGContextSaveGState(context)if1:Quartz.CGContextTranslateCTM(context,250,0)Quartz.CGContextBeginPath(context)Quartz.CGContextMoveToPoint(context,25,0)Quartz.CGContextAddArc(context,25,0,130,Utilities.DEGREES_TO_RADIANS(30),Utilities.DEGREES_TO_RADIANS(-30),0)Quartz.CGContextClip(context)# Paint the shading.Quartz.CGContextDrawShading(context,shading)# Finished with the shading so release it.delshadingQuartz.CGContextRestoreGState(context)Quartz.CGContextTranslateCTM(context,-40,-250)# Shading 3. The starting circle is completely outside# the ending circle, no extension. The circles# have the same radii.circleACenter.x=0circleACenter.y=0circleBCenter.x=125circleBCenter.y=0circleARadius=50circleBRadius=50extendStart=extendEnd=False# Create a function that paints a red to black ramp.redBlackFunction=createFunctionWithStartEndColorRamp(red,black)ifredBlackFunctionisNone:print("Couldn't create the red-black function!")returnshading=Quartz.CGShadingCreateRadial(Utilities.getTheCalibratedRGBColorSpace(),circleACenter,circleARadius,circleBCenter,circleBRadius,redBlackFunction,extendStart,extendEnd)ifshadingisNone:print("Couldn't create the shading!")returnQuartz.CGContextDrawShading(context,shading)# Finished with the shading so release it.delshading# Shading 4. The starting circle is completely outside# the ending circle. The circles have different radii.circleACenter.x=120circleACenter.y=0circleBCenter.x=0circleBCenter.y=0circleARadius=75circleBRadius=30# Extend at the start and end.extendStart=extendEnd=Trueshading=Quartz.CGShadingCreateRadial(Utilities.getTheCalibratedRGBColorSpace(),circleACenter,circleARadius,circleBCenter,circleBRadius,redBlackFunction,extendStart,extendEnd)# Finished with this function so release it.delredBlackFunctionifshadingisNone:print("Couldn't create the shading!")returnQuartz.CGContextSaveGState(context)if1:Quartz.CGContextTranslateCTM(context,270,0)# Clip to an elliptical path so the shading# does not extend to infinity at the larger end.Quartz.CGContextBeginPath(context)Utilities.myCGContextAddEllipseInRect(context,Quartz.CGRectMake(-200,-200,450,400))Quartz.CGContextClip(context)Quartz.CGContextDrawShading(context,shading)# Finished with the shading so release it.delshadingQuartz.CGContextRestoreGState(context)Quartz.CGContextTranslateCTM(context,30,-200)# The starting color is blue, the ending color is a red-green color.blueGreenFunction=createFunctionWithStartEndColorRamp(blue,redgreen)ifblueGreenFunctionisNone:print("Couldn't create the blue-Green function!")return# Shading 5. The circles partially overlap and have# different radii with the larger circle at the start.circleACenter.x=0circleACenter.y=0circleBCenter.x=90circleBCenter.y=30circleARadius=75circleBRadius=45extendStart=extendEnd=Falseshading=Quartz.CGShadingCreateRadial(Utilities.getTheCalibratedRGBColorSpace(),circleACenter,circleARadius,circleBCenter,circleBRadius,blueGreenFunction,extendStart,extendEnd)ifshadingisNone:print("Couldn't create the shading!")returnQuartz.CGContextDrawShading(context,shading)# Finished with the shading so release it.delshadingQuartz.CGContextTranslateCTM(context,200,0)# Shading 6. The circles partially overlap and have# different radii with the larger circle at the end.circleARadius=45circleBRadius=75shading=Quartz.CGShadingCreateRadial(Utilities.getTheCalibratedRGBColorSpace(),circleACenter,circleARadius,circleBCenter,circleBRadius,blueGreenFunction,extendStart,extendEnd)# Finished with this function so release it.delblueGreenFunctionifshadingisNone:print("Couldn't create the shading!")returnQuartz.CGContextDrawShading(context,shading)defdoEllipseShading(context):black=[0,0,0]red=[1,0,0]# This function describes a color ramp where the starting color# is red, the ending color is black.redBlackFunction=createFunctionWithStartEndColorRamp(red,black)ifredBlackFunctionisNone:print("Couldn't create the red-black function!")returnQuartz.CGContextTranslateCTM(context,100,300)# Shading 1.# To obtain an elliptical shading requires that user space# at the time the shading is painted is transformed so that# the circles which define the radial shading geometry are# rotated and elliptical. User space will be rotated# by 45 degrees, then scaled by 1 in x and 2 in y to produce# the ellipses.# Compute the transform needed to create the rotated ellipses.t=Quartz.CGAffineTransformMakeRotation(Utilities.DEGREES_TO_RADIANS(45))t=Quartz.CGAffineTransformScale(t,1,2)circleACenter=Quartz.CGPoint(x=0,y=0)circleBCenter=Quartz.CGPoint(x=circleACenter.x+144,y=circleACenter.y)circleARadius=45circleBRadius=45# Don't extend this shading.extendStart=extendEnd=Falseshading=Quartz.CGShadingCreateRadial(Utilities.getTheCalibratedRGBColorSpace(),circleACenter,circleARadius,circleBCenter,circleBRadius,redBlackFunction,extendStart,extendEnd)ifshadingisNone:# Couldn't create the shading so release# the function before returning.print("Couldn't create the shading!")returnQuartz.CGContextSaveGState(context)if1:# Transform coordinates for the drawing of the shading.# This transform produces the rotated elliptical shading.# This produces the left shading in the figure, the# one where both the ellipses and the shading are# rotated relative to default user space.Quartz.CGContextConcatCTM(context,t)Quartz.CGContextDrawShading(context,shading)delshadingQuartz.CGContextRestoreGState(context)Quartz.CGContextTranslateCTM(context,300,10)# Shading 2.# Now draw the shading where the shading ellipses are# rotated but the axis between the origins of# the ellipses lies parallel to the x axis in default# user space. This is similar to the shading drawn# manually in Chapter 5.## To compute the correct origins for the shading,# the code needs to compute the points that,# transformed by the matrix t used to paint the shading,# produce the desired coordinates. We want coordinates# that are transformed as follows:## P' = P x t## where P' is the point in untransformed user space that# we want as the origin, P is the point in transformed# user space that will be transformed by t, the matrix# which transforms the circles into rotated ellipses.## So we want to calculate P such that P' = P x t .## Notice that if P = P' x Inverse(t) then:## P' = P' x Inverse(t) x t = P' x Identity = P'.## This means that we can calculate the point P# by computing P' x Inverse(t).inverseT=Quartz.CGAffineTransformInvert(t)# Now the code can transform the coordinates through the# inverse transform to compute the new coordinates. These# coordinates, when transformed with the transform t,# produce the original coordinate.circleACenter=Quartz.CGPointApplyAffineTransform(circleACenter,inverseT)circleBCenter=Quartz.CGPointApplyAffineTransform(circleBCenter,inverseT)shading=Quartz.CGShadingCreateRadial(Utilities.getTheCalibratedRGBColorSpace(),circleACenter,circleARadius,circleBCenter,circleBRadius,redBlackFunction,extendStart,extendEnd)# The code is finished with the function so release it.delredBlackFunctionifshadingisNone:print("Couldn't create the shading!")return# Transform coordinates for the drawing of the shading.# This transform produces the rotated elliptical shading.Quartz.CGContextConcatCTM(context,t)Quartz.CGContextDrawShading(context,shading)

ShadowsAndTransparencyLayers.py

importQuartzimportUtilitiesdefscaleShadowOffset(offset):shadowScaling=Utilities.getScalingFactor()# Adjust the shadow offset if scaling to export as bits. This is# equivalent to scaling base space by the scaling factor.ifshadowScaling!=1.0:offset=Quartz.CGSizeApplyAffineTransform(offset,Quartz.CGAffineTransformMakeScale(shadowScaling,shadowScaling))returnoffsetdefcreateTrianglePath(context):Quartz.CGContextBeginPath(context)Quartz.CGContextMoveToPoint(context,0,0)Quartz.CGContextAddLineToPoint(context,50,0)Quartz.CGContextAddLineToPoint(context,25,50)Quartz.CGContextClosePath(context)defdrawSimpleShadow(context):r=Quartz.CGRectMake(20,20,100,200)Quartz.CGContextTranslateCTM(context,20,300)# A blur of 0 is a hard edge blur.blur=0# An offset where both components are negative casts a shadow to the# left and down from the object. The coordinate system for the offset# is base space, not current user space.offset=Quartz.CGSize(-7,-7)offset=scaleShadowOffset(offset)# Set the shadow in the context.Quartz.CGContextSetShadow(context,offset,blur)# Object 1.# Paint a rectangle.Quartz.CGContextFillRect(context,r)# Object 2.Quartz.CGContextTranslateCTM(context,150,0)# A blur of 3 is a soft blur more# appropriate for a shadow effect.blur=3Quartz.CGContextSetShadow(context,offset,blur)# Fill an ellipse to the right of the rect.Quartz.CGContextBeginPath(context)Utilities.myCGContextAddEllipseInRect(context,r)Quartz.CGContextFillPath(context)# Object 3.Quartz.CGContextTranslateCTM(context,-130,-140)# Scale the coordinate system but the shadow is not affected. The offset# is in the base space of the context. Typically it looks best if the shapes# have a uniform shadow regardless of how the shapes were created, scaled,# rotated, or otherwise transformed.Quartz.CGContextScaleCTM(context,2,2)createTrianglePath(context)Quartz.CGContextSetStrokeColorWithColor(context,Utilities.getRGBOpaqueRedColor())Quartz.CGContextSetLineWidth(context,5)# Stroking produces a shadow as well.Quartz.CGContextStrokePath(context)# Object 4.Quartz.CGContextTranslateCTM(context,75,0)createTrianglePath(context)# Cast the shadow to the left and up from# the shape painted.offset.width=-5offset.height=+7offset=scaleShadowOffset(offset)# The shadow can be colored. Create a CGColorRef# that represents a red color with opacity of 0.3333...shadowColor=Quartz.CGColorCreateCopyWithAlpha(Utilities.getRGBOpaqueRedColor(),1.0/3.0)Quartz.CGContextSetShadowWithColor(context,offset,blur,shadowColor)Quartz.CGContextStrokePath(context)# Object 5. Three stroked circles.Quartz.CGContextTranslateCTM(context,-75,-65)# Set a black shadow offset at -7,-7.offset.width=-7offset.height=-7offset=scaleShadowOffset(offset)Quartz.CGContextSetShadow(context,offset,blur)# Draw a set of three circles side by side.Quartz.CGContextBeginPath(context)Quartz.CGContextSetLineWidth(context,3)r=Quartz.CGRectMake(30,20,20,20)Utilities.myCGContextAddEllipseInRect(context,r)r=Quartz.CGRectOffset(r,20,0)Utilities.myCGContextAddEllipseInRect(context,r)r=Quartz.CGRectOffset(r,20,0)Utilities.myCGContextAddEllipseInRect(context,r)Quartz.CGContextStrokePath(context)defdoShadowScaling(context):offset=Quartz.CGSize(-7,-7)blur=3Quartz.CGContextTranslateCTM(context,20,220)Quartz.CGContextSetShadow(context,scaleShadowOffset(offset),blur)# Object 1# Draw a triangle filled with black and shadowed with black.createTrianglePath(context)Quartz.CGContextFillPath(context)# Object 2# Scaling without changing the shadow doesn't impact# the shadow offset or blur.t=Quartz.CGAffineTransformMakeScale(2,2)Quartz.CGContextConcatCTM(context,t)Quartz.CGContextTranslateCTM(context,40,0)createTrianglePath(context)Quartz.CGContextFillPath(context)# Object 3# By transforming the offset you can transform the shadow.# This may be desirable if you are drawing a zoomed view.offset=Quartz.CGSizeApplyAffineTransform(offset,t)Quartz.CGContextSetShadow(context,scaleShadowOffset(offset),blur)Quartz.CGContextTranslateCTM(context,70,0)createTrianglePath(context)Quartz.CGContextFillPath(context)defdrawFillAndStrokeWithShadow(context):r=Quartz.CGRectMake(60,60,100,100)offset=Quartz.CGSize(-7,-7)blur=3# Set the shadow.Quartz.CGContextSetShadow(context,scaleShadowOffset(offset),blur)Quartz.CGContextSetFillColorWithColor(context,Utilities.getRGBOpaqueOrangeColor())# Draw the graphic on the left.Quartz.CGContextBeginPath(context)Utilities.myCGContextAddEllipseInRect(context,r)Quartz.CGContextDrawPath(context,Quartz.kCGPathFillStroke)# Draw the graphic on the right.r=Quartz.CGRectOffset(r,125,0)# Begin the transparency layer.Quartz.CGContextBeginTransparencyLayer(context,None)if1:Utilities.myCGContextAddEllipseInRect(context,r)Quartz.CGContextDrawPath(context,Quartz.kCGPathFillStroke)# End the transparency layer.Quartz.CGContextEndTransparencyLayer(context)defdrawColoredLogo(context):r=Quartz.CGRectMake(0,0,100,100)Quartz.CGContextSaveGState(context)if1:# Position the center of the rectangle on the left.Quartz.CGContextTranslateCTM(context,140,140)# Rotate so that the rectangles are rotated 45 degrees# about the current coordinate origin.Quartz.CGContextRotateCTM(context,Utilities.DEGREES_TO_RADIANS(45))# Translate so that the center of the rect is at the previous origin.Quartz.CGContextTranslateCTM(context,-r.size.width/2,-r.size.height/2)# Set the fill color to a purple color.Quartz.CGContextSetFillColorWithColor(context,Utilities.getRGBOpaquePurpleColor())# Fill the first rectangle.Quartz.CGContextFillRect(context,r)# Position to draw the right-most rectangle.Quartz.CGContextTranslateCTM(context,60,-60)# Set the fill color to a yellow color.Quartz.CGContextSetFillColorWithColor(context,Utilities.getRGBOpaqueYellowColor())Quartz.CGContextFillRect(context,r)# Position for the center rectangle.Quartz.CGContextTranslateCTM(context,-30,+30)# Set the stroke color to an orange color.Quartz.CGContextSetStrokeColorWithColor(context,Utilities.getRGBOpaqueOrangeColor())# Stroke the rectangle with a linewidth of 12.Quartz.CGContextStrokeRectWithWidth(context,r,12)Quartz.CGContextRestoreGState(context)defshowComplexShadowIssues(context):offset=Quartz.CGSize(-6,-6)blur=3# Set the shadow.Quartz.CGContextSetShadow(context,scaleShadowOffset(offset),blur)# Draw the colored logo.drawColoredLogo(context)defshowComplexShadow(context):offset=Quartz.CGSize(-6,-6)blur=3# Set the shadow.Quartz.CGContextSetShadow(context,scaleShadowOffset(offset),blur)# Begin a transparency layer. A snapshot is made of the graphics state and# the shadow parameter is temporarily reset to no shadow, the blend mode# is set to Normal, and the global alpha parameter is set to 1.0.## All drawing that occurs after CGContextBeginTransparencyLayer but before# CGContextEndTransparencyLayer is collected together and when# CGContextEndTransparencyLayer is called, Quartz composites the collected# drawing to the context, using the global alpha, blend mode, and shadow# that was in effect when CGContextBeginTransparencyLayer was called.Quartz.CGContextBeginTransparencyLayer(context,None)# Draw the colored logo.drawColoredLogo(context)# Ending the transparency layer causes all drawing in the transparency# layer to be composited with the global alpha, blend mode, and shadow# in effect at the time CGContextBeginTransparencyLayer was called. The# graphics state is restored to that in effect when# CGContextBeginTransparencyLayer was called.# This restores the graphics state to that in effect# at the last call to CGContextBeginTransparencyLayer.Quartz.CGContextEndTransparencyLayer(context)defdoLayerCompositing(context):r=Quartz.CGRectMake(40,50,142,180)# Object 1.Quartz.CGContextTranslateCTM(context,20,20)Quartz.CGContextSetFillColorWithColor(context,Utilities.getRGBOpaqueGreenColor())# Draw a green background.Quartz.CGContextFillRect(context,r)# Draw the colored logo.drawColoredLogo(context)# Object 2.Quartz.CGContextTranslateCTM(context,300,0)Quartz.CGContextSetFillColorWithColor(context,Utilities.getRGBOpaqueGreenColor())# Draw a green background.Quartz.CGContextFillRect(context,r)# Draw the rectangles with opacity 0.75.Quartz.CGContextSetAlpha(context,0.75)drawColoredLogo(context)# Object 3.Quartz.CGContextTranslateCTM(context,300,0)# Set the alpha to 1.0 for drawing the background.Quartz.CGContextSetAlpha(context,1.0)Quartz.CGContextSetFillColorWithColor(context,Utilities.getRGBOpaqueGreenColor())Quartz.CGContextFillRect(context,r)# Draw the rectangles with opacity 0.75.Quartz.CGContextSetAlpha(context,0.75)# Begin a transparency layer. Drawing collected in# this transparency layer will be composited with an# alpha value of 0.75 when the transparency layer is ended.Quartz.CGContextBeginTransparencyLayer(context,None)if1:# Draw the colored logo into the transparency layer.drawColoredLogo(context)# Ending the transparency layer causes the drawing# to then be composited with the global alpha value# in effect when CGContextBeginTransparencyLayer was called.Quartz.CGContextEndTransparencyLayer(context)defshadowPDFDocument(context,url):pdfDoc=Quartz.CGPDFDocumentCreateWithURL(url)offset=Quartz.CGSize(-7,-7)ifpdfDocisNone:print("Couldn't create PDF document reference!")returnr=Quartz.CGPDFDocumentGetMediaBox(pdfDoc,1)r.origin.x=20r.origin.y=20# Set the shadow.Quartz.CGContextSetShadow(context,scaleShadowOffset(offset),3)# On Tiger and later, there is no need to use# a transparency layer to draw a PDF document as# a grouped object. On Panther, you can do so# by using a transparency layer. Drawing collected in# this transparency layer is drawn with the shadow# when the layer is ended.Quartz.CGContextBeginTransparencyLayer(context,None)if1:Quartz.CGContextDrawPDFDocument(context,r,pdfDoc,1)Quartz.CGContextEndTransparencyLayer(context)

Utilities.py

importmathimportQuartzimportCocoaclassExportInfo(object):command=NonefileType=NoneuseQTForExport=Falsedpi=1defDEGREES_TO_RADIANS(degrees):returndegrees*math.pi/180gScalingFactor=1.0# These routines are used for getting the correct results when# drawing shadows and patterns with Quartz and exporting the# results as bits. This is a hack; in principle the scaling# factor should be passed to the draw proc.defsetScalingFactor(scalingFactor):ifscalingFactor>0:gScalingFactor=scalingFactordefgetScalingFactor():returngScalingFactor_appBundle=NonedefgetAppBundle():global_appBundleif_appBundleisNone:_appBundle=Cocoa.CFBundleGetMainBundle()return_appBundle## This version of getTheRGBColorSpace returns# the DeviceRGB color space.#_deviceRGB=NonedefgetTheRGBColorSpace():global_deviceRGB# Set once, the first time this function is called.if_deviceRGBisNone:_deviceRGB=Quartz.CGColorSpaceCreateDeviceRGB()return_deviceRGB_genericRGBColorSpace=NonedefgetTheCalibratedRGBColorSpace():global_genericRGBColorSpaceif_genericRGBColorSpaceisNone:_genericRGBColorSpace=Quartz.CGColorSpaceCreateWithName(Quartz.kCGColorSpaceGenericRGB)return_genericRGBColorSpace_genericGrayColorSpace=NonedefgetTheCalibratedGrayColorSpace():global_genericGrayColorSpaceif_genericGrayColorSpaceisNone:_genericGrayColorSpace=Quartz.CGColorSpaceCreateWithName(Quartz.kCGColorSpaceGenericGray)return_genericGrayColorSpace_genericSRGBColorSpace=NonedefgetTheSRGBColorSpace():# This only works on 10.5 or laterglobal_genericSRGBColorSpaceif_genericSRGBColorSpaceisNone:_genericSRGBColorSpace=Quartz.CGColorSpaceCreateWithName(Quartz.kCGColorSpaceGenericRGB)# XXX: should be GenericSRGBreturn_genericSRGBColorSpacedefgetTheDisplayColorSpace():# This is a hack, basicly here because the C implementation uses APIs that# aren't wrapped yet.returngetTheRGBColorSpace()_rgbWhite=NonedefgetRGBOpaqueWhiteColor():global_rgbWhiteif_rgbWhiteisNone:opaqueWhite=(1.0,1.0,1.0,1.0)_rgbWhite=Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(),opaqueWhite)return_rgbWhite_rgbBlack=NonedefgetRGBOpaqueBlackColor():global_rgbBlackif_rgbBlackisNone:opaqueBlack=(0,0,0,1)_rgbBlack=Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(),opaqueBlack)return_rgbBlack_rgbGray=NonedefgetRGBOpaqueGrayColor():global_rgbGrayif_rgbGrayisNone:opaqueGray=(0.9,0.9,0.9,1)_rgbGray=Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(),opaqueGray)return_rgbGray_rgbRed=NonedefgetRGBOpaqueRedColor():global_rgbRedif_rgbRedisNone:opaqueRed=(0.663,0,0.031,1)_rgbRed=Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(),opaqueRed)return_rgbRed_rgbBlue=NonedefgetRGBOpaqueBlueColor():global_rgbBlueif_rgbBlueisNone:opaqueBlue=(0.482,0.62,0.871,1)_rgbBlue=Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(),opaqueBlue)return_rgbBlue_rgbPurple=NonedefgetRGBOpaquePurpleColor():global_rgbPurpleif_rgbPurpleisNone:opaquePurple=(0.69,0.486,0.722,1)_rgbPurple=Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(),opaquePurple)return_rgbPurple_rgbDarkBlue=NonedefgetRGBOpaqueDarkBlueColor():global_rgbDarkBlueif_rgbDarkBlueisNone:opaqueDarkBlue=(0.11,0.208,0.451,1)_rgbDarkBlue=Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(),opaqueDarkBlue)return_rgbDarkBlue_rgbBrown=NonedefgetRGBOpaqueBrownColor():global_rgbBrownif_rgbBrownisNone:opaqueBrown=(0.325,0.208,0.157,1)_rgbBrown=Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(),opaqueBrown)return_rgbBrown_rgbOrange=NonedefgetRGBOpaqueOrangeColor():global_rgbOrangeif_rgbOrangeisNone:opaqueOrange=(0.965,0.584,0.059,1)_rgbOrange=Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(),opaqueOrange)return_rgbOrange_rgbYellow=NonedefgetRGBOpaqueYellowColor():global_rgbYellowif_rgbYellowisNone:opaqueYellow=(1,0.816,0,1)_rgbYellow=Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(),opaqueYellow)return_rgbYellow_rgbGreen=NonedefgetRGBOpaqueGreenColor():global_rgbGreenif_rgbGreenisNone:opaqueGreen=(0.584,0.871,0.318,1)_rgbGreen=Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(),opaqueGreen)return_rgbGreen_rgbDarkGreen=NonedefgetRGBOpaqueDarkGreenColor():global_rgbDarkGreenif_rgbDarkGreenisNone:opaqueDarkGreen=(0.404,0.808,0.239,1)_rgbDarkGreen=Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(),opaqueDarkGreen)return_rgbDarkGreendefmyCGContextAddEllipseInRect(context,r):ifhasattr(Quartz,'CGContextAddEllipseInRect'):Quartz.CGContextAddEllipseInRect(context,r)else:# This is not a perfect emulation but is correct as long as there is# not an open subpath already in the current path. In that case the# CGContextClosePath here would not necessarily produce the desired# result.Quartz.CGContextSaveGState(context)if1:# Translate to the center of the ellipse.Quartz.CGContextTranslateCTM(context,Quartz.CGRectGetMidX(r),Quartz.CGRectGetMidY(r))# Scale by half the width and height of the rectangle# bounding the ellipse.Quartz.CGContextScaleCTM(context,r.size.width/2,r.size.height/2)# Establish a current point at the first point# on the ellipse. This ensures that there# is no line segment connecting the previous# current point to the first point on the subpath.Quartz.CGContextMoveToPoint(context,1,0)# Circular arc around the ellipse center with# a radius that, when scaled by the CTM, produces# the major and minor axes of the ellipse. Since# CGContextAddEllipseInRect defines the direction# of the path as clockwise, this routine will# draw the arc clockwise also.Quartz.CGContextAddArc(context,0,0,1,0,2*math.pi,1)Quartz.CGContextClosePath(context)Quartz.CGContextRestoreGState(context)# Routines that are useful for debugging.defdrawPoint(context,p):Quartz.CGContextSaveGState(context)if1:# Opaque black.Quartz.CGContextSetRGBStrokeColor(context,0,0,0,1)Quartz.CGContextSetLineWidth(context,5)Quartz.CGContextSetLineCap(context,Quartz.kCGLineCapRound)Quartz.CGContextMoveToPoint(context,p.x,p.y)Quartz.CGContextAddLineToPoint(context,p.x,p.y)Quartz.CGContextStrokePath(context)Quartz.CGContextRestoreGState(context)defprintCTM(context):t=Quartz.CGContextGetCTM(context)print("CurrentCTM is %r"%(t,))kTickLength=5kTickDistance=72kAxesLength=(20*kTickDistance)defdrawCoordinateAxes(context):tickLength=kTickLengthQuartz.CGContextSaveGState(context)if1:Quartz.CGContextBeginPath(context)# Paint the x-axis in red.Quartz.CGContextSetRGBStrokeColor(context,1,0,0,1)Quartz.CGContextMoveToPoint(context,-kTickLength,0.)Quartz.CGContextAddLineToPoint(context,kAxesLength,0.)Quartz.CGContextDrawPath(context,Quartz.kCGPathStroke)# Paint the y-axis in blue.Quartz.CGContextSetRGBStrokeColor(context,0,0,1,1)Quartz.CGContextMoveToPoint(context,0,-kTickLength)Quartz.CGContextAddLineToPoint(context,0,kAxesLength)Quartz.CGContextDrawPath(context,Quartz.kCGPathStroke)# Paint the x-axis tick marks in red.Quartz.CGContextSetRGBStrokeColor(context,1,0,0,1)foriinrange(2):fortinrange(0,kAxesLength,kTickDistance):Quartz.CGContextMoveToPoint(context,t,-tickLength)Quartz.CGContextAddLineToPoint(context,t,tickLength)Quartz.CGContextDrawPath(context,Quartz.kCGPathStroke)Quartz.CGContextRotateCTM(context,math.pi/2.)# Paint the y-axis tick marks in blue.Quartz.CGContextSetRGBStrokeColor(context,0,0,1,1)drawPoint(context,Quartz.CGPointZero)Quartz.CGContextRestoreGState(context)defdrawDebuggingRect(context,rect):Quartz.CGContextSaveGState(context)if1:Quartz.CGContextSetLineWidth(context,4.)# Draw opaque red from top-left to bottom-right.Quartz.CGContextSetRGBStrokeColor(context,1,0,0,1.0)Quartz.CGContextMoveToPoint(context,rect.origin.x,rect.origin.y+rect.size.height)Quartz.CGContextAddLineToPoint(context,rect.origin.x+rect.size.width,rect.origin.y)Quartz.CGContextStrokePath(context)# Draw opaque blue from top-right to bottom-left.Quartz.CGContextSetRGBStrokeColor(context,0,0,1,1.0)Quartz.CGContextMoveToPoint(context,rect.origin.x+rect.size.width,rect.origin.y+rect.size.height)Quartz.CGContextAddLineToPoint(context,rect.origin.x,rect.origin.y)Quartz.CGContextStrokePath(context)# Opaque black.Quartz.CGContextSetRGBStrokeColor(context,0,0,0,1.)Quartz.CGContextStrokeRect(context,rect)Quartz.CGContextRestoreGState(context)

main.py

fromPyObjCToolsimportAppHelper# Make sure all code is loadedimportAppDrawingimportBitmapContextimportColorAndGStateimportCoordinateSystemimportDataProvidersAndConsumersimportDrawingBasicsimportEPSPrintingimportFrameworkTextDrawingimportFrameworkUtilitiesimportImageMaskingimportImagesimportMyAppControllerimportMyViewimportPDFHandlingimportPathDrawingimportPatternDrawingimportQuartzTextDrawingimportShadingsimportShadowsAndTransparencyLayersimportUtilitiesimportobjc;objc.setVerbose(True)AppHelper.runEventLoop()

setup.py

"""Script for building the example.Usage: python3 setup.py py2app"""fromsetuptoolsimportsetupimportossetup(name="BasicDrawing",app=["main.py"],data_files=["English.lproj"]+[os.path.join("GraphicsFiles",fn)forfninos.listdir("GraphicsFiles")],setup_requires=["py2app","pyobjc-framework-Cocoa","pyobjc-framework-Quartz",])