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 23, 2014 8:27 PM in response to Tony T1

Used Hiroto's updated Python script. Used the Waternark.png that you posted. Used your corrected command-line settings. There is no watermark produced, and the len(args) returns 1.


I tried Hiroto's small shell script with its args unchanged except for my input and output filenames. Like above, it runs ok, and there is no watermark on the output PDF.


Can I say frustrated?

Oct 22, 2014 12:15 PM in response to VikingOSX

Well Tony, this PyObjC code is a work in progress. I cleaned up all syntax issues, but:

  • The expected stamp file is hardcoded into the application, not up top as a constant.

    Replace confidential.pdf with your document (expects text on transparent background)

  • Apparently, one must intuitively know the rect content setting
  • The stamp is applied backwards \, not in the expected / orientation
  • Syntax: ConfidentialStamper.py Lorem.pdf

    Output: Lorem.watermarked.pdf

May 19, 2015 2:26 AM in response to patriqq

Hi!

Thanks to Tony T1 & Hiroto first - great work guys!


I'm used this with previous OSX versions and now under 10.10 it's broken. So I managed to read through these topics here. What I've done so far:


Created a new folder on my desktop which should contain the neccessary files. I downloaded the tool.py from this topic - chmod'ed it and it works within command line:


./tool.py -h

['./tool.py', '-h']

Usage: watermark --input <file> --output <file> <watermark files>...


Using the tool.py manually works great as well:


./tool.py --under --xOffset 30 --yOffset 60 --angle 0 --scale 0.24 --opacity 1 --input test.pdf --output output.PDF vorlage.png

['./tool.py', '--under', '--xOffset', '30', '--yOffset', '60', '--angle', '0', '--scale', '0.24', '--opacity', '1', '--input', 'test.pdf', '--output', 'output.PDF', 'vorlage.png']

--under

watermark under PDF

--xOffset 30

--yOffset 60

--angle 0

--scale 0.24

--opacity 1

--input test.pdf

--output output.PDF


Here it is: http://pastebin.com/WyUPdh7x


Now I created an automator action based on Tony T1's - "Ask for finder objects" - "filter finder objects" - "execute shell sctipt" - it's all in german for me.


Unfortunately I get the error on line 8. I already removed every space after the "\" and I already tried to put everything into one line - no success at all.

Any idea about this?


User uploaded file


Line 8 is the input one. I already replaced that one completely with one from another quote here... no success so far.


Ideas? Thanks a lot!

Aug 12, 2016 7:19 PM in response to benwiggy

Hello


Here's sample pybojc code to draw text watermark using Core Text CTLine as a proof of concept.



#!/usr/bin/python # coding: utf-8 import math from Quartz.CoreGraphics import * from CoreText import * def drawWatermarkText(ctx, line, xOffset, yOffset, angle, scale, opacity): # CGContextRef ctx # CTLineRef line # float xOffset, yOffset, angle ([degree]), scale, opacity ([0.0, 1.0]) if line: rect = CTLineGetImageBounds(line, ctx) imageWidth = rect.size.width imageHeight = rect.size.height 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) CGContextSetTextPosition(ctx, 0.0, 0.0); CTLineDraw(line, ctx); CGContextRestoreGState(ctx) # parameters ifile = 'input.pdf' ofile = 'output.pdf' text = 'Watermark Sample' xOffset, yOffset, angle, scale, opacity = 0.0, 400.0, 60.0, 2.0, 0.2 # create CoreText line (CTLine) font = CTFontCreateWithName('Helvetica', 36.0, None) astr = CFAttributedStringCreate(kCFAllocatorDefault, text, { kCTFontAttributeName : font }) line = CTLineCreateWithAttributedString(astr) # create output pdf context ourl = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, ofile, len(ofile), False) ctx = CGPDFContextCreateWithURL(ourl, None, None) # create input pdf document iurl = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, ifile, len(ifile), False) pdf = CGPDFDocumentCreateWithURL(iurl) if pdf: for i in range(0, CGPDFDocumentGetNumberOfPages(pdf)): page = CGPDFDocumentGetPage(pdf, i + 1) if page: mbox = CGPDFPageGetBoxRect(page, kCGPDFMediaBox) if CGRectIsEmpty(mbox): mbox = None CGContextBeginPage(ctx, mbox) CGContextDrawPDFPage(ctx, page) # elementary test CGContextSetTextPosition(ctx, 10.0, 10.0) CTLineDraw(line, ctx) # using general function drawWatermarkText(ctx, line, xOffset, yOffset, angle, scale, opacity) CGContextEndPage(ctx) del pdf CGPDFContextClose(ctx) del ctx




Briefly tested with pybojc 2.2b3 and python 2.6.1 under OS X 10.6.8.


Good luck,

H

Oct 22, 2014 4:11 PM in response to Frank Caggiano

Frank,

The tool.py file had a ton of Python syntax noise that I cleaned up. When I run it in the Terminal following precisely its command-line argument order, there appears to remain a consistent problem on line 31 with one of the Core Graphics methods, and a variable that was referenced before set. Will have to spend more cross-checking with the Dev docs. Maybe I can fix it.


I was trying to get the Python bit to work, because a UNIX executable living in MacOS that may also be a contributing factor to this misadventure.

Oct 22, 2014 7:19 PM in response to VikingOSX

I had tried that (with scale = 1.0) and got the same "ValueError: depythonifying 'pointer', got 'str'" error.

But as I said, I get the same error in terminal with OS X 10.7.5, yet it works within Automator (on 10.7.5), so there's something else in the Action that's resolving this issue (in 10.7.5) — but this does nothing to help in resolving the issue in 10.10. Looks like Apple needs to fix (I'll file a bug report)

Oct 23, 2014 2:28 PM in response to VikingOSX

Another interesting quirk with the workflow in Yosemite is that when you add the Watermark PDF Documents action to the workflow, in Mavericks the gray box to the left of the adjustments is filled with a pdf document from the actions resource folder, Bears.pdf, to enable you see what the watermark will look like. In Yosemite that dummy file is not loaded.

Oct 23, 2014 2:17 PM in response to VikingOSX

I am learning more about this Python code. The correct command-line input for Hiroto's patch that does not produce any Python errors:


odin: ~$ tool2.py --angle 0.45 --over --input Lorem.pdf --output LoremD.pdf Draft.png
['./tool2.py', '--angle', '0.45', '--over', '--input', 'Lorem.pdf', '--output', 'LoremD.pdf', 'Draft.png']
--angle 0.45
--over 
watermark over PDF
--input Lorem.pdf
--output LoremD.pdf


An output PDF is written — and silently ignores the Draft.png file — so there is no watermark applied.

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.