Skip to main content
Known Participant
February 15, 2021
Answered

Extendscript: Select group that has locked/hidden sub objects?

  • February 15, 2021
  • 2 replies
  • 2995 views

Using Extendscript, how do I set the selection to be a group which contains multiple objects like paths/images/whatever where only some of them are locked/hidden?

 

If you select such a group in Illustrator manually, there is no problem - you can see that the group is selected just fine. But, for example, if you were to select such a group in Illustrator, then run an Extendscript with only this line:

 

selection = selection

 

You will encounter this error:

 

error 9063: trying to select locked or hidden art

This is obviously completely ridiculous because it is stating that you cannot select something even though it has already been selected beforehand.

 

This error is frustrating as it makes trying to set the selection of groups nearly impossible most of the time, thus disrupting my ability to write useful scripts. Is there a way around this problem? Thanks!

 

This topic has been closed for replies.
Correct answer Silly-V

Hey, I tried to experiment with operator overloading operators (https://www.indiscripts.com/post/2010/05/operator-overloading-with-extendscript) and it kind of works - but you have to instantiate the Document.selection first as your own variable because setting the ["+"] to doc.selection didn't do anything. 

 

#target illustrator
function test () {

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

	function getSelectionUuids () {
		var sel = app.activeDocument.selection;
		var storedSelectionUuids = [];
		/** @TyPe {PageItem} */
		var thisItem;
		for (var i = 0; i < sel.length; i++) {
			thisItem = sel[i];
			storedSelectionUuids.push(thisItem.uuid);
		}
		return storedSelectionUuids;
	};

	/** @9397041 {string} uuids*/
	function setSelectionUuids (uuids) {
		var thisUuid;
		/** @TyPe {AiDocument} */
		var doc = app.activeDocument;
		for (var i = 0; i < uuids.length; i++) {
			thisUuid = uuids[i];
			try {
				doc.getPageItemFromUuid(thisUuid).selected = true;
			} catch (e) {

			}
		}
	};

	sel["+"] = function (/** @TyPe {PageItem[] | PageItem | Layer[] | Layer | Artboard[] | Artboard} */aiArtContainer) {
		/** @9397041 {PageItem} aiPageItem */
		function processPageItem (aiPageItem) {
			try {
				aiPageItem.selected = true;
			} catch (e) {
				// locked or hidden
			}
		};
		/** @9397041 {Layer} aiLayer */
		function processLayer (aiLayer) {
			aiLayer.hasSelectedArtwork = true;
		};
		/** @9397041 {Artboard} aiArtboard */
		function processArtboard (aiArtboard) {
			/** @TyPe {AiDocument} */
			var doc = app.activeDocument;
			/** @TyPe {string[]} */
			var storedSelectionUuids = getSelectionUuids(this);
			/** @TyPe {Artboards} */
			var docBoards = doc.artboards;
			var targetBoardRect = aiArtboard.artboardRect;
			var thisBoard;
			for (var i = 0; i < docBoards.length; i++) {
				thisBoard = docBoards[i];
				if (thisBoard.artboardRect.toString() == targetBoardRect.toString()) {
					docBoards.setActiveArtboardIndex(i);
					doc.selectObjectsOnActiveArtboard();
					storedSelectionUuids = storedSelectionUuids.concat(getSelectionUuids(this));
					break;
				}
			}
			setSelectionUuids(storedSelectionUuids);
		};
		/** Assigns processes to various input types, assumes items that have a `.uuid` are `PageItem` @9397041 {PageItem | Layer | Artboard} item */
		function forkByType (item) {
			switch (true) {
				case ("uuid" in item):
					processPageItem(item);
					break;
				case (item.typename == "Layer"):
					processLayer(item);
					break;
				case (item.typename == "Artboard"):
					processArtboard(item);
					break;
				default:
					// do nothing.
					break;
			}
		};
		
		var isArrayOp = (("length" in aiArtContainer) && !(aiArtContainer.typename == "PathItem")); // PathItems have a length property, not to be confused with Array's `.length`.
		if (isArrayOp) {
			var thisItem;
			for (var i = 0; i < aiArtContainer.length; i++) {
				thisItem = aiArtContainer[i];
				forkByType(thisItem);
			}
		} else {
			forkByType(aiArtContainer);
		}
	};

	
	// sel + doc.pageItems;
	// --> Selects all document pageItems.

	// sel + [doc.pageItems[0], doc.pageItems[1]];
	// --> Selects just 1st and 2nd pageItem.
	
	// sel + doc.activeLayer;
	// --> Selects active layer objects. Faster than writing "aiLayer.hasSelectedArtwork = true;" ?

	// sel + [doc.layers[0], doc.layers[2].pageItems[2]];
	// --> Selects all items on 1st layer, and 3rd item on 3rd layer.

	// sel + doc.artboards[2];
	// --> Selects all items on 3rd artboard.

	// sel + [doc.artboards[0], doc.artboards[1]];
	// --> Selects everything on all layers for 1st & 2nd artboard

	sel + [doc.artboards[0], doc.layers[0]];
	// --> Selects 1st artboard items on all layers, and every item on 1st layer

};
test();

 

2 replies

m1b
Community Expert
Community Expert
February 16, 2021

Aside from the "selection = selection" error, do you have other issues with GroupItems containing locked or hidden items? I did some quick tests and everything seemed to work fine regarding selecting partially-locked/hidden groups.

 

One weird detail I found is that manually clicking on the group on the page (screenshot A) selects the group and all it's children (even the locked or hidden ones), but clicking the little area to the right of the circle in the layers palette (screenshot B) only selects the non-locked, non-hidden child and in script, "selection" showed just the single pathItem, not the groupItem!

 

Screenshot A, clicking the group item on the page

 

Screenshot B, clicking beside the <Group> in the layers palette (red circle)

On the topic of AI's selection, I've never had any success *setting* the selection directly. It works to empty the selection, eg. selection = [], or selection = null. But doesn't work to do selection = app.activeDocument.pageItems, or selection = [item1, item2]. This is a real shame in my opinion.

- Mark

ariffjeffAuthor
Known Participant
February 16, 2021

You've described what I experience frequently. Selecting in Illustrator has different outcomes based on whether you click the actual layer or the art in the viewport. Then trying to set the selection in extendscript straight up causes an error when locked/hidden items/subitems are present. So we get at least 3 different results based off trying to do one thing. Extremely inconsistent and just adds more edge cases. Thank you Adobe.

 

Other than that I don't have other problems - nothing that I've found yet.

 

The way to get around the error of selecting groups with hidden/locked sub items in ExtendScript is to record which ones are hidden/locked, then unhide/unlock them, the select the group, then rehide/relock them.

Silly-V
Legend
February 15, 2021

I would never run a line "selection = selection", maybe it's the source of the error.

If you already have a selection in Illustrator, you just use it, you don't set it to something else that's already a scripting construct anyway, it may be creating all kinds of internal blow-ups when you do this.

 

Example: you have a path selected in Illustrator. To use it just go app.activeDocument.selection[0] and do whatever you want.

ariffjeffAuthor
Known Participant
February 15, 2021

I ran "selection = selection" as a test example to show that extendscript's behavior is incorrect. It doesn't make any conceivable sense that setting a variable to itself should return an error.

 

Regardless of that... my issue is that I want to actually set the selection to any desired group because in my case I am duplicating selections which is causing the original selection and its duplicate to become selected at the same time, so I therefore go to set the selection to either group and I just receive the error due to locked/hidden sub items. Simply referencing "app.activeDocument.selection[0]" in my particular case is not actually what I need.

ariffjeffAuthor
Known Participant
February 15, 2021

As an addon reason for why I need to set selections: In some cases I need to run commands in a script that change the type of an object, such as expanding a live text frame to become a path. This invalidates the textFrame object/variable that I am referencing because the data has completely changed and Illustrator/extendscript isn't smart enough to replace the old data with the new. In cases like this I might want to set a selection to the new data but I can't because the original object, and therefore, the new object was locked. I hope that make sense. It is a bit convoluted but it does make sense in specific scripting circumstances.