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

Explorer ,
Feb 15, 2021

Copy link to clipboard

Copied

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!

 

TOPICS
Bug, Scripting

Views

121

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Adobe Community Professional ,
Feb 15, 2021

Copy link to clipboard

Copied

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.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Explorer ,
Feb 15, 2021

Copy link to clipboard

Copied

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.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Explorer ,
Feb 15, 2021

Copy link to clipboard

Copied

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.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Adobe Community Professional ,
Feb 15, 2021

Copy link to clipboard

Copied

To select something after text is outlined or stuff is embedded, you can grab the resulting item and use it because some of these methods will return a group item of the result. In other cases like expanding appearance, you can select the original item with .selected = true and afterwards look through doc.selection to get at any newly-created items from the operation.

 

Importantly, "selection" is actually not just a variable name, it's a reserved Illustrator scripting class that has a lot of under-the-hood stuff that will break things when you start to use it as a variable. Same for "path" - that's the application's file-path, etc. You don't wanna do var path = ..., it will cause problems. I think.

 

I am not sure what you mean by "locking". If your operation after which you want to select things does indeed put items into a locked/hidden place or state, then you have to unlock those items! However your writing suggests you did not intentionally place any art into a locked or hidden place or state. Therefore, I think the error is erroneously suggesting 'locked layer' or whatever, but in reality it's just blowing up internally and picks whatever the next error is out of what it's programmed to do.

 

In the case where you actually are intentionally locking or hiding items after an operation, you will have to unlock or unhide the items to select them. What happens when you have items selected and then lock them in the UI? The selection goes away, and when you try to re-select via script, it will cause the error you are seeing. If you have something selected and your script does something that locks it, it will no longer be selected. If you have a selection and lock it and expect it to still be there because it was 'selected beforehand', it will do the exact same thing as selecting something in the UI, locking it and expecting it to still be selected because it was previously at one point of time selected.

 

Note on uuids: in the newest Ai versions we can use unique identifiers for items. So now we can keep a track of items in and out of selections even though selections can be etherial. Loop through your selection when it's active and push the uuids of items to an array. After your changes which may have altered your selection, you can loop back through your array of uuids to get a reference to something which was previously selected but now is not. This is only for times when you are manipulating selection items, not for when new items result such as expand appearance or outline text.

 

See an example where Uuids are used here: https://community.adobe.com/t5/illustrator/easy-way-to-draw-connections-between-dots/m-p/11802910

Although no changes are made to the selection and the use of Uuids is not really necessary in this example, it would be in the case where the original selection is changed.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Explorer ,
Feb 15, 2021

Copy link to clipboard

Copied

While it looks like I ultimately did not do the best job of accurately explaining my issue in such a way for you to properly understand it, you still were able to help me by getting me to think in another way to find solution to my problem! Thank you so much for your writeup. I'll be unsetting and resetting locked/hidden items with UUIDs so that I can therefore set a desired selection during those two actions.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Adobe Community Professional ,
Feb 15, 2021

Copy link to clipboard

Copied

Sure thing. Note that it is not even necessary to use Uuids because you can simply push PageItem objects into an array. The problem with that is, sometimes you do something and those items change or they're destroyed and your array still has them. And if that happens and you loop over this array, it may have old references which are still active, but corrupted, so it may result in PARM errors, etc. So if you use Uuids, they are just strings and when you loop over them in an array you can try-catch any destroyed items (using the document.getPageItemFromUuid() in the try-catch) to not handle them, but there won't be a question of old/corrupted variables referencing destroyed items in scripting memory.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Engaged ,
Feb 15, 2021

Copy link to clipboard

Copied

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

Screen Shot 2021-02-16 at 2.24.21 pm.png

 

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

Screen Shot 2021-02-16 at 2.24.25 pm.png

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

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Explorer ,
Feb 16, 2021

Copy link to clipboard

Copied

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.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Adobe Community Professional ,
Feb 16, 2021

Copy link to clipboard

Copied

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;
	};

	/** @Param {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) {
		/** @Param {PageItem} aiPageItem */
		function processPageItem (aiPageItem) {
			try {
				aiPageItem.selected = true;
			} catch (e) {
				// locked or hidden
			}
		};
		/** @Param {Layer} aiLayer */
		function processLayer (aiLayer) {
			aiLayer.hasSelectedArtwork = true;
		};
		/** @Param {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` @Param {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();

 

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Adobe Community Professional ,
Feb 16, 2021

Copy link to clipboard

Copied

 

"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!"

 

@m1b , It looks like you're describing the difference between "Normal Selection" and "Direct Selection" tool. Clicking a group on the artboard with the "Direct Selection" tool will yield the same result as ScreenShot B.

 

EDIT:
Actually, I'm seeing the difference between "Groups" and "Layers". "Normal" select will select locked items in a group but not a Layer.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more