Export images embedded in PDF

I have a bunch of PDFs containing scans, one image per page.
They have been scanned in RVB, A4 format, 300ppi.
I want to export each image in JPG, grayscale, 150ppi.

Here’s all I’m able to do:

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

set folderURL to current application's NSURL's fileURLWithPath:"someFolder"
set pdfURL to current application's NSURL's fileURLWithPath:"somePDF"

set theDoc to current application's PDFDocument's alloc()'s initWithURL:pdfURL
set pageCount to (theDoc's pageCount())

repeat with iPage from 1 to pageCount
	set thePage to (theDoc's pageAtIndex:(iPage - 1))
	set theData to (thePage's dataRepresentation())
	set theImage to (current application's NSImage's alloc()'s initWithData:theData)
	set theTiff to theImage's TIFFRepresentation()
	set theBitmap to (current application's NSBitmapImageRep's imageRepWithData:theTiff)
	set theJpeg to (theBitmap's representationUsingType:(current application's NSJPEGFileType) |properties|:{NSImageCompressionFactor:0.6, NSImageProgressive:false})
	set destURL to (folderURL's URLByAppendingPathComponent:("test" & iPage & ".jpg"))
	set theResult to (theJpeg's writeToURL:destURL atomically:true)
end repeat

:wink:

It can probably be done more efficiently than this, but it should get you started:

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

set folderURL to current application's |NSURL|'s fileURLWithPath:"/Users/shane/Desktop/"
set pdfURL to current application's |NSURL|'s fileURLWithPath:"/Users/shane/Desktop/Test.pdf"

set theDoc to current application's PDFDocument's alloc()'s initWithURL:pdfURL
set pageCount to (theDoc's pageCount())

repeat with iPage from 1 to pageCount
	set thePage to (theDoc's pageAtIndex:(iPage - 1))
	set theData to (thePage's dataRepresentation())
	set pdfImageRep to (current application's NSPDFImageRep's imageRepWithData:theData)
	set theBounds to pdfImageRep's |bounds|()
	set aWidth to (current application's NSWidth(theBounds)) / 72 * 150
	set aHeight to (current application's NSHeight(theBounds)) / 72 * 150
	
	-- resize by creating new bitmap and drawing into it
	set newBitmap to (current application's NSBitmapImageRep's alloc()'s initWithBitmapDataPlanes:(missing value) pixelsWide:aWidth pixelsHigh:aHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:true isPlanar:false colorSpaceName:(current application's NSCalibratedRGBColorSpace) bytesPerRow:0 bitsPerPixel:0)
	current application's NSGraphicsContext's saveGraphicsState()
	set theContext to (current application's NSGraphicsContext's graphicsContextWithBitmapImageRep:newBitmap)
	(current application's NSGraphicsContext's setCurrentContext:theContext)
	-- set the active color to white
	current application's NSColor's whiteColor()'s |set|()
	-- fill the bitmapImageRep with white
	current application's NSRectFill({origin:{x:0, y:0}, |size|:{width:aWidth, height:aHeight}})
	(theContext's setShouldAntialias:true)
	(theContext's setImageInterpolation:(current application's NSImageInterpolationDefault))
	(pdfImageRep's drawInRect:(current application's NSMakeRect(0, 0, aWidth, aHeight)) fromRect:(current application's NSZeroRect) operation:(current application's NSCompositeSourceOver) fraction:(1.0) respectFlipped:true hints:(missing value))
	current application's NSGraphicsContext's restoreGraphicsState()
	
	-- convert to greyscale
	set targetSpace to current application's NSColorSpace's deviceGrayColorSpace()
	set newBitmap to (newBitmap's bitmapImageRepByConvertingToColorSpace:targetSpace renderingIntent:(current application's NSColorRenderingIntentDefault))
	-- save
	set theJpeg to (newBitmap's representationUsingType:(current application's NSJPEGFileType) |properties|:{NSImageCompressionFactor:0.6, NSImageProgressive:false})
	set destURL to (folderURL's URLByAppendingPathComponent:("test" & iPage & ".jpg"))
	set theResult to (theJpeg's writeToURL:destURL atomically:true)
end repeat

Thank you, Shane.

After 2 minor changes (rendering to saturation and interpolation to hight) the script is perfect.
And so fast!

Is it possible to unembed the color profile?
And to sharpen up the image?
:wink:

You could try setting the colorSpace property, or perhaps using genericGrayColorSpace.

I suspect you would need to use Core Image. Other than that, you can try changing interpolation.

You mean something like that?

	set theJpeg to (newBitmap's representationUsingType:(current application's NSJPEGFileType) |properties|:{NSImageCompressionFactor:0.6, NSImageProgressive:false, colorSpace:missing value})

I had a look at Apple’s Reference page: too hard for me!
Anyway, I can do without it.

Thanks again!
:wink:

Not exactly, although you could try that approach, setting a value of missing value for NSImageColorSyncProfileData.

I was thinking of setting the colorSpace property of the NSBitmapImageRep.

Try inserting something like this before the jpeg part:

	set theCIImage to (current application's CIImage's alloc()'s initWithBitmapImageRep:newBitmap)
	set theCIFilter to (current application's CIFilter's filterWithName:"CIUnsharpMask" withInputParameters:{inputRadius:5.0, inputIntensity:0.9}) -- your values of choice
	(theCIFilter's setValue:theCIImage forKey:(current application's kCIInputImageKey))
	set theCIImage to (theCIFilter's valueForKey:(current application's kCIOutputImageKey))
	set newBitmap to (current application's NSBitmapImageRep's alloc()'s initWithCIImage:theCIImage)

In fact, you could probably do the scaling and conversion to greyscale with CIFilters too:

	set theCIImage to (current application's CIImage's alloc()'s initWithBitmapImageRep:newBitmap)
	set theCIFilter to (current application's CIFilter's filterWithName:"CIUnsharpMask" withInputParameters:{inputRadius:5.0, inputIntensity:0.9, inputImage:theCIImage}) -- your values of choice
	set theCIImage to (theCIFilter's valueForKey:(current application's kCIOutputImageKey))
	set theCIFilter to (current application's CIFilter's filterWithName:"CIPhotoEffectMono" withInputParameters:{inputImage:theCIImage})
	set theCIImage to (theCIFilter's valueForKey:(current application's kCIOutputImageKey))
	set newBitmap to (current application's NSBitmapImageRep's alloc()'s initWithCIImage:theCIImage)

Awesome!
I’m so grateful!