Save HTML from Mail Message

October 22nd, 2005

Usually when I get an email message with HTML content, I reach for the delete key faster than you can say “spam!” But there are some messages I receive which contain important HTML content. For instance, the invoicing system one of my large corporate clients uses (why, I don’t know), is completely unusable in most regards. The only way I have found to get a personal copy for my records of invoices I issue through it is to have it email me a copy. When the copy arrives, of course it is in a table-heavy HTML format, which is unreadable even if I attempt to save it as plain text. It looks terrible as HTML too, but at least it’s readable!

So, I get this HTML email regularly, and I need to save it to my “Unpaid Invoices” folder. Unfortunately, Apple Mail has no obviously easy way to do this. I have before today resorted to various steps including showing the raw source and manually copy/pasting the HTML portion out and into another file. Today I decided the B.S. stops and I get an easy solution.

I didn’t even bother looking around for an existing script. I knew what I wanted so I wrote it. The result is a script that should do a reasonably good job of grabbing the HTML out of a selected message and saving it to disk for you. I don’t think of this as something that most people want to do often, but if you find you do need it, now you don’t have to write it yourself:

Click here to download: Save HTML Content

Blogging with Style

October 18th, 2005

Since I’m a little obsessed with AppleScript, I end up pasting AppleScript code into my blog. I recently decided it was a real shame that I couldn’t easily replicate the way AppleScript code looks in Script Editor, in my blog. I thought about this situation for a while, and then did some experimenting.

If I copy the contents of a Script Editor window, and paste it into TextEdit, it looks great! Just like Script Editor! Why can’t I get that in HTML? I decided to try saving as HTML from TextEdit. Sure enough, that yields an HTML look-alike, but it depends on a rather elaborate set of CSS definitions in the <head> block of the document. I’m not ambitious enough to figure out whether I can include non-inlined CSS styles in random locations like the content of my blog post. Besides, I’d like to be able to “grab some styled text” and run with it.

It occurred to me that maybe AppleScript itself could help me get the formatting I needed. After a little tinkering, Sure enough, with TextEdit and the Text Suite, I can parse a chunk of styled text and spit out reasonably similar-looking HTML!

In this blog entry, I submit to you a script that will turn the text from the front-most document in TextEdit into a self-contained, inline styled chunk of HTML. It’s not pretty, but it works! As a proof-of-concept, the following code was copied from Script Editor, pasted into TextEdit, and then run from Script Editor. Because of some WordPress funkiness I had to change a few minor details in the source for this page, but I think you’ll find that the script works as expected in most contexts.:


tell application "Script Editor"
-- Since we are probably capturing newlines, tabs, etc.,
--
Let’s tag the content as "pre-formatted".
set myHTML to "<pre>"

-- Prefetch lists from the runs for performance during loop
set {myFonts} to {font} of attribute runs of document 1
set {myColors} to {color} of attribute runs of document 1
set {mySizes} to {size} of attribute runs of document 1
set {myTexts} to {characters} of attribute runs of document 1

-- Build an independent HTML span for every attribute run
repeat with attrIndex from 1 to count of attribute runs of document 1
-- Figure the color
set thisColor to (item attrIndex of myColors)
set redComp to (my RGBColorNumToHex(item 1 of thisColor) as string)
set blueComp to (my RGBColorNumToHex(item 2 of thisColor) as string)
set greenComp to (my RGBColorNumToHex(item 3 of thisColor) as string)
set thisHexString to "#" & redComp & blueComp & greenComp

-- Font name and size
set thisFont to (item attrIndex of myFonts as string)
set thisFontSize to (item attrIndex of mySizes as string)

-- Get the text
set thisTextRun to (item attrIndex of myTexts as string)

-- Put it all together
set thisHTMLRun to "<span style=\"color:" & thisHexString & "; font-family:’" & thisFont & "’; font-size:" & thisFontSize & "pt;\">" & thisTextRun & "</span>"
set myHTML to myHTML & thisHTMLRun

end repeat
set myHTML to myHTML & "</pre>"
set the clipboard to myHTML
end tell

