BasicDrawing

A PyObjC Example without documentation

Sources

AppDrawing.py

import BitmapContext
import Cocoa
import ColorAndGState
import CoordinateSystem
import DrawingBasics
import EPSPrinting
import ImageMasking
import Images
import PathDrawing
import PatternDrawing
import QuartzTextDrawing
import Shadings
import ShadowsAndTransparencyLayers
import UIHandling
import Utilities

# Defines
kCatPDF = "Kitty.pdf"
kPDFForBlendMode = "blendmode.pdf"

kOurJPEG = "Poot.jpg"
kQTImage = "ptlobos.tif"
kOurSubstituteJPG = "LyingOnDeckNoProfile.JPG"
kOurEPS = "imageturkey.eps"

RAW_IMAGE_WIDTH = 400
RAW_IMAGE_HEIGHT = 300
kRawColorImage = "image-400x300x24.raw"
kOtherColorImage = "otherimage-400x300x24.raw"

MASKING_IMAGE_WIDTH = 400
MASKING_IMAGE_HEIGHT = 259
kMaskingImage = "400x259x8.bw.raw"

noOffScreen = 0
bitmapOffScreen = 1
layerOffScreen = 2

# Cache info for GetURL
_mainBundle = None
_urlMap = {}


def GetURL(name):
    """
    Returns the CFURLRef for an embedded resource, or None of that cannot be found.
    """

    global _mainBundle
    if _mainBundle is None:
        _mainBundle = Utilities.getAppBundle()

    mainBundle = _mainBundle
    if mainBundle is not None:
        if name in _urlMap:
            return _urlMap[name]

        url = Cocoa.CFBundleCopyResourceURL(mainBundle, name, None, None)
        _urlMap[name] = url
    else:
        print("Can't get the app bundle!")
        return

    if url is None:
        print(f"Couldn't get URL for {name!r}")

    return url


#
# Helper functions for drawing
#


def callPDFDrawProc(context, proc, pdfFile):
    ourPDFurl = GetURL(pdfFile)

    if ourPDFurl:
        proc(context, ourPDFurl)


def doDrawJPEGFile(context):
    ourJPEGurl = GetURL(kOurJPEG)

    if ourJPEGurl is not None:
        Images.drawJPEGImage(context, ourJPEGurl)


def doRawImageFileWithURL(context):
    url = GetURL(kRawColorImage)

    if url is not None:
        Images.drawImageFromURL(
            context, url, RAW_IMAGE_WIDTH, RAW_IMAGE_HEIGHT, 8, True
        )
        # 8 bits per component, isColor = True


def doRawImageFileWithCallbacks(context):
    url = GetURL(kRawColorImage)

    if url is not None:
        Images.doImageWithCallbacksCreatedFromURL(
            context, url, RAW_IMAGE_WIDTH, RAW_IMAGE_HEIGHT, 8, True
        )
        # 8 bits per component, isColor = True


def doDrawImageWithCGImageSource(context):
    url = GetURL(kOurJPEG)
    if url is not None:
        Images.drawImageWithCGImageDataSource(context, url)


def doIncrementalImage(context):
    url = GetURL(kOurJPEG)

    if url is not None:
        Images.doIncrementalImageWithURL(context, url)


def doQTImage(context):
    url = GetURL(kQTImage)

    if url is not None:
        Images.drawQTImageWithQuartz(context, url)


def doJPEGDocumentWithMultipleProfiles(context):
    url = GetURL(kOurSubstituteJPG)

    if url is not None:
        Images.drawJPEGDocumentWithMultipleProfiles(context, url)


def doMaskImageWithMask(context):
    theImageToMaskURL = GetURL(kOtherColorImage)
    theMaskingImageURL = GetURL(kMaskingImage)

    if theImageToMaskURL is not None and theMaskingImageURL is not None:
        ImageMasking.doMaskImageWithMaskFromURL(
            context,
            theImageToMaskURL,
            RAW_IMAGE_WIDTH,
            RAW_IMAGE_HEIGHT,
            8,
            theMaskingImageURL,
            MASKING_IMAGE_WIDTH,
            MASKING_IMAGE_HEIGHT,
        )


def doMaskImageWithGrayImage(context):
    theImageToMaskURL = GetURL(kOtherColorImage)
    theMaskingImageURL = GetURL(kMaskingImage)

    if theImageToMaskURL is not None and theMaskingImageURL is not None:
        ImageMasking.doMaskImageWithGrayImageFromURL(
            context,
            theImageToMaskURL,
            RAW_IMAGE_WIDTH,
            RAW_IMAGE_HEIGHT,
            8,
            theMaskingImageURL,
            MASKING_IMAGE_WIDTH,
            MASKING_IMAGE_HEIGHT,
        )


def doImageMaskedWithColor(context):
    url = GetURL(kOtherColorImage)

    if url is not None:
        ImageMasking.doMaskImageWithColorFromURL(
            context, url, RAW_IMAGE_WIDTH, RAW_IMAGE_HEIGHT, True
        )


def exportImageMaskedWithImage(context):
    theImageToMaskURL = GetURL(kOtherColorImage)
    theMaskingImageURL = GetURL(kMaskingImage)

    if theImageToMaskURL is not None and theMaskingImageURL is not None:
        ImageMasking.exportImageWithMaskFromURLWithDestination(
            context,
            theImageToMaskURL,
            RAW_IMAGE_WIDTH,
            RAW_IMAGE_HEIGHT,
            8,
            theMaskingImageURL,
            MASKING_IMAGE_WIDTH,
            MASKING_IMAGE_HEIGHT,
        )


def doClipMask(context):
    theMaskingImageURL = GetURL(kMaskingImage)

    if theMaskingImageURL is not None:
        ImageMasking.drawWithClippingMask(
            context, theMaskingImageURL, MASKING_IMAGE_WIDTH, MASKING_IMAGE_HEIGHT
        )


def tilePDFDocument(context, offscreenType):
    url = GetURL(kCatPDF)

    if url is not None:
        if offscreenType == noOffScreen:
            BitmapContext.TilePDFNoBuffer(context, url)
        elif offscreenType == bitmapOffScreen:
            BitmapContext.TilePDFWithOffscreenBitmap(context, url)
        else:
            BitmapContext.TilePDFWithCGLayer(context, url)


def doCompatibleEPSDrawing(context):
    ourEPSurl = GetURL(kOurEPS)

    if ourEPSurl is not None:
        EPSPrinting.drawEPSDataImage(context, ourEPSurl)


def DispatchDrawing(context, drawingType):
    """Drawing dispatcher"""
    if drawingType == UIHandling.kHICommandSimpleRect:
        DrawingBasics.doSimpleRect(context)

    elif drawingType == UIHandling.kHICommandStrokedRect:
        DrawingBasics.doStrokedRect(context)

    elif drawingType == UIHandling.kHICommandStrokedAndFilledRect:
        DrawingBasics.doStrokedAndFilledRect(context)

    elif drawingType == UIHandling.kHICommandPathRects:
        DrawingBasics.doPathRects(context)

    elif drawingType == UIHandling.kHICommandAlphaRects:
        DrawingBasics.doAlphaRects(context)

    elif drawingType == UIHandling.kHICommandDashed:
        DrawingBasics.doDashedLines(context)

    elif drawingType == UIHandling.kHICommandSimpleClip:
        DrawingBasics.doClippedCircle(context)

    elif drawingType == UIHandling.kHICommandPDFDoc:
        callPDFDrawProc(context, DrawingBasics.doPDFDocument, kCatPDF)

    elif drawingType == UIHandling.kHICommandRotatedEllipses:
        CoordinateSystem.doRotatedEllipses(context)

    elif drawingType == UIHandling.kHICommandDrawSkewCoordinates:
        CoordinateSystem.drawSkewedCoordinateSystem(context)

    elif drawingType == UIHandling.kHICommandBezierEgg:
        PathDrawing.doEgg(context)

    elif drawingType == UIHandling.kHICommandRoundedRects:
        PathDrawing.doRoundedRects(context)

    elif drawingType == UIHandling.kHICommandStrokeWithCTM:
        PathDrawing.doStrokeWithCTM(context)

    elif drawingType == UIHandling.kHICommandRotatedEllipsesWithCGPath:
        PathDrawing.doRotatedEllipsesWithCGPath(context)

    elif drawingType == UIHandling.kHICommandPixelAligned:
        PathDrawing.doPixelAlignedFillAndStroke(context)

    elif drawingType == UIHandling.kHICommandDeviceFillAndStrokeColor:
        ColorAndGState.doColorSpaceFillAndStroke(context)

    elif drawingType == UIHandling.kHICommandCLUTDrawGraphics:
        ColorAndGState.doIndexedColorDrawGraphics(context)

    elif drawingType == UIHandling.kHICommandDrawWithGlobalAlpha:
        ColorAndGState.drawWithGlobalAlpha(context)

    elif drawingType == UIHandling.kHICommandDrawWithBlendMode:
        callPDFDrawProc(
            context, ColorAndGState.drawWithColorBlendMode, kPDFForBlendMode
        )

    elif drawingType == UIHandling.kHICommandDrawWithColorRefs:
        ColorAndGState.drawWithColorRefs(context)

    elif drawingType == UIHandling.kHICommandFunctionsHaveOwnGSave:
        ColorAndGState.doClippedEllipse(context)

    elif drawingType == UIHandling.kHICommandDrawJPEGImage:
        doDrawJPEGFile(context)

    elif drawingType == UIHandling.kHICommandColorImageFromFile:
        doRawImageFileWithURL(context)

    elif drawingType == UIHandling.kHICommandColorImageFromData:
        Images.doColorRampImage(context)

    elif drawingType == UIHandling.kHICommandColorImageFromCallbacks:
        doRawImageFileWithCallbacks(context)

    elif drawingType == UIHandling.kHICommandGrayRamp:
        Images.doGrayRamp(context)

    elif drawingType == UIHandling.kHICommandDrawWithCGImageSource:
        doDrawImageWithCGImageSource(context)

    elif drawingType == UIHandling.kHICommandDrawWithCGImageSourceIncremental:
        doIncrementalImage(context)

    elif drawingType == UIHandling.kHICommandDrawWithQuickTime:
        doQTImage(context)

    elif drawingType == UIHandling.kHICommandSubstituteImageProfile:
        doJPEGDocumentWithMultipleProfiles(context)

    elif drawingType == UIHandling.kHICommandDoSubImage:
        Images.doColorRampSubImage(context)

    elif drawingType == UIHandling.kHICommandExportWithQuickTime:
        Images.exportColorRampImageWithQT(context)

    elif drawingType == UIHandling.kHICommandMaskTurkeyImage:
        ImageMasking.doOneBitMaskImages(context)

    elif drawingType == UIHandling.kHICommandImageMaskedWithMask:
        doMaskImageWithMask(context)

    elif drawingType == UIHandling.kHICommandImageMaskedWithGrayImage:
        doMaskImageWithGrayImage(context)

    elif drawingType == UIHandling.kHICommandMaskImageWithColor:
        doImageMaskedWithColor(context)

    elif drawingType == UIHandling.kHICommandClipToMask:
        doClipMask(context)

    elif drawingType == UIHandling.kHICommandExportWithCGImageDestination:
        exportImageMaskedWithImage(context)

    elif drawingType == UIHandling.kHICommandSimpleCGLayer:
        BitmapContext.doSimpleCGLayer(context)

    elif drawingType == UIHandling.kHICommandAlphaOnlyContext:
        BitmapContext.doAlphaOnlyContext(context)

    elif drawingType == UIHandling.kHICommandDrawNoOffScreenImage:
        tilePDFDocument(context, noOffScreen)

    elif drawingType == UIHandling.kHICommandDrawOffScreenImage:
        tilePDFDocument(context, bitmapOffScreen)

    elif drawingType == UIHandling.kHICommandDrawWithLayer:
        tilePDFDocument(context, layerOffScreen)

    elif drawingType == UIHandling.kHICommandQuartzRomanText:
        QuartzTextDrawing.drawQuartzRomanText(context)

    elif drawingType == UIHandling.kHICommandQuartzTextModes:
        QuartzTextDrawing.drawQuartzTextWithTextModes(context)

    elif drawingType == UIHandling.kHICommandQuartzTextMatrix:
        QuartzTextDrawing.drawQuartzTextWithTextMatrix(context)

    elif drawingType == UIHandling.kHICommandSimplePattern:
        PatternDrawing.doRedBlackCheckerboard(context)

    elif drawingType == UIHandling.kHICommandPatternPhase:
        PatternDrawing.doPatternPhase(context)

    elif drawingType == UIHandling.kHICommandPatternMatrix:
        PatternDrawing.doPatternMatrix(context)

    elif drawingType == UIHandling.kHICommandUncoloredPattern:
        PatternDrawing.doStencilPattern(context)

    elif drawingType == UIHandling.kHICommandDrawWithPDFPattern:
        callPDFDrawProc(context, PatternDrawing.drawWithPDFPattern, kCatPDF)

    elif drawingType == UIHandling.kHICommandSimpleShadow:
        ShadowsAndTransparencyLayers.drawSimpleShadow(context)

    elif drawingType == UIHandling.kHICommandShadowScaling:
        ShadowsAndTransparencyLayers.doShadowScaling(context)

    elif drawingType == UIHandling.kHICommandShadowProblems:
        ShadowsAndTransparencyLayers.showComplexShadowIssues(context)

    elif drawingType == UIHandling.kHICommandComplexShadow:
        ShadowsAndTransparencyLayers.showComplexShadow(context)

    elif drawingType == UIHandling.kHICommandMultipleShapeComposite:
        ShadowsAndTransparencyLayers.doLayerCompositing(context)

    elif drawingType == UIHandling.kHICommandFillAndStrokeWithShadow:
        ShadowsAndTransparencyLayers.drawFillAndStrokeWithShadow(context)

    elif drawingType == UIHandling.kHICommandPDFDocumentShadow:
        callPDFDrawProc(
            context, ShadowsAndTransparencyLayers.shadowPDFDocument, kCatPDF
        )

    elif drawingType == UIHandling.kHICommandSimpleAxialShading:
        Shadings.doSimpleAxialShading(context)

    elif drawingType == UIHandling.kHICommandExampleAxialShadings:
        Shadings.doExampleAxialShading(context)

    elif drawingType == UIHandling.kHICommandSimpleRadialShading:
        Shadings.doSimpleRadialShading(context)

    elif drawingType == UIHandling.kHICommandExampleRadialShadings:
        Shadings.doExampleRadialShadings(context)

    elif drawingType == UIHandling.kHICommandEllipseShading:
        Shadings.doEllipseShading(context)

    elif drawingType == UIHandling.kHICommandDoCompatibleEPS:
        doCompatibleEPSDrawing(context)

BitmapContext.py

import AppDrawing
import DrawingBasics
import LaunchServices
import Quartz
import Utilities

# import Carbon.QT


BEST_BYTE_ALIGNMENT = 16


def COMPUTE_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 = {}


def createRGBBitmapContext(
    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 = bytearray(int(bytesPerRow * height))
    except MemoryError:
        return None

    # 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.
    if wantDisplayColorSpace:
        cs = Utilities.getTheDisplayColorSpace()
    else:
        cs = Utilities.getTheCalibratedRGBColorSpace()

    if needsTransparentBitmap:
        transparency = Quartz.kCGImageAlphaPremultipliedFirst
    else:
        transparency = Quartz.kCGImageAlphaPremultipliedFirst

    context = Quartz.CGBitmapContextCreate(
        rasterData, width, height, 8, bytesPerRow, cs, transparency
    )
    if context is None:
        return None

    _rasterDataForContext[context] = rasterData

    # Either clear the rect or paint with opaque white, depending on
    # the needs of the caller.
    if needsTransparentBitmap:
        # 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)

    return context


def myCGContextGetBitmapInfo(c):
    if hasattr(Quartz, "CGBitmapContextGetBitmapInfo"):
        return Quartz.CGBitmapContextGetBitmapInfo(c)
    else:
        return Quartz.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.
def createImageFromBitmapContext(c):
    rasterData = _rasterDataForContext[c]
    # We own the data, hence remove from the mapping
    del _rasterDataForContext[c]

    imageDataSize = Quartz.CGBitmapContextGetBytesPerRow(
        c
    ) * Quartz.CGBitmapContextGetHeight(c)

    if rasterData is None:
        print("Context is not a bitmap context!")

    # Create the data provider from the image data
    dataProvider = Quartz.CGDataProviderCreateWithData(
        None, rasterData, imageDataSize, None
    )
    if dataProvider is None:
        print("Couldn't create data provider!")
        return None

    # 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,
    )

    if image is None:
        print("Couldn't create image!")
        return None
    return image


