【问题标题】:Initialize object for unicode fonts初始化 unicode 字体的对象
【发布时间】:2021-09-11 03:40:07
【问题描述】:

我编写了一个类对象来访问 unicode 块中的数学字母数字符号,如 https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols 中所述

# San-serif
LATIN_SANSERIF_NORMAL_UPPER = (120224, 120250)
LATIN_SANSERIF_NORMAL_LOWER = (120250, 120276)
LATIN_SANSERIF_BOLD_UPPER = (120276, 120302)
LATIN_SANSERIF_BOLD_LOWER = (120302, 120328)
LATIN_SANSERIF_ITALIC_UPPER = (120328, 120354)
LATIN_SANSERIF_ITALIC_LOWER = (120354, 120380)
LATIN_SANSERIF_BOLDITALIC_UPPER = (120380, 120406)
LATIN_SANSERIF_BOLDITALIC_LOWER = (120406, 120432)

class MathAlphanumeric:
    def __init__(self, script, font, style, case):
        self.script = script
        self.font = font
        self.style = style
        self.case = case
        
    def charset(self):
        start, end = eval('_'.join([self.script, self.font, self.style, self.case]).upper())
        for c in range(start, end):
            yield chr(c)
    
    @staticmethod
    def supported_scripts():
        return {'latin', 'greek', 'digits'}
    
    @staticmethod
    def supported_fonts():
        return {'serif', 'sanserif', 'calligraphy', 'fraktor', 'monospace', 'doublestruck'}
    
    @staticmethod
    def supported_style():
        return {'normal', 'bold', 'italic', 'bold-italic'}
    
    @staticmethod
    def supported_case():
        return {'upper', 'lower'}
         

要使用它,我会这样做:

ma = MathAlphanumeric('latin', 'sanserif', 'bold', 'lower')
print(list(ma.charset()))

[出]:

['????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????', '????']

代码按预期工作,但要涵盖所有数学字母符号,我必须枚举script * fonts * style * case 编号中的所有开始和结束符号。常量。

我的问题是:

  • 有没有更好的方法来创建所需的MathAlphanumeric 对象?
  • 有没有办法避免script * fonts * style * case的初始化?常量,以使MathAlphanumeric.charset() 按预期工作?
  • 在某些 unicode.org 相关库中是否提供了类似的对象或函数?

【问题讨论】:

    标签: python object unicode character-set


    【解决方案1】:

    您可能对unicodedata 标准库感兴趣,特别是:

    • unicodedata.lookup

      按名称查找字符。如果找到具有给定名称的字符,则返回相应的字符。如果未找到,则会引发 KeyError

    • unicodedata.name

      将分配给字符 chr 的名称作为字符串返回。

    一个简单的例子:

    >>> import unicodedata
    >>> unicodedata.name(chr(0x1d5a0))
    'MATHEMATICAL SANS-SERIF CAPITAL A'
    >>> unicodedata.lookup("MATHEMATICAL SANS-SERIF CAPITAL A")
    '?'
    >>> unicodedata.name(chr(0x1d504))
    'MATHEMATICAL FRAKTUR CAPITAL A'
    >>> unicodedata.lookup("MATHEMATICAL FRAKTUR CAPITAL A")
    '?'
    

    现在您必须找到unicodedata 为您的用例所期望的所有名称,从中构造相应的字符串,然后调用lookup

    这是一个迷你概念验证:

    import unicodedata
    import string
    
    
    def charset(script: str, font: str, style: str, case: str):
        features = ["MATHEMATICAL"]
        # TODO: use script
        assert font in MathAlphanumeric.supported_fonts(), f"invalid font {font!r}"
        features.append(font.upper())
        assert style in MathAlphanumeric.supported_style(), f"invalid style {style!r}"
        if style != "normal":
            if font == "fraktur":
                features.insert(-1, style.upper())  # "bold" must be before "fraktur"
            elif font in ("monospace", "double-struck"):
                pass  # it has only one style, and it is implicit
            else:
                features.append(style.upper())
        assert case in MathAlphanumeric.supported_case(), f"invalid case {case!r}"
        features.append("CAPITAL" if case == "upper" else "SMALL")
        return tuple(unicodedata.lookup(" ".join(features + [letter]), ) for letter in string.ascii_uppercase)
    
    
    if __name__ == '__main__':
        print("".join(charset("latin", "sans-serif", "bold", "lower")))
        # ??????????????????????????
        print("".join(charset("latin", "fraktur", "bold", "upper")))
        # ??????????????????????????
        print("".join(charset("latin", "monospace", "bold", "upper")))
        # ??????????????????????????
        print("".join(charset("latin", "double-struck", "bold", "upper")))
        # KeyError: "undefined character name 'MATHEMATICAL DOUBLE-STRUCK CAPITAL C'"
    

    (我改变了一点你的supported_fonts方法:return {'serif', 'sans-serif', 'calligraphy', 'fraktur', 'monospace', 'double-struck'}

    但是 Unicode 中有很多警告:它包含您可能想要的所有字形,但没有以连贯的方式组织(由于历史原因)。我的示例中的失败是由以下原因引起的:

    >>> unicodedata.name("?")  # the letter copied from the Wikipedia page
    'MATHEMATICAL FRAKTUR CAPITAL B'
    >>> unicodedata.name("ℭ")  # same, but for C
    'BLACK-LETTER CAPITAL C'
    

    所以你需要很多特殊情况。

    还有:

    • 使用eval 被认为是一种不好的做法(参见this question),如果你可以避免的话,你应该这样做。
    • 使用 unicode "characters" 的十进制值很不方便,我不得不将您的代码与 Wikipedia 页面进行比较。只需加上 0x 前缀就足以告诉 Python 它是一个十六进制值,但除了看起来“奇怪”之外,它的工作原理完全相同:0x1d5a0 == 120224 是 True。
    • 使用只有一个从实例__init__ 获取其参数的方法的类被认为是smell,您可以将其设为一个函数,更简单、更简洁。如果您想要的是 namespace,您可以改用 Python 模块。
    • 支持的脚本、字体、样式和大小写是不变的,您可以将它们设为类变量,而不是将它们放在staticmethods 中。

    【讨论】:

      猜你喜欢
      • 2013-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多