You are not logged in.
As mentioned here before the quartzfilter tool hidden inside of Mac OS X can be of real help when it comes to PDF manipulation on the command line. It allows you to apply custom quartz filters created with the ColorSync Utility to your PDF or image files. Those quartz filters in turn can perform many useful tasks. They can reduce the file size by image compression, convert color to gray scale, increase lightness or add comments, just to mention a few possibilities.
The quartzfilter tool uses the following syntax:
/System/Library/Printers/Libraries/./quartzfilter inputfile filterpath outputfile
Do not use the same file path for the in- and output file, as this will result in an error. They must be different.
The example below would apply the «Sepia Tone» quartz filter located in /System/Library/Filters/ to a PDF file named «qfin.pdf» on my desktop and save the result in a PDF file named «qfout.pdf». The path to the quartz filter is quoted as it contains spaces:
/System/Library/Printers/Libraries/./quartzfilter /Users/martin/Desktop/qfin.pdf '/System/Library/Filters/Sepia Tone.qfilter' /Users/martin/Desktop/qfout.pdf
Preinstalled quartz filters are located in /System/Library/Filters/, but your own custom quartz filters created with the ColorSync Utility are saved in your library folder under /Users/yourname/Library/Filters/.
So the quartzfilter tool let's you apply preinstalled and your own custom quartz filters. Fine.
But what if I want to write a neat little AppleScript droplet for the Mac community, that would utilize the quartzfilter tool to batch-shrink countless dropped PDF or image files by a user-chosen image compression factor?
Which leads directly to the questions: What exactly is a quartz filter and can an application/script create its own custom quartz filters on-the-fly? It would be just useless/too error-prone to ask the user to create the custom quartz filter in ColorSync Utility for us first...
Unfortunately the ColorSync Utility itself is not scriptable with AppleScript, so this way for automatically creating custom quartz filters is blocked. But opening the «Reduce File Size» quartz filter file with my favourite free text editor - TextWrangler - revealed a surprise:
Quartz filters (or better: quartz filer files) are just pure XML/PLIST files!
Applescript:
- created: 19.06.2008
-- tested on/with:
-- • Intel & PowerPC based Macs
-- • Mac OS X 10.5.3
-- This AppleScript reduces the file size of dropped PDF documents according to
-- a chosen compression quality. The script actually shrinks the PDF
-- documents by applying a custom quartz filter.
property mytitle : "Quartz-O-Shrink"
-- I am called when the user drops Finder items onto the script's icon
on open dropitems
try
-- searching the dropped Finder items for PDF documents
-- PDF documents are identified by a '*.pdf' name extension
set pdffilepaths to {}
set pdffilenames to {}
repeat with dropitem in dropitems
set iteminfo to info for dropitem
if name extension of iteminfo is in {"pdf"} then
set pdffilepaths to pdffilepaths & (dropitem as Unicode text)
set pdffilenames to pdffilenames & (name of iteminfo)
end if
end repeat
-- no PDF documents found
if pdffilepaths is {} then
set errmsg to "You did not drop any PDF files onto me."
my dsperrmsg(errmsg, "--")
return
end if
-- asking the user to choose a compression quality
set compqual to my choosecompqual()
if compqual is missing value then
return
end if
-- asking the user to choose an output folder where
-- the shrinked PDF files are saved
set outputfolderpath to my chooseoutputfolder()
if outputfolderpath is missing value then
return
end if
-- creating the custom quartz filter with the chosen compression quality
set quartzfilterpath to my createquartzfilter(compqual)
if quartzfilterpath is missing value then
return
end if
-- applying the custom quartz filter to the dropped PDF documents
set countpdffilepaths to length of pdffilepaths
repeat with i from 1 to countpdffilepaths
set pdffilepath to item i of pdffilepaths
set pdffilename to item i of pdffilenames
set unusedfilepath to my getunusedfilepath(outputfolderpath, pdffilename)
set command to "/System/Library/Printers/Libraries/./quartzfilter " & quoted form of (POSIX path of pdffilepath) & space & quoted form of (POSIX path of quartzfilterpath) & space & quoted form of (POSIX path of unusedfilepath)
do shell script command
end repeat
on error errmsg number errnum
my dsperrmsg(errmsg, errnum)
end try
end open
-- I am called when the user opens the script with a doubleclick
on run
set welcomemsg to "I am an AppleScript droplet which reduces the file size of PDF documents." & return & return & "Therefor please drag and drop a bunch of PDF documents onto my icon."
tell me
activate
display dialog welcomemsg buttons {"OK"} default button 1 with title mytitle with icon note
end tell
end run
-- I am asking the user to choose a compression quality
on choosecompqual()
set compquals to {"Best » low compression, high image quality", "1.0", "0.9", "0.8", "0.7", "0.6", "0.5", "0.4", "0.3", "0.2", "0.1", "0.0", "Least » high compression, low image quality"}
choose from list compquals with prompt "Please choose a compression quality:" OK button name "Select" with title mytitle without multiple selections allowed and empty selection allowed
set usrchoice to result
if usrchoice is not false then
set chosencompqual to item 1 of (usrchoice as list)
if chosencompqual begins with "Best" or chosencompqual begins with "Least" then
my choosecompqual()
else
return chosencompqual
end if
else
return missing value
end if
end choosecompqual
-- I am asking the user to specify an output folder for the shrinked PDF documents
-- I return the folder path or «missing value» in case the user cancels the dialog
on chooseoutputfolder()
try
set chosenfolder to choose folder with prompt "Please specify an output folder for the processed files:" without multiple selections allowed, showing package contents and invisibles
return (chosenfolder as Unicode text)
on error
return missing value
end try
end chooseoutputfolder
-- I am creating a new quartz filter containing the user chosen compression quality
-- I return the file path of the created quartz filter
on createquartzfilter(compqual)
-- path to the quartz filter template located in the script bundle
set tmpquartzfilterpath to (((path to me) as Unicode text) & "Contents:Resources:Reduce File Size.qfilter")
set filecont to my readfromfile(tmpquartzfilterpath)
-- reading the content of the quartz filter template failed :(
if filecont is false then
set errmsg to "Could not read the content of the quartz filter template supposed to be located at:" & return & return & tmpquartzfilterpath
error errmsg
end if
-- replacing the placeholder with the chosen compression quality
set newfilecont to my searchnreplace("$compqual$", compqual, filecont)
-- creating our application support folder if it does not already exist
set appsuppfolderpath to (((path to application support folder from user domain) as Unicode text) & mytitle & ":")
try
set appsuppfolderalias to appsuppfolderpath as alias
on error
do shell script "mkdir -p " & quoted form of (POSIX path of appsuppfolderpath)
end try
-- path to the new quartz filter containing the chosen compression quality
set newquartzfilterpath to appsuppfolderpath & "Reduce File Size.qfilter"
-- writing the modified template content to the new file path
set writesuccess to my writetofile(newfilecont, newquartzfilterpath)
if not writesuccess then
set errmsg to "Could not write to file at:" & return & return & newquartzfilterpath
error errmsg
end if
-- returning the file path to the newly created quartz filter
return newquartzfilterpath
end createquartzfilter
-- I am returning an unused file path
-- example:
-- destfolderpath: «DandyDisk:Users:bender:Desktop:icnsfiles:»
-- filename: «example.pdf»
-- -> filepath: «DandyDisk:Users:bender:Desktop:icnsfiles:example.png»
-- if this filepath already exists:
-- -> filepath: «DandyDisk:Users:bender:Desktop:icnsfiles:example 1.png»
-- and so on...
on getunusedfilepath(destfolderpath, filename)
set counter to 0
repeat
if counter < 1 then
set unusedfilepath to destfolderpath & filename
else
set unusedfilepath to destfolderpath & ((characters 1 through -5 of filename) as Unicode text) & space & counter & ((characters -4 through -1 of filename) as Unicode text)
end if
try
set unusedfilealias to unusedfilepath as alias
on error
exit repeat
end try
set counter to counter + 1
end repeat
return unusedfilepath
end getunusedfilepath
-- I am a very old search & replace function...
on searchnreplace(searchstr, replacestr, txt)
considering case, diacriticals and punctuation
if txt contains searchstr then
set olddelims to AppleScript's text item delimiters
set AppleScript's text item delimiters to {searchstr}
set txtitems to text items of txt
set AppleScript's text item delimiters to {replacestr}
set txt to txtitems as Unicode text
set AppleScript's text item delimiters to olddelims
end if
end considering
return txt
end searchnreplace
-- I am reading and returning the content of a given file path
-- I return «false» in case the file could not be read
on readfromfile(filepath)
try
set openfile to open for access filepath
set filecont to read openfile
close access openfile
return filecont
on error
try
close access openfile
end try
return false
end try
end readfromfile
-- I am writing given content to a given file path using UTF-8 text encoding (no BOM)
-- I return «false» in case the write attempt failed
on writetofile(cont, filepath)
try
set openfile to open for access filepath with write permission
set eof of openfile to 0
-- no BOM this time...
-- set BOM_UTF8 to ((ASCII character 239) & (ASCII character 187) & (ASCII character 191))
write cont to openfile as «class utf8»
close access openfile
return true
on error
try
close access openfile
end try
return false
end try
end writetofile
-- I am displaying error messages
on dsperrmsg(errmsg, errnum)
tell me
activate
display dialog "Sorry, an error occured:" & return & return & errmsg & " (" & errnum & ")" buttons {"Never mind"} default button 1 with icon stop with title mytitle
end tell
end dsperrmsg
Offline
Your hidden command seems to be missing in OS X Mountain Lion 10.8.5.![]()
Offline
I would like to make a script that reduces the pixel size of images in PDF files using the built in OS X pdf engine.
This thread, and my own research, seem to indicate that something like this used to be possible from the OS X command line, but appears to no longer be there in 10.8 and above.
Any help would be appreciated.
Offline
If you're running 10.9, you can use an AppleScriptObjC-based library to apply a QuartzFilter. Save this as an ASObjC-based .scptd file in your Script Libraries folder:
Applescript:
use framework "Foundation"
use framework "Quartz"
on filterPDF:pathToPDF newPath:newPDFPath
set filterPath to current application's NSString's stringWithString:"/System/Library/Filters/Reduce File Size.qfilter"
set filterURL to current application's NSURL's fileURLWithPath:filterPath
set theFilter to current application's QuartzFilter's quartzFilterWithURL:filterURL
set pdfURL to current application's NSURL's fileURLWithPath:pathToPDF
set thePDFDoc to current application's PDFDocument's alloc()'s initWithURL:pdfURL
set theOptions to {QuartzFilter:theFilter}
thePDFDoc's writeToFile:newPDFPath withOptions:theOptions
end filterPDF:newPath:
Change the filter path to suit, or use the script above to create new versions of the filter, then call the script like this:
Applescript:
use theLib : script "<name of lib file>"
theLib's filterPDF:"/Users/shane/Desktop/Everyday AppleScriptObjC.pdf" newPath:"/Users/shane/Desktop/Everyday AppleScriptObjC-2.pdf"
Offline
I am sorry, I am unfamiliar with the variable:thisVarible structure you use in this code sample.
I did purchase your overview book on ASObjC, is there a few relevant pages I can go to read up on how to underhand this?
Thanks so much for your help.
Don
Last edited by Don (2014-01-13 10:32:46 am)
Offline
Don wrote:
I am sorry, I am unfamiliar with the variable:thisVarible structure you use in this code sample.
It's known as interleaved syntax, and it was introduced in Mavericks. If you write this:
Applescript:
on doSomething_toSomething_(someArg, otherArg)
in 10.9 it will compile as:
Applescript:
on doSomething:someArg toSomething:otherArg
It's just a different way of writing the same thing.
Offline
I found this tutorial which was very helpful on exactly how to deal with the Script Libraries folder, and was able to get everything in place.
http://macscripter.net/viewtopic.php?id=41638
Now, when I run a pdf though this, I get back false as the result, and no pdf is made.
I played around with the script in ASObjC Explorer 2 and got this error:
17:14:10.790 Can’t get POSIX path of class "ObjectWithFords". (-1728)
17:14:10.790 -- End -- (~0.011s)
Last edited by Don (2014-01-14 04:20:46 pm)
Offline
I'm not sure how ObjectWithFords gets involved. I just tried it in ASObjC Explorer 2 (without the use statements and as a normal handler):
Applescript:
0000.000 -- Start --
0000.001 [2] set filterPath to current application's NSString's stringWithString:"/System/Library/Filters/Reduce File Size.qfilter"
--> /System/Library/Filters/Reduce File Size.qfilter
0000.008 [3] set filterURL to current application's NSURL's fileURLWithPath:filterPath
--> file://localhost/System/Library/Filters/Reduce%20File%20Size.qfilter
0000.009 [4] set theFilter to current application's QuartzFilter's quartzFilterWithURL:filterURL
--> QuartzFilter:0x40010c020 private:0x400ea4100 name:Reduce File Size filter:0x40010c020 owner:0x0 URL:0x40037fbc0 properties:0x4011b42e0 qf:0x0 dict:0x0
0000.010 [5] set pdfURL to current application's NSURL's fileURLWithPath:pathToPDF
--> file://localhost/Users/shane/Desktop/Everyday%20AppleScriptObjC.pdf
0000.011 [6] set thePDFDoc to current application's PDFDocument's alloc()'s initWithURL:pdfURL
--> PDFDocument, number of pages: 85
0000.012 [7] set theOptions to {QuartzFilter:theFilter}
--> {QuartzFilter:«class ocid» id «data optr0000000020C0100004000000»}
0001.239 [8] thePDFDoc's writeToFile:newPDFPath withOptions:theOptions
--> 1
0001.240 [11] my filterPDF:"/Users/shane/Desktop/Everyday AppleScriptObjC.pdf" newPath:"/Users/shane/Desktop/Everyday AppleScriptObjC-2.pdf"
--> 1
0001.241 -- End -- (~1.241s)
Offline
Shane Stanley wrote:
If you're running 10.9, you can use an AppleScriptObjC-based library to apply a QuartzFilter. Save this as an ASObjC-based .scptd file in your Script Libraries folder:
Applescript:
use framework "Foundation"
use framework "Quartz"
on filterPDF:pathToPDF newPath:newPDFPath
set filterPath to current application's NSString's stringWithString:"/System/Library/Filters/Reduce File Size.qfilter"
set filterURL to current application's NSURL's fileURLWithPath:filterPath
set theFilter to current application's QuartzFilter's quartzFilterWithURL:filterURL
set pdfURL to current application's NSURL's fileURLWithPath:pathToPDF
set thePDFDoc to current application's PDFDocument's alloc()'s initWithURL:pdfURL
set theOptions to {QuartzFilter:theFilter}
thePDFDoc's writeToFile:newPDFPath withOptions:theOptions
end filterPDF:newPath:
Change the filter path to suit, or use the script above to create new versions of the filter, then call the script like this:
Applescript:
use theLib : script "<name of lib file>"
theLib's filterPDF:"/Users/shane/Desktop/Everyday AppleScriptObjC.pdf" newPath:"/Users/shane/Desktop/Everyday AppleScriptObjC-2.pdf"
I tried to use this today but am getting error "Unrecognized function writeToFile_withOptions_." number -10000. Not sure what's going wrong as I was able to alloc/init the PDF document class and have confirmed that the path is a string and the options are a dictionary. Any ideas?
Here's the code I'm using to call the library script:
Applescript:
set inputFolder to (choose folder with prompt "Select a folder")
tell application "Finder"
set newFolderName to "Compressed"
if not (exists folder newFolderName of inputFolder) then
make new folder at inputFolder with properties {name:newFolderName}
end if
set outputFolder to folder newFolderName of inputFolder
set inputFiles to get every file of inputFolder as alias list
end tell
repeat with inputFile in inputFiles
tell application "Finder" to set theName to name of inputFile
set newPathToPDF to (outputFolder as text) & theName
set newPathToPDF to quoted form of POSIX path of newPathToPDF
set originalPathToPDF to quoted form of POSIX path of inputFile
(script "ReduceFileSize2"'s filterPDF:originalPathToPDF newPath:newPathToPDF)
end repeat
Offline
Remove the "quoted form of" each time -- you only need that for shell scripts.
Offline
Thanks - worked like a charm.
I was also getting error 4960 occasionally, but discovered from another of your posts that it was because of a bug in applescript and should only happen on the first compile.
Thanks again.
Offline
Speranza89 wrote:
I was also getting error 4960 occasionally, but discovered from another of your posts that it was because of a bug in applescript
FWIW, it's actually a bug in AppleScript Editor; if you use another editor (Script Debugger or my ASObjC Explorer) it doesn't happen.
Offline