这是用户在 2025-6-4 2:01 为 https://community.adobe.com/t5/illustrator-discussions/script-to-return-or-restore-rotation-of-multi... 保存的双语快照页面,由 沉浸式翻译 提供双语支持。了解如何保存?

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

返回或恢复多个选定对象的旋转的脚本

Feb 11, 2025 社区初学者,2025年2月11日

你好,将会有一个脚本,用于将每个选定对象的旋转恢复到零度"0°"。我向 ChatGPT 询问了,但不幸的是他没理解。我选了几个不同旋转角度的对象,手动操作非常费时费力。

主题
绘图和设计 , 实验 , 功能请求 , 脚本
971
翻译
举报
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 正确答案

Community Expert , Feb 17, 2025 Feb 17, 2025

好的 @vectora98504848 这里再次提供脚本,现在包含 René改进的函数。请告诉我们效果如何!

- 标记

 

/**
 * @file Unrotate Selected Items 2.js
 *
 * Makes an attempt to remove any recorded rotation
 * that can be derived from the selected items.
 * Finds rotation either in the item's BBAccumRotation
 * tag or the item's matrix.
 *
 * @author m1b
 * @version 2025-02-17
 * @discussion https://community.adobe.com/t5/illustrator-discussions/script-to-return-or-restore-rotation-of-mul
...
翻译
Adobe
Cover Image
illustrator.svg
操作方法

旋转和镜像对象

Cover Image
illustrator.svg
操作方法

使用旋转工具旋转对象

Cover Image
illustrator.svg
操作方法

使用镜像工具翻转对象

replies 12 Replies 12
Feb 13, 2025 社区专家,2025年2月13日

@vectora98504848,要“取消旋转”某物——恢复到零度旋转——首先需要获取该项目的旋转量。在 Illustrator 中,页面项目没有绝对旋转量,但它通常会(!)通过分配给项目的“BBAccumRotation”标签来跟踪旋转。另一种可能性是,如果该项目是一个置入图像或文本框架,那么它也可能有一个 矩阵 ,有时可以从其中推导出旋转值。

 

我编写了一个脚本,用于获取旋转量,然后取消旋转选定的项目。请告诉我结果如何,但请记住,对于某些对象,无法以这种方式确定旋转值。这个问题还有其他解决方法,但它们不是通用的——我们必须更了解项目的类型。

- Mark

 

 

/**
 * @file Unrotate Selected Items.js
 *
 * Makes an attempt to remove any recorded rotation
 * that can be derived from the selected items.
 * Finds rotation either in the item's BBAccumRotation
 * tag or the item's matrix.
 *
 * @author m1b
 * @version 2025-02-14
 * @discussion https://community.adobe.com/t5/illustrator-discussions/script-to-return-or-restore-rotation-of-multiple-selected-objects/m-p/15146692
 */
(function () {

    var doc = app.activeDocument;

    var items = getItems({
        from: doc.selection,
        getPageItems: true,
        getGroupItems: true,
    });

    if (0 === items.length)
        return alert('Please select some items to rotate and try again.');

    for (var i = 0; i < items.length; i++)
        unrotate(items[i]);

})();

/**
 * Attempt to return a page item to unrotated state.
 * Important: will only work if the item has a valid
 * BBAccumRotation tag or a matrix that is ammenable
 * to derivation of the rotation.
 * @author m1b
 * @version 2025-02-13
 * @param {PageItem} item - the item to unrotate.
 * @return {Number} - the amount of rotation, in degrees.
 */
function unrotate(item) {

    var accumRotation = undefined,
        rotationTag;

    if (item.hasOwnProperty('tags')) {

        // derive rotation from the BBAccumRotation tag
        rotationTag = getThing(item.tags, 'name', 'BBAccumRotation');

        if (rotationTag)
            accumRotation = rotationTag.value * 180 / Math.PI;

    }

    if (
        undefined == accumRotation
        && item.hasOwnProperty('matrix')
    )
        // derive rotation from the matrix
        accumRotation = getRotationFromMatrix(item.matrix);

    if (undefined == accumRotation)
        return;

    // rotate the item
    item.rotate(- accumRotation);

    if (rotationTag)
        // update tag
        rotationTag.value = 0;

    return accumRotation;

};

/**
 * Returns a thing with matching property.
 * If `key` is undefined, evaluate the object itself.
 * @author m1b
 * @version 2024-04-21
 * @param {Array|Collection} things - the things to look through.
 * @param {String} [key] - the property name (default: undefined).
 * @param {*} value - the value to match.
 */
function getThing(things, key, value) {

    for (var i = 0, obj; i < things.length; i++)
        if ((undefined == key ? things[i] : things[i][key]) == value)
            return things[i];

};

/**
 * Returns the rotation amount, in degrees,
 * of the given (not skewed) matrix.
 * @author m1b
 * @version 2023-10-20
 * @param {Matrix} matrix - an Illustrator Matrix.
 * @returns {Number}
 */
function getRotationFromMatrix(matrix) {

    if (!matrix.hasOwnProperty('mValueA'))
        throw new Error('getRotationFromMatrix: bad `matrix` supplied.');

    // scaling factors
    var scaleX = Math.sqrt(matrix.mValueA * matrix.mValueA + matrix.mValueC * matrix.mValueC);
    // scaleY = Math.sqrt(matrix.mValueB * matrix.mValueB + matrix.mValueD * matrix.mValueD);

    // rotation angle
    var radians = Math.acos(matrix.mValueA / scaleX),
        degrees = radians * (180 / Math.PI);

    return Math.round(degrees * 1000) / 1000;

};


/** ------------------------------------------------------------------- *
 *  GET ITEMS                                                           *
 * -------------------------------------------------------------------- *
 * @author m1b                                                          *
 * @version 2024-03-01                                                  *
 * -------------------------------------------------------------------- *
 * Collects page items from a `from` source, eg. a Document, Layer,     *
 * GroupItem, or Array. Will look inside group items up to `maxDepth`.  *
 * Search can be filtered using `filter` function. Note that the        *
 * filter function is evaluated last in the filtering process.          *
 * -------------------------------------------------------------------- *
 * Example 1. Get all items in document:                                *
 *                                                                      *
 *    var myItems = getItems({ from: app.activeDocument });             *
 *                                                                      *
 * -------------------------------------------------------------------- *
 * Example 2. Get all selected items except groups:                     *
 *                                                                      *
 *    var myItems = getItems({                                          *
 *      from: app.activeDocument.selection,                             *
 *      getGroupItems: false,                                           *
 *    });                                                               *
 *                                                                      *
 * -------------------------------------------------------------------- *
 * Example 3. Using `filter` function to choose item type:              *
 *                                                                      *
 *    var myItems = getItems({                                          *
 *      from: app.activeDocument,                                       *
 *      filter: function (item) {                                       *
 *        return (                                                      *
 *          'PathItem' === item.typename                                *
 *          || 'CompoundPathItem' === item.typename                     *
 *        );                                                            *
 *      }                                                               *
 *    });                                                               *
 *                                                                      *
 * -------------------------------------------------------------------- *
 * Example 4. Using `filter` function:                                  *
 *                                                                      *
 *    var myItems = getItems({                                          *
 *      from: app.activeDocument,                                       *
 *      filter: onlyPngLinks                                            *
 *    });                                                               *
 *                                                                      *
 *    function onlyPngLinks(item, depth) {                              *
 *       return (                                                       *
 *           'PlacedItem' === item.typename                             *
 *           && '.png' === item.file.name.slice(-4).toLowerCase()       *
 *       );                                                             *
 *    };                                                                *
 *                                                                      *
 * -------------------------------------------------------------------- *
 * Example 4. Using the `filter` function for custom collecting:        *
 *                                                                      *
 * This example bypasses the normal returned array and instead          *
 * captures items in an "external" array `itemsByDepth`.                *
 *                                                                      *
 *    var itemsByDepth = [];                                            *
 *                                                                      *
 *    function getItemsByDepth(item, depth) {                           *
 *      if (undefined == itemsByDepth[depth])                           *
 *        itemsByDepth[depth] = [];                                     *
 *      itemsByDepth[depth].push(item);                                 *
 *    };                                                                *
 *                                                                      *
 *    getItems({                                                        *
 *      from: app.activeDocument,                                       *
 *      filter: getItemsByDepth                                         *
 *    });                                                               *
 *                                                                      *
 * -------------------------------------------------------------------- *
 * @param {Object} options - parameters
 * @param {PageItem|Array<PageItem>|Document|Layer} options.from - the thing(s) to look in, eg. a selection.
 * @param {Function} [options.filter] - function that, given a found item, must return true (default: no filtering).
 * @param {Boolean} [options.getPageItems] - whether to include page items in returned items (default: true).
 * @param {Boolean} [options.getGroupItems] - whether to include GroupItems in returned items (default: true).
 * @param {Boolean} [options.getLayers] - whether to include Layers in returned items (default: false).
 * @param {Boolean} [options.getHiddenItems] - whether to include hidden items in returned items (default: true).
 * @param {Boolean} [options.getLockedItems] - whether to include locked items in returned items (default: true).
 * @param {Boolean} [options.getGuideItems] - whether to include guide items in returned items (default: false).
 * @param {Number} [options.maxDepth] - deepest folder level (recursion depth limit) (default: 99).
 * @param {Boolean} [options.returnFirstMatch] - whether to return only the first found item (default: false).
 * @param {Number} [depth] - the current depth (private).
 * @returns {Array|PageItem} - all the found items in a flat array, or the first found item if `returnFirstMatch`.
 */
function getItems(options, depth) {

    // defaults
    options = options || {};

    var found = [],
        depth = depth || 0,
        items = options.from;

    if (!options.initialized)
        // once-off initialization
        if (!initialize())
            return [];

    itemsLoop:
    for (var i = 0, item, len = items.length; i < len; i++) {

        item = items[i];

        if (
            false === excludeFilter(item)
            && true === includeFilter(item)
        ) {
            // item found!
            found.push(item);

            if (options.returnFirstMatch)
                break itemsLoop;
        }

        if (
            'GroupItem' !== item.constructor.name
            && 'Layer' !== item.typename
        )
            // only items with children from here
            continue itemsLoop;

        if (
            excludeHidden(item)
            || excludeLocked(item)
        )
            // don't look into excluded containers
            continue itemsLoop;

        if (depth >= options.maxDepth)
            // don't go deeper
            continue itemsLoop;

        // set up for the next depth
        options.from = item.pageItems;

        // look inside
        found = found.concat(getItems(options, depth + 1));

    }

    // this level done
    if (true == options.returnFirstMatch)
        return found[0];
    else
        return found;

    /**
     * Returns true when the item should be not be found.
     * @param {PageItem|Layer} item
     * @returns {Boolean}
     */
    function excludeFilter(item) {

        return (

            isAlreadyFound(item)

            // is hidden
            || excludeHidden(item)

            // is locked
            || excludeLocked(item)

            // is guide
            || (
                false === options.getGuideItems
                && true === item.guides
            )

            // is layer
            || (
                false === options.getLayers
                && 'Layer' === item.typename
            )

            // is group item
            || (
                false === options.getGroupItems
                && 'GroupItem' === item.typename
            )

            // is page item
            || (
                false === options.getPageItems
                && 'GroupItem' !== item.typename
                && undefined != item.uuid
            )

        );

    };

    /**
     * Returns true when the item should be included.
     * @param {PageItem|Layer} item
     * @returns {Boolean}
     */
    function includeFilter(item) {

        return (
            undefined == options.filter
            || options.filter(item, depth)
        );

    };

    /**
     * Returns true when the item should
     * be excluded because it is hidden.
     * @param {PageItem|Layer} item
     * @returns {Boolean}
     */
    function excludeHidden(item) {

        return (
            false === options.getHiddenItems
            && (
                true === item.hidden
                || false === item.visible
            )
        );

    };

    /**
     * Returns true when the item should
     * be excluded because it is locked.
     * @param {PageItem|Layer} item
     * @returns {Boolean}
     */
    function excludeLocked(item) {

        return (
            false === options.getLockedItems
            && true === item.locked
        );

    };

    /**
     * Returns true if item was already
     * found, and marks item as found,
     * to avoid finding same item twice.
     * @param {PageItem|Layer} item
     * @returns {Boolean}
     */
    function isAlreadyFound(item) {

        var uuid = item.hasOwnProperty('uuid')
            ? item.uuid
            : item.typename + item.zOrderPosition,

            isFound = !!options.isFound[uuid];

        options.isFound[uuid] = true;

        return isFound;

    }

    /**
     * Returns the initialised `options` object.
     * @returns {Object}
     */
    function initialize() {

        // make a new object, so we don't pollute the original
        options = {
            initialized: true,
            depth: 0,
            isFound: {},
            filter: options.filter,
            getPageItems: false !== options.getPageItems,
            getGroupItems: false !== options.getGroupItems,
            getLayers: true === options.getLayers,
            getHiddenItems: false !== options.getHiddenItems,
            getLockedItems: false !== options.getLockedItems,
            getGuideItems: true === options.getGuideItems,
            maxDepth: options.maxDepth,
            returnFirstMatch: options.returnFirstMatch,
        };

        if (
            undefined == options.maxDepth
            || !options.maxDepth instanceof Number
        )
            options.maxDepth = 99;

        // items is a single layer
        if ('Layer' === items.typename)
            items = [items];

        // items is a document
        else if ('Document' === items.constructor.name) {

            var layers = items.layers;
            items = [];

            for (var i = 0; i < layers.length; i++)
                items.push(layers[i]);

        }

        else if ('Array' !== items.constructor.name)
            items = [items];

        return items.length > 0;

    };

};

编辑 2025-02-14:添加了 `getItems` 函数以递归搜索组内部。

 

翻译
举报
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Feb 13, 2025 社区新手,2025年2月13日

你好 @m1b,感谢你提供的脚本,它对简单对象效果很好,但当对象是组合对象或带有剪贴蒙版的对象时,情况就不同了。有没有什么方法可以解决这个问题?

翻译
举报
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Feb 13, 2025 社区专家,2025年2月13日

哦,是的,你是对的!在这种情况下,我需要更复杂一些来获取选中的对象...

我已经更新了上面的脚本。请再试一次。

- Mark

 

P.S. 注意,Illustrator 并非直接为群组对象分配旋转值,因此群组中的内容(在大多数情况下!)会取消旋转,但群组本身不会。

翻译
举报
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Feb 13, 2025 社区新手,2025年2月13日

但它似乎没有正确处理组和高光遮罩。主要问题是脚本在组和高光遮罩内单独旋转项目,而不是将整个组或高光遮罩作为一个单一对象进行旋转。

翻译
举报
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Feb 13, 2025 社区新手,2025年2月13日
 
翻译
举报
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Feb 13, 2025 社区专家,2025年2月13日

@vectora98504848 @yep 那正是我在上面附言中提到的。不幸的是,群组似乎没有旋转标签或任何获取旋转值的方法。

 

你觉得以下想法怎么样:对于每个群组(剪贴群组也是一种群组),我读取第一个路径项的旋转,然后通过该数值旋转群组 ,不对该值之外的内容做任何处理?有机会我会试试看。

- 马克

翻译
举报
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Feb 14, 2025 倡导者,2025年2月14日

你好马克,我(昨晚)擅自修改了你的 unrotate()函数。

以处理组、透明轨迹和蒙版。

这看起来可以正常工作吗?

 

function unrotate(item) {
    var grp = false, trs = false;
    var accumRotation = undefined,
        rotationTag;

    if (item.hasOwnProperty('tags')) {
      if (item.typename == "GroupItem" || item.typename == "CompoundPathItem") {
        if (item.typename == "CompoundPathItem") {
          rotationTag = getThing(item.pathItems[0].tags, 'name', 'BBAccumRotation');
          trs = true;
        }
          else {
           rotationTag = getThing(item.pageItems[0].tags, 'name', 'BBAccumRotation');
          grp = true;
          }
      }
      else rotationTag = getThing(item.tags, 'name', 'BBAccumRotation');

    if (rotationTag)
      accumRotation = rotationTag.value * 180 / Math.PI;
    }

    if (
        undefined == accumRotation
        && item.hasOwnProperty('matrix')
    )
        // derive rotation from the matrix
        accumRotation = getRotationFromMatrix(item.matrix);

    if (undefined == accumRotation)
        return;

    // rotate the item
    item.rotate(- accumRotation);

    if (rotationTag)
        // update tag
          var pi;
          if (grp)
            for (var i = 0; i < item.pageItems.length; i++ ){
              pi = item.pageItems[i];
              rotationTag = getThing(pi.tags, 'name', 'BBAccumRotation');
              if (rotationTag != undefined) {rotationTag.value = 0;}
              }
            }
         if (trs) {
            pi = item.pathItems[0];
              rotationTag = getThing(pi.tags, 'name', 'BBAccumRotation');
              if (rotationTag != undefined) {rotationTag.value = 0;}
                rotationTag = getThing(item.tags, 'name', 'BBAccumRotation');
                if (rotationTag != undefined) {rotationTag.value = 0;}
         }
        if (!trs && !grp) rotationTag.value = 0;
};

 

René

翻译
举报
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Feb 15, 2025 社区专家,2025年2月15日

@renél80416020 太棒了!谢谢 René!我会在有机会时添加它,OP 可以进行测试。我忘记复合路径项目也没有 BBAccumRotation 标签,所以那是为了包含那个逻辑!

- Mark

 

翻译
举报
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Feb 17, 2025 社区专家,2025年2月17日

好的 @vectora98504848 这里再次提供脚本,现在包含 René改进的函数。请告诉我们效果如何!

- Mark

 

/**
 * @file Unrotate Selected Items 2.js
 *
 * Makes an attempt to remove any recorded rotation
 * that can be derived from the selected items.
 * Finds rotation either in the item's BBAccumRotation
 * tag or the item's matrix.
 *
 * @author m1b
 * @version 2025-02-17
 * @discussion https://community.adobe.com/t5/illustrator-discussions/script-to-return-or-restore-rotation-of-multiple-selected-objects/m-p/15146692
 */
(function () {

    var doc = app.activeDocument;

    var items = getItems({
        from: doc.selection,
        getPageItems: true,
        getGroupItems: true,
    });

    if (0 === items.length)
        return alert('Please select some items to rotate and try again.');

    for (var i = 0; i < items.length; i++)
        unrotate2(items[i]);

})();

/**
 * Attempt to return a page item to unrotated state.
 * Modified by renél80416020 to apply rotation
 * to group items and compound path items correctly.
 * Important: will only work if the item has a valid
 * BBAccumRotation tag or a matrix that is ammenable
 * to derivation of the rotation.
 * @author m1b and renél80416020
 * @version 2025-02-14
 * @param {PageItem} item - the item to unrotate.
 * @return {Number} - the amount of rotation, in degrees.
 */
function unrotate2(item) {

    var grp = false, trs = false;

    var accumRotation = undefined,
        rotationTag;

    if (item.hasOwnProperty('tags')) {

        if (item.typename == "GroupItem" || item.typename == "CompoundPathItem") {
            if (item.typename == "CompoundPathItem") {
                rotationTag = getThing(item.pathItems[0].tags, 'name', 'BBAccumRotation');
                trs = true;
            }

            else {
                rotationTag = getThing(item.pageItems[0].tags, 'name', 'BBAccumRotation');
                grp = true;
            }
        }
        else rotationTag = getThing(item.tags, 'name', 'BBAccumRotation');

        if (rotationTag)
            accumRotation = rotationTag.value * 180 / Math.PI;
    }

    if (
        undefined == accumRotation
        && item.hasOwnProperty('matrix')
    )
        // derive rotation from the matrix
        accumRotation = getRotationFromMatrix(item.matrix);

    if (undefined == accumRotation)
        return;

    // rotate the item
    item.rotate(- accumRotation);

    if (rotationTag) {
        // update tag
        var pi;

        if (grp)
            for (var i = 0; i < item.pageItems.length; i++) {
                pi = item.pageItems[i];
                rotationTag = getThing(pi.tags, 'name', 'BBAccumRotation');
                if (rotationTag != undefined) { rotationTag.value = 0; }
            }

        if (trs) {
            pi = item.pathItems[0];
            rotationTag = getThing(pi.tags, 'name', 'BBAccumRotation');
            if (rotationTag != undefined) { rotationTag.value = 0; }
            rotationTag = getThing(item.tags, 'name', 'BBAccumRotation');
            if (rotationTag != undefined) { rotationTag.value = 0; }
        }

        if (!trs && !grp)
            rotationTag.value = 0;

    }

};

/**
 * Returns a thing with matching property.
 * If `key` is undefined, evaluate the object itself.
 * @author m1b
 * @version 2024-04-21
 * @param {Array|Collection} things - the things to look through.
 * @param {String} [key] - the property name (default: undefined).
 * @param {*} value - the value to match.
 */
function getThing(things, key, value) {

    for (var i = 0, obj; i < things.length; i++)
        if ((undefined == key ? things[i] : things[i][key]) == value)
            return things[i];

};

/**
 * Returns the rotation amount, in degrees,
 * of the given (not skewed) matrix.
 * @author m1b
 * @version 2023-10-20
 * @param {Matrix} matrix - an Illustrator Matrix.
 * @returns {Number}
 */
function getRotationFromMatrix(matrix) {

    if (!matrix.hasOwnProperty('mValueA'))
        throw new Error('getRotationFromMatrix: bad `matrix` supplied.');

    // scaling factors
    var scaleX = Math.sqrt(matrix.mValueA * matrix.mValueA + matrix.mValueC * matrix.mValueC);
    // scaleY = Math.sqrt(matrix.mValueB * matrix.mValueB + matrix.mValueD * matrix.mValueD);

    // rotation angle
    var radians = Math.acos(matrix.mValueA / scaleX),
        degrees = radians * (180 / Math.PI);

    return Math.round(degrees * 1000) / 1000;

};

