Copy link to clipboard
Copied
Hi FrameMaker Friends:
I'm trying to help a client locate all occurrences of a specific image and delete it. Here's what I've tried so far:
So, how is the text field used for an anchored frame search? Is there something I can type in there that will allow me to narrow the scope? I tried filename and full path with filename, neither worked. Any other ideas?
Thanks in advance,
~Barb
Hi Barb, I am so sorry for the delay and for posting a broken script! I tried the script and found some errors being written to the Console. Here is the updated, working version. Note that the script will delete the anchored frame containing the image. If you want to keep the anchored frame and just delete the image, replace this line:
aframes.push (parent);
with this:
aframes.push (graphic);
Here is the updated script:
main ();
function main () {
var doc;
doc = app.Ac
...
Copy link to clipboard
Copied
Save as MIF & then go hunting for the image name? Just spit-balling.... ;>)
Copy link to clipboard
Copied
That seems like a good option that didn't occur to me. Is there any downside, Jeff?
~Barb
Copy link to clipboard
Copied
Depends if you're just using it to locate the offending images OR going to delete them in the MIF and then re-open the MIF again in FM. Latter action is riskier.
Copy link to clipboard
Copied
If the objects can be unambiguously found in MIF, I'd think it could be scripted.
Going forward, perhaps there's a more elegant way to implement these pre-pub framey things, such as:
🗏 Frame-Above/Below in Para Format, defined-away later in ¶format
🗏 Imported Insets, defined-away later at source
Copy link to clipboard
Copied
I think I would generate a list of references with referenced graphics. Search in the list for the file name and use the generated hyperlink to jump to the relevant image.
OR use the search field in the insets panel. More cumbersome, though, but doable. probly
Copy link to clipboard
Copied
Can you elaborate on "OR use the search field in the insets panel.", Bjorn?
~Barb
Copy link to clipboard
Copied
You can search for specific file names in the "Insets Panel". It will require you to open all chapters, though, which is why this is a more time consuming effort. A generated list is to be preferred I think. OR merely rename the graphics file and open all chapters. Everywhere there is a red cross in the Insets Panel, is where the file is and can be located by clicking. But it will - again - require opening all chapters.
Copy link to clipboard
Copied
Thank you, all of you! Love this forum. I'll play with both tonight so that I can show the client in the morning.
~Barb
Copy link to clipboard
Copied
Hi Barb,
I am assuming that you are looking for a particular graphic name and want to delete the anchored frame that contains it. If so, this code should do what you want. You will have to tweak the regular expression so it contains the correct filename pattern; I can help you with that.
-Rick
main ();
function main () {
var doc;
doc = app.ActiveDoc;
if (doc.ObjectValid () === 1) {
processDoc (doc);
}
}
function processDoc (doc) {
var regex, aframes, count, i;
regex = /filename\.eps$/i;
// Turn off the displaying property to speed the script and prevent flicker.
if (app.Displaying === 1) {
app.Displaying = 0;
}
// Get the anchored frames that contain an image matching
// a regular expression pattern.
aframes = getAFrames (doc, regex);
// Delete the anchored frames.
count = aframes.length;
for (i = 0; i < count; i += 1) {
aframes[i].Delete ();
}
// Restore the document display and refresh the screen.
if (app.Displaying === 0) {
app.Displaying = 1;
doc.Redisplay ();
}
}
function getAnchoredFrames (doc, regex) {
var aframes, graphic, parent;
// Make an array to store the anchored frames.
aframes = [];
// Loop through the graphics in the document.
graphics = doc.FirstGraphicInDoc;
while (graphic.ObjectValid () === 1) {
// Test for an imported graphic.
if (graphic.constructor.name === "Inset") {
// See if the imported graphic matches the regular expression.
if (regex.test (graphic.InsetFile) === true) {
Get the parent frame of the graphic.
parent = graphic.FrameParent;
// Make sure it is an anchored frame.
if (parent.construtor.name === "AFrame") {
// Push it onto the array.
aframes.push (graphic);
}
}
}
graphic = graphic.NextGraphicInDoc;
}
// Return the array of anchored frame objects.
return aframes;
}
Copy link to clipboard
Copied
Oh, this looks promising, Rick. Thank you!
So this is the line we need to edit? Let's say the file they are looking for called myfile.tif and it appears multiple times in the file.
regex = /filename\.eps$/i;
So this is the line we need to edit? Let's say the file they are looking for called myfile.tif and it appears multiple times in the file.
regex = /myfile\.tif$/i;
???
And, you know I know nothing about Extendscript. So continuing to guess:
~Barb
Copy link to clipboard
Copied
regex = /myfile\.tif$/i;
A regular expression literal is created between two forward slashes. This one essentially says,
Find the string "myfile", followed by a literal period (\.), followed by the string "tif", and make sure it is at the end of the string we are testing ($). The "i" is a flag that makes it a case-insensitive search.
Note that you can create regular expressions in FrameMaker's Find/Change dialog box and test them in FrameMaker. Make sure you select the Regular Expression radio button.
The script should be saved as a plain text file with a .jsx extension. A script like this can be put in the startup folder but you still have to run it "manually" using File > Script > Run. Or, you can choose File > Script > Catalog and add it to the Script Library panel.
When I write a script for a client, I will typically give them an "interface"; possibly a command on a dedicated menu, or add it as a custom command to an existing FrameMaker menu.
Copy link to clipboard
Copied
This is terrific, Rick! I'm still teaching so not going be able to sit down to work through this until Friday morning.
~Barb
Copy link to clipboard
Copied
OK, Rick. I finally had time to sit down to work through this but am still doing something wrong.
This is the file I am testing with: rocketwide.ai.
I copied your code to Notepad, modified the one regex line, saved it in the startup folder, can see it, can run it but nothing ?happens. Is it the encoding when I saved the Notepad file? Or do I need to reference the entire path?
~Barb
Copy link to clipboard
Copied
Hi Barb,
I didn't create a specific interface for this script, so you have to run it "manually" using File > Script > Run and choosing the script. Or, you can choose File > Script > Catalog and add it to the Script Library panel and run it from there.
-Rick
Copy link to clipboard
Copied
Hi Rick:
My apologies that this is dragging on. I'm in class all week again. I can run the script with both methods you described, but it isn't doing anything. Or at least, it isn't locating and removing the referenced image.
~Barb
Copy link to clipboard
Copied
Hi Barb, I am so sorry for the delay and for posting a broken script! I tried the script and found some errors being written to the Console. Here is the updated, working version. Note that the script will delete the anchored frame containing the image. If you want to keep the anchored frame and just delete the image, replace this line:
aframes.push (parent);
with this:
aframes.push (graphic);
Here is the updated script:
main ();
function main () {
var doc;
doc = app.ActiveDoc;
if (doc.ObjectValid () === 1) {
processDoc (doc);
}
}
function processDoc (doc) {
var regex, aframes, count, i;
regex = /rocketwide\.ai$/i;
// Turn off the displaying property to speed the script and prevent flicker.
if (app.Displaying === 1) {
app.Displaying = 0;
}
// Get the anchored frames that contain an image matching
// a regular expression pattern.
aframes = getAFrames (doc, regex);
// Delete the anchored frames.
count = aframes.length;
for (i = 0; i < count; i += 1) {
aframes[i].Delete ();
}
// Restore the document display and refresh the screen.
if (app.Displaying === 0) {
app.Displaying = 1;
doc.Redisplay ();
}
}
function getAFrames (doc, regex) {
var aframes, graphic, parent;
// Make an array to store the anchored frames.
aframes = [];
// Loop through the graphics in the document.
graphic = doc.FirstGraphicInDoc;
while (graphic.ObjectValid () === 1) {
// Test for an imported graphic.
if (graphic.constructor.name === "Inset") {
// See if the imported graphic matches the regular expression.
if (regex.test (graphic.InsetFile) === true) {
// Get the parent frame of the graphic.
parent = graphic.FrameParent;
// Make sure it is an anchored frame.
if (parent.constructor.name === "AFrame") {
// Push it onto the array.
aframes.push (parent);
}
}
}
graphic = graphic.NextGraphicInDoc;
}
// Return the array of anchored frame objects.
return aframes;
}
Copy link to clipboard
Copied
@frameexpert - Sorry for the thread resurrection, but I have a similar issue and couldn't get your script to work.
My issue: My document contains an Anchored Frame on the last page with the text "THE END" inside the frame. There will only be one such anchored frame, and it will always be at the end of the document.
The above might help as the script shouldn't have to loop through the document and find all occurences.
When the document(s) are a component of a book, I need to loop through all the components except the last one and remove the "THE END" frame.
I know how to iterate through the book file. I don't know how to find and remove the frame in the document.
I tried changing the Regex line to:
regex = /THE END$/;
but it didn't seem to work for me - probably b/c I am looking for text in the A-frame, as opposed to an image in the frame.
Thank you as always!
Copy link to clipboard
Copied
Short Summary: Ideally, what I need to do is find an anchored frame at the end of the document containing "THE END" and delete the anchored from. Alternately, I would be okay with replacing "THE END" with non-printing characters and leaving the anchored frame, but I would need to ensure "THE END" was in the anchored frame and not in the main body text of the document.
Made some progress:
main ();
function main () {
var doc;
doc = app.ActiveDoc;
if (doc.ObjectValid () === 1) {
processDoc (doc);
}
}
function processDoc (doc) {
var regex, aframes, count, i;
regex = /THE END$/;
// Turn off the displaying property to speed the script and prevent flicker.
if (app.Displaying === 1) {
app.Displaying = 0;
}
// Get the anchored frames that contain an image matching
// a regular expression pattern.
aframes = getAFrames (doc, regex);
// Delete the anchored frames.
count = aframes.length;
for (i = 0; i < count; i += 1) {
aframes[i].Delete ();
}
// Restore the document display and refresh the screen.
if (app.Displaying === 0) {
app.Displaying = 1;
doc.Redisplay ();
}
}
function getAFrames (doc, regex) {
var aframes, graphic, parent, pgf, textitems;
// Make an array to store the anchored frames.
aframes = [];
// Loop through the graphics in the document.
graphic = doc.FirstGraphicInDoc;
while (graphic.ObjectValid () === 1) {
// Test for a Text Frame.
if (graphic.constructor.name === "TextFrame") {
//Get the last paragraph in the frame. (The first might not have the paragraph number).
// pgf = graphic.LastPgf;
// See if the pgf text matches the regular expression.
// textItems = pgf.GetText(Constants.FTI_VarEnd);
// if(textItems.length > 0)
// {
// if (regex.test (textItems) === true) {
// Get the parent frame of the graphic.
parent = graphic.FrameParent;
// Make sure it is an anchored frame.
if (parent.constructor.name === "AFrame") {
// Push it onto the array.
aframes.push (parent);
}
// }
// }
}
graphic = graphic.NextGraphicInDoc;
}
// Return the array of anchored frame objects.
return aframes;
}
The above will delete ALL anchored frames containing text frames in the document. I couldn't get the regex to work. This probably works for us in most cases, but it's not perfect.
I thought for my purposes, I could simplify it by setting "graphic = doc.LastGraphicInDoc", testing the regex, and deleting the frame without looping through the doc or pushing to an array, but I got an "Undefined is not an object" error so doc.LastGraphicinDoc probably isn't valid.
Copy link to clipboard
Copied
Overall, you're on the right track, but you need the text of the paragraph. See if you can find a getText function in one of your projects. It seems that we would have used one along the way.
Copy link to clipboard
Copied
Copy link to clipboard
Copied
BINGO!!!!
main ();
function main () {
var doc;
doc = app.ActiveDoc;
if (doc.ObjectValid () === 1) {
processDoc (doc);
}
}
function processDoc (doc) {
var regex, aframes, count, i;
regex = /THE END$/;
// Turn off the displaying property to speed the script and prevent flicker.
if (app.Displaying === 1) {
app.Displaying = 0;
}
// Get the anchored frames that contain an image matching
// a regular expression pattern.
aframes = getAFrames (doc, regex);
// Delete the anchored frames.
count = aframes.length;
for (i = 0; i < count; i += 1) {
aframes[i].Delete ();
}
// Restore the document display and refresh the screen.
if (app.Displaying === 0) {
app.Displaying = 1;
doc.Redisplay ();
}
}
function getAFrames (doc, regex) {
var aframes, graphic, parent, pgf, textItems;
// Make an array to store the anchored frames.
aframes = [];
// Loop through the graphics in the document.
graphic = doc.FirstGraphicInDoc;
while (graphic.ObjectValid () === 1) {
// Test for a Text Frame.
if (graphic.constructor.name === "TextFrame") {
//Get the last paragraph in the frame. (The first might not have the paragraph number).
pgf = graphic.LastPgf;
// See if the pgf text matches the regular expression.
// textitems = pgf.GetText(Constants.FTI_VarEnd);
textItems = getText(pgf, doc)
if(textItems.length > 0)
{
if (regex.test (textItems) === true) {
// Get the parent frame of the graphic.
parent = graphic.FrameParent;
// Make sure it is an anchored frame.
if (parent.constructor.name === "AFrame") {
// Push it onto the array.
aframes.push (parent);
}
}
}
}
graphic = graphic.NextGraphicInDoc;
}
// Return the array of anchored frame objects.
return aframes;
}
function getText(textObj, doc) {
// https://community.adobe.com/t5/framemaker-discussions/script-to-change-red-text-to-black/td-p/15293234/page/2
// Gets the text from the text object or text range.
var text = "",
textItems,
i;
// Get a list of the strings in the text object or text range.
if (textObj.constructor.name !== "TextRange") {
textItems = textObj.GetText(Constants.FTI_String);
} else {
textItems = doc.GetTextForRange(textObj, Constants.FTI_String);
}
// Concatenate the strings.
for (i = 0; i < textItems.len; i += 1) {
text += (textItems[i].sdata);
}
return text; // Return the text
}
For my usage, I'd prefer to simplify it - i.e. it loops through the entire document and deletes all anchored frames containing "THE END". My frame will always be the last frame in the document, I'd prefer to start with the last frame, test it against the regex, if it passes, delete it. If it fails, it was previously deleted, exit.
But this works quickly, and more importantly, it works properly.
@frameexpert - Thank you again as always!
Find more inspiration, events, and resources on the new Adobe Community
Explore Now