Skip to main content
December 22, 2025
Answered

Issue with Image Shifting/Distortion when Embedding Linked Files via Scripting & Save Options

  • December 22, 2025
  • 3 replies
  • 273 views

Hello everyone,

I am writing to inquire about a persistent issue regarding embedding linked images in Illustrator.

I work in the large-format printing industry, and my role involves preparing client files for production. Over 90% of our workflow relies on Illustrator. To ensure file portability across different workstations, I typically check "Include Linked Files" in the Save dialog options. However, I have discovered that this standard method causes certain images to shift position or distort after saving.

To address this, I attempted to use ExtendScript to automate the embedding process (specifically using the placedItem.embed() method). While the script successfully fixed the images that were failing during the standard save process, it paradoxically caused similar shifting/distortion issues in other images that previously worked fine. In some cases, both methods fail.

Precision is critical in our industry. We simply do not have the manpower to manually inspect every single file for errors in a process that should ideally be reliable and automatic.

Technical Observations: I have analyzed the problematic files and noticed a pattern:

  1. The shifting/distortion usually occurs with multi-layer files or images with transparent backgrounds (e.g., PSD, TIFF).

  2. These images are often nested within Groups or Clipping Masks.

  3. It appears that when embed() is executed, the linked PlacedItem is converted into a GroupItem or a complex structure. During this conversion, Illustrator seems to fail to correctly recalculate the relative position and dimensions (transformation matrix) of the object within its parent hierarchy, resulting in the shift.

Current Workaround & Limitations: I tried a workaround script that copies the linked item to a temporary "isolation" document, embeds it there, and then copies it back to the original file. While this method solves the coordinate shifting issue, it has significant drawbacks:

  • It is extremely time-consuming for high-resolution large-format files.

  • The script relies on app.doScript() (or executeAction) to handle complex Copy/Paste/Ungroup operations via Actions. Due to Illustrator's limitation where a script called by an Action cannot call another Action, I cannot integrate this script into my main automation Action set. This makes the workflow inefficient.

My Question: Is there a known solution or a more robust scripting method to embed these complex, nested linked files without altering their visual appearance (position/scale)?

Note: Using "Package" or keeping external links (e.g., in a "Links" folder) are not viable options for our specific automated workflow. We require the files to be fully embedded.

Any advice or insights would be greatly appreciated.

Thank you.

my script

Correct answer Ethereal_experience8415

First of all, thank you very much for your support.

 

Later, I attempted a method where I first capture the object's layer hierarchy, coordinates, and dimensions, and insert a placeholder.

After embedding, I overwrite the new object's parameters with this saved data to ensure the artwork undergoes no unintended changes.

 

#target illustrator

function smartBatchEmbed() {
    if (app.documents.length === 0) {
        alert("Please open a document first.");
        return;
    }

    var doc = app.activeDocument;
    var links = doc.placedItems;
    var successCount = 0;
    var errorCount = 0;

    // Save the current interaction level to restore later
    // (Suppress alerts to speed up processing and prevent interruptions)
    var originalInteractionLevel = app.userInteractionLevel;
    app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS;

    try {
        // =================================================
        // Reverse Loop Traversal
        // (Processing from the last item to the first to avoid index shifting)
        // =================================================
        for (var i = links.length - 1; i >= 0; i--) {
            var targetItem = links[i];
            
            try {
                // Check validity: If 'file' property is unreadable, it's likely not a standard linked file.
                // This also excludes already embedded objects.
                var fileRef = targetItem.file; 

                // -------------------------------------------------
                // Step 1: Snapshot State (Geometry & Hierarchy)
                // -------------------------------------------------
                var savedState = {
                    left: targetItem.left,
                    top: targetItem.top,
                    width: targetItem.width,
                    height: targetItem.height
                };

                // Create a placeholder to mark the exact hierarchy level and Z-Order
                var parentContainer = targetItem.parent;
                var placeholder = parentContainer.pathItems.add();
                placeholder.name = "Embed_Placeholder_" + i;
                placeholder.filled = false;
                placeholder.stroked = false;
                
                // Move the placeholder directly BEHIND the target object
                placeholder.move(targetItem, ElementPlacement.PLACEAFTER);

                // -------------------------------------------------
                // Step 2: Execute Embed
                // -------------------------------------------------
                // Select only the current object to avoid interference
                doc.selection = null;
                targetItem.selected = true;
                
                targetItem.embed();
                
                // -------------------------------------------------
                // Step 3: Group the Result
                // -------------------------------------------------
                // After embedding, the result might be a RasterItem or a Group of paths.
                // We force a "Group" command to wrap the result into a single container.
                app.executeMenuCommand('group');
                
                // Get the newly created group (it becomes the current selection)
                var fixedGroup = doc.selection[0];

                // -------------------------------------------------
                // Step 4: Force Restore (Hierarchy & Geometry)
                // -------------------------------------------------
                
                // 4.1 Restore Hierarchy: Move the group to "IN FRONT OF" the placeholder
                fixedGroup.move(placeholder, ElementPlacement.PLACEBEFORE);

                // 4.2 Restore Geometry: Force apply original dimensions
                // Setting dimensions first, then position is generally safer
                if (fixedGroup.width !== savedState.width || fixedGroup.height !== savedState.height) {
                    fixedGroup.width = savedState.width;
                    fixedGroup.height = savedState.height;
                }
                
                // Finally, restore coordinates (Top-Left alignment)
                fixedGroup.left = savedState.left;
                fixedGroup.top = savedState.top;

                // -------------------------------------------------
                // Step 5: Cleanup
                // -------------------------------------------------
                placeholder.remove();
                successCount++;

            } catch (e) {
                // Handle individual failure without stopping the loop
                errorCount++;
                // Attempt to clean up any residual placeholder
                try {
                    if (placeholder) placeholder.remove();
                } catch(err) {}
                
                // Uncomment below to debug specific errors
                // $.writeln("Process failed: " + e.message);
            }
        }

    } finally {
        // Restore user interaction level regardless of errors
        app.userInteractionLevel = originalInteractionLevel;
    }

    // Reporting
    if (successCount > 0 || errorCount > 0) {
        var report = "Batch Embed Completed!\n";
        report += "✅ Success: " + successCount + "\n";
        if (errorCount > 0) report += "❌ Failed/Skipped: " + errorCount;
        alert(report);
    }
}

smartBatchEmbed();

 

3 replies

December 23, 2025

First of all, thank you very much for your support.

 

Later, I attempted a method where I first capture the object's layer hierarchy, coordinates, and dimensions, and insert a placeholder.

After embedding, I overwrite the new object's parameters with this saved data to ensure the artwork undergoes no unintended changes.

 

#target illustrator

