Tony T1

Q: 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:48 AM

Close

Q: Automator Watermark PDF Workflow

  • All replies
  • Helpful answers

first Previous Page 4 of 4
  • by benwiggy,

    benwiggy benwiggy Aug 13, 2016 12:26 AM in response to Hiroto
    Level 4 (1,430 points)
    Mac OS X
    Aug 13, 2016 12:26 AM in response to Hiroto

    Many thanks! That's great.

     

    I've written a whole bunch of python Quartz scripts for manipulating PDFs. They are called "PDFSuite" on github.

    I'll add this one, if that's OK.

  • by benwiggy,

    benwiggy benwiggy Aug 13, 2016 2:08 AM in response to Hiroto
    Level 4 (1,430 points)
    Mac OS X
    Aug 13, 2016 2:08 AM in response to Hiroto

    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.

  • by VikingOSX,

    VikingOSX VikingOSX Aug 13, 2016 7:10 AM in response to benwiggy
    Level 7 (20,819 points)
    Mac OS X
    Aug 13, 2016 7:10 AM in response to benwiggy

    When one formally designates the import items used in the code, instead of the more convenient splat, it runs in an eyeblink. Afterwards, 179 page PDF takes 1.8 seconds to watermark. Splat notwithstanding, this is another exemplary piece of code from Hiroto, and deserves a substantial "Thank you."

     

    Screen Shot 2016-08-13 at 9.51.39 AM.jpg

  • by benwiggy,

    benwiggy benwiggy Aug 13, 2016 9:24 AM in response to VikingOSX
    Level 4 (1,430 points)
    Mac OS X
    Aug 13, 2016 9:24 AM in response to VikingOSX

    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?

  • by VikingOSX,

    VikingOSX VikingOSX Aug 13, 2016 9:56 AM in response to benwiggy
    Level 7 (20,819 points)
    Mac OS X
    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.

  • by Hiroto,

    Hiroto Hiroto Aug 13, 2016 4:30 PM in response to benwiggy
    Level 5 (7,286 points)
    Aug 13, 2016 4:30 PM in response to benwiggy

    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

  • by VikingOSX,

    VikingOSX VikingOSX Aug 13, 2016 4:48 PM in response to Hiroto
    Level 7 (20,819 points)
    Mac OS X
    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.

  • by Hiroto,

    Hiroto Hiroto Aug 13, 2016 6:23 PM in response to VikingOSX
    Level 5 (7,286 points)
    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

  • by benwiggy,

    benwiggy benwiggy Aug 14, 2016 2:00 AM in response to Hiroto
    Level 4 (1,430 points)
    Mac OS X
    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.

first Previous Page 4 of 4