Convert to JPEG with Given Specifications

Requires Photoshop CS4.

Had a need to batch-convert Photoshop files (PSD, EPS, TIFF, JPEG) and AI files (as long as PDF Preview is turned on) for a web site. Also a similar need to convert these formats to certain “don’t exceed this size” JPEGs.

Started simple”just trying to work-around the fact that Actions have no if/then logic to them (for "resizing not to exceed a given height and width), then of course we thought of more things it should do.

So I wrote this, with a little help from MacScripter folks. Converts to RGB, resizes based on given dimensions, takes inches and pixels, strips extra channels, strips color profiles, flattens, and let’s you choose a basic JPEG quality level.

A little tedious with all the dialogs.will be converting this to a FaceSpan project for a better UI, but not sure when I’ll get to it.

--
-- DECLARE PROPERTIES
--
-- debugging on?
property g_debug : false

--basic file path and names
property g_home_folder_path : path to home folder
property g_log_file_name : "Convert Images to JPEG.txt"



--
-- UTILITY HANDLERS
--

--Log Entry Generation
-- With help from StephanK of MacScripter
-- http://bbs.applescript.net/viewtopic.php?pid=76607#p76607
--
on logMe(log_string, indent_level)
	if g_debug is true then --allows turning the debugger on and off so my logMe's can be left in final version
		set log_target to (g_home_folder_path & "Library:Logs:" & g_log_file_name) as text
		try
			set log_file_ref to open for access file log_target with write permission
			repeat indent_level times
				write tab to log_file_ref starting at eof
			end repeat
			write ((log_string as text) & return) to log_file_ref starting at eof
			close access log_file_ref
			return true
		on error
			try
				close access file log_target
			end try
			return false
		end try
	end if
end logMe

--
-- MAIN SCRIPT
--
on open actionItems
	
	-- remove folders from list
	--
	-- courtesy of Adam Bell of MacScripter
	-- http://bbs.applescript.net/viewtopic.php?pid=77739#p77739
	--
	set dropped_items to {}
	
	repeat with current_actionItem in actionItems
		tell application "Finder"
			if (contents of current_actionItem as text) ends with ":" then
				try
					set dropped_items to dropped_items & (files of (entire contents of current_actionItem) as alias list)
				on error -- only one file inside (works around bug)
					set dropped_items to dropped_items & (files of (entire contents of current_actionItem) as alias as list)
				end try
			else
				set dropped_items to dropped_items & current_actionItem
			end if
		end tell
	end repeat
	
	--gather user-entered parameters
	set u to display dialog "Units?" buttons {"inches", "pixels"} default button "pixels"
	set working_units to button returned of u
	
	set h to display dialog "Height:" default answer ""
	set new_height to text returned of h
	
	set w to display dialog "Width:" default answer ""
	set new_width to text returned of w
	
	set p to display dialog "Pad to provided dimensions?" buttons {"Yes", "No"} default button "No"
	set pad_image to button returned of p
	
	set j to display dialog "JPEG Quality:" buttons {"Medium", "High", "Maximum"} default button "Maximum"
	set JPEG_quality to button returned of j
	
	set n to display dialog "File Suffix:" default answer "_web"
	set file_suffix to text returned of n
	
	set r to display dialog "Resolution:" default answer "72"
	set file_res to text returned of r
	
	set save_location to choose folder with prompt "Choose a location to save converted files:"
	
	--convert user answer to JPEG quality to Photoshop's numeric notation
	if JPEG_quality = "Medium" then
		set JPEG_quality to 5
	else
		if JPEG_quality = "High" then
			set JPEG_quality to 8
		else
			if JPEG_quality = "Maximum" then
				set JPEG_quality to 12
			end if
		end if
	end if
	
	--step through files
	repeat with i from 1 to number of items of dropped_items
		set parseMeString to (item i of dropped_items) as string
		set parseMeStringPOSIX to POSIX path of parseMeString
		
		--get file name without extension
		tell (info for item i of dropped_items without size) to set {file_name, file_extension} to {name, name extension}
		set file_name to text 1 thru -((count file_extension) + 2) of file_name
		
		--rename file to indicate it was converted by script
		set new_file_name to file_name & file_suffix & ".jpg"
		
		try
			tell application "Adobe Photoshop CS4"
				activate
				open file (parseMeStringPOSIX as text)
				
				--set document units to pixels (resize command only uses ppi)
				set ruler units of settings to pixel units
				
				--fix pixel aspect ratio
				set pixel aspect ratio of current document to 1.0
				
				tell current document
					--get height and width of current document
					set current_height to height
					set current_width to width
					
					--flatten image
					flatten
					
					--set image resolution
					resize image resolution file_res resample method none
					
					--if user requested inches, convert dimensions in-place
					if working_units is "inches" then
						set new_height to (new_height * file_res)
						set new_width to (new_width * file_res)
					end if
					
					--change to RGB (CMYK not web compatible)
					change mode to RGB
				end tell
				
				--set background for padding (canvas resize)
				set background color to {class:RGB color, red:255, green:255, blue:255}
				
				--remove extra channels
				set safe_channels to {"Red", "Green", "Blue"}
				
				set channel_names to name of channels of current document
				
				repeat with c from 1 to number of items in channel_names
					if (item c of channel_names) is not in safe_channels then
						delete channel (item c of channel_names) of current document
					end if
				end repeat
				
				--figure out reduction percentages
				set height_ratio to new_height / current_height
				set width_ratio to new_width / current_width
				
				--make sure is not enlargement
				if (height_ratio < 1) or (width_ratio < 1) then
					
					--pick out smallest reduction and use that
					if height_ratio ≤ width_ratio then
						resize image current document height new_height resample method bicubic
					else
						if width_ratio < height_ratio then
							resize image current document width new_width resample method bicubic
						end if
					end if
				end if
				
				--pad image if required
				if pad_image is "Yes" then
					resize canvas current document width new_width height new_height anchor position middle center
				end if
				
				--create path to destination
				set new_file_location to ((save_location as Unicode text) & new_file_name)
				
				--create reference (required for Photoshop saves)
				set new_file_reference to (a reference to file new_file_location)
				
				--save and close file
				save current document appending lowercase extension as JPEG in new_file_reference with options {embed color profile:false, format options:optimized, quality:JPEG_quality}
				close current document
				
				--set completed file label to green
				tell application "Finder" to set label index of (item i of dropped_items) to 6
				
			end tell
			
		on error
			activate
			display alert file_name message "Unable to process this file, it will be skipped."
			
			--set uncompleted file label to red
			tell application "Finder" to set label index of (item i of dropped_items) to 2
		end try
	end repeat
end open

I added some features (ability to handle Inches input being the key one) and added a user interface, but only after I turned it into a FaceSpan project.

This is FaceSpan “project AppleScript” so won’t work without modification in plain-Jane AppleScript anymore:
(the key problem is path handling and UI elements that are different/won’t work in plain AppleScript)

--
-- DECLARE PROPERTIES & GLOBAL VARIABLES
--
-- debugging on?
property g_debug : false

--basic file path and names
property g_home_folder_path : path to home folder
property g_desktop_folder_path : path to desktop folder
property g_log_file_name : "Convert Images to JPEG.txt"

--global variables
global g_dropped_items

--
-- UTILITY HANDLERS
--

--Log Entry Generation
-- With help from StephanK of MacScripter
-- http://bbs.applescript.net/viewtopic.php?pid=76607#p76607
--
on logMe(log_string, indent_level)
	if g_debug is true then --allows turning the debugger on and off so my logMe's can be left in final version
		set log_target to (g_home_folder_path & "Library:Logs:" & g_log_file_name) as text
		try
			set log_file_ref to open for access file log_target with write permission
			repeat indent_level times
				write tab to log_file_ref starting at eof
			end repeat
			write ((log_string as text) & return) to log_file_ref starting at eof
			close access log_file_ref
			return true
		on error
			try
				close access file log_target
			end try
			return false
		end try
	end if
end logMe


--
-- FACESPAN HANDLERS (interface)
--

-- Display mainInterface window
--
on displayMainInterface(preset_name)
	--close other windows that might be open
	hide window "progressWindow"
	
	--display version number of app
	tell current application to set cfr_version to version
	set string value of text field "textVersion" of window "mainInterface" to ("Version " & cfr_version)
	
	--initialized fields (leave relevant pre-entered data except on reset)
	if preset_name is "web" then
		set current column of matrix "radioUnits" of window "mainInterface" to 2 --set units to pixels by default
		set string value of text field "textFillRes" of window "mainInterface" to "72"
		set current column of matrix "radioQuality" of window "mainInterface" to 2 --set quality to "High" by default
	else if preset_name is "high" then
		set current column of matrix "radioUnits" of window "mainInterface" to 1 --set units to inches by default
		set string value of text field "textFillRes" of window "mainInterface" to "300"
		set current column of matrix "radioQuality" of window "mainInterface" to 3 --set quality to "Maximum" by default
	else if preset_name is "reset" then
		--reset all fields
		set string value of text field "textFillHeight" of window "mainInterface" to ""
		set string value of text field "textFillWidth" of window "mainInterface" to ""
		set current column of matrix "radioUnits" of window "mainInterface" to 2 --set units to pixels by default
		set current column of matrix "radioPad" of window "mainInterface" to 2 --set padding to "no" by default
		set string value of text field "textFillRes" of window "mainInterface" to ""
		set current column of matrix "radioQuality" of window "mainInterface" to 3 --set quality to "Maximum" by default
		set string value of text field "textFillSuffix" of window "mainInterface" to ""
		
		set save_location to g_desktop_folder_path
		set string value of text field "textSaveLoc" of window "mainInterface" to save_location
	end if
	
	--display main window
	show window "mainInterface"
	center window "mainInterface"
	tell application "Convert Images to JPEG" to activate
end displayMainInterface

-- Button handler
--
on clicked theObject
	
	set button_clicked to name of theObject
	
	if button_clicked is "buttonStart" then
		logMe("¢ Start Button clicked", 0)
		processFiles()
	end if
	
	if button_clicked is "buttonWeb" then
		displayMainInterface("web")
	end if
	
	if button_clicked is "buttonHigh" then
		displayMainInterface("high")
	end if
	
	if button_clicked is "buttonReset" then
		displayMainInterface("reset")
	end if
	
	if button_clicked is "buttonBrowse" then
		set save_location to choose folder with prompt "Choose a location to save converted files:"
		set string value of text field "textSaveLoc" of window "mainInterface" to save_location
	end if
	
end clicked

-- Progress window
--
on displayProgress()
	hide window "mainInterface"
	show window "progressWindow"
	center window "progressWindow"
end displayProgress


--
-- MAIN SCRIPT (kick-off application and interface only)
--
on open actionItems
	
	logMe("Open Handler Started", 0)
	
	--convert UNIX paths "in place" to alias (FaceSpan droplet outputs UNIX paths, AppleScript droplets output alias paths)
	repeat with an_item in actionItems
		set contents of an_item to alias (POSIX file an_item)
	end repeat
	
	-- remove folders from list
	--
	-- courtesy of Adam Bell of MacScripter
	-- http://bbs.applescript.net/viewtopic.php?pid=77739#p77739
	--
	set g_dropped_items to {}
	
	repeat with current_actionItem in actionItems
		tell application "Finder"
			if (contents of current_actionItem as text) ends with ":" then
				try
					set g_dropped_items to g_dropped_items & (files of (entire contents of current_actionItem) as alias list)
				on error -- only one file inside (works around bug)
					set g_dropped_items to g_dropped_items & (files of (entire contents of current_actionItem) as alias as list)
				end try
			else
				set g_dropped_items to g_dropped_items & current_actionItem
			end if
		end tell
	end repeat
	
	--set mainInterface
	displayMainInterface("reset")
	
end open

--
-- MAIN SCRIPT (handler from clicking "Start")
--
on processFiles()
	logMe("processFiles Started", 0)
	logMe("¢ Files to Process: " & g_dropped_items, 1)
	logMe("¢ Number of Items: " & (count of items of g_dropped_items), 1)
	
	--display progress window
	displayProgress()
	
	--collect parameters from interface
	set new_height to get string value of text field "textFillHeight" of window "mainInterface"
	set new_width to get string value of text field "textFillWidth" of window "mainInterface"
	set working_units to get title of current cell of matrix "radioUnits" of window "mainInterface"
	set pad_image to get title of current cell of matrix "radioPad" of window "mainInterface"
	set file_res to get string value of text field "textFillRes" of window "mainInterface"
	set JPEG_quality to get title of current cell of matrix "radioQuality" of window "mainInterface"
	set file_suffix to get string value of text field "textFillSuffix" of window "mainInterface"
	set save_location to get string value of text field "textSaveLoc" of window "mainInterface"
	
	logMe("User Parameters Collected", 1)
	
	--convert user answer to JPEG quality to Photoshop's numeric notation
	if JPEG_quality = "Medium" then
		set JPEG_quality to 5
	else
		if JPEG_quality = "High" then
			set JPEG_quality to 8
		else
			if JPEG_quality = "Maximum" then
				set JPEG_quality to 12
			end if
		end if
	end if
	
	logMe("JPEG Quality Notation Converted", 1)
	
	--step through files
	repeat with i from 1 to count of items of g_dropped_items
		
		logMe("Step Through Files: Loop " & i, 2)
		
		(* Needed only outside FaceSpan?
		set parseMeString to (item i of g_dropped_items) as string
		set parseMeStringPOSIX to POSIX path of parseMeString

		logMe("¢ POSIX Conversion Completed", 2)
		*)
		
		--get file name without extension
		tell (info for item i of g_dropped_items without size) to set {file_name, file_extension} to {name, name extension}
		set file_name to text 1 thru -((count file_extension) + 2) of file_name
		
		logMe("¢ File name without extension: " & file_name, 2)
		
		--rename file to indicate it was converted by script
		set new_file_name to file_name & file_suffix & ".jpg"
		
		logMe("¢ File name with extension: " & new_file_name, 2)
		
		try
			tell application "Adobe Photoshop CS4"
				
				my logMe("Photoshop Work Started", 3)
				
				activate
				open file (item i of g_dropped_items as string)
				--open file (parseMeStringPOSIX as text) --for outside FaceSpan
				
				--set document units to pixels (resize command only uses ppi)
				set ruler units of settings to pixel units
				
				--fix pixel aspect ratio
				set pixel aspect ratio of current document to 1.0
				
				tell current document
					--get height and width of current document
					set current_height to height
					set current_width to width
					
					--flatten image
					flatten
					
					--set image resolution
					resize image resolution file_res resample method none
					
					--if user requested inches, convert dimensions to ppi in-place (resize command needs ppi)
					if working_units is "Inches" then
						set new_height to (new_height * file_res)
						set new_width to (new_width * file_res)
					end if
					
					--change to RGB (CMYK not web compatible)
					change mode to RGB
				end tell
				
				--set background for padding (canvas resize)
				set background color to {class:RGB color, red:255, green:255, blue:255}
				
				--remove extra channels
				set safe_channels to {"Red", "Green", "Blue"}
				
				set channel_names to name of channels of current document
				
				repeat with c from 1 to number of items in channel_names
					if (item c of channel_names) is not in safe_channels then
						delete channel (item c of channel_names) of current document
					end if
				end repeat
				
				--figure out reduction percentages
				set height_ratio to new_height / current_height
				set width_ratio to new_width / current_width
				
				--make sure is not enlargement
				if (height_ratio < 1) or (width_ratio < 1) then
					
					--pick out smallest reduction and use that
					if height_ratio ≤ width_ratio then
						resize image current document height new_height resample method bicubic
					else
						if width_ratio < height_ratio then
							resize image current document width new_width resample method bicubic
						end if
					end if
				end if
				
				--pad image if required
				if pad_image is "Yes" then
					resize canvas current document width new_width height new_height anchor position middle center
				end if
				
				my logMe("¢ Image Padded (if needed)", 3)
				
				--create path to destination
				my logMe("¢ Build File Path and Name", 3)
				set new_file_location to ((save_location as Unicode text) & "/" & new_file_name)
				
				my logMe("¢ Destination Path Built: " & new_file_location, 4)
				
				--create reference (required for Photoshop saves)
				set new_file_reference to (a reference to file new_file_location)
				
				my logMe("¢ File Reference Created", 4)
				
				--save and close file
				save current document appending lowercase extension as JPEG in new_file_reference with options {embed color profile:false, format options:optimized, quality:JPEG_quality}
				close current document
				
				my logMe("¢ Save Completed", 3)
				
				--set completed file label to green
				tell application "Finder" to set label index of (item i of g_dropped_items) to 6
				
			end tell
			
		on error
			activate
			display alert file_name message "Unable to process this file, it will be skipped. Report problem to Kevin (x7060)."
			
			--set uncompleted file label to red
			tell application "Finder" to set label index of (item i of g_dropped_items) to 2
		end try
	end repeat
	
	logMe("processFiles Ended", 0)
	
	quit application
	
end processFiles