#!/usr/bin/env python3 # -*- 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 re import configparser import pathlib from utils import CSVImport, CompleteEnding, get_translation 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') #echo '' begintpl = ''' ''' svgbegin = Template( begintpl ).substitute(params) # falls ein Hintergrundbild verwendet werden soll, dann das genauso gross # machen... try: params['background'] = config.get('globals', 'background') imagetpl = ''' ''' background = config.get('globals', 'background') font_path = os.path.expandvars(os.path.join('$PICCREATE_BACKGROUNDS', background )) # als file:// URL, damit es auch mit Windows-Pfaden (C:\...) klappt params['background'] = pathlib.Path(os.path.abspath(font_path)).as_uri() svgbegin = svgbegin + Template( imagetpl ).substitute(params) except configparser.NoOptionError: # dann eben kein Hintergrundbild pass svgend = '' 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 'scale' in params: params['xscale'] = params['scale'] params['yscale'] = params['scale'] if 'xscale' not in params: params['xscale'] = 1.0 if 'yscale' not in params: params['yscale'] = 1.0 pic_width = float(params['width']) pic_height = float(params['height']) if 'autoscale' in params: 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 'align' in params: 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 'yalign' in params: 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': word_height = w.calc_height() y = ystart * scale_faktor + pic_height - word_height * scale_faktor else: y = ystart params['ystart'] = y else: params['ystart'] = params['y'] scalebegin = ''' ''' scaleend = ''' ''' 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) if options.startfrom and (re.match(c,' ') or c.isupper()) : counter = 0 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(interpolation=None, strict=False) config_path = os.path.join(font_path, fontname+'.cfg') fontconfig.read(config_path, encoding='utf-8') 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, content['fontname']) f = open(glyph_path, 'r', encoding='utf-8') 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[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 'width' not in lineparams: lineparams['width'] = config.get('globals', 'width') if 'height' not in lineparams: lineparams['height'] = config.get('globals', 'height') if 'x' not in lineparams: lineparams['x'] = 0.0 if 'y' not in lineparams: 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', encoding='utf-8') 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(strict=False) config.read(config_path, encoding='utf-8') 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", "--config", "--cfg", dest="config", default="test", help="name of the config file in cfg/ with the lines to be written", metavar="CONFIG_FILE") parser.add_option("-i", "--infile", dest="infile", help="csv file with input of names/content", action='store') parser.add_option("-s", "--startfrom", dest="startfrom", help="start the numbering again at every word / capital letter", action='store_true') parser.add_option("-o", "--outdir", dest="outdir", default=os.path.expandvars('$PICCREATE_OUT'), help="directory where the pictures will be written. default is $PICCREATE_OUT", action='store') parser.add_option("-y", "--dry", dest="dry", help="dont write files, print the svg instead", 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: