Drawing: Assumptions, Mistakes, and Solutions

My most recent blog post on drawing paths in Illustrator, InDesign, and Photoshop (here) was an interesting exercise for me. In the process of writing the drawing routines, I made several mistakes, found that things I assumed to be true were, in fact, false.

I’ve solved the various problems and have created a workaround or two. I thought it might be useful for other CS Extension developers if I documented the problems and solutions. This way, you can learn from my mistakes. I make lots of mistakes, so it would be nice if we could get some use out of them.

In this article, I’ll show solutions for some of the problems I left unresolved in the previous post. For the most part, they weren’t critical to the point of the post, but it’s nice to tie up the loose ends. I’ve posted an updated version of the project here (this contains all of the code changes discussed in this post):

drawing

Update: This project is now available via Import>Adobe Creative Suite Extension Builder>Remote Creative Suite SDK examples.

My Point of View

First, let me admit to a bias: In my opinion, everything an application can do should be scriptable. Everything. I think InDesign has the best scripting support of the three applications, because its coverage is the most complete. This isn’t to say that it’s perfect, just that it’s a lot closer to my ideal. This means that I tend to approach the other applications with an InDesign bias.

Next, I’m coming to CS Extensions from the world of ExtendScript. My instinct is to develop and debug using the ExtendScript Toolkit (ESTK), then port to ActionScript. I think that this approach is probably shared by quite a few CS Extension developers.

Assumptions and Puzzles

In the extension, I needed to be able to create a sample document of a particular size. I’m somewhat new to Illustrator scripting, but I assumed that this would be easy. I went to my first source for information about the objects in an application’s scripting object model: the Object Model Viewer in the ESTK. Given my InDesign bias, I expected to find an add() method on Illustrator’s documents collection. But when I glanced at the Documents object, I found no obvious mention of an add() method.

Eventually, I realized that I needed to click the Class option in the Types list to display the relevant methods. This is different from InDesign, which puts the corresponding method in the Instance listing.

Best of all, I found that simply typing app.documents.add( in ActionScript gives me a nice listing of the method parameters, thanks to the CSAW ActionScript library for Illustrator. If I’d started in ActionScript, I’d have saved myself a certain amount of headscratching and floundering about in the ESTK.

Open the drawing project from the previous post and open the drawingIllustrator.as file. Replace the existing makeSampleDocument function with the following:

private static function makeSampleDocument():void
{
	var document:Document = app.documents.add(DocumentColorSpace.CMYK, model.sampleWidth, model.sampleHeight);
}

 

Open the AppController.as file and change the drawShape() function to the following (this removes the special case for Illustrator):

public function drawShape():void{
	var array:Array = calculatePoints();
	switch(model.hostName){
		case "indesign":
			drawingInDesign.drawShape(array);
			break;
		case "illustrator":
			drawingIllustrator.drawShape(array);
			break;
		case "photoshop":
			drawingPhotoshop.drawShape(array);
			break;
	}
}

 

Lesson: You might as well start in ActionScript, and use the ESTK only when you run into problems. The auto-complete feature is a quicker way to discover methods and method parameters than the ESTK’s Object Model Viewer. (That said, the interactive debugging in the ESTK’s JavaScript console is still faster than the more formal debugging process in Flash Builder.)

Next, I assumed that Illustrator’s path point coordinates were a.) relative to the current ruler zero point, and b.) expressed in the current measurement units. These seemed like safe assumptions, as both are true in Photoshop and InDesign. I went to the ESTK to try to find a way to get and set the zero point and ruler units. After a lot of fruitless searching, I turned to the Illustrator Scripting Guide, which told me that Illustrator scripting always uses points, regardless of the current units of measurement, and that Illustrator coordinates are always relative to the lower-left corner of the page, regardless of the location of the zero point. Neither fact, as far as I can tell, is mentioned in the ESTK’s object model viewer.

This means, however, that the Illustrator paths will be upside down, relative to the paths drawn in the other applications. This doesn’t cause a problem for our examples, but we should make changes to the project to provide completely general drawing routines.

Open the AppController.as file and add the following line at the start of the Illustrator case in the drawShape function (this inverts the path point coordinates around the vertical center point).

array = drawingIllustrator.recalculatePoints(array);

 

Open the drawingIllustrator.as file and add the following function.

public static function recalculatePoints(array:Array):Array{
	for(var counter:int = 0; counter < array.length; counter++){
		if(array[counter].length == 2){
			array[counter][1] = model.center[1] + ((model.center[1]-array[counter][1])*-1);
		}
		else{
			array[counter][0][1] = model.center[1] + ((model.center[1]-array[counter][0][1])*-1);
			array[counter][1][1] = model.center[1] + ((model.center[1]-array[counter][1][1])*-1);;
			array[counter][2][1] = model.center[1] + ((model.center[1]-array[counter][2][1])*-1);;
		}
	}
	return array;
}

 

Lesson: It’s never too early to read the documentation. While I still believe that Illustrator should behave like the other applications, I was able to find a way to do what I wanted to do.

When drawing the paths, I assumed that all applications used the same names for the same control handles–that is, that “left direction” means the control handle associated with the incoming line segment, and that “right direction” means the control handle associated with the outgoing line segment (relative to the path point anchor and the direction of the path). It could be that Photoshop’s understanding of the terms is the opposite of the behavior found in Illustrator and InDesign, or it might be that Photoshop’s default path direction differs. This may well be a bug, but we can work around it.

Open the drawingPhotoshop.as file and change the line:

pathPointInfoArray.push(makePathPointInfo(PointKind.SMOOTHPOINT, new Array(array[counter][0][0], array[counter][0][1]), new Array(array[counter][1][0], array[counter][1][1]), new Array(array[counter][2][0], array[counter][2][1])));

 

To:

pathPointInfoArray.push(makePathPointInfo
	(
		PointKind.SMOOTHPOINT,
		new Array(
			array[counter][2][0],
			array[counter][2][1]
		),
		new Array(
			array[counter][1][0],
			array[counter][1][1]
		),
		new Array(
			array[counter][0][0],
			array[counter][0][1]
		)
	)
);

 

Lesson: Everything you know might be wrong.

When I first tried to draw a path in the sample document I’d created in Photoshop, I got an error message telling me that the document wasn’t the active document, and that I could only draw a path in the active document. Ordinarily, opening or creating a document in Photoshop makes that document the active document. Setting the active document had no effect–as it turns out, this is a known problem with Photoshop in Windows (it works fine on the Mac OS).

In the sample project I presented last time, I simply punted: if you have a document open when you click the button to draw a path, you’ll be prompted to close all open documents. Again, this worked reasonably well for the sample, but it bothered me. I have not yet found a way around this problem apart from closing and then opening the document, which isn’t practical for the sample extension.

Regardless, I managed to reverse the logic in the previous example. Open the drawingPhotoshop.as file and replace the drawShape() function with the following:

public static function drawShape(array:Array):void
{
	if(app.documents.length == 0)
	{
		if(model.sampleDocument == true){
			makeSampleDocument();
		}
		var document:Document = app.activeDocument;
		var subPathInfo:SubPathInfo = makeSubPathInfo(array);
		var subPathInfoArray:Array = new Array();
		subPathInfoArray.push(subPathInfo);
		var pathItem:PathItem = document.pathItems.add("sample", subPathInfoArray);
	}
	else
	{
		trace("Please close all open documents and try again.");
	}
}

 

You Are Not Alone

If you’re struggling to write code that works in multiple Creative Suite applications, don’t feel like you’re stupid, or that you’re alone. This is a very new field. While we’re not quite making things up as we go along, there are simply too many different moving parts for us to anticipate every case. I’ve been working with the CS Extension Builder and the CSAW libraries for more than a year, and there’s still lots of stuff I don’t know.

In addition, my assumptions masked relatively subtle differences between the products—and uncovering problems took quite a bit of research and testing.

The good news is that we can help. If I were a CS Extension developer, I’d have been posting my problems on the CS SDK forum. Everyone on the team reads the forum, and there’s a good chance that we’ve run into the problem you’re having. It’s a great resource that I’m going to be using a lot more. In addition, the general scripting forums for the specific products can be useful for troubleshooting problems related to constructing and referring to objects in the applications’s scripting model.

That’s all for now, but I’ll be back with more examples—and, probably, more mistakes–soon.

One Response to Drawing: Assumptions, Mistakes, and Solutions

  1. smick says:

    THIS is a really good blog post. Though it’s more on the code side than I really understand, just acknowledging the differences is something I just simply appreciate.

    to be in Illustrator, Photoshop, InDesign and Flash and even LiveMotion (back in the day), despite being the same path tools at a glance, they always operate differently, which is off putting.