Exporting folder contents, PDFs or images to jpg

Hello guys,

I have something tricky for you.

I want to create a script that takes as input folders/files and outputs them as jpg images in a different folder and at a lower resolution.

The folders can contain only PDF documents or image files (various types).

For example:

Folder_A

  • Image1a.jpg
  • Image2a.png
  • Document1a.pdf (3 pages)
    Folder_B
  • image2b.jpg
  • document2a.pdf (1 page)

By selecting the first folder I would like to have this:

Folder_A_resized

  • Image1a_resized.jpg
  • Image2a_ resized.jpg
  • Document1a_page1_resized.jpg
  • Document1a_page2_resized.jpg
  • Document1a_page3_resized.jpg

Or by selecting both folders I would like to have this in a new folder:

Resized_Folder

  • Image1a_resized.jpg
  • Image2a_ resized.jpg
  • Document1a_page1_resized.jpg
  • Document1a_page2_resized.jpg
  • Document1a_page3_resized.jpg
  • image2b_resized.jpg
  • document2a_page1_resized.jpg

And for the end, by selecting only “Image2a.png” from the first folder and running the script, to get only the “Image2a_ resized.jpg” in the Resized_Folder.

For the moment I have two different scripts, one only for PDF files and the other only for image files, but it doesn’t work with folders and also I was wondering if I can combine them.

Thanks.

Posting the available scripts would spare time.
With a bit of luck there is no need to reinvent the wheel.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 4 juin 2020 23:20:55

Thanks to insomnia, here is a skeleton in which you just have to insert the code dedicated to PDF files and the code dedicated to pictures files whose type identifier is in {“public.png”, “public.jpeg”, “public.tiff”}.

on open draggeditems
	tell application "Finder"
		repeat with anItem in draggeditems
			if (class of (get properties of anItem)) is folder then
				my treatFolder(anItem)
			else
				my treatFile(anItem as string)
			end if
		end repeat
	end tell
end open

on treatFolder(aFolder)
	tell application "System Events"
		set theFiles to path of every file of aFolder whose visible is true
	end tell
	repeat with aFile in theFiles
		my treatFile(aFile)
	end repeat
end treatFolder

on treatFile(aFile)
	tell application "System Events"
		set typeID to get type identifier of file aFile
	end tell
	if typeID is in {"public.png", "public.jpeg", "public.tiff"} then
		my treatPicture(aFile)
	else if typeID is "com.adobe.pdf" then
		my treatPDF(aFile)
	end if
end treatFile

on treatPicture(aFile)
	-- insert the code dedicated to picture files here
end treatPicture

on treatPDF(aFile)
	-- insert the code dedicated to PDF files here
end treatPDF

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 5 juin 2020 00:10:56

Here is a completed script which use handlers from Shane Stanley and Takaaki Naganoya

use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "Quartz"
use framework "QuartzCore"

--Yvan KOENIG (VALLAURIS, France) vendredi 5 juin 2020 17:11:33

on open draggeditems
	tell application "System Events"
		repeat with anItem in draggeditems
			if (type identifier of anItem) is "public.folder" then
				my treatFolder(anItem)
			else
				my treatFile(anItem as string)
			end if
		end repeat
	end tell
end open

on treatFolder(aFolder)
	tell application "System Events"
		set theFiles to path of every file of aFolder whose visible is true
	end tell
	repeat with aFile in theFiles
		my treatFile(aFile)
	end repeat
end treatFolder

on treatFile(aFile)
	tell application "System Events"
		set typeID to get type identifier of file aFile
	end tell
	if typeID is in {"public.png", "public.jpeg", "public.tiff"} then
		my treatPicture(POSIX path of aFile) -- don't change that
	else if typeID is "com.adobe.pdf" then
		my treatPDF(aFile)
	end if
end treatFile

on treatPicture(POSIXPath)
	-- insert the code dedicated to picture files here
	my resizedJpegFromPath:POSIXPath maxHeightOrWidth:800 targetBytes:100000
end treatPicture

on treatPDF(aFile)
	-- code from Takaaki Naganoya
	set aPOSIXpath to POSIX path of aFile
	set aURL to (current application's |NSURL|'s fileURLWithPath:aPOSIXpath)
	set aPDFdoc to current application's PDFDocument's alloc()'s initWithURL:aURL
	set pCount to aPDFdoc's pageCount()
	-- Split the PDF into pages exported as Tiff files
	repeat with i from 0 to (pCount - 1)
		set thisPage to (aPDFdoc's pageAtIndex:(i))
		set thisDoc to (current application's NSImage's alloc()'s initWithData:(thisPage's dataRepresentation()))
		if thisDoc = missing value then error "Error in getting imagerep from PDF in page:" & (i as string)
		set theData to thisDoc's TIFFRepresentation()
		set newRep to (current application's NSBitmapImageRep's imageRepWithData:theData)
		set targData to (newRep's representationUsingType:(current application's NSTIFFFileType) |properties|:{NSTIFFCompressionNone:1})
		set ztext to text -4 thru -1 of ((10000 + i + 1) as string)
		set outPath to (my addString:("_" & ztext) beforeExtensionIn:aPOSIXpath addingExtension:"tiff")
		(targData's writeToFile:outPath atomically:true) -- Export
		-- added instructions
		-- apply the wanted reduction
		my treatPicture(outPath) -- outPath is a POSIX path
		set fileManager to (a reference to current application's NSFileManager's defaultManager())
		set tiffURL to (current application's |NSURL|'s fileURLWithPath:outPath)
		(fileManager's removeItemAtURL:tiffURL |error|:(missing value))
	end repeat
end treatPDF


-- Add an extra string and the name extension at the end of the POSIX path
on addString:extraString beforeExtensionIn:aPath addingExtension:aExt
	set pathString to current application's NSString's stringWithString:aPath
	--set theExtension to pathString's pathExtension()
	set thePathNoExt to pathString's stringByDeletingPathExtension()
	set newPath to (thePathNoExt's stringByAppendingString:extraString)'s stringByAppendingPathExtension:aExt
	return newPath as string
end addString:beforeExtensionIn:addingExtension:


on resizedJpegFromPath:imagePath maxHeightOrWidth:maxDim targetBytes:maxBytes
	-- handler from Shane Stanley
	set imagePath to current application's NSString's stringWithString:imagePath
	set outPath to (imagePath's stringByDeletingPathExtension()'s stringByAppendingString:"-out")
	set outPath to outPath's stringByAppendingPathExtension:"jpg"
	-- Get the contents of the passed picture
	set theImageRep to current application's NSBitmapImageRep's imageRepWithContentsOfFile:imagePath -- load the file 
	set theClip to current application's NSPasteboard's generalPasteboard()
	-- Extract its original size
	set oldHeight to theImageRep's pixelsHigh()
	set oldWidth to theImageRep's pixelsWide()
	if oldWidth > oldHeight then
		set theWidth to maxDim
		set theHeight to oldHeight * maxDim / oldWidth
	else
		set theHeight to maxDim
		set theWidth to oldWidth * maxDim / oldHeight
	end if
	set newRep to (current application's NSBitmapImageRep's alloc()'s initWithBitmapDataPlanes:(missing value) pixelsWide:theWidth pixelsHigh:theHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:yes isPlanar:false colorSpaceName:(current application's NSDeviceRGBColorSpace) bytesPerRow:0 bitsPerPixel:32)
	-- store the existing graphics context
	current application's NSGraphicsContext's saveGraphicsState()
	-- set graphics context to new context based on the new bitmapImageRep
	current application's NSGraphicsContext's setCurrentContext:(current application's NSGraphicsContext's graphicsContextWithBitmapImageRep:newRep)
	theImageRep's drawInRect:{origin:{x:0, y:0}, |size|:{width:theWidth, height:theHeight}} fromRect:(current application's NSZeroRect) operation:(current application's NSCompositeSourceOver) fraction:1.0 respectFlipped:false hints:(missing value)
	-- restore state
	current application's NSGraphicsContext's restoreGraphicsState()
	set compFactor to 0.8 -- starting compression value
	set compMax to 0.2 -- compression limit
	repeat
		set theData to newRep's representationUsingType:(current application's NSJPEGFileType) |properties|:{NSImageCompressionFactor:compFactor}
		if compFactor > compMax and theData's |length|() > maxBytes then
			-- too big, so try with more compression
			set compFactor to compFactor * 0.9
		else -- give up
			exit repeat
		end if
	end repeat
	theData's writeToFile:outPath atomically:true
end resizedJpegFromPath:maxHeightOrWidth:targetBytes:

Re-reading the original question, I saw that I missed the fact that every new files must be in a single folder.

If I have time available, I will edit my proposal.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 5 juin 2020
17:29:05

Here is a version storing the resized files in a single folder.

use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "Quartz"
use framework "QuartzCore"

--Yvan KOENIG (VALLAURIS, France) vendredi 5 juin 2020 18:58:33

on open draggeditems
	set p2d to path to desktop as string
	-- set draggeditems to {(p2d & "2018-10-07 Walter notes 1.pdf") as alias, (p2d & "Ymages:") as alias}
	
	tell application "System Events"
		set nbFolders to 0
		set nbFiles to 0
		repeat with anItem in draggeditems
			if (type identifier of anItem) is "public.folder" then
				set nbFolders to nbFolders + 1
			else
				set nbFiles to nbFiles + 1
			end if
		end repeat
		if (nbFolders ≠ 1) or ((nbFolders = 1) and nbFiles ≠ 0) then
			set storageName to "Resized_Folder"
			set storagePath to p2d & storageName
			if exists folder storagePath then
				-- rename it so we will not duplicate
				tell (current date) to set stamp to "_" & (((its year) * 10000 + (its month) * 100 + (its day)) as text) & "_" & text 2 thru -1 of ((1000000 + (its hours) * 10000 + (its minutes) * 100 + (its seconds)) as text)
				set name of folder storagePath to (storageName & stamp)
			end if
			make new folder at end of folder p2d with properties {name:storageName}
			-- Now we have an empty folder to work with
			repeat with anItem in draggeditems
				set typeID to type identifier of anItem
				if typeID is "public.folder" then
					-- duplicate every file to the new folder
					set allFiles to (path of every file of anItem whose visible is true)
					repeat with aFile in allFiles
						(my duplicateFileAt:aFile toFolder:storagePath)
					end repeat
				else
					-- here we have a flat file
					if typeID is in {"public.png", "public.jpeg", "public.tiff", "com.adobe.pdf"} then
						(my duplicateFileAt:anItem toFolder:storagePath)
					end if
				end if
			end repeat
			set allFiles to (path of every file of folder storagePath whose visible is true)
			repeat with aFile in allFiles
				(my treatFile:aFile)
				delete disk item aFile
			end repeat
		else
			-- here draggeditems contains a single file
			set allFiles to (path of every file of anItem whose visible is true)
			repeat with aFile in allFiles
				(my treatFile:aFile)
				--delete disk item aFile -- disabled because I'm not sure that it's a good idea in this case
			end repeat
		end if
	end tell
end open

#=====

on duplicateFileAt:sourcePath toFolder:destFolder
	set POSIXsource to current application's NSString's stringWithString:(POSIX path of sourcePath)
	set POSIXdest to current application's NSString's stringWithString:(POSIX path of destFolder)
	set fileName to POSIXsource's lastPathComponent()
	set theNewPath to POSIXdest's stringByAppendingPathComponent:(fileName)
	set fileManager to current application's NSFileManager's defaultManager()
	if fileManager's fileExistsAtPath:theNewPath then
		tell (current date) to set stamp to "_" & (((its year) * 10000 + (its month) * 100 + (its day)) as text) & "_" & text 2 thru -1 of ((1000000 + (its hours) * 10000 + (its minutes) * 100 + (its seconds)) as text)
		set theExtension to theNewPath's pathExtension()
		set thePathNoExt to theNewPath's stringByDeletingPathExtension()
		-- insert the stamp in the file name so it will not duplicate
		set theNewPath to (thePathNoExt's stringByAppendingString:stamp)'s stringByAppendingPathExtension:theExtension
	end if
	set {theResult, theError} to fileManager's copyItemAtPath:POSIXsource toPath:theNewPath |error|:(reference)
	if (theResult as boolean) is false then
		error (theError's localizedDescription() as text)
	end if
end duplicateFileAt:toFolder:

#=====

on treatFile:aFile
	tell application "System Events"
		set typeID to get type identifier of file aFile
	end tell
	if typeID is in {"public.png", "public.jpeg", "public.tiff"} then
		my treatPicture:(POSIX path of aFile) -- don't change that
	else if typeID is "com.adobe.pdf" then
		my treatPDF:aFile
	end if
end treatFile:

#=====

on treatPicture:POSIXPath
	-- insert the code dedicated to picture files here
	my resizedJpegFromPath:POSIXPath maxHeightOrWidth:800 targetBytes:100000
end treatPicture:

#=====

on treatPDF:aFile
	-- code from Takaaki Naganoya
	set aPOSIXpath to POSIX path of aFile
	set aURL to (current application's |NSURL|'s fileURLWithPath:aPOSIXpath)
	set aPDFdoc to current application's PDFDocument's alloc()'s initWithURL:aURL
	set pCount to aPDFdoc's pageCount()
	-- Split the PDF into pages exported as Tiff files
	repeat with i from 0 to (pCount - 1)
		set thisPage to (aPDFdoc's pageAtIndex:(i))
		set thisDoc to (current application's NSImage's alloc()'s initWithData:(thisPage's dataRepresentation()))
		if thisDoc = missing value then error "Error in getting imagerep from PDF in page:" & (i as string)
		set theData to thisDoc's TIFFRepresentation()
		set newRep to (current application's NSBitmapImageRep's imageRepWithData:theData)
		set targData to (newRep's representationUsingType:(current application's NSTIFFFileType) |properties|:{NSTIFFCompressionNone:1})
		set ztext to text -4 thru -1 of ((10000 + i + 1) as string)
		set outPath to (my addString:("_" & ztext) beforeExtensionIn:aPOSIXpath addingExtension:"tiff")
		(targData's writeToFile:outPath atomically:true) -- Export
		-- added instructions
		-- apply the wanted reduction
		(my treatPicture:outPath) -- outPath is a POSIX path
		set fileManager to (a reference to current application's NSFileManager's defaultManager())
		set tiffURL to (current application's |NSURL|'s fileURLWithPath:outPath)
		(fileManager's removeItemAtURL:tiffURL |error|:(missing value))
	end repeat
end treatPDF:

#=====

-- Add an extra string and the name extension at the end of the POSIX path
on addString:extraString beforeExtensionIn:aPath addingExtension:aExt
	set pathString to current application's NSString's stringWithString:aPath
	--set theExtension to pathString's pathExtension()
	set thePathNoExt to pathString's stringByDeletingPathExtension()
	set newPath to (thePathNoExt's stringByAppendingString:extraString)'s stringByAppendingPathExtension:aExt
	return newPath as string
end addString:beforeExtensionIn:addingExtension:

#=====

on resizedJpegFromPath:imagePath maxHeightOrWidth:maxDim targetBytes:maxBytes
	-- handler from Shane Stanley
	set imagePath to current application's NSString's stringWithString:imagePath
	set outPath to (imagePath's stringByDeletingPathExtension()'s stringByAppendingString:"-out")
	set outPath to outPath's stringByAppendingPathExtension:"jpg"
	-- Get the contents of the passed picture
	set theImageRep to current application's NSBitmapImageRep's imageRepWithContentsOfFile:imagePath -- load the file 
	set theClip to current application's NSPasteboard's generalPasteboard()
	-- Extract its original size
	set oldHeight to theImageRep's pixelsHigh()
	set oldWidth to theImageRep's pixelsWide()
	if oldWidth > oldHeight then
		set theWidth to maxDim
		set theHeight to oldHeight * maxDim / oldWidth
	else
		set theHeight to maxDim
		set theWidth to oldWidth * maxDim / oldHeight
	end if
	set newRep to (current application's NSBitmapImageRep's alloc()'s initWithBitmapDataPlanes:(missing value) pixelsWide:theWidth pixelsHigh:theHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:yes isPlanar:false colorSpaceName:(current application's NSDeviceRGBColorSpace) bytesPerRow:0 bitsPerPixel:32)
	-- store the existing graphics context
	current application's NSGraphicsContext's saveGraphicsState()
	-- set graphics context to new context based on the new bitmapImageRep
	current application's NSGraphicsContext's setCurrentContext:(current application's NSGraphicsContext's graphicsContextWithBitmapImageRep:newRep)
	theImageRep's drawInRect:{origin:{x:0, y:0}, |size|:{width:theWidth, height:theHeight}} fromRect:(current application's NSZeroRect) operation:(current application's NSCompositeSourceOver) fraction:1.0 respectFlipped:false hints:(missing value)
	-- restore state
	current application's NSGraphicsContext's restoreGraphicsState()
	set compFactor to 0.8 -- starting compression value
	set compMax to 0.2 -- compression limit
	repeat
		set theData to newRep's representationUsingType:(current application's NSJPEGFileType) |properties|:{NSImageCompressionFactor:compFactor}
		if compFactor > compMax and theData's |length|() > maxBytes then
			-- too big, so try with more compression
			set compFactor to compFactor * 0.9
		else -- give up
			exit repeat
		end if
	end repeat
	theData's writeToFile:outPath atomically:true
end resizedJpegFromPath:maxHeightOrWidth:targetBytes:

#=====

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 5 juin 2020 19:08:28

Added code deleting the original files.
At this time it’s disabled when we treat a single folder. I’m not sure that in this case, deleting is a good idea.

New version.
Would be fine to have some feedback from the OP.

use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "Quartz"
use framework "QuartzCore"

-- Yvan KOENIG (VALLAURIS, France) samedi 6 juin 2020 17:20:33

on run -- useful for tests
	set p2d to path to desktop as string
	set draggeditems to {(p2d & "2018-10-07 Walter notes 1.pdf") as alias, (p2d & "Ymages:") as alias, (p2d & "Ymages copie:") as alias}
	--set draggeditems to {(p2d & "Ymages:") as alias}
	open draggeditems
end run

on open draggeditems
	set p2d to path to desktop as string
	
	tell application "System Events"
		set nbFolders to 0
		set nbFiles to 0
		repeat with anItem in draggeditems
			if (type identifier of anItem) is "public.folder" then
				set nbFolders to nbFolders + 1
			else
				set nbFiles to nbFiles + 1
			end if
		end repeat
		if (nbFolders = 1) and nbFiles = 0 then
			set storageName to (name of anItem) & "_Resized"
			if exists disk item storageName of folder p2d then
				-- build a new name so we will not duplicate
				set storageName to storageName & my buildStamp()
			end if
		else
			set storageName to "Resized_Folder"
			if exists disk item storageName of folder p2d then
				-- build a new name so we will not duplicate
				set storageName to storageName & my buildStamp()
			end if
		end if
		set storagePath to p2d & storageName -- is an Hfs path
		make new folder at end of folder p2d with properties {name:storageName}
		-- Now we have an empty folder to work with
		repeat with anItem in draggeditems
			set typeID to type identifier of anItem
			if typeID is "public.folder" then -- It's a folder, duplicate every embedded file into the storage folder
				set allFiles to (path of every file of anItem whose visible is true)
				repeat with aFile in allFiles
					(my duplicateFileAt:aFile toFolder:storagePath) -- here, aFile is an alias and storagePath is an Hfs path
				end repeat
			else -- here we have a file, duplicate it into the storage folder
				if typeID is in {"public.png", "public.jpeg", "public.tiff", "com.adobe.pdf"} then
					(my duplicateFileAt:anItem toFolder:storagePath) -- here, anItem is an alias and storagePath is an Hfs path
				end if
			end if
		end repeat
		-- we would have build a list before
		-- but I choose this scheme because adding items to an Applescript list is slow
		set allFiles to (path of every file of folder storagePath whose visible is true)
		repeat with aFile in allFiles
			set aPOSIXPath to (my makePOSIXPath:aFile)
			set typeID to get type identifier of file aFile
			if typeID is in {"public.png", "public.jpeg", "public.tiff"} then
				(my treatPicture:aPOSIXPath) -- don't change that
			else if typeID is "com.adobe.pdf" then
				(my treatPDF:aPOSIXPath)
			end if
			delete disk item aFile
		end repeat
	end tell -- System Events
end open

#=====

on treatPicture:aPOSIXPath
	my resizedJpegFromPath:aPOSIXPath maxHeightOrWidth:800 targetBytes:100000 -- Edit to fit your needs
end treatPicture:

#=====

on buildStamp()
	tell (current date) to return "_" & (((its year) * 10000 + (its month) * 100 + (its day)) as string) & "_" & text 2 thru -1 of ((1000000 + (its hours) * 10000 + (its minutes) * 100 + (its seconds)) as string)
end buildStamp

#=====

on makePOSIXPath:aFile
	return POSIX path of aFile
end makePOSIXPath:

#=====

on duplicateFileAt:sourcePath toFolder:destFolder -- here, sourcePath is an alias and destFolder is an Hfs path
	set POSIXsource to current application's NSString's stringWithString:(POSIX path of sourcePath)
	set POSIXdest to current application's NSString's stringWithString:(POSIX path of destFolder)
	set theNewPath to POSIXdest's stringByAppendingPathComponent:(POSIXsource's lastPathComponent())
	set fileManager to current application's NSFileManager's defaultManager()
	if fileManager's fileExistsAtPath:theNewPath then
		set thePathNoExt to theNewPath's stringByDeletingPathExtension()
		set stamp to my buildStamp()
		set theExtension to POSIXsource's pathExtension()
		-- insert the stamp in the file name so it will not duplicate
		set theNewPath to (thePathNoExt's stringByAppendingString:stamp)'s stringByAppendingPathExtension:theExtension
	end if
	set {theResult, theError} to fileManager's copyItemAtPath:POSIXsource toPath:theNewPath |error|:(reference)
	if (theResult as boolean) is false then
		error (theError's localizedDescription() as string)
	end if
end duplicateFileAt:toFolder:

#=====

on treatPDF:aPOSIXPath
	-- code from Takaaki Naganoya
	-- set aPOSIXpath to POSIX path of aFile
	set aURL to (current application's |NSURL|'s fileURLWithPath:aPOSIXPath)
	set aPDFdoc to current application's PDFDocument's alloc()'s initWithURL:aURL
	set pCount to aPDFdoc's pageCount()
	-- Split the PDF into pages exported as Tiff files
	repeat with i from 0 to (pCount - 1)
		set thisPage to (aPDFdoc's pageAtIndex:(i))
		set thisDoc to (current application's NSImage's alloc()'s initWithData:(thisPage's dataRepresentation()))
		if thisDoc = missing value then error "Error in getting imagerep from PDF in page:" & (i as string)
		set theData to thisDoc's TIFFRepresentation()
		set newRep to (current application's NSBitmapImageRep's imageRepWithData:theData)
		set targData to (newRep's representationUsingType:(current application's NSTIFFFileType) |properties|:{NSTIFFCompressionNone:1})
		set ztext to text -4 thru -1 of ((10001 + i) as string)
		set outPath to (my addString:("_" & ztext) beforeExtensionIn:aPOSIXPath addingExtension:"tiff")
		(targData's writeToFile:outPath atomically:true) -- Export
		-- added instructions
		-- apply the wanted reduction
		(my treatPicture:outPath) -- outPath is a POSIX path
		set fileManager to (a reference to current application's NSFileManager's defaultManager())
		set tiffURL to (current application's |NSURL|'s fileURLWithPath:outPath)
		(fileManager's removeItemAtURL:tiffURL |error|:(missing value))
	end repeat
end treatPDF:

#=====

-- Add an extra string and the name extension at the end of the POSIX path
on addString:extraString beforeExtensionIn:aPath addingExtension:aExt
	set pathString to current application's NSString's stringWithString:aPath
	--set theExtension to pathString's pathExtension()
	set thePathNoExt to pathString's stringByDeletingPathExtension()
	set newPath to (thePathNoExt's stringByAppendingString:extraString)'s stringByAppendingPathExtension:aExt
	return newPath as string
end addString:beforeExtensionIn:addingExtension:

#=====

on resizedJpegFromPath:imagePath maxHeightOrWidth:maxDim targetBytes:maxBytes
	-- handler from Shane Stanley
	set imagePath to current application's NSString's stringWithString:imagePath
	set outPath to (imagePath's stringByDeletingPathExtension()'s stringByAppendingString:"-out")
	set outPath to outPath's stringByAppendingPathExtension:"jpg"
	-- Get the contents of the passed picture
	set theImageRep to current application's NSBitmapImageRep's imageRepWithContentsOfFile:imagePath -- load the file 
	set theClip to current application's NSPasteboard's generalPasteboard()
	-- Extract its original size
	set oldHeight to theImageRep's pixelsHigh()
	set oldWidth to theImageRep's pixelsWide()
	if oldWidth > oldHeight then
		set theWidth to maxDim
		set theHeight to oldHeight * maxDim / oldWidth
	else
		set theHeight to maxDim
		set theWidth to oldWidth * maxDim / oldHeight
	end if
	set newRep to (current application's NSBitmapImageRep's alloc()'s initWithBitmapDataPlanes:(missing value) pixelsWide:theWidth pixelsHigh:theHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:yes isPlanar:false colorSpaceName:(current application's NSDeviceRGBColorSpace) bytesPerRow:0 bitsPerPixel:32)
	-- store the existing graphics context
	current application's NSGraphicsContext's saveGraphicsState()
	-- set graphics context to new context based on the new bitmapImageRep
	current application's NSGraphicsContext's setCurrentContext:(current application's NSGraphicsContext's graphicsContextWithBitmapImageRep:newRep)
	theImageRep's drawInRect:{origin:{x:0, y:0}, |size|:{width:theWidth, height:theHeight}} fromRect:(current application's NSZeroRect) operation:(current application's NSCompositeSourceOver) fraction:1.0 respectFlipped:false hints:(missing value)
	-- restore state
	current application's NSGraphicsContext's restoreGraphicsState()
	set compFactor to 0.8 -- starting compression value
	set compMax to 0.2 -- compression limit
	repeat
		set theData to newRep's representationUsingType:(current application's NSJPEGFileType) |properties|:{NSImageCompressionFactor:compFactor}
		if compFactor > compMax and theData's |length|() > maxBytes then
			-- too big, so try with more compression
			set compFactor to compFactor * 0.9
		else -- give up
			exit repeat
		end if
	end repeat
	theData's writeToFile:outPath atomically:true
end resizedJpegFromPath:maxHeightOrWidth:targetBytes:

#=====

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) lundi 8 juin 2020 17:55:56

Yvan. The OP seems to be MIA. However, FWIW, I tested the new version of your script and it works great.

I apologizes, I don’t understand the sentence : The OP seems to be MIA.

Is it an abbreviation for “missed in action” ?

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 9 juin 2020 19:01:10

Yvan. MIA is slang and means a person hasn’t been seen in some period of time. English isn’t your native language and I shouldn’t have used this term. Sorry.

Don’t worry. Thank you for the explanation.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 9 juin 2020 23:57:32

Hello all,

My apologies for not replying earlier!

I was off a few days due to family matters. Unfortunately a cousin passed away.

But I don’t want to make you feel sorry. That’s life…

Well I am so impressed with your dedication, help and all your support to all my silly questions. Thank you so much guys, and especially to you Yvan!

Until now I was using those two scripts (sorry for omitting pasting them in my first post):

PDF to JPG
https://macscripter.net/viewtopic.php?id=25350

Image to jpg
https://macscripter.net/viewtopic.php?id=31836

I will try the new script tomorrow and I will let you know ASAP.

PS: I was not aware of MIA as well!

Hello again,

So today I tried thoroughly the latest version (post #6) of Yvan’s script and here are the results:

i.] It took me a while to realise that I had to create the folder “Ymages” on my desktop and copy the desired items inside it. And also I had to mark as comment the multiple set command (Walter notes pdf, Ymages & Ymages copie) and I enabled the next line “set draggeditems to {(p2d & “Ymages:”) as alias}” command only.

ii.] I really love the addition of the timestamp to the end of the filenames and the recognition of the duplicates! So amazing! Thank you!

iii.] If the “Ymages” folder however includes both files and folders, only the files are treated. The folders are ignored.

iv.] Moreover, if the folder “Ymages” is empty or includes only folders and the script is run, only a new folder is created on Desktop with name “Ymages_Resized” without contents. Can we have the option when the script is run to ask for the files or folders?

v.] The PDF files that I have are mostly scanned pages from books and after the double transformation (to TIFF and then to JPG) they become so blurry. Unfortunately they are illegible. Is there any way to adjust the compression level? What I would actually would want to get is exactly like the produced JPG file from the Preview app when selecting the menu option: File - Export and adjust to the one before minimum quality. Legible, keeping the resolution but reducing only the file size.

The run handler was introduced only for tests because it allow us to get a log history.
The different instructions embedded were used to test the different cases of draggeditems.
There were needed because there is no standard tool allowing us to choose a mix of files and folders as required by your original message.
The normal use is to save the script as an application then drag and drop objects on it.
Doing that you may treat several folders as well as several files or even several files and folders.
This behavior match what was described in your original question.

Taking care of possible duplicates is a basic feature.

I repeat that the run handler was introduced only to allow us to have a log history during tests.
You may remove it if you wish.
The normal entry point is the “on open” one used when we drag and drop items on the script-application icon.
In such case you will not have an useless xx_resized folder , except if you drag and drop empty folder(s)

Your original message stated :

With no more infos, I assumed that the same requirement applied to the jpegs issued from PDFs
If I assumed wrongly, it’s easy to behave differently.
Edit the handler dedicated to PDFs as :

on treatPDF:aPOSIXPath
   -- code from Takaaki Naganoya
   -- set aPOSIXpath to POSIX path of aFile
   set aURL to (current application's |NSURL|'s fileURLWithPath:aPOSIXPath)
   set aPDFdoc to current application's PDFDocument's alloc()'s initWithURL:aURL
   set pCount to aPDFdoc's pageCount()
   -- Split the PDF into pages exported as Tiff files
   repeat with i from 0 to (pCount - 1)
       set thisPage to (aPDFdoc's pageAtIndex:(i))
       set thisDoc to (current application's NSImage's alloc()'s initWithData:(thisPage's dataRepresentation()))
       if thisDoc = missing value then error "Error in getting imagerep from PDF in page:" & (i as string)
       set theData to thisDoc's TIFFRepresentation()
       set newRep to (current application's NSBitmapImageRep's imageRepWithData:theData)
       set targData to (newRep's representationUsingType:(current application's NSTIFFFileType) |properties|:{NSTIFFCompressionNone:1})
       set ztext to text -4 thru -1 of ((10001 + i) as string)
       set outPath to (my addString:("_" & ztext) beforeExtensionIn:aPOSIXPath addingExtension:"tiff")
       (targData's writeToFile:outPath atomically:true) -- Export
       -- added instructions
       -- apply the wanted reduction
       -- (my treatPicture:outPath) -- outPath is a POSIX path
       -- set fileManager to (a reference to current application's NSFileManager's defaultManager())
       -- set tiffURL to (current application's |NSURL|'s fileURLWithPath:outPath)
       -- (fileManager's removeItemAtURL:tiffURL |error|:(missing value))
   end repeat
end treatPDF:

You may also remove the instructions which I disabled. These ones were added uniquely to match the lower resolution request. I assumed that the original comment (-- added instructions) was clear enough.

I must also add that you are free to adjust the way pictures are treated as stated in the script:

on treatPicture:aPOSIXPath
   my resizedJpegFromPath:aPOSIXPath maxHeightOrWidth:800 targetBytes:100000 -- Edit to fit your needs
end treatPicture:

You may change the value of the parameter maxHeightOrWidth and/or the value of the parameter targetBytes. if you set it to 100000 * 100 you will have a better aspect.

Remember that my first proposal giving only a skeleton got no feedback so I tried to guess what was really wanted.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 12 juin 2020 04:11:16

With what I posted in message #13, the PDFs would be splitted into tiff files.
It would be easy to split them into jpg but, before editing the code, I wish to learn how they must be splitted.
Must the pages have the size of original ones or must they be standardized to a given greater dimension as we do for jpegs, at this time it’s 800 pixels?
Or must they be exported with the default resolution used by the export handlers which is 72 pixels/inch ?
Or must they be exported with a resolution of 200 pixels/inch which is the default value used by Preview ?

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 12 juin 2020 19:08:20

Hello Yvan,

Thank you so much for your interest and your help! I really appreciate it!

Well as I mentioned previously the PDFs are most of the times scanned A4 pages and what I do is with Preview app to select File → Export as JPG and set there the Quality a little bit above the least near the middle. I don’t change the resolution at all, I leave it as it is to its default value (usually it is 75 pixels/inch but in a few files it is more).

For the image files I do the same, although there is no option to select the resolution there at all. What I have noticed is that the images retain their dimensions. For example a JPG file of 4032 × 3024 pixels from 5 MB becomes 338 KB, it has the same dimensions and DPI 72 pixels/inch.

Can we achieve the same with AppleScript?

As for the script I created with Automator both a droplet and a System Service. Great implementation! However, although the input is different folders or files, it transforms always the files that exist in “Ymages” folder. Am I doing something wrong or are they not copied properly to that folder?

And also if the “Ymages” folder includes a PDF and a folder with one image file inside it, by dragging them both to the droplet app, only the PDF is transformed.

I repeat : you don’t have to use the folder Ymages.

The run handler was only created for tests.

The script is supposed to be saved as an application upon which you drag and drop items to treat.

Your original message stated :

I want to create a script that takes as input folders/files and outputs them as jpg images in a different folder and at a lower resolution.

So my script was written to do that but, as you never described what is the wanted lower resolution I delivered a script allowing you to rule it.
It’s why an the dedicated instruction was carefully commented :
my resizedJpegFromPath:aPOSIXPath maxHeightOrWidth:800 targetBytes:100000 – Edit to fit your needs

In a recent message I told you that you may change the parameter maxHeightOrWidth and/or the parameter targetBytes. I even suggested that you may set this one to 100000 * 100 to get better quality.

For the PDFs if I asked it’s because, as I already wrote, exporting a PDF as Jpeg, default to 200 pixels/inch. As I never used this function I didn’t knew that it may default to an other resolution.

I’m tired to waste time for a user which doesn’t take care of what is carefully answered.

Below is a version allowing you to set the parameter according to your needs and now, for me : GAME OVER.

use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "Quartz"
use framework "QuartzCore"

-- Yvan KOENIG (VALLAURIS, France) 13 juin 2020 12:20:33

(*
on run -- useful ONLY for tests, I repeat : useful ONLY for tests
	set p2d to path to desktop as string
	set draggeditems to {(p2d & "2018-10-07 Walter notes 1.pdf") as alias, (p2d & "Ymages:") as alias, (p2d & "Ymages copie:") as alias}
	--set draggeditems to {(p2d & "Ymages:") as alias}
	open draggeditems
end run
*)
on open draggeditems
	set p2d to path to desktop as string
	
	tell application "System Events"
		set nbFolders to 0
		set nbFiles to 0
		repeat with anItem in draggeditems
			if (type identifier of anItem) is "public.folder" then
				set nbFolders to nbFolders + 1
			else
				set nbFiles to nbFiles + 1
			end if
		end repeat
		if (nbFolders = 1) and nbFiles = 0 then
			set storageName to (name of anItem) & "_Resized"
			if exists disk item storageName of folder p2d then
				-- build a new name so we will not duplicate
				set storageName to storageName & my buildStamp()
			end if
		else
			set storageName to "Resized_Folder"
			if exists disk item storageName of folder p2d then
				-- build a new name so we will not duplicate
				set storageName to storageName & my buildStamp()
			end if
		end if
		set storagePath to p2d & storageName -- is an Hfs path
		make new folder at end of folder p2d with properties {name:storageName}
		-- Now we have an empty folder to work with
		repeat with anItem in draggeditems
			set typeID to type identifier of anItem
			if typeID is "public.folder" then -- It's a folder, duplicate every embedded file into the storage folder
				set allFiles to (path of every file of anItem whose visible is true)
				repeat with aFile in allFiles
					(my duplicateFileAt:aFile toFolder:storagePath) -- here, aFile is an alias and storagePath is an Hfs path
				end repeat
			else -- here we have a file, duplicate it into the storage folder
				if typeID is in {"public.png", "public.jpeg", "public.tiff", "com.adobe.pdf"} then
					(my duplicateFileAt:anItem toFolder:storagePath) -- here, anItem is an alias and storagePath is an Hfs path
				end if
			end if
		end repeat
		-- we would have build a list before
		-- but I choose this scheme because adding items to an Applescript list is slow
		set allFiles to (path of every file of folder storagePath whose visible is true)
		repeat with aFile in allFiles
			set aPOSIXPath to (my makePOSIXPath:aFile)
			set typeID to get type identifier of file aFile
			if typeID is in {"public.png", "public.jpeg", "public.tiff"} then
				--(my treatPicture:aPOSIXPath) -- don't change that
				(my resizedJpegFromPath:aPOSIXPath maxHeightOrWidth:800 targetBytes:100000 * 100) -- Edit to fit your needs
			else if typeID is "com.adobe.pdf" then
				(my treatPDF:aPOSIXPath)
			end if
			delete disk item aFile
		end repeat
	end tell -- System Events
end open

#=====

-- handler used for picture files

on resizedJpegFromPath:imagePath maxHeightOrWidth:maxDim targetBytes:maxBytes
	-- handler from Shane Stanley
	set imagePath to current application's NSString's stringWithString:imagePath
	set outPath to (imagePath's stringByDeletingPathExtension()'s stringByAppendingString:"-out")
	set outPath to outPath's stringByAppendingPathExtension:"jpg"
	-- Get the contents of the passed picture
	set theImageRep to current application's NSBitmapImageRep's imageRepWithContentsOfFile:imagePath -- load the file 
	set theClip to current application's NSPasteboard's generalPasteboard()
	-- Extract its original size
	set oldHeight to theImageRep's pixelsHigh()
	set oldWidth to theImageRep's pixelsWide()
	if oldWidth > oldHeight then
		set theWidth to maxDim
		set theHeight to oldHeight * maxDim / oldWidth
	else
		set theHeight to maxDim
		set theWidth to oldWidth * maxDim / oldHeight
	end if
	set newRep to (current application's NSBitmapImageRep's alloc()'s initWithBitmapDataPlanes:(missing value) pixelsWide:theWidth pixelsHigh:theHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:yes isPlanar:false colorSpaceName:(current application's NSDeviceRGBColorSpace) bytesPerRow:0 bitsPerPixel:32)
	-- store the existing graphics context
	current application's NSGraphicsContext's saveGraphicsState()
	-- set graphics context to new context based on the new bitmapImageRep
	current application's NSGraphicsContext's setCurrentContext:(current application's NSGraphicsContext's graphicsContextWithBitmapImageRep:newRep)
	theImageRep's drawInRect:{origin:{x:0, y:0}, |size|:{width:theWidth, height:theHeight}} fromRect:(current application's NSZeroRect) operation:(current application's NSCompositeSourceOver) fraction:1.0 respectFlipped:false hints:(missing value)
	-- restore state
	current application's NSGraphicsContext's restoreGraphicsState()
	set compFactor to 0.8 -- starting compression value
	set compMax to 0.2 -- compression limit
	repeat
		set theData to newRep's representationUsingType:(current application's NSJPEGFileType) |properties|:{NSImageCompressionFactor:compFactor}
		if compFactor > compMax and theData's |length|() > maxBytes then
			-- too big, so try with more compression
			set compFactor to compFactor * 0.9
		else -- give up
			exit repeat
		end if
	end repeat
	theData's writeToFile:outPath atomically:true
