Skip to content

Instantly share code, notes, and snippets.

@TomByrne
Last active March 19, 2024 10:12
Show Gist options
  • Save TomByrne/7816376 to your computer and use it in GitHub Desktop.
Save TomByrne/7816376 to your computer and use it in GitHub Desktop.
An Illustrator script for exporting layers and/or artboards into separate files (PNG8 / PNG24 / EPS / PDF / SVG / JPG / FXG).See http://www.tbyrne.org/export-illustrator-layers-to-svg-files
// MultiExporter.jsx
// Version 0.1
// Version 0.2 Adds PNG and EPS exports
// Version 0.3 Adds support for exporting at different resolutions
// Version 0.4 Adds support for SVG, changed EPS behaviour to minimise output filesize
// Version 0.5 Fixed cropping issues
// Version 0.6 Added inner padding mode to prevent circular bounds clipping
//
// Copyright 2013 Tom Byrne
// Comments or suggestions to tom@tbyrne.org
//
// Copyright 2011 Matthew Ericson
var docRef = app.activeDocument;
// Format specific functionality
getPng8Options = function ( transparency, scaling, embedImage, embedFont, trimEdges ) {
var options = new ExportOptionsPNG8();
options.antiAliasing = true;
options.transparency = transparency;
options.artBoardClipping = true;
options.horizontalScale = scaling;
options.verticalScale = scaling;
return options;
}
getPng24Options = function ( transparency, scaling, embedImage, embedFont, trimEdges ) {
var options = new ExportOptionsPNG24();
options.antiAliasing = true;
options.transparency = transparency;
options.artBoardClipping = true;
options.horizontalScale = scaling;
options.verticalScale = scaling;
return options;
}
getPdfOptions = function ( transparency, scaling, embedImage, embedFont, trimEdges ) {
var options = new PDFSaveOptions();
options.compatibility = PDFCompatibility.ACROBAT5;
options.generateThumbnails = true;
options.preserveEditability = false;
return options;
}
getJpgOptions = function ( transparency, scaling, embedImage, embedFont, trimEdges ) {
var options = new ExportOptionsJPEG();
options.antiAliasing = true;
options.artBoardClipping = true;
options.horizontalScale = scaling;
options.verticalScale = scaling;
return options;
}
getEpsOptions = function ( transparency, scaling, embedImage, embedFont, trimEdges ) {
var options = new EPSSaveOptions();
options.embedLinkedFiles = embedImage;
options.embedAllFonts = embedFont;
options.includeDocumentThumbnails = true;
options.saveMultipleArtboards = false;
return options;
}
getSvgOptions = function ( transparency, scaling, embedImage, embedFont, trimEdges ) {
options = new ExportOptionsSVG();
options.embedRasterImages = embedImage;
return options;
}
getFxg1Options = function ( transparency, scaling, embedImage, embedFont, trimEdges ) {
options = new FXGSaveOptions();
options.version = FXGVersion.VERSION1PT0;
return options;
}
getFxg2Options = function ( transparency, scaling, embedImage, embedFont, trimEdges ) {
options = new FXGSaveOptions();
options.version = FXGVersion.VERSION2PT0;
return options;
}
// Format specific save functions
savePng8 = function ( doc, filePath, options, artboardIndex, artboardName ) {
var destFile = new File( filePath + '.png' );
doc.exportFile(destFile, ExportType.PNG8 , options);
}
savePng24 = function ( doc, filePath, options, artboardIndex, artboardName ) {
var destFile = new File( filePath + '.png' );
doc.exportFile(destFile, ExportType.PNG24 , options);
}
savePdf = function ( doc, filePath, options, artboardIndex, artboardName ) {
var destFile = new File( filePath + '.pdf' );
options.artboardRange = (artboardIndex+1).toString();
doc.saveAs( destFile, options, artboardIndex, artboardName )
}
saveJpg = function ( doc, filePath, options, artboardIndex, artboardName ) {
var destFile = new File( filePath + '.jpg' );
doc.exportFile(destFile, ExportType.JPEG , options);
}
saveEps = function ( doc, filePath, options, artboardIndex, artboardName ) {
var destFile = new File( filePath + '.eps' );
options.artboardRange = (artboardIndex+1).toString();
doc.saveAs( destFile, options, artboardIndex, artboardName )
}
saveSvg = function ( doc, filePath, options, artboardIndex, artboardName ) {
var destFile = new File( filePath + '.svg' );
doc.exportFile(destFile, ExportType.SVG , options);
}
saveFxg = function ( doc, filePath, options, artboardIndex, artboardName ) {
var destFile = new File( filePath + '.fxg' );
options.artboardRange = (artboardIndex+1).toString();
doc.saveAs( destFile, options, artboardIndex, artboardName )
}
var multi_exporter = {
PREFS_LAYER_NAME: "nyt_exporter_info",
multiExporterPrefs: null,
prefix: '',
suffix: '',
base_path: '~/Desktop',
transparency: false,
embedImage: true,
embedFont: true,
trimEdges: true,
innerPadding: false,
ignoreWarnings: false,
format: "PNG 24",
artboards: null,
layers: null,
dlg: null,
prefs_xml: null,
currentFormatInfo: null,
num_to_export: 0,
failed_artboards: null,
failed_layers: null,
export_artboards: null,
export_layers: null,
whole_artboard_mode: false,
formatList:null,
artboardList:null,
progLabel:null,
// these are controls that are format dependant
controlNames:["scalingInput","transCheckBox","embedImageCheckBox","embedFontCheckBox","trimEdgesCheckBox","innerPaddingCheckBox"],
scalingInput:null,
transCheckBox:null,
embedImageCheckBox:null,
embedFontCheckBox:null,
trimEdgesCheckBox:null,
ignoreCheckBox:null,
exportArtboardsCheckBox:null,
// copyBehaviour - for vector outputs the output must be done from a copy of the document (to avoid hidden layers being included in output)
formatInfo: [ {name:"PNG 8", copyBehaviour:false, pixelDocSize:true, getOptions:getPng8Options, saveFile:savePng8, activeControls:["scalingInput","transCheckBox","trimEdgesCheckBox","innerPaddingCheckBox"]},
{name:"PNG 24", copyBehaviour:false, pixelDocSize:true, getOptions:getPng24Options, saveFile:savePng24, activeControls:["scalingInput","transCheckBox","trimEdgesCheckBox","innerPaddingCheckBox"]},
{name:"PDF", copyBehaviour:false, pixelDocSize:false, getOptions:getPdfOptions, saveFile:savePdf, activeControls:["trimEdgesCheckBox"]},
{name:"JPG", copyBehaviour:false, pixelDocSize:true, getOptions:getJpgOptions, saveFile:saveJpg, activeControls:["scalingInput","trimEdgesCheckBox","innerPaddingCheckBox"]},
{name:"EPS", copyBehaviour:true, pixelDocSize:false, getOptions:getEpsOptions, saveFile:saveEps, activeControls:["embedImageCheckBox","embedFontCheckBox","trimEdgesCheckBox"]},
{name:"SVG", copyBehaviour:true, pixelDocSize:false, getOptions:getSvgOptions, saveFile:saveSvg, activeControls:["embedImageCheckBox","trimEdgesCheckBox"]},
{name:"FXG 1.0", copyBehaviour:true, pixelDocSize:false, getOptions:getFxg1Options, saveFile:saveFxg, activeControls:["trimEdgesCheckBox"]},
{name:"FXG 2.0", copyBehaviour:true, pixelDocSize:false, getOptions:getFxg2Options, saveFile:saveFxg, activeControls:["trimEdgesCheckBox"]}],
artboardSelect: [ {code:"all", name:'All Artboards (except those beginning with - )'},
{code:"current", name:'Current Artboard'},
{name:'---'} ],
layerSelect: [ {code:"all", name:'All Layers (except those beginning with - )'},
{code:"none", name:'None (for use with \'Export Artboard Images\')'},
{code:"selected", name:'Selected Items\' Layers'},
{name:'---'} ],
init: function() {
var parse_success = this.load_prefs();
if (parse_success) {
this.show_dialog();
}
},
findExportTypeByCode: function(code){
for(var i=0; i<this.exportTypes.length; ++i){
var type = this.exportTypes[i];
if(type.code==code)return type;
}
},
load_prefs: function() {
var parse_success = false;
// find existing layers or add new one
try {
this.multiExporterPrefs = docRef.layers.getByName( this.PREFS_LAYER_NAME );
} catch ( e ) {
this.multiExporterPrefs = docRef.layers.add();
this.multiExporterPrefs.name = this.PREFS_LAYER_NAME;
var nyt_exporter_info_xml = this.multiExporterPrefs.textFrames.add();
var saved_data = new XML( '<nyt_prefs></nyt_prefs>' );
saved_data.appendChild( new XML('<nyt_prefix></nyt_prefix>') );
saved_data.appendChild( new XML('<nyt_suffix></nyt_suffix>') );
saved_data.appendChild( new XML('<nyt_base_path>~/Desktop</nyt_base_path>') );
saved_data.appendChild( new XML('<nyt_scaling>100%</nyt_scaling>') );
saved_data.appendChild( new XML('<nyt_transparency>true</nyt_transparency>') );
saved_data.appendChild( new XML('<nyt_embedImage>true</nyt_embedImage>') );
saved_data.appendChild( new XML('<nyt_embedFont>true</nyt_embedFont>') );
saved_data.appendChild( new XML('<nyt_trimEdges>true</nyt_trimEdges>') );
saved_data.appendChild( new XML('<nyt_innerPadding>false</nyt_innerPadding>') );
saved_data.appendChild( new XML('<nyt_format>PNG 24</nyt_format>') );
saved_data.appendChild( new XML('<nyt_artboards>all</nyt_artboards>') );
saved_data.appendChild( new XML('<nyt_layers>all</nyt_layers>') );
saved_data.appendChild( new XML('<nyt_exportArtboards>false</nyt_exportArtboards>') );
saved_data.appendChild( new XML('<nyt_ignoreWarnings>false</nyt_ignoreWarnings>') );
nyt_exporter_info_xml.contents = saved_data.toXMLString();
this.multiExporterPrefs.printable = false;
this.multiExporterPrefs.visible = false;
}
// get xml out of the 1 text item on that layer and parse it
if ( this.multiExporterPrefs.textFrames.length != 1 ) {
Window.alert( 'Please delete the '+this.PREFS_LAYER_NAME+' layer and try again.' );
} else {
try {
this.prefs_xml = new XML( this.multiExporterPrefs.textFrames[0].contents );
this.prefix = this.prefs_xml.nyt_prefix;
this.suffix = this.prefs_xml.nyt_suffix;
this.base_path = this.prefs_xml.nyt_base_path;
this.scaling = this.prefs_xml.nyt_scaling;
this.transparency = this.prefs_xml.nyt_transparency == "true" ? true : false;
this.embedImage = this.prefs_xml.nyt_embedImage == "true" ? true : false;
this.ignoreWarnings = this.prefs_xml.nyt_ignoreWarnings == "true" ? true : false;
this.embedFont = this.prefs_xml.nyt_embedFont == "true" ? true : false;
this.trimEdges = this.prefs_xml.nyt_trimEdges == "true" ? true : false;
this.innerPadding = this.prefs_xml.nyt_innerPadding == "true" ? true : false;
this.format = this.prefs_xml.nyt_format;
this.artboards = this.prefs_xml.nyt_artboards.toString();
this.layers = this.prefs_xml.nyt_layers.toString();
this.whole_artboard_mode = this.prefs_xml.nyt_exportArtboards == "true" ? true : false;
if(!this.artboards){
this.artboards = this.artboardSelect[0].code;
}else if(parseInt(this.artboards).toString()==this.artboards){
this.artboards = parseInt(this.artboards);
}
if(!this.layers){
this.layers = this.layerSelect[0].code;
}else if(parseInt(this.layers).toString()==this.layers){
this.layers = parseInt(this.layers);
}
if ( ! this.prefs_xml.nyt_scaling || this.prefs_xml.nyt_scaling == '' ) {
this.scaling = '100%';
}
parse_success = true;
} catch ( e ) {
Window.alert( 'Please delete the this.multiExporterPrefs layer and try again.' );
}
}
return parse_success;
},
// dialog display
show_dialog: function() {
// Export dialog
this.dlg = new Window('dialog', 'Multi Exporter');
var row;
// ARTBOARD TYPE ROW
row = this.dlg.add('group', undefined, '')
row.oreintation = 'row';
row.alignment = [ScriptUI.Alignment.LEFT, ScriptUI.Alignment.TOP]
var typeSt = row.add('statictext', undefined, 'Export artboards:');
typeSt.size = [ 100,20 ];
var artboardNames = [];
for(var i=0; i<this.artboardSelect.length; ++i){
artboardNames.push(this.artboardSelect[i].name)
}
for(var i=0; i<docRef.artboards.length; i++){
var artboard = docRef.artboards[i];
artboardNames.push((i+1)+": "+artboard.name);
}
this.artboardList = row.add('dropdownlist', undefined, artboardNames);
this.artboardList.selection = this.findDataIndex(this.artboards, this.artboardSelect);
this.exportArtboardsCheckBox = row.add('checkbox', undefined, 'Export Artboard Images');
this.exportArtboardsCheckBox.value = this.whole_artboard_mode;
// LAYER TYPE ROW
row = this.dlg.add('group', undefined, '')
row.oreintation = 'row';
row.alignment = [ScriptUI.Alignment.LEFT, ScriptUI.Alignment.TOP]
var typeSt = row.add('statictext', undefined, 'Export layers:');
typeSt.size = [ 100,20 ];
var layerNames = [];
for(var i=0; i<this.layerSelect.length; ++i){
layerNames.push(this.layerSelect[i].name)
}
for(var i=0; i<docRef.layers.length; i++){
var layer = docRef.layers[i];
layerNames.push((i+1)+": "+layer.name);
}
this.layerList = row.add('dropdownlist', undefined, layerNames);
this.layerList.selection = this.findDataIndex(this.layers, this.layerSelect);
// PREFIX GRP
row = this.dlg.add('group', undefined, '')
row.oreintation = 'row';
row.alignment = [ScriptUI.Alignment.LEFT, ScriptUI.Alignment.TOP]
var prefixSt = row.add('statictext', undefined, 'File prefix:');
prefixSt.size = [100,20]
this.prefixEt = row.add('edittext', undefined, this.prefix);
this.prefixEt.size = [ 300,20 ];
// suffix row
row = this.dlg.add('group', undefined, '')
row.oreintation = 'row';
row.alignment = [ScriptUI.Alignment.LEFT, ScriptUI.Alignment.TOP]
row = this.dlg.add('group', undefined, '')
row.oreintation = 'row';
row.alignment = [ScriptUI.Alignment.LEFT, ScriptUI.Alignment.TOP]
var suffixSt = row.add('statictext', undefined, 'File suffix:');
suffixSt.size = [100,20]
this.suffixEt = row.add('edittext', undefined, this.suffix);
this.suffixEt.size = [ 300,20 ];
// scaling row
row = this.dlg.add('group', undefined, '')
row.oreintation = 'row';
row.alignment = [ScriptUI.Alignment.LEFT, ScriptUI.Alignment.TOP]
var scalingLabel = row.add('statictext', undefined, 'Scaling:');
scalingLabel.size = [100,20]
this.scalingInput = row.add('edittext', undefined, this.scaling);
this.scalingInput.size = [ 100,20 ];
var scalingTip = row.add('statictext', undefined, '(Normally 100%; Use 200% for Retina display exports)');
scalingTip.size = [300,20]
// DIR GROUP
row = this.dlg.add( 'group', undefined, '')
row.orientation = 'row'
row.alignment = [ScriptUI.Alignment.LEFT, ScriptUI.Alignment.TOP]
var dirSt = row.add('statictext', undefined, 'Output directory:');
dirSt.size = [ 100,20 ];
this.dirEt = row.add('edittext', undefined, this.base_path);
this.dirEt.size = [ 300,20 ];
var chooseBtn = row.add('button', undefined, 'Choose ...' );
chooseBtn.onClick = function() { multi_exporter.dirEt.text = Folder.selectDialog(); }
// FORMAT ROW
row = this.dlg.add('group', undefined, '');
row.orientation = 'row'
row.alignment = [ScriptUI.Alignment.LEFT, ScriptUI.Alignment.TOP]
var formatSt = row.add('statictext', undefined, 'Export format:');
formatSt.size = [ 100,20 ];
var formatNames = [];
for(var i=0; i<this.formatInfo.length; ++i){
formatNames.push(this.formatInfo[i].name)
}
this.formatList = row.add('dropdownlist', undefined, formatNames);
this.formatList.selection = 1;
for ( var i=0; i < this.formatList.items.length; i++ ) {
if ( multi_exporter.format == this.formatList.items[i].text ) {
this.formatList.selection = i;
}
}
this.embedImageCheckBox = row.add('checkbox', undefined, 'Embed Imagery');
this.embedImageCheckBox.value = this.embedImage;
this.embedFontCheckBox = row.add('checkbox', undefined, 'Embed Fonts');
this.embedFontCheckBox.value = this.embedFont;
// TRANSPARENCY AND TRIM ROW
row = this.dlg.add('group', undefined, '');
row.orientation = 'row'
row.alignment = [ScriptUI.Alignment.CENTER, ScriptUI.Alignment.TOP]
this.transCheckBox = row.add('checkbox', undefined, 'Transparency');
this.transCheckBox.value = this.transparency;
this.trimEdgesCheckBox = row.add('checkbox', undefined, 'Trim Edges');
this.trimEdgesCheckBox.value = this.trimEdges;
this.innerPaddingCheckBox = row.add('checkbox', undefined, 'Inner Padding (to prevent curved edge clipping)');
this.innerPaddingCheckBox.value = this.innerPadding;
// progress bar
var progBar = this.dlg.add( 'progressbar', undefined, 0, 100 );
progBar.size = [400,10]
this.progLabel = this.dlg.add('statictext', undefined, '...' );
this.progLabel.size = [ 400,20 ];
// buttons row
row = this.dlg.add('group', undefined, '');
row.orientation = 'row'
var cancelBtn = row.add('button', undefined, 'Cancel', {name:'cancel'});
cancelBtn.onClick = function() { multi_exporter.dlg.close() };
var saveBtn = row.add('button', undefined, 'Save and Close', {name:'saveClose'});
saveBtn.onClick = function() {
multi_exporter.saveOptions();
multi_exporter.dlg.close()
};
// OK button
var okBtn = row.add('button', undefined, 'Export', {name:'ok'});
okBtn.onClick = function() {
multi_exporter.saveOptions(); // save options before export in case of errors
try{
multi_exporter.run_export();
}catch(e){
alert(e);
}
};
this.ignoreCheckBox = row.add('checkbox', undefined, 'Ignore Warnings');
this.ignoreCheckBox.value = this.ignoreWarnings;
// Export type handler
this.artboardList.onChange = function() {
multi_exporter.artboards = multi_exporter.getListData(multi_exporter.artboardList.selection.index, multi_exporter.artboardSelect);
multi_exporter.update_export_desc( );
};
this.layerList.onChange = function() {
multi_exporter.layers = multi_exporter.getListData(multi_exporter.layerList.selection.index, multi_exporter.layerSelect);
multi_exporter.update_export_desc( );
};
this.exportArtboardsCheckBox.onClick = function() {
multi_exporter.whole_artboard_mode = multi_exporter.exportArtboardsCheckBox.value;
multi_exporter.update_export_desc( );
};
// Format change handler
this.formatList.onChange = function() {
multi_exporter.checkFormat();
};
multi_exporter.update_export_desc( );
this.dlg.progBar = progBar;
this.checkFormat();
this.dlg.show();
},
findDataIndex: function(data, selectList){
if(typeof(data)=="string" && parseInt(data).toString()==data){
data = parseInt(data);
}
if(typeof(data)=="number"){
return selectList.length+data;
}else{
for(var i=0; i<selectList.length; ++i){
if(selectList[i].code==data){
return i;
}
}
}
alert("no find: "+data);
},
getListData: function(index, selectList){
if(index>=selectList.length){
return index-selectList.length;
}else{
return selectList[index].code;
}
},
checkFormat: function ( progLabel ) {
var formatInfo = this.formatInfo[this.formatList.selection.index];
this.currentFormatInfo = formatInfo;
for(var i=0; i<this.controlNames.length; i++){
var controlName = this.controlNames[i];
this[controlName].enabled = (this.indexOf(formatInfo.activeControls, controlName)!=-1);
}
},
indexOf: function ( array, element ) {
for(var i=0; i<array.length; i++){
if(array[i]==element)return i;
}
return -1;
},
update_export_desc: function () {
this.export_artboards = [];
this.export_layers = [];
if(this.artboards=="all"){
for(var i=0; i<docRef.artboards.length; ++i){
var artboard = docRef.artboards[i];
if(!artboard.name.match( /^\-/ )){
this.export_artboards.push(i);
}
}
}else if(this.artboards=="current"){
this.export_artboards.push(docRef.artboards.getActiveArtboardIndex());
}else if(typeof(this.artboards)=="number"){
this.export_artboards.push(this.artboards);
}
if(this.layers=="all"){
for(var i=0; i<docRef.layers.length; ++i){
var layer = docRef.layers[i];
if(!this.isAdditionalLayer(layer) && this.isIncludeLayer(layer)){
this.export_layers.push(i);
}
}
}else if(this.layers=="selected"){
for(var i=0; i<docRef.layers.length; ++i){
var layer = docRef.layers[i];
if(!this.isAdditionalLayer(layer) && this.isIncludeLayer(layer) && layer.hasSelectedArtwork){
this.export_layers.push(i);
}
}
this.export_layers.push(docRef.artboards.getActiveArtboardIndex());
}else if(typeof(this.layers)=="number"){
this.export_layers.push(this.layers);
}
this.updateExportCounts();
},
updateExportCounts: function(){
this.num_to_export = this.export_artboards.length*this.export_layers.length;
var artboardExportTxt;
if(this.whole_artboard_mode){
this.num_to_export += this.export_artboards.length;
if(this.export_layers.length){
artboardExportTxt = " (and "+this.export_artboards.length+" artboard images)";
}else{
this.progLabel.text = 'Will export ' + this.export_artboards.length + ' of ' + docRef.artboards.length + ' artboards';
return;
}
}else{
artboardExportTxt = "";
}
if(this.export_artboards.length>1 && this.export_layers.length>1){
this.progLabel.text = 'Will export ' + this.num_to_export + ' files (' + this.export_layers.length + ' layers * ' + this.export_artboards.length + ' artboards)'+artboardExportTxt;
}else if(this.export_layers.length>0 && this.export_artboards.length==1){
var artboard = docRef.artboards[this.export_artboards[0]];
this.progLabel.text = 'Will export ' + this.export_layers.length + ' of ' + docRef.layers.length+ ' layers on artboard "' + artboard.name +'"' +artboardExportTxt;
}else if(this.export_layers.length>0 && this.export_artboards.length>0){
var artboard = docRef.artboards[this.export_artboards[0]];
this.progLabel.text = 'Will export ' + this.export_layers.length+' layers on "' + this.export_artboards.length +'" artboards' +artboardExportTxt;
}else{
this.progLabel.text = 'Please select valid artboards / layers' ;
}
},
saveOptions:function(){
this.prefix = this.prefixEt.text;
this.suffix = this.suffixEt.text;
this.base_path = this.dirEt.text;
this.transparency = this.transCheckBox.value;
this.embedImage = this.embedImageCheckBox.value;
this.embedFont = this.embedFontCheckBox.value;
this.trimEdges = this.trimEdgesCheckBox.value;
this.innerPadding = this.innerPaddingCheckBox.value;
this.ignoreWarnings = this.ignoreCheckBox.value;
this.format = this.formatList.selection.text;
this.scaling = parseFloat( this.scalingInput.text.replace( /\% /, '' ));
this.prefs_xml.nyt_base_path = this.base_path;
this.prefs_xml.nyt_scaling = this.scaling;
this.prefs_xml.nyt_prefix = this.prefix;
this.prefs_xml.nyt_suffix = this.suffix;
this.prefs_xml.nyt_transparency = this.transparency;
this.prefs_xml.nyt_embedImage = this.embedImage;
this.prefs_xml.nyt_ignoreWarnings = this.ignoreWarnings;
this.prefs_xml.nyt_embedFont = this.embedFont;
this.prefs_xml.nyt_trimEdges = this.trimEdges;
this.prefs_xml.nyt_innerPadding = this.innerPadding;
this.prefs_xml.nyt_format = this.format;
this.prefs_xml.nyt_artboards = this.artboards;
this.prefs_xml.nyt_layers = this.layers;
this.prefs_xml.nyt_exportArtboards = this.whole_artboard_mode;
this.multiExporterPrefs.textFrames[0].contents = this.prefs_xml.toXMLString();
},
// run_export function. does the dirty work
run_export: function() {
this.failed_artboards = [];
this.failed_layers = [];
var formatInfo = this.currentFormatInfo;
var copyBehaviour = formatInfo.copyBehaviour || this.trimEdges;
var num_exported = 0;
var options = formatInfo.getOptions(this.transparency, this.scaling, this.embedImage, this.embedFont, this.trimEdges);
if(!copyBehaviour){
var were_shown = this.get_shown_layers();
}
if(!this.export_artboards.length || (!this.export_layers.length && !this.whole_artboard_mode)){
alert('Please select valid artboards / layers');
return;
}
if(!this.base_path){
alert('Please select select a destination');
return;
}
// offsetting must be in relation to the center of the first artboard
var firstRect = docRef.artboards[0].artboardRect;
var firstCentX = (firstRect[2]-firstRect[0])/2;
var firstCentY = (firstRect[1]-firstRect[3])/2;
for (var i = 0; i < this.export_artboards.length; i++ ) {
var artboard = docRef.artboards[this.export_artboards[i]];
var artboardName = artboard.name;
starting_artboard = docRef.artboards.setActiveArtboardIndex(i);
var rect = artboard.artboardRect;
var offsetX = firstRect[0]-rect[0];
var offsetY = firstRect[1]-rect[1];
var artW = rect[2]-rect[0];
var artH = rect[1]-rect[3];
var copyDoc;
// if exporting artboard by artboard, export layers as is
if ( this.whole_artboard_mode) {
try{
var base_filename = this.base_path + "/" + this.prefix + artboardName + this.suffix;
if(copyBehaviour){
var offset = {x:offsetX, y:offsetY};
copyDoc = this.copyDocument(docRef, formatInfo.pixelDocSize, artboard, rect, artW, artH, offset, function(layer){return (layer.name!=multi_exporter.PREFS_LAYER_NAME && layer.visible)});
formatInfo.saveFile(copyDoc, base_filename, options, i, artboardName);
copyDoc.close(SaveOptions.DONOTSAVECHANGES);
copyDoc = null;
}else{
formatInfo.saveFile(docRef, base_filename, options, i, artboardName);
}
}catch(e){
this.failed_artboards.push(i);
}
this.updateProgress(++num_exported);
}
if(this.export_layers.length){
if(copyBehaviour){
if(!this.trimEdges){
var layerDepths = [];
var offset = {x:offsetX, y:offsetY};
copyDoc = this.copyDocument(docRef, formatInfo.pixelDocSize, artboard, rect, docRef.width, docRef.height, offset, this.isAdditionalLayer, layerDepths);
var hasAdditLayers = copyDoc.layers.length>0;
}
}else{
this.hide_all_layers();
}
for ( var j=0; j < this.export_layers.length; j++ ) {
var layer = docRef.layers[this.export_layers[j]];
var lyr_name = layer.name;
try{
var layerRect
// only process layer if it has bounds (i.e. not guide layer) and falls within current artboard bounds
layerRect = this.get_layer_bounds(layer)
if(layerRect==null)continue;
if (layerRect[0]<layerRect[2] && layerRect[1]>layerRect[3]) {
var isVis = this.intersects(rect, layerRect);
if(!hasAdditLayers && !isVis && !this.trimEdges){
// skip layers where nothing is visible
continue;
}
var base_filename;
if ( this.export_artboards.length==1 ) {
base_filename = this.base_path + "/" + this.prefix + lyr_name + this.suffix;
} else{
base_filename = this.base_path + "/" + this.prefix + artboardName + '-' + lyr_name + this.suffix;
}
if(copyBehaviour){
if(this.trimEdges){
if(copyDoc){
copyDoc.close(SaveOptions.DONOTSAVECHANGES);
copyDoc = null;
}
// crop to artboard
if(layerRect[0]<rect[0]){
layerRect[0] = rect[0];
}else{
intendedX = 0;
}
if(layerRect[1]>rect[1]){
layerRect[1] = rect[1];
}
if(layerRect[2]>rect[2]){
layerRect[2] = rect[2];
}
if(layerRect[3]<rect[3]){
layerRect[3] = rect[3];
}else{
intendedY = 0;
}
layerOffsetY = rect[3] - layerRect[3];
layerOffsetX = rect[0] - layerRect[0];
docW = layerRect[2]-layerRect[0];
docH = layerRect[1]-layerRect[3];
offset = {x:offsetX+layerOffsetX, y:offsetY+layerOffsetY};
var layerDepths = [];
var copyDoc = this.copyDocument(docRef, formatInfo.pixelDocSize, artboard, rect, docW, docH, offset, this.isAdditionalLayer, layerDepths);
var hasAdditLayers = copyDoc.layers.length>1; // there will be one empty layer in the new file (which can be ignored)
if(!hasAdditLayers && !isVis){
// skip layers where nothing is visible
continue;
}
}
if(isVis){
// only copy layer if it is visible (if not only visible '+' layers will be output)
var new_layer = this.copy_layer(layer, copyDoc.layers.add(), offset, copyDoc.width, copyDoc.height, this.innerPadding);
new_layer.visible = true;
var depth = layerDepths[this.export_layers[j]];
while(new_layer.zOrderPosition<depth){
new_layer.zOrder(ZOrderMethod.BRINGFORWARD);
}
while(new_layer.zOrderPosition>depth){
new_layer.zOrder(ZOrderMethod.SENDBACKWARD);
}
}
formatInfo.saveFile(copyDoc, base_filename, options, i, artboardName);
if(new_layer && !this.trimEdges){
new_layer.remove();
new_layer = null;
}
}else{
layer.visible = true;
formatInfo.saveFile(docRef, base_filename, options, i, artboardName);
layer.visible = false;
}
}
}catch(e){
this.failed_artboards.push(i);
this.failed_layers.push(j);
if(new_layer && !this.trimEdges){
new_layer.remove();
new_layer = null;
}
}
this.updateProgress(++num_exported);
}
if(copyDoc){
copyDoc.close(SaveOptions.DONOTSAVECHANGES);
copyDoc = null;
}
}
}
if(!copyBehaviour){
this.show_layers(were_shown);
}
if((!this.failed_layers.length && !this.failed_artboards.length) || !this.redoFailed(this.failed_layers, this.failed_artboards)){
this.dlg.close();
}
},
redoFailed: function(failed_layers, failed_artboards) {
var newLayers = [];
for(var i=0; i<failed_layers.length; ++i){
var index = this.export_layers[failed_layers[i]];
if(this.indexOf(newLayers, index)==-1)newLayers.push(index);
}
var newArtboards = [];
for(var i=0; i<failed_artboards.length; ++i){
var index = this.export_artboards[failed_artboards[i]];
if(this.indexOf(newArtboards, index)==-1)newArtboards.push(index);
}
if(newLayers.length){
var layerNames = "";
for(var i=0; i<newLayers.length; ++i){
var index = newLayers[i];
layerNames += "\n - "+docRef.layers[index].name;
}
var msg = newLayers.length+" layers failed across "+newArtboards.length+" artboards:"+layerNames+"\n Retry?";
}else{
var artboardNames = "";
for(var i=0; i<newArtboards.length; ++i){
var index = newArtboards[i];
artboardNames += "\n - "+docRef.artboards[index].name;
}
var msg = newArtboards.length+" artboards failed:"+artboardNames+"\nRetry?";
}
if(confirm(msg)){
this.export_artboards = newArtboards;
this.export_layers = newLayers;
this.updateExportCounts();
this.run_export();
return true;
}
return false;
},
traceRect: function(rect) {
if(!rect)return "no rect";
return "l: "+Math.round(rect[0])+" t: "+Math.round(rect[1])+" r: "+Math.round(rect[2])+" b: "+Math.round(rect[3])+" w: "+(rect[2]-rect[0])+" h: "+(rect[1]-rect[3]);
},
isAdditionalLayer: function(layer) {
return ( layer.name.match( /^\+/ ) && layer.visible);
},
isIncludeLayer: function(layer) {
return ( !layer.name.match( /^\+/ ) && layer.name!=this.PREFS_LAYER_NAME && !layer.name.match( /^\-/) )
},
copyDocument: function(docRef, pixelDocSize, artboard, rect, w, h, offset, layerCheck, layerDepths) {
var docW;
var docH;
if(pixelDocSize){
w = Math.round(w *1000) / 1000;
h = Math.round(h *1000) / 1000;
w = Math.ceil(w);
h = Math.ceil(h);
}
var preset = new DocumentPreset();
preset.width = w;
preset.height = h;
preset.colorMode = docRef.documentColorSpace;
preset.units = docRef.rulerUnits;
var copyDoc = app.documents.addDocument(docRef.documentColorSpace, preset);
copyDoc.pageOrigin = docRef.pageOrigin;
copyDoc.rulerOrigin = docRef.rulerOrigin;
var count = 1; // indices are 1 based!
var n = docRef.layers.length;
for ( var j=docRef.layers.length-1; j >=0; j-- ) {
layer = docRef.layers[j];
if (layerCheck(layer)) {
var layerBounds = this.get_layer_bounds(layer);
if(layerBounds && this.intersects(rect, layerBounds)){
this.copy_layer(layer, copyDoc.layers.add(), offset, w, h, false);
++count;
}
}else if(layerDepths){
layerDepths[j] = count;
}
}
return copyDoc;
},
updateProgress: function(num_exported) {
this.progLabel.text = 'Exported ' + num_exported + ' of ' + this.num_to_export;
this.dlg.progBar.value = num_exported / this.num_to_export * 100;
this.dlg.update();
},
copy_layer: function(from_layer, to_layer, offset, docW, docH, doInnerPadding) {
to_layer.artworkKnockout = from_layer.artworkKnockout;
to_layer.blendingMode = from_layer.blendingMode;
to_layer.color = from_layer.color;
to_layer.dimPlacedImages = from_layer.dimPlacedImages;
to_layer.isIsolated = from_layer.isIsolated;
to_layer.name = from_layer.name;
to_layer.opacity = from_layer.opacity;
to_layer.preview = from_layer.preview;
to_layer.printable = from_layer.printable;
to_layer.sliced = from_layer.sliced;
to_layer.typename = from_layer.typename;
if(!offset.norm){
var oldBounds = this.get_layer_bounds(from_layer);
//for mystery reasons, this only works if done before copying items across
}
var items = from_layer.pageItems;
try{
this.copy_items(items, to_layer);
}catch(e){
alert("copy items failed");
}
// copy backwards for correct z-ordering
for(var i=from_layer.layers.length-1; i>=0; --i){
var child = from_layer.layers[i];
if(child.visible)this.copy_layer(child, to_layer.layers.add(), offset, docW, docH, false)
}
if(!offset.norm){
var newBounds = this.get_layer_bounds(to_layer);
if(this.rect_equal(oldBounds, newBounds)){
//$.sleep(5000); // sleeping doesn't help!!
if(!this.ignoreWarnings)alert("Illustrator visibleBounds issue workaround.\nTry removing groups on layer '"+from_layer.name+"' to avoid this in future.\nPlease press OK");
newBounds = this.get_layer_bounds(to_layer);
// sometimes it takes a moment for bounds to be updated
}
if(oldBounds && newBounds){
offset.x += oldBounds[0]-newBounds[0];
offset.y += oldBounds[3]-newBounds[3];
offset.norm = true;
}
}
if(to_layer.parent.artboards!=null){ // only if top level layer
try{
this.shift_layer(to_layer, offset.x, offset.y);
}catch(e){
alert("shift layer failed");
}
}
if(doInnerPadding)this.innerPadLayer(to_layer, docW, docH);
return to_layer;
},
innerPadLayer: function(layer, docW, docH){
docW = Math.round(docW * 100) / 100;
docH = Math.round(docH * 100) / 100;
for(var i=0; i<layer.pageItems.length; ++i){
var item = layer.pageItems[i];
var bounds = item.visibleBounds;
// round to two decimal points
var l = Math.round(bounds[0] * 100) / 100;
var b = Math.round(bounds[1] * 100) / 100;
var r = Math.round(bounds[2] * 100) / 100;
var t = Math.round(bounds[3] * 100) / 100;
var w = (r - l);
var h = (b - t);
if(w>docW-1 && h>docH-1){
var scaleX = (w-1) / w * 100; // resize takes percentage values
var scaleY = (h-1) / h * 100;
item.resize(scaleX, scaleY, null, null, null, null, null, Transformation.CENTER);
}
}
for(var i=0; i<layer.layers.length; ++i){
innerPadLayer(layer.layers[i], docW, docH);
}
},
rect_equal: function(rect1, rect2) {
return rect1[0]==rect2[0] && rect1[1]==rect2[1] && rect1[2]==rect2[2] && rect1[3]==rect2[3] ;
},
copy_items: function(from_list, to_layer) {
var visWas = to_layer.visible;
to_layer.visible = true;
for(var i=0; i<from_list.length; ++i){
var item = from_list[i].duplicate(to_layer, ElementPlacement.PLACEATEND);
/*if(shiftX!=0 || shiftY!=0){
item.translate(shiftX, shiftY)
}*/
}
to_layer.visible = visWas;
},
shift_layer: function(layer, shiftX, shiftY) {
this.shift_items(layer.pageItems, shiftX, shiftY);
// copy backwards for correct z-ordering
for(var i=layer.layers.length-1; i>=0; --i){
this.shift_layer(layer.layers[i], shiftX, shiftY)
}
},
shift_items: function(items, shiftX, shiftY) {
if(shiftX==undefined)shiftX = 0;
if(shiftY==undefined)shiftY = 0;
for(var i=0; i<items.length; ++i){
items[i].translate(shiftX, shiftY)
}
},
hide_all_layers: function() {
var n = docRef.layers.length;
for(var i=0; i<n; ++i) {
layer = docRef.layers[i];
lyr_name = layer.name;
// any layers that start with + are always turned on
if (this.isAdditionalLayer(layer)) {
layer.visible = true;
} else {
layer.visible = false;
}
}
},
get_shown_layers: function() {
var shown = []
var n = docRef.layers.length;
for(var i=0; i<n; ++i) {
layer = docRef.layers[i];
if(layer.visible){
shown.push(i);
}
}
return shown;
},
get_layer_bounds: function(layer) {
var rect;
var items = layer.pageItems;
for(var i=0; i<items.length; ++i){
var item = items[i];
if(item.guides){
continue;
}
var visBounds = item.visibleBounds;
if(visBounds==null)continue;
if(rect==null){
rect = visBounds;
}else{
if(rect[0]>visBounds[0]){
rect[0] = visBounds[0];
}
if(rect[1]<visBounds[1]){
rect[1] = visBounds[1];
}
if(rect[2]<visBounds[2]){
rect[2] = visBounds[2];
}
if(rect[3]>visBounds[3]){
rect[3] = visBounds[3];
}
}
}
for(var i=0; i<layer.layers.length; ++i){
var childRect = this.get_layer_bounds(layer.layers[i]);
if(childRect==null)continue;
if(rect==null){
rect = childRect;
}else{
if(rect[0]>childRect[0]){
rect[0] = childRect[0];
}
if(rect[1]<childRect[1]){
rect[1] = childRect[1];
}
if(rect[2]<childRect[2]){
rect[2] = childRect[2];
}
if(rect[3]>childRect[3]){
rect[3] = childRect[3];
}
}
}
return rect;
},
intersects: function(rect1, rect2) {
return !( rect2[0] > rect1[2] ||
rect2[1] < rect1[3] ||
rect2[2] < rect1[0] ||
rect2[3] > rect1[1]);
},
show_layers: function(layerIndices) {
var n = layerIndices.length;
for(var i=0; i<n; ++i) {
layer = docRef.layers[layerIndices[i]];
layer.visible = true;
}
}
};
multi_exporter.init();
@shellscape
Copy link

