1""" 2Add a watermark to all pages in a PDF document 3""" 4import sys, math, os 5 6from Quartz import * 7from Foundation import * 8 9def usage(name): 10 print >>sys.stderr, "Usage %s [inputfile]"%(name,) 11 12 13class MyPDFData (object): 14 pdfDoc = None 15 mediaRect = None 16 17# This is a simple function to create a CFURLRef from 18# a path to a file. The path can be relative to the 19# current directory or an absolute path. 20def createURL(path): 21 return CFURLCreateFromFileSystemRepresentation(None, path, 22 len(path), False) 23 24# For the supplied URL and media box, create a PDF context 25# that creates a PDF file at that URL and uses supplied rect 26# as its document media box. 27def myCreatePDFContext(url, mediaBox): 28 dict = {} 29 dict[kCGPDFContextCreator] = "PDF Stamper Application" 30 31 pdfContext = CGPDFContextCreateWithURL(url, mediaBox, dict) 32 return pdfContext 33 34# For a URL corresponding to an existing PDF document on disk, 35# create a CGPDFDocumentRef and obtain the media box of the first 36# page. 37def myCreatePDFSourceDocument(url): 38 myPDFData = MyPDFData() 39 myPDFData.pdfDoc = CGPDFDocumentCreateWithURL(url) 40 if myPDFData.pdfDoc is not None: 41 # NOTE: the original code uses CGPDFDocumentGetMediaBox, but that 42 # API is deprecated and doesn't work in Leopard. 43 page = CGPDFDocumentGetPage(myPDFData.pdfDoc, 1) 44 myPDFData.mediaRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox) 45 46 # Make the media rect origin at 0,0. 47 myPDFData.mediaRect.origin.x = myPDFData.mediaRect.origin.y = 0.0 48 49 return myPDFData 50 51# Draw the source PDF document into the context and then draw the stamp PDF document 52# on top of it. When drawing the stamp on top, place it along the diagonal from the lower 53# left corner to the upper right corner and center its media rect to the center of that 54# diagonal. 55def StampWithPDFDocument(context, 56 sourcePDFDoc, 57 stampFileDoc, stampMediaRect): 58 numPages = CGPDFDocumentGetNumberOfPages(sourcePDFDoc) 59 60 # Loop over document pages and stamp each one appropriately. 61 for i in range(1, numPages+1): 62 # Use the page rectangle of each page from the source to compute 63 # the destination media box for each page and the location of 64 # the stamp. 65 66 # NOTE: the original code uses CGPDFDocumentGetMediaBox, but that 67 # API is deprecated and doesn't work in Leopard. 68 page = CGPDFDocumentGetPage(sourcePDFDoc, i) 69 pageRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox) 70 71 CGContextBeginPage(context, pageRect) 72 CGContextSaveGState(context) 73 # Clip to the media box of the page. 74 CGContextClipToRect(context, pageRect) 75 # First draw the content of the source document. 76 CGContextDrawPDFDocument(context, pageRect, sourcePDFDoc, i) 77 # Translate to center of destination rect, that is the center of 78 # the media box of content to draw on top of. 79 CGContextTranslateCTM(context, 80 pageRect.size.width/2, pageRect.size.height/2) 81 # Compute angle of the diagonal across the destination page. 82 angle = math.atan(pageRect.size.height/pageRect.size.width) 83 # Rotate by an amount so that drawn content goes along a diagonal 84 # axis across the page. 85 CGContextRotateCTM(context, angle) 86 # Move the origin so that the media box of the PDF to stamp 87 # is centered around center point of destination. 88 CGContextTranslateCTM(context, 89 -stampMediaRect.size.width/2, 90 -stampMediaRect.size.height/2) 91 # Now draw the document to stamp with on top of original content. 92 CGContextDrawPDFDocument(context, stampMediaRect, 93 stampFileDoc, 1) 94 CGContextRestoreGState(context) 95 CGContextEndPage(context) 96 97# From an input PDF document and a PDF document whose contents you 98# want to draw on top of the other, create a new PDF document 99# containing all the pages of the input document with the first page 100# of the "stamping" overlayed. 101def createStampedFileWithFile(inURL, stampURL, outURL): 102 sourceFileData = myCreatePDFSourceDocument(inURL) 103 if sourceFileData.pdfDoc is None: 104 print >>sys.stderr, "Can't create PDFDocumentRef for source input file!" 105 return 106 107 stampFileData = myCreatePDFSourceDocument(stampURL) 108 if stampFileData.pdfDoc is None: 109 CGPDFDocumentRelease(sourceFileData.pdfDoc); 110 print >>sys.stderr, "Can't create PDFDocumentRef for file to stamp with!" 111 return 112 113 pdfContext = myCreatePDFContext(outURL, sourceFileData.mediaRect) 114 if pdfContext is None: 115 print >>sys.stderr, "Can't create PDFContext for output file!" 116 return 117 118 StampWithPDFDocument(pdfContext, sourceFileData.pdfDoc, 119 stampFileData.pdfDoc, stampFileData.mediaRect) 120 121def main(args = None): 122 if args is None: 123 args = sys.argv 124 125 suffix = ".watermarked.pdf"; 126 stampFileName = os.path.join( 127 os.path.dirname(__file__), "confidential.pdf") 128 129 if len(args) != 2: 130 usage(args[0]) 131 return 1 132 133 inputFileName = args[1]; 134 outputFileName = os.path.splitext(inputFileName)[0] + suffix 135 136 inURL = createURL(inputFileName); 137 if inURL is None: 138 print >>sys.stderr, "Couldn't create URL for input file!" 139 return 1 140 141 outURL = createURL(outputFileName) 142 if outURL is None: 143 print >>sys.stderr, "Couldn't create URL for output file!" 144 return 1 145 146 stampURL = createURL(stampFileName) 147 if stampURL is None: 148 print >>sys.stderr, "Couldn't create URL for stamping file!" 149 return 1 150 151 createStampedFileWithFile(inURL, stampURL, outURL) 152 return 0 153 154if __name__ == "__main__": 155 sys.exit(main()) 156