diff --git a/demo/moulinrouge/__init__.py b/demo/moulinrouge/__init__.py index b3b3b0d..298d2eb 100644 --- a/demo/moulinrouge/__init__.py +++ b/demo/moulinrouge/__init__.py @@ -300,7 +300,7 @@ def create_app(): def raw_svgs(): svgs = [] for color in styles['neon'].colors: - chart = pygal.Pie(style=parametric_styles['RotateStyle'](color), + chart = pygal.Pie(style=parametric_styles['rotate'](color), width=400, height=300) chart.title = color chart.disable_xml_declaration = True diff --git a/docs/api/pygal.graph.verticalpyramid.rst b/docs/api/pygal.graph.verticalpyramid.rst deleted file mode 100644 index 64a648d..0000000 --- a/docs/api/pygal.graph.verticalpyramid.rst +++ /dev/null @@ -1,7 +0,0 @@ -pygal.graph.verticalpyramid module -================================== - -.. automodule:: pygal.graph.verticalpyramid - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/api/pygal.test.test_donut.rst b/docs/api/pygal.test.test_donut.rst deleted file mode 100644 index b7b2498..0000000 --- a/docs/api/pygal.test.test_donut.rst +++ /dev/null @@ -1,7 +0,0 @@ -pygal.test.test_donut module -============================ - -.. automodule:: pygal.test.test_donut - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/changelog.rst b/docs/changelog.rst index 2d73d54..125e425 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -30,6 +30,7 @@ Changelog * Fix timezones in DateTimeLine * Rename in Style foreground_light as foreground_strong * Rename in Style foreground_dark as foreground_subtle +* Add a ``render_data_uri`` method 1.7.0 ===== diff --git a/docs/documentation/configuration/rendering.rst b/docs/documentation/configuration/rendering.rst index 14163b8..3c95a82 100644 --- a/docs/documentation/configuration/rendering.rst +++ b/docs/documentation/configuration/rendering.rst @@ -128,6 +128,7 @@ Default: Css can also specified inline by prepending `inline:` to the css: .. code-block:: python + css = ['inline:.rect { fill: blue; }'] @@ -141,3 +142,13 @@ js ] See `pygal.js `_ + + +force_uri_protocol +------------------ + +In case of rendering the svg as a data uri, it is mandatory to specify a protocol. + +It can be set to http or https and will be used for '//domain/' like uri. + +It is used along with ``render_data_uri``. diff --git a/docs/documentation/output.rst b/docs/documentation/output.rst index bd359cc..a6f7907 100644 --- a/docs/documentation/output.rst +++ b/docs/documentation/output.rst @@ -67,6 +67,19 @@ It is possible to get the xml etree root element of the chart (or lxml etree nod chart.render_tree() # Return the svg root etree node +Base 64 data URI +---------------- + +You can directly output a base 64 encoded data uri for or inclusion: + + +.. code-block:: python + + chart = pygal.Line() + ... + chart.render_data_uri() # Return `data:image/svg+xml;charset=utf-8;base64,...` + + Browser ------- diff --git a/docs/ext/pygal_sphinx_directives.py b/docs/ext/pygal_sphinx_directives.py index 4b05fde..49cad0d 100644 --- a/docs/ext/pygal_sphinx_directives.py +++ b/docs/ext/pygal_sphinx_directives.py @@ -22,7 +22,6 @@ from docutils.parsers.rst import Directive from traceback import format_exc, print_exc from sphinx.directives.code import CodeBlock -import base64 import docutils.core import pygal @@ -86,13 +85,9 @@ class PygalDirective(Directive): chart.config.width = width chart.config.height = height chart.explicit_size = True - rv = chart.render() try: - svg = ( - '' % - base64.b64encode(rv).decode('utf-8') - .replace('\n', '')) + svg = '' % chart.render_data_uri() except Exception: return [docutils.nodes.system_message( 'An exception as occured during graph generation:' diff --git a/pygal/config.py b/pygal/config.py index 0d0d4c4..f42d55e 100644 --- a/pygal/config.py +++ b/pygal/config.py @@ -483,7 +483,13 @@ class Config(CommonConfig): disable_xml_declaration = Key( False, bool, "Misc", "Don't write xml declaration and return str instead of string", - "usefull for writing output directly in html") + "useful for writing output directly in html") + + force_uri_protocol = Key( + None, str, "Misc", + "Default uri protocol", + "In case of rendering the svg as a data uri, it is mandatory to " + "specify a protocol. It can be set to http or https") explicit_size = Key( False, bool, "Misc", "Write width and height attributes") diff --git a/pygal/graph/public.py b/pygal/graph/public.py index eb5066c..b83d60e 100644 --- a/pygal/graph/public.py +++ b/pygal/graph/public.py @@ -21,6 +21,7 @@ import io from pygal._compat import u, is_list_like from pygal.graph.base import BaseGraph +import base64 class PublicApi(BaseGraph): @@ -92,6 +93,16 @@ class PublicApi(BaseGraph): return HttpResponse( self.render(**kwargs), content_type='image/svg+xml') + def render_data_uri(self, **kwargs): + """Output a base 64 encoded data uri""" + # Force protocol as data uri have none + kwargs.setdefault('force_uri_protocol', 'https') + return "data:image/svg+xml;charset=utf-8;base64,%s" % ( + base64.urlsafe_b64encode( + self.render(**kwargs) + ).decode('utf-8').replace('\n', '') + ) + def render_to_file(self, filename, **kwargs): """Render the graph, and write it to filename""" with io.open(filename, 'w', encoding='utf-8') as f: diff --git a/pygal/svg.py b/pygal/svg.py index 1745967..d867e28 100644 --- a/pygal/svg.py +++ b/pygal/svg.py @@ -120,6 +120,8 @@ class Svg(object): css_text = minify_css(css_text) all_css.append(css_text) else: + if css.startswith('//') and self.graph.force_uri_protocol: + css = '%s:%s' % (self.graph.force_uri_protocol, css) self.processing_instructions.append( etree.PI( u('xml-stylesheet'), u('href="%s"' % css))) @@ -154,6 +156,8 @@ class Svg(object): with io.open(js[len('file://'):], encoding='utf-8') as f: script.text = f.read() else: + if js.startswith('//') and self.graph.force_uri_protocol: + js = '%s:%s' % (self.graph.force_uri_protocol, js) self.node(self.defs, 'script', type='text/javascript', href=js) def node(self, parent=None, tag='g', attrib=None, **extras): diff --git a/pygal/test/test_config.py b/pygal/test/test_config.py index cb860c1..2acf151 100644 --- a/pygal/test/test_config.py +++ b/pygal/test/test_config.py @@ -491,3 +491,12 @@ def test_fill(Chart): chart.add('_', [1, 2, 3]) chart.add('?', [10, 21, 5]) assert chart.render_pyquery() + + +def test_render_data_uri(Chart): + """Test the render data uri""" + chart = Chart(fill=True) + chart.add(u('ééé'), [1, 2, 3]) + chart.add(u('èèè'), [10, 21, 5]) + assert chart.render_data_uri().startswith( + 'data:image/svg+xml;charset=utf-8;base64,')