Source code

Revision control

Copy as Markdown

Other Tools

#!/usr/bin/env python3
from lxml import etree
from utils.misc import downloadWithProgressBar, UnicodeXMLURL, InlineAxisOperatorsURL
import json
import re
from utils import mathfont
NonBreakingSpace = 0x00A0
def parseHexaNumber(string):
return int("0x%s" % string, 16)
def parseHexaSequence(string):
return tuple(map(parseHexaNumber, string[1:].split("-")))
def parseSpaces(value, entry, names):
for name in names:
attributeValue = entry.get(name)
if attributeValue is not None:
value[name] = int(attributeValue)
def parseProperties(value, entry, names):
attributeValue = entry.get("properties")
if attributeValue is not None:
for name in names:
if attributeValue.find(name) >= 0:
value[name] = True
def buildKeyAndValueFrom(characters, form):
# Concatenate characters and form to build the key.
key = ""
for c in characters:
key += chr(c)
key += " " + form
# But save characters as an individual property for easier manipulation in
# this Python script.
value = {
"characters": characters,
}
return key, value
# Retrieve the spec files.
inlineAxisOperatorsTXT = downloadWithProgressBar(InlineAxisOperatorsURL)
unicodeXML = downloadWithProgressBar(UnicodeXMLURL)
# Extract the operator dictionary.
xsltTransform = etree.XSLT(etree.parse("./operator-dictionary.xsl"))
# Put the operator dictionary into a Python structure.
inlineAxisOperators = {}
with open(inlineAxisOperatorsTXT, mode="r") as f:
for line in f:
hexaString = re.match(r"^U\+([0-9A-F]+)", line).group(1)
inlineAxisOperators[parseHexaNumber(hexaString)] = True
operatorDictionary = {}
root = xsltTransform(etree.parse(unicodeXML)).getroot()
for entry in root:
characters = parseHexaSequence(entry.get("unicode"))
assert characters != (NonBreakingSpace)
key, value = buildKeyAndValueFrom(characters, entry.get("form"))
# There is no dictionary-specified minsize/maxsize values, so no need to
# parse them.
# The fence, separator and priority properties don't have any effect on math
# layout, so they are not added to the JSON file.
parseSpaces(value, entry, ["lspace", "rspace"])
parseProperties(value, entry, ["stretchy", "symmetric", "largeop",
"movablelimits", "accent"])
if (len(characters) == 1 and characters[0] in inlineAxisOperators):
value["horizontal"] = True
operatorDictionary[key] = value
# Create entries for the non-breaking space in all forms in order to test the
# default for operators outside the official dictionary.
for form in ["infix", "prefix", "suffix"]:
key, value = buildKeyAndValueFrom(tuple([NonBreakingSpace]), form)
operatorDictionary[key] = value
# Create a WOFF font with glyphs for all the operator strings.
font = mathfont.create("operators", "Copyright (c) 2019 Igalia S.L.")
# Set parameters for largeop and stretchy tests.
font.math.DisplayOperatorMinHeight = 2 * mathfont.em
font.math.MinConnectorOverlap = mathfont.em // 2
# Set parameters for accent tests so that we only have large gap when
# overscript is an accent.
font.math.UpperLimitBaselineRiseMin = 0
font.math.StretchStackTopShiftUp = 0
font.math.AccentBaseHeight = 2 * mathfont.em
font.math.OverbarVerticalGap = 0
mathfont.createSizeVariants(font, True)
# Ensure a glyph exists for the combining characters that are handled specially
# in the specification:
# U+0338 COMBINING LONG SOLIDUS OVERLAY
# U+20D2 COMBINING LONG VERTICAL LINE OVERLAY
for combining_character in [0x338, 0x20D2]:
mathfont.createSquareGlyph(font, combining_character)
for key in operatorDictionary:
value = operatorDictionary[key]
for c in value["characters"]:
if c in font:
continue
if c == NonBreakingSpace:
g = font.createChar(c)
mathfont.drawRectangleGlyph(g, mathfont.em, mathfont.em // 3, 0)
else:
mathfont.createSquareGlyph(font, c)
mathfont.createStretchy(font, c, c in inlineAxisOperators)
mathfont.save(font)
# Generate the python file.
for key in operatorDictionary:
del operatorDictionary[key]["characters"] # Remove this temporary value.
JSON = {
"comment": "This file was automatically generated by operator-dictionary.py. Do not edit.",
"dictionary": operatorDictionary
}
with open('../support/operator-dictionary.json', 'w') as fp:
json.dump(JSON, fp, sort_keys=True, ensure_ascii=True)