Generate SVG images from Bokeh Plots and include them in a PDF Report with using "svglib" and "reportlab"

Hello!!

I have created a script that generates a pdf with SVG images taken from a bokeh plot. It was done with the python libraries reportlab and svglib. I have followed these basic steps:

  1. Use the export_svgs builtin function in order to get the SVG file
  2. Create a Flowable object to add it to the pdf in reportlab
  3. Creating pdf reports with reportlab by the platypus methd (building the pdf with a bunch of elements and template).

As export_svgs can only export one plot at the same time, this may also useful if somebody want to export a grid of plots or a more complex layout.

from reportlab.lib.pagesizes import A4
from reportlab.platypus import Table, TableStyle, SimpleDocTemplate
from reportlab.lib import colors
from reportlab.lib.units import mm

from svglib.svglib import svg2rlg

import numpy as np
from bokeh.plotting import figure
from bokeh.io import curdoc, export_svgs, export_png
from bokeh.models import Button

def get_plot():
    N = 3000
    x = np.random.random(size=N) * 100
    y = np.random.random(size=N) * 100
    radii = np.random.random(size=N) * 1.5

    p = figure(
        x_axis_label='X axis',
        y_axis_label='Y axis',
        title='Random plot',
        tools="crosshair,pan,wheel_zoom,box_zoom,reset,tap,save,lasso_select",
        output_backend = "webgl"
    )

    p.scatter(
        x=x,
        y=y,
        radius=radii,
        fill_color='red',
        fill_alpha=0.6,
        line_color=None
    )

    print('Exporting plot.svg...')
    p.output_backend = "svg"
    export_svgs(p, filename="plot.svg")

    # create a new SVG or a PDF with the new layout
    print('Reading drawing from plot.svg...')
    return svg2rlg("plot.svg")

drawing = get_plot()

print('Scaling SVG image...')
sx = sy = 0.3  # scaling factor
drawing.width = drawing.minWidth() * sx
drawing.height = drawing.height * sy
drawing.scale(sx, sy)

data = [
    ['TABLE HEADER', None],  # this works as a leading row
    [drawing, drawing],
    [drawing, drawing],
    [drawing, drawing],
    [drawing, drawing],
    [drawing, drawing],
    [drawing, None]
]

table = Table(     # Flowable object
    data,
    colWidths=200,
    rowHeights=[1 * mm] + [drawing.height] * (len(data) - 1),
    # hAlign='LEFT',
    repeatRows=1
)

table.setStyle(TableStyle([
    # LEADING ROW
    ('LINEBELOW', (0, 0), (1, 0), 0.5, colors.black),
    ('SPAN', (0, 0), (1, 0)),                           # colspan
    ('FONTSIZE', (0, 0), (1, 0), 12),
    ('LEFTPADDING', (0, 0), (1, 0), 0),
    ('BOTTOMPADDING', (0, 0), (1, 0), 3),

    # REST OF ROWS
    ('LEFTPADDING', (1, 1), (-1, -1), 0),
    ('RIGHTPADDING', (1, 1), (-1, -1), 0),
    ('BOTTOMPADDING', (1, 1), (-1, -1), 0),
    ('TOPPADDING', (1, 1), (-1, -1), 0),
]))

story = []
story.append(table)

margin = 25 * mm
doc = SimpleDocTemplate(
    'minimal_reportlab_table_.pdf', pagesize=A4,
    rightMargin=margin, leftMargin=margin,
    topMargin=margin, bottomMargin=margin
)

doc.build(story)

If you run this code you may read a warning “Unable to find a suitable font for ‘font-family:helvetica’”. But I did not paid attention to it yet.

References:

3 Likes

This is exciting! I have opened an issue https://github.com/bokeh/bokeh/issues/9169 as I have been wanting this feature when working on journal papers.