Compare commits

..

4 Commits

Author SHA1 Message Date
056391eeca
hpcu: the slightest cleanup 2018-11-01 11:38:17 +01:00
f99615c850
Make README more AsciiDoc-compatible
It seems to almost work, except for callouts without blocks.
2018-10-08 03:54:57 +02:00
d04c140a69
ht: add generated wcwidth tables and algorithm 2018-10-07 18:09:09 +02:00
cd6c9e4d8c
ht: add generated X11 keysym maps
Now we can make sense of keysyms and translate them to text.
2018-10-07 18:09:09 +02:00
8 changed files with 8197 additions and 174 deletions

35
README
View File

@ -90,8 +90,8 @@ project that makes do with a single Makefile, even for cross-compilation on
Windows. Let us avoid CMake and the likes of it. Windows. Let us avoid CMake and the likes of it.
It seems that Go can link dynamically, therefore I could build libhaven.so It seems that Go can link dynamically, therefore I could build libhaven.so
https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ (https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ
https://stackoverflow.com/questions/1757090/shared-library-in-go and https://stackoverflow.com/questions/1757090/shared-library-in-go)
and have the rest of the package as rather small binaries linking to it. and have the rest of the package as rather small binaries linking to it.
The "cannot implicitly include runtime/cgo in a shared library" error is solved The "cannot implicitly include runtime/cgo in a shared library" error is solved
by "go install", which again requires "-pkgdir" because of privileges. by "go install", which again requires "-pkgdir" because of privileges.
@ -102,6 +102,7 @@ GUI
Probably build on top of X11/Xlib or xgb<1>. Wayland can wait until it Probably build on top of X11/Xlib or xgb<1>. Wayland can wait until it
stabilizes--it should not be a major issue switching the backends. stabilizes--it should not be a major issue switching the backends.
Vector graphics can be handled by draw2d<2>. Vector graphics can be handled by draw2d<2>.
<1> https://rosettacode.org/wiki/Window_creation/X11#Go <1> https://rosettacode.org/wiki/Window_creation/X11#Go
<2> https://github.com/llgcode/draw2d <2> https://github.com/llgcode/draw2d
@ -118,7 +119,7 @@ for FreeType fonts and it's more of a choice between vectors and bitmaps.
The looks will be heavily inspired by Haiku and Windows 2000 and the user will The looks will be heavily inspired by Haiku and Windows 2000 and the user will
have no say in this, for simplicity. have no say in this, for simplicity.
Resources: .Resources:
- https://github.com/golang/exp/tree/master/shiny is a GUI library - https://github.com/golang/exp/tree/master/shiny is a GUI library
- https://github.com/as/shiny is a fork of it - https://github.com/as/shiny is a fork of it
- http://man.cat-v.org/plan_9/1/rio has a particular, unusual model - http://man.cat-v.org/plan_9/1/rio has a particular, unusual model
@ -187,8 +188,8 @@ Only UTF8_STRING-convertible selections are synchronized.
hasp -- (lib)asciidoc syntax preprocessor hasp -- (lib)asciidoc syntax preprocessor
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provisional tool to make libasciidoc understand more syntax, namely two-line/ Provisional tool to make libasciidoc understand more syntax, namely
underlined titles for my Gitea projects. two-line/underlined titles for my Gitea projects.
ht -- terminal emulator ht -- terminal emulator
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
@ -211,10 +212,10 @@ smooth integration with non-IRC "backends" such as Slack or Mattermost.
he -- text editor he -- text editor
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
VIM controls, no scripting, no syntax highlight, single-file, made for variable- VIM controls, no scripting, no syntax highlight, single-file, made for
-width/proportional fonts. Initially done primarily to produce a text editing variable-width/proportional fonts. Initially done primarily to produce a text
widget, which is going to be an interesting challenge, arguably better solved by editing widget, which is going to be an interesting challenge, arguably better
whole program composition. Scintilla may provide some inspiration. solved by whole program composition. Scintilla may provide some inspiration.
In the second stage, support for the Language Server Protocol will be added so In the second stage, support for the Language Server Protocol will be added so
that the project can be edited using its own tools. Some scripting, perhaps that the project can be edited using its own tools. Some scripting, perhaps
@ -226,7 +227,7 @@ The real model for the editor is Qt Creator with FakeVIM, though this is not to
be a clone of it, e.g. the various "Output" lists could be just special buffers, be a clone of it, e.g. the various "Output" lists could be just special buffers,
which may be have names starting on "// ". which may be have names starting on "// ".
Resources: .Resources:
- http://doc.cat-v.org/plan_9/4th_edition/papers/sam/ - http://doc.cat-v.org/plan_9/4th_edition/papers/sam/
ho -- all-powerful organizer ho -- all-powerful organizer
@ -235,8 +236,9 @@ Zettelkasten with fulltext search, arbitrary reciprocal links, arbitrary tags.
Flat storage. Should be able to use translation dictionaries for search hints. Flat storage. Should be able to use translation dictionaries for search hints.
Indexing and search may be based on a common database, no need to get all fancy: Indexing and search may be based on a common database, no need to get all fancy:
http://rachbelaid.com/postgres-full-text-search-is-good-enough/
https://www.sqlite.org/fts3.html#full_text_index_queries (FTS4 seems better) - http://rachbelaid.com/postgres-full-text-search-is-good-enough/
- https://www.sqlite.org/fts3.html#full_text_index_queries (FTS4 seems better)
htd -- translation dictionary htd -- translation dictionary
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -267,8 +269,8 @@ using cursor keys. Possibly a dialog with image metadata.
hfm -- file manager hfm -- file manager
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
All we need to achieve here is replace Midnight Commander, which besides the All we need to achieve here is replace Midnight Commander, which besides the
most basic features includes a VFS for archives. The editing widget in read- most basic features includes a VFS for archives. The editing widget in
-only mode could be used for F3. The shell is going to work very simply, read-only mode could be used for F3. The shell is going to work very simply,
creating a PTY device and running things under TERM=dumb while decoding SGR, creating a PTY device and running things under TERM=dumb while decoding SGR,
or one could decide to run a new terminal emulator with a different shortcut. or one could decide to run a new terminal emulator with a different shortcut.
ht could probably also be integrated. ht could probably also be integrated.
@ -283,8 +285,9 @@ The first version doesn't need to be able to reference other cells, and can more
or less be a CSV editor. or less be a CSV editor.
We can take inspiration from Excel: We can take inspiration from Excel:
https://docs.microsoft.com/en-us/office/client-developer/excel/excel-recalculation
https://www.microsoft.com/en-us/research/uploads/prod/2018/03/build-systems.pdf - https://docs.microsoft.com/en-us/office/client-developer/excel/excel-recalculation
- https://www.microsoft.com/en-us/research/uploads/prod/2018/03/build-systems.pdf
The rest The rest
~~~~~~~~ ~~~~~~~~

View File

@ -133,180 +133,194 @@ func requestOwnership(origin *selectionState, time xproto.Timestamp) {
} }
} }
func handleEvent(ev nexgb.Event) { func handleXfixesSelectionNotify(e xfixes.SelectionNotifyEvent) {
switch e := ev.(type) { state, ok := selections[e.Selection]
case xfixes.SelectionNotifyEvent: if !ok {
state, ok := selections[e.Selection] return
if !ok { }
break
}
// Ownership request has been granted, don't ask ourselves for data. // Ownership request has been granted, don't ask ourselves for data.
if e.Owner == wid { if e.Owner == wid {
state.owning = e.SelectionTimestamp state.owning = e.SelectionTimestamp
break return
} }
// This should always be true. // This should always be true.
if state.owning < e.SelectionTimestamp { if state.owning < e.SelectionTimestamp {
state.owning = 0 state.owning = 0
} }
// Not checking whether we should give up when our current retrieval // Not checking whether we should give up when our current retrieval
// attempt is interrupted--the timeout mostly solves this. // attempt is interrupted--the timeout mostly solves this.
if e.Owner == xproto.WindowNone { if e.Owner == xproto.WindowNone {
break return
} }
// Don't try to process two things at once. Each request gets a few // Don't try to process two things at once. Each request gets a few
// seconds to finish, then we move on, hoping that a property race // seconds to finish, then we move on, hoping that a property race
// doesn't commence. Ideally we'd set up a separate queue for these // doesn't commence. Ideally we'd set up a separate queue for these
// skipped requests and process them later. // skipped requests and process them later.
if state.inProgress != 0 && e.Timestamp-state.inProgress < 5000 { if state.inProgress != 0 && e.Timestamp-state.inProgress < 5000 {
break return
} }
// ICCCM says we should ensure the named property doesn't exist. // ICCCM says we should ensure the named property doesn't exist.
_ = xproto.DeleteProperty(X, e.Window, e.Selection) _ = xproto.DeleteProperty(X, e.Window, e.Selection)
_ = xproto.ConvertSelection(X, e.Window, e.Selection, _ = xproto.ConvertSelection(X, e.Window, e.Selection,
atomUTF8String, e.Selection, e.Timestamp) atomUTF8String, e.Selection, e.Timestamp)
state.inProgress = e.Timestamp state.inProgress = e.Timestamp
state.incr = false state.incr = false
}
case xproto.SelectionNotifyEvent: func handleSelectionNotify(e xproto.SelectionNotifyEvent) {
state, ok := selections[e.Selection] state, ok := selections[e.Selection]
if e.Requestor != wid || !ok || e.Time != state.inProgress { if e.Requestor != wid || !ok || e.Time != state.inProgress {
break return
} }
state.inProgress = 0 state.inProgress = 0
if e.Property == xproto.AtomNone { if e.Property == xproto.AtomNone {
break return
} }
state.buffer = nil state.buffer = nil
reply, err := getProperty(e.Requestor, e.Property) reply, err := getProperty(e.Requestor, e.Property)
if err != nil { if err != nil {
break return
} }
// When you select a lot of text in VIM, it starts the ICCCM // When you select a lot of text in VIM, it starts the ICCCM
// INCR mechanism, from which there is no opt-out. // INCR mechanism, from which there is no opt-out.
if reply.Type == atomINCR { if reply.Type == atomINCR {
state.inProgress = e.Time state.inProgress = e.Time
state.incr = true state.incr = true
state.incrFailed = false state.incrFailed = false
} else if appendText(state, reply) { } else if appendText(state, reply) {
requestOwnership(state, e.Time)
}
_ = xproto.DeleteProperty(X, e.Requestor, e.Property)
}
func handlePropertyNotify(e xproto.PropertyNotifyEvent) {
state, ok := selections[e.Atom]
if e.Window != wid || e.State != xproto.PropertyNewValue ||
!ok || !state.incr {
return
}
reply, err := getProperty(e.Window, e.Atom)
if err != nil {
state.incrFailed = true
return
}
if !appendText(state, reply) {
// We need to keep deleting the property.
state.incrFailed = true
}
if reply.ValueLen == 0 {
if !state.incrFailed {
requestOwnership(state, e.Time) requestOwnership(state, e.Time)
} }
state.inProgress = 0
state.incr = false
}
_ = xproto.DeleteProperty(X, e.Requestor, e.Property) _ = xproto.DeleteProperty(X, e.Window, e.Atom)
}
func handleSelectionRequest(e xproto.SelectionRequestEvent) {
property := e.Property
if property == xproto.AtomNone {
property = e.Target
}
state, ok := selections[e.Selection]
if e.Owner != wid || !ok {
return
}
var (
typ xproto.Atom
format byte
data []byte
)
// XXX: We should also support the MULTIPLE target but it seems to be
// unimportant and largely abandoned today.
targets := []xproto.Atom{atomTARGETS, atomTIMESTAMP, atomUTF8String}
switch e.Target {
case atomTARGETS:
typ = xproto.AtomAtom
format = 32
data = make([]byte, len(targets)*4)
for i, atom := range targets {
nexgb.Put32(data[i*4:], uint32(atom))
}
case atomTIMESTAMP:
typ = xproto.AtomInteger
format = 32
data = make([]byte, 4)
nexgb.Put32(data, uint32(state.owning))
case atomUTF8String:
typ = atomUTF8String
format = 8
data = []byte(contents)
}
response := xproto.SelectionNotifyEvent{
Time: e.Time,
Requestor: e.Requestor,
Selection: e.Selection,
Target: e.Target,
Property: xproto.AtomNone,
}
if typ == 0 || len(data) > int(setup.MaximumRequestLength)*4-64 ||
state.owning == 0 || e.Time < state.owning {
// TODO: Use the INCR mechanism for large data transfers instead
// of refusing the request, or at least use PropModeAppend.
//
// According to the ICCCM we need to set up a queue for concurrent
// (requestor, selection, target, timestamp) requests that differ
// only in the target property, and process them in order. The ICCCM
// has a nice rationale. It seems to only concern INCR. The queue
// might be a map[(who, what, how, when)][](where, data, offset).
//
// NOTE: Even with BigRequests support, it may technically be
// missing on the particular X server, and XGB copies buffers to yet
// another buffer, making very large transfers a very bad idea.
} else if xproto.ChangePropertyChecked(X, xproto.PropModeReplace,
e.Requestor, property, typ, format,
uint32(len(data)/int(format/8)), data).Check() == nil {
response.Property = property
}
_ = xproto.SendEvent(X, false /* propagate */, e.Requestor,
0 /* event mask */, string(response.Bytes()))
}
func handleXEvent(ev nexgb.Event) {
switch e := ev.(type) {
case xfixes.SelectionNotifyEvent:
handleXfixesSelectionNotify(e)
case xproto.SelectionNotifyEvent:
handleSelectionNotify(e)
case xproto.PropertyNotifyEvent: case xproto.PropertyNotifyEvent:
state, ok := selections[e.Atom] handlePropertyNotify(e)
if e.Window != wid || e.State != xproto.PropertyNewValue ||
!ok || !state.incr {
break
}
reply, err := getProperty(e.Window, e.Atom)
if err != nil {
state.incrFailed = true
break
}
if !appendText(state, reply) {
// We need to keep deleting the property.
state.incrFailed = true
}
if reply.ValueLen == 0 {
if !state.incrFailed {
requestOwnership(state, e.Time)
}
state.inProgress = 0
state.incr = false
}
_ = xproto.DeleteProperty(X, e.Window, e.Atom)
case xproto.SelectionRequestEvent: case xproto.SelectionRequestEvent:
property := e.Property handleSelectionRequest(e)
if property == xproto.AtomNone {
property = e.Target
}
state, ok := selections[e.Selection]
if e.Owner != wid || !ok {
break
}
var (
typ xproto.Atom
format byte
data []byte
)
// XXX: We should also support the MULTIPLE target but it seems to be
// unimportant and largely abandoned today.
targets := []xproto.Atom{atomTARGETS, atomTIMESTAMP, atomUTF8String}
switch e.Target {
case atomTARGETS:
typ = xproto.AtomAtom
format = 32
data = make([]byte, len(targets)*4)
for i, atom := range targets {
nexgb.Put32(data[i*4:], uint32(atom))
}
case atomTIMESTAMP:
typ = xproto.AtomInteger
format = 32
data = make([]byte, 4)
nexgb.Put32(data, uint32(state.owning))
case atomUTF8String:
typ = atomUTF8String
format = 8
data = []byte(contents)
}
response := xproto.SelectionNotifyEvent{
Time: e.Time,
Requestor: e.Requestor,
Selection: e.Selection,
Target: e.Target,
Property: xproto.AtomNone,
}
if typ == 0 || len(data) > int(setup.MaximumRequestLength)*4-64 ||
state.owning == 0 || e.Time < state.owning {
// TODO: Use the INCR mechanism for large data transfers instead
// of refusing the request, or at least use PropModeAppend.
//
// According to the ICCCM we need to set up a queue for concurrent
// (requestor, selection, target, timestamp) requests that differ
// only in the target property, and process them in order. The ICCCM
// has a nice rationale. It seems to only concern INCR. The queue
// might be a map[(who, what, how, when)][](where, data, offset).
//
// NOTE: Even with BigRequests support, it may technically be
// missing on the particular X server, and XGB copies buffers to yet
// another buffer, making very large transfers a very bad idea.
} else if xproto.ChangePropertyChecked(X, xproto.PropModeReplace,
e.Requestor, property, typ, format,
uint32(len(data)/int(format/8)), data).Check() == nil {
response.Property = property
}
_ = xproto.SendEvent(X, false /* propagate */, e.Requestor,
0 /* event mask */, string(response.Bytes()))
} }
} }
@ -345,7 +359,7 @@ func main() {
return return
} }
if ev != nil { if ev != nil {
handleEvent(ev) handleXEvent(ev)
} }
} }
} }