function smartBatchEmbed() {
    if (app.documents.length === 0) {
        alert("Please open a document first.");
        return;
    }

    var doc = app.activeDocument;
    var links = doc.placedItems;
    var successCount = 0;
    var errorCount = 0;

    // Save the current interaction level to restore later
    // (Suppress alerts to speed up processing and prevent interruptions)
    var originalInteractionLevel = app.userInteractionLevel;
    app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS;

    try {
        // =================================================
        // Reverse Loop Traversal
        // (Processing from the last item to the first to avoid index shifting)
        // =================================================
        for (var i = links.length - 1; i >= 0; i--) {
            var targetItem = links[i];
            
            try {
                // Check validity: If 'file' property is unreadable, it's likely not a standard linked file.
                // This also excludes already embedded objects.
                var fileRef = targetItem.file; 

                // -------------------------------------------------
                // Step 1: Snapshot State (Geometry & Hierarchy)
                // -------------------------------------------------
                var savedState = {
                    left: targetItem.left,
                    top: targetItem.top,
                    width: targetItem.width,
                    height: targetItem.height
                };

                // Create a placeholder to mark the exact hierarchy level and Z-Order
                var parentContainer = targetItem.parent;
                var placeholder = parentContainer.pathItems.add();
                placeholder.name = "Embed_Placeholder_" + i;
                placeholder.filled = false;
                placeholder.stroked = false;
                
                // Move the placeholder directly BEHIND the target object
                placeholder.move(targetItem, ElementPlacement.PLACEAFTER);

                // -------------------------------------------------
                // Step 2: Execute Embed
                // -------------------------------------------------
                // Select only the current object to avoid interference
                doc.selection = null;
                targetItem.selected = true;
                
                targetItem.embed();
                
                // -------------------------------------------------
                // Step 3: Group the Result
                // -------------------------------------------------
                // After embedding, the result might be a RasterItem or a Group of paths.
                // We force a "Group" command to wrap the result into a single container.
                app.executeMenuCommand('group');
                
                // Get the newly created group (it becomes the current selection)
                var fixedGroup = doc.selection[0];

                // -------------------------------------------------
                // Step 4: Force Restore (Hierarchy & Geometry)
                // -------------------------------------------------
                
                // 4.1 Restore Hierarchy: Move the group to "IN FRONT OF" the placeholder
                fixedGroup.move(placeholder, ElementPlacement.PLACEBEFORE);

                // 4.2 Restore Geometry: Force apply original dimensions
                // Setting dimensions first, then position is generally safer
                if (fixedGroup.width !== savedState.width || fixedGroup.height !== savedState.height) {
                    fixedGroup.width = savedState.width;
                    fixedGroup.height = savedState.height;
                }
                
                // Finally, restore coordinates (Top-Left alignment)
                fixedGroup.left = savedState.left;
                fixedGroup.top = savedState.top;

                // -------------------------------------------------
                // Step 5: Cleanup
                // -------------------------------------------------
                placeholder.remove();
                successCount++;

            } catch (e) {
                // Handle individual failure without stopping the loop
                errorCount++;
                // Attempt to clean up any residual placeholder
                try {
                    if (placeholder) placeholder.remove();
                } catch(err) {}
                
                // Uncomment below to debug specific errors
                // $.writeln("Process failed: " + e.message);
            }
        }

    } finally {
        // Restore user interaction level regardless of errors
        app.userInteractionLevel = originalInteractionLevel;
    }

    // Reporting
    if (successCount > 0 || errorCount > 0) {
        var report = "Batch Embed Completed!\n";
        report += "✅ Success: " + successCount + "\n";
        if (errorCount > 0) report += "❌ Failed/Skipped: " + errorCount;
        alert(report);
    }
}

smartBatchEmbed();

 

CarlosCanto
Community Expert
Community Expert
December 22, 2025

does the shifting happen consistently? I mean, can you replicate it? if so, check if this helps

 

the idea is to save the placedItem's position before embedding to later apply it to embedded item

 

// embed linked items and reposition
// https://community.adobe.com/t5/illustrator-discussions/issue-with-image-shifting-distortion-when-embedding-linked-files-via-scripting-amp-save-options/m-p/15640790#M460131
// carlos canto // 12/22/25

function main() {
    var idoc = app.activeDocument;
    var items = idoc.placedItems;
    
    for (var a = items.length -1; a>=0; a--) {
        var item = items[a];
        var top = item.top;
        var left = item.left;
        var name = "embedded " + a;
        item.name = name;
        
        item.embed();
        var eItem = idoc.pageItems.getByName(name);

        eItem.top = top;
        eItem.left = left;
    }
}

main();
RobOctopus
Inspiring
December 22, 2025

try added an app.redraw() after each embed. it slows the operation but I had some issues with embedding and this has helped prevent unexpected results.

try{
item.embed()
}catch(e){
}
app.redraw()