Batch Convert AI to SVG and EPS in Illustrator (Free Script)
Free Illustrator script that converts folders of AI files to SVG, EPS or both, and resizes each EPS to just under 25 megapixels for Shutterstock.
Download illustrator-batch-ai-to-svg-eps.jsxJavaScript (ExtendScript) · 317 lines · 14 KB · free
Selling the same artwork on several sites means exporting it several ways: SVG for one, EPS under 25 megapixels for another. This script takes a folder of Illustrator files and produces both, sized correctly, in one run.
What it does
- Converts every
.aifile in a folder and its subfolders to SVG, EPS or both. You choose when it starts. - Saves SVG at the original size, with text converted to outlines
- Saves EPS as Illustrator 10 compatible, with the artboard resized to the largest size under 25 megapixels
- Scales everything evenly: artwork, strokes, patterns and gradients keep their proportions
- Puts the results in
SVGandEPSfolders next to the source files - Never modifies the original
.aifiles - Skips outputs that already exist, and lists the final EPS sizes in the summary
How to use it
- Run the script in Illustrator (see below).
- In the dialog, choose SVG only, EPS only or Both SVG and EPS.
- Choose the folder that holds your
.aifiles, or a parent folder. - Read the summary. It shows how many files were converted and the pixel size of each EPS.
How to run an Illustrator script
- Download the script with the button on this page.
- Open Adobe Illustrator. No document needs to be open.
- Go to File > Scripts > Other Script... (Ctrl+F12 on Windows, Cmd+F12 on Mac).
- Choose the downloaded
.jsxfile.
To keep the script in the menu, copy it into Illustrator's Presets > en_US > Scripts folder inside the Illustrator application folder and restart Illustrator. It then appears under File > Scripts.
Illustrator may warn that scripts can change your files. That is normal for any script. This one's full code is shown at the bottom of the page so you can read what it does.
Why 25 megapixels?
Shutterstock accepts vector EPS files between 4 and 25 megapixels. A larger artboard is rejected at upload. The script measures the first artboard and scales the file so width times height lands just below the limit, which also enlarges small artwork to the biggest size allowed.
Good to know
- Sizing is based on the first artboard. Files with several artboards are scaled by the same factor.
- Locked and hidden layers are scaled too and then returned to their locked or hidden state.
- To regenerate an EPS at a new size, delete the old EPS first. Existing files are skipped.
Questions
Can I change the 25 megapixel limit?
Yes. Open the script in a text editor and change MAX_PIXELS = 25000000 near the top. For 60 megapixels, use 60000000.
Is the SVG resized as well?
No. The SVG is exported first, at the original size. Only the EPS is resized.
I only have SVG files. Is there a script for those?
Yes: batch convert SVG to EPS.
The code
This is the complete script. Nothing else is downloaded or installed.
// illustrator-batch-ai-to-svg-eps.jsx
// Free script from LinearAndLoom Icons: https://linearandloomicons.netlify.app/scripts/
// Provided as is. Try it on a copy of your files first.
/* AI Convert - Adobe Illustrator script
------------------------------------------------------------
Converts Adobe Illustrator (.ai) files to SVG, EPS (Illustrator 10), or BOTH.
You choose the mode when the script runs.
EPS files are resized so the artboard is as large as possible while staying
UNDER 25 megapixels (at 72 ppi, 1 pt = 1 px). The scale is uniform, so
proportions, layout, strokes, patterns and gradients stay exactly as drawn.
Small files are scaled up and oversized files are scaled down.
SVG files are exported at the original size (before any resizing).
Output files are saved in "SVG" and/or "EPS" folders next to the source .ai files.
Original .ai files are never modified.
Existing output files are skipped, so it is safe to run again.
(Delete old EPS files if you want them regenerated at the new size.)
HOW TO RUN
Illustrator: File > Scripts > Other Script... > choose this file.
1. A dialog asks: SVG only / EPS only / Both
2. Pick the folder that contains your .ai files (or any parent folder).
Done.
OUTPUT FOLDER STRUCTURE
YourFolder/
yourfile.ai <- never touched
SVG/
yourfile.svg <- created if SVG or Both chosen
EPS/
yourfile.eps <- created if EPS or Both chosen (resized to < 25 MP)
*/
#target illustrator
(function () {
// ── Settings ──────────────────────────────────────────────────────────────
var MAX_PIXELS = 25000000; // must stay strictly below this (25 MP)
var SAFETY = 0.9995; // tiny margin so rounding never reaches 25 MP
var MAX_SIDE = 16383; // Illustrator canvas limit in points
// ── 1. Ask the user which conversion to run ──────────────────────────────
var dlg = new Window("dialog", "AI Convert — Choose Output");
dlg.orientation = "column";
dlg.alignChildren = ["fill", "top"];
dlg.margins = 24;
dlg.spacing = 14;
var lbl = dlg.add("statictext", undefined,
"Convert .ai files to:");
lbl.graphics.font = ScriptUI.newFont("dialog", ScriptUI.FontStyle.BOLD, 13);
var grp = dlg.add("group");
grp.orientation = "column";
grp.alignChildren = ["left", "center"];
grp.spacing = 10;
var r1 = grp.add("radiobutton", undefined, "SVG only");
var r2 = grp.add("radiobutton", undefined, "EPS only (Illustrator 10, resized to max under 25 MP)");
var r3 = grp.add("radiobutton", undefined, "Both SVG and EPS");
r3.value = true; // default: both
var btnGrp = dlg.add("group");
btnGrp.alignment = "right";
btnGrp.spacing = 10;
var btnCancel = btnGrp.add("button", undefined, "Cancel", { name: "cancel" });
var btnOK = btnGrp.add("button", undefined, "Next: Choose Folder »", { name: "ok" });
btnOK.onClick = function () { dlg.close(1); };
btnCancel.onClick = function () { dlg.close(0); };
var result = dlg.show();
if (result !== 1) { return; }
var doSVG = r1.value || r3.value;
var doEPS = r2.value || r3.value;
// ── 2. Pick folder ────────────────────────────────────────────────────────
var root = Folder.selectDialog("Select the folder with the .ai files");
if (!root) { return; }
var previousLevel = app.userInteractionLevel;
app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS;
var svgConverted = 0, svgSkipped = 0;
var epsConverted = 0, epsSkipped = 0;
var failed = [];
var sizeLog = [];
// ── SVG export options ────────────────────────────────────────────────────
function svgOptions() {
var o = new ExportOptionsSVG();
o.DTD = SVGDTDVersion.SVG1_1;
o.fontType = SVGFontType.OUTLINEFONT; // text as outlines
o.embedRasterImages = true;
o.preserveEditability = false;
o.includeFileInfo = false;
o.includeUnusedStyles = false;
o.coordinatePrecision = 4;
o.documentEncoding = SVGDocumentEncoding.UTF8;
o.cssProperties = SVGCSSPropertyLocation.STYLEATTRIBUTES;
return o;
}
// ── EPS save options (Illustrator 10, Shutterstock spec) ──────────────────
function epsOptions() {
var o = new EPSSaveOptions();
o.compatibility = Compatibility.ILLUSTRATOR10;
o.preview = EPSPreview.COLORTIFF;
o.embedAllFonts = true;
o.embedLinkedFiles = true;
o.includeDocumentThumbnails = true;
o.cmykPostScript = false;
o.postScript = EPSPostScriptLevelEnum.LEVEL2;
return o;
}
// ── Unlock / show all layers (recursively), remembering their state ──────
function openLayers(layers, states) {
for (var i = 0; i < layers.length; i++) {
var L = layers[i];
states.push({ layer: L, locked: L.locked, visible: L.visible });
if (!L.visible) { L.visible = true; }
if (L.locked) { L.locked = false; }
if (L.layers.length) { openLayers(L.layers, states); }
}
}
function restoreLayers(states) {
// reverse order: sublayers are restored before their parents
for (var i = states.length - 1; i >= 0; i--) {
var s = states[i];
try { s.layer.locked = s.locked; } catch (e) {}
try { s.layer.visible = s.visible; } catch (e) {}
}
}
// ── Resize artwork so artboard 1 is the largest size under 25 MP ─────────
// Uniform scale about the artboard centre; every artboard and every
// top-level object is scaled by the same factor, so nothing shifts
// relative to anything else.
function fitUnder25MP(doc) {
var prevCS = app.coordinateSystem;
app.coordinateSystem = CoordinateSystem.DOCUMENTCOORDINATESYSTEM;
var prevScaleStrokes = null;
try {
prevScaleStrokes = app.preferences.getBooleanPreference("scaleLineWeight");
app.preferences.setBooleanPreference("scaleLineWeight", true);
} catch (e) { prevScaleStrokes = null; }
var layerStates = [];
var info = null;
try {
var r = doc.artboards[0].artboardRect; // [left, top, right, bottom]
var w = Math.abs(r[2] - r[0]);
var h = Math.abs(r[1] - r[3]);
if (w <= 0 || h <= 0) { return null; }
// Largest uniform scale that keeps w*h under 25 MP
var s = Math.sqrt((MAX_PIXELS * SAFETY) / (w * h));
if (w * s > MAX_SIDE) { s = MAX_SIDE / w; }
if (h * s > MAX_SIDE) { s = MAX_SIDE / h; }
while (Math.ceil(w * s) * Math.ceil(h * s) >= MAX_PIXELS) { s *= 0.9999; }
info = {
w: Math.round(w * s), h: Math.round(h * s),
mp: (Math.ceil(w * s) * Math.ceil(h * s) / 1000000).toFixed(2),
capped: (w * s >= MAX_SIDE - 0.5 || h * s >= MAX_SIDE - 0.5)
};
if (Math.abs(s - 1) < 0.000001) { return info; } // already the right size
var cx = (r[0] + r[2]) / 2;
var cy = (r[1] + r[3]) / 2;
var pct = s * 100;
openLayers(doc.layers, layerStates);
// Collect top-level objects only (children move with their parent)
var all = doc.pageItems, list = [], i;
for (i = 0; i < all.length; i++) {
if (all[i].parent.typename === "Layer") { list.push(all[i]); }
}
for (i = 0; i < list.length; i++) {
var it = list[i];
var wasLocked = it.locked, wasHidden = it.hidden;
if (wasLocked) { it.locked = false; }
if (wasHidden) { it.hidden = false; }
var b0 = it.geometricBounds;
var ox = (b0[0] + b0[2]) / 2, oy = (b0[1] + b0[3]) / 2;
// scale positions, fill patterns, gradients, stroke patterns, line widths
it.resize(pct, pct, true, true, true, true, pct, Transformation.CENTER);
// move the object so its centre lands at the scaled position
var b1 = it.geometricBounds;
var nx = (b1[0] + b1[2]) / 2, ny = (b1[1] + b1[3]) / 2;
it.translate((cx + (ox - cx) * s) - nx, (cy + (oy - cy) * s) - ny);
if (wasHidden) { it.hidden = true; }
if (wasLocked) { it.locked = true; }
}
// Scale every artboard about the same centre
for (i = 0; i < doc.artboards.length; i++) {
var a = doc.artboards[i].artboardRect;
doc.artboards[i].artboardRect = [
cx + (a[0] - cx) * s,
cy + (a[1] - cy) * s,
cx + (a[2] - cx) * s,
cy + (a[3] - cy) * s
];
}
} finally {
restoreLayers(layerStates);
if (prevScaleStrokes !== null) {
try { app.preferences.setBooleanPreference("scaleLineWeight", prevScaleStrokes); } catch (e) {}
}
app.coordinateSystem = prevCS;
}
return info;
}
// ── Recursive folder walker ───────────────────────────────────────────────
function walk(folder) {
var items = folder.getFiles(), ais = [], i;
for (i = 0; i < items.length; i++) {
var it = items[i];
if (it instanceof Folder) {
var n = it.name.toLowerCase();
if (n !== "eps" && n !== "svg" && n !== "illustrator" &&
n !== "__macosx" && n.charAt(0) !== ".") { walk(it); }
} else if (/\.ai$/i.test(it.name) && it.name.charAt(0) !== ".") {
ais.push(it);
}
}
if (!ais.length) { return; }
// Create output folders only if needed
var svgOut = doSVG ? new Folder(folder.fsName + "/SVG") : null;
var epsOut = doEPS ? new Folder(folder.fsName + "/EPS") : null;
if (svgOut && !svgOut.exists) { svgOut.create(); }
if (epsOut && !epsOut.exists) { epsOut.create(); }
for (i = 0; i < ais.length; i++) {
var baseName = decodeURI(ais[i].name).replace(/\.ai$/i, "");
var svgTarget = doSVG ? new File(svgOut.fsName + "/" + baseName + ".svg") : null;
var epsTarget = doEPS ? new File(epsOut.fsName + "/" + baseName + ".eps") : null;
// Check what actually needs doing for this file
var needSVG = doSVG && !svgTarget.exists;
var needEPS = doEPS && !epsTarget.exists;
if (doSVG && !needSVG) { svgSkipped++; }
if (doEPS && !needEPS) { epsSkipped++; }
if (!needSVG && !needEPS) { continue; } // both already exist
var doc = null;
try {
doc = app.open(ais[i]);
// SVG first, at the original size
if (needSVG) {
doc.exportFile(svgTarget, ExportType.SVG, svgOptions());
svgConverted++;
}
// Then resize to max under 25 MP and save EPS
if (needEPS) {
var info = fitUnder25MP(doc);
doc.saveAs(epsTarget, epsOptions());
epsConverted++;
if (info) {
sizeLog.push(baseName + ".eps " + info.w + " x " + info.h +
" px (" + info.mp + " MP" +
(info.capped ? ", limited by canvas size" : "") + ")");
}
}
doc.close(SaveOptions.DONOTSAVECHANGES);
} catch (e) {
failed.push(baseName + ".ai (" + e + ")");
try { if (doc) { doc.close(SaveOptions.DONOTSAVECHANGES); } } catch (e2) {}
}
}
}
// ── Run ───────────────────────────────────────────────────────────────────
try { walk(root); } finally { app.userInteractionLevel = previousLevel; }
// ── Summary ───────────────────────────────────────────────────────────────
var msg = "AI Convert — finished\n";
if (doSVG) {
msg += "\nSVG\n Converted : " + svgConverted + "\n Skipped : " + svgSkipped;
}
if (doEPS) {
msg += "\nEPS\n Converted : " + epsConverted + "\n Skipped : " + epsSkipped;
if (sizeLog.length) {
msg += "\n\nEPS sizes:\n" + sizeLog.slice(0, 15).join("\n");
if (sizeLog.length > 15) { msg += "\n…and " + (sizeLog.length - 15) + " more"; }
}
}
if (failed.length) {
msg += "\n\nFailed (" + failed.length + "):\n" + failed.slice(0, 15).join("\n");
}
alert(msg);
})();
Download illustrator-batch-ai-to-svg-eps.jsxJavaScript (ExtendScript) · 317 lines · 14 KB · free
These scripts are free. If one saved you time, you can say thanks by buying a pack from the LinearAndLoom Etsy shop.


