¿Cómo usar AppleScript para crear un nuevo archivo de formato de texto enriquecido a partir del texto del portapapeles?

Tengo un Servicio de Automator que toma el texto actual en el portapapeles y lo escribe en un nuevo archivo .txt. Esto se ejecuta simplemente con la siguiente pieza de código AppleScript:

do shell script "pbpaste > /path/to/your/clipboard-file.txt"

Este código es de la siguiente respuesta de desbordamiento de pila:

AppleScript para pegar texto del portapapeles en un archivo

Sin embargo, este código convierte el contenido del portapapeles en texto sin formato, eliminando todo el formato del texto.

¿Es posible usar AppleScript para escribir el contenido del portapapeles en un archivo .rtf o .rtfd, conservando el formato original del contenido del portapapeles? Por formato original, me refiero a datos estilísticos como énfasis tipográfico (negrita, cursiva, subrayado), tamaño de fuente y color del texto.

Por un capricho, probé el siguiente comando:

do shell script "pbpaste > /path/to/your/clipboard-file.rtf"

Pero este comando generó un archivo .rtf que mi computadora no pudo leer ni abrir.

Actualicé mi respuesta para que funcione con contenido RTF y HTML del Portapapeles.
¿Te funcionó mi respuesta actualizada?

Respuestas (3)

Esta respuesta se ha actualizado para reflejar el malentendido de la esfera de rubik entre lo que es Rich Text (sobre lo que se preguntó originalmente) y lo que realmente se está trabajando desde Google Chrome, siendo HTML. (Ver comentarios movidos al chat ).

Dejo la respuesta original tal como está, y debajo de este nuevo contenido, ya que técnicamente responde a la pregunta original tal como se hizo. También contiene información relevante para el proceso general de tratar con el contenido del Portapapeles en el contexto de la pregunta original y modificada.

El siguiente código es un código de ejemplo que se probará y ejecutará en (Apple) Script Editor, ya que aparte de la primera oración de la pregunta, todavía no se ha proporcionado ningún uso explícito y específico dentro de Automator. Es posible que sea necesario editar el código para que funcione dentro del uso desconocido en Automator. Dicho esto, tal como está escrito, si todo el script a continuación se colocó solo en una acción Ejecutar AppleScript, solo, en Automator... funciona como está. Si usa solo segmentos del código , es posible que sea necesario realizar algunos cambios en el código existente .

El siguiente código contiene suficientes comentarios para comprender en general lo que está haciendo el script .


--        # 
--        # Change the 'New RichText Filename.rtf' name to the wanted filename for the target file.
--        # Make sure you leave the double-quotes even if the filename does not contain spaces!
--        # Note that '(path to desktop as text)' can also be modified as needed, e.g. changed to,
--        # '(path to documents folder as text)' or the entire segment after 'set theRichTextFileName to'
--        # can be a fully qualified POSIX pathname, e.g.: set theRichTextFileName to "/path/to/filename.rtf"
--        #  

set theRichTextFileName to POSIX path of (path to desktop as text) & "New RichText Filename.rtf"


--    #        THE REMAINING CODE SHOULD NOT NEED TO BE MODIFIED.
--    # 
--    #     Note: This code, as is, works as written and intended when run from within (Apple) Script Editor.
--    #     Some AppleScript code when wrapped in Automator may not work the same as in (Apple) Script Editor.
--    #     In cases where is does not work from a Run AppleScript action in Automator, editing will be required.
--    # 
--    #     This AppleScript code preforms the following actions, sans errors caught during File I/O operations.
--    # 
--    # 1. See it the target file exists and prompts to be overwritten if it does. If yes is selected, it continues.
--    # 
--    # 2. If the Clipboard contains RTF content, writes it to the target file using plain AppleScript.
--    # 
--    # 3. If the Clipboard contains HTML content, writes it to the target file as RTF using a 'do shell script' command.
--    # 
--    # 4. If the Clipboard does not contain any RTF/HTML content, notify the user. 
--
--        #    Notes: The 'do shell script' makes use of the following:
--        #
--        #    'osascript' to get the HTML content from the Clipboard.
--        #    The content is a Hex stream within a data wrapper and
--        #    'awk' will be used to remove/replace the data wrapper.
--        #
--        #    'awk' to remove the data wrapper from 'osascript' output
--        #    replacing it with proper HTML opening/closing Tags to
--        #    ensure it actually gets processed by 'textutil' after 'xxd'.
--        #    Without the HTML opening/closing Tags 'textutil' does not 
--        #    properly, within limits, convert the HTML Clipboard content to RTF.
--        #
--        #    'xxd' to convert the Hex data from 'osascript/awk' to ASCII text.
--        #
--        #    'textutil' to convert the ASCII text HTML from 'xxd' to RTF
--        #    formatted data and write it to the target file.


tell application "Finder"

    if exists theRichTextFileName as POSIX file then
        tell current application
            display dialog "The file \"" & theRichTextFileName & "\" already exists!" & "\n\n" & "Do you want to overwrite the file?" buttons {"No", "Yes"} default button 1 with title "File Already Exists..." with icon caution

            if the button returned of the result is "No" then
                return
            else
                tell application "Finder"
                    delete the file (theRichTextFileName as POSIX file)
                end tell
            end if
        end tell
    end if

    tell current application

        --    # Find out what class types are available for the Clipboard content
        --    # and use this information to determine which action will be taken.

        set cbInfo to get (clipboard info) as string

        if cbInfo contains "RTF" then

            try
                set richTextfromClipboard to get the clipboard as «class RTF »
            on error eStr number eNum
                display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with icon caution
                return
            end try
            try
                set fileHandle to open for access theRichTextFileName with write permission
                write richTextfromClipboard to fileHandle
                close access fileHandle
            on error eStr number eNum
                display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with title "File I/O Error..." with icon caution
                try
                    close access fileHandle
                end try
            end try

        else if cbInfo contains "HTML" then

            try
                do shell script "osascript -e 'try' -e 'get the clipboard as «class HTML»' -e 'end try' | awk '{sub(/«data HTML/, \"3C68746D6C3E\") sub(/»/, \"3C2F68746D6C3E\")} {print}' | xxd -r -p | textutil -convert rtf -stdin -stdout > " & quoted form of theRichTextFileName
            on error eStr number eNum
                display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with icon caution
            end try

        else

            display dialog "The Clipboard does not contain\nany usable RTF/HTML content!" buttons {"OK"} default button 1 with title "No RTF/HTML Content on Clipboard..." with icon caution

        end if

    end tell

end tell

Respuesta original a la pregunta original:

Para obtener contenido de texto enriquecido del Portapapeles en un archivo usando AppleScript , es un poco más complejo que un simple comando .do shell script

El código AppleScript de ejemplo a continuación, si el archivo de destino aún no existe y si existe contenido de texto enriquecido en el portapapeles , lo escribirá en un archivo . Tendrá todos los atributos que tiene el contenido de RichText en el Portapapeles , como lo hizo cuando se copió en el Portapapeles .

Abra el Editor de secuencias de comandos y copie y pegue el código a continuación en un nuevo documento Sin título y luego ejecútelo desde el Editor de secuencias de comandos , revisando el resultado en Eventos/Respuestas . Ejecútelo un par de veces, con y sin contenido de texto enriquecido en el Portapapeles y con y sin la existencia del archivo , en el disco duro, definido por al inicio del script .set theRichTextFileName ...

Verá que el código se asegura de que el archivo no exista, para no sobrescribir un archivo existente del nombre y la ubicación de destino y si el Portapapeles no contiene contenido de texto enriquecido , también muestra un mensaje para eso.

Ahora, si usa esto en un Servicio de Automator , por ejemplo, donde el Servicio recibe texto enriquecido seleccionado , entonces el código puede modificarse para que no detecte un error si el contenido de Texto enriquecido no está en el Portapapeles , ya que el servicio no aparecerá en el menú Servicios si El texto enriquecido no está seleccionado en un documento. Además, si desea sobrescribir el archivo de destino en su ubicación designada, el código que lo rodea también se puede eliminar. También daré esos ejemplos de código .


Código de ejemplo para pegar en el Editor de secuencias de comandos para probarlo y revisarlo:

set theRichTextFileName to POSIX path of (path to documents folder as text) & "New RichText Filename.rtf"

tell application "Finder"
    if exists theRichTextFileName as POSIX file then
        tell current application
            display dialog "The file \"" & theRichTextFileName & "\" already exists!" buttons {"OK"} default button 1 with title "File Already Exists..." with icon caution
        end tell
    else
        tell current application
            try
                set richTextfromClipboard to get the clipboard as «class RTF »
            on error eStr number eNum
                display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with title "No Rich Text Content on Clipboard..." with icon caution
                return
            end try
            try
                set fileHandle to open for access theRichTextFileName with write permission
                write richTextfromClipboard to fileHandle
                close access fileHandle
            on error eStr number eNum
                display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with title "File I/O Error..." with icon caution
                try
                    close access fileHandle
                end try
                return
            end try
        end tell
    end if
end tell

Código de ejemplo para usar en un Servicio de Automator , por ejemplo, donde el Servicio recibe texto enriquecido seleccionado :

set theRichTextFileName to POSIX path of (path to documents folder as text) & "New RichText Filename.rtf"

tell application "Finder"
    if exists theRichTextFileName as POSIX file then
        tell current application
            display dialog "The file \"" & theRichTextFileName & "\" already exists!" buttons {"OK"} default button 1 with title "File Already Exists..." with icon caution
        end tell
    else
        tell current application
            set richTextfromClipboard to get the clipboard as «class RTF »
            set fileHandle to open for access theRichTextFileName with write permission
            write richTextfromClipboard to fileHandle
            close access fileHandle
        end tell
    end if
end tell

Código de ejemplo para usar en un Servicio de Automator , por ejemplo, donde el Servicio recibe texto enriquecido seleccionado y sobrescribe el archivo de destino existente :

set theRichTextFileName to POSIX path of (path to documents folder as text) & "New RichText Filename.rtf"

tell current application
    set richTextfromClipboard to get the clipboard as «class RTF »
    set fileHandle to open for access theRichTextFileName with write permission
    write richTextfromClipboard to fileHandle
    close access fileHandle
end tell

La siguiente imagen es de un servicio de Automator de ejemplo que crea el archivo New RichText Filename.rtf a partir del texto enriquecido seleccionado del servicio Crear archivo de texto enriquecido desde el portapapeles en el menú contextual de servicios (haciendo clic con el botón derecho) o Application_name > Services > menu , cuando El texto enriquecido está seleccionado en un documento.

Servicio de Automator: cree un archivo de texto enriquecido desde el portapapeles


Ahora, estos son solo ejemplos y se puede codificar lógica adicional para adaptarlo a las necesidades de uno. Como ejemplo, se podría agregar código para incrementar automáticamente el nombre de un archivo existente para no sobrescribirlo, o solicitar un nuevo nombre de archivo y completar la operación en lugar de abortar con un mensaje de que el archivo ya existe, etc.


Actualización para usar con un do shell script comando :

Si realmente desea hacerlo usando un do shell script comando , use el siguiente código mientras lo reemplaza con un nombre de archivo de ruta/path/to/new rich text file.rtf válido a una ubicación en la que tenga permisos de escritura. Tenga en cuenta que no elimine el antes y el después en el comando real , ya que esto maneja el nombre del archivo de la ruta si contiene espacios. Si el nombre del archivo de la ruta no contiene espacios, entonces no es necesario usar el antes y el después .\"/path/to/new rich text file.rtf\"/path/to/new rich text file.rtf

do shell script "osascript -e 'try' -e 'get the clipboard as «class RTF »' -e 'end try' | awk '{print substr($0, 12, length($0)-13)}' | xxd -r -p > \"/path/to/new rich text file.rtf\""

Aquí está la línea de comando que se muestra como texto ajustado, para facilitar la visualización:

do shell script "osascript -e 'try' -e 'get the clipboard as «class RTF »' -e 'end try' | awk '{print substr($0, 12, length($0)-13)}' | xxd -r -p > \"/path/to/new rich text file.rtf\""

  • Si bien se puede copiar y pegar (recomendar) el código , aquí se explica cómo escribir las comillas de doble ángulo, que también están disponibles entre paréntesis en caracteres (especiales), por ejemplo, optioncommandTen TextEdit.

    Nota: Tenga en cuenta que el espaciado «class RTF »está destinado a compensarse en este caso de uso.

    • Escribir comillas de doble ángulo

      • « Comillas de doble ángulo izquierdo, presione:option\
      • » Comillas de ángulo doble derecho, presione:shiftoption\

Tenga en cuenta que, tal como está escrito, este do shell script comando sobrescribe el archivo de salida si ya existe, ¡sin preguntar! Será un archivo de longitud cero si el Portapapeles no contiene ningún contenido de texto enriquecido; de lo contrario, el archivo tendrá la longitud necesaria para contener el contenido de texto enriquecido del Portapapeles. Obviamente, se podría codificar lógica adicional en el osascript comando ; sin embargo, si necesita más complejidad, es mejor que use el método presentado por primera vez en esta respuesta. O usar un script externo llamado por do shell script comando que maneja toda la lógica necesaria y el manejo de errores en función de la complejidad de las condiciones generales en las que se aplicará.

  • do shell script "pbpaste > /path/to/clipboard-file.rtf"¿ Por qué tanta complejidad vs.

Esa es una buena pregunta y, si bien pbpastetiene la -Prefer {txt | rtf | ps}opción, pbpaste -Prefer rtfes posible que no genere texto enriquecido, incluso si existe en el portapapeles. O lo que genera, si no es texto ASCII, no será una forma de texto enriquecido que se entienda, por ejemplo, TextEdit, y no contendrá todos los atributos de texto enriquecido , si los hay, que contiene el contenido del portapapeles.

Esto hace que sea necesario obtener el contenido de texto enriquecido en el Portapapeles de una manera diferente y por qué get the clipboard as «class RTF »se usa en su lugar. Cuando se usa un do shell script comando con esto, se requiere un procesamiento adicional para utilizar los datos devueltos, ya que están en un envoltorio de datos cuando se devuelven y no se pueden usar de inmediato, por lo que requiere un procesamiento adicional.

Como ejemplo, Hello World!en texto enriquecido en el portapapeles puede verse así en texto ASCII:

{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400
{\fonttbl\f0\fnil\fcharset0 ComicSansMS;}
{\colortbl;\red255\green255\blue255;}
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural

\f0\b\fs36 \cf0 Hello World!}

Desafortunadamente, el formato anterior no es fácil de usar, en todo caso, en AppleScript y creo que es por eso que se requiere capturar como datos codificados en Hex.

  • Me baso en el hecho de que aunque el get clipboard info comando para el contenido del Portapapeles en este ejemplo se devuelve {«class RTF », 265}entre la información devuelta, aunque el formato de texto ASCII de este contenido de texto enriquecido tiene 265 bytes de largo, se devuelve en formato hexadecimal en más del doble. los bytes con el contenedor de datos . El hecho de que se devuelva en hexadecimal tanto get the clipboard as «class RTF »en el editor de scripts como en el uso osascriptrespalda esta suposición.

Aquí está lo mismo Hello World!en texto enriquecido en el portapapeles en hexadecimal:

7B5C727466315C616E73695C616E7369637067313235325C636F636F61727466313138375C636F636F617375627274663430300A7B5C666F6E7474626C5C66305C666E696C5C66636861727365743020436F6D696353616E734D533B7D0A7B5C636F6C6F7274626C3B5C7265643235355C677265656E3235355C626C75653235353B7D0A5C706172645C74783732305C7478313434305C7478323136305C7478323838305C7478333630305C7478343332305C7478353034305C7478353736305C7478363438305C7478373230305C7478373932305C7478383634305C7061726469726E61747572616C0A0A5C66305C625C66733336205C6366302048656C6C6F20576F726C64217D

Sin embargo, lo que devuelve get the clipboard as «class RTF »el Hello World!ejemplo anterior es:

«data RTF 7B5C727466315C616E73695C616E7369637067313235325C636F636F61727466313138375C636F636F617375627274663430300A7B5C666F6E7474626C5C66305C666E696C5C66636861727365743020436F6D696353616E734D533B7D0A7B5C636F6C6F7274626C3B5C7265643235355C677265656E3235355C626C75653235353B7D0A5C706172645C74783732305C7478313434305C7478323136305C7478323838305C7478333630305C7478343332305C7478353034305C7478353736305C7478363438305C7478373230305C7478373932305C7478383634305C7061726469726E61747572616C0A0A5C66305C625C66733336205C6366302048656C6C6F20576F726C64217D»

La cadena codificada en hexadecimal está en un «data RTF » envoltorio que debe eliminarse antes de convertir el contenido codificado en hexadecimal en texto ASCII para escribirlo en un archivo de disco mediante el uso de la redirección de E/S, por ejemplo, >en el ejemplo do shell script de comando anterior.

Por lo tanto, la salida de osascript -e 'try' -e ' get the clipboard as «class RTF »' -e 'end try'se canaliza ( |) a awkdonde crea una subcadena, imprimiendo solo el contenido codificado en Hex, no la parte del envoltorio de datos , ya que no se procesaría correctamente en xxdel siguiente paso del proceso.

A continuación, debe canalizarse ( |) para xxdque la conversión ASCII Textse escriba en un archivo de disco mediante la redirección de E/S, por ejemplo >, en la ruta de destino del nombre de archivo .

La siguiente imagen es del Visor del portapapeles alternando entre las vistas de codificación de texto ASCII y hexadecimal, que muestra Hello World!una copia de un documento de texto enriquecido, el que se usa en este ejemplo.

Visor del portapapeles


Espero que esto proporcione una mejor comprensión de cómo funciona AppleScript con el contenido de texto enriquecido en el portapapeles, ya que de cualquier manera debe realizarse la conversión de una cadena codificada en hexadecimal a texto ASCII y esto se realiza de forma transparente en el código de ejemplo original y requiere un procesamiento adicional . fuera del código AppleScript que se está procesando osascriptcuando se usa el do shell script comando en este contexto.

Los comentarios no son para una discusión extensa; esta conversación se ha movido a chat .
Esfera de @rubik, este código tenía el mismo error que en su otra pregunta en el sentido de que el archivo de destino se sobrescribía internamente y no se eliminaba. He corregido el error agregando dentro de un bloquedelete the file (theRichTextFileName as POSIX file) Finder en la rama else agregada de la declaración . tell if the button returned of the result is "No" then
Esfera de @rubik, en cuanto a la modificación que solicitó, tendré que revisar el código y ver qué se puede hacer, sin embargo, dado que esta respuesta, ahora tal como está, en realidad responde a la pregunta original, creo que cualquier modificación debería ser intentado primero por usted y luego publicado como una pregunta separada si no puede lograr su objetivo. En mi opinión, no es exactamente justo regresar meses después solicitando que se realicen modificaciones a una respuesta ya aceptada y votada que agrega parámetros adicionales no formulados previamente a la pregunta original.
Pido disculpas. Tienes razón. Voy a crear una nueva pregunta.
Descubrí cómo agregar líneas en blanco al archivo cuando tengo un portapapeles de texto sin formato y, por lo tanto, un archivo .txt, pero no pude averiguar cómo hacerlo con un portapapeles HTML o RTF. Aquí está esa pregunta.

Los archivos RTF, a diferencia de los archivos TXT, no son solo texto sin formato, sino que tienen una estructura básica. Para crear un archivo RTF, use la siguiente estructura (en lugar del texto sin formato de su teclado):

{\rtf1\ansi\deff0 {\fonttbl {\f0 Verdana;}}
\f0\fs16
Hello World!
}

Fuente: https://www.safaribooksonline.com/library/view/rtf-pocket-guide/9781449302047/ch01.html

He editado mi pregunta para que quede más clara. ¿Tiene alguna idea de cómo puedo pegar el texto del portapapeles en el archivo RTF y hacer que el archivo RTF coincida con la fuente, el tamaño, el color, etc. del texto del portapapeles?
Estoy bastante seguro de que lo que está diciendo es que si coloca esos metadatos en un archivo de texto sin formato y le da la extensión .rtf, entonces tiene un archivo .rtf válido.
@JonathanWarner De hecho lo soy.
@rubik'ssphere Simplemente reemplace Hello World con el texto de su portapapeles y cree el archivo, la fuente del texto copiado debe anular el valor predeterminado de Verdana.
@John Ramos, en su comentario dijo "... De hecho lo soy" al comentario de JonathanWarner, sin embargo, cómo está escrita su respuesta, eso realmente no está bien implícito, y mucho menos no da un ejemplo práctico de cómo aplicarlo en codificado formato, que habría sido una mejor respuesta y no hay ambigüedad sobre lo que quería decir. Sin embargo, la esfera de Rubik no quiere tomar texto sin formato del portapapeles y convertirlo en texto enriquecido, quiere el texto enriquecido que está allí, escrito a un archivo de disco para que mantenga todos los atributos de texto enriquecido que ya tiene, no los que tendría que codificar manualmente para emplear su respuesta.
@ user3439894 Mi respuesta fue solo un buen punto de partida, tal vez no una respuesta completa ya que no hago Applescripts. Mi mejor suposición es que los atributos de texto enriquecido del texto del portapapeles anularán la fuente, el color y la configuración de tamaño elegidos y funcionarán exactamente como lo desea el OP. Si mi respuesta no es satisfactoria, creo que Applescript podría no ser capaz de lograr esto, y podría ser necesario crear una aplicación en Xcode. Si ese es el caso, Stack Overflow es un buen lugar para esta pregunta.
Así que solo espero que @rubik'ssphere al menos pruebe lo que tengo aquí.
@John Ramos, usted dijo: " Si mi respuesta no es satisfactoria, creo que Applescript podría no ser capaz de lograr esto, y podría ser necesario crear una aplicación en Xcode". ¿Leyó mi respuesta? Por supuesto, se puede hacer en AppleScript, ya que no habría proporcionado la respuesta de AppleScript que hice, si no pudiera. Creo que el problema es como dijiste, " ya que no hago Applescripts " y aquí radica el problema, ya que nunca te das cuenta o entiendes por qué lo que escribiste para una respuesta no se puede usar en el contexto de la información presentada. dentro del OP. Continúa en el siguiente comentario...
En cuanto a " Así que solo espero que @rubik'ssphere al menos intente lo que tengo aquí ", bueno, lamento decirlo, ¡pero simplemente no se puede usar en el contexto solicitado! Cuando no comprenda el tema, en este caso AppleScript, y cómo funciona, especialmente en referencia al contexto solicitado, es mejor que no intente responder la pregunta. Realmente no estoy tratando de ser duro con usted o molestarlo con lo que dije y creo que si entendiera AppleScript y el contexto de cómo se aplica a la información presentada en el OP, lo entendería y no tendría Publica lo que hiciste al principio.
@user3439894 OK, solo quería brindar información sobre la estructura de los archivos RTF, porque parece más lo que se pregunta aquí que específicamente AppleScript.

Aquí hay funciones basadas en la respuesta del usuario 3439894 para imprimir la versión de texto enriquecido del portapapeles convertido a HTML o la versión HTML del portapapeles. La versión HTML del portapapeles se incluye para el texto copiado desde una vista web.

ppr(){ osascript -e'the clipboard as"RTF "'|sed 's/«data RTF //;s/»//'|xxd -r -p|textutil -convert html -stdin -stdout; }

pph(){ osascript -e'the clipboard as"HTML"'|sed 's/«data HTML//;s/»//'|xxd -r -p; }

Los sedcomandos anteriores también se pueden reemplazar con ruby -lpe'$_=$_[10..-2]'(aunque eso seleccione las posiciones de byte incorrectas en la configuración regional C).

En versiones anteriores de OS X, pbpaste -Prefer rtfse imprimía la versión RTF del portapapeles, pero ahora solo parece imprimir texto sin formato.