diff --git a/vasl_templates/webapp/utils.py b/vasl_templates/webapp/utils.py index 781c786..b721238 100644 --- a/vasl_templates/webapp/utils.py +++ b/vasl_templates/webapp/utils.py @@ -4,11 +4,6 @@ import os import tempfile import pathlib -from selenium import webdriver -from PIL import Image, ImageChops - -from vasl_templates.webapp import app - # --------------------------------------------------------------------- class TempFile: @@ -41,76 +36,6 @@ class TempFile: # --------------------------------------------------------------------- -class HtmlScreenshots: - """Generate preview screenshots of HTML.""" - - def __init__( self ): - self.webdriver = None - - def __enter__( self ): - """Initialize the HTML screenshot engine.""" - webdriver_path = app.config.get( "WEBDRIVER_PATH" ) - if not webdriver_path: - raise SimpleError( "No webdriver has been configured." ) - # NOTE: If we are being run on Windows without a console (e.g. the frozen PyQt desktop app), - # Selenium will launch the webdriver in a visible DOS box :-( There's no way to turn this off, - # but it can be disabled by modifying the Selenium source code. Find the subprocess.Popen() call - # in $/site-packages/selenium/webdriver/common/service.py and add the following parameter: - # creationflags = 0x8000000 # win32process.CREATE_NO_WINDOW - # It's pretty icky to have to do this, but since we're in a virtualenv, it's not too bad... - kwargs = { "executable_path": webdriver_path } - if "chromedriver" in webdriver_path: - options = webdriver.ChromeOptions() - options.set_headless( headless=True ) - # OMG! The chromedriver looks for Chrome/Chromium in a hard-coded, fixed location (the default - # installation directory). We offer a way here to override this. - chrome_path = app.config.get( "CHROME_PATH" ) - if chrome_path: - options.binary_location = chrome_path - kwargs["chrome_options"] = options - self.webdriver = webdriver.Chrome( **kwargs ) - elif "geckodriver" in webdriver_path: - options = webdriver.FirefoxOptions() - options.set_headless( headless=True ) - kwargs["firefox_options"] = options - kwargs["log_path"] = app.config.get( "GECKODRIVER_LOG", - os.path.join( tempfile.gettempdir(), "geckodriver.log" ) - ) - self.webdriver = webdriver.Firefox( **kwargs ) - else: - raise SimpleError( "Can't identify webdriver: {}".format( webdriver_path ) ) - return self - - def __exit__( self, exc_type, exc_val, exc_tb ): - """Clean up.""" - if self.webdriver: - self.webdriver.quit() - - def get_screenshot( self, html, window_size ): - """Get a preview screenshot of the specified HTML.""" - - self.webdriver.set_window_size( window_size[0], window_size[1] ) - with TempFile( extn=".html", mode="w" ) as html_tempfile: - - # take a screenshot of the HTML - # NOTE: We could do some funky Javascript stuff to load the browser directly from the string, - # but using a temp file is straight-forward and pretty much guaranteed to work :-/ - html_tempfile.write( html ) - html_tempfile.close() - self.webdriver.get( "file://{}".format( html_tempfile.name ) ) - with TempFile( extn=".png" ) as screenshot_tempfile: - screenshot_tempfile.close() - self.webdriver.save_screenshot( screenshot_tempfile.name ) - img = Image.open( screenshot_tempfile.name ) - - # trim the screenshot (nb: we assume a white background) - bgd = Image.new( img.mode, img.size, (255,255,255,255) ) - diff = ImageChops.difference( img, bgd ) - bbox = diff.getbbox() - return img.crop( bbox ) - -# --------------------------------------------------------------------- - def change_extn( fname, extn ): """Change a filename's extension.""" return pathlib.Path( fname ).with_suffix( extn ) diff --git a/vasl_templates/webapp/vassal.py b/vasl_templates/webapp/vassal.py index 55a8878..e610f79 100644 --- a/vasl_templates/webapp/vassal.py +++ b/vasl_templates/webapp/vassal.py @@ -18,7 +18,8 @@ from vasl_templates.webapp import app from vasl_templates.webapp.config.constants import BASE_DIR, IS_FROZEN from vasl_templates.webapp.files import vasl_mod from vasl_templates.webapp.file_server.vasl_mod import SUPPORTED_VASL_MOD_VERSIONS -from vasl_templates.webapp.utils import TempFile, HtmlScreenshots, SimpleError +from vasl_templates.webapp.utils import TempFile, SimpleError +from vasl_templates.webapp.webdriver import WebDriver _logger = logging.getLogger( "update_vsav" ) @@ -132,60 +133,39 @@ def _save_snippets( snippets, fp ): NOTE: We save the snippets as XML because Java :-/ """ - def get_html_size( snippet_id, html, window_size ): - """Get the size of the specified HTML.""" - start_time = time.time() - img = html_screenshots.get_screenshot( html, window_size ) - elapsed_time = time.time() - start_time - width, height = img.size - _logger.debug( "Generated screenshot for %s (%.3fs): %dx%d", snippet_id, elapsed_time, width, height ) - return width, height - - def do_save_snippets( html_screenshots ): #pylint: disable=too-many-locals + def do_save_snippets( webdriver ): #pylint: disable=too-many-locals """Save the snippets.""" root = ET.Element( "snippets" ) - for key,val in snippets.items(): + for snippet_id,snippet_info in snippets.items(): # add the next snippet - auto_create = "true" if val["auto_create"] else "false" - elem = ET.SubElement( root, "snippet", id=key, autoCreate=auto_create ) - elem.text = val["content"] - label_area = val.get( "label_area" ) + auto_create = "true" if snippet_info["auto_create"] else "false" + elem = ET.SubElement( root, "snippet", id=snippet_id, autoCreate=auto_create ) + elem.text = snippet_info["content"] + label_area = snippet_info.get( "label_area" ) if label_area: elem.set( "labelArea", label_area ) # add the raw content elem2 = ET.SubElement( elem, "rawContent" ) - for node in val.get("raw_content",[]): + for node in snippet_info.get( "raw_content", [] ): ET.SubElement( elem2, "phrase" ).text = node # include the size of the snippet - if html_screenshots: + if webdriver: try: - # NOTE: Screenshots take significantly longer for larger window sizes. Since most of our snippets - # will be small, we first try with a smaller window, and switch to a larger one if necessary. - max_width, max_height = 500, 500 - max_width2, max_height2 = 1500, 1500 - if key.startswith( - ("ob_vehicles_ma_notes_","ob_vehicle_note_","ob_ordnance_ma_notes_","ob_ordnance_note_") - ): - # nb: these tend to be large, don't bother with a smaller window - max_width, max_height = max_width2, max_height2 - width, height = get_html_size( key, val["content"], (max_width,max_height) ) - if (width >= max_width*9/10 or height >= max_height*9/10) \ - and (max_width,max_height) != (max_width2,max_height2): - # NOTE: While it's tempting to set the browser window really large here, if the label ends up - # filling/overflowing the available space (e.g. because its width/height has been set to 100%), - # then the auto-created label will push any subsequent labels far down the map, possibly to - # somewhere unreachable. So, we set it somewhat more conservatively, so that if this happens, - # the user still has a chance to recover from it. Note that this doesn't mean that they can't - # have really large labels, it just affects the positioning of auto-created labels. - width, height = get_html_size( key, val["content"], (max_width2,max_height2) ) + start_time = time.time() + img = webdriver.get_snippet_screenshot( snippet_id, snippet_info["content"] ) + width, height = img.size + elapsed_time = time.time() - start_time + _logger.debug( "Generated screenshot for %s (%.3fs): %dx%d", + snippet_id, elapsed_time, width, height + ) # FUDGE! There's something weird going on in VASSAL e.g. "