Files
piccreate/libs/piccreate.py
T
2012-08-17 03:04:15 +02:00

389 lines
13 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
__version__ = '0.0.1'
__author__ = 'Michael Stangl (mistangl[-a-]syntops.de)'
__copyright__ = 'Copyright 2010, Michael Stangl'
__license__ = 'BSD'
import optparse
import os
import ConfigParser
from utils import CSVImport, CompleteEnding, get_translation
from django.utils.encoding import smart_str, smart_unicode
from string import Template
class PicError(Exception):
"""class for Picture createion exceptions in this module."""
pass
def create_picture_frame( config ):
params = dict()
params['width'] = config.get('globals', 'width')
params['height'] = config.get('globals', 'height')
params['background'] = config.get('globals', 'background')
#echo '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">'
begintpl = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
width="${width}"
height="${height}"
>
'''
imagetpl = ''' <image width="${width}" height="${height}"
xlink:href="file://${background}">
</image>
'''
svgbegin = Template( begintpl ).substitute(params)
# falls ein Hintergrundbild verwendet werden soll, dann das genauso gross
# machen...
try:
background = config.get('globals', 'background')
font_path = os.path.expandvars(os.path.join('$PICCREATE_BACKGROUNDS', background ))
params['background'] = os.path.normpath(font_path)
svgbegin = svgbegin + Template( imagetpl ).substitute(params)
except ConfigParser.NoOptionError:
# dann eben kein Hintergrundbild
pass
svgend = '</svg>'
return svgbegin, svgend
def write_font_line( content, font, params ):
''' erzeugt den SVG Teil des Bildes aus Einzelkomponenten
'''
w = Word(content, font, params)
wordsvg = w.GetSVG()
# falls das Wort nun skaliert werden soll
if params.has_key('scale'):
params['xscale'] = params['scale']
params['yscale'] = params['scale']
if not params.has_key('xscale'):
params['xscale'] = 1.0
if not params.has_key('yscale'):
params['yscale'] = 1.0
pic_width = float(params['width'])
pic_height = float(params['height'])
if params.has_key('autoscale'):
wordlength = w.calc_length()
wordheight = w.calc_height()
#print wordlength, wordheight
#print pic_width, pic_height
fw = pic_width / wordlength
fh = pic_height / wordheight
#print fw, fh
if fw < fh: scale_faktor = fw
else: scale_faktor = fh
params['xscale'] = scale_faktor
params['yscale'] = scale_faktor
else:
scale_faktor = 1.0
# berechne immer den richtigen Startpunkt, je nach Bündigkeit
if params.has_key('align'):
xstart = float(params['x'])
if params['align'] == 'left':
x = xstart
elif params['align'] == 'center':
word_length = w.calc_length()
x = xstart * scale_faktor + (pic_width - word_length * scale_faktor)/2.0
else:
word_length = w.calc_length()
x = xstart * scale_faktor + pic_width - word_length * scale_faktor
params['xstart'] = x
else:
params['xstart'] = params['x']
if params.has_key('yalign'):
ystart = float(params['y'])
if params['yalign'] == 'top':
y = 0.0
elif params['yalign'] == 'center':
word_height = w.calc_height()
y = ystart * scale_faktor + (pic_height - word_height * scale_faktor)/2.0
elif params['yalign'] == 'bottom':
y = ystart * scale_faktor + pic_height - word_height * scale_faktor
else:
y = ystart
params['ystart'] = y
else:
params['ystart'] = params['y']
scalebegin = '''<g transform="translate($xstart,$ystart) scale(${xscale},${yscale})">
'''
scaleend = ''' </g>'''
svg = Template( scalebegin ).substitute(params) + wordsvg + scaleend
return svg
def write_random_font_line( config, fontinfos, data ):
''' erzeugt den SVG Teil des Bildes aus einem Hintergrundbild und
mehreren in Einzelbereichen zufällig platzierten Einzelbildern '''
# TODO Bild aus Zufallspositionen von Einzelkomponenten
return
class Word:
"""Berechnet die x/y Daten jedes Buchstabn zu einem Wort bzw. mehreren
Wörtern einer Zeile nach den Vorgaben """
def __init__(self, line, font, params ):
self.line = line
self.font = font
self.params = params
def __repr__(self):
return self.line
def calc_length(self):
glyph_space = float(self.params['glyph_space'])
deltax = 0.0
for c in self.line:
g = self.font.GlyphGet(c)
deltax = g.GetLength() + deltax + glyph_space
return deltax
def calc_height(self):
max_y = 0.0
for c in self.line:
g = self.font.GlyphGet(c)
new_y = g.GetHeight()
if new_y > max_y:
max_y = new_y
return max_y
def GetSVG(self):
params = self.params
x = float(params['x'])
y = params['y']
glyph_space = float(params['glyph_space'])
svgstr = ''
counter = 0
for c in self.line:
content = dict()
content['posy'] = y
g = self.font.GlyphGet(c)
content['posx'] = str(x)
# die Position ist erst für den nächsten Buchstaben relevant
x = g.GetLength() + x + glyph_space
# wenn im Buchstaben Platzhalter (x1, x2, x3,...) drin sind, dann
# diese durch den aktuellen Zählerstand ersetzen
if params['counter_replace']:
xn = g.GetAttribute('npattern')
for i in range(1, int(xn)+1):
counter = counter + 1
content['x'+str(i)] = counter
svgstr = svgstr + g.GetSVG(content)
return svgstr
class Glyph:
"""enthaelt alle Daten zu einem Buchstaben """
def __init__(self, glyphname, attributes, template):
self.glyphname = glyphname
self.attributes = attributes
self.template = template
def __repr__(self):
return self.glyphname
def GetAttribute(self, att):
return self.attributes[att]
def GetLength(self):
return float(self.GetAttribute('xstep'))
def GetHeight(self):
return float(self.GetAttribute('ymax'))
def GetSVG(self, content):
t = Template(self.template)
svg = t.substitute(content)
return svg
class Font:
"""enthaelt alle Daten zu einem Font """
def __init__(self, fontname, font_path ):
self.name = fontname
self.path = font_path
self.glyphs = dict()
fontconfig = ConfigParser.ConfigParser()
config_path = os.path.join(font_path, fontname+'.cfg')
fontconfig.read(config_path)
fontconfig.optionxform = str
# lies alle Zeichen des Fonts ein
for letter in fontconfig.sections():
# hol die Fontattribute wie Ausdehnung usw.
content = dict(fontconfig.items(letter))
# lade das svg template dazu ein
glyph_path = os.path.join( font_path, smart_unicode(content['fontname']))
f = open(glyph_path, 'r')
svgstr = f.read()
f.close()
# und bau das Zeichen auf
g = Glyph(letter, content, svgstr)
self.glyphs[letter] = g
def __repr__(self):
return repr(self.glyphs)
def GlyphGet(self, glyphname):
return self.glyphs[smart_str(glyphname)]
def init_fonts( config ):
''' lädt alle benötigten Fonts für den Auftrag in den Speicher
'''
fontinfos = dict()
#[globals]
## was soll wohin geschrieben werden:
#lines=MeineZeile, bla
## das ist die TextZeile die auf das Bild kommt
#[MeineZeile]
#..inhalte und wohin es kommt
## Fontname und Fonttyp
#font=ausgangsschrift
#fonttype=replace_strings
#
#[bla]
# ...
lines = config.get('globals', 'lines').split(',')
for linename in lines:
fontname = config.get(linename, 'fontname')
font_path = os.path.expandvars(os.path.join('$PICCREATE_FONTS', fontname ))
f = Font(fontname, font_path)
fontinfos[fontname] = f
return fontinfos
def create_picture(config, fontinfos, data, pic_path):
''' schreibt den svg String des Bildes
'''
svgstr = ''
picbegin, picend = create_picture_frame( config )
svgstr = svgstr + picbegin
# gehe alle Zeilen die im Bild enthalten sein müssen durch
lines = config.get('globals', 'lines').split(',')
for linename in lines:
#[ZeilenBezeichner]
#content=${name}
## Ab dem Punkt wird zu schreiben begonnen
#x=10
#y=20
#lenght=2000
# hol den Inhalt der Zeile die ausgeben werden muss
content_template = config.get(linename, 'content')
t = Template( content_template )
content = t.substitute(data)
# hol dir dazu den Passenden Font für diese Zeile
fontname = config.get(linename, 'fontname')
font = fontinfos[fontname]
# hol dir die Eckdaten für die Zeile
# x, y, align, width, height
lineparams = dict(config.items(linename))
# falls nix angegeben, dann aus hol defaults aus globalen daten
if not lineparams.has_key('width'):
lineparams['width'] = config.get('globals', 'width')
if not lineparams.has_key('height'):
lineparams['height'] = config.get('globals', 'height')
if not lineparams.has_key('x'):
lineparams['x'] = 0.0
if not lineparams.has_key('y'):
lineparams['y'] = 0.0
picstr = write_font_line( content, font, lineparams )
if picstr: svgstr = svgstr + picstr
randstr = write_random_font_line( content, font, lineparams)
if randstr: svgstr = svgstr + randstr
svgstr = svgstr + picend
if options.dry:
print svgstr
else:
f = open(pic_path, 'w')
f.write( svgstr )
f.close()
def main( config ):
infile = options.infile
config = options.config
config_path = os.path.expandvars(os.path.join('$PICCREATE_CFG', config+'.cfg' ))
in_path = os.path.expandvars(os.path.join('$PICCREATE_IN', infile+'.csv' ))
if not config: config = 'default'
if not infile: infile = 'default'
out_path = options.outdir
config_path = os.path.expandvars(os.path.join('$PICCREATE_CFG', CompleteEnding( config, 'cfg')))
in_path = os.path.expandvars(os.path.join('$PICCREATE_IN', CompleteEnding( infile, 'csv')))
if not os.path.exists(config_path):
print '\"'+ config_path +'\" does not exist. Please give the name of a config file!'
return None
if not os.path.exists(in_path):
print '\"'+ in_path +'\" does not exist. Please give the name to a input file!'
return None
if not os.path.exists(out_path):
print '\"'+ out_path +'\" does not exist. Please give the path to the directory for the pictures!'
return None
config = ConfigParser.RawConfigParser()
config.read(config_path)
Trans = get_translation(config_path, 'translation')
Rows = CSVImport(in_path, translate=Trans)
fontinfos = init_fonts( config )
for row in Rows:
filename = row.customerid
print filename
content = row.__dict__
del content['firstrow']
del content['row']
pic_path = os.path.join(out_path, filename+'.svg')
create_picture(config, fontinfos, content, pic_path)
if __name__ == '__main__':
parser = optparse.OptionParser(usage="""%prog [options]
Example usage
-------------
Full help:
$ %prog -h (or --help for possible options)
Read Confg, create wakeup and scale this kind of jiffy box
$ %prog -c test -i test
""", version='%prog ' + __version__)
parser.add_option("-c", "--cfg", dest="config", default="test",
help="name of the config file with the Name of the rows to be used.",
metavar="CONFIG_FILE")
parser.add_option("-i", "--infile", dest="infile",
help="csv file with input of names/content",
action='store')
parser.add_option("-o", "--outdir", dest="outdir", default=os.path.expandvars('$PICCREATE_OUT'),
help="path to the directory where the maps will be generated",
action='store')
parser.add_option("-y", "--dry", dest="dry",
help="dont generate images. Just ask the coordinates and fill database.", action='store_true' )
(options, args) = parser.parse_args()
main(options.config)
else:
pass
# vim: set tw=75 ft=python ts=4 sw=4 tw=80: