Issue encountered with get_screenshot_as_png

Hi All, I have an automated application that composes a layout of a few divs, a data table, and a number of plots that varies according to various criteria with the data source. I use the export_png function as a step to convert an html report into a pdf document. The html is generated from from the file_html() function which still works for me.

After updating to Bokeh 3.0.1 (with Python 3.10), the get_screenshot_as_png function started to throw the following error:

  File "/home/Documents/pdf_script.py", line 339, in create_pdf_report
    report_image = get_screenshot_as_png(self.Report.report_layout, driver=self.pdf_driver.driver)
  File "/home/Documents/django_env/lib/python3.10/site-packages/bokeh/io/export.py", line 264, in get_screenshot_as_png
    .resize((width, height)))
  File "/home/Documents/django_env/lib/python3.10/site-packages/PIL/Image.py", line 2093, in resize
    im = im.resize(size, resample, box)
  File "/home/Documentsdjango_env/lib/python3.10/site-packages/PIL/Image.py", line 2115, in resize
    return self._new(self.im.resize(size, resample, box))
TypeError: 'float' object cannot be interpreted as an integer

The pdf creation function is called from a much larger application, so I have tried setting up a separate script with a simple example. So far I’m unable to replicate the issue. The closest I’ve come is if I set width and height attributes on the layout (a column of columns), there is no error. i.e.

layout.width, layout.height = 900, 1000

I notice that by default, these values are set to None, regardless of how I set the sizing_mode attribute (I had it set to ‘fixed_width’). Previously, I didn’t set a width or height in the png creation step because it didn’t appear to be required, and the resulting image was indistinguishable from what the html file produced. So I need to be able to let the height set itself as previously.

I’m going to keep trying to create a reproducible example, but in the meantime I figured I might check here to see if anyone else has encountered a similar issue and to ask for general guidance on self-diagnosis.

Thanks!

@Dan_Kovacek An MRE is really needed to judge whether this is a bug or a usage change for the major release.

Hi Bryan, thanks for your response.

Please let me know if this is an acceptable MRE.

from bokeh.io import export_png
from bokeh.plotting import figure
from bokeh.models import Div
from bokeh.layouts import column, row

import numpy as np

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager

class pdf_driver:
    def __init__(self):    
        chrome_options = Options()
        chrome_options.headless = True 
        svc = ChromeService(ChromeDriverManager().install())
        self.driver = webdriver.Chrome(service=svc, options=chrome_options)       

    def quit_driver(self):
        self.driver.quit()

driver = pdf_driver()


all_rows = []
for i in range(2):
    # prepare some data
    x = np.linspace(0, 5, 5)
    y = [np.random.normal() for n in x]

    # create a new plot with a title and axis labels
    p = figure(title="Simple line example", x_axis_label="x", y_axis_label="y", width=912)
    # add a line renderer with legend and line thickness to the plot
    p.line(x, y, legend_label="Temp.", line_width=2)

    div = Div(
        text=f"""Your <a href="https://en.wikipedia.org/wiki/HTML">HTML</a>-
                supported text is initialized with the <b>text</b> argument.""",
                width=950, height=100
        )
    break_section = Div(text=f"Another div.", width=950, height=100)
    
    this_row = column(div, p, break_section)
    all_rows.append(this_row)


layout = column(all_rows)

export_png(layout, filename="test_out/test_file.png")

pdf_driver.quit_driver

The above produces a png file in a fresh python virtual environment, with Bokeh 3.0.1, selenium 4.6.0, and Pillow 9.3.0.

If I change the break_section div to remove one of the dimension attributes, I get the error described in my first message, i.e. change:

break_section = Div(text=f"Another div.", width=950, height=100)

to

break_section = Div(text=f"Another div.", width=950)#, height=100)

gives me:

Traceback (most recent call last):
  File "/home/Documents/bokeh/tests/export_png_test.py", line 64, in <module>
    export_png(layout, filename='test_out/test_file.png')
  File "/home/Documents/env/lib/python3.10/site-packages/bokeh/io/export.py", line 116, in export_png
    image = get_screenshot_as_png(obj, width=width, height=height, driver=webdriver, timeout=timeout, state=state)
  File "/home/Documents/env/lib/python3.10/site-packages/bokeh/io/export.py", line 264, in get_screenshot_as_png
    .resize((width, height)))
  File "/home/Documents/env/lib/python3.10/site-packages/PIL/Image.py", line 2093, in resize
    im = im.resize(size, resample, box)
  File "/home/Documents/env/lib/python3.10/site-packages/PIL/Image.py", line 2115, in resize
    return self._new(self.im.resize(size, resample, box))
TypeError: 'float' object cannot be interpreted as an integer

Please let me know if there’s any other information I can provide.

Thanks!

@Dan_Kovacek this might just be a bug, please open a GitHub Issue with all these details.

1 Like

I had the same issue and I did some research. The problem is in bokeh.io.export.py between line 249 and 260.

with _tmp_html() as tmp:
        theme = (state or curstate()).document.theme
        html = get_layout_html(obj, resources=resources, width=width, height=height, theme=theme)
        with open(tmp.path, mode="w", encoding="utf-8") as file:
            file.write(html)

        web_driver = driver if driver is not None else webdriver_control.get()
        web_driver.maximize_window()
        web_driver.get(f"file://{tmp.path}")
        wait_until_render_complete(web_driver, timeout)
        [width, height, dpr] = _maximize_viewport(web_driver)
        png = web_driver.get_screenshot_as_png()

Bevor that part the variables width and height are integers. After that part they are floats. Don’t know were exactly they get casted.
My work arround was to cast them to integers again after that part. Now it works again.

Maybe this helps a little.

2 Likes

Fixed in: Fix export with fractional CSS lengths by mattpap · Pull Request #12613 · bokeh/bokeh · GitHub

@DappenIT please do file a GitHub Issue for things like this when you encounter them. If the solution is quick, then there is often a very quick turnaround. In this case if there had been an issues for this last week, the fix would have certainly been in yesterdays 3.0.2 release, but since this is not an urgent / critical thing, it probably won’t show up until the next regular release in January.

1 Like