summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2010-01-06 16:32:59 +0000
committerJohn Mark Bell <jmb@netsurf-browser.org>2010-01-06 16:32:59 +0000
commitf3a77d3c00c095a53f37aa7efb39d56168799596 (patch)
tree0bd2269afe0edd5018c5d231c95a5011002c83cf /src
parent628079a91ca6d86a7915906d266e6fe5593bb846 (diff)
downloadlibrufl-f3a77d3c00c095a53f37aa7efb39d56168799596.tar.gz
librufl-f3a77d3c00c095a53f37aa7efb39d56168799596.tar.bz2
Port to core buildsystem.
The python module (and associated make runes) need some love (as does non-GCC building with the core buildsystem in general) svn path=/trunk/rufl/; revision=9792
Diffstat (limited to 'src')
-rw-r--r--src/Glyphs1406
-rw-r--r--src/Makefile12
-rw-r--r--src/rufl_character_set_test.c37
-rw-r--r--src/rufl_decompose.c161
-rw-r--r--src/rufl_dump_state.c138
-rw-r--r--src/rufl_find.c201
-rw-r--r--src/rufl_init.c1678
-rw-r--r--src/rufl_internal.h194
-rw-r--r--src/rufl_invalidate_cache.c28
-rw-r--r--src/rufl_metrics.c312
-rw-r--r--src/rufl_paint.c621
-rw-r--r--src/rufl_quit.c49
-rw-r--r--src/strfuncs.c37
-rw-r--r--src/strfuncs.h5
14 files changed, 4879 insertions, 0 deletions
diff --git a/src/Glyphs b/src/Glyphs
new file mode 100644
index 0000000..d39fa0a
--- /dev/null
+++ b/src/Glyphs
@@ -0,0 +1,1406 @@
+#
+# Name: Adobe Glyph List
+# Table version: 1.2
+# Date: 22 Oct 1998
+#
+# Description:
+#
+# The Adobe Glyph List (AGL) list relates Unicode values (UVs) to glyph
+# names, and should be used only as described in the document "Unicode and
+# Glyph Names," at
+# http://www.adobe.com/supportservice/devrelations/typeforum/unicodegn.html .
+#
+# The glyph name to UV relation is one to many. 12 glyph names are mapped to
+# two UVs each; each UV has a separate entry. All other glyph names are
+# mapped to one UV each.
+#
+# The Unicode Standard version 2.1 is used for all UVs outside of the Private
+# Use area, except for 4 entries (see Revision History for 1.2 below).
+#
+# There are 1051 entries in this list, 171 of which are in the Corporate Use
+# subarea (CUS). Refer to the document "Unicode Corporate Use Subarea as used
+# by Adobe Systems," at
+# http://www.adobe.com/supportservice/devrelations/typeforum/corporateuse.txt
+# for compatibility decompositions for these characters, and to the document
+# "Unicode and Glyph Names" for more information the CUS.
+#
+# Format: Semicolon-delimited fields:
+#
+# (1) Standard UV or CUS UV. (4 uppercase hexadecimal digits)
+#
+# (2) Glyph name. (upper- and lowercase letters, digits)
+#
+# (3) Character names: Unicode character names for standard UVs, and
+# descriptive names for CUS UVs. (uppercase letters, hyphen, space)
+#
+# (4) [optional] Comment. A comment of "Duplicate" indicates one of two
+# UVs of a double-mapping. It is the UV that may be given a uni<CODE>
+# override, or the UV that is in the CUS, as described in the document
+# "Unicode and Glyph Names."
+#
+# The entries are sorted by glyph name in increasing ASCII order; entries
+# with the same glyph name are sorted in decreasing priority order.
+#
+# Lines starting with "#" are comments; blank lines should be ignored.
+#
+# Revision History:
+#
+# 1.2 [22 Oct 1998]
+#
+# Some Central European glyph names were remapped and the glyph "dotlessj"
+# was added. Some entries in the table below have not changed but are
+# included to provide a complete context for other glyphs that have been
+# remapped or double-mapped. "-" means that the entry for that UV does not
+# exist in the AGL.
+#
+# -------- ---------------------- ---------------- --------------
+# UV Character name AGL 1.1 AGL 1.2
+# (shortened) glyph name glyph name
+# -------- ---------------------- ---------------- --------------
+# 015E/F S/s with cedilla S/scommaaccent S/scedilla
+# 0162/3 T/t with cedilla T/tcommaaccent T/tcommaaccent
+# 0218/9 S/s with comma below - S/scommaaccent
+# 021A/B T/t with comma below - T/tcommaaccent
+# 1E9E/F S/s with comma below S/scedilla -
+# F6C1/2 S/s with cedilla S/scedilla S/scedilla
+# F6BE dotless j - dotlessj
+# -------- ---------------------- ---------------- --------------
+#
+# The characters at U+1E9E/F in AGL 1.1, LATIN CAPITAL/SMALL LETTER S WITH
+# COMMA BELOW, which are proposed new characters (see (b) in the notes for
+# AGL 1.1 below), have since been reassigned by the Unicode Standard to new
+# proposed values of U+0218/9. These characters, as well as U+021A/B, LATIN
+# CAPITAL/SMALL LETTER T WITH COMMA BELOW, are not in the Unicode Standard
+# 2.1.
+#
+# Entries with the same glyph name are now sorted in decreasing priority
+# order instead of in increasing UV order.
+#
+# 1.1 [24 Nov 1997]
+#
+# a. The "Euro" glyph's UV assignment is changed from U+20A0 (EURO-CURRENCY
+# SIGN) to U+20AC (EURO SIGN). While U+20AC is not defined in the
+# Unicode Standard 2.0, it has been accepted by the Unicode Technical
+# Committee for the next version of the Standard; it has not yet passed
+# the ISO approval process as of 7 November '97.
+#
+# b. Glyphs "Scedilla" and "scedilla", which were assigned in the Corporate
+# Use Subarea in AGL 1.0, are now additionally mapped to U+1E9E and
+# U+1E9F respectively. These two UVs share the same Unicode approval
+# status as the Euro glyph (see a. above).
+#
+# c. The "fraction" glyph is now additionally mapped to U+2215, to match
+# Windows Glyph List 4.
+#
+# d. The descriptive name for glyph "onefitted", in the Corporate Use
+# subarea, is changed from "TABULAR DIGIT ONE" to "PROPORTIONAL DIGIT
+# ONE".
+#
+# 1.0 [17 Jul 1997] Original version
+#
+0041;A;LATIN CAPITAL LETTER A
+00C6;AE;LATIN CAPITAL LETTER AE
+01FC;AEacute;LATIN CAPITAL LETTER AE WITH ACUTE
+F7E6;AEsmall;LATIN SMALL CAPITAL LETTER AE
+00C1;Aacute;LATIN CAPITAL LETTER A WITH ACUTE
+F7E1;Aacutesmall;LATIN SMALL CAPITAL LETTER A WITH ACUTE
+0102;Abreve;LATIN CAPITAL LETTER A WITH BREVE
+00C2;Acircumflex;LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+F7E2;Acircumflexsmall;LATIN SMALL CAPITAL LETTER A WITH CIRCUMFLEX
+F6C9;Acute;CAPITAL ACUTE ACCENT
+F7B4;Acutesmall;SMALL CAPITAL ACUTE ACCENT
+00C4;Adieresis;LATIN CAPITAL LETTER A WITH DIAERESIS
+F7E4;Adieresissmall;LATIN SMALL CAPITAL LETTER A WITH DIAERESIS
+00C0;Agrave;LATIN CAPITAL LETTER A WITH GRAVE
+F7E0;Agravesmall;LATIN SMALL CAPITAL LETTER A WITH GRAVE
+0391;Alpha;GREEK CAPITAL LETTER ALPHA
+0386;Alphatonos;GREEK CAPITAL LETTER ALPHA WITH TONOS
+0100;Amacron;LATIN CAPITAL LETTER A WITH MACRON
+0104;Aogonek;LATIN CAPITAL LETTER A WITH OGONEK
+00C5;Aring;LATIN CAPITAL LETTER A WITH RING ABOVE
+01FA;Aringacute;LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
+F7E5;Aringsmall;LATIN SMALL CAPITAL LETTER A WITH RING ABOVE
+F761;Asmall;LATIN SMALL CAPITAL LETTER A
+00C3;Atilde;LATIN CAPITAL LETTER A WITH TILDE
+F7E3;Atildesmall;LATIN SMALL CAPITAL LETTER A WITH TILDE
+0042;B;LATIN CAPITAL LETTER B
+0392;Beta;GREEK CAPITAL LETTER BETA
+F6F4;Brevesmall;SMALL CAPITAL BREVE
+F762;Bsmall;LATIN SMALL CAPITAL LETTER B
+0043;C;LATIN CAPITAL LETTER C
+0106;Cacute;LATIN CAPITAL LETTER C WITH ACUTE
+F6CA;Caron;CAPITAL CARON
+F6F5;Caronsmall;SMALL CAPITAL CARON
+010C;Ccaron;LATIN CAPITAL LETTER C WITH CARON
+00C7;Ccedilla;LATIN CAPITAL LETTER C WITH CEDILLA
+F7E7;Ccedillasmall;LATIN SMALL CAPITAL LETTER C WITH CEDILLA
+0108;Ccircumflex;LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+010A;Cdotaccent;LATIN CAPITAL LETTER C WITH DOT ABOVE
+F7B8;Cedillasmall;SMALL CAPITAL CEDILLA
+03A7;Chi;GREEK CAPITAL LETTER CHI
+F6F6;Circumflexsmall;SMALL CAPITAL MODIFIER LETTER CIRCUMFLEX ACCENT
+F763;Csmall;LATIN SMALL CAPITAL LETTER C
+0044;D;LATIN CAPITAL LETTER D
+010E;Dcaron;LATIN CAPITAL LETTER D WITH CARON
+0110;Dcroat;LATIN CAPITAL LETTER D WITH STROKE
+2206;Delta;INCREMENT
+0394;Delta;GREEK CAPITAL LETTER DELTA;Duplicate
+F6CB;Dieresis;CAPITAL DIAERESIS
+F6CC;DieresisAcute;CAPITAL DIAERESIS ACUTE ACCENT
+F6CD;DieresisGrave;CAPITAL DIAERESIS GRAVE ACCENT
+F7A8;Dieresissmall;SMALL CAPITAL DIAERESIS
+F6F7;Dotaccentsmall;SMALL CAPITAL DOT ABOVE
+F764;Dsmall;LATIN SMALL CAPITAL LETTER D
+0045;E;LATIN CAPITAL LETTER E
+00C9;Eacute;LATIN CAPITAL LETTER E WITH ACUTE
+F7E9;Eacutesmall;LATIN SMALL CAPITAL LETTER E WITH ACUTE
+0114;Ebreve;LATIN CAPITAL LETTER E WITH BREVE
+011A;Ecaron;LATIN CAPITAL LETTER E WITH CARON
+00CA;Ecircumflex;LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+F7EA;Ecircumflexsmall;LATIN SMALL CAPITAL LETTER E WITH CIRCUMFLEX
+00CB;Edieresis;LATIN CAPITAL LETTER E WITH DIAERESIS
+F7EB;Edieresissmall;LATIN SMALL CAPITAL LETTER E WITH DIAERESIS
+0116;Edotaccent;LATIN CAPITAL LETTER E WITH DOT ABOVE
+00C8;Egrave;LATIN CAPITAL LETTER E WITH GRAVE
+F7E8;Egravesmall;LATIN SMALL CAPITAL LETTER E WITH GRAVE
+0112;Emacron;LATIN CAPITAL LETTER E WITH MACRON
+014A;Eng;LATIN CAPITAL LETTER ENG
+0118;Eogonek;LATIN CAPITAL LETTER E WITH OGONEK
+0395;Epsilon;GREEK CAPITAL LETTER EPSILON
+0388;Epsilontonos;GREEK CAPITAL LETTER EPSILON WITH TONOS
+F765;Esmall;LATIN SMALL CAPITAL LETTER E
+0397;Eta;GREEK CAPITAL LETTER ETA
+0389;Etatonos;GREEK CAPITAL LETTER ETA WITH TONOS
+00D0;Eth;LATIN CAPITAL LETTER ETH
+F7F0;Ethsmall;LATIN SMALL CAPITAL LETTER ETH
+20AC;Euro;EURO SIGN
+0046;F;LATIN CAPITAL LETTER F
+F766;Fsmall;LATIN SMALL CAPITAL LETTER F
+0047;G;LATIN CAPITAL LETTER G
+0393;Gamma;GREEK CAPITAL LETTER GAMMA
+011E;Gbreve;LATIN CAPITAL LETTER G WITH BREVE
+01E6;Gcaron;LATIN CAPITAL LETTER G WITH CARON
+011C;Gcircumflex;LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+0122;Gcommaaccent;LATIN CAPITAL LETTER G WITH CEDILLA
+0120;Gdotaccent;LATIN CAPITAL LETTER G WITH DOT ABOVE
+F6CE;Grave;CAPITAL GRAVE ACCENT
+F760;Gravesmall;SMALL CAPITAL GRAVE ACCENT
+F767;Gsmall;LATIN SMALL CAPITAL LETTER G
+0048;H;LATIN CAPITAL LETTER H
+25CF;H18533;BLACK CIRCLE
+25AA;H18543;BLACK SMALL SQUARE
+25AB;H18551;WHITE SMALL SQUARE
+25A1;H22073;WHITE SQUARE
+0126;Hbar;LATIN CAPITAL LETTER H WITH STROKE
+0124;Hcircumflex;LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+F768;Hsmall;LATIN SMALL CAPITAL LETTER H
+F6CF;Hungarumlaut;CAPITAL DOUBLE ACUTE ACCENT
+F6F8;Hungarumlautsmall;SMALL CAPITAL DOUBLE ACUTE ACCENT
+0049;I;LATIN CAPITAL LETTER I
+0132;IJ;LATIN CAPITAL LIGATURE IJ
+00CD;Iacute;LATIN CAPITAL LETTER I WITH ACUTE
+F7ED;Iacutesmall;LATIN SMALL CAPITAL LETTER I WITH ACUTE
+012C;Ibreve;LATIN CAPITAL LETTER I WITH BREVE
+00CE;Icircumflex;LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+F7EE;Icircumflexsmall;LATIN SMALL CAPITAL LETTER I WITH CIRCUMFLEX
+00CF;Idieresis;LATIN CAPITAL LETTER I WITH DIAERESIS
+F7EF;Idieresissmall;LATIN SMALL CAPITAL LETTER I WITH DIAERESIS
+0130;Idotaccent;LATIN CAPITAL LETTER I WITH DOT ABOVE
+2111;Ifraktur;BLACK-LETTER CAPITAL I
+00CC;Igrave;LATIN CAPITAL LETTER I WITH GRAVE
+F7EC;Igravesmall;LATIN SMALL CAPITAL LETTER I WITH GRAVE
+012A;Imacron;LATIN CAPITAL LETTER I WITH MACRON
+012E;Iogonek;LATIN CAPITAL LETTER I WITH OGONEK
+0399;Iota;GREEK CAPITAL LETTER IOTA
+03AA;Iotadieresis;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+038A;Iotatonos;GREEK CAPITAL LETTER IOTA WITH TONOS
+F769;Ismall;LATIN SMALL CAPITAL LETTER I
+0128;Itilde;LATIN CAPITAL LETTER I WITH TILDE
+004A;J;LATIN CAPITAL LETTER J
+0134;Jcircumflex;LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+F76A;Jsmall;LATIN SMALL CAPITAL LETTER J
+004B;K;LATIN CAPITAL LETTER K
+039A;Kappa;GREEK CAPITAL LETTER KAPPA
+0136;Kcommaaccent;LATIN CAPITAL LETTER K WITH CEDILLA
+F76B;Ksmall;LATIN SMALL CAPITAL LETTER K
+004C;L;LATIN CAPITAL LETTER L
+F6BF;LL;LATIN CAPITAL LETTER LL
+0139;Lacute;LATIN CAPITAL LETTER L WITH ACUTE
+039B;Lambda;GREEK CAPITAL LETTER LAMDA
+013D;Lcaron;LATIN CAPITAL LETTER L WITH CARON
+013B;Lcommaaccent;LATIN CAPITAL LETTER L WITH CEDILLA
+013F;Ldot;LATIN CAPITAL LETTER L WITH MIDDLE DOT
+0141;Lslash;LATIN CAPITAL LETTER L WITH STROKE
+F6F9;Lslashsmall;LATIN SMALL CAPITAL LETTER L WITH STROKE
+F76C;Lsmall;LATIN SMALL CAPITAL LETTER L
+004D;M;LATIN CAPITAL LETTER M
+F6D0;Macron;CAPITAL MACRON
+F7AF;Macronsmall;SMALL CAPITAL MACRON
+F76D;Msmall;LATIN SMALL CAPITAL LETTER M
+039C;Mu;GREEK CAPITAL LETTER MU
+004E;N;LATIN CAPITAL LETTER N
+0143;Nacute;LATIN CAPITAL LETTER N WITH ACUTE
+0147;Ncaron;LATIN CAPITAL LETTER N WITH CARON
+0145;Ncommaaccent;LATIN CAPITAL LETTER N WITH CEDILLA
+F76E;Nsmall;LATIN SMALL CAPITAL LETTER N
+00D1;Ntilde;LATIN CAPITAL LETTER N WITH TILDE
+F7F1;Ntildesmall;LATIN SMALL CAPITAL LETTER N WITH TILDE
+039D;Nu;GREEK CAPITAL LETTER NU
+004F;O;LATIN CAPITAL LETTER O
+0152;OE;LATIN CAPITAL LIGATURE OE
+F6FA;OEsmall;LATIN SMALL CAPITAL LIGATURE OE
+00D3;Oacute;LATIN CAPITAL LETTER O WITH ACUTE
+F7F3;Oacutesmall;LATIN SMALL CAPITAL LETTER O WITH ACUTE
+014E;Obreve;LATIN CAPITAL LETTER O WITH BREVE
+00D4;Ocircumflex;LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+F7F4;Ocircumflexsmall;LATIN SMALL CAPITAL LETTER O WITH CIRCUMFLEX
+00D6;Odieresis;LATIN CAPITAL LETTER O WITH DIAERESIS
+F7F6;Odieresissmall;LATIN SMALL CAPITAL LETTER O WITH DIAERESIS
+F6FB;Ogoneksmall;SMALL CAPITAL OGONEK
+00D2;Ograve;LATIN CAPITAL LETTER O WITH GRAVE
+F7F2;Ogravesmall;LATIN SMALL CAPITAL LETTER O WITH GRAVE
+01A0;Ohorn;LATIN CAPITAL LETTER O WITH HORN
+0150;Ohungarumlaut;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+014C;Omacron;LATIN CAPITAL LETTER O WITH MACRON
+2126;Omega;OHM SIGN
+03A9;Omega;GREEK CAPITAL LETTER OMEGA;Duplicate
+038F;Omegatonos;GREEK CAPITAL LETTER OMEGA WITH TONOS
+039F;Omicron;GREEK CAPITAL LETTER OMICRON
+038C;Omicrontonos;GREEK CAPITAL LETTER OMICRON WITH TONOS
+00D8;Oslash;LATIN CAPITAL LETTER O WITH STROKE
+01FE;Oslashacute;LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
+F7F8;Oslashsmall;LATIN SMALL CAPITAL LETTER O WITH STROKE
+F76F;Osmall;LATIN SMALL CAPITAL LETTER O
+00D5;Otilde;LATIN CAPITAL LETTER O WITH TILDE
+F7F5;Otildesmall;LATIN SMALL CAPITAL LETTER O WITH TILDE
+0050;P;LATIN CAPITAL LETTER P
+03A6;Phi;GREEK CAPITAL LETTER PHI
+03A0;Pi;GREEK CAPITAL LETTER PI
+03A8;Psi;GREEK CAPITAL LETTER PSI
+F770;Psmall;LATIN SMALL CAPITAL LETTER P
+0051;Q;LATIN CAPITAL LETTER Q
+F771;Qsmall;LATIN SMALL CAPITAL LETTER Q
+0052;R;LATIN CAPITAL LETTER R
+0154;Racute;LATIN CAPITAL LETTER R WITH ACUTE
+0158;Rcaron;LATIN CAPITAL LETTER R WITH CARON
+0156;Rcommaaccent;LATIN CAPITAL LETTER R WITH CEDILLA
+211C;Rfraktur;BLACK-LETTER CAPITAL R
+03A1;Rho;GREEK CAPITAL LETTER RHO
+F6FC;Ringsmall;SMALL CAPITAL RING ABOVE
+F772;Rsmall;LATIN SMALL CAPITAL LETTER R
+0053;S;LATIN CAPITAL LETTER S
+250C;SF010000;BOX DRAWINGS LIGHT DOWN AND RIGHT
+2514;SF020000;BOX DRAWINGS LIGHT UP AND RIGHT
+2510;SF030000;BOX DRAWINGS LIGHT DOWN AND LEFT
+2518;SF040000;BOX DRAWINGS LIGHT UP AND LEFT
+253C;SF050000;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+252C;SF060000;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+2534;SF070000;BOX DRAWINGS LIGHT UP AND HORIZONTAL
+251C;SF080000;BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+2524;SF090000;BOX DRAWINGS LIGHT VERTICAL AND LEFT
+2500;SF100000;BOX DRAWINGS LIGHT HORIZONTAL
+2502;SF110000;BOX DRAWINGS LIGHT VERTICAL
+2561;SF190000;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+2562;SF200000;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+2556;SF210000;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+2555;SF220000;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+2563;SF230000;BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+2551;SF240000;BOX DRAWINGS DOUBLE VERTICAL
+2557;SF250000;BOX DRAWINGS DOUBLE DOWN AND LEFT
+255D;SF260000;BOX DRAWINGS DOUBLE UP AND LEFT
+255C;SF270000;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+255B;SF280000;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+255E;SF360000;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+255F;SF370000;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+255A;SF380000;BOX DRAWINGS DOUBLE UP AND RIGHT
+2554;SF390000;BOX DRAWINGS DOUBLE DOWN AND RIGHT
+2569;SF400000;BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+2566;SF410000;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+2560;SF420000;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+2550;SF430000;BOX DRAWINGS DOUBLE HORIZONTAL
+256C;SF440000;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+2567;SF450000;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+2568;SF460000;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+2564;SF470000;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+2565;SF480000;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+2559;SF490000;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+2558;SF500000;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+2552;SF510000;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+2553;SF520000;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+256B;SF530000;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+256A;SF540000;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+015A;Sacute;LATIN CAPITAL LETTER S WITH ACUTE
+0160;Scaron;LATIN CAPITAL LETTER S WITH CARON
+F6FD;Scaronsmall;LATIN SMALL CAPITAL LETTER S WITH CARON
+015E;Scedilla;LATIN CAPITAL LETTER S WITH CEDILLA
+F6C1;Scedilla;LATIN CAPITAL LETTER S WITH CEDILLA;Duplicate
+015C;Scircumflex;LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+0218;Scommaaccent;LATIN CAPITAL LETTER S WITH COMMA BELOW
+03A3;Sigma;GREEK CAPITAL LETTER SIGMA
+F773;Ssmall;LATIN SMALL CAPITAL LETTER S
+0054;T;LATIN CAPITAL LETTER T
+03A4;Tau;GREEK CAPITAL LETTER TAU
+0166;Tbar;LATIN CAPITAL LETTER T WITH STROKE
+0164;Tcaron;LATIN CAPITAL LETTER T WITH CARON
+0162;Tcommaaccent;LATIN CAPITAL LETTER T WITH CEDILLA
+021A;Tcommaaccent;LATIN CAPITAL LETTER T WITH COMMA BELOW;Duplicate
+0398;Theta;GREEK CAPITAL LETTER THETA
+00DE;Thorn;LATIN CAPITAL LETTER THORN
+F7FE;Thornsmall;LATIN SMALL CAPITAL LETTER THORN
+F6FE;Tildesmall;SMALL CAPITAL SMALL TILDE
+F774;Tsmall;LATIN SMALL CAPITAL LETTER T
+0055;U;LATIN CAPITAL LETTER U
+00DA;Uacute;LATIN CAPITAL LETTER U WITH ACUTE
+F7FA;Uacutesmall;LATIN SMALL CAPITAL LETTER U WITH ACUTE
+016C;Ubreve;LATIN CAPITAL LETTER U WITH BREVE
+00DB;Ucircumflex;LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+F7FB;Ucircumflexsmall;LATIN SMALL CAPITAL LETTER U WITH CIRCUMFLEX
+00DC;Udieresis;LATIN CAPITAL LETTER U WITH DIAERESIS
+F7FC;Udieresissmall;LATIN SMALL CAPITAL LETTER U WITH DIAERESIS
+00D9;Ugrave;LATIN CAPITAL LETTER U WITH GRAVE
+F7F9;Ugravesmall;LATIN SMALL CAPITAL LETTER U WITH GRAVE
+01AF;Uhorn;LATIN CAPITAL LETTER U WITH HORN
+0170;Uhungarumlaut;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+016A;Umacron;LATIN CAPITAL LETTER U WITH MACRON
+0172;Uogonek;LATIN CAPITAL LETTER U WITH OGONEK
+03A5;Upsilon;GREEK CAPITAL LETTER UPSILON
+03D2;Upsilon1;GREEK UPSILON WITH HOOK SYMBOL
+03AB;Upsilondieresis;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+038E;Upsilontonos;GREEK CAPITAL LETTER UPSILON WITH TONOS
+016E;Uring;LATIN CAPITAL LETTER U WITH RING ABOVE
+F775;Usmall;LATIN SMALL CAPITAL LETTER U
+0168;Utilde;LATIN CAPITAL LETTER U WITH TILDE
+0056;V;LATIN CAPITAL LETTER V
+F776;Vsmall;LATIN SMALL CAPITAL LETTER V
+0057;W;LATIN CAPITAL LETTER W
+1E82;Wacute;LATIN CAPITAL LETTER W WITH ACUTE
+0174;Wcircumflex;LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+1E84;Wdieresis;LATIN CAPITAL LETTER W WITH DIAERESIS
+1E80;Wgrave;LATIN CAPITAL LETTER W WITH GRAVE
+F777;Wsmall;LATIN SMALL CAPITAL LETTER W
+0058;X;LATIN CAPITAL LETTER X
+039E;Xi;GREEK CAPITAL LETTER XI
+F778;Xsmall;LATIN SMALL CAPITAL LETTER X
+0059;Y;LATIN CAPITAL LETTER Y
+00DD;Yacute;LATIN CAPITAL LETTER Y WITH ACUTE
+F7FD;Yacutesmall;LATIN SMALL CAPITAL LETTER Y WITH ACUTE
+0176;Ycircumflex;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+0178;Ydieresis;LATIN CAPITAL LETTER Y WITH DIAERESIS
+F7FF;Ydieresissmall;LATIN SMALL CAPITAL LETTER Y WITH DIAERESIS
+1EF2;Ygrave;LATIN CAPITAL LETTER Y WITH GRAVE
+F779;Ysmall;LATIN SMALL CAPITAL LETTER Y
+005A;Z;LATIN CAPITAL LETTER Z
+0179;Zacute;LATIN CAPITAL LETTER Z WITH ACUTE
+017D;Zcaron;LATIN CAPITAL LETTER Z WITH CARON
+F6FF;Zcaronsmall;LATIN SMALL CAPITAL LETTER Z WITH CARON
+017B;Zdotaccent;LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0396;Zeta;GREEK CAPITAL LETTER ZETA
+F77A;Zsmall;LATIN SMALL CAPITAL LETTER Z
+0061;a;LATIN SMALL LETTER A
+00E1;aacute;LATIN SMALL LETTER A WITH ACUTE
+0103;abreve;LATIN SMALL LETTER A WITH BREVE
+00E2;acircumflex;LATIN SMALL LETTER A WITH CIRCUMFLEX
+00B4;acute;ACUTE ACCENT
+0301;acutecomb;COMBINING ACUTE ACCENT
+00E4;adieresis;LATIN SMALL LETTER A WITH DIAERESIS
+00E6;ae;LATIN SMALL LETTER AE
+01FD;aeacute;LATIN SMALL LETTER AE WITH ACUTE
+2015;afii00208;HORIZONTAL BAR
+0410;afii10017;CYRILLIC CAPITAL LETTER A
+0411;afii10018;CYRILLIC CAPITAL LETTER BE
+0412;afii10019;CYRILLIC CAPITAL LETTER VE
+0413;afii10020;CYRILLIC CAPITAL LETTER GHE
+0414;afii10021;CYRILLIC CAPITAL LETTER DE
+0415;afii10022;CYRILLIC CAPITAL LETTER IE
+0401;afii10023;CYRILLIC CAPITAL LETTER IO
+0416;afii10024;CYRILLIC CAPITAL LETTER ZHE
+0417;afii10025;CYRILLIC CAPITAL LETTER ZE
+0418;afii10026;CYRILLIC CAPITAL LETTER I
+0419;afii10027;CYRILLIC CAPITAL LETTER SHORT I
+041A;afii10028;CYRILLIC CAPITAL LETTER KA
+041B;afii10029;CYRILLIC CAPITAL LETTER EL
+041C;afii10030;CYRILLIC CAPITAL LETTER EM
+041D;afii10031;CYRILLIC CAPITAL LETTER EN
+041E;afii10032;CYRILLIC CAPITAL LETTER O
+041F;afii10033;CYRILLIC CAPITAL LETTER PE
+0420;afii10034;CYRILLIC CAPITAL LETTER ER
+0421;afii10035;CYRILLIC CAPITAL LETTER ES
+0422;afii10036;CYRILLIC CAPITAL LETTER TE
+0423;afii10037;CYRILLIC CAPITAL LETTER U
+0424;afii10038;CYRILLIC CAPITAL LETTER EF
+0425;afii10039;CYRILLIC CAPITAL LETTER HA
+0426;afii10040;CYRILLIC CAPITAL LETTER TSE
+0427;afii10041;CYRILLIC CAPITAL LETTER CHE
+0428;afii10042;CYRILLIC CAPITAL LETTER SHA
+0429;afii10043;CYRILLIC CAPITAL LETTER SHCHA
+042A;afii10044;CYRILLIC CAPITAL LETTER HARD SIGN
+042B;afii10045;CYRILLIC CAPITAL LETTER YERU
+042C;afii10046;CYRILLIC CAPITAL LETTER SOFT SIGN
+042D;afii10047;CYRILLIC CAPITAL LETTER E
+042E;afii10048;CYRILLIC CAPITAL LETTER YU
+042F;afii10049;CYRILLIC CAPITAL LETTER YA
+0490;afii10050;CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+0402;afii10051;CYRILLIC CAPITAL LETTER DJE
+0403;afii10052;CYRILLIC CAPITAL LETTER GJE
+0404;afii10053;CYRILLIC CAPITAL LETTER UKRAINIAN IE
+0405;afii10054;CYRILLIC CAPITAL LETTER DZE
+0406;afii10055;CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+0407;afii10056;CYRILLIC CAPITAL LETTER YI
+0408;afii10057;CYRILLIC CAPITAL LETTER JE
+0409;afii10058;CYRILLIC CAPITAL LETTER LJE
+040A;afii10059;CYRILLIC CAPITAL LETTER NJE
+040B;afii10060;CYRILLIC CAPITAL LETTER TSHE
+040C;afii10061;CYRILLIC CAPITAL LETTER KJE
+040E;afii10062;CYRILLIC CAPITAL LETTER SHORT U
+F6C4;afii10063;CYRILLIC SMALL LETTER GHE VARIANT
+F6C5;afii10064;CYRILLIC SMALL LETTER BE VARIANT
+0430;afii10065;CYRILLIC SMALL LETTER A
+0431;afii10066;CYRILLIC SMALL LETTER BE
+0432;afii10067;CYRILLIC SMALL LETTER VE
+0433;afii10068;CYRILLIC SMALL LETTER GHE
+0434;afii10069;CYRILLIC SMALL LETTER DE
+0435;afii10070;CYRILLIC SMALL LETTER IE
+0451;afii10071;CYRILLIC SMALL LETTER IO
+0436;afii10072;CYRILLIC SMALL LETTER ZHE
+0437;afii10073;CYRILLIC SMALL LETTER ZE
+0438;afii10074;CYRILLIC SMALL LETTER I
+0439;afii10075;CYRILLIC SMALL LETTER SHORT I
+043A;afii10076;CYRILLIC SMALL LETTER KA
+043B;afii10077;CYRILLIC SMALL LETTER EL
+043C;afii10078;CYRILLIC SMALL LETTER EM
+043D;afii10079;CYRILLIC SMALL LETTER EN
+043E;afii10080;CYRILLIC SMALL LETTER O
+043F;afii10081;CYRILLIC SMALL LETTER PE
+0440;afii10082;CYRILLIC SMALL LETTER ER
+0441;afii10083;CYRILLIC SMALL LETTER ES
+0442;afii10084;CYRILLIC SMALL LETTER TE
+0443;afii10085;CYRILLIC SMALL LETTER U
+0444;afii10086;CYRILLIC SMALL LETTER EF
+0445;afii10087;CYRILLIC SMALL LETTER HA
+0446;afii10088;CYRILLIC SMALL LETTER TSE
+0447;afii10089;CYRILLIC SMALL LETTER CHE
+0448;afii10090;CYRILLIC SMALL LETTER SHA
+0449;afii10091;CYRILLIC SMALL LETTER SHCHA
+044A;afii10092;CYRILLIC SMALL LETTER HARD SIGN
+044B;afii10093;CYRILLIC SMALL LETTER YERU
+044C;afii10094;CYRILLIC SMALL LETTER SOFT SIGN
+044D;afii10095;CYRILLIC SMALL LETTER E
+044E;afii10096;CYRILLIC SMALL LETTER YU
+044F;afii10097;CYRILLIC SMALL LETTER YA
+0491;afii10098;CYRILLIC SMALL LETTER GHE WITH UPTURN
+0452;afii10099;CYRILLIC SMALL LETTER DJE
+0453;afii10100;CYRILLIC SMALL LETTER GJE
+0454;afii10101;CYRILLIC SMALL LETTER UKRAINIAN IE
+0455;afii10102;CYRILLIC SMALL LETTER DZE
+0456;afii10103;CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+0457;afii10104;CYRILLIC SMALL LETTER YI
+0458;afii10105;CYRILLIC SMALL LETTER JE
+0459;afii10106;CYRILLIC SMALL LETTER LJE
+045A;afii10107;CYRILLIC SMALL LETTER NJE
+045B;afii10108;CYRILLIC SMALL LETTER TSHE
+045C;afii10109;CYRILLIC SMALL LETTER KJE
+045E;afii10110;CYRILLIC SMALL LETTER SHORT U
+040F;afii10145;CYRILLIC CAPITAL LETTER DZHE
+0462;afii10146;CYRILLIC CAPITAL LETTER YAT
+0472;afii10147;CYRILLIC CAPITAL LETTER FITA
+0474;afii10148;CYRILLIC CAPITAL LETTER IZHITSA
+F6C6;afii10192;CYRILLIC SMALL LETTER DE VARIANT
+045F;afii10193;CYRILLIC SMALL LETTER DZHE
+0463;afii10194;CYRILLIC SMALL LETTER YAT
+0473;afii10195;CYRILLIC SMALL LETTER FITA
+0475;afii10196;CYRILLIC SMALL LETTER IZHITSA
+F6C7;afii10831;CYRILLIC SMALL LETTER PE VARIANT
+F6C8;afii10832;CYRILLIC SMALL LETTER TE VARIANT
+04D9;afii10846;CYRILLIC SMALL LETTER SCHWA
+200E;afii299;LEFT-TO-RIGHT MARK
+200F;afii300;RIGHT-TO-LEFT MARK
+200D;afii301;ZERO WIDTH JOINER
+066A;afii57381;ARABIC PERCENT SIGN
+060C;afii57388;ARABIC COMMA
+0660;afii57392;ARABIC-INDIC DIGIT ZERO
+0661;afii57393;ARABIC-INDIC DIGIT ONE
+0662;afii57394;ARABIC-INDIC DIGIT TWO
+0663;afii57395;ARABIC-INDIC DIGIT THREE
+0664;afii57396;ARABIC-INDIC DIGIT FOUR
+0665;afii57397;ARABIC-INDIC DIGIT FIVE
+0666;afii57398;ARABIC-INDIC DIGIT SIX
+0667;afii57399;ARABIC-INDIC DIGIT SEVEN
+0668;afii57400;ARABIC-INDIC DIGIT EIGHT
+0669;afii57401;ARABIC-INDIC DIGIT NINE
+061B;afii57403;ARABIC SEMICOLON
+061F;afii57407;ARABIC QUESTION MARK
+0621;afii57409;ARABIC LETTER HAMZA
+0622;afii57410;ARABIC LETTER ALEF WITH MADDA ABOVE
+0623;afii57411;ARABIC LETTER ALEF WITH HAMZA ABOVE
+0624;afii57412;ARABIC LETTER WAW WITH HAMZA ABOVE
+0625;afii57413;ARABIC LETTER ALEF WITH HAMZA BELOW
+0626;afii57414;ARABIC LETTER YEH WITH HAMZA ABOVE
+0627;afii57415;ARABIC LETTER ALEF
+0628;afii57416;ARABIC LETTER BEH
+0629;afii57417;ARABIC LETTER TEH MARBUTA
+062A;afii57418;ARABIC LETTER TEH
+062B;afii57419;ARABIC LETTER THEH
+062C;afii57420;ARABIC LETTER JEEM
+062D;afii57421;ARABIC LETTER HAH
+062E;afii57422;ARABIC LETTER KHAH
+062F;afii57423;ARABIC LETTER DAL
+0630;afii57424;ARABIC LETTER THAL
+0631;afii57425;ARABIC LETTER REH
+0632;afii57426;ARABIC LETTER ZAIN
+0633;afii57427;ARABIC LETTER SEEN
+0634;afii57428;ARABIC LETTER SHEEN
+0635;afii57429;ARABIC LETTER SAD
+0636;afii57430;ARABIC LETTER DAD
+0637;afii57431;ARABIC LETTER TAH
+0638;afii57432;ARABIC LETTER ZAH
+0639;afii57433;ARABIC LETTER AIN
+063A;afii57434;ARABIC LETTER GHAIN
+0640;afii57440;ARABIC TATWEEL
+0641;afii57441;ARABIC LETTER FEH
+0642;afii57442;ARABIC LETTER QAF
+0643;afii57443;ARABIC LETTER KAF
+0644;afii57444;ARABIC LETTER LAM
+0645;afii57445;ARABIC LETTER MEEM
+0646;afii57446;ARABIC LETTER NOON
+0648;afii57448;ARABIC LETTER WAW
+0649;afii57449;ARABIC LETTER ALEF MAKSURA
+064A;afii57450;ARABIC LETTER YEH
+064B;afii57451;ARABIC FATHATAN
+064C;afii57452;ARABIC DAMMATAN
+064D;afii57453;ARABIC KASRATAN
+064E;afii57454;ARABIC FATHA
+064F;afii57455;ARABIC DAMMA
+0650;afii57456;ARABIC KASRA
+0651;afii57457;ARABIC SHADDA
+0652;afii57458;ARABIC SUKUN
+0647;afii57470;ARABIC LETTER HEH
+06A4;afii57505;ARABIC LETTER VEH
+067E;afii57506;ARABIC LETTER PEH
+0686;afii57507;ARABIC LETTER TCHEH
+0698;afii57508;ARABIC LETTER JEH
+06AF;afii57509;ARABIC LETTER GAF
+0679;afii57511;ARABIC LETTER TTEH
+0688;afii57512;ARABIC LETTER DDAL
+0691;afii57513;ARABIC LETTER RREH
+06BA;afii57514;ARABIC LETTER NOON GHUNNA
+06D2;afii57519;ARABIC LETTER YEH BARREE
+06D5;afii57534;ARABIC LETTER AE
+20AA;afii57636;NEW SHEQEL SIGN
+05BE;afii57645;HEBREW PUNCTUATION MAQAF
+05C3;afii57658;HEBREW PUNCTUATION SOF PASUQ
+05D0;afii57664;HEBREW LETTER ALEF
+05D1;afii57665;HEBREW LETTER BET
+05D2;afii57666;HEBREW LETTER GIMEL
+05D3;afii57667;HEBREW LETTER DALET
+05D4;afii57668;HEBREW LETTER HE
+05D5;afii57669;HEBREW LETTER VAV
+05D6;afii57670;HEBREW LETTER ZAYIN
+05D7;afii57671;HEBREW LETTER HET
+05D8;afii57672;HEBREW LETTER TET
+05D9;afii57673;HEBREW LETTER YOD
+05DA;afii57674;HEBREW LETTER FINAL KAF
+05DB;afii57675;HEBREW LETTER KAF
+05DC;afii57676;HEBREW LETTER LAMED
+05DD;afii57677;HEBREW LETTER FINAL MEM
+05DE;afii57678;HEBREW LETTER MEM
+05DF;afii57679;HEBREW LETTER FINAL NUN
+05E0;afii57680;HEBREW LETTER NUN
+05E1;afii57681;HEBREW LETTER SAMEKH
+05E2;afii57682;HEBREW LETTER AYIN
+05E3;afii57683;HEBREW LETTER FINAL PE
+05E4;afii57684;HEBREW LETTER PE
+05E5;afii57685;HEBREW LETTER FINAL TSADI
+05E6;afii57686;HEBREW LETTER TSADI
+05E7;afii57687;HEBREW LETTER QOF
+05E8;afii57688;HEBREW LETTER RESH
+05E9;afii57689;HEBREW LETTER SHIN
+05EA;afii57690;HEBREW LETTER TAV
+FB2A;afii57694;HEBREW LETTER SHIN WITH SHIN DOT
+FB2B;afii57695;HEBREW LETTER SHIN WITH SIN DOT
+FB4B;afii57700;HEBREW LETTER VAV WITH HOLAM
+FB1F;afii57705;HEBREW LIGATURE YIDDISH YOD YOD PATAH
+05F0;afii57716;HEBREW LIGATURE YIDDISH DOUBLE VAV
+05F1;afii57717;HEBREW LIGATURE YIDDISH VAV YOD
+05F2;afii57718;HEBREW LIGATURE YIDDISH DOUBLE YOD
+FB35;afii57723;HEBREW LETTER VAV WITH DAGESH
+05B4;afii57793;HEBREW POINT HIRIQ
+05B5;afii57794;HEBREW POINT TSERE
+05B6;afii57795;HEBREW POINT SEGOL
+05BB;afii57796;HEBREW POINT QUBUTS
+05B8;afii57797;HEBREW POINT QAMATS
+05B7;afii57798;HEBREW POINT PATAH
+05B0;afii57799;HEBREW POINT SHEVA
+05B2;afii57800;HEBREW POINT HATAF PATAH
+05B1;afii57801;HEBREW POINT HATAF SEGOL
+05B3;afii57802;HEBREW POINT HATAF QAMATS
+05C2;afii57803;HEBREW POINT SIN DOT
+05C1;afii57804;HEBREW POINT SHIN DOT
+05B9;afii57806;HEBREW POINT HOLAM
+05BC;afii57807;HEBREW POINT DAGESH OR MAPIQ
+05BD;afii57839;HEBREW POINT METEG
+05BF;afii57841;HEBREW POINT RAFE
+05C0;afii57842;HEBREW PUNCTUATION PASEQ
+02BC;afii57929;MODIFIER LETTER APOSTROPHE
+2105;afii61248;CARE OF
+2113;afii61289;SCRIPT SMALL L
+2116;afii61352;NUMERO SIGN
+202C;afii61573;POP DIRECTIONAL FORMATTING
+202D;afii61574;LEFT-TO-RIGHT OVERRIDE
+202E;afii61575;RIGHT-TO-LEFT OVERRIDE
+200C;afii61664;ZERO WIDTH NON-JOINER
+066D;afii63167;ARABIC FIVE POINTED STAR
+02BD;afii64937;MODIFIER LETTER REVERSED COMMA
+00E0;agrave;LATIN SMALL LETTER A WITH GRAVE
+2135;aleph;ALEF SYMBOL
+03B1;alpha;GREEK SMALL LETTER ALPHA
+03AC;alphatonos;GREEK SMALL LETTER ALPHA WITH TONOS
+0101;amacron;LATIN SMALL LETTER A WITH MACRON
+0026;ampersand;AMPERSAND
+F726;ampersandsmall;SMALL CAPITAL AMPERSAND
+2220;angle;ANGLE
+2329;angleleft;LEFT-POINTING ANGLE BRACKET
+232A;angleright;RIGHT-POINTING ANGLE BRACKET
+0387;anoteleia;GREEK ANO TELEIA
+0105;aogonek;LATIN SMALL LETTER A WITH OGONEK
+2248;approxequal;ALMOST EQUAL TO
+00E5;aring;LATIN SMALL LETTER A WITH RING ABOVE
+01FB;aringacute;LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE
+2194;arrowboth;LEFT RIGHT ARROW
+21D4;arrowdblboth;LEFT RIGHT DOUBLE ARROW
+21D3;arrowdbldown;DOWNWARDS DOUBLE ARROW
+21D0;arrowdblleft;LEFTWARDS DOUBLE ARROW
+21D2;arrowdblright;RIGHTWARDS DOUBLE ARROW
+21D1;arrowdblup;UPWARDS DOUBLE ARROW
+2193;arrowdown;DOWNWARDS ARROW
+F8E7;arrowhorizex;HORIZONTAL ARROW EXTENDER
+2190;arrowleft;LEFTWARDS ARROW
+2192;arrowright;RIGHTWARDS ARROW
+2191;arrowup;UPWARDS ARROW
+2195;arrowupdn;UP DOWN ARROW
+21A8;arrowupdnbse;UP DOWN ARROW WITH BASE
+F8E6;arrowvertex;VERTICAL ARROW EXTENDER
+005E;asciicircum;CIRCUMFLEX ACCENT
+007E;asciitilde;TILDE
+002A;asterisk;ASTERISK
+2217;asteriskmath;ASTERISK OPERATOR
+F6E9;asuperior;SUPERSCRIPT LATIN SMALL LETTER A
+0040;at;COMMERCIAL AT
+00E3;atilde;LATIN SMALL LETTER A WITH TILDE
+0062;b;LATIN SMALL LETTER B
+005C;backslash;REVERSE SOLIDUS
+007C;bar;VERTICAL LINE
+03B2;beta;GREEK SMALL LETTER BETA
+2588;block;FULL BLOCK
+F8F4;braceex;CURLY BRACKET EXTENDER
+007B;braceleft;LEFT CURLY BRACKET
+F8F3;braceleftbt;LEFT CURLY BRACKET BOTTOM
+F8F2;braceleftmid;LEFT CURLY BRACKET MID
+F8F1;bracelefttp;LEFT CURLY BRACKET TOP
+007D;braceright;RIGHT CURLY BRACKET
+F8FE;bracerightbt;RIGHT CURLY BRACKET BOTTOM
+F8FD;bracerightmid;RIGHT CURLY BRACKET MID
+F8FC;bracerighttp;RIGHT CURLY BRACKET TOP
+005B;bracketleft;LEFT SQUARE BRACKET
+F8F0;bracketleftbt;LEFT SQUARE BRACKET BOTTOM
+F8EF;bracketleftex;LEFT SQUARE BRACKET EXTENDER
+F8EE;bracketlefttp;LEFT SQUARE BRACKET TOP
+005D;bracketright;RIGHT SQUARE BRACKET
+F8FB;bracketrightbt;RIGHT SQUARE BRACKET BOTTOM
+F8FA;bracketrightex;RIGHT SQUARE BRACKET EXTENDER
+F8F9;bracketrighttp;RIGHT SQUARE BRACKET TOP
+02D8;breve;BREVE
+00A6;brokenbar;BROKEN BAR
+F6EA;bsuperior;SUPERSCRIPT LATIN SMALL LETTER B
+2022;bullet;BULLET
+0063;c;LATIN SMALL LETTER C
+0107;cacute;LATIN SMALL LETTER C WITH ACUTE
+02C7;caron;CARON
+21B5;carriagereturn;DOWNWARDS ARROW WITH CORNER LEFTWARDS
+010D;ccaron;LATIN SMALL LETTER C WITH CARON
+00E7;ccedilla;LATIN SMALL LETTER C WITH CEDILLA
+0109;ccircumflex;LATIN SMALL LETTER C WITH CIRCUMFLEX
+010B;cdotaccent;LATIN SMALL LETTER C WITH DOT ABOVE
+00B8;cedilla;CEDILLA
+00A2;cent;CENT SIGN
+F6DF;centinferior;SUBSCRIPT CENT SIGN
+F7A2;centoldstyle;OLDSTYLE CENT SIGN
+F6E0;centsuperior;SUPERSCRIPT CENT SIGN
+03C7;chi;GREEK SMALL LETTER CHI
+25CB;circle;WHITE CIRCLE
+2297;circlemultiply;CIRCLED TIMES
+2295;circleplus;CIRCLED PLUS
+02C6;circumflex;MODIFIER LETTER CIRCUMFLEX ACCENT
+2663;club;BLACK CLUB SUIT
+003A;colon;COLON
+20A1;colonmonetary;COLON SIGN
+002C;comma;COMMA
+F6C3;commaaccent;COMMA BELOW
+F6E1;commainferior;SUBSCRIPT COMMA
+F6E2;commasuperior;SUPERSCRIPT COMMA
+2245;congruent;APPROXIMATELY EQUAL TO
+00A9;copyright;COPYRIGHT SIGN
+F8E9;copyrightsans;COPYRIGHT SIGN SANS SERIF
+F6D9;copyrightserif;COPYRIGHT SIGN SERIF
+00A4;currency;CURRENCY SIGN
+F6D1;cyrBreve;CAPITAL CYRILLIC BREVE
+F6D2;cyrFlex;CAPITAL CYRILLIC CIRCUMFLEX
+F6D4;cyrbreve;CYRILLIC BREVE
+F6D5;cyrflex;CYRILLIC CIRCUMFLEX
+0064;d;LATIN SMALL LETTER D
+2020;dagger;DAGGER
+2021;daggerdbl;DOUBLE DAGGER
+F6D3;dblGrave;CAPITAL DOUBLE GRAVE ACCENT
+F6D6;dblgrave;DOUBLE GRAVE ACCENT
+010F;dcaron;LATIN SMALL LETTER D WITH CARON
+0111;dcroat;LATIN SMALL LETTER D WITH STROKE
+00B0;degree;DEGREE SIGN
+03B4;delta;GREEK SMALL LETTER DELTA
+2666;diamond;BLACK DIAMOND SUIT
+00A8;dieresis;DIAERESIS
+F6D7;dieresisacute;DIAERESIS ACUTE ACCENT
+F6D8;dieresisgrave;DIAERESIS GRAVE ACCENT
+0385;dieresistonos;GREEK DIALYTIKA TONOS
+00F7;divide;DIVISION SIGN
+2593;dkshade;DARK SHADE
+2584;dnblock;LOWER HALF BLOCK
+0024;dollar;DOLLAR SIGN
+F6E3;dollarinferior;SUBSCRIPT DOLLAR SIGN
+F724;dollaroldstyle;OLDSTYLE DOLLAR SIGN
+F6E4;dollarsuperior;SUPERSCRIPT DOLLAR SIGN
+20AB;dong;DONG SIGN
+02D9;dotaccent;DOT ABOVE
+0323;dotbelowcomb;COMBINING DOT BELOW
+0131;dotlessi;LATIN SMALL LETTER DOTLESS I
+F6BE;dotlessj;LATIN SMALL LETTER DOTLESS J
+22C5;dotmath;DOT OPERATOR
+F6EB;dsuperior;SUPERSCRIPT LATIN SMALL LETTER D
+0065;e;LATIN SMALL LETTER E
+00E9;eacute;LATIN SMALL LETTER E WITH ACUTE
+0115;ebreve;LATIN SMALL LETTER E WITH BREVE
+011B;ecaron;LATIN SMALL LETTER E WITH CARON
+00EA;ecircumflex;LATIN SMALL LETTER E WITH CIRCUMFLEX
+00EB;edieresis;LATIN SMALL LETTER E WITH DIAERESIS
+0117;edotaccent;LATIN SMALL LETTER E WITH DOT ABOVE
+00E8;egrave;LATIN SMALL LETTER E WITH GRAVE
+0038;eight;DIGIT EIGHT
+2088;eightinferior;SUBSCRIPT EIGHT
+F738;eightoldstyle;OLDSTYLE DIGIT EIGHT
+2078;eightsuperior;SUPERSCRIPT EIGHT
+2208;element;ELEMENT OF
+2026;ellipsis;HORIZONTAL ELLIPSIS
+0113;emacron;LATIN SMALL LETTER E WITH MACRON
+2014;emdash;EM DASH
+2205;emptyset;EMPTY SET
+2013;endash;EN DASH
+014B;eng;LATIN SMALL LETTER ENG
+0119;eogonek;LATIN SMALL LETTER E WITH OGONEK
+03B5;epsilon;GREEK SMALL LETTER EPSILON
+03AD;epsilontonos;GREEK SMALL LETTER EPSILON WITH TONOS
+003D;equal;EQUALS SIGN
+2261;equivalence;IDENTICAL TO
+212E;estimated;ESTIMATED SYMBOL
+F6EC;esuperior;SUPERSCRIPT LATIN SMALL LETTER E
+03B7;eta;GREEK SMALL LETTER ETA
+03AE;etatonos;GREEK SMALL LETTER ETA WITH TONOS
+00F0;eth;LATIN SMALL LETTER ETH
+0021;exclam;EXCLAMATION MARK
+203C;exclamdbl;DOUBLE EXCLAMATION MARK
+00A1;exclamdown;INVERTED EXCLAMATION MARK
+F7A1;exclamdownsmall;SMALL CAPITAL INVERTED EXCLAMATION MARK
+F721;exclamsmall;SMALL CAPITAL EXCLAMATION MARK
+2203;existential;THERE EXISTS
+0066;f;LATIN SMALL LETTER F
+2640;female;FEMALE SIGN
+FB00;ff;LATIN SMALL LIGATURE FF
+FB03;ffi;LATIN SMALL LIGATURE FFI
+FB04;ffl;LATIN SMALL LIGATURE FFL
+FB01;fi;LATIN SMALL LIGATURE FI
+2012;figuredash;FIGURE DASH
+25A0;filledbox;BLACK SQUARE
+25AC;filledrect;BLACK RECTANGLE
+0035;five;DIGIT FIVE
+215D;fiveeighths;VULGAR FRACTION FIVE EIGHTHS
+2085;fiveinferior;SUBSCRIPT FIVE
+F735;fiveoldstyle;OLDSTYLE DIGIT FIVE
+2075;fivesuperior;SUPERSCRIPT FIVE
+FB02;fl;LATIN SMALL LIGATURE FL
+0192;florin;LATIN SMALL LETTER F WITH HOOK
+0034;four;DIGIT FOUR
+2084;fourinferior;SUBSCRIPT FOUR
+F734;fouroldstyle;OLDSTYLE DIGIT FOUR
+2074;foursuperior;SUPERSCRIPT FOUR
+2044;fraction;FRACTION SLASH
+2215;fraction;DIVISION SLASH;Duplicate
+20A3;franc;FRENCH FRANC SIGN
+0067;g;LATIN SMALL LETTER G
+03B3;gamma;GREEK SMALL LETTER GAMMA
+011F;gbreve;LATIN SMALL LETTER G WITH BREVE
+01E7;gcaron;LATIN SMALL LETTER G WITH CARON
+011D;gcircumflex;LATIN SMALL LETTER G WITH CIRCUMFLEX
+0123;gcommaaccent;LATIN SMALL LETTER G WITH CEDILLA
+0121;gdotaccent;LATIN SMALL LETTER G WITH DOT ABOVE
+00DF;germandbls;LATIN SMALL LETTER SHARP S
+2207;gradient;NABLA
+0060;grave;GRAVE ACCENT
+0300;gravecomb;COMBINING GRAVE ACCENT
+003E;greater;GREATER-THAN SIGN
+2265;greaterequal;GREATER-THAN OR EQUAL TO
+00AB;guillemotleft;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+00BB;guillemotright;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+2039;guilsinglleft;SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+203A;guilsinglright;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0068;h;LATIN SMALL LETTER H
+0127;hbar;LATIN SMALL LETTER H WITH STROKE
+0125;hcircumflex;LATIN SMALL LETTER H WITH CIRCUMFLEX
+2665;heart;BLACK HEART SUIT
+0309;hookabovecomb;COMBINING HOOK ABOVE
+2302;house;HOUSE
+02DD;hungarumlaut;DOUBLE ACUTE ACCENT
+002D;hyphen;HYPHEN-MINUS
+00AD;hyphen;SOFT HYPHEN;Duplicate
+F6E5;hypheninferior;SUBSCRIPT HYPHEN-MINUS
+F6E6;hyphensuperior;SUPERSCRIPT HYPHEN-MINUS
+0069;i;LATIN SMALL LETTER I
+00ED;iacute;LATIN SMALL LETTER I WITH ACUTE
+012D;ibreve;LATIN SMALL LETTER I WITH BREVE
+00EE;icircumflex;LATIN SMALL LETTER I WITH CIRCUMFLEX
+00EF;idieresis;LATIN SMALL LETTER I WITH DIAERESIS
+00EC;igrave;LATIN SMALL LETTER I WITH GRAVE
+0133;ij;LATIN SMALL LIGATURE IJ
+012B;imacron;LATIN SMALL LETTER I WITH MACRON
+221E;infinity;INFINITY
+222B;integral;INTEGRAL
+2321;integralbt;BOTTOM HALF INTEGRAL
+F8F5;integralex;INTEGRAL EXTENDER
+2320;integraltp;TOP HALF INTEGRAL
+2229;intersection;INTERSECTION
+25D8;invbullet;INVERSE BULLET
+25D9;invcircle;INVERSE WHITE CIRCLE
+263B;invsmileface;BLACK SMILING FACE
+012F;iogonek;LATIN SMALL LETTER I WITH OGONEK
+03B9;iota;GREEK SMALL LETTER IOTA
+03CA;iotadieresis;GREEK SMALL LETTER IOTA WITH DIALYTIKA
+0390;iotadieresistonos;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+03AF;iotatonos;GREEK SMALL LETTER IOTA WITH TONOS
+F6ED;isuperior;SUPERSCRIPT LATIN SMALL LETTER I
+0129;itilde;LATIN SMALL LETTER I WITH TILDE
+006A;j;LATIN SMALL LETTER J
+0135;jcircumflex;LATIN SMALL LETTER J WITH CIRCUMFLEX
+006B;k;LATIN SMALL LETTER K
+03BA;kappa;GREEK SMALL LETTER KAPPA
+0137;kcommaaccent;LATIN SMALL LETTER K WITH CEDILLA
+0138;kgreenlandic;LATIN SMALL LETTER KRA
+006C;l;LATIN SMALL LETTER L
+013A;lacute;LATIN SMALL LETTER L WITH ACUTE
+03BB;lambda;GREEK SMALL LETTER LAMDA
+013E;lcaron;LATIN SMALL LETTER L WITH CARON
+013C;lcommaaccent;LATIN SMALL LETTER L WITH CEDILLA
+0140;ldot;LATIN SMALL LETTER L WITH MIDDLE DOT
+003C;less;LESS-THAN SIGN
+2264;lessequal;LESS-THAN OR EQUAL TO
+258C;lfblock;LEFT HALF BLOCK
+20A4;lira;LIRA SIGN
+F6C0;ll;LATIN SMALL LETTER LL
+2227;logicaland;LOGICAL AND
+00AC;logicalnot;NOT SIGN
+2228;logicalor;LOGICAL OR
+017F;longs;LATIN SMALL LETTER LONG S
+25CA;lozenge;LOZENGE
+0142;lslash;LATIN SMALL LETTER L WITH STROKE
+F6EE;lsuperior;SUPERSCRIPT LATIN SMALL LETTER L
+2591;ltshade;LIGHT SHADE
+006D;m;LATIN SMALL LETTER M
+00AF;macron;MACRON
+02C9;macron;MODIFIER LETTER MACRON;Duplicate
+2642;male;MALE SIGN
+2212;minus;MINUS SIGN
+2032;minute;PRIME
+F6EF;msuperior;SUPERSCRIPT LATIN SMALL LETTER M
+00B5;mu;MICRO SIGN
+03BC;mu;GREEK SMALL LETTER MU;Duplicate
+00D7;multiply;MULTIPLICATION SIGN
+266A;musicalnote;EIGHTH NOTE
+266B;musicalnotedbl;BEAMED EIGHTH NOTES
+006E;n;LATIN SMALL LETTER N
+0144;nacute;LATIN SMALL LETTER N WITH ACUTE
+0149;napostrophe;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+0148;ncaron;LATIN SMALL LETTER N WITH CARON
+0146;ncommaaccent;LATIN SMALL LETTER N WITH CEDILLA
+0039;nine;DIGIT NINE
+2089;nineinferior;SUBSCRIPT NINE
+F739;nineoldstyle;OLDSTYLE DIGIT NINE
+2079;ninesuperior;SUPERSCRIPT NINE
+2209;notelement;NOT AN ELEMENT OF
+2260;notequal;NOT EQUAL TO
+2284;notsubset;NOT A SUBSET OF
+207F;nsuperior;SUPERSCRIPT LATIN SMALL LETTER N
+00F1;ntilde;LATIN SMALL LETTER N WITH TILDE
+03BD;nu;GREEK SMALL LETTER NU
+0023;numbersign;NUMBER SIGN
+006F;o;LATIN SMALL LETTER O
+00F3;oacute;LATIN SMALL LETTER O WITH ACUTE
+014F;obreve;LATIN SMALL LETTER O WITH BREVE
+00F4;ocircumflex;LATIN SMALL LETTER O WITH CIRCUMFLEX
+00F6;odieresis;LATIN SMALL LETTER O WITH DIAERESIS
+0153;oe;LATIN SMALL LIGATURE OE
+02DB;ogonek;OGONEK
+00F2;ograve;LATIN SMALL LETTER O WITH GRAVE
+01A1;ohorn;LATIN SMALL LETTER O WITH HORN
+0151;ohungarumlaut;LATIN SMALL LETTER O WITH DOUBLE ACUTE
+014D;omacron;LATIN SMALL LETTER O WITH MACRON
+03C9;omega;GREEK SMALL LETTER OMEGA
+03D6;omega1;GREEK PI SYMBOL
+03CE;omegatonos;GREEK SMALL LETTER OMEGA WITH TONOS
+03BF;omicron;GREEK SMALL LETTER OMICRON
+03CC;omicrontonos;GREEK SMALL LETTER OMICRON WITH TONOS
+0031;one;DIGIT ONE
+2024;onedotenleader;ONE DOT LEADER
+215B;oneeighth;VULGAR FRACTION ONE EIGHTH
+F6DC;onefitted;PROPORTIONAL DIGIT ONE
+00BD;onehalf;VULGAR FRACTION ONE HALF
+2081;oneinferior;SUBSCRIPT ONE
+F731;oneoldstyle;OLDSTYLE DIGIT ONE
+00BC;onequarter;VULGAR FRACTION ONE QUARTER
+00B9;onesuperior;SUPERSCRIPT ONE
+2153;onethird;VULGAR FRACTION ONE THIRD
+25E6;openbullet;WHITE BULLET
+00AA;ordfeminine;FEMININE ORDINAL INDICATOR
+00BA;ordmasculine;MASCULINE ORDINAL INDICATOR
+221F;orthogonal;RIGHT ANGLE
+00F8;oslash;LATIN SMALL LETTER O WITH STROKE
+01FF;oslashacute;LATIN SMALL LETTER O WITH STROKE AND ACUTE
+F6F0;osuperior;SUPERSCRIPT LATIN SMALL LETTER O
+00F5;otilde;LATIN SMALL LETTER O WITH TILDE
+0070;p;LATIN SMALL LETTER P
+00B6;paragraph;PILCROW SIGN
+0028;parenleft;LEFT PARENTHESIS
+F8ED;parenleftbt;LEFT PAREN BOTTOM
+F8EC;parenleftex;LEFT PAREN EXTENDER
+208D;parenleftinferior;SUBSCRIPT LEFT PARENTHESIS
+207D;parenleftsuperior;SUPERSCRIPT LEFT PARENTHESIS
+F8EB;parenlefttp;LEFT PAREN TOP
+0029;parenright;RIGHT PARENTHESIS
+F8F8;parenrightbt;RIGHT PAREN BOTTOM
+F8F7;parenrightex;RIGHT PAREN EXTENDER
+208E;parenrightinferior;SUBSCRIPT RIGHT PARENTHESIS
+207E;parenrightsuperior;SUPERSCRIPT RIGHT PARENTHESIS
+F8F6;parenrighttp;RIGHT PAREN TOP
+2202;partialdiff;PARTIAL DIFFERENTIAL
+0025;percent;PERCENT SIGN
+002E;period;FULL STOP
+00B7;periodcentered;MIDDLE DOT
+2219;periodcentered;BULLET OPERATOR;Duplicate
+F6E7;periodinferior;SUBSCRIPT FULL STOP
+F6E8;periodsuperior;SUPERSCRIPT FULL STOP
+22A5;perpendicular;UP TACK
+2030;perthousand;PER MILLE SIGN
+20A7;peseta;PESETA SIGN
+03C6;phi;GREEK SMALL LETTER PHI
+03D5;phi1;GREEK PHI SYMBOL
+03C0;pi;GREEK SMALL LETTER PI
+002B;plus;PLUS SIGN
+00B1;plusminus;PLUS-MINUS SIGN
+211E;prescription;PRESCRIPTION TAKE
+220F;product;N-ARY PRODUCT
+2282;propersubset;SUBSET OF
+2283;propersuperset;SUPERSET OF
+221D;proportional;PROPORTIONAL TO
+03C8;psi;GREEK SMALL LETTER PSI
+0071;q;LATIN SMALL LETTER Q
+003F;question;QUESTION MARK
+00BF;questiondown;INVERTED QUESTION MARK
+F7BF;questiondownsmall;SMALL CAPITAL INVERTED QUESTION MARK
+F73F;questionsmall;SMALL CAPITAL QUESTION MARK
+0022;quotedbl;QUOTATION MARK
+201E;quotedblbase;DOUBLE LOW-9 QUOTATION MARK
+201C;quotedblleft;LEFT DOUBLE QUOTATION MARK
+201D;quotedblright;RIGHT DOUBLE QUOTATION MARK
+2018;quoteleft;LEFT SINGLE QUOTATION MARK
+201B;quotereversed;SINGLE HIGH-REVERSED-9 QUOTATION MARK
+2019;quoteright;RIGHT SINGLE QUOTATION MARK
+201A;quotesinglbase;SINGLE LOW-9 QUOTATION MARK
+0027;quotesingle;APOSTROPHE
+0072;r;LATIN SMALL LETTER R
+0155;racute;LATIN SMALL LETTER R WITH ACUTE
+221A;radical;SQUARE ROOT
+F8E5;radicalex;RADICAL EXTENDER
+0159;rcaron;LATIN SMALL LETTER R WITH CARON
+0157;rcommaaccent;LATIN SMALL LETTER R WITH CEDILLA
+2286;reflexsubset;SUBSET OF OR EQUAL TO
+2287;reflexsuperset;SUPERSET OF OR EQUAL TO
+00AE;registered;REGISTERED SIGN
+F8E8;registersans;REGISTERED SIGN SANS SERIF
+F6DA;registerserif;REGISTERED SIGN SERIF
+2310;revlogicalnot;REVERSED NOT SIGN
+03C1;rho;GREEK SMALL LETTER RHO
+02DA;ring;RING ABOVE
+F6F1;rsuperior;SUPERSCRIPT LATIN SMALL LETTER R
+2590;rtblock;RIGHT HALF BLOCK
+F6DD;rupiah;RUPIAH SIGN
+0073;s;LATIN SMALL LETTER S
+015B;sacute;LATIN SMALL LETTER S WITH ACUTE
+0161;scaron;LATIN SMALL LETTER S WITH CARON
+015F;scedilla;LATIN SMALL LETTER S WITH CEDILLA
+F6C2;scedilla;LATIN SMALL LETTER S WITH CEDILLA;Duplicate
+015D;scircumflex;LATIN SMALL LETTER S WITH CIRCUMFLEX
+0219;scommaaccent;LATIN SMALL LETTER S WITH COMMA BELOW
+2033;second;DOUBLE PRIME
+00A7;section;SECTION SIGN
+003B;semicolon;SEMICOLON
+0037;seven;DIGIT SEVEN
+215E;seveneighths;VULGAR FRACTION SEVEN EIGHTHS
+2087;seveninferior;SUBSCRIPT SEVEN
+F737;sevenoldstyle;OLDSTYLE DIGIT SEVEN
+2077;sevensuperior;SUPERSCRIPT SEVEN
+2592;shade;MEDIUM SHADE
+03C3;sigma;GREEK SMALL LETTER SIGMA
+03C2;sigma1;GREEK SMALL LETTER FINAL SIGMA
+223C;similar;TILDE OPERATOR
+0036;six;DIGIT SIX
+2086;sixinferior;SUBSCRIPT SIX
+F736;sixoldstyle;OLDSTYLE DIGIT SIX
+2076;sixsuperior;SUPERSCRIPT SIX
+002F;slash;SOLIDUS
+263A;smileface;WHITE SMILING FACE
+0020;space;SPACE
+00A0;space;NO-BREAK SPACE;Duplicate
+2660;spade;BLACK SPADE SUIT
+F6F2;ssuperior;SUPERSCRIPT LATIN SMALL LETTER S
+00A3;sterling;POUND SIGN
+220B;suchthat;CONTAINS AS MEMBER
+2211;summation;N-ARY SUMMATION
+263C;sun;WHITE SUN WITH RAYS
+0074;t;LATIN SMALL LETTER T
+03C4;tau;GREEK SMALL LETTER TAU
+0167;tbar;LATIN SMALL LETTER T WITH STROKE
+0165;tcaron;LATIN SMALL LETTER T WITH CARON
+0163;tcommaaccent;LATIN SMALL LETTER T WITH CEDILLA
+021B;tcommaaccent;LATIN SMALL LETTER T WITH COMMA BELOW;Duplicate
+2234;therefore;THEREFORE
+03B8;theta;GREEK SMALL LETTER THETA
+03D1;theta1;GREEK THETA SYMBOL
+00FE;thorn;LATIN SMALL LETTER THORN
+0033;three;DIGIT THREE
+215C;threeeighths;VULGAR FRACTION THREE EIGHTHS
+2083;threeinferior;SUBSCRIPT THREE
+F733;threeoldstyle;OLDSTYLE DIGIT THREE
+00BE;threequarters;VULGAR FRACTION THREE QUARTERS
+F6DE;threequartersemdash;THREE QUARTERS EM DASH
+00B3;threesuperior;SUPERSCRIPT THREE
+02DC;tilde;SMALL TILDE
+0303;tildecomb;COMBINING TILDE
+0384;tonos;GREEK TONOS
+2122;trademark;TRADE MARK SIGN
+F8EA;trademarksans;TRADE MARK SIGN SANS SERIF
+F6DB;trademarkserif;TRADE MARK SIGN SERIF
+25BC;triagdn;BLACK DOWN-POINTING TRIANGLE
+25C4;triaglf;BLACK LEFT-POINTING POINTER
+25BA;triagrt;BLACK RIGHT-POINTING POINTER
+25B2;triagup;BLACK UP-POINTING TRIANGLE
+F6F3;tsuperior;SUPERSCRIPT LATIN SMALL LETTER T
+0032;two;DIGIT TWO
+2025;twodotenleader;TWO DOT LEADER
+2082;twoinferior;SUBSCRIPT TWO
+F732;twooldstyle;OLDSTYLE DIGIT TWO
+00B2;twosuperior;SUPERSCRIPT TWO
+2154;twothirds;VULGAR FRACTION TWO THIRDS
+0075;u;LATIN SMALL LETTER U
+00FA;uacute;LATIN SMALL LETTER U WITH ACUTE
+016D;ubreve;LATIN SMALL LETTER U WITH BREVE
+00FB;ucircumflex;LATIN SMALL LETTER U WITH CIRCUMFLEX
+00FC;udieresis;LATIN SMALL LETTER U WITH DIAERESIS
+00F9;ugrave;LATIN SMALL LETTER U WITH GRAVE
+01B0;uhorn;LATIN SMALL LETTER U WITH HORN
+0171;uhungarumlaut;LATIN SMALL LETTER U WITH DOUBLE ACUTE
+016B;umacron;LATIN SMALL LETTER U WITH MACRON
+005F;underscore;LOW LINE
+2017;underscoredbl;DOUBLE LOW LINE
+222A;union;UNION
+2200;universal;FOR ALL
+0173;uogonek;LATIN SMALL LETTER U WITH OGONEK
+2580;upblock;UPPER HALF BLOCK
+03C5;upsilon;GREEK SMALL LETTER UPSILON
+03CB;upsilondieresis;GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+03B0;upsilondieresistonos;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+03CD;upsilontonos;GREEK SMALL LETTER UPSILON WITH TONOS
+016F;uring;LATIN SMALL LETTER U WITH RING ABOVE
+0169;utilde;LATIN SMALL LETTER U WITH TILDE
+0076;v;LATIN SMALL LETTER V
+0077;w;LATIN SMALL LETTER W
+1E83;wacute;LATIN SMALL LETTER W WITH ACUTE
+0175;wcircumflex;LATIN SMALL LETTER W WITH CIRCUMFLEX
+1E85;wdieresis;LATIN SMALL LETTER W WITH DIAERESIS
+2118;weierstrass;SCRIPT CAPITAL P
+1E81;wgrave;LATIN SMALL LETTER W WITH GRAVE
+0078;x;LATIN SMALL LETTER X
+03BE;xi;GREEK SMALL LETTER XI
+0079;y;LATIN SMALL LETTER Y
+00FD;yacute;LATIN SMALL LETTER Y WITH ACUTE
+0177;ycircumflex;LATIN SMALL LETTER Y WITH CIRCUMFLEX
+00FF;ydieresis;LATIN SMALL LETTER Y WITH DIAERESIS
+00A5;yen;YEN SIGN
+1EF3;ygrave;LATIN SMALL LETTER Y WITH GRAVE
+007A;z;LATIN SMALL LETTER Z
+017A;zacute;LATIN SMALL LETTER Z WITH ACUTE
+017E;zcaron;LATIN SMALL LETTER Z WITH CARON
+017C;zdotaccent;LATIN SMALL LETTER Z WITH DOT ABOVE
+0030;zero;DIGIT ZERO
+2080;zeroinferior;SUBSCRIPT ZERO
+F730;zerooldstyle;OLDSTYLE DIGIT ZERO
+2070;zerosuperior;SUPERSCRIPT ZERO
+03B6;zeta;GREEK SMALL LETTER ZETA
+
+#
+# Name: ITC Zapf Dingbats encoding, Unicode values, and
+# glyph names
+# Table version: 1.0
+# Date: 17 July 1997
+#
+# Version of ITC Zapf Dingbats:
+#
+# ITC Zapf Dingbats (PostScript FontName: ZapfDingbats) contains 202
+# of the 379 glyphs in PCL Zapf Dingbats.
+#
+# Versions of ZapfDingbats previous to 002.000 do not encode 14 out of the
+# 202 available glyphs in their PostScript encoding (though these 14 glyphs
+# can be accessed on the Macintosh due to the FOND's reencoding of the
+# glyphs). ZapfDingbats version 002.000 encodes all of the 202 glyphs in its
+# PostScript encoding.
+#
+# Unicode encoding of ZapfDingbats:
+#
+# The 188 glyphs in the PostScript encoding of versions of ZapfDingbats
+# previous to 002.000 have regular Unicode values (160 in the Dingbats block
+# and 28 in other blocks). The remaining 14 glyphs have been assigned
+# Unicode values in the Corporate Use subarea, and coincide exactly with
+# Apple's MacOS Dingbats assignments in that area.
+#
+# Format: Four tab-separated columns:
+#
+# (1) The PostScript encoding. (2 uppercase hexadecimal digits)
+# 202 code points are defined.
+# Source: Adobe Systems.
+#
+# (2) The Unicode value. (4 uppercase hexadecimal digits)
+# The 14 glyphs at code points 0x80-8D have Corporate Use subarea
+# values.
+# Source: ftp://unicode.org/pub/UNIX/MAPPINGS/VENDORS/APPLE/DINGBAT.TXT
+# [as of 25 February 1997]
+#
+# (3) The glyph name. (upper- and lowercase letters, digits)
+# Source: Adobe Systems.
+#
+# (4) The Unicode 2.0 character name. (uppercase letters, hyphen, space)
+# Source: The Unicode Standard, Version 2.0
+#
+# The 14 Corporate Use subarea glyphs obviously don't have a
+# Unicode 2.0 character name. A descriptive name (in lowercase
+# letters) taken from the source for (2) above is used instead.
+#
+# The entries are in PostScript encoding order.
+#
+# Lines starting with "#" are comments; blank lines are to be ignored.
+#
+
+0020;space;SPACE
+2701;a1;UPPER BLADE SCISSORS
+2702;a2;BLACK SCISSORS
+2703;a202;LOWER BLADE SCISSORS
+2704;a3;WHITE SCISSORS
+260E;a4;BLACK TELEPHONE
+2706;a5;TELEPHONE LOCATION SIGN
+2707;a119;TAPE DRIVE
+2708;a118;AIRPLANE
+2709;a117;ENVELOPE
+261B;a11;BLACK RIGHT POINTING INDEX
+261E;a12;WHITE RIGHT POINTING INDEX
+270C;a13;VICTORY HAND
+270D;a14;WRITING HAND
+270E;a15;LOWER RIGHT PENCIL
+270F;a16;PENCIL
+2710;a105;UPPER RIGHT PENCIL
+2711;a17;WHITE NIB
+2712;a18;BLACK NIB
+2713;a19;CHECK MARK
+2714;a20;HEAVY CHECK MARK
+2715;a21;MULTIPLICATION X
+2716;a22;HEAVY MULTIPLICATION X
+2717;a23;BALLOT X
+2718;a24;HEAVY BALLOT X
+2719;a25;OUTLINED GREEK CROSS
+271A;a26;HEAVY GREEK CROSS
+271B;a27;OPEN CENTRE CROSS
+271C;a28;HEAVY OPEN CENTRE CROSS
+271D;a6;LATIN CROSS
+271E;a7;SHADOWED WHITE LATIN CROSS
+271F;a8;OUTLINED LATIN CROSS
+2720;a9;MALTESE CROSS
+2721;a10;STAR OF DAVID
+2722;a29;FOUR TEARDROP-SPOKED ASTERISK
+2723;a30;FOUR BALLOON-SPOKED ASTERISK
+2724;a31;HEAVY FOUR BALLOON-SPOKED ASTERISK
+2725;a32;FOUR CLUB-SPOKED ASTERISK
+2726;a33;BLACK FOUR POINTED STAR
+2727;a34;WHITE FOUR POINTED STAR
+2605;a35;BLACK STAR
+2729;a36;STRESS OUTLINED WHITE STAR
+272A;a37;CIRCLED WHITE STAR
+272B;a38;OPEN CENTRE BLACK STAR
+272C;a39;BLACK CENTRE WHITE STAR
+272D;a40;OUTLINED BLACK STAR
+272E;a41;HEAVY OUTLINED BLACK STAR
+272F;a42;PINWHEEL STAR
+2730;a43;SHADOWED WHITE STAR
+2731;a44;HEAVY ASTERISK
+2732;a45;OPEN CENTRE ASTERISK
+2733;a46;EIGHT SPOKED ASTERISK
+2734;a47;EIGHT POINTED BLACK STAR
+2735;a48;EIGHT POINTED PINWHEEL STAR
+2736;a49;SIX POINTED BLACK STAR
+2737;a50;EIGHT POINTED RECTILINEAR BLACK STAR
+2738;a51;HEAVY EIGHT POINTED RECTILINEAR BLACK STAR
+2739;a52;TWELVE POINTED BLACK STAR
+273A;a53;SIXTEEN POINTED ASTERISK
+273B;a54;TEARDROP-SPOKED ASTERISK
+273C;a55;OPEN CENTRE TEARDROP-SPOKED ASTERISK
+273D;a56;HEAVY TEARDROP-SPOKED ASTERISK
+273E;a57;SIX PETALLED BLACK AND WHITE FLORETTE
+273F;a58;BLACK FLORETTE
+2740;a59;WHITE FLORETTE
+2741;a60;EIGHT PETALLED OUTLINED BLACK FLORETTE
+2742;a61;CIRCLED OPEN CENTRE EIGHT POINTED STAR
+2743;a62;HEAVY TEARDROP-SPOKED PINWHEEL ASTERISK
+2744;a63;SNOWFLAKE
+2745;a64;TIGHT TRIFOLIATE SNOWFLAKE
+2746;a65;HEAVY CHEVRON SNOWFLAKE
+2747;a66;SPARKLE
+2748;a67;HEAVY SPARKLE
+2749;a68;BALLOON-SPOKED ASTERISK
+274A;a69;EIGHT TEARDROP-SPOKED PROPELLER ASTERISK
+274B;a70;HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK
+25CF;a71;BLACK CIRCLE
+274D;a72;SHADOWED WHITE CIRCLE
+25A0;a73;BLACK SQUARE
+274F;a74;LOWER RIGHT DROP-SHADOWED WHITE SQUARE
+2750;a203;UPPER RIGHT DROP-SHADOWED WHITE SQUARE
+2751;a75;LOWER RIGHT SHADOWED WHITE SQUARE
+2752;a204;UPPER RIGHT SHADOWED WHITE SQUARE
+25B2;a76;BLACK UP-POINTING TRIANGLE
+25BC;a77;BLACK DOWN-POINTING TRIANGLE
+25C6;a78;BLACK DIAMOND
+2756;a79;BLACK DIAMOND MINUS WHITE X
+25D7;a81;RIGHT HALF BLACK CIRCLE
+2758;a82;LIGHT VERTICAL BAR
+2759;a83;MEDIUM VERTICAL BAR
+275A;a84;HEAVY VERTICAL BAR
+275B;a97;HEAVY SINGLE TURNED COMMA QUOTATION MARK ORNAMENT
+275C;a98;HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT
+275D;a99;HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT
+275E;a100;HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT
+F8D7;a89;MEDIUM LEFT PARENTHESIS ORNAMENT
+F8D8;a90;MEDIUM RIGHT PARENTHESIS ORNAMENT
+F8D9;a93;MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT
+F8DA;a94;MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT
+F8DB;a91;MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT
+F8DC;a92;MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT
+F8DD;a205;HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT
+F8DE;a85;HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT
+F8DF;a206;HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT
+F8E0;a86;HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT
+F8E1;a87;LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT
+F8E2;a88;LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT
+F8E3;a95;MEDIUM LEFT CURLY BRACKET ORNAMENT
+F8E4;a96;MEDIUM RIGHT CURLY BRACKET ORNAMENT
+2761;a101;CURVED STEM PARAGRAPH SIGN ORNAMENT
+2762;a102;HEAVY EXCLAMATION MARK ORNAMENT
+2763;a103;HEAVY HEART EXCLAMATION MARK ORNAMENT
+2764;a104;HEAVY BLACK HEART
+2765;a106;ROTATED HEAVY BLACK HEART BULLET
+2766;a107;FLORAL HEART
+2767;a108;ROTATED FLORAL HEART BULLET
+2663;a112;BLACK CLUB SUIT
+2666;a111;BLACK DIAMOND SUIT
+2665;a110;BLACK HEART SUIT
+2660;a109;BLACK SPADE SUIT
+2460;a120;CIRCLED DIGIT ONE
+2461;a121;CIRCLED DIGIT TWO
+2462;a122;CIRCLED DIGIT THREE
+2463;a123;CIRCLED DIGIT FOUR
+2464;a124;CIRCLED DIGIT FIVE
+2465;a125;CIRCLED DIGIT SIX
+2466;a126;CIRCLED DIGIT SEVEN
+2467;a127;CIRCLED DIGIT EIGHT
+2468;a128;CIRCLED DIGIT NINE
+2469;a129;CIRCLED NUMBER TEN
+2776;a130;DINGBAT NEGATIVE CIRCLED DIGIT ONE
+2777;a131;DINGBAT NEGATIVE CIRCLED DIGIT TWO
+2778;a132;DINGBAT NEGATIVE CIRCLED DIGIT THREE
+2779;a133;DINGBAT NEGATIVE CIRCLED DIGIT FOUR
+277A;a134;DINGBAT NEGATIVE CIRCLED DIGIT FIVE
+277B;a135;DINGBAT NEGATIVE CIRCLED DIGIT SIX
+277C;a136;DINGBAT NEGATIVE CIRCLED DIGIT SEVEN
+277D;a137;DINGBAT NEGATIVE CIRCLED DIGIT EIGHT
+277E;a138;DINGBAT NEGATIVE CIRCLED DIGIT NINE
+277F;a139;DINGBAT NEGATIVE CIRCLED NUMBER TEN
+2780;a140;DINGBAT CIRCLED SANS-SERIF DIGIT ONE
+2781;a141;DINGBAT CIRCLED SANS-SERIF DIGIT TWO
+2782;a142;DINGBAT CIRCLED SANS-SERIF DIGIT THREE
+2783;a143;DINGBAT CIRCLED SANS-SERIF DIGIT FOUR
+2784;a144;DINGBAT CIRCLED SANS-SERIF DIGIT FIVE
+2785;a145;DINGBAT CIRCLED SANS-SERIF DIGIT SIX
+2786;a146;DINGBAT CIRCLED SANS-SERIF DIGIT SEVEN
+2787;a147;DINGBAT CIRCLED SANS-SERIF DIGIT EIGHT
+2788;a148;DINGBAT CIRCLED SANS-SERIF DIGIT NINE
+2789;a149;DINGBAT CIRCLED SANS-SERIF NUMBER TEN
+278A;a150;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ONE
+278B;a151;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT TWO
+278C;a152;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT THREE
+278D;a153;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FOUR
+278E;a154;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FIVE
+278F;a155;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SIX
+2790;a156;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SEVEN
+2791;a157;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT EIGHT
+2792;a158;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT NINE
+2793;a159;DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN
+2794;a160;HEAVY WIDE-HEADED RIGHTWARDS ARROW
+2192;a161;RIGHTWARDS ARROW
+2194;a163;LEFT RIGHT ARROW
+2195;a164;UP DOWN ARROW
+2798;a196;HEAVY SOUTH EAST ARROW
+2799;a165;HEAVY RIGHTWARDS ARROW
+279A;a192;HEAVY NORTH EAST ARROW
+279B;a166;DRAFTING POINT RIGHTWARDS ARROW
+279C;a167;HEAVY ROUND-TIPPED RIGHTWARDS ARROW
+279D;a168;TRIANGLE-HEADED RIGHTWARDS ARROW
+279E;a169;HEAVY TRIANGLE-HEADED RIGHTWARDS ARROW
+279F;a170;DASHED TRIANGLE-HEADED RIGHTWARDS ARROW
+27A0;a171;HEAVY DASHED TRIANGLE-HEADED RIGHTWARDS ARROW
+27A1;a172;BLACK RIGHTWARDS ARROW
+27A2;a173;THREE-D TOP-LIGHTED RIGHTWARDS ARROWHEAD
+27A3;a162;THREE-D BOTTOM-LIGHTED RIGHTWARDS ARROWHEAD
+27A4;a174;BLACK RIGHTWARDS ARROWHEAD
+27A5;a175;HEAVY BLACK CURVED DOWNWARDS AND RIGHTWARDS ARROW
+27A6;a176;HEAVY BLACK CURVED UPWARDS AND RIGHTWARDS ARROW
+27A7;a177;SQUAT BLACK RIGHTWARDS ARROW
+27A8;a178;HEAVY CONCAVE-POINTED BLACK RIGHTWARDS ARROW
+27A9;a179;RIGHT-SHADED WHITE RIGHTWARDS ARROW
+27AA;a193;LEFT-SHADED WHITE RIGHTWARDS ARROW
+27AB;a180;BACK-TILTED SHADOWED WHITE RIGHTWARDS ARROW
+27AC;a199;FRONT-TILTED SHADOWED WHITE RIGHTWARDS ARROW
+27AD;a181;HEAVY LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW
+27AE;a200;HEAVY UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW
+27AF;a182;NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW
+27B1;a201;NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW
+27B2;a183;CIRCLED HEAVY WHITE RIGHTWARDS ARROW
+27B3;a184;WHITE-FEATHERED RIGHTWARDS ARROW
+27B4;a197;BLACK-FEATHERED SOUTH EAST ARROW
+27B5;a185;BLACK-FEATHERED RIGHTWARDS ARROW
+27B6;a194;BLACK-FEATHERED NORTH EAST ARROW
+27B7;a198;HEAVY BLACK-FEATHERED SOUTH EAST ARROW
+27B8;a186;HEAVY BLACK-FEATHERED RIGHTWARDS ARROW
+27B9;a195;HEAVY BLACK-FEATHERED NORTH EAST ARROW
+27BA;a187;TEARDROP-BARBED RIGHTWARDS ARROW
+27BB;a188;HEAVY TEARDROP-SHANKED RIGHTWARDS ARROW
+27BC;a189;WEDGE-TAILED RIGHTWARDS ARROW
+27BD;a190;HEAVY WEDGE-TAILED RIGHTWARDS ARROW
+27BE;a191;OPEN-OUTLINED RIGHTWARDS ARROW
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..e8a7509
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,12 @@
+# Sources
+DIR_SOURCES := rufl_character_set_test.c rufl_decompose.c rufl_dump_state.c \
+ rufl_find.c rufl_init.c rufl_invalidate_cache.c \
+ rufl_metrics.c rufl_paint.c rufl_quit.c strfuncs.c
+
+SOURCES := $(SOURCES) $(BUILDDIR)/rufl_glyph_map.c
+
+$(BUILDDIR)/rufl_glyph_map.c: src/Glyphs
+ $(VQ)$(ECHO) "MKGLYPHS: $<"
+ $(Q)$(PERL) build/tools/makeglyphs < $< > $@
+
+include build/makefiles/Makefile.subdir
diff --git a/src/rufl_character_set_test.c b/src/rufl_character_set_test.c
new file mode 100644
index 0000000..45fbcaf
--- /dev/null
+++ b/src/rufl_character_set_test.c
@@ -0,0 +1,37 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2005 James Bursa <james@semichrome.net>
+ */
+
+#include "rufl_internal.h"
+
+
+/**
+ * Test if a character set contains a character.
+ *
+ * \param charset character set
+ * \param c character code
+ * \return true if present, false if absent
+ */
+
+bool rufl_character_set_test(struct rufl_character_set *charset,
+ unsigned int c)
+{
+ unsigned int block = c >> 8;
+ unsigned int byte = (c >> 3) & 31;
+ unsigned int bit = c & 7;
+
+ if (256 <= block)
+ return false;
+
+ if (charset->index[block] == BLOCK_EMPTY)
+ return false;
+ else if (charset->index[block] == BLOCK_FULL)
+ return true;
+ else {
+ unsigned char z = charset->block[charset->index[block]][byte];
+ return z & (1 << bit);
+ }
+}
diff --git a/src/rufl_decompose.c b/src/rufl_decompose.c
new file mode 100644
index 0000000..edf9748
--- /dev/null
+++ b/src/rufl_decompose.c
@@ -0,0 +1,161 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2005 John-Mark Bell <jmb202@ecs.soton.ac.uk>
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "oslib/font.h"
+
+#include "rufl_internal.h"
+
+/**
+ * Process a Draw path object
+ *
+ * \param path Pointer to path block
+ * \param funcs Struct of callback functions
+ * \param user User pointer to be passed to callbacks
+ * \return Pointer to word after this path, or NULL to terminate processing
+ */
+static int *process_path(int *path, struct rufl_decomp_funcs *funcs,
+ void *user)
+{
+ /* skip forward to style entry */
+ path += 9;
+
+ /* skip dash pattern */
+ if (path[0] & (1<<7))
+ path += path[2] + 2;
+
+ /* and move to start of path components */
+ path++;
+
+ while (path[0] != 0) {
+ switch (path[0]) {
+ case 2: /* Move to */
+ if (funcs->move_to((os_coord *)(path + 1), user))
+ return NULL;
+ path += 3;
+ break;
+ case 5: /* Close path */
+ path++;
+ break;
+ case 6: /* Cubic Bezier to */
+ if (funcs->cubic_to((os_coord *)(path + 1),
+ (os_coord *)(path + 3),
+ (os_coord *)(path + 5),
+ user))
+ return NULL;
+ path += 7;
+ break;
+ case 8: /* Line to */
+ if (funcs->line_to((os_coord *)(path + 1), user))
+ return NULL;
+ path += 3;
+ break;
+ default: /* Anything else is broken */
+ assert(0);
+ }
+ }
+
+ /* + 1 to account for tag 0 - end of path */
+ return path + 1;
+}
+
+rufl_code rufl_decompose_glyph(const char *font_family,
+ rufl_style font_style, unsigned int font_size,
+ const char *string, size_t len,
+ struct rufl_decomp_funcs *funcs, void *user)
+{
+ int *buf, *p, *ep;
+ int buf_size;
+ char *buf_end;
+ rufl_code err;
+
+ /* Get required buffer size */
+ rufl_fm_error = xfont_switch_output_to_buffer(
+ font_NO_OUTPUT | font_ADD_HINTS, (byte *)8, 0);
+ if (rufl_fm_error) {
+ LOG("xfont_switch_output_to_buffer: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+
+ err = rufl_paint(font_family, font_style, font_size, string, len,
+ 0, 0, rufl_BLEND_FONT);
+ if (err) {
+ /* reset font redirection - too bad if this fails */
+ xfont_switch_output_to_buffer(0, 0, 0);
+ return err;
+ }
+
+ rufl_fm_error = xfont_switch_output_to_buffer(0, NULL, &buf_end);
+ if (rufl_fm_error) {
+ LOG("xfont_switch_output_to_buffer: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ buf_size = buf_end - (char *)NULL;
+
+ /* Allocate and initialise buffer */
+ buf = malloc(buf_size);
+ if (!buf) {
+ LOG("Failed to allocate decompose buffer of size %i", buf_size);
+ return rufl_OUT_OF_MEMORY;
+ }
+ buf[0] = 0;
+ buf[1] = buf_size - 8;
+
+ /* Populate buffer */
+ rufl_fm_error = xfont_switch_output_to_buffer(
+ font_ADD_HINTS, (byte *)buf, 0);
+ if (rufl_fm_error) {
+ LOG("xfont_switch_output_to_buffer: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ free(buf);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+
+ err = rufl_paint(font_family, font_style, font_size, string, len,
+ 0, 0, rufl_BLEND_FONT);
+ if (err) {
+ /* reset font redirection - too bad if this fails */
+ xfont_switch_output_to_buffer(0, 0, 0);
+ free(buf);
+ return err;
+ }
+
+ rufl_fm_error = xfont_switch_output_to_buffer(0, 0, &buf_end);
+ if (rufl_fm_error) {
+ LOG("xfont_switch_output_to_buffer: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ free(buf);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ ep = (int *)(void *)buf_end;
+
+ /* Parse buffer, calling callbacks as required */
+ for (p = buf; p < ep;) {
+ if (p[0] != 2) {
+ LOG("Object type %d not known", p[0]);
+ break;
+ }
+
+ p = process_path(p, funcs, user);
+
+ /* Have the callbacks asked for us to stop? */
+ if (p == NULL)
+ break;
+ }
+
+ free(buf);
+
+ return rufl_OK;
+}
diff --git a/src/rufl_dump_state.c b/src/rufl_dump_state.c
new file mode 100644
index 0000000..06a1f22
--- /dev/null
+++ b/src/rufl_dump_state.c
@@ -0,0 +1,138 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2006 James Bursa <james@semichrome.net>
+ */
+
+#include <stdio.h>
+#include "rufl_internal.h"
+
+
+static void rufl_dump_character_set(struct rufl_character_set *charset);
+static void rufl_dump_unicode_map(struct rufl_unicode_map *umap);
+static void rufl_dump_substitution_table(void);
+
+
+/**
+ * Dump the internal library state to stdout.
+ */
+
+void rufl_dump_state(void)
+{
+ unsigned int i, j;
+
+ printf("rufl_font_list:\n");
+ for (i = 0; i != rufl_font_list_entries; i++) {
+ printf(" %u \"%s\"\n", i, rufl_font_list[i].identifier);
+ if (rufl_font_list[i].charset) {
+ printf(" ");
+ rufl_dump_character_set(rufl_font_list[i].charset);
+ printf("\n");
+ } else {
+ printf(" (no charset table)\n");
+ }
+ if (rufl_font_list[i].umap) {
+ for (j = 0; j < rufl_font_list[i].num_umaps; j++) {
+ struct rufl_unicode_map *map =
+ rufl_font_list[i].umap + j;
+
+ printf(" ");
+ rufl_dump_unicode_map(map);
+ printf("\n");
+ }
+ }
+ }
+
+ printf("rufl_family_list:\n");
+ for (i = 0; i != rufl_family_list_entries; i++) {
+ printf(" %u \"%s\"\n", i, rufl_family_list[i]);
+ for (j = 0; j != 9; j++) {
+ struct rufl_family_map_entry *e = &rufl_family_map[i];
+ printf(" %u ", j);
+ if (e->font[j][0] == NO_FONT)
+ printf("- ");
+ else
+ printf("\"%s\" ", rufl_font_list[e->font[j][0]].
+ identifier);
+ if (e->font[j][1] == NO_FONT)
+ printf("- ");
+ else
+ printf("\"%s\" ", rufl_font_list[e->font[j][1]].
+ identifier);
+ printf("\n");
+ }
+ }
+
+ printf("rufl_substitution_table:\n");
+ rufl_dump_substitution_table();
+}
+
+
+/**
+ * Dump a representation of a character set to stdout.
+ *
+ * \param charset character set to print
+ */
+
+void rufl_dump_character_set(struct rufl_character_set *charset)
+{
+ unsigned int u, t;
+
+ u = 0;
+ while (u != 0x10000) {
+ while (u != 0x10000 && !rufl_character_set_test(charset, u))
+ u++;
+ if (u != 0x10000) {
+ if (!rufl_character_set_test(charset, u + 1)) {
+ printf("%x ", u);
+ u++;
+ } else {
+ t = u;
+ while (rufl_character_set_test(charset, u))
+ u++;
+ printf("%x-%x ", t, u - 1);
+ }
+ }
+ }
+}
+
+
+/**
+ * Dump a representation of a unicode map to stdout.
+ *
+ * \param umap unicode map to print
+ */
+
+void rufl_dump_unicode_map(struct rufl_unicode_map *umap)
+{
+ unsigned int i;
+
+ if (umap->encoding)
+ printf("%s: ", umap->encoding);
+
+ for (i = 0; i != umap->entries; i++)
+ printf("%x:%x ", umap->map[i].u, umap->map[i].c);
+}
+
+
+/**
+ * Dump a representation of the substitution table to stdout.
+ */
+
+void rufl_dump_substitution_table(void)
+{
+ unsigned int font;
+ unsigned int u, t;
+
+ u = 0;
+ while (u != 0x10000) {
+ t = u;
+ font = rufl_substitution_table[t];
+ while (u != 0x10000 && font == rufl_substitution_table[u])
+ u++;
+ if (font != NOT_AVAILABLE)
+ printf(" %x-%x => %u \"%s\"\n", t, u - 1,
+ font, rufl_font_list[font].identifier);
+ }
+}
diff --git a/src/rufl_find.c b/src/rufl_find.c
new file mode 100644
index 0000000..a1c2785
--- /dev/null
+++ b/src/rufl_find.c
@@ -0,0 +1,201 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2005 James Bursa <james@semichrome.net>
+ * Copyright 2005 John-Mark Bell <jmb202@ecs.soton.ac.uk>
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+#include "rufl_internal.h"
+
+static int rufl_family_list_cmp(const void *keyval, const void *datum);
+static rufl_code rufl_place_in_cache(unsigned int font, unsigned int font_size,
+ const char *encoding, font_f f);
+
+/**
+ * Find a font family.
+ */
+rufl_code rufl_find_font_family(const char *font_family,
+ rufl_style font_style, unsigned int *font,
+ unsigned int *slanted, struct rufl_character_set **charset)
+{
+ const char **family;
+ unsigned int f;
+ unsigned int weight, slant, used_weight;
+ unsigned int search_direction;
+
+ family = bsearch(font_family, rufl_family_list,
+ rufl_family_list_entries,
+ sizeof rufl_family_list[0], rufl_family_list_cmp);
+ if (!family)
+ return rufl_FONT_NOT_FOUND;
+
+ weight = (font_style & 0xf) - 1;
+ assert(weight <= 8);
+ slant = font_style & rufl_SLANTED ? 1 : 0;
+
+ struct rufl_family_map_entry *e =
+ &rufl_family_map[family - rufl_family_list];
+ used_weight = weight;
+ if (weight <= 2)
+ search_direction = -1;
+ else
+ search_direction = +1;
+ while (1) {
+ if (e->font[used_weight][slant] != NO_FONT) {
+ /* the weight and slant is available */
+ f = e->font[used_weight][slant];
+ break;
+ }
+ if (e->font[used_weight][1 - slant] != NO_FONT) {
+ /* slanted, and non-slanted weight exists, or vv. */
+ f = e->font[used_weight][1 - slant];
+ break;
+ }
+ if (used_weight == 0) {
+ /* searched down without finding a weight: search up */
+ used_weight = weight + 1;
+ search_direction = +1;
+ } else if (used_weight == 8) {
+ /* searched up without finding a weight: search down */
+ used_weight = weight - 1;
+ search_direction = -1;
+ } else {
+ /* try the next weight in the current direction */
+ used_weight += search_direction;
+ }
+ }
+
+ if (font)
+ (*font) = f;
+
+ if (slanted)
+ (*slanted) = slant;
+
+ if (charset)
+ (*charset) = rufl_font_list[f].charset;
+
+ return rufl_OK;
+}
+
+
+/**
+ * Find a sized font, placing in the cache if necessary.
+ */
+rufl_code rufl_find_font(unsigned int font, unsigned int font_size,
+ const char *encoding, font_f *fhandle)
+{
+ font_f f;
+ char font_name[80];
+ unsigned int i;
+ rufl_code code;
+
+ assert(fhandle != NULL);
+
+ for (i = 0; i != rufl_CACHE_SIZE; i++) {
+ /* Comparing pointers for the encoding is fine, as the
+ * encoding string passed to us is either:
+ *
+ * a) NULL
+ * or b) statically allocated
+ * or c) resides in the font's umap, which is constant
+ * for the lifetime of the application.
+ */
+ if (rufl_cache[i].font == font &&
+ rufl_cache[i].size == font_size &&
+ rufl_cache[i].encoding == encoding)
+ break;
+ }
+ if (i != rufl_CACHE_SIZE) {
+ /* found in cache */
+ f = rufl_cache[i].f;
+ rufl_cache[i].last_used = rufl_cache_time++;
+ } else {
+ /* not found */
+ if (font == rufl_CACHE_CORPUS) {
+ if (encoding)
+ snprintf(font_name, sizeof font_name,
+ "Corpus.Medium\\E%s", encoding);
+ else
+ snprintf(font_name, sizeof font_name,
+ "Corpus.Medium");
+ } else {
+ if (encoding)
+ snprintf(font_name, sizeof font_name,
+ "%s\\E%s",
+ rufl_font_list[font].identifier,
+ encoding);
+ else
+ snprintf(font_name, sizeof font_name, "%s",
+ rufl_font_list[font].identifier);
+ }
+
+ rufl_fm_error = xfont_find_font(font_name,
+ font_size, font_size, 0, 0, &f, 0, 0);
+ if (rufl_fm_error) {
+ LOG("xfont_find_font: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ /* place in cache */
+ code = rufl_place_in_cache(font, font_size, encoding, f);
+ if (code != rufl_OK)
+ return code;
+ }
+
+ (*fhandle) = f;
+
+ return rufl_OK;
+}
+
+
+int rufl_family_list_cmp(const void *keyval, const void *datum)
+{
+ const char *key = keyval;
+ const char * const *entry = datum;
+ return strcasecmp(key, *entry);
+}
+
+
+/**
+ * Place a font into the recent-use cache, making space if necessary.
+ */
+
+rufl_code rufl_place_in_cache(unsigned int font, unsigned int font_size,
+ const char *encoding, font_f f)
+{
+ unsigned int i;
+ unsigned int max_age = 0;
+ unsigned int evict = 0;
+
+ for (i = 0; i != rufl_CACHE_SIZE; i++) {
+ if (rufl_cache[i].font == rufl_CACHE_NONE) {
+ evict = i;
+ break;
+ } else if (max_age < rufl_cache_time -
+ rufl_cache[i].last_used) {
+ max_age = rufl_cache_time -
+ rufl_cache[i].last_used;
+ evict = i;
+ }
+ }
+ if (rufl_cache[evict].font != rufl_CACHE_NONE) {
+ rufl_fm_error = xfont_lose_font(rufl_cache[evict].f);
+ if (rufl_fm_error)
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ rufl_cache[evict].font = font;
+ rufl_cache[evict].size = font_size;
+ rufl_cache[evict].encoding = encoding;
+ rufl_cache[evict].f = f;
+ rufl_cache[evict].last_used = rufl_cache_time++;
+
+ return rufl_OK;
+}
diff --git a/src/rufl_init.c b/src/rufl_init.c
new file mode 100644
index 0000000..533955a
--- /dev/null
+++ b/src/rufl_init.c
@@ -0,0 +1,1678 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2006 James Bursa <james@semichrome.net>
+ */
+
+#define _GNU_SOURCE /* for strndup */
+#include <assert.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <search.h>
+#include <oslib/font.h>
+#include <oslib/hourglass.h>
+#include <oslib/os.h>
+#include <oslib/osfscontrol.h>
+#include <oslib/taskwindow.h>
+#include <oslib/wimp.h>
+#include <oslib/wimpreadsysinfo.h>
+#include "rufl_internal.h"
+
+
+struct rufl_font_list_entry *rufl_font_list = 0;
+size_t rufl_font_list_entries = 0;
+const char **rufl_family_list = 0;
+unsigned int rufl_family_list_entries = 0;
+struct rufl_family_map_entry *rufl_family_map = 0;
+os_error *rufl_fm_error = 0;
+void *rufl_family_menu = 0;
+unsigned short *rufl_substitution_table = 0;
+struct rufl_cache_entry rufl_cache[rufl_CACHE_SIZE];
+int rufl_cache_time = 0;
+bool rufl_old_font_manager = false;
+wimp_w rufl_status_w = 0;
+char rufl_status_buffer[80];
+
+/** An entry in rufl_weight_table. */
+struct rufl_weight_table_entry {
+ const char *name;
+ unsigned int weight;
+};
+
+/** Map from font name part to font weight. Must be case-insensitive sorted by
+ * name. */
+const struct rufl_weight_table_entry rufl_weight_table[] = {
+ { "Black", 9 },
+ { "Bold", 7 },
+ { "Book", 3 },
+ { "Demi", 6 },
+ { "DemiBold", 6 },
+ { "Extra", 8 },
+ { "ExtraBlack", 9 },
+ { "ExtraBold", 8 },
+ { "ExtraLight", 1 },
+ { "Heavy", 8 },
+ { "Light", 2 },
+ { "Medium", 5 },
+ { "Regular", 4 },
+ { "Semi", 6 },
+ { "SemiBold", 6 },
+ { "SemiLight", 3 },
+ { "UltraBlack", 9 },
+ { "UltraBold", 9 },
+};
+
+
+static rufl_code rufl_init_font_list(void);
+static rufl_code rufl_init_add_font(const char *identifier,
+ const char *local_name);
+static int rufl_weight_table_cmp(const void *keyval, const void *datum);
+static rufl_code rufl_init_scan_font(unsigned int font);
+static rufl_code rufl_init_scan_font_no_enumerate(unsigned int font);
+static bool rufl_is_space(unsigned int u);
+static rufl_code rufl_init_scan_font_old(unsigned int font_index);
+static rufl_code rufl_init_scan_font_in_encoding(const char *font_name,
+ const char *encoding, struct rufl_character_set *charset,
+ struct rufl_unicode_map *umap, unsigned int *last);
+static rufl_code rufl_init_read_encoding(font_f font,
+ struct rufl_unicode_map *umap);
+static int rufl_glyph_map_cmp(const void *keyval, const void *datum);
+static int rufl_unicode_map_cmp(const void *z1, const void *z2);
+static rufl_code rufl_init_substitution_table(void);
+static rufl_code rufl_save_cache(void);
+static rufl_code rufl_load_cache(void);
+static int rufl_font_list_cmp(const void *keyval, const void *datum);
+static rufl_code rufl_init_family_menu(void);
+static void rufl_init_status_open(void);
+static void rufl_init_status(const char *status, float progress);
+static void rufl_init_status_close(void);
+
+
+/**
+ * Initialise RUfl.
+ *
+ * All available fonts are scanned. May take some time.
+ */
+
+rufl_code rufl_init(void)
+{
+ bool rufl_broken_font_enumerate_characters = false;
+ unsigned int changes = 0;
+ unsigned int i;
+ int fm_version;
+ rufl_code code;
+ font_f font;
+ os_colour old_sand, old_glass;
+
+ if (rufl_font_list_entries)
+ /* already initialized */
+ return rufl_OK;
+
+ xhourglass_on();
+
+ rufl_init_status_open();
+
+ /* determine if the font manager supports Unicode */
+ rufl_fm_error = xfont_find_font("Homerton.Medium\\EUTF8", 160, 160,
+ 0, 0, &font, 0, 0);
+ if (rufl_fm_error) {
+ if (rufl_fm_error->errnum == error_FONT_ENCODING_NOT_FOUND) {
+ rufl_old_font_manager = true;
+ } else {
+ LOG("xfont_find_font: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ rufl_quit();
+ xhourglass_off();
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ } else {
+ /* New font manager; see if character enumeration works */
+ int next;
+
+ rufl_fm_error = xfont_enumerate_characters(font, 0,
+ &next, NULL);
+ /* Broken if SWI fails or it doesn't return 0x20 as the first
+ * character to process. Font Managers earlier than 3.64 have
+ * a bug that means they do not return the first available
+ * range of characters in a font. We detect this by asking
+ * for the first character in Homerton.Medium, which we know
+ * is 0x20 (i.e. space). If the value returned is not this,
+ * then we assume the font manager is broken and fall back to
+ * the old code which is significantly slower.
+ */
+ if (rufl_fm_error || next != 0x20)
+ rufl_broken_font_enumerate_characters = true;
+
+ xfont_lose_font(font);
+ }
+ LOG("%s font manager", rufl_old_font_manager ? "old" : "new");
+
+ /* test if the font manager supports background blending */
+ rufl_fm_error = xfont_cache_addr(&fm_version, 0, 0);
+ if (rufl_fm_error)
+ return rufl_FONT_MANAGER_ERROR;
+ if (fm_version >= 335)
+ rufl_can_background_blend = true;
+
+ code = rufl_init_font_list();
+ if (code != rufl_OK) {
+ rufl_quit();
+ xhourglass_off();
+ return code;
+ }
+ LOG("%zu faces, %u families", rufl_font_list_entries,
+ rufl_family_list_entries);
+
+ code = rufl_load_cache();
+ if (code != rufl_OK) {
+ LOG("rufl_load_cache: 0x%x", code);
+ rufl_quit();
+ xhourglass_off();
+ return code;
+ }
+
+ xhourglass_leds(1, 0, 0);
+ for (i = 0; i != rufl_font_list_entries; i++) {
+ if (rufl_font_list[i].charset) {
+ /* character set loaded from cache */
+ continue;
+ }
+ LOG("scanning %u \"%s\"", i, rufl_font_list[i].identifier);
+ xhourglass_percentage(100 * i / rufl_font_list_entries);
+ rufl_init_status(rufl_font_list[i].identifier,
+ (float) i / rufl_font_list_entries);
+ if (rufl_old_font_manager)
+ code = rufl_init_scan_font_old(i);
+ else if (rufl_broken_font_enumerate_characters)
+ code = rufl_init_scan_font_no_enumerate(i);
+ else
+ code = rufl_init_scan_font(i);
+ if (code != rufl_OK) {
+ LOG("rufl_init_scan_font: 0x%x", code);
+ rufl_quit();
+ xhourglass_off();
+ return code;
+ }
+ changes++;
+ }
+
+ xhourglass_leds(2, 0, 0);
+ xhourglass_colours(0x0000ff, 0x00ffff, &old_sand, &old_glass);
+ code = rufl_init_substitution_table();
+ if (code != rufl_OK) {
+ LOG("rufl_init_substitution_table: 0x%x", code);
+ rufl_quit();
+ xhourglass_off();
+ return code;
+ }
+ xhourglass_colours(old_sand, old_glass, 0, 0);
+
+ if (changes) {
+ LOG("%u new charsets", changes);
+ xhourglass_leds(3, 0, 0);
+ code = rufl_save_cache();
+ if (code != rufl_OK) {
+ LOG("rufl_save_cache: 0x%x", code);
+ rufl_quit();
+ xhourglass_off();
+ return code;
+ }
+ }
+
+ for (i = 0; i != rufl_CACHE_SIZE; i++)
+ rufl_cache[i].font = rufl_CACHE_NONE;
+
+ code = rufl_init_family_menu();
+ if (code != rufl_OK) {
+ LOG("rufl_init_substitution_table: 0x%x", code);
+ rufl_quit();
+ xhourglass_off();
+ return code;
+ }
+
+ rufl_init_status_close();
+
+ xhourglass_off();
+
+ return rufl_OK;
+}
+
+
+/**
+ * Build list of font in rufl_font_list and list of font families
+ * in rufl_family_list.
+ */
+
+rufl_code rufl_init_font_list(void)
+{
+ rufl_code code;
+ font_list_context context = 0;
+ char identifier[80], local_name[80];
+
+ while (context != -1) {
+ /* read identifier */
+ rufl_fm_error = xfont_list_fonts((byte *)identifier,
+ font_RETURN_FONT_NAME |
+ font_RETURN_LOCAL_FONT_NAME |
+ context,
+ sizeof identifier,
+ (byte *)local_name, sizeof local_name, 0,
+ &context, 0, 0);
+ if (rufl_fm_error) {
+ LOG("xfont_list_fonts: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ if (context == -1)
+ break;
+
+ code = rufl_init_add_font(identifier, local_name);
+ if (code != rufl_OK)
+ return code;
+ }
+
+ return rufl_OK;
+}
+
+
+rufl_code rufl_init_add_font(const char *identifier, const char *local_name)
+{
+ int size;
+ struct rufl_font_list_entry *font_list;
+ char *dot;
+ const char **family_list;
+ const char *family, *part;
+ unsigned int weight = 0;
+ unsigned int slant = 0;
+ bool special = false;
+ struct rufl_family_map_entry *family_map;
+ unsigned int i;
+ struct rufl_weight_table_entry *entry;
+
+ /* Check that:
+ * a) it's not a RiScript generated font
+ * b) it's not a TeX font */
+
+ /* Read required buffer size */
+ rufl_fm_error = xosfscontrol_canonicalise_path(identifier, 0,
+ "Font$Path", 0, 0, &size);
+ if (rufl_fm_error) {
+ LOG("xosfscontrol_canonicalise_path(\"%s\", ...): 0x%x: %s",
+ identifier,
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_OK;
+ }
+ /* size is -(space required - 1) so negate and add 1 */
+ size = -size + 1;
+
+ /* Create buffer and canonicalise path */
+ char fullpath[size];
+ rufl_fm_error = xosfscontrol_canonicalise_path(identifier,
+ fullpath, "Font$Path", 0, size, 0);
+ if (rufl_fm_error) {
+ LOG("xosfscontrol_canonicalise_path(\"%s\", ...): 0x%x: %s",
+ identifier,
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_OK;
+ }
+
+ /* LOG("%s", fullpath); */
+
+ if (strstr(fullpath, "RiScript") || strstr(fullpath, "!TeXFonts"))
+ /* Ignore this font */
+ return rufl_OK;
+
+ /* add identifier to rufl_font_list */
+ font_list = realloc(rufl_font_list, sizeof rufl_font_list[0] *
+ (rufl_font_list_entries + 1));
+ if (!font_list)
+ return rufl_OUT_OF_MEMORY;
+ rufl_font_list = font_list;
+ rufl_font_list[rufl_font_list_entries].identifier = strdup(identifier);
+ if (!rufl_font_list[rufl_font_list_entries].identifier)
+ return rufl_OUT_OF_MEMORY;
+ rufl_font_list[rufl_font_list_entries].charset = 0;
+ rufl_font_list[rufl_font_list_entries].umap = 0;
+ rufl_font_list_entries++;
+
+ /* determine family, weight, and slant */
+ dot = strchr(local_name, '.');
+ family = local_name;
+ if (dot)
+ *dot = 0;
+ while (dot) {
+ part = dot + 1;
+ dot = strchr(part, '.');
+ if (dot)
+ *dot = 0;
+ if (strcasecmp(part, "Italic") == 0 ||
+ strcasecmp(part, "Oblique") == 0) {
+ slant = 1;
+ continue;
+ }
+ entry = bsearch(part, rufl_weight_table,
+ sizeof rufl_weight_table /
+ sizeof rufl_weight_table[0],
+ sizeof rufl_weight_table[0],
+ rufl_weight_table_cmp);
+ if (entry)
+ weight = entry->weight;
+ else
+ special = true; /* unknown weight or style */
+ }
+ if (!weight)
+ weight = 4;
+ weight--;
+
+ if (rufl_family_list_entries == 0 || strcasecmp(family,
+ rufl_family_list[rufl_family_list_entries - 1]) != 0) {
+ /* new family */
+ family_list = realloc(rufl_family_list,
+ sizeof rufl_family_list[0] *
+ (rufl_family_list_entries + 1));
+ if (!family_list)
+ return rufl_OUT_OF_MEMORY;
+ rufl_family_list = family_list;
+
+ family_map = realloc(rufl_family_map,
+ sizeof rufl_family_map[0] *
+ (rufl_family_list_entries + 1));
+ if (!family_map)
+ return rufl_OUT_OF_MEMORY;
+ rufl_family_map = family_map;
+
+ family = strdup(family);
+ if (!family)
+ return rufl_OUT_OF_MEMORY;
+
+ rufl_family_list[rufl_family_list_entries] = family;
+ for (i = 0; i != 9; i++)
+ rufl_family_map[rufl_family_list_entries].font[i][0] =
+ rufl_family_map[rufl_family_list_entries].font[i][1] =
+ NO_FONT;
+ rufl_family_list_entries++;
+ }
+
+ struct rufl_family_map_entry *e =
+ &rufl_family_map[rufl_family_list_entries - 1];
+ /* prefer fonts with no unknown weight or style in their name, so that,
+ * for example, Alps.Light takes priority over Alps.Cond.Light */
+ if (e->font[weight][slant] == NO_FONT || !special)
+ e->font[weight][slant] = rufl_font_list_entries - 1;
+
+ rufl_font_list[rufl_font_list_entries - 1].family =
+ rufl_family_list_entries - 1;
+ rufl_font_list[rufl_font_list_entries - 1].weight = weight;
+ rufl_font_list[rufl_font_list_entries - 1].slant = slant;
+
+ return rufl_OK;
+}
+
+
+int rufl_weight_table_cmp(const void *keyval, const void *datum)
+{
+ const char *key = keyval;
+ const struct rufl_weight_table_entry *entry = datum;
+ return strcasecmp(key, entry->name);
+}
+
+/**
+ * Scan a font for available characters.
+ */
+
+rufl_code rufl_init_scan_font(unsigned int font_index)
+{
+ char font_name[80];
+ int x_out, y_out;
+ unsigned int byte, bit;
+ unsigned int last_used = 0;
+ unsigned int string[2] = { 0, 0 };
+ unsigned int u, next;
+ struct rufl_character_set *charset;
+ struct rufl_character_set *charset2;
+ font_f font;
+ font_scan_block block = { { 0, 0 }, { 0, 0 }, -1, { 0, 0, 0, 0 } };
+
+ /*LOG("font %u \"%s\"", font_index,
+ rufl_font_list[font_index].identifier);*/
+
+ charset = calloc(1, sizeof *charset);
+ if (!charset)
+ return rufl_OUT_OF_MEMORY;
+ for (u = 0; u != 256; u++)
+ charset->index[u] = BLOCK_EMPTY;
+
+ snprintf(font_name, sizeof font_name, "%s\\EUTF8",
+ rufl_font_list[font_index].identifier);
+
+ rufl_fm_error = xfont_find_font(font_name, 160, 160, 0, 0, &font, 0, 0);
+ if (rufl_fm_error) {
+ LOG("xfont_find_font(\"%s\"): 0x%x: %s", font_name,
+ rufl_fm_error->errnum, rufl_fm_error->errmess);
+ free(charset);
+ return rufl_OK;
+ }
+
+ /* Scan through mapped characters */
+ for (u = 0; u != (unsigned int) -1; u = next) {
+ unsigned int internal;
+
+ rufl_fm_error = xfont_enumerate_characters(font, u,
+ (int *) &next, (int *) &internal);
+ if (rufl_fm_error) {
+ LOG("xfont_enumerate_characters: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ xfont_lose_font(font);
+ free(charset);
+ return rufl_OK;
+ }
+
+ /* Skip DELETE and C0/C1 controls */
+ if (u < 0x0020 || (0x007f <= u && u <= 0x009f))
+ continue;
+
+ /* Skip astral characters */
+ if (u > 0xffff)
+ continue;
+
+ /* Skip unmapped characters */
+ if (internal == (unsigned int) -1)
+ continue;
+
+ if (u % 0x200 == 0)
+ rufl_init_status(0, 0);
+
+ /* Character is mapped, let's see if it's really there */
+ string[0] = u;
+ rufl_fm_error = xfont_scan_string(font, (char *) string,
+ font_RETURN_BBOX | font_GIVEN32_BIT |
+ font_GIVEN_FONT | font_GIVEN_LENGTH |
+ font_GIVEN_BLOCK,
+ 0x7fffffff, 0x7fffffff,
+ &block, 0, 4,
+ 0, &x_out, &y_out, 0);
+ if (rufl_fm_error)
+ break;
+
+ if (block.bbox.x0 == 0x20000000) {
+ /* absent (no definition) */
+ } else if (x_out == 0 && y_out == 0 &&
+ block.bbox.x0 == 0 && block.bbox.y0 == 0 &&
+ block.bbox.x1 == 0 && block.bbox.y1 == 0) {
+ /* absent (empty) */
+ } else if (block.bbox.x0 == 0 && block.bbox.y0 == 0 &&
+ block.bbox.x1 == 0 && block.bbox.y1 == 0 &&
+ !rufl_is_space(u)) {
+ /* absent (space but not a space character - some
+ * fonts do this) */
+ } else {
+ /* present */
+ if (charset->index[u >> 8] == BLOCK_EMPTY) {
+ charset->index[u >> 8] = last_used;
+ last_used++;
+ if (last_used == 254)
+ /* too many characters */
+ break;
+ }
+
+ byte = (u >> 3) & 31;
+ bit = u & 7;
+ charset->block[charset->index[u >> 8]][byte] |=
+ 1 << bit;
+ }
+ }
+
+ xfont_lose_font(font);
+
+ if (rufl_fm_error) {
+ free(charset);
+ LOG("xfont_scan_string: 0x%x: %s",
+ rufl_fm_error->errnum, rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+
+ /* Determine which blocks are full, and mark them as such */
+ for (u = 0; u != 256; u++) {
+ if (charset->index[u] == BLOCK_EMPTY)
+ continue;
+
+ bit = 0xff;
+
+ for (byte = 0; byte != 32; byte++)
+ bit &= charset->block[u][byte];
+
+ if (bit == 0xff) {
+ /* Block is full */
+ charset->index[u] = BLOCK_FULL;
+
+ for (byte = 0; byte != 32; byte++)
+ charset->block[u][byte] = 0;
+ }
+ }
+
+ /* shrink-wrap */
+ charset->size = offsetof(struct rufl_character_set, block) +
+ 32 * last_used;
+ charset2 = realloc(charset, charset->size);
+ if (!charset2) {
+ free(charset);
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ rufl_font_list[font_index].charset = charset;
+
+ return rufl_OK;
+}
+
+/**
+ * Scan a font for available characters (version without character enumeration)
+ */
+
+rufl_code rufl_init_scan_font_no_enumerate(unsigned int font_index)
+{
+ char font_name[80];
+ int x_out, y_out;
+ unsigned int byte, bit;
+ unsigned int block_count = 0;
+ unsigned int last_used = 0;
+ unsigned int string[2] = { 0, 0 };
+ unsigned int u;
+ struct rufl_character_set *charset;
+ struct rufl_character_set *charset2;
+ font_f font;
+ font_scan_block block = { { 0, 0 }, { 0, 0 }, -1, { 0, 0, 0, 0 } };
+
+ /*LOG("font %u \"%s\"", font_index,
+ rufl_font_list[font_index].identifier);*/
+
+ charset = calloc(1, sizeof *charset);
+ if (!charset)
+ return rufl_OUT_OF_MEMORY;
+
+ snprintf(font_name, sizeof font_name, "%s\\EUTF8",
+ rufl_font_list[font_index].identifier);
+
+ rufl_fm_error = xfont_find_font(font_name, 160, 160, 0, 0, &font, 0, 0);
+ if (rufl_fm_error) {
+ LOG("xfont_find_font(\"%s\"): 0x%x: %s", font_name,
+ rufl_fm_error->errnum, rufl_fm_error->errmess);
+ free(charset);
+ return rufl_OK;
+ }
+
+ /* scan through all characters */
+ for (u = 0x0020; u != 0x10000; u++) {
+ if (u == 0x007f) {
+ /* skip DELETE and C1 controls */
+ u = 0x009f;
+ continue;
+ }
+
+ if (u % 0x200 == 0)
+ rufl_init_status(0, 0);
+
+ string[0] = u;
+ rufl_fm_error = xfont_scan_string(font, (char *) string,
+ font_RETURN_BBOX | font_GIVEN32_BIT |
+ font_GIVEN_FONT | font_GIVEN_LENGTH |
+ font_GIVEN_BLOCK,
+ 0x7fffffff, 0x7fffffff,
+ &block, 0, 4,
+ 0, &x_out, &y_out, 0);
+ if (rufl_fm_error)
+ break;
+
+ if (block.bbox.x0 == 0x20000000) {
+ /* absent (no definition) */
+ } else if (x_out == 0 && y_out == 0 &&
+ block.bbox.x0 == 0 && block.bbox.y0 == 0 &&
+ block.bbox.x1 == 0 && block.bbox.y1 == 0) {
+ /* absent (empty) */
+ } else if (block.bbox.x0 == 0 && block.bbox.y0 == 0 &&
+ block.bbox.x1 == 0 && block.bbox.y1 == 0 &&
+ !rufl_is_space(u)) {
+ /* absent (space but not a space character - some
+ * fonts do this) */
+ } else {
+ /* present */
+ byte = (u >> 3) & 31;
+ bit = u & 7;
+ charset->block[last_used][byte] |= 1 << bit;
+
+ block_count++;
+ }
+
+ if ((u + 1) % 256 == 0) {
+ /* end of block */
+ if (block_count == 0)
+ charset->index[u >> 8] = BLOCK_EMPTY;
+ else if (block_count == 256) {
+ charset->index[u >> 8] = BLOCK_FULL;
+ for (byte = 0; byte != 32; byte++)
+ charset->block[last_used][byte] = 0;
+ } else {
+ charset->index[u >> 8] = last_used;
+ last_used++;
+ if (last_used == 254)
+ /* too many characters */
+ break;
+ }
+ block_count = 0;
+ }
+ }
+
+ xfont_lose_font(font);
+
+ if (rufl_fm_error) {
+ free(charset);
+ LOG("xfont_scan_string: 0x%x: %s",
+ rufl_fm_error->errnum, rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+
+ /* shrink-wrap */
+ charset->size = offsetof(struct rufl_character_set, block) +
+ 32 * last_used;
+ charset2 = realloc(charset, charset->size);
+ if (!charset2) {
+ free(charset);
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ rufl_font_list[font_index].charset = charset;
+
+ return rufl_OK;
+}
+
+/**
+ * A character is one of the Unicode space characters.
+ */
+
+bool rufl_is_space(unsigned int u)
+{
+ return u == 0x0020 || u == 0x00a0 ||
+ (0x2000 <= u && u <= 0x200b) ||
+ u == 0x202f || u == 0x3000;
+}
+
+
+/**
+ * Scan a font for available characters (old font manager version).
+ */
+
+rufl_code rufl_init_scan_font_old(unsigned int font_index)
+{
+ const char *font_name = rufl_font_list[font_index].identifier;
+ struct rufl_character_set *charset;
+ struct rufl_character_set *charset2;
+ struct rufl_unicode_map *umap = NULL;
+ unsigned int num_umaps = 0;
+ unsigned int i;
+ unsigned int last_used = 0;
+ rufl_code code;
+ font_list_context context = 0;
+ char encoding[80];
+
+ /*LOG("font %u \"%s\"", font_index, font_name);*/
+
+ charset = calloc(1, sizeof *charset);
+ if (!charset)
+ return rufl_OUT_OF_MEMORY;
+ for (i = 0; i != 256; i++)
+ charset->index[i] = BLOCK_EMPTY;
+
+ /* Firstly, search through available encodings (Symbol fonts fail) */
+ while (context != -1) {
+ struct rufl_unicode_map *temp;
+
+ rufl_fm_error = xfont_list_fonts((byte *) encoding,
+ font_RETURN_FONT_NAME |
+ 0x400000 /* Return encoding name, instead */ |
+ context,
+ sizeof(encoding), NULL, 0, NULL,
+ &context, NULL, NULL);
+ if (rufl_fm_error) {
+ LOG("xfont_list_fonts: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ free(charset);
+ for (i = 0; i < num_umaps; i++)
+ free((umap + i)->encoding);
+ free(umap);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ if (context == -1)
+ break;
+
+ temp = realloc(umap, (num_umaps + 1) * sizeof *umap);
+ if (!temp) {
+ free(charset);
+ for (i = 0; i < num_umaps; i++)
+ free((umap + i)->encoding);
+ free(umap);
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ memset(temp + num_umaps, 0, sizeof *umap);
+
+ umap = temp;
+ num_umaps++;
+
+ code = rufl_init_scan_font_in_encoding(font_name, encoding,
+ charset, umap + (num_umaps - 1), &last_used);
+ if (code != rufl_OK) {
+ /* Not finding the font isn't fatal */
+ if (code != rufl_FONT_MANAGER_ERROR ||
+ (rufl_fm_error->errnum !=
+ error_FONT_NOT_FOUND &&
+ rufl_fm_error->errnum !=
+ error_FILE_NOT_FOUND)) {
+ free(charset);
+ for (i = 0; i < num_umaps; i++)
+ free((umap + i)->encoding);
+ free(umap);
+ return code;
+ }
+
+ /* Ensure we reuse the currently allocated umap */
+ num_umaps--;
+ } else {
+ /* If this mapping is identical to an existing one,
+ * then we can discard it */
+ for (i = 0; i != num_umaps - 1; i++) {
+ const struct rufl_unicode_map *a = (umap + i);
+ const struct rufl_unicode_map *b =
+ (umap + num_umaps - 1);
+
+ if (a->entries == b->entries &&
+ memcmp(a->map, b->map,
+ sizeof a->map) == 0) {
+ /* Found identical map; discard */
+ num_umaps--;
+ break;
+ }
+ }
+ }
+ }
+
+ if (num_umaps == 0) {
+ /* This is a symbol font and can only be used
+ * without an encoding */
+ struct rufl_unicode_map *temp;
+
+ temp = realloc(umap, (num_umaps + 1) * sizeof *umap);
+ if (!temp) {
+ free(charset);
+ free(umap);
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ memset(temp + num_umaps, 0, sizeof *umap);
+
+ umap = temp;
+ num_umaps++;
+
+ code = rufl_init_scan_font_in_encoding(font_name, NULL,
+ charset, umap, &last_used);
+ if (code != rufl_OK) {
+ /* Not finding the font isn't fatal */
+ if (code != rufl_FONT_MANAGER_ERROR ||
+ (rufl_fm_error->errnum !=
+ error_FONT_NOT_FOUND &&
+ rufl_fm_error->errnum !=
+ error_FILE_NOT_FOUND)) {
+ free(charset);
+ for (i = 0; i < num_umaps; i++)
+ free((umap + i)->encoding);
+ free(umap);
+ return code;
+ }
+
+ num_umaps--;
+ }
+ }
+
+ /* shrink-wrap */
+ charset->size = offsetof(struct rufl_character_set, block) +
+ 32 * last_used;
+ charset2 = realloc(charset, charset->size);
+ if (!charset2) {
+ for (i = 0; i < num_umaps; i++)
+ free((umap + i)->encoding);
+ free(umap);
+ free(charset);
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ rufl_font_list[font_index].charset = charset;
+ rufl_font_list[font_index].umap = umap;
+ rufl_font_list[font_index].num_umaps = num_umaps;
+
+ return rufl_OK;
+}
+
+/**
+ * Helper function for rufl_init_scan_font_old.
+ * Scans the given font using the given font encoding (or none, if NULL)
+ */
+
+rufl_code rufl_init_scan_font_in_encoding(const char *font_name,
+ const char *encoding, struct rufl_character_set *charset,
+ struct rufl_unicode_map *umap, unsigned int *last)
+{
+ char string[2] = { 0, 0 };
+ int x_out, y_out;
+ unsigned int byte, bit;
+ unsigned int i;
+ unsigned int last_used = *last;
+ unsigned int u;
+ rufl_code code;
+ font_f font;
+ font_scan_block block = { { 0, 0 }, { 0, 0 }, -1, { 0, 0, 0, 0 } };
+ char buf[80];
+
+ if (encoding)
+ snprintf(buf, sizeof buf, "%s\\E%s", font_name, encoding);
+ else
+ snprintf(buf, sizeof buf, "%s", font_name);
+
+ rufl_fm_error = xfont_find_font(buf, 160, 160, 0, 0, &font, 0, 0);
+ if (rufl_fm_error) {
+ LOG("xfont_find_font(\"%s\"): 0x%x: %s", buf,
+ rufl_fm_error->errnum, rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+
+ code = rufl_init_read_encoding(font, umap);
+ if (code != rufl_OK) {
+ xfont_lose_font(font);
+ return code;
+ }
+
+ for (i = 0; i != umap->entries; i++) {
+ u = umap->map[i].u;
+ string[0] = umap->map[i].c;
+ rufl_fm_error = xfont_scan_string(font, (char *) string,
+ font_RETURN_BBOX | font_GIVEN_FONT |
+ font_GIVEN_LENGTH | font_GIVEN_BLOCK,
+ 0x7fffffff, 0x7fffffff,
+ &block, 0, 1,
+ 0, &x_out, &y_out, 0);
+ if (rufl_fm_error)
+ break;
+
+ if (block.bbox.x0 == 0x20000000) {
+ /* absent (no definition) */
+ } else if (x_out == 0 && y_out == 0 &&
+ block.bbox.x0 == 0 && block.bbox.y0 == 0 &&
+ block.bbox.x1 == 0 && block.bbox.y1 == 0) {
+ /* absent (empty) */
+ } else if (block.bbox.x0 == 0 && block.bbox.y0 == 0 &&
+ block.bbox.x1 == 0 && block.bbox.y1 == 0 &&
+ !rufl_is_space(u)) {
+ /* absent (space but not a space character - some
+ * fonts do this) */
+ } else {
+ /* present */
+ if (charset->index[u >> 8] == BLOCK_EMPTY) {
+ charset->index[u >> 8] = last_used;
+ last_used++;
+ if (last_used == 254)
+ /* too many characters */
+ break;
+ }
+
+ byte = (u >> 3) & 31;
+ bit = u & 7;
+ charset->block[charset->index[u >> 8]][byte] |=
+ 1 << bit;
+ }
+ }
+
+ xfont_lose_font(font);
+
+ if (rufl_fm_error) {
+ LOG("xfont_scan_string: 0x%x: %s",
+ rufl_fm_error->errnum, rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+
+ if (encoding) {
+ umap->encoding = strdup(encoding);
+ if (!umap->encoding)
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ *last = last_used;
+
+ return rufl_OK;
+}
+
+
+/**
+ * Parse an encoding file and fill in a rufl_unicode_map.
+ */
+
+rufl_code rufl_init_read_encoding(font_f font,
+ struct rufl_unicode_map *umap)
+{
+ unsigned int u = 0;
+ unsigned int i = 0;
+ int c;
+ int n;
+ char filename[200];
+ char s[200];
+ struct rufl_glyph_map_entry *entry;
+ FILE *fp;
+
+ rufl_fm_error = xfont_read_encoding_filename(font, filename,
+ sizeof filename, 0);
+ if (rufl_fm_error) {
+ LOG("xfont_read_encoding_filename: 0x%x: %s",
+ rufl_fm_error->errnum, rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+
+ fp = fopen(filename, "r");
+ if (!fp)
+ /* many "symbol" fonts have no encoding file: assume Latin 1 */
+ fp = fopen("Resources:$.Fonts.Encodings.Latin1", "r");
+ if (!fp)
+ return rufl_IO_ERROR;
+
+ while (!feof(fp) && u != 256) {
+ c = fgetc(fp);
+ if (c == '%') {
+ /* comment line */
+ fgets(s, sizeof s, fp);
+ } else if (c == '/') {
+ /* character definition */
+ if (i++ < 32)
+ continue;
+ n = fscanf(fp, "%100s", s);
+ if (n != 1)
+ break;
+ entry = bsearch(s, rufl_glyph_map,
+ rufl_glyph_map_size,
+ sizeof rufl_glyph_map[0],
+ rufl_glyph_map_cmp);
+ if (entry) {
+ /* may be more than one unicode for the glyph
+ * sentinels stop overshooting array */
+ while (strcmp(s, (entry - 1)->glyph_name) == 0)
+ entry--;
+ for (; strcmp(s, entry->glyph_name) == 0;
+ entry++) {
+ umap->map[u].u = entry->u;
+ umap->map[u].c = i - 1;
+ u++;
+ if (u == 256)
+ break;
+ }
+ }
+ }
+ }
+
+ if (fclose(fp) == EOF)
+ return rufl_IO_ERROR;
+
+ /* sort by unicode */
+ qsort(umap->map, u, sizeof umap->map[0], rufl_unicode_map_cmp);
+ umap->entries = u;
+
+ return rufl_OK;
+}
+
+
+int rufl_glyph_map_cmp(const void *keyval, const void *datum)
+{
+ const char *key = keyval;
+ const struct rufl_glyph_map_entry *entry = datum;
+ return strcmp(key, entry->glyph_name);
+}
+
+
+int rufl_unicode_map_cmp(const void *z1, const void *z2)
+{
+ const struct rufl_unicode_map_entry *entry1 = z1;
+ const struct rufl_unicode_map_entry *entry2 = z2;
+ if (entry1->u < entry2->u)
+ return -1;
+ else if (entry2->u < entry1->u)
+ return 1;
+ return 0;
+}
+
+
+/**
+ * Construct the font substitution table.
+ */
+
+rufl_code rufl_init_substitution_table(void)
+{
+ unsigned char z;
+ unsigned int i;
+ unsigned int block, byte, bit;
+ unsigned int u;
+ unsigned int index;
+ const struct rufl_character_set *charset;
+
+ rufl_substitution_table = malloc(65536 *
+ sizeof rufl_substitution_table[0]);
+ if (!rufl_substitution_table) {
+ LOG("malloc(%zu) failed", 65536 *
+ sizeof rufl_substitution_table[0]);
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ for (u = 0; u != 0x10000; u++)
+ rufl_substitution_table[u] = NOT_AVAILABLE;
+
+ for (i = 0; i != rufl_font_list_entries; i++) {
+ charset = rufl_font_list[i].charset;
+ if (!charset)
+ continue;
+ for (block = 0; block != 256; block++) {
+ if (charset->index[block] == BLOCK_EMPTY)
+ continue;
+ if (charset->index[block] == BLOCK_FULL) {
+ for (u = block << 8; u != (block << 8) + 256;
+ u++) {
+ if (rufl_substitution_table[u] ==
+ NOT_AVAILABLE)
+ rufl_substitution_table[u] = i;
+ }
+ continue;
+ }
+ index = charset->index[block];
+ for (byte = 0; byte != 32; byte++) {
+ z = charset->block[index][byte];
+ if (z == 0)
+ continue;
+ u = (block << 8) | (byte << 3);
+ for (bit = 0; bit != 8; bit++, u++) {
+ if (rufl_substitution_table[u] ==
+ NOT_AVAILABLE &&
+ z & (1 << bit))
+ rufl_substitution_table[u] = i;
+ }
+ }
+ }
+ }
+
+ return rufl_OK;
+}
+
+
+/**
+ * Save character sets to cache.
+ */
+
+rufl_code rufl_save_cache(void)
+{
+ unsigned int i;
+ const unsigned int version = rufl_CACHE_VERSION;
+ size_t len;
+ FILE *fp;
+
+ fp = fopen(rufl_CACHE, "wb");
+ if (!fp) {
+ LOG("fopen: 0x%x: %s", errno, strerror(errno));
+ return rufl_OK;
+ }
+
+ /* cache format version */
+ if (fwrite(&version, sizeof version, 1, fp) != 1) {
+ LOG("fwrite: 0x%x: %s", errno, strerror(errno));
+ fclose(fp);
+ return rufl_OK;
+ }
+
+ /* font manager type flag */
+ if (fwrite(&rufl_old_font_manager, sizeof rufl_old_font_manager, 1,
+ fp) != 1) {
+ LOG("fwrite: 0x%x: %s", errno, strerror(errno));
+ fclose(fp);
+ return rufl_OK;
+ }
+
+ for (i = 0; i != rufl_font_list_entries; i++) {
+ if (!rufl_font_list[i].charset)
+ continue;
+
+ /* length of font identifier */
+ len = strlen(rufl_font_list[i].identifier);
+ if (fwrite(&len, sizeof len, 1, fp) != 1) {
+ LOG("fwrite: 0x%x: %s", errno, strerror(errno));
+ fclose(fp);
+ return rufl_OK;
+ }
+
+ /* font identifier */
+ if (fwrite(rufl_font_list[i].identifier, len, 1, fp) != 1) {
+ LOG("fwrite: 0x%x: %s", errno, strerror(errno));
+ fclose(fp);
+ return rufl_OK;
+ }
+
+ /* character set */
+ if (fwrite(rufl_font_list[i].charset,
+ rufl_font_list[i].charset->size, 1, fp) != 1) {
+ LOG("fwrite: 0x%x: %s", errno, strerror(errno));
+ fclose(fp);
+ return rufl_OK;
+ }
+
+ /* unicode map */
+ if (rufl_old_font_manager) {
+ unsigned int j;
+
+ if (fwrite(&rufl_font_list[i].num_umaps,
+ sizeof rufl_font_list[i].num_umaps, 1,
+ fp) != 1) {
+ LOG("fwrite: 0x%x: %s", errno, strerror(errno));
+ fclose(fp);
+ return rufl_OK;
+ }
+
+ for (j = 0; j < rufl_font_list[i].num_umaps; j++) {
+ const struct rufl_unicode_map *umap =
+ rufl_font_list[i].umap + j;
+
+ len = umap->encoding ?
+ strlen(umap->encoding) : 0;
+
+ if (fwrite(&len, sizeof len, 1, fp) != 1) {
+ LOG("fwrite: 0x%x: %s",
+ errno, strerror(errno));
+ fclose(fp);
+ return rufl_OK;
+ }
+
+ if (umap->encoding) {
+ if (fwrite(umap->encoding, len, 1,
+ fp) != 1) {
+ LOG("fwrite: 0x%x: %s",
+ errno, strerror(errno));
+ fclose(fp);
+ return rufl_OK;
+ }
+ }
+
+ if (fwrite(&umap->entries, sizeof umap->entries,
+ 1, fp) != 1) {
+ LOG("fwrite: 0x%x: %s",
+ errno, strerror(errno));
+ fclose(fp);
+ return rufl_OK;
+ }
+
+ if (fwrite(umap->map, umap->entries *
+ sizeof(struct rufl_unicode_map_entry),
+ 1, fp) != 1) {
+ LOG("fwrite: 0x%x: %s",
+ errno, strerror(errno));
+ fclose(fp);
+ return rufl_OK;
+ }
+ }
+ }
+ }
+
+ if (fclose(fp) == EOF) {
+ LOG("fclose: 0x%x: %s", errno, strerror(errno));
+ return rufl_OK;
+ }
+
+ LOG("%u charsets saved", i);
+
+ return rufl_OK;
+}
+
+
+/**
+ * Load character sets from cache.
+ */
+
+rufl_code rufl_load_cache(void)
+{
+ unsigned int version;
+ unsigned int i = 0;
+ bool old_font_manager;
+ char *identifier;
+ size_t len, size;
+ FILE *fp;
+ struct rufl_font_list_entry *entry;
+ struct rufl_character_set *charset;
+ struct rufl_unicode_map *umap = NULL;
+ unsigned int num_umaps = 0;
+
+ fp = fopen(rufl_CACHE, "rb");
+ if (!fp) {
+ LOG("fopen: 0x%x: %s", errno, strerror(errno));
+ return rufl_OK;
+ }
+
+ /* cache format version */
+ if (fread(&version, sizeof version, 1, fp) != 1) {
+ if (feof(fp))
+ LOG("fread: %s", "unexpected eof");
+ else
+ LOG("fread: 0x%x: %s", errno, strerror(errno));
+ fclose(fp);
+ return rufl_OK;
+ }
+ if (version != rufl_CACHE_VERSION) {
+ /* incompatible cache format */
+ LOG("cache version %u (now %u)", version, rufl_CACHE_VERSION);
+ fclose(fp);
+ return rufl_OK;
+ }
+
+ /* font manager type flag */
+ if (fread(&old_font_manager, sizeof old_font_manager, 1, fp) != 1) {
+ if (feof(fp))
+ LOG("fread: %s", "unexpected eof");
+ else
+ LOG("fread: 0x%x: %s", errno, strerror(errno));
+ fclose(fp);
+ return rufl_OK;
+ }
+ if (old_font_manager != rufl_old_font_manager) {
+ /* font manager type has changed */
+ LOG("font manager %u (now %u)", old_font_manager,
+ rufl_old_font_manager);
+ fclose(fp);
+ return rufl_OK;
+ }
+
+ while (!feof(fp)) {
+ /* length of font identifier */
+ if (fread(&len, sizeof len, 1, fp) != 1) {
+ /* eof at this point simply means that the whole cache
+ * file has been loaded */
+ if (!feof(fp))
+ LOG("fread: 0x%x: %s", errno, strerror(errno));
+ break;
+ }
+
+ identifier = malloc(len + 1);
+ if (!identifier) {
+ LOG("malloc(%zu) failed", len + 1);
+ fclose(fp);
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ /* font identifier */
+ if (fread(identifier, len, 1, fp) != 1) {
+ if (feof(fp))
+ LOG("fread: %s", "unexpected eof");
+ else
+ LOG("fread: 0x%x: %s", errno, strerror(errno));
+ free(identifier);
+ break;
+ }
+ identifier[len] = 0;
+
+ /* character set */
+ if (fread(&size, sizeof size, 1, fp) != 1) {
+ if (feof(fp))
+ LOG("fread: %s", "unexpected eof");
+ else
+ LOG("fread: 0x%x: %s", errno, strerror(errno));
+ free(identifier);
+ break;
+ }
+
+ charset = malloc(size);
+ if (!charset) {
+ LOG("malloc(%zu) failed", size);
+ free(identifier);
+ fclose(fp);
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ charset->size = size;
+ if (fread(charset->index, size - sizeof size, 1, fp) != 1) {
+ if (feof(fp))
+ LOG("fread: %s", "unexpected eof");
+ else
+ LOG("fread: 0x%x: %s", errno, strerror(errno));
+ free(charset);
+ free(identifier);
+ break;
+ }
+
+ /* unicode map */
+ if (rufl_old_font_manager) {
+ rufl_code code = rufl_OK;
+ unsigned int entry;
+
+ /* Number of maps */
+ if (fread(&num_umaps, sizeof num_umaps, 1, fp) != 1) {
+ if (feof(fp))
+ LOG("fread: %s", "unexpected eof");
+ else
+ LOG("fread: 0x%x: %s", errno,
+ strerror(errno));
+ free(charset);
+ free(identifier);
+ break;
+ }
+
+ umap = calloc(num_umaps, sizeof *umap);
+ if (!umap) {
+ LOG("malloc(%zu) failed", sizeof *umap);
+ free(charset);
+ free(identifier);
+ fclose(fp);
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ /* Load them */
+ for (entry = 0; entry < num_umaps; entry++) {
+ struct rufl_unicode_map *map = umap + entry;
+
+ if (fread(&len, sizeof(len), 1, fp) != 1) {
+ if (feof(fp))
+ LOG("fread: %s",
+ "unexpected eof");
+ else
+ LOG("fread: 0x%x: %s", errno,
+ strerror(errno));
+ break;
+ }
+
+ if (len > 0) {
+ map->encoding = malloc(len + 1);
+ if (!map->encoding) {
+ LOG("malloc(%zu) failed",
+ len + 1);
+ code = rufl_OUT_OF_MEMORY;
+ break;
+ }
+
+ if (fread(map->encoding, len, 1,
+ fp) != 1) {
+ if (feof(fp))
+ LOG("fread: %s",
+ "unexpected eof");
+ else
+ LOG("fread: 0x%x: %s",
+ errno,
+ strerror(errno));
+ break;
+ }
+ map->encoding[len] = 0;
+ }
+
+ if (fread(&map->entries, sizeof(map->entries),
+ 1, fp) != 1) {
+ if (feof(fp))
+ LOG("fread: %s",
+ "unexpected eof");
+ else
+ LOG("fread: 0x%x: %s", errno,
+ strerror(errno));
+ break;
+ }
+
+ if (fread(map->map, map->entries *
+ sizeof(struct rufl_unicode_map_entry),
+ 1, fp) != 1) {
+ if (feof(fp))
+ LOG("fread: %s",
+ "unexpected eof");
+ else
+ LOG("fread: 0x%x: %s", errno,
+ strerror(errno));
+ break;
+ }
+ }
+
+ /* Clean up if loading failed */
+ if (entry != num_umaps) {
+ for (num_umaps = 0; num_umaps <= entry;
+ num_umaps++) {
+ struct rufl_unicode_map *map =
+ umap + num_umaps;
+
+ free(map->encoding);
+ }
+ free(umap);
+ free(charset);
+ free(identifier);
+
+ if (code != rufl_OK)
+ return code;
+
+ break;
+ }
+ }
+
+ /* put in rufl_font_list */
+ entry = lfind(identifier, rufl_font_list,
+ &rufl_font_list_entries,
+ sizeof rufl_font_list[0], rufl_font_list_cmp);
+ if (entry) {
+ entry->charset = charset;
+ entry->umap = umap;
+ entry->num_umaps = num_umaps;
+ i++;
+ } else {
+ LOG("\"%s\" not in font list", identifier);
+ while (num_umaps > 0) {
+ struct rufl_unicode_map *map =
+ umap + num_umaps - 1;
+
+ free(map->encoding);
+
+ num_umaps--;
+ }
+ free(umap);
+ free(charset);
+ }
+
+ free(identifier);
+ }
+ fclose(fp);
+
+ LOG("%u charsets loaded", i);
+
+ return rufl_OK;
+}
+
+
+int rufl_font_list_cmp(const void *keyval, const void *datum)
+{
+ const char *key = keyval;
+ const struct rufl_font_list_entry *entry = datum;
+ return strcasecmp(key, entry->identifier);
+}
+
+
+/**
+ * Create a menu of font families.
+ */
+
+rufl_code rufl_init_family_menu(void)
+{
+ wimp_menu *menu;
+ unsigned int i;
+
+ menu = malloc(wimp_SIZEOF_MENU(rufl_family_list_entries));
+ if (!menu)
+ return rufl_OUT_OF_MEMORY;
+ menu->title_data.indirected_text.text = (char *) "Fonts";
+ menu->title_fg = wimp_COLOUR_BLACK;
+ menu->title_bg = wimp_COLOUR_LIGHT_GREY;
+ menu->work_fg = wimp_COLOUR_BLACK;
+ menu->work_bg = wimp_COLOUR_WHITE;
+ menu->width = 200;
+ menu->height = wimp_MENU_ITEM_HEIGHT;
+ menu->gap = wimp_MENU_ITEM_GAP;
+ for (i = 0; i != rufl_family_list_entries; i++) {
+ menu->entries[i].menu_flags = 0;
+ menu->entries[i].sub_menu = wimp_NO_SUB_MENU;
+ menu->entries[i].icon_flags = wimp_ICON_TEXT |
+ wimp_ICON_INDIRECTED |
+ (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
+ (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT);
+ menu->entries[i].data.indirected_text.text =
+ (char *) rufl_family_list[i];
+ menu->entries[i].data.indirected_text.validation = (char *) -1;
+ menu->entries[i].data.indirected_text.size =
+ strlen(rufl_family_list[i]);
+ }
+ menu->entries[0].menu_flags = wimp_MENU_TITLE_INDIRECTED;
+ menu->entries[i - 1].menu_flags |= wimp_MENU_LAST;
+
+ rufl_family_menu = menu;
+
+ return rufl_OK;
+}
+
+
+/**
+ * Create and open the init status window.
+ */
+
+void rufl_init_status_open(void)
+{
+ int xeig_factor, yeig_factor, xwind_limit, ywind_limit, width, height;
+ wimp_t task;
+ osbool window_task;
+ wimp_WINDOW(4) window = { { 0, 0, 0, 0 }, 0, 0, wimp_TOP,
+ wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_NEW_FORMAT,
+ wimp_COLOUR_BLACK, wimp_COLOUR_LIGHT_GREY,
+ wimp_COLOUR_BLACK, wimp_COLOUR_VERY_LIGHT_GREY,
+ wimp_COLOUR_DARK_GREY, wimp_COLOUR_LIGHT_GREY,
+ wimp_COLOUR_CREAM, 0,
+ { 0, -128, 800, 0 }, 0, 0, 0, 0, 0, { "" }, 4,
+ { { { 12, -56, 788, -12 }, wimp_ICON_TEXT |
+ wimp_ICON_HCENTRED | wimp_ICON_VCENTRED |
+ wimp_ICON_INDIRECTED |
+ wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT |
+ wimp_COLOUR_VERY_LIGHT_GREY <<wimp_ICON_BG_COLOUR_SHIFT,
+ { "" } },
+ { { 12, -116, 788, -64 }, wimp_ICON_TEXT |
+ wimp_ICON_FILLED | wimp_ICON_BORDER |
+ wimp_ICON_INDIRECTED |
+ wimp_COLOUR_VERY_LIGHT_GREY <<wimp_ICON_BG_COLOUR_SHIFT,
+ { "" } },
+ { { 16, -112, 16, -68 }, wimp_ICON_FILLED |
+ wimp_COLOUR_ORANGE << wimp_ICON_BG_COLOUR_SHIFT,
+ { "" } },
+ { { 16, -112, 784, -68 }, wimp_ICON_TEXT |
+ wimp_ICON_HCENTRED | wimp_ICON_VCENTRED |
+ wimp_ICON_INDIRECTED |
+ wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT |
+ wimp_COLOUR_MID_LIGHT_GREY << wimp_ICON_BG_COLOUR_SHIFT,
+ { "" } } },
+ };
+ wimp_window_state state;
+ os_error *error;
+
+ window.icons[0].data.indirected_text.text =
+ (char *) "Scanning fonts - please wait";
+ window.icons[0].data.indirected_text.validation = (char *) "";
+ window.icons[1].data.indirected_text.text = (char *) "";
+ window.icons[1].data.indirected_text.validation = (char *) "r2";
+ window.icons[3].data.indirected_text.text = rufl_status_buffer;
+ window.icons[3].data.indirected_text.validation = (char *) "";
+
+ xos_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_XEIG_FACTOR,
+ &xeig_factor, 0);
+ xos_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_YEIG_FACTOR,
+ &yeig_factor, 0);
+ xos_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_XWIND_LIMIT,
+ &xwind_limit, 0);
+ xos_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_YWIND_LIMIT,
+ &ywind_limit, 0);
+ width = (xwind_limit + 1) << xeig_factor;
+ height = (ywind_limit + 1) << yeig_factor;
+
+ window.visible.x0 = width / 2 - 400;
+ window.visible.y0 = height / 2 - 64;
+ window.visible.x1 = window.visible.x0 + 800;
+ window.visible.y1 = window.visible.y0 + 128;
+
+ error = xwimpreadsysinfo_task(&task, 0);
+ if (error) {
+ LOG("xwimpreadsysinfo_task: 0x%x: %s",
+ error->errnum, error->errmess);
+ return;
+ }
+ if (!task)
+ return; /* not a Wimp task */
+
+ error = xtaskwindowtaskinfo_window_task(&window_task);
+ if (error) {
+ LOG("xtaskwindowtaskinfo_window_task: 0x%x: %s",
+ error->errnum, error->errmess);
+ return;
+ }
+ if (window_task)
+ return; /* in a TaskWindow */
+
+ xwimp_create_window((const wimp_window *) &window, &rufl_status_w);
+ state.w = rufl_status_w;
+ xwimp_get_window_state(&state);
+ xwimp_open_window((wimp_open *) (void *) &state);
+}
+
+
+/**
+ * Update the status window and multitask.
+ */
+
+void rufl_init_status(const char *status, float progress)
+{
+ wimp_block block;
+ static os_t last_t = 0;
+ os_t t;
+
+ if (!rufl_status_w)
+ return;
+
+ if (status) {
+ strncpy(rufl_status_buffer, status, sizeof rufl_status_buffer);
+ rufl_status_buffer[sizeof rufl_status_buffer - 1] = 0;
+ xwimp_set_icon_state(rufl_status_w, 3, 0, 0);
+ }
+ if (progress)
+ xwimp_resize_icon(rufl_status_w, 2, 16, -112,
+ 16 + 768 * progress, -68);
+ xos_read_monotonic_time(&t);
+ if (last_t == t)
+ return;
+ xwimp_poll(wimp_QUEUE_REDRAW | wimp_MASK_LEAVING | wimp_MASK_ENTERING |
+ wimp_MASK_LOSE | wimp_MASK_GAIN | wimp_MASK_MESSAGE |
+ wimp_MASK_RECORDED | wimp_MASK_ACKNOWLEDGE,
+ &block, 0, 0);
+ last_t = t;
+}
+
+
+/**
+ * Close and delete the status window.
+ */
+
+void rufl_init_status_close(void)
+{
+ if (!rufl_status_w)
+ return;
+
+ xwimp_delete_window(rufl_status_w);
+ rufl_status_w = 0;
+}
diff --git a/src/rufl_internal.h b/src/rufl_internal.h
new file mode 100644
index 0000000..1c01e36
--- /dev/null
+++ b/src/rufl_internal.h
@@ -0,0 +1,194 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2006 James Bursa <james@semichrome.net>
+ */
+
+#include <limits.h>
+#include "oslib/font.h"
+#include "rufl.h"
+#ifdef __CC_NORCROFT
+#include "strfuncs.h"
+#endif
+
+
+/** The available characters in a font. The range which can be represented is
+ * 0x0000 to 0xffff. The size of the structure is 4 + 256 + 32 * blocks. A
+ * typical * 200 glyph font might have characters in 10 blocks, giving 580
+ * bytes. The maximum possible size of the structure is 8388 bytes. Note that
+ * since two index values are reserved, fonts with 65280-65024 glyphs may be
+ * unrepresentable, if there are no full blocks. This is unlikely. The primary
+ * aim of this structure is to make lookup fast. */
+struct rufl_character_set {
+ /** Size of structure / bytes. */
+ size_t size;
+
+ /** Index table. Each entry represents a block of 256 characters, so
+ * i[k] refers to characters [256*k, 256*(k+1)). The value is either
+ * BLOCK_EMPTY, BLOCK_FULL, or an offset into the block table. */
+ unsigned char index[256];
+ /** The block has no characters present. */
+# define BLOCK_EMPTY 254
+ /** All characters in the block are present. */
+# define BLOCK_FULL 255
+
+ /** Block table. Each entry is a 256-bit bitmap indicating which
+ * characters in the block are present and absent. */
+ unsigned char block[254][32];
+};
+
+
+/** Part of struct rufl_unicode_map. */
+struct rufl_unicode_map_entry {
+ /** Unicode value. */
+ unsigned short u;
+ /** Corresponding character. */
+ unsigned char c;
+};
+
+
+/** Old font manager: mapping from Unicode to character code. This is simply
+ * an array sorted by Unicode value, suitable for bsearch(). If a font has
+ * support for multiple encodings, then it will have multiple unicode maps.
+ * The encoding field contains the name of the encoding to pass to the
+ * font manager. This will be NULL if the font is a Symbol font. */
+struct rufl_unicode_map {
+ /** Corresponding encoding name */
+ char *encoding;
+ /** Number of valid entries in map. */
+ unsigned int entries;
+ /** Map from Unicode to character code. */
+ struct rufl_unicode_map_entry map[256];
+};
+
+
+/** An entry in rufl_font_list. */
+struct rufl_font_list_entry {
+ /** Font identifier (name). */
+ char *identifier;
+ /** Character set of font. */
+ struct rufl_character_set *charset;
+ /** Number of Unicode mapping tables */
+ unsigned int num_umaps;
+ /** Mappings from Unicode to character code. */
+ struct rufl_unicode_map *umap;
+ /** Family that this font belongs to (index in rufl_family_list and
+ * rufl_family_map). */
+ unsigned int family;
+ /** Font weight (0 to 8). */
+ unsigned int weight;
+ /** Font slant (0 or 1). */
+ unsigned int slant;
+};
+/** List of all available fonts. */
+extern struct rufl_font_list_entry *rufl_font_list;
+/** Number of entries in rufl_font_list. */
+extern size_t rufl_font_list_entries;
+
+
+/** An entry in rufl_family_map. */
+struct rufl_family_map_entry {
+ /** This style does not exist in this family. */
+# define NO_FONT UINT_MAX
+ /** Map from weight and slant to index in rufl_font_list, or NO_FONT. */
+ unsigned int font[9][2];
+};
+/** Map from font family to fonts, rufl_family_list_entries entries. */
+extern struct rufl_family_map_entry *rufl_family_map;
+
+
+/** No font contains this character. */
+#define NOT_AVAILABLE 65535
+/** Font substitution table. */
+extern unsigned short *rufl_substitution_table;
+
+
+/** Number of slots in recent-use cache. This is the maximum number of RISC OS
+ * font handles that will be used at any time by the library. */
+#define rufl_CACHE_SIZE 10
+
+/** An entry in rufl_cache. */
+struct rufl_cache_entry {
+ /** Font number (index in rufl_font_list), or rufl_CACHE_*. */
+ unsigned int font;
+ /** No font cached in this slot. */
+#define rufl_CACHE_NONE UINT_MAX
+ /** Font for rendering hex substitutions in this slot. */
+#define rufl_CACHE_CORPUS (UINT_MAX - 1)
+ /** Font size. */
+ unsigned int size;
+ /** Font encoding */
+ const char *encoding;
+ /** Value of rufl_cache_time when last used. */
+ unsigned int last_used;
+ /** RISC OS font handle. */
+ font_f f;
+};
+/** Cache of rufl_CACHE_SIZE most recently used font handles. */
+extern struct rufl_cache_entry rufl_cache[rufl_CACHE_SIZE];
+/** Counter for measuring age of cache entries. */
+extern int rufl_cache_time;
+
+/** Font manager does not support Unicode. */
+extern bool rufl_old_font_manager;
+
+/** Font manager supports background blending */
+extern bool rufl_can_background_blend;
+
+rufl_code rufl_find_font_family(const char *family, rufl_style font_style,
+ unsigned int *font, unsigned int *slanted,
+ struct rufl_character_set **charset);
+rufl_code rufl_find_font(unsigned int font, unsigned int font_size,
+ const char *encoding, font_f *fhandle);
+bool rufl_character_set_test(struct rufl_character_set *charset,
+ unsigned int c);
+
+
+#define rufl_utf8_read(s, l, u) \
+ if (4 <= l && ((s[0] & 0xf8) == 0xf0) && ((s[1] & 0xc0) == 0x80) && \
+ ((s[2] & 0xc0) == 0x80) && ((s[3] & 0xc0) == 0x80)) { \
+ u = ((s[0] & 0x7) << 18) | ((s[1] & 0x3f) << 12) | \
+ ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); \
+ s += 4; l -= 4; \
+ } else if (3 <= l && ((s[0] & 0xf0) == 0xe0) && \
+ ((s[1] & 0xc0) == 0x80) && \
+ ((s[2] & 0xc0) == 0x80)) { \
+ u = ((s[0] & 0xf) << 12) | ((s[1] & 0x3f) << 6) | \
+ (s[2] & 0x3f); \
+ s += 3; l -= 3; \
+ } else if (2 <= l && ((s[0] & 0xe0) == 0xc0) && \
+ ((s[1] & 0xc0) == 0x80)) { \
+ u = ((s[0] & 0x3f) << 6) | (s[1] & 0x3f); \
+ s += 2; l -= 2; \
+ } else if ((s[0] & 0x80) == 0) { \
+ u = s[0]; \
+ s++; l--; \
+ } else { \
+ u = 0xfffd; \
+ s++; l--; \
+ }
+
+#define rufl_CACHE "<Wimp$ScrapDir>.RUfl_cache"
+#define rufl_CACHE_VERSION 3
+
+
+struct rufl_glyph_map_entry {
+ const char *glyph_name;
+ unsigned short u;
+};
+
+extern const struct rufl_glyph_map_entry rufl_glyph_map[];
+extern const size_t rufl_glyph_map_size;
+
+
+#ifndef NDEBUG
+#ifdef __CC_NORCROFT
+#define __PRETTY_FUNCTION__ __func__
+#endif
+#define LOG(format, ...) (fprintf(stderr, __FILE__ " %s %i: ", \
+ __PRETTY_FUNCTION__, __LINE__), fprintf(stderr, format, \
+ __VA_ARGS__), fprintf(stderr, "\n"))
+#else
+#define LOG(format, ...) ((void) 0)
+#endif
diff --git a/src/rufl_invalidate_cache.c b/src/rufl_invalidate_cache.c
new file mode 100644
index 0000000..65a3897
--- /dev/null
+++ b/src/rufl_invalidate_cache.c
@@ -0,0 +1,28 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2005 James Bursa <james@semichrome.net>
+ */
+
+#include "oslib/font.h"
+#include "rufl_internal.h"
+
+
+/**
+ * Clear the internal font handle cache.
+ *
+ * Call this function on mode changes or output redirection changes.
+ */
+
+void rufl_invalidate_cache(void)
+{
+ unsigned int i;
+
+ for (i = 0; i != rufl_CACHE_SIZE; i++) {
+ if (rufl_cache[i].font != rufl_CACHE_NONE) {
+ xfont_lose_font(rufl_cache[i].f);
+ rufl_cache[i].font = rufl_CACHE_NONE;
+ }
+ }
+}
diff --git a/src/rufl_metrics.c b/src/rufl_metrics.c
new file mode 100644
index 0000000..af4727f
--- /dev/null
+++ b/src/rufl_metrics.c
@@ -0,0 +1,312 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2005 John-Mark Bell <jmb202@ecs.soton.ac.uk>
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "oslib/font.h"
+
+#include "rufl_internal.h"
+
+static int rufl_unicode_map_search_cmp(const void *keyval, const void *datum);
+
+/**
+ * Read a font's metrics (sized for a 1pt font)
+ */
+rufl_code rufl_font_metrics(const char *font_family, rufl_style font_style,
+ os_box *bbox, int *xkern, int *ykern, int *italic,
+ int *ascent, int *descent,
+ int *xheight, int *cap_height,
+ signed char *uline_position, unsigned char *uline_thickness)
+{
+ unsigned int font;
+ font_f f;
+ int misc_size;
+ font_metrics_misc_info *misc_info;
+ rufl_code code;
+
+ code = rufl_find_font_family(font_family, font_style, &font,
+ NULL, NULL);
+ if (code != rufl_OK)
+ return code;
+
+ code = rufl_find_font(font, 16 /* 1pt */, NULL, &f);
+ if (code != rufl_OK)
+ return code;
+
+ rufl_fm_error = xfont_read_font_metrics(f, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, &misc_size, 0);
+ if (rufl_fm_error) {
+ LOG("xfont_read_font_metrics: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+
+ if (misc_size == 0) {
+ LOG("no miscellaneous information in metrics for %s",
+ rufl_font_list[font].identifier);
+ /** \todo better error code */
+ return rufl_FONT_NOT_FOUND;
+ }
+
+ misc_info = (font_metrics_misc_info *)malloc(misc_size);
+ if (!misc_info)
+ return rufl_OUT_OF_MEMORY;
+
+ rufl_fm_error = xfont_read_font_metrics(f, 0, 0, 0, misc_info, 0,
+ 0, 0, 0, 0, 0, 0);
+ if (rufl_fm_error) {
+ LOG("xfont_read_font_metrics: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ free(misc_info);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+
+ /* and fill in output */
+ if (bbox) {
+ bbox->x0 = misc_info->x0;
+ bbox->y0 = misc_info->y0;
+ bbox->x1 = misc_info->x1;
+ bbox->y1 = misc_info->y1;
+ }
+
+ if (xkern)
+ (*xkern) = misc_info->xkern;
+
+ if (ykern)
+ (*ykern) = misc_info->ykern;
+
+ if (italic)
+ (*italic) = misc_info->italic_correction;
+
+ if (ascent)
+ (*ascent) = misc_info->ascender;
+
+ if (descent)
+ (*descent) = misc_info->descender;
+
+ if (xheight)
+ (*xheight) = misc_info->xheight;
+
+ if (cap_height)
+ (*cap_height) = misc_info->cap_height;
+
+ if (uline_position)
+ (*uline_position) = misc_info->underline_position;
+
+ if (uline_thickness)
+ (*uline_thickness) = misc_info->underline_thickness;
+
+ free(misc_info);
+
+ return rufl_OK;
+}
+
+/**
+ * Read a glyph's metrics
+ */
+rufl_code rufl_glyph_metrics(const char *font_family,
+ rufl_style font_style, unsigned int font_size,
+ const char *string, size_t length,
+ int *x_bearing, int *y_bearing,
+ int *width, int *height,
+ int *x_advance, int *y_advance)
+{
+ const char *font_encoding = NULL;
+ unsigned int font, font1, u;
+ unsigned short u1[2];
+ struct rufl_character_set *charset;
+ struct rufl_unicode_map_entry *umap_entry = NULL;
+ font_f f;
+ rufl_code code;
+ font_scan_block block;
+ font_string_flags flags;
+ int xa, ya;
+
+ /* Find font family containing glyph */
+ code = rufl_find_font_family(font_family, font_style,
+ &font, NULL, &charset);
+ if (code != rufl_OK)
+ return code;
+
+ rufl_utf8_read(string, length, u);
+ if (charset && rufl_character_set_test(charset, u))
+ font1 = font;
+ else if (u < 0x10000)
+ font1 = rufl_substitution_table[u];
+ else
+ font1 = rufl_CACHE_CORPUS;
+
+ /* Old font managers need the font encoding, too */
+ if (rufl_old_font_manager && font1 != rufl_CACHE_CORPUS) {
+ unsigned int i;
+ unsigned short u16 = (unsigned short) u;
+
+ for (i = 0; i < rufl_font_list[font1].num_umaps; i++) {
+ struct rufl_unicode_map *map =
+ rufl_font_list[font1].umap + i;
+
+ umap_entry = bsearch(&u16, map->map, map->entries,
+ sizeof map->map[0],
+ rufl_unicode_map_search_cmp);
+ if (umap_entry) {
+ font_encoding = map->encoding;
+ break;
+ }
+ }
+
+ assert(umap_entry != NULL);
+ }
+
+ code = rufl_find_font(font1, font_size, font_encoding, &f);
+ if (code != rufl_OK)
+ return code;
+
+ /*
+ * Glyph Metrics for horizontal text:
+ *
+ * ^ x0 x1
+ * | Xbearing : x0 - oX
+ * | +-----+-------- y1 Ybearing : y1 - oY
+ * | | | Xadvance : aX - oX
+ * | | | Yadvance : 0
+ * o---|-----|---a--> Glyph width : x1 - x0
+ * | | | Glyph height : y1 - y0
+ * | +-----+-------- y0 Right side bearing: aX - x1
+ * |
+ *
+ * The rectangle (x0,y0),(x1,y1) is the glyph bounding box.
+ *
+ * Glyph Metrics for vertical text:
+ *
+ * -------o--------->
+ * y1--+--|--+ Xbearing : x0 - oX
+ * | | | Ybearing : oY - y1
+ * | | | Xadvance : 0
+ * | | | Yadvance : aY - oY
+ * | | | Glyph width : x1 - x0
+ * y0--+-----+ Glyph height : y1 - y0
+ * ------a----------- Right side bearing: N/A
+ * x0 v x1
+ *
+ * The rectangle (x0,y0),(x1,y1) is the glyph bounding box.
+ *
+ *
+ * In order to extract the information we want from the
+ * Font Manager, a little bit of hackery is required.
+ *
+ * Firstly, we can take the origin as being (0,0). This is an
+ * arbitrary choice but makes the maths simpler.
+ *
+ * Secondly, the bounding box returned by Font_CharBBox /
+ * Font_ScanString / Font_StringBBox represents the ink area of
+ * the glyph (i.e. the smallest box needed to contain all the
+ * glyph path segments). This means that, for glyphs with no
+ * displayed content (such as a space), the bounding box will be 0.
+ * These SWIs therefore allow us to retrieve the (x0,y0),(x1,y1)
+ * coordinates marked in the diagrams above.
+ *
+ * Finally, we need to retrieve the glyph advance distance. This is
+ * returned in R3/R4 on exit from Font_ScanString (providing bit 17
+ * of the flags word on entry is clear). It is important to note,
+ * however, that the height will be returned as 0 for fonts with no
+ * Yadvance values in the font data file. Therefore, in order to
+ * achieve vertical layout of text, further work will be needed
+ * (We're also ignoring the fact that the X coordinates of all
+ * values will be in the wrong place and the Y coordinates will have
+ * the wrong sign due to the differing definitions of the Y axis for
+ * horizontal and vertical text.)
+ *
+ * Note that all values (that we're interested in, at least)
+ * returned by the SWIs mentioned above are in _millipoints_.
+ */
+
+ block.space.x = block.space.y = 0;
+ block.letter.x = block.letter.y = 0;
+ block.split_char = -1;
+
+ flags = font_GIVEN_BLOCK | font_GIVEN_LENGTH | font_GIVEN_FONT |
+ font_RETURN_BBOX;
+
+ u1[0] = (unsigned short)u;
+ u1[1] = 0;
+
+ if (font1 == rufl_CACHE_CORPUS) {
+ /* Fallback Glyph */
+ /** \todo implement this properly */
+ xa = 1000 * font_size;
+ ya = 0;
+ block.bbox.x0 = block.bbox.y0 = 0;
+ block.bbox.x1 = block.bbox.y1 = xa;
+ } else if (rufl_old_font_manager) {
+ /* Old Font Manager */
+ char s[2];
+
+ /* We found the correct umap entry when
+ * looking for the font encoding */
+ s[0] = umap_entry->c;
+ s[1] = 0;
+
+ rufl_fm_error = xfont_scan_string(f, s, flags,
+ 0x7fffffff, 0x7fffffff, &block, 0, 1,
+ 0, &xa, &ya, 0);
+ if (rufl_fm_error) {
+ LOG("xfont_scan_string: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ } else {
+ /* UCS Font Manager */
+ rufl_fm_error = xfont_scan_string(f, (const char *)u1,
+ flags | font_GIVEN16_BIT,
+ 0x7fffffff, 0x7fffffff, &block, 0, 2,
+ 0, &xa, &ya, 0);
+ if (rufl_fm_error) {
+ LOG("xfont_scan_string: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ }
+
+ /** \todo handle vertical text */
+ if (x_bearing)
+ (*x_bearing) = block.bbox.x0;
+
+ if (y_bearing)
+ (*y_bearing) = block.bbox.y1;
+
+ if (width)
+ (*width) = block.bbox.x1 - block.bbox.x0;
+
+ if (height)
+ (*height) = block.bbox.y1 - block.bbox.y0;
+
+ if (x_advance)
+ (*x_advance) = xa;
+
+ if (y_advance)
+ (*y_advance) = ya;
+
+ return rufl_OK;
+}
+
+
+int rufl_unicode_map_search_cmp(const void *keyval, const void *datum)
+{
+ const unsigned short *key = keyval;
+ const struct rufl_unicode_map_entry *entry = datum;
+ if (*key < entry->u)
+ return -1;
+ else if (entry->u < *key)
+ return 1;
+ return 0;
+}
diff --git a/src/rufl_paint.c b/src/rufl_paint.c
new file mode 100644
index 0000000..8b9edf1
--- /dev/null
+++ b/src/rufl_paint.c
@@ -0,0 +1,621 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2006 James Bursa <james@semichrome.net>
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "oslib/font.h"
+#include "rufl_internal.h"
+
+
+typedef enum { rufl_PAINT, rufl_WIDTH, rufl_X_TO_OFFSET,
+ rufl_SPLIT, rufl_PAINT_CALLBACK, rufl_FONT_BBOX } rufl_action;
+#define rufl_PROCESS_CHUNK 200
+
+bool rufl_can_background_blend = false;
+
+static const os_trfm trfm_oblique =
+ { { { 65536, 0 }, { 13930, 65536 }, { 0, 0 } } };
+
+
+static rufl_code rufl_process(rufl_action action,
+ const char *font_family, rufl_style font_style,
+ unsigned int font_size,
+ const char *string0, size_t length,
+ int x, int y, unsigned int flags,
+ int *width, int click_x, size_t *char_offset, int *actual_x,
+ rufl_callback_t callback, void *context);
+static rufl_code rufl_process_span(rufl_action action,
+ unsigned short *s, unsigned int n,
+ unsigned int font, unsigned int font_size, unsigned int slant,
+ int *x, int y, unsigned int flags,
+ int click_x, size_t *offset,
+ rufl_callback_t callback, void *context);
+static rufl_code rufl_process_span_old(rufl_action action,
+ unsigned short *s, unsigned int n,
+ unsigned int font, unsigned int font_size, unsigned int slant,
+ int *x, int y, unsigned int flags,
+ int click_x, size_t *offset,
+ rufl_callback_t callback, void *context);
+static int rufl_unicode_map_search_cmp(const void *keyval, const void *datum);
+static rufl_code rufl_process_not_available(rufl_action action,
+ unsigned short *s, unsigned int n,
+ unsigned int font_size, int *x, int y,
+ unsigned int flags,
+ int click_x, size_t *offset,
+ rufl_callback_t callback, void *context);
+
+
+/**
+ * Render Unicode text.
+ */
+
+rufl_code rufl_paint(const char *font_family, rufl_style font_style,
+ unsigned int font_size,
+ const char *string, size_t length,
+ int x, int y, unsigned int flags)
+{
+ return rufl_process(rufl_PAINT,
+ font_family, font_style, font_size, string,
+ length, x, y, flags, 0, 0, 0, 0, 0, 0);
+}
+
+
+/**
+ * Measure the width of Unicode text.
+ */
+
+rufl_code rufl_width(const char *font_family, rufl_style font_style,
+ unsigned int font_size,
+ const char *string, size_t length,
+ int *width)
+{
+ return rufl_process(rufl_WIDTH,
+ font_family, font_style, font_size, string,
+ length, 0, 0, 0, width, 0, 0, 0, 0, 0);
+}
+
+
+/**
+ * Find the nearest character boundary in a string to where an x coordinate
+ * falls.
+ */
+
+rufl_code rufl_x_to_offset(const char *font_family, rufl_style font_style,
+ unsigned int font_size,
+ const char *string, size_t length,
+ int click_x,
+ size_t *char_offset, int *actual_x)
+{
+ return rufl_process(rufl_X_TO_OFFSET,
+ font_family, font_style, font_size, string,
+ length, 0, 0, 0, 0,
+ click_x, char_offset, actual_x, 0, 0);
+}
+
+
+/**
+ * Find the prefix of a string that will fit in a specified width.
+ */
+
+rufl_code rufl_split(const char *font_family, rufl_style font_style,
+ unsigned int font_size,
+ const char *string, size_t length,
+ int width,
+ size_t *char_offset, int *actual_x)
+{
+ return rufl_process(rufl_SPLIT,
+ font_family, font_style, font_size, string,
+ length, 0, 0, 0, 0,
+ width, char_offset, actual_x, 0, 0);
+}
+
+
+/**
+ * Render text, but call a callback instead of each call to Font_Paint.
+ */
+
+rufl_code rufl_paint_callback(const char *font_family, rufl_style font_style,
+ unsigned int font_size,
+ const char *string, size_t length,
+ int x, int y,
+ rufl_callback_t callback, void *context)
+{
+ return rufl_process(rufl_PAINT_CALLBACK,
+ font_family, font_style, font_size, string,
+ length, x, y, 0, 0, 0, 0, 0, callback, context);
+}
+
+
+/**
+ * Determine the maximum bounding box of a font.
+ */
+
+rufl_code rufl_font_bbox(const char *font_family, rufl_style font_style,
+ unsigned int font_size,
+ int *bbox)
+{
+ return rufl_process(rufl_FONT_BBOX,
+ font_family, font_style, font_size, 0,
+ 0, 0, 0, 0, bbox, 0, 0, 0, 0, 0);
+}
+
+
+/**
+ * Render, measure, or split Unicode text.
+ */
+
+rufl_code rufl_process(rufl_action action,
+ const char *font_family, rufl_style font_style,
+ unsigned int font_size,
+ const char *string0, size_t length,
+ int x, int y, unsigned int flags,
+ int *width, int click_x, size_t *char_offset, int *actual_x,
+ rufl_callback_t callback, void *context)
+{
+ unsigned short s[rufl_PROCESS_CHUNK];
+ unsigned int font;
+ unsigned int font0, font1;
+ unsigned int n;
+ unsigned int u;
+ size_t offset;
+ size_t offset_u;
+ size_t offset_map[rufl_PROCESS_CHUNK];
+ unsigned int slant;
+ const char *string = string0;
+ struct rufl_character_set *charset;
+ rufl_code code;
+
+ assert(action == rufl_PAINT ||
+ (action == rufl_WIDTH && width) ||
+ (action == rufl_X_TO_OFFSET && char_offset &&
+ actual_x) ||
+ (action == rufl_SPLIT && char_offset &&
+ actual_x) ||
+ (action == rufl_PAINT_CALLBACK && callback) ||
+ (action == rufl_FONT_BBOX && width));
+
+ if ((flags & rufl_BLEND_FONT) && !rufl_can_background_blend) {
+ /* unsuitable FM => clear blending bit */
+ flags &= ~rufl_BLEND_FONT;
+ }
+
+ if (length == 0 && action != rufl_FONT_BBOX) {
+ if (action == rufl_WIDTH)
+ *width = 0;
+ else if (action == rufl_X_TO_OFFSET || action == rufl_SPLIT) {
+ *char_offset = 0;
+ *actual_x = 0;
+ }
+ return rufl_OK;
+ }
+ if ((action == rufl_X_TO_OFFSET || action == rufl_SPLIT) &&
+ click_x <= 0) {
+ *char_offset = 0;
+ *actual_x = 0;
+ return rufl_OK;
+ }
+
+ code = rufl_find_font_family(font_family, font_style,
+ &font, &slant, &charset);
+ if (code != rufl_OK)
+ return code;
+
+ if (action == rufl_FONT_BBOX) {
+ if (rufl_old_font_manager)
+ code = rufl_process_span_old(action, 0, 0, font,
+ font_size, slant, width, 0, 0,
+ 0, 0, 0, 0);
+ else
+ code = rufl_process_span(action, 0, 0, font,
+ font_size, slant, width, 0, 0,
+ 0, 0, 0, 0);
+ return code;
+ }
+
+ offset_u = 0;
+ rufl_utf8_read(string, length, u);
+ if (u <= 0x001f || (0x007f <= u && u <= 0x009f))
+ font1 = NOT_AVAILABLE;
+ else if (charset && rufl_character_set_test(charset, u))
+ font1 = font;
+ else if (u < 0x10000)
+ font1 = rufl_substitution_table[u];
+ else
+ font1 = NOT_AVAILABLE;
+ do {
+ s[0] = u;
+ offset_map[0] = offset_u;
+ n = 1;
+ font0 = font1;
+ /* invariant: s[0..n) is in font font0 */
+ while (0 < length && n < rufl_PROCESS_CHUNK && font1 == font0) {
+ offset_u = string - string0;
+ rufl_utf8_read(string, length, u);
+ s[n] = u;
+ offset_map[n] = offset_u;
+ if (u <= 0x001f || (0x007f <= u && u <= 0x009f))
+ font1 = NOT_AVAILABLE;
+ else if (charset && rufl_character_set_test(charset, u))
+ font1 = font;
+ else if (u < 0x10000)
+ font1 = rufl_substitution_table[u];
+ else
+ font1 = NOT_AVAILABLE;
+ if (font1 == font0)
+ n++;
+ }
+ if (n == rufl_PROCESS_CHUNK)
+ n--;
+ s[n] = 0;
+ offset_map[n] = offset_u;
+ if (length == 0 && font1 == font0)
+ offset_map[n] = string - string0;
+
+ if (font0 == NOT_AVAILABLE)
+ code = rufl_process_not_available(action, s, n,
+ font_size, &x, y, flags,
+ click_x, &offset, callback, context);
+ else if (rufl_old_font_manager)
+ code = rufl_process_span_old(action, s, n, font0,
+ font_size, slant, &x, y, flags,
+ click_x, &offset, callback, context);
+ else
+ code = rufl_process_span(action, s, n, font0,
+ font_size, slant, &x, y, flags,
+ click_x, &offset, callback, context);
+
+ if ((action == rufl_X_TO_OFFSET || action == rufl_SPLIT) &&
+ (offset < n || click_x < x))
+ break;
+ if (code != rufl_OK)
+ return code;
+
+ } while (!(length == 0 && font1 == font0));
+
+ if (action == rufl_WIDTH)
+ *width = x;
+ else if (action == rufl_X_TO_OFFSET || action == rufl_SPLIT) {
+ *char_offset = offset_map[offset];
+ *actual_x = x;
+ }
+
+ return rufl_OK;
+}
+
+
+/**
+ * Render a string of characters from a single RISC OS font.
+ */
+
+rufl_code rufl_process_span(rufl_action action,
+ unsigned short *s, unsigned int n,
+ unsigned int font, unsigned int font_size, unsigned int slant,
+ int *x, int y, unsigned int flags,
+ int click_x, size_t *offset,
+ rufl_callback_t callback, void *context)
+{
+ unsigned short *split_point;
+ int x_out, y_out;
+ unsigned int i;
+ char font_name[80];
+ bool oblique = slant && !rufl_font_list[font].slant;
+ font_f f;
+ rufl_code code;
+
+ code = rufl_find_font(font, font_size, "UTF8", &f);
+ if (code != rufl_OK)
+ return code;
+
+ if (action == rufl_FONT_BBOX) {
+ rufl_fm_error = xfont_read_info(f, &x[0], &x[1], &x[2], &x[3]);
+ if (rufl_fm_error)
+ return rufl_FONT_MANAGER_ERROR;
+ return rufl_OK;
+ }
+
+ if (action == rufl_PAINT) {
+ /* paint span */
+ rufl_fm_error = xfont_paint(f, (const char *) s,
+ font_OS_UNITS |
+ (oblique ? font_GIVEN_TRFM : 0) |
+ font_GIVEN_LENGTH |
+ font_GIVEN_FONT | font_KERN |
+ font_GIVEN16_BIT |
+ ((flags & rufl_BLEND_FONT) ?
+ font_BLEND_FONT : 0),
+ *x, y, 0, &trfm_oblique, n * 2);
+ if (rufl_fm_error) {
+ LOG("xfont_paint: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ for (i = 0; i != n; i++)
+ fprintf(stderr, "0x%x ", s[i]);
+ fprintf(stderr, " (%u)\n", n);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ } else if (action == rufl_PAINT_CALLBACK) {
+ snprintf(font_name, sizeof font_name, "%s\\EUTF8",
+ rufl_font_list[font].identifier);
+ callback(context, font_name, font_size, 0, s, n, *x, y);
+ }
+
+ /* increment x by width of span */
+ if (action == rufl_X_TO_OFFSET || action == rufl_SPLIT) {
+ rufl_fm_error = xfont_scan_string(f, (const char *) s,
+ font_GIVEN_LENGTH | font_GIVEN_FONT |
+ font_KERN | font_GIVEN16_BIT |
+ ((action == rufl_X_TO_OFFSET) ?
+ font_RETURN_CARET_POS : 0),
+ (click_x - *x) * 400, 0x7fffffff, 0, 0,
+ n * 2,
+ (char **)(void *)&split_point,
+ &x_out, &y_out, 0);
+ *offset = split_point - s;
+ } else {
+ rufl_fm_error = xfont_scan_string(f, (const char *) s,
+ font_GIVEN_LENGTH | font_GIVEN_FONT |
+ font_KERN | font_GIVEN16_BIT,
+ 0x7fffffff, 0x7fffffff, 0, 0, n * 2,
+ 0, &x_out, &y_out, 0);
+ }
+ if (rufl_fm_error) {
+ LOG("xfont_scan_string: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ *x += x_out / 400;
+
+ return rufl_OK;
+}
+
+
+/**
+ * Render a string of characters from a single RISC OS font (old font manager
+ * version).
+ */
+
+rufl_code rufl_process_span_old(rufl_action action,
+ unsigned short *s, unsigned int n,
+ unsigned int font, unsigned int font_size, unsigned int slant,
+ int *x, int y, unsigned int flags,
+ int click_x, size_t *offset,
+ rufl_callback_t callback, void *context)
+{
+ char s2[rufl_PROCESS_CHUNK];
+ char *split_point;
+ int x_out, y_out;
+ unsigned int i;
+ bool oblique = slant && !rufl_font_list[font].slant;
+ font_f f;
+ rufl_code code;
+
+ if (action == rufl_FONT_BBOX) {
+ /* Don't need encoding for bounding box */
+ code = rufl_find_font(font, font_size, NULL, &f);
+ if (code != rufl_OK)
+ return code;
+
+ rufl_fm_error = xfont_read_info(f, &x[0], &x[1], &x[2], &x[3]);
+ if (rufl_fm_error) {
+ LOG("xfont_read_info: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ return rufl_OK;
+ }
+
+ if (offset)
+ *offset = 0;
+
+ /* Process the span in map-coherent chunks */
+ do {
+ struct rufl_unicode_map *map = NULL;
+ struct rufl_unicode_map_entry *entry = NULL;
+ unsigned int j;
+
+ i = 0;
+
+ /* Find map for first character */
+ for (j = 0; j < rufl_font_list[font].num_umaps; j++) {
+ map = rufl_font_list[font].umap + j;
+
+ entry = bsearch(&s[i], map->map, map->entries,
+ sizeof map->map[0],
+ rufl_unicode_map_search_cmp);
+ if (entry)
+ break;
+ }
+ assert(map != NULL);
+ assert(entry != NULL);
+
+ /* Collect characters: s[0..i) use map */
+ do {
+ entry = bsearch(&s[i], map->map, map->entries,
+ sizeof map->map[0],
+ rufl_unicode_map_search_cmp);
+
+ if (entry)
+ s2[i++] = entry->c;
+ } while (i != n && entry != NULL);
+
+ s2[i] = 0;
+
+ code = rufl_find_font(font, font_size, map->encoding, &f);
+ if (code != rufl_OK)
+ return code;
+
+ if (action == rufl_PAINT) {
+ /* paint span */
+ /* call Font_SetFont to work around broken PS printer
+ * driver, which doesn't use the font handle from
+ * Font_Paint */
+ rufl_fm_error = xfont_set_font(f);
+ if (rufl_fm_error) {
+ LOG("xfont_set_font: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+
+ rufl_fm_error = xfont_paint(f, s2, font_OS_UNITS |
+ (oblique ? font_GIVEN_TRFM : 0) |
+ font_GIVEN_LENGTH | font_GIVEN_FONT |
+ font_KERN |
+ ((flags & rufl_BLEND_FONT) ?
+ font_BLEND_FONT : 0),
+ *x, y, 0, &trfm_oblique, i);
+ if (rufl_fm_error) {
+ LOG("xfont_paint: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ } else if (action == rufl_PAINT_CALLBACK) {
+ char font_name[80];
+
+ if (map->encoding)
+ snprintf(font_name, sizeof font_name, "%s\\E%s",
+ rufl_font_list[font].identifier,
+ map->encoding);
+ else
+ snprintf(font_name, sizeof font_name, "%s",
+ rufl_font_list[font].identifier);
+
+ callback(context, font_name, font_size,
+ s2, 0, i, *x, y);
+ }
+
+ /* increment x by width of span */
+ if (action == rufl_X_TO_OFFSET || action == rufl_SPLIT) {
+ rufl_fm_error = xfont_scan_string(f, s2,
+ font_GIVEN_LENGTH | font_GIVEN_FONT |
+ font_KERN |
+ ((action == rufl_X_TO_OFFSET) ?
+ font_RETURN_CARET_POS : 0),
+ (click_x - *x) * 400, 0x7fffffff,
+ 0, 0, i,
+ &split_point, &x_out, &y_out, 0);
+ *offset += split_point - s2;
+ } else {
+ rufl_fm_error = xfont_scan_string(f, s2,
+ font_GIVEN_LENGTH | font_GIVEN_FONT |
+ font_KERN,
+ 0x7fffffff, 0x7fffffff, 0, 0, i,
+ 0, &x_out, &y_out, 0);
+ }
+ if (rufl_fm_error) {
+ LOG("xfont_scan_string: 0x%x: %s",
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ *x += x_out / 400;
+
+ /* Now update s and n for the next chunk */
+ s += i;
+ n -= i;
+ } while (n != 0);
+
+ return rufl_OK;
+}
+
+
+int rufl_unicode_map_search_cmp(const void *keyval, const void *datum)
+{
+ const unsigned short *key = keyval;
+ const struct rufl_unicode_map_entry *entry = datum;
+ if (*key < entry->u)
+ return -1;
+ else if (entry->u < *key)
+ return 1;
+ return 0;
+}
+
+
+/**
+ * Render a string of characters not available in any font as their hex code.
+ */
+
+rufl_code rufl_process_not_available(rufl_action action,
+ unsigned short *s, unsigned int n,
+ unsigned int font_size, int *x, int y,
+ unsigned int flags,
+ int click_x, size_t *offset,
+ rufl_callback_t callback, void *context)
+{
+ char missing[] = "0000";
+ int dx = 7 * font_size / 64;
+ int top_y = y + 5 * font_size / 64;
+ unsigned int i;
+ font_f f;
+ rufl_code code;
+
+ if (action == rufl_WIDTH) {
+ *x += n * dx;
+ return rufl_OK;
+ } else if (action == rufl_X_TO_OFFSET || action == rufl_SPLIT) {
+ if (click_x - *x < (int) (n * dx))
+ *offset = (click_x - *x) / dx;
+ else
+ *offset = n;
+ *x += *offset * dx;
+ return rufl_OK;
+ }
+
+ code = rufl_find_font(rufl_CACHE_CORPUS, font_size / 2, "Latin1", &f);
+ if (code != rufl_OK)
+ return code;
+
+ for (i = 0; i != n; i++) {
+ missing[0] = "0123456789abcdef"[(s[i] >> 12) & 0xf];
+ missing[1] = "0123456789abcdef"[(s[i] >> 8) & 0xf];
+ missing[2] = "0123456789abcdef"[(s[i] >> 4) & 0xf];
+ missing[3] = "0123456789abcdef"[(s[i] >> 0) & 0xf];
+
+ /* first two characters in top row */
+ if (action == rufl_PAINT) {
+ rufl_fm_error = xfont_paint(f, missing, font_OS_UNITS |
+ font_GIVEN_LENGTH | font_GIVEN_FONT |
+ font_KERN |
+ ((flags & rufl_BLEND_FONT) ?
+ font_BLEND_FONT : 0),
+ *x, top_y, 0, 0, 2);
+ if (rufl_fm_error)
+ return rufl_FONT_MANAGER_ERROR;
+ } else {
+ callback(context, "Corpus.Medium\\ELatin1",
+ font_size / 2, missing, 0, 2,
+ *x, top_y);
+ }
+
+ /* last two characters underneath */
+ if (action == rufl_PAINT) {
+ rufl_fm_error = xfont_paint(f, missing + 2,
+ font_OS_UNITS |
+ font_GIVEN_LENGTH | font_GIVEN_FONT |
+ font_KERN |
+ ((flags & rufl_BLEND_FONT) ?
+ font_BLEND_FONT : 0),
+ *x, y, 0, 0, 2);
+ if (rufl_fm_error)
+ return rufl_FONT_MANAGER_ERROR;
+ } else {
+ callback(context, "Corpus.Medium\\ELatin1",
+ font_size / 2, missing + 2, 0, 2,
+ *x, y);
+ }
+
+ *x += dx;
+ }
+
+ return rufl_OK;
+}
diff --git a/src/rufl_quit.c b/src/rufl_quit.c
new file mode 100644
index 0000000..fc429c0
--- /dev/null
+++ b/src/rufl_quit.c
@@ -0,0 +1,49 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2006 James Bursa <james@semichrome.net>
+ */
+
+#include <stdlib.h>
+#include "oslib/font.h"
+#include "rufl_internal.h"
+
+
+/**
+ * Free all resources used by the library.
+ */
+
+void rufl_quit(void)
+{
+ unsigned int i;
+
+ if (!rufl_font_list)
+ return;
+
+ for (i = 0; i != rufl_font_list_entries; i++) {
+ free(rufl_font_list[i].identifier);
+ free(rufl_font_list[i].charset);
+ }
+ free(rufl_font_list);
+ rufl_font_list = 0;
+
+ for (i = 0; i != rufl_family_list_entries; i++)
+ free((void *) rufl_family_list[i]);
+ free(rufl_family_list);
+ free(rufl_family_map);
+ rufl_family_list = 0;
+
+ for (i = 0; i != rufl_CACHE_SIZE; i++) {
+ if (rufl_cache[i].font != rufl_CACHE_NONE) {
+ xfont_lose_font(rufl_cache[i].f);
+ rufl_cache[i].font = rufl_CACHE_NONE;
+ }
+ }
+
+ free(rufl_family_menu);
+ rufl_family_menu = 0;
+
+ free(rufl_substitution_table);
+ rufl_substitution_table = 0;
+}
diff --git a/src/strfuncs.c b/src/strfuncs.c
new file mode 100644
index 0000000..08b43b2
--- /dev/null
+++ b/src/strfuncs.c
@@ -0,0 +1,37 @@
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "strfuncs.h"
+
+char *strdup(const char *s)
+{
+ size_t len = strlen(s);
+ char *new = malloc(len + 1);
+ if (!new)
+ return 0;
+ memcpy(new, s, len);
+ new[len] = '\0';
+ return new;
+}
+
+char *strndup(const char *s, size_t n)
+{
+ size_t len = strlen(s);
+ if (n < len)
+ len = n;
+ char *new = malloc(len + 1);
+ if (!new)
+ return 0;
+ memcpy(new, s, len);
+ new[len] = '\0';
+ return new;
+}
+
+int strcasecmp(const char *s1, const char *s2)
+{
+ int i;
+ while ((i = tolower(*s1)) && i == tolower(*s2))
+ s1++, s2++;
+ return ((unsigned char) tolower(*s1) - (unsigned char) tolower(*s2));
+}
diff --git a/src/strfuncs.h b/src/strfuncs.h
new file mode 100644
index 0000000..495ea98
--- /dev/null
+++ b/src/strfuncs.h
@@ -0,0 +1,5 @@
+#include <stdlib.h>
+
+char *strdup(const char *s);
+char *strndup(const char *s, size_t n);
+int strcasecmp(const char *s1, const char *s2);