Thursday, September 2, 2010

#1 2008-06-20 02:44:20 am

Martin Michel
Administrator
From: Berlin, Germany
Registered: 2008-03-03
Posts: 655
Website

Thoughts and examples about using the quartzfilter tool

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!



This means that an application or script can easily create its own custom quartz filters using an XML library or/and a template engine with default quartz filters. Even vanilla AppleScript can do it with the XML suite of the System Events Scripting Addition.

Therefor a PDF or image processing droplet could contain a copy of the above mentioned «Reduce File Size» quartz filter and change the value for the XML element «Compression Quality» according to a user-chosen factor. This way you could quickly batch-shrink PDF or image files with custom settings!

Just in case you are not yet convinced I invite you to download, test and inspect the sample droplet script below, which reduces the file size of dropped PDF documents according to a chosen compression quality:



The AppleScript was tested under Mac OS X 10.5.3 with Intel and PowerPC based Macs.

Important: Opening and saving the below script code in Script Editor won't result in a usable AppleScript! That is because the AppleScript internally relies on a quartz filter template, which is located inside its Application bundle. Therefor please download the complete script here.

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


Sal Soghoian is my role model.

Filed under: PDF, Quartz, quartzfilter, filter

Offline

 

Board footer

Powered by FluxBB

[ Generated in 0.327 seconds, 11 queries executed ]

RSS (new topics) RSS (active topics)