Search code examples
pythonpdfbokehreportlab

How to save a Bokeh plot as PDF?


I'm working with Bokeh a lot and I'm looking for a way to create a PDF from the figure I created.

Is there an option to achieve this goal?


Solution

  • This is possible with a combination of the three python package bokeh, svglib and reportlab which works perfect for me.

    This will include 3 steps:

    1. creating a bokeh svg output
    2. read in this svg
    3. saving this svg as pdf

    Minimal Example

    To show how this could work please see the following example.

    from bokeh.plotting import figure
    from bokeh.io import export_svgs
    import svglib.svglib as svglib
    from reportlab.graphics import renderPDF
    
    test_name = 'bokeh_to_pdf_test'
    
    # Example plot p
    p = figure(plot_width=400, plot_height=400, tools="")
    p.circle(list(range(1,6)),[2, 5, 8, 2, 7], size=10)
    # See comment 1
    p.xaxis.axis_label_standoff = 12
    p.xaxis.major_label_standoff = 12
    
    # step 1: bokeh save as svg
    p.output_backend = "svg"
    export_svgs(p, filename = test_name + '.svg')
    
    # see comment 2
    svglib.register_font('helvetica', '/home/fonts/Helvetica.ttf')
    # step 2: read in svg
    svg = svglib.svg2rlg(test_name+".svg")
    
    # step 3: save as pdf
    renderPDF.drawToFile(svg, test_name+".pdf")
    

    Comment 1

    There is an extra information used for axis_label_standoff and major_label_standoff because the ticks of the x-axis are moving without this definition a bit up and this looks not so good.

    Comment 2

    If you get a long list of warnings like

    Unable to find a suitable font for 'font-family:helvetica'
    Unable to find a suitable font for 'font-family:helvetica'
    ....
    Unable to find a suitable font for 'font-family:helvetica'
    

    the pdf is still created. This warning appears because the default font in bokeh is named helvetica, which is not known by svglib. svglib looks for this font at a defined place. If this font is not there, the message appears. This means bokeh will use its own default font instead.

    To get rid of this message you can register a font in svglib like this

    #                    name in svglib, path to font
    svglib.register_font('helvetica'   , f'/{PATH_TO_FONT}/Helvetica.ttf')
    

    right before calling svglib.svg2rlg().

    Output

    This code will create the same figure twice, once with the suffix .svg and once with the suffix .pdf.

    The figure looks like this:

    example figure