Skip navigation
Currently Being Moderated

cleaning up text frames

Nov 1, 2011 3:56 PM

hello all you smart guys.

 

I get a big complex files from another designers every month for further processing. Among other not-so-good habits, they usually do not use layers.

To make layout a bit more organized, I move text to its own layer - simple task, have script for it. But it does the job straightforward - exactly 'as advertised' (Jongware's expression - I like it so )

 

Unfortunately, those designers didn't bother much with 'frame content' option. They can draw a frame with a text tool, fill it with color and use it as a background for some artwork or whatever...

my script 'text frames to separate layer' creates a mess on such pages, and locate it - not always is an easy task. Document usually has over 200 pages...

 

So, I'm dreaming about some magic script, which is able to "prepare" text for moving, I mean, to find and process:

 

case1: empty text frame with no fill/outline - remove (delete)

case2: empty text frame with any fill/outline - convert to 'unassigned'

case3: text frame with the same color applied to text and the frame itself (yes, it happens!) - well, maybe just 'select and stop'? I'm afraid it's too complicated to remove text from such a frame and then treat the frame as 'case2'?

 

any suggestions would be greatly appreciated...

 
Replies
  • Currently Being Moderated
    Nov 1, 2011 11:37 PM   in reply to winterm

    not sure myself, but have you checked this thread?

     

    http://forums.adobe.com/message/3306350

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 2, 2011 6:40 AM   in reply to winterm

    you need to check with textFrame properties.......

     

    if (myTextFrame.fillColor.name == None ) {

    alert("No Colors used");

    }

    else {

    alert("Colors applied on TextFrame);

    }

     

    try this..............

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 2, 2011 7:06 AM   in reply to winterm

    use this script it will help to remove only the empty text frame

     

     

    var myDocument = app.activeDocument;
    var myStories = app.activeDocument.stories.everyItem().getElements();
    for (i = myStories.length - 1; i >= 0; i--){
        var myTextFrames = myStories[i].textContainers;
        for (j = myTextFrames.length - 1; j >= 0; j--)    {
    var stroke = myTextFrames[j].strokeWeight;
    var color = myTextFrames[j].fillColor.name;
    //alert (color)
         if (myTextFrames[j].contents == "" && stroke == "0" && color == "None"){
               //alert ("yes") 
       myTextFrames[j].remove();
            }
        }
    }
     
    
     
    |
    Mark as:
  • Currently Being Moderated
    Nov 3, 2011 2:44 AM   in reply to winterm

    @winterm:
    You can try this using script menu actions. This will only work, if the certain menu action is available.
    It is not available if there is contents in the text frame (e.g. a blank character) or (much more important from a scripters point of view) if a threaded text frame is empty because it's contents cannot fit inside it and it's contents is an empty string.

     

    Following code will single out empty text frames, select them and invoke a script menu action to set them to "Unassigned":

     

    var _d = app.documents[0];
    var _allStories = _d.stories;
     
     
    for(var n=_allStories.length-1;n>=0;n--){
        var _storyAllTextFrames = _allStories[n].textContainers;
     
        for(var m=_storyAllTextFrames.length-1;m>=0;m--){
            //If the contents of a text frame is an empty string:
            if(_storyAllTextFrames[m].contents === ""){
                _storyAllTextFrames[m].select();
                //Convert frame to "Unassigned":
                try{
                app.scriptMenuActions.itemByID(11297).invoke();
                    }catch(e){};
                };
            };
        };
     
    //Trick to deselect the last selection
    //Add a new textFrame, select and remove it:
    var _tempTextFrame = _d.textFrames.add();
    _tempTextFrame.select();
    _tempTextFrame.remove();
    

     

    @all: If there is a better way to deselect a selection please give an example…

     

    Uwe

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 3, 2011 5:43 AM   in reply to Laubender

    Hi Winterm,

     

    I've corrected Uwe's script a little bit.

    There is "contentType" property for text frame that can be changed.

     

    var _d = app.documents[0],
        _allStories = _d.stories;
     
    for (var n = _allStories.length - 1; n >= 0; n--){
        var _storyAllTextFrames = _allStories[n].textContainers;
        for (var m = _storyAllTextFrames.length - 1; m >= 0; m--){
            if (_storyAllTextFrames[m].contents === "")
                _storyAllTextFrames[m].contentType = ContentType.UNASSIGNED;
        }
    }
    

     

    Uwe,

     

    To clear selection you can simply do this:

     

    app.select(null);
    

     

    or a little bit more complex:

     

    app.select(NothingEnum.NOTHING);
    

     

    Hope that helps.

     

    --

    Marijan (tomaxxi)

    http://tomaxxi.com

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 3, 2011 5:52 AM   in reply to Laubender

    1. Use ContentType:

     

     

    contentTypeContentType:
    ContentType.UNASSIGNED
    ContentType.GRAPHIC_TYPE
    ContentType.TEXT_TYPE
    r/wThe type of content that a frame can contain.

     

     

    _storyAllTextFrames[m].contentType = ContentType.UNASSIGNED;
    

     

    2. Use app.select:

     

    void select (selectableItems: varies[, existingSelection: SelectionOptions=SelectionOptions.REPLACE_WITH])
    Selects the specified object(s).

    ParameterTypeDescription
    selectableItemsArray of Objects
    NothingEnum
    Object
    SelectAll
    The objects to select. Can accept: Object, Array of Objects, NothingEnum enumerator or SelectAll enumerator.
    existingSelectionSelectionOptions:
    SelectionOptions.ADD_TO
    SelectionOptions.REMOVE_FROM
    SelectionOptions.REPLACE_WITH
    The selection status of the Application in relation to previously selected objects. (Optional) (default:SelectionOptions.REPLACE_WITH)

     

     

    app.select(NothingEnum.NOTHING);
    

     

    (Edit: Hi Marijan!)

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 3, 2011 7:44 AM   in reply to Marijan Tompa

    Thank you, Marijan & Jongware to point to the right property "contentType" and to app.select(null).

    I think the line:

     

    _storyAllTextFrames[m].contentType = ContentType.UNASSIGNED;
    


    needs a try-catch-wrapper:

     

    try{
        _storyAllTextFrames[m].contentType = ContentType.UNASSIGNED;
        }catch(e){};
    

     

    It could be that a threaded text frame has no contents because the contents is too big to fit inside.
    contents === "" will perfectly match such an empty text frame, but you cannot asign a new contentType to it.
    See my screen shot:

    ContentsOfThreadedTextFrameIsEmptyString.png

     

    Uwe

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 22, 2013 3:20 AM   in reply to siva prasath

    Hi, im working on something right now, but i just could get a script that deletes a textframe with multiple blank character/spaces. I need help. thanks in advance..

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 25, 2013 3:00 PM   in reply to Antoys

    "Empty" text frame that actually contained blank spaces were giving me headaches, until one of the grep experts around here mentioned that \S is grep for "not a space". So this:

     

    app.selection[0].contents.match(/\S/); // assuming that the selection is a text frame

     

    Returns null when the frame contains only spaces. Doesn't matter how many or what kind. If the result is something other than null, the frame has actual content.

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 29, 2013 11:03 PM   in reply to RobertKyle

    I think this is an awesome script for removing blank textboxes, but a forum on macscripter revealed a feature that, while it would rarely be used, would not be caught in this script, and that is any instance of a blank text box with no fill or stroke BUT contained a text-wrap for whatever reason (e.g. to move an object or nudge text in a "cheat's" fashion)

     

    http://macscripter.net/viewtopic.php?id=19953

     

    I have tried changing these lines:

     

    var color = myTextFrames[j].fillColor.name;

    //alert (color)

         if (myTextFrames[j].contents == "" && stroke == "0" && color == "None"){

     

    to

     

    var color = myTextFrames[j].fillColor.name;

    var wrap = myTextFrames[j].TextWrapModes.name;

    //alert (color)

         if (myTextFrames[j].contents == "" && stroke == "0" && color == "None" && wrap == "None"){

     

    but it errors. can anybody point out what I've done wrong?

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 30, 2013 2:11 AM   in reply to RobertKyle

    @Robert – I did not test that, but there might be a special occasion like the following one:

     

    After a data merge with empty fields there is a special character inserted in the process: <FEFF>. And that character is not seen as whitespace in the GREP impementation of InDesign (UI).

     

    So I foresee a case where an otherwise empty text frame with a <FEFF> character is not considered as "empty".

     

    See the following thread in the InDesign forum:

     

    LinaD36

    Multiple record data merge into paragraph styles-applies the wrong style

     

    http://forums.adobe.com/thread/1341730?tstart=0

     

    And my script snippet to remove "<FEFF>" in the same thread:

    http://forums.adobe.com/message/5883785#5883785

     

    If you want to spot that <FEFF> character, also see:

     

    Michael Gianino

    Data Merge Invisibles

     

    http://forums.adobe.com/message/3614180#3614180

     

    Answer by John Hawkinson (with a script snippet to eliminate all <FEFF> characters):

    http://forums.adobe.com/message/3615031#3615031

     

    I tested John's snippet on some material by LinaD36 and had some problems with it.
    But this is another story…

     

    Conclusion of this case (so far I can see):
    Only a TEXT search/replace action can reliably eliminate <FEFF>.

     

    I think we have to look for <FEFF> and add it to the whitespace case…

     

    Uwe

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 2, 2013 7:59 AM   in reply to Laubender

    As I mentioned, I'm trying to improve on the script so that any text box with no fill or stroke BUT with a text-wrap doesn't get deleted. I've modded the script in the following way:

     

    var stroke = myTextFrames[j].strokeWeight;

    var color = myTextFrames[j].fillColor.name;

    var wrap = myTextFrames[j].textWrapPreferences.textWrapMode;

    //alert (color)

         if (myTextFrames[j].contents == "" && stroke == "0" && color == "None" && wrap == "None"){

               //alert ("yes")

       myTextFrames[j].remove();

     

    but now when running the script, nothing is deleted, including the empty textboxes with no fill, stroke or textwrap. what have I missed?

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 2, 2013 8:15 AM   in reply to cdflash

    @Colin – at first glance, textWrapMode cannot be "None".

    That's a string.

    And you are asking for an enumeration.

     

    The answer in your case would be:

     

    TextWrapModes.NONE
    

     

    So, if you select a text frame, you could ask:

     

    var sel = app.selection[0];
    var wrap = sel.textWrapPreferences.textWrapMode;
     
    if(wrap === TextWrapModes.NONE){
        alert("textWrapMode: "+"\"NONE\"");
        };
    

     

    Did not further look into your code…

     

    Uwe

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 2, 2013 4:35 PM   in reply to Laubender

    Thank you for that Uwe. I've modded the script again but no joy. Here's what I have so far:

     

     

    var myStories = app.activeDocument.stories.everyItem().getElements();
    for (i = myStories.length - 1; i >= 0; i--){
        var myTextFrames = myStories[i].textContainers;
        for (j = myTextFrames.length - 1; j >= 0; j--)    {
    var stroke = myTextFrames[j].strokeWeight;
    var color = myTextFrames[j].fillColor.name;
    var wrap = myTextFrames[j].textWrapPreferences.textWrapMode;
    //alert (color)
         if (myTextFrames[j].contents == "" && stroke == "0" && color == "None" && wrap === "TextWrapModes.NONE"){
               //alert ("yes") 
       myTextFrames[j].remove();
            }
        }
    }
     
    var _d = app.documents[0],
        _allStories = _d.stories;
     
    for (var n = _allStories.length - 1; n >= 0; n--){
        var _storyAllTextFrames = _allStories[n].textContainers;
        for (var m = _storyAllTextFrames.length - 1; m >= 0; m--){
            if (_storyAllTextFrames[m].contents === "")
                try{
        _storyAllTextFrames[m].contentType = ContentType.UNASSIGNED;
        }catch(e){};
        }
    }
    

     

     

    The original script earlier on in this thread worked in that it identified any text container that has no text, 0 stroke and None as the fill color. Anything meeting that criteria was deleted. Another sweep is done looking for any text container with no text, and that container is converted to unassigned

     

    However, the modified script should identifiy any text container that has no text, 0 stroke, None as the fill color, and no text wrap. Anything meeting that criteria should be deleted. Another sweep is done looking for any text container with no text, and that container should be converted to unassigned.

     

    Instead, all it doesn't delete anything and any text container with nothing inside it is converted to unassigned.

     

    Message was edited by: cdflash I've been using a testfile to determine if the script is working: https://dl.dropboxusercontent.com/u/55743036/empty%20textbox%20test.in dd

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 2, 2013 10:20 PM   in reply to cdflash

    @Colin – don't know, if I have time to look into this more deeply today or the next days.

     

    Just looked at your if statement:

    You have to eliminate the quotes from TextWrapModes.NONE.

     

    TextWrapModes.NONE is no String Object. It's an Enumeration Object. If you wrap it in quotes like that: "TextWrapModes.NONE", ExtendScript will not recoginise it as an Enumerator.

     

    An Enumeration is a fixed feature baked in the DOM of InDesign.
    Instead of TextWrapModes.NONE you could also use it's number representation.

    In your case that would be the number: 1852796517

     

    So:

     

    wrap === TextWrapModes.NONE
    

     

    would be basically the same as:

     

    wrap === 1852796517
    

     

     

    You could look up all enumerations here (better use the chm-file representatons):

     

    http://www.jongware.com/idjshelp.html

     

    Or directly here (but it's more trouble to run a find action for a specific phrase or DOM object):

     

    http://jongware.mit.edu/idcs6js/

     

    Your specific case is here:

     

    http://jongware.mit.edu/idcs6js/pe_TextWrapModes.html

     

    Uwe

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 2, 2013 11:26 PM   in reply to cdflash

    @Colin – could you provide an IDML for download as well?

     

    Uwe

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 3, 2013 3:16 AM   in reply to Laubender

    G'day there Uwe.

     

    That was all it was - take the quotes off of the TextWrapModes.NONE

     

    The revised line in the script should be:

     

     

     
    if (myTextFrames[j].contents == "" && stroke == "0" && color == "None" && wrap === TextWrapModes.NONE){
     
     
    

     

    I have also added an IDML version of the file used to test this script.

     

    https://dl.dropboxusercontent.com/u/55743036/empty%20textbox%20test.id ml

     

    Fixing empty textboxes has been a bugbear of mine for a while, and now the script is complete, I think I can safely move on!!!

     

    Thank you all for your help.

     

    Colin

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 18, 2013 3:23 AM   in reply to cdflash

    ...the saga continues. I don't think Lord of the Rings went on for this long!

     

    Just found that this script - as great as it is - doesn't like text on paths. Reports an error on this line:

     

    Error 30476
    Error String: The requested action could not be completed because the object no longer exists
    
    Source:   var myTextFrames=myStories[i]textContainers;
    

     

    I've made a test script that now features text on paths.

     

    Also has an unwanted side effect: type on a path that is on a textbox that has no fill, stroke or textwrap will be deleted.

     

     

    https://dl.dropboxusercontent.com/u/55743036/empty%20textbox%20test.id ml

     

    Colly

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 18, 2013 3:51 AM   in reply to cdflash

    @Colly – yeah, that's one of the pitfalls…
    The textContainer class could point to textFrames and textPaths as well.

     

    So you have to implement another test, if a textFrame contains a textPath and if yes, if there is some contents there…

     

    Something around that code:

     

    //Naive tests, text frames could contain parts of a threaded table:
     
    //Case 1: No textPath on textFrame
    if(
        myTextFrame.contents === "" && 
        myTextFrame.textPaths.length == 0
        )
    {
        alert("Empty.");
    };
     
    //Case 2: textPath on textFrame, check both for contents
    if(
        myTextFrame.contents === "" && 
        myTextFrame.textPaths.length === 1 && 
        myTextFrame.textPaths[0].texts[0].contents === ""
        )
    {
        alert("Also Empty.");
    };
    

     

    Uwe

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 18, 2013 5:04 AM   in reply to Laubender

    Thanks for that Uwe.

     

    Have amended my empty box cleaner... again! This one will catch all text frames, graphic frames or unassigned frames that are rectangles, ovals or polygons that have

     

    • no fill
    • no stroke
    • no textwrap
    • no text on a path around it
    • possible references to data merge fields that were empty

     

    and will delete them. Have made one sacrifice from the previous script - empty textboxes that were in threaded text were deleted in the old script, but in order to catch text on paths, have had to change from textContainers to textFrames as to avoid accidentally finding text that is on a path that is not a closed shape (i.e. a line). Code looks like this:

     

     

    var myDoc = app.activeDocument;
    app.findTextPreferences = app.changeTextPreferences = null; 
     
    app.findTextPreferences.findWhat = "<FEFF>"; 
    app.changeTextPreferences.changeTo = "";
     
    myDoc.changeText();
     
    app.findTextPreferences = app.changeTextPreferences = null;
     
     
    var myStories = app.activeDocument.stories.everyItem().getElements();
    for (i = myStories.length - 1; i >= 0; i--){
        var myTextFrames = myStories[i].textFrames;
        for (j = myTextFrames.length - 1; j >= 0; j--)    {
    var stroke = myTextFrames[j].strokeWeight;
    var color = myTextFrames[j].fillColor.name;
    var tpath = myTextFrames[j].textPaths.length;
    var wrap = myTextFrames[j].textWrapPreferences.textWrapMode;
    //alert (color)
         if (myTextFrames[j].contents === "" && stroke == "0" && color == "None" && wrap === TextWrapModes.NONE && tpath == 0){
               //alert ("yes") 
       myTextFrames[j].remove();
            }
        }
    }
     
    var _d = app.documents[0],
        _allStories = _d.stories;
     
    for (var n = _allStories.length - 1; n >= 0; n--){
        var _storyAllTextFrames = _allStories[n].textContainers;
        for (var m = _storyAllTextFrames.length - 1; m >= 0; m--){
            if (_storyAllTextFrames[m].contents === "")
                try{
        _storyAllTextFrames[m].contentType = ContentType.UNASSIGNED;
        }catch(e){};
        }
    }
     
    var myGraphicFrames = app.activeDocument.rectangles;
    for (i=myGraphicFrames.length-1; i>=0; i--) {
    var stroke = myGraphicFrames[i].strokeWeight;
    var color = myGraphicFrames[i].fillColor.name;
    var tpath = myGraphicFrames[i].textPaths.length;
    var wrap = myGraphicFrames[i].textWrapPreferences.textWrapMode;
        if (myGraphicFrames[i].graphics.length < 1 && stroke == "0" && color == "None" && wrap === TextWrapModes.NONE && tpath == 0)
            myGraphicFrames[i].remove();
    }
     
    var myOvalFrames = app.activeDocument.ovals;
    for (i=myOvalFrames.length-1; i>=0; i--) {
    var stroke = myOvalFrames[i].strokeWeight;
    var color = myOvalFrames[i].fillColor.name;
    var tpath = myOvalFrames[i].textPaths.length;
    var wrap = myOvalFrames[i].textWrapPreferences.textWrapMode;
        if (myOvalFrames[i].graphics.length < 1 && stroke == "0" && color == "None" && wrap === TextWrapModes.NONE && tpath == 0)
            myOvalFrames[i].remove();
    }
     
    var myPolygonFrames = app.activeDocument.polygons;
    for (i=myPolygonFrames.length-1; i>=0; i--) {
    var stroke = myPolygonFrames[i].strokeWeight;
    var color = myPolygonFrames[i].fillColor.name;
    var tpath = myPolygonFrames[i].textPaths.length;
    var wrap = myPolygonFrames[i].textWrapPreferences.textWrapMode;
        if (myPolygonFrames[i].graphics.length < 1 && stroke == "0" && color == "None" && wrap === TextWrapModes.NONE && tpath == 0)
            myPolygonFrames[i].remove();
    }
    

     

    Before I attempt to "crack the champagne" thinking that this is it... what other conditions might one find that deleting a blank box is actually catastrophic?

     

    Colly

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 18, 2013 6:09 AM   in reply to cdflash

    @Colly – look at my screenshot in answer #9 of this thread. Yes, with threaded text frames there are constellations where the layout could change and overset text would be the result.

     

    Did I already mention tables, that run through more than one text frame?
    Also, something one could consider; but maybe a bit nit-picking…

     

    Uwe

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 20, 2013 8:45 PM   in reply to Laubender

    I think I might try something simpler next time... climbing Mt Everest perhaps.

     

    have made a testfile that I am sure covers any instance where a clear box should stay/be deleted: https://dl.dropboxusercontent.com/u/55743036/empty%20textbox%20test.id ml

     

    the script so far leaves items mentioned by Uwe (tables that run through more than one text frame, and text boxes that have nothing in them due to overset text shifting the story onto the next textbox), and am glad to say also:

     

    • does not delete no fill/stroke/text items that are part of a group
    • deletes no fill/stroke/text boxes that are anchored objects
    • does not delete fill/stroke/text boxes that contain an anchored object.

     

    however, it did NOT delete graphic/unassigned rectangles/ovals/polygons that have no fill/stroke/textwrap/path on type that are anchored objects within a text frame.

     

    An old forum features a script from harbs (http://forums.adobe.com/message/2668865) that removes any no graphic/text anchored objects, but it's too powerful (will delete the anchored object even if it has a fill or stroke or wrap or text on path). I've since modded his script and added it to the one i've been developing. without placing the entire code, i'll just include the modded snippet:

     

     

    var myDocument = app.activeDocument;
    var inlines = myDocument.stories.everyItem().pageItems.everyItem().getElements();
    while(fr=inlines.pop()){
      if(fr instanceof Group){continue}
      if(fr instanceof TextFrame){
        if(fr.contents == "" && fr.strokeWeight == "0" && fr.fillColor.name == "None" && fr.textWrapPreferences.textWrapMode === TextWrapModes.NONE && fr.textPaths.length == 0){fr.remove()}
        continue;
      }
      if(fr.graphics.length==0 && fr.strokeWeight == "0" && fr.fillColor.name == "None" && fr.textWrapPreferences.textWrapMode === TextWrapModes.NONE && fr.textPaths.length == 0){fr.remove()}
    }
    

     

    But as I look at this modded script, I have a feeling that if I made a copy of the script but replaced the line:

     

     

    var inlines = myDocument.stories.everyItem().pageItems.everyItem().getElements();
    

     

    and changed it to

     

     

    var inlines = myDocument.stories.everyItem().getElements();
    

     

    That resulting script would effectively do what the following portion of the script does, but with less syntax:

     

    var myStories = app.activeDocument.stories.everyItem().getElements();
    for (i = myStories.length - 1; i >= 0; i--){
        var myTextFrames = myStories[i].textFrames;
        for (j = myTextFrames.length - 1; j >= 0; j--)    {
    var stroke = myTextFrames[j].strokeWeight;
    var color = myTextFrames[j].fillColor.name;
    var tpath = myTextFrames[j].textPaths.length;
    var wrap = myTextFrames[j].textWrapPreferences.textWrapMode;
    //alert (color)
         if (myTextFrames[j].contents === "" && stroke == "0" && color == "None" && wrap === TextWrapModes.NONE && tpath == 0){
               //alert ("yes") 
       myTextFrames[j].remove();
            }
        }
    }
    

     

    is that correct?

     

    Colly

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 4, 2014 11:59 AM   in reply to cdflash

    @Colly – I know I'm a little late for commenting your answer #26, but my answer is lengthy ;-)

     

    Your "condensed" version* of the script will do nothing (or at least not much), because:

     

    //EDIT: After re-reading the whole thread, I can see that this last snippet you posted in answer #26 is not your's. Sorry. However, I did the following test with that snippet.

     

    1. It does not work with the text container(s) of a particular story, but only with text frames that sit inside the text of the stories (anchored or inline text frames).

     

    I ran your script (slightly altered: instead of removing I did a fill with "Magenta") on the following situation:

     

    BeforeScript.png

    After running the script:

    (the Magenta filled ones would be removed)

    AfterScript.png

     

    2. And even if this script would remove the two text frames hosting the anchored ones:

    Text frames with tables that span several threaded text frames are considered empty but the one text frame that contains the start of the table.

     

    Let's test #2 on a small example with the following code that is running on a selection of text frames:

     

    //Some threaded text frames selected.
    //They contain a table that spans several text containers.
     
    var sel = app.selection;
     
    //EASY TEST on emptyness with contents property:
     
    //Running the loop backwards in case we want to remove something
    //and so mixing up the length of the
     
    for(var n=sel.length-1;n>=0;n--){
        if(sel[n].contents == ""){sel[n].fillColor = "Magenta"};
        };
    

     

    Example screen shots:

     

    Before

     

    TableThreaded.png

     

    After running the above snippet:

    The Magenta filled text frames are considered empty (as far as this script's logic is going)!

    TextFramesToRemove_WRONG-ASSUMPTION.png

     

     

    To tackle  problems #1 and also #2 we could:

     

    1. Work with the textContainers array of a story object.

    That means we are working of an array of arrays, if the document contains more than one story.

     

    2. Duplicate all text frames or the parent of a text path (= a graphic line, a text frame, a spline item …) and check for emptyness of the duplicate and/or its text path.

     

    Background: if you duplicate a text frame containing one part of a table that is otherwise occupying a number of text frames and its contents property is suggesting it's empty, you'll get always that portion of the table, that is shown in the original text frame.

     

    Below a more elaborate code for detecting empty* text frames and removing them.

     

    However, there is one flaw in this code:

    If it occurs, that a button object contains a state with empty text containers only, the whole button will or can be removed!
    Depends on the number of states and if the removed object sits in an active state.

     

    Explanation: The Story object of the document is a deep-level object and the collection of all story objects will summerize every text frame or every text path in the document. Even in nested objects like groups, MSOs, etc. and well, also buttons.

     

    And there is another flaw (a tiny one), which I camouflaged with a "try/catch -> continue" scenario. Some objects might not exist anymore in the moment I want to remove them. I did not implement a optimization routine on double ID numbers of the array, that is responsible for removing the text containers or in case of text paths their parent objects.

     

    Warning: The script could create situations where text overflows like shown in answer #9 here.

    There is no steady control of overflow during the script's action.

     

    Code:

     

    //REMOVE_EmptyTextContainers_DuplicateMethod_STORIES-TEXTCONTAINERS_v2.jsx
    //Uwe Laubender
    /**
    * @@@BUILDINFO@@@ REMOVE_EmptyTextContainers_DuplicateMethod_STORIES-TEXTCONTAINERS_v2.jsx !Version! Sat Jan 04 2014 20:24:54 GMT+0100
    */
     
    //THERE IS NO TEST ON EFFECTS !!
    //Effects on otherwise empty objects are not visible. They need at least a stroke weight or a fill color to get in effect.
    //If I'm wrong here, empty objects with visible effects are removed as well.
     
    //Foreseeable issues:
    //BUTTONS COULD BE REMOVED, 
    //if one of the states contain empty text frames only OR empty text paths on otherwise empty parent objects only!
     
    //MSOs WILL BE REMOVED (A MESSAGE WILL POP UP), 
    //if the MSO's empty text frames are removed 
    //and only one state would be left over
    //however, all text frames with contents will survive!
     
    //WHAT IS NOT CONSIDERED EMPTY:
    //1. Frames with a fillColor.name other than "None"
    //2. Frames with a strokeWeight other than "0"
    //3. Graphic lines with a text path with a strokeWeight other than "0"
    //4. Text frames and text paths with a contents other than "".
     
    //ALL TEXTFRAMES OR TEXTPATHS ARE EXAMINED AS NOT THREADED.
     
     
    app.scriptPreferences.userInteractionLevel = UserInteractionLevels.interactWithAll;
     
    app.doScript(_RemoveEmptyTextContainers, ScriptLanguage.JAVASCRIPT, [], UndoModes.ENTIRE_SCRIPT, "Remove empty text containers");
     
    function _RemoveEmptyTextContainers(){
     
    var d=app.documents[0];
    var counterOne = 0;
    var counterTwo = 0;
     
    var IDsOfallTextContainers = new Array();
    var IDsOfTextContainersToRemove = new Array();
     
    var arrayOfArraysOfTextContainers = d.stories.everyItem().textContainers;
     
    for(var n=0;n<arrayOfArraysOfTextContainers.length;n++){
     
        var arrayOfTextContainers = arrayOfArraysOfTextContainers[n];
        for(c=0;c<arrayOfTextContainers.length;c++){
     
            IDsOfallTextContainers[counterOne] = arrayOfTextContainers[c].getElements()[0].id;
            ++counterOne;
     
            };
        };
     
    for(var n=0;n<IDsOfallTextContainers.length;n++){
     
        var myOriginal = d.pageItems.itemByID(IDsOfallTextContainers[n]).getElements()[0];
       $.writeln(myOriginal);
     
        //Here is more testing on "emptyness" needed:
     
        //Whitespace in contents
        //. . . ???
        //Column breaks, page breaks in contents
     
        //Overset text possible !!!
     
        //Case 1: myOriginal is textFrame
     
        if(myOriginal.constructor.name === "TextFrame"){
     
            var myDup = myOriginal.duplicate();
     
            if(
                      myDup.contents === "" 
                && myDup.fillColor.name === "None" 
                && myDup.strokeWeight === 0 
                && myDup.textWrapPreferences.textWrapMode === TextWrapModes.NONE
                ){
                IDsOfTextContainersToRemove[counterTwo] = myOriginal.id;
                };
     
            ++counterTwo;
     
            myDup.remove();
            };
     
        //Case 2: myOriginal is textPath
        if(myOriginal.constructor.name === "TextPath"){
     
            var myDup = myOriginal.parent.duplicate();
     
            if(
                myDup.textPaths[0].contents === "" 
                && myDup.constructor.name === "GraphicLine"
                && myDup.strokeWeight === 0
                && myDup.textWrapPreferences.textWrapMode === TextWrapModes.NONE
                ){
                IDsOfTextContainersToRemove[counterTwo] = myOriginal.id;
                ++counterTwo;
                };
     
            if(
                myDup.textPaths[0].contents === "" 
                && myDup.constructor.name != "GraphicLine"
                && myDup.strokeWeight === 0
                && myDup.fillColor.name === "None"
                && myDup.textWrapPreferences.textWrapMode === TextWrapModes.NONE
                ){
                IDsOfTextContainersToRemove[counterTwo] = myOriginal.id;
                ++counterTwo;
                };
     
            myDup.remove();
     
            };
     
        };
     
    //Array could be optimized for removing doubles in ID numbers:
     
    for(var n=0;n<IDsOfTextContainersToRemove.length;n++){
     
            try{
            d.pageItems.itemByID(IDsOfTextContainersToRemove[n]).remove();
            }catch(e){
    //~             $.writeln(e.message);
                continue;
                };
     
        };
     
     
    }; //END of function _RemoveEmptyTextContainers()
    

     

    Uwe

     

    Message was edited by: Laubender

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 4, 2014 12:00 PM   in reply to Laubender

    I updated the post above.

     

    Uwe

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 5, 2014 1:33 AM   in reply to Laubender

    An annotation to post #27 – the first situation where an empty text frame is anchored to an otherwise empty text frame.

     

    BeforeScript.png

     

    It's more tricky, than I first thought ;-)

     

    If the anchored text frame is removed, the host is considered empty, but might not be removed. It depends on the order of detecting empty frames and the order of removing them.

     

    So in a situation like this:

     

    AnchoredTextFrames_WithAnchoredOnes.png

     

    It might be better to do a search from a deeper level of nesting to not nested.

    A lot of effort.


    Or the user (or the script) is running the script more than once. Perhaps with in a while loop until no empty frame is found.

     

    Uwe

     

    Message was edited by: Laubender

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 5, 2014 2:03 AM   in reply to Laubender

    And we should think about: is it desirable to remove empty inline text frames at all?
    Text composition will be changed, if we do so.

     

    TextCompositionAndInlineTextFrames.png

     

    Uwe

     

    //EDIT: the previous version did not show inline text frames but inline rectangles

     

    Message was edited by: Laubender

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 5, 2014 2:20 AM   in reply to Laubender

    Here a test of anchoredness on an object by Hans-Gerd Claßen at:

     

    http://forums.adobe.com/message/4745348#4745348

     

    I just added a try/catch for objects that are not anchored at all:

     

    //NotAnchored_ANCHORED_Inline_Above_Custom_TEST-ON-SELECTION.jsx
    //Hans-Gerd Claßen
    //http://forums.adobe.com/message/4745348#4745348
     
    //TEST ON A SELECTION OF A SINGLE OBJECT:
    var myObject = app.selection[0];
     
    //Uwe Laubender
    //Try/catch added for examples that are not anchored at all:
     
    try{
    myObject.anchoredObjectSettings.anchoredPosition;
    }catch(e){
        alert("Not anchored!");
        exit();
        };
     
    var myAnchorPosition = myObject.anchoredObjectSettings.anchoredPosition;
     
    switch(myAnchorPosition)
    {
    case AnchorPosition.INLINE_POSITION:
        alert("Inline");
         // get inline settings
        break;
     
    case AnchorPosition.ABOVE_LINE:
        alert("Above");
         // get above line settings
        break;
     
    case AnchorPosition.ANCHORED:
        alert("Custom");
         // get custom settings
        break;
     
    default :     break;
    }
    

     

    To continue what I said in the post above: we could well  leave empty text frames untouched that are anchored with:

    "INLINE_POSITION" or "ABOVE_LINE"

     

    Uwe

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 6, 2014 4:39 AM   in reply to Laubender

    Thank you for all your efforts Uwe. I am beginning to think that I have opened Pandora's Box (or Frame) here.

     

    The concept started out simply enough, any empty text box should be deleted.

    Then it became any empty text box with no fill or stroke should be deleted.

    Then it became any empty text box with no fill or stroke or text wrap or type on a path or empty data merge legacy should be be deleted.

    Then it became any empty text box with no fill or stroke or text wrap or type on a path or empty data merge legacy or  that may/may not be an anchored object or within an anchored object or inline graphic should be be deleted.

    THEN the script... blah blah blah... and also looked for not just text frames, but graphic and unassigned frames of any shape...

     

    And now of course the use I had not considered - multi-state objects or buttons... *sigh*

     

    The list of scenarios where an empty text frame may have an alternate use is growing larger and larger. There are also scenarios where the removal of an empty text frame are subjective. An example is post 30 of this thread where empty boxes were used to indent text instead of adjusting the first line indent in the paragraph palette. In that instance I would agree that the script should leave those boxes alone, however if the script was used to tidy up a catalogue created using data merge, then I would argue that the script should remove those boxes. If a situation arose where both phenomena existed in the same file, then I put my head in my hands and cry.

     

    To that degree I'm satisfied with the script that is cobbled together at the moment: https://dl.dropboxusercontent.com/u/55743036/emptyframeremover.jsx for dealing with print purpose files, given that it successfully removes the text boxes in this test file that should be deleted with the exception of threaded text that I would like left alone: https://dl.dropboxusercontent.com/u/55743036/empty%20textbox%20test.id ml . I would like to try this script on the snippets in your last posts, are you able to PM me with links to those files?

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 6, 2014 5:10 AM   in reply to cdflash

    @Colly – Indeed: Pandora's Box ;-)

     

    Here a link to my InDesign/IDML file:

     

    https://www.dropbox.com/s/ar7w9sja4bp9433/E2-TextFramesOfAnySort_AlsoN ested.zip

     

    E2-TextFramesOfAnySort_AlsoNested.zip

     

        E2-TextFramesOfAnySort_AlsoNested.indd

        E2-TextFramesOfAnySort_AlsoNested.idml

     

    Of course, I'm not sure, if all *critical* cases are covered ;-)

     

    Uwe

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 7, 2014 7:19 AM   in reply to Laubender

    Thank you for your test file Uwe. I have run my cobbled together script on the file and am satisfied with the results. Buttons and MSOs were left alone; grouped objects were not touched; empty inlines within other empty inlines were deleted, and threaded text was ONLY deleted if (within the entire threaded story) there is no text... otherwise the threaded frames remain... but I can live with that. Whether it fulfils the requirements of anyone else is subjective, but for my purposes it does what it needs to do... until further texting reveals more bugs

     

    Colin

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 12, 2014 3:22 PM   in reply to winterm

    @Winterm – just a small thing.
    I found it valuable to write my GREPs this way:

     

    app.findGrepPreferences.findWhat = /\A\s+\Z/.source;
    

     

    Don't know, if I saw it the first time with a script by John Hawkinson or Peter Kahrel…

    Very handy, you need not to escape the \ signs.

     

    Also generally good for strings like that:

     

    alert( /He said: "What should I say? It works…"/.source );
    

     

    Uwe

     
    |
    Mark as:

More Like This

  • Retrieving data ...

Bookmarked By (0)

Answers + Points = Status

  • 10 points awarded for Correct Answers
  • 5 points awarded for Helpful Answers
  • 10,000+ points
  • 1,001-10,000 points
  • 501-1,000 points
  • 5-500 points