/* * image-export.jsx * * * Export images from an InDesign document to web-friendly formats. * * * Note: this script works with InDesign version 5.0 and above. Previous * versions may compatible, too but not tested yet. * * * Authors: Gregor Fellenz (twitter: @grefel), Martin Kraetke (@mkraetke) * * * LICENSE * * Copyright (c) 2015, Gregor Fellenz and le-tex publishing services GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ /* * set image export preferences */ lang = { pre: 0 // en = 0, de = 1 } image = { minExportDPI:1, maxExportDPI:2400, exportDPI:144, maxResolution:4000000, exportDir:"export", exportQuality:2, pageItemLabel:"letex:fileName" } /* * set panel preferences */ panel = { title:["le-tex – Export Images", "le-tex – Bilder exportieren"][lang.pre], densityTitle:["Density (ppi)", "Auflösung (ppi)"][lang.pre], qualityTitle:["Quality", "Qualität"][lang.pre], qualityValues:[["max", "high", "medium", "low"], ["Maximum", "Hoch", "Mittel", "Niedrig"]][lang.pre], optionsTitle:["Options", "Optionen"][lang.pre], maxResolutionTitle:["Max Resolution (px)", "Maximale Auflösung (px)"][lang.pre], selectDirButtonTitle:["Choose", "Auswählen"][lang.pre], selectDirMenuTitle:["Choose a directory", "Verzeichnis auswählen"][lang.pre], progressBarTitle:["export Images", "Bilder exportieren"][lang.pre], noValidLinks:["No valid links found.", "Keine Bild-Verknüpfungen gefunden"][lang.pre], finishedMessage:["images exported.", "Bilder exportiert."][lang.pre], buttonOK:"OK", buttonCancel:["Cancel", "Abbrechen"][lang.pre], errorMissingImage:["Warning! Image cannot be found: ", "Warnung! Bild konnte nicht gefunden werden: "][lang.pre], errorEmbeddedImage:["Warning! Embedded Image cannot be exported: ", "Warnung! Eingebettetes Bild kann nicht exportiert werden: "][lang.pre], promptMissingImages:["Some images cannot be exported. Proceed?","Einige Bilder können nicht exportiert werden. Fortfahren?"][lang.pre] } /* * start */ main(); function main(){ // open file dialog and load a document if (app.layoutWindows.length == 0) { var file = File.openDialog ("Select a file", "InDesign:*.indd;*.indb;*.idml, InDesign Document:*.indd, InDesign Book:*.indb, InDesign Markup:*.idml", true) try { app.open(File(file)); } catch (e) { alert(e); return; }; } var doc = app.documents[0]; // check if document is saved if ((!doc.saved || doc.modified)) { if ( confirm ("The document needs to be saved.", undefined, "Document not saved.")) { try { var userLevel = app.scriptPreferences.userInteractionLevel; app.scriptPreferences.userInteractionLevel = UserInteractionLevels.INTERACT_WITH_ALL; doc = doc.save(); app.scriptPreferences.userInteractionLevel = userLevel; } catch (e) { alert ("The document couldn't be saved.\n" + e); return; } } else { return; } } // show window try { jsExtensions(); exportImages(doc); } catch (e) { alert ("Error:\n" + e); } } function jsExtensions(){ // indexOf if (!Array.prototype.indexOf) { Array.prototype.indexOf = function(elt /*, from*/) { var len = this.length; var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) from += len; for (; from < len; from++) { if (from in this && this[from] === elt) return from; } return -1; }; } } function exportImages(doc){ if(drawWindow() == 1){ // overwrite internal Measurement Units to pixels app.scriptPreferences.measurementUnit = MeasurementUnits.PIXELS; // grab file links and export getFilelinks(doc); } else { return; } } function drawWindow(){ var myWindow = new Window("dialog", panel.title, undefined, {resizable:true}); with (myWindow) { myWindow.orientation = "column"; myWindow.alignChildren ="fill"; myWindow.formPath = add("panel", undefined, panel.selectDirMenuTitle); with(myWindow.formPath){ myWindow.formPath.alignChildren = "left"; myWindow.formPath.inputPath = add("edittext"); myWindow.formPath.inputPath.preferredSize.width = 255; myWindow.formPath.inputPath.text = getDefaultExportPath(); myWindow.formPath.buttonChoosePath = add("button", undefined, panel.selectDirButtonTitle); } myWindow.qualityGroup = add("panel", undefined, panel.qualityTitle); with(myWindow.qualityGroup){ qualityGroup.orientation = "column"; qualityGroup.alignChildren = "left"; qualityGroup.formQuality = add("group"); with(myWindow.qualityGroup.formQuality){ formQuality.orientation = "row"; for (i = 0; i < panel.qualityValues.length; i++) { formQuality.add ("radiobutton", undefined, (panel.qualityValues[i])); } formQuality.children[image.exportQuality].value =true; } myWindow.qualityGroup.formDensity = add( "group"); with(myWindow.qualityGroup.formDensity){ formDensity.inputDPI = add ("edittext", undefined, image.exportDPI); formDensity.inputDPI.characters = 4; formDensity.sliderDPI = add("slider", undefined, image.exportDPI, image.minExportDPI, image.maxExportDPI); formDensity.add ("statictext", undefined, panel.densityTitle); // synchronize slider and input field formDensity.sliderDPI.onChanging = function () {myWindow.qualityGroup.formDensity.inputDPI.text = myWindow.qualityGroup.formDensity.sliderDPI.value}; formDensity.inputDPI.onChanging = function () {myWindow.qualityGroup.formDensity.sliderDPI.value = myWindow.qualityGroup.formDensity.inputDPI.text}; } } myWindow.optionsGroup = add("panel", undefined, panel.optionsTitle); with(myWindow.optionsGroup){ myWindow.optionsGroup.maxResolutionGroup = add("group"); with(myWindow.optionsGroup.maxResolutionGroup){ maxResolutionGroup.inputMaxRes = maxResolutionGroup.add("edittext", undefined, image.maxResolution); maxResolutionGroup.add("statictext", undefined, panel.maxResolutionTitle); } } myWindow.buttonGroup = add( "group"); with(myWindow.buttonGroup){ myWindow.buttonGroup.orientation = "row"; myWindow.buttonGroup.buttonOK = add ("button", undefined, panel.buttonOK, {name: "ok"}); myWindow.buttonGroup.buttonCancel = add ("button", undefined, panel.buttonCancel, {name: "cancel"} ); } } myWindow.formPath.buttonChoosePath.onClick = function () { var result = Folder.selectDialog (); if (result) { myWindow.formPath.inputPath.text = result; } } myWindow.buttonGroup.buttonOK.onClick = function(){ //overwrite values with form input image.exportDir = Folder(myWindow.formPath.inputPath.text); image.exportDPI = Number(myWindow.qualityGroup.formDensity.inputDPI.text); image.exportQuality = selectedRadiobutton(myWindow.qualityGroup.formQuality); image.maxResolution = Number(myWindow.optionsGroup.maxResolutionGroup.inputMaxRes.text); myWindow.close(1); function selectedRadiobutton (rbuttons){ for(var i = 0; i < rbuttons.children.length; i++) { if(rbuttons.children[i].value == true) { return i; } } } } myWindow.buttonGroup.buttonCancel.onClick = function() { myWindow.close(); } return myWindow.show(); } function getFilelinks(doc){ var docLinks = doc.links; var uniqueBasenames = []; var exportLinks = [] /* * rename files if a basename exists twice */ for (var i = 0; i < docLinks.length; i++) { var link = docLinks[i]; if(isValidLink(link)){ var originalBounds = link.parent.parent.geometricBounds; var rectangle = link.parent.parent; // disable lock since this prevents images to be exported // note that just the group itself has a lock state, not their childs if(rectangle.parent.constructor.name == 'Group'){ if(rectangle.parent.locked != false){ rectangle.parent.locked = false; } }else{ if(rectangle.locked != false){ rectangle.locked = false; } } var normalizedDensity = getMaxDensity(image.exportDPI, rectangle, image.maxResolution); var basename = getBasename(link.name); // use existing filename label if(rectangle.extractLabel(image.pageItemLabel).length > 0){ var newFilename = rectangle.extractLabel(image.pageItemLabel); // generate new filename if basename exists twice }else if(inArray(basename, uniqueBasenames)) { var newFilename = renameFile(basename, "jpg", true); } else { uniqueBasenames.push(basename); var newFilename = renameFile(basename, "jpg", false); } /* * construct link object */ linkObject = { link:link, pageItem:rectangle, quality:image.exportQuality, density:normalizedDensity, newFilename:newFilename, newFilepath:File(image.exportDir + "/" + newFilename), originalBounds:originalBounds } /* * check for custom object export options */ exportLinks.push(linkObject); } else { var missingLinks = true; } } if (missingLinks) { var result = confirm ("Missing Image Links found. Proceed?"); if (!result) return; } // create directory createDir(image.exportDir); if (exportLinks.length > 0) { //var progressBar = getProgressBar(exportLinks.length); var progressBar = getProgressBar("export " + exportLinks.length + " images"); progressBar.reset(exportLinks.length); // iterate over files and store to specific location for (i = 0; i < exportLinks.length; i++) { var exportFormat = ExportFormat.JPG; var exportResolution = exportLinks[i].density; var exportQuality = exportLinks[i].quality; // JPEG export options app.jpegExportPreferences.antiAlias = false; app.jpegExportPreferences.embedColorProfile = false; app.jpegExportPreferences.exportResolution = exportResolution; app.jpegExportPreferences.jpegColorSpace = JpegColorSpaceEnum.RGB; jpegExportQualityValues = [JPEGOptionsQuality.MAXIMUM ,JPEGOptionsQuality.HIGH, JPEGOptionsQuality.MEDIUM, JPEGOptionsQuality.LOW]; app.jpegExportPreferences.jpegQuality = jpegExportQualityValues[exportQuality]; app.jpegExportPreferences.jpegRenderingStyle = JPEGOptionsFormat.BASELINE_ENCODING; app.jpegExportPreferences.simulateOverprint = true; app.jpegExportPreferences.useDocumentBleeds = true; progressBar.hit("export " + exportLinks[i].newFilename, i); exportLinks[i].pageItem.exportFile(exportFormat, exportLinks[i].newFilepath); // insert label with new file link for postprocessing exportLinks[i].pageItem.insertLabel(image.pageItemLabel, exportLinks[i].newFilename); // restore original bounds exportLinks[i].pageItem.geometricBounds = exportLinks[i].originalBounds; } progressBar.close(); alert (exportLinks.length + " " + panel.finishedMessage); doc.save(); } else { alert (panel.noValidLinks); } } // draw simple progress bar function getProgressBar (title){ var progressBarWindow = new Window("palette", title, {x:0, y:0, width:400, height:50}); var pbar = progressBarWindow.add("progressbar", {x:10, y:10, width:380, height:6}, 0, 100); var stext = progressBarWindow.add("statictext", {x:10, y:26, width:380, height:20}, ''); progressBarWindow.center(); progressBarWindow.reset = function (maxValue) { stext.text = "export export export export export export export export "; pbar.value = 0; pbar.maxvalue = maxValue||0; pbar.visible = !!maxValue; this.show(); } progressBarWindow.hit = function(msg, value) { ++pbar.value; stext.text = msg; } return progressBarWindow; } // create directory function createDir (folder) { try { folder.create(); return; } catch (e) { alert (e); } } // check if image is missing or embedded function isValidLink (link) { if(link.parent.parent.parentPage == null) { return false; } else { switch (link.status) { case LinkStatus.LINK_MISSING: alert(panel.errorMissingImage + link.name); return false; break; case LinkStatus.LINK_EMBEDDED: alert(panel.errorEmbeddedImage + link.name); return false; break; default: if(link != null) return true else return false; } } } // return filename with new extension and conditionally attach random string function renameFile(basename, extension, rename) { if(rename) { hash = ((1 + Math.random())*0x1000).toString(36).slice(1, 6); var renameFile = basename + '-' + hash + '.' + extension.toLowerCase(); return renameFile } else { var renameFile = basename + '.' + extension.toLowerCase(); return renameFile } } // get file basename function getBasename(filename) { var basename = filename.match( /^(.*?)\.[a-z]{2,4}$/i); if(basename != null){ return basename[1]; }else{ // no file extension return filename; } } // check if string exists in array function inArray(string, array) { var length = array.length; for(var i = 0; i < length; i++) { if(array[i] == string) return true; } return false; } // get density limit according to maximal resolution value function getMaxDensity(density, rectangle, maxResolution) { var bounds = rectangle.geometricBounds; var baseMultiplier = 72; var densityFactor = density / baseMultiplier; var width = (bounds[3] - bounds[1]); var height = (bounds[2] - bounds[0]); var resolution = width * height * Math.pow(densityFactor, 2); if(resolution > maxResolution) { var maxDensity = Math.floor(Math.sqrt(maxResolution * Math.pow(densityFactor, 2) / resolution) * baseMultiplier); return maxDensity; } else { return density; } } // get path relative to indesign file location function getDefaultExportPath() { var exportPath = String(app.activeDocument.fullName); exportPath = exportPath.substring(0, exportPath.lastIndexOf('/')) + '/' + image.exportDir; return exportPath }