Reputation: 743
We have a list of Framemaker document files (*.fm files) coming into a folder. We need to pick up these files and convert to xml format (same as the saveAs opertion from the File menu).
I have written the follwing function to Save fm files to xml
Code to Save fm files to xml files
function saveAsXml (doc) {
// Get required parameters for the save function.
var params = GetSaveDefaultParams();
var returnParamsp = new PropVals();
// Replace the .fm extension with .mif.
var saveName = doc.Name.replace (/\.[^\.\\]+$/,".xml");
var i = GetPropIndex(params, Constants.FS_FileType);
params[i].propVal.ival = Constants.FV_SaveFmtXml;
// Save the document as XML.
doc.Save(saveName, params, returnParamsp);
}
How to automate this process so that code checks -
Thanks
Upvotes: 0
Views: 2364
Reputation: 121
I needed to save a set of FrameMaker .book files as .xml files. To do this, I wrote a perl script that runs under Windows 10 WSL (Windows Subsystem for Linux). It takes two arguments: a .book file filename, and an XML output directory name:
fm2xml.pl /foo/bar/mybook.book ./xmldir/mybook/
This perl script writes out a temporary ExtendScript file into the Adobe Scripts directory in My Documents (to avoid security dialog boxes), then runs it, then prints the console output from the XML save. You'll need the ExtendScript Toolkit installed as well as FrameMaker.
Here's the fm2xml.pl
script:
#!/usr/bin/perl
use strict;
use warnings;
use File::Basename;
use File::Path qw(make_path remove_tree);
my $book_file = shift;
my $bookname = (basename($book_file));
$bookname =~ s!\.book$!!;
my $xml_dir = (shift or "./${bookname}_xml");
my $xml_dir_temp = $xml_dir.'_TEMP';
my $xml_file_temp = $xml_dir_temp."/${bookname}.xml";
my $maker_ini = <<'EOS';
[Frame]
ProductInterface=Structured FrameMaker
[Preferences]
ThorMessageShown=On
UnavailableFontsDialog=Off
EOS
`echo "$maker_ini" > /mnt/c/Users/$ENV{USER}/AppData/Roaming/Adobe/FrameMaker/14/maker.ini`;
my $jsx_script = <<'EOS';
#target framemaker-14.0
var books = [
"BOOK_FILE",
];
main();
function main() {
var count = books.length;
for (i = 0; i < count; i+= 1) {
Console("Converting " + books[i]);
convertBook(books[i]);
}
// alert("Finished");
// app.Close(Constants.FF_CLOSE_MODIFIED);
}
function convertBook (bookname) {
Console(" Opening book " + bookname);
SimpleOpen(bookname);
var book = app.ActiveBook;
Console(" Opened book " + book.Name);
fixLinks(book);
Console(" Saving as XML...");
var xmlName = saveAsXml (book);
Console(" Closing everything in " + book.Name);
CloseAll();
}
function fixLinks (book) {
var comp = book.FirstComponentInBook;
var docs = [];
while(comp.ObjectValid()) {
Console(" Opening doc " + comp.Name);
var doc = OpenFile(comp.Name);
Console(" Opened doc " + doc.Name);
docs.push(doc);
comp = comp.NextBookComponentInDFSOrder;
}
Console(" Fixing links...");
CallClient("XRefWizard", "SetIDs---" + book.Name + "---DoReporting");
Console(" Fixed links...");
}
function saveAsXml (doc) {
// Get required parameters for the save function.
var params = GetSaveDefaultParams();
var returnParamsp = new PropVals();
// Replace the .fm extension with .xml
var saveName = "XML_FILE";
Console(" Saving to '" + saveName + "'...");
var i = GetPropIndex(params, Constants.FS_FileType);
params[i].propVal.ival = Constants.FV_SaveFmtXml;
// Save the document as XML.
doc.Save(saveName, params, returnParamsp);
}
function OpenFile(path) {
props = new PropVals();
props = GetOpenDefaultParams();
props[GetPropIndex(props, Constants.FS_AlertUserAboutFailure)].propVal.ival = false;
props[GetPropIndex(props, Constants.FS_FileIsInUse)].propVal.ival = Constants.FV_ResetLockAndContinue;
props[GetPropIndex(props, Constants.FS_BookIsInUse)].propVal.ival = Constants.FV_ResetLockAndContinue;
props[GetPropIndex(props, Constants.FS_LockCantBeReset)].propVal.ival = Constants.FV_DoOK;
props[GetPropIndex(props, Constants.FS_FileIsOldVersion)].propVal.ival = Constants.FV_DoOK;
props[GetPropIndex(props, Constants.FS_FontChangedMetric)].propVal.ival = Constants.FV_DoOK;
props[GetPropIndex(props, Constants.FS_FontNotFoundInCatalog)].propVal.ival = Constants.FV_DoOK;
props[GetPropIndex(props, Constants.FS_FontNotFoundInDoc)].propVal.ival = Constants.FV_DoOK;
props[GetPropIndex(props, Constants.FS_LanguageNotAvailable)].propVal.ival = Constants.FV_DoOK;
props[GetPropIndex(props, Constants.FS_UpdateXRefs)].propVal.ival = Constants.FV_DoNo;
props[GetPropIndex(props, Constants.FS_UseRecoverFile)].propVal.ival = Constants.FV_DoNo;
props[GetPropIndex(props, Constants.FS_UseAutoSaveFile)].propVal.ival = Constants.FV_DoNo;
props[GetPropIndex(props, Constants.FS_OpenFileNotWritable)].propVal.ival = Constants.FV_DoOK;
returnp = new PropVals();
var file = Open(path, props, returnp);
return file;
}
function CloseAll()
{
doc=app.FirstOpenDoc
while(doc.id !=0)
{
doc2=doc.NextOpenDocInSession;
Console(" Closing doc " + doc.Name + "...");
doc.Close(Constants.FF_CLOSE_MODIFIED);
doc = doc2;
}
book=app.FirstOpenBook
while(book.id !=0)
{
book2=book.NextOpenBookInSession;
Console(" Closing book " + book.Name + "...");
book.Close(Constants.FF_CLOSE_MODIFIED);
book=book2
}
}
EOS
print "Processing book '$book_file'...\n";
`echo -n '' > /mnt/c/users/$ENV{USER}/AppData/Roaming/Adobe/FrameMaker/14/consfile.txt`; # empty out the log file
my $jsx_file = "/mnt/c/Users/$ENV{USER}/Documents/Adobe Scripts/fm_to_xml.jsx"; # script must be here to avoid Adobe security warnings
remove_tree($xml_dir, $xml_dir_temp);
make_path($xml_dir_temp);
chomp (my $book_file_win = `wslpath -a -w '$book_file'`);
chomp (my $xml_file_win = `wslpath -a -w '$xml_file_temp'`);
chomp (my $jsx_file_win = `wslpath -a -w '$jsx_file'`);
$book_file_win =~ s!\\!\\\\!g;
$xml_file_win =~ s!\\!\\\\!g;
$jsx_script =~ s!BOOK_FILE!$book_file_win!;
$jsx_script =~ s!XML_FILE!$xml_file_win!;
open(JSX_FILE, '>', "${jsx_file}") or die "Can't open '${jsx_file}': $!";
print JSX_FILE $jsx_script;
close JSX_FILE;
my $book_dir = dirname($book_file);
`find '$book_dir' -name '*.lck' -print0 | xargs -0r rm`; # try to remove lingering lock files
my $cmd = "\"/mnt/c/Program\ Files\ \(x86\)/Adobe/Adobe\ ExtendScript\ Toolkit\ CC/ExtendScript\ Toolkit.exe\" -run '$jsx_file_win'";
print "Running:\n $cmd\n";
`$cmd`;
my $start_time = time();
while (!-e "${book_file}.lck" && (time() - $start_time) < 10) {`sleep 2`;}
print "Waiting for book lock file to disappear...\n";
while (-e "${book_file}.lck") {`sleep 2`;}
print `cat /mnt/c/users/chrispy/AppData/Roaming/Adobe/FrameMaker/14/consfile.txt | egrep -v '(is not available|will be used in this session)'`; # strip out missing font messages
if (-e $xml_file_temp) {
`sed -i 's!\\]\\]>!]]\\>!' $xml_dir_temp/*.e*`; # ']]>' must be escaped in XML, but FrameMaker doesn't
print "\n*** XML write for '$bookname' succeeded.\n\n";
rename($xml_dir_temp, $xml_dir);
} else {
print "\n*** XML write for '$bookname' FAILED.\n\n";
remove_tree($xml_dir_temp);
}
Caveat emptor: This whole approach is hacky. The "error-checking" is a ten-second timeout after which it throws up its hands if it sees no signs of life from FrameMaker.
Note that in my case, I use Russ Ward's excellent XRef Wizard plugin to fix duplicate IDs that mess up XML output. If you have this plugin, uncomment the fixLinks(book)
line above.
If you're running a different version than FrameMaker 2017, you'll need to change the #target comment in the embedded JSX script.
If you're a glutton for punishment, you can also try the Makefile I used to automate this:
FM2XML = $(shell which fm2xml.pl)
fm_book_files := \
some/dir/book1.book \
/yet/another/book2.book
book_names := $(patsubst %.book, %, $(notdir $(fm_book_files)))
xml_dirs := $(foreach b, $(book_names), ./xml/$b)
dirs := $(dir ${fm_book_files})
vpath %.book $(dir $(fm_book_files))
all: $(xml_dirs)
print-%: ; @echo $* = $($*)
.SECONDEXPANSION:
$(xml_dirs): ./xml/%: %.book
${FM2XML} ${<} ${@}
But if you hold your breath and don't move, it all works. :)
Upvotes: 0
Reputation: 519
If you are on windows, you can write a simple batch file to run your extendscript in a loop via the batch file (.bat) from command line.
Upvotes: 0