svglib icon indicating copy to clipboard operation
svglib copied to clipboard

Adding an svglib drawing to a Flowable raises AttributeError

Open driscollis opened this issue 7 years ago • 18 comments

I just tried to add an svglib drawing to a document template in ReportLab and I must be doing something wrong. I am betting the following traceback:

File "/home/mdriscoll/Downloads/svg_demo2.py", line 21, in <module>
  svg_demo('snakehead.svg', 'svg_demo2.pdf')
File "/home/mdriscoll/Downloads/svg_demo2.py", line 18, in svg_demo
  doc.build(story)
File "/usr/local/lib/python2.7/dist-packages/reportlab/platypus/doctemplate.py", line 1213, in build
  BaseDocTemplate.build(self,flowables, canvasmaker=canvasmaker)
File "/usr/local/lib/python2.7/dist-packages/reportlab/platypus/doctemplate.py", line 969, in build
  self.handle_flowable(flowables)
File "/usr/local/lib/python2.7/dist-packages/reportlab/platypus/doctemplate.py", line 810, in handle_flowable
  self.handle_keepWithNext(flowables)
File "/usr/local/lib/python2.7/dist-packages/reportlab/platypus/doctemplate.py", line 777, in handle_keepWithNext
  while i<n and flowables[i].getKeepWithNext() and _ktAllow(flowables[i]): i += 1

AttributeError: 'NoneType' object has no attribute 'getKeepWithNext'

Here is the sample code:

# svg_demo2.py

import os

from reportlab.graphics import renderPDF, renderPM
from reportlab.platypus import SimpleDocTemplate
from svglib.svglib import svg2rlg


def svg_demo(image_path, output_path):
    drawing = svg2rlg(image_path)
    
    doc = SimpleDocTemplate(output_path)
    
    story = []
    story.append(drawing)
    
    doc.build(story)

if __name__ == '__main__':
    svg_demo('snakehead.svg', 'svg_demo2.pdf')

I don't see anything in the tests that actually uses Flowables with svglib, but perhaps I am missing something?

driscollis avatar Apr 11 '18 20:04 driscollis

@driscollis Which version of svglib are you using? I've tried your example with 0.8.0 (a version available on some random machine) and the Flag of Cuba used in the test suite and it works ok for Python 2.7 and 3.5. But flowables introduce a different set of potential issues, as they make assumptions and tests about what fits inside. So the errors one gets from reportlab can potentially depend on the size of your SVG input, and I'm not sure these are always describing the reason very well. I assume @replabrobin would have a longer story to share...

deeplook avatar Apr 11 '18 21:04 deeplook

The drawing that is returned is a flowable and can be used to render pdf/png/... with a save or can be added to a story. It's possible that the converted svg image that was returned represented something that would not fit into the frame.

replabrobin avatar Apr 12 '18 13:04 replabrobin

I am using 0.8.1 with ReportLab 3.4 on Python 2.7 on Xubuntu 16.04. I will try using the flag and see if it works on my machine. By the way, I forgot to link to my source code so that you would have access to the SVGs I am using, so here it is: https://github.com/driscollis/reportlabbookcode/tree/master/appendix_a_svg

driscollis avatar Apr 12 '18 13:04 driscollis

@replabrobin Yeah, maybe I should have scaled it first? Or would that have worked?

driscollis avatar Apr 12 '18 13:04 driscollis

I tried the flag of Cuba SVG and it works, even though it is too large and so the right side gets clipped. I also tried using a Python logo SVG from the repo I linked to and it doesn't through an error, but it just creates a blank PDF. I also get this weird message on stdout when I use it:

No handlers could be found for logger "svglib.svglib"

driscollis avatar Apr 12 '18 13:04 driscollis

It seems that the snakehead example returns None from svg2rlg. I'll try and see what's going wrong.

It says

!!!!! Failed to load input file, 'snakehead.svg'! (not well-formed (invalid token): line 43, column 89)!

Duh looked at the svg and it's actually an html page :(

replabrobin avatar Apr 12 '18 13:04 replabrobin

Well that doesn't make sense. I just downloaded it from there on my Windows machine and it worked there. Make sure you are downloading the RAW version though.

driscollis avatar Apr 12 '18 13:04 driscollis

Github won't let me attach an SVG, so here it is as a zip file:

snakehead.svg.zip

driscollis avatar Apr 12 '18 13:04 driscollis

I was hitting the github page and it wasn't at all raw :(; have cloned the repo now though. I do see a drawing coming out in my runs. It's offset to the right, but I suspect that's an issue related to margins etc etc.

replabrobin avatar Apr 12 '18 13:04 replabrobin

This works better for me to get the image onto the A4, but the SimpleDoc class isn't really good enough to handle this as the Frame is not controllable (not there until build time).

from svglib.svglib import svg2rlg
from rlextra.thirdparty.svg.svglib import svg2rlg
def svg_demo(image_path, output_path):
	drawing = svg2rlg(image_path)
	doc = SimpleDocTemplate(output_path)
	doc.leftMargin  = 0
	doc.rightMargin = 0
	story = []
	story.append(drawing)
	doc.build(story)
if __name__ == '__main__':
	svg_demo('snakehead.svg', 'svg_demo2.pdf')

replabrobin avatar Apr 12 '18 14:04 replabrobin

That's cool. I changed my code to just set the margins in the constructor:


from reportlab.platypus import SimpleDocTemplate
from svglib.svglib import svg2rlg


def svg_demo(image_path, output_path):
    drawing = svg2rlg(image_path)
    
    doc = SimpleDocTemplate(output_path,
                            rightMargin=0,
                            leftMargin=0)
    
    story = []
    story.append(drawing)
    
    doc.build(story)

if __name__ == '__main__':
    svg_demo('snakehead.svg', 'svg_demo3.pdf')

This works

driscollis avatar Apr 12 '18 14:04 driscollis

Note that it doesn't fix the oddball python_logo.svg one, but that should probably be a separate issue.

driscollis avatar Apr 12 '18 14:04 driscollis

Unfortunately there's padding in the Frame that gets built into SimpleDoc so the image is still offset to the right (by 6 points I think).

replabrobin avatar Apr 12 '18 14:04 replabrobin

This works pretty good with SimpleDoc

# svg_demo1.py

import os

from reportlab.graphics import renderPDF, renderPM
from reportlab.platypus import SimpleDocTemplate, Image
from svglib.svglib import svg2rlg


def svg_demo(image_path, output_path):
	drawing = svg2rlg(image_path)
	factor = 6*72.0/drawing.width
	doc = SimpleDocTemplate(output_path)
	story = []
	story.append(Image(drawing, factor*drawing.width, height=factor*drawing.height))	
	doc.build(story)

if __name__ == '__main__':
	svg_demo('snakehead.svg', 'svg_demo1.pdf')

replabrobin avatar Apr 12 '18 14:04 replabrobin

Thanks for that

driscollis avatar Apr 12 '18 14:04 driscollis

This code snippet should be adapted and integrated in the docs (README for now).

claudep avatar Apr 12 '18 15:04 claudep

I looked at the python_logo issue; first problem is that the colours are defined as gradients which I don't think the RL graphics api doesn't handle. I suspect the PDF renderer could be easily extended to cope, but the PS, PM etc etc wouldn't like it.

Second the output seems to have lost track of the x & y. If I hack to get the colours OK then the image is still mostly off screen.

replabrobin avatar Apr 12 '18 15:04 replabrobin

@claudep Feel free to use any of my examples in your documentation as well if any of them are useful for that sort of thing

@replabrobin Thanks for looking into the python_logo issue. You needn't worry about it. I was just trying out a handful of different SVGs

driscollis avatar Apr 12 '18 15:04 driscollis