1 #!/usr/bin/env python
2 '''
3 dimension.py
4 An Inkscape effect for adding CAD style dimensions to selected objects
5 in a drawing.
7 It uses the selection's bounding box, so if the bounding box has empty
8 space in the x- or y-direction (such as with some stars) the results
9 will look strange. Strokes might also overlap the edge of the
10 bounding box.
12 The dimension arrows aren't measured: use the "Visualize Path/Measure
13 Path" effect to add measurements.
15 This code contains snippets from existing effects in the Inkscape
16 extensions library, and marker data from markers.svg.
18 Copyright (C) 2007 Peter Lewerin, peter.lewerin@tele2.se
20 This program is free software; you can redistribute it and/or modify
21 it under the terms of the GNU General Public License as published by
22 the Free Software Foundation; either version 2 of the License, or
23 (at your option) any later version.
25 This program is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program; if not, write to the Free Software
32 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 '''
35 import sys, inkex, pathmodifier
36 from simpletransform import *
37 import gettext
38 _ = gettext.gettext
40 try:
41 from subprocess import Popen, PIPE
42 bsubprocess = True
43 except:
44 bsubprocess = False
46 class Dimension(pathmodifier.PathModifier):
47 def __init__(self):
48 inkex.Effect.__init__(self)
49 self.OptionParser.add_option("-x", "--xoffset",
50 action="store", type="float",
51 dest="xoffset", default=100.0,
52 help="x offset of the vertical dimension arrow")
53 self.OptionParser.add_option("-y", "--yoffset",
54 action="store", type="float",
55 dest="yoffset", default=100.0,
56 help="y offset of the horizontal dimension arrow")
57 self.OptionParser.add_option("-t", "--type",
58 action="store", type="string",
59 dest="type", default="geometric",
60 help="Bounding box type")
62 def addMarker(self, name, rotate):
63 defs = self.xpathSingle('/svg:svg//svg:defs')
64 if defs == None:
65 defs = inkex.etree.SubElement(self.document.getroot(),inkex.addNS('defs','svg'))
66 marker = inkex.etree.SubElement(defs ,inkex.addNS('marker','svg'))
67 marker.set('id', name)
68 marker.set('orient', 'auto')
69 marker.set('refX', '0.0')
70 marker.set('refY', '0.0')
71 marker.set('style', 'overflow:visible')
72 marker.set(inkex.addNS('stockid','inkscape'), name)
74 arrow = inkex.etree.Element("path")
75 arrow.set('d', 'M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z ')
76 if rotate:
77 arrow.set('transform', 'scale(0.8) rotate(180) translate(12.5,0)')
78 else:
79 arrow.set('transform', 'scale(0.8) translate(12.5,0)')
80 arrow.set('style', 'fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none')
81 marker.append(arrow)
83 def dimHLine(self, y, xlat):
84 line = inkex.etree.Element("path")
85 x1 = self.bbox[0] - xlat[0] * self.xoffset
86 x2 = self.bbox[1]
87 y = y - xlat[1] * self.yoffset
88 line.set('d', 'M %f %f H %f' % (x1, y, x2))
89 return line
91 def dimVLine(self, x, xlat):
92 line = inkex.etree.Element("path")
93 x = x - xlat[0] * self.xoffset
94 y1 = self.bbox[2] - xlat[1] * self.yoffset
95 y2 = self.bbox[3]
96 line.set('d', 'M %f %f V %f' % (x, y1, y2))
97 return line
99 def effect(self):
100 self.xoffset = self.options.xoffset
101 self.yoffset = self.options.yoffset
103 # query inkscape about the bounding box
104 if len(self.options.ids) == 0:
105 inkex.errormsg(_("Please select an object."))
106 exit()
107 if self.options.type == "geometric":
108 self.bbox = computeBBox(self.selected.values())
109 else:
110 q = {'x':0,'y':0,'width':0,'height':0}
111 file = self.args[-1]
112 id = self.options.ids[0]
113 for query in q.keys():
114 if bsubprocess:
115 p = Popen('inkscape --query-%s --query-id=%s "%s"' % (query,id,file), shell=True, stdout=PIPE, stderr=PIPE)
116 rc = p.wait()
117 q[query] = float(p.stdout.read())
118 err = p.stderr.read()
119 else:
120 f,err = os.popen3('inkscape --query-%s --query-id=%s "%s"' % (query,id,file))[1:]
121 q[query] = float(f.read())
122 f.close()
123 err.close()
124 self.bbox = (q['x'], q['x']+q['width'], q['y'], q['y']+q['height'])
126 # Avoid ugly failure on rects and texts.
127 try:
128 testing_the_water = self.bbox[0]
129 except TypeError:
130 sys.exit(_('Unable to process this object. Try changing it into a path first.'))
132 layer = self.current_layer
134 self.addMarker('Arrow1Lstart', False)
135 self.addMarker('Arrow1Lend', True)
137 group = inkex.etree.SubElement(layer, 'g')
138 # group = inkex.etree.Element("g")
139 group.set('fill', 'none')
140 group.set('stroke', 'black')
142 line = self.dimHLine(self.bbox[2], [0, 1])
143 line.set('marker-start', 'url(#Arrow1Lstart)')
144 line.set('marker-end', 'url(#Arrow1Lend)')
145 line.set('stroke-width', '1')
146 group.append(line)
148 line = self.dimVLine(self.bbox[0], [0, 2])
149 line.set('stroke-width', '0.5')
150 group.append(line)
152 line = self.dimVLine(self.bbox[1], [0, 2])
153 line.set('stroke-width', '0.5')
154 group.append(line)
156 line = self.dimVLine(self.bbox[0], [1, 0])
157 line.set('marker-start', 'url(#Arrow1Lstart)')
158 line.set('marker-end', 'url(#Arrow1Lend)')
159 line.set('stroke-width', '1')
160 group.append(line)
162 line = self.dimHLine(self.bbox[2], [2, 0])
163 line.set('stroke-width', '0.5')
164 group.append(line)
166 line = self.dimHLine(self.bbox[3], [2, 0])
167 line.set('stroke-width', '0.5')
168 group.append(line)
170 for id, node in self.selected.iteritems():
171 group.append(node)
173 layer.append(group)
175 if __name__ == '__main__':
176 e = Dimension()
177 e.affect()
180 # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99