diff options
Diffstat (limited to '')
-rwxr-xr-x | share/extensions/layout_nup.py | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/share/extensions/layout_nup.py b/share/extensions/layout_nup.py new file mode 100755 index 0000000..db7a544 --- /dev/null +++ b/share/extensions/layout_nup.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python +# coding=utf-8 +# +# 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. +# +from __future__ import absolute_import, unicode_literals + +import inkex +from inkex import Use, Rectangle +from inkex.base import SvgOutputMixin +from inkex.localization import inkex_gettext as _ + + +class Nup(inkex.OutputExtension, SvgOutputMixin): + """N-up Layout generator""" + + def add_arguments(self, pars): + pars.add_argument("--unit", default="px") + pars.add_argument("--rows", type=int, default=2) + pars.add_argument("--cols", type=int, default=2) + pars.add_argument("--paddingTop", type=float) + pars.add_argument("--paddingBottom", type=float) + pars.add_argument("--paddingLeft", type=float) + pars.add_argument("--paddingRight", type=float) + pars.add_argument("--marginTop", type=float) + pars.add_argument("--marginBottom", type=float) + pars.add_argument("--marginLeft", type=float) + pars.add_argument("--marginRight", type=float) + pars.add_argument("--pgMarginTop", type=float) + pars.add_argument("--pgMarginBottom", type=float) + pars.add_argument("--pgMarginLeft", type=float) + pars.add_argument("--pgMarginRight", type=float) + pars.add_argument("--pgSizeX", type=float) + pars.add_argument("--pgSizeY", type=float) + pars.add_argument("--sizeX", type=float) + pars.add_argument("--sizeY", type=float) + pars.add_argument("--calculateSize", type=inkex.Boolean, default=True) + pars.add_argument("--showHolder", type=inkex.Boolean, default=True) + pars.add_argument("--showCrosses", type=inkex.Boolean, default=True) + pars.add_argument("--showInner", type=inkex.Boolean, default=True) + pars.add_argument("--showOuter", type=inkex.Boolean, default=False) + pars.add_argument("--showInnerBox", type=inkex.Boolean, default=False) + pars.add_argument("--showOuterBox", type=inkex.Boolean, default=False) + pars.add_argument("--tab") + + def save(self, stream): + show_list = [] + for i in [ + "showHolder", + "showCrosses", + "showInner", + "showOuter", + "showInnerBox", + "showOuterBox", + ]: + if getattr(self.options, i): + show_list.append(i.lower().replace("show", "")) + opt = self.options + ret = self.generate_nup( + unit=opt.unit, + pgSize=(opt.pgSizeX, opt.pgSizeY), + pgMargin=( + opt.pgMarginTop, + opt.pgMarginRight, + opt.pgMarginBottom, + opt.pgMarginLeft, + ), + num=(opt.rows, opt.cols), + calculateSize=opt.calculateSize, + size=(opt.sizeX, opt.sizeY), + margin=(opt.marginTop, opt.marginRight, opt.marginBottom, opt.marginLeft), + padding=( + opt.paddingTop, + opt.paddingRight, + opt.paddingBottom, + opt.paddingLeft, + ), + show=show_list, + ) + if ret: + stream.write(ret) + + def expandTuple(self, unit, x, length=4): + try: + iter(x) + except: + return None + + if len(x) != length: + x *= 2 + if len(x) != length: + raise Exception("expandTuple: requires 2 or 4 item tuple") + try: + return tuple( + map( + lambda ev: ( + self.svg.unittouu(str(eval(str(ev))) + unit) + / self.svg.unittouu("1px") + ), + x, + ) + ) + except: + return None + + def generate_nup( + 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"], + ): + """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 + """ + + 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)) + + if not pgMargin or not pgPadding: + return inkex.errormsg(_("No padding or margin available.")) + + page_edge = list(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 is None + or calculateSize + or len(size) < 2 + or size[0] == 0 + or size[1] == 0 + ): + size = ( + ( + pgSize[width] + - page_edge[left] + - page_edge[right] + - num[cols] * (margin[left] + margin[right]) + ) + / num[cols], + ( + pgSize[height] + - page_edge[top] + - page_edge[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 = Rectangle( + x=str(page_edge[left] + margin[left] + padding[left]), + y=str(page_edge[top] + margin[top] + padding[top]), + width=str(size[width] - padding[left] - padding[right]), + height=str(size[height] - padding[top] - padding[bottom]), + style=style, + ) + margbox = Rectangle( + x=str(page_edge[left] + margin[left]), + y=str(page_edge[top] + margin[top]), + width=str(size[width]), + height=str(size[height]), + style=style, + ) + + doc = self.get_template(width=pgSize[width], height=pgSize[height]) + svg = doc.getroot() + + def make_clones(under, to): + for row in range(0, num[rows]): + for col in range(0, num[cols]): + if row == 0 and col == 0: + continue + use = under.add(Use()) + use.set("xlink:href", "#" + to) + use.transform.add_translate(col * sep[width], row * sep[height]) + + # guidelayer ##################################################### + if {"inner", "outer"}.intersection(show): + layer = svg.add(inkex.Layer.new("Guide Layer")) + if "inner" in show: + ibox = layer.add(padbox.copy()) + ibox.style["stroke"] = "#8080ff" + ibox.set("id", "innerguide") + make_clones(layer, "innerguide") + if "outer" in show: + obox = layer.add(margbox.copy()) + obox.style["stroke"] = "#8080ff" + obox.set("id", "outerguide") + make_clones(layer, "outerguide") + + # crosslayer ##################################################### + if {"crosses"}.intersection(show): + layer = svg.add(inkex.Layer.new("Cut Layer")) + + if "crosses" in show: + crosslen = 12 + group = layer.add(inkex.Group(id="cross")) + x, y = 0, 0 + path = "M%f %f" % ( + x + page_edge[left] + margin[left], + y + page_edge[top] + margin[top] - crosslen, + ) + path += " L%f %f" % ( + x + page_edge[left] + margin[left], + y + page_edge[top] + margin[top] + crosslen, + ) + path += " M%f %f" % ( + x + page_edge[left] + margin[left] - crosslen, + y + page_edge[top] + margin[top], + ) + path += " L%f %f" % ( + x + page_edge[left] + margin[left] + crosslen, + y + page_edge[top] + margin[top], + ) + group.add( + inkex.PathElement( + style=style + "stroke-width:0.05", d=path, id="crossmarker" + ) + ) + for row in 0, 1: + for col in 0, 1: + if row or col: + cln = group.add(Use()) + cln.set("xlink:href", "#crossmarker") + cln.transform.add_translate( + col * size[width], row * size[height] + ) + make_clones(layer, "cross") + + # clonelayer ##################################################### + layer = svg.add(inkex.Layer.new("Clone Layer")) + make_clones(layer, "main") + + # mainlayer ###################################################### + layer = svg.add(inkex.Layer.new("Main Layer")) + group = layer.add(inkex.Group(id="main")) + + if "innerbox" in show: + group.add(padbox) + if "outerbox" in show: + group.add(margbox) + if "holder" in show: + x, y = ( + page_edge[left] + margin[left] + padding[left], + page_edge[top] + margin[top] + padding[top], + ) + w, h = ( + size[width] - padding[left] - padding[right], + size[height] - padding[top] - padding[bottom], + ) + path = "M{:f} {:f}".format(x + w / 2.0, y) + path += " L{:f} {:f}".format(x + w, y + h / 2.0) + path += " L{:f} {:f}".format(x + w / 2.0, y + h) + path += " L{:f} {:f}".format(x, y + h / 2.0) + path += " Z" + group.add(inkex.PathElement(style=style, d=path)) + + return svg.tostring() + + +if __name__ == "__main__": + Nup().run() |