1

I have an AppleScript code that I am having trouble writing.

I would like a new, blank .rtf file to be created with a user-defined filename, saved to a predetermined location, with a predetermined document template. I already know how to accomplish all of these functions in AppleScript.

I would then like the plain text on the clipboard to be written to the document and the plain text to match the style of the predetermined document template. It is this desire that creates difficulty.

If the clipboard contains any formatting (such as font style, font size, emphasis style, text color, or highlight color), I would like this information to be ignored and instead have the clipboard text match the predetermined formatting of the RTF file.

Here's the code that I have at this point. Feel free to abandon my approach, as I cannot get it to work.

(The following AppleScript code is almost identical to this code by user3439894. I've flagged my (flawed) additions by introducing lines of code that I wrote myself with "My new code.")

global theCustomRichTextFilePathname
global customFilename
global fullDataToWriteToRTFfile




repeat
    set customFilename to the text returned of (display dialog "Save as:" with title "Do you want to create a new, blank TextEdit RTF document?" default answer "")
    if customFilename is "" then
        beep
        display alert "The filename cannot be empty!" message "Please enter a name to continue..."
    else
        exit repeat
    end if
end repeat


set theCustomRichTextFilePathname to ((path to desktop) & customFilename & ".rtf") as string




-- My new code:

set hexOfTheClipboardText to convertATextStringToItsHexFormat()

set hexOfTheClipboardText to (hexOfTheClipboardText & "7D")
-- The 7D in the above line represents the hex code of the closing bracket } that must be the last character of the RTF data string.

set dataForTheRTFDocumentTemplateThatIWant to "7B5C727466315C616E73695C616E7369637067313235325C636F636F61727466313430345C636F636F617375627274663437300A7B5C666F6E7474626C5C66305C6673776973735C6663686172736574302048656C7665746963613B7D0A7B5C636F6C6F7274626C3B5C7265643235355C677265656E3235355C626C75653235353B7D0A5C6D6172676C313434305C6D61726772313434305C766965777731303830305C7669657768383430305C766965776B696E64300A5C706172645C74783732305C7478313434305C7478323136305C7478323838305C7478333630305C7478343332305C7478353034305C7478353736305C7478363438305C7478373230305C7478373932305C7478383634305C7061726469726E61747572616C5C7061727469676874656E666163746F72300A0A5C66305C66733336205C636630205C0A"

set fullDataToWriteToRTFfile to (dataForTheRTFDocumentTemplateThatIWant & hexOfTheClipboardText)


-- End of my new code.




tell application "Finder"
    try
        if exists file theCustomRichTextFilePathname then
            tell current application
                display dialog "The file \"" & POSIX path of theCustomRichTextFilePathname & "\" already exists!" & return & return & "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 result is "No" then
                    --  # The file already exists, chose not to overwrite it, just open the document.
                    my openDocument()
                else
                    --  # The file already exists, chose to overwrite it, then open the document.
                    my createCustomRTFDocument()
                    my openDocument()
                end if
            end tell
        else
            --  # The file does not already exist. Create and open the document.
            tell current application
                my createCustomRTFDocument()
                my openDocument()
            end tell
        end if
    on error eStr number eNum
        activate
        display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with icon caution
        return
    end try
end tell







on createCustomRTFDocument()
    tell current application

         -- My new line of code:
        set customRTFDocumentTemplate to «data RTF fullDataToWriteToRTFfile»
         -- THE ABOVE LINE DOES NOT WORK AND SENDS AN ERROR.



            try
                set referenceNumber to open for access theCustomRichTextFilePathname with write permission
                write customRTFDocumentTemplate to referenceNumber
                close access referenceNumber
            on error eStr number eNum
                activate
                display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with title "File I/O Error..." with icon caution
                try
                    close access referenceNumber
                end try
                return
            end try
        end tell
    end createCustomRTFDocument






on openDocument()
    try
        tell application "TextEdit"
            open file theCustomRichTextFilePathname
            activate
            tell application "System Events"
                set displayedName to get displayed name of file theCustomRichTextFilePathname
                if displayedName contains ".rtf" then
                    tell application "TextEdit"
                        set bounds of window (customFilename & ".rtf") to {160, 22, 883, 639}
                    end tell
                    key code 125
                else
                    tell application "TextEdit"
                        set bounds of window customFilename to {160, 22, 883, 639}
                    end tell
                    key code 125
                end if
            end tell
        end tell
    on error eStr number eNum
        activate
        display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with icon caution
        return
    end try
end openDocument





-- New code:

on convertATextStringToItsHexFormat()
    set hexOfText to ""
    set letters to every character in the clipboard
    repeat with letter in letters
        set numberRepresentingTheID to (id of letter)
        set hexOfText to hexOfText & list2hex(numberRepresentingTheID)
    end repeat
    return hexOfText
end convertATextStringToItsHexFormat

on list2hex(X)
    set y to ""
    repeat with Z in {} & X
        set y to y & num2hex(Z) & ","
    end repeat
    set y to text 1 thru -2 of y
    return y
end list2hex

on num2hex(X)
    set y to ""
    repeat until X = 0
        set n to X mod 16
        if n > 9 then
            set y to character id (55 + n) & y
        else
            set y to character id (48 + n) & y
        end if
        set X to X div 16
    end repeat
    return y
end num2hex

-- End of new code

I know that perhaps the easiest way to accomplish what I want is to simply keystroke the keyboard shortcut to "Paste and Match Style" in TextEdit.app, i.e., ⌥ option + + ⌘ command + V. But I was hoping for a "back end" and more sophisticated method, since keystroking carries with it a noticeable delay.

6
  • The short answer is, you can't do it the way you're trying to! The «data RTF» wrapper has to be as shown in my answer, it cannot be concatenated, quoted or contain a variable within the «data RTF» wrapper. That said, the plain text context that you want to write to the RTF document, is this going to be written at the end of the document and is it just a one time thing at it initial creation of the document or something you need to repeat over and over during the run of the script? BTW It can be done in a different way, in the background, before the document is initially opened in TextEdit. Feb 8, 2017 at 0:55
  • I understand now, thank you. I am not at all wedded to approaching this code the way that I've been trying to; this is just the only way that I could think of. It is by no means the best or most practical approach. Additional info about the script: I want the clipboard-as-plain-text to be written to the end of the RTF document. It is just a one-time thing at the initial creation of the document. The script is complete after the file is opened (via the openDocument() subroutine). The writing to file will not repeat more than once in the script. Feb 8, 2017 at 1:16
  • So if I take my code from the "this code by user3439894" linked answer and modify it to, create the RTF file from the RTF Template Document and then add whatever text, even if RTF text, is on the Clipboard as plain text to the end of the RTF file created from the RTF Template Document so it is still a valid RTF Document and will then obviously have the attributes of what is set at the end of the template document, this is what you'd be happy with? Question: Will that newly created document be opened directly afterwards and if yes do you want to cursor at the end of the document? Feb 8, 2017 at 1:46
  • On the first question, yes. On the second question, yes. On the third question, you read my mind, yes; I would like the cursor to be moved to the last line of the RTF file. Also, it would be excellent if a blank line was inserted into the RTF file before the clipboard-as-plain-text is written to file, and another blank line was inserted immediately after the clipboard-as-plain-text text. Also, it would be great if the first blank line matched the font size of the RTF Template Document, as opposed to the blank line being formatted in TextEdit's default font size (12). Feb 8, 2017 at 1:58
  • Before I first replied I had already tested some code that added the blank line before and after. As to "Also, it would be great if the first blank line matched ..." there is an anomaly with this. That is to say when the document is opened by the script and focus is set to the other then the first line and you then go back to the first line it will be that of the template size. However when you manually open the document from the Desktop the cursor defaults to the first line and it's going to be the default 12, but set focus elsewhere and back and it's e.g. 18 not 12. This can't be changed. Feb 8, 2017 at 2:34

3 Answers 3

2

Here's another method with a Cocoa-AppleScript applet, this script create the RTF file with the methods from the Objective-C code.

-- Cocoa-AppleScript
use framework "Foundation"
use scripting additions
repeat
    set customFilename to the text returned of (display dialog "Save as:" with title "Do you want to create a new, blank TextEdit RTF document?" default answer "")
    if customFilename is "" then
        beep
        display alert "The filename cannot be empty!" message "Please enter a name to continue..."
    else
        exit repeat
    end if
end repeat
set theCustomRichTextFilePathname to ((path to desktop as string) & customFilename & ".rtf")

tell application "Finder" to set b to exists file theCustomRichTextFilePathname
tell current application
    try
        if b then
            display dialog "The file \"" & POSIX path of theCustomRichTextFilePathname & "\" already exists!" & return & return & "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 result is "No" then
                --  # The file already exists, chose not to overwrite it, just open the document.
                my openDocument(theCustomRichTextFilePathname)
                return
            end if
        end if
        -- else, the file already exists, chose to overwrite it or the file does not already exist
        my createCustomRTFDocument(theCustomRichTextFilePathname)
    on error eStr number eNum
        activate
        display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with icon caution
        return
    end try
end tell

on openDocument(f)
    tell application "TextEdit"
        set theDoc to open file f
        set bounds of (first window whose its document is theDoc) to {160, 22, 883, 639}
        activate
    end tell
    tell application "System Events" to key code 125 using command down --put the blinking cursor at the end of the document
end openDocument

on createCustomRTFDocument(f)
    set myText to return & (the clipboard as string) -- Concatenation of an empty line and the text in the clipboard
    set theFile to POSIX path of f
    tell current application
        set myFont to its (NSFont's fontWithName:"Helvetica" |size|:18) -- font and size of the rtf document
        set myColor to its (NSColor's blackColor()) -- color of the text of the rtf document
        set theDict to its (NSDictionary's alloc()'s initWithObjectsAndKeys_(myColor, its NSForegroundColorAttributeName, myFont, its NSFontAttributeName, missing value))
        set AttrString to its ((NSAttributedString's alloc)'s initWithString:myText attributes:theDict) -- create an attributed string
        set rtfData to AttrString's RTFFromRange:{0, AttrString's |length|()} documentAttributes:{NSRTFTextDocumentType:(its NSDocumentTypeDocumentAttribute)} -- create the data from an attributed string
    end tell
    rtfData's writeToFile:theFile atomically:true -- write the data to the RTF file
    if the result then my openDocument(f) -- open the file 
end createCustomRTFDocument
4
  • Nice, I knew that had to be a better way, I just didn't know what it was though! :) +1 Feb 8, 2017 at 19:24
  • I've accepted this answer as the "Best Answer" because user3439894's answer potentially fails to correctly process certain rich text punctuation symbols and jackjr300's previous answer stipulates that the TextEdit preference for "Format" is set to "Rich text." This answer is free of both issues. Feb 9, 2017 at 3:39
  • @jackjr300 I just realized that your code adds a blank line at the top of the document, but not the bottom. Can you modify your code to add both a top and bottom blank line? Thank you. Feb 12, 2017 at 8:38
  • @jackjr300 Never mind, I figured it out. I changed set myText to return & (the clipboard as string) to set myText to return & (the clipboard as string) & return. Feb 12, 2017 at 20:54
2

This script create a new document in TextEdit and it use the properties of the text to change the formatting of the RTF document (look at the createCustomRTFDocument() handler) :

repeat
    set customFilename to the text returned of (display dialog "Save as:" with title "Do you want to create a new, blank TextEdit RTF document?" default answer "")
    if customFilename is "" then
        beep
        display alert "The filename cannot be empty!" message "Please enter a name to continue..."
    else
        exit repeat
    end if
end repeat
set theCustomRichTextFilePathname to ((path to desktop) & customFilename & ".rtf") as string

tell application "Finder" to set b to exists file theCustomRichTextFilePathname
tell current application
    try
        if b then
            display dialog "The file \"" & POSIX path of theCustomRichTextFilePathname & "\" already exists!" & return & return & "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 result is "No" then
                --  # The file already exists, chose not to overwrite it, just open the document.
                my openDocument(theCustomRichTextFilePathname)
                return
            end if
        end if
        -- else, the file already exists, chose to overwrite it or the file does not already exist
        my createCustomRTFDocument(theCustomRichTextFilePathname)
    on error eStr number eNum
        activate
        display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with icon caution
        return
    end try
end tell

on openDocument(f)
    tell application "TextEdit"
        set theDoc to open file f
        activate
        set bounds of (first window whose its document is theDoc) to {160, 22, 883, 639}
    end tell
    tell application "System Events" to key code 125 using command down -- put the blinking cursor at the end of the document
end openDocument

on createCustomRTFDocument(f)
    close access (open for access f) -- create a blank file , this command do nothing on an existing file
    -- delay 0.5 -- use the delay command, If you have permission issues (you can increase the number of seconds).

    set myText to return & (the clipboard as string) -- Concatenation of an empty line and the text in the clipboard
    set theFile to POSIX path of f
    tell application "TextEdit"
        set theDoc to make new document with properties {path:theFile} -- create new document

        -- this put the text in the document, *** use (font, size and color) properties to set the predetermined formatting of the RTF document *** {0, 0, 0} = black color
        make new attribute run at beginning of theDoc with data myText with properties {font:"Helvetica", size:18, color:{0, 0, 0}} -- this put the blinking cursor at the end of the document

        set bounds of (first window whose its document is theDoc) to {160, 22, 883, 639}
        save theDoc in theFile
        activate
    end tell
end createCustomRTFDocument
6
  • This is a nice approach and when I created the original script in the other question I wanted to take this approach but had some issues with my coding so I went the route I did. I'll study your code to lean where my mistakes were, thanks! Feb 8, 2017 at 5:01
  • That said, I do want to point out one thing, if a User has changed the default preferences for TextEdit from "Rich text" to "Plain text", as I have on my system, then your code creates a plain text document and the OP wants a rich text document. This is in part why I went the route I did, because I couldn't figure out how to create the document as Rich text without having to programmatically check the menu for Make Rich Text and click it and didn't want to go that route. So how can your code be modified to make it a RTF document upon creation if the Uses preferences is set to "Plain text"? Feb 8, 2017 at 5:02
  • If the default preferences for TextEdit is "Plain text", don't use this script, even if it is possible to use the Gui Scripting to change the document format (by selecting a menu item in the Format menu).
    – jackjr300
    Feb 8, 2017 at 15:18
  • Can I ask why not? If not, is there another way to test whether or not the document opened is RTF vs plain text at the time of creation or is it always going to be what is set in Preferences? Can your code not be modified to fit the parameters of the OP without GUI Scripting? Feb 8, 2017 at 15:38
  • Okay, I've found a way without using GUI Scripting to create the document as RTF when the properties for Format has been set to "Plain text". When the default preference for the Format of a TextEdit document has been changed to "Plain text", then a RichText key is created in the com.apple.TextEdit.plist file and is set to 0 as an integer, otherwise this key does not exist. Therefore, one can test for and temporarily change it to 1, create the document, then change it back to 0, using a do shell script command with the defaults command. I've tested this and it works. Feb 8, 2017 at 18:18
2

This answer incorporates the existing code from my answer you linked in your question with the following modifications.

In the openDocument() handler, key code 125 was changed to key code 125 using command down so as to send the cursor to the last line in the document upon being opened.

The createCustomRTFDocument() handler has my addPlainTextFromClipboardToEndOfNewelyCreatedRTFDocument() added after the closing of the newly created RTF Document, having been created from the Template. The creating of the RTF Document is now a two step process, first step creates the document from the template the second step adds text from the Clipboard as plain text, if text exists, which is done before the first time the document is opened for the User by the openDocument() handler.

A new handler was created and named addPlainTextFromClipboardToEndOfNewelyCreatedRTFDocument() and coded to do just as its name reads. It will get the text from the clipboard as plain text, if it exists, and write it to the end of the newly created RTF document created by the first part customRTFDocumentTemplate() handler.

I found a bug in my original code, the file was being overwritten internally not deleting the file and recreating it as it really should be in this case. I've added code to fix that in this code and will edit the other answer to add to that code. To account for the bug, I've added & return & return & "If yes, the file will be placed in the Trash." to the "Do you want to overwrite the file?" display dialog command in the primary tell application "Finder" block. Also added the following code within the else clause of the if the button returned of result is "No" then statement to handle the bug.

tell application "Finder"
    delete the file theCustomRichTextFilePathname
end tell

This too is within the primary tell application "Finder" block.

With the aforementioned changes, here is the new code:


--  # The variables for the target file's fully qualified pathname and custom filename needs to be global as they are called from both the handlers and other code.

global theCustomRichTextFilePathname
global customFilename

--  # The createCustomRTFDocument handler contains a custom template for the target RTF document.

on createCustomRTFDocument()
    tell current application
        set customRTFDocumentTemplate to «data RTF 7B5C727466315C616E73695C616E7369637067313235325C636F636F61727466313530345C636F636F617375627274663736300A7B5C666F6E7474626C5C66305C6673776973735C6663686172736574302048656C7665746963613B7D0A7B5C636F6C6F7274626C3B5C7265643235355C677265656E3235355C626C75653235353B7D0A7B5C2A5C657870616E646564636F6C6F7274626C3B3B7D0A5C706172645C74783732305C7478313434305C7478323136305C7478323838305C7478333630305C7478343332305C7478353034305C7478353736305C7478363438305C7478373230305C7478373932305C7478383634305C7061726469726E61747572616C5C7061727469676874656E666163746F72300A0A5C66305C66733336205C636630205C0A7D»
        try
            set referenceNumber to open for access theCustomRichTextFilePathname with write permission
            write customRTFDocumentTemplate to referenceNumber
            close access referenceNumber
            my addPlainTextFromClipboardToEndOfNewelyCreatedRTFDocument()
        on error eStr number eNum
            activate
            display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with title "File I/O Error..." with icon caution
            try
                close access referenceNumber
            end try
            return
        end try
    end tell
end createCustomRTFDocument

--  # The addPlainTextFromClipboardToEndOfNewelyCreatedRTFDocument handler attemps to get plain text from the clipboard 
--  # and if there is some, it's added to the end of the newely created RTF document created by the customRTFDocumentTemplate 
--  # handler so as to maintain a valid RTF Docuemnt. The addPlainTextFromClipboardToEndOfNewelyCreatedRTFDocument handler
--  # is called from the createCustomRTFDocument handler after it has created the new RTF document from the Template.

on addPlainTextFromClipboardToEndOfNewelyCreatedRTFDocument()
    tell current application
        set plainTextFromClipboard to (get the clipboard as «class utf8»)
        if plainTextFromClipboard is not equal to "" then
            set plainTextFromClipboard to return & plainTextFromClipboard & return & "\\
}" as «class utf8»
            try
                tell application "Finder"
                    set eofMinusOne to (size of file theCustomRichTextFilePathname) - 1
                end tell
                set referenceNumber to open for access theCustomRichTextFilePathname with write permission
                write plainTextFromClipboard to referenceNumber starting at eofMinusOne
                close access referenceNumber
            on error eStr number eNum
                activate
                display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with title "File I/O Error..." with icon caution
                try
                    close access referenceNumber
                end try
                return
            end try
        end if
    end tell
end addPlainTextFromClipboardToEndOfNewelyCreatedRTFDocument

--  # The openDocument handler opens and set the bounds of the theCustomRichTextFilePathname document while placing the cursor on the second line.

on openDocument()
    try
        tell application "TextEdit"
            open file theCustomRichTextFilePathname
            activate
            tell application "System Events"
                set displayedName to get displayed name of file theCustomRichTextFilePathname
                if displayedName contains ".rtf" then
                    tell application "TextEdit"
                        set bounds of window (customFilename & ".rtf") to {160, 22, 883, 639}
                    end tell
                    key code 125 using command down
                else
                    tell application "TextEdit"
                        set bounds of window customFilename to {160, 22, 883, 639}
                    end tell
                    key code 125 using command down
                end if
            end tell
        end tell
    on error eStr number eNum
        activate
        display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with icon caution
        return
    end try
end openDocument

--  # Get the name for the RTF document, ensuring it is not blank.

repeat
    set customFilename to the text returned of (display dialog "Save as:" with title "Do you want to create a new, blank TextEdit RTF document?" default answer "")
    if customFilename is "" then
        beep
        display alert "The filename cannot be empty!" message "Please enter a name to continue..."
    else
        exit repeat
    end if
end repeat

--  # Concatenate the default location (the User's Desktop) with the chosen filename while adding the proper file extension.

set theCustomRichTextFilePathname to ((path to desktop) & customFilename & ".rtf") as string

--  # Check to see if the target file already exists. If it does not exist, create and open it. If it does exist, either open it or overwrite it and open it, based on decision made.

tell application "Finder"
    try
        if exists file theCustomRichTextFilePathname then
            tell current application
                display dialog "The file \"" & POSIX path of theCustomRichTextFilePathname & "\" already exists!" & return & return & "Do you want to overwrite the file?" & return & return & "If yes, the file will be placed in the Trash." buttons {"No", "Yes"} default button 1 with title "File Already Exists..." with icon caution
                if the button returned of result is "No" then
                    --  # The file already exists, chose not to overwrite it, just open the document.
                    my openDocument()
                else
                    --  # The file already exists, chose to overwrite it, then open the document.
                    tell application "Finder"
                        delete the file theCustomRichTextFilePathname
                    end tell
                    my createCustomRTFDocument()
                    my openDocument()
                end if
            end tell
        else
            --  # The file does not already exist. Create and open the document.
            tell current application
                my createCustomRTFDocument()
                my openDocument()
            end tell
        end if
    on error eStr number eNum
        activate
        display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with icon caution
        return
    end try
end tell
3
  • I've found that your code does not convert certain rich text punctuation symbols (e.g., an apostrophe, em dash, and quotation marks) to plain text correctly. Instead, a Spanish letter (e.g., "Õ" or "Ñ") takes the symbol's place. To reproduce the issue that I am describing, copy the first paragraph of this article onto your clipboard and run your code. Feb 8, 2017 at 7:18
  • @rubik's sphere, Sorry, my testing was limited to some lines of code placed in a RTF file on the Clipboard. You may have to take the route presented jackjr300's answer, which was going to be my original approach when you asked about the other question you linked to. However, looking in the AppleScript Dictionary for TextEdit, I saw nothing to distinguish between a plain text document and a RTF as far as a property or attribute that could be set and why I went the route I did. jackjr300's approach will get the characters right but his code assumes the default is RTF and that's not good IMO. Feb 8, 2017 at 15:03
  • @rubik's sphere, The only way I saw to check for RTF was to programmatically check the menu for Make Rich Text and click it and didn't want to go that route, however unless someone can show a different method... well that may have to be the route to take. Feb 8, 2017 at 15:03

You must log in to answer this question.

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