pythonhtmlselenium-webdriverscreenshot

Selenium screenshot of table with custom font


I have the following Python code for loading the given page in selenium and taking a screenshot of the table.

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from PIL import Image
import io
import os

chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.add_argument("--window-size=1920x1080")
chrome_options.add_argument("--force-device-scale-factor=1")
driver = webdriver.Chrome(options=chrome_options)

path = os.path.abspath("table_minimal.html")
driver.get(f"file://{path}")
table = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.TAG_NAME, "table")))
png = table.screenshot_as_png
im = Image.open(io.BytesIO(png))
im.save("table.png")
driver.quit()

Here is the table_minimal.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap" rel="stylesheet">
    <style>
        table {
            font-family: 'Montserrat';
            font-size: 20px;
        }
    </style>
</head>
<body>
<table><tr><td>Text in the table</td></tr></tbody></table>
</body>
</html>

The above produces this image:

output in headless mode

If I remove the --headless flag then I get this instead:

output in normal mode (without headless)

So first one does not use the custom font, the second does but detects incorrect table width. Is there some settings I am missing to make this work correctly? Preferably in headless mode, but now I am curious why it does not work in normal mode as well...


Solution

  • Note: Changed the font to Matemasie to make it stand out more in the screenshot.


    The issue is caused by the screenshot being taken before the custom font has loaded.

    There are some ways to wait for that to finish before continuing, eg:

    time.sleep(2) also works on such a simple DOM :)


    Adding that to the code, and using table.screenshot() to take the screenshot gives us:

    path = os.path.abspath("table_minimal.html")
    driver.get(f"file://{path}")
    
    while True:
        script = '''return document.fonts.status;'''
        loaded = driver.execute_script(script)
        if loaded == 'loaded':
            break
        time.sleep(.5)
    
    table = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.TAG_NAME, "table")))
    table.screenshot('./test3.png')
    
    driver.quit()
    

    Gives the expected result, with both --headless and without:

    enter image description here