Skip to main content
Disposition_Dev
Legend
September 9, 2015
Answered

Need help cleaning up a recursive loop, please.

  • September 9, 2015
  • 1 reply
  • 1689 views

Hello again,

I'm on yet another quest to write a completely idiot proof function to do a very specific task in Illustrator. The task is to create a new artboard in the target document (determined by a small script ui prompt) that is a certain distance from the existing artboard with the highest index (so i'm always putting the new artboard at the end). After I've created the artboard, I want to activate the source document and duplicate all artwork and layers (some layers are blank but will be used later) into the target doc and place them on the newly created artboard.

With the help and guidance of Silly-V‌ I wrote a recursive loop to handle the nested layer situation. That brings me to part one of my question. Please take a look at the recursion and let me know if there's anything I can do to clean it up and streamline it as much as possible. (I know it looks really messy. I was learning how to do it on the fly and i changed a lot of variables and loops after the fact, just trying to make it work). I'm also hoping that finding a slightly more efficient way to work this will help improve stability as well. As it stands, I can only run this script 3 or 4 times (if i'm lucky) before i get persistent MRAP errors on trivial things such as: layers.getByName("Information").visible = false; At that point, the only remedy is to quit illustrator and reopen the files. Then i'm usually good for a few more runs..

Part 2 of my question is this. Is there a way to duplicate artwork from one document to another and have it placed onto the new artboard rather than artboard[0]? Currently, the art is duplicating from artboard[0] of the source doc to artboard[0] of the target doc and then i'm manually manipulating the placement by setting: this.left = this.left + (artboards.length * 2100). First of all, this is just adding WAY too many steps to the script and slowing things down i'm sure. The second problem with this is that after 4 artboards, i need to move down to another row (to keep from spilling off the pasteboard). That adds another layer of complexity and a lot more if statements to determine how far to move it and in what direction.

Here's the script in it's entirety. Please feel free to tear it apart and ask me WTH i was thinking. In the words of carloscanto‌, script.elegant is definitely false.

var copyToMaster = function(){

  var docRef = app.activeDocument;

  var layers = docRef.layers;

  var masterFile = getTargetDoc();

  if(masterFile == null){

  return;

  }

  var master = app.documents.getByName(masterFile);

  var sourceDoc = app.activeDocument;

  var move;

  var myLayer;

  var targetLayer;

  var innerTargetLayer;

  var outerTargetLayer;

  //Logic Container//

  function outerRecursive(layer){

  var name = layer.name;

  try{

  outerTargetLayer = targetLayer.layers.getByName(layer.name);

  }

  catch(e){

  outerTargetLayer = targetLayer.layers.add(ElementPlacement.PLACEATBEGINNING);

  outerTargetLayer.name = name;

  }

  if(layer.pageItems.length>0){

  for(var b=layer.pageItems.length-1;b>-1;b--){

  var curItem = layer.pageItems;

  var copyCurItem = curItem.duplicate(outerTargetLayer,ElementPlacement.PLACEATBEGINNING);

  copyCurItem.left = curItem.left+move;

  }

  }

  if(layer.layers.length>0){

  for(var e=layer.layers.length-1;e>-1;e--){

  var subSubLayer = layer.layers;

  innerRecursive(subSubLayer);

  }

  }

  }

  function innerRecursive(subLayer){

  try{

  innerTargetLayer = outerTargetLayer.layers.getByName(subLayer.name);

  }

  catch(e){

  innerTargetLayer = outerTargetLayer.layers.add();

  innerTargetLayer.name = subLayer.name;

  }

  if(subLayer.pageItems.length>0){

  for(var g=subLayer.pageItems.length-1;g>-1;g--){

  var curItem = subLayer.pageItems;

  var copyCurItem = curItem.duplicate(innerTargetLayer,ElementPlacement.PLACEATBEGINNING);

  copyCurItem.left = copyCurItem.left + move;

  }

  }

  if(subLayer.layers.length>0){

  for(var h=subLayer.layers.length-1;h>-1;h--){

  innerRecursive(subLayer.layers);

  }

  if(name == "Information"){

  layer.locked = true;

  layer.visible = true;

  }

  else if(name == "Prepress"){

  layer.locked = false;

  layer.visible = false;

  }

  }

  if(subLayer.name == "Information"){

  subLayer.locked = true;

  subLayer.visible = true;

  }

  else if(subLayer.name == "Prepress"){

  subLayer.locked = false;

  subLayer.visible = false;

  }

  }

  function getTargetDoc(){

  var docs = [];

  for(var a=0;a<app.documents.length;a++){

  docs.push(app.documents.name);

  }

  var targetIndex = new Window("dialog", "Which is Master");

  var newTextGroup = targetIndex.add("group");

  newTextGroup.text = targetIndex.add("statictext",undefined,"Which file do you want to merge to?");

  newTextGroup.align = "center";

  var radioGroup = targetIndex.add("group");

  radioGroup.alignChildren = "left";

  radioGroup.orientation = "column";

  for(var a=0;a<docs.length;a++){

  radioGroup.add("radiobutton",undefined,docs);

  }

  radioGroup.children[0].value = true;

  var buttonGroup = targetIndex.add("group");

  var ok = buttonGroup.add("button", undefined, "OK");

  var can = buttonGroup.add("button", undefined, "Cancel");

  function selected(which){

  for(var c=0;c<which.children.length;c++){

  if(which.children.value == true){

  return which.children.text;

  }

  }

  }

  if(targetIndex.show() == 1){

  return (selected(radioGroup));

  }

  else{

  return;

  }

  targetIndex.show();

  }

  function createNewArtboard(){

  master.activate();

  move = ((master.artboards.length) * 2100)

  var aBcount = master.artboards.length;

  var lastAB = master.artboards[master.artboards.length-1];

  var aB = lastAB.artboardRect;

  var left = aB[0];

  var top = aB[1];

  var right = aB[2];

  var bot = aB[3];

  var moveRight = 2100;

  var moveDown = 2100;

  if(aBcount != 5 && aBcount != 10){

  var newLeft = left + moveRight;

  var newRight = right + moveRight;

  var rect = [newLeft,top,newRight,bot];

  var newAb = master.artboards.add(rect);

  }

  else{

  var originAb = master.artboards[0].artboardRect;

  var oLeft = originAb[0];

  var oTop = originAb[1];

  var oRight = originAb[2];

  var oBot = originAb[3];

  oTop = oTop - moveDown;

  oBot = oBot - moveDown;

  var rect = [oLeft,oTop,oRight,oBot];

  var newAb = master.artboards.add(rect);

  }

  sourceDoc.activate();

  }

  //Function Calls

  createNewArtboard();

  for(var a=0;a<layers.length;a++){

  layers.locked = false;

  layers.visible = true;

  }

  for(var a=0;a<master.layers.length;a++){

  master.layers.locked = false;

  master.layers.visible = true;

  }

  for(var a=layers.length-1;a>-1;a--){

  var curLayer = layers;

  try{

  targetLayer = master.layers.getByName(curLayer.name);

  }

  catch(e){

  targetLayer = master.layers.add();

  targetLayer.name = curLayer.name;

  }

  if(curLayer.pageItems.length>0){

  for(var k=curLayer.pageItems.length-1;k>-1;k--){

  var curItem = curLayer.pageItems;

  var copyCurItem = curItem.duplicate(targetLayer,ElementPlacement.PLACEATBEGINNING);

  copyCurItem.left = copyCurItem.left + move;

  }

  }

  if(curLayer.layers.length>0){

  for(var f=curLayer.layers.length-1;f>-1;f--){

  var curSubLayer = curLayer.layers;

  outerRecursive(curSubLayer);

  }

  }

  }

  //re-lock/hide layers;

  for(var a=0;a<master.layers.length;a++){

  if(master.layers.name == "Guides" || master.layers.name == "BKGRD, do not unlock"){

  master.layers.locked = true;

  }

  else{

  for(var b=0;b<master.layers.layers.length;b++){

  var level2 = master.layers.layers;

  if(level2.name == "Prepress"){

  level2.locked = false;

  level2.visible = false;

  }

  else if(level2.name == "Information"){

  level2.locked = true;

  level2.visible = true;

  }

  }

  }

  }

}

copyToMaster();

copyToMaster = null;

This topic has been closed for replies.
Correct answer Qwertyfly___

i do like that idea. However it looks like "Paste Remembers Layers" is not accessible through the script.. I can only read whether it's true : false. I would like to be able to avoid having the artists toggling that setting manually because other than this exact process.. it breaks almost every other process we have in some way or another..

But that did get me thinking.. what if I selected al artwork in the source document via "artboard[0].hasSelectedArtwork = true", translated the artwork all together in the source document and then duplicated the artwork with the loop i already have.. that would at least cut those 40 steps down to 21..

or. an even better solution would be for adobe to support a layer.duplicate function....


How's this?

function duplicateToMaster(){

    function pasteInMaster(){

        if(app.pasteRemembersLayers === false){

            app.pasteRemembersLayers = true;

            app.executeMenuCommand('pasteInPlace');

            app.pasteRemembersLayers = false;

        }else{

            app.executeMenuCommand('pasteInPlace');

        }

    }

var aDoc = app.activeDocument;

app.executeMenuCommand('selectall'); 

app.executeMenuCommand('copy');

var bDoc = app.documents[1];

bDoc.activate();

pasteInMaster();

}

duplicateToMaster();

1 reply

Disposition_Dev
Legend
September 9, 2015

Well I rebuilt the script as lean as I could make sense of it. I removed the lines that move the artwork after it's duplicated because i'm positive there's got to be a better way to do that... Unfortunately, my suspicion that setting the activeArtboardIndex() to the newly created artboard doesn't do anything. Illustrator shows the correct artboard is active. But the duplication is still happening relative to the coordinates of the source document. Any thoughts on that???

Here's the updated code. Still copies all the artwork and layers as it should (and in the correct order as well) but still places duplicated artwork directly on top of artboard[0] in the target document. I'll also note that this version does run a little faster than the old one, but I'm sure that's solely due to the lack of position adjustments. Basically i just cleaned up the variable names and used more variables as function arguments rather than global variables.

And back to the MRAP "Illustrator Error Occurred" issue, is this script just really poorly written? Or is CC just screwing me with it's abysmal memory management? I can deal with the script taking some time to execute. But the necessity of quitting and relaunching illustrator just seems unbelievably unacceptable..

function copyToMaster(){

  var docRef = app.activeDocument;

  var layers = docRef.layers;

  var masterFile = getTargetDoc();

  if(masterFile == null){return;}

  var master = app.documents.getByName(masterFile);

  ///////////////////////

  ////Logic Container////

  ///////////////////////

  function outer(target,source){

  if(source.pageItems.length>0){

  for(var c=source.pageItems.length-1;c>-1;c--){

  var curItem = source.pageItems;

  var curItem2 = curItem.duplicate(target);

  }

  }

  if(source.layers.length>0){

  for(var c=source.layers.length-1;c>-1;c--){

  var subSubLayer = source.layers;

  try{

  var innerTarget = target.layers.getByName(subSubLayer.name);

  }

  catch (e){

  var innerTarget = target.layers.add();

  innerTarget.name = subSubLayer.name;

  }

  inner(innerTarget,subSubLayer);

  }

  if(innerTarget.name == "Prepress"){

  innerTarget.visible = false;

  }

  else if(innerTarget.name == "Information"){

  innerTarget.locked = true;

  }

  }

  }

  function inner(inTarget, inSource){

  if(inSource.pageItems.length>0){

  for(var d=inSource.pageItems.length-1;d>-1;d--){

  var curItem = inSource.pageItems;

  var curItem2 = curItem.duplicate(inTarget);

  }

  }

  if(inSource.layers.length>0){

  for(var d=inSource.layers.length-1;d>-1;d--){

  try{

  var newInTarget = inTarget.layers.getByName(inSourceLayers.layers.name);

  }

  catch(e){

  var newInTarget = inTarget.layers.add();

  newInTarget.name = inSourceLayers.layers.name;

  }

  var newInSource = inSource.layers;

  inner(newInTarget,newInSource);

  }

  if(newInTarget.name == "Prepress"){

  newInTarget.visible = true;

  }

  else if(newInTarget.name == "Information"){

  newInTarget.locked = true;

  }

  }

  }

  function getTargetDoc(){

  var docs = [];

  for(var a=0;a<app.documents.length;a++){

  docs.push(app.documents.name);

  }

  var targetIndex = new Window("dialog", "Which is Master");

  var newTextGroup = targetIndex.add("group");

  newTextGroup.text = targetIndex.add("statictext",undefined,"Which file do you want to merge to?");

  newTextGroup.align = "center";

  var radioGroup = targetIndex.add("group");

  radioGroup.alignChildren = "left";

  radioGroup.orientation = "column";

  for(var a=0;a<docs.length;a++){

  radioGroup.add("radiobutton",undefined,docs);

  }

  radioGroup.children[0].value = true;

  var buttonGroup = targetIndex.add("group");

  var ok = buttonGroup.add("button", undefined, "OK");

  var can = buttonGroup.add("button", undefined, "Cancel");

  function selected(which){

  for(var c=0;c<which.children.length;c++){

  if(which.children.value == true){

  return which.children.text;

  }

  }

  }

  if(targetIndex.show() == 1){

  return (selected(radioGroup));

  }

  else{

  return;

  }

  targetIndex.show();

  }

  function createNewArtboard(){

  master.activate();

  var aBcount = master.artboards.length;

  var lastAB = master.artboards[master.artboards.length-1];

  var aB = lastAB.artboardRect;

  var left = aB[0];

  var top = aB[1];

  var right = aB[2];

  var bot = aB[3];

  var moveRight = 2100;

  var moveDown = 2100;

  if(aBcount != 5 && aBcount != 10){

  var newLeft = left + moveRight;

  var newRight = right + moveRight;

  var rect = [newLeft,top,newRight,bot];

  var newAb = master.artboards.add(rect);

  }

  else{

  var originAb = master.artboards[0].artboardRect;

  var oLeft = originAb[0];

  var oTop = originAb[1];

  var oRight = originAb[2];

  var oBot = originAb[3];

  oTop = oTop - moveDown;

  oBot = oBot - moveDown;

  var rect = [oLeft,oTop,oRight,oBot];

  var newAb = master.artboards.add(rect);

  }

  master.artboards.setActiveArtboardIndex(master.artboards.length-1);

  docRef.activate();

  }

  ///////////////////////

  ////Logic Container////

  ///////////////////////

////////////////////////////////////////////////////////////////////////////////

  //////////////////////

  ////Function Calls////

  //////////////////////

  createNewArtboard();

  for(var a=layers.length-1;a>-1;a--){

  var topLayer = layers;

  try{

  var targetLayer = master.layers.getByName(topLayer.name);

  }

  catch(e){

  var targetLayer = master.layers.add();

  targetLayer.name = topLayer.name;

  }

  targetLayer.locked = false;

  targetLayer.visible = true;

  if(topLayer.pageItems.length>0){

  for(var b=topLayer.pageItems.length-1;b>-1;b--){

  var curItem = topLayer.pageItems;

  var curItem2 = curItem.duplicate(targetLayer);

  }

  }

  if(topLayer.layers.length>0){

  for(var b=topLayer.layers.length-1;b>-1;b--){

  var subLayer = topLayer.layers;

  try{

  var outerTarget = targetLayer.layers.getByName(subLayer.name);

  }

  catch(e){

  var outerTarget = targetLayer.layers.add();

  outerTarget.name = subLayer.name;

  }

  outerTarget.locked = false;

  outerTarget.visible = true;

  outer(outerTarget,subLayer);

  }

  if(outerTarget.name == "Information" || outerTarget.name == "Guides" || outerTarget.name == "BKGRD, do not unlock"){

  outerTarget.locked = true;

  }

  }

  }

  //////////////////////

  ////Function Calls////

  //////////////////////

}

copyToMaster();

copyToMaster = null;

Disposition_Dev
Legend
September 10, 2015

So my next thought was to switch the app.CoordinateSystem to ARTBOARDCOORDINATESYSTEM and then set the activeArtboardIndex() to the newly created artboard, but that doesn't seem to make a difference???

Does anyone have any insight on how app.CoordinateSystem works? The documentation is abysmal...

Qwertyfly___
Legend
September 14, 2015

I did the positioning for this by adding 2 lines and changing 1 other.

it should not slow things down much.

at the end of the createNewArtboard function add a return to hand back the rect array.

docRef.activate();

  return rect;

  }

then just below that function you have a call to it.

add a variable to catch the rect array that it returns.

var newABpos = createNewArtboard();

then directly under the line that does the Duplication and the translate to put item in correct position

var curItem2 = curItem.duplicate(targetLayer);

  curItem2.translate(newABpos[0],newABpos[1]);

  }

without digging too deep I think the translate is also needed in the inner or outer functions.

if so this is repeated code and may be worth its own function?

as for the error I did not get it.

I did find that my 5th page was hanging off the drawing area.

same with the 10th page.

then it places the pages ontop of the last row.

ie. page 6 is under page 11.

the code could do with some indenting so it can be read easily.

there are a few things I can see that may help speed.

ie.

your loop to push doc names to an array. is not needed.

this can just be done when building the list of radio buttons.

not a big deal as I guess it will never loop through alot of documents but 1 less loop is 1 less loop. and 1 less array of info the engine needs to assign memory for.