hpgl_decoder.py revision 7cc5285e0a99d8e121e75abfdfea3474dcd6a86d
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński#!/usr/bin/env python
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński# coding=utf-8
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruz'''
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof KosińskiCopyright (C) 2013 Sebastian Wüst, sebi@timewaster.de
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof KosińskiThis program is free software; you can redistribute it and/or modify
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosińskiit under the terms of the GNU General Public License as published by
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosińskithe Free Software Foundation; either version 2 of the License, or
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński(at your option) any later version.
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof KosińskiThis program is distributed in the hope that it will be useful,
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosińskibut WITHOUT ANY WARRANTY; without even the implied warranty of
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof KosińskiMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof KosińskiGNU General Public License for more details.
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof KosińskiYou should have received a copy of the GNU General Public License
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosińskialong with this program; if not, write to the Free Software
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof KosińskiFoundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński'''
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński# standard libraries
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruzfrom StringIO import StringIO
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruzimport math
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruz# local library
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruzimport inkex
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruz
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruz
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruz# TODO: Unittests
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruzclass hpglDecoder:
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruz
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński def __init__(self, hpglString, options):
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński ''' options:
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruz "resolutionX":float
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński "resolutionY":float
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński "showMovements":bool
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński '''
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński self.hpglString = hpglString
ee447ac6bc305fa06a1b8fb7e98a657a3bcfe097Josh Andler self.options = options
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruz self.scaleX = options.resolutionX / 90.0 # dots/inch to dots/pixels
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński self.scaleY = options.resolutionY / 90.0 # dots/inch to dots/pixels
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruz self.warning = ''
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruz self.textMovements = _("Movements")
16e7cf17a1bc8bb0d79dfa6adc4f75b843fb6d16Krzysztof Kosiński self.textPenNumber = _("Pen #")
16e7cf17a1bc8bb0d79dfa6adc4f75b843fb6d16Krzysztof Kosiński
16e7cf17a1bc8bb0d79dfa6adc4f75b843fb6d16Krzysztof Kosiński def getSvg(self):
16e7cf17a1bc8bb0d79dfa6adc4f75b843fb6d16Krzysztof Kosiński # prepare document
16e7cf17a1bc8bb0d79dfa6adc4f75b843fb6d16Krzysztof Kosiński self.doc = inkex.etree.parse(StringIO('<svg xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" width="%s" height="%s"></svg>' %
16e7cf17a1bc8bb0d79dfa6adc4f75b843fb6d16Krzysztof Kosiński (self.options.docWidth, self.options.docHeight)))
9a8c99c1f18c1d69fee4fe12c455121abf6de6f3Krzysztof Kosiński actualLayer = 0
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruz self.layers = {}
58c5902626b1ba0feb295fab124de3160c73fbeaKrzysztof Kosiński if self.options.showMovements:
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruz self.layers[0] = inkex.etree.SubElement(self.doc.getroot(), 'g', {inkex.addNS('groupmode', 'inkscape'): 'layer', inkex.addNS('label', 'inkscape'): self.textMovements})
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński # parse paths
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński hpglData = self.hpglString.split(';')
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński if len(hpglData) < 3:
7128efb0d57297be0fbe4ef91c30fff5d1f94006Jon A. Cruz raise Exception('NO_HPGL_DATA')
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński oldCoordinates = (0.0, self.options.docHeight)
0d68d82e47abab250c99dd534da2e2d26b697b2dKrzysztof Kosiński path = ''
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński for i, command in enumerate(hpglData):
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński if command.strip() != '':
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński if command[:2] == 'IN':
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński # if Initialize command, ignore
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński pass
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński elif command[:2] == 'SP':
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński # if Select Pen command
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński actualLayer = command[2:]
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński self.createLayer(actualLayer)
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński elif command[:2] == 'PU':
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński # if Pen Up command
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński if ' L ' in path:
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński self.addPathToLayer(path, actualLayer)
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński if self.options.showMovements and i != len(hpglData) - 1:
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński path = 'M %f,%f' % oldCoordinates
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński path += ' L %f,%f' % self.getParameters(command[2:])
16a8c7d5e433b176636a4a1260c42ea43932110bKrzysztof Kosiński self.addPathToLayer(path, 0)
a4030d5ca449e7e384bc699cd249ee704faaeab0Chris Morgan path = 'M %f,%f' % self.getParameters(command[2:])
oldCoordinates = self.getParameters(command[2:])
elif command[:2] == 'PD':
# if Pen Down command
parameterString = command[2:]
if parameterString.strip() != '':
parameterString = parameterString.replace(';', '').strip()
parameter = parameterString.split(',')
oldCoordinates = (float(parameter[-2]) / self.scaleX, self.options.docHeight - float(parameter[-1]) / self.scaleX)
for i, param in enumerate(parameter):
if i % 2 == 0:
parameter[i] = str(float(param) / self.scaleX)
else:
parameter[i] = str(self.options.docHeight - float(param) / self.scaleY)
parameterString = ','.join(parameter)
path += ' L %s' % parameterString
else:
self.warning = 'UNKNOWN_COMMANDS'
if ' L ' in path:
self.addPathToLayer(path, actualLayer)
return (self.doc, self.warning)
def createLayer(self, layerNumber):
self.layers[layerNumber] = inkex.etree.SubElement(self.doc.getroot(), 'g',
{inkex.addNS('groupmode', 'inkscape'): 'layer', inkex.addNS('label', 'inkscape'): self.textPenNumber + layerNumber})
def addPathToLayer(self, path, layerNumber):
lineColor = '000000'
if layerNumber == 0:
lineColor = 'ff0000'
inkex.etree.SubElement(self.layers[layerNumber], 'path', {'d': path, 'style': 'stroke:#' + lineColor + '; stroke-width:0.4; fill:none;'})
def getParameters(self, parameterString):
# process coordinates
if parameterString.strip() == '':
return []
# remove command delimiter
parameterString = parameterString.replace(';', '').strip()
# split parameter
parameter = parameterString.split(',')
# convert to svg coordinate system and return
return (float(parameter[0]) / self.scaleX, self.options.docHeight - float(parameter[1]) / self.scaleY)
# vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 fileencoding=utf-8 textwidth=99