Start of converting the module to a proper package.

This commit is contained in:
Stephan Richter 2014-02-28 22:57:40 -05:00 committed by josch
parent 8c293291c5
commit b47cc04dd3
3 changed files with 122 additions and 49 deletions

36
setup.py Normal file
View 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
''',
)

View file

@ -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))

View file