@TomByrne thanks for this script. it would be grand to have an option for sizing the artboard of an SVG export to the bounds of the artwork (in my case I'm exporting layers from a single artboard)

@saltimbranco
Copy link

Is it possible to change the default jpg dpi and compression settings?

@MarkBuskbjerg
Copy link

Yehaa.. You (and your script) just saved me from hours of work. Thanx a bunch.

@Paul-Akuma
Copy link

@TomByrne Thanks for the great script i am having a little issue with the eps settings i thought everything was amazing worked first time but I have noticed the document raster effect resolution is exporting as screen even thou the main file is set to high. Is there any way I can edit this in the script?
Thanks

@racsfiok
Copy link

racsfiok commented Aug 28, 2017

@TomByrne Amazing job! Is there any way to add to PDF exporter: trim marks and bleeding (use document bleed settings or always 3mm bleed)? It would be a lifesaver.

@alemolinata
Copy link

@TomByrne
This is super helpful! Thank you! :)
I'm trying to edit the script so it will also export sublayers, but I have no experience with scripting for illustrator. Has anyone tried to do this?

@gimmemoreice
Copy link

THANK YOU!!

@elrayyes
Copy link

@TomByrne Thank you for this script. I've been looking for something like this.
It is awesome that it still works after all those years (currently using on CC 2017).
I have one note though, regarding exporting layers to EPS files. When it pastes the layer into a new document, it basically creates a new layer with the same name; then it saves the file. Not sure if this was overlooked, but when creating a new document, it includes a layer with the name "Layer 1". This layer is not removed before saving the document.
Is this something you can work out in your script?
Thanks again.

@dpd2
Copy link

dpd2 commented Sep 4, 2018

@TomByrne Fantastic script. Saves me a lot of time.
Is it possible to add GIF as an export type?

@badabingbreda
Copy link

Just want to tell you this script is super awesome. Just what I needed at the most difficult time, an absolute lifesaver. Exporting CS5 layers to SVG in 3 clicks...

Thanks you!

@gcoghill
Copy link

gcoghill commented Oct 24, 2018

@TomByrne
This is super helpful! Thank you! :)
I'm trying to edit the script so it will also export sublayers, but I have no experience with scripting for illustrator. Has anyone tried to do this?

I'd like to do this as well.

Also — thank you! This is mind-bogglingly helpful.

@kiibu6000
Copy link

Can this be made to export 3 layers to one pdf for example?

@sakamde
Copy link

sakamde commented Sep 19, 2019

Hi, really good script !
Could you please send me the script for the export png with resolution high (300) PPI?
I was trying but the output is not giving me the 300ppi is giving me 96ppi.
2019-09-19 17_00_46-p50604-14 copy png_ @ 200% (RGB_Preview)

i used this script for saving into png.

{
var exportOptions = new ExportOptionsPNG24;
exportOptions.resolution = 300;
exportOptions.antiAliasing = true;
exportOptions.artBoardClipping = false;
exportOptions.transparency = false;
exportOptions.verticalScale = xxxx;
exportOptions.horizontalScale = xxxx;

    var dwgNameHotSpot = new File ( hotSpotPath + "/png_Webshop/" + openDocFileName2 + ".png");
    doc.exportFile(dwgNameHotSpot, ExportType.PNG24, exportOptions);
} 

But it will not give me the 300ppi image :(
It will really helpful for me...

Thank you in advance.....

@TomByrne
Copy link
Author

TomByrne commented Sep 25, 2019 via email

@vijaypwr61
Copy link

This one helped me to export a JPEG file with 500% scaling.

@mmnowak86
Copy link

mmnowak86 commented Mar 17, 2020

Hi Thomas,

I've just purchased your Smart Layer Export script via Adobe Exchange. Unfortunately, I don't know how to install it. Exchange is so buggy these days... It seems that Creative Cloud should install this add-on automatically, but it's not happening. I've tried to download it and install it manually via Anastasiy’s Extension Manager but there was an error - "Extension's license is damaged (error code: 603)". I've read that it's because the file was corrupted, so I've tried to download it again, but I couldn't - download button on Adobe Exchange website was not working anymore and the alert "Unauthorized access" showed up (please see attached).

Long story short... I can't install it :/ Could you please advise any solution?

Thank you,

Mateusz

Screenshot 2020-03-17 at 18 24 29

@levina-de-sa
Copy link

Hi. Thanks for the script. It works super fine. Well done.

@tom-ruthenberg
Copy link

Hi. Thank you for the script. It has saved me hours. Is it possible to add an option to convert text to splines in the svg export.

@grayassociates
Copy link

grayassociates commented Oct 11, 2021

Hi there. I have recently used this script and it works perfectly and is what we are after, but I just have a small question, is there a way of adding in the original document name as the prefix to the exported files?

A client is wanting to have production colour illustrations as single svg files for there website, so we have the original file that is named after the product code e.g AA1234, which will have several colours. When running the script the exported files will be named same as the layers, which are the individual colours (blue.svg) and we would like to have the document name adding to the beginning so it will be AA1234_blue.svg.

There are thousands of files so the more automated the better.

@TomByrne
Copy link
Author

@grayassociates

It doesn't look like this is supported (I haven't looked at this script for a loooong time).

My Smart Export project, which grew out of this, does support this kind of thing though. It's freely available on github, but also isn't supported anymore.

@grayassociates
Copy link

@TomByrne

Thanks for your reply and help much appreciated, I will take a look at your Smart Export project.

@SekaiMiller
Copy link

SekaiMiller commented Nov 20, 2021

@TomByrne
Love the script!!! I just have a little question. Is there a way I can save the images in a higher resolution/quality?
It will be awesome if you could help me with this! Thanks!

@TomByrne
Copy link
Author

@SekaiMiller
I don't really support this any more, but it looks like the scale factor is supported by jpg, png and png24.
If you need more than this, the Smart Export plugin exposes all options in the API.

@SekaiMiller
Copy link

@TomByrne Thank you!!! I will take a look at it!

@Anotherbeardedguy
Copy link

@TomByrne Thanks for the script. is it possible to export each layer with the artboards boundaries?

@KittyDreadful
Copy link

@TomByrne Any chance you're back to supporting this?

@TomByrne
Copy link
Author

TomByrne commented May 5, 2022

@KittyDreadful - I'm afraid not, I doubt very much that I ever will.

@jamshedakber
Copy link

Hi Tom,

I am facing an issue with script when i am using it, there is no option for svg export in the window, please guide on it.

Thanks

@skilltimate
Copy link

@TomByrne This is not working in 2023 version, any help is appreciated.

@ingridada
Copy link

Hi Tom,
Thanks this script is a huge helper for me. Thanks!

@Swapot
Copy link

Swapot commented Nov 29, 2023

@TomByrne Does anyone have this or Tom's IllustratorSmartExport extension working on Illustrator CS6? I copy/pasted the code into (Mac) Script Editor, saved as MultiExporter.jsx and dropped into Scripts folder on Mac. Illustrator recognizes it, but returnes following error when I try to run it:
"This script contains uncompiled changes and cannot be run."

I also tried downloading and installing the latest version of Tom's Illustrator Smart Export. ZXPInstaller only works with CC, so I tried Extension Manager. An error says it does not contain valid signature.

Any workarounds? This would save me MANY hours on a current project in which I need to make .eps, .png, .jpg, and .svg from multiple layers and multiple files.

Any advice welcome!

@sbgraphic
Copy link

Hi, I am not sure about "smart layer export", but I've used this script "MultiExporter.jsx" a few years ago and it was really useful. I've just tried it again and yes, it still work on CS6/High Sierra for me.
Open you .ai file , then go to Illustrator Files menu > Scripts… > Other scripts… then choose the "MultiExporter.jsx" file from your finder location. A script popup menu should appear with serveral options…

ex-1
ex-2

@Swapot
Copy link

Swapot commented Dec 9, 2023

Thanks sbgraphic - I did get it working! I think I was corrupting the script by trying to copy/paste it into AppleScript app to make the javascript file. First time I've ever tried a script from GitHub. Didn't realize I could just right-click and "save-as" directly from GitHub.

Very grateful for this script - many thanks to Matthew Ericson and Tom Byrne. Glad the New York Times needed this and Matthew was willing to share.

I'm not sure what added options Tom built into the IllustratorSmartExport extension (which I assume was built upon this javascript), but I haven't been able to get it installed.

I've, tried the Adobe Extension Manager that came with CS6 Design Suite, but I gets part way through installing (just past accepting the extensions license agreement) and then it returns the following error:

Screen Shot 2023-12-09 at 7 57 16 AM

I've tried other zxp installers, but they all seem made for CC only.

I've tried installing it manually, but I don't know where to put it. I found other instruction online for manual installation of a extension for CS6, but they seem to be only for Photoshop. More than one says that the unzipped extension needs to be placed here:

Mac: /Users/username/Library/Application Support/Adobe/CEP/extensions

However, no such folder exists on my Mac. I created a folder named "CEP", but that didn't work either.

Another site said I would need to run a command ln in Terminal afterward manual installation. I didn't try that because I don't want to do something I don't know how to undo, and I really need Illustrator to be working for a project I'm currently on.

Does anyone who has this Extension working on Illustrator CS6? If so, do you know what folder it resides in? Any other advice on trying to manually install a .zxp file for CS apps welcome. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment