about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/a.nim1
-rw-r--r--src/buffer.nim458
-rw-r--r--src/config399
-rw-r--r--src/config.nim139
-rw-r--r--src/default.css20
-rw-r--r--src/display.nim395
-rw-r--r--src/dom.nim331
-rwxr-xr-xsrc/entitybin0 -> 85288 bytes
-rw-r--r--src/entity.json2233
-rw-r--r--src/entity.nim18
-rw-r--r--src/enums.nim103
-rw-r--r--src/main.nim65
-rw-r--r--src/parser.nim555
-rw-r--r--src/radixtree.nim151
-rw-r--r--src/style.nim42
-rw-r--r--src/termattrs.nim11
-rw-r--r--src/twtio.nim180
-rw-r--r--src/twtstr.nim423
18 files changed, 5524 insertions, 0 deletions
diff --git a/src/a.nim b/src/a.nim
new file mode 100644
index 00000000..da4e4903
--- /dev/null
+++ b/src/a.nim
@@ -0,0 +1 @@
+echo int(high(char))
diff --git a/src/buffer.nim b/src/buffer.nim
new file mode 100644
index 00000000..5eaa4717
--- /dev/null
+++ b/src/buffer.nim
@@ -0,0 +1,458 @@
+import options
+import uri
+import tables
+import strutils
+import unicode
+
+import termattrs
+import dom
+import twtio
+import enums
+import twtstr
+
+type
+  Buffer* = ref BufferObj
+  BufferObj = object
+    fmttext*: seq[string]
+    rawtext*: seq[string]
+    title*: string
+    hovertext*: string
+    width*: int
+    height*: int
+    cursorx*: int
+    cursory*: int
+    xend*: int
+    fromx*: int
+    fromy*: int
+    nodes*: seq[Node]
+    links*: seq[Node]
+    clickables*: seq[Node]
+    elements*: seq[Element]
+    idelements*: Table[string, Element]
+    selectedlink*: Node
+    printwrite*: bool
+    attrs*: TermAttributes
+    document*: Document
+
+proc newBuffer*(attrs: TermAttributes): Buffer =
+  return Buffer(width: attrs.termWidth,
+                height: attrs.termHeight,
+                attrs: attrs)
+
+func lastLine*(buffer: Buffer): int =
+  assert(buffer.fmttext.len == buffer.rawtext.len)
+  return buffer.fmttext.len - 1
+
+func lastVisibleLine*(buffer: Buffer): int =
+  return min(buffer.fromy + buffer.height - 1, buffer.lastLine())
+
+func currentLineLength*(buffer: Buffer): int =
+  return buffer.rawtext[buffer.cursory].width()
+
+func atPercentOf*(buffer: Buffer): int =
+  if buffer.fmttext.len == 0: return 100
+  return (100 * (buffer.cursory + 1)) div (buffer.lastLine() + 1)
+
+func fmtBetween*(buffer: Buffer, sx: int, sy: int, ex: int, ey: int): string =
+  if sy < ey:
+    result &= buffer.rawtext[sy].runeSubstr(sx)
+    var i = sy + 1
+    while i < ey - 1:
+      result &= buffer.rawtext[i]
+      inc i
+    result &= buffer.rawtext[i].runeSubstr(0, ex - sx)
+  else:
+    result &= buffer.rawtext[sy].runeSubstr(sx, ex - sx)
+
+func visibleText*(buffer: Buffer): string = 
+  return buffer.fmttext[buffer.fromy..buffer.lastVisibleLine()].join("\n")
+
+func lastNode*(buffer: Buffer): Node =
+  return buffer.nodes[^1]
+
+func cursorOnNode*(buffer: Buffer, node: Node): bool =
+  if node.y == node.ey and node.y == buffer.cursory:
+    return buffer.cursorx >= node.x and buffer.cursorx < node.ex
+  else:
+    return (buffer.cursory == node.y and buffer.cursorx >= node.x) or
+           (buffer.cursory > node.y and buffer.cursory < node.ey) or
+           (buffer.cursory == node.ey and buffer.cursorx < node.ex)
+
+func findSelectedElement*(buffer: Buffer): Option[HtmlElement] =
+  if buffer.selectedlink != nil and buffer.selectedLink.parentNode of HtmlElement:
+    return some(HtmlElement(buffer.selectedlink.parentNode))
+  for node in buffer.nodes:
+    if node.isElemNode():
+      if node.getFmtLen() > 0:
+        if buffer.cursorOnNode(node): return some(HtmlElement(node))
+  return none(HtmlElement)
+
+func canScroll*(buffer: Buffer): bool =
+  return buffer.lastLine() > buffer.height
+
+func getElementById*(buffer: Buffer, id: string): Element =
+  if buffer.idelements.hasKey(id):
+    return buffer.idelements[id]
+  return nil
+
+proc findSelectedNode*(buffer: Buffer): Option[Node] =
+  for node in buffer.nodes:
+    if node.getFmtLen() > 0 and node.displayed():
+      if buffer.cursory >= node.y and buffer.cursory <= node.y + node.height and buffer.cursorx >= node.x and buffer.cursorx <= node.x + node.width:
+        return some(node)
+  return none(Node)
+
+proc addNode*(buffer: Buffer, node: Node) =
+  buffer.nodes.add(node)
+
+  if node.isTextNode() and node.parentElement != nil and node.parentElement.getStyle().islink:
+    buffer.links.add(node)
+
+  if node.isElemNode():
+    case Element(node).tagType
+    of TAG_INPUT, TAG_OPTION:
+      if not Element(node).hidden:
+        buffer.clickables.add(node)
+    else: discard
+  elif node.isTextNode():
+    if node.parentElement != nil and node.parentElement.style.islink:
+      let anchor = node.ancestor(TAG_A)
+      assert(anchor != nil)
+      buffer.clickables.add(anchor)
+
+  if node.isElemNode():
+    let elem = Element(node)
+    buffer.elements.add(elem)
+    if elem.id != "" and not buffer.idelements.hasKey(elem.id):
+      buffer.idelements[elem.id] = elem
+
+proc writefmt*(buffer: Buffer, str: string) =
+  buffer.fmttext &= str
+  if buffer.printwrite:
+    stdout.write(str)
+
+proc writefmt*(buffer: Buffer, c: char) =
+  buffer.rawtext &= $c
+  if buffer.printwrite:
+    stdout.write(c)
+
+proc writeraw*(buffer: Buffer, str: string) =
+  buffer.rawtext &= str
+
+proc writeraw*(buffer: Buffer, c: char) =
+  buffer.rawtext &= $c
+
+proc write*(buffer: Buffer, str: string) =
+  buffer.writefmt(str)
+  buffer.writeraw(str)
+
+proc write*(buffer: Buffer, c: char) =
+  buffer.writefmt(c)
+  buffer.writeraw(c)
+
+proc clearText*(buffer: Buffer) =
+  buffer.fmttext.setLen(0)
+  buffer.rawtext.setLen(0)
+
+proc clearNodes*(buffer: Buffer) =
+  buffer.nodes.setLen(0)
+  buffer.links.setLen(0)
+  buffer.clickables.setLen(0)
+  buffer.elements.setLen(0)
+  buffer.idelements.clear()
+
+proc clearBuffer*(buffer: Buffer) =
+  buffer.clearText()
+  buffer.clearNodes()
+  buffer.cursorx = 0
+  buffer.cursory = 0
+  buffer.fromx = 0
+  buffer.fromy = 0
+  buffer.hovertext = ""
+  buffer.selectedlink = nil
+
+proc scrollTo*(buffer: Buffer, y: int): bool =
+  if y == buffer.fromy:
+    return false
+  buffer.fromy = min(max(buffer.lastLine() - buffer.height + 1, 0), y)
+  buffer.cursory = min(max(buffer.fromy, buffer.cursory), buffer.fromy + buffer.height)
+  return true
+
+proc cursorTo*(buffer: Buffer, x: int, y: int): bool =
+  result = false
+  buffer.cursory = min(max(y, 0), buffer.lastLine())
+  if buffer.fromy > buffer.cursory:
+    buffer.fromy = max(buffer.cursory, 0)
+    result = true
+  elif buffer.fromy + buffer.height - 1 <= buffer.cursory:
+    buffer.fromy = max(buffer.cursory - buffer.height + 2, 0)
+    result = true
+  buffer.cursorx = min(max(x, 0), buffer.currentLineLength())
+  #buffer.fromX = min(max(buffer.currentLineLength() - buffer.width + 1, 0), 0) #TODO
+
+proc cursorDown*(buffer: Buffer): bool =
+  if buffer.cursory < buffer.lastLine():
+    inc buffer.cursory
+    if buffer.cursorx >= buffer.currentLineLength():
+      buffer.cursorx = max(buffer.currentLineLength() - 1, 0)
+    elif buffer.xend > 0:
+      buffer.cursorx = min(buffer.currentLineLength() - 1, buffer.xend)
+    if buffer.cursory >= buffer.lastVisibleLine() and buffer.lastVisibleLine() != buffer.lastLine():
+      inc buffer.fromy
+      return true
+  return false
+
+proc cursorUp*(buffer: Buffer): bool =
+  if buffer.cursory > 0:
+    dec buffer.cursory
+    if buffer.cursorx > buffer.currentLineLength():
+      if buffer.cursorx == 0:
+        buffer.xend = buffer.cursorx
+      buffer.cursorx = max(buffer.currentLineLength() - 1, 0)
+    elif buffer.xend > 0:
+      buffer.cursorx = min(buffer.currentLineLength() - 1, buffer.xend)
+    if buffer.cursory < buffer.fromy:
+      dec buffer.fromy
+      return true
+  return false
+
+proc cursorRight*(buffer: Buffer): bool =
+  if buffer.cursorx < buffer.currentLineLength() - 1:
+    inc buffer.cursorx
+    buffer.xend = 0
+  else:
+    buffer.xend = buffer.cursorx
+  return false
+
+proc cursorLeft*(buffer: Buffer): bool =
+  if buffer.cursorx > 0:
+    dec buffer.cursorx
+  buffer.xend = 0
+  return false
+
+proc cursorLineBegin*(buffer: Buffer) =
+  buffer.cursorx = 0
+  buffer.xend = 0
+
+proc cursorLineEnd*(buffer: Buffer) =
+  buffer.cursorx = buffer.currentLineLength() - 1
+  buffer.xend = buffer.cursorx
+
+iterator revnodes*(buffer: Buffer): Node {.inline.} =
+  var i = buffer.nodes.len - 1
+  while i >= 0:
+    yield buffer.nodes[i]
+    dec i
+
+proc cursorNextWord*(buffer: Buffer): bool =
+  let llen = buffer.currentLineLength() - 1
+  var r: Rune
+  var x = buffer.cursorx
+  var y = buffer.cursory
+  if llen >= 0:
+    fastRuneAt(buffer.rawtext[y], x, r, false)
+
+    while r != Rune(' '):
+      if x >= llen:
+        break
+      inc x
+      fastRuneAt(buffer.rawtext[y], x, r, false)
+
+    while r == Rune(' '):
+      if x >= llen:
+        break
+      inc x
+      fastRuneAt(buffer.rawtext[y], x, r, false)
+
+  if x >= llen:
+    if y < buffer.lastLine():
+      inc y
+      x = 0
+  return buffer.cursorTo(x, y)
+
+proc cursorPrevWord*(buffer: Buffer): bool =
+  var r: Rune
+  var x = buffer.cursorx
+  var y = buffer.cursory
+  if buffer.currentLineLength() > 0:
+    fastRuneAt(buffer.rawtext[y], x, r, false)
+
+    while r != Rune(' '):
+      if x == 0:
+        break
+      dec x
+      fastRuneAt(buffer.rawtext[y], x, r, false)
+
+    while r == Rune(' '):
+      if x == 0:
+        break
+      dec x
+      fastRuneAt(buffer.rawtext[y], x, r, false)
+
+  if x == 0:
+    if y > 0:
+      dec y
+      x = buffer.rawtext[y].runeLen() - 1
+  return buffer.cursorTo(x, y)
+
+iterator revclickables*(buffer: Buffer): Node {.inline.} =
+  var i = buffer.clickables.len - 1
+  while i >= 0:
+    yield buffer.clickables[i]
+    dec i
+
+proc cursorNextLink*(buffer: Buffer): bool =
+  for node in buffer.clickables:
+    if node.y > buffer.cursory or (node.y == buffer.cursorY and node.x > buffer.cursorx):
+      result = buffer.cursorTo(node.x, node.y)
+      if buffer.cursorx < buffer.currentLineLength():
+        var r: Rune
+        fastRuneAt(buffer.rawtext[buffer.cursory], buffer.cursorx, r, false)
+        if r == Rune(' '):
+          return result or buffer.cursorNextWord()
+      return result
+  return false
+
+proc cursorPrevLink*(buffer: Buffer): bool =
+  for node in buffer.revclickables:
+    if node.y < buffer.cursorY or (node.y == buffer.cursorY and node.x < buffer.cursorx):
+      return buffer.cursorTo(node.x, node.y)
+  return false
+
+proc cursorFirstLine*(buffer: Buffer): bool =
+  if buffer.fromy > 0:
+    buffer.fromy = 0
+    result = true
+  else:
+    result = false
+
+  buffer.cursorY = 0
+  buffer.cursorLineBegin()
+
+proc cursorLastLine*(buffer: Buffer): bool =
+  if buffer.fromy < buffer.lastLine() - buffer.height:
+    buffer.fromy = buffer.lastLine() - (buffer.height - 2)
+    result = true
+  else:
+    result = false
+  buffer.cursory = buffer.lastLine()
+  buffer.cursorLineBegin()
+
+proc cursorTop*(buffer: Buffer): bool =
+  buffer.cursorY = buffer.fromy
+  return false
+
+proc cursorMiddle*(buffer: Buffer): bool =
+  buffer.cursorY = min(buffer.fromy + (buffer.height - 2) div 2, buffer.lastLine())
+  return false
+
+proc cursorBottom*(buffer: Buffer): bool =
+  buffer.cursorY = min(buffer.fromy + buffer.height - 2, buffer.lastLine())
+  return false
+
+proc centerLine*(buffer: Buffer): bool =
+  let ny = max(min(buffer.cursory - buffer.height div 2, buffer.lastLine() - buffer.height + 2), 0)
+  if ny != buffer.fromy:
+    buffer.fromy = ny
+    return true
+  return false
+
+proc halfPageUp*(buffer: Buffer): bool =
+  buffer.cursory = max(buffer.cursorY - buffer.height div 2 + 1, 0)
+  let nfy = max(0, buffer.fromy - buffer.height div 2 + 1)
+  if nfy != buffer.fromy:
+    buffer.fromy = nfy
+    return true
+  return false
+
+proc halfPageDown*(buffer: Buffer): bool =
+  buffer.cursory = min(buffer.cursorY + buffer.height div 2 - 1, buffer.lastLine())
+  let nfy = min(max(buffer.lastLine() - buffer.height + 2, 0), buffer.fromy + buffer.height div 2 - 1)
+  if nfy != buffer.fromy:
+    buffer.fromy = nfy
+    return true
+  return false
+
+proc pageUp*(buffer: Buffer): bool =
+  buffer.cursorY = max(buffer.cursorY - buffer.height + 1, 1)
+  buffer.fromy = max(0, buffer.fromy - buffer.height)
+  return true
+
+proc pageDown*(buffer: Buffer): bool =
+  buffer.cursorY = min(buffer.cursorY + buffer.height div 2 - 1, buffer.lastLine())
+  buffer.fromy = min(max(buffer.lastLine() - buffer.height + 1, 0), buffer.fromy + buffer.height div 2)
+  return true
+
+proc scrollDown*(buffer: Buffer): bool =
+  if buffer.fromy + buffer.height - 1 <= buffer.lastLine():
+    inc buffer.fromy
+    if buffer.fromy >= buffer.cursory:
+      discard buffer.cursorDown()
+    return true
+  discard buffer.cursorDown()
+  return false
+
+proc scrollUp*(buffer: Buffer): bool =
+  if buffer.fromy > 0:
+    dec buffer.fromy
+    if buffer.fromy + buffer.height - 1 <= buffer.cursorY:
+      discard buffer.cursorUp()
+    return true
+  discard buffer.cursorUp()
+  return false
+
+proc checkLinkSelection*(buffer: Buffer): bool =
+  if buffer.selectedlink != nil:
+    if buffer.cursorOnNode(buffer.selectedlink):
+      return false
+    else:
+      let anchor = buffer.selectedlink.ancestor(TAG_A)
+      anchor.style.selected = false
+      buffer.selectedlink.fmttext = buffer.selectedlink.getFmtText()
+      buffer.selectedlink = nil
+      buffer.hovertext = ""
+      var stack: seq[Node]
+      stack.add(anchor)
+      while stack.len > 0:
+        let elem = stack.pop()
+        elem.fmttext = elem.getFmtText()
+        for child in elem.childNodes:
+          stack.add(child)
+  for node in buffer.links:
+    if buffer.cursorOnNode(node):
+      buffer.selectedlink = node
+      let anchor = node.ancestor(TAG_A)
+      assert(anchor != nil)
+      anchor.style.selected = true
+      buffer.hovertext = HtmlAnchorElement(anchor).href
+      var stack: seq[Node]
+      stack.add(anchor)
+      while stack.len > 0:
+        let elem = stack.pop()
+        elem.fmttext = elem.getFmtText()
+        for child in elem.childNodes:
+          stack.add(child)
+      return true
+  return false
+
+proc gotoAnchor*(buffer: Buffer): bool =
+  if buffer.document.location.anchor != "":
+    let node =  buffer.getElementById(buffer.document.location.anchor)
+    if node != nil:
+      return buffer.scrollTo(max(node.y - buffer.height div 2, 0))
+  return false
+
+proc setLocation*(buffer: Buffer, uri: Uri) =
+  buffer.document.location = uri
+
+proc gotoLocation*(buffer: Buffer, uri: Uri) =
+  buffer.document.location = buffer.document.location.combine(uri)
+
+proc refreshTermAttrs*(buffer: Buffer): bool =
+  let newAttrs = getTermAttributes()
+  if newAttrs != buffer.attrs:
+    buffer.attrs = newAttrs
+    buffer.width = newAttrs.termWidth
+    buffer.height = newAttrs.termHeight
+    return true
+  return false
diff --git a/src/config b/src/config
new file mode 100644
index 00000000..99397288
--- /dev/null
+++ b/src/config
@@ -0,0 +1,399 @@
+#normal mode keybindings
+nmap q QUIT
+nmap h CURSOR_LEFT
+nmap j CURSOR_DOWN
+nmap k CURSOR_UP
+nmap l CURSOR_RIGHT
+nmap \e[D CURSOR_LEFT
+nmap \e[B CURSOR_DOWN
+nmap \e[A CURSOR_UP
+nmap \e[C CURSOR_RIGHT
+nmap ^ CURSOR_LINEBEGIN
+nmap $ CURSOR_LINEEND
+nmap b CURSOR_PREV_WORD
+nmap w CURSOR_NEXT_WORD
+nmap [ CURSOR_PREV_LINK
+nmap ] CURSOR_NEXT_LINK
+nmap H CURSOR_TOP
+nmap M CURSOR_MIDDLE
+nmap L CURSOR_BOTTOM
+nmap C-d HALF_PAGE_DOWN
+nmap C-u HALF_PAGE_UP
+nmap C-f PAGE_DOWN
+nmap C-b PAGE_UP
+nmap \e[6~ PAGE_DOWN
+nmap \e[5~ PAGE_UP
+nmap C-e SCROLL_DOWN
+nmap C-y SCROLL_UP
+nmap J SCROLL_DOWN
+nmap K SCROLL_UP
+nmap C-m CLICK
+nmap C-j CLICK
+nmap C-l CHANGE_LOCATION
+nmap U RELOAD
+nmap r RESHAPE
+nmap R REDRAW
+nmap gg CURSOR_FIRST_LINE
+nmap G CURSOR_LAST_LINE
+nmap \e[H CURSOR_FIRST_LINE
+nmap \e[F CURSOR_LAST_LINE
+nmap z CENTER_LINE
+nmap C-g LINE_INFO
+
+#line editing keybindings
+lemap C-m LINED_SUBMIT
+lemap C-j LINED_SUBMIT
+lemap C-h LINED_BACKSPACE
+lemap C-? LINED_BACKSPACE
+lemap C-d LINED_DELETE
+lemap C-c LINED_CANCEL
+lemap \eb LINED_PREV_WORD
+lemap \ef LINED_NEXT_WORD
+lemap C-b LINED_BACK
+lemap C-f LINED_FORWARD
+lemap C-u LINED_CLEAR
+lemap C-k LINED_KILL
+lemap C-w LINED_KILL_WORD
+lemap C-v LINED_ESC
+lemap C-g LINED_COMPOSE_TOGGLE
+
+#compose keybindings
+
+#european
+comp a: ä
+comp o: ö
+comp u: ü
+comp u" ű
+comp o" ő
+
+comp a' á
+comp e' é
+comp i' í
+comp o' ó
+comp u' ú
+
+comp sS ß
+comp n~ ñ
+
+#hiragana
+comp a あ
+comp i い
+comp u う
+comp e え
+comp o お
+
+comp la ぁ
+comp li ぃ
+comp lu ぅ
+comp le ぇ
+comp lo ぉ
+
+comp ka か
+comp ki き
+comp ku く
+comp ke け
+comp ko こ
+
+comp kka っか
+comp kki っき
+comp kku っく
+comp kke っけ
+comp kko っこ
+
+comp sa さ
+comp shi し
+comp su す
+comp se せ
+comp so そ
+
+comp ssa っさ
+comp sshi っし
+comp ssu っす
+comp sse っせ
+comp sso っそ
+
+comp ta た
+comp chi ち
+comp tsu つ
+comp te て
+comp to と
+
+comp tta った
+comp tchi っち
+comp ttsu っつ
+comp tte って
+comp tto っと
+
+comp na な
+comp ni に
+comp nu ぬ
+comp ne ね
+comp no の
+
+comp ha は
+comp hi ひ
+comp fu ふ
+comp he へ
+comp ho ほ
+
+comp ma ま
+comp mi み
+comp mu む
+comp me め
+comp mo も
+
+comp ya や
+comp yu ゆ
+comp yo よ
+
+comp ra ら
+comp ri り
+comp ru る
+comp re れ
+comp ro ろ
+
+comp wa わ
+comp wi ゐ
+comp we ゑ
+comp wo を
+
+comp ga が
+comp gi ぎ
+comp gu ぐ
+comp ge げ
+comp go ご
+
+comp za ざ
+comp ji じ
+comp zu ず
+comp ze ぜ
+comp zo ぞ
+
+comp da だ
+comp dji ぢ
+comp dzu づ
+comp de で
+comp do ど
+
+comp ba ば
+comp bi び
+comp bu ぶ
+comp be べ
+comp bo ぼ
+
+comp pa ぱ
+comp pi ぴ
+comp pu ぷ
+comp pe ぺ
+comp po ぽ
+
+comp n ん
+
+comp kya きゃ
+comp kyu きゅ
+comp kyo きょ
+
+comp sha しゃ
+comp shu しゅ
+comp sho しょ
+
+comp cha ちゃ
+comp chu ちゅ
+comp cho ちょ
+
+comp nya にゃ
+comp nyu にゅ
+comp nyo にょ
+
+comp hya ひゃ
+comp hyu ひゅ
+comp hyo ひょ
+
+comp mya みゃ
+comp myu みゅ
+comp myo みょ
+
+comp rya りゃ
+comp ryu りゅ
+comp ryo りょ
+
+comp gya ぎゃ
+comp gyu ぎゅ
+comp gyo ぎょ
+
+comp ja じゃ
+comp ju じゅ
+comp jo じょ
+
+comp bya びゃ
+comp byu びゅ
+comp byo びょ
+
+comp pya ぴゃ
+comp pyu ぴゅ
+comp pyo ぴょ
+
+comp kwa くゎ
+comp gwa ぐゎ
+
+#katakana
+comp A ア
+comp I イ
+comp U ウ
+comp E エ
+comp O オ
+
+comp LA ォ
+comp LI ィ
+comp LU ゥ
+comp LE ェ
+comp LO ォ
+
+comp KA か
+comp KI き
+comp KU く
+comp KE け
+comp KO こ
+
+comp KKA ッカ
+comp KKI ッキ
+comp KKU ック
+comp KKE ッケ
+comp KKO ッコ
+
+comp SA サ
+comp SHI シ
+comp SU ス
+comp SE セ
+comp SO ソ
+
+comp SSA ッサ
+comp SSHI ッシ
+comp SSU ッス
+comp SSE ッセ
+comp SSO ッソ
+
+comp TA タ
+comp CHI チ
+comp TSU ツ
+comp TE テ
+comp TO ト
+
+comp TTA ッタ
+comp TCHI ッチ
+comp TTSU ッツ
+comp TTE ッテ
+comp TTO ッと
+
+comp NA ナ
+comp NI ニ
+comp NU ヌ
+comp NE ネ
+comp NO ノ
+
+comp HA ハ
+comp HI ヒ
+comp FU フ
+comp HE ヘ
+comp HO ホ
+
+comp MA マ
+comp MI ミ
+comp MU ム
+comp ME メ
+comp MO モ
+
+comp YA ヤ
+comp YU ユ
+comp YO ヨ
+
+comp RA ラ
+comp RI リ
+comp RU ル
+comp RE レ
+comp RO ロ
+
+comp WA ワ
+comp WI ヰ
+comp WE ヱ
+comp WO ヲ
+
+comp GA ガ
+comp GI ギ
+comp GU グ
+comp GE ゲ
+comp GO ゴ
+
+comp ZA ザ
+comp JI ジ
+comp ZU ズ
+comp ZE ゼ
+comp ZO ゾ
+
+comp DA ダ
+comp DJI ヂ
+comp DZU ヅ
+comp DE デ
+comp DO ド
+
+comp BA バ
+comp BI ビ
+comp BU ブ
+comp BE ベ
+comp BO ボ
+
+comp PA パ
+comp PI ピ
+comp PU プ
+comp PE ペ
+comp PO ポ
+
+comp N ン
+comp xc ー
+
+comp KYA キャ
+comp KYU キュ
+comp KYO キョ
+
+comp SHA シャ
+comp SHU シュ
+comp SHO ショ
+
+comp CHA チャ
+comp CHU チュ
+comp CHO チョ
+
+comp NYA ニャ
+comp NYU ニュ
+comp NYO ニョ
+
+comp HYA ヒャ
+comp HYU ヒュ
+comp HYO ヒョ
+
+comp MYA ミャ
+comp MYU ミュ
+comp MYO ミョ
+
+comp RYA リャ
+comp RYU リュ
+comp RYO リョ
+
+comp GYA ギャ
+comp GYU ギュ
+comp GYO ギョ
+
+comp JA ジャ
+comp JU ジュ
+comp JO ジョ
+
+comp BYA ビャ
+comp BYU ビュ
+comp BYO ビョ
+
+comp PYA ピャ
+comp PYU ピュ
+comp PYO ピョ
+
+comp KWA クヮ
+comp GWA グヮ
diff --git a/src/config.nim b/src/config.nim
new file mode 100644
index 00000000..6d185a1d
--- /dev/null
+++ b/src/config.nim
@@ -0,0 +1,139 @@
+import tables
+import strutils
+
+import twtstr
+import radixtree
+
+type
+  TwtAction* =
+    enum
+    NO_ACTION,
+    ACTION_FEED_NEXT,
+    ACTION_QUIT,
+    ACTION_CURSOR_UP, ACTION_CURSOR_DOWN, ACTION_CURSOR_LEFT, ACTION_CURSOR_RIGHT,
+    ACTION_CURSOR_LINEEND, ACTION_CURSOR_LINEBEGIN,
+    ACTION_CURSOR_NEXT_WORD, ACTION_CURSOR_PREV_WORD,
+    ACTION_CURSOR_NEXT_NODE, ACTION_CURSOR_PREV_NODE,
+    ACTION_CURSOR_NEXT_LINK, ACTION_CURSOR_PREV_LINK,
+    ACTION_PAGE_DOWN, ACTION_PAGE_UP,
+    ACTION_HALF_PAGE_DOWN, ACTION_HALF_PAGE_UP,
+    ACTION_SCROLL_DOWN, ACTION_SCROLL_UP,
+    ACTION_CLICK,
+    ACTION_CHANGE_LOCATION,
+    ACTION_RELOAD, ACTION_RESHAPE, ACTION_REDRAW,
+    ACTION_CURSOR_FIRST_LINE, ACTION_CURSOR_LAST_LINE,
+    ACTION_CURSOR_TOP, ACTION_CURSOR_MIDDLE, ACTION_CURSOR_BOTTOM,
+    ACTION_CENTER_LINE, ACTION_LINE_INFO,
+    ACTION_LINED_SUBMIT, ACTION_LINED_CANCEL,
+    ACTION_LINED_BACKSPACE, ACTION_LINED_DELETE,
+    ACTION_LINED_CLEAR, ACTION_LINED_KILL, ACTION_LINED_KILL_WORD,
+    ACTION_LINED_BACK, ACTION_LINED_FORWARD,
+    ACTION_LINED_PREV_WORD, ACTION_LINED_NEXT_WORD,
+    ACTION_LINED_COMPOSE_TOGGLE, ACTION_LINED_COMPOSE_ON, ACTION_LINED_COMPOSE_OFF
+    ACTION_LINED_ESC
+
+  ActionMap = Table[string, TwtAction]
+  ComposeMap = RadixTree[string]
+
+var normalActionRemap*: ActionMap
+var linedActionRemap*: ActionMap
+var composeRemap*: ComposeMap
+
+func getRealKey(key: string): string =
+  var realk: string
+  var currchar: char
+  var control = 0
+  var skip = false
+  for c in key:
+    if c == '\\':
+      skip = true
+    elif skip:
+      if c == 'e':
+        realk &= '\e'
+      else:
+        realk &= c
+      skip = false
+    elif c == 'C':
+      inc control
+      currchar = c
+    elif c == '-' and control == 1:
+      inc control
+    elif control == 1:
+      realk &= 'C' & c
+      control = 0
+    elif control == 2:
+      realk &= getControlChar(c)
+      control = 0
+    else:
+      realk &= c
+  if control == 1:
+    realk &= 'C'
+  return realk
+
+func constructActionTable*(origTable: ActionMap): ActionMap =
+  var newTable: ActionMap
+  var strs: seq[string]
+  for k in origTable.keys:
+    let realk = getRealKey(k)
+    var teststr = ""
+    for c in realk:
+      teststr &= c
+      strs.add(teststr)
+
+  for k, v in origTable:
+    let realk = getRealKey(k)
+    var teststr = ""
+    for c in realk:
+      teststr &= c
+      if strs.contains(teststr):
+        newTable[teststr] = ACTION_FEED_NEXT
+    newTable[realk] = v
+  return newTable
+
+proc parseConfigLine(line: string, nmap: var ActionMap, lemap: var ActionMap,
+                     compose: var ComposeMap) =
+  if line.len == 0 or line[0] == '#':
+    return
+  let cmd = line.split(' ')
+  if cmd.len == 3:
+    if cmd[0] == "nmap":
+      nmap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2])
+    elif cmd[0] == "lemap":
+      lemap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2])
+    elif cmd[0] == "comp":
+      compose[getRealKey(cmd[1])] = cmd[2]
+
+proc staticReadKeymap(): (ActionMap, ActionMap, ComposeMap) =
+  let config = staticRead"config"
+  var nmap: ActionMap
+  var lemap: ActionMap
+  var compose = newRadixTree[string]()
+  for line in config.split('\n'):
+    parseConfigLine(line, nmap, lemap, compose)
+
+  nmap = constructActionTable(nmap)
+  lemap = constructActionTable(lemap)
+  return (nmap, lemap, compose)
+
+const (normalActionMap, linedActionMap, composeMap) = staticReadKeymap()
+
+normalActionRemap = normalActionMap
+linedActionRemap = linedActionMap
+composeRemap = composeMap
+
+proc readConfig*(filename: string): bool =
+  var f: File
+  let status = f.open(filename, fmRead)
+  var nmap: ActionMap
+  var lemap: ActionMap
+  var compose = newRadixTree[string]()
+  if status:
+    var line: TaintedString
+    while f.readLine(line):
+      parseConfigLine(line, nmap, lemap, compose)
+
+    normalActionRemap = constructActionTable(normalActionMap)
+    linedActionRemap = constructActionTable(linedActionMap)
+    return true
+  else:
+    return false
diff --git a/src/default.css b/src/default.css
new file mode 100644
index 00000000..db11f036
--- /dev/null
+++ b/src/default.css
@@ -0,0 +1,20 @@
+head {
+	display: none
+}
+
+a, abbr, b, bdo, br, button, cite, code,
+del, dfn, em, font, i, img, ins, input,
+iframe, kbd, label, map, object, q, samp,
+script, select, small, span, strong, sub, sup,
+textarea, tt, var, font, iframe, u, s, strike,
+frame, img, input {
+	display: block
+}
+
+li {
+	display: list-item
+}
+
+br::before {
+	content: "\\"
+}
diff --git a/src/display.nim b/src/display.nim
new file mode 100644
index 00000000..d7d69c4a
--- /dev/null
+++ b/src/display.nim
@@ -0,0 +1,395 @@
+import terminal
+import options
+import uri
+import strutils
+import unicode
+
+import buffer
+import termattrs
+import dom
+import twtstr
+import twtio
+import config
+import enums
+
+proc clearStatusMsg*(at: int) =
+  setCursorPos(0, at)
+  eraseLine()
+
+proc statusMsg*(str: string, at: int) =
+  clearStatusMsg(at)
+  print(str.ansiStyle(styleReverse).ansiReset())
+
+type
+  RenderState = object
+    x: int
+    y: int
+    lastwidth: int
+    fmtline: string
+    rawline: string
+    centerqueue: seq[Node]
+    centerlen: int
+    blanklines: int
+    blankspaces: int
+    nextspaces: int
+    docenter: bool
+    indent: int
+    listval: int
+
+func newRenderState(): RenderState =
+  return RenderState(blanklines: 1)
+
+proc write(state: var RenderState, s: string) =
+  state.fmtline &= s
+  state.rawline &= s
+
+proc write(state: var RenderState, fs: string, rs: string) =
+  state.fmtline &= fs
+  state.rawline &= rs
+
+proc flushLine(buffer: Buffer, state: var RenderState) =
+  if state.rawline.len == 0:
+    inc state.blanklines
+  assert(state.rawline.runeLen() < buffer.width, "line too long:\n" & state.rawline)
+  buffer.writefmt(state.fmtline)
+  buffer.writeraw(state.rawline)
+  state.x = 0
+  inc state.y
+  state.nextspaces = 0
+  state.fmtline = ""
+  state.rawline = ""
+
+proc addSpaces(buffer: Buffer, state: var RenderState, n: int) =
+  if state.x + n > buffer.width:
+    buffer.flushLine(state)
+    return
+  state.blankspaces += n
+  state.write(' '.repeat(n))
+  state.x += n
+
+proc writeWrappedText(buffer: Buffer, state: var RenderState, node: Node) =
+  state.lastwidth = 0
+  var n = 0
+  var fmtword = ""
+  var rawword = ""
+  var prevl = false
+  let fmttext = node.getFmtText()
+  for w in fmttext:
+    if w.len > 0 and w[0] == '\e':
+      fmtword &= w
+      continue
+
+    for r in w.runes:
+      if r == Rune(' '):
+        if rawword.len > 0 and rawword[0] == ' ' and prevl: #first byte can't fool comparison to ascii
+          fmtword = fmtword.substr(1)
+          rawword = rawword.substr(1)
+          state.x -= 1
+          prevl = false
+        state.write(fmtword, rawword)
+        fmtword = ""
+        rawword = ""
+
+      if r == Rune('\n'):
+        state.write(fmtword, rawword)
+        buffer.flushLine(state)
+        rawword = ""
+        fmtword = ""
+      else:
+        fmtword &= r
+        rawword &= r
+
+      state.x += r.width()
+
+      if state.x >= buffer.width:
+        state.lastwidth = max(state.lastwidth, state.x)
+        buffer.flushLine(state)
+        state.x = rawword.width()
+        prevl = true
+      else:
+        state.lastwidth = max(state.lastwidth, state.x)
+
+      inc n
+
+  state.write(fmtword, rawword)
+  if prevl:
+    state.x += rawword.width()
+    prevl = false
+
+  state.lastwidth = max(state.lastwidth, state.x)
+
+proc preAlignNode(buffer: Buffer, node: Node, state: var RenderState) =
+  let style = node.getStyle()
+  if state.rawline.len > 0 and node.firstNode() and state.blanklines == 0:
+    buffer.flushLine(state)
+
+  if node.firstNode():
+    while state.blanklines < max(style.margin, style.margintop):
+      buffer.flushLine(state)
+    state.indent += style.indent
+
+  if state.rawline.len > 0 and state.blanklines == 0 and node.displayed():
+    buffer.addSpaces(state, state.nextspaces)
+    state.nextspaces = 0
+    if state.blankspaces < max(style.margin, style.marginleft):
+      buffer.addSpaces(state, max(style.margin, style.marginleft) - state.blankspaces)
+
+  if style.centered and state.rawline.len == 0 and node.displayed():
+    buffer.addSpaces(state, max(buffer.width div 2 - state.centerlen div 2, 0))
+    state.centerlen = 0
+  
+  if node.isElemNode() and style.display == DISPLAY_LIST_ITEM and state.indent > 0:
+    if state.blanklines == 0:
+      buffer.flushLine(state)
+    var listchar = "•"
+    #case elem.parentElement.tagType
+    #of TAG_UL:
+    #  listchar = "•"
+    #of TAG_OL:
+    #  inc state.listval
+    #  listchar = $state.listval & ")"
+    #else:
+    #  return
+    buffer.addSpaces(state, state.indent)
+    state.write(listchar)
+    state.x += listchar.runeLen()
+    buffer.addSpaces(state, 1)
+
+proc postAlignNode(buffer: Buffer, node: Node, state: var RenderState) =
+  let style = node.getStyle()
+
+  if node.getRawLen() > 0:
+    state.blanklines = 0
+    state.blankspaces = 0
+
+  if state.rawline.len > 0 and state.blanklines == 0:
+    state.nextspaces += max(style.margin, style.marginright)
+    #if node.lastNode() and (node.isTextNode() or elem.childNodes.len == 0):
+    #  buffer.flushLine(state)
+
+  if node.lastNode():
+    while state.blanklines < max(style.margin, style.marginbottom):
+      buffer.flushLine(state)
+    state.indent -= style.indent
+
+  if style.display == DISPLAY_LIST_ITEM and node.lastNode():
+    buffer.flushLine(state)
+
+proc renderNode(buffer: Buffer, node: Node, state: var RenderState) =
+  if not (node.nodeType in {ELEMENT_NODE, TEXT_NODE}):
+    return
+  let style = node.getStyle()
+  if node.nodeType == ELEMENT_NODE:
+    if Element(node).tagType in {TAG_SCRIPT, TAG_STYLE, TAG_NOSCRIPT, TAG_TITLE}:
+      return
+  if style.hidden: return
+
+  if not state.docenter:
+    if style.centered:
+      state.centerqueue.add(node)
+      if node.lastNode():
+        state.docenter = true
+        state.centerlen = 0
+        for node in state.centerqueue:
+          state.centerlen += node.getRawLen()
+        for node in state.centerqueue:
+          buffer.renderNode(node, state)
+        state.centerqueue.setLen(0)
+        state.docenter = false
+        return
+      else:
+        return
+    if state.centerqueue.len > 0:
+      state.docenter = true
+      state.centerlen = 0
+      for node in state.centerqueue:
+        state.centerlen += node.getRawLen()
+      for node in state.centerqueue:
+        buffer.renderNode(node, state)
+      state.centerqueue.setLen(0)
+      state.docenter = false
+
+  buffer.preAlignNode(node, state)
+
+  node.x = state.x
+  node.y = state.y
+  buffer.writeWrappedText(state, node)
+  node.ex = state.x
+  node.ey = state.y
+  node.width = state.lastwidth - node.x - 1
+  node.height = state.y - node.y + 1
+
+  buffer.postAlignNode(node, state)
+
+proc setLastHtmlLine(buffer: Buffer, state: var RenderState) =
+  if state.rawline.len != 0:
+    buffer.flushLine(state)
+
+proc renderHtml*(buffer: Buffer) =
+  var stack: seq[Node]
+  let first = buffer.document
+  stack.add(first)
+
+  var state = newRenderState()
+  while stack.len > 0:
+    let currElem = stack.pop()
+    buffer.addNode(currElem)
+    buffer.renderNode(currElem, state)
+    var i = currElem.childNodes.len - 1
+    while i >= 0:
+      stack.add(currElem.childNodes[i])
+      i -= 1
+
+  buffer.setLastHtmlLine(state)
+
+proc drawHtml(buffer: Buffer) =
+  var state = newRenderState()
+  for node in buffer.nodes:
+    buffer.renderNode(node, state)
+  buffer.setLastHtmlLine(state)
+
+proc statusMsgForBuffer(buffer: Buffer) =
+  var msg = $(buffer.cursory + 1) & "/" & $(buffer.lastLine() + 1) & " (" &
+            $buffer.atPercentOf() & "%) " &
+            "<" & buffer.title & ">"
+  if buffer.hovertext.len > 0:
+    msg &= " " & buffer.hovertext
+  statusMsg(msg.maxString(buffer.width), buffer.height)
+
+proc cursorBufferPos(buffer: Buffer) =
+  var x = buffer.cursorx
+  var y = buffer.cursory - 1 - buffer.fromY
+  termGoto(x, y + 1)
+
+proc displayBuffer(buffer: Buffer) =
+  eraseScreen()
+  termGoto(0, 0)
+
+  print(buffer.visibleText().ansiReset())
+
+proc inputLoop(attrs: TermAttributes, buffer: Buffer): bool =
+  var s = ""
+  var feedNext = false
+  while true:
+    stdout.showCursor()
+    buffer.cursorBufferPos()
+    if not feedNext:
+      s = ""
+    else:
+      feedNext = false
+    let c = getch()
+    s &= c
+    let action = getNormalAction(s)
+    var redraw = false
+    var reshape = false
+    var nostatus = false
+    case action
+    of ACTION_QUIT:
+      eraseScreen()
+      return false
+    of ACTION_CURSOR_LEFT: redraw = buffer.cursorLeft()
+    of ACTION_CURSOR_DOWN: redraw = buffer.cursorDown()
+    of ACTION_CURSOR_UP: redraw = buffer.cursorUp()
+    of ACTION_CURSOR_RIGHT: redraw = buffer.cursorRight()
+    of ACTION_CURSOR_LINEBEGIN: buffer.cursorLineBegin()
+    of ACTION_CURSOR_LINEEND: buffer.cursorLineEnd()
+    of ACTION_CURSOR_NEXT_WORD: redraw = buffer.cursorNextWord()
+    of ACTION_CURSOR_PREV_WORD: redraw = buffer.cursorPrevWord()
+    of ACTION_CURSOR_NEXT_LINK: redraw = buffer.cursorNextLink()
+    of ACTION_CURSOR_PREV_LINK: redraw = buffer.cursorPrevLink()
+    of ACTION_PAGE_DOWN: redraw = buffer.pageDown()
+    of ACTION_PAGE_UP: redraw = buffer.pageUp()
+    of ACTION_HALF_PAGE_DOWN: redraw = buffer.halfPageDown()
+    of ACTION_HALF_PAGE_UP: redraw = buffer.halfPageUp()
+    of ACTION_CURSOR_FIRST_LINE: redraw = buffer.cursorFirstLine()
+    of ACTION_CURSOR_LAST_LINE: redraw = buffer.cursorLastLine()
+    of ACTION_CURSOR_TOP: redraw = buffer.cursorTop()
+    of ACTION_CURSOR_MIDDLE: redraw = buffer.cursorMiddle()
+    of ACTION_CURSOR_BOTTOM: redraw = buffer.cursorBottom()
+    of ACTION_CENTER_LINE: redraw = buffer.centerLine()
+    of ACTION_SCROLL_DOWN: redraw = buffer.scrollDown()
+    of ACTION_SCROLL_UP: redraw = buffer.scrollUp()
+    of ACTION_CLICK:
+      let selectedElem = buffer.findSelectedElement()
+      if selectedElem.isSome:
+        case selectedElem.get().tagType
+        of TAG_INPUT:
+          clearStatusMsg(buffer.height)
+          let status = readLine("TEXT: ", HtmlInputElement(selectedElem.get()).value, buffer.width)
+          if status:
+            reshape = true
+            redraw = true
+        else: discard
+        if selectedElem.get().getStyle().islink:
+          let anchor = HtmlAnchorElement(buffer.selectedlink.ancestor(TAG_A)).href
+          buffer.gotoLocation(parseUri(anchor))
+          return true
+    of ACTION_CHANGE_LOCATION:
+      var url = $buffer.document.location
+
+      clearStatusMsg(buffer.height)
+      let status = readLine("URL: ", url, buffer.width)
+      if status:
+        buffer.setLocation(parseUri(url))
+        return true
+    of ACTION_LINE_INFO:
+      statusMsg("line " & $buffer.cursory & "/" & $buffer.lastLine() & " col " & $(buffer.cursorx + 1) & "/" & $buffer.currentLineLength(), buffer.width)
+      nostatus = true
+    of ACTION_FEED_NEXT:
+      feedNext = true
+    of ACTION_RELOAD: return true
+    of ACTION_RESHAPE:
+      reshape = true
+      redraw = true
+    of ACTION_REDRAW: redraw = true
+    else: discard
+    stdout.hideCursor()
+
+    let prevlink = buffer.selectedlink
+    let sel = buffer.checkLinkSelection()
+    if sel:
+      buffer.clearText()
+      buffer.drawHtml()
+      termGoto(0, buffer.selectedlink.y - buffer.fromy)
+      stdout.eraseLine()
+      for i in buffer.selectedlink.y..buffer.selectedlink.ey:
+        if i < buffer.fromy + buffer.height - 1:
+          let line = buffer.fmttext[i]
+          print(line)
+          print('\n')
+      print("".ansiReset())
+    
+    if prevlink != nil:
+      buffer.clearText()
+      buffer.drawHtml()
+      termGoto(0, prevlink.y - buffer.fromy)
+      for i in prevlink.y..prevlink.ey:
+        if i < buffer.fromy + buffer.height - 1:
+          let line = buffer.fmttext[i]
+          stdout.eraseLine()
+          print(line)
+          print('\n')
+      print("".ansiReset())
+
+    if buffer.refreshTermAttrs():
+      redraw = true
+      reshape = true
+
+    if reshape:
+      buffer.clearText()
+      buffer.drawHtml()
+    if redraw:
+      buffer.displayBuffer()
+
+    if not nostatus:
+      buffer.statusMsgForBuffer()
+    else:
+      nostatus = false
+
+proc displayPage*(attrs: TermAttributes, buffer: Buffer): bool =
+  #buffer.printwrite = true
+  discard buffer.gotoAnchor()
+  buffer.displayBuffer()
+  buffer.statusMsgForBuffer()
+  return inputLoop(attrs, buffer)
+
diff --git a/src/dom.nim b/src/dom.nim
new file mode 100644
index 00000000..9585456a
--- /dev/null
+++ b/src/dom.nim
@@ -0,0 +1,331 @@
+import terminal
+import uri
+import unicode
+import strutils
+import tables
+
+import twtstr
+import twtio
+import enums
+import style
+
+type
+  EventTarget* = ref EventTargetObj
+  EventTargetObj = object of RootObj
+
+  Node* = ref NodeObj
+  NodeObj = object of EventTargetObj
+    nodeType*: NodeType
+    childNodes*: seq[Node]
+    firstChild*: Node
+    isConnected*: bool
+    lastChild*: Node
+    nextSibling*: Node
+    previousSibling*: Node
+    parentNode*: Node
+    parentElement*: Element
+    ownerDocument*: Document
+
+    rawtext*: string
+    fmttext*: seq[string]
+    x*: int
+    y*: int
+    ex*: int
+    ey*: int
+    width*: int
+    height*: int
+    hidden*: bool
+
+  Attr* = ref AttrObj
+  AttrObj = object of NodeObj
+    namespaceURI*: string
+    prefix*: string
+    localName*: string
+    name*: string
+    value*: string
+    ownerElement*: Element
+
+  Document* = ref DocumentObj
+  DocumentObj = object of NodeObj
+    location*: Uri
+
+  CharacterData* = ref CharacterDataObj
+  CharacterDataObj = object of NodeObj
+    data*: string
+    length*: int
+
+  Text* = ref TextObj
+  TextObj = object of CharacterDataObj
+    wholeText*: string
+
+  Comment* = ref CommentObj
+  CommentObj = object of CharacterDataObj
+
+  Element* = ref ElementObj
+  ElementObj = object of NodeObj
+    namespaceURI*: string
+    prefix*: string
+    localName*: string
+    tagName*: string
+    tagType*: TagType
+
+    id*: string
+    classList*: seq[string]
+    attributes*: Table[string, Attr]
+    style*: CSS2Properties
+
+  HTMLElement* = ref HTMLElementObj
+  HTMLElementObj = object of ElementObj
+
+  HTMLInputElement* = ref HTMLInputElementObj
+  HTMLInputElementObj = object of HTMLElementObj
+    itype*: InputType
+    autofocus*: bool
+    required*: bool
+    value*: string
+    size*: int
+
+  HTMLAnchorElement* = ref HTMLAnchorElementObj
+  HTMLAnchorElementObj = object of HTMLElementObj
+    href*: string
+
+  HTMLSelectElement* = ref HTMLSelectElementObj
+  HTMLSelectElementObj = object of HTMLElementObj
+    name*: string
+    value*: string
+    valueSet*: bool
+
+  HTMLOptionElement* = ref HTMLOptionElementObj
+  HTMLOptionElementObj = object of HTMLElementObj
+    value*: string
+  
+  HTMLHeadingElement* = ref HTMLHeadingElementObj
+  HTMLHeadingElementObj = object of HTMLElementObj
+    rank*: uint16
+
+  HTMLBRElement* = ref HTMLBRElementObj
+  HTMLBRElementObj = object of HTMLElementObj
+
+
+func getTagTypeMap(): Table[string, TagType] =
+  for i in low(TagType) .. high(TagType):
+    let enumname = $TagType(i)
+    let tagname = enumname.split('_')[1..^1].join("_").tolower()
+    result[tagname] = TagType(i)
+
+func getInputTypeMap(): Table[string, InputType] =
+  for i in low(InputType) .. high(InputType):
+    let enumname = $InputType(i)
+    let tagname = enumname.split('_')[1..^1].join("_").tolower()
+    result[tagname] = InputType(i)
+
+const tagTypeMap = getTagTypeMap()
+const inputTypeMap = getInputTypeMap()
+
+func tagType*(s: string): TagType =
+  if tagTypeMap.hasKey(s):
+    return tagTypeMap[s]
+  else:
+    return TAG_UNKNOWN
+
+func inputType*(s: string): InputType =
+  if inputTypeMap.hasKey(s):
+    return inputTypeMap[s]
+  else:
+    return INPUT_UNKNOWN
+
+#TODO
+func nodeAttr*(node: Node): HtmlElement =
+  case node.nodeType
+  of TEXT_NODE: return HtmlElement(node.parentElement)
+  of ELEMENT_NODE: return HtmlElement(node)
+  else: assert(false)
+
+func getStyle*(node: Node): CSS2Properties =
+  case node.nodeType
+  of TEXT_NODE: return node.parentElement.style
+  of ELEMENT_NODE: return Element(node).style
+  else: assert(false)
+
+func displayed*(node: Node): bool =
+  return node.rawtext.len > 0 and node.getStyle().display != DISPLAY_NONE
+
+func isTextNode*(node: Node): bool =
+  return node.nodeType == TEXT_NODE
+
+func isElemNode*(node: Node): bool =
+  return node.nodeType == ELEMENT_NODE
+
+func isComment*(node: Node): bool =
+  return node.nodeType == COMMENT_NODE
+
+func isCData*(node: Node): bool =
+  return node.nodeType == CDATA_SECTION_NODE
+
+func isDocument*(node: Node): bool =
+  return node.nodeType == DOCUMENT_NODE
+
+func getFmtLen*(htmlNode: Node): int =
+  return htmlNode.fmttext.join().runeLen()
+
+func getRawLen*(htmlNode: Node): int =
+  return htmlNode.rawtext.runeLen()
+
+func firstNode*(htmlNode: Node): bool =
+  return htmlNode.parentElement != nil and htmlNode.parentElement.childNodes[0] == htmlNode
+
+func lastNode*(htmlNode: Node): bool =
+  return htmlNode.parentElement != nil and htmlNode.parentElement.childNodes[^1] == htmlNode
+
+func toInputType*(str: string): InputType =
+  case str
+  of "button": INPUT_BUTTON
+  of "checkbox": INPUT_CHECKBOX
+  of "color": INPUT_COLOR
+  of "date": INPUT_DATE
+  of "datetime_local": INPUT_DATETIME_LOCAL
+  of "email": INPUT_EMAIL
+  of "file": INPUT_FILE
+  of "hidden": INPUT_HIDDEN
+  of "image": INPUT_IMAGE
+  of "month": INPUT_MONTH
+  of "number": INPUT_NUMBER
+  of "password": INPUT_PASSWORD
+  of "radio": INPUT_RADIO
+  of "range": INPUT_RANGE
+  of "reset": INPUT_RESET
+  of "search": INPUT_SEARCH
+  of "submit": INPUT_SUBMIT
+  of "tel": INPUT_TEL
+  of "text": INPUT_TEXT
+  of "time": INPUT_TIME
+  of "url": INPUT_URL
+  of "week": INPUT_WEEK
+  else: INPUT_UNKNOWN
+
+func toInputSize*(str: string): int =
+  if str.len == 0:
+    return 20
+  for c in str:
+    if not c.isDigit():
+      return 20
+  return str.parseInt()
+
+func getFmtInput(inputElement: HtmlInputElement): seq[string] =
+  case inputElement.itype
+  of INPUT_TEXT, INPUT_SEARCH:
+    let valueFit = fitValueToSize(inputElement.value, inputElement.size)
+    return valueFit.ansiStyle(styleUnderscore).ansiReset().buttonFmt()
+  of INPUT_SUBMIT:
+    return inputElement.value.buttonFmt()
+  else: discard
+
+func getRawInput(inputElement: HtmlInputElement): string =
+  case inputElement.itype
+  of INPUT_TEXT, INPUT_SEARCH:
+    return inputElement.value.fitValueToSize(inputElement.size).buttonRaw()
+  of INPUT_SUBMIT:
+    return inputElement.value.buttonRaw()
+  else: discard
+
+#TODO
+func ancestor*(htmlNode: Node, tagType: TagType): HtmlElement =
+  result = HtmlElement(htmlNode.parentElement)
+  while result != nil and result.tagType != tagType:
+    result = HtmlElement(result.parentElement)
+
+proc getRawText*(htmlNode: Node): string =
+  if htmlNode.isElemNode():
+    case HtmlElement(htmlNode).tagType
+    of TAG_INPUT: return HtmlInputElement(htmlNode).getRawInput()
+    else: return ""
+  elif htmlNode.isTextNode():
+    let chardata = CharacterData(htmlNode)
+    if htmlNode.parentElement != nil and htmlNode.parentElement.tagType != TAG_PRE:
+      result = chardata.data.remove("\n")
+      if unicode.strip(result).runeLen() > 0:
+        if htmlNode.getStyle().display != DISPLAY_INLINE:
+          result = unicode.strip(result)
+      else:
+        result = ""
+    else:
+      result = unicode.strip(chardata.data)
+    if htmlNode.parentElement != nil and htmlNode.parentElement.tagType == TAG_OPTION:
+      result = result.buttonRaw()
+  else:
+    assert(false)
+
+func getFmtText*(htmlNode: Node): seq[string] =
+  if htmlNode.isElemNode():
+    case HtmlElement(htmlNode).tagType
+    of TAG_INPUT: return HtmlInputElement(htmlNode).getFmtInput()
+    else: return @[]
+  elif htmlNode.isTextNode():
+    let chardata = CharacterData(htmlNode)
+    result &= chardata.data
+    if htmlNode.parentElement != nil:
+      if htmlNode.parentElement.style.islink:
+        result = result.ansiFgColor(fgBlue).ansiReset()
+        let anchor = htmlNode.ancestor(TAG_A)
+        if anchor != nil and anchor.style.selected:
+          result = result.ansiStyle(styleUnderscore).ansiReset()
+
+      if htmlNode.parentElement.tagType == TAG_OPTION:
+        result = result.ansiFgColor(fgRed).ansiReset()
+
+      if htmlNode.parentElement.style.bold:
+        result = result.ansiStyle(styleBright).ansiReset()
+      if htmlNode.parentElement.style.italic:
+        result = result.ansiStyle(styleItalic).ansiReset()
+      if htmlNode.parentElement.style.underscore:
+        result = result.ansiStyle(styleUnderscore).ansiReset()
+    else:
+      assert(false, "Uhhhh I'm pretty sure we should have parent elements for text nodes?" & htmlNode.rawtext)
+  else:
+    assert(false)
+
+func newDocument*(): Document =
+  new(result)
+  result.nodeType = DOCUMENT_NODE
+
+func newText*(): Text =
+  new(result)
+  result.nodeType = TEXT_NODE
+
+func newComment*(): Comment =
+  new(result)
+  result.nodeType = COMMENT_NODE
+
+func newHtmlElement*(tagType: TagType): HTMLElement =
+  case tagType
+  of TAG_INPUT:
+    result = new(HTMLInputElement)
+  of TAG_A:
+    result = new(HTMLAnchorElement)
+  of TAG_SELECT:
+    result = new(HTMLSelectElement)
+  of TAG_OPTION:
+    result = new(HTMLOptionElement)
+  of TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6:
+    result = new(HTMLHeadingElement)
+  of TAG_BR:
+    result = new(HTMLBRElement)
+  else:
+    new(result)
+
+  result.nodeType = ELEMENT_NODE
+  result.tagType = tagType
+  result.style = new(CSS2Properties)
+
+func newAttr*(parent: Element, key: string, value: string): Attr =
+  new(result)
+  result.nodeType = ATTRIBUTE_NODE
+  result.ownerElement = parent
+  result.name = key
+  result.value = value
+
+func getAttrValue*(element: Element, s: string): string =
+  let attr = element.attributes.getOrDefault(s, nil)
+  if attr != nil:
+    return attr.value
+  return ""
diff --git a/src/entity b/src/entity
new file mode 100755
index 00000000..05f6705f
--- /dev/null
+++ b/src/entity
Binary files differdiff --git a/src/entity.json b/src/entity.json
new file mode 100644
index 00000000..557170b4
--- /dev/null
+++ b/src/entity.json
@@ -0,0 +1,2233 @@
+{
+  "&AElig": { "codepoints": [198], "characters": "\u00C6" },
+  "&AElig;": { "codepoints": [198], "characters": "\u00C6" },
+  "&AMP": { "codepoints": [38], "characters": "\u0026" },
+  "&AMP;": { "codepoints": [38], "characters": "\u0026" },
+  "&Aacute": { "codepoints": [193], "characters": "\u00C1" },
+  "&Aacute;": { "codepoints": [193], "characters": "\u00C1" },
+  "&Abreve;": { "codepoints": [258], "characters": "\u0102" },
+  "&Acirc": { "codepoints": [194], "characters": "\u00C2" },
+  "&Acirc;": { "codepoints": [194], "characters": "\u00C2" },
+  "&Acy;": { "codepoints": [1040], "characters": "\u0410" },
+  "&Afr;": { "codepoints": [120068], "characters": "\uD835\uDD04" },
+  "&Agrave": { "codepoints": [192], "characters": "\u00C0" },
+  "&Agrave;": { "codepoints": [192], "characters": "\u00C0" },
+  "&Alpha;": { "codepoints": [913], "characters": "\u0391" },
+  "&Amacr;": { "codepoints": [256], "characters": "\u0100" },
+  "&And;": { "codepoints": [10835], "characters": "\u2A53" },
+  "&Aogon;": { "codepoints": [260], "characters": "\u0104" },
+  "&Aopf;": { "codepoints": [120120], "characters": "\uD835\uDD38" },
+  "&ApplyFunction;": { "codepoints": [8289], "characters": "\u2061" },
+  "&Aring": { "codepoints": [197], "characters": "\u00C5" },
+  "&Aring;": { "codepoints": [197], "characters": "\u00C5" },
+  "&Ascr;": { "codepoints": [119964], "characters": "\uD835\uDC9C" },
+  "&Assign;": { "codepoints": [8788], "characters": "\u2254" },
+  "&Atilde": { "codepoints": [195], "characters": "\u00C3" },
+  "&Atilde;": { "codepoints": [195], "characters": "\u00C3" },
+  "&Auml": { "codepoints": [196], "characters": "\u00C4" },
+  "&Auml;": { "codepoints": [196], "characters": "\u00C4" },
+  "&Backslash;": { "codepoints": [8726], "characters": "\u2216" },
+  "&Barv;": { "codepoints": [10983], "characters": "\u2AE7" },
+  "&Barwed;": { "codepoints": [8966], "characters": "\u2306" },
+  "&Bcy;": { "codepoints": [1041], "characters": "\u0411" },
+  "&Because;": { "codepoints": [8757], "characters": "\u2235" },
+  "&Bernoullis;": { "codepoints": [8492], "characters": "\u212C" },
+  "&Beta;": { "codepoints": [914], "characters": "\u0392" },
+  "&Bfr;": { "codepoints": [120069], "characters": "\uD835\uDD05" },
+  "&Bopf;": { "codepoints": [120121], "characters": "\uD835\uDD39" },
+  "&Breve;": { "codepoints": [728], "characters": "\u02D8" },
+  "&Bscr;": { "codepoints": [8492], "characters": "\u212C" },
+  "&Bumpeq;": { "codepoints": [8782], "characters": "\u224E" },
+  "&CHcy;": { "codepoints": [1063], "characters": "\u0427" },
+  "&COPY": { "codepoints": [169], "characters": "\u00A9" },
+  "&COPY;": { "codepoints": [169], "characters": "\u00A9" },
+  "&Cacute;": { "codepoints": [262], "characters": "\u0106" },
+  "&Cap;": { "codepoints": [8914], "characters": "\u22D2" },
+  "&CapitalDifferentialD;": { "codepoints": [8517], "characters": "\u2145" },
+  "&Cayleys;": { "codepoints": [8493], "characters": "\u212D" },
+  "&Ccaron;": { "codepoints": [268], "characters": "\u010C" },
+  "&Ccedil": { "codepoints": [199], "characters": "\u00C7" },
+  "&Ccedil;": { "codepoints": [199], "characters": "\u00C7" },
+  "&Ccirc;": { "codepoints": [264], "characters": "\u0108" },
+  "&Cconint;": { "codepoints": [8752], "characters": "\u2230" },
+  "&Cdot;": { "codepoints": [266], "characters": "\u010A" },
+  "&Cedilla;": { "codepoints": [184], "characters": "\u00B8" },
+  "&CenterDot;": { "codepoints": [183], "characters": "\u00B7" },
+  "&Cfr;": { "codepoints": [8493], "characters": "\u212D" },
+  "&Chi;": { "codepoints": [935], "characters": "\u03A7" },
+  "&CircleDot;": { "codepoints": [8857], "characters": "\u2299" },
+  "&CircleMinus;": { "codepoints": [8854], "characters": "\u2296" },
+  "&CirclePlus;": { "codepoints": [8853], "characters": "\u2295" },
+  "&CircleTimes;": { "codepoints": [8855], "characters": "\u2297" },
+  "&ClockwiseContourIntegral;": { "codepoints": [8754], "characters": "\u2232" },
+  "&CloseCurlyDoubleQuote;": { "codepoints": [8221], "characters": "\u201D" },
+  "&CloseCurlyQuote;": { "codepoints": [8217], "characters": "\u2019" },
+  "&Colon;": { "codepoints": [8759], "characters": "\u2237" },
+  "&Colone;": { "codepoints": [10868], "characters": "\u2A74" },
+  "&Congruent;": { "codepoints": [8801], "characters": "\u2261" },
+  "&Conint;": { "codepoints": [8751], "characters": "\u222F" },
+  "&ContourIntegral;": { "codepoints": [8750], "characters": "\u222E" },
+  "&Copf;": { "codepoints": [8450], "characters": "\u2102" },
+  "&Coproduct;": { "codepoints": [8720], "characters": "\u2210" },
+  "&CounterClockwiseContourIntegral;": { "codepoints": [8755], "characters": "\u2233" },
+  "&Cross;": { "codepoints": [10799], "characters": "\u2A2F" },
+  "&Cscr;": { "codepoints": [119966], "characters": "\uD835\uDC9E" },
+  "&Cup;": { "codepoints": [8915], "characters": "\u22D3" },
+  "&CupCap;": { "codepoints": [8781], "characters": "\u224D" },
+  "&DD;": { "codepoints": [8517], "characters": "\u2145" },
+  "&DDotrahd;": { "codepoints": [10513], "characters": "\u2911" },
+  "&DJcy;": { "codepoints": [1026], "characters": "\u0402" },
+  "&DScy;": { "codepoints": [1029], "characters": "\u0405" },
+  "&DZcy;": { "codepoints": [1039], "characters": "\u040F" },
+  "&Dagger;": { "codepoints": [8225], "characters": "\u2021" },
+  "&Darr;": { "codepoints": [8609], "characters": "\u21A1" },
+  "&Dashv;": { "codepoints": [10980], "characters": "\u2AE4" },
+  "&Dcaron;": { "codepoints": [270], "characters": "\u010E" },
+  "&Dcy;": { "codepoints": [1044], "characters": "\u0414" },
+  "&Del;": { "codepoints": [8711], "characters": "\u2207" },
+  "&Delta;": { "codepoints": [916], "characters": "\u0394" },
+  "&Dfr;": { "codepoints": [120071], "characters": "\uD835\uDD07" },
+  "&DiacriticalAcute;": { "codepoints": [180], "characters": "\u00B4" },
+  "&DiacriticalDot;": { "codepoints": [729], "characters": "\u02D9" },
+  "&DiacriticalDoubleAcute;": { "codepoints": [733], "characters": "\u02DD" },
+  "&DiacriticalGrave;": { "codepoints": [96], "characters": "\u0060" },
+  "&DiacriticalTilde;": { "codepoints": [732], "characters": "\u02DC" },
+  "&Diamond;": { "codepoints": [8900], "characters": "\u22C4" },
+  "&DifferentialD;": { "codepoints": [8518], "characters": "\u2146" },
+  "&Dopf;": { "codepoints": [120123], "characters": "\uD835\uDD3B" },
+  "&Dot;": { "codepoints": [168], "characters": "\u00A8" },
+  "&DotDot;": { "codepoints": [8412], "characters": "\u20DC" },
+  "&DotEqual;": { "codepoints": [8784], "characters": "\u2250" },
+  "&DoubleContourIntegral;": { "codepoints": [8751], "characters": "\u222F" },
+  "&DoubleDot;": { "codepoints": [168], "characters": "\u00A8" },
+  "&DoubleDownArrow;": { "codepoints": [8659], "characters": "\u21D3" },
+  "&DoubleLeftArrow;": { "codepoints": [8656], "characters": "\u21D0" },
+  "&DoubleLeftRightArrow;": { "codepoints": [8660], "characters": "\u21D4" },
+  "&DoubleLeftTee;": { "codepoints": [10980], "characters": "\u2AE4" },
+  "&DoubleLongLeftArrow;": { "codepoints": [10232], "characters": "\u27F8" },
+  "&DoubleLongLeftRightArrow;": { "codepoints": [10234], "characters": "\u27FA" },
+  "&DoubleLongRightArrow;": { "codepoints": [10233], "characters": "\u27F9" },
+  "&DoubleRightArrow;": { "codepoints": [8658], "characters": "\u21D2" },
+  "&DoubleRightTee;": { "codepoints": [8872], "characters": "\u22A8" },
+  "&DoubleUpArrow;": { "codepoints": [8657], "characters": "\u21D1" },
+  "&DoubleUpDownArrow;": { "codepoints": [8661], "characters": "\u21D5" },
+  "&DoubleVerticalBar;": { "codepoints": [8741], "characters": "\u2225" },
+  "&DownArrow;": { "codepoints": [8595], "characters": "\u2193" },
+  "&DownArrowBar;": { "codepoints": [10515], "characters": "\u2913" },
+  "&DownArrowUpArrow;": { "codepoints": [8693], "characters": "\u21F5" },
+  "&DownBreve;": { "codepoints": [785], "characters": "\u0311" },
+  "&DownLeftRightVector;": { "codepoints": [10576], "characters": "\u2950" },
+  "&DownLeftTeeVector;": { "codepoints": [10590], "characters": "\u295E" },
+  "&DownLeftVector;": { "codepoints": [8637], "characters": "\u21BD" },
+  "&DownLeftVectorBar;": { "codepoints": [10582], "characters": "\u2956" },
+  "&DownRightTeeVector;": { "codepoints": [10591], "characters": "\u295F" },
+  "&DownRightVector;": { "codepoints": [8641], "characters": "\u21C1" },
+  "&DownRightVectorBar;": { "codepoints": [10583], "characters": "\u2957" },
+  "&DownTee;": { "codepoints": [8868], "characters": "\u22A4" },
+  "&DownTeeArrow;": { "codepoints": [8615], "characters": "\u21A7" },
+  "&Downarrow;": { "codepoints": [8659], "characters": "\u21D3" },
+  "&Dscr;": { "codepoints": [119967], "characters": "\uD835\uDC9F" },
+  "&Dstrok;": { "codepoints": [272], "characters": "\u0110" },
+  "&ENG;": { "codepoints": [330], "characters": "\u014A" },
+  "&ETH": { "codepoints": [208], "characters": "\u00D0" },
+  "&ETH;": { "codepoints": [208], "characters": "\u00D0" },
+  "&Eacute": { "codepoints": [201], "characters": "\u00C9" },
+  "&Eacute;": { "codepoints": [201], "characters": "\u00C9" },
+  "&Ecaron;": { "codepoints": [282], "characters": "\u011A" },
+  "&Ecirc": { "codepoints": [202], "characters": "\u00CA" },
+  "&Ecirc;": { "codepoints": [202], "characters": "\u00CA" },
+  "&Ecy;": { "codepoints": [1069], "characters": "\u042D" },
+  "&Edot;": { "codepoints": [278], "characters": "\u0116" },
+  "&Efr;": { "codepoints": [120072], "characters": "\uD835\uDD08" },
+  "&Egrave": { "codepoints": [200], "characters": "\u00C8" },
+  "&Egrave;": { "codepoints": [200], "characters": "\u00C8" },
+  "&Element;": { "codepoints": [8712], "characters": "\u2208" },
+  "&Emacr;": { "codepoints": [274], "characters": "\u0112" },
+  "&EmptySmallSquare;": { "codepoints": [9723], "characters": "\u25FB" },
+  "&EmptyVerySmallSquare;": { "codepoints": [9643], "characters": "\u25AB" },
+  "&Eogon;": { "codepoints": [280], "characters": "\u0118" },
+  "&Eopf;": { "codepoints": [120124], "characters": "\uD835\uDD3C" },
+  "&Epsilon;": { "codepoints": [917], "characters": "\u0395" },
+  "&Equal;": { "codepoints": [10869], "characters": "\u2A75" },
+  "&EqualTilde;": { "codepoints": [8770], "characters": "\u2242" },
+  "&Equilibrium;": { "codepoints": [8652], "characters": "\u21CC" },
+  "&Escr;": { "codepoints": [8496], "characters": "\u2130" },
+  "&Esim;": { "codepoints": [10867], "characters": "\u2A73" },
+  "&Eta;": { "codepoints": [919], "characters": "\u0397" },
+  "&Euml": { "codepoints": [203], "characters": "\u00CB" },
+  "&Euml;": { "codepoints": [203], "characters": "\u00CB" },
+  "&Exists;": { "codepoints": [8707], "characters": "\u2203" },
+  "&ExponentialE;": { "codepoints": [8519], "characters": "\u2147" },
+  "&Fcy;": { "codepoints": [1060], "characters": "\u0424" },
+  "&Ffr;": { "codepoints": [120073], "characters": "\uD835\uDD09" },
+  "&FilledSmallSquare;": { "codepoints": [9724], "characters": "\u25FC" },
+  "&FilledVerySmallSquare;": { "codepoints": [9642], "characters": "\u25AA" },
+  "&Fopf;": { "codepoints": [120125], "characters": "\uD835\uDD3D" },
+  "&ForAll;": { "codepoints": [8704], "characters": "\u2200" },
+  "&Fouriertrf;": { "codepoints": [8497], "characters": "\u2131" },
+  "&Fscr;": { "codepoints": [8497], "characters": "\u2131" },
+  "&GJcy;": { "codepoints": [1027], "characters": "\u0403" },
+  "&GT": { "codepoints": [62], "characters": "\u003E" },
+  "&GT;": { "codepoints": [62], "characters": "\u003E" },
+  "&Gamma;": { "codepoints": [915], "characters": "\u0393" },
+  "&Gammad;": { "codepoints": [988], "characters": "\u03DC" },
+  "&Gbreve;": { "codepoints": [286], "characters": "\u011E" },
+  "&Gcedil;": { "codepoints": [290], "characters": "\u0122" },
+  "&Gcirc;": { "codepoints": [284], "characters": "\u011C" },
+  "&Gcy;": { "codepoints": [1043], "characters": "\u0413" },
+  "&Gdot;": { "codepoints": [288], "characters": "\u0120" },
+  "&Gfr;": { "codepoints": [120074], "characters": "\uD835\uDD0A" },
+  "&Gg;": { "codepoints": [8921], "characters": "\u22D9" },
+  "&Gopf;": { "codepoints": [120126], "characters": "\uD835\uDD3E" },
+  "&GreaterEqual;": { "codepoints": [8805], "characters": "\u2265" },
+  "&GreaterEqualLess;": { "codepoints": [8923], "characters": "\u22DB" },
+  "&GreaterFullEqual;": { "codepoints": [8807], "characters": "\u2267" },
+  "&GreaterGreater;": { "codepoints": [10914], "characters": "\u2AA2" },
+  "&GreaterLess;": { "codepoints": [8823], "characters": "\u2277" },
+  "&GreaterSlantEqual;": { "codepoints": [10878], "characters": "\u2A7E" },
+  "&GreaterTilde;": { "codepoints": [8819], "characters": "\u2273" },
+  "&Gscr;": { "codepoints": [119970], "characters": "\uD835\uDCA2" },
+  "&Gt;": { "codepoints": [8811], "characters": "\u226B" },
+  "&HARDcy;": { "codepoints": [1066], "characters": "\u042A" },
+  "&Hacek;": { "codepoints": [711], "characters": "\u02C7" },
+  "&Hat;": { "codepoints": [94], "characters": "\u005E" },
+  "&Hcirc;": { "codepoints": [292], "characters": "\u0124" },
+  "&Hfr;": { "codepoints": [8460], "characters": "\u210C" },
+  "&HilbertSpace;": { "codepoints": [8459], "characters": "\u210B" },
+  "&Hopf;": { "codepoints": [8461], "characters": "\u210D" },
+  "&HorizontalLine;": { "codepoints": [9472], "characters": "\u2500" },
+  "&Hscr;": { "codepoints": [8459], "characters": "\u210B" },
+  "&Hstrok;": { "codepoints": [294], "characters": "\u0126" },
+  "&HumpDownHump;": { "codepoints": [8782], "characters": "\u224E" },
+  "&HumpEqual;": { "codepoints": [8783], "characters": "\u224F" },
+  "&IEcy;": { "codepoints": [1045], "characters": "\u0415" },
+  "&IJlig;": { "codepoints": [306], "characters": "\u0132" },
+  "&IOcy;": { "codepoints": [1025], "characters": "\u0401" },
+  "&Iacute": { "codepoints": [205], "characters": "\u00CD" },
+  "&Iacute;": { "codepoints": [205], "characters": "\u00CD" },
+  "&Icirc": { "codepoints": [206], "characters": "\u00CE" },
+  "&Icirc;": { "codepoints": [206], "characters": "\u00CE" },
+  "&Icy;": { "codepoints": [1048], "characters": "\u0418" },
+  "&Idot;": { "codepoints": [304], "characters": "\u0130" },
+  "&Ifr;": { "codepoints": [8465], "characters": "\u2111" },
+  "&Igrave": { "codepoints": [204], "characters": "\u00CC" },
+  "&Igrave;": { "codepoints": [204], "characters": "\u00CC" },
+  "&Im;": { "codepoints": [8465], "characters": "\u2111" },
+  "&Imacr;": { "codepoints": [298], "characters": "\u012A" },
+  "&ImaginaryI;": { "codepoints": [8520], "characters": "\u2148" },
+  "&Implies;": { "codepoints": [8658], "characters": "\u21D2" },
+  "&Int;": { "codepoints": [8748], "characters": "\u222C" },
+  "&Integral;": { "codepoints": [8747], "characters": "\u222B" },
+  "&Intersection;": { "codepoints": [8898], "characters": "\u22C2" },
+  "&InvisibleComma;": { "codepoints": [8291], "characters": "\u2063" },
+  "&InvisibleTimes;": { "codepoints": [8290], "characters": "\u2062" },
+  "&Iogon;": { "codepoints": [302], "characters": "\u012E" },
+  "&Iopf;": { "codepoints": [120128], "characters": "\uD835\uDD40" },
+  "&Iota;": { "codepoints": [921], "characters": "\u0399" },
+  "&Iscr;": { "codepoints": [8464], "characters": "\u2110" },
+  "&Itilde;": { "codepoints": [296], "characters": "\u0128" },
+  "&Iukcy;": { "codepoints": [1030], "characters": "\u0406" },
+  "&Iuml": { "codepoints": [207], "characters": "\u00CF" },
+  "&Iuml;": { "codepoints": [207], "characters": "\u00CF" },
+  "&Jcirc;": { "codepoints": [308], "characters": "\u0134" },
+  "&Jcy;": { "codepoints": [1049], "characters": "\u0419" },
+  "&Jfr;": { "codepoints": [120077], "characters": "\uD835\uDD0D" },
+  "&Jopf;": { "codepoints": [120129], "characters": "\uD835\uDD41" },
+  "&Jscr;": { "codepoints": [119973], "characters": "\uD835\uDCA5" },
+  "&Jsercy;": { "codepoints": [1032], "characters": "\u0408" },
+  "&Jukcy;": { "codepoints": [1028], "characters": "\u0404" },
+  "&KHcy;": { "codepoints": [1061], "characters": "\u0425" },
+  "&KJcy;": { "codepoints": [1036], "characters": "\u040C" },
+  "&Kappa;": { "codepoints": [922], "characters": "\u039A" },
+  "&Kcedil;": { "codepoints": [310], "characters": "\u0136" },
+  "&Kcy;": { "codepoints": [1050], "characters": "\u041A" },
+  "&Kfr;": { "codepoints": [120078], "characters": "\uD835\uDD0E" },
+  "&Kopf;": { "codepoints": [120130], "characters": "\uD835\uDD42" },
+  "&Kscr;": { "codepoints": [119974], "characters": "\uD835\uDCA6" },
+  "&LJcy;": { "codepoints": [1033], "characters": "\u0409" },
+  "&LT": { "codepoints": [60], "characters": "\u003C" },
+  "&LT;": { "codepoints": [60], "characters": "\u003C" },
+  "&Lacute;": { "codepoints": [313], "characters": "\u0139" },
+  "&Lambda;": { "codepoints": [923], "characters": "\u039B" },
+  "&Lang;": { "codepoints": [10218], "characters": "\u27EA" },
+  "&Laplacetrf;": { "codepoints": [8466], "characters": "\u2112" },
+  "&Larr;": { "codepoints": [8606], "characters": "\u219E" },
+  "&Lcaron;": { "codepoints": [317], "characters": "\u013D" },
+  "&Lcedil;": { "codepoints": [315], "characters": "\u013B" },
+  "&Lcy;": { "codepoints": [1051], "characters": "\u041B" },
+  "&LeftAngleBracket;": { "codepoints": [10216], "characters": "\u27E8" },
+  "&LeftArrow;": { "codepoints": [8592], "characters": "\u2190" },
+  "&LeftArrowBar;": { "codepoints": [8676], "characters": "\u21E4" },
+  "&LeftArrowRightArrow;": { "codepoints": [8646], "characters": "\u21C6" },
+  "&LeftCeiling;": { "codepoints": [8968], "characters": "\u2308" },
+  "&LeftDoubleBracket;": { "codepoints": [10214], "characters": "\u27E6" },
+  "&LeftDownTeeVector;": { "codepoints": [10593], "characters": "\u2961" },
+  "&LeftDownVector;": { "codepoints": [8643], "characters": "\u21C3" },
+  "&LeftDownVectorBar;": { "codepoints": [10585], "characters": "\u2959" },
+  "&LeftFloor;": { "codepoints": [8970], "characters": "\u230A" },
+  "&LeftRightArrow;": { "codepoints": [8596], "characters": "\u2194" },
+  "&LeftRightVector;": { "codepoints": [10574], "characters": "\u294E" },
+  "&LeftTee;": { "codepoints": [8867], "characters": "\u22A3" },
+  "&LeftTeeArrow;": { "codepoints": [8612], "characters": "\u21A4" },
+  "&LeftTeeVector;": { "codepoints": [10586], "characters": "\u295A" },
+  "&LeftTriangle;": { "codepoints": [8882], "characters": "\u22B2" },
+  "&LeftTriangleBar;": { "codepoints": [10703], "characters": "\u29CF" },
+  "&LeftTriangleEqual;": { "codepoints": [8884], "characters": "\u22B4" },
+  "&LeftUpDownVector;": { "codepoints": [10577], "characters": "\u2951" },
+  "&LeftUpTeeVector;": { "codepoints": [10592], "characters": "\u2960" },
+  "&LeftUpVector;": { "codepoints": [8639], "characters": "\u21BF" },
+  "&LeftUpVectorBar;": { "codepoints": [10584], "characters": "\u2958" },
+  "&LeftVector;": { "codepoints": [8636], "characters": "\u21BC" },
+  "&LeftVectorBar;": { "codepoints": [10578], "characters": "\u2952" },
+  "&Leftarrow;": { "codepoints": [8656], "characters": "\u21D0" },
+  "&Leftrightarrow;": { "codepoints": [8660], "characters": "\u21D4" },
+  "&LessEqualGreater;": { "codepoints": [8922], "characters": "\u22DA" },
+  "&LessFullEqual;": { "codepoints": [8806], "characters": "\u2266" },
+  "&LessGreater;": { "codepoints": [8822], "characters": "\u2276" },
+  "&LessLess;": { "codepoints": [10913], "characters": "\u2AA1" },
+  "&LessSlantEqual;": { "codepoints": [10877], "characters": "\u2A7D" },
+  "&LessTilde;": { "codepoints": [8818], "characters": "\u2272" },
+  "&Lfr;": { "codepoints": [120079], "characters": "\uD835\uDD0F" },
+  "&Ll;": { "codepoints": [8920], "characters": "\u22D8" },
+  "&Lleftarrow;": { "codepoints": [8666], "characters": "\u21DA" },
+  "&Lmidot;": { "codepoints": [319], "characters": "\u013F" },
+  "&LongLeftArrow;": { "codepoints": [10229], "characters": "\u27F5" },
+  "&LongLeftRightArrow;": { "codepoints": [10231], "characters": "\u27F7" },
+  "&LongRightArrow;": { "codepoints": [10230], "characters": "\u27F6" },
+  "&Longleftarrow;": { "codepoints": [10232], "characters": "\u27F8" },
+  "&Longleftrightarrow;": { "codepoints": [10234], "characters": "\u27FA" },
+  "&Longrightarrow;": { "codepoints": [10233], "characters": "\u27F9" },
+  "&Lopf;": { "codepoints": [120131], "characters": "\uD835\uDD43" },
+  "&LowerLeftArrow;": { "codepoints": [8601], "characters": "\u2199" },
+  "&LowerRightArrow;": { "codepoints": [8600], "characters": "\u2198" },
+  "&Lscr;": { "codepoints": [8466], "characters": "\u2112" },
+  "&Lsh;": { "codepoints": [8624], "characters": "\u21B0" },
+  "&Lstrok;": { "codepoints": [321], "characters": "\u0141" },
+  "&Lt;": { "codepoints": [8810], "characters": "\u226A" },
+  "&Map;": { "codepoints": [10501], "characters": "\u2905" },
+  "&Mcy;": { "codepoints": [1052], "characters": "\u041C" },
+  "&MediumSpace;": { "codepoints": [8287], "characters": "\u205F" },
+  "&Mellintrf;": { "codepoints": [8499], "characters": "\u2133" },
+  "&Mfr;": { "codepoints": [120080], "characters": "\uD835\uDD10" },
+  "&MinusPlus;": { "codepoints": [8723], "characters": "\u2213" },
+  "&Mopf;": { "codepoints": [120132], "characters": "\uD835\uDD44" },
+  "&Mscr;": { "codepoints": [8499], "characters": "\u2133" },
+  "&Mu;": { "codepoints": [924], "characters": "\u039C" },
+  "&NJcy;": { "codepoints": [1034], "characters": "\u040A" },
+  "&Nacute;": { "codepoints": [323], "characters": "\u0143" },
+  "&Ncaron;": { "codepoints": [327], "characters": "\u0147" },
+  "&Ncedil;": { "codepoints": [325], "characters": "\u0145" },
+  "&Ncy;": { "codepoints": [1053], "characters": "\u041D" },
+  "&NegativeMediumSpace;": { "codepoints": [8203], "characters": "\u200B" },
+  "&NegativeThickSpace;": { "codepoints": [8203], "characters": "\u200B" },
+  "&NegativeThinSpace;": { "codepoints": [8203], "characters": "\u200B" },
+  "&NegativeVeryThinSpace;": { "codepoints": [8203], "characters": "\u200B" },
+  "&NestedGreaterGreater;": { "codepoints": [8811], "characters": "\u226B" },
+  "&NestedLessLess;": { "codepoints": [8810], "characters": "\u226A" },
+  "&NewLine;": { "codepoints": [10], "characters": "\u000A" },
+  "&Nfr;": { "codepoints": [120081], "characters": "\uD835\uDD11" },
+  "&NoBreak;": { "codepoints": [8288], "characters": "\u2060" },
+  "&NonBreakingSpace;": { "codepoints": [160], "characters": "\u00A0" },
+  "&Nopf;": { "codepoints": [8469], "characters": "\u2115" },
+  "&Not;": { "codepoints": [10988], "characters": "\u2AEC" },
+  "&NotCongruent;": { "codepoints": [8802], "characters": "\u2262" },
+  "&NotCupCap;": { "codepoints": [8813], "characters": "\u226D" },
+  "&NotDoubleVerticalBar;": { "codepoints": [8742], "characters": "\u2226" },
+  "&NotElement;": { "codepoints": [8713], "characters": "\u2209" },
+  "&NotEqual;": { "codepoints": [8800], "characters": "\u2260" },
+  "&NotEqualTilde;": { "codepoints": [8770, 824], "characters": "\u2242\u0338" },
+  "&NotExists;": { "codepoints": [8708], "characters": "\u2204" },
+  "&NotGreater;": { "codepoints": [8815], "characters": "\u226F" },
+  "&NotGreaterEqual;": { "codepoints": [8817], "characters": "\u2271" },
+  "&NotGreaterFullEqual;": { "codepoints": [8807, 824], "characters": "\u2267\u0338" },
+  "&NotGreaterGreater;": { "codepoints": [8811, 824], "characters": "\u226B\u0338" },
+  "&NotGreaterLess;": { "codepoints": [8825], "characters": "\u2279" },
+  "&NotGreaterSlantEqual;": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" },
+  "&NotGreaterTilde;": { "codepoints": [8821], "characters": "\u2275" },
+  "&NotHumpDownHump;": { "codepoints": [8782, 824], "characters": "\u224E\u0338" },
+  "&NotHumpEqual;": { "codepoints": [8783, 824], "characters": "\u224F\u0338" },
+  "&NotLeftTriangle;": { "codepoints": [8938], "characters": "\u22EA" },
+  "&NotLeftTriangleBar;": { "codepoints": [10703, 824], "characters": "\u29CF\u0338" },
+  "&NotLeftTriangleEqual;": { "codepoints": [8940], "characters": "\u22EC" },
+  "&NotLess;": { "codepoints": [8814], "characters": "\u226E" },
+  "&NotLessEqual;": { "codepoints": [8816], "characters": "\u2270" },
+  "&NotLessGreater;": { "codepoints": [8824], "characters": "\u2278" },
+  "&NotLessLess;": { "codepoints": [8810, 824], "characters": "\u226A\u0338" },
+  "&NotLessSlantEqual;": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" },
+  "&NotLessTilde;": { "codepoints": [8820], "characters": "\u2274" },
+  "&NotNestedGreaterGreater;": { "codepoints": [10914, 824], "characters": "\u2AA2\u0338" },
+  "&NotNestedLessLess;": { "codepoints": [10913, 824], "characters": "\u2AA1\u0338" },
+  "&NotPrecedes;": { "codepoints": [8832], "characters": "\u2280" },
+  "&NotPrecedesEqual;": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" },
+  "&NotPrecedesSlantEqual;": { "codepoints": [8928], "characters": "\u22E0" },
+  "&NotReverseElement;": { "codepoints": [8716], "characters": "\u220C" },
+  "&NotRightTriangle;": { "codepoints": [8939], "characters": "\u22EB" },
+  "&NotRightTriangleBar;": { "codepoints": [10704, 824], "characters": "\u29D0\u0338" },
+  "&NotRightTriangleEqual;": { "codepoints": [8941], "characters": "\u22ED" },
+  "&NotSquareSubset;": { "codepoints": [8847, 824], "characters": "\u228F\u0338" },
+  "&NotSquareSubsetEqual;": { "codepoints": [8930], "characters": "\u22E2" },
+  "&NotSquareSuperset;": { "codepoints": [8848, 824], "characters": "\u2290\u0338" },
+  "&NotSquareSupersetEqual;": { "codepoints": [8931], "characters": "\u22E3" },
+  "&NotSubset;": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" },
+  "&NotSubsetEqual;": { "codepoints": [8840], "characters": "\u2288" },
+  "&NotSucceeds;": { "codepoints": [8833], "characters": "\u2281" },
+  "&NotSucceedsEqual;": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" },
+  "&NotSucceedsSlantEqual;": { "codepoints": [8929], "characters": "\u22E1" },
+  "&NotSucceedsTilde;": { "codepoints": [8831, 824], "characters": "\u227F\u0338" },
+  "&NotSuperset;": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" },
+  "&NotSupersetEqual;": { "codepoints": [8841], "characters": "\u2289" },
+  "&NotTilde;": { "codepoints": [8769], "characters": "\u2241" },
+  "&NotTildeEqual;": { "codepoints": [8772], "characters": "\u2244" },
+  "&NotTildeFullEqual;": { "codepoints": [8775], "characters": "\u2247" },
+  "&NotTildeTilde;": { "codepoints": [8777], "characters": "\u2249" },
+  "&NotVerticalBar;": { "codepoints": [8740], "characters": "\u2224" },
+  "&Nscr;": { "codepoints": [119977], "characters": "\uD835\uDCA9" },
+  "&Ntilde": { "codepoints": [209], "characters": "\u00D1" },
+  "&Ntilde;": { "codepoints": [209], "characters": "\u00D1" },
+  "&Nu;": { "codepoints": [925], "characters": "\u039D" },
+  "&OElig;": { "codepoints": [338], "characters": "\u0152" },
+  "&Oacute": { "codepoints": [211], "characters": "\u00D3" },
+  "&Oacute;": { "codepoints": [211], "characters": "\u00D3" },
+  "&Ocirc": { "codepoints": [212], "characters": "\u00D4" },
+  "&Ocirc;": { "codepoints": [212], "characters": "\u00D4" },
+  "&Ocy;": { "codepoints": [1054], "characters": "\u041E" },
+  "&Odblac;": { "codepoints": [336], "characters": "\u0150" },
+  "&Ofr;": { "codepoints": [120082], "characters": "\uD835\uDD12" },
+  "&Ograve": { "codepoints": [210], "characters": "\u00D2" },
+  "&Ograve;": { "codepoints": [210], "characters": "\u00D2" },
+  "&Omacr;": { "codepoints": [332], "characters": "\u014C" },
+  "&Omega;": { "codepoints": [937], "characters": "\u03A9" },
+  "&Omicron;": { "codepoints": [927], "characters": "\u039F" },
+  "&Oopf;": { "codepoints": [120134], "characters": "\uD835\uDD46" },
+  "&OpenCurlyDoubleQuote;": { "codepoints": [8220], "characters": "\u201C" },
+  "&OpenCurlyQuote;": { "codepoints": [8216], "characters": "\u2018" },
+  "&Or;": { "codepoints": [10836], "characters": "\u2A54" },
+  "&Oscr;": { "codepoints": [119978], "characters": "\uD835\uDCAA" },
+  "&Oslash": { "codepoints": [216], "characters": "\u00D8" },
+  "&Oslash;": { "codepoints": [216], "characters": "\u00D8" },
+  "&Otilde": { "codepoints": [213], "characters": "\u00D5" },
+  "&Otilde;": { "codepoints": [213], "characters": "\u00D5" },
+  "&Otimes;": { "codepoints": [10807], "characters": "\u2A37" },
+  "&Ouml": { "codepoints": [214], "characters": "\u00D6" },
+  "&Ouml;": { "codepoints": [214], "characters": "\u00D6" },
+  "&OverBar;": { "codepoints": [8254], "characters": "\u203E" },
+  "&OverBrace;": { "codepoints": [9182], "characters": "\u23DE" },
+  "&OverBracket;": { "codepoints": [9140], "characters": "\u23B4" },
+  "&OverParenthesis;": { "codepoints": [9180], "characters": "\u23DC" },
+  "&PartialD;": { "codepoints": [8706], "characters": "\u2202" },
+  "&Pcy;": { "codepoints": [1055], "characters": "\u041F" },
+  "&Pfr;": { "codepoints": [120083], "characters": "\uD835\uDD13" },
+  "&Phi;": { "codepoints": [934], "characters": "\u03A6" },
+  "&Pi;": { "codepoints": [928], "characters": "\u03A0" },
+  "&PlusMinus;": { "codepoints": [177], "characters": "\u00B1" },
+  "&Poincareplane;": { "codepoints": [8460], "characters": "\u210C" },
+  "&Popf;": { "codepoints": [8473], "characters": "\u2119" },
+  "&Pr;": { "codepoints": [10939], "characters": "\u2ABB" },
+  "&Precedes;": { "codepoints": [8826], "characters": "\u227A" },
+  "&PrecedesEqual;": { "codepoints": [10927], "characters": "\u2AAF" },
+  "&PrecedesSlantEqual;": { "codepoints": [8828], "characters": "\u227C" },
+  "&PrecedesTilde;": { "codepoints": [8830], "characters": "\u227E" },
+  "&Prime;": { "codepoints": [8243], "characters": "\u2033" },
+  "&Product;": { "codepoints": [8719], "characters": "\u220F" },
+  "&Proportion;": { "codepoints": [8759], "characters": "\u2237" },
+  "&Proportional;": { "codepoints": [8733], "characters": "\u221D" },
+  "&Pscr;": { "codepoints": [119979], "characters": "\uD835\uDCAB" },
+  "&Psi;": { "codepoints": [936], "characters": "\u03A8" },
+  "&QUOT": { "codepoints": [34], "characters": "\u0022" },
+  "&QUOT;": { "codepoints": [34], "characters": "\u0022" },
+  "&Qfr;": { "codepoints": [120084], "characters": "\uD835\uDD14" },
+  "&Qopf;": { "codepoints": [8474], "characters": "\u211A" },
+  "&Qscr;": { "codepoints": [119980], "characters": "\uD835\uDCAC" },
+  "&RBarr;": { "codepoints": [10512], "characters": "\u2910" },
+  "&REG": { "codepoints": [174], "characters": "\u00AE" },
+  "&REG;": { "codepoints": [174], "characters": "\u00AE" },
+  "&Racute;": { "codepoints": [340], "characters": "\u0154" },
+  "&Rang;": { "codepoints": [10219], "characters": "\u27EB" },
+  "&Rarr;": { "codepoints": [8608], "characters": "\u21A0" },
+  "&Rarrtl;": { "codepoints": [10518], "characters": "\u2916" },
+  "&Rcaron;": { "codepoints": [344], "characters": "\u0158" },
+  "&Rcedil;": { "codepoints": [342], "characters": "\u0156" },
+  "&Rcy;": { "codepoints": [1056], "characters": "\u0420" },
+  "&Re;": { "codepoints": [8476], "characters": "\u211C" },
+  "&ReverseElement;": { "codepoints": [8715], "characters": "\u220B" },
+  "&ReverseEquilibrium;": { "codepoints": [8651], "characters": "\u21CB" },
+  "&ReverseUpEquilibrium;": { "codepoints": [10607], "characters": "\u296F" },
+  "&Rfr;": { "codepoints": [8476], "characters": "\u211C" },
+  "&Rho;": { "codepoints": [929], "characters": "\u03A1" },
+  "&RightAngleBracket;": { "codepoints": [10217], "characters": "\u27E9" },
+  "&RightArrow;": { "codepoints": [8594], "characters": "\u2192" },
+  "&RightArrowBar;": { "codepoints": [8677], "characters": "\u21E5" },
+  "&RightArrowLeftArrow;": { "codepoints": [8644], "characters": "\u21C4" },
+  "&RightCeiling;": { "codepoints": [8969], "characters": "\u2309" },
+  "&RightDoubleBracket;": { "codepoints": [10215], "characters": "\u27E7" },
+  "&RightDownTeeVector;": { "codepoints": [10589], "characters": "\u295D" },
+  "&RightDownVector;": { "codepoints": [8642], "characters": "\u21C2" },
+  "&RightDownVectorBar;": { "codepoints": [10581], "characters": "\u2955" },
+  "&RightFloor;": { "codepoints": [8971], "characters": "\u230B" },
+  "&RightTee;": { "codepoints": [8866], "characters": "\u22A2" },
+  "&RightTeeArrow;": { "codepoints": [8614], "characters": "\u21A6" },
+  "&RightTeeVector;": { "codepoints": [10587], "characters": "\u295B" },
+  "&RightTriangle;": { "codepoints": [8883], "characters": "\u22B3" },
+  "&RightTriangleBar;": { "codepoints": [10704], "characters": "\u29D0" },
+  "&RightTriangleEqual;": { "codepoints": [8885], "characters": "\u22B5" },
+  "&RightUpDownVector;": { "codepoints": [10575], "characters": "\u294F" },
+  "&RightUpTeeVector;": { "codepoints": [10588], "characters": "\u295C" },
+  "&RightUpVector;": { "codepoints": [8638], "characters": "\u21BE" },
+  "&RightUpVectorBar;": { "codepoints": [10580], "characters": "\u2954" },
+  "&RightVector;": { "codepoints": [8640], "characters": "\u21C0" },
+  "&RightVectorBar;": { "codepoints": [10579], "characters": "\u2953" },
+  "&Rightarrow;": { "codepoints": [8658], "characters": "\u21D2" },
+  "&Ropf;": { "codepoints": [8477], "characters": "\u211D" },
+  "&RoundImplies;": { "codepoints": [10608], "characters": "\u2970" },
+  "&Rrightarrow;": { "codepoints": [8667], "characters": "\u21DB" },
+  "&Rscr;": { "codepoints": [8475], "characters": "\u211B" },
+  "&Rsh;": { "codepoints": [8625], "characters": "\u21B1" },
+  "&RuleDelayed;": { "codepoints": [10740], "characters": "\u29F4" },
+  "&SHCHcy;": { "codepoints": [1065], "characters": "\u0429" },
+  "&SHcy;": { "codepoints": [1064], "characters": "\u0428" },
+  "&SOFTcy;": { "codepoints": [1068], "characters": "\u042C" },
+  "&Sacute;": { "codepoints": [346], "characters": "\u015A" },
+  "&Sc;": { "codepoints": [10940], "characters": "\u2ABC" },
+  "&Scaron;": { "codepoints": [352], "characters": "\u0160" },
+  "&Scedil;": { "codepoints": [350], "characters": "\u015E" },
+  "&Scirc;": { "codepoints": [348], "characters": "\u015C" },
+  "&Scy;": { "codepoints": [1057], "characters": "\u0421" },
+  "&Sfr;": { "codepoints": [120086], "characters": "\uD835\uDD16" },
+  "&ShortDownArrow;": { "codepoints": [8595], "characters": "\u2193" },
+  "&ShortLeftArrow;": { "codepoints": [8592], "characters": "\u2190" },
+  "&ShortRightArrow;": { "codepoints": [8594], "characters": "\u2192" },
+  "&ShortUpArrow;": { "codepoints": [8593], "characters": "\u2191" },
+  "&Sigma;": { "codepoints": [931], "characters": "\u03A3" },
+  "&SmallCircle;": { "codepoints": [8728], "characters": "\u2218" },
+  "&Sopf;": { "codepoints": [120138], "characters": "\uD835\uDD4A" },
+  "&Sqrt;": { "codepoints": [8730], "characters": "\u221A" },
+  "&Square;": { "codepoints": [9633], "characters": "\u25A1" },
+  "&SquareIntersection;": { "codepoints": [8851], "characters": "\u2293" },
+  "&SquareSubset;": { "codepoints": [8847], "characters": "\u228F" },
+  "&SquareSubsetEqual;": { "codepoints": [8849], "characters": "\u2291" },
+  "&SquareSuperset;": { "codepoints": [8848], "characters": "\u2290" },
+  "&SquareSupersetEqual;": { "codepoints": [8850], "characters": "\u2292" },
+  "&SquareUnion;": { "codepoints": [8852], "characters": "\u2294" },
+  "&Sscr;": { "codepoints": [119982], "characters": "\uD835\uDCAE" },
+  "&Star;": { "codepoints": [8902], "characters": "\u22C6" },
+  "&Sub;": { "codepoints": [8912], "characters": "\u22D0" },
+  "&Subset;": { "codepoints": [8912], "characters": "\u22D0" },
+  "&SubsetEqual;": { "codepoints": [8838], "characters": "\u2286" },
+  "&Succeeds;": { "codepoints": [8827], "characters": "\u227B" },
+  "&SucceedsEqual;": { "codepoints": [10928], "characters": "\u2AB0" },
+  "&SucceedsSlantEqual;": { "codepoints": [8829], "characters": "\u227D" },
+  "&SucceedsTilde;": { "codepoints": [8831], "characters": "\u227F" },
+  "&SuchThat;": { "codepoints": [8715], "characters": "\u220B" },
+  "&Sum;": { "codepoints": [8721], "characters": "\u2211" },
+  "&Sup;": { "codepoints": [8913], "characters": "\u22D1" },
+  "&Superset;": { "codepoints": [8835], "characters": "\u2283" },
+  "&SupersetEqual;": { "codepoints": [8839], "characters": "\u2287" },
+  "&Supset;": { "codepoints": [8913], "characters": "\u22D1" },
+  "&THORN": { "codepoints": [222], "characters": "\u00DE" },
+  "&THORN;": { "codepoints": [222], "characters": "\u00DE" },
+  "&TRADE;": { "codepoints": [8482], "characters": "\u2122" },
+  "&TSHcy;": { "codepoints": [1035], "characters": "\u040B" },
+  "&TScy;": { "codepoints": [1062], "characters": "\u0426" },
+  "&Tab;": { "codepoints": [9], "characters": "\u0009" },
+  "&Tau;": { "codepoints": [932], "characters": "\u03A4" },
+  "&Tcaron;": { "codepoints": [356], "characters": "\u0164" },
+  "&Tcedil;": { "codepoints": [354], "characters": "\u0162" },
+  "&Tcy;": { "codepoints": [1058], "characters": "\u0422" },
+  "&Tfr;": { "codepoints": [120087], "characters": "\uD835\uDD17" },
+  "&Therefore;": { "codepoints": [8756], "characters": "\u2234" },
+  "&Theta;": { "codepoints": [920], "characters": "\u0398" },
+  "&ThickSpace;": { "codepoints": [8287, 8202], "characters": "\u205F\u200A" },
+  "&ThinSpace;": { "codepoints": [8201], "characters": "\u2009" },
+  "&Tilde;": { "codepoints": [8764], "characters": "\u223C" },
+  "&TildeEqual;": { "codepoints": [8771], "characters": "\u2243" },
+  "&TildeFullEqual;": { "codepoints": [8773], "characters": "\u2245" },
+  "&TildeTilde;": { "codepoints": [8776], "characters": "\u2248" },
+  "&Topf;": { "codepoints": [120139], "characters": "\uD835\uDD4B" },
+  "&TripleDot;": { "codepoints": [8411], "characters": "\u20DB" },
+  "&Tscr;": { "codepoints": [119983], "characters": "\uD835\uDCAF" },
+  "&Tstrok;": { "codepoints": [358], "characters": "\u0166" },
+  "&Uacute": { "codepoints": [218], "characters": "\u00DA" },
+  "&Uacute;": { "codepoints": [218], "characters": "\u00DA" },
+  "&Uarr;": { "codepoints": [8607], "characters": "\u219F" },
+  "&Uarrocir;": { "codepoints": [10569], "characters": "\u2949" },
+  "&Ubrcy;": { "codepoints": [1038], "characters": "\u040E" },
+  "&Ubreve;": { "codepoints": [364], "characters": "\u016C" },
+  "&Ucirc": { "codepoints": [219], "characters": "\u00DB" },
+  "&Ucirc;": { "codepoints": [219], "characters": "\u00DB" },
+  "&Ucy;": { "codepoints": [1059], "characters": "\u0423" },
+  "&Udblac;": { "codepoints": [368], "characters": "\u0170" },
+  "&Ufr;": { "codepoints": [120088], "characters": "\uD835\uDD18" },
+  "&Ugrave": { "codepoints": [217], "characters": "\u00D9" },
+  "&Ugrave;": { "codepoints": [217], "characters": "\u00D9" },
+  "&Umacr;": { "codepoints": [362], "characters": "\u016A" },
+  "&UnderBar;": { "codepoints": [95], "characters": "\u005F" },
+  "&UnderBrace;": { "codepoints": [9183], "characters": "\u23DF" },
+  "&UnderBracket;": { "codepoints": [9141], "characters": "\u23B5" },
+  "&UnderParenthesis;": { "codepoints": [9181], "characters": "\u23DD" },
+  "&Union;": { "codepoints": [8899], "characters": "\u22C3" },
+  "&UnionPlus;": { "codepoints": [8846], "characters": "\u228E" },
+  "&Uogon;": { "codepoints": [370], "characters": "\u0172" },
+  "&Uopf;": { "codepoints": [120140], "characters": "\uD835\uDD4C" },
+  "&UpArrow;": { "codepoints": [8593], "characters": "\u2191" },
+  "&UpArrowBar;": { "codepoints": [10514], "characters": "\u2912" },
+  "&UpArrowDownArrow;": { "codepoints": [8645], "characters": "\u21C5" },
+  "&UpDownArrow;": { "codepoints": [8597], "characters": "\u2195" },
+  "&UpEquilibrium;": { "codepoints": [10606], "characters": "\u296E" },
+  "&UpTee;": { "codepoints": [8869], "characters": "\u22A5" },
+  "&UpTeeArrow;": { "codepoints": [8613], "characters": "\u21A5" },
+  "&Uparrow;": { "codepoints": [8657], "characters": "\u21D1" },
+  "&Updownarrow;": { "codepoints": [8661], "characters": "\u21D5" },
+  "&UpperLeftArrow;": { "codepoints": [8598], "characters": "\u2196" },
+  "&UpperRightArrow;": { "codepoints": [8599], "characters": "\u2197" },
+  "&Upsi;": { "codepoints": [978], "characters": "\u03D2" },
+  "&Upsilon;": { "codepoints": [933], "characters": "\u03A5" },
+  "&Uring;": { "codepoints": [366], "characters": "\u016E" },
+  "&Uscr;": { "codepoints": [119984], "characters": "\uD835\uDCB0" },
+  "&Utilde;": { "codepoints": [360], "characters": "\u0168" },
+  "&Uuml": { "codepoints": [220], "characters": "\u00DC" },
+  "&Uuml;": { "codepoints": [220], "characters": "\u00DC" },
+  "&VDash;": { "codepoints": [8875], "characters": "\u22AB" },
+  "&Vbar;": { "codepoints": [10987], "characters": "\u2AEB" },
+  "&Vcy;": { "codepoints": [1042], "characters": "\u0412" },
+  "&Vdash;": { "codepoints": [8873], "characters": "\u22A9" },
+  "&Vdashl;": { "codepoints": [10982], "characters": "\u2AE6" },
+  "&Vee;": { "codepoints": [8897], "characters": "\u22C1" },
+  "&Verbar;": { "codepoints": [8214], "characters": "\u2016" },
+  "&Vert;": { "codepoints": [8214], "characters": "\u2016" },
+  "&VerticalBar;": { "codepoints": [8739], "characters": "\u2223" },
+  "&VerticalLine;": { "codepoints": [124], "characters": "\u007C" },
+  "&VerticalSeparator;": { "codepoints": [10072], "characters": "\u2758" },
+  "&VerticalTilde;": { "codepoints": [8768], "characters": "\u2240" },
+  "&VeryThinSpace;": { "codepoints": [8202], "characters": "\u200A" },
+  "&Vfr;": { "codepoints": [120089], "characters": "\uD835\uDD19" },
+  "&Vopf;": { "codepoints": [120141], "characters": "\uD835\uDD4D" },
+  "&Vscr;": { "codepoints": [119985], "characters": "\uD835\uDCB1" },
+  "&Vvdash;": { "codepoints": [8874], "characters": "\u22AA" },
+  "&Wcirc;": { "codepoints": [372], "characters": "\u0174" },
+  "&Wedge;": { "codepoints": [8896], "characters": "\u22C0" },
+  "&Wfr;": { "codepoints": [120090], "characters": "\uD835\uDD1A" },
+  "&Wopf;": { "codepoints": [120142], "characters": "\uD835\uDD4E" },
+  "&Wscr;": { "codepoints": [119986], "characters": "\uD835\uDCB2" },
+  "&Xfr;": { "codepoints": [120091], "characters": "\uD835\uDD1B" },
+  "&Xi;": { "codepoints": [926], "characters": "\u039E" },
+  "&Xopf;": { "codepoints": [120143], "characters": "\uD835\uDD4F" },
+  "&Xscr;": { "codepoints": [119987], "characters": "\uD835\uDCB3" },
+  "&YAcy;": { "codepoints": [1071], "characters": "\u042F" },
+  "&YIcy;": { "codepoints": [1031], "characters": "\u0407" },
+  "&YUcy;": { "codepoints": [1070], "characters": "\u042E" },
+  "&Yacute": { "codepoints": [221], "characters": "\u00DD" },
+  "&Yacute;": { "codepoints": [221], "characters": "\u00DD" },
+  "&Ycirc;": { "codepoints": [374], "characters": "\u0176" },
+  "&Ycy;": { "codepoints": [1067], "characters": "\u042B" },
+  "&Yfr;": { "codepoints": [120092], "characters": "\uD835\uDD1C" },
+  "&Yopf;": { "codepoints": [120144], "characters": "\uD835\uDD50" },
+  "&Yscr;": { "codepoints": [119988], "characters": "\uD835\uDCB4" },
+  "&Yuml;": { "codepoints": [376], "characters": "\u0178" },
+  "&ZHcy;": { "codepoints": [1046], "characters": "\u0416" },
+  "&Zacute;": { "codepoints": [377], "characters": "\u0179" },
+  "&Zcaron;": { "codepoints": [381], "characters": "\u017D" },
+  "&Zcy;": { "codepoints": [1047], "characters": "\u0417" },
+  "&Zdot;": { "codepoints": [379], "characters": "\u017B" },
+  "&ZeroWidthSpace;": { "codepoints": [8203], "characters": "\u200B" },
+  "&Zeta;": { "codepoints": [918], "characters": "\u0396" },
+  "&Zfr;": { "codepoints": [8488], "characters": "\u2128" },
+  "&Zopf;": { "codepoints": [8484], "characters": "\u2124" },
+  "&Zscr;": { "codepoints": [119989], "characters": "\uD835\uDCB5" },
+  "&aacute": { "codepoints": [225], "characters": "\u00E1" },
+  "&aacute;": { "codepoints": [225], "characters": "\u00E1" },
+  "&abreve;": { "codepoints": [259], "characters": "\u0103" },
+  "&ac;": { "codepoints": [8766], "characters": "\u223E" },
+  "&acE;": { "codepoints": [8766, 819], "characters": "\u223E\u0333" },
+  "&acd;": { "codepoints": [8767], "characters": "\u223F" },
+  "&acirc": { "codepoints": [226], "characters": "\u00E2" },
+  "&acirc;": { "codepoints": [226], "characters": "\u00E2" },
+  "&acute": { "codepoints": [180], "characters": "\u00B4" },
+  "&acute;": { "codepoints": [180], "characters": "\u00B4" },
+  "&acy;": { "codepoints": [1072], "characters": "\u0430" },
+  "&aelig": { "codepoints": [230], "characters": "\u00E6" },
+  "&aelig;": { "codepoints": [230], "characters": "\u00E6" },
+  "&af;": { "codepoints": [8289], "characters": "\u2061" },
+  "&afr;": { "codepoints": [120094], "characters": "\uD835\uDD1E" },
+  "&agrave": { "codepoints": [224], "characters": "\u00E0" },
+  "&agrave;": { "codepoints": [224], "characters": "\u00E0" },
+  "&alefsym;": { "codepoints": [8501], "characters": "\u2135" },
+  "&aleph;": { "codepoints": [8501], "characters": "\u2135" },
+  "&alpha;": { "codepoints": [945], "characters": "\u03B1" },
+  "&amacr;": { "codepoints": [257], "characters": "\u0101" },
+  "&amalg;": { "codepoints": [10815], "characters": "\u2A3F" },
+  "&amp": { "codepoints": [38], "characters": "\u0026" },
+  "&amp;": { "codepoints": [38], "characters": "\u0026" },
+  "&and;": { "codepoints": [8743], "characters": "\u2227" },
+  "&andand;": { "codepoints": [10837], "characters": "\u2A55" },
+  "&andd;": { "codepoints": [10844], "characters": "\u2A5C" },
+  "&andslope;": { "codepoints": [10840], "characters": "\u2A58" },
+  "&andv;": { "codepoints": [10842], "characters": "\u2A5A" },
+  "&ang;": { "codepoints": [8736], "characters": "\u2220" },
+  "&ange;": { "codepoints": [10660], "characters": "\u29A4" },
+  "&angle;": { "codepoints": [8736], "characters": "\u2220" },
+  "&angmsd;": { "codepoints": [8737], "characters": "\u2221" },
+  "&angmsdaa;": { "codepoints": [10664], "characters": "\u29A8" },
+  "&angmsdab;": { "codepoints": [10665], "characters": "\u29A9" },
+  "&angmsdac;": { "codepoints": [10666], "characters": "\u29AA" },
+  "&angmsdad;": { "codepoints": [10667], "characters": "\u29AB" },
+  "&angmsdae;": { "codepoints": [10668], "characters": "\u29AC" },
+  "&angmsdaf;": { "codepoints": [10669], "characters": "\u29AD" },
+  "&angmsdag;": { "codepoints": [10670], "characters": "\u29AE" },
+  "&angmsdah;": { "codepoints": [10671], "characters": "\u29AF" },
+  "&angrt;": { "codepoints": [8735], "characters": "\u221F" },
+  "&angrtvb;": { "codepoints": [8894], "characters": "\u22BE" },
+  "&angrtvbd;": { "codepoints": [10653], "characters": "\u299D" },
+  "&angsph;": { "codepoints": [8738], "characters": "\u2222" },
+  "&angst;": { "codepoints": [197], "characters": "\u00C5" },
+  "&angzarr;": { "codepoints": [9084], "characters": "\u237C" },
+  "&aogon;": { "codepoints": [261], "characters": "\u0105" },
+  "&aopf;": { "codepoints": [120146], "characters": "\uD835\uDD52" },
+  "&ap;": { "codepoints": [8776], "characters": "\u2248" },
+  "&apE;": { "codepoints": [10864], "characters": "\u2A70" },
+  "&apacir;": { "codepoints": [10863], "characters": "\u2A6F" },
+  "&ape;": { "codepoints": [8778], "characters": "\u224A" },
+  "&apid;": { "codepoints": [8779], "characters": "\u224B" },
+  "&apos;": { "codepoints": [39], "characters": "\u0027" },
+  "&approx;": { "codepoints": [8776], "characters": "\u2248" },
+  "&approxeq;": { "codepoints": [8778], "characters": "\u224A" },
+  "&aring": { "codepoints": [229], "characters": "\u00E5" },
+  "&aring;": { "codepoints": [229], "characters": "\u00E5" },
+  "&ascr;": { "codepoints": [119990], "characters": "\uD835\uDCB6" },
+  "&ast;": { "codepoints": [42], "characters": "\u002A" },
+  "&asymp;": { "codepoints": [8776], "characters": "\u2248" },
+  "&asympeq;": { "codepoints": [8781], "characters": "\u224D" },
+  "&atilde": { "codepoints": [227], "characters": "\u00E3" },
+  "&atilde;": { "codepoints": [227], "characters": "\u00E3" },
+  "&auml": { "codepoints": [228], "characters": "\u00E4" },
+  "&auml;": { "codepoints": [228], "characters": "\u00E4" },
+  "&awconint;": { "codepoints": [8755], "characters": "\u2233" },
+  "&awint;": { "codepoints": [10769], "characters": "\u2A11" },
+  "&bNot;": { "codepoints": [10989], "characters": "\u2AED" },
+  "&backcong;": { "codepoints": [8780], "characters": "\u224C" },
+  "&backepsilon;": { "codepoints": [1014], "characters": "\u03F6" },
+  "&backprime;": { "codepoints": [8245], "characters": "\u2035" },
+  "&backsim;": { "codepoints": [8765], "characters": "\u223D" },
+  "&backsimeq;": { "codepoints": [8909], "characters": "\u22CD" },
+  "&barvee;": { "codepoints": [8893], "characters": "\u22BD" },
+  "&barwed;": { "codepoints": [8965], "characters": "\u2305" },
+  "&barwedge;": { "codepoints": [8965], "characters": "\u2305" },
+  "&bbrk;": { "codepoints": [9141], "characters": "\u23B5" },
+  "&bbrktbrk;": { "codepoints": [9142], "characters": "\u23B6" },
+  "&bcong;": { "codepoints": [8780], "characters": "\u224C" },
+  "&bcy;": { "codepoints": [1073], "characters": "\u0431" },
+  "&bdquo;": { "codepoints": [8222], "characters": "\u201E" },
+  "&becaus;": { "codepoints": [8757], "characters": "\u2235" },
+  "&because;": { "codepoints": [8757], "characters": "\u2235" },
+  "&bemptyv;": { "codepoints": [10672], "characters": "\u29B0" },
+  "&bepsi;": { "codepoints": [1014], "characters": "\u03F6" },
+  "&bernou;": { "codepoints": [8492], "characters": "\u212C" },
+  "&beta;": { "codepoints": [946], "characters": "\u03B2" },
+  "&beth;": { "codepoints": [8502], "characters": "\u2136" },
+  "&between;": { "codepoints": [8812], "characters": "\u226C" },
+  "&bfr;": { "codepoints": [120095], "characters": "\uD835\uDD1F" },
+  "&bigcap;": { "codepoints": [8898], "characters": "\u22C2" },
+  "&bigcirc;": { "codepoints": [9711], "characters": "\u25EF" },
+  "&bigcup;": { "codepoints": [8899], "characters": "\u22C3" },
+  "&bigodot;": { "codepoints": [10752], "characters": "\u2A00" },
+  "&bigoplus;": { "codepoints": [10753], "characters": "\u2A01" },
+  "&bigotimes;": { "codepoints": [10754], "characters": "\u2A02" },
+  "&bigsqcup;": { "codepoints": [10758], "characters": "\u2A06" },
+  "&bigstar;": { "codepoints": [9733], "characters": "\u2605" },
+  "&bigtriangledown;": { "codepoints": [9661], "characters": "\u25BD" },
+  "&bigtriangleup;": { "codepoints": [9651], "characters": "\u25B3" },
+  "&biguplus;": { "codepoints": [10756], "characters": "\u2A04" },
+  "&bigvee;": { "codepoints": [8897], "characters": "\u22C1" },
+  "&bigwedge;": { "codepoints": [8896], "characters": "\u22C0" },
+  "&bkarow;": { "codepoints": [10509], "characters": "\u290D" },
+  "&blacklozenge;": { "codepoints": [10731], "characters": "\u29EB" },
+  "&blacksquare;": { "codepoints": [9642], "characters": "\u25AA" },
+  "&blacktriangle;": { "codepoints": [9652], "characters": "\u25B4" },
+  "&blacktriangledown;": { "codepoints": [9662], "characters": "\u25BE" },
+  "&blacktriangleleft;": { "codepoints": [9666], "characters": "\u25C2" },
+  "&blacktriangleright;": { "codepoints": [9656], "characters": "\u25B8" },
+  "&blank;": { "codepoints": [9251], "characters": "\u2423" },
+  "&blk12;": { "codepoints": [9618], "characters": "\u2592" },
+  "&blk14;": { "codepoints": [9617], "characters": "\u2591" },
+  "&blk34;": { "codepoints": [9619], "characters": "\u2593" },
+  "&block;": { "codepoints": [9608], "characters": "\u2588" },
+  "&bne;": { "codepoints": [61, 8421], "characters": "\u003D\u20E5" },
+  "&bnequiv;": { "codepoints": [8801, 8421], "characters": "\u2261\u20E5" },
+  "&bnot;": { "codepoints": [8976], "characters": "\u2310" },
+  "&bopf;": { "codepoints": [120147], "characters": "\uD835\uDD53" },
+  "&bot;": { "codepoints": [8869], "characters": "\u22A5" },
+  "&bottom;": { "codepoints": [8869], "characters": "\u22A5" },
+  "&bowtie;": { "codepoints": [8904], "characters": "\u22C8" },
+  "&boxDL;": { "codepoints": [9559], "characters": "\u2557" },
+  "&boxDR;": { "codepoints": [9556], "characters": "\u2554" },
+  "&boxDl;": { "codepoints": [9558], "characters": "\u2556" },
+  "&boxDr;": { "codepoints": [9555], "characters": "\u2553" },
+  "&boxH;": { "codepoints": [9552], "characters": "\u2550" },
+  "&boxHD;": { "codepoints": [9574], "characters": "\u2566" },
+  "&boxHU;": { "codepoints": [9577], "characters": "\u2569" },
+  "&boxHd;": { "codepoints": [9572], "characters": "\u2564" },
+  "&boxHu;": { "codepoints": [9575], "characters": "\u2567" },
+  "&boxUL;": { "codepoints": [9565], "characters": "\u255D" },
+  "&boxUR;": { "codepoints": [9562], "characters": "\u255A" },
+  "&boxUl;": { "codepoints": [9564], "characters": "\u255C" },
+  "&boxUr;": { "codepoints": [9561], "characters": "\u2559" },
+  "&boxV;": { "codepoints": [9553], "characters": "\u2551" },
+  "&boxVH;": { "codepoints": [9580], "characters": "\u256C" },
+  "&boxVL;": { "codepoints": [9571], "characters": "\u2563" },
+  "&boxVR;": { "codepoints": [9568], "characters": "\u2560" },
+  "&boxVh;": { "codepoints": [9579], "characters": "\u256B" },
+  "&boxVl;": { "codepoints": [9570], "characters": "\u2562" },
+  "&boxVr;": { "codepoints": [9567], "characters": "\u255F" },
+  "&boxbox;": { "codepoints": [10697], "characters": "\u29C9" },
+  "&boxdL;": { "codepoints": [9557], "characters": "\u2555" },
+  "&boxdR;": { "codepoints": [9554], "characters": "\u2552" },
+  "&boxdl;": { "codepoints": [9488], "characters": "\u2510" },
+  "&boxdr;": { "codepoints": [9484], "characters": "\u250C" },
+  "&boxh;": { "codepoints": [9472], "characters": "\u2500" },
+  "&boxhD;": { "codepoints": [9573], "characters": "\u2565" },
+  "&boxhU;": { "codepoints": [9576], "characters": "\u2568" },
+  "&boxhd;": { "codepoints": [9516], "characters": "\u252C" },
+  "&boxhu;": { "codepoints": [9524], "characters": "\u2534" },
+  "&boxminus;": { "codepoints": [8863], "characters": "\u229F" },
+  "&boxplus;": { "codepoints": [8862], "characters": "\u229E" },
+  "&boxtimes;": { "codepoints": [8864], "characters": "\u22A0" },
+  "&boxuL;": { "codepoints": [9563], "characters": "\u255B" },
+  "&boxuR;": { "codepoints": [9560], "characters": "\u2558" },
+  "&boxul;": { "codepoints": [9496], "characters": "\u2518" },
+  "&boxur;": { "codepoints": [9492], "characters": "\u2514" },
+  "&boxv;": { "codepoints": [9474], "characters": "\u2502" },
+  "&boxvH;": { "codepoints": [9578], "characters": "\u256A" },
+  "&boxvL;": { "codepoints": [9569], "characters": "\u2561" },
+  "&boxvR;": { "codepoints": [9566], "characters": "\u255E" },
+  "&boxvh;": { "codepoints": [9532], "characters": "\u253C" },
+  "&boxvl;": { "codepoints": [9508], "characters": "\u2524" },
+  "&boxvr;": { "codepoints": [9500], "characters": "\u251C" },
+  "&bprime;": { "codepoints": [8245], "characters": "\u2035" },
+  "&breve;": { "codepoints": [728], "characters": "\u02D8" },
+  "&brvbar": { "codepoints": [166], "characters": "\u00A6" },
+  "&brvbar;": { "codepoints": [166], "characters": "\u00A6" },
+  "&bscr;": { "codepoints": [119991], "characters": "\uD835\uDCB7" },
+  "&bsemi;": { "codepoints": [8271], "characters": "\u204F" },
+  "&bsim;": { "codepoints": [8765], "characters": "\u223D" },
+  "&bsime;": { "codepoints": [8909], "characters": "\u22CD" },
+  "&bsol;": { "codepoints": [92], "characters": "\u005C" },
+  "&bsolb;": { "codepoints": [10693], "characters": "\u29C5" },
+  "&bsolhsub;": { "codepoints": [10184], "characters": "\u27C8" },
+  "&bull;": { "codepoints": [8226], "characters": "\u2022" },
+  "&bullet;": { "codepoints": [8226], "characters": "\u2022" },
+  "&bump;": { "codepoints": [8782], "characters": "\u224E" },
+  "&bumpE;": { "codepoints": [10926], "characters": "\u2AAE" },
+  "&bumpe;": { "codepoints": [8783], "characters": "\u224F" },
+  "&bumpeq;": { "codepoints": [8783], "characters": "\u224F" },
+  "&cacute;": { "codepoints": [263], "characters": "\u0107" },
+  "&cap;": { "codepoints": [8745], "characters": "\u2229" },
+  "&capand;": { "codepoints": [10820], "characters": "\u2A44" },
+  "&capbrcup;": { "codepoints": [10825], "characters": "\u2A49" },
+  "&capcap;": { "codepoints": [10827], "characters": "\u2A4B" },
+  "&capcup;": { "codepoints": [10823], "characters": "\u2A47" },
+  "&capdot;": { "codepoints": [10816], "characters": "\u2A40" },
+  "&caps;": { "codepoints": [8745, 65024], "characters": "\u2229\uFE00" },
+  "&caret;": { "codepoints": [8257], "characters": "\u2041" },
+  "&caron;": { "codepoints": [711], "characters": "\u02C7" },
+  "&ccaps;": { "codepoints": [10829], "characters": "\u2A4D" },
+  "&ccaron;": { "codepoints": [269], "characters": "\u010D" },
+  "&ccedil": { "codepoints": [231], "characters": "\u00E7" },
+  "&ccedil;": { "codepoints": [231], "characters": "\u00E7" },
+  "&ccirc;": { "codepoints": [265], "characters": "\u0109" },
+  "&ccups;": { "codepoints": [10828], "characters": "\u2A4C" },
+  "&ccupssm;": { "codepoints": [10832], "characters": "\u2A50" },
+  "&cdot;": { "codepoints": [267], "characters": "\u010B" },
+  "&cedil": { "codepoints": [184], "characters": "\u00B8" },
+  "&cedil;": { "codepoints": [184], "characters": "\u00B8" },
+  "&cemptyv;": { "codepoints": [10674], "characters": "\u29B2" },
+  "&cent": { "codepoints": [162], "characters": "\u00A2" },
+  "&cent;": { "codepoints": [162], "characters": "\u00A2" },
+  "&centerdot;": { "codepoints": [183], "characters": "\u00B7" },
+  "&cfr;": { "codepoints": [120096], "characters": "\uD835\uDD20" },
+  "&chcy;": { "codepoints": [1095], "characters": "\u0447" },
+  "&check;": { "codepoints": [10003], "characters": "\u2713" },
+  "&checkmark;": { "codepoints": [10003], "characters": "\u2713" },
+  "&chi;": { "codepoints": [967], "characters": "\u03C7" },
+  "&cir;": { "codepoints": [9675], "characters": "\u25CB" },
+  "&cirE;": { "codepoints": [10691], "characters": "\u29C3" },
+  "&circ;": { "codepoints": [710], "characters": "\u02C6" },
+  "&circeq;": { "codepoints": [8791], "characters": "\u2257" },
+  "&circlearrowleft;": { "codepoints": [8634], "characters": "\u21BA" },
+  "&circlearrowright;": { "codepoints": [8635], "characters": "\u21BB" },
+  "&circledR;": { "codepoints": [174], "characters": "\u00AE" },
+  "&circledS;": { "codepoints": [9416], "characters": "\u24C8" },
+  "&circledast;": { "codepoints": [8859], "characters": "\u229B" },
+  "&circledcirc;": { "codepoints": [8858], "characters": "\u229A" },
+  "&circleddash;": { "codepoints": [8861], "characters": "\u229D" },
+  "&cire;": { "codepoints": [8791], "characters": "\u2257" },
+  "&cirfnint;": { "codepoints": [10768], "characters": "\u2A10" },
+  "&cirmid;": { "codepoints": [10991], "characters": "\u2AEF" },
+  "&cirscir;": { "codepoints": [10690], "characters": "\u29C2" },
+  "&clubs;": { "codepoints": [9827], "characters": "\u2663" },
+  "&clubsuit;": { "codepoints": [9827], "characters": "\u2663" },
+  "&colon;": { "codepoints": [58], "characters": "\u003A" },
+  "&colone;": { "codepoints": [8788], "characters": "\u2254" },
+  "&coloneq;": { "codepoints": [8788], "characters": "\u2254" },
+  "&comma;": { "codepoints": [44], "characters": "\u002C" },
+  "&commat;": { "codepoints": [64], "characters": "\u0040" },
+  "&comp;": { "codepoints": [8705], "characters": "\u2201" },
+  "&compfn;": { "codepoints": [8728], "characters": "\u2218" },
+  "&complement;": { "codepoints": [8705], "characters": "\u2201" },
+  "&complexes;": { "codepoints": [8450], "characters": "\u2102" },
+  "&cong;": { "codepoints": [8773], "characters": "\u2245" },
+  "&congdot;": { "codepoints": [10861], "characters": "\u2A6D" },
+  "&conint;": { "codepoints": [8750], "characters": "\u222E" },
+  "&copf;": { "codepoints": [120148], "characters": "\uD835\uDD54" },
+  "&coprod;": { "codepoints": [8720], "characters": "\u2210" },
+  "&copy": { "codepoints": [169], "characters": "\u00A9" },
+  "&copy;": { "codepoints": [169], "characters": "\u00A9" },
+  "&copysr;": { "codepoints": [8471], "characters": "\u2117" },
+  "&crarr;": { "codepoints": [8629], "characters": "\u21B5" },
+  "&cross;": { "codepoints": [10007], "characters": "\u2717" },
+  "&cscr;": { "codepoints": [119992], "characters": "\uD835\uDCB8" },
+  "&csub;": { "codepoints": [10959], "characters": "\u2ACF" },
+  "&csube;": { "codepoints": [10961], "characters": "\u2AD1" },
+  "&csup;": { "codepoints": [10960], "characters": "\u2AD0" },
+  "&csupe;": { "codepoints": [10962], "characters": "\u2AD2" },
+  "&ctdot;": { "codepoints": [8943], "characters": "\u22EF" },
+  "&cudarrl;": { "codepoints": [10552], "characters": "\u2938" },
+  "&cudarrr;": { "codepoints": [10549], "characters": "\u2935" },
+  "&cuepr;": { "codepoints": [8926], "characters": "\u22DE" },
+  "&cuesc;": { "codepoints": [8927], "characters": "\u22DF" },
+  "&cularr;": { "codepoints": [8630], "characters": "\u21B6" },
+  "&cularrp;": { "codepoints": [10557], "characters": "\u293D" },
+  "&cup;": { "codepoints": [8746], "characters": "\u222A" },
+  "&cupbrcap;": { "codepoints": [10824], "characters": "\u2A48" },
+  "&cupcap;": { "codepoints": [10822], "characters": "\u2A46" },
+  "&cupcup;": { "codepoints": [10826], "characters": "\u2A4A" },
+  "&cupdot;": { "codepoints": [8845], "characters": "\u228D" },
+  "&cupor;": { "codepoints": [10821], "characters": "\u2A45" },
+  "&cups;": { "codepoints": [8746, 65024], "characters": "\u222A\uFE00" },
+  "&curarr;": { "codepoints": [8631], "characters": "\u21B7" },
+  "&curarrm;": { "codepoints": [10556], "characters": "\u293C" },
+  "&curlyeqprec;": { "codepoints": [8926], "characters": "\u22DE" },
+  "&curlyeqsucc;": { "codepoints": [8927], "characters": "\u22DF" },
+  "&curlyvee;": { "codepoints": [8910], "characters": "\u22CE" },
+  "&curlywedge;": { "codepoints": [8911], "characters": "\u22CF" },
+  "&curren": { "codepoints": [164], "characters": "\u00A4" },
+  "&curren;": { "codepoints": [164], "characters": "\u00A4" },
+  "&curvearrowleft;": { "codepoints": [8630], "characters": "\u21B6" },
+  "&curvearrowright;": { "codepoints": [8631], "characters": "\u21B7" },
+  "&cuvee;": { "codepoints": [8910], "characters": "\u22CE" },
+  "&cuwed;": { "codepoints": [8911], "characters": "\u22CF" },
+  "&cwconint;": { "codepoints": [8754], "characters": "\u2232" },
+  "&cwint;": { "codepoints": [8753], "characters": "\u2231" },
+  "&cylcty;": { "codepoints": [9005], "characters": "\u232D" },
+  "&dArr;": { "codepoints": [8659], "characters": "\u21D3" },
+  "&dHar;": { "codepoints": [10597], "characters": "\u2965" },
+  "&dagger;": { "codepoints": [8224], "characters": "\u2020" },
+  "&daleth;": { "codepoints": [8504], "characters": "\u2138" },
+  "&darr;": { "codepoints": [8595], "characters": "\u2193" },
+  "&dash;": { "codepoints": [8208], "characters": "\u2010" },
+  "&dashv;": { "codepoints": [8867], "characters": "\u22A3" },
+  "&dbkarow;": { "codepoints": [10511], "characters": "\u290F" },
+  "&dblac;": { "codepoints": [733], "characters": "\u02DD" },
+  "&dcaron;": { "codepoints": [271], "characters": "\u010F" },
+  "&dcy;": { "codepoints": [1076], "characters": "\u0434" },
+  "&dd;": { "codepoints": [8518], "characters": "\u2146" },
+  "&ddagger;": { "codepoints": [8225], "characters": "\u2021" },
+  "&ddarr;": { "codepoints": [8650], "characters": "\u21CA" },
+  "&ddotseq;": { "codepoints": [10871], "characters": "\u2A77" },
+  "&deg": { "codepoints": [176], "characters": "\u00B0" },
+  "&deg;": { "codepoints": [176], "characters": "\u00B0" },
+  "&delta;": { "codepoints": [948], "characters": "\u03B4" },
+  "&demptyv;": { "codepoints": [10673], "characters": "\u29B1" },
+  "&dfisht;": { "codepoints": [10623], "characters": "\u297F" },
+  "&dfr;": { "codepoints": [120097], "characters": "\uD835\uDD21" },
+  "&dharl;": { "codepoints": [8643], "characters": "\u21C3" },
+  "&dharr;": { "codepoints": [8642], "characters": "\u21C2" },
+  "&diam;": { "codepoints": [8900], "characters": "\u22C4" },
+  "&diamond;": { "codepoints": [8900], "characters": "\u22C4" },
+  "&diamondsuit;": { "codepoints": [9830], "characters": "\u2666" },
+  "&diams;": { "codepoints": [9830], "characters": "\u2666" },
+  "&die;": { "codepoints": [168], "characters": "\u00A8" },
+  "&digamma;": { "codepoints": [989], "characters": "\u03DD" },
+  "&disin;": { "codepoints": [8946], "characters": "\u22F2" },
+  "&div;": { "codepoints": [247], "characters": "\u00F7" },
+  "&divide": { "codepoints": [247], "characters": "\u00F7" },
+  "&divide;": { "codepoints": [247], "characters": "\u00F7" },
+  "&divideontimes;": { "codepoints": [8903], "characters": "\u22C7" },
+  "&divonx;": { "codepoints": [8903], "characters": "\u22C7" },
+  "&djcy;": { "codepoints": [1106], "characters": "\u0452" },
+  "&dlcorn;": { "codepoints": [8990], "characters": "\u231E" },
+  "&dlcrop;": { "codepoints": [8973], "characters": "\u230D" },
+  "&dollar;": { "codepoints": [36], "characters": "\u0024" },
+  "&dopf;": { "codepoints": [120149], "characters": "\uD835\uDD55" },
+  "&dot;": { "codepoints": [729], "characters": "\u02D9" },
+  "&doteq;": { "codepoints": [8784], "characters": "\u2250" },
+  "&doteqdot;": { "codepoints": [8785], "characters": "\u2251" },
+  "&dotminus;": { "codepoints": [8760], "characters": "\u2238" },
+  "&dotplus;": { "codepoints": [8724], "characters": "\u2214" },
+  "&dotsquare;": { "codepoints": [8865], "characters": "\u22A1" },
+  "&doublebarwedge;": { "codepoints": [8966], "characters": "\u2306" },
+  "&downarrow;": { "codepoints": [8595], "characters": "\u2193" },
+  "&downdownarrows;": { "codepoints": [8650], "characters": "\u21CA" },
+  "&downharpoonleft;": { "codepoints": [8643], "characters": "\u21C3" },
+  "&downharpoonright;": { "codepoints": [8642], "characters": "\u21C2" },
+  "&drbkarow;": { "codepoints": [10512], "characters": "\u2910" },
+  "&drcorn;": { "codepoints": [8991], "characters": "\u231F" },
+  "&drcrop;": { "codepoints": [8972], "characters": "\u230C" },
+  "&dscr;": { "codepoints": [119993], "characters": "\uD835\uDCB9" },
+  "&dscy;": { "codepoints": [1109], "characters": "\u0455" },
+  "&dsol;": { "codepoints": [10742], "characters": "\u29F6" },
+  "&dstrok;": { "codepoints": [273], "characters": "\u0111" },
+  "&dtdot;": { "codepoints": [8945], "characters": "\u22F1" },
+  "&dtri;": { "codepoints": [9663], "characters": "\u25BF" },
+  "&dtrif;": { "codepoints": [9662], "characters": "\u25BE" },
+  "&duarr;": { "codepoints": [8693], "characters": "\u21F5" },
+  "&duhar;": { "codepoints": [10607], "characters": "\u296F" },
+  "&dwangle;": { "codepoints": [10662], "characters": "\u29A6" },
+  "&dzcy;": { "codepoints": [1119], "characters": "\u045F" },
+  "&dzigrarr;": { "codepoints": [10239], "characters": "\u27FF" },
+  "&eDDot;": { "codepoints": [10871], "characters": "\u2A77" },
+  "&eDot;": { "codepoints": [8785], "characters": "\u2251" },
+  "&eacute": { "codepoints": [233], "characters": "\u00E9" },
+  "&eacute;": { "codepoints": [233], "characters": "\u00E9" },
+  "&easter;": { "codepoints": [10862], "characters": "\u2A6E" },
+  "&ecaron;": { "codepoints": [283], "characters": "\u011B" },
+  "&ecir;": { "codepoints": [8790], "characters": "\u2256" },
+  "&ecirc": { "codepoints": [234], "characters": "\u00EA" },
+  "&ecirc;": { "codepoints": [234], "characters": "\u00EA" },
+  "&ecolon;": { "codepoints": [8789], "characters": "\u2255" },
+  "&ecy;": { "codepoints": [1101], "characters": "\u044D" },
+  "&edot;": { "codepoints": [279], "characters": "\u0117" },
+  "&ee;": { "codepoints": [8519], "characters": "\u2147" },
+  "&efDot;": { "codepoints": [8786], "characters": "\u2252" },
+  "&efr;": { "codepoints": [120098], "characters": "\uD835\uDD22" },
+  "&eg;": { "codepoints": [10906], "characters": "\u2A9A" },
+  "&egrave": { "codepoints": [232], "characters": "\u00E8" },
+  "&egrave;": { "codepoints": [232], "characters": "\u00E8" },
+  "&egs;": { "codepoints": [10902], "characters": "\u2A96" },
+  "&egsdot;": { "codepoints": [10904], "characters": "\u2A98" },
+  "&el;": { "codepoints": [10905], "characters": "\u2A99" },
+  "&elinters;": { "codepoints": [9191], "characters": "\u23E7" },
+  "&ell;": { "codepoints": [8467], "characters": "\u2113" },
+  "&els;": { "codepoints": [10901], "characters": "\u2A95" },
+  "&elsdot;": { "codepoints": [10903], "characters": "\u2A97" },
+  "&emacr;": { "codepoints": [275], "characters": "\u0113" },
+  "&empty;": { "codepoints": [8709], "characters": "\u2205" },
+  "&emptyset;": { "codepoints": [8709], "characters": "\u2205" },
+  "&emptyv;": { "codepoints": [8709], "characters": "\u2205" },
+  "&emsp13;": { "codepoints": [8196], "characters": "\u2004" },
+  "&emsp14;": { "codepoints": [8197], "characters": "\u2005" },
+  "&emsp;": { "codepoints": [8195], "characters": "\u2003" },
+  "&eng;": { "codepoints": [331], "characters": "\u014B" },
+  "&ensp;": { "codepoints": [8194], "characters": "\u2002" },
+  "&eogon;": { "codepoints": [281], "characters": "\u0119" },
+  "&eopf;": { "codepoints": [120150], "characters": "\uD835\uDD56" },
+  "&epar;": { "codepoints": [8917], "characters": "\u22D5" },
+  "&eparsl;": { "codepoints": [10723], "characters": "\u29E3" },
+  "&eplus;": { "codepoints": [10865], "characters": "\u2A71" },
+  "&epsi;": { "codepoints": [949], "characters": "\u03B5" },
+  "&epsilon;": { "codepoints": [949], "characters": "\u03B5" },
+  "&epsiv;": { "codepoints": [1013], "characters": "\u03F5" },
+  "&eqcirc;": { "codepoints": [8790], "characters": "\u2256" },
+  "&eqcolon;": { "codepoints": [8789], "characters": "\u2255" },
+  "&eqsim;": { "codepoints": [8770], "characters": "\u2242" },
+  "&eqslantgtr;": { "codepoints": [10902], "characters": "\u2A96" },
+  "&eqslantless;": { "codepoints": [10901], "characters": "\u2A95" },
+  "&equals;": { "codepoints": [61], "characters": "\u003D" },
+  "&equest;": { "codepoints": [8799], "characters": "\u225F" },
+  "&equiv;": { "codepoints": [8801], "characters": "\u2261" },
+  "&equivDD;": { "codepoints": [10872], "characters": "\u2A78" },
+  "&eqvparsl;": { "codepoints": [10725], "characters": "\u29E5" },
+  "&erDot;": { "codepoints": [8787], "characters": "\u2253" },
+  "&erarr;": { "codepoints": [10609], "characters": "\u2971" },
+  "&escr;": { "codepoints": [8495], "characters": "\u212F" },
+  "&esdot;": { "codepoints": [8784], "characters": "\u2250" },
+  "&esim;": { "codepoints": [8770], "characters": "\u2242" },
+  "&eta;": { "codepoints": [951], "characters": "\u03B7" },
+  "&eth": { "codepoints": [240], "characters": "\u00F0" },
+  "&eth;": { "codepoints": [240], "characters": "\u00F0" },
+  "&euml": { "codepoints": [235], "characters": "\u00EB" },
+  "&euml;": { "codepoints": [235], "characters": "\u00EB" },
+  "&euro;": { "codepoints": [8364], "characters": "\u20AC" },
+  "&excl;": { "codepoints": [33], "characters": "\u0021" },
+  "&exist;": { "codepoints": [8707], "characters": "\u2203" },
+  "&expectation;": { "codepoints": [8496], "characters": "\u2130" },
+  "&exponentiale;": { "codepoints": [8519], "characters": "\u2147" },
+  "&fallingdotseq;": { "codepoints": [8786], "characters": "\u2252" },
+  "&fcy;": { "codepoints": [1092], "characters": "\u0444" },
+  "&female;": { "codepoints": [9792], "characters": "\u2640" },
+  "&ffilig;": { "codepoints": [64259], "characters": "\uFB03" },
+  "&fflig;": { "codepoints": [64256], "characters": "\uFB00" },
+  "&ffllig;": { "codepoints": [64260], "characters": "\uFB04" },
+  "&ffr;": { "codepoints": [120099], "characters": "\uD835\uDD23" },
+  "&filig;": { "codepoints": [64257], "characters": "\uFB01" },
+  "&fjlig;": { "codepoints": [102, 106], "characters": "\u0066\u006A" },
+  "&flat;": { "codepoints": [9837], "characters": "\u266D" },
+  "&fllig;": { "codepoints": [64258], "characters": "\uFB02" },
+  "&fltns;": { "codepoints": [9649], "characters": "\u25B1" },
+  "&fnof;": { "codepoints": [402], "characters": "\u0192" },
+  "&fopf;": { "codepoints": [120151], "characters": "\uD835\uDD57" },
+  "&forall;": { "codepoints": [8704], "characters": "\u2200" },
+  "&fork;": { "codepoints": [8916], "characters": "\u22D4" },
+  "&forkv;": { "codepoints": [10969], "characters": "\u2AD9" },
+  "&fpartint;": { "codepoints": [10765], "characters": "\u2A0D" },
+  "&frac12": { "codepoints": [189], "characters": "\u00BD" },
+  "&frac12;": { "codepoints": [189], "characters": "\u00BD" },
+  "&frac13;": { "codepoints": [8531], "characters": "\u2153" },
+  "&frac14": { "codepoints": [188], "characters": "\u00BC" },
+  "&frac14;": { "codepoints": [188], "characters": "\u00BC" },
+  "&frac15;": { "codepoints": [8533], "characters": "\u2155" },
+  "&frac16;": { "codepoints": [8537], "characters": "\u2159" },
+  "&frac18;": { "codepoints": [8539], "characters": "\u215B" },
+  "&frac23;": { "codepoints": [8532], "characters": "\u2154" },
+  "&frac25;": { "codepoints": [8534], "characters": "\u2156" },
+  "&frac34": { "codepoints": [190], "characters": "\u00BE" },
+  "&frac34;": { "codepoints": [190], "characters": "\u00BE" },
+  "&frac35;": { "codepoints": [8535], "characters": "\u2157" },
+  "&frac38;": { "codepoints": [8540], "characters": "\u215C" },
+  "&frac45;": { "codepoints": [8536], "characters": "\u2158" },
+  "&frac56;": { "codepoints": [8538], "characters": "\u215A" },
+  "&frac58;": { "codepoints": [8541], "characters": "\u215D" },
+  "&frac78;": { "codepoints": [8542], "characters": "\u215E" },
+  "&frasl;": { "codepoints": [8260], "characters": "\u2044" },
+  "&frown;": { "codepoints": [8994], "characters": "\u2322" },
+  "&fscr;": { "codepoints": [119995], "characters": "\uD835\uDCBB" },
+  "&gE;": { "codepoints": [8807], "characters": "\u2267" },
+  "&gEl;": { "codepoints": [10892], "characters": "\u2A8C" },
+  "&gacute;": { "codepoints": [501], "characters": "\u01F5" },
+  "&gamma;": { "codepoints": [947], "characters": "\u03B3" },
+  "&gammad;": { "codepoints": [989], "characters": "\u03DD" },
+  "&gap;": { "codepoints": [10886], "characters": "\u2A86" },
+  "&gbreve;": { "codepoints": [287], "characters": "\u011F" },
+  "&gcirc;": { "codepoints": [285], "characters": "\u011D" },
+  "&gcy;": { "codepoints": [1075], "characters": "\u0433" },
+  "&gdot;": { "codepoints": [289], "characters": "\u0121" },
+  "&ge;": { "codepoints": [8805], "characters": "\u2265" },
+  "&gel;": { "codepoints": [8923], "characters": "\u22DB" },
+  "&geq;": { "codepoints": [8805], "characters": "\u2265" },
+  "&geqq;": { "codepoints": [8807], "characters": "\u2267" },
+  "&geqslant;": { "codepoints": [10878], "characters": "\u2A7E" },
+  "&ges;": { "codepoints": [10878], "characters": "\u2A7E" },
+  "&gescc;": { "codepoints": [10921], "characters": "\u2AA9" },
+  "&gesdot;": { "codepoints": [10880], "characters": "\u2A80" },
+  "&gesdoto;": { "codepoints": [10882], "characters": "\u2A82" },
+  "&gesdotol;": { "codepoints": [10884], "characters": "\u2A84" },
+  "&gesl;": { "codepoints": [8923, 65024], "characters": "\u22DB\uFE00" },
+  "&gesles;": { "codepoints": [10900], "characters": "\u2A94" },
+  "&gfr;": { "codepoints": [120100], "characters": "\uD835\uDD24" },
+  "&gg;": { "codepoints": [8811], "characters": "\u226B" },
+  "&ggg;": { "codepoints": [8921], "characters": "\u22D9" },
+  "&gimel;": { "codepoints": [8503], "characters": "\u2137" },
+  "&gjcy;": { "codepoints": [1107], "characters": "\u0453" },
+  "&gl;": { "codepoints": [8823], "characters": "\u2277" },
+  "&glE;": { "codepoints": [10898], "characters": "\u2A92" },
+  "&gla;": { "codepoints": [10917], "characters": "\u2AA5" },
+  "&glj;": { "codepoints": [10916], "characters": "\u2AA4" },
+  "&gnE;": { "codepoints": [8809], "characters": "\u2269" },
+  "&gnap;": { "codepoints": [10890], "characters": "\u2A8A" },
+  "&gnapprox;": { "codepoints": [10890], "characters": "\u2A8A" },
+  "&gne;": { "codepoints": [10888], "characters": "\u2A88" },
+  "&gneq;": { "codepoints": [10888], "characters": "\u2A88" },
+  "&gneqq;": { "codepoints": [8809], "characters": "\u2269" },
+  "&gnsim;": { "codepoints": [8935], "characters": "\u22E7" },
+  "&gopf;": { "codepoints": [120152], "characters": "\uD835\uDD58" },
+  "&grave;": { "codepoints": [96], "characters": "\u0060" },
+  "&gscr;": { "codepoints": [8458], "characters": "\u210A" },
+  "&gsim;": { "codepoints": [8819], "characters": "\u2273" },
+  "&gsime;": { "codepoints": [10894], "characters": "\u2A8E" },
+  "&gsiml;": { "codepoints": [10896], "characters": "\u2A90" },
+  "&gt": { "codepoints": [62], "characters": "\u003E" },
+  "&gt;": { "codepoints": [62], "characters": "\u003E" },
+  "&gtcc;": { "codepoints": [10919], "characters": "\u2AA7" },
+  "&gtcir;": { "codepoints": [10874], "characters": "\u2A7A" },
+  "&gtdot;": { "codepoints": [8919], "characters": "\u22D7" },
+  "&gtlPar;": { "codepoints": [10645], "characters": "\u2995" },
+  "&gtquest;": { "codepoints": [10876], "characters": "\u2A7C" },
+  "&gtrapprox;": { "codepoints": [10886], "characters": "\u2A86" },
+  "&gtrarr;": { "codepoints": [10616], "characters": "\u2978" },
+  "&gtrdot;": { "codepoints": [8919], "characters": "\u22D7" },
+  "&gtreqless;": { "codepoints": [8923], "characters": "\u22DB" },
+  "&gtreqqless;": { "codepoints": [10892], "characters": "\u2A8C" },
+  "&gtrless;": { "codepoints": [8823], "characters": "\u2277" },
+  "&gtrsim;": { "codepoints": [8819], "characters": "\u2273" },
+  "&gvertneqq;": { "codepoints": [8809, 65024], "characters": "\u2269\uFE00" },
+  "&gvnE;": { "codepoints": [8809, 65024], "characters": "\u2269\uFE00" },
+  "&hArr;": { "codepoints": [8660], "characters": "\u21D4" },
+  "&hairsp;": { "codepoints": [8202], "characters": "\u200A" },
+  "&half;": { "codepoints": [189], "characters": "\u00BD" },
+  "&hamilt;": { "codepoints": [8459], "characters": "\u210B" },
+  "&hardcy;": { "codepoints": [1098], "characters": "\u044A" },
+  "&harr;": { "codepoints": [8596], "characters": "\u2194" },
+  "&harrcir;": { "codepoints": [10568], "characters": "\u2948" },
+  "&harrw;": { "codepoints": [8621], "characters": "\u21AD" },
+  "&hbar;": { "codepoints": [8463], "characters": "\u210F" },
+  "&hcirc;": { "codepoints": [293], "characters": "\u0125" },
+  "&hearts;": { "codepoints": [9829], "characters": "\u2665" },
+  "&heartsuit;": { "codepoints": [9829], "characters": "\u2665" },
+  "&hellip;": { "codepoints": [8230], "characters": "\u2026" },
+  "&hercon;": { "codepoints": [8889], "characters": "\u22B9" },
+  "&hfr;": { "codepoints": [120101], "characters": "\uD835\uDD25" },
+  "&hksearow;": { "codepoints": [10533], "characters": "\u2925" },
+  "&hkswarow;": { "codepoints": [10534], "characters": "\u2926" },
+  "&hoarr;": { "codepoints": [8703], "characters": "\u21FF" },
+  "&homtht;": { "codepoints": [8763], "characters": "\u223B" },
+  "&hookleftarrow;": { "codepoints": [8617], "characters": "\u21A9" },
+  "&hookrightarrow;": { "codepoints": [8618], "characters": "\u21AA" },
+  "&hopf;": { "codepoints": [120153], "characters": "\uD835\uDD59" },
+  "&horbar;": { "codepoints": [8213], "characters": "\u2015" },
+  "&hscr;": { "codepoints": [119997], "characters": "\uD835\uDCBD" },
+  "&hslash;": { "codepoints": [8463], "characters": "\u210F" },
+  "&hstrok;": { "codepoints": [295], "characters": "\u0127" },
+  "&hybull;": { "codepoints": [8259], "characters": "\u2043" },
+  "&hyphen;": { "codepoints": [8208], "characters": "\u2010" },
+  "&iacute": { "codepoints": [237], "characters": "\u00ED" },
+  "&iacute;": { "codepoints": [237], "characters": "\u00ED" },
+  "&ic;": { "codepoints": [8291], "characters": "\u2063" },
+  "&icirc": { "codepoints": [238], "characters": "\u00EE" },
+  "&icirc;": { "codepoints": [238], "characters": "\u00EE" },
+  "&icy;": { "codepoints": [1080], "characters": "\u0438" },
+  "&iecy;": { "codepoints": [1077], "characters": "\u0435" },
+  "&iexcl": { "codepoints": [161], "characters": "\u00A1" },
+  "&iexcl;": { "codepoints": [161], "characters": "\u00A1" },
+  "&iff;": { "codepoints": [8660], "characters": "\u21D4" },
+  "&ifr;": { "codepoints": [120102], "characters": "\uD835\uDD26" },
+  "&igrave": { "codepoints": [236], "characters": "\u00EC" },
+  "&igrave;": { "codepoints": [236], "characters": "\u00EC" },
+  "&ii;": { "codepoints": [8520], "characters": "\u2148" },
+  "&iiiint;": { "codepoints": [10764], "characters": "\u2A0C" },
+  "&iiint;": { "codepoints": [8749], "characters": "\u222D" },
+  "&iinfin;": { "codepoints": [10716], "characters": "\u29DC" },
+  "&iiota;": { "codepoints": [8489], "characters": "\u2129" },
+  "&ijlig;": { "codepoints": [307], "characters": "\u0133" },
+  "&imacr;": { "codepoints": [299], "characters": "\u012B" },
+  "&image;": { "codepoints": [8465], "characters": "\u2111" },
+  "&imagline;": { "codepoints": [8464], "characters": "\u2110" },
+  "&imagpart;": { "codepoints": [8465], "characters": "\u2111" },
+  "&imath;": { "codepoints": [305], "characters": "\u0131" },
+  "&imof;": { "codepoints": [8887], "characters": "\u22B7" },
+  "&imped;": { "codepoints": [437], "characters": "\u01B5" },
+  "&in;": { "codepoints": [8712], "characters": "\u2208" },
+  "&incare;": { "codepoints": [8453], "characters": "\u2105" },
+  "&infin;": { "codepoints": [8734], "characters": "\u221E" },
+  "&infintie;": { "codepoints": [10717], "characters": "\u29DD" },
+  "&inodot;": { "codepoints": [305], "characters": "\u0131" },
+  "&int;": { "codepoints": [8747], "characters": "\u222B" },
+  "&intcal;": { "codepoints": [8890], "characters": "\u22BA" },
+  "&integers;": { "codepoints": [8484], "characters": "\u2124" },
+  "&intercal;": { "codepoints": [8890], "characters": "\u22BA" },
+  "&intlarhk;": { "codepoints": [10775], "characters": "\u2A17" },
+  "&intprod;": { "codepoints": [10812], "characters": "\u2A3C" },
+  "&iocy;": { "codepoints": [1105], "characters": "\u0451" },
+  "&iogon;": { "codepoints": [303], "characters": "\u012F" },
+  "&iopf;": { "codepoints": [120154], "characters": "\uD835\uDD5A" },
+  "&iota;": { "codepoints": [953], "characters": "\u03B9" },
+  "&iprod;": { "codepoints": [10812], "characters": "\u2A3C" },
+  "&iquest": { "codepoints": [191], "characters": "\u00BF" },
+  "&iquest;": { "codepoints": [191], "characters": "\u00BF" },
+  "&iscr;": { "codepoints": [119998], "characters": "\uD835\uDCBE" },
+  "&isin;": { "codepoints": [8712], "characters": "\u2208" },
+  "&isinE;": { "codepoints": [8953], "characters": "\u22F9" },
+  "&isindot;": { "codepoints": [8949], "characters": "\u22F5" },
+  "&isins;": { "codepoints": [8948], "characters": "\u22F4" },
+  "&isinsv;": { "codepoints": [8947], "characters": "\u22F3" },
+  "&isinv;": { "codepoints": [8712], "characters": "\u2208" },
+  "&it;": { "codepoints": [8290], "characters": "\u2062" },
+  "&itilde;": { "codepoints": [297], "characters": "\u0129" },
+  "&iukcy;": { "codepoints": [1110], "characters": "\u0456" },
+  "&iuml": { "codepoints": [239], "characters": "\u00EF" },
+  "&iuml;": { "codepoints": [239], "characters": "\u00EF" },
+  "&jcirc;": { "codepoints": [309], "characters": "\u0135" },
+  "&jcy;": { "codepoints": [1081], "characters": "\u0439" },
+  "&jfr;": { "codepoints": [120103], "characters": "\uD835\uDD27" },
+  "&jmath;": { "codepoints": [567], "characters": "\u0237" },
+  "&jopf;": { "codepoints": [120155], "characters": "\uD835\uDD5B" },
+  "&jscr;": { "codepoints": [119999], "characters": "\uD835\uDCBF" },
+  "&jsercy;": { "codepoints": [1112], "characters": "\u0458" },
+  "&jukcy;": { "codepoints": [1108], "characters": "\u0454" },
+  "&kappa;": { "codepoints": [954], "characters": "\u03BA" },
+  "&kappav;": { "codepoints": [1008], "characters": "\u03F0" },
+  "&kcedil;": { "codepoints": [311], "characters": "\u0137" },
+  "&kcy;": { "codepoints": [1082], "characters": "\u043A" },
+  "&kfr;": { "codepoints": [120104], "characters": "\uD835\uDD28" },
+  "&kgreen;": { "codepoints": [312], "characters": "\u0138" },
+  "&khcy;": { "codepoints": [1093], "characters": "\u0445" },
+  "&kjcy;": { "codepoints": [1116], "characters": "\u045C" },
+  "&kopf;": { "codepoints": [120156], "characters": "\uD835\uDD5C" },
+  "&kscr;": { "codepoints": [120000], "characters": "\uD835\uDCC0" },
+  "&lAarr;": { "codepoints": [8666], "characters": "\u21DA" },
+  "&lArr;": { "codepoints": [8656], "characters": "\u21D0" },
+  "&lAtail;": { "codepoints": [10523], "characters": "\u291B" },
+  "&lBarr;": { "codepoints": [10510], "characters": "\u290E" },
+  "&lE;": { "codepoints": [8806], "characters": "\u2266" },
+  "&lEg;": { "codepoints": [10891], "characters": "\u2A8B" },
+  "&lHar;": { "codepoints": [10594], "characters": "\u2962" },
+  "&lacute;": { "codepoints": [314], "characters": "\u013A" },
+  "&laemptyv;": { "codepoints": [10676], "characters": "\u29B4" },
+  "&lagran;": { "codepoints": [8466], "characters": "\u2112" },
+  "&lambda;": { "codepoints": [955], "characters": "\u03BB" },
+  "&lang;": { "codepoints": [10216], "characters": "\u27E8" },
+  "&langd;": { "codepoints": [10641], "characters": "\u2991" },
+  "&langle;": { "codepoints": [10216], "characters": "\u27E8" },
+  "&lap;": { "codepoints": [10885], "characters": "\u2A85" },
+  "&laquo": { "codepoints": [171], "characters": "\u00AB" },
+  "&laquo;": { "codepoints": [171], "characters": "\u00AB" },
+  "&larr;": { "codepoints": [8592], "characters": "\u2190" },
+  "&larrb;": { "codepoints": [8676], "characters": "\u21E4" },
+  "&larrbfs;": { "codepoints": [10527], "characters": "\u291F" },
+  "&larrfs;": { "codepoints": [10525], "characters": "\u291D" },
+  "&larrhk;": { "codepoints": [8617], "characters": "\u21A9" },
+  "&larrlp;": { "codepoints": [8619], "characters": "\u21AB" },
+  "&larrpl;": { "codepoints": [10553], "characters": "\u2939" },
+  "&larrsim;": { "codepoints": [10611], "characters": "\u2973" },
+  "&larrtl;": { "codepoints": [8610], "characters": "\u21A2" },
+  "&lat;": { "codepoints": [10923], "characters": "\u2AAB" },
+  "&latail;": { "codepoints": [10521], "characters": "\u2919" },
+  "&late;": { "codepoints": [10925], "characters": "\u2AAD" },
+  "&lates;": { "codepoints": [10925, 65024], "characters": "\u2AAD\uFE00" },
+  "&lbarr;": { "codepoints": [10508], "characters": "\u290C" },
+  "&lbbrk;": { "codepoints": [10098], "characters": "\u2772" },
+  "&lbrace;": { "codepoints": [123], "characters": "\u007B" },
+  "&lbrack;": { "codepoints": [91], "characters": "\u005B" },
+  "&lbrke;": { "codepoints": [10635], "characters": "\u298B" },
+  "&lbrksld;": { "codepoints": [10639], "characters": "\u298F" },
+  "&lbrkslu;": { "codepoints": [10637], "characters": "\u298D" },
+  "&lcaron;": { "codepoints": [318], "characters": "\u013E" },
+  "&lcedil;": { "codepoints": [316], "characters": "\u013C" },
+  "&lceil;": { "codepoints": [8968], "characters": "\u2308" },
+  "&lcub;": { "codepoints": [123], "characters": "\u007B" },
+  "&lcy;": { "codepoints": [1083], "characters": "\u043B" },
+  "&ldca;": { "codepoints": [10550], "characters": "\u2936" },
+  "&ldquo;": { "codepoints": [8220], "characters": "\u201C" },
+  "&ldquor;": { "codepoints": [8222], "characters": "\u201E" },
+  "&ldrdhar;": { "codepoints": [10599], "characters": "\u2967" },
+  "&ldrushar;": { "codepoints": [10571], "characters": "\u294B" },
+  "&ldsh;": { "codepoints": [8626], "characters": "\u21B2" },
+  "&le;": { "codepoints": [8804], "characters": "\u2264" },
+  "&leftarrow;": { "codepoints": [8592], "characters": "\u2190" },
+  "&leftarrowtail;": { "codepoints": [8610], "characters": "\u21A2" },
+  "&leftharpoondown;": { "codepoints": [8637], "characters": "\u21BD" },
+  "&leftharpoonup;": { "codepoints": [8636], "characters": "\u21BC" },
+  "&leftleftarrows;": { "codepoints": [8647], "characters": "\u21C7" },
+  "&leftrightarrow;": { "codepoints": [8596], "characters": "\u2194" },
+  "&leftrightarrows;": { "codepoints": [8646], "characters": "\u21C6" },
+  "&leftrightharpoons;": { "codepoints": [8651], "characters": "\u21CB" },
+  "&leftrightsquigarrow;": { "codepoints": [8621], "characters": "\u21AD" },
+  "&leftthreetimes;": { "codepoints": [8907], "characters": "\u22CB" },
+  "&leg;": { "codepoints": [8922], "characters": "\u22DA" },
+  "&leq;": { "codepoints": [8804], "characters": "\u2264" },
+  "&leqq;": { "codepoints": [8806], "characters": "\u2266" },
+  "&leqslant;": { "codepoints": [10877], "characters": "\u2A7D" },
+  "&les;": { "codepoints": [10877], "characters": "\u2A7D" },
+  "&lescc;": { "codepoints": [10920], "characters": "\u2AA8" },
+  "&lesdot;": { "codepoints": [10879], "characters": "\u2A7F" },
+  "&lesdoto;": { "codepoints": [10881], "characters": "\u2A81" },
+  "&lesdotor;": { "codepoints": [10883], "characters": "\u2A83" },
+  "&lesg;": { "codepoints": [8922, 65024], "characters": "\u22DA\uFE00" },
+  "&lesges;": { "codepoints": [10899], "characters": "\u2A93" },
+  "&lessapprox;": { "codepoints": [10885], "characters": "\u2A85" },
+  "&lessdot;": { "codepoints": [8918], "characters": "\u22D6" },
+  "&lesseqgtr;": { "codepoints": [8922], "characters": "\u22DA" },
+  "&lesseqqgtr;": { "codepoints": [10891], "characters": "\u2A8B" },
+  "&lessgtr;": { "codepoints": [8822], "characters": "\u2276" },
+  "&lesssim;": { "codepoints": [8818], "characters": "\u2272" },
+  "&lfisht;": { "codepoints": [10620], "characters": "\u297C" },
+  "&lfloor;": { "codepoints": [8970], "characters": "\u230A" },
+  "&lfr;": { "codepoints": [120105], "characters": "\uD835\uDD29" },
+  "&lg;": { "codepoints": [8822], "characters": "\u2276" },
+  "&lgE;": { "codepoints": [10897], "characters": "\u2A91" },
+  "&lhard;": { "codepoints": [8637], "characters": "\u21BD" },
+  "&lharu;": { "codepoints": [8636], "characters": "\u21BC" },
+  "&lharul;": { "codepoints": [10602], "characters": "\u296A" },
+  "&lhblk;": { "codepoints": [9604], "characters": "\u2584" },
+  "&ljcy;": { "codepoints": [1113], "characters": "\u0459" },
+  "&ll;": { "codepoints": [8810], "characters": "\u226A" },
+  "&llarr;": { "codepoints": [8647], "characters": "\u21C7" },
+  "&llcorner;": { "codepoints": [8990], "characters": "\u231E" },
+  "&llhard;": { "codepoints": [10603], "characters": "\u296B" },
+  "&lltri;": { "codepoints": [9722], "characters": "\u25FA" },
+  "&lmidot;": { "codepoints": [320], "characters": "\u0140" },
+  "&lmoust;": { "codepoints": [9136], "characters": "\u23B0" },
+  "&lmoustache;": { "codepoints": [9136], "characters": "\u23B0" },
+  "&lnE;": { "codepoints": [8808], "characters": "\u2268" },
+  "&lnap;": { "codepoints": [10889], "characters": "\u2A89" },
+  "&lnapprox;": { "codepoints": [10889], "characters": "\u2A89" },
+  "&lne;": { "codepoints": [10887], "characters": "\u2A87" },
+  "&lneq;": { "codepoints": [10887], "characters": "\u2A87" },
+  "&lneqq;": { "codepoints": [8808], "characters": "\u2268" },
+  "&lnsim;": { "codepoints": [8934], "characters": "\u22E6" },
+  "&loang;": { "codepoints": [10220], "characters": "\u27EC" },
+  "&loarr;": { "codepoints": [8701], "characters": "\u21FD" },
+  "&lobrk;": { "codepoints": [10214], "characters": "\u27E6" },
+  "&longleftarrow;": { "codepoints": [10229], "characters": "\u27F5" },
+  "&longleftrightarrow;": { "codepoints": [10231], "characters": "\u27F7" },
+  "&longmapsto;": { "codepoints": [10236], "characters": "\u27FC" },
+  "&longrightarrow;": { "codepoints": [10230], "characters": "\u27F6" },
+  "&looparrowleft;": { "codepoints": [8619], "characters": "\u21AB" },
+  "&looparrowright;": { "codepoints": [8620], "characters": "\u21AC" },
+  "&lopar;": { "codepoints": [10629], "characters": "\u2985" },
+  "&lopf;": { "codepoints": [120157], "characters": "\uD835\uDD5D" },
+  "&loplus;": { "codepoints": [10797], "characters": "\u2A2D" },
+  "&lotimes;": { "codepoints": [10804], "characters": "\u2A34" },
+  "&lowast;": { "codepoints": [8727], "characters": "\u2217" },
+  "&lowbar;": { "codepoints": [95], "characters": "\u005F" },
+  "&loz;": { "codepoints": [9674], "characters": "\u25CA" },
+  "&lozenge;": { "codepoints": [9674], "characters": "\u25CA" },
+  "&lozf;": { "codepoints": [10731], "characters": "\u29EB" },
+  "&lpar;": { "codepoints": [40], "characters": "\u0028" },
+  "&lparlt;": { "codepoints": [10643], "characters": "\u2993" },
+  "&lrarr;": { "codepoints": [8646], "characters": "\u21C6" },
+  "&lrcorner;": { "codepoints": [8991], "characters": "\u231F" },
+  "&lrhar;": { "codepoints": [8651], "characters": "\u21CB" },
+  "&lrhard;": { "codepoints": [10605], "characters": "\u296D" },
+  "&lrm;": { "codepoints": [8206], "characters": "\u200E" },
+  "&lrtri;": { "codepoints": [8895], "characters": "\u22BF" },
+  "&lsaquo;": { "codepoints": [8249], "characters": "\u2039" },
+  "&lscr;": { "codepoints": [120001], "characters": "\uD835\uDCC1" },
+  "&lsh;": { "codepoints": [8624], "characters": "\u21B0" },
+  "&lsim;": { "codepoints": [8818], "characters": "\u2272" },
+  "&lsime;": { "codepoints": [10893], "characters": "\u2A8D" },
+  "&lsimg;": { "codepoints": [10895], "characters": "\u2A8F" },
+  "&lsqb;": { "codepoints": [91], "characters": "\u005B" },
+  "&lsquo;": { "codepoints": [8216], "characters": "\u2018" },
+  "&lsquor;": { "codepoints": [8218], "characters": "\u201A" },
+  "&lstrok;": { "codepoints": [322], "characters": "\u0142" },
+  "&lt": { "codepoints": [60], "characters": "\u003C" },
+  "&lt;": { "codepoints": [60], "characters": "\u003C" },
+  "&ltcc;": { "codepoints": [10918], "characters": "\u2AA6" },
+  "&ltcir;": { "codepoints": [10873], "characters": "\u2A79" },
+  "&ltdot;": { "codepoints": [8918], "characters": "\u22D6" },
+  "&lthree;": { "codepoints": [8907], "characters": "\u22CB" },
+  "&ltimes;": { "codepoints": [8905], "characters": "\u22C9" },
+  "&ltlarr;": { "codepoints": [10614], "characters": "\u2976" },
+  "&ltquest;": { "codepoints": [10875], "characters": "\u2A7B" },
+  "&ltrPar;": { "codepoints": [10646], "characters": "\u2996" },
+  "&ltri;": { "codepoints": [9667], "characters": "\u25C3" },
+  "&ltrie;": { "codepoints": [8884], "characters": "\u22B4" },
+  "&ltrif;": { "codepoints": [9666], "characters": "\u25C2" },
+  "&lurdshar;": { "codepoints": [10570], "characters": "\u294A" },
+  "&luruhar;": { "codepoints": [10598], "characters": "\u2966" },
+  "&lvertneqq;": { "codepoints": [8808, 65024], "characters": "\u2268\uFE00" },
+  "&lvnE;": { "codepoints": [8808, 65024], "characters": "\u2268\uFE00" },
+  "&mDDot;": { "codepoints": [8762], "characters": "\u223A" },
+  "&macr": { "codepoints": [175], "characters": "\u00AF" },
+  "&macr;": { "codepoints": [175], "characters": "\u00AF" },
+  "&male;": { "codepoints": [9794], "characters": "\u2642" },
+  "&malt;": { "codepoints": [10016], "characters": "\u2720" },
+  "&maltese;": { "codepoints": [10016], "characters": "\u2720" },
+  "&map;": { "codepoints": [8614], "characters": "\u21A6" },
+  "&mapsto;": { "codepoints": [8614], "characters": "\u21A6" },
+  "&mapstodown;": { "codepoints": [8615], "characters": "\u21A7" },
+  "&mapstoleft;": { "codepoints": [8612], "characters": "\u21A4" },
+  "&mapstoup;": { "codepoints": [8613], "characters": "\u21A5" },
+  "&marker;": { "codepoints": [9646], "characters": "\u25AE" },
+  "&mcomma;": { "codepoints": [10793], "characters": "\u2A29" },
+  "&mcy;": { "codepoints": [1084], "characters": "\u043C" },
+  "&mdash;": { "codepoints": [8212], "characters": "\u2014" },
+  "&measuredangle;": { "codepoints": [8737], "characters": "\u2221" },
+  "&mfr;": { "codepoints": [120106], "characters": "\uD835\uDD2A" },
+  "&mho;": { "codepoints": [8487], "characters": "\u2127" },
+  "&micro": { "codepoints": [181], "characters": "\u00B5" },
+  "&micro;": { "codepoints": [181], "characters": "\u00B5" },
+  "&mid;": { "codepoints": [8739], "characters": "\u2223" },
+  "&midast;": { "codepoints": [42], "characters": "\u002A" },
+  "&midcir;": { "codepoints": [10992], "characters": "\u2AF0" },
+  "&middot": { "codepoints": [183], "characters": "\u00B7" },
+  "&middot;": { "codepoints": [183], "characters": "\u00B7" },
+  "&minus;": { "codepoints": [8722], "characters": "\u2212" },
+  "&minusb;": { "codepoints": [8863], "characters": "\u229F" },
+  "&minusd;": { "codepoints": [8760], "characters": "\u2238" },
+  "&minusdu;": { "codepoints": [10794], "characters": "\u2A2A" },
+  "&mlcp;": { "codepoints": [10971], "characters": "\u2ADB" },
+  "&mldr;": { "codepoints": [8230], "characters": "\u2026" },
+  "&mnplus;": { "codepoints": [8723], "characters": "\u2213" },
+  "&models;": { "codepoints": [8871], "characters": "\u22A7" },
+  "&mopf;": { "codepoints": [120158], "characters": "\uD835\uDD5E" },
+  "&mp;": { "codepoints": [8723], "characters": "\u2213" },
+  "&mscr;": { "codepoints": [120002], "characters": "\uD835\uDCC2" },
+  "&mstpos;": { "codepoints": [8766], "characters": "\u223E" },
+  "&mu;": { "codepoints": [956], "characters": "\u03BC" },
+  "&multimap;": { "codepoints": [8888], "characters": "\u22B8" },
+  "&mumap;": { "codepoints": [8888], "characters": "\u22B8" },
+  "&nGg;": { "codepoints": [8921, 824], "characters": "\u22D9\u0338" },
+  "&nGt;": { "codepoints": [8811, 8402], "characters": "\u226B\u20D2" },
+  "&nGtv;": { "codepoints": [8811, 824], "characters": "\u226B\u0338" },
+  "&nLeftarrow;": { "codepoints": [8653], "characters": "\u21CD" },
+  "&nLeftrightarrow;": { "codepoints": [8654], "characters": "\u21CE" },
+  "&nLl;": { "codepoints": [8920, 824], "characters": "\u22D8\u0338" },
+  "&nLt;": { "codepoints": [8810, 8402], "characters": "\u226A\u20D2" },
+  "&nLtv;": { "codepoints": [8810, 824], "characters": "\u226A\u0338" },
+  "&nRightarrow;": { "codepoints": [8655], "characters": "\u21CF" },
+  "&nVDash;": { "codepoints": [8879], "characters": "\u22AF" },
+  "&nVdash;": { "codepoints": [8878], "characters": "\u22AE" },
+  "&nabla;": { "codepoints": [8711], "characters": "\u2207" },
+  "&nacute;": { "codepoints": [324], "characters": "\u0144" },
+  "&nang;": { "codepoints": [8736, 8402], "characters": "\u2220\u20D2" },
+  "&nap;": { "codepoints": [8777], "characters": "\u2249" },
+  "&napE;": { "codepoints": [10864, 824], "characters": "\u2A70\u0338" },
+  "&napid;": { "codepoints": [8779, 824], "characters": "\u224B\u0338" },
+  "&napos;": { "codepoints": [329], "characters": "\u0149" },
+  "&napprox;": { "codepoints": [8777], "characters": "\u2249" },
+  "&natur;": { "codepoints": [9838], "characters": "\u266E" },
+  "&natural;": { "codepoints": [9838], "characters": "\u266E" },
+  "&naturals;": { "codepoints": [8469], "characters": "\u2115" },
+  "&nbsp": { "codepoints": [160], "characters": "\u00A0" },
+  "&nbsp;": { "codepoints": [160], "characters": "\u00A0" },
+  "&nbump;": { "codepoints": [8782, 824], "characters": "\u224E\u0338" },
+  "&nbumpe;": { "codepoints": [8783, 824], "characters": "\u224F\u0338" },
+  "&ncap;": { "codepoints": [10819], "characters": "\u2A43" },
+  "&ncaron;": { "codepoints": [328], "characters": "\u0148" },
+  "&ncedil;": { "codepoints": [326], "characters": "\u0146" },
+  "&ncong;": { "codepoints": [8775], "characters": "\u2247" },
+  "&ncongdot;": { "codepoints": [10861, 824], "characters": "\u2A6D\u0338" },
+  "&ncup;": { "codepoints": [10818], "characters": "\u2A42" },
+  "&ncy;": { "codepoints": [1085], "characters": "\u043D" },
+  "&ndash;": { "codepoints": [8211], "characters": "\u2013" },
+  "&ne;": { "codepoints": [8800], "characters": "\u2260" },
+  "&neArr;": { "codepoints": [8663], "characters": "\u21D7" },
+  "&nearhk;": { "codepoints": [10532], "characters": "\u2924" },
+  "&nearr;": { "codepoints": [8599], "characters": "\u2197" },
+  "&nearrow;": { "codepoints": [8599], "characters": "\u2197" },
+  "&nedot;": { "codepoints": [8784, 824], "characters": "\u2250\u0338" },
+  "&nequiv;": { "codepoints": [8802], "characters": "\u2262" },
+  "&nesear;": { "codepoints": [10536], "characters": "\u2928" },
+  "&nesim;": { "codepoints": [8770, 824], "characters": "\u2242\u0338" },
+  "&nexist;": { "codepoints": [8708], "characters": "\u2204" },
+  "&nexists;": { "codepoints": [8708], "characters": "\u2204" },
+  "&nfr;": { "codepoints": [120107], "characters": "\uD835\uDD2B" },
+  "&ngE;": { "codepoints": [8807, 824], "characters": "\u2267\u0338" },
+  "&nge;": { "codepoints": [8817], "characters": "\u2271" },
+  "&ngeq;": { "codepoints": [8817], "characters": "\u2271" },
+  "&ngeqq;": { "codepoints": [8807, 824], "characters": "\u2267\u0338" },
+  "&ngeqslant;": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" },
+  "&nges;": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" },
+  "&ngsim;": { "codepoints": [8821], "characters": "\u2275" },
+  "&ngt;": { "codepoints": [8815], "characters": "\u226F" },
+  "&ngtr;": { "codepoints": [8815], "characters": "\u226F" },
+  "&nhArr;": { "codepoints": [8654], "characters": "\u21CE" },
+  "&nharr;": { "codepoints": [8622], "characters": "\u21AE" },
+  "&nhpar;": { "codepoints": [10994], "characters": "\u2AF2" },
+  "&ni;": { "codepoints": [8715], "characters": "\u220B" },
+  "&nis;": { "codepoints": [8956], "characters": "\u22FC" },
+  "&nisd;": { "codepoints": [8954], "characters": "\u22FA" },
+  "&niv;": { "codepoints": [8715], "characters": "\u220B" },
+  "&njcy;": { "codepoints": [1114], "characters": "\u045A" },
+  "&nlArr;": { "codepoints": [8653], "characters": "\u21CD" },
+  "&nlE;": { "codepoints": [8806, 824], "characters": "\u2266\u0338" },
+  "&nlarr;": { "codepoints": [8602], "characters": "\u219A" },
+  "&nldr;": { "codepoints": [8229], "characters": "\u2025" },
+  "&nle;": { "codepoints": [8816], "characters": "\u2270" },
+  "&nleftarrow;": { "codepoints": [8602], "characters": "\u219A" },
+  "&nleftrightarrow;": { "codepoints": [8622], "characters": "\u21AE" },
+  "&nleq;": { "codepoints": [8816], "characters": "\u2270" },
+  "&nleqq;": { "codepoints": [8806, 824], "characters": "\u2266\u0338" },
+  "&nleqslant;": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" },
+  "&nles;": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" },
+  "&nless;": { "codepoints": [8814], "characters": "\u226E" },
+  "&nlsim;": { "codepoints": [8820], "characters": "\u2274" },
+  "&nlt;": { "codepoints": [8814], "characters": "\u226E" },
+  "&nltri;": { "codepoints": [8938], "characters": "\u22EA" },
+  "&nltrie;": { "codepoints": [8940], "characters": "\u22EC" },
+  "&nmid;": { "codepoints": [8740], "characters": "\u2224" },
+  "&nopf;": { "codepoints": [120159], "characters": "\uD835\uDD5F" },
+  "&not": { "codepoints": [172], "characters": "\u00AC" },
+  "&not;": { "codepoints": [172], "characters": "\u00AC" },
+  "&notin;": { "codepoints": [8713], "characters": "\u2209" },
+  "&notinE;": { "codepoints": [8953, 824], "characters": "\u22F9\u0338" },
+  "&notindot;": { "codepoints": [8949, 824], "characters": "\u22F5\u0338" },
+  "&notinva;": { "codepoints": [8713], "characters": "\u2209" },
+  "&notinvb;": { "codepoints": [8951], "characters": "\u22F7" },
+  "&notinvc;": { "codepoints": [8950], "characters": "\u22F6" },
+  "&notni;": { "codepoints": [8716], "characters": "\u220C" },
+  "&notniva;": { "codepoints": [8716], "characters": "\u220C" },
+  "&notnivb;": { "codepoints": [8958], "characters": "\u22FE" },
+  "&notnivc;": { "codepoints": [8957], "characters": "\u22FD" },
+  "&npar;": { "codepoints": [8742], "characters": "\u2226" },
+  "&nparallel;": { "codepoints": [8742], "characters": "\u2226" },
+  "&nparsl;": { "codepoints": [11005, 8421], "characters": "\u2AFD\u20E5" },
+  "&npart;": { "codepoints": [8706, 824], "characters": "\u2202\u0338" },
+  "&npolint;": { "codepoints": [10772], "characters": "\u2A14" },
+  "&npr;": { "codepoints": [8832], "characters": "\u2280" },
+  "&nprcue;": { "codepoints": [8928], "characters": "\u22E0" },
+  "&npre;": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" },
+  "&nprec;": { "codepoints": [8832], "characters": "\u2280" },
+  "&npreceq;": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" },
+  "&nrArr;": { "codepoints": [8655], "characters": "\u21CF" },
+  "&nrarr;": { "codepoints": [8603], "characters": "\u219B" },
+  "&nrarrc;": { "codepoints": [10547, 824], "characters": "\u2933\u0338" },
+  "&nrarrw;": { "codepoints": [8605, 824], "characters": "\u219D\u0338" },
+  "&nrightarrow;": { "codepoints": [8603], "characters": "\u219B" },
+  "&nrtri;": { "codepoints": [8939], "characters": "\u22EB" },
+  "&nrtrie;": { "codepoints": [8941], "characters": "\u22ED" },
+  "&nsc;": { "codepoints": [8833], "characters": "\u2281" },
+  "&nsccue;": { "codepoints": [8929], "characters": "\u22E1" },
+  "&nsce;": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" },
+  "&nscr;": { "codepoints": [120003], "characters": "\uD835\uDCC3" },
+  "&nshortmid;": { "codepoints": [8740], "characters": "\u2224" },
+  "&nshortparallel;": { "codepoints": [8742], "characters": "\u2226" },
+  "&nsim;": { "codepoints": [8769], "characters": "\u2241" },
+  "&nsime;": { "codepoints": [8772], "characters": "\u2244" },
+  "&nsimeq;": { "codepoints": [8772], "characters": "\u2244" },
+  "&nsmid;": { "codepoints": [8740], "characters": "\u2224" },
+  "&nspar;": { "codepoints": [8742], "characters": "\u2226" },
+  "&nsqsube;": { "codepoints": [8930], "characters": "\u22E2" },
+  "&nsqsupe;": { "codepoints": [8931], "characters": "\u22E3" },
+  "&nsub;": { "codepoints": [8836], "characters": "\u2284" },
+  "&nsubE;": { "codepoints": [10949, 824], "characters": "\u2AC5\u0338" },
+  "&nsube;": { "codepoints": [8840], "characters": "\u2288" },
+  "&nsubset;": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" },
+  "&nsubseteq;": { "codepoints": [8840], "characters": "\u2288" },
+  "&nsubseteqq;": { "codepoints": [10949, 824], "characters": "\u2AC5\u0338" },
+  "&nsucc;": { "codepoints": [8833], "characters": "\u2281" },
+  "&nsucceq;": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" },
+  "&nsup;": { "codepoints": [8837], "characters": "\u2285" },
+  "&nsupE;": { "codepoints": [10950, 824], "characters": "\u2AC6\u0338" },
+  "&nsupe;": { "codepoints": [8841], "characters": "\u2289" },
+  "&nsupset;": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" },
+  "&nsupseteq;": { "codepoints": [8841], "characters": "\u2289" },
+  "&nsupseteqq;": { "codepoints": [10950, 824], "characters": "\u2AC6\u0338" },
+  "&ntgl;": { "codepoints": [8825], "characters": "\u2279" },
+  "&ntilde": { "codepoints": [241], "characters": "\u00F1" },
+  "&ntilde;": { "codepoints": [241], "characters": "\u00F1" },
+  "&ntlg;": { "codepoints": [8824], "characters": "\u2278" },
+  "&ntriangleleft;": { "codepoints": [8938], "characters": "\u22EA" },
+  "&ntrianglelefteq;": { "codepoints": [8940], "characters": "\u22EC" },
+  "&ntriangleright;": { "codepoints": [8939], "characters": "\u22EB" },
+  "&ntrianglerighteq;": { "codepoints": [8941], "characters": "\u22ED" },
+  "&nu;": { "codepoints": [957], "characters": "\u03BD" },
+  "&num;": { "codepoints": [35], "characters": "\u0023" },
+  "&numero;": { "codepoints": [8470], "characters": "\u2116" },
+  "&numsp;": { "codepoints": [8199], "characters": "\u2007" },
+  "&nvDash;": { "codepoints": [8877], "characters": "\u22AD" },
+  "&nvHarr;": { "codepoints": [10500], "characters": "\u2904" },
+  "&nvap;": { "codepoints": [8781, 8402], "characters": "\u224D\u20D2" },
+  "&nvdash;": { "codepoints": [8876], "characters": "\u22AC" },
+  "&nvge;": { "codepoints": [8805, 8402], "characters": "\u2265\u20D2" },
+  "&nvgt;": { "codepoints": [62, 8402], "characters": "\u003E\u20D2" },
+  "&nvinfin;": { "codepoints": [10718], "characters": "\u29DE" },
+  "&nvlArr;": { "codepoints": [10498], "characters": "\u2902" },
+  "&nvle;": { "codepoints": [8804, 8402], "characters": "\u2264\u20D2" },
+  "&nvlt;": { "codepoints": [60, 8402], "characters": "\u003C\u20D2" },
+  "&nvltrie;": { "codepoints": [8884, 8402], "characters": "\u22B4\u20D2" },
+  "&nvrArr;": { "codepoints": [10499], "characters": "\u2903" },
+  "&nvrtrie;": { "codepoints": [8885, 8402], "characters": "\u22B5\u20D2" },
+  "&nvsim;": { "codepoints": [8764, 8402], "characters": "\u223C\u20D2" },
+  "&nwArr;": { "codepoints": [8662], "characters": "\u21D6" },
+  "&nwarhk;": { "codepoints": [10531], "characters": "\u2923" },
+  "&nwarr;": { "codepoints": [8598], "characters": "\u2196" },
+  "&nwarrow;": { "codepoints": [8598], "characters": "\u2196" },
+  "&nwnear;": { "codepoints": [10535], "characters": "\u2927" },
+  "&oS;": { "codepoints": [9416], "characters": "\u24C8" },
+  "&oacute": { "codepoints": [243], "characters": "\u00F3" },
+  "&oacute;": { "codepoints": [243], "characters": "\u00F3" },
+  "&oast;": { "codepoints": [8859], "characters": "\u229B" },
+  "&ocir;": { "codepoints": [8858], "characters": "\u229A" },
+  "&ocirc": { "codepoints": [244], "characters": "\u00F4" },
+  "&ocirc;": { "codepoints": [244], "characters": "\u00F4" },
+  "&ocy;": { "codepoints": [1086], "characters": "\u043E" },
+  "&odash;": { "codepoints": [8861], "characters": "\u229D" },
+  "&odblac;": { "codepoints": [337], "characters": "\u0151" },
+  "&odiv;": { "codepoints": [10808], "characters": "\u2A38" },
+  "&odot;": { "codepoints": [8857], "characters": "\u2299" },
+  "&odsold;": { "codepoints": [10684], "characters": "\u29BC" },
+  "&oelig;": { "codepoints": [339], "characters": "\u0153" },
+  "&ofcir;": { "codepoints": [10687], "characters": "\u29BF" },
+  "&ofr;": { "codepoints": [120108], "characters": "\uD835\uDD2C" },
+  "&ogon;": { "codepoints": [731], "characters": "\u02DB" },
+  "&ograve": { "codepoints": [242], "characters": "\u00F2" },
+  "&ograve;": { "codepoints": [242], "characters": "\u00F2" },
+  "&ogt;": { "codepoints": [10689], "characters": "\u29C1" },
+  "&ohbar;": { "codepoints": [10677], "characters": "\u29B5" },
+  "&ohm;": { "codepoints": [937], "characters": "\u03A9" },
+  "&oint;": { "codepoints": [8750], "characters": "\u222E" },
+  "&olarr;": { "codepoints": [8634], "characters": "\u21BA" },
+  "&olcir;": { "codepoints": [10686], "characters": "\u29BE" },
+  "&olcross;": { "codepoints": [10683], "characters": "\u29BB" },
+  "&oline;": { "codepoints": [8254], "characters": "\u203E" },
+  "&olt;": { "codepoints": [10688], "characters": "\u29C0" },
+  "&omacr;": { "codepoints": [333], "characters": "\u014D" },
+  "&omega;": { "codepoints": [969], "characters": "\u03C9" },
+  "&omicron;": { "codepoints": [959], "characters": "\u03BF" },
+  "&omid;": { "codepoints": [10678], "characters": "\u29B6" },
+  "&ominus;": { "codepoints": [8854], "characters": "\u2296" },
+  "&oopf;": { "codepoints": [120160], "characters": "\uD835\uDD60" },
+  "&opar;": { "codepoints": [10679], "characters": "\u29B7" },
+  "&operp;": { "codepoints": [10681], "characters": "\u29B9" },
+  "&oplus;": { "codepoints": [8853], "characters": "\u2295" },
+  "&or;": { "codepoints": [8744], "characters": "\u2228" },
+  "&orarr;": { "codepoints": [8635], "characters": "\u21BB" },
+  "&ord;": { "codepoints": [10845], "characters": "\u2A5D" },
+  "&order;": { "codepoints": [8500], "characters": "\u2134" },
+  "&orderof;": { "codepoints": [8500], "characters": "\u2134" },
+  "&ordf": { "codepoints": [170], "characters": "\u00AA" },
+  "&ordf;": { "codepoints": [170], "characters": "\u00AA" },
+  "&ordm": { "codepoints": [186], "characters": "\u00BA" },
+  "&ordm;": { "codepoints": [186], "characters": "\u00BA" },
+  "&origof;": { "codepoints": [8886], "characters": "\u22B6" },
+  "&oror;": { "codepoints": [10838], "characters": "\u2A56" },
+  "&orslope;": { "codepoints": [10839], "characters": "\u2A57" },
+  "&orv;": { "codepoints": [10843], "characters": "\u2A5B" },
+  "&oscr;": { "codepoints": [8500], "characters": "\u2134" },
+  "&oslash": { "codepoints": [248], "characters": "\u00F8" },
+  "&oslash;": { "codepoints": [248], "characters": "\u00F8" },
+  "&osol;": { "codepoints": [8856], "characters": "\u2298" },
+  "&otilde": { "codepoints": [245], "characters": "\u00F5" },
+  "&otilde;": { "codepoints": [245], "characters": "\u00F5" },
+  "&otimes;": { "codepoints": [8855], "characters": "\u2297" },
+  "&otimesas;": { "codepoints": [10806], "characters": "\u2A36" },
+  "&ouml": { "codepoints": [246], "characters": "\u00F6" },
+  "&ouml;": { "codepoints": [246], "characters": "\u00F6" },
+  "&ovbar;": { "codepoints": [9021], "characters": "\u233D" },
+  "&par;": { "codepoints": [8741], "characters": "\u2225" },
+  "&para": { "codepoints": [182], "characters": "\u00B6" },
+  "&para;": { "codepoints": [182], "characters": "\u00B6" },
+  "&parallel;": { "codepoints": [8741], "characters": "\u2225" },
+  "&parsim;": { "codepoints": [10995], "characters": "\u2AF3" },
+  "&parsl;": { "codepoints": [11005], "characters": "\u2AFD" },
+  "&part;": { "codepoints": [8706], "characters": "\u2202" },
+  "&pcy;": { "codepoints": [1087], "characters": "\u043F" },
+  "&percnt;": { "codepoints": [37], "characters": "\u0025" },
+  "&period;": { "codepoints": [46], "characters": "\u002E" },
+  "&permil;": { "codepoints": [8240], "characters": "\u2030" },
+  "&perp;": { "codepoints": [8869], "characters": "\u22A5" },
+  "&pertenk;": { "codepoints": [8241], "characters": "\u2031" },
+  "&pfr;": { "codepoints": [120109], "characters": "\uD835\uDD2D" },
+  "&phi;": { "codepoints": [966], "characters": "\u03C6" },
+  "&phiv;": { "codepoints": [981], "characters": "\u03D5" },
+  "&phmmat;": { "codepoints": [8499], "characters": "\u2133" },
+  "&phone;": { "codepoints": [9742], "characters": "\u260E" },
+  "&pi;": { "codepoints": [960], "characters": "\u03C0" },
+  "&pitchfork;": { "codepoints": [8916], "characters": "\u22D4" },
+  "&piv;": { "codepoints": [982], "characters": "\u03D6" },
+  "&planck;": { "codepoints": [8463], "characters": "\u210F" },
+  "&planckh;": { "codepoints": [8462], "characters": "\u210E" },
+  "&plankv;": { "codepoints": [8463], "characters": "\u210F" },
+  "&plus;": { "codepoints": [43], "characters": "\u002B" },
+  "&plusacir;": { "codepoints": [10787], "characters": "\u2A23" },
+  "&plusb;": { "codepoints": [8862], "characters": "\u229E" },
+  "&pluscir;": { "codepoints": [10786], "characters": "\u2A22" },
+  "&plusdo;": { "codepoints": [8724], "characters": "\u2214" },
+  "&plusdu;": { "codepoints": [10789], "characters": "\u2A25" },
+  "&pluse;": { "codepoints": [10866], "characters": "\u2A72" },
+  "&plusmn": { "codepoints": [177], "characters": "\u00B1" },
+  "&plusmn;": { "codepoints": [177], "characters": "\u00B1" },
+  "&plussim;": { "codepoints": [10790], "characters": "\u2A26" },
+  "&plustwo;": { "codepoints": [10791], "characters": "\u2A27" },
+  "&pm;": { "codepoints": [177], "characters": "\u00B1" },
+  "&pointint;": { "codepoints": [10773], "characters": "\u2A15" },
+  "&popf;": { "codepoints": [120161], "characters": "\uD835\uDD61" },
+  "&pound": { "codepoints": [163], "characters": "\u00A3" },
+  "&pound;": { "codepoints": [163], "characters": "\u00A3" },
+  "&pr;": { "codepoints": [8826], "characters": "\u227A" },
+  "&prE;": { "codepoints": [10931], "characters": "\u2AB3" },
+  "&prap;": { "codepoints": [10935], "characters": "\u2AB7" },
+  "&prcue;": { "codepoints": [8828], "characters": "\u227C" },
+  "&pre;": { "codepoints": [10927], "characters": "\u2AAF" },
+  "&prec;": { "codepoints": [8826], "characters": "\u227A" },
+  "&precapprox;": { "codepoints": [10935], "characters": "\u2AB7" },
+  "&preccurlyeq;": { "codepoints": [8828], "characters": "\u227C" },
+  "&preceq;": { "codepoints": [10927], "characters": "\u2AAF" },
+  "&precnapprox;": { "codepoints": [10937], "characters": "\u2AB9" },
+  "&precneqq;": { "codepoints": [10933], "characters": "\u2AB5" },
+  "&precnsim;": { "codepoints": [8936], "characters": "\u22E8" },
+  "&precsim;": { "codepoints": [8830], "characters": "\u227E" },
+  "&prime;": { "codepoints": [8242], "characters": "\u2032" },
+  "&primes;": { "codepoints": [8473], "characters": "\u2119" },
+  "&prnE;": { "codepoints": [10933], "characters": "\u2AB5" },
+  "&prnap;": { "codepoints": [10937], "characters": "\u2AB9" },
+  "&prnsim;": { "codepoints": [8936], "characters": "\u22E8" },
+  "&prod;": { "codepoints": [8719], "characters": "\u220F" },
+  "&profalar;": { "codepoints": [9006], "characters": "\u232E" },
+  "&profline;": { "codepoints": [8978], "characters": "\u2312" },
+  "&profsurf;": { "codepoints": [8979], "characters": "\u2313" },
+  "&prop;": { "codepoints": [8733], "characters": "\u221D" },
+  "&propto;": { "codepoints": [8733], "characters": "\u221D" },
+  "&prsim;": { "codepoints": [8830], "characters": "\u227E" },
+  "&prurel;": { "codepoints": [8880], "characters": "\u22B0" },
+  "&pscr;": { "codepoints": [120005], "characters": "\uD835\uDCC5" },
+  "&psi;": { "codepoints": [968], "characters": "\u03C8" },
+  "&puncsp;": { "codepoints": [8200], "characters": "\u2008" },
+  "&qfr;": { "codepoints": [120110], "characters": "\uD835\uDD2E" },
+  "&qint;": { "codepoints": [10764], "characters": "\u2A0C" },
+  "&qopf;": { "codepoints": [120162], "characters": "\uD835\uDD62" },
+  "&qprime;": { "codepoints": [8279], "characters": "\u2057" },
+  "&qscr;": { "codepoints": [120006], "characters": "\uD835\uDCC6" },
+  "&quaternions;": { "codepoints": [8461], "characters": "\u210D" },
+  "&quatint;": { "codepoints": [10774], "characters": "\u2A16" },
+  "&quest;": { "codepoints": [63], "characters": "\u003F" },
+  "&questeq;": { "codepoints": [8799], "characters": "\u225F" },
+  "&quot": { "codepoints": [34], "characters": "\u0022" },
+  "&quot;": { "codepoints": [34], "characters": "\u0022" },
+  "&rAarr;": { "codepoints": [8667], "characters": "\u21DB" },
+  "&rArr;": { "codepoints": [8658], "characters": "\u21D2" },
+  "&rAtail;": { "codepoints": [10524], "characters": "\u291C" },
+  "&rBarr;": { "codepoints": [10511], "characters": "\u290F" },
+  "&rHar;": { "codepoints": [10596], "characters": "\u2964" },
+  "&race;": { "codepoints": [8765, 817], "characters": "\u223D\u0331" },
+  "&racute;": { "codepoints": [341], "characters": "\u0155" },
+  "&radic;": { "codepoints": [8730], "characters": "\u221A" },
+  "&raemptyv;": { "codepoints": [10675], "characters": "\u29B3" },
+  "&rang;": { "codepoints": [10217], "characters": "\u27E9" },
+  "&rangd;": { "codepoints": [10642], "characters": "\u2992" },
+  "&range;": { "codepoints": [10661], "characters": "\u29A5" },
+  "&rangle;": { "codepoints": [10217], "characters": "\u27E9" },
+  "&raquo": { "codepoints": [187], "characters": "\u00BB" },
+  "&raquo;": { "codepoints": [187], "characters": "\u00BB" },
+  "&rarr;": { "codepoints": [8594], "characters": "\u2192" },
+  "&rarrap;": { "codepoints": [10613], "characters": "\u2975" },
+  "&rarrb;": { "codepoints": [8677], "characters": "\u21E5" },
+  "&rarrbfs;": { "codepoints": [10528], "characters": "\u2920" },
+  "&rarrc;": { "codepoints": [10547], "characters": "\u2933" },
+  "&rarrfs;": { "codepoints": [10526], "characters": "\u291E" },
+  "&rarrhk;": { "codepoints": [8618], "characters": "\u21AA" },
+  "&rarrlp;": { "codepoints": [8620], "characters": "\u21AC" },
+  "&rarrpl;": { "codepoints": [10565], "characters": "\u2945" },
+  "&rarrsim;": { "codepoints": [10612], "characters": "\u2974" },
+  "&rarrtl;": { "codepoints": [8611], "characters": "\u21A3" },
+  "&rarrw;": { "codepoints": [8605], "characters": "\u219D" },
+  "&ratail;": { "codepoints": [10522], "characters": "\u291A" },
+  "&ratio;": { "codepoints": [8758], "characters": "\u2236" },
+  "&rationals;": { "codepoints": [8474], "characters": "\u211A" },
+  "&rbarr;": { "codepoints": [10509], "characters": "\u290D" },
+  "&rbbrk;": { "codepoints": [10099], "characters": "\u2773" },
+  "&rbrace;": { "codepoints": [125], "characters": "\u007D" },
+  "&rbrack;": { "codepoints": [93], "characters": "\u005D" },
+  "&rbrke;": { "codepoints": [10636], "characters": "\u298C" },
+  "&rbrksld;": { "codepoints": [10638], "characters": "\u298E" },
+  "&rbrkslu;": { "codepoints": [10640], "characters": "\u2990" },
+  "&rcaron;": { "codepoints": [345], "characters": "\u0159" },
+  "&rcedil;": { "codepoints": [343], "characters": "\u0157" },
+  "&rceil;": { "codepoints": [8969], "characters": "\u2309" },
+  "&rcub;": { "codepoints": [125], "characters": "\u007D" },
+  "&rcy;": { "codepoints": [1088], "characters": "\u0440" },
+  "&rdca;": { "codepoints": [10551], "characters": "\u2937" },
+  "&rdldhar;": { "codepoints": [10601], "characters": "\u2969" },
+  "&rdquo;": { "codepoints": [8221], "characters": "\u201D" },
+  "&rdquor;": { "codepoints": [8221], "characters": "\u201D" },
+  "&rdsh;": { "codepoints": [8627], "characters": "\u21B3" },
+  "&real;": { "codepoints": [8476], "characters": "\u211C" },
+  "&realine;": { "codepoints": [8475], "characters": "\u211B" },
+  "&realpart;": { "codepoints": [8476], "characters": "\u211C" },
+  "&reals;": { "codepoints": [8477], "characters": "\u211D" },
+  "&rect;": { "codepoints": [9645], "characters": "\u25AD" },
+  "&reg": { "codepoints": [174], "characters": "\u00AE" },
+  "&reg;": { "codepoints": [174], "characters": "\u00AE" },
+  "&rfisht;": { "codepoints": [10621], "characters": "\u297D" },
+  "&rfloor;": { "codepoints": [8971], "characters": "\u230B" },
+  "&rfr;": { "codepoints": [120111], "characters": "\uD835\uDD2F" },
+  "&rhard;": { "codepoints": [8641], "characters": "\u21C1" },
+  "&rharu;": { "codepoints": [8640], "characters": "\u21C0" },
+  "&rharul;": { "codepoints": [10604], "characters": "\u296C" },
+  "&rho;": { "codepoints": [961], "characters": "\u03C1" },
+  "&rhov;": { "codepoints": [1009], "characters": "\u03F1" },
+  "&rightarrow;": { "codepoints": [8594], "characters": "\u2192" },
+  "&rightarrowtail;": { "codepoints": [8611], "characters": "\u21A3" },
+  "&rightharpoondown;": { "codepoints": [8641], "characters": "\u21C1" },
+  "&rightharpoonup;": { "codepoints": [8640], "characters": "\u21C0" },
+  "&rightleftarrows;": { "codepoints": [8644], "characters": "\u21C4" },
+  "&rightleftharpoons;": { "codepoints": [8652], "characters": "\u21CC" },
+  "&rightrightarrows;": { "codepoints": [8649], "characters": "\u21C9" },
+  "&rightsquigarrow;": { "codepoints": [8605], "characters": "\u219D" },
+  "&rightthreetimes;": { "codepoints": [8908], "characters": "\u22CC" },
+  "&ring;": { "codepoints": [730], "characters": "\u02DA" },
+  "&risingdotseq;": { "codepoints": [8787], "characters": "\u2253" },
+  "&rlarr;": { "codepoints": [8644], "characters": "\u21C4" },
+  "&rlhar;": { "codepoints": [8652], "characters": "\u21CC" },
+  "&rlm;": { "codepoints": [8207], "characters": "\u200F" },
+  "&rmoust;": { "codepoints": [9137], "characters": "\u23B1" },
+  "&rmoustache;": { "codepoints": [9137], "characters": "\u23B1" },
+  "&rnmid;": { "codepoints": [10990], "characters": "\u2AEE" },
+  "&roang;": { "codepoints": [10221], "characters": "\u27ED" },
+  "&roarr;": { "codepoints": [8702], "characters": "\u21FE" },
+  "&robrk;": { "codepoints": [10215], "characters": "\u27E7" },
+  "&ropar;": { "codepoints": [10630], "characters": "\u2986" },
+  "&ropf;": { "codepoints": [120163], "characters": "\uD835\uDD63" },
+  "&roplus;": { "codepoints": [10798], "characters": "\u2A2E" },
+  "&rotimes;": { "codepoints": [10805], "characters": "\u2A35" },
+  "&rpar;": { "codepoints": [41], "characters": "\u0029" },
+  "&rpargt;": { "codepoints": [10644], "characters": "\u2994" },
+  "&rppolint;": { "codepoints": [10770], "characters": "\u2A12" },
+  "&rrarr;": { "codepoints": [8649], "characters": "\u21C9" },
+  "&rsaquo;": { "codepoints": [8250], "characters": "\u203A" },
+  "&rscr;": { "codepoints": [120007], "characters": "\uD835\uDCC7" },
+  "&rsh;": { "codepoints": [8625], "characters": "\u21B1" },
+  "&rsqb;": { "codepoints": [93], "characters": "\u005D" },
+  "&rsquo;": { "codepoints": [8217], "characters": "\u2019" },
+  "&rsquor;": { "codepoints": [8217], "characters": "\u2019" },
+  "&rthree;": { "codepoints": [8908], "characters": "\u22CC" },
+  "&rtimes;": { "codepoints": [8906], "characters": "\u22CA" },
+  "&rtri;": { "codepoints": [9657], "characters": "\u25B9" },
+  "&rtrie;": { "codepoints": [8885], "characters": "\u22B5" },
+  "&rtrif;": { "codepoints": [9656], "characters": "\u25B8" },
+  "&rtriltri;": { "codepoints": [10702], "characters": "\u29CE" },
+  "&ruluhar;": { "codepoints": [10600], "characters": "\u2968" },
+  "&rx;": { "codepoints": [8478], "characters": "\u211E" },
+  "&sacute;": { "codepoints": [347], "characters": "\u015B" },
+  "&sbquo;": { "codepoints": [8218], "characters": "\u201A" },
+  "&sc;": { "codepoints": [8827], "characters": "\u227B" },
+  "&scE;": { "codepoints": [10932], "characters": "\u2AB4" },
+  "&scap;": { "codepoints": [10936], "characters": "\u2AB8" },
+  "&scaron;": { "codepoints": [353], "characters": "\u0161" },
+  "&sccue;": { "codepoints": [8829], "characters": "\u227D" },
+  "&sce;": { "codepoints": [10928], "characters": "\u2AB0" },
+  "&scedil;": { "codepoints": [351], "characters": "\u015F" },
+  "&scirc;": { "codepoints": [349], "characters": "\u015D" },
+  "&scnE;": { "codepoints": [10934], "characters": "\u2AB6" },
+  "&scnap;": { "codepoints": [10938], "characters": "\u2ABA" },
+  "&scnsim;": { "codepoints": [8937], "characters": "\u22E9" },
+  "&scpolint;": { "codepoints": [10771], "characters": "\u2A13" },
+  "&scsim;": { "codepoints": [8831], "characters": "\u227F" },
+  "&scy;": { "codepoints": [1089], "characters": "\u0441" },
+  "&sdot;": { "codepoints": [8901], "characters": "\u22C5" },
+  "&sdotb;": { "codepoints": [8865], "characters": "\u22A1" },
+  "&sdote;": { "codepoints": [10854], "characters": "\u2A66" },
+  "&seArr;": { "codepoints": [8664], "characters": "\u21D8" },
+  "&searhk;": { "codepoints": [10533], "characters": "\u2925" },
+  "&searr;": { "codepoints": [8600], "characters": "\u2198" },
+  "&searrow;": { "codepoints": [8600], "characters": "\u2198" },
+  "&sect": { "codepoints": [167], "characters": "\u00A7" },
+  "&sect;": { "codepoints": [167], "characters": "\u00A7" },
+  "&semi;": { "codepoints": [59], "characters": "\u003B" },
+  "&seswar;": { "codepoints": [10537], "characters": "\u2929" },
+  "&setminus;": { "codepoints": [8726], "characters": "\u2216" },
+  "&setmn;": { "codepoints": [8726], "characters": "\u2216" },
+  "&sext;": { "codepoints": [10038], "characters": "\u2736" },
+  "&sfr;": { "codepoints": [120112], "characters": "\uD835\uDD30" },
+  "&sfrown;": { "codepoints": [8994], "characters": "\u2322" },
+  "&sharp;": { "codepoints": [9839], "characters": "\u266F" },
+  "&shchcy;": { "codepoints": [1097], "characters": "\u0449" },
+  "&shcy;": { "codepoints": [1096], "characters": "\u0448" },
+  "&shortmid;": { "codepoints": [8739], "characters": "\u2223" },
+  "&shortparallel;": { "codepoints": [8741], "characters": "\u2225" },
+  "&shy": { "codepoints": [173], "characters": "\u00AD" },
+  "&shy;": { "codepoints": [173], "characters": "\u00AD" },
+  "&sigma;": { "codepoints": [963], "characters": "\u03C3" },
+  "&sigmaf;": { "codepoints": [962], "characters": "\u03C2" },
+  "&sigmav;": { "codepoints": [962], "characters": "\u03C2" },
+  "&sim;": { "codepoints": [8764], "characters": "\u223C" },
+  "&simdot;": { "codepoints": [10858], "characters": "\u2A6A" },
+  "&sime;": { "codepoints": [8771], "characters": "\u2243" },
+  "&simeq;": { "codepoints": [8771], "characters": "\u2243" },
+  "&simg;": { "codepoints": [10910], "characters": "\u2A9E" },
+  "&simgE;": { "codepoints": [10912], "characters": "\u2AA0" },
+  "&siml;": { "codepoints": [10909], "characters": "\u2A9D" },
+  "&simlE;": { "codepoints": [10911], "characters": "\u2A9F" },
+  "&simne;": { "codepoints": [8774], "characters": "\u2246" },
+  "&simplus;": { "codepoints": [10788], "characters": "\u2A24" },
+  "&simrarr;": { "codepoints": [10610], "characters": "\u2972" },
+  "&slarr;": { "codepoints": [8592], "characters": "\u2190" },
+  "&smallsetminus;": { "codepoints": [8726], "characters": "\u2216" },
+  "&smashp;": { "codepoints": [10803], "characters": "\u2A33" },
+  "&smeparsl;": { "codepoints": [10724], "characters": "\u29E4" },
+  "&smid;": { "codepoints": [8739], "characters": "\u2223" },
+  "&smile;": { "codepoints": [8995], "characters": "\u2323" },
+  "&smt;": { "codepoints": [10922], "characters": "\u2AAA" },
+  "&smte;": { "codepoints": [10924], "characters": "\u2AAC" },
+  "&smtes;": { "codepoints": [10924, 65024], "characters": "\u2AAC\uFE00" },
+  "&softcy;": { "codepoints": [1100], "characters": "\u044C" },
+  "&sol;": { "codepoints": [47], "characters": "\u002F" },
+  "&solb;": { "codepoints": [10692], "characters": "\u29C4" },
+  "&solbar;": { "codepoints": [9023], "characters": "\u233F" },
+  "&sopf;": { "codepoints": [120164], "characters": "\uD835\uDD64" },
+  "&spades;": { "codepoints": [9824], "characters": "\u2660" },
+  "&spadesuit;": { "codepoints": [9824], "characters": "\u2660" },
+  "&spar;": { "codepoints": [8741], "characters": "\u2225" },
+  "&sqcap;": { "codepoints": [8851], "characters": "\u2293" },
+  "&sqcaps;": { "codepoints": [8851, 65024], "characters": "\u2293\uFE00" },
+  "&sqcup;": { "codepoints": [8852], "characters": "\u2294" },
+  "&sqcups;": { "codepoints": [8852, 65024], "characters": "\u2294\uFE00" },
+  "&sqsub;": { "codepoints": [8847], "characters": "\u228F" },
+  "&sqsube;": { "codepoints": [8849], "characters": "\u2291" },
+  "&sqsubset;": { "codepoints": [8847], "characters": "\u228F" },
+  "&sqsubseteq;": { "codepoints": [8849], "characters": "\u2291" },
+  "&sqsup;": { "codepoints": [8848], "characters": "\u2290" },
+  "&sqsupe;": { "codepoints": [8850], "characters": "\u2292" },
+  "&sqsupset;": { "codepoints": [8848], "characters": "\u2290" },
+  "&sqsupseteq;": { "codepoints": [8850], "characters": "\u2292" },
+  "&squ;": { "codepoints": [9633], "characters": "\u25A1" },
+  "&square;": { "codepoints": [9633], "characters": "\u25A1" },
+  "&squarf;": { "codepoints": [9642], "characters": "\u25AA" },
+  "&squf;": { "codepoints": [9642], "characters": "\u25AA" },
+  "&srarr;": { "codepoints": [8594], "characters": "\u2192" },
+  "&sscr;": { "codepoints": [120008], "characters": "\uD835\uDCC8" },
+  "&ssetmn;": { "codepoints": [8726], "characters": "\u2216" },
+  "&ssmile;": { "codepoints": [8995], "characters": "\u2323" },
+  "&sstarf;": { "codepoints": [8902], "characters": "\u22C6" },
+  "&star;": { "codepoints": [9734], "characters": "\u2606" },
+  "&starf;": { "codepoints": [9733], "characters": "\u2605" },
+  "&straightepsilon;": { "codepoints": [1013], "characters": "\u03F5" },
+  "&straightphi;": { "codepoints": [981], "characters": "\u03D5" },
+  "&strns;": { "codepoints": [175], "characters": "\u00AF" },
+  "&sub;": { "codepoints": [8834], "characters": "\u2282" },
+  "&subE;": { "codepoints": [10949], "characters": "\u2AC5" },
+  "&subdot;": { "codepoints": [10941], "characters": "\u2ABD" },
+  "&sube;": { "codepoints": [8838], "characters": "\u2286" },
+  "&subedot;": { "codepoints": [10947], "characters": "\u2AC3" },
+  "&submult;": { "codepoints": [10945], "characters": "\u2AC1" },
+  "&subnE;": { "codepoints": [10955], "characters": "\u2ACB" },
+  "&subne;": { "codepoints": [8842], "characters": "\u228A" },
+  "&subplus;": { "codepoints": [10943], "characters": "\u2ABF" },
+  "&subrarr;": { "codepoints": [10617], "characters": "\u2979" },
+  "&subset;": { "codepoints": [8834], "characters": "\u2282" },
+  "&subseteq;": { "codepoints": [8838], "characters": "\u2286" },
+  "&subseteqq;": { "codepoints": [10949], "characters": "\u2AC5" },
+  "&subsetneq;": { "codepoints": [8842], "characters": "\u228A" },
+  "&subsetneqq;": { "codepoints": [10955], "characters": "\u2ACB" },
+  "&subsim;": { "codepoints": [10951], "characters": "\u2AC7" },
+  "&subsub;": { "codepoints": [10965], "characters": "\u2AD5" },
+  "&subsup;": { "codepoints": [10963], "characters": "\u2AD3" },
+  "&succ;": { "codepoints": [8827], "characters": "\u227B" },
+  "&succapprox;": { "codepoints": [10936], "characters": "\u2AB8" },
+  "&succcurlyeq;": { "codepoints": [8829], "characters": "\u227D" },
+  "&succeq;": { "codepoints": [10928], "characters": "\u2AB0" },
+  "&succnapprox;": { "codepoints": [10938], "characters": "\u2ABA" },
+  "&succneqq;": { "codepoints": [10934], "characters": "\u2AB6" },
+  "&succnsim;": { "codepoints": [8937], "characters": "\u22E9" },
+  "&succsim;": { "codepoints": [8831], "characters": "\u227F" },
+  "&sum;": { "codepoints": [8721], "characters": "\u2211" },
+  "&sung;": { "codepoints": [9834], "characters": "\u266A" },
+  "&sup1": { "codepoints": [185], "characters": "\u00B9" },
+  "&sup1;": { "codepoints": [185], "characters": "\u00B9" },
+  "&sup2": { "codepoints": [178], "characters": "\u00B2" },
+  "&sup2;": { "codepoints": [178], "characters": "\u00B2" },
+  "&sup3": { "codepoints": [179], "characters": "\u00B3" },
+  "&sup3;": { "codepoints": [179], "characters": "\u00B3" },
+  "&sup;": { "codepoints": [8835], "characters": "\u2283" },
+  "&supE;": { "codepoints": [10950], "characters": "\u2AC6" },
+  "&supdot;": { "codepoints": [10942], "characters": "\u2ABE" },
+  "&supdsub;": { "codepoints": [10968], "characters": "\u2AD8" },
+  "&supe;": { "codepoints": [8839], "characters": "\u2287" },
+  "&supedot;": { "codepoints": [10948], "characters": "\u2AC4" },
+  "&suphsol;": { "codepoints": [10185], "characters": "\u27C9" },
+  "&suphsub;": { "codepoints": [10967], "characters": "\u2AD7" },
+  "&suplarr;": { "codepoints": [10619], "characters": "\u297B" },
+  "&supmult;": { "codepoints": [10946], "characters": "\u2AC2" },
+  "&supnE;": { "codepoints": [10956], "characters": "\u2ACC" },
+  "&supne;": { "codepoints": [8843], "characters": "\u228B" },
+  "&supplus;": { "codepoints": [10944], "characters": "\u2AC0" },
+  "&supset;": { "codepoints": [8835], "characters": "\u2283" },
+  "&supseteq;": { "codepoints": [8839], "characters": "\u2287" },
+  "&supseteqq;": { "codepoints": [10950], "characters": "\u2AC6" },
+  "&supsetneq;": { "codepoints": [8843], "characters": "\u228B" },
+  "&supsetneqq;": { "codepoints": [10956], "characters": "\u2ACC" },
+  "&supsim;": { "codepoints": [10952], "characters": "\u2AC8" },
+  "&supsub;": { "codepoints": [10964], "characters": "\u2AD4" },
+  "&supsup;": { "codepoints": [10966], "characters": "\u2AD6" },
+  "&swArr;": { "codepoints": [8665], "characters": "\u21D9" },
+  "&swarhk;": { "codepoints": [10534], "characters": "\u2926" },
+  "&swarr;": { "codepoints": [8601], "characters": "\u2199" },
+  "&swarrow;": { "codepoints": [8601], "characters": "\u2199" },
+  "&swnwar;": { "codepoints": [10538], "characters": "\u292A" },
+  "&szlig": { "codepoints": [223], "characters": "\u00DF" },
+  "&szlig;": { "codepoints": [223], "characters": "\u00DF" },
+  "&target;": { "codepoints": [8982], "characters": "\u2316" },
+  "&tau;": { "codepoints": [964], "characters": "\u03C4" },
+  "&tbrk;": { "codepoints": [9140], "characters": "\u23B4" },
+  "&tcaron;": { "codepoints": [357], "characters": "\u0165" },
+  "&tcedil;": { "codepoints": [355], "characters": "\u0163" },
+  "&tcy;": { "codepoints": [1090], "characters": "\u0442" },
+  "&tdot;": { "codepoints": [8411], "characters": "\u20DB" },
+  "&telrec;": { "codepoints": [8981], "characters": "\u2315" },
+  "&tfr;": { "codepoints": [120113], "characters": "\uD835\uDD31" },
+  "&there4;": { "codepoints": [8756], "characters": "\u2234" },
+  "&therefore;": { "codepoints": [8756], "characters": "\u2234" },
+  "&theta;": { "codepoints": [952], "characters": "\u03B8" },
+  "&thetasym;": { "codepoints": [977], "characters": "\u03D1" },
+  "&thetav;": { "codepoints": [977], "characters": "\u03D1" },
+  "&thickapprox;": { "codepoints": [8776], "characters": "\u2248" },
+  "&thicksim;": { "codepoints": [8764], "characters": "\u223C" },
+  "&thinsp;": { "codepoints": [8201], "characters": "\u2009" },
+  "&thkap;": { "codepoints": [8776], "characters": "\u2248" },
+  "&thksim;": { "codepoints": [8764], "characters": "\u223C" },
+  "&thorn": { "codepoints": [254], "characters": "\u00FE" },
+  "&thorn;": { "codepoints": [254], "characters": "\u00FE" },
+  "&tilde;": { "codepoints": [732], "characters": "\u02DC" },
+  "&times": { "codepoints": [215], "characters": "\u00D7" },
+  "&times;": { "codepoints": [215], "characters": "\u00D7" },
+  "&timesb;": { "codepoints": [8864], "characters": "\u22A0" },
+  "&timesbar;": { "codepoints": [10801], "characters": "\u2A31" },
+  "&timesd;": { "codepoints": [10800], "characters": "\u2A30" },
+  "&tint;": { "codepoints": [8749], "characters": "\u222D" },
+  "&toea;": { "codepoints": [10536], "characters": "\u2928" },
+  "&top;": { "codepoints": [8868], "characters": "\u22A4" },
+  "&topbot;": { "codepoints": [9014], "characters": "\u2336" },
+  "&topcir;": { "codepoints": [10993], "characters": "\u2AF1" },
+  "&topf;": { "codepoints": [120165], "characters": "\uD835\uDD65" },
+  "&topfork;": { "codepoints": [10970], "characters": "\u2ADA" },
+  "&tosa;": { "codepoints": [10537], "characters": "\u2929" },
+  "&tprime;": { "codepoints": [8244], "characters": "\u2034" },
+  "&trade;": { "codepoints": [8482], "characters": "\u2122" },
+  "&triangle;": { "codepoints": [9653], "characters": "\u25B5" },
+  "&triangledown;": { "codepoints": [9663], "characters": "\u25BF" },
+  "&triangleleft;": { "codepoints": [9667], "characters": "\u25C3" },
+  "&trianglelefteq;": { "codepoints": [8884], "characters": "\u22B4" },
+  "&triangleq;": { "codepoints": [8796], "characters": "\u225C" },
+  "&triangleright;": { "codepoints": [9657], "characters": "\u25B9" },
+  "&trianglerighteq;": { "codepoints": [8885], "characters": "\u22B5" },
+  "&tridot;": { "codepoints": [9708], "characters": "\u25EC" },
+  "&trie;": { "codepoints": [8796], "characters": "\u225C" },
+  "&triminus;": { "codepoints": [10810], "characters": "\u2A3A" },
+  "&triplus;": { "codepoints": [10809], "characters": "\u2A39" },
+  "&trisb;": { "codepoints": [10701], "characters": "\u29CD" },
+  "&tritime;": { "codepoints": [10811], "characters": "\u2A3B" },
+  "&trpezium;": { "codepoints": [9186], "characters": "\u23E2" },
+  "&tscr;": { "codepoints": [120009], "characters": "\uD835\uDCC9" },
+  "&tscy;": { "codepoints": [1094], "characters": "\u0446" },
+  "&tshcy;": { "codepoints": [1115], "characters": "\u045B" },
+  "&tstrok;": { "codepoints": [359], "characters": "\u0167" },
+  "&twixt;": { "codepoints": [8812], "characters": "\u226C" },
+  "&twoheadleftarrow;": { "codepoints": [8606], "characters": "\u219E" },
+  "&twoheadrightarrow;": { "codepoints": [8608], "characters": "\u21A0" },
+  "&uArr;": { "codepoints": [8657], "characters": "\u21D1" },
+  "&uHar;": { "codepoints": [10595], "characters": "\u2963" },
+  "&uacute": { "codepoints": [250], "characters": "\u00FA" },
+  "&uacute;": { "codepoints": [250], "characters": "\u00FA" },
+  "&uarr;": { "codepoints": [8593], "characters": "\u2191" },
+  "&ubrcy;": { "codepoints": [1118], "characters": "\u045E" },
+  "&ubreve;": { "codepoints": [365], "characters": "\u016D" },
+  "&ucirc": { "codepoints": [251], "characters": "\u00FB" },
+  "&ucirc;": { "codepoints": [251], "characters": "\u00FB" },
+  "&ucy;": { "codepoints": [1091], "characters": "\u0443" },
+  "&udarr;": { "codepoints": [8645], "characters": "\u21C5" },
+  "&udblac;": { "codepoints": [369], "characters": "\u0171" },
+  "&udhar;": { "codepoints": [10606], "characters": "\u296E" },
+  "&ufisht;": { "codepoints": [10622], "characters": "\u297E" },
+  "&ufr;": { "codepoints": [120114], "characters": "\uD835\uDD32" },
+  "&ugrave": { "codepoints": [249], "characters": "\u00F9" },
+  "&ugrave;": { "codepoints": [249], "characters": "\u00F9" },
+  "&uharl;": { "codepoints": [8639], "characters": "\u21BF" },
+  "&uharr;": { "codepoints": [8638], "characters": "\u21BE" },
+  "&uhblk;": { "codepoints": [9600], "characters": "\u2580" },
+  "&ulcorn;": { "codepoints": [8988], "characters": "\u231C" },
+  "&ulcorner;": { "codepoints": [8988], "characters": "\u231C" },
+  "&ulcrop;": { "codepoints": [8975], "characters": "\u230F" },
+  "&ultri;": { "codepoints": [9720], "characters": "\u25F8" },
+  "&umacr;": { "codepoints": [363], "characters": "\u016B" },
+  "&uml": { "codepoints": [168], "characters": "\u00A8" },
+  "&uml;": { "codepoints": [168], "characters": "\u00A8" },
+  "&uogon;": { "codepoints": [371], "characters": "\u0173" },
+  "&uopf;": { "codepoints": [120166], "characters": "\uD835\uDD66" },
+  "&uparrow;": { "codepoints": [8593], "characters": "\u2191" },
+  "&updownarrow;": { "codepoints": [8597], "characters": "\u2195" },
+  "&upharpoonleft;": { "codepoints": [8639], "characters": "\u21BF" },
+  "&upharpoonright;": { "codepoints": [8638], "characters": "\u21BE" },
+  "&uplus;": { "codepoints": [8846], "characters": "\u228E" },
+  "&upsi;": { "codepoints": [965], "characters": "\u03C5" },
+  "&upsih;": { "codepoints": [978], "characters": "\u03D2" },
+  "&upsilon;": { "codepoints": [965], "characters": "\u03C5" },
+  "&upuparrows;": { "codepoints": [8648], "characters": "\u21C8" },
+  "&urcorn;": { "codepoints": [8989], "characters": "\u231D" },
+  "&urcorner;": { "codepoints": [8989], "characters": "\u231D" },
+  "&urcrop;": { "codepoints": [8974], "characters": "\u230E" },
+  "&uring;": { "codepoints": [367], "characters": "\u016F" },
+  "&urtri;": { "codepoints": [9721], "characters": "\u25F9" },
+  "&uscr;": { "codepoints": [120010], "characters": "\uD835\uDCCA" },
+  "&utdot;": { "codepoints": [8944], "characters": "\u22F0" },
+  "&utilde;": { "codepoints": [361], "characters": "\u0169" },
+  "&utri;": { "codepoints": [9653], "characters": "\u25B5" },
+  "&utrif;": { "codepoints": [9652], "characters": "\u25B4" },
+  "&uuarr;": { "codepoints": [8648], "characters": "\u21C8" },
+  "&uuml": { "codepoints": [252], "characters": "\u00FC" },
+  "&uuml;": { "codepoints": [252], "characters": "\u00FC" },
+  "&uwangle;": { "codepoints": [10663], "characters": "\u29A7" },
+  "&vArr;": { "codepoints": [8661], "characters": "\u21D5" },
+  "&vBar;": { "codepoints": [10984], "characters": "\u2AE8" },
+  "&vBarv;": { "codepoints": [10985], "characters": "\u2AE9" },
+  "&vDash;": { "codepoints": [8872], "characters": "\u22A8" },
+  "&vangrt;": { "codepoints": [10652], "characters": "\u299C" },
+  "&varepsilon;": { "codepoints": [1013], "characters": "\u03F5" },
+  "&varkappa;": { "codepoints": [1008], "characters": "\u03F0" },
+  "&varnothing;": { "codepoints": [8709], "characters": "\u2205" },
+  "&varphi;": { "codepoints": [981], "characters": "\u03D5" },
+  "&varpi;": { "codepoints": [982], "characters": "\u03D6" },
+  "&varpropto;": { "codepoints": [8733], "characters": "\u221D" },
+  "&varr;": { "codepoints": [8597], "characters": "\u2195" },
+  "&varrho;": { "codepoints": [1009], "characters": "\u03F1" },
+  "&varsigma;": { "codepoints": [962], "characters": "\u03C2" },
+  "&varsubsetneq;": { "codepoints": [8842, 65024], "characters": "\u228A\uFE00" },
+  "&varsubsetneqq;": { "codepoints": [10955, 65024], "characters": "\u2ACB\uFE00" },
+  "&varsupsetneq;": { "codepoints": [8843, 65024], "characters": "\u228B\uFE00" },
+  "&varsupsetneqq;": { "codepoints": [10956, 65024], "characters": "\u2ACC\uFE00" },
+  "&vartheta;": { "codepoints": [977], "characters": "\u03D1" },
+  "&vartriangleleft;": { "codepoints": [8882], "characters": "\u22B2" },
+  "&vartriangleright;": { "codepoints": [8883], "characters": "\u22B3" },
+  "&vcy;": { "codepoints": [1074], "characters": "\u0432" },
+  "&vdash;": { "codepoints": [8866], "characters": "\u22A2" },
+  "&vee;": { "codepoints": [8744], "characters": "\u2228" },
+  "&veebar;": { "codepoints": [8891], "characters": "\u22BB" },
+  "&veeeq;": { "codepoints": [8794], "characters": "\u225A" },
+  "&vellip;": { "codepoints": [8942], "characters": "\u22EE" },
+  "&verbar;": { "codepoints": [124], "characters": "\u007C" },
+  "&vert;": { "codepoints": [124], "characters": "\u007C" },
+  "&vfr;": { "codepoints": [120115], "characters": "\uD835\uDD33" },
+  "&vltri;": { "codepoints": [8882], "characters": "\u22B2" },
+  "&vnsub;": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" },
+  "&vnsup;": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" },
+  "&vopf;": { "codepoints": [120167], "characters": "\uD835\uDD67" },
+  "&vprop;": { "codepoints": [8733], "characters": "\u221D" },
+  "&vrtri;": { "codepoints": [8883], "characters": "\u22B3" },
+  "&vscr;": { "codepoints": [120011], "characters": "\uD835\uDCCB" },
+  "&vsubnE;": { "codepoints": [10955, 65024], "characters": "\u2ACB\uFE00" },
+  "&vsubne;": { "codepoints": [8842, 65024], "characters": "\u228A\uFE00" },
+  "&vsupnE;": { "codepoints": [10956, 65024], "characters": "\u2ACC\uFE00" },
+  "&vsupne;": { "codepoints": [8843, 65024], "characters": "\u228B\uFE00" },
+  "&vzigzag;": { "codepoints": [10650], "characters": "\u299A" },
+  "&wcirc;": { "codepoints": [373], "characters": "\u0175" },
+  "&wedbar;": { "codepoints": [10847], "characters": "\u2A5F" },
+  "&wedge;": { "codepoints": [8743], "characters": "\u2227" },
+  "&wedgeq;": { "codepoints": [8793], "characters": "\u2259" },
+  "&weierp;": { "codepoints": [8472], "characters": "\u2118" },
+  "&wfr;": { "codepoints": [120116], "characters": "\uD835\uDD34" },
+  "&wopf;": { "codepoints": [120168], "characters": "\uD835\uDD68" },
+  "&wp;": { "codepoints": [8472], "characters": "\u2118" },
+  "&wr;": { "codepoints": [8768], "characters": "\u2240" },
+  "&wreath;": { "codepoints": [8768], "characters": "\u2240" },
+  "&wscr;": { "codepoints": [120012], "characters": "\uD835\uDCCC" },
+  "&xcap;": { "codepoints": [8898], "characters": "\u22C2" },
+  "&xcirc;": { "codepoints": [9711], "characters": "\u25EF" },
+  "&xcup;": { "codepoints": [8899], "characters": "\u22C3" },
+  "&xdtri;": { "codepoints": [9661], "characters": "\u25BD" },
+  "&xfr;": { "codepoints": [120117], "characters": "\uD835\uDD35" },
+  "&xhArr;": { "codepoints": [10234], "characters": "\u27FA" },
+  "&xharr;": { "codepoints": [10231], "characters": "\u27F7" },
+  "&xi;": { "codepoints": [958], "characters": "\u03BE" },
+  "&xlArr;": { "codepoints": [10232], "characters": "\u27F8" },
+  "&xlarr;": { "codepoints": [10229], "characters": "\u27F5" },
+  "&xmap;": { "codepoints": [10236], "characters": "\u27FC" },
+  "&xnis;": { "codepoints": [8955], "characters": "\u22FB" },
+  "&xodot;": { "codepoints": [10752], "characters": "\u2A00" },
+  "&xopf;": { "codepoints": [120169], "characters": "\uD835\uDD69" },
+  "&xoplus;": { "codepoints": [10753], "characters": "\u2A01" },
+  "&xotime;": { "codepoints": [10754], "characters": "\u2A02" },
+  "&xrArr;": { "codepoints": [10233], "characters": "\u27F9" },
+  "&xrarr;": { "codepoints": [10230], "characters": "\u27F6" },
+  "&xscr;": { "codepoints": [120013], "characters": "\uD835\uDCCD" },
+  "&xsqcup;": { "codepoints": [10758], "characters": "\u2A06" },
+  "&xuplus;": { "codepoints": [10756], "characters": "\u2A04" },
+  "&xutri;": { "codepoints": [9651], "characters": "\u25B3" },
+  "&xvee;": { "codepoints": [8897], "characters": "\u22C1" },
+  "&xwedge;": { "codepoints": [8896], "characters": "\u22C0" },
+  "&yacute": { "codepoints": [253], "characters": "\u00FD" },
+  "&yacute;": { "codepoints": [253], "characters": "\u00FD" },
+  "&yacy;": { "codepoints": [1103], "characters": "\u044F" },
+  "&ycirc;": { "codepoints": [375], "characters": "\u0177" },
+  "&ycy;": { "codepoints": [1099], "characters": "\u044B" },
+  "&yen": { "codepoints": [165], "characters": "\u00A5" },
+  "&yen;": { "codepoints": [165], "characters": "\u00A5" },
+  "&yfr;": { "codepoints": [120118], "characters": "\uD835\uDD36" },
+  "&yicy;": { "codepoints": [1111], "characters": "\u0457" },
+  "&yopf;": { "codepoints": [120170], "characters": "\uD835\uDD6A" },
+  "&yscr;": { "codepoints": [120014], "characters": "\uD835\uDCCE" },
+  "&yucy;": { "codepoints": [1102], "characters": "\u044E" },
+  "&yuml": { "codepoints": [255], "characters": "\u00FF" },
+  "&yuml;": { "codepoints": [255], "characters": "\u00FF" },
+  "&zacute;": { "codepoints": [378], "characters": "\u017A" },
+  "&zcaron;": { "codepoints": [382], "characters": "\u017E" },
+  "&zcy;": { "codepoints": [1079], "characters": "\u0437" },
+  "&zdot;": { "codepoints": [380], "characters": "\u017C" },
+  "&zeetrf;": { "codepoints": [8488], "characters": "\u2128" },
+  "&zeta;": { "codepoints": [950], "characters": "\u03B6" },
+  "&zfr;": { "codepoints": [120119], "characters": "\uD835\uDD37" },
+  "&zhcy;": { "codepoints": [1078], "characters": "\u0436" },
+  "&zigrarr;": { "codepoints": [8669], "characters": "\u21DD" },
+  "&zopf;": { "codepoints": [120171], "characters": "\uD835\uDD6B" },
+  "&zscr;": { "codepoints": [120015], "characters": "\uD835\uDCCF" },
+  "&zwj;": { "codepoints": [8205], "characters": "\u200D" },
+  "&zwnj;": { "codepoints": [8204], "characters": "\u200C" }
+}
diff --git a/src/entity.nim b/src/entity.nim
new file mode 100644
index 00000000..3ebe9df0
--- /dev/null
+++ b/src/entity.nim
@@ -0,0 +1,18 @@
+import radixtree
+import json
+import tables
+import strutils
+import unicode
+import twtstr
+
+proc genEntityMap(): RadixTree[string] =
+  let entity = staticRead"entity.json"
+  let entityJson = parseJson(entity)
+  var entityMap = newRadixTree[string]()
+
+  for k, v in entityJson:
+    entityMap[k.substr(1)] = v{"characters"}.getStr()
+
+  return entityMap
+
+const entityMap* = genEntityMap()
diff --git a/src/enums.nim b/src/enums.nim
new file mode 100644
index 00000000..a0606332
--- /dev/null
+++ b/src/enums.nim
@@ -0,0 +1,103 @@
+type
+  NodeType* =
+    enum
+    UNKNOWN_NODE = 0,
+    ELEMENT_NODE = 1,
+    ATTRIBUTE_NODE = 2,
+    TEXT_NODE = 3,
+    CDATA_SECTION_NODE = 4,
+    ENTITY_REFERENCE_NODE = 5,
+    ENTITY_NODE = 6
+    PROCESSING_INSTRUCTION_NODE = 7,
+    COMMENT_NODE = 8,
+    DOCUMENT_NODE = 9,
+    DOCUMENT_TYPE_NODE = 10,
+    DOCUMENT_FRAGMENT_NODE = 11,
+    NOTATION_NODE = 12
+
+  DisplayType* =
+    enum
+    DISPLAY_INLINE, DISPLAY_BLOCK, DISPLAY_LIST_ITEM, DISPLAY_TABLE_COLUMN,
+    DISPLAY_INLINE_BLOCK, DISPLAY_NONE
+
+  InputType* =
+    enum
+    INPUT_UNKNOWN, INPUT_BUTTON, INPUT_CHECKBOX, INPUT_COLOR, INPUT_DATE,
+    INPUT_DATETIME_LOCAL, INPUT_EMAIL, INPUT_FILE, INPUT_HIDDEN, INPUT_IMAGE,
+    INPUT_MONTH, INPUT_NUMBER, INPUT_PASSWORD, INPUT_RADIO, INPUT_RANGE,
+    INPUT_RESET, INPUT_SEARCH, INPUT_SUBMIT, INPUT_TEL, INPUT_TEXT, INPUT_TIME,
+    INPUT_URL, INPUT_WEEK
+
+  WhitespaceType* =
+    enum
+    WHITESPACE_UNKNOWN, WHITESPACE_NORMAL, WHITESPACE_NOWRAP, WHITESPACE_PRE,
+    WHITESPACE_PRE_LINE, WHITESPACE_PRE_WRAP, WHITESPACE_INITIAL,
+    WHITESPACE_INHERIT
+
+  TagType* =
+    enum
+    TAG_UNKNOWN, TAG_HTML, TAG_BASE, TAG_HEAD, TAG_LINK, TAG_META, TAG_STYLE,
+    TAG_TITLE, TAG_BODY, TAG_ADDRESS, TAG_ARTICLE, TAG_ASIDE, TAG_FOOTER,
+    TAG_HEADER, TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6, TAG_HGROUP,
+    TAG_MAIN, TAG_NAV, TAG_SECTION, TAG_BLOCKQUOTE, TAG_DD, TAG_DIV, TAG_DL,
+    TAG_DT, TAG_FIGCAPTION, TAG_FIGURE, TAG_HR, TAG_LI, TAG_OL, TAG_P, TAG_PRE,
+    TAG_UL, TAG_A, TAG_ABBR, TAG_B, TAG_BDI, TAG_BDO, TAG_BR, TAG_CITE,
+    TAG_CODE, TAG_DATA, TAG_DFN, TAG_EM, TAG_I, TAG_KBD, TAG_MARK, TAG_Q,
+    TAG_RB, TAG_RP, TAG_RT, TAG_RTC, TAG_RUBY, TAG_S, TAG_SAMP, TAG_SMALL,
+    TAG_SPAN, TAG_STRONG, TAG_SUB, TAG_SUP, TAG_TIME, TAG_U, TAG_VAR, TAG_WBR,
+    TAG_AREA, TAG_AUDIO, TAG_IMG, TAG_MAP, TAG_TRACK, TAG_VIDEO,
+    TAG_IFRAME, TAG_OBJECT, TAG_PARAM, TAG_PICTURE, TAG_PORTAL, TAG_SOURCE,
+    TAG_CANVAS, TAG_NOSCRIPT, TAG_SCRIPT, TAG_DEL, TAG_INS, TAG_CAPTION,
+    TAG_COL, TAG_COLGROUP, TAG_TABLE, TAG_TBODY, TAG_TD, TAG_TFOOT, TAG_TH,
+    TAG_THEAD, TAG_TR, TAG_BUTTON, TAG_DATALIST, TAG_FIELDSET, TAG_FORM,
+    TAG_INPUT, TAG_LABEL, TAG_LEGEND, TAG_METER, TAG_OPTGROUP, TAG_OPTION,
+    TAG_OUTPUT, TAG_PROGRESS, TAG_SELECT, TAG_TEXTAREA, TAG_DETAILS,
+    TAG_DIALOG, TAG_MENU, TAG_SUMMARY, TAG_BLINK, TAG_CENTER, TAG_CONTENT,
+    TAG_DIR, TAG_FONT, TAG_FRAME, TAG_NOFRAMES, TAG_FRAMESET, TAG_STRIKE, TAG_TT
+
+  CSSTokenType* =
+    enum
+    CSS_NO_TOKEN, CSS_IDENT_TOKEN, CSS_FUNCTION_TOKEN, CSS_AT_KEYWORD_TOKEN,
+    CSS_HASH_TOKEN, CSS_STRING_TOKEN, CSS_BAD_STRING_TOKEN, CSS_URL_TOKEN,
+    CSS_BAD_URL_TOKEN, CSS_DELIM_TOKEN, CSS_NUMBER_TOKEN, CSS_PERCENTAGE_TOKEN,
+    CSS_DIMENSION_TOKEN, CSS_WHITESPACE_TOKEN, CSS_CDO_TOKEN, CSS_CDC_TOKEN,
+    CSS_COLON_TOKEN, CSS_SEMICOLON_TOKEN, CSS_COMMA_TOKEN, CSS_OBRACKET_TOKEN,
+    CSS_CBRACKET_TOKEN, CSS_OPAREN_TOKEN, CSS_CPAREN_TOKEN, CSS_OBRACE_TOKEN,
+    CSS_CBRACE_TOKEN 
+
+const DisplayInlineTags* = {
+  TAG_A, TAG_ABBR, TAG_B, TAG_BDO, TAG_BR, TAG_BUTTON, TAG_CITE, TAG_CODE,
+  TAG_DEL, TAG_DFN, TAG_EM, TAG_FONT, TAG_I, TAG_IMG, TAG_INS, TAG_INPUT,
+  TAG_IFRAME, TAG_KBD, TAG_LABEL, TAG_MAP, TAG_OBJECT, TAG_Q, TAG_SAMP,
+  TAG_SCRIPT, TAG_SELECT, TAG_SMALL, TAG_SPAN, TAG_STRONG, TAG_SUB, TAG_SUP,
+  TAG_TEXTAREA, TAG_TT, TAG_VAR, TAG_FONT, TAG_IFRAME, TAG_U, TAG_S, TAG_STRIKE,
+  TAG_FRAME, TAG_IMG, TAG_INPUT
+}
+
+const DisplayNoneTags* = {
+  TAG_AREA, TAG_BASE, TAG_SOURCE, TAG_TRACK, TAG_LINK, TAG_META, TAG_PARAM, TAG_WBR
+}
+
+const DisplayInlineBlockTags* = {
+  TAG_IMG
+}
+
+const DisplayTableColumnTags* = {
+  TAG_COL
+}
+
+const DisplayBlockTags* = {
+  TAG_ADDRESS, TAG_BLOCKQUOTE, TAG_CENTER, TAG_DEL, TAG_DIR, TAG_DIV, TAG_DL,
+  TAG_FIELDSET, TAG_FORM, TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6,
+  TAG_HR, TAG_INS, TAG_MENU, TAG_NOFRAMES, TAG_NOSCRIPT, TAG_OL, TAG_P, TAG_PRE,
+  TAG_TABLE, TAG_UL, TAG_CENTER, TAG_DIR, TAG_MENU, TAG_NOFRAMES, TAG_BODY,
+}
+
+const SelfClosingTagTypes* = {
+  TAG_LI, TAG_P
+}
+
+const VoidTagTypes* = {
+  TAG_AREA, TAG_BASE, TAG_BR, TAG_COL, TAG_FRAME, TAG_HR, TAG_IMG, TAG_INPUT,
+  TAG_SOURCE, TAG_TRACK, TAG_LINK, TAG_META, TAG_PARAM, TAG_WBR, TAG_HR
+}
diff --git a/src/main.nim b/src/main.nim
new file mode 100644
index 00000000..bc212ef0
--- /dev/null
+++ b/src/main.nim
@@ -0,0 +1,65 @@
+import httpClient
+import uri
+import os
+import streams
+
+import display
+import termattrs
+import buffer
+import twtio
+import config
+import parser
+
+let clientInstance = newHttpClient()
+proc loadRemotePage*(url: string): string =
+  return clientInstance.getContent(url)
+
+proc loadLocalPage*(url: string): string =
+  return readFile(url)
+
+proc getRemotePage*(url: string): Stream =
+  return clientInstance.get(url).bodyStream
+
+proc getLocalPage*(url: string): Stream =
+  return newFileStream(url, fmRead)
+
+proc getPageUri(uri: Uri): Stream =
+  var moduri = uri
+  moduri.anchor = ""
+  if uri.scheme == "" or uri.scheme == "file":
+    return getLocalPage($moduri)
+  else:
+    return getRemotePage($moduri)
+
+var buffers: seq[Buffer]
+
+proc main*() =
+  if paramCount() != 1:
+    eprint "Invalid parameters. Usage:\ntwt <url>"
+    quit(1)
+  if not readConfig("config"):
+    eprint "Failed to read keymap, falling back to default"
+  let attrs = getTermAttributes()
+  let buffer = newBuffer(attrs)
+  let uri = parseUri(paramStr(1))
+  buffers.add(buffer)
+  buffer.document = parseHtml(getPageUri(uri))
+  buffer.setLocation(uri)
+  buffer.renderHtml()
+  var lastUri = uri
+  while displayPage(attrs, buffer):
+    statusMsg("Loading...", buffer.height)
+    var newUri = buffer.document.location
+    lastUri.anchor = ""
+    newUri.anchor = ""
+    if $lastUri != $newUri:
+      buffer.clearBuffer()
+      if uri.scheme == "" and uri.path == "" and uri.anchor != "":
+        discard
+      else:
+        buffer.document = parseHtml(getPageUri(buffer.document.location))
+      buffer.renderHtml()
+    lastUri = newUri
+
+main()
+#parseCSS(newFileStream("default.css", fmRead))
diff --git a/src/parser.nim b/src/parser.nim
new file mode 100644
index 00000000..724d2b30
--- /dev/null
+++ b/src/parser.nim
@@ -0,0 +1,555 @@
+import streams
+import unicode
+import strutils
+import tables
+import json
+
+import twtio
+import enums
+import twtstr
+import dom
+import radixtree
+import style
+import entity
+
+type
+  HTMLParseState = object
+    closed: bool
+    parents: seq[Node]
+    parsedNode: Node
+    a: string
+    b: string
+    attrs: seq[string]
+    in_comment: bool
+    in_script: bool
+    in_style: bool
+    in_noscript: bool
+    parentNode: Node
+    textNode: Text
+
+  CSSParseState = object
+    in_comment: bool
+
+#func newHtmlElement(tagType: TagType, parentNode: Node): HtmlElement =
+#  case tagType
+#  of TAG_INPUT: result = new(HtmlInputElement)
+#  of TAG_A: result = new(HtmlAnchorElement)
+#  of TAG_SELECT: result = new(HtmlSelectElement)
+#  of TAG_OPTION: result = new(HtmlOptionElement)
+#  else: result = new(HtmlElement)
+#
+#  result.nodeType = ELEMENT_NODE
+#  result.tagType = tagType
+#  result.parentNode = parentNode
+#  if parentNode.isElemNode():
+#    result.parentElement = HtmlElement(parentNode)
+#
+#  if tagType in DisplayInlineTags:
+#    result.display = DISPLAY_INLINE
+#  elif tagType in DisplayBlockTags:
+#    result.display = DISPLAY_BLOCK
+#  elif tagType in DisplayInlineBlockTags:
+#    result.display = DISPLAY_INLINE_BLOCK
+#  elif tagType == TAG_LI:
+#    result.display = DISPLAY_LIST_ITEM
+#  else:
+#    result.display = DISPLAY_NONE
+#
+#  case tagType
+#  of TAG_CENTER:
+#    result.centered = true
+#  of TAG_B:
+#    result.bold = true
+#  of TAG_I:
+#    result.italic = true
+#  of TAG_U:
+#    result.underscore = true
+#  of TAG_HEAD:
+#    result.hidden = true
+#  of TAG_STYLE:
+#    result.hidden = true
+#  of TAG_SCRIPT:
+#    result.hidden = true
+#  of TAG_OPTION:
+#    result.hidden = true #TODO
+#  of TAG_PRE, TAG_TD, TAG_TH:
+#    result.margin = 1
+#  of TAG_UL, TAG_OL:
+#    result.indent = 2
+#    result.margin = 1
+#  of TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6:
+#    result.bold = true
+#    result.margin = 1
+#  of TAG_A:
+#    result.islink = true
+#  of TAG_INPUT:
+#    HtmlInputElement(result).size = 20
+#  else: discard
+#
+#  if parentNode.isElemNode():
+#    let parent = HtmlElement(parentNode)
+#    result.centered = result.centered or parent.centered
+#    result.bold = result.bold or parent.bold
+#    result.italic = result.italic or parent.italic
+#    result.underscore = result.underscore or parent.underscore
+#    result.hidden = result.hidden or parent.hidden
+#    result.islink = result.islink or parent.islink
+
+func inputSize*(str: string): int =
+  if str.len == 0:
+    return 20
+  for c in str:
+    if not c.isDigit:
+      return 20
+  return str.parseInt()
+
+#w3m's getescapecmd and parse_tag, transpiled to nim.
+#(C) Copyright 1994-2002 by Akinori Ito
+#(C) Copyright 2002-2011 by Akinori Ito, Hironori Sakamoto, Fumitoshi Ukai
+#
+#Use, modification and redistribution of this software is hereby granted,
+#provided that this entire copyright notice is included on any copies of
+#this software and applications and derivations thereof.
+#
+#This software is provided on an "as is" basis, without warranty of any
+#kind, either expressed or implied, as to any matter including, but not
+#limited to warranty of fitness of purpose, or merchantability, or
+#results obtained from use of this software.
+proc getescapecmd(buf: string, at: var int): string =
+  var i = at
+
+  if buf[i] == '#': #num
+    inc i
+    var num: int
+    if buf[i].tolower() == 'x': #hex
+      inc i
+      if not isdigit(buf[i]):
+        at = i
+        return ""
+
+      num = hexValue(buf[i])
+      inc i
+      while i < buf.len and hexValue(buf[i]) != -1:
+        num *= 0x10
+        num += hexValue(buf[i])
+        inc i
+    else: #dec
+      if not isDigit(buf[i]):
+        at = i
+        return ""
+
+      num = decValue(buf[i])
+      inc i
+      while i < buf.len and isDigit(buf[i]):
+        num *= 10
+        num += decValue(buf[i])
+        inc i
+
+    if buf[i] == ';':
+      inc i
+    at = i
+    return $(Rune(num))
+  elif not isAlphaAscii(buf[i]):
+    return ""
+
+  var n = 0
+  var s = ""
+  while true:
+    s &= buf[i]
+    if not entityMap.hasPrefix(s, n):
+      break
+    let pn = n
+    n = entityMap[s, n]
+    if n != pn:
+      s = ""
+    inc i
+
+  if entityMap.nodes[n].leaf:
+    at = i
+    return entityMap.nodes[n].value
+
+  return ""
+
+type
+  DOMParsedTag = object
+    tagid: TagType
+    attrs: Table[string, string]
+    open: bool
+
+proc parse_tag(buf: string, at: var int): DOMParsedTag =
+  var tag = DOMParsedTag()
+  tag.open = true
+
+  #Parse tag name
+  var tagname = ""
+  inc at
+  if buf[at] == '/':
+    inc at
+    tag.open = false
+    at = skipBlanks(buf, at)
+
+  while at < buf.len and not buf[at].isWhitespace() and not (tag.open and buf[at] == '/') and buf[at] != '>':
+    tagname &= buf[at].tolower()
+    at += buf.runeLenAt(at)
+
+  tag.tagid = tagType(tagname)
+  at = skipBlanks(buf, at)
+
+  while at < buf.len and buf[at] != '>':
+    var value = ""
+    var attrname = ""
+    while at < buf.len and buf[at] != '=' and not buf[at].isWhitespace() and buf[at] != '>':
+      attrname &= buf[at].tolower()
+      at += buf.runeLenAt(at)
+
+    at = skipBlanks(buf, at)
+    if buf[at] == '=':
+      inc at
+      at = skipBlanks(buf, at)
+      if at < buf.len and (buf[at] == '"' or buf[at] == '\''):
+        let startc = buf[at]
+        inc at
+        while at < buf.len and buf[at] != startc:
+          var r: Rune
+          fastRuneAt(buf, at, r)
+          if r == Rune('&'):
+            value &= getescapecmd(buf, at)
+          else:
+            value &= $r
+        if at < buf.len:
+          inc at
+      elif at < buf.len:
+        while at < buf.len and not buf[at].isWhitespace() and buf[at] != '>':
+          value &= buf[at]
+          at += buf.runeLenAt(at)
+
+    if attrname.len > 0:
+      tag.attrs[attrname] = value
+
+  while at < buf.len and buf[at] != '>':
+    at += buf.runeLenAt(at)
+
+  if at < buf.len and buf[at] == '>':
+    inc at
+  return tag
+
+proc insertNode(parent: Node, node: Node) =
+  parent.childNodes.add(node)
+
+  if parent.firstChild == nil:
+    parent.firstChild = node
+
+  parent.lastChild = node
+
+  if parent.childNodes.len > 1:
+    let prevSibling = parent.childNodes[^1]
+    prevSibling.nextSibling = node
+    node.previousSibling = prevSibling
+
+  node.parentNode = parent
+  if parent.nodeType == ELEMENT_NODE:
+    node.parentElement = Element(parent)
+
+  if parent.ownerDocument != nil:
+    node.ownerDocument = parent.ownerDocument
+  elif parent.nodeType == DOCUMENT_NODE:
+    node.ownerDocument = Document(parent)
+
+proc processDocumentStartNode(state: var HTMLParseState, newNode: Node) =
+  insertNode(state.parentNode, newNode)
+  state.parentNode = newNode
+
+proc processDocumentEndNode(state: var HTMLParseState) =
+  if state.parentNode == nil or state.parentNode.parentNode == nil:
+    return
+  state.parentNode = state.parentNode.parentNode
+
+proc processDocumentText(state: var HTMLParseState) =
+  if state.textNode == nil:
+    state.textNode = newText()
+
+    processDocumentStartNode(state, state.textNode)
+    processDocumentEndNode(state)
+
+proc processDocumentStartElement(state: var HTMLParseState, element: Element, tag: DOMParsedTag) =
+  for k, v in tag.attrs:
+    element.attributes[k] = element.newAttr(k, v)
+  
+  element.id = element.getAttrValue("id")
+  if element.attributes.hasKey("class"):
+    for w in unicode.split(element.attributes["class"].value, Rune(' ')):
+      element.classList.add(w)
+
+  case element.tagType
+  of TAG_SCRIPT:
+    state.in_script = true
+  of TAG_NOSCRIPT:
+    state.in_noscript = true
+  of TAG_STYLE:
+    state.in_style = true
+  of TAG_SELECT:
+    HTMLSelectElement(element).name = element.getAttrValue("name")
+    HTMLSelectElement(element).value = element.getAttrValue("value")
+  of TAG_INPUT:
+    HTMLInputElement(element).value = element.getAttrValue("value")
+    HTMLInputElement(element).itype = element.getAttrValue("type").inputType()
+    HTMLInputElement(element).size = element.getAttrValue("size").inputSize()
+  of TAG_A:
+    HTMLAnchorElement(element).href = element.getAttrValue("href")
+  of TAG_OPTION:
+    HTMLOptionElement(element).value = element.getAttrValue("href")
+  else: discard
+
+  if state.parentNode.nodeType == ELEMENT_NODE:
+    case element.tagType
+    of TAG_LI, TAG_P:
+      if Element(state.parentNode).tagType == element.tagType:
+        processDocumentEndNode(state)
+    of TAG_H1:
+      HTMLHeadingElement(element).rank = 1
+    of TAG_H2:
+      HTMLHeadingElement(element).rank = 2
+    of TAG_H3:
+      HTMLHeadingElement(element).rank = 3
+    of TAG_H4:
+      HTMLHeadingElement(element).rank = 4
+    of TAG_H5:
+      HTMLHeadingElement(element).rank = 5
+    of TAG_H6:
+      HTMLHeadingElement(element).rank = 6
+    else: discard
+
+  processDocumentStartNode(state, element)
+
+  if element.tagType in VoidTagTypes:
+    processDocumentEndNode(state)
+
+proc processDocumentEndElement(state: var HTMLParseState, tag: DOMParsedTag) =
+  if tag.tagid in VoidTagTypes:
+    return
+  if state.parentNode.nodeType == ELEMENT_NODE:
+    if Element(state.parentNode).tagType in {TAG_LI, TAG_P}:
+      processDocumentEndNode(state)
+  
+  processDocumentEndNode(state)
+
+proc processDocumentTag(state: var HTMLParseState, tag: DOMParsedTag) =
+  if state.in_script:
+    if tag.tagid == TAG_SCRIPT:
+      state.in_script = false
+    else:
+      return
+
+  if state.in_style:
+    if tag.tagid == TAG_STYLE:
+      state.in_style = false
+    else:
+      return
+
+  if state.in_noscript:
+    if tag.tagid == TAG_NOSCRIPT:
+      state.in_noscript = false
+    else:
+      return
+
+  if tag.open:
+    processDocumentStartElement(state, newHtmlElement(tag.tagid), tag)
+  else:
+    processDocumentEndElement(state, tag)
+
+proc processDocumentPart(state: var HTMLParseState, buf: string) =
+  var at = 0
+  var max = 0
+  var was_script = false
+
+  max = buf.len
+
+  while at < max:
+    case buf[at]
+    of '&':
+      inc at
+      let p = getescapecmd(buf, at)
+      if state.in_comment:
+        CharacterData(state.parentNode).data &= p
+      else:
+        processDocumentText(state)
+        state.textNode.data &= p
+    of '<':
+      if state.in_comment:
+        CharacterData(state.parentNode).data &= buf[at]
+        inc at
+      else:
+        var p = at
+        inc p
+        if p < max and buf[p] == '!':
+          inc p
+          if p < max and buf[p] == '-':
+            inc p
+            if p < max and buf[p] == '-':
+              inc p
+              at = p
+              state.in_comment = true
+              processDocumentStartNode(state, newComment())
+              if state.textNode != nil:
+                state.textNode.rawtext = state.textNode.getRawText()
+                state.textNode = nil
+
+        if not state.in_comment:
+          if state.textNode != nil:
+            state.textNode.rawtext = state.textNode.getRawText()
+            state.textNode = nil
+          p = at
+          var tag = parse_tag(buf, at)
+          was_script = state.in_script
+
+          processDocumentTag(state, tag)
+#         if (was_script) {
+#             if (state->in_script) {
+#                 ptr = p;
+#                 processDocumentText(&state->parentNode, &state->textNode);
+#                 Strcat_char(((CharacterData *)state->textNode)->data, *ptr++);
+#             } else if (buffer->javascript_enabled) {
+#                 loadJSToBuffer(buffer, childTextContentNode(state->parentNode->lastChild)->ptr, "<inline>", state->document);
+#             }
+#         }
+    elif buf[at] == '-' and state.in_comment:
+      var p = at
+      inc p
+      if p < max and buf[p] == '-':
+        inc p
+        if p < max and buf[p] == '>':
+          inc p
+          at = p
+          state.in_comment = false
+          processDocumentEndNode(state)
+
+      if state.in_comment:
+        CharacterData(state.parentNode).data &= buf[at]
+        inc at
+    else:
+      var r: Rune
+      fastRuneAt(buf, at, r)
+      if state.in_comment:
+        CharacterData(state.parentNode).data &= $r
+      else:
+        processDocumentText(state)
+        state.textNode.data &= $r
+
+proc parseHtml*(inputStream: Stream): Document =
+  let document = newDocument()
+
+  var state = HTMLParseState()
+  state.parentNode = document
+
+  var till_when = false
+
+  var buf = ""
+  var lineBuf: string
+  while not inputStream.atEnd():
+    lineBuf = inputStream.readLine()
+    buf &= lineBuf
+
+    var at = 0
+    while at < lineBuf.len:
+      case lineBuf[at]
+      of '<':
+        till_when = true
+      of '>':
+        till_when = false
+      else: discard
+      at += lineBuf.runeLenAt(at)
+
+    if till_when:
+      continue
+
+    processDocumentPart(state, buf)
+    buf = ""
+
+  inputStream.close()
+  return document
+
+proc consumeCSSString(state: var CSSParseState, line: seq[Rune], at: var int): CSSToken =
+    var s: seq[Rune]
+    let ending = line[at]
+    inc at
+    if at >= line.len:
+      return CSSToken(tokenType: CSS_STRING_TOKEN, value: s)
+
+    while line[at] != ending:
+      inc at
+
+      if at >= line.len:
+        return CSSToken(tokenType: CSS_STRING_TOKEN, value: s)
+
+      s &= line[at]
+
+      case line[at]
+      of Rune('\n'):
+        return CSSToken(tokenType: CSS_BAD_STRING_TOKEN)
+      of Rune('\\'):
+        inc at
+        if at > line.len:
+          break
+        var num = hexValue(line[at])
+        if num != -1:
+          let ca = at
+          inc at
+          while at < line.len and hexValue(line[at]) != -1 and ca - at <= 5:
+            num *= 0x10
+            num += hexValue(line[at])
+            inc at
+          if num == 0 or num > 0x10FFFF or num in {0xD800..0xDFFF}:
+            s &= Rune(0xFFFD)
+          else:
+            s &= Rune(num)
+      else: discard
+
+proc consumeCSSNumberSign(state: var CSSParseState, line: seq[Rune], at: var int): CSSToken =
+  inc at
+  if at < line.len:
+    if isNameCodePoint(line[at]):
+      discard
+  else:
+    discard
+
+
+proc consumeCSSToken(state: var CSSParseState, line: seq[Rune], at: var int): CSSToken =
+  case line[at]
+  of Rune('\n'), Rune('\t'), Rune(' '):
+    while at < line.len and line[at].isWhitespace():
+      inc at
+    return CSSToken(tokenType: CSS_WHITESPACE_TOKEN)
+  of Rune('"'):
+    return consumeCSSString(state, line, at)
+  of Rune('#'):
+    return consumeCSSNumberSign(state, line, at)
+  else: inc at
+
+proc tokenizeCSS*(inputStream: Stream): seq[CSSToken] =
+  var line: seq[Rune]
+  var state: CSSParseState
+  while not inputStream.atEnd():
+    line = inputStream.readLine().toRunes()
+    var cline: seq[Rune] = @[]
+    var lfc = false
+    for r in line:
+      case r
+      of Rune('\r'), Rune('\f'), Rune('\n'):
+        lfc = true
+      of Rune(0), Rune(0xD800)..Rune(0xDFFF):
+        cline &= Rune(0xFFFD)
+      else:
+        if lfc:
+          cline &= Rune('\n')
+        cline &= r
+
+    cline &= Rune('\n')
+    var lat = 0
+    while lat < cline.len:
+      result.add(consumeCSSToken(state, cline, lat))
+
+  inputStream.close()
+
+proc parseCSS*(inputStream: Stream) =
+  for t in tokenizeCSS(inputStream):
+    eprint t.tokenType
diff --git a/src/radixtree.nim b/src/radixtree.nim
new file mode 100644
index 00000000..b04e1c22
--- /dev/null
+++ b/src/radixtree.nim
@@ -0,0 +1,151 @@
+# Radix tree implementation, with some caveats:
+# * insertion takes forever, so try to insert only during compile-time
+# * it isn't that much faster than a hash table, even when used for e.g. parsing
+
+import tables
+import strutils
+import json
+
+type
+  RadixNode[T] = object
+    children*: Table[string, int]
+    case leaf*: bool
+    of true: value*: T
+    of false: discard
+
+  RadixTree*[T] = object
+    nodes*: seq[RadixNode[T]]
+
+func newRadixTree*[T](): RadixTree[T] =
+  result.nodes.add(RadixNode[T](leaf: false))
+
+proc `[]=`*[T](tree: var RadixTree[T], key: string, value: T) =
+  var n = 0
+  var p = 0
+  var i = 0
+  var j = 0
+  var s = ""
+  var t = ""
+  var nodeKey = ""
+  # find last matching node
+  while i < key.len:
+    s &= key[i]
+    inc i
+    if s in tree.nodes[n].children:
+      p = n
+      n = tree.nodes[n].children[s]
+      t &= s
+      j = i
+      nodeKey = s
+      s = ""
+
+  for k in tree.nodes[n].children.keys:
+    if s.len > 0 and k.startsWith(s[0]):
+      p = n
+      n = tree.nodes[n].children[k]
+      t &= k
+      nodeKey = k
+      break
+
+  # if first node, just add normally
+  if n == 0:
+    tree.nodes.add(RadixNode[T](leaf: true, value: value))
+    tree.nodes[n].children[key] = int(tree.nodes.len - 1)
+  else:
+    i = 0
+    var conflict = false
+    # compare new key with the one we found so far
+    while i < t.len and i < key.len:
+      if key[i] == t[i]:
+        inc i
+      else:
+        conflict = true
+        break
+
+    if conflict:
+      # conflict somewhere, so:
+      # * add new non-leaf to parent
+      # * add old to non-leaf
+      # * add new to non-leaf
+      # * remove old from parent
+      assert(i != 0)
+
+      tree.nodes[p].children[key.substr(j, i - 1)] = int(tree.nodes.len)
+      tree.nodes.add(RadixNode[T](leaf: false))
+      tree.nodes[^1].children[t.substr(i)] = n
+      tree.nodes[^1].children[key.substr(i)] = int(tree.nodes.len)
+      tree.nodes.add(RadixNode[T](leaf: true, value: value))
+      tree.nodes[p].children.del(nodeKey)
+    else: # new is either substr of old or old is substr of new
+      # new matches a node, so replace
+      if key.len == t.len:
+        let children = tree.nodes[n].children
+        tree.nodes[n] = RadixNode[T](leaf: true, value: value)
+        tree.nodes[n].children = children
+      elif i == j:
+      # new is longer than the old, so add child to old
+        tree.nodes[n].children[key.substr(i)] = int(tree.nodes.len)
+        tree.nodes.add(RadixNode[T](leaf: true, value: value))
+      elif i > 0:
+      # new is shorter than old, so:
+      # * add new to parent
+      # * add old to new
+      # * remove old from parent
+        tree.nodes[p].children[key.substr(j, i - 1)] = int(tree.nodes.len)
+        tree.nodes.add(RadixNode[T](leaf: true, value: value))
+        tree.nodes[^1].children[t.substr(i)] = n
+        tree.nodes[p].children.del(nodeKey)
+
+func `[]`*[T](tree: RadixTree[T], key: string, at: int = 0): int =
+  return tree.nodes[at].children.getOrDefault(key, at)
+
+func hasPrefix*[T](tree: RadixTree[T], prefix: string, at: int = 0): bool =
+  var n = at
+  var i = 0
+  var j = 0
+  var s = ""
+  while i < prefix.len:
+    s &= prefix[i]
+    inc i
+    if s in tree.nodes[n].children:
+      n = tree.nodes[n].children[s]
+      j = i
+
+  if j == prefix.len:
+    return true
+
+  for k in tree.nodes[n].children.keys:
+    if prefix.len - j < k.len and k[0] == prefix[j]:
+      i = 1
+      inc j
+      while j < prefix.len:
+        inc i
+        inc j
+        if k[i] != k[j]:
+          return false
+      return true
+
+  return false
+
+#tests
+#var tree = newRadixTree[string]()
+#tree.insert("hb", "abc")
+#tree.insert("hi", "second")
+#tree.insert("hia", "second")
+#tree.insert("hia", "third")
+#tree.insert("hiahhhooo", "two point fifth")
+#tree.insert("hiahhho", "two point sixth")
+#assert(tree.hasPrefix("h"))
+#assert(tree.hasPrefix("hi"))
+#assert(not tree.hasPrefix("hio"))
+#assert(tree.hasPrefix("hiah"))
+#assert(tree.hasPrefix("hiahhho"))
+#assert(tree.hasPrefix("hiahhhooo"))
+#assert(tree.lookup("hi", "error") != "error")
+#assert(tree.lookup("hb", "error") != "error")
+#assert(tree.lookup("hio", "error") == "error")
+#assert(tree.lookup("hia", "error") != "error")
+#assert(tree.lookup("hiahhhooo", "error") != "error")
+#assert(tree.lookup("hiahhho", "error") != "error")
+#assert(tree.lookup("hiahhhoo", "error") == "error")
+#assert(tree.lookup("h", "error") == "error")
diff --git a/src/style.nim b/src/style.nim
new file mode 100644
index 00000000..7de1e767
--- /dev/null
+++ b/src/style.nim
@@ -0,0 +1,42 @@
+import enums
+import unicode
+
+type
+  CSS2Properties* = ref object
+    rawtext*: string
+    fmttext*: seq[string]
+    x*: int
+    y*: int
+    ex*: int
+    ey*: int
+    width*: int
+    height*: int
+    hidden*: bool
+    before*: CSS2Properties
+    after*: CSS2Properties
+    margintop*: int
+    marginbottom*: int
+    marginleft*: int
+    marginright*: int
+    margin*: int
+    centered*: bool
+    display*: DisplayType
+    bold*: bool
+    italic*: bool
+    underscore*: bool
+    islink*: bool
+    selected*: bool
+    indent*: int
+
+  CSSToken* = object
+    case tokenType*: CSSTokenType
+    of CSS_IDENT_TOKEN, CSS_FUNCTION_TOKEN, CSS_AT_KEYWORD_TOKEN,
+       CSS_HASH_TOKEN, CSS_STRING_TOKEN, CSS_URL_TOKEN:
+      value*: seq[Rune]
+      tflaga*: bool #id / unrestricted
+    of CSS_DELIM_TOKEN:
+      rvalue*: Rune
+    of CSS_NUMBER_TOKEN, CSS_PERCENTAGE_TOKEN, CSS_DIMENSION_TOKEN:
+      ivalue*: int
+      tflagb*: bool #integer / number
+    else: discard
diff --git a/src/termattrs.nim b/src/termattrs.nim
new file mode 100644
index 00000000..d49800ae
--- /dev/null
+++ b/src/termattrs.nim
@@ -0,0 +1,11 @@
+import terminal
+
+type
+  TermAttributes* = object
+    termWidth*: int
+    termHeight*: int
+
+proc getTermAttributes*(): TermAttributes =
+  let attrs = TermAttributes(termWidth: terminalWidth(),
+                             termHeight: terminalHeight())
+  return attrs
diff --git a/src/twtio.nim b/src/twtio.nim
new file mode 100644
index 00000000..0fdbed7c
--- /dev/null
+++ b/src/twtio.nim
@@ -0,0 +1,180 @@
+import terminal
+import tables
+import unicode
+
+import twtstr
+import config
+import radixtree
+
+template print*(s: varargs[string, `$`]) =
+  for x in s:
+    stdout.write(x)
+
+template printesc*(s: string) =
+  for r in s.runes:
+    if r.isControlChar():
+      stdout.write(('^' & $($r)[0].getControlLetter())
+                   .ansiFgColor(fgBlue).ansiStyle(styleBright).ansiReset())
+    else:
+      stdout.write($r)
+
+template eprint*(s: varargs[string, `$`]) = {.cast(noSideEffect).}:
+  var a = false
+  for x in s:
+    if not a:
+      a = true
+    else:
+      stderr.write(' ')
+    stderr.write(x)
+  stderr.write('\n')
+
+proc termGoto*(x: int, y: int) =
+  setCursorPos(stdout, x, y)
+
+proc getNormalAction*(s: string): TwtAction =
+  if normalActionRemap.hasKey(s):
+    return normalActionRemap[s]
+  return NO_ACTION
+
+proc getLinedAction*(s: string): TwtAction =
+  if linedActionRemap.hasKey(s):
+    return linedActionRemap[s]
+  return NO_ACTION
+
+proc readLine*(prompt: string, current: var string, termwidth: int): bool =
+  var news = current.toRunes()
+  let maxlen = termwidth - prompt.len
+  var s = ""
+  var feedNext = false
+  var escNext = false
+  var comp = false
+  var compi = 0
+  var compa = 0
+  var comps = ""
+  var cursor = news.len
+  var shift = 0
+  while true:
+    let rl = news.len
+    if cursor < shift:
+      shift = max(cursor - 1, 0)
+    else:
+      while news.substr(shift, shift + cursor).width() > maxlen - 1:
+        shift += news[^1].width()
+
+    eraseLine()
+    printesc(prompt & $news.substr(shift, shift + maxlen - 1))
+
+    print('\r')
+    cursorForward(prompt.len + news.substr(shift, cursor).width())
+
+    if not feedNext:
+      s = ""
+    else:
+      feedNext = false
+    let c = getch()
+    s &= c
+    var action = getLinedAction(s)
+    if escNext:
+      action = NO_ACTION
+    case action
+    of ACTION_LINED_CANCEL:
+      return false
+    of ACTION_LINED_SUBMIT:
+      current = $news
+      return true
+    of ACTION_LINED_BACKSPACE:
+      if cursor > 0:
+        news = news.substr(0, cursor - 1) & news.substr(cursor)
+        dec cursor
+    of ACTION_LINED_DELETE:
+      if cursor > 0 and cursor < rl:
+        news = news.substr(0, cursor) & news.substr(cursor + 1)
+    of ACTION_LINED_ESC:
+      escNext = true
+    of ACTION_LINED_CLEAR:
+      news = news.substr(cursor)
+      cursor = 0
+    of ACTION_LINED_KILL:
+      news = news.substr(0, cursor)
+    of ACTION_LINED_BACK:
+      if cursor > 0:
+        dec cursor
+    of ACTION_LINED_FORWARD:
+      if cursor < rl:
+        inc cursor
+    of ACTION_LINED_PREV_WORD:
+      while cursor > 0:
+        dec cursor
+        if news[cursor].breaksWord():
+          break
+    of ACTION_LINED_NEXT_WORD:
+      while cursor < rl:
+        inc cursor
+        if cursor < rl:
+          if news[cursor].breaksWord():
+            break
+    of ACTION_LINED_KILL_WORD:
+      var chars = 0
+      while cursor > chars:
+        inc chars
+        if news[cursor - chars].breaksWord():
+          break
+      if chars > 0:
+        news = news.substr(0, cursor - chars) & news.substr(cursor)
+        cursor -= chars
+    of ACTION_LINED_COMPOSE_ON:
+      comp = true
+      compi = 0
+      compa = 0
+      comps = ""
+    of ACTION_LINED_COMPOSE_OFF:
+      comp = false
+      compi = 0
+      compa = 0
+      comps = ""
+    of ACTION_LINED_COMPOSE_TOGGLE:
+      comp = not comp
+      compi = 0
+      compa = 0
+      comps = ""
+    of ACTION_FEED_NEXT:
+      feedNext = true
+    elif comp:
+      comps &= c
+      let n = composeRemap[comps, compi]
+      if n != compi:
+        compi = n
+        compa += comps.len
+        comps = ""
+      if composeRemap.hasPrefix(comps, compi) and composeRemap.nodes[n].children.len > 0:
+        feedNext = true
+      else:
+        var cs = ""
+        if composeRemap.nodes[compi].leaf:
+          cs = composeRemap.nodes[compi].value
+        else:
+          cs = s.substr(0, compa - 1)
+        comps = s.substr(compa)
+        if not composeRemap.hasPrefix(comps, 0):
+          cs &= comps
+          comps = ""
+
+        news = news.substr(0, cursor) & cs.toRunes() & news.substr(cursor)
+        cursor += cs.runeLen()
+        compi = 0
+        compa = 0
+    elif validateUtf8(s) == -1:
+      var cs = ""
+      for c in s:
+        if not c.isControlChar():
+          cs &= c
+        elif escNext:
+          cs &= c
+          escNext = false
+      escNext = false
+      if cs.len == 0:
+        continue
+      news = news.substr(0, cursor) & cs.toRunes() & news.substr(cursor)
+      cursor += cs.runeLen()
+    else:
+      feedNext = true
diff --git a/src/twtstr.nim b/src/twtstr.nim
new file mode 100644
index 00000000..52db36cf
--- /dev/null
+++ b/src/twtstr.nim
@@ -0,0 +1,423 @@
+import terminal
+import strutils
+import unicode
+
+func ansiStyle*(str: string, style: Style): seq[string] =
+  result &= ansiStyleCode(style)
+  result &= str
+
+func ansiFgColor*(str: string, color: ForegroundColor): seq[string] =
+  result &= ansiForegroundColorCode(color)
+  result &= str
+
+func ansiReset*(str: string): seq[string] =
+  result &= str
+  result &= ansiResetCode
+
+func ansiStyle*(str: seq[string], style: Style): seq[string] =
+  return ansiStyleCode(style) & str
+
+func ansiFgColor*(str: seq[string], color: ForegroundColor): seq[string] =
+  return ansiForegroundColorCode(color) & str
+
+func ansiReset*(str: seq[string]): seq[string] =
+  return str & ansiResetCode
+
+func maxString*(str: string, max: int): string =
+  if max < str.runeLen():
+    return str.runeSubstr(0, max - 2) & "$"
+  return str
+
+func fitValueToSize*(str: string, size: int): string =
+  if str.runeLen < size:
+    return str & ' '.repeat(size - str.runeLen)
+  return str.maxString(size)
+
+func buttonFmt*(str: string): seq[string] =
+  return "[".ansiFgColor(fgRed) & str.ansiFgColor(fgRed).ansiReset() & "]".ansiFgColor(fgRed).ansiReset()
+
+func buttonFmt*(str: seq[string]): seq[string] =
+  return "[".ansiFgColor(fgRed) & str.ansiFgColor(fgRed).ansiReset() & "]".ansiFgColor(fgRed).ansiReset()
+
+func buttonRaw*(str: string): string =
+  return "[" & str & "]"
+
+func remove*(str: string, c: string): string =
+  let rem = c.toRunes()[0]
+  for rune in str.runes:
+    if rem != rune:
+      result &= $rune
+
+func isWhitespace*(c: char): bool =
+  case c
+  of ' ', '\n', '\r', '\t', '\f': return true
+  else: return false
+
+func isControlChar*(c: char): bool =
+  case c
+  of chr(0x00)..chr(0x1F): return true
+  of chr(0x7F): return true
+  else: return false
+
+func isControlChar*(r: Rune): bool =
+  case r
+  of Rune(0x00)..Rune(0x1F): return true
+  of Rune(0x7F): return true
+  else: return false
+
+func genControlCharMap*(): string =
+  for c in low(char)..high(char):
+    if c >= 'a':
+      result &= char(int(c) - int('a') + 1)
+    elif c == '?':
+      result &= char(127)
+    else:
+      result &= char(0)
+
+const controlCharMap = genControlCharMap()
+
+func getControlChar*(c: char): char =
+  return controlCharMap[int(c)]
+
+func getControlLetter*(c: char): char =
+  if int(c) <= 0x1F:
+    return char(int(c) + int('A') - 1)
+  elif c == '\x7F':
+    return '?'
+  assert(false)
+
+func findChar*(str: string, c: char, start: int = 0): int =
+  var i = start
+  while i < str.len:
+    if str[i] == c:
+      return i
+    inc i
+  return -1
+
+func findChar*(str: string, c: Rune, start: int = 0): int =
+  var i = start
+  var n = i
+  while i < str.runeLen():
+    var r: Rune
+    fastRuneAt(str, n, r)
+    if r == c:
+      return i
+    i = n
+  return -1
+
+func getLowerChars*(): string =
+  result = ""
+  for i in 0..255:
+    if chr(i) >= 'A' and chr(i) <= 'Z':
+      result &= chr(i + 32)
+    else:
+      result &= chr(i)
+
+const lowerChars = getLowerChars()
+
+func tolower*(c: char): char =
+  return lowerChars[int(c)]
+
+const breakWord = [
+  Rune('\n'), Rune('/'), Rune('\\'), Rune(' '), Rune('&'), Rune('='),
+  Rune('?'), Rune('.'), Rune(';')
+]
+
+func genHexCharMap(): seq[int] =
+  for i in 0..255:
+    case chr(i)
+    of '0'..'9': result &= i - ord('0')
+    of 'a'..'f': result &= i - ord('a') + 10
+    of 'A'..'F': result &= i - ord('A') + 10
+    else: result &= -1
+
+func genDecCharMap(): seq[int] =
+  for i in 0..255:
+    case chr(i)
+    of '0'..'9': result &= i - ord('0')
+    else: result &= -1
+
+const hexCharMap = genHexCharMap()
+const decCharMap = genDecCharMap()
+
+func hexValue*(c: char): int =
+  return hexCharMap[int(c)]
+
+func decValue*(c: char): int =
+  return decCharMap[int(c)]
+
+func isAscii*(r: Rune): bool =
+  return int(r) <= int(high(char))
+
+func hexValue*(r: Rune): int =
+  if isAscii(r):
+    return hexValue(char(r))
+  return -1
+
+func decValue*(r: Rune): int =
+  if isAscii(r):
+    return decValue(char(r))
+  return -1
+
+func breaksWord*(r: Rune): bool =
+  return r in breakWord
+
+func isAlphaAscii*(r: Rune): bool =
+  return isAscii(r) and isAlphaAscii(char(r))
+
+func isDigitAscii*(r: Rune): bool =
+  return isAscii(r) and isDigit(char(r))
+
+func isNameStartCodePoint*(r: Rune): bool =
+  return not isAscii(r) or r == Rune('_') or isAlphaAscii(r)
+
+func isNameCodePoint*(r: Rune): bool =
+  return isNameStartCodePoint(r) or isDigitAscii(r) or r == Rune('-')
+
+func substr*(s: seq[Rune], i: int, j: int): seq[Rune] =
+  if s.len == 0:
+    return @[]
+  return s[min(high(s), i)..min(high(s), j - 1)]
+
+func substr*(s: seq[Rune], i: int): seq[Rune] =
+  if i >= high(s) or s.len == 0:
+    return @[]
+  return s[min(high(s), i)..high(s)]
+
+#Measure length of rune. From https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
+
+#auxiliary function for binary search in interval table
+func bisearch(ucs: Rune, table: openarray[(int, int)]): bool =
+  var max = table.high
+  var min = 0
+  var mid: int
+
+  if int(ucs) < table[0][0] or int(ucs) > table[max][1]:
+    return false
+
+  while max >= min:
+    mid = (min + max) div 2
+    if int(ucs) > table[mid][1]:
+      min = mid + 1
+    elif int(ucs) < table[mid][0]:
+      max = mid - 1
+    else:
+      return true
+  return false
+
+#The following two functions define the column width of an ISO 10646
+#character as follows:
+#
+#   - The null character (U+0000) has a column width of 0.
+#
+#   - Other C0/C1 control characters and DEL will lead to a return
+#     value of 2.
+#
+#   - Non-spacing and enclosing combining characters (general
+#     category code Mn or Me in the Unicode database) have a
+#     column width of 0.
+#
+#   - SOFT HYPHEN (U+00AD) has a column width of 1.
+#
+#   - Other format characters (general category code Cf in the Unicode
+#     database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
+#
+#   - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
+#     have a column width of 0.
+#
+#   - Spacing characters in the East Asian Wide (W) or East Asian
+#     Full-width (F) category as defined in Unicode Technical
+#     Report #11 have a column width of 2.
+#
+#   - All remaining characters (including all printable
+#     ISO 8859-1 and WGL4 characters, Unicode control characters,
+#     etc.) have a column width of 1.
+#
+#This implementation assumes that wchar_t characters are encoded
+#in ISO 10646.
+#
+
+const combining = [
+  ( 0x0300, 0x036F ), ( 0x0483, 0x0486 ), ( 0x0488, 0x0489 ),
+  ( 0x0591, 0x05BD ), ( 0x05BF, 0x05BF ), ( 0x05C1, 0x05C2 ),
+  ( 0x05C4, 0x05C5 ), ( 0x05C7, 0x05C7 ), ( 0x0600, 0x0603 ),
+  ( 0x0610, 0x0615 ), ( 0x064B, 0x065E ), ( 0x0670, 0x0670 ),
+  ( 0x06D6, 0x06E4 ), ( 0x06E7, 0x06E8 ), ( 0x06EA, 0x06ED ),
+  ( 0x070F, 0x070F ), ( 0x0711, 0x0711 ), ( 0x0730, 0x074A ),
+  ( 0x07A6, 0x07B0 ), ( 0x07EB, 0x07F3 ), ( 0x0901, 0x0902 ),
+  ( 0x093C, 0x093C ), ( 0x0941, 0x0948 ), ( 0x094D, 0x094D ),
+  ( 0x0951, 0x0954 ), ( 0x0962, 0x0963 ), ( 0x0981, 0x0981 ),
+  ( 0x09BC, 0x09BC ), ( 0x09C1, 0x09C4 ), ( 0x09CD, 0x09CD ),
+  ( 0x09E2, 0x09E3 ), ( 0x0A01, 0x0A02 ), ( 0x0A3C, 0x0A3C ),
+  ( 0x0A41, 0x0A42 ), ( 0x0A47, 0x0A48 ), ( 0x0A4B, 0x0A4D ),
+  ( 0x0A70, 0x0A71 ), ( 0x0A81, 0x0A82 ), ( 0x0ABC, 0x0ABC ),
+  ( 0x0AC1, 0x0AC5 ), ( 0x0AC7, 0x0AC8 ), ( 0x0ACD, 0x0ACD ),
+  ( 0x0AE2, 0x0AE3 ), ( 0x0B01, 0x0B01 ), ( 0x0B3C, 0x0B3C ),
+  ( 0x0B3F, 0x0B3F ), ( 0x0B41, 0x0B43 ), ( 0x0B4D, 0x0B4D ),
+  ( 0x0B56, 0x0B56 ), ( 0x0B82, 0x0B82 ), ( 0x0BC0, 0x0BC0 ),
+  ( 0x0BCD, 0x0BCD ), ( 0x0C3E, 0x0C40 ), ( 0x0C46, 0x0C48 ),
+  ( 0x0C4A, 0x0C4D ), ( 0x0C55, 0x0C56 ), ( 0x0CBC, 0x0CBC ),
+  ( 0x0CBF, 0x0CBF ), ( 0x0CC6, 0x0CC6 ), ( 0x0CCC, 0x0CCD ),
+  ( 0x0CE2, 0x0CE3 ), ( 0x0D41, 0x0D43 ), ( 0x0D4D, 0x0D4D ),
+  ( 0x0DCA, 0x0DCA ), ( 0x0DD2, 0x0DD4 ), ( 0x0DD6, 0x0DD6 ),
+  ( 0x0E31, 0x0E31 ), ( 0x0E34, 0x0E3A ), ( 0x0E47, 0x0E4E ),
+  ( 0x0EB1, 0x0EB1 ), ( 0x0EB4, 0x0EB9 ), ( 0x0EBB, 0x0EBC ),
+  ( 0x0EC8, 0x0ECD ), ( 0x0F18, 0x0F19 ), ( 0x0F35, 0x0F35 ),
+  ( 0x0F37, 0x0F37 ), ( 0x0F39, 0x0F39 ), ( 0x0F71, 0x0F7E ),
+  ( 0x0F80, 0x0F84 ), ( 0x0F86, 0x0F87 ), ( 0x0F90, 0x0F97 ),
+  ( 0x0F99, 0x0FBC ), ( 0x0FC6, 0x0FC6 ), ( 0x102D, 0x1030 ),
+  ( 0x1032, 0x1032 ), ( 0x1036, 0x1037 ), ( 0x1039, 0x1039 ),
+  ( 0x1058, 0x1059 ), ( 0x1160, 0x11FF ), ( 0x135F, 0x135F ),
+  ( 0x1712, 0x1714 ), ( 0x1732, 0x1734 ), ( 0x1752, 0x1753 ),
+  ( 0x1772, 0x1773 ), ( 0x17B4, 0x17B5 ), ( 0x17B7, 0x17BD ),
+  ( 0x17C6, 0x17C6 ), ( 0x17C9, 0x17D3 ), ( 0x17DD, 0x17DD ),
+  ( 0x180B, 0x180D ), ( 0x18A9, 0x18A9 ), ( 0x1920, 0x1922 ),
+  ( 0x1927, 0x1928 ), ( 0x1932, 0x1932 ), ( 0x1939, 0x193B ),
+  ( 0x1A17, 0x1A18 ), ( 0x1B00, 0x1B03 ), ( 0x1B34, 0x1B34 ),
+  ( 0x1B36, 0x1B3A ), ( 0x1B3C, 0x1B3C ), ( 0x1B42, 0x1B42 ),
+  ( 0x1B6B, 0x1B73 ), ( 0x1DC0, 0x1DCA ), ( 0x1DFE, 0x1DFF ),
+  ( 0x200B, 0x200F ), ( 0x202A, 0x202E ), ( 0x2060, 0x2063 ),
+  ( 0x206A, 0x206F ), ( 0x20D0, 0x20EF ), ( 0x302A, 0x302F ),
+  ( 0x3099, 0x309A ), ( 0xA806, 0xA806 ), ( 0xA80B, 0xA80B ),
+  ( 0xA825, 0xA826 ), ( 0xFB1E, 0xFB1E ), ( 0xFE00, 0xFE0F ),
+  ( 0xFE20, 0xFE23 ), ( 0xFEFF, 0xFEFF ), ( 0xFFF9, 0xFFFB ),
+  ( 0x10A01, 0x10A03 ), ( 0x10A05, 0x10A06 ), ( 0x10A0C, 0x10A0F ),
+  ( 0x10A38, 0x10A3A ), ( 0x10A3F, 0x10A3F ), ( 0x1D167, 0x1D169 ),
+  ( 0x1D173, 0x1D182 ), ( 0x1D185, 0x1D18B ), ( 0x1D1AA, 0x1D1AD ),
+  ( 0x1D242, 0x1D244 ), ( 0xE0001, 0xE0001 ), ( 0xE0020, 0xE007F ),
+  ( 0xE0100, 0xE01EF )
+]
+
+func width*(r: Rune): int =
+  let ucs = int(r)
+  # sorted list of non-overlapping intervals of non-spacing characters
+  # generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c"
+
+  # binary search in table of non-spacing characters
+  if bisearch(r, combining):
+    return 0
+
+  if r.isControlChar():
+    return 2
+
+  # if we arrive here, ucs is not a combining or C0/C1 control character
+
+  if (ucs >= 0x1100 and
+     (ucs <= 0x115f or                    # Hangul Jamo init. consonants
+      ucs == 0x2329 or ucs == 0x232a or
+      (ucs >= 0x2e80 and ucs <= 0xa4cf and
+       ucs != 0x303f) or                  # CJK ... Yi
+      (ucs >= 0xac00 and ucs <= 0xd7a3) or # Hangul Syllables
+      (ucs >= 0xf900 and ucs <= 0xfaff) or # CJK Compatibility Ideographs
+      (ucs >= 0xfe10 and ucs <= 0xfe19) or # Vertical forms
+      (ucs >= 0xfe30 and ucs <= 0xfe6f) or # CJK Compatibility Forms
+      (ucs >= 0xff00 and ucs <= 0xff60) or # Fullwidth Forms
+      (ucs >= 0xffe0 and ucs <= 0xffe6) or
+      (ucs >= 0x20000 and ucs <= 0x2fffd) or
+      (ucs >= 0x30000 and ucs <= 0x3fffd))):
+    return 2
+  return 1
+
+func width*(s: string): int =
+  for r in s.runes():
+    result += width(r)
+
+func width*(s: seq[Rune]): int =
+  for r in s:
+    result += width(r)
+
+# 
+# The following functions are the same as mk_wcwidth() and
+# mk_wcswidth(), except that spacing characters in the East Asian
+# Ambiguous (A) category as defined in Unicode Technical Report #11
+# have a column width of 2. This variant might be useful for users of
+# CJK legacy encodings who want to migrate to UCS without changing
+# the traditional terminal character-width behaviour. It is not
+# otherwise recommended for general use.
+# 
+const ambiguous = [
+  ( 0x00A1, 0x00A1 ), ( 0x00A4, 0x00A4 ), ( 0x00A7, 0x00A8 ),
+  ( 0x00AA, 0x00AA ), ( 0x00AE, 0x00AE ), ( 0x00B0, 0x00B4 ),
+  ( 0x00B6, 0x00BA ), ( 0x00BC, 0x00BF ), ( 0x00C6, 0x00C6 ),
+  ( 0x00D0, 0x00D0 ), ( 0x00D7, 0x00D8 ), ( 0x00DE, 0x00E1 ),
+  ( 0x00E6, 0x00E6 ), ( 0x00E8, 0x00EA ), ( 0x00EC, 0x00ED ),
+  ( 0x00F0, 0x00F0 ), ( 0x00F2, 0x00F3 ), ( 0x00F7, 0x00FA ),
+  ( 0x00FC, 0x00FC ), ( 0x00FE, 0x00FE ), ( 0x0101, 0x0101 ),
+  ( 0x0111, 0x0111 ), ( 0x0113, 0x0113 ), ( 0x011B, 0x011B ),
+  ( 0x0126, 0x0127 ), ( 0x012B, 0x012B ), ( 0x0131, 0x0133 ),
+  ( 0x0138, 0x0138 ), ( 0x013F, 0x0142 ), ( 0x0144, 0x0144 ),
+  ( 0x0148, 0x014B ), ( 0x014D, 0x014D ), ( 0x0152, 0x0153 ),
+  ( 0x0166, 0x0167 ), ( 0x016B, 0x016B ), ( 0x01CE, 0x01CE ),
+  ( 0x01D0, 0x01D0 ), ( 0x01D2, 0x01D2 ), ( 0x01D4, 0x01D4 ),
+  ( 0x01D6, 0x01D6 ), ( 0x01D8, 0x01D8 ), ( 0x01DA, 0x01DA ),
+  ( 0x01DC, 0x01DC ), ( 0x0251, 0x0251 ), ( 0x0261, 0x0261 ),
+  ( 0x02C4, 0x02C4 ), ( 0x02C7, 0x02C7 ), ( 0x02C9, 0x02CB ),
+  ( 0x02CD, 0x02CD ), ( 0x02D0, 0x02D0 ), ( 0x02D8, 0x02DB ),
+  ( 0x02DD, 0x02DD ), ( 0x02DF, 0x02DF ), ( 0x0391, 0x03A1 ),
+  ( 0x03A3, 0x03A9 ), ( 0x03B1, 0x03C1 ), ( 0x03C3, 0x03C9 ),
+  ( 0x0401, 0x0401 ), ( 0x0410, 0x044F ), ( 0x0451, 0x0451 ),
+  ( 0x2010, 0x2010 ), ( 0x2013, 0x2016 ), ( 0x2018, 0x2019 ),
+  ( 0x201C, 0x201D ), ( 0x2020, 0x2022 ), ( 0x2024, 0x2027 ),
+  ( 0x2030, 0x2030 ), ( 0x2032, 0x2033 ), ( 0x2035, 0x2035 ),
+  ( 0x203B, 0x203B ), ( 0x203E, 0x203E ), ( 0x2074, 0x2074 ),
+  ( 0x207F, 0x207F ), ( 0x2081, 0x2084 ), ( 0x20AC, 0x20AC ),
+  ( 0x2103, 0x2103 ), ( 0x2105, 0x2105 ), ( 0x2109, 0x2109 ),
+  ( 0x2113, 0x2113 ), ( 0x2116, 0x2116 ), ( 0x2121, 0x2122 ),
+  ( 0x2126, 0x2126 ), ( 0x212B, 0x212B ), ( 0x2153, 0x2154 ),
+  ( 0x215B, 0x215E ), ( 0x2160, 0x216B ), ( 0x2170, 0x2179 ),
+  ( 0x2190, 0x2199 ), ( 0x21B8, 0x21B9 ), ( 0x21D2, 0x21D2 ),
+  ( 0x21D4, 0x21D4 ), ( 0x21E7, 0x21E7 ), ( 0x2200, 0x2200 ),
+  ( 0x2202, 0x2203 ), ( 0x2207, 0x2208 ), ( 0x220B, 0x220B ),
+  ( 0x220F, 0x220F ), ( 0x2211, 0x2211 ), ( 0x2215, 0x2215 ),
+  ( 0x221A, 0x221A ), ( 0x221D, 0x2220 ), ( 0x2223, 0x2223 ),
+  ( 0x2225, 0x2225 ), ( 0x2227, 0x222C ), ( 0x222E, 0x222E ),
+  ( 0x2234, 0x2237 ), ( 0x223C, 0x223D ), ( 0x2248, 0x2248 ),
+  ( 0x224C, 0x224C ), ( 0x2252, 0x2252 ), ( 0x2260, 0x2261 ),
+  ( 0x2264, 0x2267 ), ( 0x226A, 0x226B ), ( 0x226E, 0x226F ),
+  ( 0x2282, 0x2283 ), ( 0x2286, 0x2287 ), ( 0x2295, 0x2295 ),
+  ( 0x2299, 0x2299 ), ( 0x22A5, 0x22A5 ), ( 0x22BF, 0x22BF ),
+  ( 0x2312, 0x2312 ), ( 0x2460, 0x24E9 ), ( 0x24EB, 0x254B ),
+  ( 0x2550, 0x2573 ), ( 0x2580, 0x258F ), ( 0x2592, 0x2595 ),
+  ( 0x25A0, 0x25A1 ), ( 0x25A3, 0x25A9 ), ( 0x25B2, 0x25B3 ),
+  ( 0x25B6, 0x25B7 ), ( 0x25BC, 0x25BD ), ( 0x25C0, 0x25C1 ),
+  ( 0x25C6, 0x25C8 ), ( 0x25CB, 0x25CB ), ( 0x25CE, 0x25D1 ),
+  ( 0x25E2, 0x25E5 ), ( 0x25EF, 0x25EF ), ( 0x2605, 0x2606 ),
+  ( 0x2609, 0x2609 ), ( 0x260E, 0x260F ), ( 0x2614, 0x2615 ),
+  ( 0x261C, 0x261C ), ( 0x261E, 0x261E ), ( 0x2640, 0x2640 ),
+  ( 0x2642, 0x2642 ), ( 0x2660, 0x2661 ), ( 0x2663, 0x2665 ),
+  ( 0x2667, 0x266A ), ( 0x266C, 0x266D ), ( 0x266F, 0x266F ),
+  ( 0x273D, 0x273D ), ( 0x2776, 0x277F ), ( 0xE000, 0xF8FF ),
+  ( 0xFFFD, 0xFFFD ), ( 0xF0000, 0xFFFFD ), ( 0x100000, 0x10FFFD )
+]
+
+func mk_wcwidth_cjk(ucs: Rune): int =
+  # sorted list of non-overlapping intervals of East Asian Ambiguous
+  # characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c"
+
+  # binary search in table of non-spacing characters
+  if bisearch(ucs, ambiguous):
+    return 2;
+
+  return width(ucs);
+
+func mk_wcswidth_cjk(s: string): int =
+  for r in s.runes:
+    result += mk_wcwidth_cjk(r)
+  return result
+
+func skipBlanks*(buf: string, at: int): int =
+  result = at
+  while result < buf.len and buf[result].isWhitespace():
+    inc result
+
+iterator split*(s: seq[Rune], sep: Rune): seq[Rune] =
+  var i = 0
+  var prev = 0
+  while i < s.len:
+    if s[i] == sep:
+      yield s.substr(prev, i)
+      prev = i
+    inc i
+
+  if prev < i:
+    yield s.substr(prev, i)