How to start programming in Adobe Illustrator. Part one

I want to warn you right away that this series of posts is not for seasoned programmers, or even for programmers at all. I understand that this sounds extremely defiant, given the IT theme of the resource, and still let me explain ... As an audience, I see ordinary designers who would like to start programming in the Adobe environment, but for some reason (because of fear of unknown, lack of confidence in their capabilities or lack of knowledge of the language) can not take the first steps in this direction. I see my modest task in helping them to understand that “it’s not the gods who burn the pots” and any sufficiently motivated person can learn to write working program code. It is possible that some of them will be so carried away by this game that they decide to become real developers. What code is not joking?


This post will tell you how to write your own small tool (JavaScript script) to create your own unique tool in Adobe Illustrator, which will not only reduce your time, but also improve interaction with this wonderful graphics editor. First, I will formulate the problem, then I will show the code that solves it and, further, I will tell you in detail about how it was created. It will not discuss the basics of Javascript, the features of the Illustrator object model or various editors for writing / debugging code. You can find this information yourself if you wish. The main thing, in my opinion, is an understanding of the basic principles of writing programs, which is the main focus of this article. If you are ready to jump just above your head, welcome to the cut!


Adobe Illustrator has a Clipping Mask tool that works with clipping masks. The Clipping Mask contains three commands: Make, Release, and Edit Mask. The first creates a mask, the second - parses, the third - allows you to edit. We are interested in the second command, which splits the Clipping Mask object into a contour and the contents of the mask. Very often it is necessary not only to disassemble the mask, but also to get rid of the outline of the mask, leaving only the contents. The regular Release Clipping Mask command does not do this, so after applying it, you need to perform three more steps:


  1. Remove selection from objects
  2. Select mask outline only
  3. Remove mask outline

If you have to perform this sequential operation quite often during the day, the question arises: is it impossible to somehow reduce the number of these actions to get the same result? And the point here is not laziness at all, but the lack of the necessary tool. Now, imagine for a second that you have such a tool.


Here you are briefly distracted from work and plunged into thoughts about how cool it would be if there was a team in the arsenal of Adobe Illustrator, such as the Expand Clipping Mask, which performed all these actions for you. Cool idea! I need to write Adobe technical support, you think. The next thought: all of a sudden they have been written about this many times? And if someday and maybe they will add such a tool, then what is this for you? This team is needed here and now!

And here comes the moment of truth - you can write the script yourself!

No sooner said than done!


1  #target illustrator
2  if (app.documents.length > 0) {
3    var doc = app.activeDocument;
4    var sel = doc.selection;
5    var clipPath;
6    if (sel.length > 0) {
7      if (sel[0].typename == 'GroupItem' && sel[0].clipped == true) {
8        var clipGroup = sel[0].pageItems.length;
9        for (var i = 0; i < clipGroup; i++) {
10         if (sel[0].pageItems[i].typename == 'PathItem' &&
               sel[0].pageItems[i].clipping == true) {
11           clipPath = sel[0].pageItems[i];
12           break;
13         };
14       };
15       app.executeMenuCommand('releaseMask');
16       clipPath.remove();
17     }
18     else {
19      alert ('Выделение не является объектом-маской!');
20     };
21   }
22   else {
23     alert ('Нет выделенных объектов!');
24   };
25 }
26 else {
27   alert ('Нет открытых документов!');
28 };

Here is such a small code to help solve this problem. Instead of several, already in the order of bored actions, you only need to do one thing - run the Expand Clipping Mask script. Now you have a handy tool for working with masks, made by the same do-it-yourself.


Of course, here I am a little disingenuous. Firstly, the script was not written by you, and secondly - it is not as universal as we would like. However, if you are interested in how it works, and most importantly, how to learn how to write such programs yourself, then I will be glad to tell you about this in order and with detailed comments.


To begin with, any script (script / program / JavaScript code) consists of several main blocks of code: declaration (initialization) of variables, basic checks (conditions) and, say, the “program engine” - code that implements the main working functional script. Of course, this division is very arbitrary, since the functional part also has checks, but the structural principle is the same. Naturally, the larger the program, the more difficult it will be to divide it into similar blocks. But in our case, it is possible. Lines 8 to 16 are the script engine, the remaining lines are variable declarations and various basic checks with their processing. If you count, it turns out that the number of lines in the check block is greater than the number of lines in the function block. Are these checks really important?


Why do we need checks?


Serious programmers will understand me, and it will be useful for beginning developers to find out about it. Checks are needed in order to ensure the normal functioning of the functional part of the program. If you do not write them, then any error during the execution of the script will lead to a software failure. And this is not good.


Of course, the serious comrades mentioned above use the try / catch construct for such purposes, but I decided that the usual if / else would be a laconic and more understandable construct. Especially for novice script writers.


Let us examine in detail what these lines do. The first line is responsible for the fact that even if the script is not run from Adobe Illustrator, it will be executed in it. Accordingly, if you run the script from Illustrator, this line can be omitted.


#target illustrator

Next, it checks for open documents in Adobe Illustrator at the time the script is run. These lines should be read as follows: if ( if) in the application ( app) the number of documents ( documents.length) is greater than zero (> 0), then the code enclosed in {...} should be executed. Otherwise ( else), display the message ( alert) 'No documents open!' and complete the script.


if (app.documents.length > 0) {
...
...
}
else {
  alert ('Нет открытых документов!');
};

The following block of code checks for selection in the document.


  if (sel.length > 0) {
...
...
  }
  else {
    alert ('Нет выделенных объектов!');
  };

It should be noted that if in the previous examples we used reserved names (such as, appor documents), then here we use the sel variable, which we ourselves determined in lines 3 and 4,


  var doc = app.activeDocument;
  var sel = doc.selection;

where docis the link to the active Illustrator document, and selis the link to the selected object / objects in the active document.


A reference (or reference) is a pointer to a specific object. Of course, I am well aware that the completely harmless Russian word "pointer" is capable of introducing into the stupor any person who is not familiar with OOP (object-oriented programming). But take a word, everything is not as complicated as it seems. Links are stored in variables and are used to access objects. In a variable, docwe store (assign a value to it through the assignment operator =) a pointer to the active document (activeDocument) of the application (app), and in a variable sel, we save the pointer to selection (selection) in the active document (activeDocument) of the application (app). Just so as not to write app.activeDocument again, we use the variable insteaddocin which this code is already contained. That's why the link will look like this sel = doc.selection. I hope I clearly explained.

Thus, in this condition, if (sel.length > 0)it is checked whether there are selected objects in the active document, and if not, the message “No selected objects!” Is displayed.


The following lines check the veracity, sorry for the pun, two conditions at once. The first that the selected object is a group ( GroupItem) and (&&) the second that this group is really a clipping mask (the property of clippedthis object is equal true).


    if (sel[0].typename == 'GroupItem' && sel[0].clipped == true) {
...
...
    }
    else {
      alert ('Выделение не является объектом-маской!');
    };

Here we need a little explanation.


What is a mask object? This is a group, but not an ordinary one. A regular group is a collection of different objects that is subordinate to, say, the “main” or “parent” object. As for the mask object, this is also a group, but unlike the usual one, it consists of two parts - the outline of the mask and its contents. So, to determine from the script what is in front of you - an ordinary group or a mask group allows its property clipped. If the value of the clipped property is false(false), then this is an ordinary group, if true(true) - then this is a clipping group.

Inquiring minds will notice that instead of the variable selthat we defined earlier, the construct is used sel[0]. This is explained by the fact that, from the point of view of the script, a selection is a collection (collection) of elements, and not a specific object (even if only one object is selected). And to check the type ( typename) of this object for matching the type of the selected collection element, a construct is used sel[0]that refers to the first [0]element of the collection, that is, in our case, to the selected group.


As a result, if the selected object is both a group and a mask, then the further code is executed, otherwise the message is displayed: 'Selection is not a mask object!'


With checks - that's it. Move on.


How was the main code created?


In this part of the article I’ll try not just to comment on how the code works, but to describe the process of its creation. If not the whole process, then at least some key points. Let's get started!


Earlier, three actions were described that must be performed to solve the problem of “disassembling” the Clipping Mask with the subsequent removal of the mask outline. They will be supplemented with another action (Release command), from which our algorithm will begin. I will repeat them here to refresh the context.


  1. Run the Release Clipping Mask command
  2. Remove selection from objects
  3. Select mask outline only
  4. Remove mask outline

If you implement this sequence of actions strictly according to the list, then the first two points can be easily solved by calling the method executeMenuCommand(). But then, at the third point, we will face an unsolvable problem. How to get a link to the mask outline, if after the first action (Release Clipping Mask) there is no mask anymore, but there is only a set of selected objects? Yes, and he is structurally not the same as before performing this operation.


In general, logic suggests that first you need to create a reference to the mask outline object. After thinking about what makes the mask contour so unique compared to the usual contour, we will find a property clippingin the class PathItem. Now we just have to iterate over the objects of the mask group in the (for) loop and find them PathItemwith the property clipping = true. This will be the desired circuit. As a result of executing this code, we will get a link to the mask outline object and save it in a variable clipPath.


      for (var i = 0; i < clipGroup; i++) {
        if (sel[0].pageItems[i].typename == 'PathItem' &&
            sel[0].pageItems[i].clipping == true) {
          clipPath = sel[0].pageItems[i];
          break;
        };
      };

What's next? Let's go back to the algorithm and write the code for item 1. This line executes the Release Clipping Mask command, but not through the user interface, but from the script. Yes, it is so simple!


      app.executeMenuCommand('releaseMask');

We skip steps 2 and 3 (because we already have a mask outline, or rather, a link to the clipPath object) and go directly to step 4. Here we call the remove()object's method clipPath. This method removes the outline of the mask.


      clipPath.remove();

That's all for now. Thanks for attention!


I hope you now understand that starting to program in Adobe Illustrator is not as difficult as it seems at first glance.


PS Of course, the resulting script is far from ideal. It does not work with masks whose outlines are represented by objects CompoundPath, CompoundShapeor TextFrame. Read how to modify the script so that it becomes a truly full-fledged tool in the second part .


Also popular now: