diff --git a/pixelle_video/services/frame_html.py b/pixelle_video/services/frame_html.py index 9fff226..4efd02d 100644 --- a/pixelle_video/services/frame_html.py +++ b/pixelle_video/services/frame_html.py @@ -31,6 +31,7 @@ from typing import Dict, Any, Optional from pathlib import Path from html2image import Html2Image from loguru import logger +from PIL import Image from pixelle_video.utils.template_util import parse_template_size @@ -52,6 +53,13 @@ class HTMLFrameGenerator: ... ) """ + # Workaround for Chromium screenshot height issue + # Due to a Chromium bug that causes screenshots to be cropped at the bottom, + # we temporarily render with extra height and then crop it back. + # See: https://issues.chromium.org/issues/405165895 + # This is a temporary workaround until the issue is fixed in Chromium. + CHROMIUM_HEIGHT_OFFSET = 87 + def __init__(self, template_path: str): """ Initialize HTML frame generator @@ -363,8 +371,11 @@ class HTMLFrameGenerator: # Try to find non-snap browser browser_path = self._find_chrome_executable() + # Workaround: Add extra height to compensate for Chromium screenshot cropping bug + # The extra pixels will be cropped back in generate_frame() after rendering + # See CHROMIUM_HEIGHT_OFFSET constant for details kwargs = { - 'size': (width, height), + 'size': (width, height + self.CHROMIUM_HEIGHT_OFFSET), 'custom_flags': custom_flags } @@ -465,6 +476,18 @@ class HTMLFrameGenerator: if os.path.exists(temp_file) and temp_file != output_path: shutil.move(temp_file, output_path) + # Workaround: Crop image to remove extra height added to compensate for Chromium bug + # Chromium screenshots are cropped at the bottom, so we render with extra height + # and then crop it back to the desired size. See CHROMIUM_HEIGHT_OFFSET constant. + # Reference: https://issues.chromium.org/issues/405165895 + if os.path.exists(output_path): + with Image.open(output_path) as img: + # Crop from (0, 0) to (originWidth, originHeight) + # This removes the extra CHROMIUM_HEIGHT_OFFSET pixels added during rendering + cropped_img = img.crop((0, 0, self.width, self.height)) + cropped_img.save(output_path) + logger.debug(f"Cropped image to size: {self.width}x{self.height} (removed {self.CHROMIUM_HEIGHT_OFFSET}px workaround offset)") + logger.info(f"✅ Frame generated: {output_path}") return output_path