#!/usr/bin/env python # coding=utf-8 # # NextGenerator - an Inkscape extension to export images with automatically replaced values # Copyright (C) 2008 Aurélio A. Heckert (original Generator extension in Bash) # 2019 Maren Hachmann (Python rewrite, update for Inkscape 1.0) # # 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 3 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. # # Version 0.9 """ An Inkscape extension to automatically replace values (text, attribute values) in an SVG file and to then export the result to various file formats. This is useful e.g. for generating images for name badges and other similar items. """ from __future__ import unicode_literals import os import csv import json import time #for debugging purposes import inkex from inkex.command import inkscape class NextGenerator(inkex.base.TempDirMixin, inkex.base.InkscapeExtension): """Generate image files by replacing variables in the current file""" def add_arguments(self, pars): pars.add_argument("-c", "--csv_file", type=str, dest="csv_file", help="path to a CSV file") pars.add_argument("-e", "--extra-vars", help="additional variables to replace and the corresponding columns, in JSON format") pars.add_argument("-f", "--format", help="file format to export to: png, pdf, svg, ps, eps") pars.add_argument("-d", "--dpi", type=int, default="300", help="dpi value for exported raster images") pars.add_argument("-o", "--output_folder", help="path to output folder") pars.add_argument("-p", "--file_pattern", help="pattern for the output file") pars.add_argument("-t", "--tab", type=str, default="any", help="not needed at all") pars.add_argument("-i", "--id", type=str, default="", help="not needed at all") def effect(self): # load the attributes that should be replaced in addition to textual values if self.options.extra_vars == None: self.options.extra_vars = '{}' extra_vars = json.loads(self.options.extra_vars) # load the CSV file # spaces around commas will be stripped csv.register_dialect('generator', 'excel', skipinitialspace=True) with open(self.options.csv_file, newline='', encoding='utf-8-sig') as csvfile: data = csv.DictReader(csvfile, dialect='generator') for row in data: export_base_name = self.options.file_pattern self.new_doc = self.document for i, (key, value) in enumerate(row.items()): search_string = "%VAR_" + key + "%" # replace any occurrances of %VAR_my_variable_name% in the SVG file source code self.new_doc = self.new_doc.replace(search_string, value) # build the file name, still without file extension export_base_name = export_base_name.replace(search_string, value) for key, svg_cont in extra_vars.items(): if key in row.keys(): # replace any attributes and other SVG content by the values from the CSV file self.new_doc = self.new_doc.replace(svg_cont, row[key]) else: inkex.errormsg("The replacements in the generated images may be incomplete. Please check your entry '{key}' in the field for the non-text values.").format(key=key) if self.export(export_base_name) != True: return def export(self, export_base_name): export_file_name = '{0}.{1}'.format(export_base_name, self.options.format) if os.path.exists(self.options.output_folder): export_file_path = os.path.join(self.options.output_folder, export_file_name) else: inkex.errormsg("The selected output folder does not exist.") return False if self.options.format == 'svg': # would use this, but it cannot overwrite, nor handle strings for writing...: # write_svg(self.new_doc, export_file_path) with open(export_file_path, 'w') as f: f.write(self.new_doc) else: actions = { 'png' : 'export-dpi:{dpi};export-filename:{file_name};export-do;FileClose'.\ format(dpi=self.options.dpi, file_name=export_file_path), 'pdf' : 'export-dpi:{dpi};export-pdf-version:1.5;export-text-to-path;export-filename:{file_name};export-do;FileClose'.\ format(dpi=self.options.dpi, file_name=export_file_path), 'ps' : 'export-dpi:{dpi};export-text-to-path;export-filename:{file_name};export-do;FileClose'.\ format(dpi=self.options.dpi, file_name=export_file_path), 'eps' : 'export-dpi:{dpi};export-text-to-path;export-filename:{file_name};export-do;FileClose'.\ format(dpi=self.options.dpi, file_name=export_file_path), } # create a temporary svg file from our string temp_svg_name = '{0}.{1}'.format(export_base_name, 'svg') temp_svg_path = os.path.join(self.tempdir, temp_svg_name) #inkex.utils.debug("temp_svg_path=" + temp_svg_path) with open(temp_svg_path, 'w') as f: f.write(self.new_doc) #inkex.utils.debug("self.new_doc=" + self.new_doc) # let Inkscape do the exporting # self.debug(actions[self.options.format]) cli_output = inkscape(temp_svg_path, actions=actions[self.options.format]) if len(cli_output) > 0: self.debug(_("Inkscape returned the following output when trying to run the file export; the file export may still have worked:")) self.debug(cli_output) return False return True def load(self, stream): return str(stream.read(), 'utf-8') def save(self, stream): # must be implemented, but isn't needed. pass if __name__ == '__main__': NextGenerator().run()