7

I have an Automator Service that takes the current text on the clipboard and writes it to a new .txt file. This is executed simply with the following piece of AppleScript code:

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

This code is from the following Stack Overflow answer:

AppleScript to paste text from clipboard into a file

However, this code converts the clipboard contents to plain text, removing all of the text's formatting.

Is it possible to use AppleScript to write the clipboard contents to an .rtf or .rtfd file, while preserving the original formatting of the clipboard contents? By original formatting, I am referring to stylistic data such as typographical emphasis (bold, italics, underline), font size, and text color.

On a whim, I tried the following command:

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

But this command resulted in an .rtf file that my computer was unable to read or open.

2
  • I've updated my answer to work with both RTF and HTML Clipboard content. Commented Sep 14, 2016 at 2:19
  • Did my updated answer work for you? Commented Sep 18, 2016 at 14:39

3 Answers 3

12

This answer has been updated to reflect rubik's sphere's misunderstanding between what Rich Text is (what was originally asked about) and what's actually being worked with from Google Chrome, being HTML. (See comments moved to chat.)

I'm leaving the original answer as is, and below this new content, as it does technically answer the original question as asked. It also contains relevant information to the overall process of dealing with Clipboard content in the context of the original and modified question.

The code below is example code to be tested and run within (Apple) Script Editor, as aside from the first sentence in the question, no explicit and specific usage within Automator has, yet, been given. The code may need to be edited to work within the unknown usage in Automator. That said, as written, if the entire script below was placed by itself into a Run AppleScript action, by itself, in Automator... it works as is. If using only segments of the code, some changes to the existing code could need to be made.

The code below contains sufficient commenting so as to understand overall what the script is doing.


--        # 
--        # 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

Original answer to the original question asked:

To get Rich Text content from the Clipboard into a file using AppleScript, it's a bit more complex then a simple do shell script command.

The example AppleScript code below will, if the target file doesn't already exist and if Rich Text content exists on the Clipboard, write it to a file. It will have all the attributes as the RichText content on the Clipboard has, as it did when copied to the Clipboard.

Open Script Editor and copy and paste the code below into a new Untitled document and then run it from Script Editor, reviewing the output in Events/Replies. Run it a couple of times, with and without Rich Text content on the Clipboard and with and without the existence of the file, on the hard drive, defined by set theRichTextFileName ... at the start of the script.

You'll see the code makes sure the file doesn't exist, so as not to overwrite an exiting file of the target name and location and if the Clipboard doesn't contain Rich Text content, it displays a message for that too.

Now if using this in an Automator Service e.g., where Service receives selected rich text, then the code can be modified not to trap for an error if Rich Text content isn't on the Clipboard as the service will not appear on the Services menu if Rich Text is not selected in a document. Also if you want to overwrite the target file at its designated location the code around that can be removed too. I'll give those code examples as well.


Example code to paste into Script Editor for testing and review:

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

Example code to use in an Automator Service e.g., where Service receives selected rich text:

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

Example code to use in an Automator Service e.g., where Service receives selected rich text and overwrites existing target file:

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

The image below is of an example Automator Service that creates the New RichText Filename.rtf file from selected Rich Text from the Create Rich Text file from Clipboard service on the Services Context menu (by right-click) or Application_name > Services > menu, when Rich Text is selected in a document.

Automator Service - Create Rich Text file from Clipboard


Now these are just examples and additional logic can be coded to tailor it to meet ones needs. As an example, code could be added to automatically increment an existing file's name so as not to overwrite it, or prompt for a new file name and complete the operation vs. aborting with a message that the file already exists, etc.


Update for use with a do shell script command:

If you really want to do it using a do shell script command, then use the following code while replacing /path/to/new rich text file.rtf with a valid path filename to a location you have write permissions. Note not to remove the \" before and after /path/to/new rich text file.rtf in the actual command as this handles the path filename if containing spaces. If the path filename does not contain spaces, then the \" before and after /path/to/new rich text file.rtf does not need to be used.

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\""

Here's the command line showing as wrapped text, for easier viewing:

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\""

  • While one can copy and paste (recommend) the code, nonetheless here's how to type the Double Angle Quotation Marks, which are also available under Parentheses in (Special) Characters, e.g. optioncommandT in TextEdit.

    Note: Keep in mind the spacing in «class RTF » is meant to be offset in this use case.

    • Typing Double Angle Quotation Marks

      • « Left Double Angle Quotation Mark, press: option\
      • » Right Double Angle Quotation Mark, press: shiftoption\

Note that, as written, this do shell script command overwrites the output file if it already exists, without prompting! It will either be a zero length file if the Clipboard does not contain any Rich Text content, otherwise the file will be of necessary length to contain the Rich Text content from the Clipboard. Obviously, additional logic could be coded into the osascript command however if you need more complexity then this you're better off using the method first presented in this answer. Or using an external script being called by do shell script command that handles all the necessary logic and error handling based on the complexity of the overall conditions this will be applied under.

  • Why such complexity vs. do shell script "pbpaste > /path/to/clipboard-file.rtf"?

That's a good question and while pbpaste does have the -Prefer {txt | rtf | ps} option, nonetheless pbpaste -Prefer rtf may not output Rich Text even if it exists on the Clipboard. Or what it does output, if not ASCII Text, will not be a form of Rich Text that is understood, by e.g TextEdit, and or will not contain all the Rich Text attributes if any, that the Clipboard content contains.

This makes it necessary to get the Rich Text content on Clipboard in a different manner and why get the clipboard as «class RTF » is being used instead. When using a do shell script command with this, it requires additional processing to utilize the data returned, as it's in a data wrapper when returned and not immediately usable, thus requires further processing.

As an example, Hello World! in Rich Text on the Clipboard can look like this in ASCII Text:

{\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!}

The above format unfortunately is not easily usable, if at all, in AppleScript and I believe that's why being grabbed as Hex encoded data is required.

  • I'm basing this on the fact that even though the get clipboard info command for the Clipboard content in this example returns {«class RTF », 265} among the info returned, nonetheless while the ASCII Text form of this Rich Text content is 265 bytes long it is returned in Hex format at over twice the bytes with the data wrapper. The fact that it's returned in Hex by get the clipboard as «class RTF » in both the Script Editor or using osascript support this supposition.

Here's the same Hello World! in Rich Text on the Clipboard in Hex:

7B5C727466315C616E73695C616E7369637067313235325C636F636F61727466313138375C636F636F617375627274663430300A7B5C666F6E7474626C5C66305C666E696C5C66636861727365743020436F6D696353616E734D533B7D0A7B5C636F6C6F7274626C3B5C7265643235355C677265656E3235355C626C75653235353B7D0A5C706172645C74783732305C7478313434305C7478323136305C7478323838305C7478333630305C7478343332305C7478353034305C7478353736305C7478363438305C7478373230305C7478373932305C7478383634305C7061726469726E61747572616C0A0A5C66305C625C66733336205C6366302048656C6C6F20576F726C64217D

However, what's returned by get the clipboard as «class RTF » for the Hello World! example above is:

«data RTF 7B5C727466315C616E73695C616E7369637067313235325C636F636F61727466313138375C636F636F617375627274663430300A7B5C666F6E7474626C5C66305C666E696C5C66636861727365743020436F6D696353616E734D533B7D0A7B5C636F6C6F7274626C3B5C7265643235355C677265656E3235355C626C75653235353B7D0A5C706172645C74783732305C7478313434305C7478323136305C7478323838305C7478333630305C7478343332305C7478353034305C7478353736305C7478363438305C7478373230305C7478373932305C7478383634305C7061726469726E61747572616C0A0A5C66305C625C66733336205C6366302048656C6C6F20576F726C64217D»

The Hex encoded string is in a «data RTF » wrapper which needs to be removed before converting the Hex encoded content to ASCII Text to be written to a disk file by the use of I/O Redirection, e.g.>, in the do shell script command example above.

