Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Extract Figure & Table Names From Document

Contributor ,
Jul 11, 2025 Jul 11, 2025

Last April I asked for help counting figures in our documents.  The code you helped me with has been a real help.  In addition to getting a figure count, I also get a table count.  The customer uses these to help determine the level of effort for editing various documents.  

Recently, I was asked if it's possible to extract the Title associated with each figure and table.  I tried boring down in the data browser, but can't find where that information stored.  I'm currently using the following code to get the counts:

function countFigures() {

    figureCtr = 0;
    var figure = doc.FirstGraphicInDoc;
        while (figure.ObjectValid()) {
 Alert ('Stop script and check for figure name.');           
            if (figure.constructor.name === "AFrame") { 
                 figureCtr++;
                }    
            figure = figure.NextGraphicInDoc;
        }
    return figureCtr;
    }
function countTbls() {

    tableCtr = 0;
    textList = doc.MainFlowInDoc.GetText (Constants.FTI_TblAnchor);

// If you want to loop through all of the tables, you can do this:
    count = textList.length;
    tableCtr = tableCtr + count;
    for (i = 0; i < count; i += 1) {
        tbl = textList[i].obj // The table object.
        tbl = doc.FirstTblInDoc;
        if(tbl.ObjectValid() == false) {
            Console('   No tables found!');
        }
    }
    return tableCtr;
}

Was hoping it would be as simple as doc.FirstGraphicInDoc.Name would work, but it's obviously not that simple. As always.  Any suggestions very much appreciated.

362
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 2 Correct answers

Community Expert , Jul 16, 2025 Jul 16, 2025

This will return the table titles into an array. As a result, the count will only be the number of tables with titles.

main ();

function main () {
    
    var tblTitles, doc;
    
    // Make an array to hold the table titles.
    tblTitles = [];
    
    doc = app.ActiveDoc;
    if (doc.ObjectValid () === 1) {
        getTblTitles (doc, tblTitles);
        alert (getTblTitles.length);
        // Write the table titles to the Console.
        Console (tblTitles.join ("\r\n"));
    }
}

functio
...
Translate
Community Expert , Jul 16, 2025 Jul 16, 2025

A non-trivial part of the challenge here that FM has no default structure for a figure title at all, and authors don't always use the standard Title feature of the Table structure.

 

Lacking Anchored Frame above, I have often used various hacks to get the title and other metadata into the bottom of the AF. I use consistent Â¶names when doing this, but you won't encounter those. I've also used an invisible run-in ¶Figure.Anchor for the AF, with a Â¶Figure.Caption as Next, both set to keep.

 

Since

...
Translate
Community Expert ,
Jul 11, 2025 Jul 11, 2025

Tables can have titles and you can check for one with this:

if (tbl.TblTitlePosition !== Constants.FV_TBL_NO_TITLE) {
    // The FirstPgf property is always the table title.
    pgf = tbl.FirstPgf;
    // ... Get the text of the pgf here.
}

Anchored frames don't have built-in titles, so you have to figure out if there is a paragraph format to look for that has the title in it. I assuming you mean the title on the page. If you mean the graphic's file name, then it is this:

// See if it is an imported graphic.
if (graphic.constructor.name === "Inset") {
    filename = graphic.InsetFile;
}

If the graphic is imported by copy, the filename will be an empty string.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Jul 15, 2025 Jul 15, 2025

Thanks for the code suggestions.  I must be doing something wrong, because I don't get any "hits" on titles.  Here's how I incorporated your code into my table count script.  It doesn't appear the "if" statement condition is being met.  I'm working with unstructured FM 2022 docs, if that makes a difference.  

function countTbls() {

    tableCtr = 0;
    textList = doc.MainFlowInDoc.GetText (Constants.FTI_TblAnchor);

// If you want to loop through all of the tables, you can do this:
    count = textList.length;
    tableCtr = tableCtr + count;
    for (i = 0; i < count; i += 1) {
        tbl = textList[i].obj // The table object.
        tbl = doc.FirstTblInDoc;
        
        if (tbl.TblTitlePosition !== Constants.FV_TBL_NO_TITLE) {
            pgf = tbl.FirstPgf
            alert('Table title = ' + pgf);
            }

//var find = getText (textRange, doc);
        
        if(tbl.ObjectValid() == false) {
            Console('   No tables found!');
        }
    }
    return tableCtr;
}

 I haven't had a chance to work with the figure titles.  Wanted to get this working first.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 16, 2025 Jul 16, 2025

Some comments on your code:

 

You need to declare your function variables inside the function or else they will be global to the whole script. You should also pass in the document object when calling the function.

function countTbls(doc) {

    var tableCtr, textList, count, i, tbl, pgf;

 The tableCtr variable is unnecessarily because you already have the table count with this line:

count = textList.length;

If you are using this to get the number of tables in a book, it's best to increment this outside the function.

 

This line is forcing the script to the first table everytime through the loop. It should be deleted.

tbl = doc.FirstTblInDoc;

If this is true, then you will have to use a function to get the text from the table's first paragraph (the title).

if (tbl.TblTitlePosition !== Constants.FV_TBL_NO_TITLE) {
    pgf = tbl.FirstPgf;
    alert('Table title paragraph object = ' + pgf);
    // Use a function to get the paragraph object's text, for example:
    text = getText (pgf, doc);
}

If you use "text" as a variable, be sure to declare it at the top of the function.

 

This will never match in the position you have it, because if textList returns tables (text items), then there is at least one table in the document's main flow.

if(tbl.ObjectValid() == false) {
    Console('   No tables found!');
}

If there are no tables, then the count variable will be 0.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 16, 2025 Jul 16, 2025

Since your requirements have changed a bit (you are getting titles and not just the count), this raises a couple of questions: do you still need the total table count? Or just the count of tables with titles?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 16, 2025 Jul 16, 2025

This will return the table titles into an array. As a result, the count will only be the number of tables with titles.

main ();

function main () {
    
    var tblTitles, doc;
    
    // Make an array to hold the table titles.
    tblTitles = [];
    
    doc = app.ActiveDoc;
    if (doc.ObjectValid () === 1) {
        getTblTitles (doc, tblTitles);
        alert (getTblTitles.length);
        // Write the table titles to the Console.
        Console (tblTitles.join ("\r\n"));
    }
}

function getTblTitles (doc, tblTitles) {

    var textList, count, i, tbl, pgf, title;
    
    // Get all of the table text items in the main text flow.
    textList = doc.MainFlowInDoc.GetText (Constants.FTI_TblAnchor);

    count = textList.length;
    for (i = 0; i < count; i += 1) {
        tbl = textList[i].obj // The table object.
        // See if it has a title.
        if (tbl.TblTitlePosition !== Constants.FV_TBL_NO_TITLE) {
            pgf = tbl.FirstPgf;
            // Get the title and push it into the array.
            title = getTitleText (pgf, doc);
            tblTitles.push (title);
        }
    }
}

function getTitleText (pgf, doc) {
    
    // Gets the text (and paragraph number) from the paragraph.

    var text, textItems, i;
    
    text = "";
    
    // Get a list of the strings in the paragraph.
    textItems = pgf.GetText (Constants.FTI_String);
    
    // Concatenate the strings.
    for (i = 0; i < textItems.len; i += 1) {
        text += (textItems[i].sdata);
    }
    
    // If the paragraph has an autonumber, prepend it.
    if (pgf.PgfIsAutoNum === 1) {
        text = pgf.PgfNumber + text;
    }
    
    return text; // Return the text.
}
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Jul 16, 2025 Jul 16, 2025
LATEST

Thank you both for your answers. As always, I learn much at the feet of the experts. I'm a self-taught coder and fact we're limited to ES3 makes it difficult to find relevant training books.

 

Bob...you're right. Our editors don't appear to be using the Title feature of the table structure. While I get a table count of 12 tables (textList.length) on my test document, the 

if (tbl.TblTitlePosition !== Constants.FV_TBL_NO_TITLE) {

 statement never tests true. If I delete the statement and run the code inside, "title" is always empty.

 

Rick...thanks for the tip that "textList.length" gives me the table count for the doc. I can collapse my entire tableCount function to about three lines.  Also, thanks for all the other suggestions...esp. the one about passing the document object into a function.  Clears up one point of confusion.  

 

If you don't mind, I still have some confusion as to variable scope. I think I understand global variables, and in the example you provided I didn't see any global variables. I'm guessing you try to avoid them if at all possible.

 

My confusion lies with variables declared within a function, which I understand are function scoped. From your example, it appears a function scoped variable (or object) can be passed to another function. Conversely, a variable declared in a "called function" can be passed back to the "calling" function by including the variable in the return statement; e.g., "return text".  Am I correct so far?

 

Can a variable that is passed from a calling function to a called function then be passed by the called function to a third function and so on?

 

P.S. Bob, good suggestion on using AI to search for the titles, but the customer won't allow it. That said, I can probably come up with a text search to find them. Turns out they all are a unique paragraph style.  Thanks for the hint.

 

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 15, 2025 Jul 15, 2025

Why do you have this in there?

tbl = doc.FirstTblInDoc;
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Jul 15, 2025 Jul 15, 2025

Not exactly sure...I think it was in a piece of code I found on the forum, or recommended by one of the contributors.  Since the counter worked with it, I never bothered to question it.  However, I just ran the script with that line remarked out and it had no effect.  Still unable to pull the titles using the two lines of code you provided.  I'm guessing I have to use the getText function, but can't determine what textObj I need to send it.  Thanks for you time on this!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 16, 2025 Jul 16, 2025

A non-trivial part of the challenge here that FM has no default structure for a figure title at all, and authors don't always use the standard Title feature of the Table structure.

 

Lacking Anchored Frame above, I have often used various hacks to get the title and other metadata into the bottom of the AF. I use consistent Â¶names when doing this, but you won't encounter those. I've also used an invisible run-in ¶Figure.Anchor for the AF, with a Â¶Figure.Caption as Next, both set to keep.

 

Since Tables have to be anchored to some Â¶format, that's usually ¶Table.Anchor or ¶Table.Title in my work, and may serve as the Table title.

 

Are any of the AIs smart enough to scan a PDF and both count and name objects of interest?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines