add latest features to nextgen
This commit is contained in:
parent
37e0135daa
commit
8b89f6ebfd
@ -8,7 +8,7 @@
|
||||
<param gui-text="CSV file:" gui-description="A file with comma-separated values, one line per exported file. The first line contains the column names. Put quotation marks around your variables if they contain a comma." name="csv_file" type="path" mode="file" filetypes="csv">/path/to/file.csv</param>
|
||||
<spacer />
|
||||
<label>Non-text values to replace (see Help tab):</label>
|
||||
<param name="extra-vars" type="string" gui-text="" />
|
||||
<param name="extra-vars" type="string" gui-text="⁣"></param>
|
||||
<param gui-text="Number of sets in the template:" gui-description="How many datasets fit into one template file - e.g. if you have an A4 page with 8 visitor badges for 8 different persons which use the same variable names, choose '8'. See 'Help' tab for more info." name="num_sets" type="int" min="1" max="10000">1</param>
|
||||
<spacer />
|
||||
<separator />
|
||||
@ -23,7 +23,11 @@
|
||||
</param>
|
||||
<param gui-text="DPI (for PNG and filters):" gui-description="The resolution for your exported raster images" name="dpi" type="int" min="1" max="10000">300</param>
|
||||
<param gui-text="File name pattern:" gui-description="The pattern for the names of the generated files. If 'Number of sets' is 1, it should contain at least one unique column variable (in the form of '%VAR_my_variable_name%'), or a unique combination of column variables, so new files won't overwrite those that have already been generated. Do not include the file extension, it will be added automatically. If 'Number of sets' is greater than 1, use any name you like. The exported files will be numbered automatically." name="file_pattern" type="string">%VAR_my_variable_name%</param>
|
||||
<param gui-text="Merge output PDF files into one PDF file" gui-description="When outputting to PDF format, merge the resulting files into a single, multi-page PDF file. Requires ghostscript (https://ghostscript.com) to be installed."
|
||||
name="merge_pdf" type="bool">false</param>
|
||||
<param gui-text="Save in:" gui-description="The name of the folder where the generated images should be saved" name="output_folder" type="path" mode="folders">/tmp</param>
|
||||
<param gui-text="Limit to first output file" gui-description="This is for testing purpose only. Savely check the output before creating hundreds of files."
|
||||
name="limit_output" type="bool">false</param>
|
||||
</page>
|
||||
<page name="help" gui-text="Help">
|
||||
<param name="helptabs" type="notebook">
|
||||
@ -47,7 +51,7 @@ Also make sure to use a variable only exactly once per set. If you do not adhere
|
||||
</page>
|
||||
<page name="about" gui-text="About">
|
||||
<label appearance="header">NextGenerator</label>
|
||||
<label indent="1">Version 1.1.1</label>
|
||||
<label indent="1">Version 1.6</label>
|
||||
<spacer />
|
||||
<label xml:space="preserve">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.
|
||||
|
||||
@ -56,6 +60,7 @@ This extension is a Python rewrite of the Generator bash script extension by Aur
|
||||
</param>
|
||||
<effect needs-live-preview="false">
|
||||
<object-type>all</object-type>
|
||||
<menu-tip>Automatically replace values and export the result.</menu-tip>
|
||||
<effects-menu>
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="Import/Export/Transfer" />
|
||||
|
@ -19,6 +19,7 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
"""
|
||||
An Inkscape extension to automatically replace values (text, attribute values)
|
||||
in an SVG file and to then export the result to various file formats.
|
||||
@ -31,12 +32,12 @@ from __future__ import unicode_literals
|
||||
import os
|
||||
import csv
|
||||
import json
|
||||
import time #for debugging purposes
|
||||
import inkex
|
||||
import html
|
||||
from inkex.command import inkscape
|
||||
|
||||
__version__ = '1.2'
|
||||
import inkex
|
||||
from inkex.command import inkscape, call, which
|
||||
|
||||
__version__ = '1.6'
|
||||
|
||||
class NextGenerator(inkex.base.TempDirMixin, inkex.base.InkscapeExtension):
|
||||
"""Generate image files by replacing variables in the current file"""
|
||||
@ -47,6 +48,8 @@ class NextGenerator(inkex.base.TempDirMixin, inkex.base.InkscapeExtension):
|
||||
pars.add_argument("-n", "--num_sets", type=int, default="1", help="number of sets in the template")
|
||||
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("-k", "--limit_output", type=inkex.Boolean, default=False, help="Limit to first output file")
|
||||
pars.add_argument("-m", "--merge_pdf", type=inkex.Boolean, default=False, help="Merge PDF files into one")
|
||||
pars.add_argument("-o", "--output_folder", help="path to output folder")
|
||||
pars.add_argument("-p", "--file_pattern", help="pattern for the output file")
|
||||
|
||||
@ -62,6 +65,15 @@ class NextGenerator(inkex.base.TempDirMixin, inkex.base.InkscapeExtension):
|
||||
|
||||
extra_vars = json.loads(self.options.extra_vars)
|
||||
|
||||
pdf_files_to_merge = []
|
||||
|
||||
# warn user if Ghostscript is not available and they checked the option to merge pdfs
|
||||
if self.options.merge_pdf:
|
||||
try:
|
||||
which('gs')
|
||||
except inkex.command.CommandNotFound as e:
|
||||
exit("Cannot merge PDF files, please install Ghostscript (see https://ghostscript.com).")
|
||||
|
||||
|
||||
# load the CSV file
|
||||
# spaces around commas will be stripped
|
||||
@ -70,6 +82,7 @@ class NextGenerator(inkex.base.TempDirMixin, inkex.base.InkscapeExtension):
|
||||
with open(self.options.csv_file, newline='', encoding='utf-8-sig') as csvfile:
|
||||
|
||||
data = csv.DictReader(csvfile, dialect='generator')
|
||||
n = 0
|
||||
|
||||
if self.options.num_sets == 1:
|
||||
for row in data:
|
||||
@ -78,17 +91,30 @@ class NextGenerator(inkex.base.TempDirMixin, inkex.base.InkscapeExtension):
|
||||
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
|
||||
# inkex.utils.debug(value)
|
||||
self.new_doc = self.new_doc.replace(search_string, html.escape(value))
|
||||
# build the file name, still without file extension
|
||||
export_base_name = export_base_name.replace(search_string, value)
|
||||
#if export_base_name == self.options.file_pattern:
|
||||
# export_base_name += '_{}'.format(str(n))
|
||||
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
|
||||
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))
|
||||
# clean export base name
|
||||
export_base_name = "".join([x if x.isalnum() else "_" for x in export_base_name])
|
||||
res = self.export(export_base_name)
|
||||
if res != True:
|
||||
return()
|
||||
if self.options.format == "pdf" and self.options.merge_pdf:
|
||||
pdf_files_to_merge.append("{}.pdf".format(export_base_name))
|
||||
n+=1
|
||||
if self.options.limit_output is True:
|
||||
break
|
||||
if self.options.format == "pdf" and self.options.merge_pdf:
|
||||
output_file_base = pdf_files_to_merge[0][:-4]
|
||||
|
||||
elif self.options.num_sets > 1:
|
||||
# we need a list to access specific rows and to be able to count it
|
||||
@ -105,9 +131,12 @@ class NextGenerator(inkex.base.TempDirMixin, inkex.base.InkscapeExtension):
|
||||
num_exports = -((-len(data))//self.options.num_sets)
|
||||
# now we hope that the document is properly prepared and the stacking order cycles through datasets - if not, the result will be nonsensical, but we can't know.
|
||||
|
||||
output_file_base = "".join([x if x.isalnum() else "_" for x in self.options.file_pattern])
|
||||
|
||||
for export_file_num in range(num_exports):
|
||||
# we only number the export files if there are sets
|
||||
export_base_name = "".join([x if x.isalnum() else "_" for x in self.options.file_pattern]) + '_{}'.format(str(export_file_num))
|
||||
export_base_name = output_file_base + '_{}'.format(str(export_file_num))
|
||||
|
||||
self.new_doc = self.document
|
||||
|
||||
for set_num in range(self.options.num_sets):
|
||||
@ -133,6 +162,15 @@ class NextGenerator(inkex.base.TempDirMixin, inkex.base.InkscapeExtension):
|
||||
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))
|
||||
self.export(export_base_name)
|
||||
|
||||
if self.options.format == "pdf" and self.options.merge_pdf:
|
||||
pdf_files_to_merge.append("{}.pdf".format(export_base_name))
|
||||
|
||||
if self.options.limit_output is True:
|
||||
break
|
||||
if len(pdf_files_to_merge) > 1:
|
||||
self.merge_pdfs(pdf_files_to_merge, output_file_base)
|
||||
|
||||
|
||||
def export(self, export_base_name):
|
||||
|
||||
export_file_name = '{0}.{1}'.format(export_base_name, self.options.format)
|
||||
@ -140,35 +178,39 @@ class NextGenerator(inkex.base.TempDirMixin, inkex.base.InkscapeExtension):
|
||||
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.")
|
||||
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:
|
||||
try:
|
||||
with open(export_file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(self.new_doc)
|
||||
return True
|
||||
except IOError as e:
|
||||
print("Couldn't open or write to file (%s)." % e)
|
||||
return False
|
||||
else:
|
||||
|
||||
actions = {
|
||||
'png' : 'export-dpi:{dpi};export-filename:{file_name};export-do;FileClose'.\
|
||||
'png' : 'export-dpi:{dpi};export-filename:{file_name};export-do;file-close'.\
|
||||
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'.\
|
||||
'pdf' : 'export-dpi:{dpi};export-pdf-version:1.5;export-text-to-path;export-filename:{file_name};export-do;file-close'.\
|
||||
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'.\
|
||||
'ps' : 'export-dpi:{dpi};export-text-to-path;export-filename:{file_name};export-do;file-close'.\
|
||||
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'.\
|
||||
'eps' : 'export-dpi:{dpi};export-text-to-path;export-filename:{file_name};export-do;file-close'.\
|
||||
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:
|
||||
with open(temp_svg_path, 'w', encoding='utf-8') 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])
|
||||
@ -179,6 +221,25 @@ class NextGenerator(inkex.base.TempDirMixin, inkex.base.InkscapeExtension):
|
||||
return False
|
||||
return True
|
||||
|
||||
def merge_pdfs(self, pdf_files_to_merge, output_file_base):
|
||||
|
||||
pdf_paths = [os.path.join(self.options.output_folder, f) for f in pdf_files_to_merge]
|
||||
|
||||
output_file_name = '{}_all.pdf'.format(output_file_base)
|
||||
|
||||
#inkex.utils.debug(pdf_paths)
|
||||
try:
|
||||
inkex.command.call('gs', '-q', '-dBATCH', '-dNOPAUSE', '-q', '-sDEVICE=pdfwrite', '-sOutputFile={}'.format(os.path.join(self.options.output_folder, output_file_name)), *pdf_paths)
|
||||
except inkex.command.ProgramRunError as e:
|
||||
inkex.utils.debug("PDF files could not be merged for the following reason: {}".format(e))
|
||||
# don't delete generated files when merging did not work
|
||||
return()
|
||||
|
||||
# delete merged files
|
||||
for f in pdf_paths:
|
||||
if os.path.exists(f):
|
||||
os.remove(f)
|
||||
|
||||
def load(self, stream):
|
||||
return str(stream.read(), 'utf-8')
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user