So, the output of osascript -e 'try' -e ' get the clipboard as «class RTF »' -e 'end try' gets piped (|) to awk where it creates a substring, printing only the Hex encoded content itself, not the data wrapper portion, as that would not be processed properly by xxd in the next step of the process.

It then has to be piped (|) to xxd for conversion to ASCII Text to be written to a disk file using I/O Redirection, e.g.>, to the target path filename.

The image below is of Clipboard Viewer toggling between ASCII Text and Hex encoding views, showing Hello World! copied from a Rich Text Document, the one used in this example.

Clipboard Viewer


I hope this provides a better understanding of how AppleScript works with Rich Text content on the Clipboard, as either way conversion from a Hex encoded string to ASCII Text has to take place and this is being done transparently in the original example code while requiring additional processing outside of AppleScript code being processed by osascript when using the do shell script command in this context.

5
  • Comments are not for extended discussion; this conversation has been moved to chat.
    – nohillside
    Commented Sep 11, 2016 at 11:24
  • @rubik's sphere, This code had the same bug as in your other question in that the target file was being overwritten internally not deleted. I've corrected the bug by adding delete the file (theRichTextFileName as POSIX file) within a Finder tell block in the added else branch of the if the button returned of the result is "No" then statement. Commented Feb 9, 2017 at 17:14
  • @rubik's sphere, As to the modification you've asked for, I'll have to review the code and see what can be done, however since this answer, now as is, actually answers the original question asked, I feel any modification should be attempted first by you and then posted as a separate question if you can't achieve your goal. IMO It's not exactly fair to come back months later requesting modifications be made to an already accepted and up-voted answer that adds additional previously unasked parameters to the original question. Commented Feb 9, 2017 at 17:15
  • I apologize. You're right. I will create a new question. Commented Feb 9, 2017 at 17:41
  • I figured out how to add blank lines to the file when I have a plain text clipboard and thus .txt file, but I couldn't figure out how to do it with an HTML or RTF clipboard. Here's that question. Commented Feb 9, 2017 at 19:55
1

RTF files, unlike TXT files, are not just plain text but have a basic structure. To create an RTF file, use the following structure (instead of just the plain text from your keyboard):

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

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

10
  • I've edited my question so it is clearer. Do you have any idea how I can paste the clipboard text into the RTF file, and have the RTF file match the font, size, color, etc. of the clipboard text? Commented Sep 3, 2016 at 23:35
  • Pretty sure what he's saying is that if you put that metadata in a plain text file, and give that the extension .rtf, then you have a valid .rtf file.
    – Toadfish
    Commented Sep 5, 2016 at 12:52
  • @JonathanWarner I am indeed.
    – owlswipe
    Commented Sep 5, 2016 at 12:55
  • @rubik'ssphere Just replace Hello World with your clipboard text and create the file, the font of the copied text should override the default there of Verdana.
    – owlswipe
    Commented Sep 5, 2016 at 12:56
  • @John Ramos, In your comment you said `... I am indeed" to JonathanWarner's comment however, how your answer is written, that really isn't implied well, much less it gives no practical example of how to apply it in coded format, which would have been a better answer then it was and no ambiguity about what you meant. However, rubik's sphere does not want to take plain text from the clipboard and make it rich text, he wants the rich text that is there, written to a disk file so it maintains all the rich text attributes it already has, not ones you'd have to manually code to employ your answer. Commented Sep 5, 2016 at 15:55
0

Here's functions based on the answer by user3439894 for printing the rich text version of the clipboard converted to HTML or the HTML version of the clipboard. The HTML version of the clipboard is included for text copied from a web view.

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; }

The sed commands above can also be replaced with ruby -lpe'$_=$_[10..-2]' (even though that selects the wrong byte positions in the C locale).

In older versions of OS X pbpaste -Prefer rtf printed the RTF version of the clipboard but now it just seems to print plain text.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .