diff --git a/README.md b/README.md
index 961c469..a034e4a 100644
--- a/README.md
+++ b/README.md
@@ -283,31 +283,72 @@ For ease of use, some flags are set by default. However default flags are not us
```
## Using the CLI
-HTML2image comes with a Command Line Interface which you can use to generate screenshots from files and URLs on the go.
-
-The CLI is a work in progress and may undergo changes.
-You can call it by typing `hti` or `html2image` into a terminal.
-
-
-| argument | description | example |
-| - | - | - |
-| -h, --help | Shows the help message | `hti -h` |
-| -U, --urls | Screenshots a list of URLs | `hti -U https://www.python.org` |
-| -H, --html | Screenshots a list of HTML files | `hti -H file.html` |
-| -C, --css | Attaches a CSS files to the HTML ones | `hti -H file.html -C style.css` |
-| -O, --other | Screenshots a list of files of type "other" | `hti -O star.svg` |
-| -S, --save-as | A list of the screenshot filename(s) | `hti -O star.svg -S star.png` |
-| -s, --size | A list of the screenshot size(s) | `hti -O star.svg -s 50,50`|
-| -o, --output_path| Change the output path of the screenshots (default is current working directory) | `hti star.svg -o screenshot_dir` |
-| -q, --quiet| Disable all CLI's outputs | `hti --quiet` |
-| -v, --verbose| More details, can help debugging | `hti --verbose` |
-| --chrome_path| Specify a different chrome path ||
-| --custom_flags| Specify custom browser flags |
-| --temp_path| Specify a different temp path (where the files are loaded)||
+HTML2image comes with a Command Line Interface which you can use to generate screenshots from files and URLs on the go. You can call it by typing `hti` or `html2image` into a terminal.
+
+
+**Example Usage (quick start):**
+Screenshot a URL with a specific output name and size:
+```bash
+hti --url https://example.com --save-as example_page.png --size 1280,720
+```
+
+Screenshot multiple HTML files, applying a common CSS file, and saving with custom names:
+```bash
+hti --html-file page1.html page2.html --css-file common_styles.css --save-as shot1.jpg shot2.jpg
+```
+
+Screenshot an HTML string with a custom browser flags and verbose output:
+```bash
+hti --html-string "
Test
Content
" --custom-flags '--no-sandbox' -v
+```
+
+**Html2Image Instance Configuration:**
+
+These arguments configure the underlying `Html2Image` instance.
+
+| Argument | Description | Example |
+|----------|-------------|---------|
+| `-h, --help` | Show the help message and exit. | `hti --help` |
+| `-o, --output-path PATH` | Directory to save screenshots. (Default: current working directory)| `hti --url example.com -o my_images/` |
+| `--browser BROWSER`| Browser to use. Choices: `chrome`, `chromium`, `google-chrome`, `google-chrome-stable`, `googlechrome`, `edge`, `chrome-cdp`, `chromium-cdp`. (Default: `chrome`)| `hti --url example.com --browser edge` |
+| `--browser-executable EXECUTABLE_PATH` | Path to the browser executable. Auto-detected if not provided. | `hti --browser-executable /usr/bin/google-chrome-stable`|
+| `--cdp-port PORT` | CDP port for CDP-enabled browsers (e.g., `chrome-cdp`). (Default: library-dependent)| `hti --browser chrome-cdp --cdp-port 9222 --url example.com` |
+| `--temp-path TEMP_DIR_PATH` | Directory for temporary files. (Default: system temp directory in an `html2image` subfolder) | `hti --html-file page.html --temp-path /my/tmp`|
+| `--keep-temp-files`| Do not delete temporary files after screenshot generation.| `hti --html-file page.html --keep-temp-files` |
+| `--custom-flags [FLAG ...]` | Custom flags to pass to the browser (e.g., `'--no-sandbox' '--disable-gpu'`). If provided, these flags will be used. | `hti --url example.com --custom-flags '--no-sandbox' '--disable-gpu'` `hti --url example.com --custom-flags '--no-sandbox --disable-gpu'` |
+
+**Screenshot Sources:**
+
+Specify what content to screenshot. At least one source type is required.
+
+| Argument | Description | Example |
+|----------|-------------|---------|
+| `-U, --url [URL ...]` | URL(s) to screenshot. | `hti -U https://python.org https://example.com`|
+| `--html-file [FILE ...]` | HTML file(s) to screenshot. | `hti --html-file mypage.html another.html` |
+| `--html-string [STRING ...]`| HTML string(s) to screenshot. | `hti --html-string "
Hello
" "
World
"` |
+| `--css-file [FILE ...]` | CSS file(s) to load. Used by HTML files or applied with HTML strings. | `hti --html-file page.html --css-file style1.css style2.css` |
+| `--css-string [STRING ...]` | CSS string(s) to apply. Combined and used with HTML strings. | `hti --html-string "
Hi
" --css-string "body{color:red;}" "h1{font-size:40px;}"` |
+| `-O, --other-file [FILE ...]` | Other file(s) to screenshot (e.g., SVG).| `hti -O star.svg`|
+
+**Screenshot Output Options:**
+
+Control how the screenshots are saved.
+
+| Argument | Description | Example |
+|----------|-------------|---------|
+| `-S, --save-as [FILENAME ...]` | Filename(s) for output images. If not provided or fewer names than items, names are auto-generated (e.g., `screenshot.png`, `screenshot_0.png`). | `hti -U python.org example.com -S py.png ex.png` |
+| `-s, --size [W,H ...]`| Size(s) for screenshots as `Width,Height`. If one W,H pair is given, it applies to all screenshots. If multiple W,H pairs are given, they apply to corresponding screenshots sequentially; if fewer pairs than items, the last pair is repeated. If omitted, the library's default (1920,1080) is used. Width and height must be positive integers. | `hti -U python.org --size 800,600` `hti -U python.org example.com -s 800,600 1024,768` |
+
+**General Options:**
+
+| Argument | Description | Example |
+|----------|-------------|---------|
+| `-q, --quiet`| Suppress informational output from html2image library (sets `disable_logging=True`). | `hti -U python.org -q` |
+| `-v, --verbose` | Enable verbose output, including browser commands if supported by the browser handler. | `hti -U python.org -v` |
-### ... now within a Docker container !
+### Using a Docker Container
You can also test the package and the CLI without having to install everything on your local machine, via a Docker container.
diff --git a/html2image/cli.py b/html2image/cli.py
index 80ccca9..2e3e74b 100644
--- a/html2image/cli.py
+++ b/html2image/cli.py
@@ -2,81 +2,247 @@
"""
import argparse
-
+import os
from html2image import Html2Image
-def main():
-
- def size_type(string):
- try:
- x, y = map(int, string.split(','))
- return x, y
- except Exception:
+def size_type(string):
+ try:
+ width, height = map(int, string.split(','))
+ if width <= 0 or height <= 0:
raise argparse.ArgumentTypeError(
- f"size should be int,int, instead got {string}"
+ 'Width and height must be positive integers.'
)
+ return width, height
+ except ValueError: # incorrect number of values
+ raise argparse.ArgumentTypeError(
+ f"Size should be W,H (e.g., 1920,1080), instead got '{string}'"
+ )
+ except Exception as e: # unexpected errors
+ raise argparse.ArgumentTypeError(f"Invalid size format '{string}': {e}")
- parser = argparse.ArgumentParser()
- parser.add_argument('-U', '--url', nargs='*', required=False, default=[])
- parser.add_argument('-H', '--html', nargs='*', required=False, default=[])
- parser.add_argument('-C', '--css', nargs='*', required=False, default=[])
- parser.add_argument('-O', '--other', nargs='*', required=False, default=[])
+def main():
+ parser = argparse.ArgumentParser(
+ description='Generate images from HTML/CSS or URLs using the html2image library.',
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter
+ )
+
+ # Html2Image instantiation arguments
+ group_hti_init = parser.add_argument_group('Html2Image Instance Configuration')
+ group_hti_init.add_argument(
+ '--output-path', '-o',
+ default=os.getcwd(),
+ help='Directory to save screenshots.'
+ )
- parser.add_argument(
- '-S', '--save-as', nargs='*', required=False, default="screenshot.png"
+ # TODO : this list is duplicated from browser_map in html2image.py
+ browser_choices = [
+ 'chrome', 'chromium', 'google-chrome', 'google-chrome-stable',
+ 'googlechrome', 'edge', 'chrome-cdp', 'chromium-cdp'
+ ]
+ group_hti_init.add_argument(
+ '--browser',
+ default='chrome',
+ choices=browser_choices,
+ help='Browser to use for screenshots.'
)
- parser.add_argument(
- '-s', '--size', nargs='*', required=False, default=[], type=size_type
+ group_hti_init.add_argument(
+ '--browser-executable',
+ default=None,
+ help='Path to the browser executable. Auto-detected if not provided.'
+ )
+ group_hti_init.add_argument(
+ '--cdp-port',
+ type=int,
+ default=None,
+ help='CDP port for CDP-enabled browsers (e.g., chrome-cdp). Default is library-dependent.'
+ )
+ group_hti_init.add_argument(
+ '--temp-path',
+ default=None,
+ help="Directory for temporary files. Defaults to system temp directory within an 'html2image' subfolder."
+ )
+ group_hti_init.add_argument(
+ '--keep-temp-files',
+ action='store_true',
+ help='Do not delete temporary files after screenshot generation.'
+ )
+ group_hti_init.add_argument(
+ '--custom-flags',
+ nargs='*',
+ default=[], # If not provided, defaults are used
+ help="Custom flags to pass to the browser (e.g., '--no-sandbox' '--disable-gpu'). If provided, these flags will be used."
)
- parser.add_argument('-o', '--output_path', required=False)
+ # Screenshot sources arguments
+ group_sources = parser.add_argument_group('Screenshot Sources (at least one type is required)')
+ group_sources.add_argument(
+ '--url', '-U',
+ nargs='*', default=[],
+ metavar='URL',
+ help='URL(s) to screenshot.'
+ )
+ group_sources.add_argument(
+ '--html-file',
+ nargs='*', default=[],
+ metavar='FILE',
+ help='HTML file(s) to screenshot.'
+ )
+ group_sources.add_argument(
+ '--html-string',
+ nargs='*', default=[],
+ metavar='STRING',
+ help='HTML string(s) to screenshot.'
+ )
+ group_sources.add_argument(
+ '--css-file',
+ nargs='*', default=[],
+ metavar='FILE',
+ help='CSS file(s) to load. Used by HTML files or applied with HTML strings.'
+ )
+ group_sources.add_argument(
+ '--css-string',
+ nargs='*', default=[],
+ metavar='STRING',
+ help='CSS string(s) to apply. Combined and used with HTML strings.'
+ )
+ group_sources.add_argument(
+ '--other-file', '-O',
+ nargs='*', default=[],
+ metavar='FILE',
+ help='Other file(s) to screenshot (e.g., SVG).'
+ )
- parser.add_argument('-q', '--quiet', required=False, action="store_true")
- parser.add_argument('-v', '--verbose', required=False, action="store_true")
+ # Screenshot output control arguments
+ group_output_ctrl = parser.add_argument_group('Screenshot Output Options')
+ group_output_ctrl.add_argument(
+ '--save-as', '-S',
+ nargs='*', default=None, # html2image handles default naming if only one source
+ metavar='FILENAME',
+ help='Filename(s) for the output images. If not provided or fewer names than items, names are auto-generated.'
+ )
+ group_output_ctrl.add_argument(
+ '--size', '-s',
+ nargs='*', default=[],
+ type=size_type,
+ metavar='W,H',
+ help="Size(s) for screenshots as W,H. If one W,H pair is given, it applies to all. If multiple, they apply to corresponding screenshots; if fewer pairs than items, the last is repeated. If omitted, (1920,1080) is used."
+ )
- # parser.add_argument('--browser', required=False)
- parser.add_argument('--chrome_path', required=False)
- # parser.add_argument('--firefox_path', required=False)
- parser.add_argument('--temp_path', required=False)
- parser.add_argument('--custom_flags', required=False)
+ # General arguments
+ group_general = parser.add_argument_group('General Options')
+ group_general.add_argument(
+ '--quiet', '-q',
+ action='store_true',
+ help='Suppress output from browsers (sets disable_logging=True).'
+ )
+ group_general.add_argument(
+ '--verbose', '-v',
+ action='store_true',
+ help='Enable verbose output, including browser commands if supported by the browser handler.'
+ )
args = parser.parse_args()
+ # Prepare Html2Image()
+ hti_kwargs = {
+ 'output_path': args.output_path,
+ 'browser': args.browser,
+ 'browser_executable': args.browser_executable,
+ 'custom_flags': [cf.replace("'", '') for cf in args.custom_flags],
+ 'disable_logging': args.quiet,
+ 'temp_path': args.temp_path,
+ 'keep_temp_files': args.keep_temp_files,
+ }
+
+ # Only pass cdp_port if a CDP browser is likely selected and port is given
+ if args.cdp_port and 'cdp' in args.browser.lower():
+ hti_kwargs['browser_cdp_port'] = args.cdp_port
+ elif args.cdp_port:
+ print(
+ f"Warning: --cdp-port ({args.cdp_port}) was specified, but the selected browser ('{args.browser}') might not be a CDP browser."
+ )
+
try:
- hti = Html2Image(disable_logging=args.quiet)
+ # Filter out None values so defaults are used for those specific kwargs
+ # keep_temp_files and disable_logging are bools, always pass them.
+ # custom_flags should be passed even if None, so Html2Image can use its defaults or an empty list.
+ active_hti_kwargs = {
+ k: v for k, v in hti_kwargs.items()
+ if v is not None or k in ['keep_temp_files', 'disable_logging', 'custom_flags']
+ }
+ hti = Html2Image(**active_hti_kwargs)
+
except Exception as e:
- print('Could not instanciate html2image.')
- print(e)
+ print(f'Error: Could not instantiate Html2Image: {e}')
exit(1)
if args.verbose:
- print(f'args = {args}')
- hti.browser.print_command = True
-
- if args.output_path:
- hti.output_path = args.output_path
+ # The `print_command` attribute is specific to ChromiumHeadless.
+ # CDP browsers print logs internally.
+ if hasattr(hti.browser, 'print_command'):
+ hti.browser.print_command = True
+ print('Verbose mode: Browser commands will be printed for compatible handlers.')
+ else:
+ print('Verbose mode enabled. Note: Detailed browser command printing depends on the selected browser handler.')
+
+ has_sources = any([
+ args.url, args.html_file, args.html_string, args.other_file
+ ])
+
+ # Print help message if no sources were passed
+ if not has_sources:
+ print('Error: No screenshot sources (URL, HTML file/string, other file) provided.')
+ parser.print_usage()
+ exit(1)
- if args.chrome_path:
- hti.chrome_path = args.chrome_path
+ # Perform screenshot
+ screenshot_kwargs = {
+ 'url': args.url,
+ 'html_file': args.html_file,
+ 'html_str': args.html_string,
+ 'css_file': args.css_file,
+ 'css_str': args.css_string,
+ 'other_file': args.other_file,
+ 'size': args.size, # Pass the list of sizes directly from the --size CLI arg
+ }
- if args.custom_flags:
- hti.browser.flags = args.custom_flags
+ if args.save_as is not None:
+ screenshot_kwargs['save_as'] = args.save_as
- if args.temp_path:
- hti.temp_path = args.temp_path
+ try:
+ if args.verbose:
+ print('--- Html2Image Instance Configuration ---')
+ for k, v in active_hti_kwargs.items():
+ print(f' {k}: {v}')
+ print('--- Screenshot Call Arguments ---')
+ for k, v in screenshot_kwargs.items():
+ if v or k == 'size': # print if list not empty, or always for size
+ print(f' {k}: {v}')
+
+ paths = hti.screenshot(**screenshot_kwargs)
+
+ if not args.quiet:
+ print(f'Successfully created {len(paths)} image(s):')
+ for path in paths:
+ print(f' {path}')
+
+ except FileNotFoundError as e:
+ print(f'Error: A required file was not found: {e}')
+ exit(1)
- paths = hti.screenshot(
- html_file=args.html, css_file=args.css, other_file=args.other,
- url=args.url, save_as=args.save_as, size=args.size
- )
+ except ValueError as e: # Can be raised by browser screenshot method for bad size etc.
+ print(f'Error: Invalid value encountered: {e}')
+ exit(1)
- if not args.quiet:
- print(f'Created {len(paths)} file(s):')
- for path in paths:
- print(f'\t{path}')
+ except Exception as e:
+ print(f'An unexpected error occurred during screenshotting: {e}')
+ if args.verbose:
+ import traceback
+ traceback.print_exc()
+ exit(1)
-if __name__ == "__main__":
+if __name__ == '__main__':
main()
diff --git a/html2image/html2image.py b/html2image/html2image.py
index 38b010a..7e46f30 100644
--- a/html2image/html2image.py
+++ b/html2image/html2image.py
@@ -389,7 +389,7 @@ def _extend_size_param(self, sizes, desired_length):
return sizes
@staticmethod
- def _prepare_html_string(html_body, css_style_string):
+ def _prepare_html_string(html_body, css_style_string) -> str:
""" Creates a basic HTML string from an HTML body and a css string.
Parameters
@@ -422,8 +422,8 @@ def _prepare_html_string(html_body, css_style_string):
return dedent(prepared_html)
@staticmethod
- def _prepare_css_str(css_file):
- """ Creates a basic string fromatted from a css file.
+ def _prepare_css_string(css_file: list[str])-> str:
+ """ Creates a basic string fromatted from a list of css files.
Parameters
----------
@@ -432,10 +432,11 @@ def _prepare_css_str(css_file):
Returns
-------
- str
- The contents of each css file.
+ The concatenated content of each css files.
"""
- css_str = ""
+
+ css_str = ''
for css in css_file:
temp_css_str = ''
with open(css, "r") as fd:
@@ -444,6 +445,7 @@ def _prepare_css_str(css_file):
return css_str
+
def screenshot(
self,
html_str=[], # html_str: Union[str, list] = [],
@@ -455,7 +457,7 @@ def screenshot(
save_as='screenshot.png',
size=[],
):
- """ Takes a screeshot using different resources.
+ """ Takes a screenshot using different resources.
Parameters
----------
@@ -465,10 +467,14 @@ def screenshot(
+ Filepath(s) of HTML file(s) that will be screenshotted.
- `css_str`: list of str or str
+ CSS string(s) that will be "associated" with the given
- + HTML string(s)
+ + HTML string(s).
- `css_file`: list of str or str
- + CSS file(s) supposedly already mentionned by their filenames
- + in the content of the `html_file`(s).
+ + Filepath(s) of CSS file(s). These files serve two purposes:
+ + 1. Their content is combined with `css_str` and embedded
+ + directly when screenshotting `html_str` (HTML strings).
+ + 2. They are loaded into the temporary directory, making
+ + them available to `html_file` (HTML files) that link to them
+ + (e.g., via ``).
- `other_file`: list of str or str
+ Filepath(s) of non-HTML file(s) that will be screenshotted.
- `url`: list of str or str
@@ -500,47 +506,46 @@ def screenshot(
# convert each parameter into list
# e.g: param=value becomes param=[value]
- html_str = [html_str] if isinstance(html_str, str) else html_str
- html_file = [html_file] if isinstance(html_file, str) else html_file
- css_str = [css_str] if isinstance(css_str, str) else css_str
- css_file = [css_file] if isinstance(css_file, str) else css_file
- other_file = (
- [other_file] if isinstance(other_file, str) else other_file
- )
- url = [url] if isinstance(url, str) else url
- save_as = [save_as] if isinstance(save_as, str) else save_as
- size = [size] if isinstance(size, tuple) else size
+ html_strings = [html_str] if isinstance(html_str, str) else html_str
+ html_files = [html_file] if isinstance(html_file, str) else html_file
+ css_strings = [css_str] if isinstance(css_str, str) else css_str
+ css_files = [css_file] if isinstance(css_file, str) else css_file
+ other_files = [other_file] if isinstance(other_file, str) else other_file
+ urls = [url] if isinstance(url, str) else url
+ save_as_filenames = [save_as] if isinstance(save_as, str) else save_as
+ sizes = [size] if isinstance(size, tuple) else size
planned_screenshot_count = (
- len(html_str) + len(html_file) + len(other_file) + len(url)
+ len(html_strings) + len(html_files) + len(other_files) + len(urls)
)
- save_as = Html2Image._extend_save_as_param(
- save_as,
+ save_as_filenames = Html2Image._extend_save_as_param(
+ save_as_filenames,
planned_screenshot_count,
)
- size = self._extend_size_param(size, planned_screenshot_count)
+ sizes = self._extend_size_param(sizes, planned_screenshot_count)
- css_style_string = ""
+ css_style_string = '\n'.join(css_strings) + '\n'
- for css in css_str:
- css_style_string += css + '\n'
+ if css_files:
+ # add content from css_files, regardless of whether css_strings was present
+ css_style_string += Html2Image._prepare_css_string(css_files)
- for css in css_file:
+
+ for css in css_files:
if os.path.isfile(css):
self.load_file(src=css)
else:
raise FileNotFoundError(css)
- for html in html_str:
- name = save_as.pop(0)
- current_size = size.pop(0)
+ for html in html_strings:
+ name = save_as_filenames.pop(0)
+ current_size = sizes.pop(0)
base_name, _ = os.path.splitext(name)
html_filename = base_name + '.html'
- content = Html2Image._prepare_html_string(
- html, css_style_string if css_style_string != '' else Html2Image._prepare_css_str(
- css_file)
- )
+
+ content = Html2Image._prepare_html_string(html, css_style_string)
+
self.load_str(content=content, as_filename=html_filename)
self.screenshot_loaded_file(
file=html_filename,
@@ -552,10 +557,10 @@ def screenshot(
screenshot_paths.append(os.path.join(self.output_path, name))
- for screenshot_target in html_file + other_file:
+ for screenshot_target in html_files + other_files:
- name = save_as.pop(0)
- current_size = size.pop(0)
+ name = save_as_filenames.pop(0)
+ current_size = sizes.pop(0)
if os.path.isfile(screenshot_target):
self.load_file(src=screenshot_target)
@@ -564,14 +569,16 @@ def screenshot(
output_file=name,
size=current_size,
)
+ if not self.keep_temp_files:
+ self._remove_temp_file(os.path.basename(screenshot_target))
else:
raise FileNotFoundError(screenshot_target)
screenshot_paths.append(os.path.join(self.output_path, name))
- for target_url in url:
- name = save_as.pop(0)
- current_size = size.pop(0)
+ for target_url in urls:
+ name = save_as_filenames.pop(0)
+ current_size = sizes.pop(0)
self.screenshot_url(
url=target_url,
diff --git a/pyproject.toml b/pyproject.toml
index acb7f20..fb55b07 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "html2image"
-version = "2.0.6" # todo take a look at dynamic versionning (e.g., hatch-vcs)
+version = "2.0.7" # todo take a look at dynamic versionning (e.g., hatch-vcs)
description = "Package acting as a wrapper around the headless mode of existing web browsers to generate images from URLs and from HTML+CSS strings or files."
authors = [
{ name = "vgalin" }
diff --git a/tests/test_main.py b/tests/test_main.py
index 0573c84..6ebc175 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -1,9 +1,11 @@
from html2image import Html2Image
-from PIL import Image
+from PIL import Image, ImageChops
import pytest
+import os
OUTPUT_PATH = "tests_output"
+os.makedirs(OUTPUT_PATH, exist_ok=True)
TEST_BROWSERS = ["edGe", "cHrOme"]
@@ -96,8 +98,8 @@ def test_screenshot_url_sizes_missing_custom_names(browser):
],
size=test_sizes,
)
-
- for wanted_size, path in zip(test_sizes, paths):
+ effective_wanted_sizes = test_sizes + [test_sizes[-1]]
+ for wanted_size, path in zip(effective_wanted_sizes, paths):
img = Image.open(path)
assert wanted_size == img.size
@@ -118,7 +120,7 @@ def test_screenshot_string(browser):
assert (1920, 1080) == img.size # default size
# check colors at top left corner
- assert pixels[0, 0] == (0, 0, 255) # blue + no transparency
+ assert pixels[0, 0][:3] == (0, 0, 255)
@pytest.mark.parametrize("browser", TEST_BROWSERS)
def test_screenshot_string_different_sizes(browser):
@@ -155,8 +157,9 @@ def test_screenshot_other_svg(browser):
assert (1920, 1080) == img.size # default size
- # check colors at top left corner
- assert pixels[0, 0] == (0, 0, 0, 0) # full transparency no color
+ # check colors at top left corner (assuming transparent background for the SVG)
+ # for transparent PNG, the alpha channel will be 0
+ assert pixels[0, 0][3] == 0 # check alpha channel for full transparency
@pytest.mark.parametrize("browser", TEST_BROWSERS)
def test_screenshot_file(browser):
@@ -164,7 +167,7 @@ def test_screenshot_file(browser):
paths = hti.screenshot(
html_file="./examples/blue_page.html",
- css_file="./examples/blue_background.css",
+ css_file="./examples/blue_background.css", # this CSS file is linked in blue_page.html
save_as="from_file.png",
)
@@ -174,7 +177,7 @@ def test_screenshot_file(browser):
assert (1920, 1080) == img.size # default size
# check colors at top left corner
- assert pixels[0, 0] == (0, 0, 255) # blue + no transparency
+ assert pixels[0, 0][:3] == (0, 0, 255) # blue + no transparency
@pytest.mark.parametrize("browser", TEST_BROWSERS)
def test_screenshot_file_different_sizes(browser):
@@ -196,6 +199,119 @@ def test_screenshot_file_different_sizes(browser):
img = Image.open(path)
assert wanted_size == img.size
+
+@pytest.mark.parametrize("browser", TEST_BROWSERS)
+def test_screenshot_html_str_with_css_file_only(browser):
+ """Test html_str styled by a css_file only."""
+ hti = Html2Image(browser=browser, output_path=OUTPUT_PATH, disable_logging=True)
+ html_content = "
Background should be blue from file
"
+ css_file_path = "./examples/blue_background.css"
+
+ paths = hti.screenshot(
+ html_str=html_content,
+ css_file=css_file_path,
+ save_as="html_str_blue_bg_from_file.png"
+ )
+
+ img = Image.open(paths[0])
+ pixels = img.load()
+ assert (1920, 1080) == img.size
+
+ # Check top-left corner for blue background
+ assert pixels[0, 0][:3] == (0, 0, 255) # blue
+
+@pytest.mark.parametrize("browser", TEST_BROWSERS)
+def test_screenshot_html_str_with_css_str_and_css_file(browser):
+ """Test html_str styled by both css_str (for a green block) and css_file (for blue body background)."""
+ hti = Html2Image(browser=browser, output_path=OUTPUT_PATH, disable_logging=True)
+
+ # this CSS describes a green block, positioned absolutely,
+ # using !important to ensure these styles apply
+ css_string_content = """
+ #green-block {
+ background-color: rgb(0, 255, 0) !important; /* Green background */
+ width: 150px !important;
+ height: 100px !important;
+ position: absolute !important;
+ top: 75px !important;
+ left: 75px !important;
+ }
+ """
+
+ css_file_path = "./examples/blue_background.css"
+
+ # div that will be styled by css_string_content
+ html_content = ''
+
+ paths = hti.screenshot(
+ html_str=html_content,
+ css_str=css_string_content, # Styles #green-block
+ css_file=css_file_path, # Styles body background
+ save_as="html_str_green_block_blue_bg.png",
+ size=(500,400),
+ )
+
+ img = Image.open(paths[0])
+ pixels = img.load()
+ assert (500,400) == img.size
+
+ # check body background color (blue)
+ assert pixels[10, 10][:3] == (0, 0, 255) # blue
+
+ # check #green-block color (green from css_string_content)
+ assert pixels[150, 125][:3] == (0, 255, 0) # green
+
+
+@pytest.mark.parametrize("browser", TEST_BROWSERS)
+def test_screenshot_html_str_with_multiple_css_files(browser):
+ """Test html_str styled by multiple css_files using distinct background colors."""
+ hti = Html2Image(browser=browser, output_path=OUTPUT_PATH, disable_logging=True)
+
+ # this CSS describes a red block, positioned absolutely,
+ # using !important to ensure these styles apply
+ temp_css_content = """
+ #red-block {
+ background-color: rgb(255, 0, 0) !important; /* red background */
+ width: 200px !important;
+ height: 150px !important;
+ position: absolute !important; /* for predictable positioning */
+ top: 50px !important;
+ left: 50px !important;
+ }
+ """
+ temp_css_filename = os.path.join(OUTPUT_PATH, "_temp_red_block.css")
+ with open(temp_css_filename, "w") as f:
+ f.write(temp_css_content)
+
+ # the div that will be styled by _temp_red_block.css
+ html_content = ''
+
+ css_file_paths = [
+ "./examples/blue_background.css", # sets body { background: blue; }
+ temp_css_filename # styles #red-block
+ ]
+
+ paths = hti.screenshot(
+ html_str=html_content,
+ css_file=css_file_paths,
+ save_as="html_str_multi_css_blocks.png",
+ size=(400,300),
+ )
+
+ # clean up temporary CSS file
+ if os.path.exists(temp_css_filename):
+ os.remove(temp_css_filename)
+
+ img = Image.open(paths[0])
+ pixels = img.load()
+ assert (400,300) == img.size
+
+ # check body background color (blue)
+ assert pixels[10, 10][:3] == (0, 0, 255) # blue
+
+ # check #green-block color (red from _temp_red_block.css)
+ assert pixels[150, 125][:3] == (255, 0, 0) # red
+
@pytest.mark.parametrize("browser", TEST_BROWSERS)
def test_extend_size_param(browser):
hti = Html2Image(browser=browser, output_path=OUTPUT_PATH, disable_logging=True)