tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

operator-dictionary.py (4526B)


      1 #!/usr/bin/env python3
      2 
      3 from lxml import etree
      4 from utils.misc import downloadWithProgressBar, UnicodeXMLURL, InlineAxisOperatorsURL
      5 import json
      6 import re
      7 from utils import mathfont
      8 
      9 NonBreakingSpace = 0x00A0
     10 
     11 
     12 def parseHexaNumber(string):
     13    return int("0x%s" % string, 16)
     14 
     15 
     16 def parseHexaSequence(string):
     17    return tuple(map(parseHexaNumber, string[1:].split("-")))
     18 
     19 
     20 def parseSpaces(value, entry, names):
     21    for name in names:
     22        attributeValue = entry.get(name)
     23        if attributeValue is not None:
     24            value[name] = int(attributeValue)
     25 
     26 
     27 def parseProperties(value, entry, names):
     28    attributeValue = entry.get("properties")
     29    if attributeValue is not None:
     30        for name in names:
     31            if attributeValue.find(name) >= 0:
     32                value[name] = True
     33 
     34 
     35 def buildKeyAndValueFrom(characters, form):
     36    # Concatenate characters and form to build the key.
     37    key = ""
     38    for c in characters:
     39        key += chr(c)
     40    key += " " + form
     41    # But save characters as an individual property for easier manipulation in
     42    # this Python script.
     43    value = {
     44        "characters": characters,
     45    }
     46    return key, value
     47 
     48 
     49 # Retrieve the spec files.
     50 inlineAxisOperatorsTXT = downloadWithProgressBar(InlineAxisOperatorsURL)
     51 unicodeXML = downloadWithProgressBar(UnicodeXMLURL)
     52 
     53 # Extract the operator dictionary.
     54 xsltTransform = etree.XSLT(etree.parse("./operator-dictionary.xsl"))
     55 
     56 # Put the operator dictionary into a Python structure.
     57 inlineAxisOperators = {}
     58 with open(inlineAxisOperatorsTXT, mode="r") as f:
     59    for line in f:
     60        hexaString = re.match(r"^U\+([0-9A-F]+)", line).group(1)
     61        inlineAxisOperators[parseHexaNumber(hexaString)] = True
     62 
     63 operatorDictionary = {}
     64 root = xsltTransform(etree.parse(unicodeXML)).getroot()
     65 for entry in root:
     66    characters = parseHexaSequence(entry.get("unicode"))
     67    assert characters != (NonBreakingSpace)
     68    key, value = buildKeyAndValueFrom(characters, entry.get("form"))
     69    # There is no dictionary-specified minsize/maxsize values, so no need to
     70    # parse them.
     71    # The fence, separator and priority properties don't have any effect on math
     72    # layout, so they are not added to the JSON file.
     73    parseSpaces(value, entry, ["lspace", "rspace"])
     74    parseProperties(value, entry, ["stretchy", "symmetric", "largeop",
     75                                   "movablelimits", "accent"])
     76    if (len(characters) == 1 and characters[0] in inlineAxisOperators):
     77        value["horizontal"] = True
     78    operatorDictionary[key] = value
     79 
     80 # Create entries for the non-breaking space in all forms in order to test the
     81 # default for operators outside the official dictionary.
     82 for form in ["infix", "prefix", "suffix"]:
     83    key, value = buildKeyAndValueFrom(tuple([NonBreakingSpace]), form)
     84    operatorDictionary[key] = value
     85 
     86 # Create a WOFF font with glyphs for all the operator strings.
     87 font = mathfont.create("operators", "Copyright (c) 2019 Igalia S.L.")
     88 
     89 # Set parameters for largeop and stretchy tests.
     90 font.math.DisplayOperatorMinHeight = 2 * mathfont.em
     91 font.math.MinConnectorOverlap = mathfont.em // 2
     92 
     93 # Set parameters for accent tests so that we only have large gap when
     94 # overscript is an accent.
     95 font.math.UpperLimitBaselineRiseMin = 0
     96 font.math.StretchStackTopShiftUp = 0
     97 font.math.AccentBaseHeight = 2 * mathfont.em
     98 font.math.OverbarVerticalGap = 0
     99 
    100 mathfont.createSizeVariants(font, True)
    101 
    102 # Ensure a glyph exists for the combining characters that are handled specially
    103 # in the specification:
    104 # U+0338 COMBINING LONG SOLIDUS OVERLAY
    105 # U+20D2 COMBINING LONG VERTICAL LINE OVERLAY
    106 for combining_character in [0x338, 0x20D2]:
    107    mathfont.createSquareGlyph(font, combining_character)
    108 
    109 for key in operatorDictionary:
    110    value = operatorDictionary[key]
    111    for c in value["characters"]:
    112        if c in font:
    113            continue
    114        if c == NonBreakingSpace:
    115            g = font.createChar(c)
    116            mathfont.drawRectangleGlyph(g, mathfont.em, mathfont.em // 3, 0)
    117        else:
    118            mathfont.createSquareGlyph(font, c)
    119        mathfont.createStretchy(font, c, c in inlineAxisOperators)
    120 mathfont.save(font)
    121 
    122 # Generate the python file.
    123 for key in operatorDictionary:
    124    del operatorDictionary[key]["characters"]  # Remove this temporary value.
    125 JSON = {
    126    "comment": "This file was automatically generated by operator-dictionary.py. Do not edit.",
    127    "dictionary": operatorDictionary
    128 }
    129 with open('../support/operator-dictionary.json', 'w') as fp:
    130    json.dump(JSON, fp, sort_keys=True, ensure_ascii=True)