¿Cómo puedo rotar mediante programación todas las páginas de un PDF con las herramientas nativas de macOS?

Dada una ruta de archivo, necesito rotar todas las páginas de un PDF en esa ruta de archivo sin usar nada que no esté incluido en macOS. El envoltorio para esto será AppleScript, por lo que también tengo acceso a la línea de comandos y, por lo tanto, a todos los lenguajes de secuencias de comandos instalados de forma predeterminada con macOS, pero nada que requiera, por ejemplo, brewPython's pip.

Esta publicación en SuperUser puede proporcionar alguna ayuda.
@icondaemon, ¿esto no requiere software adicional?
¿Puedo sugerirle que edite su pregunta para incluir qué versión de macOS está ejecutando? Además, ¿puede aclarar cómo necesita rotar las páginas (por ejemplo, 90° en el sentido de las agujas del reloj o...)? Finalmente, ¿sería aceptable una solución que involucre Automator, o una que involucre JavaScript para Automatización (JXA)?
@patrix - cierto. Pasé unos días el año pasado tratando de encontrar herramientas nativas de manipulación de PDF disponibles desde la línea de comandos y encontré solo 16 automatizaciones en /System/Library/Automator, pero ninguna que haga lo que necesita el OP, como probablemente lo haya descubierto él mismo. Automator tiene una función de rotación de imágenes para vista previa, pero no creo que funcione en archivos PDF.
Estoy investigando automator's Split PDF( Rotate Imageso un AppleScript que tengo que funcionará en archivos PDF de una sola página) y Combine PDF Pages. Esos podrían hacer el trabajo.

Respuestas (2)

Que yo sepa, macOS no tiene ningún ejecutable de Unix de línea de comando nativo que pueda rotar todas las páginas en un PDF (mientras mantiene las basadas en texto). sippuede rotar un PDF de una sola página; sin embargo, el PDF resultante es una imagen encapsulada , no texto si, para empezar, era una base de texto. Además, no estoy seguro de si hay una manera con AppleScript simple , que no sea a través de UI Scripting la aplicación de vista previa predeterminada , sin ir a AppleScriptObjC ( Cocoa-AppleScript ) y/o Pitón , etc

Usar utilidades de línea de comandos de terceros es probablemente lo más fácil, pero usted dijo que solo debe hacerse usando lo que es una parte predeterminada de macOS . Por lo tanto, ofreceré una solución de AppleScript que usa UI Scripting como la aplicación de vista previa predeterminada , que se puede usar en caso de que no haya otra forma con AppleScriptObjC o sin utilidades de terceros , etc.

Esta solución, tal como se ofrece (y codifica), asume que Preview es la aplicación predeterminada para documentos PDF y la usa para rotar todas las páginas del documento PDF . También se configura como un flujo de trabajo de Automator . (Aunque hay otras formas de incorporar el código AppleScript que se muestra a continuación).

Primero, en Finder , haga una copia de los documentos PDF de destino y trabaje con ellos.

En Automator , cree un nuevo documento de flujo de trabajo , agregando las siguientes acciones :

  • Obtener elementos de búsqueda especificados
    • Agregue el documento PDF de destino copiado a esta acción .
  • Ejecutar secuencia de comandos AppleScript
    • Reemplace el código predeterminado con el siguiente código :

Código AppleScript :

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

Notas:

  • Como este script usa UI Scripting , cuando se ejecuta desde Automator (o Script Editor ), la aplicación en ejecución debe agregarse a Preferencias del sistema > Seguridad y privacidad > Accesibilidad para que se ejecute correctamente. Guardada como una aplicación , la aplicación guardada deberá agregarse.
  • También con UI Scripting , es posible que sea necesario cambiar el valor de los delay comandos para usarlos en su sistema (o agregar delay comandos adicionales según corresponda, aunque en el caso de que no se necesiten delay comandos adicionales). No hace falta decir, sin embargo, primero pruebe esto en un conjunto de algunos documentos para asegurarse de que el valor establecido para thisLongfuncione en su sistema. En mi sistema esto funcionó como codificado.
  • Cuando se usa UI Scripting de esta manera, una vez que la tarea ha comenzado, uno debe dejar el sistema solo y dejar que termine de procesar los archivos. Intentar realizar múltiples tareas solo alejará el enfoque de la tarea en cuestión y hará que falle.
  • Si necesita rotar más de una vez, agregue más theRotation as text,a:

      repeat with thisKey in {"a", theRotation as text, "s"} -- # {Select All, Rotate Direction, Save}
    

    Ejemplo:

      repeat with thisKey in {"a", theRotation as text, theRotation as text, "s"}
    

Este script de python rotará 90º en el sentido de las agujas del reloj todas las páginas de cualquier PDF dado como argumento, guardando el resultado en un nuevo archivo con "+90" agregado al nombre del archivo.

#!/usr/bin/python
# coding=utf-8
# Produces new PDF file with all pages rotated by 90 degrees.

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]
        outFilename = shortName + "+90.pdf"
        pdfURL = NSURL.fileURLWithPath_(filename)
        pdfDoc = PDFDocument.alloc().initWithURL_(pdfURL)
        if pdfDoc:
            pages = pdfDoc.pageCount()
            for p in range(0, pages):
                page = pdfDoc.pageAtIndex_(p)
                existingRotation = page.rotation()
                newRotation = existingRotation + 90
                page.setRotation_(newRotation)

            pdfDoc.writeToFile_(outFilename)