// philg-pscs2-processor.jsx.txt.jsx // from http://photo.net/learn/photoshop/ // tested with Adobe Photoshop versions CS2 through CS5 // updated July 5, 2010 based on a script from April 2, 2009 // by philg@mit.edu based on a script from a kind soul at Adobe // this script requires the installation of fonts named "9x15" and "6x13"; these are fixed-pitch ASCII terminal-style // fonts from the X Windows system. You can use other fonts but I was never able to get the letters to be legible // maybe because Photoshop is always trying to do fancy anti-aliasing. These fonts are available for Windows from // http://philip.greenspun.com/photography/photoshop-scripts/ // April 2, 2009: fixed failure to run with CS4/CS5 // prior latest addition is that if it doesn't start with a .JPG, this writes // a JPEG file that is just as large as the camera original (good for huge // monitors) // February 17, 2006: added "info" to the list of extensions to skip, // thus working around a problem created by using Canon's Zoom Browser, // which leaves behind a ZbThumbnail.info file in each folder // this sets the debug level, 0 means debugger off, 1 is on $.level = 1; // set to false if you don't want them var debug_alerts = false; if (debug_alerts) { alert("will run with debug alerts"); } var gVersion = 4; // a global variable for the title of the dialog // this string will also be used for the preferences file I write to disk // Photoshop Install Directory/Presets/JPEGenator/RawToJPEGs.xml for example var gScriptName = "RawToJPEGs"; // A list of file extensions I will skip, keep them lower case var gFilesToSkip = Array( "db", "xmp", "thm", "htm", "txt", "doc", "md0", "tb0", "dat", "info" ); // A list of camera raw extensions, keep them lower case var gFilesForCameraRaw = Array( "tif", "crw", "nef", "raf", "orf", "mrw", "dcr", "mos", "srf", "pef", "dcr" ); // limit the length of text in edit boxes by this length var gShortFileNameLengthWin = 30; var gShortFileNameLengthMac = 22; var gShortFileNameLength = gShortFileNameLengthMac; // remember some things var gStartRulerUnits = preferences.rulerUnits; var gStartTypeUnits = preferences.typeUnits; var gStartDisplayDialogs = displayDialogs; // Set Photoshop to use pixels and display no dialogs preferences.rulerUnits = Units.PIXELS; preferences.typeUnits = TypeUnits.PIXELS; displayDialogs = DialogModes.NO; // global parameters var gParams = null; // the main routine // the ImageProcessor class does most of the work try { CheckVersion(); if ( IsWindowsOS() ) { gShortFileNameLength = gShortFileNameLengthWin; } var gIP = new ImageProcessor(); // load my preferences from disk LoadParamsFromDisk( GetDefaultParamsFile(), gParams ); // run without the dialog if ( gParams["dialogsoff"] ) { // don't bother with the dialog or saving the params gIP.Execute(); } else { // run with the dialog if ( gIP.RunDialog() ) { // execute accordingly gIP.Execute(); // write my params back to disk for the next run SaveParamsToDisk( GetDefaultParamsFile(), gParams ); } } } // Lot's of things can go wrong, Give a generic alert and see if they want the details catch(e) { if ( confirm("Sorry, something major happened and I can't continue! Would you like to see more info?" ) ) { alert(e); } } // restore the dialog modes preferences.rulerUnits = gStartRulerUnits; preferences.typeUnits = gStartTypeUnits; displayDialogs = gStartDisplayDialogs; ////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////// // given a file name and a list of extensions // determine if this file is in the list of extensions function IsFileOneOfThese( inFileName, inArrayOfFileExtensions ) { var lastDot = inFileName.toString().lastIndexOf( "." ); if ( lastDot == -1 ) { return false; } var strLength = inFileName.toString().length; var extension = inFileName.toString().substr( lastDot + 1, strLength - lastDot ); extension = extension.toLowerCase(); for (var i = 0; i < inArrayOfFileExtensions.length; i++ ) { if ( extension == inArrayOfFileExtensions[i] ) { return true; } } return false; } // jpeg1 is the 50% size, jpeg2 is the 25% size, jpeg3 is thumbnail // added: function InitParams() { // main params var params = new Array(); params["version"] = gVersion; params["source"] = ""; params["jpeg1"] = true; params["jpeg2"] = true; params["jpeg3"] = true; params["q1"] = 8; params["q2"] = 6; params["q3"] = 5; params["s"] = 225; params["visibleCopyrightText"] = ""; params["hiddenCopyrightText"] = ""; params["photographerName"] = ""; return params; } // the main class function ImageProcessor() { gParams = InitParams(); // define the main dialog resource // [left, top, right, bottom] var dialogResource = "dialog { text: 'Image Processor', bounds:[100, 100, 480, 480], " dialogResource += " sourceSt: StaticText { text:'Source:', bounds:[10, 10, 300, 30] }, " dialogResource += " sourceEt: EditText { text:'', bounds:[10, 30, 200, 50], properties: { multiline:false } }, " dialogResource += " sourceBtn: Button { text:'Browse...', bounds:[210, 30, 290, 50] }, " dialogResource += " runBtn: Button { text:'Run', bounds:[310, 20, 370, 40] }, " dialogResource += " cancelBtn: Button { text:'Cancel', bounds:[310, 50, 370, 70] }, " dialogResource += " fileTypePnl: Panel { text: 'File Type:', bounds:[10, 80, 340, 185], " dialogResource += " jpeg1Cb: Checkbox { text:'JPEG 50%', bounds:[10, 15, 120, 35], value:true }, " dialogResource += " quality1St: StaticText { text:'Quality:', bounds:[130, 17, 180, 35] }, " dialogResource += " quality1Et: EditText { text:'5', bounds:[180, 15, 210, 33] }, " dialogResource += " jpeg2Cb: Checkbox { text:'JPEG 25%', bounds:[10, 40, 120, 60], value:true }, " dialogResource += " quality2St: StaticText { text:'Quality:', bounds:[130, 42, 180, 60] }, " dialogResource += " quality2Et: EditText { text:'5', bounds:[180, 40, 210, 58] }, " dialogResource += " jpeg3Cb: Checkbox { text:'JPEG 225 px.', bounds:[10, 65, 120, 85], value:true }, " dialogResource += " quality3St: StaticText { text:'Quality:', bounds:[130, 67, 180, 85] }, " dialogResource += " quality3Et: EditText { text:'5', bounds:[180, 65, 210, 83] }, " dialogResource += " sizeSt: StaticText { text:'Size:', bounds:[220, 67, 250, 85] }, " dialogResource += " sizeEt: EditText { text:'225', bounds:[260, 65, 290, 83] }, " dialogResource += " }, " dialogResource += " copyrightInfoHiddenSt: StaticText { text:'Hidden Copyright Info:', bounds:[10, 195, 200, 210] }, " dialogResource += " copyrightInfoHiddenEt: EditText { text:'', bounds:[10, 215, 340, 235] }, " dialogResource += " copyrightInfoVisibleSt: StaticText { text:'Visible Copyright Info:', bounds:[10, 240, 200, 255] }, " dialogResource += " copyrightInfoVisibleEt: EditText { text:'', bounds:[10, 260, 340, 280] }, " dialogResource += " photographerSt: StaticText { text:'Photographer Name:', bounds:[10, 285, 200, 300] }, " dialogResource += " photographerEt: EditText { text:'', bounds:[10, 305, 340, 325] } " dialogResource += " }, " dialogResource += " } " dialogResource += "}"; // create the main dialog window, this holds all our data this.mainDialog = new Window( dialogResource ); // routine for running the dialog and it's interactions this.RunDialog = function () { this.mainDialog.text = gScriptName; this.mainDialog.cancelBtn.onClick = function() { this.parent.close( false ); } this.mainDialog.sourceBtn.onClick = function() { var selFolder = Folder.selectDialog( "Pick a source folder", this.parent.sourceLongText ); if ( selFolder != null ) { this.parent.sourceLongText = selFolder.fsName.toString() this.parent.sourceEt.text = CreateShortFileName( this.parent.sourceLongText ); } } this.mainDialog.fileTypePnl.jpeg1Cb.onClick = function() { this.parent.parent.fileTypePnl.quality1St.enabled = this.value; this.parent.parent.fileTypePnl.quality1Et.enabled = this.value; } this.mainDialog.fileTypePnl.jpeg2Cb.onClick = function() { this.parent.parent.fileTypePnl.quality2St.enabled = this.value; this.parent.parent.fileTypePnl.quality2Et.enabled = this.value; } this.mainDialog.fileTypePnl.jpeg3Cb.onClick = function() { this.parent.parent.fileTypePnl.quality3St.enabled = this.value; this.parent.parent.fileTypePnl.quality3Et.enabled = this.value; this.parent.parent.fileTypePnl.sizeEt.enabled = this.value; } this.mainDialog.runBtn.onClick = function () { // check if the settings are proper var testFolder = null; if ( this.parent.sourceEt.text != CreateShortFileName( this.parent.sourceLongText ) ) { this.parent.sourceLongText = this.parent.sourceEt.text; } if ( this.parent.sourceLongText.length > 0 && this.parent.sourceLongText[0] != '.' ) { testFolder = new Folder( this.parent.sourceLongText ); if ( !testFolder.exists ) { alert( "Please specify a source folder." ); return; } } else { alert( "Please specify a source folder." ); return; } if ( this.parent.fileTypePnl.jpeg1Cb.value == true ) { var q = Number( this.parent.fileTypePnl.quality1Et.text ); if ( q < 0 || q > 12 ) { alert( "JPEG Quality must be between 0 and 12." ); return; } } if ( this.parent.fileTypePnl.jpeg2Cb.value == true ) { var q = Number( this.parent.fileTypePnl.quality2Et.text ); if ( q < 0 || q > 12 ) { alert( "JPEG Quality must be between 0 and 12." ); return; } } if ( this.parent.fileTypePnl.jpeg3Cb.value == true ) { var q = Number( this.parent.fileTypePnl.quality3Et.text ); if ( q < 0 || q > 12 ) { alert( "JPEG Quality must be between 0 and 12." ); return; } } // make sure they have at least one file format specified for output var outputCount = 0; if ( this.parent.fileTypePnl.jpeg1Cb.value == true ) { outputCount++; } if ( this.parent.fileTypePnl.jpeg2Cb.value == true ) { outputCount++; } if ( this.parent.fileTypePnl.jpeg3Cb.value == true ) { outputCount++; } if ( outputCount == 0 ) { alert( "You must save to at least one file type." ); return; } // transfer the params out of the dialog GetParamsFromDialog( this.parent, gParams ) // success this.parent.close( true ); } InitDialog( this.mainDialog, gParams ); ForceDialogUpdate( this.mainDialog ); // this.mainDialog.center(); return this.mainDialog.show(); } // if I get here then the dialog params are ok and it is time // to do what they want this.Execute = function() { var cameraRawParams = new Object(); cameraRawParams.desc = undefined; cameraRawParams.useDescriptor = false; cameraRawParams.fileName = gParams["source"] + "/x"; if ( gParams["open"] == true ) { cameraRawParams.useDescriptor = true; cameraRawParams.fileName = this.GetFirstFile( gParams["source"] ); if (debug_alerts) { alert("after GetFirstFile, thinking about working on " + cameraRawParams.fileName); } if ( ! IsFileOneOfThese( cameraRawParams.fileName, gFilesToSkip ) ) { if (debug_alerts) { alert("after GetFirstFile, trying to open, as a raw, " + cameraRawParams.fileName); } if ( this.OpenCameraRaw( cameraRawParams ) ) { this.AdjustFile( cameraRawParams ); this.SaveFile( cameraRawParams.fileName, gParams["source"] ); activeDocument.close( SaveOptions.DONOTSAVECHANGES ); } else { return; } } else { alert( "This is not a camera raw file according to my list of extensions, " + cameraRawParams.fileName ); return; } } this.OpenAdjustSaveTheRest( cameraRawParams ); } // the heart of the script is this routine and Execute // loop through all the files and save accordingly this.OpenAdjustSaveTheRest = function( inCameraRawParams ) { var firstFileAsLowerCaseString = ""; if ( inCameraRawParams.fileName != undefined ) { firstFileAsLowerCaseString = inCameraRawParams.fileName.toString().toLowerCase(); } var inputFiles; if ( gParams["flagged"] == true ) { inputFiles = GetFilesFromFileBrowser( true, false ); } else if ( gParams["selected"] == true ) { inputFiles = GetFilesFromFileBrowser( false, true ); } else { var workingFolder = File( inCameraRawParams.fileName ).parent; inputFiles = Folder( workingFolder ).getFiles(); } for (var i = 0; i < inputFiles.length; i++) { var thisFileAsLowerCaseString = inputFiles[i].toString().toLowerCase(); if (inputFiles[i] instanceof File && inputFiles[i].hidden == false && firstFileAsLowerCaseString != thisFileAsLowerCaseString ) { inCameraRawParams.fileName = inputFiles[i].toString(); if (debug_alerts) { alert("thinking about working on " + inCameraRawParams.fileName); } if ( ! IsFileOneOfThese( inCameraRawParams.fileName, gFilesToSkip ) ) { if (debug_alerts) { alert("opening with OpenCameraRaw " + inCameraRawParams.fileName); } this.OpenCameraRaw( inCameraRawParams, DialogModes.NO ); this.AdjustFile( inCameraRawParams ); this.SaveFile( inCameraRawParams.fileName, gParams["source"] ); activeDocument.close( SaveOptions.DONOTSAVECHANGES ); } } } } // using the dialog adjust the active document this.AdjustFile = function ( inOutCameraRawParams ) { var docRef = activeDocument; if ( gParams["hiddenCopyrightText"] != "" ) { docRef.info.copyrightNotice = gParams["hiddenCopyrightText"]; docRef.info.copyrighted = CopyrightedType.COPYRIGHTEDWORK; } if ( gParams["photographerName"] != "" ) { docRef.info.author = gParams["photographerName"]; docRef.info.ownerUrl = "http://philip.greenspun.com"; } } // I can save in three formats, JPEG, JPEG, JPEG this.SaveFile = function ( inFileName, inFolderLocation ) { var lastDot = inFileName.lastIndexOf( "." ); var fileNameNoPath = inFileName.substr( 0, lastDot ); // nameExtensionArray[0].split("/"); var lastSlash = fileNameNoPath.lastIndexOf( "/" ); fileNameNoPath = fileNameNoPath.substr( lastSlash + 1, fileNameNoPath.length ); if ( ! Folder( inFolderLocation + "/JPEG/" ).exists ) { if ( ! Folder( inFolderLocation + "/JPEG/" ).create() ) { throw( "Could not create output folder " + inFolderLocation + "/JPEG/" ); } } activeDocument.flatten(); activeDocument.bitsPerChannel = BitsPerChannelType.EIGHT; RemoveAlphaChannels(); if ( gParams["jpeg1"] == true ) { var subFolderText = inFolderLocation; var historyState = activeDocument.activeHistoryState; FitImage( 1536, 1536 ); activeDocument.activeLayer.applyUnSharpMask(100,2.0,2); AdjustBigForCopyright(); var uniqueFileName = subFolderText + "/JPEG/" + fileNameNoPath + ".4.jpg"; SaveAsJPEG( uniqueFileName, gParams["q1"], true ); activeDocument.activeHistoryState = historyState; } if ( gParams["jpeg2"] == true ) { var subFolderText = inFolderLocation; var historyState = activeDocument.activeHistoryState; FitImage( 768, 768 ); activeDocument.activeLayer.applyUnSharpMask(100,1.0,2); AdjustMediumForCopyright(); var uniqueFileName = subFolderText + "/JPEG/" + fileNameNoPath + ".3.jpg"; SaveAsJPEG( uniqueFileName, gParams["q2"], true ); activeDocument.activeHistoryState = historyState; } // this is a thumbnail so we don't AdjustforCopyright if ( gParams["jpeg3"] == true ) { var subFolderText = inFolderLocation; var historyState = activeDocument.activeHistoryState; FitImage( gParams["s"], gParams["s"] ); // let's sharpen now, after resizing, just before jpegging activeDocument.activeLayer.applyUnSharpMask(100,0.25,2); AddThumbnailBorders(); var uniqueFileName = subFolderText + "/JPEG/" + fileNameNoPath + ".1.jpg"; // no embed color profile SaveAsJPEG( uniqueFileName, gParams["q3"], false ); activeDocument.activeHistoryState = historyState; } // let's also make a .2.jpg size (about 256x384) var subFolderText = inFolderLocation; var historyState = activeDocument.activeHistoryState; FitImage( 384, 384 ); // let's sharpen now, after resizing, just before jpegging activeDocument.activeLayer.applyUnSharpMask(100,0.5,2); AddThumbnailBorders(); var uniqueFileName = subFolderText + "/JPEG/" + fileNameNoPath + ".2.jpg"; // no embed color profile SaveAsJPEG( uniqueFileName, gParams["q3"], false ); activeDocument.activeHistoryState = historyState; // layer upon layer of crud... added October 2006 by philg@mit.edu // to make a JPEG the same size as the original var subFolderText = inFolderLocation; var historyState = activeDocument.activeHistoryState; // no resize, just sharpen activeDocument.activeLayer.applyUnSharpMask(100,2.0,2); // add big borders and text, same as the .5.jpg AdjustBigForCopyright(); var uniqueFileName = subFolderText + "/JPEG/" + fileNameNoPath + ".jpg"; // this is for printing, so we make it high quality SaveAsJPEG( uniqueFileName, 9, true ); activeDocument.activeHistoryState = historyState; } // open a camera raw file returning the camera raw action desc this.OpenCameraRaw = function( inOutCameraRawParams, inDialogMode ) { var keyNull = charIDToTypeID( 'null' ); var keyAs = charIDToTypeID( 'As ' ); var adobeCameraRawID = stringIDToTypeID( "Adobe Camera Raw" ); var desc = new ActionDescriptor(); desc.putPath( keyNull, File( inOutCameraRawParams.fileName ) ); if ( inOutCameraRawParams.desc != undefined && inOutCameraRawParams.useDescriptor == true && IsFileOneOfThese( inOutCameraRawParams.fileName, gFilesForCameraRaw ) ) { desc.putObject( keyAs, adobeCameraRawID, inOutCameraRawParams.desc ); } if ( inDialogMode == undefined ) { inDialogMode = DialogModes.ALL; } var returnDesc = executeAction( charIDToTypeID( 'Opn ' ), desc, inDialogMode ); if ( returnDesc.hasKey( keyAs ) ) { inOutCameraRawParams.desc = returnDesc.getObjectValue( keyAs, adobeCameraRawID ); if ( returnDesc.hasKey( keyNull ) ) { inOutCameraRawParams.fileName = returnDesc.getPath( keyNull ).toString(); return true; } } return false; } // given a folder return the first valid file in that folder this.GetFirstFile = function ( inFolder ) { if ( inFolder.length > 0 && inFolder[0] != '.' ) { var fileList = Folder( inFolder ).getFiles(); for ( var i = 0; i < fileList.length; i++) { if ( fileList[i] instanceof File && fileList[i].hidden == false && ! IsFileOneOfThese( fileList[i], gFilesToSkip ) ) { return fileList[i].toString(); } } } return ""; } } function SaveAsJPEG( inFileName, inQuality, inEmbedICC ) { var jpegOptions = new JPEGSaveOptions(); jpegOptions.quality = inQuality; jpegOptions.embedColorProfile = inEmbedICC; activeDocument.saveAs( File( inFileName ), jpegOptions ); } function SaveAsPSD( inFileName, inMaximizeCompatibility, inEmbedICC ) { var psdSaveOptions = new PhotoshopSaveOptions(); psdSaveOptions.embedColorProfile = inEmbedICC; psdSaveOptions.maximizeCompatibility = inMaximizeCompatibility; activeDocument.saveAs( File( inFileName ), psdSaveOptions ); } function SaveAsTIFF( inFileName, inEmbedICC, inLZW ) { var tiffSaveOptions = new TiffSaveOptions(); tiffSaveOptions.embedColorProfile = inEmbedICC; if ( inLZW ) { tiffSaveOptions.imageCompression = TIFFEncoding.TIFFLZW; } else { tiffSaveOptions.imageCompression = TIFFEncoding.NONE; } activeDocument.saveAs( File( inFileName ), tiffSaveOptions ); } // use the fit image automation plug-in to do this work for me function FitImage( inWidth, inHeight ) { if ( inWidth == undefined || inHeight == undefined ) { alert("Width and Height must be defined to use FitImage function!"); return; } var desc = new ActionDescriptor(); var unitPixels = charIDToTypeID( '#Pxl' ); desc.putUnitDouble( charIDToTypeID( 'Wdth' ), unitPixels, inWidth ); desc.putUnitDouble( charIDToTypeID( 'Hght' ), unitPixels, inHeight ); var runtimeEventID = stringIDToTypeID( "3caa3434-cb67-11d1-bc43-0060b0a13dc4" ); executeAction( runtimeEventID, desc, DialogModes.NO ); } // get all the files in the file browser that are selected or flagged function GetFilesFromFileBrowser( flagged, selected ) { var fileArray = new Array(); var ffIndex = 0; var ref = new ActionReference(); var fileBrowserStrID = stringIDToTypeID( "fileBrowser" ) // ask for all keys and then do a haskey ref.putProperty( charIDToTypeID( 'Prpr' ), fileBrowserStrID ); ref.putEnumerated( charIDToTypeID( 'capp' ), charIDToTypeID( 'Ordn' ), charIDToTypeID( 'Trgt' ) ); var desc = executeActionGet( ref ); if ( desc.count > 0 && desc.hasKey( fileBrowserStrID ) ) { var fbDesc = desc.getObjectValue( fileBrowserStrID ); var keyFilesList = charIDToTypeID( 'flst' ); if ( fbDesc.count > 0 && fbDesc.hasKey( keyFilesList ) ) { var fileList = fbDesc.getList( keyFilesList ); var flaggedID = stringIDToTypeID( "flagged" ); var selectedID = charIDToTypeID( 'fsel' ); var keyPath = charIDToTypeID( 'Path' ); for ( var i = 0; i < fileList.count; i++ ) { var fileDesc = fileList.getObjectValue( i ); if ( fileDesc.count > 0 && fileDesc.hasKey( keyPath )) { if ( flagged == true && fileDesc.hasKey( flaggedID ) && fileDesc.getBoolean( flaggedID )) { var fileOrFolder = fileDesc.getPath( keyPath ); if ( fileOrFolder instanceof File ) { fileArray[ffIndex++] = fileOrFolder; } } if ( selected == true && fileDesc.hasKey( selectedID ) && fileDesc.getBoolean( selectedID )) { var fileOrFolder = fileDesc.getPath( keyPath ); if ( fileOrFolder instanceof File ) { fileArray[ffIndex++] = fileOrFolder; } } } } } } return fileArray; } // a very crude xml parser, this reads the "Tag" of the Data function ReadHeader( inFile ) { var returnValue = ""; if ( ! inFile.eof ) { var c = ""; while ( c != "<" && ! inFile.eof ) { c = inFile.read( 1 ); } while ( c != ">" && ! inFile.eof ) { c = inFile.read( 1 ); if ( c != ">" ) { returnValue += c; } } } else { returnValue = "end of file"; } return returnValue; } // very crude xml parser, this reads the "Data" of the Data function ReadData( inFile ) { var returnValue = ""; if ( ! inFile.eof ) { var c = ""; while ( c != "<" && ! inFile.eof ) { c = inFile.read( 1 ); if ( c != "<" ) { returnValue += c; } } inFile.seek( -1, 1 ); } return returnValue; } function CheckVersion() { var numberArray = version.split("."); if ( numberArray[0] < 8 ) { alert( "You must use Photoshop CS or later to run this script!" ); throw( "You must use Photoshop CS or later to run this script!" ); } } function CreateUniqueFileName( inFolder, inFileName, inExtension ) { var uniqueFileName = inFolder + inFileName + inExtension; var fileNumber = 1; while ( File( uniqueFileName ).exists ) { uniqueFileName = inFolder + inFileName + "_" + fileNumber + inExtension; fileNumber++; } return uniqueFileName; } function CreateShortFileName( inFileName ) { if ( inFileName.length > gShortFileNameLength ) { return "..." + inFileName.substr( inFileName.length - gShortFileNameLength + 3, gShortFileNameLength - 3 ); } return inFileName; } function IsWindowsOS() { if ( $.os.search(/windows/i) != -1 ) { return true; } else { return false; } } function RemoveAlphaChannels() { var channels = activeDocument.channels; var channelCount = channels.length - 1; while ( channels[channelCount].kind != ChannelType.COMPONENT ) { channels[channelCount].remove(); channelCount--; } } function ConvertTosRGBProfile() { var id136 = stringIDToTypeID( "convertToProfile" ); var desc31 = new ActionDescriptor(); var id137 = charIDToTypeID( "null" ); var ref6 = new ActionReference(); var id138 = charIDToTypeID( "Dcmn" ); var id139 = charIDToTypeID( "Ordn" ); var id140 = charIDToTypeID( "Trgt" ); ref6.putEnumerated( id138, id139, id140 ); desc31.putReference( id137, ref6 ); var id141 = charIDToTypeID( "T " ); desc31.putString( id141, "sRGB IEC61966-2.1" ); var id142 = charIDToTypeID( "Inte" ); var id143 = charIDToTypeID( "Inte" ); var id144 = charIDToTypeID( "Clrm" ); desc31.putEnumerated( id142, id143, id144 ); var id145 = charIDToTypeID( "MpBl" ); desc31.putBoolean( id145, true ); var id146 = charIDToTypeID( "Dthr" ); desc31.putBoolean( id146, true ); executeAction( id136, desc31, DialogModes.NO ); } // load my params from the xml file on disk if it exists // gParams["myoptionname"] = myoptionvalue // I wrote a very simple xml parser, I'm sure it needs work function LoadParamsFromDisk ( loadFile, params ) { // var params = new Array(); if ( loadFile.exists ) { loadFile.open( "r" ); var projectSpace = ReadHeader( loadFile ); if ( projectSpace == GetScriptNameForXML() ) { while ( ! loadFile.eof ) { var starter = ReadHeader( loadFile ); var data = ReadData( loadFile ); var ender = ReadHeader( loadFile ); if ( ( "/" + starter ) == ender ) { params[starter] = data; } // force boolean values to boolean types if ( data == "true" || data == "false" ) { params[starter] = data == "true"; } } } loadFile.close(); if ( params["version"] != gVersion ) { // do something here to fix version conflicts // this should do it params["version"] = gVersion; } } } // save out my params, this is much easier function SaveParamsToDisk ( saveFile, params ) { saveFile.encoding = "UTF8"; saveFile.open( "w", "TEXT", "????" ); // unicode signature, this is UTF16 but will convert to UTF8 "EF BB BF" saveFile.write("\uFEFF"); var scriptNameForXML = GetScriptNameForXML(); saveFile.writeln( "<" + scriptNameForXML + ">" ); for ( var p in params ) { saveFile.writeln( "\t<" + p + ">" + params[p] + "" ); } saveFile.writeln( "" ); saveFile.close(); } // you can't save certain characters in xml, strip them here // this list is not complete function GetScriptNameForXML () { var scriptNameForXML = new String( gScriptName ); var charsToStrip = Array( " ", "'", "." ); for (var a = 0; a < charsToStrip.length; a++ ) { var nameArray = scriptNameForXML.split( charsToStrip[a] ); scriptNameForXML = ""; for ( var b = 0; b < nameArray.length; b++ ) { scriptNameForXML += nameArray[b]; } } return scriptNameForXML; } // transfer from the default settings or settings I read off disk to the dialog widgets function InitDialog( dialog, params ) { dialog.sourceEt.text = CreateShortFileName( params["source"] ); dialog.sourceLongText = params["source"]; dialog.lastSource = params["source"]; dialog.fileTypePnl.jpeg1Cb.value = params["jpeg1"]; dialog.fileTypePnl.jpeg2Cb.value = params["jpeg2"]; dialog.fileTypePnl.jpeg3Cb.value = params["jpeg3"]; dialog.fileTypePnl.quality1Et.text = params["q1"]; dialog.fileTypePnl.quality2Et.text = params["q2"]; dialog.fileTypePnl.quality3Et.text = params["q3"]; dialog.fileTypePnl.sizeEt.text = params["s"]; dialog.copyrightInfoHiddenEt.text = params["hiddenCopyrightText"]; dialog.copyrightInfoVisibleEt.text = params["visibleCopyrightText"]; dialog.photographerEt.text = params["photographerName"]; } // pretend like i clicked it to get the other items to respond to the current settings function ForceDialogUpdate( dialog ) { dialog.fileTypePnl.jpeg1Cb.onClick(); dialog.fileTypePnl.jpeg2Cb.onClick(); dialog.fileTypePnl.jpeg3Cb.onClick(); } // transfer from the dialog widgets to my internal params function GetParamsFromDialog( dialog, params ) { if ( dialog.sourceEt.text != CreateShortFileName( dialog.sourceLongText ) ) params["source"] = dialog.sourceEt.text; else params["source"] = dialog.sourceLongText; params["jpeg1"] = dialog.fileTypePnl.jpeg1Cb.value; params["jpeg2"] = dialog.fileTypePnl.jpeg2Cb.value; params["jpeg3"] = dialog.fileTypePnl.jpeg3Cb.value; params["q1"] = dialog.fileTypePnl.quality1Et.text; params["q2"] = dialog.fileTypePnl.quality2Et.text; params["q3"] = dialog.fileTypePnl.quality3Et.text; params["s"] = dialog.fileTypePnl.sizeEt.text; params["hiddenCopyrightText"] = dialog.copyrightInfoHiddenEt.text; params["visibleCopyrightText"] = dialog.copyrightInfoVisibleEt.text; params["photographerName"] = dialog.photographerEt.text; return params; } ///////////////////////////////////////////////////////////////////// // Function: MacXMLFilter // Input: f, file or folder to check // Return: true or false, true if file or folder is to be displayed ///////////////////////////////////////////////////////////////////// function MacXMLFilter( f ) { var xmlExtension = "*.xml"; var lCaseName = f.name; lCaseName.toLowerCase(); if ( lCaseName.indexOf( xmlExtension ) == f.name.length - xmlExtension.length ) return true; else if ( f.type == 'TEXT' ) return true; else if ( f instanceof Folder ) return true; else return false; } // figure out what I call my params file function GetDefaultParamsFile() { var paramsFolder = new Folder( path + "/Presets/" + gScriptName ); paramsFolder.create(); return ( new File( paramsFolder + "/" + gScriptName + ".xml" ) ); } function AdjustBigForCopyright() { CanvasSize( 35, 35 ); CanvasSizeHeightAnchorTop( 7 ); AddBigTextAtBottom(); } function AdjustMediumForCopyright() { CanvasSize( 20, 20 ); CanvasSizeHeightAnchorTop( 5 ); AddMediumTextAtBottom(); } function AddThumbnailBorders () { CanvasSize( 5, 5 ); } function CanvasSize( width, height ) { var id24 = charIDToTypeID( "CnvS" ); var desc5 = new ActionDescriptor(); var id25 = charIDToTypeID( "Rltv" ); desc5.putBoolean( id25, true ); var id26 = charIDToTypeID( "Wdth" ); var id27 = charIDToTypeID( "#Pxl" ); desc5.putUnitDouble( id26, id27, width ); var id28 = charIDToTypeID( "Hght" ); var id29 = charIDToTypeID( "#Pxl" ); desc5.putUnitDouble( id28, id29, height ); var id30 = charIDToTypeID( "Hrzn" ); var id31 = charIDToTypeID( "HrzL" ); var id32 = charIDToTypeID( "Cntr" ); desc5.putEnumerated( id30, id31, id32 ); var id33 = charIDToTypeID( "Vrtc" ); var id34 = charIDToTypeID( "VrtL" ); var id35 = charIDToTypeID( "Cntr" ); desc5.putEnumerated( id33, id34, id35 ); var id36 = stringIDToTypeID( "canvasExtensionColorType" ); var id37 = stringIDToTypeID( "canvasExtensionColorType" ); var id38 = charIDToTypeID( "Blck" ); desc5.putEnumerated( id36, id37, id38 ); executeAction( id24, desc5, DialogModes.NO ); } function CanvasSizeHeightAnchorTop( height ) { var id106 = charIDToTypeID( "CnvS" ); var desc22 = new ActionDescriptor(); var id107 = charIDToTypeID( "Rltv" ); desc22.putBoolean( id107, true ); var id108 = charIDToTypeID( "Hght" ); var id109 = charIDToTypeID( "#Pxl" ); desc22.putUnitDouble( id108, id109, height ); var id110 = charIDToTypeID( "Vrtc" ); var id111 = charIDToTypeID( "VrtL" ); var id112 = charIDToTypeID( "Top " ); desc22.putEnumerated( id110, id111, id112 ); var id113 = stringIDToTypeID( "canvasExtensionColorType" ); var id114 = stringIDToTypeID( "canvasExtensionColorType" ); var id115 = charIDToTypeID( "Blck" ); desc22.putEnumerated( id113, id114, id115 ); executeAction( id106, desc22, DialogModes.NO ); } // functions that actually draw the text // tried textLayer.textItem.height = 20; but got an error saying that this was only for "paragraph text" function AddBigTextAtBottom() { // foregroundColor.model = ColorModel.RGB; foregroundColor.rgb.red = 255; foregroundColor.rgb.green = 255; foregroundColor.rgb.blue = 255; var textLayer = activeDocument.artLayers.add(); textLayer.kind = LayerKind.TEXT; textLayer.name = "Copyright Note"; textLayer.textItem.contents = gParams["visibleCopyrightText"]; textLayer.textItem.color = foregroundColor; textLayer.textItem.font = "9x15"; textLayer.textItem.antiAliasMethod = AntiAlias.NONE; stringwidth = textLayer.textItem.contents.length*9 textLayer.textItem.position = new Array( activeDocument.width - stringwidth - 20, activeDocument.height - 6 ); } function AddMediumTextAtBottom() { // foregroundColor.model = ColorModel.RGB; foregroundColor.rgb.red = 255; foregroundColor.rgb.green = 255; foregroundColor.rgb.blue = 255; var textLayer = activeDocument.artLayers.add(); textLayer.kind = LayerKind.TEXT; textLayer.name = "Copyright Note"; textLayer.textItem.contents = gParams["visibleCopyrightText"]; stringwidth = textLayer.textItem.contents.length*6 textLayer.textItem.position = new Array( activeDocument.width - stringwidth - 10, activeDocument.height - 3 ); textLayer.textItem.color = foregroundColor; textLayer.textItem.font = "6x13"; textLayer.textItem.antiAliasMethod = AntiAlias.NONE; textLayer.textItem.kind = TextType.PARAGRAPHTEXT; // font size is very odd, so says our Adobe guru } // JavaScript lore // you need some sort of weird prefix character before the copyright symbol /// or JavaScript complains about an unterminated string // textLayer.name = "© 2004 http://philip.greenspun.com/copyright/";