-- Based on Num32ToHex by Walter Ian Kaye
--
http://www.natural-innovations.com/as/snippets.html
on RGBColorNumToHex(num)
-- We want smaller numbers
set num to num / 256
set out to ""
repeat with idx from 1 to 0 by -1
set bas to (16 ^ idx) as integer
if numbas then
set mult to ((num – (num mod bas)) / bas) as integer
set out to out & character mult of "123456789ABCDEF"
set num to (numbas * mult)
else
set out to out & "0"
end if
end repeat
return out
end RGBColorNumToHex

Wow, that looks quite a bit more Script-Editor-like than your average web posting! It’s shamefully inefficent (take a look at the HTML it generates), but it gets the job done! I am happy to have this new tool at my disposal, but aside from producing rotten HTML code, the script is also dog slow. Also, when I copy the script above and paste it into Script Editor, it comes out double-spaced. Anybody out there have any tips for achieving this goal in a slightly more gallant manner?

Update: The script above has been updated since the original post. I discovered a few prefetching tricks that dramatically improve performance of the script. How dramatically? Thanks to Shark I can tell you! The test case for both scripts is converting the styled script text above from TextEdit to inline HTML. The old script? 58 seconds! 40% of which was spent in TextEdit itself. The optimized version above, which prefetches the pertinent styled text data from TextEdit? 4.5 seconds, with only 5.4% of the processor taken up by TextEdit. More than 10-times faster! Just imagine how fast it will be when a good scripter rewrites it!

Shark Bites Script

October 17th, 2005

Most Mac developers have, by now, given Apple’s CHUD Performance Tools Suite at least a cursory run-through. These tools, and in particular Shark, are incredibly effective for pinpointing performance problems that may be plaguing your application (or other code).

The presence of these tools has helped many developers inside and outside of Apple produc more efficient code. By effectively painting the low hanging fruit neon green, Shark allows us to easily locate our most embarrassingly slow code – hopefully before anybody else does.

One area of “coding” on the Mac where optimization has been sorely overlooked is that of AppleScript authoring. AppleScript performance can range from instantaneous to mind-bogglingly slow, and usually clocks in at some random level between the two, having been left complete up to chance by the script’s author. While laziness in the past may have been excusable, poor script performance is inexcusable in a post-Shark world.

Chumming Along

When professionals want to lure a shark nearby for viewing or killing pleasure, they may litter the water with visceral mix of blood and flesh: irresistible to the carnivorous beasts. Luckily, attracting Shark to your AppleScript is much easier to stomach, if you know a few tricks.

In AppleScript, as in other code that is executed in linear fashion by a computer, it is frequently most interesting to examine the performance metrics over a quite specific period of time. If you’re experienced with Shark, you may know that a clumsy method of starting and stopping the sampler is provided by way of a keyboard shortcut. This is useful, but often not precise enough for gauging the minute differences between scripting choices. By taking advantage of the the often-overlooked chudRemoteCtrl tool, which can be used to (among other things) remotely start and stop sampling in the main Shark application, we can get fairly precise performance details which are furthermore broken down by process, a detail which can be especially useful in assigning blame.

Preparing Shark for Duty

We can put control of Shark literally at our fingertips, but as the CHUD tools do have some eccentric behaviors, there are a few steps which must be followed to prepare Shark for reliable, scripted obedience:

  1. Launch Shark
  2. Ensure that Shark is set up for “Remote” control. This is off by default but can be turned on by selecting the “Programmatic (Remote)” menu item from the Sampling menu.
  3. Ensure that all sampling settings are to your liking. You probably want to target “Everything” as opposed to a specific process, because a given AppleScript is liable to enlist the help of many processes to achieve its goals. If your Shark window looks more or less like this, then you’re probably in good shape:

  4. Optional: Since sifting through the large quantity of data Shark produces can be daunting, it may be helpful to quit as many running Applications as you can before running your tests.

The power of Shark is now “cocked and ready to fire.” Next we’ll look at what we need to do from an AppleScript to take advantage of this potential.

Unleashing the Beast

Programmers familiar with using Shark from languages like Objective-C or C++ may be familiar with the CHUD library calls that allow procedural starting and stopping of Shark’s profiler. While AppleScript doesn’t include this functonality, we can take advantage of AppleScript’s shell scripting interface to invoke a powerful tool: chudRemoteCtrl. By passing the appropriate arguments to this tool, we can easily start and stop the profiler during the execution of a script, while giving meaningful descriptions to the associated sample sets that are generated as a consequence.

To demonstrate the power of this technique, I will use Shark to profile the performance of an extremely trivial example script. This scripts purpose is to compute the 64th power of 2, through methods ranging from completely idiotic to downright obvious. I chose this example simply to give you dramatically different performance numbers to compare. In real life, the numbers may not always be as “no-brainer,” but the same principles apply.

on run
	-- Run some tests
	set powersOfTwo to 64
	
	my SharkStartSampling("Shell Scripted Dumb Calculator")
	set worstResult to 2
	repeat (powersOfTwo - 1) times
		set badScript to "echo '" & worstResult &"* 2' | bc"
		set worstResult to do shell script badScript
	end repeat
	my SharkStopSampling()
	
	my SharkStartSampling("AppleScript Built-In Dumb Calculator")
	set betterResult to 2
	repeat (powersOfTwo - 1) times
		set betterResult to betterResult * 2
	end repeat
	my SharkStopSampling()
	
	my SharkStartSampling("AppleScript built-in expression")
	set bestResult to 2 ^ powersOfTwo
	my SharkStopSampling()
	
	my SharkStartSampling("Null Operation")
	my SharkStopSampling()
end run

on SharkStartSampling(sampleLabel)
	do shell script "chudRemoteCtrl -s " & quoted form of sampleLabel
end SharkStartSampling

on SharkStopSampling()
	do shell script "chudRemoteCtrl -e"
end SharkStopSampling

Paste the above code into your script editor and, after double-checking that Shark is “ready”, run the script. It’s important to keep Shark happy because it seems to misbehave after unexpected events like attempting to start sampling twice without ending the previous sample. If you get into trouble, you have to quit and relaunch Shark, putting it back into the prepared state described above.

As the script runs, you’ll see Shark processing samples and popping up analysis windows – one for each of the named tests I included in the script. When the script is complete, take a look at the resulting data sets. The first thing that strikes me as I look at the results on my PowerMac G5, is that the dumbest of all the code takes at least 10 times longer to execute than the smartest. As you look at the results in Shark, you can focus on particular processes and apply the powerful data mining utilities to eliminate uninteresting stack calls. For instance, in a real world situation, you will often only be interested in the samples obtained from the program your script was executed in, and the program your script is “telling” to do something (if applicable).

As you glance through the resulting sample sets, you may notice that even the smartest technique comes up with a significant number of samples. There is some overhead to the profiling mechanism, and being a multi-tasking system, the other processes on your computer may be stealing time away from your test. For this reason, I included as a final example a “null operation” which simply starts and stops sampling as quickly as possible. On my computer, the total difference between the control case and the “built-in expression” case is only 15.7ms. By comparison, the dumbest case clocks in at a whopping 8 seconds!

In Summary…

Clearly this example was an extreme case of low hanging fruit, but the principles can be applied in any script to obtain information about its effect on any process in the system. You may also find yourself using this technique to verify or dismiss suspicions about the software you use. What exactly happens when an application asks the system to beep? Is perl faster than ruby? Does iTunes hesitate just a bit longer than usual when asked to play a Britney Spears song? Only one way to find out…

An Update…

On the AppleScript-Users mailing list, Has makes some interesting points about this approach. He points out that, because Shark doesn’t gather data about the particular AppleScript subroutines that time is spent in, it can’t really be described as profiling the script. While I think this is a fair point, I maintain that it can be very useful to figure out where the system is spending time while executing your script. The nice thing about Shark is that it will represent samples from code in any process that was invoked to fulfill the demands of your script. True, if your script is 100% AppleScript and doesn’t invoke any scripting additions that reach the system APIs, then the profile might be kind of boring, but that is in itself informative as well.

Kool and the Nano Gang

October 16th, 2005

With all the hooplah about iPod Nano’s vulnerabilities: scratching, cracking, etc., I admit I’ve become a little more coddling of precious than I might otherwise be. I have owned exactly two iPods in my life: the 5GB original that I bought for half-price a majillion years ago as an Apple employee, and the 4GB iPod Nano that I bought a few weeks ago as an enthusiastic endorsement of my former employer’s kick-ass sense of product design. Maybe in another few years I’ll spend even more money for a 3GB version!

My first iPod, I remember, was purchased as a glorified hard drive. “I’ll never listen to music on this thing,” I thought, “but I can’t argue with a $200 5GB hard drive.”

That’s how Apple won me, an enthusiastic music lover, over to the iPod. God only knows how they get the rest of America to sign on! Anyway, after a few weeks of using my iPod for extremely exciting things like storing mixes of my own acoustic guitar home recordings, I thought I might actually put some pre-recorded tunes on it. You know, something forward-thinking, like songs to work-out to! I put a couple albums on the thing and walked over to the Apple fitness center to test it out.

“Hey! What do you know, this thing really rocks!” I thought to myself (thankfully, because that would sound really embarrassing if I said it out loud). I was grooving to some Built To Spill or something, pushing 3 miles or so on the treadmill as Judge Judy (always on at the Apple fitness center at 3:30 or so) tried to intrude through my (already replacement black) headphones.

As everybody in the room was distracted by their respective entertainment choices, I sensed a slight tugging at my arm. What was that? I looked down instantly to see my spanking new iPod being lifted out of the little storage space on the treadmill, launching into the air as my spasmatic arm caught the headphone wire.

This is where, in a movie, you might hear a voice, lowered in pitch due to the slow motion effect, screaming “NOOOOOO.” I watched my poor little iPod bounce out of its holster, and fall to the speeding treadmill track below. I then jumped off the track, only to look behind me and watch as my precious little device shot out behind me about three feet, while half the gym looked on in alarmed dismay.

Taking it in stride, I walked over to the little (big, by today’s standards) chunk of plastic and picked it up. It looked OK. My headphones still hung around my neck. I plugged them in. “The Plan Keeps Coming Back Again!” I heard Doug Martsch bellow. Not only had my iPod survived, but it was still playing! I jumped back on the treadmill and finished my workout, a satisfied customer.

Last week I finally got the nerve up to take my precious iPod Nano to the gym. Going to the gym these days means riding my bike down to the Cambridge YMCA, so it feels a bit more awkward to be flashing crazy tech gadgets. I got on the treadmill, plugged in my headphones, and started jogging. This time, as it happens, I was listening to a somber George Jones album. I looked around the Cambridge Y as I ran and tried to imagine the sorry words I was listening to, applied to the lives of those I observed.

Since the iPod Nano is so dang small, and apparently so dang scratchable, I had to carefully place it in a little nook at the bottom of my treadmill’s “magazine cozy.” The Nano sat there undisturbed, next to the boring issue of Running magazine that I will never finish and should donate to the gym (so everybody else can be bored by it). I was about halfway through my run, really enjoying the Nano life, when history repeated itself. I felt a slight tug at my arm, the sign that I’d twitched in such a way to snag the headphone wire just right. Before I could even estimate what was about to happen, my precious iPod Nano appeared in the air before me, almost floating. It proceeded to sink quickly, and before I knew what had happened, it struck the treadmill surface, and as before, shot out like a dart onto the ground behind me.

I felt the gym stand still. Before, it was at Apple. Everybody knew how significant the iPod was, but everybody also already had one. I knew subconsciously that everybody at the Y today was thinking, “that guy has the sexy new iPod.” To see me show such obvious disregard for its safety was more than a little embarrassing. I decided the only thing to do was assume a lackadaisical posture. I slowly disembarked from my machine, strolled the few feet to where my iPod had landed, and picked it up. I carelessly plugged my headphones back into the device, and waited with hopeful anticipation. Nothing.

I panicked for a moment, before realizing that it was no longer 2002. I stared into my iPod Nano and, realizing that it had kindly paused upon removal of my headphones, pressed the play button. George Jones resumed on cue, expressing my sentiments exactly: “Just the thought of losing you, scares me half to death.”