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

Using ExtendScript to automate a task

Explorer ,
Aug 19, 2013 Aug 19, 2013

Copy link to clipboard

Copied

Hello,

First off, I like to say that I do not have any experience with ExtendScript and looking for some code to get me started. Any help is most welcome.

Here's my current FM book's layout.

For each page, I have 3 main elements in this order from top to bottom.

     - An imported GRAPHICS inside an anchored frame.

     - Below the anchored frame is the figure CAPTION (e.g. Figure 3. Title of the graphics here).

     - Following the caption is some NOTES (description of the graphics)

Goal: Insert/Paste the CAPTION of the next page onto the bottom of the current page after the NOTES section.

Why: I'm converting from FM to RoboHelp. RH Requires a topic title for each page. Our current template does not have any heading text so I need to copy and paste the figure CAPTION to use as the heading text.

How: At the end of each NOTES section of the current page, I like to copy/paste the CAPTION of the next page, with a specific paragraph tag (e.g. h2x or something similar).

The layout after this process will be:

     - GRAPHICS

     - CAPTION (of current page)

     - NOTES

     - CAPTION (of next page)

Result: At the end of each notes section, I will have the CAPTION of the next page that I can use to tell RH to create that as a separate topic.

Let me know if I need to provide any more details, but the main idea is that I need to have some script to automate this process as it is very tedious to copy/paste for each page everytime I need to convert a FM book.

Thanks, and appreciate any help I can get.

- Henry

Views

3.6K

Translate

Translate

Report

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 1 Correct answer

Mentor , Aug 30, 2013 Aug 30, 2013

Lucky for Henry, I became intrigued by this task and had a little time to mess around with a sample script. I'm posting it here for the benefit of the community. Basically, it:

- Goes through the book or document and finds all instances of a "z_anchor" pgf followed by a "fig" pgf followed by a "v_num" pgf followed by a "z_notes" pgf. This test is to make sure we are at a place to do the text operation.

- Retrieves the text from the fig pgf

- Inserts a new pgf above the "z_anchor" pgf, gives it a sp

...

Votes

Translate

Translate
Mentor ,
Aug 20, 2013 Aug 20, 2013

Copy link to clipboard

Copied

Hi Henry,

This is a perfect job for ExtendScript, but I think you may have a tough time getting some sample code here, at least as your request stands currently. Your requirements involve some tricky operations and hands-on knowledge of your files. Folks here are happy to help with small snippets and advice for targeted issues, but you are starting from zero and I don't find it likely that somebody will write up this script for free.

That said, I don't want to discourage you because the extensibility of FM is one of its great strengths. With the right skills, you or someone could pull this off. I will acknowledge, though, that the initial learning curve is steep. I had many years of heavy FDK experience before ES came around and I am still stumbling through it myself.

In the meantime, you might think about the general architecture... perhaps you could simplify and thus reduce the overhead. Are you looking to do this as a one-time operation just before a conversion; that is, you don't want the copied CAPTIONs to hang around after that? Or do you want them to stay permanently and just have the script refresh itself? If they could stay permanently, would cross-references work? That is, will RoboHelp recognize the text within a cross-reference to generate a heading? I'm asking because I'm not that familiar with RH and I don't know.

Just some ideas. There may be others.

Russ

Votes

Translate

Translate

Report

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
Explorer ,
Aug 20, 2013 Aug 20, 2013

Copy link to clipboard

Copied

Hello Russ,

Thanks for your response.

I do not have the requirement for the CAPTION to be removed after it is copied. Basically, as long as I can get the CAPTIONS above the GRAPHICS in the anchored frame (currently residing in the z_anchor paragraph tag), then the requirement would be satisfied. I will then be able to do the conversion to RH.

I like your idea of using cross-references for RH to use to generate a heading. If that would be possible, then perhaps it might be easier than what I need to do. I'm not entirely sure if that's possible either, but I will definitely look into it while I wait for anyone with some ExtendScript experience  to help out here with ideas or code.

Regards,

Henry

Votes

Translate

Translate

Report

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
Mentor ,
Aug 21, 2013 Aug 21, 2013

Copy link to clipboard

Copied

Hi Henry,

One of the challenges here is that your original idea requires a text operation, where you need to go one place, get the text, then paste it somewhere else. While one might think it should be otherwise, text operations like this are some of the most difficult tasks to figure out with a script. It seems deceptively simple when you are manually using your eyes, a keyboard, and a mouse. However, when it comes to programmatically navigating a document where you can't "see" the whole thing in front of you, the story is much different. I presume your documents are unstructured, which doubles the complexity.

Again, I don't mean to say it can't be done... it surely can and it is just the kind of thing that the scripting engine is provided for. It's just that a newcomer would likely struggle with it and a guru would likely charge for it, given the advanced nature of the task. I would encourage you to keep pursuing about it, though, because once you get a hold of skills like this, your efficiency and marketability will ascend quickly.

Russ

Votes

Translate

Translate

Report

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
Explorer ,
Aug 21, 2013 Aug 21, 2013

Copy link to clipboard

Copied

Hi Russ,

Let me try to shed a bit more light on the requirement.

The CAPTIONs that I have all use a specific paragraph tag, namely, "fig". I want to move the text of those paragraph tags to the bottom of the their previous page. I think locating the destination within the document might be the more difficult than locating the source, which in this case, is all the paragraph tags of "fig".

Thanks,

Henry

Votes

Translate

Translate

Report

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
Mentor ,
Aug 22, 2013 Aug 22, 2013

Copy link to clipboard

Copied

Hi Henry,

I think you are exactly right about that. Is there anything in there to positively identify the destination location? Like, is there some unique paragraph format that applies a page break or something? Or, is there some unique sequence of formats? If there is something unique there, this task may not be quite as complicated as I originally imagined.

Your persistence with this thread has sparked my curiosity. If you could send me a sample file at russ@weststreetconsulting.com, I might be able to take a quick look.

Russ

Votes

Translate

Translate

Report

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
Explorer ,
Aug 22, 2013 Aug 22, 2013

Copy link to clipboard

Copied

Yes, there is a specific sequence of tags.

Each page has these set of tags.

z_anchor (with the graphics)

fig (the caption text)

v_num (the version text)

z_notes (notes heading text)

p0 (the notes text)

I basically need to copy the 'fig' text up to the after the 'p0' from the previous page. Then assign that new 'fig' text a specific paragraph tag (so RH can use it to distinguish where to create a new topic)

Votes

Translate

Translate

Report

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
Mentor ,
Aug 22, 2013 Aug 22, 2013

Copy link to clipboard

Copied

Well, like I said, I'm thinking that it may be less complex than I originally thought. If you can send me a sample file and you are willing to wait a few days, I'll work up some kind of sample code for you. I definitely need a sample file.

Votes

Translate

Translate

Report

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 ,
Aug 22, 2013 Aug 22, 2013

Copy link to clipboard

Copied

While I can't help with the inevitable politics, if you simply move the fig title above the graphic, you can use that as your RH topic title.

Votes

Translate

Translate

Report

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
Explorer ,
Aug 22, 2013 Aug 22, 2013

Copy link to clipboard

Copied

All of documents comes out from a template in that order, and I are unable to change the template for reasons beyond my control.

As far as moving/copy the fig title above the graphics instead of the bottom of the previous page, I think that would work also. We would just need to locate that z_anchor tag that is right above the fig tag.

Russ -- I will be getting a sample created to send to you.

Thanks,

Henry

Votes

Translate

Translate

Report

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
Mentor ,
Aug 30, 2013 Aug 30, 2013

Copy link to clipboard

Copied

LATEST

Lucky for Henry, I became intrigued by this task and had a little time to mess around with a sample script. I'm posting it here for the benefit of the community. Basically, it:

- Goes through the book or document and finds all instances of a "z_anchor" pgf followed by a "fig" pgf followed by a "v_num" pgf followed by a "z_notes" pgf. This test is to make sure we are at a place to do the text operation.

- Retrieves the text from the fig pgf

- Inserts a new pgf above the "z_anchor" pgf, gives it a specified pgf format and adds the text retrieved in the previous step.

It also has a function to clean out all old insertions, by simply deleting all pgfs with that format. In this way, it will only work correctly if the template is using a unique pgf tag for the auto-inserted Robohelp titles.

Hopefully it can be of some use to someone. At the very least, I hope it might inspire others to explore the capabilities of ExtendScript, because while it is tough to figure out, it is a marvelous thing. I have historically been an FDK buff, but I'm really starting to lean in the ES direction for tasks like this. Once you get a hold of it (and despite the clunky ESTK editor), it is much easier than the FDK.

Russ

//Define some tags that appear in the template.

//We do this here for convenience, to make them

//easier to change in the future, if necessary.

var roboHelpFormatTag = "RoboHelpTitle";

var anchoredFrameTag = "z_anchor";

var figCaptionTagPrefix = "fig";

var figNumTag = "v_num";

var notesHeadingTag = "z_notes";

//Get the active book or document object

var file = app.ActiveDoc;

if(!file.ObjectValid())

    file = app.ActiveBook;

   

//If there is no active file, abort.

if(!file.ObjectValid())

{

    alert("No active book or document. Cannot continue.");

}

//Otherwise, let's get started

else InitiateScript(file);

//Everything after this point is a set of functions that drive the

//main features of the script.

//This initation function is the front

//door that  handles overall file iteration,

//reporting, etc. It also allows us

//to confirm with the user at the beginning.

function InitiateScript(file)

{

    //Make sure we really want to do this.

    if(!confirm("Run the RoboHelp title script " +

        "on the following file?\n\n" + file.Name))

            return;

           

    //we'll keep counters of what we do.

    var doc;

    var doInsertions = false;

    var totalCleanups = 0;

    var totalInsertions = 0;

    var totalFilesProcessed = 0;

   

    //If we are processing a book, let's get the first open chapter.

    //Otherwise, we'll process directly on the document object

    //we retrieved earlier.

    //GetNextOpenBookChapter is a custom function defined later.

    if(file.constructor.name == "Book")

        doc = GetNextOpenBookChapter(file, null);

    else doc = file;

    //Confirmation. We allow the option to just remove old

    //titles without new insertions.

    if(confirm("Would you like to do the insertions or " +

        "just the cleanup phase? Click Yes to do both."))

            doInsertions = true;

    //Iterate through our documents for processing.

    while(doc != null && doc.ObjectValid())

    {

        totalFilesProcessed++;

       

        //Clear out the titles currently in the doc

        totalCleanups += ClearOutRoboHelpTitles(doc); 

       

        //...and, do the insertions, if requested       

        if(doInsertions)

            totalInsertions += InsertRoboHelpTitles(doc);

   

        //If we are processing a book, get the next open

        //chapter, otherwise null out the object to end the loop.

        if(file.constructor.name == "Book")

            doc = GetNextOpenBookChapter(file, doc);

        else doc = null;

    }

    //Report what we did.

    alert("Process complete.\n\n" + totalFilesProcessed +

        " file(s) processed.\n" + totalCleanups +

        " previous title(s) deleted.\n" + totalInsertions +

        " new title(s) inserted.");

}

//This is the function that cleans out the

//old titles from a single document.

function ClearOutRoboHelpTitles(doc)

{

    var deletedPgfs = 0;

    var nextPgf;

    var pgf;

    //Get the first paragraph in the main flow

    //to start the iteration.

    pgf = doc.MainFlowInDoc.FirstTextFrameInFlow.FirstPgf;

   

    //Iterate over all paragraphs in the main flow.

    while(pgf.ObjectValid())

    {

        //Before we do anything, we need to

        //get the next paragraph object, so we

        //don't get lost if we end up deleting this one.

        nextPgf = pgf.NextPgfInFlow;

       

        //If the paragraph name (ie, the last assigned

        //format tag) is the title tag, let's delete.

        if(pgf.Name == roboHelpFormatTag)

        {

            pgf.Delete();

            deletedPgfs++;

        }

    

        //Reassign our main loop variable

        //and continue iteration.

        pgf = nextPgf;

    }

    //Return the total count of deleted pgfs.

    return deletedPgfs;

}

//This is the function that inserts new titles

//for a single document.

function InsertRoboHelpTitles(doc)

{

    var totalInsertions = 0;

    var pgf;

   

    //Get the first paragraph in the main flow and begin iteration.

    pgf = doc .MainFlowInDoc.FirstTextFrameInFlow.FirstPgf;

    while(pgf.ObjectValid())

    {

        //Check to see if we are at a place where a title requires

        //insertion. We'll use a custom function for this, defined

        //later.

        if(CheckIfInsertionIsRequired(doc, pgf))

        {

            //If we get here, an insertion is required. Let's use this custom

            //function to do it.

            InsertPgf(doc, roboHelpFormatTag,

               pgf.PrevPgfInFlow,

               GetPgfText(pgf.NextPgfInFlow));

          

           totalInsertions++;

        }

   

        //get the next paragraph to continue the loop. 

        pgf = pgf.NextPgfInFlow;       

    }

    return totalInsertions;      

}   

//This is the function that checks whether

//we are at an anchor paragraph tag, for which

//a title insertion is required before it.

//It basically checks a sequence of

//tags, which if they all match, we

//assume that it is a place for insertion.

function CheckIfInsertionIsRequired(doc, pgf)

