Commit 11199156 authored by Hartmut Goebel's avatar Hartmut Goebel
Browse files

Initial checking of working code.

parents
# crazy-compiler standard excludes
/projectlogo.png
/docs/_build/
*.[1-9]
# editor artefacts
*~
.#*
\#*#
.*.swp
# build artefacts
/.sconf_temp
/.sconsign.*
/config.h
/config.log
/config.status
/autom4te*.cache
/options.cache
/stamp-h1
# C, C++ build artefacts
*.[oa]
*.l[oa]
*.al
.libs
.deps
*.so
*.so.[0-9]*
*.rej
*.dll
.idea
# Python build artefacts
/build/
/dist/
/_venv/
*.py[cod]
*.egg
*.egg-info
*.egg-link
/nosetests.xml
/.coverage
/.tox
/.achievements
/.installed.cfg
/.ropeproject
# Mr Developer
/.mr.developer.cfg
/.project
/.pydevproject
# OS generated files
*.DS_[sS]Store*
ehthumbs.db
Icon?
Thumbs.db
*.pid
._.**~
This diff is collapsed.
.. -*- mode: rst -*-
==========================
pdfbook
==========================
----------------------------------------------------------------
Rearrange pages in PDF file into signatures for printing books.
----------------------------------------------------------------
:Author: Hartmut Goebel <h.goebel@goebel-consult.de>
:Version: Version 0.5.0
:Copyright: GNU Public Licence v3 (GPLv3)
:Homepage: http://pdfposter.origo.ethz.ch/
``Pdfbook`` rearranges pages from a PDF document into "signatures"
for printing books or booklets, creating a new PDF file.
This is much like ``psbook`` does for Postscript files, but working
with PDF. Indeed ``pdfbook`` was inspired by ``psbook``.
For more information please refer to the manpage or visit
the `project homepage <http://pdfposter.origo.ethz.ch/>`_.
Requirements
~~~~~~~~~~~~~~~~~~~~
``Pdfbook`` requires
* Python 2.x (tested with 2.5, but other versions should work, too),
* `pyPdf <http://pybrary.net/pyPdf/>`_ > 1.10,
* setuptools for installation (see below).
Installation
~~~~~~~~~~~~~~~~~~~
Installation Requirements
----------------------------
``Pdfbook`` uses setuptools for installation. Thus you need
either
* network access, so the install script will automatically download
and install setuptools if they are not already installed
or
* the correct version of setuptools preinstalled using the
`EasyInstall installation instructions`__. Those instructions also
have tips for dealing with firewalls as well as how to manually
download and install setuptools.
__ http://peak.telecommunity.com/DevCenter/EasyInstall#installation-instructions
Installation
-------------------------
Install ``pdfbook`` by just running::
python ./setup.py install
Custom Installation Locations
------------------------------
``pdfbook`` is just a single script (aka Python program). So you can
copy it where ever you want (maybe fixing the first line). But it's
easier to just use::
# install to /usr/local/bin
python ./setup.py install --prefix /usr/local
# install to your Home directory (~/bin)
python ./setup.py install --home ~
Please mind: This effects also the installation of pfPDf (and
setuptools) if they are not already installed.
For more information about Custom Installation Locations please refer
to the `Custom Installation Locations Instructions`__ before
installing ``pdfbook``.
__ http://peak.telecommunity.com/DevCenter/EasyInstall#custom-installation-locations
# -*- mode: python ; coding: utf-8 -*-
#
# Build requirements
# - docutils
# - inkscape
#
# For building tests
# - pdf2ps (comes with ghostscript)
# - psbook
#
# For building examles
# Build requirements
# - ImageMagick
#
import os
ENV = {
'PYTHONPATH': './docutils-manpage-writer/:'+os.environ['PYTHONPATH'],
'PATH': os.environ['PATH'],
}
env = Environment(ENV=ENV)
env.SConsignFile()
env.Export(['env'])
env.SConscript(dirs='test')
#env.SConscript(dirs='examples')
env.Command('pdfbook.1', 'pdfbook.rst',
'python -S ./docutils-manpage-writer/tools/rst2man.py $SOURCE $TARGET')
# create PNG projectlogo for project homepage
env.Command('projectlogo.png', 'projectlogo.svg',
'inkscape -z -f $SOURCE -e $TARGET --export-height=100')
hires_logo = env.Command('build/icons/projectlogo-hires.png', 'projectlogo.svg',
'inkscape -z -f $SOURCE -e $TARGET --dpi=300')
icon_parts = [
env.Command('build/icons/project-${WIDTH}x${HEIGHT}.pnm', hires_logo,
'pngtopnm $SOURCE ' \
'| pnmscale -width=$WIDTH -height=$HEIGHT ' \
'| ppmquant 256 > $TARGET',
WIDTH=w, HEIGHT=w)
for w in (16, 32, 48)
]
env.Command('projectlogo.ico', icon_parts,
'ppmtowinicon $SOURCES > $TARGET')
# -*- mode: python ; coding: utf-8 -*-
#
# Build requirements
# - ImageMagick
#
Import('*')
def postersamples(pages_h, pages_v, which,
tiles_h=0,
numpages=0, reverse=False,
rotate=False, scale_to=100):
inputfile = 'testpage-%s.pdf' % which
geo = '%ix%ia4' % (pages_h, pages_v)
# create pdf poster
poster = env.Command('poster-%s-%s.pdf' % (which, geo),
inputfile,
'pdfposter -ma4 -p$POSTERSIZE $SOURCE $TARGET',
POSTERSIZE=geo)[0]
# montage the ppm imgages to show how they have to be glued together
tiles_h = tiles_h or pages_h
rotate = rotate and ' png:- | convert png:- -rotate -90 ' or ''
page_range = ''
if reverse:
# this is lazy: better way would be to get the number of pages
# from 'poster', but this would requier an extra builder
numpages = numpages or pages_h*pages_v
page_range = repr( range(numpages-1, -1, -1)).replace(' ', '')
env.Command('poster-%s-%s.png' % (which, geo), poster,
"montage $SOURCE$RANGE "
" -background '#336699' -geometry ${SCALE}x+2+2 "
" -tile $P_COLS "
" $ROTATE $TARGET",
P_COLS=tiles_h, ROTATE=rotate, SCALE=scale_to,
RANGE=page_range)
env.Command('testpage-tall.png', 'testpage-tall.pdf',
"convert $SOURCE -background '#336699' $TARGET")
env.Command('testpage-wide.png', 'testpage-wide.pdf',
"convert $SOURCE -background '#336699' $TARGET")
# tall on two landscape sheets
postersamples(1, 2, 'tall', reverse=1)
# tall on two portrait sheets
postersamples(2, 1, 'tall', rotate=0)
# wide on two landscape sheets
postersamples(1, 2, 'wide', rotate=0)
# wide on two portrait sheets
postersamples(2, 1, 'wide')
# wide on landscape sheets (landscape a4 heigh)
postersamples(1, 99, 'wide', scale_to=50, rotate=1)
# wide on portrait sheets (portrait a4 heigh)
postersamples(99, 1, 'wide', scale_to=50, tiles_h=9)
try:
__import__('pkg_resources').declare_namespace(__name__)
except:
# bootstrapping
pass
#!/usr/bin/env python
"""
pdftools.pdfbook - rearrange pages in PDF file into signatures.
"""
#
# Copyright 2010 by Hartmut Goebel <h.goebel@goebel-consult.de>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
__author__ = "Hartmut Goebel <h.goebel@goebel-consult.de>"
__copyright__ = "Copyright 2010 by Hartmut Goebel <h.goebel@goebel-consult.de>"
__licence__ = "GNU General Public License version 3 (GPL v3)"
__version__ = "0.1.0"
from pyPdf.pdf import PdfFileWriter, PdfFileReader, PageObject, \
NameObject, RectangleObject, NullObject, DictionaryObject
import logging
from logging import log
class DecryptionError(ValueError): pass
PAGE_BOXES = ("/MediaBox", "/CropBox")
class _EmptyPage(PageObject):
def __init__(self, pdf):
PageObject.__init__(self, pdf)
self.__setitem__(NameObject('/Type'), NameObject('/Page'))
self.__setitem__(NameObject('/Parent'), NullObject())
self.__setitem__(NameObject('/Resources'), DictionaryObject())
firstpage = pdf.getPage(0)
for attr in PAGE_BOXES:
if firstpage.has_key(attr):
self[NameObject(attr)] = RectangleObject(list(firstpage[attr]))
def bookify(inpdf, outpdf, signature):
def sort_pages(numPages, signature):
maxPage = numPages + (signature - numPages % signature) % signature
for currentpg in xrange(maxPage):
actualpg = currentpg - (currentpg % signature)
if currentpg % 4 in (0, 3):
actualpg += signature-1 - (currentpg % signature)/2
else:
actualpg += (currentpg % signature)/2
if actualpg >= numPages:
yield '*', emptyPage
else:
yield actualpg+1, inpdf.getPage(actualpg)
emptyPage = _EmptyPage(inpdf)
pagelist = []
for num, page in sort_pages(inpdf.numPages, signature):
pagelist.append(num)
outpdf.addPage(page)
log(19, ' '.join('[%s]' % p for p in pagelist))
def password_hook():
import getpass
return getpass.getpass()
def main(opts, infilename, outfilename, password_hook=password_hook):
logging.basicConfig(level=20-opts.verbose, format="%(message)s")
outpdf = PdfFileWriter()
inpdf = PdfFileReader(open(infilename, 'rb'))
if inpdf.isEncrypted:
log(16, 'File is encrypted')
# try empty password first
if not inpdf.decrypt(''):
if not inpdf.decrypt(password_hook()):
raise DecryptionError("Can't decrypt PDF. Wrong Password?")
log(17, 'Signature :', opts.signature)
log(18, 'Number of input pages :', inpdf.numPages)
bookify(inpdf, outpdf, opts.signature)
if not opts.dry_run:
outpdf.write(open(outfilename, 'wb'))
#!/usr/bin/env python
"""
pdftools.pdfbook.cmd - rearrange pages in PDF file into signatures.
"""
#
# Copyright 2010 by Hartmut Goebel <h.goebel@goebel-consult.de>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
__author__ = "Hartmut Goebel <h.goebel@goebel-consult.de>"
__copyright__ = "Copyright 2010 by Hartmut Goebel <h.goebel@goebel-consult.de>"
__licence__ = "GNU General Public License version 3 (GPL v3)"
from . import main, __version__, DecryptionError
def run():
import optparse
parser = optparse.OptionParser('%prog [options] InputFile OutputFile',
version=__version__)
parser.add_option('-v', '--verbose', action='count', default=0,
help='Be verbose. Tell about number of pages rearranged. Can be used more than once to increase the verbosity. ')
parser.add_option('-n', '--dry-run', action='store_true',
help='Show what would have been done, but do not generate files.')
group = parser.add_option_group('Define Target')
group.add_option('-s', '--signature', type=int, default=4,
help='Specify the size of the signature (number '
'of sides which will be folded and bound together). Default: %default')
opts, args = parser.parse_args()
if len(args) != 2:
parser.error('Requires both input and output filename.')
if opts.signature % 4 != 0 or opts.signature < 0:
parser.error('-s/--signature <signature> must be positive and divisible by 4.')
try:
main(opts, *args)
except DecryptionError, e:
raise SystemExit(str(e))
if __name__ == '__main__':
run()
"""
``Pdfbook`` can be used to create a large book by building it from
multple pages and/or printing it on large media. It expects as input a
PDF file, normally printing on a single page. The output is again a
PDF file, maybe containing multiple pages together building the
book.
The input page will be scaled to obtain the desired size.
This is much like ``book`` does for Postscript files, but working
with PDF. Since sometimes book does not like your files converted
from PDF. :-) Indeed ``pdfbook`` was inspired by ``book``.
For more information please refere to the manpage or visit
the `project homepage <http://pdfbook.origo.ethz.ch/>`_.
"""
import ez_setup
ez_setup.use_setuptools()
from setuptools import setup, find_packages
additional_keywords ={}
try:
import py2exe
except ImportError:
py2exe = None
if py2exe:
resources = {
#'other_resources': [(u"VERSIONTAG",1,myrevisionstring)],
'icon_resources' : [(1,'projectlogo.ico')]
}
additional_keywords.update({
'windows': [],
'console': [dict(script='pdfbook', **resources)],
'zipfile': None,
})
setup(
name = "pdftools.pdfbook",
version = "0.1.0",
#scripts = ['pdfbook'],
install_requires = ['pyPdf>1.10', 'setuptools'],
packages=find_packages(exclude=['ez_setup']),
namespace_packages=['pdftools'],
package_data = {
# If any package contains *.txt or *.rst files, include them:
'': ['*.txt', '*.rst'],
# And include any *.msg files found in the 'hello' package, too:
'hello': ['*.msg'],
},
# metadata for upload to PyPI
author = "Hartmut Goebel",
author_email = "h.goebel@goebel-consult.de",
description = "Scale and tile PDF images/pages to print on multiple pages.",
long_description = __doc__,
license = "GPL 3.0",
keywords = "pdf book",
url = "http://pdfbook.origo.ethz.ch/",
download_url = "http://pdfbook.origo.ethz.ch/download",
classifiers = [
'Development Status :: 2 - Alpha',
'Environment :: Console',
'Intended Audience :: Developers',
'Intended Audience :: End Users/Desktop',
'Intended Audience :: System Administrators',
'License :: OSI Approved :: GNU General Public License (GPL)',
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Printing',
'Topic :: Utilities',
],
# these are for easy_install (used by bdist_*)
zip_safe = True,
entry_points = {
"console_scripts": [
"pdfbook = pdftools.pdfbook.cmd:run",
],
},
# these are for py2exe
options = {
# bundle_files 1: bundle everything, including the Python interpreter
# bundle_files 2: bundle everything but the Python interpreter
# bundle_files 3: don't bundle
"py2exe":{"optimize": 2,
"bundle_files": 1,
"includes": [],
}
},
**additional_keywords
)
import glob, os
for fn in glob.glob('*.egg-link'): os.remove(fn)
# -*- mode: python ; coding: utf-8 -*-
#
# Build requirements
# - pdf2ps (comes with ghostscript)
# - psbook
#
Import('*')
"Generate test PDF document for pdfbook."
from reportlab.lib.units import mm
from reportlab.lib.colors import black, white, pink, lightblue
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen.canvas import Canvas
try:
from pdftools import pdfbook
except ImportError:
pdfbook = None
def genTestFile(target, source, env):
"Generate a PDF doc with *very* big page numbers on all pages."
numPages = source[0].read()
size = A4
canv = Canvas(target[0].path, pagesize=size)
for i in range(numPages):
canv.setFont("Helvetica", size[1]*1.2)
x, y = size[0]/2.0, size[1]*0.1
text = u"%s" % (i+1)
if i % 2 == 1:
canv.setStrokeColor(black)
canv.setFillColor(black)
else:
canv.setStrokeColor(lightblue)
canv.setFillColor(lightblue)
canv.rect(0, 0, A4[0], A4[1], fill=True)
if i % 2 == 1:
canv.setFillColor(lightblue)
else:
canv.setFillColor(black)
canv.drawCentredString(x, y, text)
canv.showPage()
canv.save()
def genPDFbook(target, source, env):
class Options: pass
opts = Options()
opts.signature = env['SIGNATURE']
opts.verbose = 1
opts.dry_run = 0
pdfbook.main(opts, source[0].path, target[0].path)
pat_PDF = "test-%02i.pdf"
pat_PS = "test-%02i.ps"
pat_PSBOOK = "test-%02i-%02i.ps"
pat_PDFBOOK = "test-%02i-%02i.pdf"
for numPages in range(1,12+1):
pdf = env.Command(pat_PDF % numPages, Value(numPages), genTestFile)
ps = env.Command(pat_PS % numPages, pdf, 'pdf2ps $SOURCE $TARGET')
for s in [4,8,12]:
env.Command(pat_PSBOOK % (numPages, s), ps,
'psbook -s$SIGNATURE $SOURCE $TARGET', SIGNATURE=s)
if pdfbook:
env.Command(pat_PDFBOOK % (numPages, s), pdf,
genPDFbook, SIGNATURE=s)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment