diff options
Diffstat (limited to '')
-rwxr-xr-x | share/extensions/print_win32_vector.py | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/share/extensions/print_win32_vector.py b/share/extensions/print_win32_vector.py new file mode 100755 index 0000000..58e598c --- /dev/null +++ b/share/extensions/print_win32_vector.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python +# coding=utf-8 +# +# Copyright (C) 2012 Alvin Penner, penner@vaxxine.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. +# +""" +This extension will generate vector graphics printout, specifically for Windows GDI32. + +This is a modified version of the file dxf_outlines.py by Aaron Spike, aaron@ekips.org +It will write only to the default printer. +The printing preferences dialog will be called. +In order to ensure a pure vector output, use a linewidth < 1 printer pixel + +- see http://www.lessanvaezi.com/changing-printer-settings-using-the-windows-api/ +- get GdiPrintSample.zip at http://archive.msdn.microsoft.com/WindowsPrintSample + +""" + +import sys +import ctypes + +import inkex +from inkex import PathElement, Rectangle, Group, Use, Transform +from inkex.paths import Path + +if sys.platform.startswith("win"): + myspool = ctypes.WinDLL("winspool.drv") + mygdi = ctypes.WinDLL("gdi32.dll") +else: + myspool = None + mygdi = None + +LOGBRUSH = ctypes.c_long * 3 +DM_IN_PROMPT = 4 # call printer property sheet +DM_OUT_BUFFER = 2 # write to DEVMODE structure + + +class PrintWin32Vector(inkex.EffectExtension): + def __init__(self): + super(PrintWin32Vector, self).__init__() + self.visibleLayers = True # print only visible layers + + def process_shape(self, node, mat): + """Process shape""" + rgb = (0, 0, 0) # stroke color + fillcolor = None # fill color + stroke = 1 # pen width in printer pixels + # Very NB : If the pen width is greater than 1 then the output will Not be a vector output ! + style = node.style + if style: + if "stroke" in style: + if ( + style["stroke"] + and style["stroke"] != "none" + and style["stroke"][0:3] != "url" + ): + rgb = inkex.Color(style["stroke"]).to_rgb() + if "stroke-width" in style: + stroke = self.svg.unittouu(style["stroke-width"]) / self.svg.unittouu( + "1px" + ) + stroke = int(stroke * self.scale) + if "fill" in style: + if ( + style["fill"] + and style["fill"] != "none" + and style["fill"][0:3] != "url" + ): + fill = inkex.Color(style["fill"]).to_rgb() + fillcolor = fill[0] + 256 * fill[1] + 256 * 256 * fill[2] + color = rgb[0] + 256 * rgb[1] + 256 * 256 * rgb[2] + if isinstance(node, PathElement): + p = node.path.to_superpath() + if not p: + return + elif isinstance(node, Rectangle): + x = float(node.get("x")) + y = float(node.get("y")) + width = float(node.get("width")) + height = float(node.get("height")) + p = [[[x, y], [x, y], [x, y]]] + p.append([[x + width, y], [x + width, y], [x + width, y]]) + p.append( + [ + [x + width, y + height], + [x + width, y + height], + [x + width, y + height], + ] + ) + p.append([[x, y + height], [x, y + height], [x, y + height]]) + p.append([[x, y], [x, y], [x, y]]) + p = [p] + else: + return + mat += node.transform + p = Path(p).transform(Transform(mat)).to_arrays() + hPen = mygdi.CreatePen(0, stroke, color) + mygdi.SelectObject(self.hDC, hPen) + self.emit_path(p) + if fillcolor is not None: + brush = LOGBRUSH(0, fillcolor, 0) + hBrush = mygdi.CreateBrushIndirect(ctypes.addressof(brush)) + mygdi.SelectObject(self.hDC, hBrush) + mygdi.BeginPath(self.hDC) + self.emit_path(p) + mygdi.EndPath(self.hDC) + mygdi.FillPath(self.hDC) + return + + def emit_path(self, p): + for sub in p: + mygdi.MoveToEx(self.hDC, int(sub[0][1][0]), int(sub[0][1][1]), None) + POINTS = ctypes.c_long * (6 * (len(sub) - 1)) + points = POINTS() + for i in range(len(sub) - 1): + points[6 * i] = int(sub[i][2][0]) + points[6 * i + 1] = int(sub[i][2][1]) + points[6 * i + 2] = int(sub[i + 1][0][0]) + points[6 * i + 3] = int(sub[i + 1][0][1]) + points[6 * i + 4] = int(sub[i + 1][1][0]) + points[6 * i + 5] = int(sub[i + 1][1][1]) + mygdi.PolyBezierTo(self.hDC, ctypes.addressof(points), 3 * (len(sub) - 1)) + return + + def process_clone(self, node): + trans = node.get("transform") + x = node.get("x") + y = node.get("y") + mat = Transform([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) + if trans: + mat *= Transform(trans) + if x: + mat *= Transform([[1.0, 0.0, float(x)], [0.0, 1.0, 0.0]]) + if y: + mat *= Transform([[1.0, 0.0, 0.0], [0.0, 1.0, float(y)]]) + # push transform + if trans or x or y: + self.groupmat.append(Transform(self.groupmat[-1]) * mat) + # get referenced node + refnode = node.href + if refnode is not None: + if isinstance(refnode, inkex.Group): + self.process_group(refnode) + elif refnode.tag == "svg:use": + self.process_clone(refnode) + else: + self.process_shape(refnode, self.groupmat[-1]) + # pop transform + if trans or x or y: + self.groupmat.pop() + + def process_group(self, group): + if isinstance(group, inkex.Layer): + style = group.style + if "display" in style: + if style["display"] == "none" and self.visibleLayers: + return + trans = group.get("transform") + if trans: + self.groupmat.append(Transform(self.groupmat[-1]) * Transform(trans)) + for node in group: + if isinstance(node, Group): + self.process_group(node) + elif isinstance(node, Use): + self.process_clone(node) + else: + self.process_shape(node, self.groupmat[-1]) + if trans: + self.groupmat.pop() + + def doc_name(self): + docname = self.svg.xpath("@sodipodi:docname") + if not docname: + docname = ["New document 1"] + return ctypes.create_string_buffer( + ("Inkscape " + docname[0].split("\\")[-1]).encode("ascii", "replace") + ) + + def effect(self): + pcchBuffer = ctypes.c_long() + myspool.GetDefaultPrinterA( + None, ctypes.byref(pcchBuffer) + ) # get length of printer name + pname = ctypes.create_string_buffer(pcchBuffer.value) + myspool.GetDefaultPrinterA(pname, ctypes.byref(pcchBuffer)) # get printer name + hPrinter = ctypes.c_long() + if myspool.OpenPrinterA(pname.value, ctypes.byref(hPrinter), None) == 0: + return inkex.errormsg(_("Failed to open default printer")) + + # get printer properties dialog + + pcchBuffer = myspool.DocumentPropertiesA(0, hPrinter, pname, None, None, 0) + pDevMode = ctypes.create_string_buffer( + pcchBuffer + 100 + ) # allocate extra just in case + pcchBuffer = myspool.DocumentPropertiesA( + 0, + hPrinter, + pname, + ctypes.byref(pDevMode), + None, + DM_IN_PROMPT + DM_OUT_BUFFER, + ) + myspool.ClosePrinter(hPrinter) + if pcchBuffer != 1: # user clicked Cancel + exit() + + # initiallize print document + + lpszDocName = self.doc_name() + DOCINFO = ctypes.c_long * 5 + docInfo = DOCINFO(20, ctypes.addressof(lpszDocName), 0, 0, 0) + self.hDC = mygdi.CreateDCA(None, pname, None, ctypes.byref(pDevMode)) + if mygdi.StartDocA(self.hDC, ctypes.byref(docInfo)) < 0: + exit() # user clicked Cancel + + self.scale = ( + ord(pDevMode[58]) + 256.0 * ord(pDevMode[59]) + ) / 96 # use PrintQuality from DEVMODE + self.scale /= self.svg.unittouu("1px") + h = self.svg.unittouu(self.svg.xpath("@height")[0]) + doc = self.document.getroot() + # process viewBox height attribute to correct page scaling + viewBox = doc.get("viewBox") + if viewBox: + viewBox2 = viewBox.split(",") + if len(viewBox2) < 4: + viewBox2 = viewBox.split(" ") + self.scale *= h / self.svg.unittouu(self.addDocumentUnit(viewBox2[3])) + self.groupmat = [[[self.scale, 0.0, 0.0], [0.0, self.scale, 0.0]]] + self.process_group(doc) + mygdi.EndDoc(self.hDC) + + +if __name__ == "__main__": + PrintWin32Vector().run() |