/** ------------------------------------------------------------------- *
 *  GET ITEMS                                                           *
 * -------------------------------------------------------------------- *
 * @author m1b                                                          *
 * @version 2024-03-01                                                  *
 * -------------------------------------------------------------------- *
 * Collects page items from a `from` source, eg. a Document, Layer,     *
 * GroupItem, or Array. Will look inside group items up to `maxDepth`.  *
 * Search can be filtered using `filter` function. Note that the        *
 * filter function is evaluated last in the filtering process.          *
 * -------------------------------------------------------------------- *
 * Example 1. Get all items in document:                                *
 *                                                                      *
 *    var myItems = getItems({ from: app.activeDocument });             *
 *                                                                      *
 * -------------------------------------------------------------------- *
 * Example 2. Get all selected items except groups:                     *
 *                                                                      *
 *    var myItems = getItems({                                          *
 *      from: app.activeDocument.selection,                             *
 *      getGroupItems: false,                                           *
 *    });                                                               *
 *                                                                      *
 * -------------------------------------------------------------------- *
 * Example 3. Using `filter` function to choose item type:              *
 *                                                                      *
 *    var myItems = getItems({                                          *
 *      from: app.activeDocument,                                       *
 *      filter: function (item) {                                       *
 *        return (                                                      *
 *          'PathItem' === item.typename                                *
 *          || 'CompoundPathItem' === item.typename                     *
 *        );                                                            *
 *      }                                                               *
 *    });                                                               *
 *                                                                      *
 * -------------------------------------------------------------------- *
 * Example 4. Using `filter` function:                                  *
 *                                                                      *
 *    var myItems = getItems({                                          *
 *      from: app.activeDocument,                                       *
 *      filter: onlyPngLinks                                            *
 *    });                                                               *
 *                                                                      *
 *    function onlyPngLinks(item, depth) {                              *
 *       return (                                                       *
 *           'PlacedItem' === item.typename                             *
 *           && '.png' === item.file.name.slice(-4).toLowerCase()       *
 *       );                                                             *
 *    };                                                                *
 *                                                                      *
 * -------------------------------------------------------------------- *
 * Example 4. Using the `filter` function for custom collecting:        *
 *                                                                      *
 * This example bypasses the normal returned array and instead          *
 * captures items in an "external" array `itemsByDepth`.                *
 *                                                                      *
 *    var itemsByDepth = [];                                            *
 *                                                                      *
 *    function getItemsByDepth(item, depth) {                           *
 *      if (undefined == itemsByDepth[depth])                           *
 *        itemsByDepth[depth] = [];                                     *
 *      itemsByDepth[depth].push(item);                                 *
 *    };                                                                *
 *                                                                      *
 *    getItems({                                                        *
 *      from: app.activeDocument,                                       *
 *      filter: getItemsByDepth                                         *
 *    });                                                               *
 *                                                                      *
 * -------------------------------------------------------------------- *
 * @param {Object} options - parameters
 * @param {PageItem|Array<PageItem>|Document|Layer} options.from - the thing(s) to look in, eg. a selection.
 * @param {Function} [options.filter] - function that, given a found item, must return true (default: no filtering).
 * @param {Boolean} [options.getPageItems] - whether to include page items in returned items (default: true).
 * @param {Boolean} [options.getGroupItems] - whether to include GroupItems in returned items (default: true).
 * @param {Boolean} [options.getLayers] - whether to include Layers in returned items (default: false).
 * @param {Boolean} [options.getHiddenItems] - whether to include hidden items in returned items (default: true).
 * @param {Boolean} [options.getLockedItems] - whether to include locked items in returned items (default: true).
 * @param {Boolean} [options.getGuideItems] - whether to include guide items in returned items (default: false).
 * @param {Number} [options.maxDepth] - deepest folder level (recursion depth limit) (default: 99).
 * @param {Boolean} [options.returnFirstMatch] - whether to return only the first found item (default: false).
 * @param {Number} [depth] - the current depth (private).
 * @returns {Array|PageItem} - all the found items in a flat array, or the first found item if `returnFirstMatch`.
 */
function getItems(options, depth) {

    // defaults
    options = options || {};

    var found = [],
        depth = depth || 0,
        items = options.from;

    if (!options.initialized)
        // once-off initialization
        if (!initialize())
            return [];

    itemsLoop:
    for (var i = 0, item, len = items.length; i < len; i++) {

        item = items[i];

        if (
            false === excludeFilter(item)
            && true === includeFilter(item)
        ) {
            // item found!
            found.push(item);

            if (options.returnFirstMatch)
                break itemsLoop;
        }

        if (
            'GroupItem' !== item.constructor.name
            && 'Layer' !== item.typename
        )
            // only items with children from here
            continue itemsLoop;

        if (
            excludeHidden(item)
            || excludeLocked(item)
        )
            // don't look into excluded containers
            continue itemsLoop;

        if (depth >= options.maxDepth)
            // don't go deeper
            continue itemsLoop;

        // set up for the next depth
        options.from = item.pageItems;

        // look inside
        found = found.concat(getItems(options, depth + 1));

    }

    // this level done
    if (true == options.returnFirstMatch)
        return found[0];
    else
        return found;

    /**
     * Returns true when the item should be not be found.
     * @param {PageItem|Layer} item
     * @returns {Boolean}
     */
    function excludeFilter(item) {

        return (

            isAlreadyFound(item)

            // is hidden
            || excludeHidden(item)

            // is locked
            || excludeLocked(item)

            // is guide
            || (
                false === options.getGuideItems
                && true === item.guides
            )

            // is layer
            || (
                false === options.getLayers
                && 'Layer' === item.typename
            )

            // is group item
            || (
                false === options.getGroupItems
                && 'GroupItem' === item.typename
            )

            // is page item
            || (
                false === options.getPageItems
                && 'GroupItem' !== item.typename
                && undefined != item.uuid
            )

        );

    };

    /**
     * Returns true when the item should be included.
     * @param {PageItem|Layer} item
     * @returns {Boolean}
     */
    function includeFilter(item) {

        return (
            undefined == options.filter
            || options.filter(item, depth)
        );

    };

    /**
     * Returns true when the item should
     * be excluded because it is hidden.
     * @param {PageItem|Layer} item
     * @returns {Boolean}
     */
    function excludeHidden(item) {

        return (
            false === options.getHiddenItems
            && (
                true === item.hidden
                || false === item.visible
            )
        );

    };

    /**
     * Returns true when the item should
     * be excluded because it is locked.
     * @param {PageItem|Layer} item
     * @returns {Boolean}
     */
    function excludeLocked(item) {

        return (
            false === options.getLockedItems
            && true === item.locked
        );

    };

    /**
     * Returns true if item was already
     * found, and marks item as found,
     * to avoid finding same item twice.
     * @param {PageItem|Layer} item
     * @returns {Boolean}
     */
    function isAlreadyFound(item) {

        var uuid = item.hasOwnProperty('uuid')
            ? item.uuid
            : item.typename + item.zOrderPosition,

            isFound = !!options.isFound[uuid];

        options.isFound[uuid] = true;

        return isFound;

    }

    /**
     * Returns the initialised `options` object.
     * @returns {Object}
     */
    function initialize() {

        // make a new object, so we don't pollute the original
        options = {
            initialized: true,
            depth: 0,
            isFound: {},
            filter: options.filter,
            getPageItems: false !== options.getPageItems,
            getGroupItems: false !== options.getGroupItems,
            getLayers: true === options.getLayers,
            getHiddenItems: false !== options.getHiddenItems,
            getLockedItems: false !== options.getLockedItems,
            getGuideItems: true === options.getGuideItems,
            maxDepth: options.maxDepth,
            returnFirstMatch: options.returnFirstMatch,
        };

        if (
            undefined == options.maxDepth
            || !options.maxDepth instanceof Number
        )
            options.maxDepth = 99;

        // items is a single layer
        if ('Layer' === items.typename)
            items = [items];

        // items is a document
        else if ('Document' === items.constructor.name) {

            var layers = items.layers;
            items = [];

            for (var i = 0; i < layers.length; i++)
                items.push(layers[i]);

        }

        else if ('Array' !== items.constructor.name)
            items = [items];

        return items.length > 0;

    };

};

 

翻译
举报
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Feb 17, 2025 社区新手,2025年2月17日

@m1b 我得到这个错误,第 17 行

翻译
举报
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Feb 17, 2025 社区专家,2025年2月17日

哦,抱歉 @vectora98504848 我不小心没复制脚本的一半!我现在已经修复了。 - Mark

翻译
举报
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Feb 18, 2025 社区新手,2025年2月18日
最新

谢谢兄弟,我喜欢 Illustrator,而且脚本帮助很大,但我编程的水平还达不到你的程度。 :winking_face:

翻译
举报
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines