1from Quartz import * 2import Quartz 3import objc 4 5import Utilities 6import BitmapContext 7 8import sys 9 10# We're using a function that isn't made available through a wrapper, just 11# load it manually: 12if not hasattr(Quartz, 'PMCGImageCreateWithEPSDataProvider'): 13 functions = [ 14 ('PMCGImageCreateWithEPSDataProvider', '@@@'), 15 ] 16 import AppKit 17 d = {} 18 objc.loadBundleFunctions(AppKit.__bundle__, d, functions) 19 20 if 'PMCGImageCreateWithEPSDataProvider' in d: 21 PMCGImageCreateWithEPSDataProvider=d['PMCGImageCreateWithEPSDataProvider'] 22 else: 23 print >>sys.stderr, "PMCGImageCreateWithEPSDataProvider doesn't exist" 24 25def getEPSBBox(epspath): 26 try: 27 fp = open(epspath, 'rU') 28 except IOError, msg: 29 return CGRectZero 30 31 try: 32 # This is a VERY poor man's EPS DSC parser, here just so that 33 # this sample code can handle simple EPS files. It is 34 # simple but very inefficient. In addition it does not ensure 35 # that the DSC comments are at the beginning of a line, 36 # nor does it handle (atend) style comments at all. 37 # It will simply find the first occurance of a 38 # %%BoundingBox comment and if it is of the typical 39 # form, it will obtain the bounding box data. 40 # 41 for ln in fp: 42 if ln.startswith("%%BoundingBox:"): 43 fields = ln.split()[1:] 44 if len(fields) >= 4: 45 llx = int(fields[0]) 46 lly = int(fields[1]) 47 urx = int(fields[2]) 48 ury = int(fields[3]) 49 return CGRectMake(llx, lly, urx - llx, ury - lly) 50 finally: 51 fp.close() 52 53 return CGRectZero 54 55def createEPSPreviewImage(url): 56 # The CGImage used as the preview needs to have the 57 # same width and height as the EPS data it will 58 # be associated with. This sample code doesn't attempt 59 # to use any preview image associated with the EPS 60 # data but instead simply draws a box of an appropriate 61 # size. Your code would most likely create an image 62 # that reflects a PICT or TIFF preview present in the 63 # EPS data. 64 result, path = CFURLGetFileSystemRepresentation(url, True, None, 1024) 65 if not result: 66 print >>sys.stderr, "Couldn't get the path for EPS file!" 67 return None 68 69 path = path.rstrip('\0') 70 71 epsRect = getEPSBBox(path) 72 # Check whether the EPS bounding box is empty. 73 if epsRect == CGRectZero: 74 print >>sys.stderr, "Couldn't find BoundingBox comment!" 75 return None 76 77 wantDisplayColorSpace = False 78 needsTransparentBitmap = True 79 # Create a bitmap context to draw to in order to 80 # create the preview image. Use the routine 81 # createRGBBitmapContext from the earlier chapter. 82 bitmapContext = BitmapContext.createRGBBitmapContext( 83 epsRect.size.width, 84 epsRect.size.height, 85 wantDisplayColorSpace, 86 needsTransparentBitmap) 87 if bitmapContext is None: 88 print >>sys.stderr, "Couldn't create bitmap context" 89 return None 90 91 epsRect.origin.x = epsRect.origin.y = 0 92 # Draw the contents of the preview. The preview consists 93 # of two lines and a stroke around the bounding box. One 94 # of the two lines is drawn from the lower-left corner to 95 # the upper-right corner of the bounding box and the other 96 # line is from the lower-right corner to the upper-left 97 # corner of the bounding box. 98 CGContextBeginPath(bitmapContext) 99 CGContextMoveToPoint(bitmapContext, 0, 0) 100 CGContextAddLineToPoint(bitmapContext, epsRect.size.width, epsRect.size.height) 101 CGContextMoveToPoint(bitmapContext, epsRect.size.width, 0) 102 CGContextAddLineToPoint(bitmapContext, 0, epsRect.size.height) 103 CGContextStrokePath(bitmapContext) 104 # Stroke the bounding rectangle, inset so that the stroke is 105 # completely contained in the EPS bounding rect. 106 CGContextStrokeRect(bitmapContext, CGRectInset(epsRect, 0.5, 0.5)) 107 108 # Now create an image from the bitmap raster data. This image 109 # has a data provider that releases the image raster data when 110 # the image is released. Use the createImageFromBitmapContext 111 # from Chapter 12. Calling createImageFromBitmapContext 112 # gives up ownership of the raster data used by the context. 113 epsPreviewImage = BitmapContext.createImageFromBitmapContext(bitmapContext) 114 115 if epsPreviewImage is None: 116 print >>sys.stderr, "Couldn't create preview image!" 117 return None 118 119 return epsPreviewImage 120 121# This technique of handling EPS data is available in 122# Mac OS X v10.1 and later and is one alternative method 123# of supporting EPS data during printing as compared to 124# converting EPS data to PDF data using CGPSConverter which 125# is only available in Panther and later. 126def createCGEPSImage(url): 127 previewImage = createEPSPreviewImage(url) 128 if previewImage is None: 129 print >>sys.stderr, "Couldn't create EPS preview!" 130 return None 131 132 # It is important that the data provider supplying the 133 # EPS data conform to the Quartz guidelines for data providers 134 # and is able to provide the data until the data releaser function 135 # is called. If you have a custom data provider, you need 136 # to follow these guidelines since your data provider 137 # is not necessarily called before you release the image 138 # that uses the provider. 139 epsDataProvider = CGDataProviderCreateWithURL(url) 140 if epsDataProvider is None: 141 print >>sys.stderr, "Couldn't create EPS data provider!" 142 return None 143 144 # Create the hybrid CGImage that contains the preview image 145 # and the EPS data. Note that the data provider isn't 146 # called during image creation but at some later point in time. 147 148 149 epsImage = PMCGImageCreateWithEPSDataProvider(epsDataProvider, previewImage) 150 # The preview image and data provider are no longer needed 151 # because Quartz retains them and this code doesn't 152 # require them further. 153 del previewImage 154 del epsDataProvider 155 156 if epsImage is None: 157 print >>sys.stderr, "Couldn't create EPS hybrid image!" 158 return None 159 160 return epsImage 161 162def drawEPSDataImage(context, url): 163 # Create the a CGImage that has EPS data associated with it. 164 epsDataImage = createCGEPSImage(url) 165 if epsDataImage is None: 166 return 167 168 # Create a destination rectangle at the location 169 # to draw the EPS document. The size of the rect is scaled 170 # down to 1/2 the size of the EPS graphic. 171 destinationRect = CGRectMake(100, 100, 172 CGImageGetWidth(epsDataImage), 173 CGImageGetHeight(epsDataImage)) 174 # Draw the image to the destination. When the EPS 175 # data associated with the image is sent to a PostScript 176 # printer, the EPS bounding box is mapped to this 177 # destination rectangle, translated and scaled as necessary. 178 CGContextDrawImage(context, destinationRect, epsDataImage) 179 180 # Draw the image a second time. This time the image is 181 # rotated by 45 degrees and scaled by an additional scaling factor 182 # of 0.5 in the x dimension. The center point of this image coincides 183 # with the center point of the earlier drawing. 184 CGContextTranslateCTM(context, 185 destinationRect.origin.x + destinationRect.size.width/2, 186 destinationRect.origin.y + destinationRect.size.height/2) 187 CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(45)) 188 CGContextScaleCTM(context, 0.5, 1) 189 CGContextTranslateCTM(context, 190 -(destinationRect.origin.x + destinationRect.size.width/2), 191 -(destinationRect.origin.y + destinationRect.size.height/2) ) 192 CGContextDrawImage(context, destinationRect, epsDataImage) 193