end resizedJpegFromPath:maxHeightOrWidth:targetBytes:

#=====

on buildStamp()
	tell (current date) to return "_" & (((its year) * 10000 + (its month) * 100 + (its day)) as string) & "_" & text 2 thru -1 of ((1000000 + (its hours) * 10000 + (its minutes) * 100 + (its seconds)) as string)
end buildStamp

#=====

on makePOSIXPath:aFile
	return POSIX path of aFile
end makePOSIXPath:

#=====

on duplicateFileAt:sourcePath toFolder:destFolder -- here, sourcePath is an alias and destFolder is an Hfs path
	set POSIXsource to current application's NSString's stringWithString:(POSIX path of sourcePath)
	set POSIXdest to current application's NSString's stringWithString:(POSIX path of destFolder)
	set theNewPath to POSIXdest's stringByAppendingPathComponent:(POSIXsource's lastPathComponent())
	set fileManager to current application's NSFileManager's defaultManager()
	if fileManager's fileExistsAtPath:theNewPath then
		set thePathNoExt to theNewPath's stringByDeletingPathExtension()
		set stamp to my buildStamp()
		set theExtension to POSIXsource's pathExtension()
		-- insert the stamp in the file name so it will not duplicate
		set theNewPath to (thePathNoExt's stringByAppendingString:stamp)'s stringByAppendingPathExtension:theExtension
	end if
	set {theResult, theError} to fileManager's copyItemAtPath:POSIXsource toPath:theNewPath |error|:(reference)
	if (theResult as boolean) is false then
		error (theError's localizedDescription() as string)
	end if
end duplicateFileAt:toFolder:

#=====

on treatPDF:aPOSIXPath
	-- code from Takaaki Naganoya
	-- set aPOSIXpath to POSIX path of aFile
	set aURL to (current application's |NSURL|'s fileURLWithPath:aPOSIXPath)
	set aPDFdoc to current application's PDFDocument's alloc()'s initWithURL:aURL
	set pCount to aPDFdoc's pageCount()
	-- Split the PDF into pages exported as Tiff files
	repeat with i from 0 to (pCount - 1)
		set thisPage to (aPDFdoc's pageAtIndex:(i))
		set thisDoc to (current application's NSImage's alloc()'s initWithData:(thisPage's dataRepresentation()))
		if thisDoc = missing value then error "Error in getting imagerep from PDF in page:" & (i as string)
		set theData to thisDoc's TIFFRepresentation()
		set newRep to (current application's NSBitmapImageRep's imageRepWithData:theData)
		set targData to (newRep's representationUsingType:(current application's NSTIFFFileType) |properties|:{NSTIFFCompressionNone:1})
		set ztext to text -4 thru -1 of ((10001 + i) as string)
		set outPath to (my addString:("_" & ztext) beforeExtensionIn:aPOSIXPath addingExtension:"tiff")
		(targData's writeToFile:outPath atomically:true) -- Export
		-- added instructions
		-- apply the wanted reduction
		(my resizedJpegFromPath:outPath newRes:200) -- Edit the newRes to fit your needs
		set fileManager to (a reference to current application's NSFileManager's defaultManager())
		set tiffURL to (current application's |NSURL|'s fileURLWithPath:outPath)
		(fileManager's removeItemAtURL:tiffURL |error|:(missing value))
	end repeat
end treatPDF:

#=====
-- Handler used for splitted PDFs
on resizedJpegFromPath:imagePath newRes:theRes
	-- handler from Shane Stanley, edited by Yvan Koenig
	set imagePath to current application's NSString's stringWithString:imagePath
	set outPath to (imagePath's stringByDeletingPathExtension()'s stringByAppendingString:"-out")
	set outPath to outPath's stringByAppendingPathExtension:"jpg"
	-- Get the contents of the passed picture
	set theImageRep to current application's NSBitmapImageRep's imageRepWithContentsOfFile:imagePath -- load the file 
	set theClip to current application's NSPasteboard's generalPasteboard()
	-- Extract its original size
	set oldHeight to theImageRep's pixelsHigh()
	set oldWidth to theImageRep's pixelsWide()
	-- Calculate new size according to, wanted resolution
	set theWidth to oldWidth * theRes / 72
	set theHeight to oldHeight * theRes / 72
	set newRep to (current application's NSBitmapImageRep's alloc()'s initWithBitmapDataPlanes:(missing value) pixelsWide:theWidth pixelsHigh:theHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:yes isPlanar:false colorSpaceName:(current application's NSDeviceRGBColorSpace) bytesPerRow:0 bitsPerPixel:32)
	-- store the existing graphics context
	current application's NSGraphicsContext's saveGraphicsState()
	-- set graphics context to new context based on the new bitmapImageRep
	current application's NSGraphicsContext's setCurrentContext:(current application's NSGraphicsContext's graphicsContextWithBitmapImageRep:newRep)
	theImageRep's drawInRect:{origin:{x:0, y:0}, |size|:{width:theWidth, height:theHeight}} fromRect:(current application's NSZeroRect) operation:(current application's NSCompositeSourceOver) fraction:1.0 respectFlipped:false hints:(missing value)
	-- restore state
	current application's NSGraphicsContext's restoreGraphicsState()
	set compFactor to 1 -- Edit to fit your needs. Allowed range 0 < compFactor ≤ 1
	set theData to newRep's representationUsingType:(current application's NSJPEGFileType) |properties|:{NSImageCompressionFactor:compFactor}
	theData's writeToFile:outPath atomically:true
end resizedJpegFromPath:newRes:

#=====

-- Add an extra string and the name extension at the end of the POSIX path
on addString:extraString beforeExtensionIn:aPath addingExtension:aExt
	set pathString to current application's NSString's stringWithString:aPath
	--set theExtension to pathString's pathExtension()
	set thePathNoExt to pathString's stringByDeletingPathExtension()
	set newPath to (thePathNoExt's stringByAppendingString:extraString)'s stringByAppendingPathExtension:aExt
	return newPath as string
end addString:beforeExtensionIn:addingExtension:

#=====

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 13 juin 2020 12:42:06

My apologies that I didn’t understand!

When you mentioned Application I thought to create it in Automator, that’s why it was not working for me.

Sorry, I didn’t want to piss you off. It is totally my fault (and the language barrier) that I did not describe it properly from the beginning and of course did not comprehend your posts as I should.

By reading again carefully all your notes I would like to let you know that it works perfectly!

Man you are amazing! With this you helped me so much to save a lot of time!

P.S.: I have a last question that I would like to ask. If you feel however that you do not want to reply, I totally understand. So, I would like to ask if it it feasible to retain the image/PDF dimensions as Preview app does while exporting to JPG by reducing only the file size? I experimented with different values, but the exports from the script comparing to the Preview app are blurry.

Thank you again!

The version in message #16 doesn’t reduce the size of the PDFs. It attempts to apply the resolution used by Preview. I defined it to 200 because it’s the one which is used by Preview on my machine.
I don’t know which tools are used by Preview. Here, I am unable to really change the resolution itself. I apply a scale factor to give us the dimensions in pixels which is returned by Preview. This is why I apply the scale 200/72 because 72 is the standard resolution applied by the used tools.
If somebody know an other way to do the job I am interested to learn it.
I wrote an alternate handler using CoreImage and “CILanczosScaleTransform” but, if the result is slightly different, this difference seems to be really small.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 16 juin 2020 01:53:31

Yes, you are right the PDFs are fine. But can the same be done for the images?

For the files I already answered at least twice.Change the requested targetBytes value.
The given code was designed to allow us to get small files so it reduce the quality level until the resulting file match the requested targetBytes value. If you define the parameter to a greater value, the quality will be greater. I don’t know the files which you treat. Maybe the parameter must be set to 100000 *100 or to 100000 *1000 or to 100000 * 10000.
I gave you a script which may be customized to fit your real needs. It’s your duty to adjust the available parameters to fit your needs.
Maybe the maxHeightOrWidth parameter is greater than the dimension of the original. If its the case, the resulting file will be blurred.

If I have free time available, I will post two new handlers. One remove the piece of code supposed to rule the parameter targetBytes.
An other one will drop the two parameters. The jpegs will not be resized but I don’t see the interest to treat them in this case.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 16 juin 2020 05:04:33