diff --git a/modules/extras.py b/modules/extras.py index 2a310ae3f..a6fc9f497 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -2,27 +2,62 @@ import os import re import shutil import json - +import html import torch import tqdm -from modules import shared, images, sd_models, sd_vae, sd_models_config, errors +from modules import shared, images, sd_models, sd_vae, sd_models_config, errors, png_parser from modules.ui_common import plaintext_to_html import gradio as gr import safetensors.torch +def pnginfo_format_setting(name, value): + cls_name = 'geninfo-setting-string' if value.startswith('"') else 'geninfo-setting-value' + return f"{html.escape(name)}: {html.escape(value)}" + def run_pnginfo(image): if image is None: return '', '', '' geninfo, items = images.read_info_from_image(image) - items = {**{'parameters': geninfo}, **items} info = '' - for key, text in items.items(): - info += f""" + parser = png_parser.PngParser(geninfo) + if parser.valid: + info += f""" +
+

parameters

+{plaintext_to_html(str(parser.positive))} +

+Negative prompt:
{html.escape(str(parser.negative))} +

+""" + if parser.settings is None: + info += f"{plaintext_to_html(str(parser.parameters))}" + else: + info += f"

" + first = True + for setting in parser.settings: + if first: + first = False + else: + info += ", " + info += pnginfo_format_setting(str(setting[0]), str(setting[1])+str(setting[2])) + info += f"

" + + if parser.extra is not None: + info += f"{plaintext_to_html(str(parser.extra))}" + + info += f""" +
\n +""" + else: + items = {**{'parameters': geninfo}, **items} + + for key, text in items.items(): + info += f"""

{plaintext_to_html(str(key))}

{plaintext_to_html(str(text))}

diff --git a/modules/png_parser.py b/modules/png_parser.py new file mode 100644 index 000000000..f06576fb5 --- /dev/null +++ b/modules/png_parser.py @@ -0,0 +1,48 @@ +import re + +class PngParser: + re_top_level = None + re_extra_newline = None + re_parameters = None + + def __init__(self, pnginfo_string): + PngParser.init_re() + + self.valid = self.parse_pnginfo(pnginfo_string) + + def parse_pnginfo(self, pnginfo_string): + try: + # separate positive, negative, and parameters + tlen = len(pnginfo_string) + m = PngParser.re_top_level.search(pnginfo_string) + if m is None: + return False + + self.positive = m.group(1) + self.negative = m.group(2) + self.parameters = m.group(3) + self.extra = None + self.settings = None + + # parse extra parameters (if they exist) by a newline outside of quotes + m = PngParser.re_extra_newline.search(self.parameters) + if m is not None: + s = m.span() + self.extra = self.parameters[s[1]:] + self.parameters = self.parameters[:s[0]] + + # parse standard parameters + self.settings = PngParser.re_parameters.findall(self.parameters) + if self.settings is None: + return False + except: + return False + + return True + + @classmethod + def init_re(cls): + if cls.re_top_level is None: + cls.re_top_level = re.compile(r'^(?P(?:.|\n)*)\nNegative prompt: (?P(?:.|\n)*)\n(?=Steps: )(?P(?:.|\n)*)$') + cls.re_extra_newline = re.compile(r'\n(?=(?:[^"]*"[^"]*")*[^"]*$)') + cls.re_parameters = re.compile(r'\s*(?P[^:,]+):\s*(?P")?(?P(?(2)(?:.)*?(?:(? { + el.classList.add('animate'); + }, 0); +} diff --git a/style.css b/style.css index 64ef61bad..2e054614e 100644 --- a/style.css +++ b/style.css @@ -1665,3 +1665,79 @@ body.resizing .resize-handle { visibility: visible; width: auto; } + +/* PngInfo colors */ +:root { + --pnginfo-value-color:var(--secondary-600); + --pnginfo-string-color:var(--primary-600); + --pnginfo-value-hover:var(--secondary-100); + --pnginfo-string-hover:var(--primary-100); + --pnginfo-copy-color:#22c922; + --pnginfo-copy-background:#a9cfa9; +} + +.dark { + --pnginfo-value-color:var(--secondary-400); + --pnginfo-string-color:var(--primary-400); + --pnginfo-value-hover:var(--secondary-700); + --pnginfo-string-hover:var(--primary-700); + --pnginfo-copy-color:#a9cfa9; + --pnginfo-copy-background:#22c922; +} + +/* PngInfo styles */ +.pnginfo-page p span.geninfo-setting-name { + font-weight: var(--weight-semibold); +} + +.pnginfo-page p span.geninfo-setting-value { + color: var(--pnginfo-value-color); + cursor: pointer; +} + +.pnginfo-page p span.geninfo-setting-value:hover { + background-color: var(--pnginfo-value-hover); +} + +.pnginfo-page p span.geninfo-setting-string { + color: var(--pnginfo-string-color); + font-style: italic; + cursor: pointer; +} + +.pnginfo-page p span.geninfo-setting-string:hover { + background-color: var(--pnginfo-string-hover); +} + +/* PngInfo animations */ +@keyframes copyAnimationSettingValue { + 0% { + color: var(--pnginfo-copy-color); + background-color: var(--pnginfo-copy-background); + } + 100% { + color: var(--pnginfo-value-color); + background-color: unset; + } +} + +span.geninfo-setting-value.animate { + -webkit-animation: copyAnimationSettingValue 1s 1; + animation: copyAnimationSettingValue 1s 1; +} + +@keyframes copyAnimationSettingString { + 0% { + color: var(--pnginfo-copy-color); + background-color: var(--pnginfo-copy-background); + } + 100% { + color: var(--pnginfo-string-color); + background-color: unset; + } +} + +span.geninfo-setting-string.animate { + -webkit-animation: copyAnimationSettingString 1s 1; + animation: copyAnimationSettingString 1s 1; +} \ No newline at end of file