Reputation: 4892
Given a file path, I need to rotate all the pages in a PDF at that file path using nothing that isn't included with macOS. The wrapper for this will be AppleScript, so I also have access to the command line and therefore all of the scripting languages installed by default with macOS, but nothing that would require brew
or, for example, Python's pip
.
Upvotes: 1
Views: 2169
Reputation: 2719
You can now use Swift as a scripting language. The Shortcuts.app includes an option to use Swift in its "Run Shell Script" action.
The following script will rotate all pages of PDFs supplied as filenames in its arguments.
#!/usr/bin/swift
// ROTATE PAGES: Rotate all pages of selected PDFs by 90˚
import Foundation
import Quartz
func newURL(filepath: String, newbit: String) -> String {
var newname = filepath
while (FileManager.default.fileExists(atPath: newname)) {
newname = (newname as NSString).deletingPathExtension
// Could be improved with incremental number added to filename
newname += newbit
}
return newname
}
func rotatePage(filepath: String) -> Void {
let pdfURL = URL(fileURLWithPath: filepath)
let newFilepath = newURL(filepath: filepath, newbit: " +90.pdf")
if let pdfDoc = PDFDocument.init(url: pdfURL) {
let pages = pdfDoc.pageCount
for p in (0...pages) {
let page = pdfDoc.page(at: p)
var newRotation: Int = 90
if let existingRotation = page?.rotation {
newRotation = (existingRotation + 90) as Int
}
page?.rotation = newRotation
}
pdfDoc.write(toFile: newFilepath)
}
return
}
// "main"
if CommandLine.argc > 1 {
for (index, args) in CommandLine.arguments.enumerated() {
if index > 0 {
rotatePage(filepath: args)
}
}
}
Upvotes: 1
Reputation: 2719
MacOS comes with python, and you can use it in Automator (Run shell script - set to python, pass inputs as arguments) to create a service that will work on PDFs in the Finder.
#!/usr/bin/python
# coding=utf-8
import sys
import os
from Quartz import PDFDocument
from CoreFoundation import NSURL
if __name__ == '__main__':
for filename in sys.argv[1:]:
filename = filename.decode('utf-8')
shortName = os.path.splitext(filename)[0]
pdfURL = NSURL.fileURLWithPath_(filename)
pdfDoc = PDFDocument.alloc().initWithURL_(pdfURL)
pages = pdfDoc.pageCount()
for p in range(0, pages):
page = pdfDoc.pageAtIndex_(p)
existingRotation = page.rotation()
newRotation = existingRotation + 90
page.setRotation_(newRotation)
pdfDoc.writeToFile_(filename)
You can find a whole load of similar scripts and Automator workflows at this GitHub site
Upvotes: 0
Reputation: 7191
You can use a method of the PDFPage from a Cocoa-AppleScript.
Look at https://developer.apple.com/documentation/pdfkit/pdfdocument?language=objc and https://developer.apple.com/documentation/pdfkit/pdfpage?language=objc
Here's the script:
use scripting additions
use framework "Foundation"
set thisPath to POSIX path of (choose file of type {"pdf"} with prompt "Choose a PDF") -- a POSIX path, not an alias or an HFSPath
set thisDoc to current application's PDFDocument's alloc()'s initWithURL:(current application's NSURL's fileURLWithPath:thisPath)
set pCount to thisDoc's pageCount()
repeat with i from 0 to (pCount - 1)
((thisDoc's pageAtIndex:i)'s setRotation:90) -- rotate to 90, the rotation must be a positive or negative multiple of 90: (0, 90, 180, 270 or -90 -180 -270)
end repeat
thisDoc's writeToFile:thisPath -- save --
Note: this script will work properly, if the rotation of the page is 0 (no rotation), otherwise you must get the rotation of the page and do a calculation.
Here's an example to rotate the pages to right or left:
use scripting additions
use framework "Foundation"
set thisPath to POSIX path of (choose file of type {"pdf"} with prompt "Choose a PDF") -- a POSIX path, not an alias or an HFSPath
my rotatePages(thisPath, 90) -- rotate right , use -90 to rotate left
on rotatePages(thisPath, r)
set thisDoc to current application's PDFDocument's alloc()'s initWithURL:(current application's NSURL's fileURLWithPath:thisPath)
set pCount to thisDoc's pageCount()
repeat with i from 0 to (pCount - 1)
set thisPage to (thisDoc's pageAtIndex:i)
(thisPage's setRotation:((thisPage's |rotation|()) + r)) -- add 90 to the current rotation, note: at 360, the value of the rotation will be set to 0, not to 360
end repeat
thisDoc's writeToFile:thisPath -- save --
end rotatePages
Upvotes: 1
Reputation: 4892
Here's the solution I used.
do shell script
to open
each of the Automator workflows as needed.Upvotes: 0
Reputation: 7555
To my knowledge macOS does not have any one native command line Unix executable that can rotate all pages in a PDF (while keeping text based ones text based). sip
can rotate a single page PDF however the resulting PDF is an encapsulated image, not text if it was text base to begin with. Also, not sure if there is a way with just plain AppleScript, other then via UI Scripting the default Preview application, without going to AppleScriptObjC (Cocoa-AppleScript) and or Python, etc.
Using third-party command line utilities is probably the easiest, but you said it has to be done only using what's a default part of macOS. So, I'll offer an AppleScript solution that uses UI Scripting the default Preview application, that can be used in the event there isn't another way with AppleScriptObjC or without third-party utilities, etc.
This solution, as offered (and coded), assumes that Preview is the default application for PDF documents and uses it to rotate all the pages in the PDF document. It is also setup as an Automator workflow. (Although there are other ways to incorporate the AppleScript code shown below.)
First, in Finder, make a copy of the target PDF documents and work with those.
In Automator, create a new workflow document, adding the following actions:
AppleScript code:
on run {input}
set thisLong to 0.25 -- # The value of 'thisLong' is decimal seconds delay between keystrokes, adjust as necessary.
set theRotation to "r" -- # Valid values are 'l' or 'r' for Rotate Left or Rotate Right.
set theViewMenuCheckedList to {}
set theMenuItemChecked to missing value
repeat with thisItem in input
tell application "Finder" to open file thisItem -- # By default, in this use case, the PDF file will open in Preview.
delay 1 -- # Adjust as necessary. This is the only 'delay' not defined by the value of 'thisLong'.
tell application "System Events"
perform action "AXRaise" of window 1 of application process "Preview" -- # Just to make sure 'window 1' is front-most.
delay thisLong
-- # Ascertain which of the first six 'View' menu items is checked.
set theViewMenuCheckedList to (value of attribute "AXMenuItemMarkChar" of menu items 1 thru 6 of menu 1 of menu bar item 5 of menu bar 1 of application process "Preview")
repeat with i from 1 to 6
if item i in theViewMenuCheckedList is not missing value then
set theMenuItemChecked to i as integer
exit repeat
end if
end repeat
-- # Process keystrokes based on which 'View' menu item is checked.
-- # This is being done so the subsequent keystroke ⌘A 'Select All'
-- # occurs on the 'Thumbnails', not the body of the document.
if theMenuItemChecked is not 2 then
repeat with thisKey in {"2", "1", "2"}
keystroke thisKey using {option down, command down}
delay thisLong
end repeat
else
repeat with thisKey in {"1", "2"}
keystroke thisKey using {option down, command down}
delay thisLong
end repeat
end if
repeat with thisKey in {"a", theRotation as text, "s"} -- # {Select All, Rotate Direction, Save}
keystroke thisKey using {command down}
delay thisLong
end repeat
keystroke theMenuItemChecked as text using {option down, command down} -- # Resets the 'View' menu to the original view.
delay thisLong
keystroke "w" using {command down} -- # Close Window.
end tell
end repeat
end run
Notes:
delay
commands may need to be changed for use on your system (and or additional delay
commands added as appropriate, although in the case additional delay
commands should not be needed). It should go without saying however test this on a set of a few document first to make sure the value set for thisLong
works on your system. On my system this worked as coded.If you need to rotate more then one time, add additional theRotation as text,
to:
repeat with thisKey in {"a", theRotation as text, "s"} -- # {Select All, Rotate Direction, Save}
Example:
repeat with thisKey in {"a", theRotation as text, theRotation as text, "s"}
Upvotes: 1
Reputation: 3095
PDF page rotation can be performed using standard Preview application.
Just after opening a PDF document, hit 1 or 2 times tab key (depending of your Preview version) in order to select all pages on side bar with 'cmd a'. Then hit 'cmd R' (or L) to rotate 90% right (or left) all selected pages and finally hit 'cmd w' to close the window. Preview will ask to save changes by default, then only hit enter key to save.
You can simulate all these keys in Applescript using Keystroke instruction from System Events.
To use this subroutine for many PDF files, you have 2 solutions, still in Applescript:
1) Select a folder and loop through all PDF files of that folder to apply this sub-routine.
2) Select your files in "Finder" and your script will take directly all Finder selected files to apply same sub-routine.
Such GUI scripting is not perfect, but Preview is not scriptable ...unfortunately )-:
Upvotes: 5