Automator Watermark PDF Workflow

Seems with every major upgrade, Apple breaks the Automator Watermark PDF Workflow.

Can someone test this workflow in Yosemite so I know if the problem I'm having is with OS X 10.10.

OS X Yosemite (10.10)

Posted on Oct 22, 2014 9:43 AM

Reply
Question marked as Top-ranking reply

Posted on Oct 23, 2014 3:11 AM

Hello


Under 10.6.8, Watermark PDF Documents.action/Contents/Resources/tool.py works happily without any errors when invoked as follows with a.pdf and watermark.png in ~/desktop/test/ :


#!/bin/bash py='/System/Library/Automator/Watermark PDF Documents.action/Contents/Resources/tool.py' cd ~/desktop/test || exit args=( --input a.pdf --output a_wm.pdf --verbose --over --xOffset 0.0 --yOffset -150.0 --angle 300.0 --scale 0.3 --opacity 0.1 watermark.png ) "$py" "${args[@]}"



If the said error – ValueError: depythonifying 'pointer', got 'str' – is raised at the line:


provider = CGDataProviderCreateWithFilename(imagePath)


I'd think it is because imagePath is not a C string pointer which the function expects but a CFStringRef or something which is implicitly converted from python string. Since I don't get this error with pyobjc 2.2b3 & python 2.6 under 10.6.8, it is caused by something introduced in later versions.


Anyway, the statement in question may be replaced with the following statements if it helps:


url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, imagePath, len(imagePath), False) provider = CGDataProviderCreateWithURL(url)



I modified the tool.py with these changes along with other minor fixes and run it successfully under 10.6.8. I'm not sure at all whether it works under later OSes as well. And even if it does, editing tool.py will require the Automator action to be re-codesigned.


Here's the revised tool.py.


#!/usr/bin/python # Watermark each page in a PDF document import sys #, os import getopt import math from Quartz.CoreGraphics import * from Quartz.ImageIO import * def drawWatermark(ctx, image, xOffset, yOffset, angle, scale, opacity): if image: imageWidth = CGImageGetWidth(image) imageHeight = CGImageGetHeight(image) imageBox = CGRectMake(0, 0, imageWidth, imageHeight) CGContextSaveGState(ctx) CGContextSetAlpha(ctx, opacity) CGContextTranslateCTM(ctx, xOffset, yOffset) CGContextScaleCTM(ctx, scale, scale) CGContextTranslateCTM(ctx, imageWidth / 2, imageHeight / 2) CGContextRotateCTM(ctx, angle * math.pi / 180) CGContextTranslateCTM(ctx, -imageWidth / 2, -imageHeight / 2) CGContextDrawImage(ctx, imageBox, image) CGContextRestoreGState(ctx) def createImage(imagePath): image = None # provider = CGDataProviderCreateWithFilename(imagePath) # FIXED: replaced by the following CGDataProviderCreateWithURL() url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, imagePath, len(imagePath), False) provider = CGDataProviderCreateWithURL(url) if provider: imageSrc = CGImageSourceCreateWithDataProvider(provider, None) if imageSrc: image = CGImageSourceCreateImageAtIndex(imageSrc, 0, None) if not image: print "Cannot import the image from file %s" % imagePath return image def watermark(inputFile, watermarkFiles, outputFile, under, xOffset, yOffset, angle, scale, opacity, verbose): images = map(createImage, watermarkFiles) ctx = CGPDFContextCreateWithURL(CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, outputFile, len(outputFile), False), None, None) if ctx: pdf = CGPDFDocumentCreateWithURL(CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, inputFile, len(inputFile), False)) if pdf: for i in range(1, CGPDFDocumentGetNumberOfPages(pdf) + 1): image = images[i % len(images) - 1] page = CGPDFDocumentGetPage(pdf, i) if page: mediaBox = CGPDFPageGetBoxRect(page, kCGPDFMediaBox) if CGRectIsEmpty(mediaBox): mediaBox = None CGContextBeginPage(ctx, mediaBox) if under: drawWatermark(ctx, image, xOffset, yOffset, angle, scale, opacity) CGContextDrawPDFPage(ctx, page) if not under: drawWatermark(ctx, image, xOffset, yOffset, angle, scale, opacity) CGContextEndPage(ctx) del pdf CGPDFContextClose(ctx) del ctx def main(argv): verbose = False readFilename = None writeFilename = None under = False xOffset = 0.0 # FIXED: changed to float value yOffset = 0.0 # FIXED: changed to float value angle = 0.0 # FIXED: changed to float value scale = 1.0 # FIXED: added opacity = 1.0 # Parse the command line options try: options, args = getopt.getopt(argv, "vutx:y:a:p:s:i:o:", ["verbose", "under", "over", "xOffset=", "yOffset=", "angle=", "opacity=", "scale=", "input=", "output=", ]) except getopt.GetoptError: usage() sys.exit(2) for option, arg in options: print option, arg if option in ("-i", "--input") : if verbose: print "Reading pages from %s." % (arg) readFilename = arg elif option in ("-o", "--output") : if verbose: print "Setting %s as the output." % (arg) writeFilename = arg elif option in ("-v", "--verbose") : print "Verbose mode enabled." verbose = True elif option in ("-u", "--under"): print "watermark under PDF" under = True elif option in ("-t", "--over"): # FIXED: changed to "-t" from "t" print "watermark over PDF" under = False elif option in ("-x", "--xOffset"): xOffset = float(arg) elif option in ("-y", "--yOffset"): yOffset = float(arg) elif option in ("-a", "--angle"): angle = -float(arg) elif option in ("-s", "--scale"): scale = float(arg) elif option in ("-p", "--opacity"): opacity = float(arg) else: print "Unknown argument: %s" % (option) if (len(args) > 0): watermark(readFilename, args, writeFilename, under, xOffset, yOffset, angle, scale, opacity, verbose); else: shutil.copyfile(readFilename, writeFilename); def usage(): print "Usage: watermark --input <file> --output <file> <watermark files>..." if __name__ == "__main__": print sys.argv main(sys.argv[1:])



All the best,

H

53 replies

Oct 24, 2014 5:40 AM in response to Tony T1

Ok. After additional fiddling with the args, I now have watermarks using your Watermark.png and my Draft.png. Since my image is 50% transparency text on a transparent background, it does not act as a screen (dimming the underlying page of text) when placed above. I still could not arrive at any setting that would place either image behind the text, but I can live with that.

Jan 18, 2016 5:19 AM in response to VikingOSX

I have the same problem.


I am creating a banner image file from metadata. That all works well. When the image file (png) is passed into the watermark routine, it does not work. The log shows that the image file is being converted to a pdf before or during the watermark process. Not sure if that is the problem but haven't found a way around it. Any suggestions?

Aug 12, 2016 8:14 AM in response to benwiggy

Have you looked at PDFAnnotationStamp in PDFKit? The old PDF Annotation Editor source code has an example of applying a stamp. It uses a PDF for the stamp though, and code would need to be added for scaling and rotation of the stamp box.


You could extend the tool.py in Automator's Watermark PDF Documents action with an additional command-line switch for -text, and the PDFAnnotationStamp functionality.


I could not find any existing watermark/stamping examples that used text instead of images or PDF, after several Google searches.

Aug 12, 2016 8:19 AM in response to VikingOSX

Thanks, I'll have a look at that. I've not found much via web searches either. Adding text to a Core Graphics context used to be easy, but Apple have deprecated all the easy APIs, in favour of CoreText, which is monstrous.


I've produced python scripts to combine, rotate, add graphics, count pages, make booklet spreads, apply quartz filters, etc, etc to PDFs, all using CoreGraphics, and I'm determined to persevere with adding text!!!


It can be done if you download some cross-patform python PDF library, but I don't really want to do that.

Aug 13, 2016 9:56 AM in response to benwiggy

I am unaware of an automatic process for matching objects and their associated constants to their respective class import statements. It is just grunt work, with occasional trips to the class/object declarations and contant definitions to be certain.


This is why you see alot of Python code with the convenient import splat — and its cost to performance. PEP8 (last bullet under Import section) discourages the use of the splat as it pollutes the name space.


I use the Anaconda plug-in in Sublime Text 3, and it syntax checks, and alerts when PEP8 is transgressed. Good habit building tool.

Aug 13, 2016 4:48 PM in response to Hiroto

Hiroto,


Aha! I like your name space alternative, and it is just as quick to watermark a 179 page input.pdf as my earlier laborious import naming efforts. I am sold, and thanks again.


I think what may confuse people is that there is the Python Scripting Bridge inherent to OS X, which we alternatively refer to as PyObjC, and then there is that other third-party PyObjC solution that installs the PyObjC bridging solution into Python releases other than supplied by OS X.

Aug 13, 2016 6:23 PM in response to VikingOSX

Oops. I thought importing multiple frameworks into single OSX name space by those import statements works but indeed does not. The previous code happens to work because CoreText module internally imports CoreFoundation and Quartz.


In other words, the following:


import CoreFoundation as OSX import Quartz.CoreGraphics as OSX import CoreText as OSX



is effectively equivalent to:


import CoreText as OSX




If you change the import statements order such as:


import Quartz.CoreGraphics as OSX import CoreText as OSX import CoreFoundation as OSX



code to access CoreText or CoreGraphics symbols should fail...



Sorry for confusion.


Hiroto

Aug 14, 2016 2:00 AM in response to Hiroto

Thanks once more for your further comments. Yes, I've used the python script tool.py, as well as the other three python scripts hidden inside Automator actions for Combining PDF Pages, Extracting pages and making graph paper.


Yes, I was confused by the third-party PyObjC software. I don't think there's any need for that, unless you're building GUI apps.??


I'll try using the name space idea, as it is otherwise easy to miss one out! My library of python scripts to work with PDFs is now looking pretty comprehensive, thanks to you and various other bits of code I've gleaned.

This thread has been closed by the system or the community team. You may vote for any posts you find helpful, or search the Community for additional answers.

Automator Watermark PDF Workflow

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple Account.