📜 ⬆️ ⬇️

How to start programming in Adobe Illustrator. Part two

This post is a continuation of the first part , where the Expand Clipping Mask script was presented and described in detail what and how it does, as well as the basic principles for creating such programs as a whole. In this part, I will continue the story of how to add new functionality to the program in order to get the “finished product” out of the “blank”. There is no way to do without a deeper immersion in the subject area, which is one of the necessary conditions for creating a full-fledged product. So, start the dive!


The following graphic primitives can be used as a mask outline in Adobe Illustrator: a simple path (Path), a composite path (Compound Path), a composite form (Compound Shape) and text objects (Point Text and Text on the Path). At the moment, the script only works with simple outlines, as can be seen from the code below, where PathItem is a call to the Path element.


  var clipGroup = sel[0].pageItems.length; 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; }; }; 

Before that, we declared the variable clipPath , but did not assign a value to it.


  var clipPath; 

This means that its value has not yet been determined, i.e. it is undefined . If you now select the mask, the contour of which is, say, the Compound Path and run the script, the program will generate an error on the last line of the functional part of the script,


  clipPath.remove(); 

since the condition in the loop will not be fulfilled, the clipPath variable will remain undefined , and it is impossible to apply the remove() method to something undefined. In order to prevent such a situation, we will do the following - assign clipPath value null , which, unlike undefined , is already something more specific, which can at least be checked.


Let's think about how to determine that some Compound Path is the outline of our mask? When I say "let's think", it means that I suggest to look into the documentation and find the property we need. Similar to PathItem we are looking for the clipping property. It turns out that the CompoundPathItem object does not have such a property, but there is a pathItems property through which you can get to the simple PathItem contours that have a clipping property.

Now we can turn our thoughts / searches into code. First of all, we check that the clipPath in the previous iteration has not been determined, and then we copy the already written block of code and make small changes to it.


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

Actually, the changes will affect only one line. As we see here, 'PathItem' has changed to 'CompoundPathItem', and also added a new construct 'pathItems [0]', with which we refer to the element of the composite path.


 if (sel[0].pageItems[i].typename == 'CompoundPathItem' && sel[0].pageItems[i].pathItems[0].clipping == true) { 

Below is the functional code block that has been created at the moment.


  var clipGroup = sel[0].pageItems.length; 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; }; }; if (clipPath == null) { for (var i = 0; i < clipGroup; i++) { if (sel[0].pageItems[i].typename == 'CompoundPathItem' && sel[0].pageItems[i].pathItems[0].clipping == true) { clipPath = sel[0].pageItems[i]; break; }; }; }; 

The next “patient” is the Compound Shape. Here it becomes quite interesting! In the documentation, we do not find such an object at all. What to do? To begin with, let's define what class of objects it belongs to. To find out, let's write a small helper code, which we throw out later. As it was said in the first part , we don’t touch upon the issue of the tools used for writing / debugging code. Therefore, suppose that this is a separate file, which will then simply go to the recycle bin. The code will be as follows:


 var obj = app.activeDocument.selection[0]; alert(obj.typename); 

In the first line, we create a link to the selected object, and in the second, we display a message about what type it is. Select the outline of the mask in Adobe Illustrator, i.e. That same Compound Shape object and run the script. In the message box, we see that the Compound Shape is a PluginItem. We get rid of the auxiliary code, we return to the documentation again, but we do not find the clipping property or pathItems in PluginItem. Nothing at all that would help us unequivocally indicate that this object is a mask outline. From the script you can not even determine what kind of plugin. Some external module and that's it!


That's an ambush! - you exclaim in hearts. And the brain feverishly works, in the hope of solving an unsolvable problem. And then, having touched all possible and impossible options, you desperately press Del and delete the hated Compound Shape. And here, out of the corner of your eye on the Layers palette, you notice that after this action, the mask container, which was the Clip Group , became just the Group . What could it mean? And the fact that the property of the clipped mask object from true became false . Here it is, the solution that can work! Of course, this is, by and large, a hack, but who cares if it helps to determine the desired contour.

The algorithm for determining the contour of the mask, represented by the Compound Shape object, will then be as follows: loop through all the mask objects and, when we find the PluginItem, we remove it and check if the clipped property of the mask container has changed. If it becomes false , then this is our circuit. The only thing for this hack to work is to update the DOM Illustrator after removing the object, which can be done using the app.redraw() method. Then you have to remember to return the deleted object, which is done using the app.undo() method.


Below is the code for the Compound Shape contour:


  if (clipPath == null) { for (var i = 0; i < clipGroup; i++) { if (sel[0].pageItems[i].typename == 'PluginItem') { sel[0].pageItems[i].remove(); app.redraw(); if (sel[0].clipped == false) { app.undo(); clipPath = sel[0].pageItems[i]; break; } else { app.undo(); } }; }; }; 

Now, of all the possible options for the type of objects that can be a mask outline, only text remains (or TextFrameItem , in illustrator scripting references terminology). Again we refer to the documentation and again we do not find clipping properties there. But this time we no longer worry so much about this and calmly find out that TextFrameItem has a property of kind , which determines the type of text object ( TextType ). We find out that there can be three such types: AREATEXT, POINTTEXT and PATHTEXT. The first type is not interesting to us, since it cannot be used as a mask outline, and the other two are just as interesting. It remains only to find the hack, which will help us determine now not the outline, but the text object, which is the outline of the mask. And this hack will be the team, Convert To Area Type, which converts POINTTEXT to AREATEXT. As with the Compound Shape, an implicit change in the clipped property clipped .


Accordingly, the code for a TextFrameItem of type POINTTEXT will be as follows:


  if (clipPath == null) { for (var i = 0; i < clipGroup; i++) { if (sel[0].pageItems[i].typename == 'TextFrame' && sel[0].pageItems[i].kind == 'TextType.POINTTEXT') { sel[0].pageItems[i].convertPointObjectToAreaObject(); app.redraw(); if (sel[0].clipped == false) { app.undo(); clipPath = sel[0].pageItems[i]; break; } else { app.undo(); } }; }; }; 

Only TextFrameItem type PATHTEXT remains. Unfortunately, when converting PATHTEXT to AREATEXT, the clipped property clipped not change. But since this is the last of the possible contenders for the title of “mask outline,” it is this behavior that can be used. That is, we check that after the execution of the Convert To Area Type clipped , the clipped property remains true . Below is the code for the TextFrameItem of type PATHTEXT.


  if (clipPath == null) { for (var i = 0; i < clipGroup; i++) { if (sel[0].pageItems[i].typename == 'TextFrame' && sel[0].pageItems[i].kind == 'TextType.PATHTEXT') { sel[0].pageItems[i].convertPointObjectToAreaObject(); app.redraw(); if (sel[0].clipped == true) { clipPath = sel[0].pageItems[i]; break; } else { app.undo(); } }; }; }; 

Thus, if we put together consistently written parts of the code, including a block of checks, we will get such code, the execution of which, as stated in the first part of the post, will implement the action of the Expand Clipping Mask command in Adobe Illustrator.


 #target illustrator if (app.documents.length > 0) { var doc = app.activeDocument; var sel = doc.selection; var clipPath = null; if (sel.length > 0) { if (sel[0].typename == 'GroupItem' && sel[0].clipped == true) { var clipGroup = sel[0].pageItems.length; 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; }; }; if (clipPath == null) { for (var i = 0; i < clipGroup; i++) { if (sel[0].pageItems[i].typename == 'CompoundPathItem' && sel[0].pageItems[i].pathItems[0].clipping == true) { clipPath = sel[0].pageItems[i]; break; }; }; }; if (clipPath == null) { for (var i = 0; i < clipGroup; i++) { if (sel[0].pageItems[i].typename == 'PluginItem') { sel[0].pageItems[i].remove(); app.redraw(); if (sel[0].clipped == false) { app.undo(); clipPath = sel[0].pageItems[i]; break; } else { app.undo(); } }; }; }; if (clipPath == null) { for (var i = 0; i < clipGroup; i++) { if (sel[0].pageItems[i].typename == 'TextFrame' && sel[0].pageItems[i].kind == 'TextType.POINTTEXT') { sel[0].pageItems[i].convertPointObjectToAreaObject(); app.redraw(); if (sel[0].clipped == false) { app.undo(); clipPath = sel[0].pageItems[i]; break; } else { app.undo(); } }; }; }; if (clipPath == null) { for (var i = 0; i < clipGroup; i++) { if (sel[0].pageItems[i].typename == 'TextFrame' && sel[0].pageItems[i].kind == 'TextType.PATHTEXT') { sel[0].pageItems[i].convertPointObjectToAreaObject(); app.redraw(); if (sel[0].clipped == true) { clipPath = sel[0].pageItems[i]; break; } else { app.undo(); } }; }; }; app.executeMenuCommand('releaseMask'); clipPath.remove(); } else { alert ('   -!'); }; } else { alert ('  !'); }; } else { alert ('  !'); }; 

Here you can put a full stop. No, better semicolon.


I hope these posts I helped you get a little closer to your goal - to start programming in Adobe Illustrator. Thanks for attention!


')

Source: https://habr.com/ru/post/452614/


All Articles