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

Jump to orphaned target marks (MText) via scripting

Guest
Oct 11, 2023 Oct 11, 2023

Copy link to clipboard

Copied

Hi,

I am currently working on a script for FrameMaker which should directly jump to the orphaned target markers (MText) inside the book. To identify them in the first place, I have already written a code where I collect all cross-references (mainly the ID numbers), both source markers (XRef) and target markers, to match them. This works for me so far. However, I am a bit stuck with the part where the according book components of the orphaned target markers should be directly accessed. I was thinking of running through the entire book again while trying to match with the ID numbers of the target markers but since I collected them all in an array, this does not work properly. Maybe my approach is not that elegant.

main();

function main()
{
    var numXRefTextPaths = []
    var numMTextPaths = []

    var book = app.ActiveBook;
    
    if(!book.ObjectValid()) 
    {
        book = app.FirstOpenBook;
    }

    var comp = book.FirstComponentInBook;
        
    while(comp.ObjectValid())
    {
        comp.ComponentIsSelected = true;

        var compType = comp.ComponentType;
            
        if(compType == Constants.FV_BK_FILE)
        {                        
            var doc = OpenFile(comp.Name);

            if(doc.ObjectValid() == true) 
            {
                crXRefText = doc.FirstXRefInDoc;
                while(crXRefText.ObjectValid())
                {
                    var longXRefText = crXRefText.XRefSrcText.toString();
                    var id_number = longXRefText.substr(0, 5);
                    //alert("CR-XRef: " + id_number);
                    numXRefTextPaths.push(id_number);
                    
                    crXRefText = crXRefText.NextXRefInDoc;
                }
            
                crMText = doc.FirstMarkerInDoc;
                while(crMText.ObjectValid())
                {  
                    if (crMText.MarkerText.match(/^[0-9]{5}/)) 
                    {
                        var longMText = crMText.MarkerText.toString();
                        var cr_number = longMText.substr(0, 5);
                        //alert("CR-MText: " + cr_number);
                        numMTextPaths.push(cr_number);
                    } 
                    crMText = crMText.NextMarkerInDoc;
                }
            }

            else
            {
                alert("Book cannot be found.\nRun the script again.");
                return;
            }
        }
        comp.ComponentIsSelected = false;

        comp = comp.NextBookComponentInDFSOrder;
    }

    alert("Following cross-references were found:\n\n" 
    + "\n\nXRefs ID: " + numXRefTextPaths
    + "\n\nMText ID: "+ numMTextPaths
    );

    function getOne(arr1, arr2) 
    {
    var uniqueOne = [];
    var matches = false;

        for ( var i = 0; i < arr1.length; i++ ) 
        {
            matches = false;
            for ( var j = 0; j < arr2.length; j++ ) 
            {
                if (arr1[i] === arr2[j]) matches = true;
            }
            if(!matches) uniqueOne.push(arr1[i]);
        }
        return uniqueOne;
    }

    alert("Orphaned target marks: " + getOne(numMTextPaths, numXRefTextPaths));
    
    
    var comp = book.FirstComponentInBook;
        
    while(comp.ObjectValid())
    {                
        comp.ComponentIsSelected = true;
        
        var compType = comp.ComponentType;
            
        if(compType == Constants.FV_BK_FILE)
        {                        
            var doc = OpenFile(comp.Name);

            if(doc.ObjectValid() == true) 
            {
                var test = getOne(numMTextPaths, numXRefTextPaths);
                
                jumpMText = doc.FirstMarkerInDoc;
                
                while(jumpMText.ObjectValid())
                {  
                    if (jumpMText.MarkerText.match(test)) 
                    {
                        alert("Orphaned target marks: " + jumpMText.MarkerText);
                    }
                    jumpMText = jumpMText.NextMarkerInDoc;
                }
            }

            else
            {
                alert("Book cannot be found.\nRun the script again.");
                return;
            }
        }
        comp.ComponentIsSelected = false;

        comp = comp.NextBookComponentInDFSOrder;
    }
}

Thank you in advance.

Views

432

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
Adobe Employee ,
Oct 11, 2023 Oct 11, 2023

Copy link to clipboard

Copied

Three suggestions:

  1. Instead of using two separate arrays for numXRefTextPaths and numMTextPaths, you can use an array of objects for the markers. This way, you can store both the ID and the marker object itself, which will make it easier to jump to the orphaned markers later.
  2. Use the indexOf method to check if a marker ID exists in the cross-references array.
  3. There's a potential issue. The variable doc is defined inside the loop where you're iterating through the book components. However, when you're trying to jump to the orphanedMarker later on, the doc variable might not be pointing to the correct document where the orphanedMarker is located. I suggest to store the document reference along with each marker.

 

 

main();

function main() {
    var xRefIDs = [];
    var mTextMarkers = [];

    var book = app.ActiveBook;

    if (!book.ObjectValid()) {
        book = app.FirstOpenBook;
    }

    var comp = book.FirstComponentInBook;

    while (comp.ObjectValid()) {
        comp.ComponentIsSelected = true;

        if (comp.ComponentType == Constants.FV_BK_FILE) {
            var doc = OpenFile(comp.Name);

            if (doc.ObjectValid() == true) {
                var crXRefText = doc.FirstXRefInDoc;
                while (crXRefText.ObjectValid()) {
                    var id_number = crXRefText.XRefSrcText.substr(0, 5);
                    xRefIDs.push(id_number);
                    crXRefText = crXRefText.NextXRefInDoc;
                }

                var crMText = doc.FirstMarkerInDoc;
                while (crMText.ObjectValid()) {
                    if (crMText.MarkerText.match(/^[0-9]{5}/)) {
                        var cr_number = crMText.MarkerText.substr(0, 5);
                        mTextMarkers.push({
                            id: cr_number,
                            marker: crMText,
                            document: doc // Store the document reference
                        });
                    }
                    crMText = crMText.NextMarkerInDoc;
                }
            } else {
                alert("Book cannot be found.\nRun the script again.");
                return;
            }
        }
        comp.ComponentIsSelected = false;
        comp = comp.NextBookComponentInDFSOrder;
    }

    // Generate lists of IDs for the alert
    var xRefIDList = xRefIDs.join(", ");
    var mTextIDList = mTextMarkers.map(function(obj) { return obj.id; }).join(", ");

    alert("Following cross-references were found:\n\n" 
        + "\n\nXRefs ID: " + xRefIDList
        + "\n\nMText ID: " + mTextIDList
    );

    // Identify and jump to orphaned markers
    for (var i = 0; i < mTextMarkers.length; i++) {
        if (xRefIDs.indexOf(mTextMarkers[i].id) === -1) {
            var orphanedMarker = mTextMarkers[i].marker;
            alert("Orphaned target mark: " + orphanedMarker.MarkerText);

            // Jump to the orphanedMarker
            var orphanedDoc = mTextMarkers[i].document;
            var textLoc = orphanedMarker.TextLoc;
            var textSel = new TextSelection();
            textSel.beg = textLoc;
            textSel.end = textLoc;
            orphanedDoc.TextSelection = textSel;
            orphanedDoc.ScrollToText(textSel); // This will ensure the marker is visible in the FrameMaker window

            // If you want to pause after each jump (e.g., to allow the user to review the marker), 
            // you can introduce a confirmation dialog here, like:
            if (!confirm("Found an orphaned marker. Continue searching?")) {
                break; // This will exit the loop if the user clicks "Cancel"
            }
        }
    }

}

 

 

This approach should be more efficient in ExtendScript and should allow you to identify and jump to orphaned markers in Adobe FrameMaker.

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
Adobe Employee ,
Oct 11, 2023 Oct 11, 2023

Copy link to clipboard

Copied

I have edited my reply above to cover the doc var problem and also added code to jump to the orphaned marker.

Please try and let me know if it works.

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
Guest
Oct 16, 2023 Oct 16, 2023

Copy link to clipboard

Copied

Thank you so much!

Unfortunately, the script stops running at the part "mTextMarkers.map(function(obj) { return obj.id; }).join(", ");" since this part is marked in red on my editor. I have no idea why.

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
Guest
Oct 16, 2023 Oct 16, 2023

Copy link to clipboard

Copied

Maybe I should mention that I am using Adobe ExtendScript Toolkit CC.

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
Guest
Oct 18, 2023 Oct 18, 2023

Copy link to clipboard

Copied

Okay, so I figured out why the script always stops at line "mTextMarkers.map(function(obj) { return obj.id; }).join(", ");" - the map(method is not a standard function in ECMAScript. I came up with a workaround, so this should be fine even if it is not the most elegant approach, as long as it works...

But now I have another problem: The script stops now at line "var textSel = new TextSelection();" and I feel like this is a very tricky one since this syntax should be actually supported by ExtendScript when scripting for FrameMaker. The code looks like this so far:

 

main();

function main() 
{
    var xRefIDs = [];
    var mTextMarkers = [];

    var book = app.ActiveBook;

    if (!book.ObjectValid()) 
    {
        book = app.FirstOpenBook;
    }

    var comp = book.FirstComponentInBook;

    while (comp.ObjectValid()) {
        comp.ComponentIsSelected = true;

        if (comp.ComponentType == Constants.FV_BK_FILE) {
            var doc = OpenFile(comp.Name);

            if (doc.ObjectValid() == true) {
                var crXRefText = doc.FirstXRefInDoc;
                while (crXRefText.ObjectValid()) {
                    var id_number = crXRefText.XRefSrcText.substr(0, 5);
                    xRefIDs.push(id_number);
                    crXRefText = crXRefText.NextXRefInDoc;
                }

                var crMText = doc.FirstMarkerInDoc;
                while (crMText.ObjectValid()) {
                    if (crMText.MarkerText.match(/^[0-9]{5}/)) {
                        var cr_number = crMText.MarkerText.substr(0, 5);
                        mTextMarkers.push({
                            id: cr_number,
                            marker: crMText,
                            document: doc // Store the document reference
                        });
                    }
                    crMText = crMText.NextMarkerInDoc;
                }
            } else {
                alert("Book cannot be found.\nRun the script again.");
                return;
            }
        }
        comp.ComponentIsSelected = false;
        comp = comp.NextBookComponentInDFSOrder;
    }

    // Generate lists of IDs for the alert
    var xRefIDList = "";
    var mTextIDList = "";

    for (var i = 0; i < xRefIDs.length; i++) {
      xRefIDList = xRefIDList + xRefIDs[i].toString() + ", ";
    }

    for (var j = 0; j < mTextMarkers.length; j++) {
      var obj = mTextMarkers[j];
      mTextIDList = mTextIDList + obj.id.toString() + ", ";
    }

    alert("Following cross-references were found:\n\n" 
        + "\n\nXRefs ID: " + xRefIDList
        + "\n\nMText ID: " + mTextIDList
    );

    // Identify and jump to orphaned markers
    for (var i = 0; i < mTextMarkers.length; i++) 
    {
        if (xRefIDs.indexOf(mTextMarkers[i].id) === -1) 
        {
            var orphanedMarker = mTextMarkers[i].marker;
            alert("Orphaned target mark: " + orphanedMarker.MarkerText);

             // Jump to the orphanedMarker
            var orphanedDoc = mTextMarkers[i].document;
            var textLoc = orphanedMarker.TextLoc;
            var textSel = new TextSelection();
            textSel.beg = textLoc;
            textSel.end = textLoc;
            orphanedDoc.TextSelection = textSel;
            orphanedDoc.ScrollToText(textSel); // This will ensure the marker is visible in the FrameMaker window

            // If you want to pause after each jump (e.g., to allow the user to review the marker), 
            // you can introduce a confirmation dialog here, like:
            if (!confirm("Found an orphaned marker. Continue searching?")) 
            {
                break; // This will exit the loop if the user clicks "Cancel"
            }
        }
    }
}

 

 

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 ,
Oct 18, 2023 Oct 18, 2023

Copy link to clipboard

Copied

TextSelection is a document property and is a TextRange. What you need is this:

var textSel = new TextRange();

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
Guest
Oct 31, 2023 Oct 31, 2023

Copy link to clipboard

Copied

LATEST

Thank you so much! Now it works.

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 ,
Oct 11, 2023 Oct 11, 2023

Copy link to clipboard

Copied

Hi, I want to make sure I understand the overall task. Are you trying to find Cross-Ref markers that don't have any XRef pointing to them? That is one of the purposes of my CrossrefReporter script. In that script, and others, I collect my data in a simple XML object instead of using arrays of objects. I can then save the XML object to a file for a later pass through the data. The key advantage is that I can open the XML file and see the data that I have collected, which makes development and troubleshooting much easier. Here is an example of data that is collected with my CrossrefReporter script:

image.pngexpand image

 

Also, you have your code in a single main function, which makes it difficult to troubleshoot. Each task should be in its own function; this makes it easier to test and troubleshoot each part of the process. And small, general purpose functions can be reused in other scripts. Here is a list of functions in my CrossrefReporter script:

image.pngexpand image

This script does a lot more that you are trying to do so you would have less functions, but I want you to see how granularly I make my scripts.

 

I realize that I haven't given you specific help for your issue, but I may record some YouTube videos that illustrate these scripting principles. Can you confirm the overall purpose of your script? It would make a good example for instruction. Thank you.

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 ,
Oct 11, 2023 Oct 11, 2023

Copy link to clipboard

Copied

Here is a playlist that shows how to use XML objects and files to store FrameMaker data with ExtendScript. These are unrehearsed and unedited, but should be helpful for your script and others. I will add a couple more videos in the near future.

 

https://www.youtube.com/playlist?list=PLRPNZaAC4LoSvyfl6S5PJ0euqFSi4NDCR

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 ,
Oct 12, 2023 Oct 12, 2023

Copy link to clipboard

Copied

Hi Rick, very helpful! I had heard of that you can store data in an XML file, but I had not known how to do this. Your videos give a very good start! Thank you very much!

Only one question: Can you post the script, which you develop in your videos, somewhere? There are a few parts which I might re-use.

Best regards, Winfried

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 ,
Oct 14, 2023 Oct 14, 2023

Copy link to clipboard

Copied

I completed this playlist by adding 3 more videos. Questions and comments are welcome.

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
Guest
Oct 16, 2023 Oct 16, 2023

Copy link to clipboard

Copied

Yes, this was exactly my question. Your approach of saving the XML object to a file sounds very interesting but I think I will stick with the arrays. However, I briefly looked into your videos and I will definitively keep them in my mind for the future. Thank you!

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