Skip to main content
Silly-V
Legend
March 22, 2019
Question

[BUG] copying threaded text-frames.

  • March 22, 2019
  • 2 replies
  • 2888 views

I discovered an unfortunate bug when trying to copy a group containing threaded text frames from one document to another using the duplicate() command.

#target illustrator

function test(){

  var doc = app.documents.getByName("Test-1.ai");

  doc.activate();

  var group = doc.groupItems[0]; // contains two text frames which are threaded to each-other

  var otherDoc = app.documents.getByName("Test-2.ai");

  var groupCopied = group.duplicate(otherDoc.layers[0], ElementPlacement.INSIDE);

  otherDoc.activate();

};

test();

Basically you want to copy something with two threaded textframes into another document, they become un-threaded. Really hard to be aware of if not paying attention and expecting the duplicate() function to work exactly like copy/paste command.

I have an idea of how to deal with it so I hope this will remind me to try it out: I'll look at documenting any threads of text into tags and then reconstructing the threads in the destination document. Ideally the tags would be auto applied and removes so as to not ever get copied and pasted along with an art item into other user documents.

This topic has been closed for replies.

2 replies

Community Expert
March 27, 2019

Silly-V  wrote

… Basically you want to copy something with two threaded textframes into another document, they become un-threaded. Really hard to be aware of if not paying attention and expecting the duplicate() function to work exactly like copy/paste command.

Hi,

thank you very much for this!

I can confirm the bug.

Did my tests with a group of threaded text paths.


I also would expect* that the threaded frames of a group stay threaded if the group is duplicated.

*

Duplicating to a different layer in the same document with method duplicate() has no issue with unthreaded text paths or frames.

UI: Dragging a bunch of threaded frames from one doc to another will work as well.

FWIW: InDesign's group.duplicate() would work accross documents:

// InDesign script:

// Duplicate group of active doc to page 1 of document with index 1.

// Group contains threaded text frames.

app.documents[0].groups[0].duplicate( app.documents[1].pages[0] );

Regards,
Uwe

EDIT: All my tests with German Illustrator CC 2019 version 23.0.2 on Windows 10.

Silly-V
Silly-VAuthor
Legend
March 27, 2019

I think it's because it's got to do with stories and stories are something that are a document-level item.

And hence, when you take some objects out of one document to another, the story isn't transported.

Well, I ended up just doing tagging - then sorting through the text frames, etc.

function recordStoriesInTags (artGroup) {

  // artGroup must be named art group

  var allStories = app.activeDocument.stories;

  var thisStory, thisFrame, thisTag, timestamp;

  timestamp = new Date().getTime();

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

    thisStory = allStories[i];

    if (thisStory.textFrames.length > 1) {

      for (var j = 0; j < thisStory.textFrames.length; j++) {

        thisFrame = thisStory.textFrames[j];

        if (searchForParentGroup(thisFrame, function (inItem) {

          return inItem.name == artGroup.name;

        }) == null) {

          continue;

        }

        // clear any previous such tags

        try {

          thisTag = thisFrame.tags.getByName("THREADED");

          thisTag.remove();

        } catch (e) {

        

        }

        thisTag = thisFrame.tags.add();

        thisTag.name = "THREADED";

        thisTag.value = timestamp + "|" + (i + 1) + "-" + (j + 1);

      }

    }

  }

};


function reThreadTextFramesInGroup (newArtGroup) {

  var textBoxesInGroup, threadedBox;

  textBoxesInGroup = artNodeSearch(newArtGroup, function (item) {

    try {

      item.tags.getByName("THREADED");

      return item.typename == "TextFrame";

    } catch (e) {

      return false;

    }

  });


  var threadedBoxGroupNames = [], thisTagValue, groupName, order;

  for (var j = 0; j < textBoxesInGroup.length; j++) {

    threadedBox = textBoxesInGroup[j];

    thisTagValue = threadedBox.tags.getByName("THREADED").value;

    groupName = thisTagValue.split("-")[0];

    order = thisTagValue.split("-")[1];

    if (threadedBoxGroupNames.indexOf(groupName) == -1) {

      threadedBoxGroupNames.push(groupName);

    }

  }


  var threadedGroupObj = {};

  for (var j = 0; j < threadedBoxGroupNames.length; j++) {

    for (var k = 0; k < textBoxesInGroup.length; k++) {

      threadedBox = textBoxesInGroup[k];

      thisTagValue = threadedBox.tags.getByName("THREADED").value;

      groupName = thisTagValue.split("-")[0];

      order = thisTagValue.split("-")[1];


      if (groupName == threadedBoxGroupNames[j]) {

        if (!(groupName in threadedGroupObj)) {

          threadedGroupObj[groupName] = [];

        }

        threadedGroupObj[groupName].push({

          element : threadedBox,

          groupName : groupName,

          order : order

        });

      }

    }

  }

  var sortedTextFrames, nextBox;

  for (var all in threadedGroupObj) {

    sortedTextFrames = threadedGroupObj[all].sort(function (a, b) {

      return a.order > b.order;

    });

    // threadedBox now an object of info

    for (var j = 0; j < sortedTextFrames.length; j++) {

      threadedBox = sortedTextFrames[j];

      if (j < sortedTextFrames.length - 1) {

        nextBox = sortedTextFrames[j + 1];

        threadedBox.element.nextFrame = nextBox.element;

      }

      try {

        threadedBox.element.tags.getByName("THREADED").remove();

      } catch (e) {


      }

    }

  }

};

function searchForParentGroup (node, nodeFunc) {

  if (node.typename == "Layer") {

    throw ("The Parent-node search cannot run on a Layer.");

  }

  var foundItem = null, thisParent = node.parent;

  while (thisParent.typename == "GroupItem" && foundItem == null) {

    if (nodeFunc(thisParent)) {

      foundItem = thisParent;

      return foundItem;

    } else {

      thisParent = thisParent.parent;

    }

  }

  return null;

};

function artNodeSearch (parent, nodeFunc) {

  if (parent.typename == "Layer") {

    throw ("Only Group containers are allowed when performing a node search on an art nodes.");

  }

  var arr = [];

  function findFunc (node) {

    if (nodeFunc(node)) {

      arr.push(node);

    }

    var thisPageItem;

    if (node.typename == "GroupItem") {

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

        thisPageItem = node.pageItems[i];

        findFunc(thisPageItem);

      }

    }

  };

  findFunc(parent);

  return arr;

};

What I found was that the record stories function, the area 'return inItem.name == artGroup.name;' used to be just 'return inItem == artGroup;'  - which worked in one document but would cause a CRASH when I used it in a document where there were already items in it... where I pasted things to.. anyways - hope it helps someone and please report any bugs with the above if you use it.

Community Expert
March 27, 2019

Hi Silly-V ,

oh boy!

Wouldn't a set of menu commands do?

Like select, copy, paste.

Something like the code below is working in my test and threading of text paths or text frames is maintained in the target document:

function test(){

    var doc = app.documents.getByName("Test-1.ai");

    doc.activate();

    var group = doc.groupItems[0]; // contains two text frames which are threaded to each-other

    group.selected = true ;

    app.executeMenuCommand( "copy" );

    var otherDoc = app.documents.getByName("Test-2.ai");

    otherDoc.activate();

    app.executeMenuCommand( "pasteInPlace" );

};

test();

Regards,
Uwe

CarlosCanto
Community Expert
Community Expert
March 23, 2019

same here, thanks for sharing.