Copy link to clipboard
Copied
Hi all , is it possible to convert RasterItem to placedItem in illustrator? Using script.
i have rasterise the placedItem manually but when I check it through the script It shows the typename as placedItem.
Hi @Abpujar, I have good news I think. I've been working on a script that will "unembed" raster items. Maybe this is what you need? Currently the script will "unembed" all raster items in document. It will save them into a "Links" folder in the same folder as the .ai document as .psd files and link them (so they will be linked placed items). I couldn't find any other scripts that solved this problem. Let me know if it helps.
- Mark
/**
* Unembed Raster Items
* for Adobe Illustrator 2022
*
...
Copy link to clipboard
Copied
Hi @Abpujar, I have good news I think. I've been working on a script that will "unembed" raster items. Maybe this is what you need? Currently the script will "unembed" all raster items in document. It will save them into a "Links" folder in the same folder as the .ai document as .psd files and link them (so they will be linked placed items). I couldn't find any other scripts that solved this problem. Let me know if it helps.
- Mark
/**
* Unembed Raster Items
* for Adobe Illustrator 2022
* @author m1b
* @discussion https://community.adobe.com/t5/illustrator-discussions/is-it-possible-to-convert-rasteritem-to-placeditem/m-p/13081172
*
* Known issues:
* 1. raster items with effects applied
* will be rasterized and the effect will
* become part of the linked image;
* 2. the resolution of the effect will match
* the resolution of the embedded image, not
* the document raster resolution setting,
* and sometimes the effect scale will be wrong;
* 3. some effects will cause the linked image
* to be offset in position compared to the
* embedded image.
* 4. the script uses various sources to derive
* the unembedded file's name, including the
* file's XMP manifest, and this hasn't been
* tested on a wide range of documents.
*/
(function () {
// Will use the group name if it contains a file extension
// (I recommend leaving this on, unless it causes a problem
// in your case.)
var useGroupNameAsFileName = true;
// if testMode == true, don't close the temp document
var testMode = false;
if (app.documents.length > 0)
unembedRasterImages(app.activeDocument, useGroupNameAsFileName);
/**
* Unembeds all rasterImages in document.
* @author m1b
* @version 2023-01-17
* It will export embedding images as PSD
* format into a 'Links' folder in the
* same folder as the Illustrator document.
* It will add suffix to exported images
* to ensure it does NOT overwrite files
* that already exist in the Links folder.
* @param {Document} doc - an Illustrator document.
* @param {Boolean} useGroupNameAsFileName - whether to Will use the group name if it contains a file extension (default: true).
*/
function unembedRasterImages(doc, useGroupNameAsFileName) {
var previousInteractionLevel = app.userInteractionLevel;
app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS;
var myImages = doc.rasterItems,
imageCount = myImages.length,
removeMeUUIDs = [],
exportErrors = [],
unembeds = [],
counter = 0;
// set up export folder
var exportFolder = Folder(doc.fullName.parent.fsName + '/Links/');
if (!exportFolder.exists)
exportFolder.create();
// set up the images for export
for (var i = imageCount - 1; i >= 0; i--) {
var oldImage = myImages[i],
colorSpace = oldImage.imageColorSpace,
position = oldImage.position,
// get current scale and rotation
sr = getLinkScaleAndRotation(oldImage),
scale = [sr[0], sr[1]],
rotation = sr[2],
width = oldImage.width,
height = oldImage.height,
fileExtensionRegex = /\.[^\.]+$/,
imageTitle = oldImage.name.replace(fileExtensionRegex, '') || undefined;
try {
imageTitle = (decodeURIComponent(oldImage.file.name)).replace(fileExtensionRegex, '');
} catch (error) { }
if (
useGroupNameAsFileName !== false
&& oldImage.parent.name.match(fileExtensionRegex)
)
imageTitle = oldImage.parent.name.replace(fileExtensionRegex, '');
var workingTitle = imageTitle || 'image' + (i + 1);
// script can't handle other colorSpaces
if (
colorSpace != ImageColorSpace.CMYK
&& colorSpace != ImageColorSpace.RGB
&& colorSpace != ImageColorSpace.GrayScale
) {
exportErrors.push(workingTitle + ' has unsupported color space. (' + colorSpace + ')');
continue;
}
// move to new document for exporting
var temp = newDocument(workingTitle, colorSpace, 1000, 1000);
// duplciate to the new document
var workingImage = oldImage.duplicate(temp.layers[0], ElementPlacement.PLACEATBEGINNING);
// set image to 100% scale 0° rotation and position
var tm = app.getRotationMatrix(-rotation);
tm = app.concatenateScaleMatrix(tm, 100 / scale[0] * 100, 100 / scale[1] * 100);
workingImage.transform(tm, true, true, true, true, true);
workingImage.position = [0, workingImage.height];
temp.artboards[0].artboardRect = [0, workingImage.height, workingImage.width, 0];
// export
try {
var workingFileName = new Date().getTime(),
path = exportFolder.fsName + '/' + workingFileName + '.psd',
// path = getFilePathWithOverwriteProtectionSuffix(exportFolder.fsName + '/' + workingFileName + '.psd'),
workingFile = exportAsPSD(temp, path, colorSpace, 72);
} catch (error) {
exportErrors.push('"' + imageTitle + '" failed to export. (' + error.message + ')');
}
if (!testMode)
// close the temp document
temp.close(SaveOptions.DONOTSAVECHANGES);
if (
workingFile == undefined
|| !workingFile.exists
)
continue;
// see if the exported file has fileName in it's XMP manifest
// var xmpFileName = getFilePathFromXMP(file, true);
imageTitle = (
imageTitle
|| getFilePathFromXMP(workingFile, true)
|| 'image' + (i + 1)
);
// rename the working file
var newPath = exportFolder.fsName + '/' + imageTitle + '.psd';
workingFile.copy(newPath);
var file = File(newPath);
workingFile.remove();
// relink to the embedded image
var newImage = oldImage.layer.placedItems.add();
newImage.file = file;
// scale and rotate and position
// to match the original
tm = app.getScaleMatrix(scale[0], scale[1]);
tm = app.concatenateRotationMatrix(tm, rotation);
newImage.transform(tm, true, true, true, true, true);
newImage.move(oldImage, ElementPlacement.PLACEAFTER);
newImage.position = position;
if (
round(oldImage.width, 2) != round(newImage.width, 2)
|| round(oldImage.height, 2) != round(newImage.height, 2)
) {
// When size is different here, it probably means
// there was an effect on the rasterItem eg.dropshadow.
// Change `false` to `true` below if you want script
// to ignore rasterItems that have this problem.
if (false) {
exportErrors.push('Did not unembed "' + imageTitle + '" because it couldn\'t be sized correctly. Try removing special appearance and trying again.')
file.remove();
newImage.remove();
continue;
}
else
exportErrors.push('Warning: File "' + imageTitle + '" has altered dimensions, probably due to an effect or special appearance.');
}
// delete old image later
removeMeUUIDs.push(oldImage.uuid);
// for reporting
counter++
}
// remove old embedded images
for (var i = 0; i < removeMeUUIDs.length; i++) {
var removeMe = getItemByUUID(myImages, removeMeUUIDs[i]);
if (removeMe != undefined)
removeMe.remove();
}
// clean up
app.userInteractionLevel = previousInteractionLevel;
app.redraw();
// reporting
var result = 'Unembedded ' + counter + ' of ' + imageCount + ' raster items.';
if (exportErrors.length > 0)
result += '\n' + exportErrors.join('\n');
alert(result);
};
/**
* Returns a page item with matching UUID.
* @param {PageItems} items - Illustrator PageItems or Array of PageItems.
* @param {Number} uuid - Illustrator PageItem uuid.
* @returns {PageItem}
*/
function getItemByUUID(items, uuid) {
for (var i = 0; i < items.length; i++) {
if (items[i].uuid === uuid)
return items[i];
}
};
/**
* Exports a document as PSD.
* @param {Document} doc - the document to export.
* @param {String} path - name of file, without extension.
* @param {ImageColorSpace} colorSpace - the colorSpace of the image.
* @param {Number} resolution - export resolution
* @returns {File} - the exported psd file.
*/
function exportAsPSD(doc, path, colorSpace, resolution) {
var file = File(path),
options = new ExportOptionsPhotoshop();
options.antiAliasing = false;
options.artBoardClipping = true;
options.imageColorSpace = colorSpace;
options.editableText = false;
options.flatten = true;
options.maximumEditability = false;
options.resolution = (resolution || 72);
options.warnings = false;
options.writeLayers = false;
doc.exportFile(file, ExportType.PHOTOSHOP, options);
return file;
};
/**
* Return the scale, rotation and size
* of a PlacedItem or RasterItem.
* @author m1b
* @version 2023-03-09
* @param {PlacedItem|RasterItem} item - an Illustrator item.
* @param {Boolean} round - whether to round numbers to nearest integer.
* @returns {Array} [scaleX%, scaleY%, rotation°, width, height]
*/
function getLinkScaleAndRotation(item, round) {
if (item == undefined)
return;
var m = item.matrix,
rotatedAmount,
unrotatedMatrix,
scaledAmount;
var flipPlacedItem = (item.typename == 'PlacedItem') ? 1 : -1;
try {
rotatedAmount = item.tags.getByName('BBAccumRotation').value * 180 / Math.PI;
} catch (error) {
rotatedAmount = 0;
}
unrotatedMatrix = app.concatenateRotationMatrix(m, rotatedAmount * flipPlacedItem);
if (
unrotatedMatrix.mValueA == 0
&& unrotatedMatrix.mValueB !== 0
&& unrotatedMatrix.mValueC !== 0
&& unrotatedMatrix.mValueD == 0
)
scaledAmount = [unrotatedMatrix.mValueB * 100, unrotatedMatrix.mValueC * -100 * flipPlacedItem];
else
scaledAmount = [unrotatedMatrix.mValueA * 100, unrotatedMatrix.mValueD * -100 * flipPlacedItem];
if (scaledAmount[0] == 0 || scaledAmount[1] == 0)
return;
if (round)
return [round(scaledAmount[0]), round(scaledAmount[1]), round(rotatedAmount)];
else
return [scaledAmount[0], scaledAmount[1], rotatedAmount];
};
/**
* Rounds `n` to `places` decimal places.
* @param {Number} n - the number to round
* @param {Number} places - number of decimal places, can be negative
* @returns {Number}
*/
function round(n, places) {
var m = Math.pow(10, places != undefined ? places : 3);
return Math.round(n * m) / m;
};
/**
* Create a new basic document with some options.
* @author m1b
* @version 2022-07-21
* @param {String} name - the title of the document.
* @param {ImageColorSpace} colorSpace - ImageColorSpace.RGB or ImageColorSpace.CMYK.
* @param {Number} width - width of the default artboard.
* @param {Number} height - height of the default artboard.
* @returns {Document}
*/
function newDocument(name, colorSpace, width, height) {
var myDocPreset = new DocumentPreset(),
myDocPresetType;
myDocPreset.title = name;
myDocPreset.width = width || 1000;
myDocPreset.height = height || 1000;
if (
colorSpace == ImageColorSpace.CMYK
|| colorSpace == ImageColorSpace.GrayScale
|| colorSpace == ImageColorSpace.DeviceN
) {
myDocPresetType = DocumentPresetType.BasicCMYK;
myDocPreset.colorMode = DocumentColorSpace.CMYK
}
else { // if (colorSpace == ImageColorSpace.RGB) {
myDocPresetType = DocumentPresetType.BasicRGB;
myDocPreset.colorMode = DocumentColorSpace.RGB
}
return app.documents.addDocument(myDocPresetType, myDocPreset);
};
/**
* Returns a path that will not overwrite a file.
* Adds an incremental suffix to avoid overwrites.
* @author m1b
* @version 2022-07-20
* For example, if `myFile.txt` already exists it
* will return `myFile(1).txt` and if that already
* exists, it will return `myFile(2).txt` etc.
* @param {String} path - the path to test.
* @returns {String} a path that will not overwrite a file.
*/
function getFilePathWithOverwriteProtectionSuffix(path) {
var index = 1,
parts = path.split(/(\.[^\.]+)$/);
while (File(path).exists)
path = parts[0] + '(' + (++index) + ')' + parts[1];
return path;
};
/**
* Returns the file path stored in XMP data
* of a psd file, if available.
* @param {File} file - a File.
* @param {Boolean} fileNameOnly - whether to return just file names (default: false).
* @returns {String} - file path or file name or undefined.
*/
function getFilePathFromXMP(file, fileNameOnly) {
if (ExternalObject.AdobeXMPScript == undefined)
ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');
var xmpFile = new XMPFile(file.fsName, XMPConst.UNKNOWN, XMPConst.OPEN_FOR_READ),
xmp = xmpFile.getXMP(),
xpath = 'xmpMM:Manifest[1]/stMfs:reference/stRef:filePath',
value = xmp.getProperty(XMPConst.NS_XMP_MM, xpath);
if (
value == undefined
|| !value.hasOwnProperty('value')
)
return;
value = value.value;
if (fileNameOnly !== false) {
var onlyFileName = value.match(/\/([^\/]+)\.[^\.\/]+/);
if (
onlyFileName != null
&& onlyFileName.length == 2
)
value = onlyFileName[1];
}
return value;
};
})();
Edit 1: fixed un-localized string in newDocument function. Thanks @Kurt Gold!
Edit 2: fixed problem with rotated rasterItems and now shows warning in special cases. Again thanks @Kurt Gold!
Edit 3 (2022-12-03): added better file naming, as requested by @ignacio267368455fix. Good call!
Edit 4 (2022-12-03): @ignacio267368455fix found cases where the RasterItem's name was actually in the parent GroupItem. I've added an option "useGroupNameAsFileName" to handle this case.
Edit 5 (2023-01-15): added ability to use fileNames taken from the document's XMP manifest, to handle a case in @Filip.Czechowski's sample document. This feature could do with some more testing, but worked well for Filip's file.
Edit (2023-01-16): added a step to save the document, to ensure that the XMP manifest was updated because after reordering raster items in the document (without saving) the manifest would have the old ordering.
Edit (2023-01-17): my previous attempt to get meaningful file names using the document's XMP data didn't pan out in many cases, so I implemented an idea by @Filip.Czechowski to get the filename from the exported document's XMP data.
Edit (2023-03-09): Added a check for an edge case, found by @ajabon grinsmith, where the raster item had no BBAccumRotation tag and returned zero scale.
Copy link to clipboard
Copied
this is awesome, works 100%
just need something to work perfect for me...any idea how to get the name of the embed image?
Copy link to clipboard
Copied
@ignacio267368455fix, if you run the above (updated) script and your file says "image1.psd" instead of the expected name, then it is almost certainly because the name is not in the illustrator file. I try to get the name from the raster item's name property, then from the file, but some embedded images don't have either of those. If you would like to post a sample document I can have a close look at it and confirm if a name exists. Or you could try a test: in the Links panel, select the image and go to the panel menu and choose "Unembed..." — when you do this, does it give the name you expected?
- Mark
Copy link to clipboard
Copied
ty for ur fast response!
it does have a name under in the Links panel bun when i do unembed, it dosent call the "layer" name
i mean, that name, can it be used to save the file?
Copy link to clipboard
Copied
wel.. i manage to take an example! can ¿i use the name of the image to save the file?
Copy link to clipboard
Copied
Can you please post the ai file? I can't tell from the screenshot.
Copy link to clipboard
Copied
Copy link to clipboard
Copied
I see. In your case the RasterItem's name was "Capa 1" and it's parent GroupItem's name was "Venezuelan Wood-Quail.psd". It seems strange because the Links Panel shows the correct name "Venezuelan Wood-Quail.psd". Anyway, I've added the option (copy the script above again) to handle this situation. - Mark
Copy link to clipboard
Copied
Hey!!! it's working perfect right now
Thanks u a lot! it's so helpful this script!!!!!!!!!!!!
Copy link to clipboard
Copied
Thank you for the scripe this is excellent and exactly what I need.... Except for one thing:
How can I remove the overwrite protection?
I have a large number of illustrator files that have pattern of psd elements -- I have unfortunately inherited these files will all the pattern elements embedded. I need to edit these elements so I'd love to create a "clean" links folder.
Using your script will allow me to do that, except that it is created as many versions of the assets as there are instances in my patterns...
Thank you in advance!
Copy link to clipboard
Copied
Hi @Enchanting_Delight15C2 can you try changing this line
var path = getFilePathWithOverwriteProtectionSuffix(exportFolder.fsName + '/' + imageTitle + '.psd'),
to this
var path =(exportFolder.fsName + '/' + imageTitle + '.psd'),
If that isn't what you want, please send me a sample file that shows your case.
- Mark
Copy link to clipboard
Copied
Hi Mark -- thank you for such a quick answer!
It worked!! thanks a lot!!
Nicolas
Copy link to clipboard
Copied
Hi! The script is wonderful and helps a lot. I wonder if its possible that it preservers original names of embeded files. It does, when the embeded file was a PSD, but when it was eg. PNG image, it produces image1.psd, image2.psd and so on. It would be perfect to keep oroginal png name when saving psd file, apart from extension of course 🙂
Copy link to clipboard
Copied
Hi @Filip.Czechowski, thanks for the sample file—that's just what I needed. For me it looked the same as for you: there were image names correctly listed in the Links panel, but they were nowhere to be found and the script didn't get them. It took a bit of digging, but I found them in the XMP manifest and they seemed to line up each time. I've incorporated that feature in the script above, so please give it a try. - Mark
Edit: I had to add another step to save the document before unembedding, to ensure that any re-ordering of the raster items within the document would be reflected in the XMP manifest.
Copy link to clipboard
Copied
There are two places, where you can find images names in XMP desription. One is Maniefest, and second is "xmpMM:Ingredient".
Copy link to clipboard
Copied
Yes, you're right but they have exactly the same information as far as I can tell, including order.
Copy link to clipboard
Copied
Okay, Filip, that method wasn't reliable. I have adjusted the script again and I think this will be good. It uses your suggestion of getting the file name from the exported file's XMP data. - Mark
Copy link to clipboard
Copied
Wow. Now it works fantastic. I've tried on few fiels. Thank you Mark!
Copy link to clipboard
Copied
Hello @m1b ,
Your script is much excellent.
I wonder if you can help me to solve the error that I have.
I attach a sample file.
"tm = app.concatenateScaleMatrix...." on line 116.
I get "Illegal argument - argument 2"
It seems that the argument is dividing by zero.
Thanks.
Copy link to clipboard
Copied
Hi @ajabon grinsmith, thanks for the sample file—yes, it broke the script! I have updated it to handle that case now, so please try again with the updated script.
- Mark
Copy link to clipboard
Copied
Copy link to clipboard
Copied
Hello
I would love to use this script but I'm unfamilar with using scripts in Illustrator. I have copied the highlighted text into a text and save it as a .jsx but when I try to run it the script will not run and give me an error? Am I doing this incorrectly? Your ensight is appreciated. Thank you!
Copy link to clipboard
Copied
Hi @EricaSholycow, well one common problem is that the file hasn't been saved as *plain text*. If you are on MacOS, you can use Text Edit, and on Windows I think you can use Note Pad. If that doesn't work, and you still get a script error, please post the exact error.
- Mark
Copy link to clipboard
Copied
Hey m1b
can you make a script that will embedd then unembedd images, bypass the save prompt for psd and then release mask and delete mask?