#!/usr/bin/env python
'''
Copyright (C) 2007 Terry Brown, terry_n_brown@yahoo.com
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 2 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
'''
import inkex
import sys
try:
import xml.etree.ElementTree as ElementTree
except:
try:
from lxml import etree as ElementTree
except:
try:
from elementtree.ElementTree import ElementTree
except:
sys.stderr.write("""Requires ElementTree module, included
in Python 2.5 or supplied by lxml or elementtree modules.
""")
class Nup(inkex.Effect):
def __init__(self):
inkex.Effect.__init__(self)
opts = [('', '--unit', 'string', 'unit', 'px', ''),
('', '--rows', 'int', 'rows', '2', ''),
('', '--cols', 'int', 'cols', '2', ''),
('', '--paddingTop', 'string', 'paddingTop', '', ''),
('', '--paddingBottom', 'string', 'paddingBottom', '', ''),
('', '--paddingLeft', 'string', 'paddingLeft', '', ''),
('', '--paddingRight', 'string', 'paddingRight', '', ''),
('', '--marginTop', 'string', 'marginTop', '', ''),
('', '--marginBottom', 'string', 'marginBottom', '', ''),
('', '--marginLeft', 'string', 'marginLeft', '', ''),
('', '--marginRight', 'string', 'marginRight', '', ''),
('', '--pgSizeX', 'string', 'pgSizeX', '', ''),
('', '--pgSizeY', 'string', 'pgSizeY', '', ''),
('', '--sizeX', 'string', 'sizeX', '', ''),
('', '--sizeY', 'string', 'sizeY', '', ''),
('', '--calculateSize', 'inkbool', 'calculateSize', True, ''),
('', '--pgMarginTop', 'string', 'pgMarginTop', '', ''),
('', '--pgMarginBottom', 'string', 'pgMarginBottom', '', ''),
('', '--pgMarginLeft', 'string', 'pgMarginLeft', '', ''),
('', '--pgMarginRight', 'string', 'pgMarginRight', '', ''),
('', '--showHolder', 'inkbool', 'showHolder', True, ''),
('', '--showCrosses', 'inkbool', 'showCrosses', True, ''),
('', '--showInner', 'inkbool', 'showInner', True, ''),
('', '--showOuter', 'inkbool', 'showOuter', False, ''),
('', '--showInnerBox', 'inkbool', 'showInnerBox', False, ''),
('', '--showOuterBox', 'inkbool', 'showOuterBox', False, ''),
('', '--tab', 'string', 'tab', '', ''),
]
for o in opts:
self.OptionParser.add_option(o[0], o[1], action="store", type=o[2],
dest=o[3], default=o[4], help=o[5])
def effect(self):
showList = []
for i in ['showHolder','showCrosses','showInner','showOuter',
'showInnerBox','showOuterBox',]:
if getattr(self.options, i):
showList.append(i.lower().replace('show', ''))
o = self.options
self.pf = self.GenerateNup(
unit=o.unit,
pgSize=(o.pgSizeX,o.pgSizeY),
pgMargin=(o.pgMarginTop,o.pgMarginRight,o.pgMarginBottom,o.pgMarginLeft),
num=(o.rows,o.cols),
calculateSize = o.calculateSize,
size=(o.sizeX,o.sizeY),
margin=(o.marginTop,o.marginRight,o.marginBottom,o.marginLeft),
padding=(o.paddingTop,o.paddingRight,o.paddingBottom,o.paddingLeft),
show=showList,
)
def setAttr(self, node, name, value):
attr = node.ownerDocument.createAttribute(name)
attr.value = value
node.attributes.setNamedItem(attr)
def output(self):
sys.stdout.write(self.pf)
def expandTuple(self, unit, x, length = 4):
try:
iter(x)
except:
return None
if len(x) != length: x = x*2
if len(x) != length:
raise Exception("expandTuple: requires 2 or 4 item tuple")
try:
return tuple(map(lambda ev: (self.unittouu(str(eval(str(ev)))+unit)/self.unittouu('1px')), x))
except:
return None
def GenerateNup(self,
unit="px",
pgSize=("8.5*96","11*96"),
pgMargin=(0,0),
pgPadding=(0,0),
num=(2,2),
calculateSize=True,
size=None,
margin=(0,0),
padding=(20,20),
show=['default'],
container='svg',
returnTree = False,
):
"""Generate the SVG. Inputs are run through 'eval(str(x))' so you can use
'8.5*72' instead of 612. Margin / padding dimension tuples can be
(top & bottom, left & right) or (top, right, bottom, left).
Keyword arguments:
pgSize -- page size, width x height
pgMargin -- extra space around each page
pgPadding -- added to pgMargin
n -- rows x cols
size -- override calculated size, width x height
margin -- white space around each piece
padding -- inner padding for each piece
show -- list of keywords indicating what to show
- 'crosses' - cutting guides
- 'inner' - inner boundary
- 'outer' - outer boundary
container -- 'svg' or 'g'
returnTree -- whether to return the ElementTree or the string
"""
if 'default' in show:
show = set(show).union(['inner', 'innerbox', 'holder', 'crosses'])
pgMargin = self.expandTuple(unit, pgMargin)
pgPadding = self.expandTuple(unit, pgPadding)
margin = self.expandTuple(unit, margin)
padding = self.expandTuple(unit, padding)
pgSize = self.expandTuple(unit, pgSize, length = 2)
# num = tuple(map(lambda ev: eval(str(ev)), num))
pgEdge = map(sum,zip(pgMargin, pgPadding))
top, right, bottom, left = 0,1,2,3
width, height = 0,1
rows, cols = 0,1
size = self.expandTuple(unit, size, length = 2)
if size == None or calculateSize == True or len(size) < 2 or size[0] == 0 or size[1] == 0:
size = ((pgSize[width]
- pgEdge[left] - pgEdge[right]
- num[cols]*(margin[left] + margin[right])) / num[cols],
(pgSize[height]
- pgEdge[top] - pgEdge[bottom]
- num[rows]*(margin[top] + margin[bottom])) / num[rows]
)
else:
size = self.expandTuple(unit, size, length = 2)
# sep is separation between same points on pieces
sep = (size[width]+margin[right]+margin[left],
size[height]+margin[top]+margin[bottom])
style = 'stroke:#000000;stroke-opacity:1;fill:none;fill-opacity:1;'
padbox = 'rect', {
'x': str(pgEdge[left] + margin[left] + padding[left]),
'y': str(pgEdge[top] + margin[top] + padding[top]),
'width': str(size[width] - padding[left] - padding[right]),
'height': str(size[height] - padding[top] - padding[bottom]),
'style': style,
}
margbox = 'rect', {
'x': str(pgEdge[left] + margin[left]),
'y': str(pgEdge[top] + margin[top]),
'width': str(size[width]),
'height': str(size[height]),
'style': style,
}
doc = ElementTree.ElementTree(ElementTree.Element(container,
{'xmlns:inkscape':"http://www.inkscape.org/namespaces/inkscape",
'xmlns:xlink':"http://www.w3.org/1999/xlink",
'width':str(pgSize[width]),
'height':str(pgSize[height]),
}))
sub = ElementTree.SubElement
root = doc.getroot()
def makeClones(under, to):
for r in range(0,num[rows]):
for c in range(0,num[cols]):
if r == 0 and c == 0: continue
sub(under, 'use', {
'xlink:href': '#' + to,
'transform': 'translate(%f,%f)' %
(c*sep[width], r*sep[height])})
# guidelayer #####################################################
if set(['inner', 'outer']).intersection(show):
layer = sub(root, 'g', {'id':'guidelayer',
'inkscape:groupmode':'layer'})
if 'inner' in show:
padbox[1]['id'] = 'innerguide'
padbox[1]['style'] = padbox[1]['style'].replace('stroke:#000000',
'stroke:#8080ff')
sub(layer, *padbox)
del padbox[1]['id']
padbox[1]['style'] = padbox[1]['style'].replace('stroke:#8080ff',
'stroke:#000000')
makeClones(layer, 'innerguide')
if 'outer' in show:
margbox[1]['id'] = 'outerguide'
margbox[1]['style'] = padbox[1]['style'].replace('stroke:#000000',
'stroke:#8080ff')
sub(layer, *margbox)
del margbox[1]['id']
margbox[1]['style'] = padbox[1]['style'].replace('stroke:#8080ff',
'stroke:#000000')
makeClones(layer, 'outerguide')
# crosslayer #####################################################
if set(['crosses']).intersection(show):
layer = sub(root, 'g', {'id':'cutlayer',
'inkscape:groupmode':'layer'})
if 'crosses' in show:
crosslen = 12
group = sub(layer, 'g', id='cross')
x,y = 0,0
path = 'M%f %f' % (x+pgEdge[left] + margin[left],
y+pgEdge[top] + margin[top]-crosslen)
path += ' L%f %f' % (x+pgEdge[left] + margin[left],
y+pgEdge[top] + margin[top]+crosslen)
path += ' M%f %f' % (x+pgEdge[left] + margin[left]-crosslen,
y+pgEdge[top] + margin[top])
path += ' L%f %f' % (x+pgEdge[left] + margin[left]+crosslen,
y+pgEdge[top] + margin[top])
sub(group, 'path', style=style+'stroke-width:0.05',
d = path, id = 'crossmarker')
for r in 0, 1:
for c in 0, 1:
if r or c:
x,y = c*size[width], r*size[height]
sub(group, 'use', {
'xlink:href': '#crossmarker',
'transform': 'translate(%f,%f)' %
(x,y)})
makeClones(layer, 'cross')
# clonelayer #####################################################
layer = sub(root, 'g', {'id':'clonelayer', 'inkscape:groupmode':'layer'})
makeClones(layer, 'main')
# mainlayer ######################################################
layer = sub(root, 'g', {'id':'mainlayer', 'inkscape:groupmode':'layer'})
group = sub(layer, 'g', {'id':'main'})
if 'innerbox' in show:
sub(group, *padbox)
if 'outerbox' in show:
sub(group, *margbox)
if 'holder' in show:
x, y = (pgEdge[left] + margin[left] + padding[left],
pgEdge[top] + margin[top] + padding[top])
w, h = (size[width] - padding[left] - padding[right],
size[height] - padding[top] - padding[bottom])
path = 'M%f %f' % (x + w/2., y)
path += ' L%f %f' % (x + w, y + h/2.)
path += ' L%f %f' % (x + w/2., y + h)
path += ' L%f %f' % (x, y + h/2.)
path += ' Z'
sub(group, 'path', style=style, d = path)
if returnTree:
return doc
else:
return ElementTree.tostring(root)
e = Nup()
e.affect()