# ------------------------------- # Difficult Rocket # Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com # All rights reserved # ------------------------------- """ writen by shenjackyuanjie mail: 3695888@qq.com github: @shenjackyuanjie gitee: @shenjackyuanjie """ import re from Difficult_Rocket.api.types import Fonts from pyglet.text.formats import structured default_style = { "font_name": "Times New Roman", "font_size": 12, "bold": False, "italic": False, } class MarkdownTextDecoder(structured.StructuredTextDecoder): ... class SingleTextStyle: """ 单个字符的字体样式 """ def __init__( self, font_name: str = "", font_size: int = 12, bold: bool = False, italic: bool = False, color: str = "white", text_tag: list = None, show: bool = True, prefix: str = "", suffix: str = "", text: str = "", ): self.font_name = font_name self.font_size = font_size self.bold = bold self.italic = italic self.color = color self.prefix = prefix self.suffix = suffix if not text_tag: self._tag = [] else: self._tag = text_tag self.show = show self.text = text @property def tag(self) -> list: return self._tag @tag.setter def tag(self, value: list): assert isinstance(value, list), "SingleTextStyle.tag must be list" for tag in value: if tag not in self._tag: self._tag.append(tag) self._tag.sort() """ 对运算操作的支持 """ def __add__(self, other: "SingleTextStyle") -> "SingleTextStyle": """ 叠加两个字体样式 优先使用 other 的样式 :param other: 叠加的字体样式 :return: 叠加后的字体样式 """ assert isinstance( other, SingleTextStyle ), f"SingleTextStyle + other\n other must be the same type, not a {type(other)}" return SingleTextStyle( font_name=other.font_name or self.font_name, font_size=other.font_size or self.font_size, bold=other.bold or self.bold, italic=other.italic or self.italic, color=other.color or self.color, text_tag=other.tag + self.tag, show=other.show or self.show, prefix=other.prefix + self.prefix, suffix=other.suffix + self.suffix, text=self.text, ) def __iadd__(self, other: "SingleTextStyle") -> "SingleTextStyle": """ 叠加两个字体样式 优先使用 other 的样式 :param other: 叠加的字体样式 :return: 叠加后的字体样式 """ assert isinstance( other, SingleTextStyle ), f"SingleTextStyle += other\n other must be the same type, not a {type(other)}" self.font_name = other.font_name or self.font_name self.font_size = other.font_size or self.font_size self.bold = other.bold or self.bold self.italic = other.italic or self.italic self.color = other.color or self.color self.tag += other.tag self.show = other.show or self.show self.prefix += other.prefix self.suffix += other.suffix self.text = self.text return self """ 对各种判定的支持 """ def have_tag(self, other: "SingleTextStyle") -> bool: """ 比较两个字体样式tag是否相同 :param other: 叠加的字体样式 :return: 是否相同 """ assert isinstance(other, SingleTextStyle) return other.tag in self.tag def same_font(self, other: "SingleTextStyle") -> bool: """ 比较两个字体样式的字体属性是否相同 :param other: 叠加的字体样式 :return: 是否相同 """ assert isinstance(other, SingleTextStyle) return ( self.font_name == other.font_name and self.font_size == other.font_size and self.color == other.color and self.show == other.show ) def same_bold(self, other: "SingleTextStyle") -> bool: """ 比较两个字体样式的加粗属性是否相同 :param other: 叠加的字体样式 :return: 是否相同 """ assert isinstance(other, SingleTextStyle) return self.bold == other.bold def same_italic(self, other: "SingleTextStyle") -> bool: """ 比较两个字体样式的斜体属性是否相同 :param other: 叠加的字体样式 :return: 是否相同 """ assert isinstance(other, SingleTextStyle) return self.italic == other.italic """ 自动输出一些属性的支持 """ def HTML_font(self, suffix: bool = False) -> str: """ 输出字体样式的HTML字符 :return: HTML 格式字符 """ if suffix: return font_HTML_end text = f' str: """ 输出字体粗体的HTML字符 :return: HTML 格式字符 """ if self.bold: if suffix: return bold_HTML_end return "" else: return "" def HTML_italic(self, suffix: bool = False) -> str: """ 输出字体斜体的HTML字符 :return: HTML 格式字符 """ if self.italic: if suffix: return italic_HTML_end return "" else: return "" def HTML(self, suffix: bool = False) -> str: """ 输出字体样式的HTML字符 :return: HTML 格式字符 """ return ( ( font_HTML_end + (bold_HTML_end if self.bold else "") + (italic_HTML_end if self.italic else "") ) if suffix else self.HTML_bold() + self.HTML_italic() + self.HTML_font() ) # [\u4e00-\u9fa5] 中文字符 default_fonts_config = [ { "match": re.compile(r""), # 匹配的字符 匹配选项是re.compile() "shown": re.compile(r""), # 匹配到的字符中显示的部分 匹配选项是re.compile() "style": SingleTextStyle( font_name=Fonts.鸿蒙简体, font_size=15, bold=False, italic=False, show=True, color="white", ), }, { "match": re.compile(r"[a-zA-Z0-9]"), "shown": re.compile(r"[a-zA-Z0-9]"), "style": SingleTextStyle(font_name=Fonts.微软等宽, font_size=15), }, # Markdown 语法规则匹配 { # Markdown 粗体语法规则匹配 "match": re.compile(r"\*\*(.*?(?" bold_HTML = "" bold_HTML_end = "" italic_HTML = "" italic_HTML_end = "" def decode_text2HTML(text: str, configs=None, show_style: bool = False) -> str: if not text: return "" if configs is None: configs = default_fonts_config style_list = [SingleTextStyle(text=text[x]) for x in range(len(text))] # 根据输入的配置对每一个字符进行样式设定 for config in configs: # 根据 配置"文件" match_texts = config["match"].finditer(text) # 使用 config.match 匹配 for match_text in match_texts: # 每一个匹配到的匹配项 text_match = match_text.group() # 缓存一下匹配到的字符,用于匹配显示的字符 shown_texts = config["shown"].finditer(text_match) # 使用 config.shown 匹配 match_start, match_end = match_text.span() if "ignore" in config: # 如果样式选项包含忽略某些字符的tag ignore_texts = config["ignore"]["match"].finditer( text_match ) # 根据选项匹配可能忽略的字符 ignore = False # 忽略先为False for ignore_text in ignore_texts: # 每一个可能忽略的字符 if ignore: # 为了方便退出 break for ignore_index in range( match_start + ignore_text.span()[0], match_start + ignore_text.span()[1], ): # 对每一个可能的字符进行检测 if style_list[ignore_index].have_tag( config["ignore"]["tag"] ): # 如果确实包含要忽略的 ignore = True # 忽略为True break if ignore: continue # 跳过本次匹配 if "tag" in config: # 如果样式选项包含对部分字符添加tag tag_texts = config["tag"]["match"].finditer( text_match ) # 根据配置的正则表达式匹配要添加tag的字符 for tag_text in tag_texts: # 对每一个匹配到的~~~~~~ for tag_index in range( match_start + tag_text.span()[0], match_start + tag_text.span()[1] ): # 用于遍历匹配到的字符 style_list[tag_index] += config["tag"]["style"] # 为匹配到的字符添加样式 for match_index in range(match_start, match_end): # 用于遍历匹配到的字符 # 这里用 match index 来精确读写列表里的元素,毕竟 re.Match 返回的 span 是两个标点,得遍历 style_list[match_index] += config[ "style" ] # 字体样式列表的 [match_index] += config['style'] 的样式 style_list[match_index].show = show_style # 设置显示属性变为 False # 为每一个显示的字符设置显示属性 for shown_text in shown_texts: # 每一个显示的匹配项 for shown_index in range( match_start + shown_text.span()[0], match_start + shown_text.span()[1] ): style_list[shown_index].show = True # 字体样式列表的 [shown_index] 设置显示属性变为 True # 开始根据配置好的样式输出HTML文本 style_list[0].prefix += style_list[0].HTML() # 不管怎么说都要在最前面加一个字符标识 for style_index in range(1, len(style_list)): if style_list[style_index].show: # 如果这个字符显示 if style_list[style_index - 1].show: # 开始根据前面的情况处理每种单独的标签 if not style_list[style_index - 1].same_font(style_list[style_index]): style_list[style_index - 1].suffix += style_list[ style_index - 1 ].HTML_font(suffix=True) style_list[style_index].prefix += style_list[style_index].HTML_font() if not style_list[style_index - 1].same_bold(style_list[style_index]): style_list[style_index - 1].suffix += style_list[ style_index - 1 ].HTML_bold(suffix=True) style_list[style_index].prefix += style_list[style_index].HTML_bold() if not style_list[style_index - 1].same_italic(style_list[style_index]): style_list[style_index - 1].suffix += style_list[ style_index - 1 ].HTML_italic(suffix=True) style_list[style_index].prefix += style_list[ style_index ].HTML_italic() else: # 如果前面一个字符不显示(且这个字符显示) style_list[style_index].prefix += style_list[ style_index ].HTML() # 那么就直接给这个字符的前缀添加 elif style_list[style_index - 1].show: # 如果前面一个字符显示(且这个字符不显示) style_list[style_index - 1].suffix += style_list[style_index - 1].HTML( suffix=True ) if style_list[-1].show: style_list[-1].suffix += style_list[-1].HTML(suffix=True) formatted_HTML_text = "".join( style.prefix + style.text + style.suffix for style in style_list if style.show ) del style_list # 主动删掉 style_list 释放内存 return formatted_HTML_text # 返回,DONE!