How Do I base64 Decode and Encode Multiple Lines?

foundation
asobjc

(Jim Underwood) #1

Hey guys,

I’ve found a few bash commands that do this, but none of them seem to handle encode of multiple lines.

I’m hoping there is a ASObjC solution for this. Can anyone point me in the right direction?

I could also use help in extracting the encoded string from a XML string I get from an Keyboard Maestro Action object. It looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Action</key>
	<string>DisplayWindow</string>
	<key>MacroActionType</key>
	<string>InsertText</string>
	<key>StyledText</key>
	<data>
	cnRmZAAAAAADAAAAAgAAAAcAAABUWFQucnRmAQAAAC5WAQAAKwAAAAEAAABOAQAAe1xy
	dGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNDA0XGNvY29hc3VicnRmNDcwCntc
	Zm9udHRibFxmMFxmbmlsXGZjaGFyc2V0MCBIZWx2ZXRpY2FOZXVlO30Ke1xjb2xvcnRi
	bDtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7fQpccGFyZFx0eDU2MFx0eDExMjBcdHgx
	NjgwXHR4MjI0MFx0eDI4MDBcdHgzMzYwXHR4MzkyMFx0eDQ0ODBcdHg1MDQwXHR4NTYw
	MFx0eDYxNjBcdHg2NzIwXHBhcmRpcm5hdHVyYWxccGFydGlnaHRlbmZhY3RvcjAKClxm
	MFxmczI2IFxjZjAgJVZhcmlhYmxlJVRFU1RfX0VsYXBzZWRUaW1lJVwKVEVTVF9fVmFy
	MjoJJVZhcmlhYmxlJVRFU1RfX1ZhcjIlfQEAAAAjAAAAAQAAAAcAAABUWFQucnRmEAAA
	AJ4Ez1m2AQAAAAAAAAAAAAA=
	</data>
	<key>Text</key>
	<string>%Variable%TEST__ElapsedTime%
TEST__Var2:	%Variable%TEST__Var2%</string>
</dict>
</plist>

The text I need to decode is in the

<key>StyledText</key>
	<data>
       -- rich text base64 encoded
    </data>

So I need to:

  1. extract the rich text base64 encoded from the XML string.
  2. base64 Decode (results in multiple lines)
  3. Make some changes
  4. base64 encode multiple lines – haven’t been able to do this
  5. Update the xml source.

The only part I have not been able to figure out is #4.
I’ve been using RegEx for find, and then replace, but I’m thinking there is probably a better ASObjC dictionary method that I could/should use.

Here is the bash script, but it fails with multiple lines:

set encodedStr to "cnRmZAAAAAADAAAAAgAAAAcAAABUWFQucnRmAQAAAC5LAQAAKwAAAAEAAABDAQAAe1xy
  dGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNDA0XGNvY29hc3VicnRmNDcwCntc
  Zm9udHRibFxmMFxmbmlsXGZjaGFyc2V0MCBIZWx2ZXRpY2FOZXVlO30Ke1xjb2xvcnRi
  bDtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDBcZ3JlZW4wXGJsdWUwO30KXHBh
  cmRcdHg1NjBcdHgxMTIwXHR4MTY4MFx0eDIyNDBcdHgyODAwXHR4MzM2MFx0eDM5MjBc
  dHg0NDgwXHR4NTA0MFx0eDU2MDBcdHg2MTYwXHR4NjcyMFxwYXJkaXJuYXR1cmFsXHBh
  cnRpZ2h0ZW5mYWN0b3IwCgpcZjBcZnMyNiBcY2YyIFRFU1RfX1ZhcjM6CSVWYXJpYWJs
  ZSVURVNUX19WYXIzJX0BAAAAIwAAAAEAAAAHAAAAVFhULnJ0ZhAAAABoCs9ZtgEAAAAA
  AAAAAAAA"

--- base64 DECODE THE STRING --
set cmdStr to "echo '" & encodedStr & "' | openssl base64 -d"
set decodedStr to do shell script cmdStr

### In my production script will make changes to decodedStr here ###

--- NOW, base64 ENCODE after my changes ---
-- for testing, just encoding what I just decoded
set cmdStr to "echo -n '" & decodedStr & "' | openssl base64"
set encoded2Str to do shell script cmdStr
-->ERROR:  
(*
sh: -c: line 0: unexpected EOF while looking for matching `''
sh: -c: line 1: syntax error: unexpected end of file
*)

TIA for all help and suggestions.


(Shane Stanley) #2

That’s a property list, so I’d expect something like this to work:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set theString to "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
	<key>Action</key>
	<string>DisplayWindow</string>
	<key>MacroActionType</key>
	<string>InsertText</string>
	<key>StyledText</key>
	<data>
	cnRmZAAAAAADAAAAAgAAAAcAAABUWFQucnRmAQAAAC5WAQAAKwAAAAEAAABOAQAAe1xy
	dGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNDA0XGNvY29hc3VicnRmNDcwCntc
	Zm9udHRibFxmMFxmbmlsXGZjaGFyc2V0MCBIZWx2ZXRpY2FOZXVlO30Ke1xjb2xvcnRi
	bDtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7fQpccGFyZFx0eDU2MFx0eDExMjBcdHgx
	NjgwXHR4MjI0MFx0eDI4MDBcdHgzMzYwXHR4MzkyMFx0eDQ0ODBcdHg1MDQwXHR4NTYw
	MFx0eDYxNjBcdHg2NzIwXHBhcmRpcm5hdHVyYWxccGFydGlnaHRlbmZhY3RvcjAKClxm
	MFxmczI2IFxjZjAgJVZhcmlhYmxlJVRFU1RfX0VsYXBzZWRUaW1lJVwKVEVTVF9fVmFy
	MjoJJVZhcmlhYmxlJVRFU1RfX1ZhcjIlfQEAAAAjAAAAAQAAAAcAAABUWFQucnRmEAAA
	AJ4Ez1m2AQAAAAAAAAAAAAA=
	</data>
	<key>Text</key>
	<string>%Variable%TEST__ElapsedTime%
TEST__Var2:	%Variable%TEST__Var2%</string>
</dict>
</plist>
"
set theString to current application's NSString's stringWithString:theString
set stringData to theString's dataUsingEncoding:(current application's NSUTF8StringEncoding)
set {theDict, theError} to current application's NSPropertyListSerialization's propertyListWithData:stringData options:0 |format|:(missing value) |error|:(reference)
if theDict is missing value then error (theError's localizedDescription() as text)
set theData to theDict's objectForKey:"StyledText"
set theAttstring to current application's NSKeyedUnarchiver's unarchiveObjectWithData:theData

Sadly, it doesn’t. However, if I run it with:

set theText to current application's NSString's alloc()'s initWithData:theData encoding:(current application's NSASCIIStringEncoding)

I can see the rtf stuff in the data.

It’s possible something’s happened to the data in posting to the forum, so you should try it out there. But if that fails, I suspect your best bet is to ask Peter why he thinks it’s failing. He’s the one putting the data there, so he’ll know exactly what to do to extract it.

Just trying to edit the raw rtf string is a recipe for trouble, IMO.


(Jim Underwood) #3

Shane, thanks for the great help.
You script runs fine for me, when I add the last line, which produces the decoded RTF text. What problem were you having?

All I want to change is the KM Variable names embedded in the RTF, which is easy after decoding.

But the main issue I have is how to base64 encode the changed RTF, which has multiple lines. Can you please help with that?

Thanks.

EDIT: I also need to know how to update the dictionary with the changed “StyledText” AFTER it has been base64 encoded, and then convert the dict back to XML.

Thanks.


(Shane Stanley) #4

But that’s not really what you want.

What you want is the for the script to work without it. Then reassembling becomes a much simpler job.


(Jim Underwood) #5

OK, but if it’s not working then I’ll have to use more conventional methods, like split using:

set AppleScript's text item delimiters to {"<key>StyledText</key>
  <data>", "</data>"}
set xmlParts to text items of actXML

This puts the encoded RTF in item 2.
Your ASObjC script showed me how to decode it.

Can you please show me how to base64 encode a string having multiple lines?

Then I can just rejoin the list.

Thanks.


(Jim Underwood) #6

@ShaneStanley, thanks for all your help, but I’m back to my first, basic questions:

  1. How do I base64 decode a string.
  2. How do I base64 encode a string with multiple lines?

I tried to pull just the decoding parts from your above ASObjC script, but it’s not working.
Could you please show me how?

Many thanks.

Here’s my test script:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions


set actXML to "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
  <key>Action</key>
  <string>DisplayWindow</string>
  <key>ActionColor</key>
  <string>Red</string>
  <key>ActionName</key>
  <string>❗️ Display Text “TEST__Var3:  %Variable%TEST__Var3%” in Window</string>
  <key>ActionNotes</key>
  <string>UNABLE to Change Variables in Rich Text Area</string>
  <key>MacroActionType</key>
  <string>InsertText</string>
  <key>StyledText</key>
  <data>
  cnRmZAAAAAADAAAAAgAAAAcAAABUWFQucnRmAQAAAC5LAQAAKwAAAAEAAABDAQAAe1xy
  dGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNDA0XGNvY29hc3VicnRmNDcwCntc
  Zm9udHRibFxmMFxmbmlsXGZjaGFyc2V0MCBIZWx2ZXRpY2FOZXVlO30Ke1xjb2xvcnRi
  bDtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDBcZ3JlZW4wXGJsdWUwO30KXHBh
  cmRcdHg1NjBcdHgxMTIwXHR4MTY4MFx0eDIyNDBcdHgyODAwXHR4MzM2MFx0eDM5MjBc
  dHg0NDgwXHR4NTA0MFx0eDU2MDBcdHg2MTYwXHR4NjcyMFxwYXJkaXJuYXR1cmFsXHBh
  cnRpZ2h0ZW5mYWN0b3IwCgpcZjBcZnMyNiBcY2YyIFRFU1RfX1ZhcjM6CSVWYXJpYWJs
  ZSVURVNUX19WYXIzJX0BAAAAIwAAAAEAAAAHAAAAVFhULnJ0ZhAAAABoCs9ZtgEAAAAA
  AAAAAAAA
  </data>
  <key>Text</key>
  <string>TEST__Var3:  %Variable%TEST__Var3%</string>
</dict>
</plist>
"


set splitDelim to {"<key>StyledText</key>
  <data>", "</data>"}
set AppleScript's text item delimiters to splitDelim
set xmlParts to text items of actXML
set rtfEncoded to item 2 of xmlParts


set nsStr to current application's NSString's stringWithString:rtfEncoded

set nsDecoded to current application's NSString's alloc()'s initWithData:nsStr encoding:(current application's NSASCIIStringEncoding)
-->ERROR:  -[__NSCFString bytes]: unrecognized selector sent to instance 0x7fa2b8f43060

set rtfDecoded to nsDecoded as text

--- Requires Satimage.osax ---
set rtfDecoded to change "TEST__" into "Local__" in rtfDecoded

### How do I base64 encode rtfDecoded ???
---set rtfEncodedNew to ???

set xmlNew to item 1 of xmlParts & item 1 of splitDelim & ¬
  rtfEncodedNew & item 2 of splitDelim & item 3 of xmlParts


(Shane Stanley) #7

It’s not a matter base64 encoding a string. What’s happening is that some object is being archived by NSKeyedArchiver (or NSArchiver) to serialize it into a block of data. What you’re thinking is a base64 encoded rtf string is actually something that contains the rtf string.

So the correct approach is get the attributed string, modify it how you want, archive it, make a mutable copy of theDict and set the new value for the StyledText key, then create a new property list from that.

Please, see if Peter can answer the question above.


(Jim Underwood) #8

In one of my discussions with Peter, he said this:

It is the NSAttributedString archived to RTF and encoded in data format.

If you can help me with the proper solution, that would be great.

But, as a workaround, using base64 decode does work – it gives me the RTF codes in plain text that I can modify.

I have a bash script that will do the decode.
What I’ve been unable to do is base64 encode of multiple lines.

If you can’t figure out the proper solution, it would be really helpful if you could provide the ASObjC script to decode and encode.

Thanks.

BTW, if you, or anyone, is interested in the KM details, and the discussion I’ve had with Peter, see this KM forum thread: XML of Display Text in Window Does Not Update by Script


(Shane Stanley) #9

OK, that means he is doing what I suggested. So my code earlier is working to this point:

set theString to current application's NSString's stringWithString:theString
set stringData to theString's dataUsingEncoding:(current application's NSUTF8StringEncoding)
set {theDict, theError} to current application's NSPropertyListSerialization's propertyListWithData:stringData options:0 |format|:(missing value) |error|:(reference)
if theDict is missing value then error (theError's localizedDescription() as text)
set theData to theDict's objectForKey:"StyledText"

Peter put that data in there by archiving an NSAttributedString. There are two ways to do that: using NSArchiver or NSKeyedArchiver. To undo that, you use NSUnarchiver or NSKeyedUnarchiver. So the next line should be either:

set theAttstring to current application's NSKeyedUnarchiver's unarchiveObjectWithData:theData

or:

set theAttstring to current application's NSUnarchiver's unarchiveObjectWithData:theData

But using the sample you have posted here, both those return missing value. That suggests the data is mangled. It might be happening when you post here, or where you extract it — I don’t know. But that’s the correct code.

If you get it to work, then the rest of your script would be like this (untested, obviously):

-- edit theAttstring however you want, for example
set mutableAttString to theAttstring's mutableCopy()
mutableAttString's replaceCharactersInRange:{0, 3} withString:"?"
-- archive new attributed string to data, using NSKeyedArchiver **or** NSArchiver, depending on what was used above
set theData to current application's NSKeyedArchiver's archivedDataWithRootObject:mutableAttString
-- **or**:
set theData to current application's NSArchiver's archivedDataWithRootObject:mutableAttString
-- make new dictionary
set newDict to theDict's mutableCopy()
newDict's setObject:theData forKey:"StyledText"
-- make new plist from dictionary
set {plistData, theError} to current application's NSPropertyListSerialization's dataWithPropertyList:newDict |format|:(current application's NSPropertyListXMLFormat_v1_0) options:0 |error|:(reference)
set newString to (current application's NSString's alloc()'s initWithData:plistData encoding:(current application's NSUTF8StringEncoding)) as text

(Jim Underwood) #10

Shane, many thanks for you revised script.
Unfortunately, when I run it, after pulling the XML directly from the KM Action object, I also get missing value for both lines.

Peter suggested this ObjC to decode/encode the data:

The Objective C is:

Data to plain string:

[[[NSAttributedString alloc] initWithRTFD:data documentAttributes:nil] string];

String to data:

[[[NSAttributedString alloc] initWithString:s] RTFDFromRange:NSMakeRange(0,s.length) documentAttributes:@{}];

Can you please translate that into ASObjC I can use in your script?

BTW, just for reference, here is how I get the XML:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

tell application "Keyboard Maestro"
  set oMacro to item 1 of (get selected macros)
  
  set actionList to actions in oMacro
  set oAction to item 1 in actionList
  
  tell oAction
    set actXML to xml
  end tell -- oAction
  
end tell -- KM

set theString to actXML

set theString to current application's NSString's stringWithString:theString
set stringData to theString's dataUsingEncoding:(current application's NSUTF8StringEncoding)
set {theDict, theError} to current application's NSPropertyListSerialization's propertyListWithData:stringData options:0 |format|:(missing value) |error|:(reference)
if theDict is missing value then error (theError's localizedDescription() as text)
set theData to theDict's objectForKey:"StyledText"

(*
Peter put that data in there by archiving an NSAttributedString. There are two ways to do that: using NSArchiver or NSKeyedArchiver. To undo that, you use NSUnarchiver or NSKeyedUnarchiver. So the next line should be either:
*)

### set theAttstring to current application's NSKeyedUnarchiver's unarchiveObjectWithData:theData
-->missing value

## or:

### set theAttstring to current application's NSUnarchiver's unarchiveObjectWithData:theData
-->missing value

(Shane Stanley) #11

Perfect, thanks. So here’s working code:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

set theString to "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
	<key>Action</key>
	<string>DisplayWindow</string>
	<key>MacroActionType</key>
	<string>InsertText</string>
	<key>StyledText</key>
	<data>
	cnRmZAAAAAADAAAAAgAAAAcAAABUWFQucnRmAQAAAC5WAQAAKwAAAAEAAABOAQAAe1xy
	dGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNDA0XGNvY29hc3VicnRmNDcwCntc
	Zm9udHRibFxmMFxmbmlsXGZjaGFyc2V0MCBIZWx2ZXRpY2FOZXVlO30Ke1xjb2xvcnRi
	bDtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7fQpccGFyZFx0eDU2MFx0eDExMjBcdHgx
	NjgwXHR4MjI0MFx0eDI4MDBcdHgzMzYwXHR4MzkyMFx0eDQ0ODBcdHg1MDQwXHR4NTYw
	MFx0eDYxNjBcdHg2NzIwXHBhcmRpcm5hdHVyYWxccGFydGlnaHRlbmZhY3RvcjAKClxm
	MFxmczI2IFxjZjAgJVZhcmlhYmxlJVRFU1RfX0VsYXBzZWRUaW1lJVwKVEVTVF9fVmFy
	MjoJJVZhcmlhYmxlJVRFU1RfX1ZhcjIlfQEAAAAjAAAAAQAAAAcAAABUWFQucnRmEAAA
	AJ4Ez1m2AQAAAAAAAAAAAAA=
	</data>
	<key>Text</key>
	<string>%Variable%TEST__ElapsedTime%
TEST__Var2:	%Variable%TEST__Var2%</string>
</dict>
</plist>
"
set theString to current application's NSString's stringWithString:theString
-- convert string to data
set stringData to theString's dataUsingEncoding:(current application's NSUTF8StringEncoding)
-- convert plist to mutable dictionary
set {mutableDict, theError} to current application's NSPropertyListSerialization's propertyListWithData:stringData options:(current application's NSPropertyListMutableContainersAndLeaves) |format|:(missing value) |error|:(reference)
if mutableDict is missing value then error (theError's localizedDescription() as text)
-- extract RTFD data and convert to a mutable atributed string
set theData to mutableDict's objectForKey:"StyledText"
set mutableAttString to current application's NSMutableAttributedString's alloc()'s initWithRTFD:theData documentAttributes:(missing value)
set plainString to mutableAttString's |string|()
-- modify the mutable atributed string
-- how you do that depends on what you want to do, obviously
set theRange to plainString's rangeOfString:"TEST__ElapsedTime"
mutableAttString's replaceCharactersInRange:theRange withString:"TEST__SomethingElse"
-- convert back to RTFD data
set theData to mutableAttString's RTFDFromRange:{0, mutableAttString's |length|()} documentAttributes:(missing value)
-- update the dictionary
mutableDict's setObject:theData forKey:"StyledText"
mutableDict's setObject:(mutableAttString's |string|()) forKey:"Text"
-- make new plist from dictionary
set {plistData, theError} to current application's NSPropertyListSerialization's dataWithPropertyList:mutableDict |format|:(current application's NSPropertyListXMLFormat_v1_0) options:0 |error|:(reference)
-- get text version
set newString to (current application's NSString's alloc()'s initWithData:plistData encoding:(current application's NSUTF8StringEncoding)) as text

The part this doesn’t really cover is how to edit the styled text (attributed string), which can be complicated depending on what you want to do. Assuming you don’t want to change the attributes themselves, the methods you’d use are replaceCharactersInRange:withString: and deleteCharactersInRange:. You get the ranges you use based on the unstyled text, as above.


(Jim Underwood) #12

Shane, you are the man​:exclamation:️ :+1:

Your script works perfectly, even updating the styled (rich) text properly.

I just have one more request: How do I do a find and replace ALL occurrences of a string?

These lines work perfectly to update the first occurrence of prefixCur

  -- modify the mutable atributed string
  -- how you do that depends on what you want to do, obviously
  set theRange to plainString's rangeOfString:prefixCur
  mutableAttString's replaceCharactersInRange:theRange withString:prefixNew

How do I update ALL occurrences of prefixCur?

Many, many thanks. You have solved two major problems I was having with reading and updating KM Actions.


(Jim Underwood) #13

@ShaneStanley, OK, I fibbed. :wink:
I do have another request:

I also need to change all occurrences of prefixCur in the plain text parts of the XML. I can do this with a simple Satimage.osax change command on the XML string, but I thought I’d ask you if there is a better way with ASObjC?

I’m really in your debt. If you need anything that I can possibly do to help you (I know, that’s really funny), please don’t hesitate to ask, either here or by email. I’m happy to be your grunt. :wink:


(Shane Stanley) #14

The easiest way is a repeat loop:

repeat
	set theRange to plainString's rangeOfString:"TEST__"
	if |length| of theRange = 0 then exit repeat
	mutableAttString's replaceCharactersInRange:theRange withString:"BLAH__"
end repeat

It may look a bit odd, but plainString is a pointer to the actual string content of the attributed string, so every time you modify the attributed string, you effectively also modify plainString at the same time.

For something more complex you could use NSRegularExpression and replace from back to front.

That’s what this line does:

mutableDict's setObject:(mutableAttString's |string|()) forKey:"Text"

But because of what I said above about plainString, you could actually simplify it to:

mutableDict's setObject:plainString forKey:"Text"

(Jim Underwood) #15

OK, great! That works perfectly.

But some KM Actions will NOT have a “StyledText” key, only a “Text” key.
How do I get a mutableAttString reference to that?

I know how to get theData:
set theData to mutableDict's objectForKey:"Text"

It is the next step that’s got me baffled – getting mutableAttString reference.

Thanks again.


(Shane Stanley) #16

Unless you’re trying to add your own styling, you don’t. You need to test for the presence of “StyledText”, and behave accordingly:

[...]
-- extract RTFD data and convert to a mutable atributed string
set theData to mutableDict's objectForKey:"StyledText"
if theData = missing value then -- only plain text
	set plainText to mutableDict's objectForKey:"Text"
	-- edit text
	set plainText to plainText's stringByReplacingOccurrencesOfString:"TEST__" withString:"BLAH__"
else
	set mutableAttString to current application's NSMutableAttributedString's alloc()'s initWithRTFD:theData documentAttributes:(missing value)
	set plainString to mutableAttString's |string|()
	-- modify the mutable atributed string
	-- how you do that depends on what you want to do, obviously
	repeat
		set theRange to plainString's rangeOfString:"TEST__"
		if |length| of theRange = 0 then exit repeat
		mutableAttString's replaceCharactersInRange:theRange withString:"BLAH__"
	end repeat
	-- convert back to RTFD data
	set theData to mutableAttString's RTFDFromRange:{0, mutableAttString's |length|()} documentAttributes:(missing value)
	-- update the dictionary
	mutableDict's setObject:theData forKey:"StyledText"
end if
mutableDict's setObject:plainString forKey:"Text"
-- make new plist from dictionary
[...]

(Jim Underwood) #17

OK, that is what I was looking for. I’ll test it and get back to you.

Sorry I’m so ASObjC illiterate. I keep trying, but I seem to be slow in learning this new (for me) language.


(Jim Underwood) #18

That works great Shane! :+1:

Now I need to use that solution and apply it to a broader set of KM Actions.