Script Help/Question

Explorer ,
Aug 30, 2021 Aug 30, 2021

Copy link to clipboard

Copied

I'm trying to teach myself to script. I have a basic understanding of programming syntax and a passing familiarity with basic JavaScript, but I'm pretty much learning on the go.

 

My goal is to write a script that steps through each table in a document and modifies various settings of each. The first part of the script is borrowed directly from the second example script of the Adobe FrameMaker Scripting Guide (Ch 3, p. 8). The Tbl properties I took from the same doc (Ch 5, p. 523).

 

When I run the script in the ExtendScript Toolkit, the script executes without error, but none of the settings are applied in my open Frame document (i.e., all of the tables appear as they did before I ran the script).

 

I'm not sure why it's not working. The documentation available (PDFs and Object Model Viewer) are pretty opaque, and there's very little in the way of explanatory information to help me connect the pieces. I'm guessing I'm out of my depth, but I'd really like to gain proficiency; I'm just not sure how/where to start in general, or how to evaluate deficits in this current project in particular.

 

I'd very much appreciate any help this fantastic community can offer. Thank you for your time and patience!

---

var doc = app.ActiveDoc;

if(doc.ObjectValid() == true) {
var flow = doc.MainFlowInDoc;
var tbl = 0;
var textItems = flow.GetText(Constants.FTI_TblAnchor);
for (var i = 0; i < textItems.len; i += 1) {
tbl = textItems[i].obj;
tbl.ContentHeight = 2;
tbl.OrphanRows = 2;
tbl.TblCellBottomMargin = 2;
tbl.TblCellLeftMargin = 2;
tbl.TblCellTopMargin = 2;
tbl.TblAlignment = 0;
tbl.TblPlacement = 5;
tbl.TblSpaceAbove = 15;
tbl.TblSpaceBelow = 15;
tbl.TblWidth = 6;
tbl.TblBodyRowRulingPeriod = 1;
tbl.TblBodyRowRuling.Pen = 0;
tbl.TblBodyRowRuling.RulingPenWidth = 0.4;
tbl.TblTopRuling.Pen = 0;
tbl.TblTopRuling.RulingPenWidth = 1.5;
tbl.TblBottomRuling.Pen = 0;
tbl.TblBottomRuling.RulingPenWidth = 1.5;
}
}

--

Note, there may be an easier way to do this--for example by just applying an existing Table style to every table in a doc, which would also work. Just not sure whether that's possible or how to do it.

Views

147

Likes

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

Adobe Community Professional , Sep 01, 2021 Sep 01, 2021
Here is how I would do it. I would use a function to create new Ruling Formats to apply to the table. For example: #target framemaker var doc, tbl, PT = 65536, rulingFmt; doc = app.ActiveDoc; // Get or make the custom ruling format. rulingFmt = getRulingFmt ("CustomTop", doc, 12 * PT, 1); tbl = doc.SelectedTbl; // Set the top ruling only. tbl.TblTopRuling = rulingFmt; function getRulingFmt (name, doc, width, lines) { var rulingFmt; rulingFmt = doc.GetNamedRulingFmt (name); // If the ...

Likes

Translate

Translate
Enthusiast ,
Aug 30, 2021 Aug 30, 2021

Copy link to clipboard

Copied

Hi and welcome

 

The script works, but you have to take care about metrics.

But you can't see changes, because they are very very small

 

CITE from Adobe Scripting Guide:

Use the TblColWidths() method to change the width of the column. This method accepts the argument in a metric form. So, create the arguments as a metric object using the new Metrics()method.
NOTE: By convention, there are 72 points per inch. Multiply the inch value with 65536 to get the correct value. 1 inch is equal to 1 * 72 * 65536 points.

 

 

Likes

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 31, 2021 Aug 31, 2021

Copy link to clipboard

Copied

Klaus, thank you.

 

The data types for most of the settings I was trying to specify were int. Do they still require the metric conversion? I assumed the int value was in points, as that's how it's set up in the Table Designer menu, but maybe not?

 

Even after changing the values to their metric equivalents I'm not getting the behavior I expected. Maybe I need to format them at the Cell rather than Tbl object level.

 

Matt

Likes

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 Community Professional ,
Aug 31, 2021 Aug 31, 2021

Copy link to clipboard

Copied

I usually set up a variable for the metric value and then use it in my code.

