diff --git a/README.md b/README.md
index 5954e09..7966918 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,30 @@
# inkscape_uprint
Inkscape plugin to create multiple clones of the same object with pattern substitution
+
+## Install
+
+copy the source content into your inkscape extension directory:
+
+ ${HOME}/.config/inkscape/extensions/inkscape_uprint
+
+or equivalently get it using git clone:
+
+ cd ${HOME}/.config/inkscape/extensions/
+ git clone[https://gitea.mildstone.org/YFinGames/inkscape_uprint.git]()
+
+reload inkscape
+
+find the extension in **Extensions -> Renderg -> InkscapeUprint**
+
+## INSTRUCTIONS:
+
+1. Create any shape with text wherever you like;
+ place text elements in a frame as much as possible ( multiline substitution works in framed text only )
+2. place {{tags}} where you like, each tag is eventually searched in the first line of the xlsx file ( the header ) and a set of replicas of the selection will be generatend in a separate page, one for each line of the xlsx. Any keyword can be used as a tag and it will be substituted unless wrapped between double curly braces.
+3. apply any transform to the shape lou like
+4. group anything together but do not apply any transform to the group afterwards. If you need to ungroup make what you like ( translate, rotate, scale ) and then regroup everything together. The changes applied to the toplevel group are affecting the position of the shape after the placement on the output pages.
+5. Select the group and apply InkscapeUprint. The parameters are:
+ - padding: padding space among placed shapes
+ - page margins: From the page to the page collection
+ - The size of the output pages is copied from the model one
diff --git a/example.svg b/example.svg
new file mode 100644
index 0000000..ab9c204
--- /dev/null
+++ b/example.svg
@@ -0,0 +1,385 @@
+
+
+
+
diff --git a/example.xlsx b/example.xlsx
index b070051..5fa1ff9 100644
Binary files a/example.xlsx and b/example.xlsx differ
diff --git a/inkscape_uprint.inx b/inkscape_uprint.inx
index 75b09b8..b4884b2 100644
--- a/inkscape_uprint.inx
+++ b/inkscape_uprint.inx
@@ -7,79 +7,35 @@
- 0
- 0
+
+ 2
- px
- mm
-
-
- 14
- 0
- 0
- 0
- 0
- 0
+
+
+ 10
+ 0
+ 0
+ 0
+ 0
-
-
-
- 9
- 0
- 0
- 0
- 0
- 0
-
-
-
- 21
- 0
- 0
- 0
- 0
- 0
-
- -->
diff --git a/inkscape_uprint.py b/inkscape_uprint.py
index a934ee0..647fae1 100755
--- a/inkscape_uprint.py
+++ b/inkscape_uprint.py
@@ -37,6 +37,7 @@ dec = decimal.Decimal
+
# ['NAMESPACE', 'PARSER', 'TAG', 'WRAPPED_ATTRS', '__abstractmethods__', '__bool__',
# '__class__', '__contains__', '__copy__', '__deepcopy__', '__delattr__', '__delitem__',
# '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__',
@@ -71,13 +72,16 @@ dec = decimal.Decimal
# |_| |_|
-class _Spacing():
- size = 0.
- padding = 0.
- margin_top = 0.
- margin_bottom = 0.
- margin_left = 0.
- margin_right = 0.
+# class _Spacing():
+# size = 0.
+# padding = 0.
+# margin_top = 0.
+# margin_bottom = 0.
+# margin_left = 0.
+# margin_right = 0.
+
+
+
class InkscapeUprint(inkex.Effect):
def __init__(self):
@@ -105,79 +109,54 @@ class InkscapeUprint(inkex.Effect):
type=str,
dest="subst_file", default='',
help="xlsx file to load substitutions")
- # self.arg_parser.add_argument("--font_family",
- # type=str,
- # dest="font_family", default="Apple Garamond",
- # help="Font family to use for days and labels")
- # self.arg_parser.add_argument("--year",
- # type=int,
- # dest="year", default=1970,
- # help="Calendar Year")
- # self.arg_parser.add_argument("-m", "--month",
- # type=str,
- # dest="month", default="Gennaio",
- # help="month to render")
- # self.arg_parser.add_argument("--dayfrom",
- # type=int,
- # dest="dayfrom", default=1,
- # help="Range to render day from")
- # self.arg_parser.add_argument("--dayto",
- # type=int,
- # dest="dayto", default=31,
- # help="Range to render day to")
- # self.arg_parser.add_argument("--day_text_size",
- # type=float,
- # dest="day_text_size", default=12,
- # help="Text size of day")
- # self.arg_parser.add_argument("--saint_text_size",
- # type=float,
- # dest="saint_text_size", default=8,
- # help="Text size of saint")
- # spacing
- # self.__elements = ['day', 'wday', 'saint', 'moon']
self.__element_spacing = {
'size' : 0,
'padding' : 0,
- 'margin-top' : 5,
- 'margin-bottom' : 5,
- 'margin-left' : 5,
- 'margin-right' : 5
+ 'margin-top' : 0,
+ 'margin-bottom' : 0,
+ 'margin-left' : 0,
+ 'margin-right' : 0
}
self.__page_spacing = {
'size' : 0,
'padding' : 0,
- 'margin-top' : 5,
- 'margin-bottom' : 5,
- 'margin-left' : 5,
- 'margin-right' : 5
+ 'margin-top' : 0,
+ 'margin-bottom' : 0,
+ 'margin-left' : 0,
+ 'margin-right' : 0
}
- # for el in self.__elements:
- # for sk in self.__spacing.keys():
- # sk = sk.replace('-','_')
- # self.arg_parser.add_argument("--" + el + "_" + sk,
- # type=float,
- # dest=el + "_" + sk,
- # help="Spacing " + el + " " + sk)
- self.pages = []
+ for sk in self.__element_spacing.keys():
+ sk = sk.replace('-','_')
+ self.arg_parser.add_argument("--element_" + sk,
+ type=float,
+ dest="element_" + sk,
+ help="Spacing element" + " " + sk)
+ for sk in self.__page_spacing.keys():
+ sk = sk.replace('-','_')
+ self.arg_parser.add_argument("--page_" + sk,
+ type=float,
+ dest="page_" + sk,
+ help="Spacing page" + " " + sk)
+
- def svg_traverse_node(self, node, tag=None):
- def _tag(tag):
- return re.sub(r'\{.*?\}', r'', tag)
+ def svg_traverse_node(self, node, tag=None, exclude=None):
+ def _tag(t):
+ return re.sub(r'\{.*?\}', r'', t)
l = []
if isinstance(node, etree._Element):
- if tag is None or _tag(node.tag) == tag:
+ if (tag is None or _tag(node.tag) == tag) and (exclude is None or _tag(node.tag) != exclude):
l.append(node)
for child in node.getchildren():
- l.extend(self.svg_traverse_node(child))
+ l.extend(self.svg_traverse_node(child, tag=tag, exclude=exclude))
elif isinstance(node, etree._Comment):
pass
elif isinstance(node, list):
for child in node:
- l.extend(self.svg_traverse_node(child))
+ l.extend(self.svg_traverse_node(child, tag=tag, exclude=exclude))
return l
def replace_text(self, selected, substs):
@@ -189,31 +168,34 @@ class InkscapeUprint(inkex.Effect):
for t in tspans:
if t.text is None: continue
t.text = patt.sub(v, t.text)
- multilines = t.text.split('\n')
- if len(multilines) > 1:
- t.text = multilines[0]
- for i in range(1, len(multilines)):
- new_t = etree.Element('tspan', nsmap=t.nsmap)
- new_t.text = multilines[i]
- new_t.tail = t.tail
- new_t.attrib.update(t.attrib) # copy style
- p = t.getparent()
- p.insert(p.index(t) + 1, new_t)
+ # multilines = t.text.split('\n')
+ # if len(multilines) > 1:
+ # t.text = multilines[0]
+ # for i in range(1, len(multilines)):
+ # new_t = etree.Element('tspan', nsmap=t.nsmap)
+ # new_t.text = multilines[i]
+ # new_t.tail = t.tail
+ # new_t.attrib.update(t.attrib) # copy style
+ # p = t.getparent()
+ # p.insert(p.index(t) + 1, new_t)
def duplicate_object(self, parent, selected):
duplicated = []
+ import copy
for node in selected:
- duplicated.append(etree.Element(node.tag, node.attrib, nsmap=node.nsmap))
- self.duplicate_object(duplicated[-1], node.getchildren())
- duplicated[-1].text = node.text
- if 'transform' in node.attrib:
- duplicated[-1].set('transform', node.attrib['transform'])
+ new_node = node.duplicate()
+ duplicated.append(new_node)
+ # duplicated.append(etree.Element(node.tag, node.attrib, nsmap=node.nsmap))
+ # # # inkex.utils.debug(' node.text = ' + str(type(node.text)))
+ # duplicated[-1].tail = node.tail
+ # duplicated[-1].text = node.text
+ # self.duplicate_object(duplicated[-1], node.getchildren())
parent.append(duplicated[-1])
return duplicated
def translate_object(self, obj, x, y):
- trans = 'translate(' + str(x) + ',' + str(y) + ')'
+ trans = 'translate(' + str(float(x)) + ',' + str(float(y)) + ')'
for d in obj:
if 'transform' in d.attrib: d.set('transform', d.get('transform') + ' ' + trans)
else: d.set('transform', trans)
@@ -222,68 +204,92 @@ class InkscapeUprint(inkex.Effect):
if elements is None:
return
first_page = self.svg.getElement("//inkscape:page")
- parent = first_page.getparent()
- pages = [ node for node in self.svg_traverse_node(parent, tag='inkscape:page')]
- rightmost_page = max(pages, key=lambda p: p.get('x'))
-
+ if first_page is not None:
+ page_width = float(first_page.get('width'))
+ page_height = float(first_page.get('height'))
+ view = first_page.getparent()
+ else:
+ view = self.svg.getElement("//sodipodi:namedview")
+ if view is None:
+ inkex.utils.errormsg('No view found')
+ return
+ page_width = self.svg.get_page_bbox().width
+ page_height = self.svg.get_page_bbox().height
+
+ pages = [ node for node in self.svg_traverse_node(view, tag='inkscape:page')]
+
# the reference page is always the first one
- pgbbox = {"width": first_page.get('width'), "height": first_page.get('height')}
- elbbox = self.svg.selected.bounding_box()
- # number of elements per page
- nx = int (( float(pgbbox['width']) - self.__page_spacing['margin-left'] - self.__page_spacing['margin-right'] ) / \
- ( elbbox.width + self.__element_spacing['margin-left'] + self.__element_spacing['margin-right'] ))
- ny = int (( float(pgbbox['height']) - self.__page_spacing['margin-top'] - self.__page_spacing['margin-bottom'] ) / \
- ( elbbox.height + self.__element_spacing['margin-top'] + self.__element_spacing['margin-bottom'] ))
+ pgbbox = {"width": page_width, "height": page_height}
+ elbbox = self.bbox
+ nx = int (( float(pgbbox['width']) - self.__page_spacing['margin-left'] - self.__page_spacing['margin-right'] - self.__element_spacing['padding']) / \
+ ( elbbox.width + self.__element_spacing['padding'] ))
+ ny = int (( float(pgbbox['height']) - self.__page_spacing['margin-top'] - self.__page_spacing['margin-bottom'] - self.__element_spacing['padding'] ) / \
+ ( elbbox.height + self.__element_spacing['padding'] ))
# remove model position
- correct_x = self.svg.selected.bounding_box().left
- correct_y = self.svg.selected.bounding_box().top
+ correct_x = self.bbox.left
+ correct_y = self.bbox.top
# compute page position zero
- px = float(rightmost_page.get('x')) + float(rightmost_page.get('width'))
- py = float(rightmost_page.get('y'))
+ if len(pages) > 0:
+ rightmost_page = max(pages, key=lambda p: p.get('x'))
+ px = float(rightmost_page.get('x'))
+ py = float(rightmost_page.get('y'))
+ else:
+ px = 0.
+ py = 0.
# place elements
for i, el in enumerate(elements):
# create new page if needed
+ # inkex.utils.debug('nx: '+str(nx) + ' ny: '+str(ny))
if i % int(nx * ny) == 0:
- new_page = etree.Element('{http://www.inkscape.org/namespaces/inkscape}page', nsmap=first_page.nsmap)
- new_page.set('x', str(float(rightmost_page.get('x')) + float(rightmost_page.get('width')) + 20 ))
- new_page.set('y', str(float(rightmost_page.get('y')) + 0 ))
- new_page.set('width', str(float(first_page.get('width'))))
- new_page.set('height', str(float(first_page.get('height'))))
- parent.append(new_page)
+ new_page = etree.Element('{http://www.inkscape.org/namespaces/inkscape}page', nsmap=view.nsmap)
+ new_page.set('x', str(px + ( page_width + self.__page_spacing['padding'] )))
+ new_page.set('y', str(py) )
+ new_page.set('width', str(page_width))
+ new_page.set('height', str(page_height))
+ view.append(new_page)
rightmost_page = new_page
px = float(rightmost_page.get('x'))
py = float(rightmost_page.get('y'))
- x = px + (i % nx) * ( elbbox.width + self.__element_spacing['margin-left'] + self.__element_spacing['margin-right'] ) + self.__page_spacing['margin-left']
- y = py + (i // nx) * ( elbbox.height + self.__element_spacing['margin-top'] + self.__element_spacing['margin-bottom'] ) + self.__page_spacing['margin-top']
- self.translate_object(el, x-correct_x, y-correct_y)
+ x = px + (i % nx) * ( elbbox.width + self.__element_spacing['padding'] ) \
+ + self.__page_spacing['margin-left'] + self.__element_spacing['padding']
+
+ y = py + (i % (nx*ny) // nx) * ( elbbox.height + self.__element_spacing['padding'] ) \
+ + self.__page_spacing['margin-top'] + self.__element_spacing['padding']
+
+ self.translate_object(el, x - correct_x, y - correct_y)
+
- # inkex.utils.errormsg("pages:" + repr(pages))
- # for p in pages:
- # inkex.utils.errormsg("p:" + str(p))
- pass
+ def compute_bounding_box(self, selected):
+ _tag = lambda t: re.sub(r'\{.*?\}', r'', t)
+ objs = [ n for n in self.svg_traverse_node(selected[0]) if _tag(n.tag) not in ['text', 'tspan','g'] ]
+ l = inkex.elements._selected.ElementList(self.svg, objs)
+ return l.bounding_box()
+
+
def effect(self):
-
- # for el in self.options.element_spacing:
- # self.__element_spacing[el] = self.svg.unittouu(str(self.options.element_spacing[el]) + self.options.units)
- # for el in self.options.page_spacing:
- # self.__page_spacing[el] = self.svg.unittouu(str(self.options.page_spacing[el]) + self.options.units)
-
+ # inkex.utils.debug('inkex: ' + inkex.__file__)
parent = self.svg.get_current_layer()
selected = self.svg.selected
+ for k in self.__page_spacing.keys():
+ self.__page_spacing[k] = self.svg.unittouu( str(self.options.__dict__['page_'+k.replace('-','_')]) + self.options.units )
+ for k in self.__element_spacing.keys():
+ self.__element_spacing[k] = self.svg.unittouu(str(self.options.__dict__['element_'+k.replace('-','_')]) + self.options.units)
+
if selected:
- self.bbox = selected.bounding_box()
- self.x_offset = self.svg.unittouu(str(self.options.x) + self.options.units)
- self.y_offset = self.svg.unittouu(str(self.options.y) + self.options.units)
+ self.bbox = self.compute_bounding_box(selected)
+ if self.bbox is None:
+ inkex.utils.errormsg('bbox is None')
+ # self.x_offset = self.svg.unittouu(str(self.options.x) + self.options.units)
+ # self.y_offset = self.svg.unittouu(str(self.options.y) + self.options.units)
else:
inkex.utils.errormsg("Error - No object selected")
- # self.place_in_pages()
return
wb = openpyxl.load_workbook(self.options.subst_file)