forked from josch/img2pdf
Start of converting the module to a proper package.
This commit is contained in:
parent
8c293291c5
commit
b47cc04dd3
3 changed files with 122 additions and 49 deletions
36
setup.py
Normal file
36
setup.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup (
|
||||||
|
name='img2pdf',
|
||||||
|
version='0.1.0',
|
||||||
|
author = "Johannes 'josch' Schauer",
|
||||||
|
description = "Convert images to PDF via direct JPEG inclusion.",
|
||||||
|
long_description = open('README.md').read(),
|
||||||
|
license = "GPL",
|
||||||
|
keywords = "jpeg pdf converter",
|
||||||
|
classifiers = [
|
||||||
|
'Development Status :: 4 - Beta',
|
||||||
|
'Intended Audience :: Developers',
|
||||||
|
'Programming Language :: Python',
|
||||||
|
'Programming Language :: Python :: 2',
|
||||||
|
'Programming Language :: Python :: 2.6',
|
||||||
|
'Programming Language :: Python :: 2.7',
|
||||||
|
'Programming Language :: Python :: Implementation :: CPython',
|
||||||
|
'License :: OSI Approved :: General Public License',
|
||||||
|
'Programming Language :: Python',
|
||||||
|
'Natural Language :: English',
|
||||||
|
'Operating System :: OS Independent'],
|
||||||
|
url = 'http://pypi.python.org/pypi/img2pdf',
|
||||||
|
package_dir={"": "src"},
|
||||||
|
py_modules=['img2pdf', 'jp2'],
|
||||||
|
include_package_data = True,
|
||||||
|
test_suite = 'tests.test_suite',
|
||||||
|
zip_safe = True,
|
||||||
|
install_requires=(
|
||||||
|
'Pillow',
|
||||||
|
),
|
||||||
|
entry_points='''
|
||||||
|
[console_scripts]
|
||||||
|
img2pdf = img2pdf:main
|
||||||
|
''',
|
||||||
|
)
|
|
@ -1,5 +1,3 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
#
|
|
||||||
# Copyright (C) 2012-2013 Johannes 'josch' Schauer <j.schauer at email.de>
|
# Copyright (C) 2012-2013 Johannes 'josch' Schauer <j.schauer at email.de>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
@ -15,17 +13,19 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import Image
|
|
||||||
import sys
|
import sys
|
||||||
import zlib
|
import zlib
|
||||||
import argparse
|
import argparse
|
||||||
import struct
|
import struct
|
||||||
|
from Pillow import Image
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from jp2 import parsejp2
|
from jp2 import parsejp2
|
||||||
|
|
||||||
def parse(cont, indent=1):
|
def parse(cont, indent=1):
|
||||||
if type(cont) is dict:
|
if type(cont) is dict:
|
||||||
return "<<\n"+"\n".join([4*indent*" "+"%s %s"%(k, parse(v, indent+1)) for k, v in cont.items()])+"\n"+4*(indent-1)*" "+">>"
|
return "<<\n"+"\n".join(
|
||||||
|
[4 * indent * " " + "%s %s" % (k, parse(v, indent+1))
|
||||||
|
for k, v in cont.items()])+"\n"+4*(indent-1)*" "+">>"
|
||||||
elif type(cont) is int or type(cont) is float:
|
elif type(cont) is int or type(cont) is float:
|
||||||
return str(cont)
|
return str(cont)
|
||||||
elif isinstance(cont, obj):
|
elif isinstance(cont, obj):
|
||||||
|
@ -35,26 +35,29 @@ def parse(cont, indent=1):
|
||||||
elif type(cont) is list:
|
elif type(cont) is list:
|
||||||
return "[ "+" ".join([parse(c, indent) for c in cont])+" ]"
|
return "[ "+" ".join([parse(c, indent) for c in cont])+" ]"
|
||||||
|
|
||||||
class obj():
|
class obj(object):
|
||||||
def __init__(self, content, stream=None):
|
def __init__(self, content, stream=None):
|
||||||
self.content = content
|
self.content = content
|
||||||
self.stream = stream
|
self.stream = stream
|
||||||
|
|
||||||
def tostring(self):
|
def tostring(self):
|
||||||
if self.stream:
|
if self.stream:
|
||||||
return "%d 0 obj "%self.identifier+parse(self.content)+"\nstream\n"+self.stream+"\nendstream\nendobj\n"
|
return "%d 0 obj " % (
|
||||||
|
self.identifier+parse(self.content) +
|
||||||
|
"\nstream\n" + self.stream + "\nendstream\nendobj\n")
|
||||||
else:
|
else:
|
||||||
return "%d 0 obj "%self.identifier+parse(self.content)+" endobj\n"
|
return "%d 0 obj "%self.identifier+parse(self.content)+" endobj\n"
|
||||||
|
|
||||||
class pdfdoc():
|
class pdfdoc(object):
|
||||||
objects = list()
|
|
||||||
|
|
||||||
def __init__(self, version=3, title=None, author=None, creator=None, producer=None,
|
def __init__(self, version=3, title=None, author=None, creator=None,
|
||||||
creationdate=None, moddate=None, subject=None, keywords=None):
|
producer=None, creationdate=None, moddate=None, subject=None,
|
||||||
|
keywords=None):
|
||||||
self.version = version # default pdf version 1.3
|
self.version = version # default pdf version 1.3
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
|
objects = []
|
||||||
|
|
||||||
info = dict()
|
info = {}
|
||||||
if title:
|
if title:
|
||||||
info["/Title"] = "("+title+")"
|
info["/Title"] = "("+title+")"
|
||||||
if author:
|
if author:
|
||||||
|
@ -78,7 +81,8 @@ class pdfdoc():
|
||||||
|
|
||||||
self.info = obj(info)
|
self.info = obj(info)
|
||||||
|
|
||||||
# create an incomplete pages object so that a /Parent entry can be added to each page
|
# create an incomplete pages object so that a /Parent entry can be
|
||||||
|
# added to each page
|
||||||
self.pages = obj({
|
self.pages = obj({
|
||||||
"/Type": "/Pages",
|
"/Type": "/Pages",
|
||||||
"/Kids": [],
|
"/Kids": [],
|
||||||
|
@ -106,7 +110,8 @@ class pdfdoc():
|
||||||
error_out("unsupported color space: %s"%color)
|
error_out("unsupported color space: %s"%color)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
pdf_x, pdf_y = 72.0*width/dpi[0], 72.0*height/dpi[1] # pdf units = 1/72 inch
|
# pdf units = 1/72 inch
|
||||||
|
pdf_x, pdf_y = 72.0*width/dpi[0], 72.0*height/dpi[1]
|
||||||
|
|
||||||
if pdf_x < 3.00 or pdf_y < 3.00:
|
if pdf_x < 3.00 or pdf_y < 3.00:
|
||||||
warning_out("pdf width or height is below 3.00 - decrease the dpi")
|
warning_out("pdf width or height is below 3.00 - decrease the dpi")
|
||||||
|
@ -126,7 +131,8 @@ class pdfdoc():
|
||||||
"/Width": width,
|
"/Width": width,
|
||||||
"/Height": height,
|
"/Height": height,
|
||||||
"/ColorSpace": color,
|
"/ColorSpace": color,
|
||||||
"/BitsPerComponent": 8, # hardcoded as PIL doesnt provide bits for non-jpeg formats
|
# hardcoded as PIL doesnt provide bits for non-jpeg formats
|
||||||
|
"/BitsPerComponent": 8,
|
||||||
"/Length": len(imgdata)
|
"/Length": len(imgdata)
|
||||||
}, imgdata)
|
}, imgdata)
|
||||||
|
|
||||||
|
@ -178,9 +184,9 @@ class pdfdoc():
|
||||||
result += "%%EOF\n"
|
result += "%%EOF\n"
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def main(images, dpi, title=None, author=None, creator=None, producer=None,
|
def convert(images, dpi, title=None, author=None, creator=None, producer=None,
|
||||||
creationdate=None, moddate=None, subject=None, keywords=None,
|
creationdate=None, moddate=None, subject=None, keywords=None,
|
||||||
colorspace=None, verbose=False):
|
colorspace=None, verbose=False):
|
||||||
|
|
||||||
def debug_out(message):
|
def debug_out(message):
|
||||||
if verbose:
|
if verbose:
|
||||||
|
@ -190,7 +196,8 @@ def main(images, dpi, title=None, author=None, creator=None, producer=None,
|
||||||
def warning_out(message):
|
def warning_out(message):
|
||||||
sys.stderr.write("W: "+message+"\n")
|
sys.stderr.write("W: "+message+"\n")
|
||||||
|
|
||||||
pdf = pdfdoc(3, title, author, creator, producer, creationdate, moddate, subject, keywords)
|
pdf = pdfdoc(3, title, author, creator, producer, creationdate,
|
||||||
|
moddate, subject, keywords)
|
||||||
|
|
||||||
for im in images:
|
for im in images:
|
||||||
rawdata = im.read()
|
rawdata = im.read()
|
||||||
|
@ -261,34 +268,64 @@ def main(images, dpi, title=None, author=None, creator=None, producer=None,
|
||||||
|
|
||||||
return pdf.tostring()
|
return pdf.tostring()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
parser = argparse.ArgumentParser(description='lossless conversion/embedding of images (in)to pdf')
|
def positive_float(string):
|
||||||
parser.add_argument('images', metavar='infile', type=argparse.FileType('rb'),
|
value = float(string)
|
||||||
nargs='+', help='input file(s)')
|
if value <= 0:
|
||||||
parser.add_argument('-o', '--output', metavar='out', type=argparse.FileType('wb'),
|
msg = "%r is not positive"%string
|
||||||
default=sys.stdout, help='output file (default: stdout)')
|
raise argparse.ArgumentTypeError(msg)
|
||||||
def positive_float(string):
|
return value
|
||||||
value = float(string)
|
|
||||||
if value <= 0:
|
def valid_date(string):
|
||||||
msg = "%r is not positive"%string
|
return datetime.strptime(string, "%Y-%m-%dT%H:%M:%S")
|
||||||
raise argparse.ArgumentTypeError(msg)
|
|
||||||
return value
|
parser = argparse.ArgumentParser(
|
||||||
parser.add_argument('-d', '--dpi', metavar='dpi', type=positive_float, help='dpi for pdf output (default: 96.0)')
|
description='Lossless conversion/embedding of images (in)to pdf')
|
||||||
parser.add_argument('-t', '--title', metavar='title', type=str, help='title for metadata')
|
parser.add_argument(
|
||||||
parser.add_argument('-a', '--author', metavar='author', type=str, help='author for metadata')
|
'images', metavar='infile', type=argparse.FileType('rb'),
|
||||||
parser.add_argument('-c', '--creator', metavar='creator', type=str, help='creator for metadata')
|
nargs='+', help='input file(s)')
|
||||||
parser.add_argument('-p', '--producer', metavar='producer', type=str, help='producer for metadata')
|
parser.add_argument(
|
||||||
def valid_date(string):
|
'-o', '--output', metavar='out', type=argparse.FileType('wb'),
|
||||||
return datetime.strptime(string, "%Y-%m-%dT%H:%M:%S")
|
default=sys.stdout, help='output file (default: stdout)')
|
||||||
parser.add_argument('-r', '--creationdate', metavar='creationdate',
|
parser.add_argument(
|
||||||
type=valid_date, help='creation date for metadata in YYYY-MM-DDTHH:MM:SS format')
|
'-d', '--dpi', metavar='dpi', type=positive_float,
|
||||||
parser.add_argument('-m', '--moddate', metavar='moddate',
|
help='dpi for pdf output (default: 96.0)')
|
||||||
type=valid_date, help='modification date for metadata in YYYY-MM-DDTHH:MM:SS format')
|
parser.add_argument(
|
||||||
parser.add_argument('-s', '--subject', metavar='subject', type=str, help='subject for metadata')
|
'-t', '--title', metavar='title', type=str,
|
||||||
parser.add_argument('-k', '--keywords', metavar='kw', type=str, nargs='+', help='keywords for metadata')
|
help='title for metadata')
|
||||||
parser.add_argument('-C', '--colorspace', metavar='colorspace', type=str, help='force PIL colorspace (one of: RGB, L, 1)')
|
parser.add_argument(
|
||||||
parser.add_argument('-v', '--verbose', help='verbose mode', action="store_true")
|
'-a', '--author', metavar='author', type=str,
|
||||||
args = parser.parse_args()
|
help='author for metadata')
|
||||||
args.output.write(main(args.images, args.dpi, args.title, args.author,
|
parser.add_argument(
|
||||||
args.creator, args.producer, args.creationdate, args.moddate,
|
'-c', '--creator', metavar='creator', type=str,
|
||||||
args.subject, args.keywords, args.colorspace, args.verbose))
|
help='creator for metadata')
|
||||||
|
parser.add_argument(
|
||||||
|
'-p', '--producer', metavar='producer', type=str,
|
||||||
|
help='producer for metadata')
|
||||||
|
parser.add_argument(
|
||||||
|
'-r', '--creationdate', metavar='creationdate', type=valid_date,
|
||||||
|
help='creation date for metadata in YYYY-MM-DDTHH:MM:SS format')
|
||||||
|
parser.add_argument(
|
||||||
|
'-m', '--moddate', metavar='moddate', type=valid_date,
|
||||||
|
help='modification date for metadata in YYYY-MM-DDTHH:MM:SS format')
|
||||||
|
parser.add_argument(
|
||||||
|
'-s', '--subject', metavar='subject', type=str,
|
||||||
|
help='subject for metadata')
|
||||||
|
parser.add_argument(
|
||||||
|
'-k', '--keywords', metavar='kw', type=str, nargs='+',
|
||||||
|
help='keywords for metadata')
|
||||||
|
parser.add_argument(
|
||||||
|
'-C', '--colorspace', metavar='colorspace', type=str,
|
||||||
|
help='force PIL colorspace (one of: RGB, L, 1)')
|
||||||
|
parser.add_argument(
|
||||||
|
'-v', '--verbose', help='verbose mode', action="store_true")
|
||||||
|
|
||||||
|
def main(args=None):
|
||||||
|
if args is None:
|
||||||
|
args = sys.argv[1:]
|
||||||
|
args = parser.parse_args(args)
|
||||||
|
args.output.write(
|
||||||
|
convert(
|
||||||
|
args.images, args.dpi, args.title, args.author,
|
||||||
|
args.creator, args.producer, args.creationdate, args.moddate,
|
||||||
|
args.subject, args.keywords, args.colorspace, args.verbose))
|
Loading…
Reference in a new issue