Copy link to clipboard
Copied
I have an array of allPathItems (this is an array of pathItems and compoundPathItems) and I want to export each of the allPathItems as a *.svg
I get the right number of SVG files, with the correct names and in the correct place, but each SVG is of the whole document.
How do I get one SVG for each allPathItems?
So allPathItems[0] is a SVG of that path item and only that item.
allPathItems[1] is a SVG of allPathItems[1] and only that item.
And so on until allPathItems.length
var doc = app.activeDocument;
...
for (var j = 0; j < allPathItems.length; j++) {
var filename = allPathItems[j].name;
var svgFile = new File(doc.path + "/" + filename + '.svg');
doc.exportFile(svgFile, ExportType.SVG, exportOptions, allPathItems[j]);
}
Hi @Paul_St_George I have never done this exact thing before, but I've made an attempt that I think may be useful, although due to some surprising complications, the script is a bit more complicated than I would have expected.
Are you a scripter? If so, you can follow the code to see what's going on, but you will see some strangeness. For example, to export a single path item as SVG, the way I got this to work was by deleting all the other items, temporarily, prior to the export. You will see
...Copy link to clipboard
Copied
Hi @Paul_St_George I have never done this exact thing before, but I've made an attempt that I think may be useful, although due to some surprising complications, the script is a bit more complicated than I would have expected.
Are you a scripter? If so, you can follow the code to see what's going on, but you will see some strangeness. For example, to export a single path item as SVG, the way I got this to work was by deleting all the other items, temporarily, prior to the export. You will see that I only use item's UUIDs to access DOM references—this is for two reasons: (1) the references were broken by the use of app.undo(), even though I would have thought that the items were still valid, and (2) Illustrator crashed a lot when I tried simpler approaches.
In the end I think I've come up with a robust script—but I'm also sure there are going to be plenty of bugs, so let me know what happens if you end up using it, or parts of it.
You can edit the `setttings` object as you like. For example, try turning off `enforceArtboardBounds`.
- Mark
/**
* @file Export Selected PathItems As SVG.js
*
* Demonstration script:
* - Exports every selected path item and compound path item
* on the active artboard as separate SVG file.
* - Exported files will be named by their item name
* or item typename and uuid.
*
* @author m1b
* @version 2025-05-25
* @discussion https://community.adobe.com/t5/illustrator-discussions/how-to-export-multiple-svg-from-one-layer/m-p/15338630
*/
(function () {
var doc = app.activeDocument;
var settings = {
// whether to export an unpainted rectangle to enforce the SVG viewBox
enforceArtboardBounds: true,
// where to export to
exportFolderPath: '#DOCUMENT_FOLDER#/SVG',
// the SVG export options to use
exportOptions: {
coordinatePrecision: 1,
cssProperties: SVGCSSPropertyLocation.STYLEATTRIBUTES,
fontType: SVGFontType.OUTLINEFONT,
rasterImageLocation: RasterImageLocation.EMBED,
svgId: SVGIdType.SVGIDREGULAR,
svgMinify: false,
svgResponsive: true,
},
// these are the things to export, choose `doc.pageItems` to export every item
targetItems: doc.selection,
// whether to show the results dialog afterwards
showResult: true,
};
if (!doc.fullName.exists)
return alert('Please save this document first.');
if (0 === settings.targetItems.length)
return alert('Please select some path items to export and try again.');
var counter = 0;
// collect the selection, so we can restore it later
var selectedUUIDs = [];
for (var i = 0; i < doc.selection.length; i++)
selectedUUIDs.push(doc.selection[i].uuid);
// uuids of every page item
var allItemUUIDs = [];
// uuids of the items to export
var targetItemUUIDs = [];
// arrays to record whether an item has been processed
var allAlreadyDone = [],
targetAlreadyDone = [];
// in this loop we collect UUIDs for the things do and don't want to export
for (var i = 0, item; i < doc.pageItems.length; i++) {
item = doc.pageItems[i];
if ('CompoundPathItem' === item.parent.constructor.name)
// we don't want a mere path of a compound path item
item = item.parent;
if (allAlreadyDone[item.uuid])
// already got this one
continue;
// store uuid for later
allItemUUIDs.push(item.uuid);
allAlreadyDone[item.uuid] = true;
if (
'PathItem' !== item.constructor.name
&& 'CompoundPathItem' !== item.constructor.name
)
// not a path item or compound path item
continue;
if (-1 === indexOfArray(settings.targetItems, item))
// not a target item to export
continue;
if (targetAlreadyDone[item.uuid])
// already got this one
continue;
// store item for later
targetItemUUIDs.push(item.uuid);
// mark as done
targetAlreadyDone[item.uuid] = true;
}
// prepare the layers
var layerStatus = [];
for (var i = 0; i < doc.layers.length; i++) {
layerStatus[i] = {
locked: doc.layers[i].locked,
visible: doc.layers[i].visible,
};
doc.layers[i].locked = false;
doc.layers[i].visible = true;
}
// add an undo level here so this work doesn't get undone later
app.redraw();
// get the exporter function
var exportArtboard = getSVGExporter(doc, settings.exportFolderPath, settings.exportOptions, settings.enforceArtboardBounds);
// this loop is where we do the exporting
for (var i = 0, item, itemUUID; i < targetItemUUIDs.length; i++) {
itemUUID = targetItemUUIDs[i];
item = getThing(doc.pageItems, 'uuid', itemUUID)
// remove all the items we don't want to export
for (var j = allItemUUIDs.length - 1; j >= 0; j--)
if (allItemUUIDs[j] !== itemUUID)
getThing(doc.pageItems, 'uuid', allItemUUIDs[j]).remove();
// establish the artboard index for this item
var artboardIndex = getArtboardIndexOfItem(item, doc.artboards);
if (undefined == artboardIndex)
continue;
// export with its uuid as filename
exportArtboard(artboardIndex, item.name || item.typename + '-' + itemUUID);
counter++;
// bring everything back for the next go
app.undo();
}
// reinstate the selection
var selection = [];
for (var i = 0; i < selectedUUIDs.length; i++)
selection.push(getThing(doc.pageItems, 'uuid', selectedUUIDs[i]));
doc.selection = selection;
// restore the layers' properties
for (var i = 0; i < doc.layers.length; i++) {
doc.layers[i].locked = layerStatus[i].locked;
doc.layers[i].visible = layerStatus[i].visible;
}
if (settings.showResult)
// show result
alert('Exported ' + counter + ' path items as SVG.');
// reveal the exported files
doc.fullName.parent.execute();
})();
/**
* Returns a pre-configured exporter function.
* @author m1b
* @version 2025-05-25
* @param {Document} doc - an Illustrator Document.
* @param {String} [exportFolderPath] - the export folder path (default: SVG folder in document's folder).
* @param {Object} [properties] - the export options properties to set (default: none, use default values).
* @param {Boolean} [enforceArtboardBounds] - whether to force the svg viewBox to match the artboard bounds (default: false).,
*/
function getSVGExporter(doc, exportFolderPath, properties, enforceArtboardBounds) {
properties = properties || {};
if (!exportFolderPath)
exportFolderPath = '#DOCUMENT_FOLDER#/SVG';
// replace placeholders in export folder path
exportFolderPath = exportFolderPath
.replace('#DOCUMENT_FOLDER#', String(doc.fullName.parent));
var exportFolder = Folder(exportFolderPath);
if (!exportFolder.exists)
exportFolder.create();
// set up the SVG export options
var exportOptions = new ExportOptionsWebOptimizedSVG();
// apply export options properties
for (var key in properties)
if (properties.hasOwnProperty(key))
exportOptions[key] = properties[key];
return exportArtboardAsSVG;
/**
* Exports an artboard as SVG into the same folder as `doc`.
* @author m1b
* @version 2025-05-25
* @param {Number} artboardIndex - the artboard index (zero based).
* @param {String} fileName - the filename for the exported file (do not include file extension).
*/
function exportArtboardAsSVG(artboardIndex, fileName) {
artboardIndex = Math.min(artboardIndex, doc.artboards.length - 1);
// draw this to force the svg viewBox to match the artboard bounds
var tempRectangle = enforceArtboardBounds
? drawRectangle(doc, doc.artboards[artboardIndex].artboardRect, undefined, doc, { filled: false, stroked: false })
: undefined;
// set artboard range
doc.artboards.setActiveArtboardIndex(artboardIndex);
exportOptions.artboardRange = artboardIndex + 1;
var file = File(exportFolder + '/' + fileName + '.svg');
doc.exportFile(file, ExportType.WOSVG, exportOptions);
if (tempRectangle)
tempRectangle.remove();
};
};
/**
* 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.
* @returns {*?} - the thing, if found.
*/
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 index of `obj` within `array`,
* or -1 if `obj` is not found.
* @param {Array<*>} array - the array to search in.
* @param {*} obj - the object to look for.
* @returns {Number}
*/
function indexOfArray(array, obj) {
for (var i = 0; i < array.length; i++)
if (array[i] == obj)
return i;
return -1;
};
/**
* Draws a rectangle in the container.
* Takes Large Scale Documents into account.
* @author m1b
* @version 2024-09-26
* @param {Document} doc - an Illustrator document.
* @param {Array<Number>} rect - the rectangle dimensions [left, top, width, height].
* @param {Number} [unitFactor] - eg. 72 to convert to inches. All values will be scaled by this (default: 1).
* @param {Document|Layer|GroupItem} container - an Illustrator page item container (default: doc).
* @param {Object} [props] - properties to assign to the rectangle (default: none).
* @return {PathItem}
*/
function drawRectangle(doc, rect, unitFactor, container, properties) {
properties = properties || {};
unitFactor = unitFactor || 1;
container = container || doc;
var sf = 1 / doc.scaleFactor * unitFactor;
var rectangle = container.pathItems.rectangle(rect[1] * -sf, rect[0] * sf, (rect[2] - rect[0]) * sf, (rect[3] - rect[1]) * -sf); // T,L,W,H
// defaults
rectangle.filled = true;
rectangle.stroked = false;
// apply properties
for (var key in properties)
if (properties.hasOwnProperty(key))
rectangle[key] = properties[key];
return rectangle;
};
/**
* Returns the index of the artboard that has most overlap the item's bounds.
* @author m1b
* @version 2025-05-25
* @param {PageItem} item - a page item.
* @param {Array<Artboard>} artboards - artboards to search, if undefined, search all.
* @param {Boolean} preferIndex - if true, returns array of artboard indices {Array[Number]}
* @returns {Number?} - the artboard index;
*/
function getArtboardIndexOfItem(item, artboards) {
var artboardIndex;
for (var i = 0, overlap, maxOverlap = 0; i < artboards.length; i++) {
overlap = getOverlapArea(item.geometricBounds, artboards[i].artboardRect);
if (!overlap)
continue;
if (overlap > maxOverlap) {
maxOverlap = overlap;
artboardIndex = i;
}
}
if (undefined !== artboardIndex)
return artboardIndex;
};
/**
* Returns the overlapping area between
* two rectangular bounds.
* @author m1b
* @version 2025-05-25
* @param {bounds} a - First bounds [L, T, R, B].
* @param {bounds} b - Second bounds [L, T, R, B].
* @returns {Number|null} - area of overlap, or null if no overlap.
*/
function getOverlapArea(a, b) {
var left = Math.max(a[0], b[0]),
top = Math.min(a[1], b[1]),
right = Math.min(a[2], b[2]),
bottom = Math.max(a[3], b[3]);
var width = right - left,
height = top - bottom;
if (
width > 0
&& height > 0
)
return width * height;
// No overlap
return null;
};
Edit 2025-05-25: added check for locked or hidden layers.
Edit 2025-05-25: fixed bug where selection was not restored when `settings.targetItems` is set to `doc.pageItems`, ie. to export every path item in document.
Copy link to clipboard
Copied
Wow! That certainly does the job. Thank you. You asked me to let know if there are any wrinkles. There is a trivial issue when there are items I do not want to export which are also hidden or locked.
Then we have:
Error: Target layer cannot be modified.
I said it was trivial as it is a simple matter to unlock or unhide the offending layer.
I will certainly use your script, but would be interested to know why my humble effort is not working.
I tried to explicitly select each item, but I think I need a way to select NONE and even then, I am not sure I can select one item at a time.
for (var j = 0; j < allPathItems.length; j++) {
var filename = allPathItems[j].name;
allPathItems[j].selected = true;
for (var i = 0; i < selection.length; i++) {
var svgFile = new File(doc.path + "/" + filename + '.svg');
doc.exportFile(svgFile, ExportType.SVG, exportOptions, selection[i]);
allPathItems.selected = false; // my attempt to select NONE
}
}
Copy link to clipboard
Copied
Hi @Paul_St_George I've updated the script above with an added check for locked or hidden layers that should fix the issue you describe.
> ... would be interested to know why my humble effort is not working
First, this isn't a beginner project—I've been scripting for many years and I fiddled quite a bit to get this working reliably. Your best bet would be to adjust your code slowly towards mine until you understand why I did what I did. Except that it really isn't a good beginner project, so you should start somewhere a bit more achievable I think.
I don't know of an easier way to achieve what this script does. I also made a version that uses the asset export system, but it was not really better, and in some ways worse, although it was a bit faster to run overall. I might post it for comparison.
In your example, you omit a number of things that are necessary for that approach, eg. deleting everything except what you are exporting. I also can't see where you are configuring the artboard range to export.
Other things:
- You've added an extraneous parameter to Document.exportFile: "selection[i]" which does nothing.
- Assuming allPathItems is an array of pathItems, there is no "selected" property of Array, so that won't work.
- To select none, do
doc.selection = [ ];
Sorry, it's just a bit of a difficult script to start out on. Don't be discouraged.
- Mark
Copy link to clipboard
Copied
For my learning I have made another version of this script that uses the asset export system (Document.exportForScreens and ExportForScreensItemToExport.assets) , which is different to the previous script, which uses Document.exportFile. Unfortunately it isn't much less fiddly than the other one.
I'd also be interested to know which one you think is better, as a user, if you can tell. I tried to make them ultimately give the same result. One slight difference is that Document.exportForScreens automatically creates the "SVG" subfolder which I can't seem to configure to be otherwise.
- Mark
/**
* @file Export Selected PathItems As SVG Assets.js
*
* Demonstration script:
* - This version uses the asset export system
* (Document.exportForScreens and ExportForScreensItemToExport.assets)
* - Exports every selected path item and compound path item
* on the active artboard as separate SVG file.
* - Exported files will be named by their item name
* or item typename and uuid.
*
* @author m1b
* @version 2025-05-25
* @discussion https://community.adobe.com/t5/illustrator-discussions/how-to-export-multiple-svg-from-one-layer/m-p/15338630
*/
(function () {
var doc = app.activeDocument;
var settings = {
// whether to export an unpainted rectangle to enforce the SVG viewBox
enforceArtboardBounds: true,
// where to export to
exportFolderPath: '#DOCUMENT_FOLDER#/',
// the SVG export options to use
exportOptions: {
coordinatePrecision: 3,
cssProperties: SVGCSSPropertyLocation.STYLEATTRIBUTES,
fontType: SVGFontType.OUTLINEFONT,
rasterImageLocation: RasterImageLocation.EMBED, // RasterImageLocation.LINK
svgId: SVGIdType.SVGIDREGULAR,
svgMinify: false,
svgResponsive: true,
},
// these are the things to export, choose `doc.pageItems` to export every item
targetItems: doc.selection,
// whether to show the results dialog afterwards
showResult: true,
};
if (!doc.fullName.exists)
return alert('Please save this document first.');
if (0 === settings.targetItems.length)
return alert('Please select some path items to export and try again.');
// this is added as a `note` to temporary group items created
// when exporting artboard bounds rectangle
const GROUP_ID = 'artboardBounds';
// prepare the layers
var layerStatus = [];
for (var i = 0; i < doc.layers.length; i++) {
layerStatus[i] = {
locked: doc.layers[i].locked,
visible: doc.layers[i].visible,
};
doc.layers[i].locked = false;
doc.layers[i].visible = true;
}
var counter = 0;
// uuids of the items to export
var targetItems = [];
// keep track of the (temporary) assets
var assets = [],
assetIDs = [];
// arrays to record whether an item has been processed
var already = [];
// in this loop we collect UUIDs for the things do and don't want to export
for (var i = 0, item, asset; i < settings.targetItems.length; i++) {
item = settings.targetItems[i];
if ('CompoundPathItem' === item.parent.constructor.name)
// we don't want a mere path of a compound path item
item = item.parent;
if (
'PathItem' !== item.constructor.name
&& 'CompoundPathItem' !== item.constructor.name
)
// not a path item or compound path item
continue;
if (already[item.uuid])
// already got this one
continue;
// unlock and unhide every target item
item.hidden = false;
item.locked = false;
// store item for later
targetItems.push(item);
// mark as done
already[item.uuid] = true;
}
for (var i = 0, item, group, artboardIndex, rectangle; i < targetItems.length; i++) {
item = targetItems[i];
if (settings.enforceArtboardBounds) {
artboardIndex = getArtboardIndexOfItem(item, doc.artboards);
if (undefined != artboardIndex) {
// create a temporary group and rectangle to enforce the artboard bounds
group = item.parent.groupItems.add();
group.note = GROUP_ID;
group.move(item, ElementPlacement.PLACEBEFORE);
// draw this to force the svg viewBox to match the artboard bounds
rectangle = settings.enforceArtboardBounds
? drawRectangle(doc, doc.artboards[artboardIndex].artboardRect, undefined, group, { note: 'boundsRectangle', filled: false, stroked: false })
: undefined;
// put item in the group
item.move(rectangle, ElementPlacement.PLACEAFTER);
// the item is now the group
item = group;
}
}
// create and store the asset and its ID
asset = doc.assets.add(item);
assets.push(asset);
assetIDs.push(asset.assetID)
}
// get the exporter function
var exportItems = getSVGExporter(doc, settings.exportFolderPath, settings.exportOptions, fileNamer);
// export assets
counter = exportItems(assetIDs, targetItems);
// remove the temporary assets
for (var i = assets.length - 1, group; i >= 0; i--) {
assets[i].remove();
if (GROUP_ID !== targetItems[i].parent.note)
continue;
// ungroup and remove temporary rectangle
group = targetItems[i].parent;
group.pageItems[0].remove();
targetItems[i] = group.pageItems[0];
targetItems[i].move(group, ElementPlacement.PLACEAFTER);
group.remove();
}
// restore the layers' properties
for (var i = 0; i < doc.layers.length; i++) {
doc.layers[i].locked = layerStatus[i].locked;
doc.layers[i].visible = layerStatus[i].visible;
}
if (settings.showResult)
// show result
alert('Exported ' + counter + ' path items as SVG.');
// reveal the exported files
doc.fullName.parent.execute();
/**
* Returns a name based on `item`.
* @param {PageItem} item - the item to name;
* @returns {String}
*/
function fileNamer(item) {
if (GROUP_ID === item.note)
// get the name of the item, not the group
item = item.pageItems[1];
return item.name || item.typename + '-' + item.uuid;
};
})();
/**
* Returns a pre-configured exporter function.
* @author m1b
* @version 2025-05-25
* @param {Document} doc - an Illustrator Document.
* @param {String} [exportFolderPath] - the export folder path (default: SVG folder in document's folder).
* @param {Object} [properties] - the export options properties to set (default: none, use default values).
*/
function getSVGExporter(doc, exportFolderPath, properties, namer) {
properties = properties || {};
if (!exportFolderPath)
exportFolderPath = '#DOCUMENT_FOLDER#/SVG';
// replace placeholders in export folder path
exportFolderPath = exportFolderPath
.replace('#DOCUMENT_FOLDER#', String(doc.fullName.parent));
var exportFolder = Folder(exportFolderPath);
if (!exportFolder.exists)
exportFolder.create();
// set up the SVG export options
var exportOptions = new ExportForScreensOptionsWebOptimizedSVG();
// apply export options properties
for (var key in properties)
if (properties.hasOwnProperty(key))
exportOptions[key] = properties[key];
return exportAssetsAsSVG;
/**
* Exports an asset as SVG.
*
* Note: exportForScreens always saves in an "SVG" subfolder.
*
* @author m1b
* @version 2025-05-25
* @param {Number} assetIDs - the ID of the asset to export.
* @param {Array<PageItem>} items - the filename for the exported file (do not include file extension).
* @returns {Number} - the count of assets successfully exported and named.
*/
function exportAssetsAsSVG(assetIDs, items) {
var counter = 0;
var itemToExport = new ExportForScreensItemToExport();
itemToExport.artboards = '';
itemToExport.document = false;
itemToExport.assets = assetIDs;
// the actual export
doc.exportForScreens(Folder(exportFolder + '/tmp'), ExportForScreensType.SE_SVG, exportOptions, itemToExport);
// exportForScreens always saves in a subfolder called "SVG"
exportFolder = Folder(exportFolder + '/SVG')
if (!exportFolder.exists)
// sorry can't find the expected folder
return;
// clean up the file names
for (var i = 0, f, newPath, path, paths; i < assetIDs.length; i++) {
// the path we want
newPath = exportFolder + '/' + namer(items[i]) + '.svg';
paths = [
// naming scheme 1
exportFolder + '/Asset ' + assetIDs[i] + '.svg',
// item parent (group) naming scheme 2
exportFolder + '/' + items[i].parent.name + '.svg',
// naming scheme 2
exportFolder + '/' + items[i].name + '.svg',
];
while (
// try each path until one exists
(path = paths.shift())
&& !(f = File(path)).exists
);
if (!path)
// no path worked!
continue;
if (newPath !== String(f)) {
// rename the exported file, by copying and destroying original
f.copy(newPath);
f.remove();
}
counter++;
}
return counter;
};
};
/**
* Draws a rectangle in the container.
* Takes Large Scale Documents into account.
* @author m1b
* @version 2024-09-26
* @param {Document} doc - an Illustrator document.
* @param {Array<Number>} rect - the rectangle dimensions [left, top, width, height].
* @param {Number} [unitFactor] - eg. 72 to convert to inches. All values will be scaled by this (default: 1).
* @param {Document|Layer|GroupItem} container - an Illustrator page item container (default: doc).
* @param {Object} [props] - properties to assign to the rectangle (default: none).
* @return {PathItem}
*/
function drawRectangle(doc, rect, unitFactor, container, properties) {
properties = properties || {};
unitFactor = unitFactor || 1;
container = container || doc;
var sf = 1 / doc.scaleFactor * unitFactor;
var rectangle = container.pathItems.rectangle(rect[1] * -sf, rect[0] * sf, (rect[2] - rect[0]) * sf, (rect[3] - rect[1]) * -sf); // T,L,W,H
// defaults
rectangle.filled = true;
rectangle.stroked = false;
// apply properties
for (var key in properties)
if (properties.hasOwnProperty(key))
rectangle[key] = properties[key];
return rectangle;
};
/**
* Returns the index of the artboard that has most overlap the item's bounds.
* @author m1b
* @version 2025-05-25
* @param {PageItem} item - a page item.
* @param {Array<Artboard>} artboards - artboards to search, if undefined, search all.
* @param {Boolean} preferIndex - if true, returns array of artboard indices {Array[Number]}
* @returns {Number?} - the artboard index;
*/
function getArtboardIndexOfItem(item, artboards) {
var artboardIndex;
for (var i = 0, overlap, maxOverlap = 0; i < artboards.length; i++) {
overlap = getOverlapArea(item.geometricBounds, artboards[i].artboardRect);
if (!overlap)
continue;
if (overlap > maxOverlap) {
maxOverlap = overlap;
artboardIndex = i;
}
}
if (undefined !== artboardIndex)
return artboardIndex;
};
/**
* Returns the overlapping area between
* two rectangular bounds.
* @author m1b
* @version 2025-05-25
* @param {bounds} a - First bounds [L, T, R, B].
* @param {bounds} b - Second bounds [L, T, R, B].
* @returns {Number|null} - area of overlap, or null if no overlap.
*/
function getOverlapArea(a, b) {
var left = Math.max(a[0], b[0]),
top = Math.min(a[1], b[1]),
right = Math.min(a[2], b[2]),
bottom = Math.max(a[3], b[3]);
var width = right - left,
height = top - bottom;
if (
width > 0
&& height > 0
)
return width * height;
// No overlap
return null;
};
Get ready! An upgraded Adobe Community experience is coming in January.
Learn more