Copy link to clipboard
Copied
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...
@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 = a
Copy link to clipboard
Copied
not sure myself, but have you checked this thread?
http://forums.adobe.com/message/3306350
Copy link to clipboard
Copied
thank you for your reply, I see my fault now - I didn't mention, I'm on PC, ID 7.5.2 (CS5.5), so java is preferable...
anyway, this one
http://forums.adobe.com/message/2291359#2291359
is closer to my needs, but in both threads guys are talking about "deleting empty text frames" - that's not the biggest part for me...
my main problem is, that not all frames, which content marked as "text", are really text.
What I really need is proper content marking.
Let's say, you draw a box with text tool, fill it with a some color, maybe even tap spacebar inside that frame inadvertently... and use that "text frame" as a background for your artwork.
Later you send me that file for further processing, and:
1) if I run script "remove empty text frames", I loose your "background" (if you didnt tap spacebar or whatever in it)
or
2) if I run script "move text frames to layer" - I get your "text frame" (a.k.a. "background") all over the artwork and maybe real text frames...
You keep your text layer above images, didn't you?
It's a mess...
Copy link to clipboard
Copied
you need to check with textFrame properties.......
if (myTextFrame.fillColor.name == None ) {
alert("No Colors used");
}
else {
alert("Colors applied on TextFrame);
}
try this..............
Copy link to clipboard
Copied
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.textContainers;
for (j = myTextFrames.length - 1; j >= 0; j--) {var stroke = myTextFrames
.strokeWeight;
var color = myTextFrames.fillColor.name; //alert (color)
if (myTextFrames.contents == "" && stroke == "0" && color == "None"){
//alert ("yes")
myTextFrames.remove();
}
}
}
Copy link to clipboard
Copied
nice job, thank you!
your script nicely removes really empty text frames, which are just trash, in fact...
unfortunately, after this cleaning I still can't move remaining frames to their own layer - I still must find out the way to convert empty, but filled/outlined text frames to "unassigned" first...
Copy link to clipboard
Copied
@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
.textContainers; for(var m=_storyAllTextFrames.length-1;m>=0;m--){
//If the contents of a text frame is an empty string:
if(_storyAllTextFrames
.contents === ""){ _storyAllTextFrames
.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
Copy link to clipboard
Copied
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
.textContainers; for (var m = _storyAllTextFrames.length - 1; m >= 0; m--){
if (_storyAllTextFrames
.contents === "") _storyAllTextFrames
.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)
Copy link to clipboard
Copied
Thank you, Marijan & Jongware to point to the right property "contentType" and to app.select(null).
I think the line:
_storyAllTextFrames
.contentType = ContentType.UNASSIGNED;
needs a try-catch-wrapper:
try{
_storyAllTextFrames
.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:
Uwe
Copy link to clipboard
Copied
Many thanks to all you, guys, for your so elegant solutions. Reading your codes, I can see how and why it works, but couldn't write something similar for ages...
So far I did "field testing" for Uwe's code, and it fits perfectly my needs in "case 2". Sometimes I get shapes, copy-pasted from Illustrator, and with content type: text applied in Id... Uwe's script fixes them back to "unassigned" too! Very nice.
I think so far I'll add Marijan's line for clearing selection (it's not so critical for practical purposes, though. It's just elegant) and some simple alert at the end - something like "job is done"...
Oh, and I need another free day to study Marijan's & Jongware's solutions - it's a valuable school to me and other fresh people here at forum. Thank you!
Copy link to clipboard
Copied
1. Use ContentType:
contentType | ContentType: ContentType.UNASSIGNED ContentType.GRAPHIC_TYPE ContentType.TEXT_TYPE | r/w | The type of content that a frame can contain. |
_storyAllTextFrames
.contentType = ContentType.UNASSIGNED;
2. Use app.select:
void select (selectableItems: varies[, existingSelection: SelectionOptions=SelectionOptions.REPLACE_WITH])
Selects the specified object(s).
Parameter | Type | Description |
---|---|---|
selectableItems | Array of Objects NothingEnum Object SelectAll | The objects to select. Can accept: Object, Array of Objects, NothingEnum enumerator or SelectAll enumerator. |
existingSelection | SelectionOptions: 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!)
Copy link to clipboard
Copied
Ah, and recently I realized there is just another way to deselect after the script is done...
Laubender, its your way - calling Menu Action item by ID!
I just duplicated this piece of your script:
try{
app.scriptMenuActions.itemByID(11297).invoke();
}catch(e){};
and changed ID to 278 - it stands for "Deselect All" and does the trick too
Copy link to clipboard
Copied
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..
Copy link to clipboard
Copied
"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.
Copy link to clipboard
Copied
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
//alert (color)
if (myTextFrames
to
var color = myTextFrames
var wrap = myTextFrames
//alert (color)
if (myTextFrames
but it errors. can anybody point out what I've done wrong?
Copy link to clipboard
Copied
@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
Copy link to clipboard
Copied
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
var color = myTextFrames
var wrap = myTextFrames
//alert (color)
if (myTextFrames
//alert ("yes")
myTextFrames
but now when running the script, nothing is deleted, including the empty textboxes with no fill, stroke or textwrap. what have I missed?
Copy link to clipboard
Copied
@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
Copy link to clipboard
Copied
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.textContainers;
for (j = myTextFrames.length - 1; j >= 0; j--) {
var stroke = myTextFrames
.strokeWeight; var color = myTextFrames
.fillColor.name; var wrap = myTextFrames
.textWrapPreferences.textWrapMode; //alert (color)
if (myTextFrames
.contents == "" && stroke == "0" && color == "None" && wrap === "TextWrapModes.NONE"){ //alert ("yes")
myTextFrames
.remove(); }
}
}
var _d = app.documents[0],
_allStories = _d.stories;
for (var n = _allStories.length - 1; n >= 0; n--){
var _storyAllTextFrames = _allStories
.textContainers; for (var m = _storyAllTextFrames.length - 1; m >= 0; m--){
if (_storyAllTextFrames
.contents === "") try{
_storyAllTextFrames
.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.indd
Copy link to clipboard
Copied
@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
Copy link to clipboard
Copied
@Colin – could you provide an IDML for download as well?
Uwe
Copy link to clipboard
Copied
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 |
I have also added an IDML version of the file used to test this script.
https://dl.dropboxusercontent.com/u/55743036/empty%20textbox%20test.idml
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
Copy link to clipboard
Copied
...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=myStoriestextContainers;
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.idml
Colly
Copy link to clipboard
Copied
@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
Copy link to clipboard
Copied
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
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.textFrames;
for (j = myTextFrames.length - 1; j >= 0; j--) {
var stroke = myTextFrames
.strokeWeight; var color = myTextFrames
.fillColor.name; var tpath = myTextFrames
.textPaths.length; var wrap = myTextFrames
.textWrapPreferences.textWrapMode; //alert (color)
if (myTextFrames
.contents === "" && stroke == "0" && color == "None" && wrap === TextWrapModes.NONE && tpath == 0){ //alert ("yes")
myTextFrames
.remove(); }
}
}
var _d = app.documents[0],
_allStories = _d.stories;
for (var n = _allStories.length - 1; n >= 0; n--){
var _storyAllTextFrames = _allStories
.textContainers; for (var m = _storyAllTextFrames.length - 1; m >= 0; m--){
if (_storyAllTextFrames
.contents === "") try{
_storyAllTextFrames
.contentType = ContentType.UNASSIGNED; }catch(e){};
}
}
var myGraphicFrames = app.activeDocument.rectangles;
for (i=myGraphicFrames.length-1; i>=0; i--) {
var stroke = myGraphicFrames.strokeWeight;
var color = myGraphicFrames.fillColor.name;
var tpath = myGraphicFrames.textPaths.length;
var wrap = myGraphicFrames.textWrapPreferences.textWrapMode;
if (myGraphicFrames.graphics.length < 1 && stroke == "0" && color == "None" && wrap === TextWrapModes.NONE && tpath == 0)
myGraphicFrames.remove();
}
var myOvalFrames = app.activeDocument.ovals;
for (i=myOvalFrames.length-1; i>=0; i--) {
var stroke = myOvalFrames.strokeWeight;
var color = myOvalFrames.fillColor.name;
var tpath = myOvalFrames.textPaths.length;
var wrap = myOvalFrames.textWrapPreferences.textWrapMode;
if (myOvalFrames.graphics.length < 1 && stroke == "0" && color == "None" && wrap === TextWrapModes.NONE && tpath == 0)
myOvalFrames.remove();
}
var myPolygonFrames = app.activeDocument.polygons;
for (i=myPolygonFrames.length-1; i>=0; i--) {
var stroke = myPolygonFrames.strokeWeight;
var color = myPolygonFrames.fillColor.name;
var tpath = myPolygonFrames.textPaths.length;
var wrap = myPolygonFrames.textWrapPreferences.textWrapMode;
if (myPolygonFrames.graphics.length < 1 && stroke == "0" && color == "None" && wrap === TextWrapModes.NONE && tpath == 0)
myPolygonFrames.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