Skip to main content
malinn6934325
Participating Frequently
May 8, 2015
Answered

Script for randomly replacing symbols

  • May 8, 2015
  • 2 replies
  • 5377 views

Hi!

I'm looking for a script that will allow you to select some objects or symbols in your AI-file and then select some of your symbols from the symbols-library in your file and then randomly replace the original objects/symbols with the symbols selected. I have found a few scripts that does basically this but with random replacement of selected colours, transparency or angles but I need one for randomly replacing symbols.

Can someone help?

This topic has been closed for replies.
Correct answer wckdtall

I revised an alternate workaround to selecting the symbols you want to randomize by placing them in a specific layer. On first run it will create the layer if it can't find it. Also revised the original script to size proportinatly based on height, and place the items within the original parent layer:

var doc = app.activeDocument;
var sym = doc.symbols;
var fullSelection = doc.selection;
try { doc.layers["Symbols_To_Place"] }
catch (e) {
    symLyr = doc.layers.add();
    symLyr.name = "Symbols_To_Place";
    alert("Select Symbols to randomize\nBy placing in \"Symbols_To_Place\" Layer.")
}
var symTgt = doc.layers["Symbols_To_Place"].symbolItems;
var symAry = [];
for (var s = 0; s < symTgt.length; s++) {
    nm = symTgt[s].symbol.name;
    symAry.push(nm);
}
for (var i = 0; i < fullSelection.length; i++) {
    var sel = fullSelection[i];

    var place = doc.symbolItems.add(sym[symAry[Math.floor(Math.random() * symAry.length)]]);
    place.move(sel, ElementPlacement.PLACEBEFORE);
    //Base it off the height
    percent = sel.height / place.height;
    place.height = sel.height;
    place.width = place.width * percent;
    place.top = sel.top;
    place.left = sel.left;
    sel.remove();
    place.selected = true;
}

 

 

 

 

2 replies

Participating Frequently
January 29, 2021

Great script! Could it be modified to use only selected symbols from the Symbols palette?

Silly-V
Legend
January 29, 2021

The problem is that with scripting they don't let us know what symbols are even selected in the Symbols palette. However through ponderous workarounds, we can sort of get there.

If the action of using the Symbol panel's flyout menu's delete or duplicate commands is recordable, we can play it with a script. This means that we can read all the symbols in the document's symbols collection and then perform one of those actions and compare the before/after difference of what was there and not. However, if you do the delete action, you will have to do an Undo to get the symbols back and worse it may mangle some symbol instances you did not wish to disturb if those symbols are removed.

With Duplicate action all you have to do is remove all the symbols which are added that are considered new.

I can try this sometime this weekend and see if it works!

Silly-V
Legend
January 30, 2021

Silly-V,

Thanks for your response.

Very kind of you to test a workaround.

Take care.


It worked. However, one must be wary of Illustrator's symbol naming copy conventions as new symbols get a sequential number on the end of them. When you copy "Symbol 1", it becomes called "Symbol 2". But when you copy both "Symbol 1" and "Symbol 2" you get "Symbol 3" and "Symbol 4". We could have extra code that does a more thorough check that tries to match that "Symbol 3" is a copy of "Symbol 1" and so on, but if you rename "Symbol 1" in set ["Symbol 1", "Symbol 2"] to "Symbol 23", the next duplicated symbol name would be "Symbol 3". And if you have ["Symbol 1", "Symbol 2", "Symbol 4"], the duplicated copies will fill a spot for "Symbol 3" but skip over and become "Symbol 5" the next duplication.

With all this happening, it's better to just stick with a convention of don't name symbols with digits on the end or don't copy symbols without renaming them to something less-generic. In my code it will only get the latest copy name so looking to match an auto-named "Symbol 3" will actually find "Symbol 4" in ["Symbol 1", "Symbol 2", "Symbol 4"] but auto-named "Symbol 5" will find "Symbol 4" in ["Symbol 1", "Symbol 2", "Symbol 3",  "Symbol 4"].

 

 

#target illustrator
function test () {

	if (typeof(String.escapeForRegexp) != "function") {
		String.prototype.escapeForRegexp = function () {
			return this.replace(/([\/.*+?|()[\]{}\\^$])/g, "\\$1");
		};
	}

	if (!Array.prototype.indexOf) {
		Array.prototype.indexOf = function(searchElement, fromIndex) {
			var k;
			if (this == null) {
				throw new TypeError('"this" is null or not defined');
			}
			var o = Object(this);
			var len = o.length >>> 0;
			if (len === 0) {
				return -1;
			}
			var n = +fromIndex || 0;
			if (Math.abs(n) === Infinity) {
				n = 0;
			}
			if (n >= len) {
				return -1;
			}
			k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
			while (k < len) {
				if (k in o && o[k] === searchElement) {
					return k;
				}
				k++;
			}
			return -1;
		};
	};

	/**
	 * Matches a name of a new copied symbol "Symbol 1" to a name in the list of symbols ["Symbol", "Other Symbol"] before processing.
	 * Will have no choice but to use latest copy name: "Symbol 3" in ["Symbol", "Symbol 1", "Symbol 2"] will result in "Symbol 2" even if the copied original was actually "Symbol 1".
	 *  {string[]} originalSymbolNames - Array of original symbol names before any were duplicated via action.
	 *  {string} symbolNameToCheck - The name of a new copied symbol.
	 *  {string} - Name of the original symbol to use, or name of the last copy of that symbol in the symbols panel.
	 */
	function getLatestSymbolCopyName (originalSymbolNames, symbolNameToCheck) {
		var thisSymbolName;
		var previousCopyNames = [];
		var endSuffixRx = /\s\d+$/;
		for (var i = 0; i < originalSymbolNames.length; i++) {
			thisSymbolName = originalSymbolNames[i];
			if (thisSymbolName.replace(endSuffixRx, "") == symbolNameToCheck.replace(endSuffixRx, "")) {
				previousCopyNames.push(thisSymbolName);
			}
		}
		previousCopyNames.sort(function (a, b) {
			if (endSuffixRx.test(a) && !endSuffixRx.test(b)) {
				return true;
			} else if (!endSuffixRx.test(a) && endSuffixRx.test(b)) {
				return false;
			}
			var aSuffix = a.match(endSuffixRx)[0].substr(1) * 1;
			var bSuffix = b.match(endSuffixRx)[0].substr(1) * 1;
			return aSuffix < bSuffix;
		});
		var latestCopyName = previousCopyNames[0];
		return latestCopyName;
	};

	/**
	 * Replaces a symbol instance art object's symbol reference to another symbol definition.
	 *  {AiSymbolItem} symbolArt - The symbol instance inside the document (on an artboard/in a layer).
	 *  {AiSymbol} symbolDefinition - Symbol definition in the Symbols panel.
	 */
	function replaceSymbolReference (symbolArt, symbolDefinition) {
		symbolArt.symbol = symbolDefinition;
	};

	function replaceDocumentArt (doc, docArt, symbolDefinition) {
		var newPlacedArt = doc.symbolItems.add(symbolDefinition);
		newPlacedArt.move(docArt, ElementPlacement.PLACEAFTER);
		newPlacedArt.height = docArt.height;
		newPlacedArt.width = docArt.width;
		newPlacedArt.top = docArt.top;
		newPlacedArt.left = docArt.left;
		docArt.remove();
		return newPlacedArt;
	};

	var doc = app.activeDocument;
	doc.layers[0].visible = true;
	doc.layers[0].locked = false;
	var sel = doc.selection;
	if (sel.length == 0) {
		alert("No selection");
		return;
	}
	var sym = doc.symbols;
	var originalSymbolNames = [];
	var endSuffixRx = /\s\d+$/;
	
	var thisSymbol, thisSymbolName;

	for (var i = 0; i < sym.length; i++) {
		thisSymbol = sym[i];
		thisSymbolName = thisSymbol.name;
		originalSymbolNames.push(thisSymbolName);
	}

	app.doScript("Duplicate Symbol", "Duplicate Symbol Set");

	var newSymbolNames = [];

	for (var i = 0; i < sym.length; i++) {
		thisSymbol = sym[i];
		thisSymbolName = thisSymbol.name;
		if (originalSymbolNames.indexOf(thisSymbolName) == -1) {
			newSymbolNames.push(thisSymbolName);
		}
	}

	if (newSymbolNames.length == 0) {
		alert("No symbols were found to be selected.");
		return;
	}

	// clean up copied symbols.
	for (var i = 0; i < newSymbolNames.length; i++) {
		thisSymbolName = newSymbolNames[i];
		thisSymbol = doc.symbols.getByName(thisSymbolName);
		thisSymbol.remove();
	}

	var selectedSymbolNames = [];
	var thisOriginalName;
	var thisNewName;
	var latestCopyName;
	for (var i = 0; i < originalSymbolNames.length; i++) {
		thisOriginalName = originalSymbolNames[i];
		for (var j = 0; j < newSymbolNames.length; j++) {
			thisNewName = newSymbolNames[j];
			if (thisNewName.replace(endSuffixRx, "") == thisOriginalName.replace(endSuffixRx, "")) {
				latestCopyName = getLatestSymbolCopyName(originalSymbolNames, thisNewName);
				selectedSymbolNames.push(latestCopyName);
				continue;
			}
		}
	}

	var symbolToPlace, symbolToPlaceName, dynamicIndex;
	var thisSelectedArt, replacingArt;
	var replacingItemUuids = [];
	for (var i = sel.length - 1; i > -1; i--) {
		thisSelectedArt = sel[i];
		dynamicIndex = Math.floor(Math.random() * selectedSymbolNames.length);
		symbolToPlaceName = selectedSymbolNames[dynamicIndex];
		symbolToPlace = sym.getByName(symbolToPlaceName);
		if (thisSelectedArt.typename == "SymbolItem") { // selected item is a symbol: replace the symbol.
			replaceSymbolReference(thisSelectedArt, symbolToPlace);
		} else if (thisSelectedArt.typename == "GroupItem" && thisSelectedArt.pageItems.length == 1) {
			if (thisSelectedArt.symbolItems.length == 1) {
				replaceSymbolReference(thisSelectedArt.symbolItems[0], symbolToPlace);
			} else {
				replacingArt = replaceDocumentArt(doc, thisSelectedArt.pageItems[0], symbolToPlace);
				replacingItemUuids.push(replacingArt.uuid);
			}
		} else {
			replacingArt = replaceDocumentArt(doc, thisSelectedArt, symbolToPlace);
			replacingItemUuids.push(replacingArt.uuid);
		}
	}

	var thisItem;
	for (var i = 0; i < replacingItemUuids.length; i++) {
		thisItem = doc.getPageItemFromUuid(replacingItemUuids[i]);
		thisItem.selected = true;
	}

};
test();

ANd here's the action file

/version 3
/name [ 20
	4475706c69636174652053796d626f6c20536574
]
/isOpen 1
/actionCount 1
/action-1 {
	/name [ 16
		4475706c69636174652053796d626f6c
	]
	/keyIndex 0
	/colorIndex 0
	/isOpen 1
	/eventCount 1
	/event-1 {
		/useRulersIn1stQuadrant 0
		/internalName (ai_plugin_symbol_palette)
		/localizedName [ 7
			53796d626f6c73
		]
		/isOpen 1
		/isOn 1
		/hasDialog 0
		/parameterCount 1
		/parameter-1 {
			/key 1835363957
			/showInPalette -1
			/type (enumerated)
			/name [ 16
				4475706c69636174652053796d626f6c
			]
			/value 4
		}
	}
}

 

Qwertyfly___
Legend
May 11, 2015

Try this. it will use all symbols in document.

var doc = app.activeDocument;

var sym = doc.symbols;

var sel = doc.selection;

for(var i = 0; i < sel.length; i++){

    var place = doc.symbolItems.add(sym[Math.floor(Math.random() * sym.length)]);

    place.height = sel.height;

    place.width = sel.width;

    place.top = sel.top;

    place.left = sel.left;

    sel.remove();

    place.selected = true;

}

malinn6934325
Participating Frequently
May 11, 2015

That works perfectly!!!

Thanks so much!!!

Participant
March 27, 2018

Hi!

I need the exact action to do with a script. But it is my first time using script, how can I install it?

I tried through extendscript toolkit, but the script doesnt appear in illustrator.