Hello
Ah, I've not noticed the transparent background issue in my brief tests with limited samples. Here's a revised code to fill the canvas with opaque white colour before drawing pdf content.
#!/usr/bin/python2.6
# coding: utf-8
#
# file:
# pdf2tiff.py
#
# function:
# convert pdf to multi-page tiff file
#
# usage:
# ./pdf2tiff.py pdf [pdf ...] [outdir]
#
# pdf : input pdf file
# outdir : output directory
#
# * Input file without name extension as .pdf is ignored.
# * If outdir directory is specified and present, tiff for each input file is saved in the directory
# with the same basename without extension followed by '.tiff'; e.g., src/a.pdf => outdir/a.tiff.
# Othewise, tiff is saved in the same directory as the original with new file name as
# basename without extension followed by .tiff; e.g., src/a.pdf => src/a.tiff.
# * Resolution of output tiff is defined by DPI constant, currently DPI = 150.0.
# * If PDF_DISPOSITION == 1, source pdf is removed when its tiff version is created successfully.
# * If LOGGING == 1, each conversion is logged to stdout.
#
# version:
# 0.12
# - invoking /usr/bin/python2.6 in order to avoid lazy importer error of pybobjc bundled with python 2.7
# - added code to fill CGContext with white background before drawing PDF page
#
# 0.11c
# - using CGImageDestinationCreateWithData() in lieu of CGImageDestinationCreateWithURL() and
# invoking writeToURL:options:error: method on CFData so as to treat certain errors properly.
#
# 0.11a
# - added code to handle source file disposition
# - added code to log each conversion
# 0.11
# - increased pyobjc performance (for recent versions of pyobjc) by using:
# - import Quartz.CoreGraphics as CG
# - import Quartz.ImageIO as CGIO
# 0.10
# - draft
#
# written by Hiroto, 2017-07
#
import sys, os
import re, math, time
import Quartz.CoreGraphics as CG
import Quartz.ImageIO as CGIO
def usage():
sys.stderr.write('Usage: %s pdf [pdf...] [outdir]\n' % os.path.basename(sys.argv[0]))
sys.exit(2)
def tstamp():
return time.strftime('%Y-%m-%d %H:%M:%S %Z', time.localtime())
def main():
uargv = [ a.decode('utf-8') for a in sys.argv ]
odir = uargv.pop(-1).rstrip('/') if os.path.isdir(uargv[-1]) else None
if len(uargv) < 2: usage()
err = 0
cspace = CG.CGColorSpaceCreateWithName(CG.kCGColorSpaceAdobeRGB1998)
blanc1 = CG.CGColorCreate(cspace, [1.0, 1.0, 1.0, 1.0])
for f in [ a for a in uargv[1:] if re.search(r'\.pdf$', a, re.I | re.U) ]:
url = CG.CFURLCreateWithFileSystemPath(CG.kCFAllocatorDefault, f, CG.kCFURLPOSIXPathStyle, False)
pdf = CG.CGPDFDocumentCreateWithURL(url)
if not pdf:
err += 1
sys.stderr.write('%-24s Not a pdf file: %s\n' % (tstamp(), f.encode('utf-8')))
continue
pcnt = CG.CGPDFDocumentGetNumberOfPages(pdf)
if pcnt < 1: continue # ignore blank pdf
n = os.path.basename(f)
m, ext = os.path.splitext(n)
if not odir:
odir = os.path.dirname(f)
if odir != '':
f1 = '%s/%s.tiff' % (odir, m)
else:
f1 = '%s.tiff' % (m)
data = CG.CFDataCreateMutable(CG.kCFAllocatorDefault, 0)
idst = CGIO.CGImageDestinationCreateWithData(data, 'public.tiff', pcnt, None)
err1 = 0
for i in range(0, pcnt):
page = CG.CGPDFDocumentGetPage(pdf, i + 1)
if not page:
err += 1
err1 += 1
sys.stderr.write('%-24s Could not get page %d of %s\n' % (tstamp(), i + 1, f.encode('utf-8')))
continue
scale = DPI / 72.0
r = CG.CGPDFPageGetBoxRect(page, CG.kCGPDFMediaBox)
w = math.ceil(r.size.width * scale)
h = math.ceil(r.size.height * scale)
ctx = CG.CGBitmapContextCreate(
None, w, h, 8, 0,
cspace,
CG.kCGImageAlphaPremultipliedLast)
CG.CGContextSaveGState(ctx)
# fill white
CG.CGContextSetFillColorWithColor(ctx, blanc1)
CG.CGContextFillRect(ctx, CG.CGRectMake(0.0, 0.0, w, h))
# draw pdf page
CG.CGContextScaleCTM(ctx, scale, scale)
CG.CGContextDrawPDFPage(ctx, page)
CG.CGContextRestoreGState(ctx)
cgi = CG.CGBitmapContextCreateImage(ctx)
CGIO.CGImageDestinationAddImage(idst, cgi,
{
CGIO.kCGImagePropertyDPIHeight : DPI,
CGIO.kCGImagePropertyDPIWidth : DPI,
CGIO.kCGImagePropertyTIFFDictionary : {
CGIO.kCGImagePropertyTIFFCompression : 5
# kCGImagePropertyTIFFCompression
# 1 : no compression
# 5 : LZW
# 32773 : PackBits
}
})
del ctx
del cgi
b = CGIO.CGImageDestinationFinalize(idst)
del idst
del pdf
if not b:
err += 1
sys.stderr.write('%-24s Failed to finalize destination data: %s\n' % (tstamp(), f1.encode('utf-8')))
del data
continue
url1 = CG.CFURLCreateWithFileSystemPath(CG.kCFAllocatorDefault, f1, CG.kCFURLPOSIXPathStyle, False)
b, e = data.writeToURL_options_error_(url1, 0, None)
del data
if not b:
err += 1
sys.stderr.write('%-24s Failed to write destination file: %s: %s\n' %
(tstamp(), f1.encode('utf-8'), e.description().encode('utf-8')))
continue
if err1 == 0 and PDF_DISPOSITION == 1:
# source file dispositon
try:
os.remove(f)
except Exception as e:
sys.stderr.write('%-24s Failed to delete source file: %s: %s\n' %
(tstamp(), f.encode('utf-8'), e.__repr__().encode('utf-8')))
if LOGGING == 1:
sys.stdout.write('%-24s Completed conversion: %s => %s\n' % (tstamp(), f.encode('utf-8'), f1.encode('utf-8')))
sys.exit(1 if err > 0 else 0)
DPI = 150.0
PDF_DISPOSITION = 0
# PDF_DISPOSITION
# 0 : leave pdf alone
# 1 : remove pdf if tiff is created successfully
LOGGING = 1
# LOGGING
# 0 : without logging
# 1 : with logging
main()
---
And here's its AppleScript wrapper if it helps the original poster.
--APPLESCRIPT
on run
choose file of type {"pdf"} with multiple selections allowed
pdf2tiff(result)
end run
on open aa
repeat with a in aa
if a's POSIX path ends with ".pdf" then
set a's contents to a as alias
else
set a's contents to false
end if
end repeat
if (count aa's aliases) > 0 then pdf2tiff(aa's aliases)
end open
on adding folder items to d after receiving aa
repeat with a in aa
if a's POSIX path ends with ".pdf" then
set a's contents to a as alias
else
set a's contents to false
end if
end repeat
if (count aa's aliases) > 0 then pdf2tiff(aa's aliases & (d's POSIX path & "tiff"))
end adding folder items to
on pdf2tiff(argv)
(*
list argv : list of alias or POSIX path of pdf files, optionally last item as output directory
*)
set args to ""
repeat with a in argv
if a's class is in {alias, «class bmrk»} then set a to a's POSIX path
set args to args & a's quoted form & space
end repeat
do shell script "/bin/bash -s <<'EOF' - " & args & "
LOG=${1%/*}/_log.txt
exec >> \"$LOG\" 2>&1
/usr/bin/python2.6 <<'END' - \"$@\"; exit 0
# coding: utf-8
#
# file:
# pdf2tiff.py
#
# function:
# convert pdf to multi-page tiff file
#
# usage:
# ./pdf2tiff.py pdf [pdf ...] [outdir]
#
# pdf : input pdf file
# outdir : output directory
#
# * Input file without name extension as .pdf is ignored.
# * If outdir directory is specified and present, tiff for each input file is saved in the directory
# with the same basename without extension followed by '.tiff'; e.g., src/a.pdf => outdir/a.tiff.
# Othewise, tiff is saved in the same directory as the original with new file name as
# basename without extension followed by .tiff; e.g., src/a.pdf => src/a.tiff.
# * Resolution of output tiff is defined by DPI constant, currently DPI = 150.0.
# * If PDF_DISPOSITION == 1, source pdf is removed when its tiff version is created successfully.
# * If LOGGING == 1, each conversion is logged to stdout.
#
# version:
# 0.12
# - invoking /usr/bin/python2.6 in order to avoid lazy importer error of pybobjc bundled with python 2.7
# - added code to fill CGContext with white background before drawing PDF page
#
# 0.11c
# - using CGImageDestinationCreateWithData() in lieu of CGImageDestinationCreateWithURL() and
# invoking writeToURL:options:error: method on CFData so as to treat certain errors properly.
#
# 0.11a
# - added code to handle source file disposition
# - added code to log each conversion
# 0.11
# - increased pyobjc performance (for recent versions of pyobjc) by using:
# - import Quartz.CoreGraphics as CG
# - import Quartz.ImageIO as CGIO
# 0.10
# - draft
#
# written by Hiroto, 2017-07
#
import sys, os
import re, math, time
import Quartz.CoreGraphics as CG
import Quartz.ImageIO as CGIO
def usage():
sys.stderr.write('Usage: %s pdf [pdf...] [outdir]\\n' % os.path.basename(sys.argv[0]))
sys.exit(2)
def tstamp():
return time.strftime('%Y-%m-%d %H:%M:%S %Z', time.localtime())
def main():
uargv = [ a.decode('utf-8') for a in sys.argv ]
odir = uargv.pop(-1).rstrip('/') if os.path.isdir(uargv[-1]) else None
if len(uargv) < 2: usage()
err = 0
cspace = CG.CGColorSpaceCreateWithName(CG.kCGColorSpaceAdobeRGB1998)
blanc1 = CG.CGColorCreate(cspace, [1.0, 1.0, 1.0, 1.0])
for f in [ a for a in uargv[1:] if re.search(r'\\.pdf$', a, re.I | re.U) ]:
url = CG.CFURLCreateWithFileSystemPath(CG.kCFAllocatorDefault, f, CG.kCFURLPOSIXPathStyle, False)
pdf = CG.CGPDFDocumentCreateWithURL(url)
if not pdf:
err += 1
sys.stderr.write('%-24s Not a pdf file: %s\\n' % (tstamp(), f.encode('utf-8')))
continue
pcnt = CG.CGPDFDocumentGetNumberOfPages(pdf)
if pcnt < 1: continue # ignore blank pdf
n = os.path.basename(f)
m, ext = os.path.splitext(n)
if not odir:
odir = os.path.dirname(f)
if odir != '':
f1 = '%s/%s.tiff' % (odir, m)
else:
f1 = '%s.tiff' % (m)
data = CG.CFDataCreateMutable(CG.kCFAllocatorDefault, 0)
idst = CGIO.CGImageDestinationCreateWithData(data, 'public.tiff', pcnt, None)
err1 = 0
for i in range(0, pcnt):
page = CG.CGPDFDocumentGetPage(pdf, i + 1)
if not page:
err += 1
err1 += 1
sys.stderr.write('%-24s Could not get page %d of %s\\n' % (tstamp(), i + 1, f.encode('utf-8')))
continue
scale = DPI / 72.0
r = CG.CGPDFPageGetBoxRect(page, CG.kCGPDFMediaBox)
w = math.ceil(r.size.width * scale)
h = math.ceil(r.size.height * scale)
ctx = CG.CGBitmapContextCreate(
None, w, h, 8, 0,
cspace,
CG.kCGImageAlphaPremultipliedLast)
CG.CGContextSaveGState(ctx)
# fill white
CG.CGContextSetFillColorWithColor(ctx, blanc1)
CG.CGContextFillRect(ctx, CG.CGRectMake(0.0, 0.0, w, h))
# draw pdf page
CG.CGContextScaleCTM(ctx, scale, scale)
CG.CGContextDrawPDFPage(ctx, page)
CG.CGContextRestoreGState(ctx)
cgi = CG.CGBitmapContextCreateImage(ctx)
CGIO.CGImageDestinationAddImage(idst, cgi,
{
CGIO.kCGImagePropertyDPIHeight : DPI,
CGIO.kCGImagePropertyDPIWidth : DPI,
CGIO.kCGImagePropertyTIFFDictionary : {
CGIO.kCGImagePropertyTIFFCompression : 5
# kCGImagePropertyTIFFCompression
# 1 : no compression
# 5 : LZW
# 32773 : PackBits
}
})
del ctx
del cgi
b = CGIO.CGImageDestinationFinalize(idst)
del idst
del pdf
if not b:
err += 1
sys.stderr.write('%-24s Failed to finalize destination data: %s\\n' % (tstamp(), f1.encode('utf-8')))
del data
continue
url1 = CG.CFURLCreateWithFileSystemPath(CG.kCFAllocatorDefault, f1, CG.kCFURLPOSIXPathStyle, False)
b, e = data.writeToURL_options_error_(url1, 0, None)
del data
if not b:
err += 1
sys.stderr.write('%-24s Failed to write destination file: %s: %s\\n' %
(tstamp(), f1.encode('utf-8'), e.description().encode('utf-8')))
continue
if err1 == 0 and PDF_DISPOSITION == 1:
# source file dispositon
try:
os.remove(f)
except Exception as e:
sys.stderr.write('%-24s Failed to delete source file: %s: %s\\n' %
(tstamp(), f.encode('utf-8'), e.__repr__().encode('utf-8')))
if LOGGING == 1:
sys.stdout.write('%-24s Completed conversion: %s => %s\\n' % (tstamp(), f.encode('utf-8'), f1.encode('utf-8')))
sys.exit(1 if err > 0 else 0)
DPI = 150.0
PDF_DISPOSITION = 1
# PDF_DISPOSITION
# 0 : leave pdf alone
# 1 : remove pdf if tiff is created successfully
LOGGING = 1
# LOGGING
# 0 : without logging
# 1 : with logging
main()
END
EOF"
end pdf2tiff
--END OF APPLESCRIPT
All the best,
Hiroto
PS. As for system's python version under 10.13, I found this:
https://forums.developer.apple.com/thread/78891