Copy link to clipboard
Copied
I am trying to find all overridden ObjectStyles and where it is overridden in document! but i didn't find any solution regarding this online..there is only option to clearOverridden objectStyles. If anyone knows please help!
And one last addition, for anyone who is interested. I've made huge improvements under the hood, including quite good xml parsing (thanks to Peter Sirka's nifty little function—see script comments) and displays the differences with much better context. The underlying function listDifferences(items,options) can now handle two or more files, and the formatting is separated.
- Mark
/**
* Shows object style override differences
* for the selected page item.
* m1b
* 2022-07-18
* @discuss
...
Copy link to clipboard
Copied
I suggest the following approach:
Find all text frames with an object style applied and compare the properties used in style with those applied to each found item, If they are different, push the info into the array. Finally, display all the info in an alert, or write to a log file / console / whatever.
var arr = [];
main();
function main() {
var doc = app.activeDocument;
var objStyle = doc.objectStyles.itemByName("My Object Style");
var foundItem;
app.findChangeObjectOptions.objectType = ObjectTypes.TEXT_FRAMES_TYPE;
app.findObjectPreferences = app.changeObjectPreferences = NothingEnum.NOTHING;
app.findObjectPreferences.appliedObjectStyles = "My Object Style";
var foundItems = doc.findObject();
for (var i = 0; i < foundItems.length; i++) {
foundItem = foundItems[i];
ckeckProperties(foundItem, objStyle);
}
if (arr.length == 0) {
alert("No overriden text frames were found.", "Script");
}
else {
alert(arr.length + " overriden text frame" + ((arr.length == 1) ? " was" : "s were") + " found:\n" +arr.join("\n"), "Script");
}
}
function ckeckProperties(item, objStyle) {
if (item.fillColor != objStyle.fillColor) {
arr.push("Color " + item.fillColor.name + " on page " + item.parentPage.name);
}
}
The test doc is attached.
I used only one property -- fill color -- as an example. Other properties should be added depending on what's in your object style.
Copy link to clipboard
Copied
HI @Kasyan Servetsky This is also working fine but we need to specify the property of each, What i actually want is to get the overridedObjectStyle's name with its page numbers! Thanks for your effort and it also made @m1b interested in this post.
Copy link to clipboard
Copied
HI @Kasyan Servetsky , How to find ObjectStyle Override of anchored Object alone! I also attached a sample in this post which you can use.
Copy link to clipboard
Copied
Hi @Karthik SG, and @Kasyan Servetsky, this problem grabbed my interest and I came up with a strange script that has an interesting quirk: it is more strict than Indesign in deciding if an object has overrides.
The script reports if there are *any* differences between the object and the object after it has had any object style overrides removed. So this is great, because it does exactly what @Karthik SG has asked for.
However, it does a "better" job than Indesign's own system of determining if an item has object style overrides, so sometimes it will say there are overrides even when the Object Styles panel doesn't show the trailing (+) symbol. The script is technically correct though, because if you alt-click on the object style (to remove overrides) and run script again it will then report no overrides.
Please try it and let me know if it is useful!
- Mark
P.S. if you have control over what properties you are interested in, then Kasyan's approach might suit you more.
function main() {
var doc = app.activeDocument,
item = doc.selection[0];
var result = hasObjectStyleOverrides(item);
if (result != undefined)
alert('Has style overrides: ' + (result ? 'YES' : 'NO'));
/**
* Determines if item has ObjectStyle overrides.
*
* @author m1b
* @version 2022-07-16
* @discussion https://community.adobe.com/t5/indesign-discussions/how-to-find-whether-my-textframe-have-overriddenobjectstyle-or-not-in-my-document-using-javascript/m-p/13071120
*
* Note: this function is WAY MORE STRICT
* than Indesign's normal determination
* of overridden-ness.
*
* @param {PageItem} item - an Indesign page item.
* @returns {Boolean} - if true, then object has overrides.
*/
function hasObjectStyleOverrides(item) {
if (
item == undefined
|| !item.isValid
|| typeof item.exportFile !== 'function'
)
return;
// make a duplicate item
dup = item.duplicate();
var itemSnippet = saveSnippet(dup, 'item.xml');
// with no objectStyle overrides
dup.clearObjectStyleOverrides();
// and make a snippet of that
dupSnippet = saveSnippet(dup, 'dup.xml');
// compare the files, line by line
// and count differences
var differences = getDifferences(itemSnippet, dupSnippet, 10);
// clean up
dup.remove();
// found any differences?
return differences > 0;
}
/**
* Compares two text files, line by line,
* and word by word, and returns number
* of differences.
* @author m1b
* @param {File} file1
* @param {File} file2
* @returns {Number}
*/
function getDifferences(file1, file2, maxDifferences) {
var differences = 0,
atEverySpace = /\s+/g;
// open both files
file1.open('r');
file2.open('r');
while (
!file1.eof
&& !file2.eof
&& differences <= maxDifferences
) {
// compare, line by line, word by word
var line1 = file1.readln().split(atEverySpace),
line2 = file2.readln().split(atEverySpace),
len = Math.min(line1.length, line2.length);
// nothing to see after this element starts
if (line1[1] == '<?xpacket')
break;
// compare words
for (var i = 0; i < len; i++)
if (line1[i] != line2[i])
differences++;
}
return differences;
}
/**
* Exports item as an Indesign snippet (XML) file.
* @author m1b
* @param {PageItem} item - an Indesign page item.
* @param {String} name - the snippet file name (include extension).
*/
function saveSnippet(item, name) {
// put in temp folder
var snippetFile = File(Folder.temp.fsName + '/' + name);
// export as indesign snippet
item.exportFile(ExportFormat.INDESIGN_SNIPPET, snippetFile, false);
if (snippetFile.exists)
return snippetFile;
}
}
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Check ObjectStyle Overrides');
Copy link to clipboard
Copied
Here is an example usage that selects any objects on the active spread that have overridden object styles.
- Mark
var doc = app.activeDocument,
items = doc.layoutWindows[0].activeSpread.pageItems;
doc.selection = [];
for (var i = 0; i < items.length; i++) {
if (hasObjectStyleOverrides(items[i]))
items[i].select(SelectionOptions.ADD_TO);
}
Copy link to clipboard
Copied
And one last addition, for anyone who is interested. I've made huge improvements under the hood, including quite good xml parsing (thanks to Peter Sirka's nifty little function—see script comments) and displays the differences with much better context. The underlying function listDifferences(items,options) can now handle two or more files, and the formatting is separated.
- Mark
/**
* Shows object style override differences
* for the selected page item.
* m1b
* 2022-07-18
* @discussion https://community.adobe.com/t5/indesign-discussions/how-to-find-whether-my-textframe-have-overriddenobjectstyle-or-not-in-my-document-using-javascript/m-p/13071120
*
* Important note:
* This is more strict than Indesign's own
* object-style-overridden criteria, so
* sometimes an object may show differences
* while not showing the (+) symbol in the
* Object Style panel.
*/
function main() {
var doc = app.activeDocument,
item = doc.selection[0];
if (item.isValid)
showObjectStyleOverrides(item);
}
/**
* Determines if item has ObjectStyle overrides
* and displays a list of the overriden properties.
*
* m1b
* 2022-07-18
* @discussion https://community.adobe.com/t5/indesign-discussions/how-to-find-whether-my-textframe-have-overriddenobjectstyle-or-not-in-my-document-using-javascript/m-p/13071120
*
* Note: this function is MORE STRICT
* than Indesign's normal determination
* of overridden-ness.
*
* {PageItem} item - an Indesign page item.
* {Boolean} - if true, then object has overrides.
*/
function showObjectStyleOverrides(item) {
if (
item == undefined
|| !item.isValid
|| typeof item.exportFile !== 'function'
)
return;
// make a duplicate item
dup = item.duplicate();
var itemSnippet = saveSnippet(dup, 'item.xml');
// with no objectStyle overrides
dup.clearObjectStyleOverrides();
// and make a snippet of that
dupSnippet = saveSnippet(dup, 'dup.xml');
// clean up
dup.remove();
// set up arguments for listDifferences
var comparisons = [
{ label: 'PageItem', file: itemSnippet },
{ label: 'Object Style', file: dupSnippet }
];
// in snippet XML, we don't care about these
var options = { ignoreThese: /x:xmpmeta/ };
// collect differences between the two
var differences = listDifferences(comparisons, options);
// display differences
if (differences.length > 0)
alerter(differences.length + ' Object Style Overrides', formatDifferences(differences), 1000, 500);
else
alert('This item has no Object Style overrides.');
}
/**
* Compares XML text or XML text files and
* returns a text summary of differences.
* m1b
* 2022-07-18
*
*
* listDifferences(
* [
* { label: 'PageItem', file: itemSnippetFile },
* { label: 'Object Style', xml: dupSnippetText }
* ], {
* onlyThese: /text/i,
* ignoreThese: /color/i
* }
* );
*
* {Array} items - an array of items to compare
* {Object} [options] - object with options
* {String|RegExp} [options.onlyThese] - test to choose keys
* {String|RegExp} [options.ignoreThese] - test to exclude keys
* {String} - the formatted different properties
*/
function listDifferences(items, options) {
var differences = [],
objs = [],
labels = [],
lines = [],
keys = [],
// constants
empty = '-- empty --';
// initialise the items
for (var i = 0; i < items.length; i++) {
var item = items[i];
// label
if (item.label == undefined)
item.label = String.fromCharCode(65 + k);
// file
if (
item.file != undefined
&& item.file.exists
)
item.obj = parseXML(readFile(item.file));
// string
if (typeof (item.xml) === 'string')
item.obj = parseXML(item.xml);
// xml
if (typeof (item.xml) === 'xml')
item.obj = parseXML(item.xml.toString());
// xml text
if (item.obj == undefined)
return ['Error: item "' + item.label + '" had no usable XML.'];
objs[i] = item.obj;
labels[i] = item.label;
lines[item.label] = [];
var itemKeys = item.obj.reflect.properties;
// collect the item's keys
for (var k = 0; k < itemKeys.length; k++)
if (item.obj.hasOwnProperty(itemKeys[k]))
keys.push(String(itemKeys[k]));
}
// combine keys from all items
var keys = uniquify(keys);
// expand the key/values
for (var k = 0; k < keys.length; k++) {
// current key to compare
var key = keys[k];
// add lines by expanding objs recursively
for (var o = 0; o < objs.length; o++)
lines[labels[o]] = lines[labels[o]].concat(expandLines(key, objs[o][key]));
}
// make an object that has an object for *every* key
// and store values in that object, using the item's label
var keys = [],
comparator = [];
for (var o = 0; o < objs.length; o++) {
var label = labels[o];
linesLoop:
for (var l = 0; l < lines[label].length; l++) {
var linesForKey = lines[label][l],
key = linesForKey[0],
value = linesForKey[1];
if (
options.ignoreThese != undefined
&& options.ignoreThese.test(key)
)
// user doesn't want this key
continue linesLoop;
if (
options.onlyThese != undefined
&& !options.onlyThese.test(key)
)
// not what user asked for
continue linesLoop;
// add to new master keys
keys.push(key);
if (comparator[key] == undefined)
// add line's key as an empty object
comparator[key] = [];
// comparator[key][label] = [];
// add value, using label as key
comparator[key][label] = value;
}
}
// tidy up new keys
var keys = uniquify(keys);
// check if values are different
// and add them to differences
for (var k = 0; k < keys.length; k++) {
var key = keys[k],
isDifferent = false,
// the value we check against
currentValue = null,
// will hold array for each object's line
linesForKey = [];
for (var o = 0; o < objs.length; o++) {
var label = labels[o],
v;
// add label for this object
if (
comparator[key] === undefined
|| !comparator[key].hasOwnProperty(label)
|| comparator[key][label] == undefined
|| comparator[key][label] == 'undefined'
)
v = empty;
else
v = comparator[key][label];
// store this line in case we need it
linesForKey.push([label, key, v]);
// initialise current value
if (currentValue === null)
currentValue = v;
// or check if values are different
else if (currentValue !== v)
isDifferent = true;
}
if (isDifferent === true)
differences.push(linesForKey);
}
return differences;
}
/**
* Format the output of listDifferences function.
* {Array} differences - listDifferences() output
*/
function formatDifferences(differences) {
var formatted = [],
labelWidth = 0,
// regex to tidy strings
firstTwoElements = /^([^\.]+\.[^\.]+\.)/,
linefeeds = /[\r\n]/g,
emptyBrackets = /\[\]/g,
tabs = /\t/g;
// calculate label width
for (var o = 0; o < differences[0].length; o++)
if (labelWidth < differences[0][o][0].length)
labelWidth = differences[0][o][0].length;
for (var d = 0; d < differences.length; d++) {
var linesForKey = [];
for (var o = 0; o < differences[d].length; o++)
linesForKey[o] = formatDifference.apply(null, differences[d][o]);
formatted.push(linesForKey.join('\n'));
}
return formatted.join('\n\n');
// helper functions
function formatDifference() { var a = arguments; return pad(a[0], labelWidth + 2) + '\t' + tidyKey(a[1]) + ': ' + tidyValue(a[2]) };
function tidyKey(key) { return key.replace(firstTwoElements, '').replace(emptyBrackets, '') };
function tidyValue(value) { return value.replace(linefeeds, '\\n').replace(tabs, '\\t') };
}
/**
* Convert a simple key/value object into
* array of [[key, value], [key, value], ...]
* if value is also an object, it will
* recursively convert that also.
* {String} key - the starting key
* {String|Array|Object} obj - the starting value
* {Array} - [[key, value], [key, value], ...]
*/
function expandLines(key, obj, i) {
key = String(key);
var lines = [];
if (obj == undefined)
lines.push([key, 'undefined']);
else if (obj.constructor.name == 'String')
lines.push([key, obj]);
else if (obj.constructor.name == 'Array')
for (var i = 0; i < obj.length; i++)
lines = lines.concat(expandLines(key + '[' + i + ']', obj[i], i));
else if (obj.constructor.name == 'Object')
for (var k in obj)
if (obj.hasOwnProperty(k))
lines = lines.concat(expandLines(key + '.' + k, obj[k], i));
return lines;
}
/**
* Reads a file contents.
* {File} file - the file to read.
* {String}
*/
function readFile(file) {
if (
file == undefined
|| !file.exists
)
return;
file.open('r');
var text = file.read();
file.close();
return text;
}
/**
* Exports item as an Indesign snippet (XML) file.
* m1b
* {PageItem} item - an Indesign page item.
* {String} name - the snippet file name (include extension).
*/
function saveSnippet(item, name) {
// put in temp folder
var snippetFile = File(Folder.temp.fsName + '/' + name);
// export as indesign snippet
item.exportFile(ExportFormat.INDESIGN_SNIPPET, snippetFile, false);
if (snippetFile.exists)
return snippetFile;
}
/**
* Show text in a multiline edit text field.
* {String} title - the title of the alerter
* {String|Array} obj - the text content (array of strings will work)
* {Number} [width] - the dialog width
* {Number} [height] - the dialog height
*/
function alerter(title, obj, width, height) {
width = width || -1;
height = height || -1;
if (obj instanceof Array)
obj = obj.join("\r");
var w = new Window("dialog", title),
et = w.add("edittext", undefined, obj, { multiline: true, scrolling: true });
et.maximumSize.height = w.maximumSize.height - 100;
et.minimumSize.height = 350;
et.minimumSize.width = 350;
et.preferredSize = [width, height];
w.add("button", undefined, "Close", { name: "ok", alignment: ['right', 'center'] });
w.show();
}
function uniquify(arr) {
var seen = {},
out = [],
j = 0;
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
if (seen[item] !== 1) {
seen[item] = 1;
out[j++] = item;
}
}
return out;
}
/**
* Simple XML parser
* Peter Sirka
* @url https://gist.github.com/petersirka/9e79b1d43cf6e579fc62
* {String} xml
* {Object}
*/
function parseXML(xml) {
var beg = -1;
var end = 0;
var tmp = 0;
var current = [];
var obj = {};
var from = -1;
while (true) {
beg = xml.indexOf('<', beg + 1);
if (beg === -1)
break;
end = xml.indexOf('>', beg + 1);
if (end === -1)
break;
var el = xml.substring(beg, end + 1);
var c = el[1];
if (c === '?' || c === '/') {
var o = current.pop();
if (from === -1 || o !== el.substring(2, el.length - 1))
continue;
var path = current.join('.') + '.' + o;
var value = xml.substring(from, beg);
if (typeof (obj[path]) === 'undefined')
obj[path] = value;
else if (obj[path] instanceof Array)
obj[path].push(value);
else
obj[path] = [obj[path], value];
from = -1;
continue;
}
tmp = el.indexOf(' ');
var hasAttributes = true;
if (tmp === -1) {
tmp = el.length - 1;
hasAttributes = false;
}
from = beg + el.length;
var isSingle = el[el.length - 2] === '/';
var name = el.substring(1, tmp);
if (!isSingle)
current.push(name);
if (!hasAttributes)
continue;
var match = el.match(/\w+\=\".*?\"/g);
if (match === null)
continue;
var attr = {};
var length = match.length;
for (var i = 0; i < length; i++) {
var index = match[i].indexOf('"');
attr[match[i].substring(0, index - 1)] = match[i].substring(index + 1, match[i].length - 1);
}
obj[current.join('.') + (isSingle ? '.' + name : '') + '[]'] = attr;
}
return obj;
};
/**
* Pad a string.
* m1b
* {String} str - the text to pad.
* {Number} width - number of characters to pad to.
* {String} [paddingChar] - the character to pad with.
* {Boolean} [rightAligned] - if true, will right align text.
*/
function pad(str, width, paddingChar, rightAligned) {
width = Number(width);
paddingChar = paddingChar || ' ';
var padding = '';
while (width > padding.length)
padding += paddingChar;
if (rightAligned)
str = (padding + str).slice(-width);
else
str = (str + padding).slice(0, width);
return str
}
// polyfill
if (!String.prototype.indexOf) {
String.prototype.indexOf = function (obj) {
var i;
for (i = 0; i < this.length; i++) if (this[i] == obj) return i;
return -1;
}
}
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Show ObjectStyle Overrides');
Example 1: First I alt-click on the object style in the Object Style panel (to clear overrides) then I change the column number of the text box to 2 column (the object style is 1 column).
Example 2: Again I start by alt-clicking on the object style in the Object Style panel (to clear overrides) then I move the rectangle's graphic.
- Mark
Copy link to clipboard
Copied
Hi @m1b Mark, Its working fine but i don't need lineNumber or changes. I need the page-no and overridedObjectStyleNames present in each page! Thanks for your effort in finding a new way to do this!
Copy link to clipboard
Copied
What i actually want is to get the overridedObjectStyle's name with its page number! Please make a try it would be helpful for many as this property doesn't available on Indesign DOM.
Copy link to clipboard
Copied
Hi @Karthik SG, Okay, that is quite easy to do with my script, so I'll post a version soon. Also I have improved the underlying script's capabilities greatly so it will do a much better job of reporting differences. I'll edit my previous posts to include the improved code.
- Mark
Copy link to clipboard
Copied
Hi @Karthik SG, I've made a variation of my script that does what you are asking I think. Here is example output:
Here is the script:
/**
* List pages with object style overrides.
**
* @author m1b
* @version 2022-07-21
* @discussion https://community.adobe.com/t5/indesign-discussions/how-to-find-whether-my-textframe-have-overriddenobjectstyle-or-not-in-my-document-using-javascript/m-p/13071120
**
* Important note:
* This is more strict than Indesign's own
* object-style-overridden criteria, so
* sometimes an object may show differences
* while not showing the (+) symbol in the
* Object Style panel.
*/
function main() {
if (app.documents == 0)
return;
// we are going to modify the document
// so open a temporary copy
var doc = openDocumentCopy(app.activeDocument, false);
if (doc == undefined)
return;
var items = doc.allPageItems,
results = [],
documentOffsets = [];
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (!item.isValid)
continue;
if (hasObjectStyleOverrides(item)) {
if (item.parentPage) {
if (item.parentPage.parent.constructor.name != 'MasterSpread') {
results.push('Page ' + pad(item.parentPage.name + ':', 10) + '\t' + item.appliedObjectStyle.name);
documentOffsets.push(item.parentPage.documentOffset);
}
}
else {
// include items on pasteboard?
results.push('Page ' + pad('pb:', 10) + '\t' + item.appliedObjectStyle.name);
documentOffsets.push(Infinity);
}
}
}
// clean up
doc.close(SaveOptions.NO);
// sort by document offset
results = orderArraysTogether(documentOffsets, results);
// display results
alerter('Overridden Object Styles', results.join('\n'));
}
/**
* Determines if item has ObjectStyle overrides
* and displays a list of the overriden properties.
*
* @author m1b
* @version 2022-07-18
* @discussion https://community.adobe.com/t5/indesign-discussions/how-to-find-whether-my-textframe-have-overriddenobjectstyle-or-not-in-my-document-using-javascript/m-p/13071120
*
* Note: this function is MORE STRICT
* than Indesign's normal determination
* of overridden-ness.
*
* @param {PageItem} item - an Indesign page item.
* @returns {Boolean} - if true, then object has overrides.
*/
function hasObjectStyleOverrides(item) {
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;
app.scriptPreferences.enableRedraw = false;
if (
item == undefined
|| !item.isValid
|| typeof item.exportFile !== 'function'
)
return;
if (item.parent.constructor.name == 'Character')
// must release anchored object
item.anchoredObjectSettings.releaseAnchoredObject();
// make a duplicate item
dup = item.duplicate();
var itemSnippet = saveSnippet(dup, 'item.xml');
// with no objectStyle overrides
dup.clearObjectStyleOverrides();
// and make a snippet of that
dupSnippet = saveSnippet(dup, 'dup.xml');
// clean up
dup.remove();
// set up arguments for listDifferences
var comparisons = [
{ label: 'PageItem', file: itemSnippet },
{ label: 'Object Style', file: dupSnippet }
];
// in snippet XML, we don't care about these
var options = { ignoreThese: /x:xmpmeta/ };
// collect differences between the two
var differences = listDifferences(comparisons, options);
return differences.length > 0;
}
/**
* Compares XML text or XML text files and
* returns a text summary of differences.
* @author m1b
* @version 2022-07-18
*
* @example
* listDifferences(
* [
* { label: 'PageItem', file: itemSnippetFile },
* { label: 'Object Style', xml: dupSnippetText }
* ], {
* onlyThese: /text/i,
* ignoreThese: /color/i
* }
* );
*
* @param {Array} items - an array of items to compare
* @param {Object} [options] - object with options
* @param {String|RegExp} [options.onlyThese] - test to choose keys
* @param {String|RegExp} [options.ignoreThese] - test to exclude keys
* @returns {String} - the formatted different properties
*/
function listDifferences(items, options) {
var differences = [],
objs = [],
labels = [],
lines = [],
keys = [],
// constants
empty = '-- empty --';
// initialise the items
for (var i = 0; i < items.length; i++) {
var item = items[i];
// label
if (item.label == undefined)
item.label = String.fromCharCode(65 + k);
// file
if (
item.file != undefined
&& item.file.exists
)
item.obj = parseXML(readFile(item.file));
// string
if (typeof (item.xml) === 'string')
item.obj = parseXML(item.xml);
// xml
if (typeof (item.xml) === 'xml')
item.obj = parseXML(item.xml.toString());
// xml text
if (item.obj == undefined)
return ['Error: item "' + item.label + '" had no usable XML.'];
objs[i] = item.obj;
labels[i] = item.label;
lines[item.label] = [];
var itemKeys = item.obj.reflect.properties;
// collect the item's keys
for (var k = 0; k < itemKeys.length; k++)
if (item.obj.hasOwnProperty(itemKeys[k]))
keys.push(String(itemKeys[k]));
}
// combine keys from all items
var keys = uniquify(keys);
// expand the key/values
for (var k = 0; k < keys.length; k++) {
// current key to compare
var key = keys[k];
// add lines by expanding objs recursively
for (var o = 0; o < objs.length; o++)
lines[labels[o]] = lines[labels[o]].concat(expandLines(key, objs[o][key]));
}
// make an object that has an object for *every* key
// and store values in that object, using the item's label
var keys = [],
comparator = [];
for (var o = 0; o < objs.length; o++) {
var label = labels[o];
linesLoop:
for (var l = 0; l < lines[label].length; l++) {
var linesForKey = lines[label][l],
key = linesForKey[0],
value = linesForKey[1];
if (
options.ignoreThese != undefined
&& options.ignoreThese.test(key)
)
// user doesn't want this key
continue linesLoop;
if (
options.onlyThese != undefined
&& !options.onlyThese.test(key)
)
// not what user asked for
continue linesLoop;
// add to new master keys
keys.push(key);
if (comparator[key] == undefined)
// add line's key as an empty object
comparator[key] = [];
// comparator[key][label] = [];
// add value, using label as key
comparator[key][label] = value;
}
}
// tidy up new keys
var keys = uniquify(keys);
// check if values are different
// and add them to differences
for (var k = 0; k < keys.length; k++) {
var key = keys[k],
isDifferent = false,
// the value we check against
currentValue = null,
// will hold array for each object's line
linesForKey = [];
for (var o = 0; o < objs.length; o++) {
var label = labels[o],
v;
// add label for this object
if (
comparator[key] === undefined
|| !comparator[key].hasOwnProperty(label)
|| comparator[key][label] == undefined
|| comparator[key][label] == 'undefined'
)
v = empty;
else
v = comparator[key][label];
// store this line in case we need it
linesForKey.push([label, key, v]);
// initialise current value
if (currentValue === null)
currentValue = v;
// or check if values are different
else if (currentValue !== v)
isDifferent = true;
}
if (isDifferent === true)
differences.push(linesForKey);
}
return differences;
}
/**
* Format the output of listDifferences function.
* @param {Array} differences - listDifferences() output
*/
function formatDifferences(differences) {
var formatted = [],
labelWidth = 0,
// regex to tidy strings
firstTwoElements = /^([^\.]+\.[^\.]+\.)/,
linefeeds = /[\r\n]/g,
emptyBrackets = /\[\]/g,
tabs = /\t/g;
// calculate label width
for (var o = 0; o < differences[0].length; o++)
if (labelWidth < differences[0][o][0].length)
labelWidth = differences[0][o][0].length;
for (var d = 0; d < differences.length; d++) {
var linesForKey = [];
for (var o = 0; o < differences[d].length; o++)
linesForKey[o] = formatDifference.apply(null, differences[d][o]);
formatted.push(linesForKey.join('\n'));
}
return formatted.join('\n\n');
// helper functions
function formatDifference() { var a = arguments; return pad(a[0], labelWidth + 2) + '\t' + tidyKey(a[1]) + ': ' + tidyValue(a[2]) };
function tidyKey(key) { return key.replace(firstTwoElements, '').replace(emptyBrackets, '') };
function tidyValue(value) { return value.replace(linefeeds, '\\n').replace(tabs, '\\t') };
}
/**
* Convert a simple key/value object into
* array of [[key, value], [key, value], ...]
* if value is also an object, it will
* recursively convert that also.
* @param {String} key - the starting key
* @param {String|Array|Object} obj - the starting value
* @returns {Array} - [[key, value], [key, value], ...]
*/
function expandLines(key, obj, i) {
key = String(key);
var lines = [];
if (obj == undefined)
lines.push([key, 'undefined']);
else if (obj.constructor.name == 'String')
lines.push([key, obj]);
else if (obj.constructor.name == 'Array')
for (var i = 0; i < obj.length; i++)
lines = lines.concat(expandLines(key + '[' + i + ']', obj[i], i));
else if (obj.constructor.name == 'Object')
for (var k in obj)
if (obj.hasOwnProperty(k))
lines = lines.concat(expandLines(key + '.' + k, obj[k], i));
return lines;
}
/**
* Reads a file contents.
* @param {File} file - the file to read.
* @returns {String}
*/
function readFile(file) {
if (
file == undefined
|| !file.exists
)
return;
file.open('r');
var text = file.read();
file.close();
return text;
}
/**
* Exports item as an Indesign snippet (XML) file.
* @author m1b
* @param {PageItem} item - an Indesign page item.
* @param {String} name - the snippet file name (include extension).
*/
function saveSnippet(item, name) {
// put in temp folder
var snippetFile = File(Folder.temp.fsName + '/' + name);
// export as indesign snippet
item.exportFile(ExportFormat.INDESIGN_SNIPPET, snippetFile, false);
if (snippetFile.exists)
return snippetFile;
}
/**
* Show text in a multiline edit text field.
* @param {String} title - the title of the alerter
* @param {String|Array} obj - the text content (array of strings will work)
* @param {Number} [width] - the dialog width
* @param {Number} [height] - the dialog height
*/
function alerter(title, obj, width, height) {
width = width || -1;
height = height || -1;
if (obj instanceof Array)
obj = obj.join("\r");
var w = new Window("dialog", title),
et = w.add("edittext", undefined, obj, { multiline: true, scrolling: true });
et.maximumSize.height = w.maximumSize.height - 100;
et.minimumSize.height = 350;
et.minimumSize.width = 350;
et.preferredSize = [width, height];
w.add("button", undefined, "Close", { name: "ok", alignment: ['right', 'center'] });
w.show();
}
function uniquify(arr) {
var seen = {},
out = [],
j = 0;
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
if (seen[item] !== 1) {
seen[item] = 1;
out[j++] = item;
}
}
return out;
}
/**
* Simple XML parser
* @author Peter Sirka
* @url https://gist.github.com/petersirka/9e79b1d43cf6e579fc62
* @param {String} xml
* @return {Object}
*/
function parseXML(xml) {
var beg = -1;
var end = 0;
var tmp = 0;
var current = [];
var obj = {};
var from = -1;
while (true) {
beg = xml.indexOf('<', beg + 1);
if (beg === -1)
break;
end = xml.indexOf('>', beg + 1);
if (end === -1)
break;
var el = xml.substring(beg, end + 1);
var c = el[1];
if (c === '?' || c === '/') {
var o = current.pop();
if (from === -1 || o !== el.substring(2, el.length - 1))
continue;
var path = current.join('.') + '.' + o;
var value = xml.substring(from, beg);
if (typeof (obj[path]) === 'undefined')
obj[path] = value;
else if (obj[path] instanceof Array)
obj[path].push(value);
else
obj[path] = [obj[path], value];
from = -1;
continue;
}
tmp = el.indexOf(' ');
var hasAttributes = true;
if (tmp === -1) {
tmp = el.length - 1;
hasAttributes = false;
}
from = beg + el.length;
var isSingle = el[el.length - 2] === '/';
var name = el.substring(1, tmp);
if (!isSingle)
current.push(name);
if (!hasAttributes)
continue;
var match = el.match(/\w+\=\".*?\"/g);
if (match === null)
continue;
var attr = {};
var length = match.length;
for (var i = 0; i < length; i++) {
var index = match[i].indexOf('"');
attr[match[i].substring(0, index - 1)] = match[i].substring(index + 1, match[i].length - 1);
}
obj[current.join('.') + (isSingle ? '.' + name : '') + '[]'] = attr;
}
return obj;
};
/**
* Pad a string.
* @author m1b
* @param {String} str - the text to pad.
* @param {Number} width - number of characters to pad to.
* @param {String} [paddingChar] - the character to pad with.
* @param {Boolean} [rightAligned] - if true, will right align text.
*/
function pad(str, width, paddingChar, rightAligned) {
width = Number(width);
paddingChar = paddingChar || ' ';
var padding = '';
while (width > padding.length)
padding += paddingChar;
if (rightAligned)
str = (padding + str).slice(-width);
else
str = (str + padding).slice(0, width);
return str
}
/**
* Open a copy of document.
* `doc` argument can be a
* Document or a File pointing
* to a document. Document
* must be saved first.
* @author m1b
* @version 2022-07-21
* @param {Document|File} doc - the document to copy.
* @param {Boolean} showWindow - if false, will open hidden.
* @return {Document} - the unsaved copy.
*/
function openDocumentCopy(doc, showWindow) {
if (doc.constructor.name == 'Document') {
if (!doc.saved) {
alert('Please save document before running script.');
return;
}
doc = doc.fullName;
}
if (doc.constructor.name == 'File') {
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;
return app.open(doc, (showWindow !== false), OpenOptions.OPEN_COPY);
}
}
/**
* Sort arrays based on sorting
* the first numerical array.
**
* @author m1b
* @version 2022-07-21
**
* @example
* orderArraysTogether([1000, 10, 20, 30],['a', 'b', 'c', 'd']);
* -> // ['b','c','d','a']
**
* Note: function outputs new arrays,
* without altering input arrays.
* Will output either a single sorted
* array (if only one extra array was
* supplied) or an array of sorted arrays.
* @param {Array} orderArray - sort this and match.
* @param {Array} arr1 - array to sort.
* @param {Array} [arr2] - 2nd array to sort.
* @param {Array} [arr3] - 3rd ...
* @return {Array}
*/
function orderArraysTogether(orderer, arr1, arr2) {
var len = orderer.length,
sortedArrays = [],
// the sorted orderer can be used
// to access the other arrays
sortedOrderer = orderer
.reflect.properties.slice(0, len)
.sort(function (a, b) { return orderer[a] - orderer[b] });
// loop over arrays, not including the orderArray
for (var i = 1; i < arguments.length; i++) {
if (arguments[i].length != len)
throw Error('orderArraysTogether: all arrays must have same length.');
sortedArrays[i - 1] = [];
// make new array in sorted order
for (var j = 0; j < len; j++)
sortedArrays[i - 1][j] = arguments[i][sortedOrderer[j]];
}
// return a single sorted array
// or an array of sorted arrays
return (sortedArrays.length === 1)
? sortedArrays[0]
: sortedArrays;
}
// polyfill
if (!String.prototype.indexOf) {
String.prototype.indexOf = function (obj) {
var i;
for (i = 0; i < this.length; i++) if (this[i] == obj) return i;
return -1;
}
}
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Show ObjectStyle Overrides');
Edit 2022-07-21: updated script to handle anchored text objects and ignore master page items.
Copy link to clipboard
Copied
HI @m1b , the script works but it is showing only one particluar override in a page and not showing other overrides which i made manually for testing it! and the script also shows all the masterPage overrides like (Page A, Page B...)
for sample,
Page 10: Zeilenzähler
Page 10: [None]
Page 11: Zeilenzähler
Page 11: [None]
Page 12: Zeilenzähler
Page 12: [None]
Page 5: [None]
Page 6: Zeilenzähler
Page 6: [None]
Page 7: Zeilenzähler
Page 7: [None]
Page 8: [None]
Page 9: Zeilenzähler
Page 9: [None]
Page A: [Basic Text Frame]
Page A: [Basic Text Frame]
Page A: [None]
Page A: [None]
Page A: [None]
Page A: [None]
Page B: [Basic Text Frame]
Page B: [Basic Text Frame]
Page B: [None]
Page B: [None]
Page B: [None]
Page B: [None]
Page B: [None]
Page B: [None]
Page B: [None]
Page B: [None]
Page C: [Basic Graphics Frame]
Page C: [Basic Text Frame]
Page C: [Basic Text Frame]
Page C: [Basic Text Frame]
Page C: [None]
Page C: [None]
Page C: [None]
Page D: [Basic Graphics Frame]
Page D: [Basic Text Frame]
Page D: [Basic Text Frame]
Page D: [Basic Text Frame]
Page D: [None]
Page D: [None]
Page D: [None]
Page E: [Basic Graphics Frame]
Page E: [Basic Text Frame]
Page E: [Basic Text Frame]
Page E: [Basic Text Frame]
Page E: [None]
Page E: [None]
Page E: [None]
Page F: [None]
Page G: [None]
Page H: [None]
Page H: [None]
Page H: [None]
Page H: [None]
Copy link to clipboard
Copied
Hi @Karthik SG, so you're saying that it isn't seeing some overridden page items? Could you send me a simple one-page indesign document that shows this error? I'll look into it. Also are you saying you don't want it to check items on master pages?
- Mark
Copy link to clipboard
Copied
Yes Mark @m1b ,some overridden page items were missing also please ignore master pages overrides. please use this sample document for your reference!
Copy link to clipboard
Copied
Hi Mark @m1b ,overridden page items of anchored objects were missing mainly. Did you found anyway? or please suggest a way to make it work!
Copy link to clipboard
Copied
Yes I see in your sample file. My script didn't collect the anchored items. I will add that, and also exclude the master items. Please give me time. If you would like to hire me for the job it will be higher in the queue! Let me know, otherwise please wait until I have time. 🙂
- Mark
Copy link to clipboard
Copied
Haha, I'd rather wait then @m1b .Thanks for the response Mark!
Copy link to clipboard
Copied
Hi @Karthik SG ,
see into the first lines of the script code.
items = doc.pageItems,
I did not test it with:
doc.allPageItems,
With doc.allPageItems the scope of the script would be expanded hugely!
To items that are positioned in nested structures like groups, anchored objects, pasted inside structures or objects in graphic cells. Also to items in active states of form elements like buttons, also to active states of Multistate objects.
And it still includes items on parent pages (parent spreads) plus document spreads.
doc.spreads.everyItem().allPageItems
would not do this. It cannot be used in Mark's code right off of the bat, because it returns an array of arrays that should be flattened. But principally with that approach, parent pages are left out of the game.
Do you want this?
Ok, here a big "Thank you!" to Mark for providing that code with that special twist regarding XML.
And also a big shout-out to Peter Sirka!
Thanks,
Uwe Laubender
( Adobe Community Professional )
Copy link to clipboard
Copied
Hi @Laubender , I tried using "doc.allPageItems" but no improvement in output! when i tried using this "doc.spreads.everyItem().allPageItems" output becomes empty. I think i should wait for @m1b response!
Copy link to clipboard
Copied
Hi @Karthik SG, I've updated the script above. Can you give it a try on your document? It works on the sample document you posted, but we'll see. To compare the anchored objects required a bit of lateral thinking. I decided the best way was to open a copy of the document and release all the anchored objects before comparing them. This means that the script will ask you to save your document first to ensure that the copy reflects the latest changes. The script will not modify your document, just the copy (which it will then close without saving).
- Mark
Copy link to clipboard
Copied
Hi @m1b , it's not working in the test file i attached for your reference itself! override was in objectStyleName "gs03 Annotationen" but it was not listed in output alerterbox!
Copy link to clipboard
Copied
Hi Mark @m1b , your script is not catching Overrides caused by displacing the anchored objectFrames in document. you tried releasing the AnchoredObject to catch the that pageItems with "item.anchoredObjectSettings.releaseAnchoredObject();". but that's not working. I got an idea to find that particular property and check the change in position of pageItems using @Kasyan Servetsky suggested method. After completing that process i am starting your script. Now its working fine I attached the updated script for others reference.
ObjectOverrided();
function ObjectOverrided(){
results = [],
arrAnchor = [];
var objStylesList = app.activeDocument.objectStyles;
mainanchor();
function mainanchor() {
var doc = app.activeDocument;
for(var b=0; b< objStylesList.length;b++){
var objStyleName = objStylesList[b].name;
var objStyle = doc.objectStyles.itemByName(objStyleName);
var foundItem;
try{
app.findChangeObjectOptions.objectType = ObjectTypes.TEXT_FRAMES_TYPE;
app.findObjectPreferences = app.changeObjectPreferences = NothingEnum.NOTHING;
app.findObjectPreferences.appliedObjectStyles = objStyleName;
var foundItems = doc.findObject();
for (var i = 0; i < foundItems.length; i++) {
foundItem = foundItems[i];
ckeckProperties(foundItem, objStyle);
}
}catch(e){};
}
}
function ckeckProperties(item, objStyle) {
try{
var objectAnchor = item.appliedObjectStyle.anchoredObjectSettings.anchorXoffset;
var pageItemAnchor = item.anchoredObjectSettings.anchorYoffset;
if ((objectAnchor !=pageItemAnchor)) {
arrAnchor.push("Page " + item.parentPage.name +": "+ '\t \t' + item.appliedObjectStyle.name);
}
}catch(e){};
}
/**
* List pages with object style overrides.
**
* @author m1b
* @version 2022-07-21
* @discussion https://community.adobe.com/t5/indesign-discussions/how-to-find-whether-my-textframe-have-overriddenobjectstyle-or-not-in-my-document-using-javascript/m-p/13071120
**
* Important note:
* This is more strict than Indesign's own
* object-style-overridden criteria, so
* sometimes an object may show differences
* while not showing the (+) symbol in the
* Object Style panel.
*/
function main() {
if (app.documents == 0)
return;
// we are going to modify the document
// so open a temporary copy
var doc = openDocumentCopy(app.activeDocument, false);
if (doc == undefined)
return;
var items = doc.allPageItems,
documentOffsets = [];
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (!item.isValid)
continue;
if (hasObjectStyleOverrides(item)) {
if (item.parentPage) {
if (item.parentPage.parent.constructor.name != 'MasterSpread') {
results.push('Page ' + pad(item.parentPage.name + ':', 10) + '\t' + item.appliedObjectStyle.name);
documentOffsets.push(item.parentPage.documentOffset);
}
}
else {
// include items on pasteboard?
results.push('Page ' + pad('pb:', 10) + '\t' + item.appliedObjectStyle.name);
documentOffsets.push(Infinity);
}
}
}
// clean up
doc.close(SaveOptions.NO);
// sort by document offset
results = orderArraysTogether(documentOffsets, results);
}
/**
* Determines if item has ObjectStyle overrides
* and displays a list of the overriden properties.
*
* @author m1b
* @version 2022-07-18
* @discussion https://community.adobe.com/t5/indesign-discussions/how-to-find-whether-my-textframe-have-overriddenobjectstyle-or-not-in-my-document-using-javascript/m-p/13071120
*
* Note: this function is MORE STRICT
* than Indesign's normal determination
* of overridden-ness.
*
* @param {PageItem} item - an Indesign page item.
* @returns {Boolean} - if true, then object has overrides.
*/
function hasObjectStyleOverrides(item) {
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;
app.scriptPreferences.enableRedraw = false;
if (item == undefined || !item.isValid|| typeof item.exportFile !== 'function')
return;
//~ if (item.parent.constructor.name == 'Character')
//~ // must release anchored object
//~ item.anchoredObjectSettings.releaseAnchoredObject();
// make a duplicate item
dup = item.duplicate();
var itemSnippet = saveSnippet(dup, 'item.xml');
// with no objectStyle overrides
dup.clearObjectStyleOverrides();
// and make a snippet of that
dupSnippet = saveSnippet(dup, 'dup.xml');
// clean up
dup.remove();
// set up arguments for listDifferences
var comparisons = [
{ label: 'PageItem', file: itemSnippet },
{ label: 'Object Style', file: dupSnippet }
];
// in snippet XML, we don't care about these
var options = { ignoreThese: /x:xmpmeta/ };
// collect differences between the two
var differences = listDifferences(comparisons, options);
return differences.length > 0;
}
/**
* Compares XML text or XML text files and
* returns a text summary of differences.
* @author m1b
* @version 2022-07-18
*
* @example
* listDifferences(
* [
* { label: 'PageItem', file: itemSnippetFile },
* { label: 'Object Style', xml: dupSnippetText }
* ], {
* onlyThese: /text/i,
* ignoreThese: /color/i
* }
* );
*
* @param {Array} items - an array of items to compare
* @param {Object} [options] - object with options
* @param {String|RegExp} [options.onlyThese] - test to choose keys
* @param {String|RegExp} [options.ignoreThese] - test to exclude keys
* @returns {String} - the formatted different properties
*/
function listDifferences(items, options) {
var differences = [],
objs = [],
labels = [],
lines = [],
keys = [],
// constants
empty = '-- empty --';
// initialise the items
for (var i = 0; i < items.length; i++) {
var item = items[i];
// label
if (item.label == undefined)
item.label = String.fromCharCode(65 + k);
// file
if (
item.file != undefined
&& item.file.exists
)
item.obj = parseXML(readFile(item.file));
// string
if (typeof (item.xml) === 'string')
item.obj = parseXML(item.xml);
// xml
if (typeof (item.xml) === 'xml')
item.obj = parseXML(item.xml.toString());
// xml text
if (item.obj == undefined)
return ['Error: item "' + item.label + '" had no usable XML.'];
objs[i] = item.obj;
labels[i] = item.label;
lines[item.label] = [];
var itemKeys = item.obj.reflect.properties;
// collect the item's keys
for (var k = 0; k < itemKeys.length; k++)
if (item.obj.hasOwnProperty(itemKeys[k]))
keys.push(String(itemKeys[k]));
}
// combine keys from all items
var keys = uniquify(keys);
// expand the key/values
for (var k = 0; k < keys.length; k++) {
// current key to compare
var key = keys[k];
// add lines by expanding objs recursively
for (var o = 0; o < objs.length; o++)
lines[labels[o]] = lines[labels[o]].concat(expandLines(key, objs[o][key]));
}
// make an object that has an object for *every* key
// and store values in that object, using the item's label
var keys = [],
comparator = [];
for (var o = 0; o < objs.length; o++) {
var label = labels[o];
linesLoop:
for (var l = 0; l < lines[label].length; l++) {
var linesForKey = lines[label][l],
key = linesForKey[0],
value = linesForKey[1];
if (
options.ignoreThese != undefined
&& options.ignoreThese.test(key)
)
// user doesn't want this key
continue linesLoop;
if (
options.onlyThese != undefined
&& !options.onlyThese.test(key)
)
// not what user asked for
continue linesLoop;
// add to new master keys
keys.push(key);
if (comparator[key] == undefined)
// add line's key as an empty object
comparator[key] = [];
// comparator[key][label] = [];
// add value, using label as key
comparator[key][label] = value;
}
}
// tidy up new keys
var keys = uniquify(keys);
// check if values are different
// and add them to differences
for (var k = 0; k < keys.length; k++) {
var key = keys[k],
isDifferent = false,
// the value we check against
currentValue = null,
// will hold array for each object's line
linesForKey = [];
for (var o = 0; o < objs.length; o++) {
var label = labels[o],
v;
// add label for this object
if (
comparator[key] === undefined
|| !comparator[key].hasOwnProperty(label)
|| comparator[key][label] == undefined
|| comparator[key][label] == 'undefined'
)
v = empty;
else
v = comparator[key][label];
// store this line in case we need it
linesForKey.push([label, key, v]);
// initialise current value
if (currentValue === null)
currentValue = v;
// or check if values are different
else if (currentValue !== v)
isDifferent = true;
}
if (isDifferent === true)
differences.push(linesForKey);
}
return differences;
}
/**
* Format the output of listDifferences function.
* @param {Array} differences - listDifferences() output
*/
function formatDifferences(differences) {
var formatted = [],
labelWidth = 0,
// regex to tidy strings
firstTwoElements = /^([^\.]+\.[^\.]+\.)/,
linefeeds = /[\r\n]/g,
emptyBrackets = /\[\]/g,
tabs = /\t/g;
// calculate label width
for (var o = 0; o < differences[0].length; o++)
if (labelWidth < differences[0][o][0].length)
labelWidth = differences[0][o][0].length;
for (var d = 0; d < differences.length; d++) {
var linesForKey = [];
for (var o = 0; o < differences[d].length; o++)
linesForKey[o] = formatDifference.apply(null, differences[d][o]);
formatted.push(linesForKey.join('\n'));
}
return formatted.join('\n\n');
// helper functions
function formatDifference() { var a = arguments; return pad(a[0], labelWidth + 2) + '\t' + tidyKey(a[1]) + ': ' + tidyValue(a[2]) };
function tidyKey(key) { return key.replace(firstTwoElements, '').replace(emptyBrackets, '') };
function tidyValue(value) { return value.replace(linefeeds, '\\n').replace(tabs, '\\t') };
}
/**
* Convert a simple key/value object into
* array of [[key, value], [key, value], ...]
* if value is also an object, it will
* recursively convert that also.
* @param {String} key - the starting key
* @param {String|Array|Object} obj - the starting value
* @returns {Array} - [[key, value], [key, value], ...]
*/
function expandLines(key, obj, i) {
key = String(key);
var lines = [];
if (obj == undefined)
lines.push([key, 'undefined']);
else if (obj.constructor.name == 'String')
lines.push([key, obj]);
else if (obj.constructor.name == 'Array')
for (var i = 0; i < obj.length; i++)
lines = lines.concat(expandLines(key + '[' + i + ']', obj[i], i));
else if (obj.constructor.name == 'Object')
for (var k in obj)
if (obj.hasOwnProperty(k))
lines = lines.concat(expandLines(key + '.' + k, obj[k], i));
return lines;
}
/**
* Reads a file contents.
* @param {File} file - the file to read.
* @returns {String}
*/
function readFile(file) {
if (
file == undefined
|| !file.exists
)
return;
file.open('r');
var text = file.read();
file.close();
return text;
}
/**
* Exports item as an Indesign snippet (XML) file.
* @author m1b
* @param {PageItem} item - an Indesign page item.
* @param {String} name - the snippet file name (include extension).
*/
function saveSnippet(item, name) {
// put in temp folder
var snippetFile = File(Folder.temp.fsName + '/' + name);
// export as indesign snippet
item.exportFile(ExportFormat.INDESIGN_SNIPPET, snippetFile, false);
if (snippetFile.exists)
return snippetFile;
}
/**
* Show text in a multiline edit text field.
* @param {String} title - the title of the alerter
* @param {String|Array} obj - the text content (array of strings will work)
* @param {Number} [width] - the dialog width
* @param {Number} [height] - the dialog height
*/
function alerter(title, obj, width, height) {
width = width || -1;
height = height || -1;
if (obj instanceof Array)
obj = obj.join("\r");
var w = new Window("dialog", title),
et = w.add("edittext", undefined, obj, { multiline: true, scrolling: true });
et.maximumSize.height = w.maximumSize.height - 100;
et.minimumSize.height = 350;
et.minimumSize.width = 350;
et.preferredSize = [width, height];
w.add("button", undefined, "Close", { name: "ok", alignment: ['right', 'center'] });
w.show();
}
function uniquify(arr) {
var seen = {},
out = [],
j = 0;
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
if (seen[item] !== 1) {
seen[item] = 1;
out[j++] = item;
}
}
return out;
}
/**
* Simple XML parser
* @author Peter Sirka
* @url https://gist.github.com/petersirka/9e79b1d43cf6e579fc62
* @param {String} xml
* @return {Object}
*/
function parseXML(xml) {
var beg = -1;
var end = 0;
var tmp = 0;
var current = [];
var obj = {};
var from = -1;
while (true) {
beg = xml.indexOf('<', beg + 1);
if (beg === -1)
break;
end = xml.indexOf('>', beg + 1);
if (end === -1)
break;
var el = xml.substring(beg, end + 1);
var c = el[1];
if (c === '?' || c === '/') {
var o = current.pop();
if (from === -1 || o !== el.substring(2, el.length - 1))
continue;
var path = current.join('.') + '.' + o;
var value = xml.substring(from, beg);
if (typeof (obj[path]) === 'undefined')
obj[path] = value;
else if (obj[path] instanceof Array)
obj[path].push(value);
else
obj[path] = [obj[path], value];
from = -1;
continue;
}
tmp = el.indexOf(' ');
var hasAttributes = true;
if (tmp === -1) {
tmp = el.length - 1;
hasAttributes = false;
}
from = beg + el.length;
var isSingle = el[el.length - 2] === '/';
var name = el.substring(1, tmp);
if (!isSingle)
current.push(name);
if (!hasAttributes)
continue;
var match = el.match(/\w+\=\".*?\"/g);
if (match === null)
continue;
var attr = {};
var length = match.length;
for (var i = 0; i < length; i++) {
var index = match[i].indexOf('"');
attr[match[i].substring(0, index - 1)] = match[i].substring(index + 1, match[i].length - 1);
}
obj[current.join('.') + (isSingle ? '.' + name : '') + '[]'] = attr;
}
return obj;
};
/**
* Pad a string.
* @author m1b
* @param {String} str - the text to pad.
* @param {Number} width - number of characters to pad to.
* @param {String} [paddingChar] - the character to pad with.
* @param {Boolean} [rightAligned] - if true, will right align text.
*/
function pad(str, width, paddingChar, rightAligned) {
width = Number(width);
paddingChar = paddingChar || ' ';
var padding = '';
while (width > padding.length)
padding += paddingChar;
if (rightAligned)
str = (padding + str).slice(-width);
else
str = (str + padding).slice(0, width);
return str
}
/**
* Open a copy of document.
* `doc` argument can be a
* Document or a File pointing
* to a document. Document
* must be saved first.
* @author m1b
* @version 2022-07-21
* @param {Document|File} doc - the document to copy.
* @param {Boolean} showWindow - if false, will open hidden.
* @return {Document} - the unsaved copy.
*/
function openDocumentCopy(doc, showWindow) {
if (doc.constructor.name == 'Document') {
if (!doc.saved) {
alert('Please save document before running script.');
return;
}
doc = doc.fullName;
}
if (doc.constructor.name == 'File') {
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;
return app.open(doc, (showWindow !== false), OpenOptions.OPEN_COPY);
}
}
/**
* Sort arrays based on sorting
* the first numerical array.
**
* @author m1b
* @version 2022-07-21
**
* @example
* orderArraysTogether([1000, 10, 20, 30],['a', 'b', 'c', 'd']);
* -> // ['b','c','d','a']
**
* Note: function outputs new arrays,
* without altering input arrays.
* Will output either a single sorted
* array (if only one extra array was
* supplied) or an array of sorted arrays.
* @param {Array} orderArray - sort this and match.
* @param {Array} arr1 - array to sort.
* @param {Array} [arr2] - 2nd array to sort.
* @param {Array} [arr3] - 3rd ...
* @return {Array}
*/
function orderArraysTogether(orderer, arr1, arr2) {
var len = orderer.length,
sortedArrays = [],
// the sorted orderer can be used
// to access the other arrays
sortedOrderer = orderer
.reflect.properties.slice(0, len)
.sort(function (a, b) { return orderer[a] - orderer[b] });
// loop over arrays, not including the orderArray
for (var i = 1; i < arguments.length; i++) {
if (arguments[i].length != len)
throw Error('orderArraysTogether: all arrays must have same length.');
sortedArrays[i - 1] = [];
// make new array in sorted order
for (var j = 0; j < len; j++)
sortedArrays[i - 1][j] = arguments[i][sortedOrderer[j]];
}
// return a single sorted array
// or an array of sorted arrays
return (sortedArrays.length === 1)
? sortedArrays[0]
: sortedArrays;
}
// polyfill
if (!String.prototype.indexOf) {
String.prototype.indexOf = function (obj) {
var i;
for (i = 0; i < this.length; i++) if (this[i] == obj) return i;
return -1;
}
}
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Show ObjectStyle Overrides');
for(var i=0; i<arrAnchor.length; i++){
results.push(arrAnchor[i]);
}
// display results
alerter('Overridden Object Styles', results.join('\n'));
}
Copy link to clipboard
Copied
Okay I've reproduced the problem, but it can't be solved the way I am doing it now. I'll have to think of another way. The problem is that the ObjectStyle has anchoredObjectSettings, but I need to release it (un-anchor the item) in order to export the snippet xml. This is fine in most cases, but in this case, as soon as the anchored item is released, it no longer technically is overriding the object style. So the script won't flag it. This is a tricky situation. I'll have a think about it. Might be a while before I get back to it though.
- Mark
Copy link to clipboard
Copied
Hi @Laubender, thanks. I have now updated script to use allPageItems. We will see how this works in practice. On my simple test documents it was okay, but on a huge complex job it might crash. An easy possible solution would be to limit the script, via a parameter, to only certain target objects, eg. TextFrames.
- Mark
Copy link to clipboard
Copied
@Laubender I've just noticed that Peter Sirka's parseXML function is having a problem with overwriting multiple elements in the object. For example it overwrites "RootObjectStyleGroup.ObjectStyle", so it only shows the last ObjectStyle in the snippet XML. When I get a chance I'll have to fix that or it won't work properly in many cases.
- Mark