Script: Split RTF(D) records (preserving images and attachments)

This script explodes RTF(D) records preserving images and attachments.

It removes leading blank lines, there’s no trying out necessary how many returns used together with your delimiter produce best results.

The script needs a closing delimiter at the end, if this is missing it adds one but it’s removed afterwards (there’s not really something removed from your source record, it restores the text as it was before the script ran).

There’s one important thing I recognized too late as I was too lazy to really look into at this point:

The delimiter has to be at the beginning or end of a paragraph - if not you won’t get the desired result. I’ll see if this can be easily changed.

Don’t forget to set theDelimiter

… and try with duplicates

Happy exploding!

-- Explode RTF(D)s at Delimiters (preserving images and attachments)

property theDelimiter : "^^^"
property trimStart : true -- remove leading blank lines

tell application id "DNtp"
	try
		set windowClass to class of window 1
		if {viewer window, search window} contains windowClass then
			set currentRecord_s to selection of window 1
		else if windowClass = document window then
			set currentRecord_s to content record of window 1 as list
		end if
		
		repeat with theRecord in currentRecord_s
			set plainText to plain text of theRecord
			
			if word -1 in plainText is not in theDelimiter then
				tell text of theRecord
					set endAttributeText to (text of attribute run -1) as string
					set text of attribute run -1 to ((endAttributeText & return & theDelimiter) as string)
				end tell
				set addedEndDelimiter to true
			else
				set addedEndDelimiter to false
			end if
			
			set theName to name of theRecord
			set theDelimiterCount to do shell script "echo " & quoted form of plainText & " | grep -c " & quoted form of theDelimiter
			show progress indicator "Exploding... " & theName steps theDelimiterCount with cancel button
			
			set currentDate to do shell script "date \"+%Y-%m-%d% %H.%M.%S\""
			set theGroup to create location (location of theRecord) & "/" & theName & " (Exploded) " & currentDate & "/" in current database
			
			set theAttributeRuns to {}
			
			repeat with thisAttribute in attribute runs of text of theRecord
				set end of theAttributeRuns to thisAttribute
			end repeat
			
			set thisName to ""
			set theseAttributeRuns to {}
			
			repeat with theAttributeRun in theAttributeRuns
				set theAttributeText to ((text of theAttributeRun) as string)
				
				if theAttributeText does not contain theDelimiter then
					set end of theseAttributeRuns to theAttributeRun
					if thisName = "" then
						try
							set w to first word of theAttributeText
							set thisName to theAttributeText
						end try
					end if
					
				else
					if thisName = "" then
						try
							set w to first word of theAttributeText
							set thisName to theAttributeText
						end try
					end if
					
					step progress indicator "Creating... " & thisName
					set theRecord to create record with {name:thisName, rich text:"", type:rtfd} in theGroup
					
					tell text of theRecord
						if theAttributeText = theDelimiter then
							repeat with thisAttributeRun in theseAttributeRuns
								make new attribute run at end with data thisAttributeRun
							end repeat
							
						else
							set end of theseAttributeRuns to theAttributeRun
							repeat with thisAttributeRun in theseAttributeRuns
								make new attribute run at end with data thisAttributeRun
							end repeat
							set text of attribute run -1 to my findAndReplaceInText(theAttributeText, theDelimiter, "")
						end if
						
						if trimStart = true then
							set i to 1
							set cnt to number of paragraphs
							repeat while i ≤ cnt
								if length of ((text of paragraph i) as string) is 1 then
									set text of paragraph i to ""
									set cnt to cnt - 1
								else
									exit repeat
								end if
							end repeat
						end if
					end tell
					
					set thisName to ""
					set theseAttributeRuns to {}
				end if
			end repeat
			
			if addedEndDelimiter = true then
				tell text of theRecord to set text of attribute run -1 to endAttributeText
			end if
			
			hide progress indicator
		end repeat
		
		
	on error error_message number error_number
		hide progress indicator
		if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
		return
	end try
end tell

on findAndReplaceInText(theText, theSearchString, theReplacementString)
	set AppleScript's text item delimiters to theSearchString
	set theTextItems to every text item of theText
	set AppleScript's text item delimiters to theReplacementString
	set theText to theTextItems as string
	set AppleScript's text item delimiters to ""
	return theText
end findAndReplaceInText

:clap: :pray: :+1:

1 Like

Isn’t it easier to get inside RTFD package and take a ready RTF and/or attachments from there?

Don‘t get what you mean here.

The string: tell application "Finder" to set theList to every file in ((POSIX file thePath) as alias) will give you all content files of RTFD at the path thePath: RTF and all the attachments. Do what you want with them.

Ah.

This script explodes RTF(D) records at a delimiter you‘ve set.

In your RTF(D) you have for example used ^^^ as delimiter between parts of your record, in order to split the record later. Now there are three ways I know of:

  • Manually: go through your record, copy each part, create a new record, paste the copied part, rename the new record.

  • AppleScript: use one of the existing scripts but loose images and attachments in the new records.

  • AppleScript: use the script posted above preserving images and attachments.

Prepare an RTF(D) with delimiters and try the script to see what it does.

oh, sorry, misunderstood “explosion”… )

In Tinderbox it is called „explode“. Changed thread title to „split“.

IMHO, this title looks better :wink:
Bookmarked

In case you’re trying to run the script I posted in this thread you’ll find that it doesn’t work in DEVONthink 3.6. That’s due to DEVONthink’s new handling of “invalide arguments”.

After the release of DEVONthink 3 I decided to continue to use “search window” in scripts so that DEVONthink 2 users could use them in, well, search windows.

With version 3.6 that’s not possible anymore.

If you want to use the script you’ll have to replace this voluminous block …

set windowClass to class of window 1
if {viewer window, search window} contains windowClass then
	set currentRecord_s to selection of window 1
else if windowClass = document window then
	set currentRecord_s to content record of window 1 as list
end if

… with this neat line …

set currentRecord_s to selected records

… which does what the six lines have done. Wow, that’s great! :smiley:

i tried to do this with the delimiter “end of document” - is there a reason that wouldn’t work? for some reason it just kept identifying my delimiter as a quotation mark, which caused a LOT of splits.

No idea. You could duplicate the record and replace the delimiter that didn’t work with something else.

i got this script to work, and it seemed to be working great - thank you much @pete31! but then suddenly it started splitting the document at strange places, not just the delimiter. i’m assuming this has somethign to do with the attribute runs element, but i’m not sure what it is. i didn’t change the script at all, but i changed the formatting of some of my RTF documents. I have the delimiter currently as “§§”. any ideas on what i’m doing wrong? thank you so much.

oh for some reason its now not working again! i don’t know what i’m doing wrong. very strange!!

Hi, sorry for the late reply. I did not use the script in a very long time but now used it again. I had some problems too because I forgot that the delimiter may not be in the same attribute run as e.g. the chapter titles. After I changed the font size of all delimiters (so that they don’t match the chapter titles’ attribute runs anymore) it worked without problems. I guess that would solve it for you too.

Please use the new Script: Split RTF(D) at Delimiter