The Art of AppleScript

The Illustrated DOM

Drawing Hypertext with Adobe Illustrator

Use Adobe Illustrator and AppleScript Mac Automation Scripting to create a custom menu command for Illustrator that generates an HTML document from the current artboard in one quick and easy step, including optional CSS level 3 + SVG stylesheet, JavaScript scripting, and JSON database.

The Artwork Master

Start with an artwork master in native Adobe Illustrator format (or PDF with Illustrator editing.) This file represents all of the work you do within the Adobe environment, encapsulated in a way that maintains full fidelity and is likely to be usable in Illustrator and viewable on every computing device for many years to come.

Metadata Matters

A document is more than just an illustration. There is also important metadata like the title, description, author, and copyright notice. This is true both for Illustrator documents and hypertext documents. We’ll connect the dots and export our Illustrator document’s metadata along with its illustration and both will be included in the final HTML document. Therefore, it is essential that our Illustrator documents actually have metadata.

To add metadata to your Illustrator document, choose File → File Info from Illustrator’s menus and fill-in the following Basic metadata fields:

Including this metadata enables us to go from Illustrator to a complete live Web document in one step.

Attaching Styles, Scripts, and Data

In addition to metadata, a hypertext document may optionally have CSS styles, JavaScript scripts, and JSON data.

To include styles, scripts, and data, simply make sidecar files in the same folder as your illustration.

In our scripting, we’ll look for sidecar files and if they exist, include them in the hypertext output.

HTML with Inline SVG

Rather than export our Illustrator document as a standalone SVG document, we are going to export it as graphics-rich hypertext: HTML with inline SVG. This will give us the advantage of rendering with full fidelity in any browser, and also enable us to include scripts, styles, and databases using widely-known standard HTML Web development techniques.

To convert an illustration into exactly the hypertext document that we want — in one step — we have to talk to Illustrator in AppleScript.

Talking to Illustrator

If you Run the following AppleScript on a Mac with Adobe Illustrator installed, Illustrator will answer. This conversation is the core of AppleScript Mac Automation Scripting.

tell application "Adobe Illustrator" display notification "message received" with title (the name as text) subtitle (my name as text) end tell

Custom Illustrator Menu Commands

To create a custom Illustrator menu command, create a new AppleScript in Script Editor, and save it as “Export Hypertext” and in Illustrator’s script folder.

To run your AppleScript from within Illustrator, launch Script Menu, which is built into every Mac.

Develop the Conversation

In order to convert an illustration into a hypertext document, a minimum set of steps are required. Figuring out what those steps are is a process that is similar to working interactively with Illustrator, choosing command after command from the menus, until you have put 5 or 10 steps together into one action.

A basic to-do list for the task at hand would look like this:

1) use the current document as the source illustration 2) get the name of the source illustration 3) export files to ~/Output/ but put them in a subfolder named for the source folder 4) include optional sidecar CSS, JavaScript, and JSON documents, if any 5) build an HTML document from the source illustration 6) specify a pathname for the HTML file we’re making 7) put the HTML into a text file and Save it 8) notify when the hypertext export is complete

Rewriting the to-do list in AppleScript makes it functional:

tell application "Adobe Illustrator" activate if document 1 exists then (* use the current document as the source illustration *) set theSourceIllustration to the current document set theSourceIllustrationName to the name of theSourceIllustration set AppleScript's text item delimiters to "." set theSourceIllustrationName to the first text item of theSourceIllustrationName set AppleScript's text item delimiters to {""} set theSourceIllustrationPath to the file path of theSourceIllustration as text (* export files to ~/Output/ but put them in a subfolder named for the source illustration *) set theOutputFolderPath to prepOutputFolder("Output", theSourceIllustrationName) of me tell application "Finder" to set theOutputFolder to theOutputFolderPath as alias (* include linked files *) set theSidecarFileExtensions to {"json", "js", "css"} set theIncludedSidecarFileExtensions to includeSidecarFiles(theSourceIllustrationPath, theSidecarFileExtensions, theOutputFolder) of me (* build an HTML document from the source illustration *) set theHTMLFileContents to buildHypertextFrom(theSourceIllustration, theSourceIllustrationName, theIncludedSidecarFileExtensions, theOutputFolderPath) of me (* specify a pathname for the HTML file we’re making *) set theHTMLFilePath to theOutputFolderPath & "index.html" (* put the HTML into a text file and Save it *) makeTextFile(theHTMLFilePath, theHTMLFileContents) of me (* notify when the hypertext export is complete *) tell application "Finder" to open theOutputFolder open file theSourceIllustrationPath set theNotificationMessage to "hypertext built" else set theNotificationMessage to "please open a document" end if display notification theNotificationMessage with title (the name as text) subtitle (my name as text) end tell (* include includeSidecarFiles, buildHypertextFrom, exportSVGIllustration, makeTextFile, getTextFileAsVariable, and prepOutputFolder subroutines here *)

The Meat of the Workflow

A handful of reusable subroutines will provide Illustrator with the recipes it needs to cook up hypertext. Assisting in the preparations: Bare Bones Editor, known for its expertise with text files, and available in a free or paid version. Substitute your own text editor as required.

You may want to write your own hypertext export subroutines, or you may want to use my subroutines under MIT License. Paste the following code block at the end of an AppleScript in Script Editor to make the subroutines available within that AppleScript.

on includeSidecarFiles(theMainFilePath, theSidecarFileExtensions, theOutputFolder) tell application "Finder" set theIncludedSidecarFileExtensions to {} set AppleScript's text item delimiters to "." set theMainFileExtension to the last text item of theMainFilePath set AppleScript's text item delimiters to {""} repeat with theSidecarFileExtension in theSidecarFileExtensions tell application "BBEdit" set theSidecarFilePath to replace ("." & theMainFileExtension) using ("." & theSidecarFileExtension) searchingString theMainFilePath end tell if file theSidecarFilePath exists then set theSidecarFile to theSidecarFilePath as alias duplicate theSidecarFile to theOutputFolder replacing yes set the end of theIncludedSidecarFileExtensions to (theSidecarFileExtension as text) end if end repeat end tell return theIncludedSidecarFileExtensions end includeSidecarFiles on buildHypertextFrom(theSourceIllustration, theSourceIllustrationName, theIncludedSidecarFileExtensions, theOutputFolderPath) tell application "Adobe Illustrator" (* get the metadata for the illustration *) set theSourceXMPString to the XMP string of theSourceIllustration tell application "System Events" set theSourceXMPXML to make new XML data with properties {name:"theSourceXMPXML", text:theSourceXMPString} tell theSourceXMPXML's XML element "x:xmpmeta"'s XML element "rdf:RDF"'s XML element "rdf:Description" (* try to get the title from the metadata and if not, then ask the user *) try set theTitleXML to the first XML element whose name is "dc:title" set theTitleValue to theTitleXML's XML element "rdf:Alt"'s XML element "rdf:li"'s value on error set theTitleDialogPrompt to "Title metadata is missing. Please enter a title." set theDefaultTitleAnswer to "[Title]" display dialog theTitleDialogPrompt default answer theDefaultTitleAnswer buttons {"OK"} default button "OK" with title (my name as text) giving up after 60 set theTitleValue to the text returned of the result end try try set theCreatorXML to the first XML element whose name is "dc:creator" set theCreatorValue to theCreatorXML's XML element "rdf:Seq"'s XML element "rdf:li"'s value on error set theCreatorDialogPrompt to "Creator metadata is missing. Please enter a creator." set theDefaultCreatorAnswer to "[Creator]" display dialog theCreatorDialogPrompt default answer theDefaultCreatorAnswer buttons {"OK"} default button "OK" with title (my name as text) giving up after 60 set theCreatorValue to the text returned of the result end try try set theDescriptionXML to the first XML element whose name is "dc:description" set theDescriptionValue to theDescriptionXML's XML element "rdf:Alt"'s XML element "rdf:li"'s value on error set theDescriptionDialogPrompt to "Description metadata is missing. Please enter a description." set theDefaultDescriptionAnswer to "[Description]" display dialog theDescriptionDialogPrompt default answer theDefaultDescriptionAnswer buttons {"OK"} default button "OK" with title (my name as text) giving up after 60 set theDescriptionValue to the text returned of the result end try try set theRightsXML to the first XML element whose name is "dc:rights" set theRightsValue to theRightsXML's XML element "rdf:Alt"'s XML element "rdf:li"'s value on error set theRightsDialogPrompt to "Rights metadata is missing. Please enter a rights notice." set theDefaultRightsAnswer to "[Rights]" display dialog theRightsDialogPrompt default answer theDefaultRightsAnswer buttons {"OK"} default button "OK" with title (my name as text) giving up after 60 set theRightsValue to the text returned of the result end try end tell end tell set theHTMLTitle to theTitleValue set theHTMLName to theSourceIllustrationName set theHTMLAuthor to theCreatorValue set theHTMLDescription to theDescriptionValue set theCopyrightNotice to theRightsValue (* add the header to the hypertext *) set theHTMLFileContents to "<!DOCTYPE html> <html lang=\"en\"> <head> <meta charset=\"utf-8\"> <title>" & theHTMLTitle & "</title> <meta name=\"description\" content=\"" & theHTMLDescription & "\"> <meta name=\"author\" content=\"" & theHTMLAuthor & "\"> <meta name=\"application-name\" content=\"" & theHTMLTitle & "\"> <meta name=\"apple-mobile-web-app-title\" content=\"" & theHTMLTitle & "\"> <meta name=\"viewport\" content=\"width=device-width, user-scalable=yes, initial-scale=1.0\">" & return if theIncludedSidecarFileExtensions contains "css" then set theHTMLFileContents to theHTMLFileContents & "<link type=\"text/css\" rel=\"stylesheet\" href=\"" & theSourceIllustrationName & ".css\">" & return end if if theIncludedSidecarFileExtensions contains "js" then set theHTMLFileContents to theHTMLFileContents & "<script type=\"application/javascript\" src=\"" & theSourceIllustrationName & ".js\"></script>" & return end if if theIncludedSidecarFileExtensions contains "json" then set theHTMLFileContents to theHTMLFileContents & "<link type=\"application/json\" rel=\"manifest alternate\" href=\"" & theSourceIllustrationName & ".json\">" & return end if set theHTMLFileContents to theHTMLFileContents & "</head> <body id=\"" & theHTMLName & "\" class=\"application typography navigation animation " & theHTMLName & "\"> <header> <h1>" & theHTMLTitle & "</h1> </header> <main> <div id=\"theArtworkContainer\">" & return (* export an svg of the illustration *) set theSVGFilePath to exportSVGIllustration(theSourceIllustrationName, theOutputFolderPath) of me (* get the code from the exported svg *) set theSVGFileContents to getTextFileAsVariable(theSVGFilePath) of me (* remove the svg XML document header to create an HTML svg tag *) set theSVGFileContentsParagraphCount to the count of paragraphs in theSVGFileContents set theSVGTagContents to "" repeat with theCounter from 2 to theSVGFileContentsParagraphCount set theSVGTagContents to theSVGTagContents & paragraph theCounter of theSVGFileContents & return end repeat (* add the svg tag to the hypertext*) set theHTMLFileContents to theHTMLFileContents & theSVGTagContents (* add the footer to the hypertext *) set theHTMLFileContents to theHTMLFileContents & " </div> </main> <footer> <p><small class=\"copyright\">" & theCopyrightNotice & "</small></p> </footer> </body> </html>" & return end tell return theHTMLFileContents end buildHypertextFrom on exportSVGIllustration(theFileName, theOutputFolderPath) tell application "Adobe Illustrator" set theSourceIllustration to the current document set theSVGFilePath to theOutputFolderPath & theFileName & ".svg" export theSourceIllustration to file theSVGFilePath as SVG with options {artboard range:"", compressed:false, coordinate precision:3, CSS properties:style attributes, font type:SVG font, kerning:false, minify svg:false, raster image location:preserve, responsive svg:true, save multiple artboards:false} end tell return theSVGFilePath end exportSVGIllustration on makeTextFile(theTextFilePath, theTextFileContents) tell application "BBEdit" activate set theTextFile to make new document with properties {encoding:"Unicode (UTF-8)", text:theTextFileContents} set the line breaks of theTextFile to Unix set theTextFileContents to the contents of theTextFile set the contents of theTextFile to normalize line endings theTextFileContents select insertion point 1 of the contents of the front document save theTextFile to file theTextFilePath end tell return end makeTextFile on getTextFileAsVariable(theTextFilePath) tell application "BBEdit" activate open file theTextFilePath tell the front document set the encoding to "Unicode (UTF-8)" normalize line endings set line breaks to Unix set theTextFileContents to the contents end tell close the front document saving no end tell return theTextFileContents end getTextFileAsVariable on prepOutputFolder(theOutputFolderName, theSubFolderName) tell application "Finder" set theHomeFolderPath to the path to the home folder as text set theHomeFolder to theHomeFolderPath as alias set theOutputFolderPath to theHomeFolderPath & theOutputFolderName & ":" if folder theOutputFolderPath exists then set theOutputFolder to theOutputFolderPath as alias else set theOutputFolder to make new folder at theHomeFolder with properties {name:theOutputFolderName} end if if theSubFolderName is equal to "" then set theTargetFolderPath to theOutputFolderPath else set theSubFolderPath to theHomeFolderPath & theOutputFolderName & ":" & theSubFolderName & ":" set theTargetFolderPath to theSubFolderPath if folder theSubFolderPath exists then set theSubFolder to theSubFolderPath as alias else set theSubFolder to make new folder at theOutputFolder with properties {name:theSubFolderName} end if end if end tell return theTargetFolderPath end prepOutputFolder

Example Scripts

AppleScripts made from the example code on this page.

Code Reuse

The blocks of example code on this page and the attached example scripts are open source software that everyone can use and modify and customize for their own purposes under MIT License.

(* Copyright 2015 Simon White http://simonwhite.com/ MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *)

Author

Simon White

Last updated .


Keywords: artist, animator, designer-developer, agile, agile-developer, agile-development, full-stack, developer, coder, graphic-artist, web-animator, illustrator, adobe-illustrator, photoshop, adobe-photoshop, vector-graphics, raster-graphics, pixel-graphics, bitmap-graphics, wacom, mac, macintosh, apple-mac, unix, animation, artwork, code, digital-pen, digital-pencil, digital-ink, hand-drawn, hand-coded, handmade, interactive, html, svg, animated-svg, html5, css3, es5, unobtrusive-javascript, dom-scripting, responsive-animations, responsive-layouts, w3c, iso, audio, classlist, cssanimations, csstransforms, csstransitions, eventlistener, inlinesvg, json, opacity, preserve3d, queryselector, requestanimationframe, rgba, webaudio, xhrresponsetypejson.