Automating Adobe Illustrator using JavaScript scripts

authored by Frank Lynam at 05/08/2014 10:17:54

A couple of weeks ago I was working on an archaeological project in which we were required to annotate our site photographs with relevant contextual information such as wall numbers, context extents and the locations of finds. I enjoy using Adobe Illustrator for this type of task. I know that people tend to associate photograph editing with Photoshop but in this case where the photograph raster image is to remain unchanged, it makes a lot more sense in my opinion to use Adobe Illustrator’s vector approach.

To do all this you need some very basic Illustrator functionality. I added text using the Type tool. I wanted to include the context labels within a triangle as was the project convention and so I did this using the Pen tool. Finally, in order to delineate certain areas within the photographs, I used the Pen tool with a stroked line and no fill. All of these additions were placed in their own individual layers so as to organise them.

A very basic knowledge of Adobe Illustrator will allow you to achieve all of this pretty quickly. I would estimate that the processing of a single photograph would take about 5 minutes. The problem arose for me when the number of photographs that I needed to edit reached about 200. I would estimate that this would have taken me about 17 hours had I completed the task using the manual approach.

Looking for an alternative, I started to investigate Adobe scripting and the following account details how I created a script that prompts the user for an input folder location and then processes each of the JPEG image files found within this folder. It creates an Adobe Illustrator file of a set dimension (that of the source images). It adds a number of layers, one for each type of annotation to be added to the image. And finally it adds some text and pen objects to these layers. When the script completes I am then able to go into each AI file and quickly edit the text and the positioning of the triangles and pen paths depending on what is contained within the image in question.

I use a Mac and the following details describe how you would complete this project using the Apple system. Having said that the UIs for both ExtendScript Toolkit and Illustrator differ very little on a Windows system.

  1. Launch ExtendScript Toolkit having found it using Spotlight.

ExtendScript Toolkit

  1. The first thing that you need to do is select the Adobe application that your script will target. There should be a drop-down list just below the menu bar on the left that you can use to select this. Choose your Adobe Illustrator version from the list.
  2. Notice how the Data Browser window changes to include a list of objects that represent your target Adobe application (you need to be running the application in parallel in order for this to work). This is live information and a great way of figuring out exactly which properties and objects are exposed by the application.
  3. OK, now it’s time to start coding. Create a new JavaScript document or use the default document that was opened when ExtendScript Toolkit first launched. The first thing we want to do is ask the user for the location of the folder that contains the source image files. Enter the following code into the text editor.

    var szDirPath = prompt("Image folder path", "~/Documents/archaeology/aiscripts/photosadddetails/img");
    if ((szDirPath == null) || (szDirPath == "")) {
        alert("The directory specified was not found.");
        return false;
    }
  1. The first observation to make here is that we are obviously dealing with JavaScript. If you are new to JavaScript check out any of the excellent JavaScript introductory courses that can found on Google. Adobe’s applications expose a number of objects that we can take advantage of to gain access to the application’s functionality.
  2. In this first bit of code, I’m using the prompt() function. This presents a dialog box to the user with a message (the first argument. The second argument specifies the default value of the text box) and then returns the text that the user enters as a string. I check that this is a valid string and then create a Folder object and use its getFiles() function to get an array containing a list of all the files in the specified folder.

    var dirPhotos = new Folder(szDirPath);
    var pFiles = dirPhotos.getFiles ();
  1. I then start to loop through the files array. This is the main loop for the script. It will read in each of the image files and then create a new Illustrator file for each of these files. First, it needs to check that each of the files has a JPG file extension.

    for (var i=0;i= 0)) {
  1. Now I need to ask Illustrator to create a new document. You do this by calling the app.documents.add() function.

            var doc = app.documents.add();
  1. The artboard dimensions now have to be changed to be the same size as that of the image. In this case I’m using constants but I’m sure that there is a way to find the length and width in pixels of the image to be imported.

            doc.artboards[0].artboardRect = [0, 3240, 4320, 0];
  1. Now we need to create the layers for the document, which you do as follows.

            var layerImage = doc.layers[0];
            layerImage.name = "image";
            var layerText = doc.layers.add();
            layerText.name = "text";
            var layerMarkers = doc.layers.add();
            layerMarkers.name = "markers";
            var layerLoci = doc.layers.add();
            layerLoci.name = "loci";
  1. We need to create a white RGBColor object that will be used to colour a number of the objects that will be subsequently created.

            var rgbWhite = new RGBColor();
            rgbWhite.red = 255;
            rgbWhite.green = 255;
            rgbWhite.blue = 255;
  1. Next we need to place the image within the image layer. You do this by creating a placeItem object and setting its file and positional properties.

            var pItem = layerImage.placedItems.add();
            pItem.file = pFiles[i];
            pItem.top = 3240;
            pItem.left = 0;
            pItem.width = 4320;
            pItem.height = 3240;
  1. Now we need to add our text objects. Create and initialise a textFrame object as follows.

            var textME = layerText.textFrames.add();
            textME.top = 1500;
            textME.left = 2000;
            textME.textRange.characterAttributes.size = 100;
            textME.textRange.characterAttributes.fillColor = rgbWhite;
            textME.contents = "ME XXX
+55.55";
  1. Now I want to add the dashed line area marker. Do this as follows.

            var cpathME = layerMarkers.compoundPathItems.add();
            var pathME = cpathME.pathItems.add();
            pathME.setEntirePath(Array(Array(1000, 2000), Array(1000, 2500), Array(500, 2500), Array(500, 2000), Array(1000, 2000)));
            pathME.stroked = true;
            pathME.strokeWidth= 10;
            pathME.strokeDashes = [50];
            pathME.filled = false;
            pathME.strokeColor= rgbWhite;
  1. Most of these properties are self-explanatory. Notice how the setEntirePath function takes an Array of 2D Arrays as its single argument. Each 2D Array is a point on the artboard in the order X, Y, in which the origin is in the top-left corner of the artboard.

Dashed line

 

  1. I wanted to add a particular design element to identify what are the principal stratigraphic entities used in the archaeological project. These loci are drawn within a triangle and so I accomplished this by creating a white path object in a triangular shape and positioning a text object inside of this. Note how I centre justify the text.

            var cpathLocus = layerLoci.compoundPathItems.add();
            var pathLocus = cpathLocus.pathItems.add();
            pathLocus.setEntirePath(Array(Array(2000, 2000), Array(2100, 2200), Array(2200, 2000), Array(2000, 2000)));
            pathLocus .stroked = true;
            pathLocus.strokeWidth= 10;
            pathLocus.filled = false;
            pathLocus.strokeColor= rgbWhite;
            var textLocus = layerLoci.textFrames.add();
            textLocus.top = 2050;
            textLocus.left = 2100;
            textLocus.textRange.characterAttributes.size = 85;
            textLocus.textRange.characterAttributes.fillColor = rgbWhite;
            textLocus.contents = "1";
            textLocus.paragraphs[0].paragraphAttributes.justification = Justification.CENTER;

Triangle

 

  1. As a finishing touch, I want the full artboard to be visible when the document is opened and so I need to change the zoom and centrePoint properties of the doc’s default view as follows.

            doc.views[0].zoom = 0.18;
            doc.views[0].centerPoint = [2500, 1000];
  1. We then need to specify a file path for the new AI file. You can see below that I edit the source image file name, which comes in a particular format. And finally, the document is closed.

            var szFileNameAI = (pFiles[i].name.substring(0, pFiles[i].name.indexOf("_")) + "A" + pFiles[i].name.substring(pFiles[i].name.indexOf("_"), pFiles[i].name.toLowerCase().indexOf(".jpg")));
            var szFilePathAI = (szDirPath + "/" + szFileNameAI + ".ai");
            var fileAI = new File(szFilePathAI);
            var saveOptions = new IllustratorSaveOptions();
            doc.saveAs(fileAI, saveOptions);
            doc.close();
  1. The loop code then repeats for the next image file found in the source folder location and it does this until the last file is found.
  2. And that’s it. The entire script can be downloaded from here. You install the script by copying the JSX file over to /Applications/Adobe Illustrator CC (or whatever version you are using)/Presets/en-US/Scripts and then restarting Illustrator. The script can be run by accessing it under File-Scripts.

It’s easy to see how you could take this code and modify it to do a whole number of tasks that if done by hand would take you ages. Enjoy!

Comments

submit