# ------------------------------- # Difficult Rocket # Copyright © 2021-2022 by shenjackyuanjie 3695888@qq.com # All rights reserved # ------------------------------- """ writen by shenjackyuanjie mail: 3695888@qq.com github: @shenjackyuanjie gitee: @shenjackyuanjie """ import re from Difficult_Rocket.utils import translate 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 type(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 type(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 type(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 type(other) == SingleTextStyle return other.tag in self.tag def same_font(self, other: 'SingleTextStyle') -> bool: """ 比较两个字体样式的字体属性是否相同 :param other: 叠加的字体样式 :return: 是否相同 """ assert type(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 type(other) == SingleTextStyle return self.bold == other.bold def same_italic(self, other: 'SingleTextStyle') -> bool: """ 比较两个字体样式的斜体属性是否相同 :param other: 叠加的字体样式 :return: 是否相同 """ assert type(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 格式字符 """ if not suffix: return self.HTML_bold() + self.HTML_italic() + self.HTML_font() else: return font_HTML_end + (bold_HTML_end if self.bold else '') + (italic_HTML_end if self.italic else '') # [\u4e00-\u9fa5] 中文字符 default_fonts_config = [ { 'match': re.compile(r''), # 匹配的字符 匹配选项是re.compile() 'shown': re.compile(r''), # 匹配到的字符中显示的部分 匹配选项是re.compile() 'style': SingleTextStyle(font_name=translate.鸿蒙简体, 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=translate.微软等宽, 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 text == '': return '' if configs is None: configs = default_fonts_config style_list = [SingleTextStyle(text=text[x]) for x in range(0, 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 not style_list[style_index - 1].show: # 如果前面一个字符不显示(且这个字符显示) style_list[style_index].prefix += style_list[style_index].HTML() # 那么就直接给这个字符的前缀添加 else: # 开始根据前面的情况处理每种单独的标签 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: # 如果这个字符不显示 if style_list[style_index - 1].show: # 如果前面一个字符显示(且这个字符不显示) style_list[style_index - 1].suffix += style_list[style_index - 1].HTML(suffix=True) # 如果前面一个字符也不显示那就直接 pass if style_list[-1].show: style_list[-1].suffix += style_list[-1].HTML(suffix=True) # 输出最终的 HTML 文本 formatted_HTML_text = '' # 初始化一下 for style in style_list: # 每一个样式 if style.show: # 如果这个字符显示 formatted_HTML_text += style.prefix + style.text + style.suffix # 文本的后面附加一下 del style_list # 主动删掉 style_list 释放内存 return formatted_HTML_text # 返回,DONE!