How to find whether my textFrame have OverriddenObjectStyle or not in my document using javascript

Participant ,
Jul 15, 2022 Jul 15, 2022

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!

TOPICS
How to , Scripting

Views

481

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 , Jul 15, 2022 Jul 15, 2022
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...

Likes

Translate

Translate
Guru ,
Jul 15, 2022 Jul 15, 2022

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);
	}
}

 

 

overridden object styles.png



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.

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
Participant ,
Jul 18, 2022 Jul 18, 2022

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.

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
Participant ,
Jul 20, 2022 Jul 20, 2022

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.

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 ,
Jul 15, 2022 Jul 15, 2022

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');

 

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 ,
Jul 15, 2022 Jul 15, 2022

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);

}

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 ,
Jul 15, 2022 Jul 15, 2022

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).

Screen Shot 2022-07-18 at 10.31.35 pm.png

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.

Screen Shot 2022-07-18 at 10.39.39 pm.png

 - Mark

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
Participant ,
Jul 18, 2022 Jul 18, 2022

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!

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
Participant ,
Jul 18, 2022 Jul 18, 2022

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.

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 ,
Jul 18, 2022 Jul 18, 2022

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

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 ,
Jul 18, 2022 Jul 18, 2022

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:

Screen Shot 2022-07-18 at 11.10.03 pm.png

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.

 

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
Participant ,
Jul 18, 2022 Jul 18, 2022

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 😧 [Basic Graphics Frame]
Page 😧 [Basic Text Frame]
Page 😧 [Basic Text Frame]
Page 😧 [Basic Text Frame]
Page 😧 [None]
Page 😧 [None]
Page 😧 [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]

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 ,
Jul 18, 2022 Jul 18, 2022

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

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
Participant ,
Jul 18, 2022 Jul 18, 2022

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!

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
Participant ,
Jul 20, 2022 Jul 20, 2022

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!

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 ,
Jul 20, 2022 Jul 20, 2022

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

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
Participant ,
Jul 20, 2022 Jul 20, 2022

Copy link to clipboard

Copied

Haha, I'd rather wait then @m1b .Thanks for the response Mark!

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 ,
Jul 20, 2022 Jul 20, 2022

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 )

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
Participant ,
Jul 20, 2022 Jul 20, 2022

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!

 

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 ,
Jul 20, 2022 Jul 20, 2022

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

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
Participant ,
Jul 20, 2022 Jul 20, 2022

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!

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
Participant ,
Jul 21, 2022 Jul 21, 2022

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'));
}

 

 

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 ,
Jul 21, 2022 Jul 21, 2022

Copy link to clipboard

Copied

LATEST

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

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 ,
Jul 20, 2022 Jul 20, 2022

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

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 ,
Jul 20, 2022 Jul 20, 2022

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

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