{

    var returnVal = false;

   

    //Check if we are at the right anchored frame tag

    if(pgf.Name == anchoredFrameTag)

    {

        //If so, see if the next paragraph is a figure caption

        pgf = pgf.NextPgfInFlow;

        if(pgf.ObjectValid() && pgf.Name.indexOf(figCaptionTagPrefix) == 0)

        {

            //If so, see if the next paragraph is a figure number

            pgf = pgf.NextPgfInFlow;

            if(pgf.ObjectValid() && pgf.Name == figNumTag)

            {

                //If so, see if the next paragraph is a notes heading

                pgf = pgf.NextPgfInFlow;

                if(pgf.ObjectValid() && pgf.Name == notesHeadingTag)

                {

                    //if so, we have a winner!

                    returnVal = true;

                } //end notes heading tag if

            } //end fig num tag if

        } //end fig caption tag if

    } //end anchored frame tag if

    return returnVal;

}   

//A general purpose paragraph inserter. It will insert

//the new paragraph after the reference paragraph,

//using the format and text provided.

function InsertPgf(doc, formatName, referencePgf, newText)

{

    var newPgf;

    var everythingWorkedRight = false;

   

    //Insert the new paragraph.   

    if(referencePgf.ObjectValid())

        newPgf = doc.NewSeriesObject (Constants.FO_Pgf, referencePgf);

    else

        newPgf = doc.NewSeriesObject (Constants.FO_Pgf, 0);

   

    //If it inserted OK, set the text and the format;

    if(newPgf.ObjectValid())

    {

        //Here is how to add text

        var tr = new TextRange();

        tr.beg.obj = tr.end.obj = newPgf;

        tr.beg.offset = 0;

        doc.AddText(tr.beg, newText);

       

        //Reset the text range offset to the end of the

        //newly-inserted text to prepare for formatting.

        tr.end.offset = Constants.FV_OBJ_END_OFFSET;

       

        //Get the format tag object, then apply if it exists in the template

        var formatObj =

            doc.GetNamedObject(Constants.FO_PgfFmt, formatName);

        if(formatObj.ObjectValid())

        {

            //This is how you apply a paragraph format, by

            //copying the properties from the format object

            //to the paragraph object.

            var props = formatObj.GetProps();

            newPgf.SetProps(props);

           

            everythingWorkedRight = true;

        }

    }

    return everythingWorkedRight;

}   

//A simple function to retrieve the text of

//a paragraph.

function GetPgfText(pgf)

{

    var text = "";

   

    if(!pgf.ObjectValid()) return text;

   

    var ti = pgf.GetText(Constants.FTI_String);

   

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

        text += ti.sdata;

       

    return text;

}   

//A function to allow stepping through a book, chapter

//by chapter. Send it the document you are currently at

//and it will send back the next one that is open. It will

//not open any closed files.

//Send null for currentDoc to get the first open chapter.

function GetNextOpenBookChapter(book, currentDoc)

{

    var comp;

    var returnDoc = null;

    var foundReference = false;

   

    //We will loop through all chapters (components).

    //However, we don't really want to consider any

    //until we've found the one we are currently at,

    //which is the doc sent here. So, the loop will operate

    //on this flag to know when to start considering

    //components. In the case where we want the first

    //open chapter, we can automatically assume that

    //any component is a valid candidate.

    if(currentDoc == null) foundReference = true;

   

    //get the first component in the book.

    comp = book.FirstComponentInBook;

   

    //Iterate through the components.

    while(comp != null && comp.ObjectValid())

    {

        //If we find that we have reached the reference point;

        //that is, the current document, we can reset

        //this flag to start considering components

        //for return.

        if(!foundReference && comp.Name == currentDoc.Name)

            foundReference = true;

       

        //Otherwise, if we are already considering components,

        //let's see if there is an open document for this one.

        else if(foundReference)

            returnDoc = DocIsOpen(comp.Name);

           

        //If we got ourselves the next chapter, we are done. Null

        //out the loop variable to end iteration.

        if(returnDoc != null)

            comp = null;

           

        //otherwise, get the next component and keep going.

        else comp = comp.NextBookComponentInDFSOrder;

    }

     //Send back whatever we got, if anything.

     return returnDoc;

}

//A simple function to find an open document

//based on its fully-qualified path.

function DocIsOpen(path)

{

    var tempDoc;

    var returnDoc = null;

   

    //If our path is empty, no sense continuing.

    if(path == "") return returnDoc;

   

    //get the first open document in the session.

    tempDoc = app.FirstOpenDoc;

   

    //Loop through all open documents

    //until we find the one we want or we just

    //run out.

    while(tempDoc.ObjectValid())

    {

        if(tempDoc.Name == path)

        {

            //if we found it, set our return

            //variable and break out of the loop.

            returnDoc = tempDoc;

            break;

        }

        tempDoc = tempDoc.NextOpenDocInSession;

    }

   

    //send back whatever we got, if anything.

    return returnDoc;

Votes

Translate

Translate

Report

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