def exportCGImageToFileWithQT(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.
    """
    # XXX: Finish
    return

    if outputFormat.lower() == LaunchServices.kUTTypeTIFF.lower():
        imageExportType = LaunchServices.kQTFileTypeTIFF

    elif outputFormat.lower() == LaunchServices.kUTTypePNG.lower():
        imageExportType = LaunchServices.kQTFileTypePNG

    elif outputFormat.lower() == LaunchServices.kUTTypeJPEG.lower():
        imageExportType = LaunchServices.kQTFileTypeJPEG

    else:
        print(f"Requested image export format {outputFormat!r} unsupported")
        return

        result, dataRef, dataRefType = Quartz.QTNewDataReferenceFromCFURL(
            url, 0, None, None
        )
        if result == 0:
            result, graphicsExporter = Quartz.OpenADefaultComponent(
                Quartz.GraphicsExporterComponentType,
                imageExportType,
                graphicsExporter,  # noqa: F821
            )
            if result == 0:
                result = Quartz.GraphicsExportSetInputCGImage(graphicsExporter, image)
                if result == 0:
                    result = Quartz.GraphicsExportSetResolution(
                        graphicsExporter,
                        Quartz.FloatToFixed(dpi),
                        Quartz.FloatToFixed(dpi),
                    )
                if result == 0:
                    result = Quartz.GraphicsExportSetOutputDataReference(
                        graphicsExporter, dataRef, dataRefType
                    )
                if result == 0:
                    result, sizeWritten = Quartz.GraphicsExportDoExport(
                        graphicsExporter, None
                    )
                Quartz.CloseComponent(graphicsExporter)

        if dataRef:
            Quartz.DisposeHandle(dataRef)

        if result:
            print("QT export got bad result = %d!" % (result,))


def exportCGImageToFileWithDestination(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
    )

    if imageDestination is None:
        print("Couldn't create image destination!")
        return

    # Create an options dictionary with the X&Y resolution of the image
    options = {
        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)


def MakeImageDocument(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
    )

    if c is None:
        print("Couldn't make destination bitmap context")
        return -1

    # 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.
    if hasattr(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.
    del c

    if image is None:
        # Users of this code should update this to be an error code they find useful.
        return -1

    # Now export the image.
    if exportInfo.useQTForExport:
        exportCGImageToFileWithQT(image, url, imageType, exportInfo.dpi)
    else:
        exportCGImageToFileWithDestination(image, url, imageType, exportInfo.dpi)


def MakeTIFFDocument(url, exportInfo):
    return MakeImageDocument(url, LaunchServices.kUTTypeTIFF, exportInfo)


def MakePNGDocument(url, exportInfo):
    return MakeImageDocument(url, LaunchServices.kUTTypePNG, exportInfo)


def MakeJPEGDocument(url, exportInfo):
    return MakeImageDocument(url, LaunchServices.kUTTypeJPEG, exportInfo)


def createCGLayerForDrawing(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)
    if layer is None:
        return None

    # Get the context corresponding to the layer.
    layerContext = Quartz.CGLayerGetContext(layer)
    if layerContext is None:
        return None

    # $ 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.
    return layer


def doSimpleCGLayer(context):
    # Create the layer.
    layer = createCGLayerForDrawing(context)

    if layer is None:
        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.
    for j in range(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.
        for _ in range(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.
        if j % 2:
            Quartz.CGContextTranslateCTM(context, s.width, s.height)
        else:
            Quartz.CGContextTranslateCTM(context, -s.width, s.height)


def createAlphaOnlyContext(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 = bytearray(bytesPerRow * height)
    except MemoryError:
        return None

    # 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
    )
    if context is None:
        print("Couldn't create the context!")
        return None

    _rasterDataForContext[context] = rasterData

    # Clear the context bits so they are initially transparent.
    Quartz.CGContextClearRect(context, Quartz.CGRectMake(0, 0, width, height))

    return context


# 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.


def createMaskFromAlphaOnlyContext(alphaContext):
    rasterData = _rasterDataForContext[alphaContext]
    # We own the data, hence remove from the mapping
    del _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
    )

    if dataProvider is None:
        print("Couldn't create data provider!")
        return None

    mask = 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,
    )

    if mask is None:
        print("Couldn't create image mask!")
        return None

    return mask


def doAlphaOnlyContext(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 = 520
    height = 400
    alphaContext = createAlphaOnlyContext(width, height)
    if context is None:
        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 finished with the context so it can
    # release it.
    del alphaContext

    if mask is None:
        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 = 0


def getThePDFDoc(url):
    """
    This function caches a CGPDFDocumentRef for
    the most recently requested PDF document.
    """
    global _pdfDoc, _pdfURL, _width, _height

    if url is None:
        return None, 0, 0

    # See whether to update the cached PDF document.
    if _pdfDoc is None or url != _pdfURL:
        # Release any cached document or URL.
        _pdfDoc = Quartz.CGPDFDocumentCreateWithURL(url)
        if _pdfDoc is not None:
            pdfMediaRect = Quartz.CGPDFDocumentGetMediaBox(_pdfDoc, 1)
            _width = pdfMediaRect.size.width
            _height = pdfMediaRect.size.height
            # Keep the URL of the PDF file being cached.
            _pdfURL = url

        else:
            _pdfURL = None

    if _pdfDoc is not None:
        return _pdfDoc, _width, _height

    else:
        return None, 0, 0


# Defining this scales the content down by 1/3.
DOSCALING = True


def TilePDFNoBuffer(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.0
    fillheight = 792.0
    extraOffset = 6.0
    pdfDoc, tileX, tileY = getThePDFDoc(url)
    if pdfDoc is None:
        print("Couldn't get the PDF document!")
        return

    if DOSCALING:
        # Make the tiles 1/3 the size of the PDF document.
        tileX /= 3
        tileY /= 3
        extraOffset /= 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.
    for h in range(0, int(fillheight), int(tileOffsetY)):
        for w in range(0, int(fillwidth), int(tileOffsetX)):
            Quartz.CGContextDrawPDFDocument(
                context, Quartz.CGRectMake(w, h, tileX, tileY), pdfDoc, 1
            )


def TilePDFWithOffscreenBitmap(context, url):
    # Again this should really be computed based on
    # the area intended to be tiled.
    fillwidth = 612.0
    fillheight = 792.0
    extraOffset = 6.0

    pdfDoc, tileX, tileY = getThePDFDoc(url)
    if pdfDoc is None:
        print("Couldn't get the PDF document")
        return

    if DOSCALING:
        # Make the tiles 1/3 the size of the PDF document.
        tileX /= 3
        tileY /= 3
        extraOffset /= 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
    )
    if bitmapContext is None:
        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.
    del bitmapContext

    if image is None:
        return

    # Now tile the image.
    for h in range(0, int(fillheight), int(tileOffsetY)):
        for w in range(0, int(fillwidth), int(tileOffsetX)):
            Quartz.CGContextDrawImage(
                context, Quartz.CGRectMake(w, h, tileX, tileY), image
            )


def createLayerWithImageForContext(c, url):
    layerSize = Quartz.CGSize()
    pdfDoc, layerSize.width, layerSize.height = getThePDFDoc(url)
    if pdfDoc is None:
        return None

    if DOSCALING:
        # Make the layer 1/3 the size of the PDF document.
        layerSize.width /= 3
        layerSize.height /= 3

    # Create the layer to draw into.
    layer = Quartz.CGLayerCreateWithContext(c, layerSize, None)
    if layer is None:
        return None

    # 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)
    if layerContext is None:
        return None

    # 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.
    return layer


def TilePDFWithCGLayer(context, url):
    # Again this should really be computed based on
    # the area intended to be tiled.
    fillwidth = 612.0
    fillheight = 792.0
    layer = createLayerWithImageForContext(context, url)
    if layer is None:
        print("Couldn't create the layer!")
        return

    # Compute the tile size and offset.
    s = Quartz.CGLayerGetSize(layer)
    tileX = s.width
    tileY = s.height

    if DOSCALING:
        # 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.0 + tileX
        tileOffsetY = 6.0 + 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.
    for h in range(0, int(fillheight), int(tileOffsetY)):
        for w in range(0, int(fillwidth), int(tileOffsetX)):
            Quartz.CGContextDrawLayerAtPoint(context, Quartz.CGPointMake(w, h), layer)

ColorAndGState.py

import array

import Quartz
import Utilities


def doColorSpaceFillAndStroke(context):
    theColorSpace = Utilities.getTheCalibratedRGBColorSpace()
    opaqueRed = (0.663, 0.0, 0.031, 1.0)  # red,green,blue,alpha
    aBlue = (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.5
    Quartz.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 = None


def drawWithColorRefs(context):
    global _opaqueRedColor
    global _opaqueBlueColor
    global _transparentBlueColor

    # Initialize the CGColorRefs if necessary
    if _opaqueRedColor is None:
        # 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 (
            _opaqueRedColor is None
            or _opaqueBlueColor is None
            or _transparentBlueColor is None
        ):
            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)


def doIndexedColorDrawGraphics(context):
    theBaseRGBSpace = Utilities.getTheCalibratedRGBColorSpace()
    lookupTable = array.array("B", (0,) * 6)
    opaqueRed = (0, 1)  # index, alpha
    aBlue = (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)
    if theIndexedSpace is not None:
        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.5
        Quartz.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!")


def drawWithGlobalAlpha(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)
    for _ in range(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.0


def drawWithColorBlendMode(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)
    if pdfDoc is None:
        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)


def createEllipsePath(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 = None


def doClippedEllipse(context):
    global _opaqueBrownColor, _opaqueOrangeColor

    theCenterPoint = Quartz.CGPoint(120.0, 120.0)
    theEllipseSize = Quartz.CGSize(100.0, 200.0)
    dash = [2.0]

    # Initialize the CGColorRefs if necessary.
    if _opaqueBrownColor is None:
        # 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 1
    createEllipsePath(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 3
    createEllipsePath(context, theCenterPoint, theEllipseSize)
    Quartz.CGContextSetStrokeColorWithColor(context, _opaqueBrownColor)
    Quartz.CGContextSetLineDash(context, 0, dash, 1)
    # Stroke the path with a dash.
    Quartz.CGContextStrokePath(context)
    # Ellipse 4
    createEllipsePath(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 5
    createEllipsePath(context, Quartz.CGPointZero, theEllipseSize)
    Quartz.CGContextSetFillColorWithColor(context, _opaqueOrangeColor)
    Quartz.CGContextFillPath(context)
    Quartz.CGContextRestoreGState(context)

CoordinateSystem.py

import math

import Quartz
import Utilities


def doRotatedEllipses(context):
    totreps = 144
    tint = 1.0
    tintIncrement = 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)

    for _ in range(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)


def drawSkewedCoordinateSystem(context):
    # alpha is 22.5 degrees and beta is 15 degrees.
    alpha = math.pi / 8
    beta = 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

import os
import sys

import Cocoa
import Quartz


def createDataProviderFromPathName(path):
    # Create a CFURL for the supplied file system path.
    url = Cocoa.CFURLCreateWithFileSystemPath(
        None, path, Cocoa.kCFURLPOSIXPathStyle, False
    )
    if url is None:
        print("Couldn't create url!")
        return None

    # Create a Quartz data provider for the URL.
    dataProvider = Quartz.CGDataProviderCreateWithURL(url)
    if dataProvider is None:
        print("Couldn't create data provider!")
        return None

    return dataProvider


def createRGBRampDataProvider():
    width = 256
    height = 256
    imageDataSize = width * height * 3

    dataP = bytearray(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 = 0
    for g in range(height):
        for r in range(width):
            dataP[idx] = chr(r) if sys.version_info[0] == 2 else r
            dataP[idx + 1] = chr(g) if sys.version_info[0] == 2 else g
            dataP[idx + 2] = "\0" if sys.version_info[0] == 2 else 0
            idx += 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)
    return dataProvider


class MyImageDataInfo:
    fp = None
    totalBytesRead = 0
    skippedBytes = 0
    numRewinds = 0


def getBytesSequentialAccessDP(data, buffer, count):
    buf = data.fp.read(count)
    buffer[: len(buf)] = buf
    data.totalBytesRead += len(buf)
    return len(buf), buffer


def skipBytesSequentialAccessDP(data, count):
    try:
        data.fp.seek(count, os.SEEK_CUR)
        data.skippedBytes += count

    except OSError as msg:
        print("Couldn't seek %d bytes because of %s" % (count, msg))


def rewindSequentialAccessDP(data):
    # Rewind the beginning of the data.
    data.fp.seek(0, 0)
    data.numRewinds += 1


def releaseSequentialAccessDP(data):
    if data is not None:
        print(
            "read %d bytes, skipped %d bytes, rewind called %d times"
            % (data.totalBytesRead, data.skippedBytes, data.numRewinds)
        )
        data.fp.close()


def createSequentialAccessDPForURL(url):
    success, pathString = Cocoa.CFURLGetFileSystemRepresentation(url, True, None, 1024)
    pathString = pathString.rstrip(b"\0")
    if not success:
        print("Couldn't get the path name C string!")
        return None

    fp = open(pathString, "rb")
    if fp is None:
        print(f"Couldn't open path to file {pathString}!")
        return None

    imageDataInfoP = MyImageDataInfo()
    imageDataInfoP.fp = fp

    provider = Quartz.CGDataProviderCreate(
        imageDataInfoP,
        (
            getBytesSequentialAccessDP,
            skipBytesSequentialAccessDP,
            rewindSequentialAccessDP,
            releaseSequentialAccessDP,
        ),
    )
    if provider is None:
        print("Couldn't create data provider!")
        # Release the info data and cleanup.
        releaseSequentialAccessDP(imageDataInfoP)
        return None

    return provider


def getBytesGrayRampDirectAccess(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 - offset

    for i in range(offset, offset + count):
        buffer[idx] = chr(i) if sys.version_info[0] == 2 else i
        idx += 1

    return count, buffer


def createGrayRampDirectAccessDP():
    provider = Quartz.CGDataProviderCreateDirectAccess(
        None, 256, (None, None, getBytesGrayRampDirectAccess, None)
    )
    if provider is None:
        print("Couldn't create data provider!")
        return None

    return provider


# This only builds on Tiger and later.
def myCGDataProviderCreateWithCFData(data):
    # If the CFData object passed in is None, this code returns
    # a None data provider.
    if data is None:
        return None

    # Test to see if the Quartz version is available and if so, use it.

    # XXX: force the replacement code to be used
    # if hasattr(Quartz, 'CGDataProviderCreateWithCFData'):
    #    return CGDataProviderCreateWithCFData(data)

    dataSize = Cocoa.CFDataGetLength(data)
    provider = Quartz.CGDataProviderCreateWithData(data, data, dataSize, None)
    return provider


def createDataConsumerFromPathName(path):
    # Create a CFURL for the supplied file system path.
    url = Cocoa.CFURLCreateWithFileSystemPath(
        None, path, Cocoa.kCFURLPOSIXPathStyle, False
    )
    if url is None:
        print("Couldn't create url!")
        return None
    # Create a Quartz data provider for the URL.
    dataConsumer = Quartz.CGDataConsumerCreateWithURL(url)
    if dataConsumer is None:
        print("Couldn't create data consumer!")
        return None

    return dataConsumer


def myCFDataConsumerPutBytes(data, buffer, count):
    # Append 'count' bytes from 'buffer' to the CFData
    # object 'data'.
    Cocoa.CFDataAppendBytes(data, buffer, count)
    return count


# This only builds on Tiger and later.
def myCGDataConsumerCreateWithCFData(data):
    # If the CFData object passed in is None, this code returns
    # a None data consumer.
    if data is None:
        return None

    # 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))
    return consumer

DrawingBasics.py

import math

import Quartz


def doSimpleRect(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)


def doStrokedRect(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)


def doStrokedAndFilledRect(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.0
    ourRect.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)


def createRectPath(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)


def doPathRects(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.


def doAlphaRects(context):
    # ***** Part 1 *****
    ourRect = Quartz.CGRectMake(0.0, 0.0, 130.0, 100.0)
    numRects = 6
    rotateAngle = 2 * math.pi / numRects
    tintAdjust = 1.0 / numRects

    # ***** Part 2 *****
    Quartz.CGContextTranslateCTM(
        context, 2 * ourRect.size.width, 2 * ourRect.size.height
    )

    # ***** Part 3 *****
    tint = 1.0
    for _ in range(numRects):
        Quartz.CGContextSetRGBFillColor(context, tint, 0.0, 0.0, tint)
        Quartz.CGContextFillRect(context, ourRect)
        # These transformations are cumulative.
        Quartz.CGContextRotateCTM(context, rotateAngle)
        tint -= tintAdjust


def drawStrokedLine(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)


def doDashedLines(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)


def doClippedCircle(context):
    circleCenter = Quartz.CGPoint(150.0, 150.0)
    circleRadius = 100.0
    startingAngle = 0.0
    endingAngle = 2 * math.pi
    ourRect = Quartz.CGRectMake(65.0, 65.0, 170.0, 170.0)

    # ***** Filled Circle *****
    Quartz.CGContextSetRGBFillColor(context, 0.663, 0.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)


def doPDFDocument(context, url):
    pdfDoc = Quartz.CGPDFDocumentCreateWithURL(url)
    if pdfDoc is not None:
        Quartz.CGContextScaleCTM(context, 0.5, 0.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.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

import BitmapContext
import Cocoa
import objc
import Quartz
import Utilities

# We're using a function that isn't made available through a wrapper, just
# load it manually:
if not hasattr(Quartz, "PMCGImageCreateWithEPSDataProvider"):
    functions = [("PMCGImageCreateWithEPSDataProvider", b"@@@")]
    import AppKit

    d = {}
    objc.loadBundleFunctions(AppKit.__bundle__, d, functions)

    if "PMCGImageCreateWithEPSDataProvider" in d:
        PMCGImageCreateWithEPSDataProvider = d["PMCGImageCreateWithEPSDataProvider"]
    else:
        print("PMCGImageCreateWithEPSDataProvider doesn't exist")


def getEPSBBox(epspath):
    try:
        fp = open(epspath)
    except OSError:
        return Quartz.CGRectZero

    try:
        #  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 occurrence of a
        #  %%BoundingBox comment and if it is of the typical
        # form, it will obtain the bounding box data.
        #
        for ln in fp:
            if ln.startswith("%%BoundingBox:"):
                fields = ln.split()[1:]
                if len(fields) >= 4:
                    llx = int(fields[0])
                    lly = int(fields[1])
                    urx = int(fields[2])
                    ury = int(fields[3])
                    return Quartz.CGRectMake(llx, lly, urx - llx, ury - lly)
    finally:
        fp.close()

    return Quartz.CGRectZero


def createEPSPreviewImage(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)
    if not result:
        print("Couldn't get the path for EPS file!")
        return None

    path = path.rstrip(b"\0")

    epsRect = getEPSBBox(path)
    # Check whether the EPS bounding box is empty.
    if epsRect == Quartz.CGRectZero:
        print("Couldn't find BoundingBox comment!")
        return None

    wantDisplayColorSpace = False
    needsTransparentBitmap = 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,
    )
    if bitmapContext is None:
        print("Couldn't create bitmap context")
        return None

    epsRect.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)

    if epsPreviewImage is None:
        print("Couldn't create preview image!")
        return None

    return epsPreviewImage


# This technique of handling EPS data is available in
# macOS 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.
def createCGEPSImage(url):
    previewImage = createEPSPreviewImage(url)
    if previewImage is None:
        print("Couldn't create EPS preview!")
        return None

    # 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)
    if epsDataProvider is None:
        print("Couldn't create EPS data provider!")
        return None

    # 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.
    del previewImage
    del epsDataProvider

    if epsImage is None:
        print("Couldn't create EPS hybrid image!")
        return None

    return epsImage


def drawEPSDataImage(context, url):
    # Create the a CGImage that has EPS data associated with it.
    epsDataImage = createCGEPSImage(url)
    if epsDataImage is None:
        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

import Cocoa
import objc
import Quartz
import QuartzTextDrawing
import Utilities
from objc import super  # noqa: A004


def getTextString():
    # 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 object
    return Cocoa.NSString.stringWithString_(
        "\u0051\u0075\u0061\u0072\u0074\u007A\u266A\u2766\u265B\u3042\u304E"
    )


doPointDrawing = 1


def drawNSStringWithAttributes():
    textString = getTextString()
    if doPointDrawing:
        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)

    if doPointDrawing:
        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_(  # noqa: B950
            0.663, 0, 0.031, 1.0
        ),
    }

    # Draw the text.
    textString.drawAtPoint_withAttributes_(p, stringAttributes)

    if doPointDrawing:
        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.0
    textString.drawAtPoint_withAttributes_(p, stringAttributes)

    if doPointDrawing:
        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)

    if doPointDrawing:
        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 -= 50
    rect = Cocoa.NSRect(origin=p, size=Cocoa.NSSize(0, 0))
    textString.drawWithRect_options_attributes_(
        rect, Cocoa.NSStringDrawingDisableScreenFontSubstitution, stringAttributes
    )

    if doPointDrawing:
        Utilities.drawPoint(context, p)


_myLayout = None
_textStorage = None
_myTextRange = None


def drawWithNSLayout():
    global _myLayout, _textStorage, _myTextRange

    if _myLayout is None:
        # 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.
        del textContainer
        # 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_(  # noqa: B950
                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)

    if doPointDrawing:
        context = Cocoa.NSGraphicsContext.currentContext().graphicsPort()
        Utilities.drawPoint(context, p)


# The interface to the NSLayoutManager subclass.
class MyNSLayoutManager(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.
    def setTextMode_(self, textMode):
        self._textMode = textMode

    def setFillColor_(self, color):
        self._fColor = color

    def setStrokeColor_(self, color):
        self._sColor = color

    def setTextLineWidth_(self, width):
        self._lineWidth = width

    def setClippingDrawProc_withInfo_(self, clippingDrawProc, info):
        self._clippingDrawProc = clippingDrawProc
        self._clippingInfo = info

    def init(self):
        self = super().init()
        if self is None:
            return None

        # Initialize the custom instance variables.
        self._textMode = Quartz.kCGTextFill
        self._fColor = None
        self._sColor = None
        self._yStartPosition = 0
        self._lineWidth = 1
        self._clippingDrawProc = None
        self._clippingInfo = None
        return self

    # This code overrides this method to record the y coordinate
    # to use as the True baseline for the text drawing.
    def drawGlyphsForGlyphRange_atPoint_(self, glyphsToShow, origin):
        self._yStartPosition = origin.y
        super().drawGlyphsForGlyphRange_atPoint_(glyphsToShow, origin)

    # This is the rendering method of NSLayoutManager that the
    # code overrides to perform its custom rendering.
    def showPackedGlyphs_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 = self._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.kCGTextFill
            or self.textMode == Quartz.kCGTextFillStroke
            or self._textMode == Quartz.kCGTextFillClip
            or self._textMode == Quartz.kCGTextFillStrokeClip
        ):
            if self._fColor is not None:
                Quartz.CGContextSetFillColorWithColor(context, self._fColor)

        # Set the  line width and the stroke color if needed.
        if (
            self._textMode == Quartz.kCGTextStroke
            or self._textMode == Quartz.kCGTextFillStroke
            or self._textMode == Quartz.kCGTextStrokeClip
            or self._textMode == Quartz.kCGTextFillStrokeClip
        ):
            Quartz.CGContextSetLineWidth(context, self._lineWidth)
            if self._sColor is not None:
                Quartz.CGContextSetStrokeColorWithColor(context, self._sColor)

        # Now draw the text. Check whether to adjust for printing widths
        # and if needed adjust extra character spacing accordingly.
        if printingAdjustment.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.kCGTextClip
            or self._textMode == Quartz.kCGTextFillClip
            or self._textMode == Quartz.kCGTextStrokeClip
            or self._textMode == Quartz.kCGTextFillStrokeClip
        ) and self._clippingDrawProc is not None:
            self._clippingDrawProc(context, point.x, point.y, self._clippingInfo)

        Quartz.CGContextRestoreGState(context)


def MyClipProc(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 = None


def drawWithCustomNSLayout():
    global _myLayout2, _textStorage2, _myTextRange2

    if _myLayout2 is None:
        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.
        del textContainer
        _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)

    if doPointDrawing:
        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)

    if doPointDrawing:
        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)

    if doPointDrawing:
        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)

    if doPointDrawing:
        Utilities.drawPoint(context, p)

    # Set the clipping proc to None for future drawing.
    _myLayout2.setClippingDrawProc_withInfo_(None, None)

FrameworkUtilities.py

import AppDrawing  # noqa: F401
from PDFHandling import cfDataCreatePDFDocumentFromCommand
import Cocoa


def myCreatePDFDataFromPasteBoard():
    # 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.
    pboard_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.
    if pboard_type is not None:
        # 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.
        if pboard_type == Cocoa.NSPDFPboardType:
            # Get the PDF data from the pasteboard.
            pdfData = pboard.dataForType_(pboard_type)
            if pdfData is not None:
                return pdfData
            else:
                Cocoa.NSLog("Couldn't get PDF data from pasteboard!")
    else:
        Cocoa.NSLog("Pasteboard doesn't contain PDF data!")
    return None


def addPDFDataToPasteBoard(command):
    pdfData = cfDataCreatePDFDocumentFromCommand(command)
    if pdfData is not None:
        pboard = Cocoa.NSPasteboard.generalPasteboard()
        pboard.declareTypes_owner_(Cocoa.NSPDFPboardType, None)
        pboard.setData_forType_(pdfData, Cocoa.NSPDFPboardType)

ImageMasking.py

import sys

import Cocoa

# import DataProvidersAndConsumers
import Images
import Quartz
import Utilities


def exportImageWithMaskFromURLWithDestination(
    context,
    imageURL,
    imagewidth,
    imageheight,
    bitsPerComponent,
    theMaskingImageURL,
    maskwidth,
    maskheight,
):
    imageBitsPerPixel = bitsPerComponent * 3
    bytesPerRow = ((imagewidth * imageBitsPerPixel) + 7) / 8
    shouldInterpolate = True
    imageDataProvider = Quartz.CGDataProviderCreateWithURL(imageURL)
    if imageDataProvider is None:
        print("Couldn't create Image Data provider!")
        return

    colorspace = Utilities.getTheCalibratedRGBColorSpace()
    image = Quartz.CGImageCreate(
        imagewidth,
        imageheight,
        bitsPerComponent,
        imageBitsPerPixel,
        bytesPerRow,
        colorspace,
        Quartz.kCGImageAlphaNone,
        imageDataProvider,
        None,
        shouldInterpolate,
        Quartz.kCGRenderingIntentDefault,
    )
    del imageDataProvider
    if image is None:
        print("Couldn't create CGImageRef for this data!")
        return

    imageRect = Quartz.CGRectMake(0.0, imageheight, imagewidth, imageheight)
    # Draw the image.
    Quartz.CGContextDrawImage(context, imageRect, image)

    # Now the mask.
    maskDataProvider = Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)
    if maskDataProvider is None:
        print("Couldn't create Image Data provider!")
        return

    mask = Quartz.CGImageMaskCreate(
        maskwidth,
        maskheight,
        bitsPerComponent,
        bitsPerComponent,
        maskwidth,
        maskDataProvider,
        None,
        shouldInterpolate,
    )
    del maskDataProvider
    if mask is None:
        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.
    del image
    del mask

    if imageMaskedWithImage is None:
        print("Couldn't create image masked with mask!")
        return

    imageRect = 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
    )

    if exportURL is not None:
        Images.exportCGImageToPNGFileWithDestination(imageMaskedWithImage, exportURL)


if sys.version_info[0] == 2:

    def make_bytes(values):
        return "".join(map(chr, values))

else:

    def make_bytes(values):
        return bytes(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,
    )
)


def getMaskData1():
    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,
    )
)


def getMaskData2():
    return _data2


def doOneBitMaskImages(context):
    bitsPerComponent = 1
    bitsPerPixel = 1
    width = 96
    height = 96
    bytesPerRow = 12

    imageDataSize = bytesPerRow * height
    shouldInterpolate = True
    lightBlue = [0.482, 0.62, 0.871, 1.0]
    black = [0.0, 0.0, 0.0, 1.0]
    darkRed = [0.663, 0.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)
    if dataProvider is None:
        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.
    del dataProvider

    if mask1 is None or invertedmask1 is None:
        if mask1 is None:
            print("Couldn't create CGImageRef for the mask data 1!")

        if invertedmask1 is None:
            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)
    if dataProvider is None:
        print("Couldn't create Mask2 Data provider!")
        return

    mask2 = 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.
    del dataProvider
    if mask2 is None or invertedmask2 is None:
        if mask2 is None:
            print("Couldn't create CGImageRef for the mask data 2!")

        if invertedmask2 is None:
            print("Couldn't create CGImageRef for the inverted mask data 2!")

        return

    Quartz.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.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.5
    Quartz.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.5
    Quartz.CGContextSetFillColor(context, purple)
    # Mask 8.
    Quartz.CGContextDrawImage(context, imageRect, invertedmask1)


def doMaskImageWithMaskFromURL(
    context,
    imageURL,
    imagewidth,
    imageheight,
    bitsPerComponent,
    theMaskingImageURL,
    maskwidth,
    maskheight,
):
    imageBitsPerPixel = bitsPerComponent * 3
    bytesPerRow = ((imagewidth * imageBitsPerPixel) + 7) / 8
    shouldInterpolate = True
    imageDataProvider = Quartz.CGDataProviderCreateWithURL(imageURL)
    if imageDataProvider is None:
        print("Couldn't create Image Data provider!")
        return

    colorspace = Utilities.getTheCalibratedRGBColorSpace()
    image = Quartz.CGImageCreate(
        imagewidth,
        imageheight,
        bitsPerComponent,
        imageBitsPerPixel,
        bytesPerRow,
        colorspace,
        Quartz.kCGImageAlphaNone,
        imageDataProvider,
        None,
        shouldInterpolate,
        Quartz.kCGRenderingIntentDefault,
    )
    del imageDataProvider
    if image is None:
        print("Couldn't create CGImageRef for this data!")
        return

    imageRect = Quartz.CGRectMake(0.0, imageheight, imagewidth, imageheight)
    # Draw the image.
    Quartz.CGContextDrawImage(context, imageRect, image)

    # Now the mask.
    maskDataProvider = Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)
    if maskDataProvider is None:
        print("Couldn't create Image Data provider!")
        return

    mask = Quartz.CGImageMaskCreate(
        maskwidth,
        maskheight,
        bitsPerComponent,
        bitsPerComponent,
        maskwidth,
        maskDataProvider,
        None,
        shouldInterpolate,
    )
    del maskDataProvider
    if mask is None:
        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'.
    del image
    del mask
    if imageMaskedWithImage is None:
        print("Couldn't create image masked with mask!")
        return

    imageRect = 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)


def doMaskImageWithGrayImageFromURL(
    context,
    imageURL,
    imagewidth,
    imageheight,
    bitsPerComponent,
    theMaskingImageURL,
    maskwidth,
    maskheight,
):
    imageBitsPerPixel = bitsPerComponent * 3
    bytesPerRow = ((imagewidth * imageBitsPerPixel) + 7) / 8
    shouldInterpolate = True

    imageDataProvider = Quartz.CGDataProviderCreateWithURL(imageURL)
    if imageDataProvider is None:
        print("Couldn't create Image Data provider!")
        return

    colorspace = Utilities.getTheCalibratedRGBColorSpace()
    image = Quartz.CGImageCreate(
        imagewidth,
        imageheight,
        bitsPerComponent,
        imageBitsPerPixel,
        bytesPerRow,
        colorspace,
        Quartz.kCGImageAlphaNone,
        imageDataProvider,
        None,
        shouldInterpolate,
        Quartz.kCGRenderingIntentDefault,
    )
    del imageDataProvider
    if image is None:
        print("Couldn't create CGImageRef for this data!")
        return

    imageRect = Quartz.CGRectMake(0.0, imageheight, imagewidth, imageheight)
    # Draw the image.
    Quartz.CGContextDrawImage(context, imageRect, image)

    # Now the mask.
    maskDataProvider = Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)
    if maskDataProvider is None:
        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.
    del deviceGraySpace
    del maskDataProvider

    if mask is None:
        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, 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'.
    del image
    del mask

    if imageMaskedWithImage is None:
        print("Couldn't create image masked with mask!")
        return

    imageRect = 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.
    del imageMaskedWithImage


def doMaskImageWithColorFromURL(context, url, width, height, isColor):
    # This routine treats color images as RGB.
    bitsPerComponent = 8
    if isColor:
        bitsPerPixel = bitsPerComponent * 3
    else:
        bitsPerPixel = bitsPerComponent

    bytesPerRow = ((width * bitsPerPixel) + 7) / 8
    shouldInterpolate = 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.0, 0.0, 1.0)  # Opaque red.

    # Create a Quartz data provider from the supplied URL.
    dataProvider = Quartz.CGDataProviderCreateWithURL(url)
    if dataProvider is None:
        print("Couldn't create Image data provider!")
        return

    # Create an image of the specified width, height and bits per pixel
    # from the URL.
    if isColor:
        colorspace = Utilities.getTheCalibratedRGBColorSpace()
    else:
        colorspace = Utilities.getTheCalibratedGrayColorSpace()

    image = Quartz.CGImageCreate(
        width,
        height,
        bitsPerComponent,
        bitsPerPixel,
        bytesPerRow,
        colorspace,
        Quartz.kCGImageAlphaNone,
        dataProvider,
        None,
        shouldInterpolate,
        Quartz.kCGRenderingIntentDefault,
    )
    del dataProvider
    if image is None:
        print("Couldn't create CGImageRef for this data!")
        return

    imageRect = Quartz.CGRectMake(10.0, 10.0, 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.
    del image
    if imageMaskedWithColor is None:
        print("Couldn't create CGImageRef for masking color!")
        return

    # Paint the rectangle behind the next image with red.
    imageRect = Quartz.CGRectMake(30.0 + width, 10.0, 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)


if 1:  # Set to 1 for code in the book.

    def drawWithClippingMask(context, theMaskingImageURL, imagewidth, imageheight):
        # An array of CGColor objects.
        colors = (
            Utilities.getRGBOpaqueDarkGreenColor(),
            Utilities.getRGBOpaqueDarkBlueColor(),
            Utilities.getRGBOpaqueBlueColor(),
            Utilities.getRGBOpaqueRedColor(),
        )

        imageBitsPerComponent = 8
        bytesPerRow = imagewidth
        shouldInterpolate = True
        decode = (1, 0)

        # Create the data.
        dataProvider = Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)
        if dataProvider is None:
            print("Couldn't create Image data provider!")
            return

        cs = Quartz.CGColorSpaceCreateDeviceGray()
        image = Quartz.CGImageCreate(
            imagewidth,
            imageheight,
            imageBitsPerComponent,
            imageBitsPerComponent,
            bytesPerRow,
            cs,
            Quartz.kCGImageAlphaNone,
            dataProvider,
            decode,
            shouldInterpolate,
            Quartz.kCGRenderingIntentDefault,
        )
        del cs
        del dataProvider

        if image is None:
            print("Couldn't create Image!")
            return

        imageRect = 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.
        del image

        # 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.
        for j in range(3):
            Quartz.CGContextSaveGState(context)
            for i in range(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.
    def drawWithClippingMask(context, theMaskingImageURL, maskwidth, maskheight):
        # An array of Quartz.CGColor objects.
        colors = (
            Utilities.getRGBOpaqueDarkGreenColor(),
            Utilities.getRGBOpaqueDarkBlueColor(),
            Utilities.getRGBOpaqueBlueColor(),
            Utilities.getRGBOpaqueRedColor(),
        )
        maskBitsPerComponent = 8
        # bytesPerRow = ((maskwidth * maskBitsPerComponent) + 7) / 8
        shouldInterpolate = True
        maskDataProvider = Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)

        if maskDataProvider is None:
            print("Couldn't create Image Mask provider!")
            return
        mask = Quartz.CGImageMaskCreate(
            maskwidth,
            maskheight,
            maskBitsPerComponent,
            maskBitsPerComponent,
            maskwidth,
            maskDataProvider,
            None,
            shouldInterpolate,
        )
        del maskDataProvider

        if mask is None:
            print("Couldn't create Image Mask!")
            return

        maskRect = 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, Utilities.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.
        del mask

        # 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.
        for j in range(3):
            Quartz.CGContextSaveGState(context)
            for i in range(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

import Cocoa
import DataProvidersAndConsumers
import Quartz
import Utilities
from LaunchServices import kUTTypePNG


def drawJPEGImage(context, url):
    # Create a Quartz data provider for the supplied URL.
    jpgProvider = Quartz.CGDataProviderCreateWithURL(url)
    if jpgProvider is None:
        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.
    del jpgProvider

    if jpgImage is None:
        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)


def drawImageFromURL(context, url, width, height, bitsPerComponent, isRGB):
    # This routine treats color images as RGB
    if isRGB:
        bitsPerPixel = bitsPerComponent * 3
    else:
        bitsPerPixel = bitsPerComponent

    bytesPerRow = (width * bitsPerPixel + 7) / 8
    shouldInterpolate = True

    # Create a Quartz data provider from the supplied URL.
    dataProvider = Quartz.CGDataProviderCreateWithURL(url)
    if dataProvider is None:
        print("Couldn't create Image data provider!")
        return

    # Get a Quartz color space object appropriate for the image type.
    if isRGB:
        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.
    del dataProvider
    if image is None:
        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)


def doColorRampImage(context):
    width = 256
    height = 256
    bitsPerComponent = 8
    bitsPerPixel = 24
    bytesPerRow = width * 3
    shouldInterpolate = True

    imageDataProvider = DataProvidersAndConsumers.createRGBRampDataProvider()
    if imageDataProvider is None:
        print("Couldn't create Image Data provider!")
        return

    colorspace = Utilities.getTheCalibratedRGBColorSpace()
    image = Quartz.CGImageCreate(
        width,
        height,
        bitsPerComponent,
        bitsPerPixel,
        bytesPerRow,
        colorspace,
        Quartz.kCGImageAlphaNone,
        imageDataProvider,
        None,
        shouldInterpolate,
        Quartz.kCGRenderingIntentDefault,
    )
    # No longer need the data provider.
    del imageDataProvider
    if image is None:
        print("Couldn't create CGImageRef for this data!")
        return

    imageRect = Quartz.CGRectMake(0.0, 0.0, width, height)
    # Draw the image.
    Quartz.CGContextDrawImage(context, imageRect, image)


def doImageWithCallbacksCreatedFromURL(
    context, url, width, height, bitsPerComponent, isRGB
):
    if isRGB:
        bitsPerPixel = bitsPerComponent * 3
    else:
        bitsPerPixel = bitsPerComponent

    bytesPerRow = ((width * bitsPerPixel) + 7) / 8
    shouldInterpolate = True

    dataProvider = DataProvidersAndConsumers.createSequentialAccessDPForURL(url)
    if dataProvider is None:
        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.
    if isRGB:
        colorspace = Utilities.getTheCalibratedRGBColorSpace()
    else:
        colorspace = Utilities.getTheCalibratedGrayColorSpace()

    image = Quartz.CGImageCreate(
        width,
        height,
        bitsPerComponent,
        bitsPerPixel,
        bytesPerRow,
        colorspace,
        Quartz.kCGImageAlphaNone,
        dataProvider,
        None,
        shouldInterpolate,
        Quartz.kCGRenderingIntentDefault,
    )
    del dataProvider
    if image is None:
        print("Couldn't create CGImageRef for this data!")
        return

    imageRect = Quartz.CGRectMake(0.0, 0.0, width, height)

    # Draw the image into the rectangle.
    Quartz.CGContextDrawImage(context, imageRect, image)


def doGrayRamp(context):
    width = 256
    height = 1
    bitsPerComponent = 8
    bitsPerPixel = 8
    bytesPerRow = width
    shouldInterpolate = True

    dataProvider = DataProvidersAndConsumers.createGrayRampDirectAccessDP()
    if dataProvider is None:
        print("Couldn't create Gray Ramp provider!")
        return

    colorspace = Utilities.getTheCalibratedGrayColorSpace()
    image = Quartz.CGImageCreate(
        width,
        height,
        bitsPerComponent,
        bitsPerPixel,
        bytesPerRow,
        colorspace,
        Quartz.kCGImageAlphaNone,
        dataProvider,
        None,
        shouldInterpolate,
        Quartz.kCGRenderingIntentDefault,
    )
    del dataProvider
    if image is None:
        print("Couldn't create CGImageRef for image data!")
        return

    imageRect = 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.
def createFloatingPointImageOptions(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)
    if properties is not None:
        # Get the value for the kCGImagePropertyIsFloat if it exists
        # and if the value is a CFBoolean then get the corresponding
        # Boolean result.
        if Quartz.kCGImagePropertyIsFloat in properties:
            isFloat = bool(properties[Quartz.kCGImagePropertyIsFloat])

    if not isFloat:
        return None

    return options


def myCreateImageUsingImageSource(url):
    # Set to zero, indicating the property was unavailable.
    xdpi = ydpi = 0

    # Create the image source from the URL.
    imageSource = Quartz.CGImageSourceCreateWithURL(url, None)
    if imageSource is None:
        print("Couldn't create image source from URL!")
        return (None, xdpi, ydpi)

    if False:
        options = createFloatingPointImageOptions(imageSource)
        if options is not None:
            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)
    if properties is not None:
        # 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.
    del imageSource
    del options

    if image is None:
        print("Couldn't create image from image source!")
        return None

    return (image, xdpi, ydpi)


def myCreateThumbnailFromImageSource(url):
    maxThumbSize = 160

    # Create the image source from the URL.
    imageSource = Quartz.CGImageSourceCreateWithURL(url, None)
    if imageSource is None:
        print("Couldn't create image source from URL!")
        return None

    options = {
        # 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.
    del options
    # Release the image source the code created.
    del imageSource

    if thumb is None:
        print("Couldn't create thumbnail from image source!")
        return None

    return thumb


def imageHasFloatingPointSamples(image):
    if hasattr(Quartz, "CGImageGetBitmapInfo"):
        return (
            Quartz.kCGBitmapFloatComponents & Quartz.CGImageGetBitmapInfo(image)
        ) != 0
    return False


def drawImageWithCGImageDataSource(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)
    if image is None:
        print("myCreateImageFromImageSource didn't create a CGImage!")
        return

    print(f"xdpi = {xdpi:2.f}, ydpi = {ydpi:2.f}")
    imageRect = Quartz.CGRectMake(
        0.0, 0.0, Quartz.CGImageGetWidth(image) / 3, Quartz.CGImageGetHeight(image) / 3
    )
    Quartz.CGContextDrawImage(context, imageRect, image)

    if 0:
        isFloatingImage = imageHasFloatingPointSamples(image)
        if isFloatingImage:
            print("First image IS a floating point image")
        else:
            print("First image IS NOT a floating point image")

    del image

    image = myCreateThumbnailFromImageSource(url)
    if image is None:
        print("myCreateThumbnailFromImageSource didn't create a CGImage!")
        return

    imageRect = Quartz.CGRectMake(
        400.0, 0.0, Quartz.CGImageGetWidth(image), Quartz.CGImageGetHeight(image)
    )
    Quartz.CGContextDrawImage(context, imageRect, image)

    del image


class MyIncrementalData:
    data = None
    dataSize = 0
    repCount = 0
    chunkSize = 0


# This is a dummy data accumulation routine used to demonstrate incremental
# loading of an image.
def myCreateAccumulatedDataSoFar(myDataP):
    myDataP.repCount += 1
    sizeToReturn = myDataP.chunkSize * myDataP.repCount

    if sizeToReturn > myDataP.dataSize:
        sizeToReturn = myDataP.dataSize

    done = sizeToReturn == myDataP.dataSize
    data = Cocoa.CFDataCreate(None, myDataP.data, sizeToReturn)
    return data, done


def MyDrawIncrementalImage(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)


def myDrawFirstImageIncrementally(context, myDataP):
    height = -1
    # Create an incremental image source.
    imageSource = Quartz.CGImageSourceCreateIncremental(None)
    if imageSource is None:
        print("Couldn't create incremental imagesource!")
        return

    # Loop, gathering the necessary data to find the True
    # height of the image.
    while 1:
        # 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.
        del data

        if height < 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)
            if properties is not None:
                if Quartz.kCGImagePropertyPixelHeight in properties:
                    height = properties[Quartz.kCGImagePropertyPixelHeight]
            del properties

        # Once the height is obtained, go ahead and see if Quartz
        # has enough data to create a CGImage object.
        print("height", height)
        if height > 0:
            # Now create the CGImageRef from the image source for the
            # first image.
            image = Quartz.CGImageSourceCreateImageAtIndex(imageSource, 0, None)
            if image is not None:
                # 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.
                del image
                # 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)

        if done:  # or status  == Quartz.kCGImageStatusComplete:
            print(done, status, status == Quartz.kCGImageStatusComplete)
            break


def createMyIncrementalDataFromURL(url, myDataP):
    myDataP.data = None
    myDataP.dataSize = 0
    myDataP.repCount = 0

    success, pathString = Cocoa.CFURLGetFileSystemRepresentation(url, True, None, 1024)
    pathString = pathString.rstrip(b"\0")

    if success and len(pathString):
        fp = open(pathString, "rb")
        myDataP.data = fp.read()
        fp.close()
        myDataP.dataSize = len(myDataP.data)

    if myDataP.dataSize > 0:
        myDataP.chunkSize = myDataP.dataSize / 10  # 10 chunks


def doIncrementalImageWithURL(context, url):
    myData = MyIncrementalData()
    createMyIncrementalDataFromURL(url, myData)
    if myData.data is None:
        print("couldn't read data from URL!")

    myDrawFirstImageIncrementally(context, myData)
    del myData


# This code requires QuickTime.framework.
# from Carbon import Qt
def createCGImageWithQuickTimeFromURL(url):
    """
    Note: this function doesn't actually worked because the APIs used in here
    aren't properly wrapped (yet).
    """
    return None

    imageRef = None

    err = 0
    result, dataRef, dataRefType = Quartz.QTNewDataReferenceFromCFURL(
        url, 0, None, None
    )
    if dataRef is not None:
        err, gi = Quartz.GetGraphicsImporterForDataRefWithFlags(
            dataRef, dataRefType, None, 0
        )
        if not err and gi:
            # 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 = Quartz.GraphicsImportSetFlags(
                gi,
                (
                    Quartz.kGraphicsImporterDontDoGammaCorrection
                    + Quartz.kGraphicsImporterDontUseColorMatching
                ),
            )
            if result == 0:
                result, imageRef = Quartz.GraphicsImportCreateCGImage(gi, None, 0)
                if result != 0:
                    print("got a bad result = %d!" % (result,))
            Quartz.DisposeHandle(dataRef)
            Quartz.CloseComponent(gi)

    return imageRef


def drawQTImageWithQuartz(context, url):
    image = createCGImageWithQuickTimeFromURL(url)
    if image is None:
        print("createCGImageWithQuickTimeFromURL didn't create a CGImage!")
        return

    imageRect = Quartz.CGRectMake(
        0.0, 0.0, Quartz.CGImageGetWidth(image), Quartz.CGImageGetHeight(image)
    )
    Quartz.CGContextDrawImage(context, imageRect, image)


def drawJPEGDocumentWithMultipleProfiles(context, url):
    # isDeviceRGBImage = False

    # Create a Quartz data provider for the supplied URL.
    jpgProvider = Quartz.CGDataProviderCreateWithURL(url)
    if jpgProvider is None:
        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
    )
    del jpgProvider
    if jpgImage is None:
        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)
    if originalColorSpace is None:
        print("image is a masking image, not an image with color!")
        return

    if Quartz.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.
    del jpgImage
    if updatedImage1 is None:
        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.
    del updatedImage1
    if updatedImage2 is None:
        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)


def createRedGreenRampImageData(width, height, size):
    try:
        dataP = bytearray(size)
    except MemoryError:
        return None

    idx = 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.
    for g in range(height):
        for r in range(width):
            dataP[idx + 0] = r
            dataP[idx + 1] = g
            dataP[idx + 2] = 0
            idx += 3

    return dataP


def createRGBRampSubDataProvider(subRect):
    bytesPerSample = 3
    width = 256
    height = 256
    bytesPerRow = width * bytesPerSample
    startOffsetX = subRect.origin.x
    startOffsetY = subRect.origin.y
    imageDataSize = 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)
    if dataP is None:
        print("Couldn't create image data!")
        return None

    # 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, dataP[firstByteOffset:], totalBytesProvided, None
    )

    if dataProvider is None:
        return None

    return dataProvider


def doColorRampSubImage(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 = 16
    insetTop = 4
    insetRight = 64
    insetBottom = 40

    fullImageWidth = 256
    fullImageHeight = 256
    subImageWidth = fullImageWidth - insetLeft - insetRight
    subImageHeight = fullImageHeight - insetTop - insetBottom
    bitsPerComponent = 8
    bitsPerPixel = 24
    bytesPerRow = fullImageWidth * 3
    shouldInterpolate = True

    imageSubRect = Quartz.CGRectMake(insetLeft, insetTop, subImageWidth, subImageHeight)
    colorspace = Utilities.getTheCalibratedRGBColorSpace()

    if hasattr(Quartz, "CGImageCreateWithImageInRect"):
        imageDataProvider = DataProvidersAndConsumers.createRGBRampDataProvider()
        if imageDataProvider is None:
            print("Couldn't create Image Data provider!")
            return

        fullImage = Quartz.CGImageCreate(
            fullImageWidth,
            fullImageHeight,
            bitsPerComponent,
            bitsPerPixel,
            bytesPerRow,
            colorspace,
            Quartz.kCGImageAlphaNone,
            imageDataProvider,
            None,
            shouldInterpolate,
            Quartz.kCGRenderingIntentDefault,
        )
        if fullImage is not None:
            image = Quartz.CGImageCreateWithImageInRect(fullImage, imageSubRect)
            # release the full image since it is no longer required.
            del fullImage

    # If the image hasn't been created yet, this code uses the
    # customized data provider to do so.
    if image is None:
        imageDataProvider = createRGBRampSubDataProvider(imageSubRect)
        if imageDataProvider is None:
            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.
    del imageDataProvider

    if image is None:
        print("Couldn't create CGImageRef for this data!")
        return

    # Draw the subimage.
    rect = Quartz.CGRectMake(0, 0, subImageWidth, subImageHeight)
    Quartz.CGContextDrawImage(context, rect, image)


def exportCGImageToPNGFileWithDestination(image, url):
    resolution = 144.0

    # Create an image destination at the supplied URL that
    # corresponds to the PNG image format.
    imageDestination = Quartz.CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, None)

    if imageDestination is None:
        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.
    del options

    # When all the images are added to the destination, finalize it.
    Quartz.CGImageDestinationFinalize(imageDestination)

    # Release the destination when done with it.
    del imageDestination


# This code requires QuickTime.framework
#   include <QuickTime/QuickTime.h>
def exportCGImageToJPEGFile(imageRef, url):
    # This doesn't actually work due to lame Python Quicktime bindings...
    return

    result, dataRef, dataRefType = Quartz.QTNewDataReferenceFromCFURL(
        url, 0, None, None
    )
    if result == 0:
        result, graphicsExporter = Quartz.OpenADefaultComponent(
            Quartz.GraphicsExporterComponentType, Quartz.kQTFileTypeJPEG
        )
        if result == 0:
            result = Quartz.GraphicsExportSetInputCGImage(graphicsExporter, imageRef)
            if result == 0:
                result = Quartz.GraphicsExportSetOutputDataReference(
                    graphicsExporter, dataRef, dataRefType
                )
            if result == 0:
                result, sizeWritten = Quartz.GraphicsExportDoExport(
                    graphicsExporter, None
                )

            Quartz.CloseComponent(graphicsExporter)

    if dataRef is not None:
        Quartz.DisposeHandle(dataRef)

    if result != 0:
        print("Exporting QT image got bad result = %d!" % (result,))


def exportColorRampImageWithQT(context):
    width = 256
    height = 256
    bitsPerComponent = 8
    bitsPerPixel = 24
    bytesPerRow = width * 3
    shouldInterpolate = True

    imageDataProvider = DataProvidersAndConsumers.createRGBRampDataProvider()
    if imageDataProvider is None:
        print("Couldn't create Image Data provider!")
        return

    colorspace = Utilities.getTheCalibratedRGBColorSpace()
    image = Quartz.CGImageCreate(
        width,
        height,
        bitsPerComponent,
        bitsPerPixel,
        bytesPerRow,
        colorspace,
        Quartz.kCGImageAlphaNone,
        imageDataProvider,
        None,
        shouldInterpolate,
        Quartz.kCGRenderingIntentDefault,
    )
    del imageDataProvider
    if image is None:
        print("Couldn't create CGImageRef for this data!")
        return

    rect = 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 = Cocoa.CFURLCreateFromFileSystemRepresentation(
        None, outPath, len(outPath), False
    )
    if exportURL:
        exportCGImageToJPEGFile(image, exportURL)

MyAppController.py

import BitmapContext
import Cocoa
import objc
import PDFHandling
import Utilities

# Initial defaults
_dpi = 144
_useQT = False


def getURLToExport(suffix):
    savePanel = Cocoa.NSSavePanel.savePanel()

    initialFileName = f"BasicDrawing.{suffix}"

    if (
        savePanel.runModalForDirectory_file_(None, initialFileName)
        == Cocoa.NSFileHandlingPanelOKButton
    ):
        return savePanel.URL()

    return None


class MyAppController(Cocoa.NSObject):
    theView = objc.IBOutlet()
    currentDPIMenuItem = objc.IBOutlet()
    currentExportStyleMenuItem = objc.IBOutlet()

    @objc.IBAction
    def print_(self, sender):
        self.theView.print_(sender)

    def updateDPIMenu_(self, sender):
        if self.currentDPIMenuItem is not sender:
            # Uncheck the previous item.
            if self.currentDPIMenuItem is not None:
                self.currentDPIMenuItem.setState_(Cocoa.NSOffState)
            # Update to the current item.
            self.currentDPIMenuItem = sender
            # Check new menu item.
            self.currentDPIMenuItem.setState_(Cocoa.NSOnState)

    def updateExportStyleMenu_(self, sender):
        if self.currentExportStyleMenuItem is not sender:
            # Uncheck the previous item.
            if self.currentExportStyleMenuItem is not None:
                self.currentExportStyleMenuItem.setState_(Cocoa.NSOffState)
            # Update to the current item.
            self.currentExportStyleMenuItem = sender
            # Check new menu item.
            self.currentExportStyleMenuItem.setState_(Cocoa.NSOnState)

    @objc.IBAction
    def setExportResolution_(self, sender):
        global _dpi
        _dpi = sender.tag()
        self.updateDPIMenu_(sender)

    @objc.IBAction
    def setUseQT_(self, sender):
        global _useQT
        _useQT = True
        self.updateExportStyleMenu_(sender)

    @objc.IBAction
    def setUseCGImageSource_(self, sender):
        global _useQT
        _useQT = False
        self.updateExportStyleMenu_(sender)

    def setupExportInfo_(self, exportInfoP):
        # Use the printable version of the current command. This produces
        # the best results for exporting.
        exportInfoP.command = self.theView.currentPrintableCommand()
        exportInfoP.fileType = "    "  # unused
        exportInfoP.useQTForExport = _useQT
        exportInfoP.dpi = _dpi

    @objc.IBAction
    def exportAsPDF_(self, sender):
        url = getURLToExport("pdf")
        if url is not None:
            exportInfo = Utilities.ExportInfo()
            self.setupExportInfo_(exportInfo)
            PDFHandling.MakePDFDocument(url, exportInfo)

    @objc.IBAction
    def exportAsPNG_(self, sender):
        url = getURLToExport("png")
        if url is not None:
            exportInfo = Utilities.ExportInfo()
            self.setupExportInfo_(exportInfo)
            BitmapContext.MakePNGDocument(url, exportInfo)

    @objc.IBAction
    def exportAsTIFF_(self, sender):
        url = getURLToExport("tif")
        if url is not None:
            exportInfo = Utilities.ExportInfo()
            self.setupExportInfo_(exportInfo)
            BitmapContext.MakeTIFFDocument(url, exportInfo)

    @objc.IBAction
    def exportAsJPEG_(self, sender):
        url = getURLToExport("jpg")
        if url is not None:
            exportInfo = Utilities.ExportInfo()
            self.setupExportInfo_(exportInfo)
            BitmapContext.MakeJPEGDocument(url, exportInfo)

    def validateMenuItem_(self, menuItem):
        if menuItem.tag == _dpi:
            self.currentDPIMenuItem = menuItem
            menuItem.setState_(True)
        elif menuItem.action() == "setUseQT:":
            if _useQT:
                self.currentDPIMenuItem = menuItem
                menuItem.setState_(True)
            else:
                menuItem.setState_(False)

        elif menuItem.action() == "setUseCGImageSource:":
            if _useQT:
                self.currentDPIMenuItem = menuItem
                menuItem.setState_(True)
            else:
                menuItem.setState_(False)

        return True

MyView.py

import AppDrawing
import Cocoa
import Quartz
import FrameworkTextDrawing
import FrameworkUtilities
import objc
import UIHandling
import PDFHandling
from objc import super  # noqa: A004

# XXX: Why are these global?
_drawingCommand = UIHandling.kHICommandSimpleRect
_pdfDocument = None


class MyView(Cocoa.NSView):
    currentMenuItem = objc.IBOutlet()

    def initWithFrame_(self, frameRect):
        self = super().initWithFrame_(frameRect)
        if self is None:
            return None

        global _pdfDocument
        _pdfDocument = None
        return self

    if False:

        def isFlipped(self):
            return True

    def drawRect_(self, rect):
        context = Cocoa.NSGraphicsContext.currentContext().graphicsPort()

        if _pdfDocument is None:
            if _drawingCommand in (
                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 = Quartz.CGPDFDocumentGetMediaBox(_pdfDocument, 1)
            mediaRect.origin.x = mediaRect.origin.y = 0
            Quartz.CGContextDrawPDFDocument(context, mediaRect, _pdfDocument, 1)

    @objc.IBAction
    def setDrawCommand_(self, sender):
        global _drawingCommand, _pdfDocument

        newCommand = 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.
            if self.currentMenuItem is not None:
                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 = None

    def currentPrintableCommand(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 _drawingCommand in (
            UIHandling.kHICommandDrawOffScreenImage,
            UIHandling.kHICommandDrawWithLayer,
        ):
            return UIHandling.kHICommandDrawNoOffScreenImage

        return _drawingCommand

    def print_(self, sender):
        global _drawingCommand

        savedDrawingCommand = _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 = savedDrawingCommand

    def acceptsFirstResponder(self):
        return True

    @objc.IBAction
    def copy_(self, sender):
        FrameworkUtilities.addPDFDataToPasteBoard(_drawingCommand)

    @objc.IBAction
    def paste_(self, sender):
        global _pdfDocument

        newPDFDocument = PDFHandling.createNewPDFRefFromPasteBoard()
        if newPDFDocument is not None:
            _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.
    def knowsPageRange_(self, aRange):
        return True, Cocoa.NSRange(1, 1)

    # Return the drawing rectangle for a particular page number.
    # For this application it is always the page width and height.
    def rectForPage_(self, page):
        pi = Cocoa.NSPrintOperation.currentOperation().printInfo()

        # Calculate the page height in points.
        paperSize = pi.paperSize()
        return Cocoa.NSMakeRect(0, 0, paperSize.width, paperSize.height)

    def validateMenuItem_(self, menuItem):
        if menuItem.tag() == _drawingCommand:
            self.currentMenuItem = menuItem
            menuItem.setState_(True)
        else:
            menuItem.setState_(False)

        return True

PDFHandling.py

import AppDrawing
import Cocoa
import DataProvidersAndConsumers
import FrameworkUtilities
import Quartz


def createNewPDFRefFromPasteBoard():
    # 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()

    if pasteBoardData is None:
        print("There is no PDF data on pasteboard!")
        return None

    # Create a data provider from the pasteboard data.
    dataProvider = Quartz.CGDataProviderCreateWithCFData(pasteBoardData)
    # Release the pasteboard data since the data provider retains
    # it and this code owns a reference but no longer requires it.
    del pasteBoardData

    if dataProvider is None:
        print("Couldn't create data provider.")
        return None

    pasteBoardPDFDocument = Quartz.CGPDFDocumentCreateWithProvider(dataProvider)
    # Release the data provider now that the code is done with it.
    del dataProvider

    if pasteBoardPDFDocument is None:
        print("Couldn't create PDF document from pasteboard data provider.")
        return None
    return pasteBoardPDFDocument


_pdfDoc = None


def getPasteBoardPDFDoc(reset):
    global _pdfDoc
    if reset:
        # Release any existing document.
        _pdfDoc = createNewPDFRefFromPasteBoard()
    else:
        # If there isn't already one, create it fresh.
        if _pdfDoc is None:
            _pdfDoc = createNewPDFRefFromPasteBoard()
    return _pdfDoc


def drawPasteBoardPDF(context):
    pdfDoc = getPasteBoardPDFDoc(False)  # Obtain the existing one.
    if pdfDoc is None:
        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.0
    Quartz.CGContextDrawPDFDocument(context, pdfRect, pdfDoc, 1)
    # page 1


def cfDataCreatePDFDocumentFromCommand(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.
    info_dict = {}

    # Add the creator and title information to the PDF content.
    info_dict[Quartz.kCGPDFContextTitle] = "Pasted From Sample Quartz Application"
    info_dict[Quartz.kCGPDFContextCreator] = "Sample Quartz Application"

    # Create a mutable CFData object with unlimited capacity.
    data = Cocoa.CFDataCreateMutable(None, 0)
    if data is None:
        print("Couldn't make CFData!")
        return None

    # Create the data consumer to capture the PDF data.
    consumer = DataProvidersAndConsumers.myCGDataConsumerCreateWithCFData(data)
    if consumer is None:
        print("Couldn't create data consumer!")
        return None

    pdfContext, mediaRect = Quartz.CGPDFContextCreate(consumer, None, info_dict)
    del consumer
    del info_dict

    if pdfContext is None:
        print("Couldn't create pdf context!")
        return None

    mediaRect = Quartz.CGContextBeginPage(pdfContext)
    if 1:
        Quartz.CGContextSaveGState(pdfContext)
        if 1:
            Quartz.CGContextClipToRect(pdfContext, mediaRect)
            AppDrawing.DispatchDrawing(pdfContext, command)
        Quartz.CGContextRestoreGState(pdfContext)
    Quartz.CGContextEndPage(pdfContext)

    return data


def MakePDFDocument(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",
    }

    if 0:
        # Before using the kCGPDFContextCropBox key, check to ensure that it
        # is available.
        if hasattr(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)

    if url is not None:
        pdfContext = Quartz.CGPDFContextCreateWithURL(url, mediaRect, info)
        if pdfContext is not None:
            Quartz.CGContextBeginPage(pdfContext, mediaRect)
            if 1:
                Quartz.CGContextSaveGState(pdfContext)
                if 1:
                    Quartz.CGContextClipToRect(pdfContext, mediaRect)
                    AppDrawing.DispatchDrawing(pdfContext, exportInfo.command)
                Quartz.CGContextRestoreGState(pdfContext)
            Quartz.CGContextEndPage(pdfContext)
            del pdfContext
        else:
            print("Can't create PDF document!")

PathDrawing.py

import math

import Quartz


def doEgg(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)


def addRoundedRectToPath(context, rect, ovalWidth, ovalHeight):
    # If either ovalWidth or ovalHeight is 0, draw a regular rectangle.
    if ovalWidth == 0 or ovalHeight == 0:
        Quartz.CGContextAddRect(context, rect)
    else:
        Quartz.CGContextSaveGState(context)
        if 1:
            # 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 1
            Quartz.CGContextAddArcToPoint(context, fw, fh, fw / 2, fh, 0.5)
            # Segment 2
            Quartz.CGContextAddArcToPoint(context, 0, fh, 0, fh / 2, 0.5)
            # Segment 3
            Quartz.CGContextAddArcToPoint(context, 0, 0, fw / 2, 0, 0.5)
            # Segment 4
            Quartz.CGContextAddArcToPoint(context, fw, 0, fw, fh / 2, 0.5)
            # Closing the path adds the last segment.
            Quartz.CGContextClosePath(context)
        Quartz.CGContextRestoreGState(context)


def doRoundedRects(context):
    rect = Quartz.CGRectMake(10, 10, 210, 150)
    ovalWidth = 100
    ovalHeight = 100
    Quartz.CGContextSetLineWidth(context, 2.0)
    Quartz.CGContextBeginPath(context)
    addRoundedRectToPath(context, rect, ovalWidth, ovalHeight)
    Quartz.CGContextSetRGBStrokeColor(context, 1, 0, 0, 1)
    Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)


def doStrokeWithCTM(context):
    Quartz.CGContextTranslateCTM(context, 150.0, 180.0)
    Quartz.CGContextSetLineWidth(context, 10)
    # Draw ellipse 1 with a uniform stroke.
    Quartz.CGContextSaveGState(context)
    if 1:
        # 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, 0.0, 45.0, 0.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, -120
    Quartz.CGContextTranslateCTM(context, 220.0, 0.0)
    # Draw ellipse 2 with non-uniform stroke.
    Quartz.CGContextSaveGState(context)
    if 1:
        # 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, 0.0, 45.0, 0.0, 2 * math.pi, 0)
        # Stroke the path with the scaled coordinate system in effect.
        Quartz.CGContextStrokePath(context)
    Quartz.CGContextRestoreGState(context)


def doRotatedEllipsesWithCGPath(context):
    totreps = 144
    tint = 1.0
    tintIncrement = 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()
    if path is None:
        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, 0.0, 45.0, 0.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)
    for _ in range(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, 0.0, 1.0)
        # 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.0)


def alignPointToUserSpace(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.
    return Quartz.CGContextConvertPointToUserSpace(context, p)


def alignSizeToUserSpace(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.
    return Quartz.CGContextConvertSizeToUserSpace(context, s)


def alignRectToUserSpace(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.
    return Quartz.CGContextConvertRectToUserSpace(context, r)


def doPixelAlignedFillAndStroke(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.0, 0.0, 1.0)
    Quartz.CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0)

    # 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

import Quartz
import Utilities


def scalePatternPhase(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()
    if patternScaling != 1.0:
        phase = Quartz.CGSizeApplyAffineTransform(
            phase, Quartz.CGAffineTransformMakeScale(patternScaling, patternScaling)
        )

    return phase


def scalePatternMatrix(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()
    if patternScaling != 1.0:
        patternTransform = Quartz.CGAffineTransformConcat(
            patternTransform,
            Quartz.CGAffineTransformMakeScale(patternScaling, patternScaling),
        )

    return patternTransform


def myDrawRedBlackCheckerBoardPattern(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
    # macOS 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))


def createRedBlackCheckerBoardPattern(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,
    )
    return pattern


def doRedBlackCheckerboard(context):
    dash = [4]
    pattern = createRedBlackCheckerBoardPattern(
        Quartz.CGAffineTransformMakeScale(20, 20)
    )
    if pattern is None:
        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)
    if 1:
        # 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)


def doPatternMatrix(context):
    basePatternMatrix = Quartz.CGAffineTransformMakeScale(20, 20)
    pattern = createRedBlackCheckerBoardPattern(basePatternMatrix)
    if pattern is None:
        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)
    del patternColorSpace

    Quartz.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)
    if 1:
        # 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.
        del pattern
    Quartz.CGContextRestoreGState(context)

    Quartz.CGContextSaveGState(context)
    if 1:
        # 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.
        del pattern
        Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, 100, 100))
    Quartz.CGContextRestoreGState(context)

    Quartz.CGContextSaveGState(context)
    if 1:
        # 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.
        del pattern
        Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, 100, 100))
    Quartz.CGContextRestoreGState(context)


def doPatternPhase(context):
    pattern = createRedBlackCheckerBoardPattern(
        Quartz.CGAffineTransformMakeScale(20, 20)
    )
    if pattern is None:
        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 1
    Quartz.CGContextFillRect(context, Quartz.CGRectMake(20, 150, 100, 100))

    # Rectangle 2
    Quartz.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))


def drawRotatedRect(c, p):
    r = Quartz.CGRectMake(0, 0, 1, 1)
    Quartz.CGContextSaveGState(c)
    if 1:
        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)


def myStencilPatternProc(info, patternCellContext):
    drawRotatedRect(patternCellContext, Quartz.CGPointMake(1, 1))
    drawRotatedRect(patternCellContext, Quartz.CGPointMake(1.75, 1))


def createStencilPattern(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,
    )
    return pattern


def doStencilPattern(context):
    pattern = createStencilPattern(Quartz.CGAffineTransformMakeScale(20, 20))
    if pattern is None:
        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.
    del patternColorSpace

    # 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)
    if 1:
        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.75
    Quartz.CGContextSetFillPattern(context, pattern, color)
    Quartz.CGContextSetPatternPhase(
        context, scalePatternPhase(Quartz.CGSizeMake(140, 40))
    )
    Quartz.CGContextFillRect(context, Quartz.CGRectMake(140, 40, 105, 80))


class MyPDFPatternInfo:
    rect = None
    pdfDoc = None


def myDrawPDFPattern(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 = 1
SCALEPATTERN = 1
OPTIMIZEDPERF = 0


def createPDFPatternPattern(additionalTransformP, url):
    patternInfoP = MyPDFPatternInfo()

    patternInfoP.pdfDoc = Quartz.CGPDFDocumentCreateWithURL(url)
    if patternInfoP.pdfDoc is None:
        print("Couldn't create PDF document reference!")
        return

    patternInfoP.rect = Quartz.CGPDFDocumentGetMediaBox(patternInfoP.pdfDoc, 1)
    # Set the origin of the media rect for the PDF document to (0,0).
    patternInfoP.rect.origin = Quartz.CGPointZero

    if additionalTransformP is not None:
        patternTransform = additionalTransformP
    else:
        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.
    if SCALEPATTERN:
        tileOffsetX = 6.0 + patternInfoP.rect.size.width
        tileOffsetY = 6.0 + patternInfoP.rect.size.height
    else:
        tileOffsetX = 2.0 + patternInfoP.rect.size.width
        tileOffsetY = 2.0 + 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.
    if OPTIMIZEDPERF or TIGERSTEPWORKAROUND:
        patternRect = Quartz.CGRectMake(0, 0, tileOffsetX, tileOffsetY)
    else:
        patternRect = patternInfoP.rect

    if OPTIMIZEDPERF:
        # Produces best performance if bbox == xstep/ystep
        spacing = Quartz.kCGPatternTilingConstantSpacing
    else:
        spacing = Quartz.kCGPatternTilingConstantSpacingMinimalDistortion

    pattern = 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.
    if pattern is None:
        patternInfoP = None

    return pattern


def drawWithPDFPattern(context, url):
    if SCALEPATTERN:
        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)
    if pdfPattern is None:
        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.
    del patternColorSpace

    # 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.
    del pdfPattern

    # Fill a US Letter size rect with the pattern.
    Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, 612, 792))

QuartzTextDrawing.py

import Quartz
import Utilities


def drawQuartzRomanText(context):
    text = b"Quartz"
    textlen = len(text)
    fontSize = 60

    opaqueBlack = [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)

    for _ in range(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)


def myCGContextStrokeLineSegments(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.
    if hasattr(Quartz, "CGContextStrokeLineSegments"):
        Quartz.CGContextStrokeLineSegments(context, s, count)
    else:
        Quartz.CGContextBeginPath(context)
        for k in range(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 = []


def drawGridLines(context):
    numlines = 60

    if not _gridLines:
        stepsize = 4.0
        val = 0
        for _ in range(0, 2 * numlines, 2):
            _gridLines.append(Quartz.CGPointMake(val, -60))
            _gridLines.append(Quartz.CGPointMake(val, 200))
            val += stepsize

        val = -20
        for _ in range(2 * numlines, 4 * numlines, 2):
            _gridLines.append(Quartz.CGPointMake(0, val))
            _gridLines.append(Quartz.CGPointMake(400, val))
            val += stepsize

    myCGContextStrokeLineSegments(context, _gridLines, len(_gridLines))


def drawQuartzTextWithTextModes(context):
    fillText = b"Fill "
    strokeText = b"Stroke "
    fillAndStrokeText = b"FillStroke "
    invisibleText = b"Invisible "
    clipText = b"ClipText "
    fillStrokeClipText = b"FillStrokeClip "
    fontSize = 40.0
    extraLeading = 5.0
    dash = (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)
    if 1:
        # 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)
    if 1:
        # 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.
def showFlippedTextAtPoint(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)


def drawQuartzTextWithTextMatrix(context):
    fontSize = 60.0
    extraLeading = 10.0
    text = 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)
    if 1:
        # 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)
    if 1:
        # 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

import Quartz
import Utilities


def RedBlackRedRampEvaluate(info, input_value, output_value):
    # 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_value[0] * 2),
        # The green and blue components are always 0.
        0,
        0,
        # The alpha component is 1 for the entire shading.
        1,
    )


def createFunctionForRGB(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.

    function_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, function_range, evaluationFunction
    )

    if function is None:
        print("Couldn't create the CGFunction!")
        return None

    return function


def doSimpleAxialShading(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)
    if axialFunction is None:
        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 = False

    shading = 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.
    del axialFunction
    if shading is None:
        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)


def RedGreenRampEvaluate(info, input_value, output_value):
    # 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_value[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_value[0],
        # The blue component is always 0.
        0,
        # The alpha component is always 1, the shading is always opaque.
        1,
    )


def doExampleAxialShading(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)
    if redGreenFunction is None:
        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 = False
    shading = Quartz.CGShadingCreateAxial(
        Utilities.getTheCalibratedRGBColorSpace(),
        startPoint,
        endPoint,
        redGreenFunction,
        extendStart,
        extendEnd,
    )

    if shading is None:
        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)
    if 1:
        # 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.
        del shading
        # 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 = True
    shading = 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.
    del redGreenFunction
    if shading is None:
        print("Couldn't create the shading!")
        return

    # Stroke with the current stroke color.
    Quartz.CGContextStrokeRect(context, rect)

    Quartz.CGContextSaveGState(context)
    if 1:
        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)
    if 1:
        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.
    del shading


class MyStartEndColor:
    def __init__(self):
        self.startColor = [0.0] * 3
        self.endColor = [0.0] * 3


def StartColorEndColorEvaluate(info, input_value, output_value):
    # 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_value[0]) + info.endColor[0] * input_value[0]),
        (info.startColor[1] * (1 - input_value[0]) + info.endColor[1] * input_value[0]),
        (info.startColor[2] * (1 - input_value[0]) + info.endColor[2] * input_value[0]),
        # The alpha component is always 1, the shading is always opaque.
        1,
    )


def createFunctionWithStartEndColorRamp(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.

    function_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, function_range, StartColorEndColorEvaluate
    )

    if function is None:
        print("Couldn't create the CGFunction!")
        return None

    return function


def doSimpleRadialShading(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)
    if redYellowFunction is None:
        return

    Quartz.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 = 50
    circleBRadius = 100

    #  Don't extend the shading.
    extendStart = extendEnd = False
    shading = Quartz.CGShadingCreateRadial(
        Utilities.getTheCalibratedRGBColorSpace(),
        circleACenter,
        circleARadius,
        circleBCenter,
        circleBRadius,
        redYellowFunction,
        extendStart,
        extendEnd,
    )

    del redYellowFunction
    if shading is None:
        print("Couldn't create the shading!")
        return
    Quartz.CGContextDrawShading(context, shading)


def doExampleRadialShadings(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)
    if magentaFunction is None:
        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 = 0
    circleBRadius = 100

    # Don't extend the shading.
    extendStart = extendEnd = False
    shading = Quartz.CGShadingCreateRadial(
        Utilities.getTheCalibratedRGBColorSpace(),
        circleACenter,
        circleARadius,
        circleBCenter,
        circleBRadius,
        magentaFunction,
        extendStart,
        extendEnd,
    )
    # Finished with the magenta function so release it.
    del magentaFunction
    if shading is None:
        print("Couldn't create the shading!")
        return

    Quartz.CGContextDrawShading(context, shading)
    # Finished with the shading so release it.
    del shading

    # 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)
    if redGreenFunction is None:
        print("Couldn't create the red-Green function!")
        return

    circleACenter.x = 55
    circleACenter.y = 70
    circleBCenter.x = 20
    circleBCenter.y = 0
    circleARadius = 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 = False
    extendEnd = True
    shading = Quartz.CGShadingCreateRadial(
        Utilities.getTheCalibratedRGBColorSpace(),
        circleACenter,
        circleARadius,
        circleBCenter,
        circleBRadius,
        redGreenFunction,
        extendStart,
        extendEnd,
    )
    # Finished with this function so release it.
    del redGreenFunction
    if shading is None:
        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)
    if 1:
        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.
        del shading
    Quartz.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 = 0
    circleACenter.y = 0
    circleBCenter.x = 125
    circleBCenter.y = 0

    circleARadius = 50
    circleBRadius = 50

    extendStart = extendEnd = False

    # Create a function that paints a red to black ramp.
    redBlackFunction = createFunctionWithStartEndColorRamp(red, black)
    if redBlackFunction is None:
        print("Couldn't create the red-black function!")
        return

    shading = Quartz.CGShadingCreateRadial(
        Utilities.getTheCalibratedRGBColorSpace(),
        circleACenter,
        circleARadius,
        circleBCenter,
        circleBRadius,
        redBlackFunction,
        extendStart,
        extendEnd,
    )
    if shading is None:
        print("Couldn't create the shading!")
        return

    Quartz.CGContextDrawShading(context, shading)
    # Finished with the shading so release it.
    del shading

    # Shading 4. The starting circle is completely outside
    # the ending circle. The circles have different radii.
    circleACenter.x = 120
    circleACenter.y = 0
    circleBCenter.x = 0
    circleBCenter.y = 0

    circleARadius = 75
    circleBRadius = 30

    # Extend at the start and end.
    extendStart = extendEnd = True
    shading = Quartz.CGShadingCreateRadial(
        Utilities.getTheCalibratedRGBColorSpace(),
        circleACenter,
        circleARadius,
        circleBCenter,
        circleBRadius,
        redBlackFunction,
        extendStart,
        extendEnd,
    )
    # Finished with this function so release it.
    del redBlackFunction
    if shading is None:
        print("Couldn't create the shading!")
        return

    Quartz.CGContextSaveGState(context)
    if 1:
        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.
        del shading
    Quartz.CGContextRestoreGState(context)

    Quartz.CGContextTranslateCTM(context, 30, -200)

    # The starting color is blue, the ending color is a red-green color.
    blueGreenFunction = createFunctionWithStartEndColorRamp(blue, redgreen)
    if blueGreenFunction is None:
        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 = 0
    circleACenter.y = 0
    circleBCenter.x = 90
    circleBCenter.y = 30

    circleARadius = 75
    circleBRadius = 45

    extendStart = extendEnd = False
    shading = Quartz.CGShadingCreateRadial(
        Utilities.getTheCalibratedRGBColorSpace(),
        circleACenter,
        circleARadius,
        circleBCenter,
        circleBRadius,
        blueGreenFunction,
        extendStart,
        extendEnd,
    )
    if shading is None:
        print("Couldn't create the shading!")
        return

    Quartz.CGContextDrawShading(context, shading)
    # Finished with the shading so release it.
    del shading

    Quartz.CGContextTranslateCTM(context, 200, 0)

    # Shading 6. The circles partially overlap and have
    # different radii with the larger circle at the end.

    circleARadius = 45
    circleBRadius = 75
    shading = Quartz.CGShadingCreateRadial(
        Utilities.getTheCalibratedRGBColorSpace(),
        circleACenter,
        circleARadius,
        circleBCenter,
        circleBRadius,
        blueGreenFunction,
        extendStart,
        extendEnd,
    )
    # Finished with this function so release it.
    del blueGreenFunction
    if shading is None:
        print("Couldn't create the shading!")
        return

    Quartz.CGContextDrawShading(context, shading)


def doEllipseShading(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)
    if redBlackFunction is None:
        print("Couldn't create the red-black function!")
        return

    Quartz.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 = 45
    circleBRadius = 45

    # Don't extend this shading.
    extendStart = extendEnd = False

    shading = Quartz.CGShadingCreateRadial(
        Utilities.getTheCalibratedRGBColorSpace(),
        circleACenter,
        circleARadius,
        circleBCenter,
        circleBRadius,
        redBlackFunction,
        extendStart,
        extendEnd,
    )
    if shading is None:
        # Couldn't create the shading so release
        # the function before returning.
        print("Couldn't create the shading!")
        return

    Quartz.CGContextSaveGState(context)
    if 1:
        # 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)
        del shading
    Quartz.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.
    del redBlackFunction
    if shading is None:
        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

import Quartz
import Utilities


def scaleShadowOffset(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.
    if shadowScaling != 1.0:
        offset = Quartz.CGSizeApplyAffineTransform(
            offset, Quartz.CGAffineTransformMakeScale(shadowScaling, shadowScaling)
        )
    return offset


def createTrianglePath(context):
    Quartz.CGContextBeginPath(context)
    Quartz.CGContextMoveToPoint(context, 0, 0)
    Quartz.CGContextAddLineToPoint(context, 50, 0)
    Quartz.CGContextAddLineToPoint(context, 25, 50)
    Quartz.CGContextClosePath(context)


def drawSimpleShadow(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 = 3
    Quartz.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 = -5
    offset.height = +7
    offset = 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 = -7
    offset.height = -7

    offset = 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)


def doShadowScaling(context):
    offset = Quartz.CGSize(-7, -7)
    blur = 3

    Quartz.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)


def drawFillAndStrokeWithShadow(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)
    if 1:
        Utilities.myCGContextAddEllipseInRect(context, r)
        Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
    # End the transparency layer.
    Quartz.CGContextEndTransparencyLayer(context)


def drawColoredLogo(context):
    r = Quartz.CGRectMake(0, 0, 100, 100)
    Quartz.CGContextSaveGState(context)
    if 1:
        # 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)


def showComplexShadowIssues(context):
    offset = Quartz.CGSize(-6, -6)
    blur = 3

    # Set the shadow.
    Quartz.CGContextSetShadow(context, scaleShadowOffset(offset), blur)
    # Draw the colored logo.
    drawColoredLogo(context)


def showComplexShadow(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)


def doLayerCompositing(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)
    if 1:
        # 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)


def shadowPDFDocument(context, url):
    pdfDoc = Quartz.CGPDFDocumentCreateWithURL(url)
    offset = Quartz.CGSize(-7, -7)

    if pdfDoc is None:
        print("Couldn't create PDF document reference!")
        return

    r = Quartz.CGPDFDocumentGetMediaBox(pdfDoc, 1)
    r.origin.x = 20
    r.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)
    if 1:
        Quartz.CGContextDrawPDFDocument(context, r, pdfDoc, 1)

    Quartz.CGContextEndTransparencyLayer(context)

UIHandling.py

# ***    Drawing Basics ***

kHICommandSimpleRect = 1000
kHICommandStrokedRect = 1001
kHICommandStrokedAndFilledRect = 1002
kHICommandPathRects = 1003
kHICommandAlphaRects = 1004
kHICommandDashed = 1005
kHICommandSimpleClip = 1006
kHICommandPDFDoc = 1007

# ***    Coordinate System ***
kHICommandRotatedEllipses = 1008
kHICommandDrawSkewCoordinates = 1082

# ***    Path Drawing ***
kHICommandBezierEgg = 1009
kHICommandRoundedRects = 1010
kHICommandStrokeWithCTM = 1011
kHICommandRotatedEllipsesWithCGPath = 1012
kHICommandPixelAligned = 1099

# ***    Color and GState ***
kHICommandDeviceFillAndStrokeColor = 1013
kHICommandCLUTDrawGraphics = 1014
kHICommandDrawWithGlobalAlpha = 1015
kHICommandDrawWithBlendMode = 1068
kHICommandDrawWithColorRefs = 1016
kHICommandFunctionsHaveOwnGSave = 1017

# ***    Images ***
kHICommandDrawJPEGImage = 1018
kHICommandColorImageFromFile = 1019
kHICommandColorImageFromData = 1020
kHICommandColorImageFromCallbacks = 1021
kHICommandGrayRamp = 1022
kHICommandDrawWithCGImageSource = 1023
kHICommandDrawWithCGImageSourceIncremental = 1024
kHICommandDrawWithQuickTime = 1025
kHICommandSubstituteImageProfile = 1026
kHICommandDoSubImage = 1027
kHICommandExportWithQuickTime = 1028

# ***    Image Masking ***
kHICommandMaskTurkeyImage = 1029
kHICommandImageMaskedWithMask = 1030
kHICommandImageMaskedWithGrayImage = 1031
kHICommandMaskImageWithColor = 1033
kHICommandClipToMask = 1080
kHICommandExportWithCGImageDestination = 1034

# *** Bitmap Graphics Context and CGLayerRef ***
kHICommandSimpleCGLayer = 1090
kHICommandAlphaOnlyContext = 1091
kHICommandDrawNoOffScreenImage = 1035
kHICommandDrawOffScreenImage = 1036
kHICommandDrawWithLayer = 1037

# ***    Text ***
kHICommandQuartzRomanText = 1038
kHICommandQuartzTextModes = 1039
kHICommandQuartzTextMatrix = 1040

kHICommandDrawNSString = 1041
kHICommandDrawNSLayoutMgr = 1042
kHICommandDrawCustomNSLayoutMgr = 1043

# ***    Advanced Drawing ***
kHICommandSimplePattern = 1050
kHICommandPatternMatrix = 1051
kHICommandPatternPhase = 1052
kHICommandUncoloredPattern = 1053
kHICommandDrawWithPDFPattern = 1054

kHICommandSimpleShadow = 1055
kHICommandShadowScaling = 1056
kHICommandShadowProblems = 1057
kHICommandComplexShadow = 1058

kHICommandMultipleShapeComposite = 1059
kHICommandFillAndStrokeWithShadow = 1085
kHICommandPDFDocumentShadow = 1060

kHICommandSimpleAxialShading = 1061
kHICommandExampleAxialShadings = 1062
kHICommandSimpleRadialShading = 1063
kHICommandExampleRadialShadings = 1064
kHICommandEllipseShading = 1065

# *** EPS drawing ***
kHICommandDoCompatibleEPS = 1066

Utilities.py

import math

import Cocoa
import Quartz


class ExportInfo:
    command = None
    fileType = None
    useQTForExport = False
    dpi = 1


def DEGREES_TO_RADIANS(degrees):
    return degrees * math.pi / 180


gScalingFactor = 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.
def setScalingFactor(scalingFactor):
    global gScalingFactor
    if scalingFactor > 0:
        gScalingFactor = scalingFactor


def getScalingFactor():
    return gScalingFactor


_appBundle = None


def getAppBundle():
    global _appBundle

    if _appBundle is None:
        _appBundle = Cocoa.CFBundleGetMainBundle()

    return _appBundle


#
#    This version of getTheRGBColorSpace returns
#    the DeviceRGB color space.
#
_deviceRGB = None


def getTheRGBColorSpace():
    global _deviceRGB

    # Set once, the first time this function is called.
    if _deviceRGB is None:
        _deviceRGB = Quartz.CGColorSpaceCreateDeviceRGB()

    return _deviceRGB


_genericRGBColorSpace = None


def getTheCalibratedRGBColorSpace():
    global _genericRGBColorSpace

    if _genericRGBColorSpace is None:
        _genericRGBColorSpace = Quartz.CGColorSpaceCreateWithName(
            Quartz.kCGColorSpaceGenericRGB
        )

    return _genericRGBColorSpace


_genericGrayColorSpace = None


def getTheCalibratedGrayColorSpace():
    global _genericGrayColorSpace

    if _genericGrayColorSpace is None:
        _genericGrayColorSpace = Quartz.CGColorSpaceCreateWithName(
            Quartz.kCGColorSpaceGenericGray
        )
    return _genericGrayColorSpace


_genericSRGBColorSpace = None


def getTheSRGBColorSpace():
    # This only works on 10.5 or later
    global _genericSRGBColorSpace

    if _genericSRGBColorSpace is None:
        _genericSRGBColorSpace = Quartz.CGColorSpaceCreateWithName(
            Quartz.kCGColorSpaceGenericRGB
        )  # XXX: should be GenericSRGB
    return _genericSRGBColorSpace


def getTheDisplayColorSpace():
    # This is a hack, basically here because the C implementation uses APIs that
    # aren't wrapped yet.
    return getTheRGBColorSpace()


_rgbWhite = None


def getRGBOpaqueWhiteColor():
    global _rgbWhite

    if _rgbWhite is None:
        opaqueWhite = (1.0, 1.0, 1.0, 1.0)
        _rgbWhite = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueWhite)
    return _rgbWhite


_rgbBlack = None


def getRGBOpaqueBlackColor():
    global _rgbBlack
    if _rgbBlack is None:
        opaqueBlack = (0, 0, 0, 1)
        _rgbBlack = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueBlack)
    return _rgbBlack


_rgbGray = None


def getRGBOpaqueGrayColor():
    global _rgbGray
    if _rgbGray is None:
        opaqueGray = (0.9, 0.9, 0.9, 1)
        _rgbGray = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueGray)
    return _rgbGray


_rgbRed = None


def getRGBOpaqueRedColor():
    global _rgbRed
    if _rgbRed is None:
        opaqueRed = (0.663, 0, 0.031, 1)
        _rgbRed = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueRed)
    return _rgbRed


_rgbBlue = None


def getRGBOpaqueBlueColor():
    global _rgbBlue
    if _rgbBlue is None:
        opaqueBlue = (0.482, 0.62, 0.871, 1)
        _rgbBlue = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueBlue)
    return _rgbBlue


_rgbPurple = None


def getRGBOpaquePurpleColor():
    global _rgbPurple
    if _rgbPurple is None:
        opaquePurple = (0.69, 0.486, 0.722, 1)
        _rgbPurple = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaquePurple)
    return _rgbPurple


_rgbDarkBlue = None


def getRGBOpaqueDarkBlueColor():
    global _rgbDarkBlue
    if _rgbDarkBlue is None:
        opaqueDarkBlue = (0.11, 0.208, 0.451, 1)
        _rgbDarkBlue = Quartz.CGColorCreate(
            getTheCalibratedRGBColorSpace(), opaqueDarkBlue
        )
    return _rgbDarkBlue


_rgbBrown = None


def getRGBOpaqueBrownColor():
    global _rgbBrown
    if _rgbBrown is None:
        opaqueBrown = (0.325, 0.208, 0.157, 1)
        _rgbBrown = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueBrown)
    return _rgbBrown


_rgbOrange = None


def getRGBOpaqueOrangeColor():
    global _rgbOrange
    if _rgbOrange is None:
        opaqueOrange = (0.965, 0.584, 0.059, 1)
        _rgbOrange = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueOrange)
    return _rgbOrange


_rgbYellow = None


def getRGBOpaqueYellowColor():
    global _rgbYellow
    if _rgbYellow is None:
        opaqueYellow = (1, 0.816, 0, 1)
        _rgbYellow = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueYellow)
    return _rgbYellow


_rgbGreen = None


def getRGBOpaqueGreenColor():
    global _rgbGreen
    if _rgbGreen is None:
        opaqueGreen = (0.584, 0.871, 0.318, 1)
        _rgbGreen = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueGreen)
    return _rgbGreen


_rgbDarkGreen = None


def getRGBOpaqueDarkGreenColor():
    global _rgbDarkGreen
    if _rgbDarkGreen is None:
        opaqueDarkGreen = (0.404, 0.808, 0.239, 1)
        _rgbDarkGreen = Quartz.CGColorCreate(
            getTheCalibratedRGBColorSpace(), opaqueDarkGreen
        )
    return _rgbDarkGreen


def myCGContextAddEllipseInRect(context, r):
    if hasattr(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)
        if 1:
            # 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.


def drawPoint(context, p):
    Quartz.CGContextSaveGState(context)
    if 1:
        # 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)


def printCTM(context):
    t = Quartz.CGContextGetCTM(context)
    print(f"CurrentCTM is {t!r}")


kTickLength = 5
kTickDistance = 72
kAxesLength = 20 * kTickDistance


def drawCoordinateAxes(context):
    tickLength = kTickLength

    Quartz.CGContextSaveGState(context)
    if 1:
        Quartz.CGContextBeginPath(context)
        # Paint the x-axis in red.
        Quartz.CGContextSetRGBStrokeColor(context, 1, 0, 0, 1)
        Quartz.CGContextMoveToPoint(context, -kTickLength, 0.0)
        Quartz.CGContextAddLineToPoint(context, kAxesLength, 0.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)
        for _ in range(2):
            for t in range(0, kAxesLength, kTickDistance):
                Quartz.CGContextMoveToPoint(context, t, -tickLength)
                Quartz.CGContextAddLineToPoint(context, t, tickLength)

            Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)
            Quartz.CGContextRotateCTM(context, math.pi / 2.0)
            # Paint the y-axis tick marks in blue.
            Quartz.CGContextSetRGBStrokeColor(context, 0, 0, 1, 1)

        drawPoint(context, Quartz.CGPointZero)
    Quartz.CGContextRestoreGState(context)


def drawDebuggingRect(context, rect):
    Quartz.CGContextSaveGState(context)
    if 1:
        Quartz.CGContextSetLineWidth(context, 4.0)
        # 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.0)
        Quartz.CGContextStrokeRect(context, rect)
    Quartz.CGContextRestoreGState(context)

main.py

# Make sure all code is loaded
import AppDrawing  # noqa: F401
import BitmapContext  # noqa: F401
import ColorAndGState  # noqa: F401
import CoordinateSystem  # noqa: F401
import DataProvidersAndConsumers  # noqa: F401
import DrawingBasics  # noqa: F401
import EPSPrinting  # noqa: F401
import FrameworkTextDrawing  # noqa: F401
import FrameworkUtilities  # noqa: F401
import ImageMasking  # noqa: F401
import Images  # noqa: F401
import MyAppController  # noqa: F401
import MyView  # noqa: F401
import PathDrawing  # noqa: F401
import PatternDrawing  # noqa: F401
import PDFHandling  # noqa: F401
import QuartzTextDrawing  # noqa: F401
import Shadings  # noqa: F401
import ShadowsAndTransparencyLayers  # noqa: F401
import Utilities  # noqa: F401
from PyObjCTools import AppHelper

AppHelper.runEventLoop()

setup.py

"""
Script for building the example.

Usage:
    python3 setup.py py2app
"""

import os

from setuptools import setup

setup(
    name="BasicDrawing",
    app=["main.py"],
    data_files=["English.lproj"]
    + [os.path.join("GraphicsFiles", fn) for fn in os.listdir("GraphicsFiles")],
    setup_requires=["py2app", "pyobjc-framework-Cocoa", "pyobjc-framework-Quartz"],
)