var PT = 65536, doc, tbl;

doc = app.ActiveDoc;
tbl = doc.SelectedTbl; // Table with the insertion point.
// Set the left indent to 12 points.
tbl.TblLeftIndent = 12 * PT;

Which properties are not working as you expect?

Likes

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 31, 2021 Aug 31, 2021

Copy link to clipboard

Copied

None of the ruling properties seem to be registering at all, which makes me think I'm not defining them correctly. Maybe I misunderstand how the RulingFmt properties are defined/used? I've relisted below the properties I'm trying to define. The first two sets of properties (Alignment & Position and Margins & Spacing) all update correctly in the document (sweet progress!). MULT is a constant set to 65536 (thanks for that suggestion).

 

None of the RulingFmt properties work at all. The table ruling in the document doesn't change, and the ruling properties in the Table Designer aren't updated when I run the script. I've also tried multiplying by MULT for the properties that don't take a constant as a setting, though the property descriptions in the Scripting Guide table seem to imply that isn't necessary. In either case, nothing changes.

 

Am I screwing up by trying to access the RulingFmt properties through the Tbl properties? If so, I guess I'm just not sure what I'm supposed to be doing.

 

The easiest thing would be if I could just write a script that applies an existing Table Style to all imported tables and in the process override any imported properties/settings. But maybe that's not an option?

 

(Unrelated, I noticed many of the Cell properties are duplicates of the Tbl properties, which makes me think those supersede the Tbl properties? The tables I import from Word, and which I'm trying to batch set properties for with this script, include cell settings/format that I'll want to clear, including Row Formats.)

 

I very much appreciate your time and help!

 

// Alignment & Position
tbl.TblTitlePosition = 1;.
tbl.ContentHeight = 10 * MULT;
tbl.TblTitleGap = 4 * MULT;
tbl.TblPlacement = 5;
tbl.OrphanRows = 2;
tbl.TblWidth = 5.5 * 72 * MULT;
tbl.TblAlignment = 0;

// Margins & Spacing
tbl.TblCellBottomMargin = 3 * MULT;
tbl.TblCellLeftMargin = 0;
tbl.TblCellRightMargin = 0;
tbl.TblCellTopMargin = 4 * MULT;
tbl.TblLeftIndent = 0;
tbl.TblSpaceAbove = 15 * MULT;
tbl.TblSpaceBelow = 15 * MULT;

tbl.TblCatalogEntry = 1;
tbl.TblTag = "StandardTable";

/* Row Ruling
tbl.TblBodyRowRulingPeriod = 2;
tbl.TblBodyRowRuling.Pen = 0;
tbl.TblBodyRowRuling.RulingPenWidth = 0.4 * MULT;
tbl.TblTopRuling.Pen = 0;
tbl.TblTopRuling.RulingPenWidth = 1.5 * MULT;
tbl.TblBottomRuling.Pen = 0;
tbl.TblBottomRuling.RulingPenWidth = 1.5 * MULT;


/*Column Ruling
tbl.TblColRulingPeriod = 1;
tbl.TblColRuling.Name = "Thin";
tbl.TblColRuling.Pen = 0;
tbl.TblColRuling.RulingPenWidth = 0.4;
tbl.TblRightRuling.Pen = 0;
tbl.TblRightRuling.RulingPenWidth = 0;
tbl.TblLeftRuling.Pen = 0;
tbl.TblLeftRuling.RulingPenWidth = 0;

//Header and Footer Ruling
tbl.TblHFRowRuling.Pen = 0;
tbl.TblHFRowRuling.Name = "Medium";
tbl.TblHFRowRuling.RulingPenWidth = 1.5;
tbl.TblHFSeparatorRuling.Pen = 0;
tbl.TblHFSeparatorRuling.Name = "Double";
tbl.TblHFSeparatorRuling.RulingPenWidth = 0.4;
tbl.TblHFSeparatorRuling.RulingLines = 2;
tbl.TblHFSeparatorRuling.RulingGap = 2;*/

Likes

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 Community Professional ,
Aug 31, 2021 Aug 31, 2021

Copy link to clipboard

Copied

One thing to check if these tables come from Word: Sometimes Word tables will import into FrameMaker with custom ruling and shading applied. That means no matter what settings you apply via the Table Designer, they won't show because of the custom ruling and shading settings.

 

To find out, try this: select one or more cells in the table and choose Table > Custom Ruling and Shading. Then click the Show Current Settings button. The resulting dialog box should show From Table on all of the settings. If not, then custom ruling and shading is applied and will have to be removed in order for the Table Designer settings to be applied.

frameexpert_0-1630464471267.png

Custom ruling and shading is applied and removed at the Cell level. I will try to post some code tomorrow that shows how to remove custom ruling and shading.

 

By the way, that is the reason I wrote my TableCleaner script: To clean up tables imported from Word, including removing custom ruling and shading.

Likes

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 Community Professional ,
Sep 01, 2021 Sep 01, 2021

Copy link to clipboard

Copied

Just for completeness, I selected a table cell and applied custom ruling to it. I am using the same ruling format as the table calls for, but now the settings dialog box shows it as a custom setting. If I change the ruling in the Table Designer, the ruling for this cell will remain Very Thin.

frameexpert_0-1630499802278.png

 

So, before trying to update a table, you may want to use the code I posted to "reset" the table by removing all custom ruling and shading.

Likes

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 Community Professional ,
Sep 01, 2021 Sep 01, 2021

Copy link to clipboard

Copied

To reset in the interface, you use the From Table choices in the Custom Ruling and Shading panel:

frameexpert_0-1630500068066.png

 

Likes

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 Community Professional ,
Aug 31, 2021 Aug 31, 2021

Copy link to clipboard

Copied

Here is a function that will remove all custom ruling and shading from a table.

#target framemaker

var doc, tbl;

doc = app.ActiveDoc;
tbl = doc.SelectedTbl;
if (tbl.ObjectValid () === 1) {
    removeCustomRulingAndShading (tbl);
}

function removeCustomRulingAndShading (tbl) {
    
    var cell;
    
    cell = tbl.FirstRowInTbl.FirstCellInRow;
    while (cell.ObjectValid () === 1) {
        cell.CellOverrideBottomRuling = null;
        cell.CellOverrideFill = null;
        cell.CellOverrideLeftRuling = null;
        cell.CellOverrideRightRuling = null;
        cell.CellOverrideShading = null;
        cell.CellOverrideTopRuling = null;
        cell.CellUseOverrideBRuling = false;
        cell.CellUseOverrideFill = false;
        cell.CellUseOverrideLRuling = false;
        cell.CellUseOverrideRRuling = false;
        cell.CellUseOverrideShading = false;
        cell.CellUseOverrideTRuling = false;
        cell = cell.NextCellInTbl;
    }
}

Likes

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 Community Professional ,
Sep 01, 2021 Sep 01, 2021

Copy link to clipboard

Copied

Back to the task at hand. What do you think will happen if you just run this code with the selected table?

#target framemaker

var doc, tbl, PT = 65536;

doc = app.ActiveDoc;
tbl = doc.SelectedTbl;

// Set the top ruling only?
tbl.TblTopRuling.RulingPenWidth = 12 * PT;

You may have to refresh the screen (Control + L), but you may see something like this:

frameexpert_0-1630500529271.png

I purposely chose a large value (12 pt) see we can see it. What the code does is actually change the definition of the Very Thin Ruling Format. So, whereever that Ruling Format is applied, you will see the change, including in other table formats where it is used. Here is the proof from the Edit Ruling Style dialog box:

frameexpert_1-1630500798008.png

 

Likes

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 Community Professional ,
Sep 01, 2021 Sep 01, 2021

Copy link to clipboard

Copied

Here is how I would do it. I would use a function to create new Ruling Formats to apply to the table. For example:

#target framemaker

var doc, tbl, PT = 65536, rulingFmt;

doc = app.ActiveDoc;

// Get or make the custom ruling format.
rulingFmt = getRulingFmt ("CustomTop", doc, 12 * PT, 1);

tbl = doc.SelectedTbl;

// Set the top ruling only.
tbl.TblTopRuling = rulingFmt;

function getRulingFmt (name, doc, width, lines) {

	var rulingFmt;
	
	rulingFmt = doc.GetNamedRulingFmt (name);
	// If the Ruling Format doesn't exist, create it.
	if (rulingFmt.ObjectValid () === 0) {
		rulingFmt = doc.NewNamedRulingFmt (name);
	}
	
	// If the width is supplied, apply it to the Ruling Format.
	if (width) {
		rulingFmt.RulingPenWidth = width;
	}
	
	// If the lines is supplied, apply it to the Ruling Format.
	if (width) {
		rulingFmt.RulineLines = lines;
	}
	
	return rulingFmt;
}

Which will give you this:

frameexpert_0-1630503033411.png

 

Likes

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 Community Professional ,
Sep 01, 2021 Sep 01, 2021

Copy link to clipboard

Copied

LATEST

Some things to consider about scripting:

  1. It helps to know exactly what is happening when you change things in the interface. You can relate that to what needs to happen in code. I usually ask my clients: "How would you do this by hand in FrameMaker?"
  2. It can take more code than you might expect to do things via scripting. I think this example illustrates that.
  3. Always start with small "units" when scripting; for example a single table or a single property. It makes it easier to test, troubleshoot, and debug. Once you have all of your "units" working, you can combine them to work with larger sets (for example, all tables in the document, all documents in a book, etc.).
  4. Always separate code into functions. Functions can be isolated for testing and reuse.
  5. Don't be afraid to hire an expert :-).

Likes

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 Community Professional ,
Aug 31, 2021 Aug 31, 2021

Copy link to clipboard

Copied

When experimenting with scripting, I would suggest working with a single object instead of loop through all of the objects in the flow, document, etc. Here are some examples which are intended to be used as individual scripts.

#target FrameMaker

/* Selections.jsx. Carmen Publishing Inc.
    Copyright 2015. All rights reserved. Not to be resold.
    Contact rick@frameexpert.com or call 585-729-6746,
    
    This script is not meant to be run as is, but is a collection of snippets
    for working with selected objects in the active document. Copy the 
    desired snippet to a new script, so you can work with a selected object.
    
    Code for the following selected objects are included:
    * Marker
    * Variable
    * Cross-Reference
    * Text inset
    * Anchored frame
    * Table
    * Table cell and row
    * Paragraph
    * Element
    
    There is a "showProperties" utility function at the end that will write
    an object's properties to the JavaScript Console.
*/

// Marker --------------------------------------------------------------------
// Set a variable for the active document.
var doc = app.ActiveDoc;

// Set a variable for the selected text range in the document.
var textRange = doc.TextSelection;

// Get a list of markers in the selected text.
var textItems = doc.GetTextForRange(textRange,Constants.FTI_MarkerAnchor);
// Make sure there is a marker in the selection.
if (textItems.len) {
    // Get the first marker and display its marker text and marker type.
    var marker = textItems[0].obj;
    alert (marker.MarkerText);
    alert (marker.MarkerTypeId.Name);
}

// Variable -----------------------------------------------------------
// Set a variable for the active document.
var doc = app.ActiveDoc;

// Set a variable for the selected text range in the document.
var textRange = doc.TextSelection;

// Get a list of variables in the selected text.
var textItems = doc.GetTextForRange(textRange,Constants.FTI_VarBegin);
// Make sure there is a variable in the selection.
if (textItems.len) {
    // Get the first variable and display its format name and definition.
    var variable = textItems[0].obj;
    alert (variable.VarFmt.Name);
    alert (variable.VarFmt.Fmt);
}

// Cross-Reference -----------------------------------------------------------
// Set a variable for the active document.
var doc = app.ActiveDoc;

// Set a variable for the selected text range in the document.
var textRange = doc.TextSelection;

// Get a list of cross-references in the selected text.
var textItems = doc.GetTextForRange(textRange,Constants.FTI_XRefBegin);
// Make sure there is a cross-reference in the selection.
if (textItems.len) {
    // Get the first cross-reference and display its source text and format.
    var xref = textItems[0].obj;
    alert (xref.XRefSrcText);
    alert (xref.XRefFmt.Name);
}

// Text Inset ----------------------------------------------------------------
// Set a variable for the active document.
var doc = app.ActiveDoc;

// Set a variable for the selected text range in the document.
var textRange = doc.TextSelection;

// Get a list of text insets in the selected text.
var textItems = doc.GetTextForRange(textRange,Constants.FTI_TextInsetBegin);
// Make sure there is a text inset in the selection.
if (textItems.len) {
    // Get the first text inset and display its file path.
    var textInset = textItems[0].obj;
    alert (textInset.TiFile);
}

// Anchored frame ------------------------------------------------------------
// Method 1: select the anchored frame's anchor as a text selection.

// Set a variable for the active document.
var doc = app.ActiveDoc;

// Set a variable for the selected text range in the document.
var textRange = doc.TextSelection;

// Get a list of anchored frames in the selected text.
var textItems = doc.GetTextForRange(textRange,Constants.FTI_FrameAnchor);
// Make sure there is an anchored frame in the selection.
if (textItems.len) {
    // Get the first anchored frame and display its anchor type and width.
    var aFrame = textItems[0].obj;
    alert (aFrame.AnchorType);
    alert (aFrame.Width/65536);
}

// Anchored frame ------------------------------------------------------------
// Method 2: select the anchored frame as a graphic.

// Set a variable for the active document.
var doc = app.ActiveDoc;

// Get the first selected graphic in the document.
var aFrame = doc.FirstSelectedGraphicInDoc;
if (aFrame.constructor.name === "AFrame") {
    // If it is an anchored frame, display its anchor type and width.
    alert (aFrame.AnchorType);
    alert (aFrame.Width/65536);
}

// Paragraph -----------------------------------------------------------------

// Set a variable for the active document.
var doc = app.ActiveDoc;

// Get the paragraph at the beginning of the selection or insertion point.
var pgf = doc.TextSelection.beg.obj;
if (pgf.constructor.name === "Pgf") {
    // If the selection or insertion point is in a paragraph, display its name.
    alert (pgf.Name);
}

// Table ---------------------------------------------------------------------

// Set a variable for the active document.
var doc = app.ActiveDoc;

// Get the table currently selected or that contains the insertion point.
var tbl = doc.SelectedTbl;
if (tbl.constructor.name === "Tbl") {
    // If a table is selected, display its table tag and number of columns.
    alert (tbl.TblTag);
    alert(tbl.TblNumCols);
}

// Table cell (containing the insertion point) -------------------------------

// Set a variable for the active document.
var doc = app.ActiveDoc;

// Get the paragraph that contains the insertion point.
var pgf = doc.TextSelection.beg.obj;
// Get the table cell that contains the paragraph.
var cell = pgf.InTextObj;
if (cell.constructor.name === "Cell") {
    // If a cell is selected, display its row object and first paragraph's name.
    alert(cell.CellRow);
    alert (cell.FirstPgf.Name);
}

// Element (selected) --------------------------------------------------------

// Set a variable for the active document.
var doc = app.ActiveDoc;

// Make sure the document is structured.
if (doc.MainFlowInDoc.HighestLevelElement.ObjectValid () === 1) {
    // Get the currently selected element.
    var element = doc.ElementSelection.beg.child;
    // If an element is selected, show its element definition name.
    if (element.constructor.name === "Element") {
        alert (element.ElementDef.Name);
    }
}

// Element (insertion point) -------------------------------------------------

// Set a variable for the active document.
var doc = app.ActiveDoc;

// Make sure the document is structured.
if (doc.MainFlowInDoc.HighestLevelElement.ObjectValid () === 1) {
    // Get the parent element of the current insertion point.
    var element = doc.ElementSelection.beg.parent;
    // If there is a parent element, show its element definition name.
    if (element.constructor.name === "Element") {
        alert (element.ElementDef.Name);
    }
}

// Utility function for writing the properties of an object to the 
// JavaScript Console.
function showProperties (oObject) {
 
    // Get a list of the object's properties.
    var props = oObject.reflect.properties;
    
    for (var i = 0; i < props.length; i++) {
        $.writeln(props[i].name + ': ' + oObject[props[i].name]);
    }
}

// Utility function for getting the text from a text object such as a paragraph,
// table cell, text frame, text line, etc. Can also get text from a TextRange or
// text selection.
function getText (textObj, doc) {

    var text = "";
    // Get a list of the strings in the text object or text range.
    if (textObj.constructor.name !== "TextRange") {
        var textItems = textObj.GetText(Constants.FTI_String);
    } else {
        var textItems = doc.GetTextForRange(textObj, Constants.FTI_String);
    }
    // Concatenate the strings.
    for (var i = 0; i < textItems.len; i += 1) {
        text += (textItems[i].sdata);
    }
    return text; // Return the text
}

Likes

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 31, 2021 Aug 31, 2021

Copy link to clipboard

Copied

Thank you very much! I've saved and will study these.

Likes

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