Don't use temporary file for SVG data when rendering with Inkscape
This was already suggested in #22. In addition to potentially being a little more efficient, this also avoids the problem of files referenced from the SVG not being found, since the SVG is now rendered while in the artwork directory, so relative paths inside the SVG are still correct. Please note that the pipe functionality of Inkscape requires a relatively new version of Inkscape, i.e. the version from Debian Buster is not sufficient (the Buster backport from Debian should be though). Unfortunately, the same does not work when using ImageMagick, since it seems like they use different delegates/libraries to render the SVG based on how it is passed, i.e. when passed as file, it got rendered with Inkscape on my machine, when passing it as blob, it seemed to be some internal library or another delegate which did not seem to support the same feature set as Inkscape, which resulted in inferior output. Therefore, a temporary file is still used for ImageMagick. However, the issue of included images that could be solved for Inkscape with these changes still persists, since at least when using the Inkscape delegate, ImageMagick seems to create a temporary symbolic link in /tmp, which prevents that the Inkscape delegate finds included images.
This commit is contained in:
parent
082a4f359f
commit
7dd51d08b6
2 changed files with 17 additions and 26 deletions
30
renderlib.py
30
renderlib.py
|
@ -11,6 +11,7 @@ from svgtemplate import SVGTemplate
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
from wand.image import Image
|
from wand.image import Image
|
||||||
|
from tempfile import NamedTemporaryFile
|
||||||
|
|
||||||
# Frames per second. Increasing this renders more frames, the avconf-statements would still need modifications
|
# Frames per second. Increasing this renders more frames, the avconf-statements would still need modifications
|
||||||
fps = 25
|
fps = 25
|
||||||
|
@ -83,12 +84,16 @@ def ensureFilesRemoved(pattern):
|
||||||
for f in glob.glob(pattern):
|
for f in glob.glob(pattern):
|
||||||
os.unlink(f)
|
os.unlink(f)
|
||||||
|
|
||||||
def renderFrame(infile, task, outfile):
|
def renderFrame(svg, task, outfile):
|
||||||
width = 1920
|
width = 1920
|
||||||
height = 1080
|
height = 1080
|
||||||
|
outfile = os.path.abspath(outfile)
|
||||||
if args.imagemagick:
|
if args.imagemagick:
|
||||||
# invoke imagemagick to convert the generated svg-file into a png inside the .frames-directory
|
# invoke imagemagick to convert the generated svg-file into a png inside the .frames-directory
|
||||||
with Image(filename=infile) as img:
|
with NamedTemporaryFile(dir=task.workdir, suffix='.svg') as svgfile:
|
||||||
|
svgfile.write(svg.svgstr.encode('utf-8'))
|
||||||
|
svgfile.flush()
|
||||||
|
with Image(filename=svgfile.name) as img:
|
||||||
with img.convert('png') as converted:
|
with img.convert('png') as converted:
|
||||||
converted.save(filename=outfile)
|
converted.save(filename=outfile)
|
||||||
elif args.resvg:
|
elif args.resvg:
|
||||||
|
@ -98,11 +103,10 @@ def renderFrame(infile, task, outfile):
|
||||||
if errorReturn != '':
|
if errorReturn != '':
|
||||||
print("resvg exited with error\n" + errorReturn)
|
print("resvg exited with error\n" + errorReturn)
|
||||||
# sys.exit(42)
|
# sys.exit(42)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# invoke inkscape to convert the generated svg-file into a png inside the .frames-directory
|
# invoke inkscape to convert the generated svg-file into a png inside the .frames-directory
|
||||||
cmd = 'inkscape --export-background=white --export-background-opacity=0 --export-width={1} --export-height={2} --export-filename="{3}" "{4}" --pipe 2>&1 >/dev/null'.format(task.workdir, width, height, os.path.abspath(outfile), os.path.abspath(infile))
|
cmd = 'inkscape --export-background=white --export-background-opacity=0 --export-width={1} --export-height={2} --export-filename="{3}" --pipe 2>&1 >/dev/null'.format(task.workdir, width, height, outfile)
|
||||||
errorReturn = subprocess.check_output(cmd, shell=True, universal_newlines=True, stderr=subprocess.STDOUT, cwd=task.workdir)
|
errorReturn = subprocess.check_output(cmd, shell=True, universal_newlines=True, input=svg.svgstr, stderr=subprocess.STDOUT, cwd=task.workdir)
|
||||||
if errorReturn != '':
|
if errorReturn != '':
|
||||||
print("inkscape exited with error\n" + errorReturn)
|
print("inkscape exited with error\n" + errorReturn)
|
||||||
# sys.exit(42)
|
# sys.exit(42)
|
||||||
|
@ -135,26 +139,20 @@ def cachedRenderFrame(frame, frameNr, task, cache):
|
||||||
elif not skip_rendering:
|
elif not skip_rendering:
|
||||||
cache[frame] = frameNr
|
cache[frame] = frameNr
|
||||||
|
|
||||||
svgfile = '{0}/.frames/{1:04d}.svg'.format(task.workdir, frameNr)
|
outfile = '{0}/.frames/{1:04d}.png'.format(task.workdir, frameNr)
|
||||||
|
with SVGTemplate(task) as svg:
|
||||||
with SVGTemplate(task, svgfile) as svg:
|
|
||||||
svg.replacetext()
|
svg.replacetext()
|
||||||
svg.transform(frame)
|
svg.transform(frame)
|
||||||
svg.write()
|
renderFrame(svg, task, outfile)
|
||||||
|
|
||||||
outfile = '{0}/.frames/{1:04d}.png'.format(task.workdir, frameNr)
|
|
||||||
renderFrame(svgfile, task, outfile)
|
|
||||||
|
|
||||||
# increment frame-number
|
# increment frame-number
|
||||||
frameNr += 1
|
frameNr += 1
|
||||||
|
|
||||||
|
|
||||||
def rendertask_image(task):
|
def rendertask_image(task):
|
||||||
svgfile = '{0}/image.svg'.format(task.workdir)
|
with SVGTemplate(task) as svg:
|
||||||
with SVGTemplate(task, svgfile) as svg:
|
|
||||||
svg.replacetext()
|
svg.replacetext()
|
||||||
svg.write()
|
renderFrame(svg, task, task.outfile)
|
||||||
renderFrame(svgfile, task, task.outfile)
|
|
||||||
|
|
||||||
def rendertask_video(task):
|
def rendertask_video(task):
|
||||||
# iterate through the animation sequence frame by frame
|
# iterate through the animation sequence frame by frame
|
||||||
|
|
|
@ -12,21 +12,14 @@ cssutils.ser.prefs.lineSeparator = ' '
|
||||||
cssutils.log.setLevel(logging.FATAL)
|
cssutils.log.setLevel(logging.FATAL)
|
||||||
|
|
||||||
class SVGTemplate:
|
class SVGTemplate:
|
||||||
def __init__(self, task, outfile):
|
def __init__(self, task):
|
||||||
self.task = task
|
self.task = task
|
||||||
self.outfile = outfile
|
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
with builtins.open(os.path.join(self.task.workdir, self.task.infile), 'r') as fp:
|
with builtins.open(os.path.join(self.task.workdir, self.task.infile), 'r') as fp:
|
||||||
self.svgstr = fp.read()
|
self.svgstr = fp.read()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def write(self):
|
|
||||||
# open the output-file (named ".gen.svg" in the workdir)
|
|
||||||
with builtins.open(self.outfile, 'w') as fp:
|
|
||||||
# write the generated svg-text into the output-file
|
|
||||||
fp.write(self.svgstr)
|
|
||||||
|
|
||||||
def replacetext(self):
|
def replacetext(self):
|
||||||
for key in self.task.parameters.keys():
|
for key in self.task.parameters.keys():
|
||||||
self.svgstr = self.svgstr.replace(key, xmlescape(str(self.task.parameters[key])))
|
self.svgstr = self.svgstr.replace(key, xmlescape(str(self.task.parameters[key])))
|
||||||
|
|
Loading…
Add table
Reference in a new issue