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

ExtendScript to loop through directory and save fm files as pdf

Community Beginner ,
Dec 27, 2025 Dec 27, 2025

I have 600+ fm files that I want to save as a pdf. They are all in the same directory. This is the script I started with (though I currently have Frankencode doing other stuff that was added between me and things suggested by Claudet/Gemini/etc).

#target framemaker

(function () {

    // --- CONFIG ---
    var PDF_SETTINGS_NAME = ""; 
    // Leave empty "" to use the document's default PDF settings
    // Or put the name of a PDF setup, e.g. "High Quality Print"

    // ----------------

    function log(msg) {
        $.writeln(msg);
        var oFile = new File ("C:/Catalog/output/run.log" );
        oFile.open("a");
        oFile.writeln(msg);
        oFile.close();
    }

    function saveAsPdf(doc, savePath) {
        // Get default save parameters
        var params = GetSaveDefaultParams();
        var i;

        // Set the file type to PDF
        i = GetPropIndex(params, Constants.FS_FileType);
        params[i].propVal.ival = Constants.FV_SaveFmtPdf;

        // Optional: Configure other PDF settings if needed
        // Example: Set to not use Distiller (rely on modern internal PDF generation)
        i = GetPropIndex(params, Constants.FS_PDFUseDistiller);
        if (i > -1) {
            params[i].propVal.ival = 0; // 0 for false, 1 for true
        }

        // Call the Save function
        // FA_errno is a global variable that will contain the error code after the call
        FA_errno = 0;
        var returnParams = new PropVals();
        
        // Check if the input is a book or a document to use the correct function
        if (doc.Type === Constants.FA_Book) {
            doc.Save(savePath, params, returnParams);
        } else {
            // For a single document, use F_ApiSave (often accessed via the document object in modern ExtendScript)
            // Note: Direct doc.Save(path, params, returnParams) works for documents too in many cases.
            // The Book object has a specific method signature in some versions.
            doc.Save(savePath, params, returnParams);
        }
        
        if (FA_errno !== 0) {
            log("Failed to save PDF. Error code: " + FA_errno);
        } else {
            log("PDF saved successfully to: " + savePath);
        }
    }

    var runFile = new File("C:/Catalog/output/run.go");
    if (!runFile.exists) {
        $.writeln("run.go file does not exist. Exiting.");
        return;
    }

    var folder = new Folder("C:/Catalog/output");

    var files = folder.getFiles(function (f) {
        return f instanceof File && /\.fm$/i.test(f.name);
    });

    if (files.length === 0) {
        log("No .fm files found.");
        app.Close(Constants.FF_CLOSE_MODIFIED);
    }

    log("Found " + files.length + " file(s).");

    for (var i = 0; i < files.length; i++) {
        var file = files[i];
        log("Processing: " + file.name);

        var doc = null;

        try {
            doc = SimpleOpen(file.fsName, false);
            

            var pdfPath = file.fsName.replace(/\.fm$/i, ".pdf");

            if (saveAsPdf(doc, pdfPath)) {
                log("  PDF created: " + pdfPath);
            }

        } catch (e) {
            log("  ERROR opening file: " + e);
        } finally {
            if (doc) {
                try {
                    // Close without saving changes
                    doc.Close(Constants.FF_CLOSE_MODIFIED);
                    
                } catch (closeErr) {
                    log("  ERROR closing file: " + closeErr);
                }
            }
        }
    }

    log("Batch PDF export complete.");

    app.Close(Constants.FF_CLOSE_MODIFIED);

})();

 

I get timeout errors at random places. It will handle anywhere between 2-7 files, then give me a timeout error. I have the script in the startup folder so that I can place a file in the output folder, open FM, and then let it go to work. I'd also like to move the fm files to an archive folder once it's done.

 

Can someone give me a push in the right direction? I also don't really know if the SaveAsPdf function is what it should be, I'd be surprised if it was.

TOPICS
Scripting
210
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 Beginner ,
Dec 29, 2025 Dec 29, 2025

After looking at more posts on here, I was able to cobble something together that works. 

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 ,
Dec 29, 2025 Dec 29, 2025

Would you care to post your solution here to help other beginner scripters?

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 ,
Dec 29, 2025 Dec 29, 2025

Here is a script that works on a book, but you could modify it to work on a folder. Or, you could put all of the folder's FM files into a temporary book. I originally wrote it for a client, and thus the AC_SCP namespace.

#target framemaker

var AC_SCP = AC_SCP || {}; // Save each component to PDF.
AC_SCP.version = "Version 0.1b January 11, 2022";
AC_SCP.file = $.fileName;

AC_SCP.main = function (book) {
	
	var doc;
	
	if ((book) && (book.ObjectValid () === 1)) {
		AC_SCP.processBook (book);
	}
	else {
		book = app.ActiveBook;
		if (book.ObjectValid () === 1) {
			AC_SCP.processBook (book);
		}
	}
};

AC_SCP.saveDocAsPdf = function (doc, name) {	
    
    var params, returnParams, i;
    
    if (!name) {
        name = doc.Name.replace (/[^\.]+$/, "pdf");
    }
        
    params = GetSaveDefaultParams();
    returnParams = new PropVals();
    
    i = GetPropIndex (params, Constants.FS_FileType);
    params[i].propVal.ival = Constants.FV_SaveFmtPdf;
	i = GetPropIndex (params, Constants.FS_PDFUseDistiller);
    params[i].propVal.ival = 0;

    FA_errno = 0;
    doc.Save (name, params, returnParams);
	if (FA_errno !== 0) {
		PrintSaveStatus (returnParams);
	}
    return FA_errno;
};

AC_SCP.processBook = function (book) {
	
    var bookComp, file;
    
    // Loop through all of the components in the book.
    bookComp = book.FirstComponentInBook;
    while (bookComp.ObjectValid ()) {
        if (bookComp.ComponentType === 512) {
            file = new File (bookComp.Name);
			// Get the document returned in a JavaScript object.
			doc = CP.getDocument (bookComp.Name, undefined, true);
			if ((doc) && (doc.ObjectValid () === 1)) {
				book.StatusLine = "Processing " + file.name;
				AC_SCP.saveDocAsPdf (doc);
				// If the document was opened by the script, close it.
				if (doc.openedByScript === true) {
					doc.Close (true);
				}
			}
        }
        bookComp = bookComp.NextBookComponentInDFSOrder;
    }
    // Reset the book status line.
    book.StatusLine = "";
};

 

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 ,
Dec 29, 2025 Dec 29, 2025

I just realized that the script refers to a function in another script (CP.getDocument). You should be able to use the code from your original script to open each document.

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 ,
Jan 07, 2026 Jan 07, 2026
LATEST

Hi,

When you want to automate publishing, it could be that this is not allowed with a regular FrameMaker licence. You would need FrameMaker Publishing Server.

https://www.adobe.com/products/framemaker/publishing-server.html

Best regards, Winfried

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