composetable: Drop GtkComposeTableCompact

All tables use the compact format now, and we generate
caches in that format too. Bump the cache version to 3
for this.

Replace the python script for generating the builtin table
by a small C program using the same code to generate the data
for the builtin table. This drops the restriction on only
generating a single character in the builtin sequences.
This commit is contained in:
Matthias Clasen 2021-07-08 17:59:50 -04:00
parent 1cff4bb27a
commit 54dffa07f3
27 changed files with 5022 additions and 5741 deletions

BIN
gtk/compose/chars Normal file

Binary file not shown.

View File

@ -0,0 +1,63 @@
#include <gtk/gtk.h>
#include "gtk/gtkcomposetable.h"
#include <locale.h>
/* This program reads a Compose file and generates files with sequences,
* character data, and definitions for the builtin compose table of GTK.
* Run it like this:
*
* compose-parse Compose sequences chars gtkcomposedata.h
*
* The GTK build expects the output files to be in the source tree, in
* the gtk/compose directory.
*/
int
main (int argc, char *argv[])
{
GtkComposeTable *table;
GError *error = NULL;
GString *str;
setlocale (LC_ALL, "");
if (argc < 5)
{
g_print ("Usage: compose-parse INPUT OUTPUT1 OUTPUT2 OUTPUT3\n");
exit (1);
}
table = gtk_compose_table_parse (argv[1]);
if (!table)
g_error ("Failed to parse %s", argv[1]);
/* data_size is the size in guint16 */
if (!g_file_set_contents (argv[2], (char *)table->data, 2 * table->data_size, &error))
g_error ("%s", error->message);
if (!g_file_set_contents (argv[3], table->char_data, table->n_chars + 1, &error))
g_error ("%s", error->message);
str = g_string_new ("");
g_string_append (str,
"#ifndef __GTK_COMPOSE_DATA__\n"
"#define __GTK_COMPOSE_DATA__\n"
"\n");
g_string_append_printf (str,
"#define MAX_SEQ_LEN %d\n", table->max_seq_len);
g_string_append_printf (str,
"#define N_INDEX_SIZE %d\n", table->n_index_size);
g_string_append_printf (str,
"#define DATA_SIZE %d\n", table->data_size);
g_string_append_printf (str,
"#define N_CHARS %d\n", table->n_chars);
g_string_append (str,
"\n"
"#endif\n");
if (!g_file_set_contents (argv[4], str->str, str->len, &error))
g_error ("%s", error->message);
g_string_free (str, TRUE);
return 0;
}

View File

@ -1,984 +0,0 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
#
# compose-parse.py, version 1.4
#
# multifunction script that helps manage the compose sequence table in GTK+ (gtk/gtkimcontextsimple.c)
# the script produces statistics and information about the whole process, run with --help for more.
#
# You may need to switch your python installation to utf-8, if you get 'ascii' codec errors.
#
# Complain to Simos Xenitellis (simos@gnome.org, http://simos.info/blog) for this craft.
from re import findall, match, split, sub
from string import atoi
from unicodedata import normalize
from urllib import urlretrieve
from os.path import isfile, getsize
from copy import copy
import sys
import getopt
# We grab files off the web, left and right.
URL_COMPOSE = 'http://cgit.freedesktop.org/xorg/lib/libX11/plain/nls/en_US.UTF-8/Compose.pre'
URL_KEYSYMSTXT = "http://www.cl.cam.ac.uk/~mgk25/ucs/keysyms.txt"
URL_GDKKEYSYMSH = "http://git.gnome.org/browse/gtk%2B/plain/gdk/gdkkeysyms.h"
URL_UNICODEDATATXT = 'http://www.unicode.org/Public/6.0.0/ucd/UnicodeData.txt'
FILENAME_COMPOSE_SUPPLEMENTARY = 'gtk-compose-lookaside.txt'
FILENAME_COMPOSE_NEGATIVE_SUPPLEMENTARY = 'gtk-compose-remove.txt'
# We currently support keysyms of size 2; once upstream xorg gets sorted,
# we might produce some tables with size 2 and some with size 4.
SIZEOFINT = 2
# Current max compose sequence length; in case it gets increased.
WIDTHOFCOMPOSETABLE = 5
keysymdatabase = {}
keysymunicodedatabase = {}
unicodedatabase = {}
headerfile_start = """/* GTK - The GIMP Tool Kit
* Copyright (C) 2007, 2008 GNOME Foundation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* File auto-generated from script found at http://bugzilla.gnome.org/show_bug.cgi?id=321896
* using the input files
* Input : http://cgit.freedesktop.org/xorg/lib/libX11/plain/nls/en_US.UTF-8/Compose.pre
* Input : http://www.cl.cam.ac.uk/~mgk25/ucs/keysyms.txt
* Input : http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
*
* This table is optimised for space and requires special handling to access the content.
* This table is used solely by http://svn.gnome.org/viewcvs/gtk%2B/trunk/gtk/gtkimcontextsimple.c
*
* The resulting file is placed at http://svn.gnome.org/viewcvs/gtk%2B/trunk/gtk/gtkimcontextsimpleseqs.h
* This file is described in bug report http://bugzilla.gnome.org/show_bug.cgi?id=321896
*/
/*
* Modified by the GTK+ Team and others 2007, 2008. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#ifndef __GTK_IM_CONTEXT_SIMPLE_SEQS_H__
#define __GTK_IM_CONTEXT_SIMPLE_SEQS_H__
/* === These are the original comments of the file; we keep for historical purposes ===
*
* The following table was generated from the X compose tables include with
* XFree86 4.0 using a set of Perl scripts. Contact Owen Taylor <otaylor@redhat.com>
* to obtain the relevant perl scripts.
*
* The following compose letter letter sequences conflicted
* Dstroke/dstroke and ETH/eth; resolved to Dstroke (Croatian, Vietnamese, Lappish), over
* ETH (Icelandic, Faroese, old English, IPA) [ D- -D d- -d ]
* Amacron/amacron and ordfeminine; resolved to ordfeminine [ _A A_ a_ _a ]
* Amacron/amacron and Atilde/atilde; resolved to atilde [ -A A- a- -a ]
* Omacron/Omacron and masculine; resolved to masculine [ _O O_ o_ _o ]
* Omacron/omacron and Otilde/atilde; resolved to otilde [ -O O- o- -o ]
*
* [ Amacron and Omacron are in Latin-4 (Baltic). ordfeminine and masculine are used for
* spanish. atilde and otilde are used at least for Portuguese ]
*
* at and Aring; resolved to Aring [ AA ]
* guillemotleft and caron; resolved to guillemotleft [ << ]
* ogonek and cedilla; resolved to cedilla [ ,, ]
*
* This probably should be resolved by first checking an additional set of compose tables
* that depend on the locale or selected input method.
*/
static const guint16 gtk_compose_seqs_compact[] = {"""
headerfile_end = """};
#endif /* __GTK_IM_CONTEXT_SIMPLE_SEQS_H__ */
"""
def stringtohex(str): return atoi(str, 16)
def factorial(n):
if n <= 1:
return 1
else:
return n * factorial(n-1)
def uniq(*args) :
""" Performs a uniq operation on a list or lists """
theInputList = []
for theList in args:
theInputList += theList
theFinalList = []
for elem in theInputList:
if elem not in theFinalList:
theFinalList.append(elem)
return theFinalList
def all_permutations(seq):
""" Borrowed from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252178 """
""" Produces all permutations of the items of a list """
if len(seq) <=1:
yield seq
else:
for perm in all_permutations(seq[1:]):
for i in range(len(perm)+1):
#nb str[0:1] works in both string and list contexts
yield perm[:i] + seq[0:1] + perm[i:]
def usage():
print """compose-parse available parameters:
-h, --help this craft
-s, --statistics show overall statistics (both algorithmic, non-algorithmic)
-a, --algorithmic show sequences saved with algorithmic optimisation
-g, --gtk show entries that go to GTK+
-u, --unicodedatatxt show compose sequences derived from UnicodeData.txt (from unicode.org)
-v, --verbose show verbose output
-p, --plane1 show plane1 compose sequences
-n, --numeric when used with --gtk, create file with numeric values only
-e, --gtk-expanded when used with --gtk, create file that repeats first column; not usable in GTK+
Default is to show statistics.
"""
try:
opts, args = getopt.getopt(sys.argv[1:], "pvgashune", ["help", "algorithmic", "statistics", "unicodedatatxt",
"stats", "gtk", "verbose", "plane1", "numeric", "gtk-expanded"])
except:
usage()
sys.exit(2)
opt_statistics = False
opt_algorithmic = False
opt_gtk = False
opt_unicodedatatxt = False
opt_verbose = False
opt_plane1 = False
opt_numeric = False
opt_gtkexpanded = False
for o, a in opts:
if o in ("-h", "--help"):
usage()
sys.exit()
if o in ("-s", "--statistics"):
opt_statistics = True
if o in ("-a", "--algorithmic"):
opt_algorithmic = True
if o in ("-g", "--gtk"):
opt_gtk = True
if o in ("-u", "--unicodedatatxt"):
opt_unicodedatatxt = True
if o in ("-v", "--verbose"):
opt_verbose = True
if o in ("-p", "--plane1"):
opt_plane1 = True
if o in ("-n", "--numeric"):
opt_numeric = True
if o in ("-e", "--gtk-expanded"):
opt_gtkexpanded = True
if not opt_algorithmic and not opt_gtk and not opt_unicodedatatxt:
opt_statistics = True
def download_hook(blocks_transferred, block_size, file_size):
""" A download hook to provide some feedback when downloading """
if blocks_transferred == 0:
if file_size > 0:
if opt_verbose:
print "Downloading", file_size, "bytes: ",
else:
if opt_verbose:
print "Downloading: ",
sys.stdout.write('#')
sys.stdout.flush()
def download_file(url):
""" Downloads a file provided a URL. Returns the filename. """
""" Borks on failure """
localfilename = url.split('/')[-1]
if not isfile(localfilename) or getsize(localfilename) <= 0:
if opt_verbose:
print "Downloading ", url, "..."
try:
urlretrieve(url, localfilename, download_hook)
except IOError, (errno, strerror):
print "I/O error(%s): %s" % (errno, strerror)
sys.exit(-1)
except:
print "Unexpected error: ", sys.exc_info()[0]
sys.exit(-1)
print " done."
else:
if opt_verbose:
print "Using cached file for ", url
return localfilename
def process_gdkkeysymsh():
""" Opens the gdkkeysyms.h file from GTK+/gdk/gdkkeysyms.h """
""" Fills up keysymdb with contents """
filename_gdkkeysymsh = download_file(URL_GDKKEYSYMSH)
try:
gdkkeysymsh = open(filename_gdkkeysymsh, 'r')
except IOError, (errno, strerror):
print "I/O error(%s): %s" % (errno, strerror)
sys.exit(-1)
except:
print "Unexpected error: ", sys.exc_info()[0]
sys.exit(-1)
""" Parse the gdkkeysyms.h file and place contents in keysymdb """
linenum_gdkkeysymsh = 0
keysymdb = {}
for line in gdkkeysymsh.readlines():
linenum_gdkkeysymsh += 1
line = line.strip()
if line == "" or not match('^#define GDK_KEY_', line):
continue
components = split('\s+', line)
if len(components) < 3:
print "Invalid line %(linenum)d in %(filename)s: %(line)s"\
% {'linenum': linenum_gdkkeysymsh, 'filename': filename_gdkkeysymsh, 'line': line}
print "Was expecting 3 items in the line"
sys.exit(-1)
if not match('^GDK_KEY_', components[1]):
print "Invalid line %(linenum)d in %(filename)s: %(line)s"\
% {'linenum': linenum_gdkkeysymsh, 'filename': filename_gdkkeysymsh, 'line': line}
print "Was expecting a keysym starting with GDK_KEY_"
sys.exit(-1)
if match('^0x[0-9a-fA-F]+$', components[2]):
unival = long(components[2][2:], 16)
if unival == 0:
continue
keysymdb[components[1][8:]] = unival
else:
print "Invalid line %(linenum)d in %(filename)s: %(line)s"\
% {'linenum': linenum_gdkkeysymsh, 'filename': filename_gdkkeysymsh, 'line': line}
print "Was expecting a hexadecimal number at the end of the line"
sys.exit(-1)
gdkkeysymsh.close()
""" Patch up the keysymdb with some of our own stuff """
""" This is for a missing keysym from the currently upstream file """
###keysymdb['dead_stroke'] = 0x338
""" This is for a missing keysym from the currently upstream file """
###keysymdb['dead_belowring'] = 0x323
###keysymdb['dead_belowmacron'] = 0x331
###keysymdb['dead_belowcircumflex'] = 0x32d
###keysymdb['dead_belowtilde'] = 0x330
###keysymdb['dead_belowbreve'] = 0x32e
###keysymdb['dead_belowdiaeresis'] = 0x324
""" This is^Wwas preferential treatment for Greek """
# keysymdb['dead_tilde'] = 0x342
""" This is^was preferential treatment for Greek """
#keysymdb['combining_tilde'] = 0x342
""" Fixing VoidSymbol """
keysymdb['VoidSymbol'] = 0xFFFF
return keysymdb
def process_keysymstxt():
""" Grabs and opens the keysyms.txt file that Markus Kuhn maintains """
""" This file keeps a record between keysyms <-> unicode chars """
filename_keysymstxt = download_file(URL_KEYSYMSTXT)
try:
keysymstxt = open(filename_keysymstxt, 'r')
except IOError, (errno, strerror):
print "I/O error(%s): %s" % (errno, strerror)
sys.exit(-1)
except:
print "Unexpected error: ", sys.exc_info()[0]
sys.exit(-1)
""" Parse the keysyms.txt file and place content in keysymdb """
linenum_keysymstxt = 0
keysymdb = {}
for line in keysymstxt.readlines():
linenum_keysymstxt += 1
line = line.strip()
if line == "" or match('^#', line):
continue
components = split('\s+', line)
if len(components) < 5:
print "Invalid line %(linenum)d in %(filename)s: %(line)s'"\
% {'linenum': linenum_keysymstxt, 'filename': filename_keysymstxt, 'line': line}
print "Was expecting 5 items in the line"
sys.exit(-1)
if match('^U[0-9a-fA-F]+$', components[1]):
unival = long(components[1][1:], 16)
if unival == 0:
continue
keysymdb[components[4]] = unival
keysymstxt.close()
""" Patch up the keysymdb with some of our own stuff """
""" This is for a missing keysym from the currently upstream file """
keysymdb['dead_belowring'] = 0x323
keysymdb['dead_belowmacron'] = 0x331
keysymdb['dead_belowcircumflex'] = 0x32d
keysymdb['dead_belowtilde'] = 0x330
keysymdb['dead_belowbreve'] = 0x32e
keysymdb['dead_belowdiaeresis'] = 0x324
""" This is preferential treatment for Greek """
""" => we get more savings if used for Greek """
# keysymdb['dead_tilde'] = 0x342
""" This is preferential treatment for Greek """
# keysymdb['combining_tilde'] = 0x342
""" This is for a missing keysym from Markus Kuhn's db """
keysymdb['dead_stroke'] = 0x338
""" This is for a missing keysym from Markus Kuhn's db """
keysymdb['Oslash'] = 0x0d8
""" This is for a missing keysym from Markus Kuhn's db """
keysymdb['Ssharp'] = 0x1e9e
""" This is for a missing (recently added) keysym """
keysymdb['dead_psili'] = 0x313
""" This is for a missing (recently added) keysym """
keysymdb['dead_dasia'] = 0x314
""" Allows to import Multi_key sequences """
keysymdb['Multi_key'] = 0xff20
keysymdb['zerosubscript'] = 0x2080
keysymdb['onesubscript'] = 0x2081
keysymdb['twosubscript'] = 0x2082
keysymdb['threesubscript'] = 0x2083
keysymdb['foursubscript'] = 0x2084
keysymdb['fivesubscript'] = 0x2085
keysymdb['sixsubscript'] = 0x2086
keysymdb['sevensubscript'] = 0x2087
keysymdb['eightsubscript'] = 0x2088
keysymdb['ninesubscript'] = 0x2089
keysymdb['dead_doublegrave'] = 0x030F
keysymdb['dead_invertedbreve'] = 0x0311
keysymdb['dead_belowcomma'] = 0xfe6e
keysymdb['dead_currency'] = 0xfe6f
keysymdb['dead_greek'] = 0xfe8c
return keysymdb
def keysymvalue(keysym, file = "n/a", linenum = 0):
""" Extracts a value from the keysym """
""" Find the value of keysym, using the data from keysyms """
""" Use file and linenum to when reporting errors """
if keysym == "":
return 0
if keysymdatabase.has_key(keysym):
return keysymdatabase[keysym]
elif keysym[0] == 'U' and match('[0-9a-fA-F]+$', keysym[1:]):
return atoi(keysym[1:], 16)
elif keysym[:2] == '0x' and match('[0-9a-fA-F]+$', keysym[2:]):
return atoi(keysym[2:], 16)
else:
print 'keysymvalue: UNKNOWN{%(keysym)s}' % { "keysym": keysym }
#return -1
sys.exit(-1)
def keysymunicodevalue(keysym, file = "n/a", linenum = 0):
""" Extracts a value from the keysym """
""" Find the value of keysym, using the data from keysyms """
""" Use file and linenum to when reporting errors """
if keysym == "":
return 0
if keysymunicodedatabase.has_key(keysym):
return keysymunicodedatabase[keysym]
elif keysym[0] == 'U' and match('[0-9a-fA-F]+$', keysym[1:]):
return atoi(keysym[1:], 16)
elif keysym[:2] == '0x' and match('[0-9a-fA-F]+$', keysym[2:]):
return atoi(keysym[2:], 16)
else:
print 'keysymunicodevalue: UNKNOWN{%(keysym)s}' % { "keysym": keysym }
sys.exit(-1)
def rename_combining(seq):
filtered_sequence = []
for ks in seq:
if findall('^combining_', ks):
ks = sub('^combining_', 'dead_', ks)
if ks == 'dead_double_grave':
ks = 'dead_doublegrave'
if ks == 'dead_inverted_breve':
ks = 'dead_invertedbreve'
filtered_sequence.append(ks)
return filtered_sequence
keysymunicodedatabase = process_keysymstxt()
keysymdatabase = process_gdkkeysymsh()
""" Grab and open the compose file from upstream """
filename_compose = download_file(URL_COMPOSE)
try:
composefile = open(filename_compose, 'r')
except IOError, (errno, strerror):
print "I/O error(%s): %s" % (errno, strerror)
sys.exit(-1)
except:
print "Unexpected error: ", sys.exc_info()[0]
sys.exit(-1)
""" Look if there is a lookaside (supplementary) compose file in the current
directory, and if so, open, then merge with upstream Compose file.
"""
xorg_compose_sequences_raw = []
for seq in composefile.readlines():
xorg_compose_sequences_raw.append(seq)
try:
composefile_lookaside = open(FILENAME_COMPOSE_NEGATIVE_SUPPLEMENTARY, 'r')
for seq in composefile_lookaside.readlines():
xorg_compose_sequences_raw.remove(seq)
except IOError, (errno, strerror):
if opt_verbose:
print "I/O error(%s): %s" % (errno, strerror)
print "Did not find negative lookaside compose file. Continuing..."
except:
print "Unexpected error: ", sys.exc_info()[0]
sys.exit(-1)
try:
composefile_lookaside = open(FILENAME_COMPOSE_SUPPLEMENTARY, 'r')
for seq in composefile_lookaside.readlines():
xorg_compose_sequences_raw.append(seq)
except IOError, (errno, strerror):
if opt_verbose:
print "I/O error(%s): %s" % (errno, strerror)
print "Did not find lookaside compose file. Continuing..."
except:
print "Unexpected error: ", sys.exc_info()[0]
sys.exit(-1)
""" Parse the compose file in xorg_compose_sequences"""
xorg_compose_sequences = []
xorg_compose_sequences_algorithmic = []
linenum_compose = 0
comment_nest_depth = 0
for line in xorg_compose_sequences_raw:
linenum_compose += 1
line = line.strip()
if match("^XCOMM", line) or match("^#", line):
continue
line = sub(r"\/\*([^\*]*|[\*][^/])\*\/", "", line)
comment_start = line.find("/*")
if comment_start >= 0:
if comment_nest_depth == 0:
line = line[:comment_start]
else:
line = ""
comment_nest_depth += 1
else:
comment_end = line.find("*/")
if comment_end >= 0:
comment_nest_depth -= 1
if comment_nest_depth < 0:
print "Invalid comment %(linenum_compose)d in %(filename)s: \
Closing '*/' without opening '/*'" % { "linenum_compose": linenum_compose, "filename": filename_compose }
exit(-1)
if comment_nest_depth > 0:
line = ""
else:
line = line[comment_end + 2:]
if line is "":
continue
#line = line[:-1]
components = split(':', line, 1)
if len(components) != 2:
print "Invalid line %(linenum_compose)d in %(filename)s: No sequence\
/value pair found" % { "linenum_compose": linenum_compose, "filename": filename_compose }
exit(-1)
(seq, val ) = split(':', line, 1)
seq = seq.strip()
val = val.strip()
raw_sequence = findall('\w+', seq)
values = split('\s+', val)
unichar_temp = split('"', values[0])
unichar_utf8 = unichar_temp[1]
if len(values) == 1:
continue
codepointstr = values[1]
if values[1] == '#':
# No codepoints that are >1 characters yet.
continue
if raw_sequence[0][0] == 'U' and match('[0-9a-fA-F]+$', raw_sequence[0][1:]):
raw_sequence[0] = '0x' + raw_sequence[0][1:]
if match('^U[0-9a-fA-F]+$', codepointstr):
codepoint = long(codepointstr[1:], 16)
elif keysymunicodedatabase.has_key(codepointstr):
#if keysymdatabase[codepointstr] != keysymunicodedatabase[codepointstr]:
#print "DIFFERENCE: 0x%(a)X 0x%(b)X" % { "a": keysymdatabase[codepointstr], "b": keysymunicodedatabase[codepointstr]},
#print raw_sequence, codepointstr
codepoint = keysymunicodedatabase[codepointstr]
else:
unichar = unicode(unichar_utf8, 'utf-8')
codepoint = ord(unichar)
sequence = rename_combining(raw_sequence)
reject_this = False
for i in sequence:
if keysymvalue(i) > 0xFFFF:
reject_this = True
if opt_plane1:
print sequence
break
if keysymvalue(i) < 0:
reject_this = True
break
if reject_this:
continue
if "U0342" in sequence or \
"U0313" in sequence or \
"U0314" in sequence or \
"0x0313" in sequence or \
"0x0342" in sequence or \
"0x0314" in sequence:
continue
if codepoint > 0xFFFF:
if opt_verbose:
print "Ignore the line greater than guint16:\n%s" % line
continue
#for i in range(len(sequence)):
# if sequence[i] == "0x0342":
# sequence[i] = "dead_tilde"
if "Multi_key" not in sequence:
""" Ignore for now >0xFFFF keysyms """
if codepoint < 0xFFFF:
original_sequence = copy(sequence)
stats_sequence = copy(sequence)
base = sequence.pop()
basechar = keysymvalue(base, filename_compose, linenum_compose)
if basechar < 0xFFFF:
counter = 1
unisequence = []
not_normalised = True
skipping_this = False
for i in range(0, len(sequence)):
""" If the sequence has dead_tilde and is for Greek, we don't do algorithmically
because of lack of dead_perispomeni (i.e. conflict)
"""
bc = basechar
"""if sequence[-1] == "dead_tilde" and (bc >= 0x370 and bc <= 0x3ff) or (bc >= 0x1f00 and bc <= 0x1fff):
skipping_this = True
break
if sequence[-1] == "dead_horn" and (bc >= 0x370 and bc <= 0x3ff) or (bc >= 0x1f00 and bc <= 0x1fff):
skipping_this = True
break
if sequence[-1] == "dead_ogonek" and (bc >= 0x370 and bc <= 0x3ff) or (bc >= 0x1f00 and bc <= 0x1fff):
skipping_this = True
break
if sequence[-1] == "dead_psili":
sequence[i] = "dead_horn"
if sequence[-1] == "dead_dasia":
sequence[-1] = "dead_ogonek"
"""
unisequence.append(unichr(keysymunicodevalue(sequence.pop(), filename_compose, linenum_compose)))
if skipping_this:
unisequence = []
for perm in all_permutations(unisequence):
# print counter, original_sequence, unichr(basechar) + "".join(perm)
# print counter, map(unichr, perm)
normalized = normalize('NFC', unichr(basechar) + "".join(perm))
if len(normalized) == 1:
# print 'Base: %(base)s [%(basechar)s], produces [%(unichar)s] (0x%(codepoint)04X)' \
# % { "base": base, "basechar": unichr(basechar), "unichar": unichar, "codepoint": codepoint },
# print "Normalized: [%(normalized)s] SUCCESS %(c)d" % { "normalized": normalized, "c": counter }
stats_sequence_data = map(keysymunicodevalue, stats_sequence)
stats_sequence_data.append(normalized)
xorg_compose_sequences_algorithmic.append(stats_sequence_data)
not_normalised = False
break;
counter += 1
if not_normalised:
original_sequence.append(codepoint)
xorg_compose_sequences.append(original_sequence)
""" print xorg_compose_sequences[-1] """
else:
print "Error in base char !?!"
exit(-2)
else:
print "OVER", sequence
exit(-1)
else:
sequence.append(codepoint)
xorg_compose_sequences.append(sequence)
""" print xorg_compose_sequences[-1] """
def sequence_cmp(x, y):
if keysymvalue(x[0]) > keysymvalue(y[0]):
return 1
elif keysymvalue(x[0]) < keysymvalue(y[0]):
return -1
elif len(x) > len(y):
return 1
elif len(x) < len(y):
return -1
elif keysymvalue(x[1]) > keysymvalue(y[1]):
return 1
elif keysymvalue(x[1]) < keysymvalue(y[1]):
return -1
elif len(x) < 4:
return 0
elif keysymvalue(x[2]) > keysymvalue(y[2]):
return 1
elif keysymvalue(x[2]) < keysymvalue(y[2]):
return -1
elif len(x) < 5:
return 0
elif keysymvalue(x[3]) > keysymvalue(y[3]):
return 1
elif keysymvalue(x[3]) < keysymvalue(y[3]):
return -1
elif len(x) < 6:
return 0
elif keysymvalue(x[4]) > keysymvalue(y[4]):
return 1
elif keysymvalue(x[4]) < keysymvalue(y[4]):
return -1
else:
return 0
def sequence_unicode_cmp(x, y):
if keysymunicodevalue(x[0]) > keysymunicodevalue(y[0]):
return 1
elif keysymunicodevalue(x[0]) < keysymunicodevalue(y[0]):
return -1
elif len(x) > len(y):
return 1
elif len(x) < len(y):
return -1
elif keysymunicodevalue(x[1]) > keysymunicodevalue(y[1]):
return 1
elif keysymunicodevalue(x[1]) < keysymunicodevalue(y[1]):
return -1
elif len(x) < 4:
return 0
elif keysymunicodevalue(x[2]) > keysymunicodevalue(y[2]):
return 1
elif keysymunicodevalue(x[2]) < keysymunicodevalue(y[2]):
return -1
elif len(x) < 5:
return 0
elif keysymunicodevalue(x[3]) > keysymunicodevalue(y[3]):
return 1
elif keysymunicodevalue(x[3]) < keysymunicodevalue(y[3]):
return -1
elif len(x) < 6:
return 0
elif keysymunicodevalue(x[4]) > keysymunicodevalue(y[4]):
return 1
elif keysymunicodevalue(x[4]) < keysymunicodevalue(y[4]):
return -1
else:
return 0
def sequence_algorithmic_cmp(x, y):
if len(x) < len(y):
return -1
elif len(x) > len(y):
return 1
else:
for i in range(len(x)):
if x[i] < y[i]:
return -1
elif x[i] > y[i]:
return 1
return 0
xorg_compose_sequences.sort(sequence_cmp)
xorg_compose_sequences_uniqued = []
first_time = True
item = None
for next_item in xorg_compose_sequences:
if first_time:
first_time = False
item = next_item
if sequence_unicode_cmp(item, next_item) != 0:
xorg_compose_sequences_uniqued.append(item)
item = next_item
xorg_compose_sequences = copy(xorg_compose_sequences_uniqued)
counter_multikey = 0
for item in xorg_compose_sequences:
if findall('Multi_key', "".join(item[:-1])) != []:
counter_multikey += 1
xorg_compose_sequences_algorithmic.sort(sequence_algorithmic_cmp)
xorg_compose_sequences_algorithmic_uniqued = uniq(xorg_compose_sequences_algorithmic)
firstitem = ""
num_first_keysyms = 0
zeroes = 0
num_entries = 0
num_algorithmic_greek = 0
for sequence in xorg_compose_sequences:
if keysymvalue(firstitem) != keysymvalue(sequence[0]):
firstitem = sequence[0]
num_first_keysyms += 1
zeroes += 6 - len(sequence) + 1
num_entries += 1
for sequence in xorg_compose_sequences_algorithmic_uniqued:
ch = ord(sequence[-1:][0])
if ch >= 0x370 and ch <= 0x3ff or ch >= 0x1f00 and ch <= 0x1fff:
num_algorithmic_greek += 1
if opt_algorithmic:
for sequence in xorg_compose_sequences_algorithmic_uniqued:
letter = "".join(sequence[-1:])
print '0x%(cp)04X, %(uni)s, seq: [ <0x%(base)04X>,' % { 'cp': ord(unicode(letter)), 'uni': letter.encode('utf-8'), 'base': sequence[-2] },
for elem in sequence[:-2]:
print "<0x%(keysym)04X>," % { 'keysym': elem },
""" Yeah, verified... We just want to keep the output similar to -u, so we can compare/sort easily """
print "], recomposed as", letter.encode('utf-8'), "verified"
def num_of_keysyms(seq):
return len(seq) - 1
def convert_UnotationToHex(arg):
if isinstance(arg, str):
if match('^U[0-9A-F][0-9A-F][0-9A-F][0-9A-F]$', arg):
return sub('^U', '0x', arg)
return arg
def addprefix_GDK(arg):
if match('^0x', arg):
return '%(arg)s, ' % { 'arg': arg }
elif match('^U[0-9A-F][0-9A-F][0-9A-F][0-9A-F]$', arg.upper()):
keysym = ''
for k, c in keysymunicodedatabase.items():
if c == keysymvalue(arg):
keysym = k
break
if keysym != '':
return 'GDK_KEY_%(arg)s, ' % { 'arg': keysym }
else:
return '0x%(arg)04X, ' % { 'arg': keysymvalue(arg) }
else:
return 'GDK_KEY_%(arg)s, ' % { 'arg': arg }
if opt_gtk:
first_keysym = ""
sequence = []
compose_table = []
ct_second_part = []
ct_sequence_width = 2
start_offset = num_first_keysyms * (WIDTHOFCOMPOSETABLE+1)
we_finished = False
counter = 0
sequence_iterator = iter(xorg_compose_sequences)
sequence = sequence_iterator.next()
while True:
first_keysym = sequence[0] # Set the first keysym
compose_table.append([first_keysym, 0, 0, 0, 0, 0])
while sequence[0] == first_keysym:
compose_table[counter][num_of_keysyms(sequence)-1] += 1
try:
sequence = sequence_iterator.next()
except StopIteration:
we_finished = True
break
if we_finished:
break
counter += 1
ct_index = start_offset
for line_num in range(len(compose_table)):
for i in range(WIDTHOFCOMPOSETABLE):
occurrences = compose_table[line_num][i+1]
compose_table[line_num][i+1] = ct_index
ct_index += occurrences * (i+2)
for sequence in xorg_compose_sequences:
ct_second_part.append(map(convert_UnotationToHex, sequence))
print headerfile_start
for i in compose_table:
if opt_gtkexpanded:
print "0x%(ks)04X," % { "ks": keysymvalue(i[0]) },
print '%(str)s' % { 'str': "".join(map(lambda x : str(x) + ", ", i[1:])) }
elif not match('^0x', i[0]):
print 'GDK_KEY_%(str)s' % { 'str': "".join(map(lambda x : str(x) + ", ", i)) }
else:
print '%(str)s' % { 'str': "".join(map(lambda x : str(x) + ", ", i)) }
for i in ct_second_part:
if opt_numeric:
for ks in i[1:][:-1]:
print '0x%(seq)04X, ' % { 'seq': keysymvalue(ks) },
print '0x%(cp)04X, ' % { 'cp':i[-1] }
"""
for ks in i[:-1]:
print '0x%(seq)04X, ' % { 'seq': keysymvalue(ks) },
print '0x%(cp)04X, ' % { 'cp':i[-1] }
"""
elif opt_gtkexpanded:
print '%(seq)s0x%(cp)04X, ' % { 'seq': "".join(map(addprefix_GDK, i[:-1])), 'cp':i[-1] }
else:
print '%(seq)s0x%(cp)04X, ' % { 'seq': "".join(map(addprefix_GDK, i[:-1][1:])), 'cp':i[-1] }
print headerfile_end
def redecompose(codepoint):
(name, decomposition, combiningclass) = unicodedatabase[codepoint]
if decomposition[0] == '' or decomposition[0] == '0':
return [codepoint]
if match('<\w+>', decomposition[0]):
numdecomposition = map(stringtohex, decomposition[1:])
return map(redecompose, numdecomposition)
numdecomposition = map(stringtohex, decomposition)
return map(redecompose, numdecomposition)
def process_unicodedata_file(verbose = False):
""" Grab from wget http://www.unicode.org/Public/UNIDATA/UnicodeData.txt """
filename_unicodedatatxt = download_file(URL_UNICODEDATATXT)
try:
unicodedatatxt = open(filename_unicodedatatxt, 'r')
except IOError, (errno, strerror):
print "I/O error(%s): %s" % (errno, strerror)
sys.exit(-1)
except:
print "Unexpected error: ", sys.exc_info()[0]
sys.exit(-1)
for line in unicodedatatxt.readlines():
if line[0] == "" or line[0] == '#':
continue
line = line[:-1]
uniproperties = split(';', line)
codepoint = stringtohex(uniproperties[0])
""" We don't do Plane 1 or CJK blocks. The latter require reading additional files. """
if codepoint > 0xFFFF or (codepoint >= 0x4E00 and codepoint <= 0x9FFF) or (codepoint >= 0xF900 and codepoint <= 0xFAFF):
continue
name = uniproperties[1]
category = uniproperties[2]
combiningclass = uniproperties[3]
decomposition = uniproperties[5]
unicodedatabase[codepoint] = [name, split('\s+', decomposition), combiningclass]
counter_combinations = 0
counter_combinations_greek = 0
counter_entries = 0
counter_entries_greek = 0
for item in unicodedatabase.keys():
(name, decomposition, combiningclass) = unicodedatabase[item]
if decomposition[0] == '':
continue
print name, "is empty"
elif match('<\w+>', decomposition[0]):
continue
print name, "has weird", decomposition[0]
else:
sequence = map(stringtohex, decomposition)
chrsequence = map(unichr, sequence)
normalized = normalize('NFC', "".join(chrsequence))
""" print name, sequence, "Combining: ", "".join(chrsequence), normalized, len(normalized), """
decomposedsequence = []
for subseq in map(redecompose, sequence):
for seqitem in subseq:
if isinstance(seqitem, list):
for i in seqitem:
if isinstance(i, list):
for j in i:
decomposedsequence.append(j)
else:
decomposedsequence.append(i)
else:
decomposedsequence.append(seqitem)
recomposedchar = normalize('NFC', "".join(map(unichr, decomposedsequence)))
if len(recomposedchar) == 1 and len(decomposedsequence) > 1:
counter_entries += 1
counter_combinations += factorial(len(decomposedsequence)-1)
ch = item
if ch >= 0x370 and ch <= 0x3ff or ch >= 0x1f00 and ch <= 0x1fff:
counter_entries_greek += 1
counter_combinations_greek += factorial(len(decomposedsequence)-1)
if verbose:
print "0x%(cp)04X, %(uni)c, seq:" % { 'cp':item, 'uni':unichr(item) },
print "[",
for elem in decomposedsequence:
print '<0x%(hex)04X>,' % { 'hex': elem },
print "], recomposed as", recomposedchar,
if unichr(item) == recomposedchar:
print "verified"
if verbose == False:
print "Unicode statistics from UnicodeData.txt"
print "Number of entries that can be algorithmically produced :", counter_entries
print " of which are for Greek :", counter_entries_greek
print "Number of compose sequence combinations requiring :", counter_combinations
print " of which are for Greek :", counter_combinations_greek
print "Note: We do not include partial compositions, "
print "thus the slight discrepancy in the figures"
print
if opt_unicodedatatxt:
process_unicodedata_file(True)
if opt_statistics:
print
print "Total number of compose sequences (from file) :", len(xorg_compose_sequences) + len(xorg_compose_sequences_algorithmic)
print " of which can be expressed algorithmically :", len(xorg_compose_sequences_algorithmic)
print " of which cannot be expressed algorithmically :", len(xorg_compose_sequences)
print " of which have Multi_key :", counter_multikey
print
print "Algorithmic (stats for Xorg Compose file)"
print "Number of sequences off due to algo from file (len(array)) :", len(xorg_compose_sequences_algorithmic)
print "Number of sequences off due to algo (uniq(sort(array))) :", len(xorg_compose_sequences_algorithmic_uniqued)
print " of which are for Greek :", num_algorithmic_greek
print
process_unicodedata_file()
print "Not algorithmic (stats from Xorg Compose file)"
print "Number of sequences :", len(xorg_compose_sequences)
print "Flat array looks like :", len(xorg_compose_sequences), "rows of 6 integers (2 bytes per int, or 12 bytes per row)"
print "Flat array would have taken up (in bytes) :", num_entries * 2 * 6, "bytes from the GTK+ library"
print "Number of items in flat array :", len(xorg_compose_sequences) * 6
print " of which are zeroes :", zeroes, "or ", (100 * zeroes) / (len(xorg_compose_sequences) * 6), " per cent"
print "Number of different first items :", num_first_keysyms
print "Number of max bytes (if using flat array) :", num_entries * 2 * 6
print "Number of savings :", zeroes * 2 - num_first_keysyms * 2 * 5
print
print "Memory needs if both algorithmic+optimised table in latest Xorg compose file"
print " :", num_entries * 2 * 6 - zeroes * 2 + num_first_keysyms * 2 * 5
print
print "Existing (old) implementation in GTK+"
print "Number of sequences in old gtkimcontextsimple.c :", 691
print "The existing (old) implementation in GTK+ takes up :", 691 * 2 * 12, "bytes"

View File

@ -1,405 +0,0 @@
#
# This file contains the compose sequences that GTK+ used to have until GTK+ 2.12
# but are not found anymore at the upstream Compose file at X.Org.
# When updating gtkimcontextsimpleseqs.h with compose-parse.py,
# we include this file as well. There are 15 conflicts currently
# in the compose sequences, and we currently favour the sequences from
# this file (against the upstream X.Org file). For more, see
# http://bugzilla.gnome.org/show_bug.cgi?id=557420
#
<Greek_accentdieresis> <Greek_iota> : "ἴ" U0390
<Greek_accentdieresis> <Greek_upsilon> : "ΐ" U03B0
<Multi_key> <B> <period> : "Ḃ" U1E02
<Multi_key> <b> <period> : "ḃ" U1E03
<Multi_key> <D> <period> : "Ḋ" U1E0A
<Multi_key> <d> <period> : "ḋ" U1E0B
<Multi_key> <F> <period> : "Ḟ" U1E1E
<Multi_key> <f> <period> : "ḟ" U1E1F
<Multi_key> <M> <period> : "Ṁ" U1E40
<Multi_key> <S> <period> : "Ṡ" U1E60
<Multi_key> <P> <period> : "Ṗ" U1E56
<Multi_key> <p> <period> : "ṗ" U1E57
<Multi_key> <s> <period> : "ṡ" U1E61
<Multi_key> <T> <period> : "Ṫ" U1E6A
<Multi_key> <t> <period> : "ṫ" U1E6B
<Multi_key> <e> <period> : "ė" U0117
<Multi_key> <C> <bar> : "¢" U00A2
<Multi_key> <bar> <C> : "¢" U00A2
<Multi_key> <minus> <l> : "£" U00A3
<Multi_key> <equal> <l> : "£" U00A3
<Multi_key> <L> <equal> : "£" U00A3
<Multi_key> <l> <minus> : "£" U00A3
<Multi_key> <l> <equal> : "£" U00A3
<Multi_key> <0> <X> : "¤" U00A4
<Multi_key> <0> <x> : "¤" U00A4
<Multi_key> <O> <X> : "¤" U00A4
<Multi_key> <O> <x> : "¤" U00A4
<Multi_key> <X> <0> : "¤" U00A4
<Multi_key> <X> <O> : "¤" U00A4
<Multi_key> <X> <o> : "¤" U00A4
<Multi_key> <o> <X> : "¤" U00A4
<Multi_key> <x> <0> : "¤" U00A4
<Multi_key> <x> <O> : "¤" U00A4
<Multi_key> <minus> <Y> : "¥" U00A5
<Multi_key> <minus> <y> : "¥" U00A5
<Multi_key> <equal> <y> : "¥" U00A5
<Multi_key> <Y> <minus> : "¥" U00A5
<Multi_key> <y> <minus> : "¥" U00A5
<Multi_key> <y> <equal> : "¥" U00A5
<Multi_key> <0> <S> : "§" U00A7
<Multi_key> <0> <s> : "§" U00A7
<Multi_key> <O> <S> : "§" U00A7
<Multi_key> <S> <exclam> : "§" U00A7
<Multi_key> <S> <0> : "§" U00A7
<Multi_key> <S> <O> : "§" U00A7
<Multi_key> <s> <exclam> : "§" U00A7
<Multi_key> <s> <0> : "§" U00A7
<Multi_key> <quotedbl> <quotedbl> : "¨" U00A8
<Multi_key> <parenleft> <c> : "©" U00A9
<Multi_key> <0> <C> : "©" U00A9
<Multi_key> <0> <c> : "©" U00A9
<Multi_key> <C> <0> : "©" U00A9
<Multi_key> <C> <O> : "©" U00A9
<Multi_key> <C> <o> : "©" U00A9
<Multi_key> <c> <0> : "©" U00A9
<Multi_key> <A> <underscore> : "ª" U00AA
<Multi_key> <a> <underscore> : "ª" U00AA
<Multi_key> <C> <comma> : "Ç" U00C7
<Multi_key> <minus> <minus> <space> : "­" U00AD
<Multi_key> <parenleft> <r> : "®" U00AE
<Multi_key> <R> <O> : "®" U00AE
<Multi_key> <minus> <asciicircum> : "¯" U00AF
<Multi_key> <asciicircum> <minus> : "¯" U00AF
<Multi_key> <asciicircum> <underscore> : "¯" U00AF
<Multi_key> <underscore> <asciicircum> : "¯" U00AF
<Multi_key> <underscore> <underscore> : "¯" U00AF
<Multi_key> <asterisk> <0> : "°" U00B0
<Multi_key> <0> <asterisk> : "°" U00B0
<Multi_key> <0> <asciicircum> : "°" U00B0
<Multi_key> <minus> <plus> : "±" U00B1
<Multi_key> <2> <S> : "²" U00B2
<Multi_key> <2> <asciicircum> : "²" U00B2
<Multi_key> <2> <s> : "²" U00B2
<Multi_key> <S> <2> : "²" U00B2
<Multi_key> <s> <2> : "²" U00B2
<Multi_key> <3> <S> : "³" U00B3
<Multi_key> <3> <asciicircum> : "³" U00B3
<Multi_key> <3> <s> : "³" U00B3
<Multi_key> <S> <3> : "³" U00B3
<Multi_key> <s> <3> : "³" U00B3
<Multi_key> <apostrophe> <apostrophe> : "´" U00B4
<Multi_key> <slash> <U> : "µ" U00B5
<Multi_key> <slash> <u> : "µ" U00B5
<Multi_key> <U> <slash> : "µ" U00B5
<Multi_key> <u> <slash> : "µ" U00B5
<Multi_key> <exclam> <P> : "¶" U00B6
<Multi_key> <exclam> <p> : "¶" U00B6
<Multi_key> <period> <asciicircum> : "·" U00B7
<Multi_key> <asciicircum> <period> : "·" U00B7
<Multi_key> <comma> <comma> : "¸" U00B8
<Multi_key> <1> <S> : "¹" U00B9
<Multi_key> <1> <asciicircum> : "¹" U00B9
<Multi_key> <1> <s> : "¹" U00B9
<Multi_key> <S> <1> : "¹" U00B9
<Multi_key> <s> <1> : "¹" U00B9
<Multi_key> <O> <underscore> : "º" U00BA
<Multi_key> <o> <underscore> : "º" U00BA
<Multi_key> <A> <grave> : "À" U00C0
<Multi_key> <A> <apostrophe> : "Á" U00C1
<Multi_key> <A> <acute> : "Á" U00C1
<Multi_key> <greater> <A> : "Â" U00C2
<Multi_key> <A> <greater> : "Â" U00C2
<Multi_key> <A> <asciicircum> : "Â" U00C2
<Multi_key> <minus> <A> : "Ã" U00C3
<Multi_key> <A> <minus> : "Ã" U00C3
<Multi_key> <A> <asciitilde> : "Ã" U00C3
<Multi_key> <A> <quotedbl> : "Ä" U00C4
<Multi_key> <A> <diaeresis> : "Ä" U00C4
<Multi_key> <diaeresis> <A> : "Ä" U00C4
<Multi_key> <asterisk> <A> : "Å" U00C5
<Multi_key> <A> <asterisk> : "Å" U00C5
<Multi_key> <A> <A> : "Å" U00C5
<Multi_key> <space> <less> : "ˇ" U02C7
<Multi_key> <less> <space> : "ˇ" U02C7
<Multi_key> <E> <grave> : "È" U00C8
<Multi_key> <E> <apostrophe> : "É" U00C9
<Multi_key> <E> <acute> : "É" U00C9
<Multi_key> <greater> <E> : "Ê" U00CA
<Multi_key> <E> <greater> : "Ê" U00CA
<Multi_key> <E> <asciicircum> : "Ê" U00CA
<Multi_key> <E> <quotedbl> : "Ë" U00CB
<Multi_key> <E> <diaeresis> : "Ë" U00CB
<Multi_key> <diaeresis> <E> : "Ë" U00CB
<Multi_key> <I> <grave> : "Ì" U00CC
<Multi_key> <I> <apostrophe> : "Í" U00CD
<Multi_key> <I> <acute> : "Í" U00CD
<Multi_key> <greater> <I> : "Î" U00CE
<Multi_key> <I> <greater> : "Î" U00CE
<Multi_key> <I> <asciicircum> : "Î" U00CE
<Multi_key> <I> <quotedbl> : "Ï" U00CF
<Multi_key> <I> <diaeresis> : "Ï" U00CF
<Multi_key> <diaeresis> <I> : "Ï" U00CF
<Multi_key> <minus> <N> : "Ñ" U00D1
<Multi_key> <N> <minus> : "Ñ" U00D1
<Multi_key> <N> <asciitilde> : "Ñ" U00D1
<Multi_key> <O> <grave> : "Ò" U00D2
<Multi_key> <O> <apostrophe> : "Ó" U00D3
<Multi_key> <O> <acute> : "Ó" U00D3
<Multi_key> <greater> <O> : "Ô" U00D4
<Multi_key> <O> <greater> : "Ô" U00D4
<Multi_key> <O> <asciicircum> : "Ô" U00D4
<Multi_key> <minus> <O> : "Õ" U00D5
<Multi_key> <O> <minus> : "Õ" U00D5
<Multi_key> <O> <asciitilde> : "Õ" U00D5
<Multi_key> <O> <quotedbl> : "Ö" U00D6
<Multi_key> <O> <diaeresis> : "Ö" U00D6
<Multi_key> <diaeresis> <O> : "Ö" U00D6
<Multi_key> <space> <parenleft> : "˘" U02D8
<Multi_key> <parenleft> <space> : "˘" U02D8
<Multi_key> <U> <grave> : "Ù" U00D9
<Multi_key> <U> <apostrophe> : "Ú" U00DA
<Multi_key> <U> <acute> : "Ú" U00DA
<Multi_key> <greater> <U> : "Û" U00DB
<Multi_key> <U> <greater> : "Û" U00DB
<Multi_key> <U> <asciicircum> : "Û" U00DB
<Multi_key> <U> <quotedbl> : "Ü" U00DC
<Multi_key> <U> <diaeresis> : "Ü" U00DC
<Multi_key> <diaeresis> <U> : "Ü" U00DC
<Multi_key> <Y> <apostrophe> : "Ý" U00DD
<Multi_key> <Y> <acute> : "Ý" U00DD
<Multi_key> <a> <grave> : "à" U00E0
<Multi_key> <a> <apostrophe> : "á" U00E1
<Multi_key> <a> <acute> : "á" U00E1
<Multi_key> <greater> <a> : "â" U00E2
<Multi_key> <a> <greater> : "â" U00E2
<Multi_key> <a> <asciicircum> : "â" U00E2
<Multi_key> <minus> <a> : "ā" U0101
<Multi_key> <a> <minus> : "ā" U0101
<Multi_key> <a> <asciitilde> : "ã" U00E3
<Multi_key> <a> <quotedbl> : "ä" U00E4
<Multi_key> <a> <diaeresis> : "ä" U00E4
<Multi_key> <diaeresis> <a> : "ä" U00E4
<Multi_key> <asterisk> <a> : "å" U00E5
<Multi_key> <a> <asterisk> : "å" U00E5
<Multi_key> <a> <a> : "å" U00E5
<Multi_key> <c> <comma> : "ç" U00E7
<Multi_key> <e> <grave> : "è" U00E8
<Multi_key> <e> <apostrophe> : "é" U00E9
<Multi_key> <e> <acute> : "é" U00E9
<Multi_key> <greater> <e> : "ê" U00EA
<Multi_key> <e> <greater> : "ê" U00EA
<Multi_key> <e> <asciicircum> : "ê" U00EA
<Multi_key> <e> <quotedbl> : "ë" U00EB
<Multi_key> <e> <diaeresis> : "ë" U00EB
<Multi_key> <diaeresis> <e> : "ë" U00EB
<Multi_key> <i> <grave> : "ì" U00EC
<Multi_key> <i> <apostrophe> : "í" U00ED
<Multi_key> <i> <acute> : "í" U00ED
<Multi_key> <greater> <i> : "î" U00EE
<Multi_key> <i> <greater> : "î" U00EE
<Multi_key> <i> <asciicircum> : "î" U00EE
<Multi_key> <i> <quotedbl> : "ï" U00EF
<Multi_key> <i> <diaeresis> : "ï" U00EF
<Multi_key> <diaeresis> <i> : "ï" U00EF
<Multi_key> <minus> <n> : "ñ" U00F1
<Multi_key> <n> <minus> : "ñ" U00F1
<Multi_key> <n> <asciitilde> : "ñ" U00F1
<Multi_key> <o> <grave> : "ò" U00F2
<Multi_key> <o> <apostrophe> : "ó" U00F3
<Multi_key> <o> <acute> : "ó" U00F3
<Multi_key> <greater> <o> : "ô" U00F4
<Multi_key> <o> <greater> : "ô" U00F4
<Multi_key> <o> <asciicircum> : "ô" U00F4
<Multi_key> <minus> <o> : "ō" U014D
<Multi_key> <o> <minus> : "ō" U014D
<Multi_key> <o> <asciitilde> : "õ" U00F5
<Multi_key> <o> <quotedbl> : "ö" U00F6
<Multi_key> <o> <diaeresis> : "ö" U00F6
<Multi_key> <diaeresis> <o> : "ö" U00F6
<Multi_key> <o> <slash> : "ø" U00F8
<Multi_key> <u> <grave> : "ù" U00F9
<Multi_key> <u> <apostrophe> : "ú" U00FA
<Multi_key> <u> <acute> : "ú" U00FA
<Multi_key> <greater> <u> : "û" U00FB
<Multi_key> <u> <greater> : "û" U00FB
<Multi_key> <u> <asciicircum> : "û" U00FB
<Multi_key> <u> <quotedbl> : "ü" U00FC
<Multi_key> <u> <diaeresis> : "ü" U00FC
<Multi_key> <diaeresis> <u> : "ü" U00FC
<Multi_key> <y> <apostrophe> : "ý" U00FD
<Multi_key> <y> <acute> : "ý" U00FD
<Multi_key> <y> <quotedbl> : "ÿ" U00FF
<Multi_key> <y> <diaeresis> : "ÿ" U00FF
<Multi_key> <diaeresis> <y> : "ÿ" U00FF
<Multi_key> <parenleft> <A> : "Ă" U0102
<Multi_key> <A> <parenleft> : "Ă" U0102
<Multi_key> <parenleft> <a> : "ă" U0103
<Multi_key> <a> <parenleft> : "ă" U0103
<Multi_key> <comma> <A> : "Ą" U0104
<Multi_key> <A> <comma> : "Ą" U0104
<Multi_key> <comma> <a> : "ą" U0105
<Multi_key> <a> <comma> : "ą" U0105
<Multi_key> <C> <apostrophe> : "Ć" U0106
<Multi_key> <c> <apostrophe> : "ć" U0107
<Multi_key> <C> <period> : "Ċ" U010A
<Multi_key> <c> <period> : "ċ" U010B
<Multi_key> <less> <C> : "Č" U010C
<Multi_key> <C> <less> : "Č" U010C
<Multi_key> <less> <c> : "č" U010D
<Multi_key> <c> <less> : "č" U010D
<Multi_key> <less> <D> : "Ď" U010E
<Multi_key> <D> <less> : "Ď" U010E
<Multi_key> <less> <d> : "ď" U010F
<Multi_key> <d> <less> : "ď" U010F
<Multi_key> <minus> <D> : "Đ" U0110
<Multi_key> <D> <minus> : "Đ" U0110
<Multi_key> <minus> <d> : "đ" U0111
<Multi_key> <minus> <E> : "Ē" U0112
<Multi_key> <E> <minus> : "Ē" U0112
<Multi_key> <E> <underscore> : "Ē" U0112
<Multi_key> <minus> <e> : "ē" U0113
<Multi_key> <e> <minus> : "ē" U0113
<Multi_key> <e> <underscore> : "ē" U0113
<Multi_key> <E> <period> : "Ė" U0116
<Multi_key> <E> <comma> : "Ę" U0118
<Multi_key> <e> <comma> : "ę" U0119
<Multi_key> <less> <E> : "Ě" U011A
<Multi_key> <E> <less> : "Ě" U011A
<Multi_key> <less> <e> : "ě" U011B
<Multi_key> <e> <less> : "ě" U011B
<Multi_key> <parenleft> <G> : "Ğ" U011E
<Multi_key> <G> <parenleft> : "Ğ" U011E
<Multi_key> <G> <U> : "Ğ" U011E
<Multi_key> <G> <breve> : "Ğ" U011E
<Multi_key> <breve> <G> : "Ğ" U011E
<Multi_key> <parenleft> <g> : "ğ" U011F
<Multi_key> <g> <parenleft> : "ğ" U011F
<Multi_key> <g> <U> : "ğ" U011F
<Multi_key> <g> <breve> : "ğ" U011F
<Multi_key> <breve> <g> : "ğ" U011F
<Multi_key> <G> <period> : "Ġ" U0120
<Multi_key> <g> <period> : "ġ" U0121
<Multi_key> <G> <comma> : "Ģ" U0122
<Multi_key> <g> <comma> : "ģ" U0123
<Multi_key> <I> <asciitilde> : "Ĩ" U0128
<Multi_key> <i> <asciitilde> : "ĩ" U0129
<Multi_key> <minus> <I> : "Ī" U012A
<Multi_key> <I> <minus> : "Ī" U012A
<Multi_key> <I> <underscore> : "Ī" U012A
<Multi_key> <minus> <i> : "ī" U012B
<Multi_key> <i> <minus> : "ī" U012B
<Multi_key> <i> <underscore> : "ī" U012B
<Multi_key> <comma> <I> : "Į" U012E
<Multi_key> <I> <comma> : "Į" U012E
<Multi_key> <I> <period> : "İ" U0130
<Multi_key> <period> <i> : "ı" U0131
<Multi_key> <K> <comma> : "Ķ" U0136
<Multi_key> <k> <comma> : "ķ" U0137
<Multi_key> <L> <apostrophe> : "Ĺ" U0139
<Multi_key> <l> <apostrophe> : "ĺ" U013A
<Multi_key> <L> <comma> : "Ļ" U013B
<Multi_key> <l> <comma> : "ļ" U013C
<Multi_key> <less> <L> : "Ľ" U013D
<Multi_key> <L> <less> : "Ľ" U013D
<Multi_key> <less> <l> : "ľ" U013E
<Multi_key> <l> <less> : "ľ" U013E
<Multi_key> <L> <slash> : "Ł" U0141
<Multi_key> <l> <slash> : "ł" U0142
<Multi_key> <N> <apostrophe> : "Ń" U0143
<Multi_key> <n> <apostrophe> : "ń" U0144
<Multi_key> <N> <comma> : "Ņ" U0145
<Multi_key> <n> <comma> : "ņ" U0146
<Multi_key> <less> <N> : "Ň" U0147
<Multi_key> <N> <less> : "Ň" U0147
<Multi_key> <less> <n> : "ň" U0148
<Multi_key> <n> <less> : "ň" U0148
<Multi_key> <R> <apostrophe> : "Ŕ" U0154
<Multi_key> <r> <apostrophe> : "ŕ" U0155
<Multi_key> <R> <comma> : "Ŗ" U0156
<Multi_key> <r> <comma> : "ŗ" U0157
<Multi_key> <less> <R> : "Ř" U0158
<Multi_key> <R> <less> : "Ř" U0158
<Multi_key> <less> <r> : "ř" U0159
<Multi_key> <r> <less> : "ř" U0159
<Multi_key> <S> <apostrophe> : "Ś" U015A
<Multi_key> <s> <apostrophe> : "ś" U015B
<Multi_key> <O> <slash> : "Ø" U00D8
<Multi_key> <S> <comma> : "Ş" U015E
<Multi_key> <S> <cedilla> : "Ş" U015E
<Multi_key> <s> <comma> : "ş" U015F
<Multi_key> <s> <cedilla> : "ş" U015F
<Multi_key> <less> <S> : "Š" U0160
<Multi_key> <S> <less> : "Š" U0160
<Multi_key> <less> <s> : "š" U0161
<Multi_key> <s> <less> : "š" U0161
<Multi_key> <less> <T> : "Ť" U0164
<Multi_key> <T> <less> : "Ť" U0164
<Multi_key> <less> <t> : "ť" U0165
<Multi_key> <t> <less> : "ť" U0165
<Multi_key> <T> <minus> : "Ŧ" U0166
<Multi_key> <T> <slash> : "Ŧ" U0166
<Multi_key> <t> <minus> : "ŧ" U0167
<Multi_key> <t> <slash> : "ŧ" U0167
<Multi_key> <U> <asciitilde> : "Ũ" U0168
<Multi_key> <u> <asciitilde> : "ũ" U0169
<Multi_key> <minus> <U> : "Ū" U016A
<Multi_key> <U> <minus> : "Ū" U016A
<Multi_key> <U> <underscore> : "Ū" U016A
<Multi_key> <minus> <u> : "ū" U016B
<Multi_key> <u> <minus> : "ū" U016B
<Multi_key> <u> <underscore> : "ū" U016B
<Multi_key> <asterisk> <U> : "Ů" U016E
<Multi_key> <U> <asterisk> : "Ů" U016E
<Multi_key> <asterisk> <u> : "ů" U016F
<Multi_key> <u> <asterisk> : "ů" U016F
<Multi_key> <comma> <U> : "Ų" U0172
<Multi_key> <U> <comma> : "Ų" U0172
<Multi_key> <comma> <u> : "ų" U0173
<Multi_key> <u> <comma> : "ų" U0173
<Multi_key> <W> <asciicircum> : "Ŵ" U0174
<Multi_key> <w> <asciicircum> : "ŵ" U0175
<Multi_key> <Y> <asciicircum> : "Ŷ" U0176
<Multi_key> <y> <asciicircum> : "ŷ" U0177
<Multi_key> <Y> <quotedbl> : "Ÿ" U0178
<Multi_key> <Y> <diaeresis> : "Ÿ" U0178
<Multi_key> <diaeresis> <Y> : "Ÿ" U0178
<Multi_key> <Z> <apostrophe> : "Ź" U0179
<Multi_key> <z> <apostrophe> : "ź" U017A
<Multi_key> <Z> <period> : "Ż" U017B
<Multi_key> <z> <period> : "ż" U017C
<Multi_key> <less> <Z> : "Ž" U017D
<Multi_key> <Z> <less> : "Ž" U017D
<Multi_key> <v> <Z> : "Ž" U017D
<Multi_key> <less> <z> : "ž" U017E
<Multi_key> <v> <z> : "ž" U017E
<Multi_key> <z> <less> : "ž" U017E
<dead_acute> <dead_diaeresis> <space> : "΅" U0385
<dead_diaeresis> <dead_acute> <space> : "΅" U0385
<Multi_key> <quotedbl> <apostrophe> <space> : "΅" U0385
<Multi_key> <apostrophe> <quotedbl> <space> : "΅" U0385
<Multi_key> <Greek_ALPHA> <apostrophe> : "Ά" U0386
<Multi_key> <m> <period> : "ṁ" U1E41
<Multi_key> <Greek_EPSILON> <apostrophe> : "Έ" U0388
<Multi_key> <Greek_ETA> <apostrophe> : "Ή" U0389
<Multi_key> <Greek_IOTA> <apostrophe> : "Ί" U038A
<Multi_key> <Greek_OMICRON> <apostrophe> : "Ό" U038C
<Multi_key> <Greek_UPSILON> <apostrophe> : "Ύ" U038E
<Multi_key> <Greek_OMEGA> <apostrophe> : "Ώ" U038F
<dead_diaeresis> <dead_acute> <Greek_iota> : "ΐ" U0390
<Multi_key> <quotedbl> <apostrophe> <Greek_iota> : "ΐ" U0390
<Multi_key> <comma> <i> : "į" U012F
<Multi_key> <i> <comma> : "į" U012F
<Multi_key> <Greek_IOTA> <quotedbl> : "Ϊ" U03AA
<Multi_key> <Greek_UPSILON> <quotedbl> : "Ϋ" U03AB
<Multi_key> <Greek_alpha> <apostrophe> : "ά" U03AC
<Multi_key> <Greek_epsilon> <apostrophe> : "έ" U03AD
<Multi_key> <Greek_eta> <apostrophe> : "ή" U03AE
<Multi_key> <Greek_iota> <apostrophe> : "ί" U03AF
<dead_diaeresis> <dead_acute> <Greek_upsilon> : "ΰ" U03B0
<Multi_key> <quotedbl> <apostrophe> <Greek_upsilon> : "ΰ" U03B0
<Multi_key> <Greek_iota> <quotedbl> : "ϊ" U03CA
<Multi_key> <Greek_upsilon> <quotedbl> : "ϋ" U03CB
<Multi_key> <Greek_omicron> <apostrophe> : "ό" U03CC
<Multi_key> <Greek_upsilon> <apostrophe> : "ύ" U03CD
<Multi_key> <Greek_omega> <apostrophe> : "ώ" U03CE

View File

@ -0,0 +1,9 @@
#ifndef __GTK_COMPOSE_DATA__
#define __GTK_COMPOSE_DATA__
#define MAX_SEQ_LEN 5
#define N_INDEX_SIZE 30
#define DATA_SIZE 16521
#define N_CHARS 1572
#endif

7
gtk/compose/meson.build Normal file
View File

@ -0,0 +1,7 @@
compose_parse = executable('compose-parse',
sources: 'compose-parse.c',
c_args: gtk_cargs + common_cflags,
include_directories: [confinc, gtkinc],
dependencies: libgtk_static_dep,
install: false,
)

BIN
gtk/compose/sequences Normal file

Binary file not shown.

View File

@ -84,6 +84,8 @@ for f in get_files('inspector', '.ui'):
xml += '''
<file>inspector/inspector.css</file>
<file>emoji/en.data</file>
<file>compose/sequences</file>
<file>compose/chars</file>
</gresource>
</gresources>'''

View File

@ -28,9 +28,9 @@
#define GTK_COMPOSE_TABLE_MAGIC "GtkComposeTable"
#define GTK_COMPOSE_TABLE_VERSION (2)
#define GTK_COMPOSE_TABLE_VERSION (3)
extern const GtkComposeTableCompact gtk_compose_table_compact;
extern const GtkComposeTable builtin_compose_table;
/* Maximum length of sequences we parse */
@ -326,36 +326,27 @@ handle_substitutions (const char *start,
return g_string_free (s, FALSE);
}
static void
add_sequence (gunichar *sequence,
int len,
const char *value,
gpointer data)
{
GtkComposeParser *parser = data;
gunichar *seq;
seq = g_new (gunichar, len + 1);
memcpy (seq, sequence, (len + 1) * sizeof (gunichar));
g_hash_table_replace (parser->sequences, seq, g_strdup (value));
}
static void
parser_add_default_sequences (GtkComposeParser *parser)
{
const GtkComposeTableCompact *table = &gtk_compose_table_compact;
const GtkComposeTable *table = &builtin_compose_table;
for (int idx = 0; idx < table->n_index_size; idx++)
{
const guint16 *seq_index = table->data + (idx * table->n_index_stride);
for (int i = 1; i < table->max_seq_len; i++)
{
int row_stride = i + 1;
for (int j = seq_index[i]; j < seq_index[i + 1]; j += row_stride)
{
char buf[8] = { 0, };
gunichar *sequence;
sequence = g_new0 (gunichar, row_stride + 2);
sequence[0] = seq_index[0];
for (int k = 0; k < i; k++)
sequence[1 + k] = table->data[j + k];
sequence[1 + i] = 0;
g_unichar_to_utf8 (table->data[j + i], buf);
g_hash_table_replace (parser->sequences, sequence, g_strdup (buf));
}
}
}
gtk_compose_table_foreach (table, add_sequence, parser);
}
static void
@ -547,29 +538,54 @@ next:
}
}
static int
parser_compute_max_compose_len (GtkComposeParser *parser)
static void
parser_compute_max_compose_len (GtkComposeParser *parser,
int *max_compose_len,
int *n_first,
int *size)
{
GHashTableIter iter;
int max_compose_len = 0;
gunichar *sequence;
char *value;
int max = 0;
int count = 0;
GHashTable *first;
first = g_hash_table_new (NULL, NULL);
g_hash_table_iter_init (&iter, parser->sequences);
while (g_hash_table_iter_next (&iter, (gpointer *)&sequence, (gpointer *)&value))
{
g_hash_table_add (first, GUINT_TO_POINTER (sequence[0]));
for (int i = 0; i < MAX_COMPOSE_LEN + 1; i++)
{
if (sequence[i] == 0)
{
if (max_compose_len < i)
max_compose_len = i;
count += i;
if (max < i)
max = i;
break;
}
}
}
return max_compose_len;
*max_compose_len = max;
*n_first = g_hash_table_size (first);
*size = count;
g_hash_table_unref (first);
}
static inline int
sequence_length (gpointer a)
{
gunichar *seq = a;
int i;
for (i = 0; seq[i]; i++) ;
return i;
}
static int
@ -579,12 +595,26 @@ sequence_compare (gpointer a,
{
gunichar *seq_a = a;
gunichar *seq_b = b;
int max_compose_len = GPOINTER_TO_INT (data);
int i;
for (i = 0; i < max_compose_len; i++)
gunichar code_a, code_b;
int len_a, len_b;
code_a = seq_a[0];
code_b = seq_b[0];
if (code_a != code_b)
return code_a - code_b;
len_a = sequence_length (a);
len_b = sequence_length (b);
if (len_a != len_b)
return len_a - len_b;
for (i = 1; i < len_a; i++)
{
gunichar code_a = seq_a[i];
gunichar code_b = seq_b[i];
code_a = seq_a[i];
code_b = seq_b[i];
if (code_a != code_b)
return code_a - code_b;
@ -640,29 +670,29 @@ gtk_compose_table_serialize (GtkComposeTable *compose_table,
gsize *count)
{
char *p, *contents;
gsize length, total_length;
gsize header_length, total_length;
guint16 bytes;
const char *header = GTK_COMPOSE_TABLE_MAGIC;
const guint16 version = GTK_COMPOSE_TABLE_VERSION;
guint16 max_seq_len = compose_table->max_seq_len;
guint16 index_stride = max_seq_len + 2;
guint16 n_seqs = compose_table->n_seqs;
guint16 n_index_size = compose_table->n_index_size;
guint16 data_size = compose_table->data_size;
guint16 n_chars = compose_table->n_chars;
guint32 i;
g_return_val_if_fail (compose_table != NULL, NULL);
g_return_val_if_fail (max_seq_len > 0, NULL);
g_return_val_if_fail (index_stride > 0, NULL);
g_return_val_if_fail (n_index_size > 0, NULL);
length = strlen (header);
total_length = length + sizeof (guint16) * (4 + index_stride * n_seqs) + n_chars;
header_length = strlen (header);
total_length = header_length + sizeof (guint16) * (5 + data_size) + n_chars;
if (count)
*count = total_length;
p = contents = g_malloc (total_length);
memcpy (p, header, length);
p += length;
memcpy (p, header, header_length);
p += header_length;
#define APPEND_GUINT16(elt) \
bytes = GUINT16_TO_BE (elt); \
@ -671,10 +701,11 @@ gtk_compose_table_serialize (GtkComposeTable *compose_table,
APPEND_GUINT16 (version);
APPEND_GUINT16 (max_seq_len);
APPEND_GUINT16 (n_seqs);
APPEND_GUINT16 (n_index_size);
APPEND_GUINT16 (data_size);
APPEND_GUINT16 (n_chars);
for (i = 0; i < (guint32) index_stride * n_seqs; i++)
for (i = 0; i < data_size; i++)
{
APPEND_GUINT16 (compose_table->data[i]);
}
@ -701,13 +732,13 @@ gtk_compose_table_load_cache (const char *compose_file)
guint16 bytes;
guint16 version;
guint16 max_seq_len;
guint16 index_stride;
guint16 n_seqs;
guint16 n_index_size;
guint16 data_size;
guint16 n_chars;
guint32 i;
guint16 *gtk_compose_seqs = NULL;
GtkComposeTable *retval;
guint16 *data = NULL;
char *char_data = NULL;
GtkComposeTable *retval;
hash = g_str_hash (compose_file);
if ((path = gtk_compose_hash_get_cache_path (hash)) == NULL)
@ -755,21 +786,21 @@ gtk_compose_table_load_cache (const char *compose_file)
}
GET_GUINT16 (max_seq_len);
GET_GUINT16 (n_seqs);
GET_GUINT16 (n_index_size);
GET_GUINT16 (data_size);
GET_GUINT16 (n_chars);
if (max_seq_len == 0 || n_seqs == 0)
if (max_seq_len == 0 || data_size == 0)
{
g_warning ("cache size is not correct %d %d", max_seq_len, n_seqs);
g_warning ("cache size is not correct %d %d", max_seq_len, data_size);
goto out_load_cache;
}
index_stride = max_seq_len + 2;
gtk_compose_seqs = g_new0 (guint16, n_seqs * index_stride);
data = g_new0 (guint16, data_size);
for (i = 0; i < (guint32) index_stride * n_seqs; i++)
for (i = 0; i < data_size; i++)
{
GET_GUINT16 (gtk_compose_seqs[i]);
GET_GUINT16 (data[i]);
}
if (n_chars > 0)
@ -780,9 +811,10 @@ gtk_compose_table_load_cache (const char *compose_file)
}
retval = g_new0 (GtkComposeTable, 1);
retval->data = gtk_compose_seqs;
retval->data = data;
retval->max_seq_len = max_seq_len;
retval->n_seqs = n_seqs;
retval->n_index_size = n_index_size;
retval->data_size = data_size;
retval->char_data = char_data;
retval->n_chars = n_chars;
retval->id = hash;
@ -795,7 +827,7 @@ gtk_compose_table_load_cache (const char *compose_file)
#undef GET_GUINT16
out_load_cache:
g_free (gtk_compose_seqs);
g_free (data);
g_free (char_data);
g_free (contents);
g_free (path);
@ -834,94 +866,147 @@ out_save_cache:
static GtkComposeTable *
parser_get_compose_table (GtkComposeParser *parser)
{
guint length;
guint n = 0;
int i, j;
guint16 *gtk_compose_seqs = NULL;
GList *list;
GtkComposeTable *retval = NULL;
gunichar codepoint;
guint16 *data;
GtkComposeTable *table;
guint16 encoded_value;
GString *char_data;
int max_compose_len = 0;
int max_compose_len;
GList *sequences;
guint32 hash;
GList *list;
int i;
int size;
int n_first;
int first_pos;
int rest_pos;
int index_rowstride;
gunichar current_first;
parser_remove_duplicates (parser);
if (g_hash_table_size (parser->sequences) == 0)
return NULL;
hash = g_str_hash (parser->compose_file);
max_compose_len = parser_compute_max_compose_len (parser);
parser_compute_max_compose_len (parser, &max_compose_len, &n_first, &size);
sequences = g_hash_table_get_keys (parser->sequences);
sequences = g_list_sort_with_data (sequences,
(GCompareDataFunc) sequence_compare,
GINT_TO_POINTER (max_compose_len));
NULL);
length = g_list_length (sequences);
gtk_compose_seqs = g_new0 (guint16, length * (max_compose_len + 2));
index_rowstride = max_compose_len + 1;
data = g_new0 (guint16, n_first * index_rowstride + size);
char_data = g_string_new ("");
current_first = 0;
first_pos = 0;
rest_pos = n_first * index_rowstride;
for (list = sequences; list != NULL; list = list->next)
{
gunichar *sequence = list->data;
char *value = g_hash_table_lookup (parser->sequences, sequence);
int len = sequence_length (sequence);
for (i = 0; i < max_compose_len; i++)
g_assert (2 <= len && len <= max_compose_len);
/* Encode the value. If the value is a single
* character with a value smaller than 1 << 15,
* we just use it directly.
* Otherwise, we store the value as string and
* put the offset into the table, with the high
* bit set.
*/
if (g_utf8_strlen (value, -1) == 1 &&
g_utf8_get_char (value) < 0x8000)
{
if (sequence[i] == 0)
{
for (j = i; j < max_compose_len; j++)
gtk_compose_seqs[n++] = 0;
break;
}
gtk_compose_seqs[n++] = (guint16) sequence[i];
}
if (g_utf8_strlen (value, -1) > 1)
{
if (char_data->len > 0)
g_string_append_c (char_data, 0);
codepoint = char_data->len | (1 << 31);
g_string_append (char_data, value);
encoded_value = (guint16) g_utf8_get_char (value);
}
else
{
codepoint = g_utf8_get_char (value);
g_assert ((codepoint & (1 << 31)) == 0);
g_assert (strlen (value) < 20);
if (char_data->len > 0)
g_string_append_c (char_data, 0);
g_assert (char_data->len < 0x8000);
encoded_value = (guint16) (char_data->len | 0x8000);
g_string_append (char_data, value);
}
gtk_compose_seqs[n++] = (codepoint & 0xffff0000) >> 16;
gtk_compose_seqs[n++] = codepoint & 0xffff;
if (sequence[0] != current_first)
{
g_assert (sequence[0] <= 0xffff);
if (current_first != 0)
first_pos += index_rowstride;
current_first = (guint16)sequence[0];
data[first_pos] = (guint16)sequence[0];
for (i = 1; i < index_rowstride; i++)
data[first_pos + i] = rest_pos;
}
for (i = 1; i < len; i++)
{
g_assert (sequence[i] != 0);
g_assert (sequence[i] <= 0xffff);
data[rest_pos + i - 1] = (guint16) sequence[i];
}
g_assert (encoded_value != 0);
data[rest_pos + len - 1] = encoded_value;
rest_pos += len;
for (i = len; i <= max_compose_len; i++)
data[first_pos + i] = rest_pos;
for (i = 1; i < max_compose_len; i++)
g_assert (data[first_pos + i] <= data[first_pos + i + 1]);
}
retval = g_new0 (GtkComposeTable, 1);
retval->data = gtk_compose_seqs;
retval->max_seq_len = max_compose_len;
retval->n_seqs = length;
retval->id = hash;
retval->n_chars = char_data->len;
retval->char_data = g_string_free (char_data, FALSE);
g_assert (first_pos + index_rowstride == n_first * index_rowstride);
g_assert (rest_pos == n_first * index_rowstride + size);
if (char_data->len > 0)
g_string_append_c (char_data, 0);
table = g_new0 (GtkComposeTable, 1);
table->data = data;
table->data_size = n_first * index_rowstride + size;
table->max_seq_len = max_compose_len;
table->n_index_size = n_first;
table->n_chars = char_data->len;
table->char_data = g_string_free (char_data, FALSE);
table->id = g_str_hash (parser->compose_file);
g_list_free (sequences);
return retval;
return table;
}
static char *
canonicalize_filename (const char *path)
canonicalize_filename (GtkComposeParser *parser,
const char *path)
{
GFile *file;
char *retval;
file = g_file_new_for_path (path);
if (path[0] != '/' && parser->compose_file)
{
GFile *orig = g_file_new_for_path (parser->compose_file);
GFile *parent = g_file_get_parent (orig);
file = g_file_resolve_relative_path (parent, path);
g_object_unref (parent);
g_object_unref (orig);
}
else
{
file = g_file_new_for_path (path);
}
retval = g_file_get_path (file);
g_object_unref (file);
@ -939,7 +1024,7 @@ parser_parse_file (GtkComposeParser *parser,
if (parser->compose_file == NULL)
parser->compose_file = compose_file;
path = canonicalize_filename (compose_file);
path = canonicalize_filename (parser, compose_file);
if (g_list_find_custom (parser->files, path, (GCompareFunc)strcmp))
{
@ -956,24 +1041,31 @@ parser_parse_file (GtkComposeParser *parser,
}
GtkComposeTable *
gtk_compose_table_new_with_file (const char *compose_file)
gtk_compose_table_parse (const char *compose_file)
{
GtkComposeParser *parser;
GtkComposeTable *compose_table;
parser = parser_new ();
parser_parse_file (parser, compose_file);
compose_table = parser_get_compose_table (parser);
parser_free (parser);
return compose_table;
}
GtkComposeTable *
gtk_compose_table_new_with_file (const char *compose_file)
{
GtkComposeTable *compose_table;
g_assert (compose_file != NULL);
compose_table = gtk_compose_table_load_cache (compose_file);
if (compose_table != NULL)
return compose_table;
parser = parser_new ();
parser_parse_file (parser, compose_file);
compose_table = parser_get_compose_table (parser);
parser_free (parser);
compose_table = gtk_compose_table_parse (compose_file);
if (compose_table != NULL)
gtk_compose_table_save_cache (compose_table);
@ -986,34 +1078,30 @@ gtk_compose_table_new_with_data (const guint16 *data,
int max_seq_len,
int n_seqs)
{
GtkComposeParser *parser;
GtkComposeTable *compose_table;
gsize n_index_stride;
gsize length;
int i;
guint16 *gtk_compose_seqs = NULL;
g_return_val_if_fail (data != NULL, NULL);
g_return_val_if_fail (max_seq_len >= 0, NULL);
g_return_val_if_fail (n_seqs >= 0, NULL);
parser = parser_new ();
n_index_stride = max_seq_len + 2;
if (!g_size_checked_mul (&length, n_index_stride, n_seqs))
for (i = 0; i < n_seqs; i++)
{
g_critical ("Overflow in the compose sequences");
return NULL;
const guint16 *seq = data + i * (max_seq_len + 2);
guint16 *sequence;
gunichar ch;
char buf[8] = { 0, };
sequence = g_new0 (guint16, max_seq_len + 1);
memcpy (sequence, seq, sizeof (guint16) * max_seq_len);
ch = ((gunichar)seq[max_seq_len]) << 16 | (gunichar)seq[max_seq_len + 1];
g_unichar_to_utf8 (ch, buf);
g_hash_table_replace (parser->sequences, sequence, g_strdup (buf));
}
gtk_compose_seqs = g_new0 (guint16, length);
for (i = 0; i < length; i++)
gtk_compose_seqs[i] = data[i];
compose_table = g_new (GtkComposeTable, 1);
compose_table->data = gtk_compose_seqs;
compose_table->max_seq_len = max_seq_len;
compose_table->n_seqs = n_seqs;
compose_table->id = data_hash (data, length);
compose_table->char_data = NULL;
compose_table->n_chars = 0;
compose_table = parser_get_compose_table (parser);
parser_free (parser);
return compose_table;
}
@ -1038,6 +1126,20 @@ compare_seq (const void *key, const void *value)
return 0;
}
static int
compare_seq_index (const void *key, const void *value)
{
const guint16 *keysyms = key;
const guint16 *seq = value;
if (keysyms[0] < seq[0])
return -1;
else if (keysyms[0] > seq[0])
return 1;
return 0;
}
/*
* gtk_compose_table_check:
* @table: the table to check
@ -1058,97 +1160,6 @@ gtk_compose_table_check (const GtkComposeTable *table,
gboolean *compose_finish,
gboolean *compose_match,
GString *output)
{
int row_stride = table->max_seq_len + 2;
guint16 *seq;
*compose_finish = FALSE;
*compose_match = FALSE;
g_string_set_size (output, 0);
/* Will never match, if the sequence in the compose buffer is longer
* than the sequences in the table. Further, compare_seq (key, val)
* will overrun val if key is longer than val.
*/
if (n_compose > table->max_seq_len)
return FALSE;
seq = bsearch (compose_buffer,
table->data, table->n_seqs,
sizeof (guint16) * row_stride,
compare_seq);
if (seq)
{
guint16 *prev_seq;
/* Back up to the first sequence that matches to make sure
* we find the exact match if there is one.
*/
while (seq > table->data)
{
prev_seq = seq - row_stride;
if (compare_seq (compose_buffer, prev_seq) != 0)
break;
seq = prev_seq;
}
if (n_compose == table->max_seq_len ||
seq[n_compose] == 0) /* complete sequence */
{
guint16 *next_seq;
gunichar value;
value = (seq[table->max_seq_len] << 16) | seq[table->max_seq_len + 1];
if ((value & (1 << 31)) != 0)
g_string_append (output, &table->char_data[value & ~(1 << 31)]);
else
g_string_append_unichar (output, value);
*compose_match = TRUE;
/* We found a tentative match. See if there are any longer
* sequences containing this subsequence
*/
next_seq = seq + row_stride;
if (next_seq < table->data + row_stride * table->n_seqs)
{
if (compare_seq (compose_buffer, next_seq) == 0)
return TRUE;
}
*compose_finish = TRUE;
return TRUE;
}
return TRUE;
}
return FALSE;
}
static int
compare_seq_index (const void *key, const void *value)
{
const guint16 *keysyms = key;
const guint16 *seq = value;
if (keysyms[0] < seq[0])
return -1;
else if (keysyms[0] > seq[0])
return 1;
return 0;
}
gboolean
gtk_compose_table_compact_check (const GtkComposeTableCompact *table,
const guint16 *compose_buffer,
int n_compose,
gboolean *compose_finish,
gboolean *compose_match,
gunichar *output_char)
{
int row_stride;
guint16 *seq_index;
@ -1161,8 +1172,6 @@ gtk_compose_table_compact_check (const GtkComposeTableCompact *table,
*compose_finish = FALSE;
if (compose_match)
*compose_match = FALSE;
if (output_char)
*output_char = 0;
/* Will never match, if the sequence in the compose buffer is longer
* than the sequences in the table. Further, compare_seq (key, val)
@ -1174,7 +1183,7 @@ gtk_compose_table_compact_check (const GtkComposeTableCompact *table,
seq_index = bsearch (compose_buffer,
table->data,
table->n_index_size,
sizeof (guint16) * table->n_index_stride,
sizeof (guint16) * (table->max_seq_len + 1),
compare_seq_index);
if (!seq_index)
@ -1196,7 +1205,7 @@ gtk_compose_table_compact_check (const GtkComposeTableCompact *table,
seq = bsearch (compose_buffer + 1,
table->data + seq_index[i],
(seq_index[i + 1] - seq_index[i]) / row_stride,
sizeof (guint16) * row_stride,
sizeof (guint16) * row_stride,
compare_seq);
if (seq)
@ -1204,12 +1213,15 @@ gtk_compose_table_compact_check (const GtkComposeTableCompact *table,
if (i == n_compose - 1)
{
value = seq[row_stride - 1];
if ((value & (1 << 15)) != 0)
g_string_append (output, &table->char_data[value & ~(1 << 15)]);
else
g_string_append_unichar (output, value);
match = TRUE;
}
else
{
if (output_char)
*output_char = value;
if (match)
{
if (compose_match)
@ -1228,8 +1240,6 @@ gtk_compose_table_compact_check (const GtkComposeTableCompact *table,
*compose_match = TRUE;
if (compose_finish)
*compose_finish = TRUE;
if (output_char)
*output_char = value;
return TRUE;
}
@ -1237,6 +1247,65 @@ gtk_compose_table_compact_check (const GtkComposeTableCompact *table,
return FALSE;
}
void
gtk_compose_table_foreach (const GtkComposeTable *table,
GtkComposeSequenceCallback callback,
gpointer data)
{
int index_stride = table->max_seq_len + 1;
gunichar *sequence;
int seqno;
sequence = g_new0 (gunichar, table->max_seq_len + 1);
seqno = 0;
for (int idx = 0; idx < table->n_index_size; idx++)
{
const guint16 *seq_index = table->data + (idx * index_stride);
for (int i = 1; i < table->max_seq_len; i++)
{
int len = i + 1;
g_assert (seq_index[i] <= seq_index[i + 1]);
g_assert (seq_index[i + 1] <= table->data_size);
g_assert ((seq_index[i + 1] - seq_index[i]) % len == 0);
for (int j = seq_index[i]; j < seq_index[i + 1]; j += len)
{
char buf[8] = { 0, };
guint16 encoded_value;
char *value;
sequence[0] = seq_index[0];
for (int k = 0; k < len - 1; k++)
sequence[k + 1] = (gunichar) table->data[j + k];
sequence[len] = 0;
encoded_value = table->data[j + len - 1];
g_assert (encoded_value != 0);
if ((encoded_value & (1 << 15)) != 0)
{
int char_offset = encoded_value & ~(1 << 15);
g_assert (char_offset < table->n_chars);
value = &table->char_data[char_offset];
g_assert (strlen (value) < 20);
}
else
{
g_unichar_to_utf8 ((gunichar)encoded_value, buf);
value = buf;
}
callback (sequence, len, value, data);
seqno++;
}
}
}
g_free (sequence);
}
/* Checks if a keysym is a dead key.
* Dead key keysym values are defined in ../gdk/gdkkeysyms.h and the
* first is GDK_KEY_dead_grave. As X.Org is updated, more dead keys
@ -1428,4 +1497,3 @@ gtk_check_algorithmically (const guint16 *compose_buffer,
return FALSE;
}

View File

@ -26,50 +26,65 @@ G_BEGIN_DECLS
typedef struct _GtkComposeTable GtkComposeTable;
typedef struct _GtkComposeTableCompact GtkComposeTableCompact;
/* The layout of the data is as follows:
*
* The first part of the data contains rows of length max_seq_len + 1,
* where the first element is the item of the sequence, and the
* following elements are offsets to the data for sequences that
* start with the first item of length 2, ..., max_seq_len.
*
* The second part of the data contains the rest of the sequence
* data. It does not have a fixed stride. For each sequence, we
* put seq[2], ..., seq[len - 1], followed by the encoded value
* for this sequence.
*
* The values are encoded as follows:
*
* If the value is a single Unicode character smaler than 0x8000,
* then we place it directly. Otherwise, we put the UTF8-encoded
* value in the char_data array, and use offset | 0x8000 as the
* encoded value.
*/
struct _GtkComposeTable
{
guint16 *data;
char *char_data;
int max_seq_len;
int n_seqs;
int n_index_size;
int data_size;
int n_chars;
guint32 id;
};
struct _GtkComposeTableCompact
{
const guint16 *data;
int max_seq_len;
int n_index_size;
int n_index_stride;
};
GtkComposeTable * gtk_compose_table_new_with_file (const char *compose_file);
GtkComposeTable * gtk_compose_table_parse (const char *compose_file);
GtkComposeTable * gtk_compose_table_new_with_data (const guint16 *data,
int max_seq_len,
int n_seqs);
GtkComposeTable * gtk_compose_table_new_with_file (const char *compose_file);
GtkComposeTable * gtk_compose_table_new_with_data (const guint16 *data,
int max_seq_len,
int n_seqs);
typedef void (* GtkComposeSequenceCallback) (gunichar *sequence,
int len,
const char *value,
gpointer data);
gboolean gtk_compose_table_check (const GtkComposeTable *table,
const guint16 *compose_buffer,
int n_compose,
gboolean *compose_finish,
gboolean *compose_match,
GString *output);
void gtk_compose_table_foreach (const GtkComposeTable *table,
GtkComposeSequenceCallback callback,
gpointer data);
gboolean gtk_compose_table_compact_check (const GtkComposeTableCompact *table,
const guint16 *compose_buffer,
int n_compose,
gboolean *compose_finish,
gboolean *compose_match,
gunichar *output_char);
gboolean gtk_compose_table_check (const GtkComposeTable *table,
const guint16 *compose_buffer,
int n_compose,
gboolean *compose_finish,
gboolean *compose_match,
GString *output);
gboolean gtk_check_algorithmically (const guint16 *compose_buffer,
int n_compose,
gunichar *output);
gboolean gtk_check_algorithmically (const guint16 *compose_buffer,
int n_compose,
gunichar *output);
guint32 gtk_compose_table_data_hash (const guint16 *data,
int max_seq_len,
int n_seqs);
guint32 gtk_compose_table_data_hash (const guint16 *data,
int max_seq_len,
int n_seqs);
G_END_DECLS

View File

@ -74,18 +74,32 @@ struct _GtkIMContextSimplePrivate
guint modifiers_dropped : 1;
};
/* From the values below, the value 30 means the number of different first keysyms
* that exist in the Compose file (from Xorg). When running compose-parse.py without
* parameters, you get the count that you can put here. Needed when updating the
* gtkimcontextsimpleseqs.h header file (contains the compose sequences).
*/
const GtkComposeTableCompact gtk_compose_table_compact = {
gtk_compose_seqs_compact,
5,
30,
6
#include "gtk/compose/gtkcomposedata.h"
GtkComposeTable builtin_compose_table = {
NULL,
NULL,
MAX_SEQ_LEN,
N_INDEX_SIZE,
DATA_SIZE,
N_CHARS,
0
};
static void
init_builtin_table (void)
{
GBytes *bytes;
bytes = g_resources_lookup_data ("/org/gtk/libgtk/compose/sequences", 0, NULL);
builtin_compose_table.data = (guint16 *) g_bytes_get_data (bytes, NULL);
g_bytes_unref (bytes);
bytes = g_resources_lookup_data ("/org/gtk/libgtk/compose/chars", 0, NULL);
builtin_compose_table.char_data = (char *) g_bytes_get_data (bytes, NULL);
g_bytes_unref (bytes);
}
G_LOCK_DEFINE_STATIC (global_tables);
static GSList *global_tables;
static gboolean omit_builtin_sequences;
@ -147,6 +161,7 @@ gtk_im_context_simple_class_init (GtkIMContextSimpleClass *class)
im_context_class->get_preedit_string = gtk_im_context_simple_get_preedit_string;
gobject_class->finalize = gtk_im_context_simple_finalize;
init_builtin_table ();
init_compose_table_async (NULL, NULL, NULL);
}
@ -200,9 +215,9 @@ add_compose_table_from_file (const char *compose_file,
}
static void
add_compose_table_from_data (const guint16 *data,
int max_seq_len,
int n_seqs)
add_compose_table_from_data (guint16 *data,
int max_seq_len,
int n_seqs)
{
guint hash;
@ -337,7 +352,7 @@ gtk_im_context_simple_init (GtkIMContextSimple *context_simple)
priv = context_simple->priv = gtk_im_context_simple_get_instance_private (context_simple);
priv->compose_buffer_len = gtk_compose_table_compact.max_seq_len + 1;
priv->compose_buffer_len = builtin_compose_table.max_seq_len + 1;
priv->compose_buffer = g_new0 (guint16, priv->compose_buffer_len);
priv->tentative_match = g_string_new ("");
priv->tentative_match_len = 0;
@ -1069,18 +1084,19 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
G_UNLOCK (global_tables);
g_string_free (output, TRUE);
if (success)
return TRUE;
{
g_string_free (output, TRUE);
return TRUE;
}
G_LOCK (global_tables);
if (!omit_builtin_sequences &&
gtk_compose_table_compact_check (&gtk_compose_table_compact,
priv->compose_buffer, n_compose,
&compose_finish, &compose_match,
&output_char))
gtk_compose_table_check (&builtin_compose_table,
priv->compose_buffer, n_compose,
&compose_finish, &compose_match,
output))
{
if (!priv->in_compose_sequence)
{
@ -1091,24 +1107,28 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
if (compose_finish)
{
if (compose_match)
gtk_im_context_simple_commit_char (context_simple, output_char);
gtk_im_context_simple_commit_string (context_simple, output->str);
}
else
{
if (compose_match)
{
g_string_set_size (priv->tentative_match, 0);
g_string_append_unichar (priv->tentative_match, output_char);
g_string_assign (priv->tentative_match, output->str);
priv->tentative_match_len = n_compose;
}
g_signal_emit_by_name (context_simple, "preedit-changed");
}
return TRUE;
success = TRUE;
}
G_UNLOCK (global_tables);
g_string_free (output, TRUE);
if (success)
return TRUE;
if (gtk_check_algorithmically (priv->compose_buffer, n_compose, &output_char))
{
if (!priv->in_compose_sequence)
@ -1261,12 +1281,14 @@ gtk_im_context_simple_get_preedit_string (GtkIMContext *context,
* The table must be sorted in dictionary order on the
* numeric value of the key symbol fields. (Values beyond
* the length of the sequence should be zero.)
**/
*
* Deprecated: 4.4: Use gtk_im_context_simple_add_compose_file()
*/
void
gtk_im_context_simple_add_table (GtkIMContextSimple *context_simple,
guint16 *data,
int max_seq_len,
int n_seqs)
guint16 *data,
int max_seq_len,
int n_seqs)
{
g_return_if_fail (GTK_IS_IM_CONTEXT_SIMPLE (context_simple));

View File

@ -63,11 +63,11 @@ GType gtk_im_context_simple_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkIMContext *gtk_im_context_simple_new (void);
GDK_AVAILABLE_IN_ALL
GDK_DEPRECATED_IN_4_4_FOR(gtk_im_context_simple_add_compose_file)
void gtk_im_context_simple_add_table (GtkIMContextSimple *context_simple,
guint16 *data,
int max_seq_len,
int n_seqs);
guint16 *data,
int max_seq_len,
int n_seqs);
GDK_AVAILABLE_IN_ALL
void gtk_im_context_simple_add_compose_file (GtkIMContextSimple *context_simple,
const char *compose_file);

View File

@ -1276,3 +1276,5 @@ libgtk_static_dep = declare_dependency(sources: gtk_dep_sources,
link_with: [libgtk_static, libgtk_css, libgdk, libgsk ],
link_args: common_ldflags,
)
subdir('compose')

View File

@ -1,3 +1,5 @@
# n_seqs: 1
# max_seq_len: 4
# n_index_size: 1
# data_size: 9
# n_chars: 0
<Uff20> <U73> <U65> <U71> : "!" # U21

View File

@ -1,3 +1,5 @@
# n_seqs: 1
# max_seq_len: 4
# n_index_size: 1
# data_size: 9
# n_chars: 0
<Uff20> <U73> <U6f> <U7a> : "!" # U21

View File

@ -1,5 +1,7 @@
# n_seqs: 3
# max_seq_len: 2
# n_index_size: 1
# data_size: 9
# n_chars: 0
<Uff20> <U61> : "a" # U61
<Uff20> <U62> : "#" # U23
<Uff20> <U63> : "a" # U61

View File

@ -1,3 +1,3 @@
include "testsuite/gtk/compose/cycle" # create an include cycle
include "cycle" # create an include cycle
<Multi_key> <s> <e> <q> : "!"

View File

@ -1,3 +1,5 @@
# n_seqs: 1
# max_seq_len: 4
# n_index_size: 1
# data_size: 9
# n_chars: 7
<Uff20> <U73> <U65> <U71> : "⏾⏳"

View File

@ -1,4 +1,4 @@
include "testsuite/gtk/compose/included" # see if this works
include "included" # see if this works
<Multi_key> <s> <s> <s> : "!" # replace this entry
<Multi_key> <a> <a> <a> : "" # remove this entry

View File

@ -1,3 +1,5 @@
# n_seqs: 1
# max_seq_len: 4
# n_index_size: 1
# data_size: 9
# n_chars: 0
<Uff20> <U73> <U73> <U73> : "!" # U21

View File

@ -1,3 +1,5 @@
# n_seqs: 1
# max_seq_len: 11
# n_index_size: 1
# data_size: 23
# n_chars: 5
<Uff20> <U65> <U6d> <U6d> <U65> <U6e> <U74> <U61> <U6c> <U65> <U72> : "🧀" # U1f9c0

View File

@ -1,5 +1,7 @@
# n_seqs: 3
# max_seq_len: 7
<Uff20> <U73> <U65> <U71> <U0> <U0> <U0> : "!" # U21
<Uff20> <U73> <U65> <U71> <U75> <U0> <U0> : "?" # U3f
<Uff20> <U7a> <U77> <U69> <U6e> <U65> <U73> : "🥂" # U1f942
# n_index_size: 1
# data_size: 24
# n_chars: 4
<Uff20> <U73> <U65> <U71> : "!" # U21
<Uff20> <U73> <U65> <U71> <U75> : "?" # U3f
<Uff20> <U7a> <U77> <U69> <U6e> <U65> <U73> : "🥂"

View File

@ -1,5 +1,7 @@
# n_seqs: 3
# max_seq_len: 5
<Uff20> <U73> <U61> <U73> <U0> : "_" # U5f
<Uff20> <U73> <U65> <U71> <U0> : "!" # U21
# n_index_size: 1
# data_size: 19
# n_chars: 0
<Uff20> <U73> <U61> <U73> : "_" # U5f
<Uff20> <U73> <U65> <U71> : "!" # U21
<Uff20> <U75> <U62> <U32> <U33> : "/" # U2f

View File

@ -1,3 +1,5 @@
# n_seqs: 1
# max_seq_len: 4
# n_index_size: 1
# data_size: 9
# n_chars: 0
<Uff20> <U73> <U65> <U71> : "!" # U21

View File

@ -1,6 +1,8 @@
# n_seqs: 4
# max_seq_len: 5
<Uff20> <U73> <U61> <U73> <U0> : "\"\\"
<Uff20> <U73> <U65> <U71> <U0> : "!a"
# n_index_size: 1
# data_size: 24
# n_chars: 9
<Uff20> <U73> <U61> <U73> : "\"\\"
<Uff20> <U73> <U65> <U71> : "!a"
<Uff20> <U73> <U65> <U71> <U75> : "?" # U3f
<Uff20> <U75> <U62> <U32> <U33> : "QR"

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,8 @@
#include <gtk/gtk.h>
#include <locale.h>
#include <glib/gstdio.h>
#include "../gtk/gtkcomposetable.h"
#include "../gtk/gtkimcontextsimpleseqs.h"
#include "testsuite/testutils.h"
@ -33,43 +35,59 @@ append_escaped (GString *str,
}
}
static void
print_sequence (gunichar *sequence,
int len,
const char *value,
gpointer data)
{
GString *str = data;
for (int j = 0; j < len; j++)
g_string_append_printf (str, "<U%x> ", sequence[j]);
g_string_append (str, ": \"");
append_escaped (str, value);
g_string_append (str, "\"");
if (g_utf8_strlen (value, -1) == 1)
{
gunichar ch = g_utf8_get_char (value);
g_string_append_printf (str, " # U%x", ch);
}
g_string_append_c (str, '\n');
}
static char *
gtk_compose_table_print (GtkComposeTable *table)
{
int i, j;
guint16 *seq;
GString *str;
str = g_string_new ("");
g_string_append_printf (str, "# n_seqs: %d\n# max_seq_len: %d\n",
table->n_seqs,
table->max_seq_len);
g_string_append_printf (str, "# max_seq_len: %d\n# n_index_size: %d\n# data_size: %d\n# n_chars: %d\n",
table->max_seq_len,
table->n_index_size,
table->data_size,
table->n_chars);
for (i = 0, seq = table->data; i < table->n_seqs; i++, seq += table->max_seq_len + 2)
#if 0
int index_stride = table->max_seq_len + 1;
for (int idx = 0; idx < table->n_index_size; idx++)
{
gunichar value;
char buf[8] = { 0 };
const guint16 *seq_index = table->data + (idx * index_stride);
for (j = 0; j < table->max_seq_len; j++)
g_string_append_printf (str, "<U%x> ", seq[j]);
value = (seq[table->max_seq_len] << 16) | seq[table->max_seq_len + 1];
if ((value & (1 << 31)) != 0)
{
const char *out = &table->char_data[value & ~(1 << 31)];
g_string_append (str, ": \"");
append_escaped (str, out);
g_string_append (str, "\"\n");
}
else
{
g_unichar_to_utf8 (value, buf);
g_string_append_printf (str, ": \"%s\" # U%x\n", buf, value);
}
g_print ("<U%x>", seq_index[0]);
for (int i = 1; i < index_stride; i++)
g_print (" %d", seq_index[i]);
g_print ("\n");
}
#endif
gtk_compose_table_foreach (table, print_sequence, str);
return g_string_free (str, FALSE);
}
@ -80,7 +98,7 @@ generate_output (const char *file)
GtkComposeTable *table;
char *output;
table = gtk_compose_table_new_with_file (file);
table = gtk_compose_table_parse (file);
output = gtk_compose_table_print (table);
g_print ("%s", output);
@ -97,12 +115,12 @@ compose_table_compare (gconstpointer data)
char *diff;
GError *error = NULL;
file = g_build_filename (g_test_get_dir (G_TEST_DIST), "compose", basename, NULL);
file = g_test_build_filename (G_TEST_DIST, "compose", basename, NULL);
expected = g_strconcat (file, ".expected", NULL);
table = gtk_compose_table_new_with_file (file);
table = gtk_compose_table_parse (file);
output = gtk_compose_table_print (table);
diff = diff_with_file (expected, output, -1, &error);
g_assert_no_error (error);
@ -125,9 +143,9 @@ compose_table_cycle (void)
char *file;
GtkComposeTable *table;
file = g_build_filename (g_test_get_dir (G_TEST_DIST), "compose", "cycle", NULL);
file = g_test_build_filename (G_TEST_DIST, "compose", "cycle", NULL);
table = gtk_compose_table_new_with_file (file);
table = gtk_compose_table_parse (file);
g_assert_nonnull (table);
g_free (file);
@ -149,7 +167,7 @@ compose_table_nofile (void)
file = g_build_filename (g_test_get_dir (G_TEST_DIST), "compose", "nofile", NULL);
table = gtk_compose_table_new_with_file (file);
table = gtk_compose_table_parse (file);
g_assert_nonnull (table);
g_free (file);
@ -175,7 +193,7 @@ compose_table_match (void)
file = g_build_filename (g_test_get_dir (G_TEST_DIST), "compose", "match", NULL);
table = gtk_compose_table_new_with_file (file);
table = gtk_compose_table_parse (file);
buffer[0] = GDK_KEY_Multi_key;
buffer[1] = 0;
@ -229,39 +247,38 @@ compose_table_match (void)
g_free (file);
}
extern const GtkComposeTable builtin_compose_table;
/* just check some random sequences */
static void
compose_table_match_compact (void)
compose_table_match_builtin (void)
{
const GtkComposeTableCompact table = {
gtk_compose_seqs_compact,
5,
30,
6
};
const GtkComposeTable *table = &builtin_compose_table;
guint16 buffer[8] = { 0, };
gboolean finish, match, ret;
gunichar ch;
GString *s;
buffer[0] = GDK_KEY_Multi_key;
buffer[1] = 0;
ret = gtk_compose_table_compact_check (&table, buffer, 1, &finish, &match, &ch);
s = g_string_new ("");
ret = gtk_compose_table_check (table, buffer, 1, &finish, &match, s);
g_assert_true (ret);
g_assert_false (finish);
g_assert_false (match);
g_assert_true (ch == 0);
g_assert_true (s->len == 0);
buffer[0] = GDK_KEY_a;
buffer[1] = GDK_KEY_b;
buffer[2] = GDK_KEY_c;
buffer[3] = 0;
ret = gtk_compose_table_compact_check (&table, buffer, 3, &finish, &match, &ch);
ret = gtk_compose_table_check (table, buffer, 3, &finish, &match, s);
g_assert_false (ret);
g_assert_false (finish);
g_assert_false (match);
g_assert_true (ch == 0);
g_assert_true (s->len == 0);
buffer[0] = GDK_KEY_Multi_key;
buffer[1] = GDK_KEY_parenleft;
@ -269,31 +286,37 @@ compose_table_match_compact (void)
buffer[3] = GDK_KEY_parenright;
buffer[4] = 0;
ret = gtk_compose_table_compact_check (&table, buffer, 4, &finish, &match, &ch);
ret = gtk_compose_table_check (table, buffer, 4, &finish, &match, s);
g_assert_true (ret);
g_assert_true (finish);
g_assert_true (match);
g_assert_true (ch == 0x24d9); /* CIRCLED LATIN SMALL LETTER J */
g_assert_cmpstr (s->str, ==, ""); /* CIRCLED LATIN SMALL LETTER J */
buffer[0] = GDK_KEY_dead_acute;
buffer[1] = GDK_KEY_space;
buffer[2] = 0;
ret = gtk_compose_table_compact_check (&table, buffer, 2, &finish, &match, &ch);
g_string_set_size (s, 0);
ret = gtk_compose_table_check (table, buffer, 2, &finish, &match, s);
g_assert_true (ret);
g_assert_true (finish);
g_assert_true (match);
g_assert_true (ch == 0x27);
g_assert_cmpstr (s->str, ==, "'");
buffer[0] = GDK_KEY_dead_acute;
buffer[1] = GDK_KEY_dead_acute;
buffer[2] = 0;
ret = gtk_compose_table_compact_check (&table, buffer, 2, &finish, &match, &ch);
g_string_set_size (s, 0);
ret = gtk_compose_table_check (table, buffer, 2, &finish, &match, s);
g_assert_true (ret);
g_assert_true (finish);
g_assert_true (match);
g_assert_true (ch == 0xb4);
g_assert_cmpstr (s->str, ==, "´");
g_string_free (s, TRUE);
}
static void
@ -381,16 +404,16 @@ match_algorithmic (void)
int
main (int argc, char *argv[])
{
char *dir;
g_setenv ("LC_ALL", "en_US.UTF-8", TRUE);
dir = g_dir_make_tmp ("composetableXXXXXX", NULL);
g_setenv ("XDG_CACHE_HOME", dir, TRUE);
g_free (dir);
if (argc == 3 && strcmp (argv[1], "--generate") == 0)
{
setlocale (LC_ALL, "");
gtk_disable_setlocale();
setlocale (LC_ALL, "en_US.UTF-8");
gtk_init ();
/* Ensure that the builtin table is initialized */
GtkIMContext *ctx = gtk_im_context_simple_new ();
g_object_unref (ctx);
generate_output (argv[2]);
return 0;
@ -398,6 +421,10 @@ main (int argc, char *argv[])
gtk_test_init (&argc, &argv, NULL);
/* Ensure that the builtin table is initialized */
GtkIMContext *ctx = gtk_im_context_simple_new ();
g_object_unref (ctx);
g_test_add_data_func ("/compose-table/basic", "basic", compose_table_compare);
g_test_add_data_func ("/compose-table/long", "long", compose_table_compare);
g_test_add_data_func ("/compose-table/octal", "octal", compose_table_compare);
@ -410,7 +437,7 @@ main (int argc, char *argv[])
g_test_add_func ("/compose-table/include-cycle", compose_table_cycle);
g_test_add_func ("/compose-table/include-nofile", compose_table_nofile);
g_test_add_func ("/compose-table/match", compose_table_match);
g_test_add_func ("/compose-table/match-compact", compose_table_match_compact);
g_test_add_func ("/compose-table/match-builtin", compose_table_match_builtin);
g_test_add_func ("/compose-table/match-algorithmic", match_algorithmic);
return g_test_run ();