from Quartz import *
import Quartz
import objc

import Utilities
import BitmapContext

import sys

# We're using a function that isn't made available through a wrapper, just
# load it manually:
if not hasattr(Quartz, 'PMCGImageCreateWithEPSDataProvider'):
    functions = [
        ('PMCGImageCreateWithEPSDataProvider', '@@@'),
    ]
    import AppKit
    d = {}
    objc.loadBundleFunctions(AppKit.__bundle__, d, functions)

    if 'PMCGImageCreateWithEPSDataProvider' in d:
        PMCGImageCreateWithEPSDataProvider=d['PMCGImageCreateWithEPSDataProvider']
    else:
        print >>sys.stderr, "PMCGImageCreateWithEPSDataProvider doesn't exist"

def getEPSBBox(epspath):
    try:
        fp = open(epspath, 'rU')
    except IOError, msg:
        return 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 occurance 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 CGRectMake(llx, lly, urx - llx, ury - lly)
    finally:
        fp.close()

    return 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 = CFURLGetFileSystemRepresentation(url, True, None, 1024)
    if not result:
        print >>sys.stderr, "Couldn't get the path for EPS file!"
        return None

    path = path.rstrip('\0')
    
    epsRect = getEPSBBox(path)
    # Check whether the EPS bounding box is empty.
    if epsRect == CGRectZero:
        print >>sys.stderr, "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 >>sys.stderr, "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.
    CGContextBeginPath(bitmapContext)
    CGContextMoveToPoint(bitmapContext, 0, 0)
    CGContextAddLineToPoint(bitmapContext, epsRect.size.width, epsRect.size.height)
    CGContextMoveToPoint(bitmapContext, epsRect.size.width, 0)
    CGContextAddLineToPoint(bitmapContext, 0, epsRect.size.height)
    CGContextStrokePath(bitmapContext)
    # Stroke the bounding rectangle, inset so that the stroke is
    # completely contained in the EPS bounding rect.
    CGContextStrokeRect(bitmapContext, 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 >>sys.stderr, "Couldn't create preview image!"
        return None
    
    return epsPreviewImage

# This technique of handling EPS data is available in 
# Mac OS X v10.1 and later and is one alternative method
# of supporting EPS data during printing as compared to 
# converting EPS data to PDF data using CGPSConverter which 
# is only available in Panther and later. 
def createCGEPSImage(url):
    previewImage = createEPSPreviewImage(url)
    if previewImage is None:
        print >>sys.stderr, "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 = CGDataProviderCreateWithURL(url)
    if epsDataProvider is None:
        print >>sys.stderr, "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 >>sys.stderr, "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 = CGRectMake(100, 100, 
			CGImageGetWidth(epsDataImage), 
			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.
    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.
    CGContextTranslateCTM(context, 
	    destinationRect.origin.x + destinationRect.size.width/2, 
	    destinationRect.origin.y + destinationRect.size.height/2)
    CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(45))
    CGContextScaleCTM(context, 0.5, 1)
    CGContextTranslateCTM(context, 
	    -(destinationRect.origin.x + destinationRect.size.width/2), 
	    -(destinationRect.origin.y + destinationRect.size.height/2) )
    CGContextDrawImage(context, destinationRect, epsDataImage)