49
ht/gen-keysyms.sh Executable file
View File

@ -0,0 +1,49 @@
#!/bin/sh
gofmt <<EOF | sed 's, *//$,,'
// Code generated by running "go generate" in janouch.name/haven. DO NOT EDIT.
package $GOPACKAGE
import "janouch.name/haven/nexgb/xproto"
$(curl --silent --show-error \
https://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h \
https://cgit.freedesktop.org/xorg/proto/x11proto/plain/XF86keysym.h \
| perl -lne '
next unless /^\#define\s+
(XF86)?(XK_)([a-zA-Z_0-9]+)\s+
0x([0-9a-fA-F]+)\s*
(?:\/\*\s*(.*?)\s*\*\/)?\s*
$/x;
my ($name, $ident, $hex, $comment) =
(($1 // "") . $3, ($1 // "") . $2 . $3, lc $4, ($5 // ""));
# They are already somewhat sorted in the source file.
push @a, { hex => $hex, ident => $ident, comment => $comment };
$nametokeysym{$name} = $ident;
# All but the first name listed should be considered deprecated.
$keysymtoname{$ident} = $name unless exists $seen{$hex};
$seen{$hex}++;
END {
print "const (";
print "$_->{ident} = 0x$_->{hex} // $_->{comment}" for @a;
print ")";
# Very large tables, should be an on-demand package :(
print "// KeysymToName maps X11 keysym constants to their names";
print "var KeysymToName = map[xproto.Keysym]string{";
print "$_: \"$keysymtoname{$_}\"," for sort keys %keysymtoname;
print "}";
print "// NameToKeysym maps X11 keysym names to their constants";
print "var NameToKeysym = map[string]xproto.Keysym{";
print "\"$_\": $nametokeysym{$_}," for sort keys %nametokeysym;
print "}";
}
')
EOF

84
ht/gen-rune-width.sh Executable file
View File

@ -0,0 +1,84 @@
#!/bin/sh
u=https://www.unicode.org/Public/
# Download and filter Unicode data files with the given category expression,
# producing a list of possibly duplicitous codepoints in decimal format
retrieve() {
curl --silent --show-error --location "$2" | perl -lne 's/#.*//; s/ //g;
next unless /^([0-9A-F]+)(?:\.\.([0-9A-F]+))?;('"$1"')$/;
print for hex $1 .. hex ($2 // $1);'
}
togo() {
sort -nu | perl -lne '
sub flush { printf "{0x%04x, 0x%04x},\n", $first, $last }
BEGIN { $first = $last = <> }
if ($_ != $last + 1) { flush; $first = $_; }
$last = $_;
END { flush if defined $first }' | column -xc 72
}
gofmt <<EOF
// Code generated by running "go generate" in janouch.name/haven. DO NOT EDIT.
package $GOPACKAGE
// RuneWidth returns the column width of Go runes, using an algorithm and tables
// derived from https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c:
// - 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 -1.
// - 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 UAX #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.
//
// Local changes:
// - Tables are generated from the latest available version of Unicode.
func RuneWidth(r rune) int {
switch {
case r == 0:
return 0
case r < 32 || r >= 0x7f && r < 0xa0:
return -1
case zeroWidthRunes.contains(r):
return 0
case fullWidthRunes.contains(r):
return 2
}
return 1
}
type runeRange struct{ first, last rune }
type runeRangeTable []runeRange
func (table runeRangeTable) contains(r rune) bool {
min, max := 0, len(table)-1
for max >= min {
mid := (min + max) / 2
if table[mid].last < r {
min = mid + 1
} else if table[mid].first > r {
max = mid - 1
} else {
return true
}
}
return false
}
var zeroWidthRunes = runeRangeTable{
$({ retrieve 'Me|Mn|Cf' $u/UCD/latest/ucd/extracted/DerivedGeneralCategory.txt;
seq 0x1160 0x11ff; echo $((0x200B)); } | grep -xv $((0x00AD)) | togo)
}
var fullWidthRunes = runeRangeTable{
$(retrieve 'W|F' $u/UCD/latest/ucd/EastAsianWidth.txt | togo)
}
EOF

49
ht/gen-unicode-map.sh Executable file
View File

@ -0,0 +1,49 @@
#!/bin/sh
gofmt <<EOF
// Code generated by running "go generate" in janouch.name/haven. DO NOT EDIT.
package $GOPACKAGE
import "janouch.name/haven/nexgb/xproto"
// KeysymToRune tries to translate an X11 keysym to an appropriate rune.
// Returns -1 when no match is found.
func KeysymToRune(ks xproto.Keysym) rune {
// Visible Latin-1 is mapped 1:1
if (ks >= 0x20 && ks <= 0x7e) || (ks >= 0xa0 && ks <= 0xff) {
return rune(ks)
}
// Directly encoded 24-bit Unicode (going even above plane 16)
if (ks & 0xff000000) == 0x01000000 {
return rune(ks & 0x00ffffff)
}
min, max := 0, len(keysymToRuneTable)-1
for max >= min {
mid := (min + max) / 2
if keysymToRuneTable[mid].keysym < ks {
min = mid + 1
} else if keysymToRuneTable[mid].keysym > ks {
max = mid - 1
} else {
return keysymToRuneTable[mid].unicode
}
}
return -1
}
var keysymToRuneTable = []struct{
keysym xproto.Keysym
unicode rune
}{
$(curl --silent --show-error --location \
https://invisible-island.net/datafiles/release/xterm.tar.gz | \
tar --wildcards -xzOf - 'xterm-*/unicode/keysym.map' | \
sort | perl -lne '
next unless /^(0x([0-9a-f]{4}))\s+U([0-9a-f]{4})\s*(?:\#\s*(.*))?$/;
my ($keysym, $ks, $unicode, $comment) = ($1, hex($2), $3, ($4 // ""));
print "{$keysym, 0x$unicode}, // $comment"
if $ks >= 0x100 && $unicode ne "0000";
')
}
EOF

6761
ht/keysyms.go Normal file

File diff suppressed because it is too large Load Diff

204
ht/rune_width.go Normal file
View File

@ -0,0 +1,204 @@
// Code generated by running "go generate" in janouch.name/haven. DO NOT EDIT.
package main
// RuneWidth returns the column width of Go runes, using an algorithm and tables
// derived from https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c:
// - 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 -1.
// - 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 UAX #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.
//
// Local changes:
// - Tables are generated from the latest available version of Unicode.
func RuneWidth(r rune) int {
switch {
case r == 0:
return 0
case r < 32 || r >= 0x7f && r < 0xa0:
return -1
case zeroWidthRunes.contains(r):
return 0
case fullWidthRunes.contains(r):
return 2
}
return 1
}
type runeRange struct{ first, last rune }
type runeRangeTable []runeRange
func (table runeRangeTable) contains(r rune) bool {
min, max := 0, len(table)-1
for max >= min {
mid := (min + max) / 2
if table[mid].last < r {
min = mid + 1
} else if table[mid].first > r {
max = mid - 1
} else {
return true
}
}
return false
}
var zeroWidthRunes = runeRangeTable{
{0x0300, 0x036f}, {0x0483, 0x0489}, {0x0591, 0x05bd},
{0x05bf, 0x05bf}, {0x05c1, 0x05c2}, {0x05c4, 0x05c5},
{0x05c7, 0x05c7}, {0x0600, 0x0605}, {0x0610, 0x061a},
{0x061c, 0x061c}, {0x064b, 0x065f}, {0x0670, 0x0670},
{0x06d6, 0x06dd}, {0x06df, 0x06e4}, {0x06e7, 0x06e8},
{0x06ea, 0x06ed}, {0x070f, 0x070f}, {0x0711, 0x0711},
{0x0730, 0x074a}, {0x07a6, 0x07b0}, {0x07eb, 0x07f3},
{0x07fd, 0x07fd}, {0x0816, 0x0819}, {0x081b, 0x0823},
{0x0825, 0x0827}, {0x0829, 0x082d}, {0x0859, 0x085b},
{0x08d3, 0x0902}, {0x093a, 0x093a}, {0x093c, 0x093c},
{0x0941, 0x0948}, {0x094d, 0x094d}, {0x0951, 0x0957},
{0x0962, 0x0963}, {0x0981, 0x0981}, {0x09bc, 0x09bc},
{0x09c1, 0x09c4}, {0x09cd, 0x09cd}, {0x09e2, 0x09e3},
{0x09fe, 0x09fe}, {0x0a01, 0x0a02}, {0x0a3c, 0x0a3c},
{0x0a41, 0x0a42}, {0x0a47, 0x0a48}, {0x0a4b, 0x0a4d},
{0x0a51, 0x0a51}, {0x0a70, 0x0a71}, {0x0a75, 0x0a75},
{0x0a81, 0x0a82}, {0x0abc, 0x0abc}, {0x0ac1, 0x0ac5},
{0x0ac7, 0x0ac8}, {0x0acd, 0x0acd}, {0x0ae2, 0x0ae3},
{0x0afa, 0x0aff}, {0x0b01, 0x0b01}, {0x0b3c, 0x0b3c},
{0x0b3f, 0x0b3f}, {0x0b41, 0x0b44}, {0x0b4d, 0x0b4d},
{0x0b56, 0x0b56}, {0x0b62, 0x0b63}, {0x0b82, 0x0b82},
{0x0bc0, 0x0bc0}, {0x0bcd, 0x0bcd}, {0x0c00, 0x0c00},
{0x0c04, 0x0c04}, {0x0c3e, 0x0c40}, {0x0c46, 0x0c48},
{0x0c4a, 0x0c4d}, {0x0c55, 0x0c56}, {0x0c62, 0x0c63},
{0x0c81, 0x0c81}, {0x0cbc, 0x0cbc}, {0x0cbf, 0x0cbf},
{0x0cc6, 0x0cc6}, {0x0ccc, 0x0ccd}, {0x0ce2, 0x0ce3},
{0x0d00, 0x0d01}, {0x0d3b, 0x0d3c}, {0x0d41, 0x0d44},
{0x0d4d, 0x0d4d}, {0x0d62, 0x0d63}, {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}, {0x0f8d, 0x0f97}, {0x0f99, 0x0fbc},
{0x0fc6, 0x0fc6}, {0x102d, 0x1030}, {0x1032, 0x1037},
{0x1039, 0x103a}, {0x103d, 0x103e}, {0x1058, 0x1059},
{0x105e, 0x1060}, {0x1071, 0x1074}, {0x1082, 0x1082},
{0x1085, 0x1086}, {0x108d, 0x108d}, {0x109d, 0x109d},
{0x1160, 0x11ff}, {0x135d, 0x135f}, {0x1712, 0x1714},
{0x1732, 0x1734}, {0x1752, 0x1753}, {0x1772, 0x1773},
{0x17b4, 0x17b5}, {0x17b7, 0x17bd}, {0x17c6, 0x17c6},
{0x17c9, 0x17d3}, {0x17dd, 0x17dd}, {0x180b, 0x180e},
{0x1885, 0x1886}, {0x18a9, 0x18a9}, {0x1920, 0x1922},
{0x1927, 0x1928}, {0x1932, 0x1932}, {0x1939, 0x193b},
{0x1a17, 0x1a18}, {0x1a1b, 0x1a1b}, {0x1a56, 0x1a56},
{0x1a58, 0x1a5e}, {0x1a60, 0x1a60}, {0x1a62, 0x1a62},
{0x1a65, 0x1a6c}, {0x1a73, 0x1a7c}, {0x1a7f, 0x1a7f},
{0x1ab0, 0x1abe}, {0x1b00, 0x1b03}, {0x1b34, 0x1b34},
{0x1b36, 0x1b3a}, {0x1b3c, 0x1b3c}, {0x1b42, 0x1b42},
{0x1b6b, 0x1b73}, {0x1b80, 0x1b81}, {0x1ba2, 0x1ba5},
{0x1ba8, 0x1ba9}, {0x1bab, 0x1bad}, {0x1be6, 0x1be6},
{0x1be8, 0x1be9}, {0x1bed, 0x1bed}, {0x1bef, 0x1bf1},
{0x1c2c, 0x1c33}, {0x1c36, 0x1c37}, {0x1cd0, 0x1cd2},
{0x1cd4, 0x1ce0}, {0x1ce2, 0x1ce8}, {0x1ced, 0x1ced},
{0x1cf4, 0x1cf4}, {0x1cf8, 0x1cf9}, {0x1dc0, 0x1df9},
{0x1dfb, 0x1dff}, {0x200b, 0x200f}, {0x202a, 0x202e},
{0x2060, 0x2064}, {0x2066, 0x206f}, {0x20d0, 0x20f0},
{0x2cef, 0x2cf1}, {0x2d7f, 0x2d7f}, {0x2de0, 0x2dff},
{0x302a, 0x302d}, {0x3099, 0x309a}, {0xa66f, 0xa672},
{0xa674, 0xa67d}, {0xa69e, 0xa69f}, {0xa6f0, 0xa6f1},
{0xa802, 0xa802}, {0xa806, 0xa806}, {0xa80b, 0xa80b},
{0xa825, 0xa826}, {0xa8c4, 0xa8c5}, {0xa8e0, 0xa8f1},
{0xa8ff, 0xa8ff}, {0xa926, 0xa92d}, {0xa947, 0xa951},
{0xa980, 0xa982}, {0xa9b3, 0xa9b3}, {0xa9b6, 0xa9b9},
{0xa9bc, 0xa9bc}, {0xa9e5, 0xa9e5}, {0xaa29, 0xaa2e},
{0xaa31, 0xaa32}, {0xaa35, 0xaa36}, {0xaa43, 0xaa43},
{0xaa4c, 0xaa4c}, {0xaa7c, 0xaa7c}, {0xaab0, 0xaab0},
{0xaab2, 0xaab4}, {0xaab7, 0xaab8}, {0xaabe, 0xaabf},
{0xaac1, 0xaac1}, {0xaaec, 0xaaed}, {0xaaf6, 0xaaf6},
{0xabe5, 0xabe5}, {0xabe8, 0xabe8}, {0xabed, 0xabed},
{0xfb1e, 0xfb1e}, {0xfe00, 0xfe0f}, {0xfe20, 0xfe2f},
{0xfeff, 0xfeff}, {0xfff9, 0xfffb}, {0x101fd, 0x101fd},
{0x102e0, 0x102e0}, {0x10376, 0x1037a}, {0x10a01, 0x10a03},
{0x10a05, 0x10a06}, {0x10a0c, 0x10a0f}, {0x10a38, 0x10a3a},
{0x10a3f, 0x10a3f}, {0x10ae5, 0x10ae6}, {0x10d24, 0x10d27},
{0x10f46, 0x10f50}, {0x11001, 0x11001}, {0x11038, 0x11046},
{0x1107f, 0x11081}, {0x110b3, 0x110b6}, {0x110b9, 0x110ba},
{0x110bd, 0x110bd}, {0x110cd, 0x110cd}, {0x11100, 0x11102},
{0x11127, 0x1112b}, {0x1112d, 0x11134}, {0x11173, 0x11173},
{0x11180, 0x11181}, {0x111b6, 0x111be}, {0x111c9, 0x111cc},
{0x1122f, 0x11231}, {0x11234, 0x11234}, {0x11236, 0x11237},
{0x1123e, 0x1123e}, {0x112df, 0x112df}, {0x112e3, 0x112ea},
{0x11300, 0x11301}, {0x1133b, 0x1133c}, {0x11340, 0x11340},
{0x11366, 0x1136c}, {0x11370, 0x11374}, {0x11438, 0x1143f},
{0x11442, 0x11444}, {0x11446, 0x11446}, {0x1145e, 0x1145e},
{0x114b3, 0x114b8}, {0x114ba, 0x114ba}, {0x114bf, 0x114c0},
{0x114c2, 0x114c3}, {0x115b2, 0x115b5}, {0x115bc, 0x115bd},
{0x115bf, 0x115c0}, {0x115dc, 0x115dd}, {0x11633, 0x1163a},
{0x1163d, 0x1163d}, {0x1163f, 0x11640}, {0x116ab, 0x116ab},
{0x116ad, 0x116ad}, {0x116b0, 0x116b5}, {0x116b7, 0x116b7},
{0x1171d, 0x1171f}, {0x11722, 0x11725}, {0x11727, 0x1172b},
{0x1182f, 0x11837}, {0x11839, 0x1183a}, {0x11a01, 0x11a0a},
{0x11a33, 0x11a38}, {0x11a3b, 0x11a3e}, {0x11a47, 0x11a47},
{0x11a51, 0x11a56}, {0x11a59, 0x11a5b}, {0x11a8a, 0x11a96},
{0x11a98, 0x11a99}, {0x11c30, 0x11c36}, {0x11c38, 0x11c3d},
{0x11c3f, 0x11c3f}, {0x11c92, 0x11ca7}, {0x11caa, 0x11cb0},
{0x11cb2, 0x11cb3}, {0x11cb5, 0x11cb6}, {0x11d31, 0x11d36},
{0x11d3a, 0x11d3a}, {0x11d3c, 0x11d3d}, {0x11d3f, 0x11d45},
{0x11d47, 0x11d47}, {0x11d90, 0x11d91}, {0x11d95, 0x11d95},
{0x11d97, 0x11d97}, {0x11ef3, 0x11ef4}, {0x16af0, 0x16af4},
{0x16b30, 0x16b36}, {0x16f8f, 0x16f92}, {0x1bc9d, 0x1bc9e},
{0x1bca0, 0x1bca3}, {0x1d167, 0x1d169}, {0x1d173, 0x1d182},
{0x1d185, 0x1d18b}, {0x1d1aa, 0x1d1ad}, {0x1d242, 0x1d244},
{0x1da00, 0x1da36}, {0x1da3b, 0x1da6c}, {0x1da75, 0x1da75},
{0x1da84, 0x1da84}, {0x1da9b, 0x1da9f}, {0x1daa1, 0x1daaf},
{0x1e000, 0x1e006}, {0x1e008, 0x1e018}, {0x1e01b, 0x1e021},
{0x1e023, 0x1e024}, {0x1e026, 0x1e02a}, {0x1e8d0, 0x1e8d6},
{0x1e944, 0x1e94a}, {0xe0001, 0xe0001}, {0xe0020, 0xe007f},
{0xe0100, 0xe01ef},
}
var fullWidthRunes = runeRangeTable{
{0x1100, 0x115f}, {0x231a, 0x231b}, {0x2329, 0x232a},
{0x23e9, 0x23ec}, {0x23f0, 0x23f0}, {0x23f3, 0x23f3},
{0x25fd, 0x25fe}, {0x2614, 0x2615}, {0x2648, 0x2653},
{0x267f, 0x267f}, {0x2693, 0x2693}, {0x26a1, 0x26a1},
{0x26aa, 0x26ab}, {0x26bd, 0x26be}, {0x26c4, 0x26c5},
{0x26ce, 0x26ce}, {0x26d4, 0x26d4}, {0x26ea, 0x26ea},
{0x26f2, 0x26f3}, {0x26f5, 0x26f5}, {0x26fa, 0x26fa},
{0x26fd, 0x26fd}, {0x2705, 0x2705}, {0x270a, 0x270b},
{0x2728, 0x2728}, {0x274c, 0x274c}, {0x274e, 0x274e},
{0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797},
{0x27b0, 0x27b0}, {0x27bf, 0x27bf}, {0x2b1b, 0x2b1c},
{0x2b50, 0x2b50}, {0x2b55, 0x2b55}, {0x2e80, 0x2e99},
{0x2e9b, 0x2ef3}, {0x2f00, 0x2fd5}, {0x2ff0, 0x2ffb},
{0x3000, 0x303e}, {0x3041, 0x3096}, {0x3099, 0x30ff},
{0x3105, 0x312f}, {0x3131, 0x318e}, {0x3190, 0x31ba},
{0x31c0, 0x31e3}, {0x31f0, 0x321e}, {0x3220, 0x3247},
{0x3250, 0x32fe}, {0x3300, 0x4dbf}, {0x4e00, 0xa48c},
{0xa490, 0xa4c6}, {0xa960, 0xa97c}, {0xac00, 0xd7a3},
{0xf900, 0xfaff}, {0xfe10, 0xfe19}, {0xfe30, 0xfe52},
{0xfe54, 0xfe66}, {0xfe68, 0xfe6b}, {0xff01, 0xff60},
{0xffe0, 0xffe6}, {0x16fe0, 0x16fe1}, {0x17000, 0x187f1},
{0x18800, 0x18af2}, {0x1b000, 0x1b11e}, {0x1b170, 0x1b2fb},
{0x1f004, 0x1f004}, {0x1f0cf, 0x1f0cf}, {0x1f18e, 0x1f18e},
{0x1f191, 0x1f19a}, {0x1f200, 0x1f202}, {0x1f210, 0x1f23b},
{0x1f240, 0x1f248}, {0x1f250, 0x1f251}, {0x1f260, 0x1f265},
{0x1f300, 0x1f320}, {0x1f32d, 0x1f335}, {0x1f337, 0x1f37c},
{0x1f37e, 0x1f393}, {0x1f3a0, 0x1f3ca}, {0x1f3cf, 0x1f3d3},
{0x1f3e0, 0x1f3f0}, {0x1f3f4, 0x1f3f4}, {0x1f3f8, 0x1f43e},
{0x1f440, 0x1f440}, {0x1f442, 0x1f4fc}, {0x1f4ff, 0x1f53d},
{0x1f54b, 0x1f54e}, {0x1f550, 0x1f567}, {0x1f57a, 0x1f57a},
{0x1f595, 0x1f596}, {0x1f5a4, 0x1f5a4}, {0x1f5fb, 0x1f64f},
{0x1f680, 0x1f6c5}, {0x1f6cc, 0x1f6cc}, {0x1f6d0, 0x1f6d2},
{0x1f6eb, 0x1f6ec}, {0x1f6f4, 0x1f6f9}, {0x1f910, 0x1f93e},
{0x1f940, 0x1f970}, {0x1f973, 0x1f976}, {0x1f97a, 0x1f97a},
{0x1f97c, 0x1f9a2}, {0x1f9b0, 0x1f9b9}, {0x1f9c0, 0x1f9c2},
{0x1f9d0, 0x1f9ff}, {0x20000, 0x2fffd}, {0x30000, 0x3fffd},
}

859
ht/unicode_map.go Normal file
View File

@ -0,0 +1,859 @@
// Code generated by running "go generate" in janouch.name/haven. DO NOT EDIT.
package main
import "janouch.name/haven/nexgb/xproto"
// KeysymToRune tries to translate an X11 keysym to an appropriate rune.
// Returns -1 when no match is found.
func KeysymToRune(ks xproto.Keysym) rune {
// Visible Latin-1 is mapped 1:1
if (ks >= 0x20 && ks <= 0x7e) || (ks >= 0xa0 && ks <= 0xff) {
return rune(ks)
}
// Directly encoded 24-bit Unicode (going even above plane 16)
if (ks & 0xff000000) == 0x01000000 {
return rune(ks & 0x00ffffff)
}
min, max := 0, len(keysymToRuneTable)-1
for max >= min {
mid := (min + max) / 2
if keysymToRuneTable[mid].keysym < ks {
min = mid + 1
} else if keysymToRuneTable[mid].keysym > ks {
max = mid - 1
} else {
return keysymToRuneTable[mid].unicode
}
}
return -1
}
var keysymToRuneTable = []struct {
keysym xproto.Keysym
unicode rune
}{
{0x01a1, 0x0104}, // Aogonek
{0x01a2, 0x02d8}, // breve
{0x01a3, 0x0141}, // Lstroke
{0x01a5, 0x013d}, // Lcaron
{0x01a6, 0x015a}, // Sacute
{0x01a9, 0x0160}, // Scaron
{0x01aa, 0x015e}, // Scedilla
{0x01ab, 0x0164}, // Tcaron
{0x01ac, 0x0179}, // Zacute
{0x01ae, 0x017d}, // Zcaron
{0x01af, 0x017b}, // Zabovedot
{0x01b1, 0x0105}, // aogonek
{0x01b2, 0x02db}, // ogonek
{0x01b3, 0x0142}, // lstroke
{0x01b5, 0x013e}, // lcaron
{0x01b6, 0x015b}, // sacute
{0x01b7, 0x02c7}, // caron
{0x01b9, 0x0161}, // scaron
{0x01ba, 0x015f}, // scedilla
{0x01bb, 0x0165}, // tcaron
{0x01bc, 0x017a}, // zacute
{0x01bd, 0x02dd}, // doubleacute
{0x01be, 0x017e}, // zcaron
{0x01bf, 0x017c}, // zabovedot
{0x01c0, 0x0154}, // Racute
{0x01c3, 0x0102}, // Abreve
{0x01c5, 0x0139}, // Lacute
{0x01c6, 0x0106}, // Cacute
{0x01c8, 0x010c}, // Ccaron
{0x01ca, 0x0118}, // Eogonek
{0x01cc, 0x011a}, // Ecaron
{0x01cf, 0x010e}, // Dcaron
{0x01d0, 0x0110}, // Dstroke
{0x01d1, 0x0143}, // Nacute
{0x01d2, 0x0147}, // Ncaron
{0x01d5, 0x0150}, // Odoubleacute
{0x01d8, 0x0158}, // Rcaron
{0x01d9, 0x016e}, // Uring
{0x01db, 0x0170}, // Udoubleacute
{0x01de, 0x0162}, // Tcedilla
{0x01e0, 0x0155}, // racute
{0x01e3, 0x0103}, // abreve
{0x01e5, 0x013a}, // lacute
{0x01e6, 0x0107}, // cacute
{0x01e8, 0x010d}, // ccaron
{0x01ea, 0x0119}, // eogonek
{0x01ec, 0x011b}, // ecaron
{0x01ef, 0x010f}, // dcaron
{0x01f0, 0x0111}, // dstroke
{0x01f1, 0x0144}, // nacute
{0x01f2, 0x0148}, // ncaron
{0x01f5, 0x0151}, // odoubleacute
{0x01f8, 0x0159}, // rcaron
{0x01f9, 0x016f}, // uring
{0x01fb, 0x0171}, // udoubleacute
{0x01fe, 0x0163}, // tcedilla
{0x01ff, 0x02d9}, // abovedot
{0x02a1, 0x0126}, // Hstroke
{0x02a6, 0x0124}, // Hcircumflex
{0x02a9, 0x0130}, // Iabovedot
{0x02ab, 0x011e}, // Gbreve
{0x02ac, 0x0134}, // Jcircumflex
{0x02b1, 0x0127}, // hstroke
{0x02b6, 0x0125}, // hcircumflex
{0x02b9, 0x0131}, // idotless
{0x02bb, 0x011f}, // gbreve
{0x02bc, 0x0135}, // jcircumflex
{0x02c5, 0x010a}, // Cabovedot
{0x02c6, 0x0108}, // Ccircumflex
{0x02d5, 0x0120}, // Gabovedot
{0x02d8, 0x011c}, // Gcircumflex
{0x02dd, 0x016c}, // Ubreve
{0x02de, 0x015c}, // Scircumflex
{0x02e5, 0x010b}, // cabovedot
{0x02e6, 0x0109}, // ccircumflex
{0x02f5, 0x0121}, // gabovedot
{0x02f8, 0x011d}, // gcircumflex
{0x02fd, 0x016d}, // ubreve
{0x02fe, 0x015d}, // scircumflex
{0x03a2, 0x0138}, // kra
{0x03a3, 0x0156}, // Rcedilla
{0x03a5, 0x0128}, // Itilde
{0x03a6, 0x013b}, // Lcedilla
{0x03aa, 0x0112}, // Emacron
{0x03ab, 0x0122}, // Gcedilla
{0x03ac, 0x0166}, // Tslash
{0x03b3, 0x0157}, // rcedilla
{0x03b5, 0x0129}, // itilde
{0x03b6, 0x013c}, // lcedilla
{0x03ba, 0x0113}, // emacron
{0x03bb, 0x0123}, // gcedilla
{0x03bc, 0x0167}, // tslash
{0x03bd, 0x014a}, // ENG
{0x03bf, 0x014b}, // eng
{0x03c0, 0x0100}, // Amacron
{0x03c7, 0x012e}, // Iogonek
{0x03cc, 0x0116}, // Eabovedot
{0x03cf, 0x012a}, // Imacron
{0x03d1, 0x0145}, // Ncedilla
{0x03d2, 0x014c}, // Omacron
{0x03d3, 0x0136}, // Kcedilla
{0x03d9, 0x0172}, // Uogonek
{0x03dd, 0x0168}, // Utilde
{0x03de, 0x016a}, // Umacron
{0x03e0, 0x0101}, // amacron
{0x03e7, 0x012f}, // iogonek
{0x03ec, 0x0117}, // eabovedot
{0x03ef, 0x012b}, // imacron
{0x03f1, 0x0146}, // ncedilla
{0x03f2, 0x014d}, // omacron
{0x03f3, 0x0137}, // kcedilla
{0x03f9, 0x0173}, // uogonek
{0x03fd, 0x0169}, // utilde
{0x03fe, 0x016b}, // umacron
{0x047e, 0x203e}, // overline
{0x04a1, 0x3002}, // kana_fullstop
{0x04a2, 0x300c}, // kana_openingbracket
{0x04a3, 0x300d}, // kana_closingbracket
{0x04a4, 0x3001}, // kana_comma
{0x04a5, 0x30fb}, // kana_conjunctive
{0x04a6, 0x30f2}, // kana_WO
{0x04a7, 0x30a1}, // kana_a
{0x04a8, 0x30a3}, // kana_i
{0x04a9, 0x30a5}, // kana_u
{0x04aa, 0x30a7}, // kana_e
{0x04ab, 0x30a9}, // kana_o
{0x04ac, 0x30e3}, // kana_ya
{0x04ad, 0x30e5}, // kana_yu
{0x04ae, 0x30e7}, // kana_yo
{0x04af, 0x30c3}, // kana_tsu
{0x04b0, 0x30fc}, // prolongedsound
{0x04b1, 0x30a2}, // kana_A
{0x04b2, 0x30a4}, // kana_I
{0x04b3, 0x30a6}, // kana_U
{0x04b4, 0x30a8}, // kana_E
{0x04b5, 0x30aa}, // kana_O
{0x04b6, 0x30ab}, // kana_KA
{0x04b7, 0x30ad}, // kana_KI
{0x04b8, 0x30af}, // kana_KU
{0x04b9, 0x30b1}, // kana_KE
{0x04ba, 0x30b3}, // kana_KO
{0x04bb, 0x30b5}, // kana_SA
{0x04bc, 0x30b7}, // kana_SHI
{0x04bd, 0x30b9}, // kana_SU
{0x04be, 0x30bb}, // kana_SE
{0x04bf, 0x30bd}, // kana_SO
{0x04c0, 0x30bf}, // kana_TA
{0x04c1, 0x30c1}, // kana_CHI
{0x04c2, 0x30c4}, // kana_TSU
{0x04c3, 0x30c6}, // kana_TE
{0x04c4, 0x30c8}, // kana_TO
{0x04c5, 0x30ca}, // kana_NA
{0x04c6, 0x30cb}, // kana_NI
{0x04c7, 0x30cc}, // kana_NU
{0x04c8, 0x30cd}, // kana_NE
{0x04c9, 0x30ce}, // kana_NO
{0x04ca, 0x30cf}, // kana_HA
{0x04cb, 0x30d2}, // kana_HI
{0x04cc, 0x30d5}, // kana_FU
{0x04cd, 0x30d8}, // kana_HE
{0x04ce, 0x30db}, // kana_HO
{0x04cf, 0x30de}, // kana_MA
{0x04d0, 0x30df}, // kana_MI
{0x04d1, 0x30e0}, // kana_MU
{0x04d2, 0x30e1}, // kana_ME
{0x04d3, 0x30e2}, // kana_MO
{0x04d4, 0x30e4}, // kana_YA
{0x04d5, 0x30e6}, // kana_YU
{0x04d6, 0x30e8}, // kana_YO
{0x04d7, 0x30e9}, // kana_RA
{0x04d8, 0x30ea}, // kana_RI
{0x04d9, 0x30eb}, // kana_RU
{0x04da, 0x30ec}, // kana_RE
{0x04db, 0x30ed}, // kana_RO
{0x04dc, 0x30ef}, // kana_WA
{0x04dd, 0x30f3}, // kana_N
{0x04de, 0x309b}, // voicedsound
{0x04df, 0x309c}, // semivoicedsound
{0x05ac, 0x060c}, // Arabic_comma
{0x05bb, 0x061b}, // Arabic_semicolon
{0x05bf, 0x061f}, // Arabic_question_mark
{0x05c1, 0x0621}, // Arabic_hamza
{0x05c2, 0x0622}, // Arabic_maddaonalef
{0x05c3, 0x0623}, // Arabic_hamzaonalef
{0x05c4, 0x0624}, // Arabic_hamzaonwaw
{0x05c5, 0x0625}, // Arabic_hamzaunderalef
{0x05c6, 0x0626}, // Arabic_hamzaonyeh
{0x05c7, 0x0627}, // Arabic_alef
{0x05c8, 0x0628}, // Arabic_beh
{0x05c9, 0x0629}, // Arabic_tehmarbuta
{0x05ca, 0x062a}, // Arabic_teh
{0x05cb, 0x062b}, // Arabic_theh
{0x05cc, 0x062c}, // Arabic_jeem
{0x05cd, 0x062d}, // Arabic_hah
{0x05ce, 0x062e}, // Arabic_khah
{0x05cf, 0x062f}, // Arabic_dal
{0x05d0, 0x0630}, // Arabic_thal
{0x05d1, 0x0631}, // Arabic_ra
{0x05d2, 0x0632}, // Arabic_zain
{0x05d3, 0x0633}, // Arabic_seen
{0x05d4, 0x0634}, // Arabic_sheen
{0x05d5, 0x0635}, // Arabic_sad
{0x05d6, 0x0636}, // Arabic_dad
{0x05d7, 0x0637}, // Arabic_tah
{0x05d8, 0x0638}, // Arabic_zah
{0x05d9, 0x0639}, // Arabic_ain
{0x05da, 0x063a}, // Arabic_ghain
{0x05e0, 0x0640}, // Arabic_tatweel
{0x05e1, 0x0641}, // Arabic_feh
{0x05e2, 0x0642}, // Arabic_qaf
{0x05e3, 0x0643}, // Arabic_kaf
{0x05e4, 0x0644}, // Arabic_lam
{0x05e5, 0x0645}, // Arabic_meem
{0x05e6, 0x0646}, // Arabic_noon
{0x05e7, 0x0647}, // Arabic_ha
{0x05e8, 0x0648}, // Arabic_waw
{0x05e9, 0x0649}, // Arabic_alefmaksura
{0x05ea, 0x064a}, // Arabic_yeh
{0x05eb, 0x064b}, // Arabic_fathatan
{0x05ec, 0x064c}, // Arabic_dammatan
{0x05ed, 0x064d}, // Arabic_kasratan
{0x05ee, 0x064e}, // Arabic_fatha
{0x05ef, 0x064f}, // Arabic_damma
{0x05f0, 0x0650}, // Arabic_kasra
{0x05f1, 0x0651}, // Arabic_shadda
{0x05f2, 0x0652}, // Arabic_sukun
{0x06a1, 0x0452}, // Serbian_dje
{0x06a2, 0x0453}, // Macedonia_gje
{0x06a3, 0x0451}, // Cyrillic_io
{0x06a4, 0x0454}, // Ukrainian_ie
{0x06a5, 0x0455}, // Macedonia_dse
{0x06a6, 0x0456}, // Ukrainian_i
{0x06a7, 0x0457}, // Ukrainian_yi
{0x06a8, 0x0458}, // Cyrillic_je
{0x06a9, 0x0459}, // Cyrillic_lje
{0x06aa, 0x045a}, // Cyrillic_nje
{0x06ab, 0x045b}, // Serbian_tshe
{0x06ac, 0x045c}, // Macedonia_kje
{0x06ad, 0x0491}, // Ukrainian_ghe_with_upturn
{0x06ae, 0x045e}, // Byelorussian_shortu
{0x06af, 0x045f}, // Cyrillic_dzhe
{0x06b0, 0x2116}, // numerosign
{0x06b1, 0x0402}, // Serbian_DJE
{0x06b2, 0x0403}, // Macedonia_GJE
{0x06b3, 0x0401}, // Cyrillic_IO
{0x06b4, 0x0404}, // Ukrainian_IE
{0x06b5, 0x0405}, // Macedonia_DSE
{0x06b6, 0x0406}, // Ukrainian_I
{0x06b7, 0x0407}, // Ukrainian_YI
{0x06b8, 0x0408}, // Cyrillic_JE
{0x06b9, 0x0409}, // Cyrillic_LJE
{0x06ba, 0x040a}, // Cyrillic_NJE
{0x06bb, 0x040b}, // Serbian_TSHE
{0x06bc, 0x040c}, // Macedonia_KJE
{0x06bd, 0x0490}, // Ukrainian_GHE_WITH_UPTURN
{0x06be, 0x040e}, // Byelorussian_SHORTU
{0x06bf, 0x040f}, // Cyrillic_DZHE
{0x06c0, 0x044e}, // Cyrillic_yu
{0x06c1, 0x0430}, // Cyrillic_a
{0x06c2, 0x0431}, // Cyrillic_be
{0x06c3, 0x0446}, // Cyrillic_tse
{0x06c4, 0x0434}, // Cyrillic_de
{0x06c5, 0x0435}, // Cyrillic_ie
{0x06c6, 0x0444}, // Cyrillic_ef
{0x06c7, 0x0433}, // Cyrillic_ghe
{0x06c8, 0x0445}, // Cyrillic_ha
{0x06c9, 0x0438}, // Cyrillic_i
{0x06ca, 0x0439}, // Cyrillic_shorti
{0x06cb, 0x043a}, // Cyrillic_ka
{0x06cc, 0x043b}, // Cyrillic_el
{0x06cd, 0x043c}, // Cyrillic_em
{0x06ce, 0x043d}, // Cyrillic_en
{0x06cf, 0x043e}, // Cyrillic_o
{0x06d0, 0x043f}, // Cyrillic_pe
{0x06d1, 0x044f}, // Cyrillic_ya
{0x06d2, 0x0440}, // Cyrillic_er
{0x06d3, 0x0441}, // Cyrillic_es
{0x06d4, 0x0442}, // Cyrillic_te
{0x06d5, 0x0443}, // Cyrillic_u
{0x06d6, 0x0436}, // Cyrillic_zhe
{0x06d7, 0x0432}, // Cyrillic_ve
{0x06d8, 0x044c}, // Cyrillic_softsign
{0x06d9, 0x044b}, // Cyrillic_yeru
{0x06da, 0x0437}, // Cyrillic_ze
{0x06db, 0x0448}, // Cyrillic_sha
{0x06dc, 0x044d}, // Cyrillic_e
{0x06dd, 0x0449}, // Cyrillic_shcha
{0x06de, 0x0447}, // Cyrillic_che
{0x06df, 0x044a}, // Cyrillic_hardsign
{0x06e0, 0x042e}, // Cyrillic_YU
{0x06e1, 0x0410}, // Cyrillic_A
{0x06e2, 0x0411}, // Cyrillic_BE
{0x06e3, 0x0426}, // Cyrillic_TSE
{0x06e4, 0x0414}, // Cyrillic_DE
{0x06e5, 0x0415}, // Cyrillic_IE
{0x06e6, 0x0424}, // Cyrillic_EF
{0x06e7, 0x0413}, // Cyrillic_GHE
{0x06e8, 0x0425}, // Cyrillic_HA
{0x06e9, 0x0418}, // Cyrillic_I
{0x06ea, 0x0419}, // Cyrillic_SHORTI
{0x06eb, 0x041a}, // Cyrillic_KA
{0x06ec, 0x041b}, // Cyrillic_EL
{0x06ed, 0x041c}, // Cyrillic_EM
{0x06ee, 0x041d}, // Cyrillic_EN
{0x06ef, 0x041e}, // Cyrillic_O
{0x06f0, 0x041f}, // Cyrillic_PE
{0x06f1, 0x042f}, // Cyrillic_YA
{0x06f2, 0x0420}, // Cyrillic_ER
{0x06f3, 0x0421}, // Cyrillic_ES
{0x06f4, 0x0422}, // Cyrillic_TE
{0x06f5, 0x0423}, // Cyrillic_U
{0x06f6, 0x0416}, // Cyrillic_ZHE
{0x06f7, 0x0412}, // Cyrillic_VE
{0x06f8, 0x042c}, // Cyrillic_SOFTSIGN
{0x06f9, 0x042b}, // Cyrillic_YERU
{0x06fa, 0x0417}, // Cyrillic_ZE
{0x06fb, 0x0428}, // Cyrillic_SHA
{0x06fc, 0x042d}, // Cyrillic_E
{0x06fd, 0x0429}, // Cyrillic_SHCHA
{0x06fe, 0x0427}, // Cyrillic_CHE
{0x06ff, 0x042a}, // Cyrillic_HARDSIGN
{0x07a1, 0x0386}, // Greek_ALPHAaccent
{0x07a2, 0x0388}, // Greek_EPSILONaccent
{0x07a3, 0x0389}, // Greek_ETAaccent
{0x07a4, 0x038a}, // Greek_IOTAaccent
{0x07a5, 0x03aa}, // Greek_IOTAdieresis
{0x07a7, 0x038c}, // Greek_OMICRONaccent
{0x07a8, 0x038e}, // Greek_UPSILONaccent
{0x07a9, 0x03ab}, // Greek_UPSILONdieresis
{0x07ab, 0x038f}, // Greek_OMEGAaccent
{0x07ae, 0x0385}, // Greek_accentdieresis
{0x07af, 0x2015}, // Greek_horizbar
{0x07b1, 0x03ac}, // Greek_alphaaccent
{0x07b2, 0x03ad}, // Greek_epsilonaccent
{0x07b3, 0x03ae}, // Greek_etaaccent
{0x07b4, 0x03af}, // Greek_iotaaccent
{0x07b5, 0x03ca}, // Greek_IOTAdieresis
{0x07b6, 0x0390}, // Greek_iotaaccentdieresis
{0x07b7, 0x03cc}, // Greek_omicronaccent
{0x07b8, 0x03cd}, // Greek_upsilonaccent
{0x07b9, 0x03cb}, // Greek_upsilondieresis
{0x07ba, 0x03b0}, // Greek_upsilonaccentdieresis
{0x07bb, 0x03ce}, // Greek_omegaaccent
{0x07c1, 0x0391}, // Greek_ALPHA
{0x07c2, 0x0392}, // Greek_BETA
{0x07c3, 0x0393}, // Greek_GAMMA
{0x07c4, 0x0394}, // Greek_DELTA
{0x07c5, 0x0395}, // Greek_EPSILON
{0x07c6, 0x0396}, // Greek_ZETA
{0x07c7, 0x0397}, // Greek_ETA
{0x07c8, 0x0398}, // Greek_THETA
{0x07c9, 0x0399}, // Greek_IOTA
{0x07ca, 0x039a}, // Greek_KAPPA
{0x07cb, 0x039b}, // Greek_LAMBDA
{0x07cb, 0x039b}, // Greek_LAMDA
{0x07cc, 0x039c}, // Greek_MU
{0x07cd, 0x039d}, // Greek_NU
{0x07ce, 0x039e}, // Greek_XI
{0x07cf, 0x039f}, // Greek_OMICRON
{0x07d0, 0x03a0}, // Greek_PI
{0x07d1, 0x03a1}, // Greek_RHO
{0x07d2, 0x03a3}, // Greek_SIGMA
{0x07d4, 0x03a4}, // Greek_TAU
{0x07d5, 0x03a5}, // Greek_UPSILON
{0x07d6, 0x03a6}, // Greek_PHI
{0x07d7, 0x03a7}, // Greek_CHI
{0x07d8, 0x03a8}, // Greek_PSI
{0x07d9, 0x03a9}, // Greek_OMEGA
{0x07e1, 0x03b1}, // Greek_alpha
{0x07e2, 0x03b2}, // Greek_beta
{0x07e3, 0x03b3}, // Greek_gamma
{0x07e4, 0x03b4}, // Greek_delta
{0x07e5, 0x03b5}, // Greek_epsilon
{0x07e6, 0x03b6}, // Greek_zeta
{0x07e7, 0x03b7}, // Greek_eta
{0x07e8, 0x03b8}, // Greek_theta
{0x07e9, 0x03b9}, // Greek_iota
{0x07ea, 0x03ba}, // Greek_kappa
{0x07eb, 0x03bb}, // Greek_lambda
{0x07ec, 0x03bc}, // Greek_mu
{0x07ed, 0x03bd}, // Greek_nu
{0x07ee, 0x03be}, // Greek_xi
{0x07ef, 0x03bf}, // Greek_omicron
{0x07f0, 0x03c0}, // Greek_pi
{0x07f1, 0x03c1}, // Greek_rho
{0x07f2, 0x03c3}, // Greek_sigma
{0x07f3, 0x03c2}, // Greek_finalsmallsigma
{0x07f4, 0x03c4}, // Greek_tau
{0x07f5, 0x03c5}, // Greek_upsilon
{0x07f6, 0x03c6}, // Greek_phi
{0x07f7, 0x03c7}, // Greek_chi
{0x07f8, 0x03c8}, // Greek_psi
{0x07f9, 0x03c9}, // Greek_omega
{0x08a1, 0x23b7}, // leftradical
{0x08a2, 0x250c}, // topleftradical
{0x08a3, 0x2500}, // horizconnector
{0x08a4, 0x2320}, // topintegral
{0x08a5, 0x2321}, // botintegral
{0x08a6, 0x2502}, // vertconnector
{0x08a7, 0x23a1}, // topleftsqbracket
{0x08a8, 0x23a3}, // botleftsqbracket
{0x08a9, 0x23a4}, // toprightsqbracket
{0x08aa, 0x23a6}, // botrightsqbracket
{0x08ab, 0x239b}, // topleftparens
{0x08ac, 0x239d}, // botleftparens
{0x08ad, 0x239e}, // toprightparens
{0x08ae, 0x23a0}, // botrightparens
{0x08af, 0x23a8}, // leftmiddlecurlybrace
{0x08b0, 0x23ac}, // rightmiddlecurlybrace
{0x08bc, 0x2264}, // lessthanequal
{0x08bd, 0x2260}, // notequal
{0x08be, 0x2265}, // greaterthanequal
{0x08bf, 0x222b}, // integral
{0x08c0, 0x2234}, // therefore
{0x08c1, 0x221d}, // variation
{0x08c2, 0x221e}, // infinity
{0x08c5, 0x2207}, // nabla
{0x08c8, 0x223c}, // approximate
{0x08c9, 0x2243}, // similarequal
{0x08cd, 0x21d4}, // ifonlyif
{0x08ce, 0x21d2}, // implies
{0x08cf, 0x2261}, // identical
{0x08d6, 0x221a}, // radical
{0x08da, 0x2282}, // includedin
{0x08db, 0x2283}, // includes
{0x08dc, 0x2229}, // intersection
{0x08dd, 0x222a}, // union
{0x08de, 0x2227}, // logicaland
{0x08df, 0x2228}, // logicalor
{0x08ef, 0x2202}, // partialderivative
{0x08f6, 0x0192}, // function
{0x08fb, 0x2190}, // leftarrow
{0x08fc, 0x2191}, // uparrow
{0x08fd, 0x2192}, // rightarrow
{0x08fe, 0x2193}, // downarrow
{0x09df, 0x2422}, // blank
{0x09e0, 0x25c6}, // soliddiamond
{0x09e1, 0x2592}, // checkerboard
{0x09e2, 0x2409}, // ht
{0x09e3, 0x240c}, // ff
{0x09e4, 0x240d}, // cr
{0x09e5, 0x240a}, // lf
{0x09e8, 0x2424}, // nl
{0x09e9, 0x240b}, // vt
{0x09ea, 0x2518}, // lowrightcorner
{0x09eb, 0x2510}, // uprightcorner
{0x09ec, 0x250c}, // upleftcorner
{0x09ed, 0x2514}, // lowleftcorner
{0x09ee, 0x253c}, // crossinglines
{0x09ef, 0x23ba}, // horizlinescan1
{0x09f0, 0x23bb}, // horizlinescan3
{0x09f1, 0x2500}, // horizlinescan5
{0x09f2, 0x23bc}, // horizlinescan7
{0x09f3, 0x23bd}, // horizlinescan9
{0x09f4, 0x251c}, // leftt
{0x09f5, 0x2524}, // rightt
{0x09f6, 0x2534}, // bott
{0x09f7, 0x252c}, // topt
{0x09f8, 0x2502}, // vertbar
{0x0aa1, 0x2003}, // emspace
{0x0aa2, 0x2002}, // enspace
{0x0aa3, 0x2004}, // em3space
{0x0aa4, 0x2005}, // em4space
{0x0aa5, 0x2007}, // digitspace
{0x0aa6, 0x2008}, // punctspace
{0x0aa7, 0x2009}, // thinspace
{0x0aa8, 0x200a}, // hairspace
{0x0aa9, 0x2014}, // emdash
{0x0aaa, 0x2013}, // endash
{0x0aac, 0x2423}, // signifblank
{0x0aae, 0x2026}, // ellipsis
{0x0aaf, 0x2025}, // doubbaselinedot
{0x0ab0, 0x2153}, // onethird
{0x0ab1, 0x2154}, // twothirds
{0x0ab2, 0x2155}, // onefifth
{0x0ab3, 0x2156}, // twofifths
{0x0ab4, 0x2157}, // threefifths
{0x0ab5, 0x2158}, // fourfifths
{0x0ab6, 0x2159}, // onesixth
{0x0ab7, 0x215a}, // fivesixths
{0x0ab8, 0x2105}, // careof
{0x0abb, 0x2012}, // figdash
{0x0abc, 0x2329}, // leftanglebracket (not U+27E8)
{0x0abd, 0x002e}, // decimalpoint
{0x0abe, 0x232a}, // rightanglebracket (not U+27E9)
{0x0ac3, 0x215b}, // oneeighth
{0x0ac4, 0x215c}, // threeeighths
{0x0ac5, 0x215d}, // fiveeighths
{0x0ac6, 0x215e}, // seveneighths
{0x0ac9, 0x2122}, // trademark
{0x0aca, 0x2613}, // signaturemark
{0x0acc, 0x25c1}, // leftopentriangle
{0x0acd, 0x25b7}, // rightopentriangle
{0x0ace, 0x25cb}, // emopencircle
{0x0acf, 0x25af}, // emopenrectangle
{0x0ad0, 0x2018}, // leftsinglequotemark
{0x0ad1, 0x2019}, // rightsinglequotemark
{0x0ad2, 0x201c}, // leftdoublequotemark
{0x0ad3, 0x201d}, // rightdoublequotemark
{0x0ad4, 0x211e}, // prescription
{0x0ad5, 0x2030}, // per mille
{0x0ad6, 0x2032}, // minutes
{0x0ad7, 0x2033}, // seconds
{0x0ad9, 0x271d}, // latincross
{0x0adb, 0x25ac}, // filledrectbullet
{0x0adc, 0x25c0}, // filledlefttribullet
{0x0add, 0x25b6}, // filledrighttribullet
{0x0ade, 0x25cf}, // emfilledcircle
{0x0adf, 0x25ae}, // emfilledrect
{0x0ae0, 0x25e6}, // enopencircbullet
{0x0ae1, 0x25ab}, // enopensquarebullet
{0x0ae2, 0x25ad}, // openrectbullet
{0x0ae3, 0x25b3}, // opentribulletup
{0x0ae4, 0x25bd}, // opentribulletdown
{0x0ae5, 0x2606}, // openstar
{0x0ae6, 0x2022}, // enfilledcircbullet
{0x0ae7, 0x25aa}, // enfilledsqbullet
{0x0ae8, 0x25b2}, // filledtribulletup
{0x0ae9, 0x25bc}, // filledtribulletdown
{0x0aea, 0x261c}, // leftpointer
{0x0aeb, 0x261e}, // rightpointer
{0x0aec, 0x2663}, // club
{0x0aed, 0x2666}, // diamond
{0x0aee, 0x2665}, // heart
{0x0af0, 0x2720}, // maltesecross
{0x0af1, 0x2020}, // dagger
{0x0af2, 0x2021}, // doubledagger
{0x0af3, 0x2713}, // checkmark
{0x0af4, 0x2717}, // ballotcross
{0x0af5, 0x266f}, // musicalsharp
{0x0af6, 0x266d}, // musicalflat
{0x0af7, 0x2642}, // malesymbol
{0x0af8, 0x2640}, // femalesymbol
{0x0af9, 0x260e}, // telephone
{0x0afa, 0x2315}, // telephonerecorder
{0x0afb, 0x2117}, // phonographcopyright
{0x0afc, 0x2038}, // caret
{0x0afd, 0x201a}, // singlelowquotemark
{0x0afe, 0x201e}, // doublelowquotemark
{0x0ba3, 0x003c}, // leftcaret
{0x0ba6, 0x003e}, // rightcaret
{0x0ba8, 0x2228}, // downcaret
{0x0ba9, 0x2227}, // upcaret
{0x0bc0, 0x00af}, // overbar
{0x0bc2, 0x22a4}, // downtack
{0x0bc3, 0x2229}, // upshoe
{0x0bc4, 0x230a}, // downstile
{0x0bc6, 0x005f}, // underbar
{0x0bca, 0x2218}, // jot
{0x0bcc, 0x2395}, // quad
{0x0bce, 0x22a5}, // uptack
{0x0bcf, 0x25cb}, // circle
{0x0bd3, 0x2308}, // upstile
{0x0bd6, 0x222a}, // downshoe
{0x0bd8, 0x2283}, // rightshoe
{0x0bda, 0x2282}, // leftshoe
{0x0bdc, 0x22a3}, // lefttack
{0x0bfc, 0x22a2}, // righttack
{0x0cdf, 0x2017}, // hebrew_doublelowline
{0x0ce0, 0x05d0}, // hebrew_aleph
{0x0ce1, 0x05d1}, // hebrew_bet
{0x0ce2, 0x05d2}, // hebrew_gimel
{0x0ce3, 0x05d3}, // hebrew_dalet
{0x0ce4, 0x05d4}, // hebrew_he
{0x0ce5, 0x05d5}, // hebrew_waw
{0x0ce6, 0x05d6}, // hebrew_zain
{0x0ce7, 0x05d7}, // hebrew_chet
{0x0ce8, 0x05d8}, // hebrew_tet
{0x0ce9, 0x05d9}, // hebrew_yod
{0x0cea, 0x05da}, // hebrew_finalkaph
{0x0ceb, 0x05db}, // hebrew_kaph
{0x0cec, 0x05dc}, // hebrew_lamed
{0x0ced, 0x05dd}, // hebrew_finalmem
{0x0cee, 0x05de}, // hebrew_mem
{0x0cef, 0x05df}, // hebrew_finalnun
{0x0cf0, 0x05e0}, // hebrew_nun
{0x0cf1, 0x05e1}, // hebrew_samech
{0x0cf2, 0x05e2}, // hebrew_ayin
{0x0cf3, 0x05e3}, // hebrew_finalpe
{0x0cf4, 0x05e4}, // hebrew_pe
{0x0cf5, 0x05e5}, // hebrew_finalzade
{0x0cf6, 0x05e6}, // hebrew_zade
{0x0cf7, 0x05e7}, // hebrew_qoph
{0x0cf8, 0x05e8}, // hebrew_resh
{0x0cf9, 0x05e9}, // hebrew_shin
{0x0cfa, 0x05ea}, // hebrew_taw
{0x0da1, 0x0e01}, // Thai_kokai
{0x0da2, 0x0e02}, // Thai_khokhai
{0x0da3, 0x0e03}, // Thai_khokhuat
{0x0da4, 0x0e04}, // Thai_khokhwai
{0x0da5, 0x0e05}, // Thai_khokhon
{0x0da6, 0x0e06}, // Thai_khorakhang
{0x0da7, 0x0e07}, // Thai_ngongu
{0x0da8, 0x0e08}, // Thai_chochan
{0x0da9, 0x0e09}, // Thai_choching
{0x0daa, 0x0e0a}, // Thai_chochang
{0x0dab, 0x0e0b}, // Thai_soso
{0x0dac, 0x0e0c}, // Thai_chochoe
{0x0dad, 0x0e0d}, // Thai_yoying
{0x0dae, 0x0e0e}, // Thai_dochada
{0x0daf, 0x0e0f}, // Thai_topatak
{0x0db0, 0x0e10}, // Thai_thothan
{0x0db1, 0x0e11}, // Thai_thonangmontho
{0x0db2, 0x0e12}, // Thai_thophuthao
{0x0db3, 0x0e13}, // Thai_nonen
{0x0db4, 0x0e14}, // Thai_dodek
{0x0db5, 0x0e15}, // Thai_totao
{0x0db6, 0x0e16}, // Thai_thothung
{0x0db7, 0x0e17}, // Thai_thothahan
{0x0db8, 0x0e18}, // Thai_thothong
{0x0db9, 0x0e19}, // Thai_nonu
{0x0dba, 0x0e1a}, // Thai_bobaimai
{0x0dbb, 0x0e1b}, // Thai_popla
{0x0dbc, 0x0e1c}, // Thai_phophung
{0x0dbd, 0x0e1d}, // Thai_fofa
{0x0dbe, 0x0e1e}, // Thai_phophan
{0x0dbf, 0x0e1f}, // Thai_fofan
{0x0dc0, 0x0e20}, // Thai_phosamphao
{0x0dc1, 0x0e21}, // Thai_moma
{0x0dc2, 0x0e22}, // Thai_yoyak
{0x0dc3, 0x0e23}, // Thai_rorua
{0x0dc4, 0x0e24}, // Thai_ru
{0x0dc5, 0x0e25}, // Thai_loling
{0x0dc6, 0x0e26}, // Thai_lu
{0x0dc7, 0x0e27}, // Thai_wowaen
{0x0dc8, 0x0e28}, // Thai_sosala
{0x0dc9, 0x0e29}, // Thai_sorusi
{0x0dca, 0x0e2a}, // Thai_sosua
{0x0dcb, 0x0e2b}, // Thai_hohip
{0x0dcc, 0x0e2c}, // Thai_lochula
{0x0dcd, 0x0e2d}, // Thai_oang
{0x0dce, 0x0e2e}, // Thai_honokhuk
{0x0dcf, 0x0e2f}, // Thai_paiyannoi
{0x0dd0, 0x0e30}, // Thai_saraa
{0x0dd1, 0x0e31}, // Thai_maihanakat
{0x0dd2, 0x0e32}, // Thai_saraaa
{0x0dd3, 0x0e33}, // Thai_saraam
{0x0dd4, 0x0e34}, // Thai_sarai
{0x0dd5, 0x0e35}, // Thai_saraii
{0x0dd6, 0x0e36}, // Thai_saraue
{0x0dd7, 0x0e37}, // Thai_sarauee
{0x0dd8, 0x0e38}, // Thai_sarau
{0x0dd9, 0x0e39}, // Thai_sarauu
{0x0dda, 0x0e3a}, // Thai_phinthu
{0x0ddf, 0x0e3f}, // Thai_baht
{0x0de0, 0x0e40}, // Thai_sarae
{0x0de1, 0x0e41}, // Thai_saraae
{0x0de2, 0x0e42}, // Thai_sarao
{0x0de3, 0x0e43}, // Thai_saraaimaimuan
{0x0de4, 0x0e44}, // Thai_saraaimaimalai
{0x0de5, 0x0e45}, // Thai_lakkhangyao
{0x0de6, 0x0e46}, // Thai_maiyamok
{0x0de7, 0x0e47}, // Thai_maitaikhu
{0x0de8, 0x0e48}, // Thai_maiek
{0x0de9, 0x0e49}, // Thai_maitho
{0x0dea, 0x0e4a}, // Thai_maitri
{0x0deb, 0x0e4b}, // Thai_maichattawa
{0x0dec, 0x0e4c}, // Thai_thanthakhat
{0x0ded, 0x0e4d}, // Thai_nikhahit
{0x0df0, 0x0e50}, // Thai_leksun
{0x0df1, 0x0e51}, // Thai_leknung
{0x0df2, 0x0e52}, // Thai_leksong
{0x0df3, 0x0e53}, // Thai_leksam
{0x0df4, 0x0e54}, // Thai_leksi
{0x0df5, 0x0e55}, // Thai_lekha
{0x0df6, 0x0e56}, // Thai_lekhok
{0x0df7, 0x0e57}, // Thai_lekchet
{0x0df8, 0x0e58}, // Thai_lekpaet
{0x0df9, 0x0e59}, // Thai_lekkao
{0x0ea1, 0x3131}, // Hangul_Kiyeog
{0x0ea2, 0x3132}, // Hangul_SsangKiyeog
{0x0ea3, 0x3133}, // Hangul_KiyeogSios
{0x0ea4, 0x3134}, // Hangul_Nieun
{0x0ea5, 0x3135}, // Hangul_NieunJieuj
{0x0ea6, 0x3136}, // Hangul_NieunHieuh
{0x0ea7, 0x3137}, // Hangul_Dikeud
{0x0ea8, 0x3138}, // Hangul_SsangDikeud
{0x0ea9, 0x3139}, // Hangul_Rieul
{0x0eaa, 0x313a}, // Hangul_RieulKiyeog
{0x0eab, 0x313b}, // Hangul_RieulMieum
{0x0eac, 0x313c}, // Hangul_RieulPieub
{0x0ead, 0x313d}, // Hangul_RieulSios
{0x0eae, 0x313e}, // Hangul_RieulTieut
{0x0eaf, 0x313f}, // Hangul_RieulPhieuf
{0x0eb0, 0x3140}, // Hangul_RieulHieuh
{0x0eb1, 0x3141}, // Hangul_Mieum
{0x0eb2, 0x3142}, // Hangul_Pieub
{0x0eb3, 0x3143}, // Hangul_SsangPieub
{0x0eb4, 0x3144}, // Hangul_PieubSios
{0x0eb5, 0x3145}, // Hangul_Sios
{0x0eb6, 0x3146}, // Hangul_SsangSios
{0x0eb7, 0x3147}, // Hangul_Ieung
{0x0eb8, 0x3148}, // Hangul_Jieuj
{0x0eb9, 0x3149}, // Hangul_SsangJieuj
{0x0eba, 0x314a}, // Hangul_Cieuc
{0x0ebb, 0x314b}, // Hangul_Khieuq
{0x0ebc, 0x314c}, // Hangul_Tieut
{0x0ebd, 0x314d}, // Hangul_Phieuf
{0x0ebe, 0x314e}, // Hangul_Hieuh
{0x0ebf, 0x314f}, // Hangul_A
{0x0ec0, 0x3150}, // Hangul_AE
{0x0ec1, 0x3151}, // Hangul_YA
{0x0ec2, 0x3152}, // Hangul_YAE
{0x0ec3, 0x3153}, // Hangul_EO
{0x0ec4, 0x3154}, // Hangul_E
{0x0ec5, 0x3155}, // Hangul_YEO
{0x0ec6, 0x3156}, // Hangul_YE
{0x0ec7, 0x3157}, // Hangul_O
{0x0ec8, 0x3158}, // Hangul_WA
{0x0ec9, 0x3159}, // Hangul_WAE
{0x0eca, 0x315a}, // Hangul_OE
{0x0ecb, 0x315b}, // Hangul_YO
{0x0ecc, 0x315c}, // Hangul_U
{0x0ecd, 0x315d}, // Hangul_WEO
{0x0ece, 0x315e}, // Hangul_WE
{0x0ecf, 0x315f}, // Hangul_WI
{0x0ed0, 0x3160}, // Hangul_YU
{0x0ed1, 0x3161}, // Hangul_EU
{0x0ed2, 0x3162}, // Hangul_YI
{0x0ed3, 0x3163}, // Hangul_I
{0x0ed4, 0x11a8}, // Hangul_J_Kiyeog
{0x0ed5, 0x11a9}, // Hangul_J_SsangKiyeog
{0x0ed6, 0x11aa}, // Hangul_J_KiyeogSios
{0x0ed7, 0x11ab}, // Hangul_J_Nieun
{0x0ed8, 0x11ac}, // Hangul_J_NieunJieuj
{0x0ed9, 0x11ad}, // Hangul_J_NieunHieuh
{0x0eda, 0x11ae}, // Hangul_J_Dikeud
{0x0edb, 0x11af}, // Hangul_J_Rieul
{0x0edc, 0x11b0}, // Hangul_J_RieulKiyeog
{0x0edd, 0x11b1}, // Hangul_J_RieulMieum
{0x0ede, 0x11b2}, // Hangul_J_RieulPieub
{0x0edf, 0x11b3}, // Hangul_J_RieulSios
{0x0ee0, 0x11b4}, // Hangul_J_RieulTieut
{0x0ee1, 0x11b5}, // Hangul_J_RieulPhieuf
{0x0ee2, 0x11b6}, // Hangul_J_RieulHieuh
{0x0ee3, 0x11b7}, // Hangul_J_Mieum
{0x0ee4, 0x11b8}, // Hangul_J_Pieub
{0x0ee5, 0x11b9}, // Hangul_J_PieubSios
{0x0ee6, 0x11ba}, // Hangul_J_Sios
{0x0ee7, 0x11bb}, // Hangul_J_SsangSios
{0x0ee8, 0x11bc}, // Hangul_J_Ieung
{0x0ee9, 0x11bd}, // Hangul_J_Jieuj
{0x0eea, 0x11be}, // Hangul_J_Cieuc
{0x0eeb, 0x11bf}, // Hangul_J_Khieuq
{0x0eec, 0x11c0}, // Hangul_J_Tieut
{0x0eed, 0x11c1}, // Hangul_J_Phieuf
{0x0eee, 0x11c2}, // Hangul_J_Hieuh
{0x0eef, 0x316d}, // Hangul_RieulYeorinHieuh
{0x0ef0, 0x3171}, // Hangul_SunkyeongeumMieum
{0x0ef1, 0x3178}, // Hangul_SunkyeongeumPieub
{0x0ef2, 0x317f}, // Hangul_PanSios
{0x0ef3, 0x3181}, // Hangul_KkogjiDalrinIeung
{0x0ef4, 0x3184}, // Hangul_SunkyeongeumPhieuf
{0x0ef5, 0x3186}, // Hangul_YeorinHieuh
{0x0ef6, 0x318d}, // Hangul_AraeA
{0x0ef7, 0x318e}, // Hangul_AraeAE
{0x0ef8, 0x11eb}, // Hangul_J_PanSios
{0x0ef9, 0x11f0}, // Hangul_J_KkogjiDalrinIeung
{0x0efa, 0x11f9}, // Hangul_J_YeorinHieuh
{0x0eff, 0x20a9}, // Korean_Won
{0x13a4, 0x20ac}, // Euro
{0x13bc, 0x0152}, // OE
{0x13bd, 0x0153}, // oe
{0x13be, 0x0178}, // Ydiaeresis
{0x20a0, 0x20a0}, // EcuSign
{0x20a1, 0x20a1}, // ColonSign
{0x20a2, 0x20a2}, // CruzeiroSign
{0x20a3, 0x20a3}, // FFrancSign
{0x20a4, 0x20a4}, // LiraSign
{0x20a5, 0x20a5}, // MillSign
{0x20a6, 0x20a6}, // NairaSign
{0x20a7, 0x20a7}, // PesetaSign
{0x20a8, 0x20a8}, // RupeeSign
{0x20a9, 0x20a9}, // WonSign
{0x20aa, 0x20aa}, // NewSheqelSign
{0x20ab, 0x20ab}, // DongSign
{0x20ac, 0x20ac}, // EuroSign
{0xfe50, 0x0300}, // dead_grave
{0xfe51, 0x0301}, // dead_acute
{0xfe52, 0x0302}, // dead_circumflex
{0xfe53, 0x0303}, // dead_tilde
{0xfe54, 0x0304}, // dead_macron
{0xfe55, 0x0306}, // dead_breve
{0xfe56, 0x0307}, // dead_abovedot
{0xfe57, 0x0308}, // dead_diaeresis
{0xfe58, 0x030a}, // dead_abovering
{0xfe59, 0x030b}, // dead_doubleacute
{0xfe5a, 0x030c}, // dead_caron
{0xfe5b, 0x0327}, // dead_cedilla
{0xfe5c, 0x0328}, // dead_ogonek
{0xfe5d, 0x0345}, // dead_iota
{0xfe5e, 0x3099}, // dead_voiced_sound
{0xfe5f, 0x309a}, // dead_semivoiced_sound
{0xff08, 0x0008}, // BackSpace /* back space, back char */
{0xff09, 0x0009}, // Tab
{0xff0a, 0x000a}, // Linefeed /* Linefeed, LF */
{0xff0b, 0x000b}, // Clear
{0xff0d, 0x000d}, // Return /* Return, enter */
{0xff13, 0x0013}, // Pause /* Pause, hold */
{0xff14, 0x0014}, // Scroll_Lock
{0xff15, 0x0015}, // Sys_Req
{0xff1b, 0x001b}, // Escape
{0xff80, 0x0032}, // KP_Space /* space */
{0xff89, 0x0009}, // KP_Tab
{0xff8d, 0x000d}, // KP_Enter /* enter */
{0xffaa, 0x002a}, // KP_Multiply
{0xffab, 0x002b}, // KP_Add
{0xffac, 0x002c}, // KP_Separator /* separator, often comma */
{0xffad, 0x002d}, // KP_Subtract
{0xffae, 0x002e}, // KP_Decimal
{0xffaf, 0x002f}, // KP_Divide
{0xffb0, 0x0030}, // KP_0
{0xffb1, 0x0031}, // KP_1
{0xffb2, 0x0032}, // KP_2
{0xffb3, 0x0033}, // KP_3
{0xffb4, 0x0034}, // KP_4
{0xffb5, 0x0035}, // KP_5
{0xffb6, 0x0036}, // KP_6
{0xffb7, 0x0037}, // KP_7
{0xffb8, 0x0038}, // KP_8
{0xffb9, 0x0039}, // KP_9
{0xffbd, 0x003d}, // KP_Equal /* equals */
}