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)
You can make a difference in the Apple Support Community!
When you sign up with your Apple Account, you can provide valuable feedback to other community members by upvoting helpful replies and User Tips.
When you sign up with your Apple Account, you can provide valuable feedback to other community members by upvoting helpful replies and User Tips.
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)
I may agree with you. 😠 Can't get it to work as drag/drop application, with a rename after the watermark — or as a Printer plugin as below.
I create a Print plugin in Automator. Two actions:
Image: draft.jpg (will not allow you to select a draft.pdf with the chooser, but you can drag/drop one in the well - doesn't work either)
Print > PDF > myDraft
Hiroto wrote:
Briefly tested with pybojc 2.2b3 and python 2.6.1 under OS X 10.6.8.
I don't have pyObjC, but it runs here fine. (Though it's quite slow - over 20 seconds!)
I'll start wrapping it so that it puts page numbers on.
Indeed: I'm immensely grateful to Hiroto, as I've been wrestling with the CoreText bit for a week, without success.
I didn't know you could import items like that, either. Is there an automatic way of getting the items, or do I just go through the code?
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.
My pleasure! And feel free to adapt it to your project, for it is trivial sample code with nothing special but series of framework function calls. Only that I have accommodated the code to tool.py located at (under OS X 10.6.8):
/System/Library/Automator/Watermark PDF Documents.action/Contents/Resources/tool.py
so if you feel uneasy or uncertain about using the code as is, arrange it as you see fit. 😉
Regarding PyObjC, it is part of standard installation of OS X 10.5 through 10.11 if I'm not mistaken. Under OS X 10.6.8, for instance, it is installed in the following directories.
/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python/PyObjC /System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python/PyObjC
Regarding PyObjC performance, unfortunately, Python's importing PyObjC modules is blooming slow as compared to Ruby's importing RubyCocoa modules which is so quick and almost instant. In recent PyObjC's implementation, however, module importing performance can be improved reportedly either by specifying symbols selectively as VikingOSX described or by importing module in separate name space by means of 'as' qualifier of import statement, such as:
import Quartz.CoreGraphics as CG
and qualify symbols with CG prefixed such as:
CG.CGPDFContextCreateWithURL(url)
(Sadly, these techniques gain nothing under OS X 10.6.8 I'm using...)
Anyway, I'd employ the following variation for faster importing pyobjc modules, where I use single OSX name space for simplicity. Framework's symbols are well distinguished by framework-specific names, so there should be no problem, any more than 'from Framework import *'.
#!/usr/bin/python # coding: utf-8 import math import CoreFoundation as OSX import Quartz.CoreGraphics as OSX import CoreText as OSX 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 = OSX.CTLineGetImageBounds(line, ctx) imageWidth = rect.size.width imageHeight = rect.size.height OSX.CGContextSaveGState(ctx) OSX.CGContextSetAlpha(ctx, opacity) OSX.CGContextTranslateCTM(ctx, xOffset, yOffset) OSX.CGContextScaleCTM(ctx, scale, scale) OSX.CGContextTranslateCTM(ctx, imageWidth / 2, imageHeight / 2) OSX.CGContextRotateCTM(ctx, angle * math.pi / 180) OSX.CGContextTranslateCTM(ctx, -imageWidth / 2, -imageHeight / 2) OSX.CGContextSetTextPosition(ctx, 0.0, 0.0); OSX.CTLineDraw(line, ctx); OSX.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 = OSX.CTFontCreateWithName('Helvetica', 36.0, None) astr = OSX.CFAttributedStringCreate(OSX.kCFAllocatorDefault, text, { OSX.kCTFontAttributeName : font }) line = OSX.CTLineCreateWithAttributedString(astr) # create output pdf context ourl = OSX.CFURLCreateFromFileSystemRepresentation(OSX.kCFAllocatorDefault, ofile, len(ofile), False) ctx = OSX.CGPDFContextCreateWithURL(ourl, None, None) # create input pdf document iurl = OSX.CFURLCreateFromFileSystemRepresentation(OSX.kCFAllocatorDefault, ifile, len(ifile), False) pdf = OSX.CGPDFDocumentCreateWithURL(iurl) if pdf: for i in range(0, OSX.CGPDFDocumentGetNumberOfPages(pdf)): page = OSX.CGPDFDocumentGetPage(pdf, i + 1) if page: mbox = OSX.CGPDFPageGetBoxRect(page, OSX.kCGPDFMediaBox) if OSX.CGRectIsEmpty(mbox): mbox = None OSX.CGContextBeginPage(ctx, mbox) OSX.CGContextDrawPDFPage(ctx, page) # elementary test OSX.CGContextSetTextPosition(ctx, 10.0, 10.0) OSX.CTLineDraw(line, ctx) # using general function drawWatermarkText(ctx, line, xOffset, yOffset, angle, scale, opacity) OSX.CGContextEndPage(ctx) del pdf OSX.CGPDFContextClose(ctx) del ctx
All the best,
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.
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
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.
Automator Watermark PDF Workflow